From 20a2074d69b50f9090edf641254aedb1638903a2 Mon Sep 17 00:00:00 2001 From: lujiajian <204551278@qq.com> Date: Tue, 9 Dec 2025 14:28:45 +0800 Subject: [PATCH] =?UTF-8?q?XcharT=E8=A1=A8=E6=A0=BC=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scenes/ChartTable.cs | 18 + Assets/Scenes/ChartTable.cs.meta | 11 + Assets/Scenes/xianchang.unity | 9423 +++++++++++++++++ Assets/Scripts/Line/LineManager.cs | 22 +- Assets/Scripts/Line/LineShowModel.cs | 137 +- Assets/Scripts/LineChart.cs | 28 + Assets/Scripts/LineChart.cs.meta | 11 + Assets/Scripts/SubjectToggle.cs | 2 +- Assets/Scripts/UI/UIPanel/UI_TopTitlePanel.cs | 2 +- Assets/XCharts.meta | 8 + Assets/XCharts/Editor.meta | 8 + Assets/XCharts/Editor/Attributes.meta | 8 + .../Attributes/ComponentEditorAttribute.cs | 15 + .../ComponentEditorAttribute.cs.meta | 11 + .../Editor/Attributes/SerieEditorAttribute.cs | 15 + .../Attributes/SerieEditorAttribute.cs.meta | 11 + Assets/XCharts/Editor/Charts.meta | 8 + .../XCharts/Editor/Charts/BaseChartEditor.cs | 325 + .../Editor/Charts/BaseChartEditor.cs.meta | 11 + Assets/XCharts/Editor/ChildComponents.meta | 8 + .../Editor/ChildComponents/AnimationDrawer.cs | 95 + .../ChildComponents/AnimationDrawer.cs.meta | 11 + .../Editor/ChildComponents/AreaStyleDrawer.cs | 27 + .../ChildComponents/AreaStyleDrawer.cs.meta | 11 + .../ChildComponents/BackgroundDrawer.cs | 28 + .../ChildComponents/BackgroundDrawer.cs.meta | 11 + .../ChildComponents/BasePropertyDrawer.cs | 219 + .../BasePropertyDrawer.cs.meta | 11 + .../ChildComponents/BorderStyleDrawer.cs | 25 + .../ChildComponents/BorderStyleDrawer.cs.meta | 11 + .../ChildComponents/CommentItemDrawer.cs | 26 + .../ChildComponents/CommentItemDrawer.cs.meta | 11 + .../ChildComponents/CommentMarkStyleDrawer.cs | 22 + .../CommentMarkStyleDrawer.cs.meta | 11 + .../ChildComponents/ComponentThemeDrawer.cs | 160 + .../ComponentThemeDrawer.cs.meta | 11 + .../Editor/ChildComponents/DebugInfoDrawer.cs | 25 + .../ChildComponents/DebugInfoDrawer.cs.meta | 11 + .../Editor/ChildComponents/IconStyleDrawer.cs | 30 + .../ChildComponents/IconStyleDrawer.cs.meta | 11 + .../ChildComponents/ImageStyleDrawer.cs | 27 + .../ChildComponents/ImageStyleDrawer.cs.meta | 11 + .../Editor/ChildComponents/ItemStyleDrawer.cs | 40 + .../ChildComponents/ItemStyleDrawer.cs.meta | 11 + .../Editor/ChildComponents/LabelLineDrawer.cs | 31 + .../ChildComponents/LabelLineDrawer.cs.meta | 11 + .../ChildComponents/LabelStyleDrawer.cs | 41 + .../ChildComponents/LabelStyleDrawer.cs.meta | 11 + .../ChildComponents/LevelStyleDrawer.cs | 42 + .../ChildComponents/LevelStyleDrawer.cs.meta | 11 + .../Editor/ChildComponents/LineArrowDrawer.cs | 43 + .../ChildComponents/LineArrowDrawer.cs.meta | 11 + .../Editor/ChildComponents/LineDrawer.cs | 93 + .../Editor/ChildComponents/LineDrawer.cs.meta | 11 + .../Editor/ChildComponents/LineStyleDrawer.cs | 31 + .../ChildComponents/LineStyleDrawer.cs.meta | 11 + .../Editor/ChildComponents/LocationDrawer.cs | 25 + .../ChildComponents/LocationDrawer.cs.meta | 11 + .../Editor/ChildComponents/MLValueDrawer.cs | 27 + .../ChildComponents/MLValueDrawer.cs.meta | 11 + .../ChildComponents/MarqueeStyleDrawer.cs | 25 + .../MarqueeStyleDrawer.cs.meta | 11 + .../ChildComponents/SerieSymbolDrawer.cs | 53 + .../ChildComponents/SerieSymbolDrawer.cs.meta | 11 + .../Editor/ChildComponents/SettingsDrawer.cs | 38 + .../ChildComponents/SettingsDrawer.cs.meta | 11 + .../ChildComponents/StateStyleDrawer.cs | 55 + .../ChildComponents/StateStyleDrawer.cs.meta | 11 + .../ChildComponents/SymbolStyleDrawer.cs | 39 + .../ChildComponents/SymbolStyleDrawer.cs.meta | 11 + .../Editor/ChildComponents/TextLimitDrawer.cs | 24 + .../ChildComponents/TextLimitDrawer.cs.meta | 11 + .../ChildComponents/TextPaddingDrawer.cs | 30 + .../ChildComponents/TextPaddingDrawer.cs.meta | 11 + .../Editor/ChildComponents/TextStyleDrawer.cs | 43 + .../ChildComponents/TextStyleDrawer.cs.meta | 11 + .../Editor/ChildComponents/ThemeDrawer.cs | 136 + .../ChildComponents/ThemeDrawer.cs.meta | 11 + .../ChildComponents/TitleStyleDrawer.cs | 12 + .../ChildComponents/TitleStyleDrawer.cs.meta | 11 + .../ChildComponents/ViewControlDrawer.cs | 23 + .../ChildComponents/ViewControlDrawer.cs.meta | 11 + Assets/XCharts/Editor/MainComponents.meta | 8 + .../Editor/MainComponents/AxisEditor.cs | 237 + .../Editor/MainComponents/AxisEditor.cs.meta | 11 + .../Editor/MainComponents/BackgroundEditor.cs | 21 + .../MainComponents/BackgroundEditor.cs.meta | 11 + .../Editor/MainComponents/BaseGraphEditor.cs | 112 + .../MainComponents/BaseGraphEditor.cs.meta | 11 + .../Editor/MainComponents/CommentEditor.cs | 18 + .../MainComponents/CommentEditor.cs.meta | 11 + .../Editor/MainComponents/DataZoomEditor.cs | 65 + .../MainComponents/DataZoomEditor.cs.meta | 11 + .../MainComponents/GridCoord3DEditor.cs | 23 + .../MainComponents/GridCoord3DEditor.cs.meta | 11 + .../Editor/MainComponents/GridCoordEditor.cs | 25 + .../MainComponents/GridCoordEditor.cs.meta | 11 + .../Editor/MainComponents/GridLayoutEditor.cs | 23 + .../MainComponents/GridLayoutEditor.cs.meta | 11 + .../Editor/MainComponents/LegendEditor.cs | 32 + .../MainComponents/LegendEditor.cs.meta | 11 + .../MainComponents/MainComponentBaseEditor.cs | 116 + .../MainComponentBaseEditor.cs.meta | 11 + .../MainComponents/MainComponentEditor.cs | 8 + .../MainComponentEditor.cs.meta | 11 + .../MainComponents/MainComponentListEditor.cs | 184 + .../MainComponentListEditor.cs.meta | 11 + .../Editor/MainComponents/MarkAreaEditor.cs | 55 + .../MainComponents/MarkAreaEditor.cs.meta | 11 + .../Editor/MainComponents/MarkLineEditor.cs | 60 + .../MainComponents/MarkLineEditor.cs.meta | 11 + .../MainComponents/ParallelCoordEditor.cs | 21 + .../ParallelCoordEditor.cs.meta | 11 + .../Editor/MainComponents/PolarCoordEditor.cs | 19 + .../MainComponents/PolarCoordEditor.cs.meta | 11 + .../Editor/MainComponents/RadarCoordEditor.cs | 52 + .../MainComponents/RadarCoordEditor.cs.meta | 11 + .../Editor/MainComponents/ThemeEditor.cs | 64 + .../Editor/MainComponents/ThemeEditor.cs.meta | 11 + .../Editor/MainComponents/TitleEditor.cs | 21 + .../Editor/MainComponents/TitleEditor.cs.meta | 11 + .../Editor/MainComponents/TooltipEditor.cs | 49 + .../MainComponents/TooltipEditor.cs.meta | 11 + .../MainComponents/UIComponentEditor.cs | 121 + .../MainComponents/UIComponentEditor.cs.meta | 11 + .../MainComponents/UIComponentThemeDrawer.cs | 23 + .../UIComponentThemeDrawer.cs.meta | 11 + .../Editor/MainComponents/VisualMapEditor.cs | 64 + .../MainComponents/VisualMapEditor.cs.meta | 11 + Assets/XCharts/Editor/Series.meta | 8 + Assets/XCharts/Editor/Series/BarEditor.cs | 63 + .../XCharts/Editor/Series/BarEditor.cs.meta | 11 + .../Editor/Series/CandlestickEditor.cs | 26 + .../Editor/Series/CandlestickEditor.cs.meta | 11 + .../Editor/Series/EffectScatterEditor.cs | 26 + .../Editor/Series/EffectScatterEditor.cs.meta | 11 + Assets/XCharts/Editor/Series/HeatmapEditor.cs | 30 + .../Editor/Series/HeatmapEditor.cs.meta | 11 + Assets/XCharts/Editor/Series/LineEditor.cs | 47 + .../XCharts/Editor/Series/LineEditor.cs.meta | 11 + .../XCharts/Editor/Series/ParallelEditor.cs | 17 + .../Editor/Series/ParallelEditor.cs.meta | 11 + Assets/XCharts/Editor/Series/PieEditor.cs | 33 + .../XCharts/Editor/Series/PieEditor.cs.meta | 11 + Assets/XCharts/Editor/Series/RadarEditor.cs | 22 + .../XCharts/Editor/Series/RadarEditor.cs.meta | 11 + Assets/XCharts/Editor/Series/RingEditor.cs | 25 + .../XCharts/Editor/Series/RingEditor.cs.meta | 11 + Assets/XCharts/Editor/Series/ScatterEditor.cs | 27 + .../Editor/Series/ScatterEditor.cs.meta | 11 + .../XCharts/Editor/Series/SerieBaseEditor.cs | 160 + .../Editor/Series/SerieBaseEditor.cs.meta | 11 + .../Editor/Series/SerieDataLinkDrawer.cs | 24 + .../Editor/Series/SerieDataLinkDrawer.cs.meta | 11 + Assets/XCharts/Editor/Series/SerieEditor.cs | 362 + .../XCharts/Editor/Series/SerieEditor.cs.meta | 11 + .../XCharts/Editor/Series/SerieListEditor.cs | 275 + .../Editor/Series/SerieListEditor.cs.meta | 11 + .../Editor/Series/SimplifiedBarEditor.cs | 19 + .../Editor/Series/SimplifiedBarEditor.cs.meta | 11 + .../Series/SimplifiedCandlestickEditor.cs | 17 + .../SimplifiedCandlestickEditor.cs.meta | 11 + .../Editor/Series/SimplifiedLineEditor.cs | 19 + .../Series/SimplifiedLineEditor.cs.meta | 11 + Assets/XCharts/Editor/Utilities.meta | 8 + .../Editor/Utilities/ChartEditorHelper.cs | 802 ++ .../Utilities/ChartEditorHelper.cs.meta | 11 + .../XCharts/Editor/Utilities/EditorStyles.cs | 32 + .../Editor/Utilities/EditorStyles.cs.meta | 11 + .../XCharts/Editor/Utilities/XChartsDaemon.cs | 90 + .../Editor/Utilities/XChartsDaemon.cs.meta | 11 + Assets/XCharts/Editor/Windows.meta | 8 + .../Editor/Windows/PraseExternalDataEditor.cs | 258 + .../Windows/PraseExternalDataEditor.cs.meta | 11 + .../Editor/Windows/XCSettingsEditor.cs | 59 + .../Editor/Windows/XCSettingsEditor.cs.meta | 11 + .../Editor/Windows/XChartsEditor.BarChart.cs | 111 + .../Windows/XChartsEditor.BarChart.cs.meta | 11 + .../Editor/Windows/XChartsEditor.LineChart.cs | 95 + .../Windows/XChartsEditor.LineChart.cs.meta | 11 + .../Editor/Windows/XChartsEditor.PieChart.cs | 63 + .../Windows/XChartsEditor.PieChart.cs.meta | 11 + .../Windows/XChartsEditor.PolarChart.cs | 49 + .../Windows/XChartsEditor.PolarChart.cs.meta | 11 + .../XCharts/Editor/Windows/XChartsEditor.cs | 403 + .../Editor/Windows/XChartsEditor.cs.meta | 11 + Assets/XCharts/Editor/XCharts.Editor.asmdef | 17 + .../XCharts/Editor/XCharts.Editor.asmdef.meta | 7 + Assets/XCharts/Examples.meta | 8 + .../XCharts/Examples/Example00_CheatSheet.cs | 313 + .../Examples/Example00_CheatSheet.cs.meta | 11 + .../XCharts/Examples/Example01_RandomData.cs | 238 + .../Examples/Example01_RandomData.cs.meta | 11 + .../XCharts/Examples/Example02_ChartEvent.cs | 113 + .../Examples/Example02_ChartEvent.cs.meta | 11 + .../Examples/Example03_ChartAnimation.cs | 38 + .../Examples/Example03_ChartAnimation.cs.meta | 11 + Assets/XCharts/Examples/Example04_DataZoom.cs | 50 + .../Examples/Example04_DataZoom.cs.meta | 11 + .../Examples/Example05_DynamicChart.cs | 95 + .../Examples/Example05_DynamicChart.cs.meta | 11 + .../XCharts/Examples/Example10_LineChart.cs | 260 + .../Examples/Example10_LineChart.cs.meta | 11 + .../XCharts/Examples/Example11_AddSinCurve.cs | 62 + .../Examples/Example11_AddSinCurve.cs.meta | 11 + .../Examples/Example12_CustomDrawing.cs | 41 + .../Examples/Example12_CustomDrawing.cs.meta | 11 + .../XCharts/Examples/Example13_LineSimple.cs | 61 + .../Examples/Example13_LineSimple.cs.meta | 11 + Assets/XCharts/Examples/Example20_BarChart.cs | 158 + .../Examples/Example20_BarChart.cs.meta | 11 + Assets/XCharts/Examples/Example21_BarRace.cs | 46 + .../Examples/Example21_BarRace.cs.meta | 11 + Assets/XCharts/Examples/Example30_PieChart.cs | 207 + .../Examples/Example30_PieChart.cs.meta | 11 + .../Examples/Example31_PieUpdateName.cs | 77 + .../Examples/Example31_PieUpdateName.cs.meta | 11 + Assets/XCharts/Examples/Example40_Radar.cs | 139 + .../XCharts/Examples/Example40_Radar.cs.meta | 11 + .../XCharts/Examples/Example41_RadarUpdate.cs | 76 + .../Examples/Example41_RadarUpdate.cs.meta | 11 + Assets/XCharts/Examples/Example50_Scatter.cs | 29 + .../Examples/Example50_Scatter.cs.meta | 11 + Assets/XCharts/Examples/Example60_Heatmap.cs | 112 + .../Examples/Example60_Heatmap.cs.meta | 11 + Assets/XCharts/Examples/Example80_Polar.cs | 55 + .../XCharts/Examples/Example80_Polar.cs.meta | 11 + .../XCharts/Examples/Example90_Candlestick.cs | 69 + .../Examples/Example90_Candlestick.cs.meta | 11 + Assets/XCharts/Examples/Example_Test.cs | 47 + Assets/XCharts/Examples/Example_Test.cs.meta | 11 + .../XCharts/Examples/XCharts.Examples.asmdef | 15 + .../Examples/XCharts.Examples.asmdef.meta | 7 + Assets/XCharts/LICENSE.md | 21 + Assets/XCharts/LICENSE.md.meta | 7 + Assets/XCharts/Plugins.meta | 8 + Assets/XCharts/Plugins/Download.jslib | 24 + Assets/XCharts/Plugins/Download.jslib.meta | 34 + Assets/XCharts/README-en.md | 115 + Assets/XCharts/README-en.md.meta | 7 + Assets/XCharts/README.md | 152 + Assets/XCharts/README.md.meta | 7 + Assets/XCharts/Resources.meta | 8 + Assets/XCharts/Resources/XCLang-EN.asset | 57 + Assets/XCharts/Resources/XCLang-EN.asset.meta | 8 + Assets/XCharts/Resources/XCLang-ZH.asset | 89 + Assets/XCharts/Resources/XCLang-ZH.asset.meta | 8 + Assets/XCharts/Resources/XCSettings.asset | 52 + .../XCharts/Resources/XCSettings.asset.meta | 8 + Assets/XCharts/Resources/XCTheme-Dark.asset | 203 + .../XCharts/Resources/XCTheme-Dark.asset.meta | 8 + .../XCharts/Resources/XCTheme-Default.asset | 160 + .../Resources/XCTheme-Default.asset.meta | 8 + Assets/XCharts/Runtime.meta | 8 + Assets/XCharts/Runtime/Chart.meta | 8 + Assets/XCharts/Runtime/Chart/BarChart.cs | 178 + Assets/XCharts/Runtime/Chart/BarChart.cs.meta | 11 + .../XCharts/Runtime/Chart/CandlestickChart.cs | 30 + .../Runtime/Chart/CandlestickChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/HeatmapChart.cs | 112 + .../Runtime/Chart/HeatmapChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/LineChart.cs | 146 + .../XCharts/Runtime/Chart/LineChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/ParallelChart.cs | 35 + .../Runtime/Chart/ParallelChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/PieChart.cs | 89 + Assets/XCharts/Runtime/Chart/PieChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/PolarChart.cs | 168 + .../XCharts/Runtime/Chart/PolarChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/RadarChart.cs | 35 + .../XCharts/Runtime/Chart/RadarChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/RingChart.cs | 36 + .../XCharts/Runtime/Chart/RingChart.cs.meta | 11 + Assets/XCharts/Runtime/Chart/ScatterChart.cs | 48 + .../Runtime/Chart/ScatterChart.cs.meta | 11 + .../Runtime/Chart/SimplifiedBarChart.cs | 30 + .../Runtime/Chart/SimplifiedBarChart.cs.meta | 11 + .../Chart/SimplifiedCandlestickChart.cs | 30 + .../Chart/SimplifiedCandlestickChart.cs.meta | 11 + .../Runtime/Chart/SimplifiedLineChart.cs | 30 + .../Runtime/Chart/SimplifiedLineChart.cs.meta | 11 + Assets/XCharts/Runtime/Component.meta | 8 + .../XCharts/Runtime/Component/Animation.meta | 8 + .../Component/Animation/AnimationInfo.cs | 496 + .../Component/Animation/AnimationInfo.cs.meta | 11 + .../Animation/AnimationInfoContext.cs | 26 + .../Animation/AnimationInfoContext.cs.meta | 11 + .../Component/Animation/AnimationStyle.cs | 613 ++ .../Animation/AnimationStyle.cs.meta | 11 + .../Animation/AnimationStyleContext.cs | 14 + .../Animation/AnimationStyleContext.cs.meta | 11 + .../Animation/AnimationStyleHelper.cs | 77 + .../Animation/AnimationStyleHelper.cs.meta | 11 + Assets/XCharts/Runtime/Component/Axis.meta | 8 + .../Runtime/Component/Axis/AngleAxis.meta | 8 + .../Component/Axis/AngleAxis/AngleAxis.cs | 53 + .../Axis/AngleAxis/AngleAxis.cs.meta | 11 + .../Axis/AngleAxis/AngleAxisHandler.cs | 174 + .../Axis/AngleAxis/AngleAxisHandler.cs.meta | 11 + Assets/XCharts/Runtime/Component/Axis/Axis.cs | 971 ++ .../Runtime/Component/Axis/Axis.cs.meta | 11 + .../Runtime/Component/Axis/Axis3DHelper.cs | 160 + .../Component/Axis/Axis3DHelper.cs.meta | 11 + .../Runtime/Component/Axis/AxisAnimation.cs | 63 + .../Component/Axis/AxisAnimation.cs.meta | 11 + .../Runtime/Component/Axis/AxisContext.cs | 168 + .../Component/Axis/AxisContext.cs.meta | 11 + .../Runtime/Component/Axis/AxisHandler.cs | 1251 +++ .../Component/Axis/AxisHandler.cs.meta | 11 + .../Runtime/Component/Axis/AxisHelper.cs | 648 ++ .../Runtime/Component/Axis/AxisHelper.cs.meta | 11 + .../Runtime/Component/Axis/AxisLabel.cs | 185 + .../Runtime/Component/Axis/AxisLabel.cs.meta | 11 + .../Runtime/Component/Axis/AxisLine.cs | 77 + .../Runtime/Component/Axis/AxisLine.cs.meta | 11 + .../Component/Axis/AxisMinorSplitLine.cs | 62 + .../Component/Axis/AxisMinorSplitLine.cs.meta | 11 + .../Runtime/Component/Axis/AxisMinorTick.cs | 63 + .../Component/Axis/AxisMinorTick.cs.meta | 11 + .../Runtime/Component/Axis/AxisName.cs | 86 + .../Runtime/Component/Axis/AxisName.cs.meta | 11 + .../Runtime/Component/Axis/AxisSplitArea.cs | 80 + .../Component/Axis/AxisSplitArea.cs.meta | 11 + .../Runtime/Component/Axis/AxisSplitLine.cs | 112 + .../Component/Axis/AxisSplitLine.cs.meta | 11 + .../Runtime/Component/Axis/AxisTick.cs | 110 + .../Runtime/Component/Axis/AxisTick.cs.meta | 11 + .../Runtime/Component/Axis/ParallelAxis.meta | 8 + .../Axis/ParallelAxis/ParallelAxis.cs | 30 + .../Axis/ParallelAxis/ParallelAxis.cs.meta | 11 + .../Axis/ParallelAxis/ParallelAxisHander.cs | 168 + .../ParallelAxis/ParallelAxisHander.cs.meta | 11 + .../Runtime/Component/Axis/RadiusAxis.meta | 8 + .../Component/Axis/RadiusAxis/RadiusAxis.cs | 28 + .../Axis/RadiusAxis/RadiusAxis.cs.meta | 11 + .../Axis/RadiusAxis/RadiusAxisHandler.cs | 217 + .../Axis/RadiusAxis/RadiusAxisHandler.cs.meta | 11 + .../Runtime/Component/Axis/SingleAxis.meta | 8 + .../Component/Axis/SingleAxis/SingleAxis.cs | 162 + .../Axis/SingleAxis/SingleAxis.cs.meta | 11 + .../Axis/SingleAxis/SingleAxisHandler.cs | 122 + .../Axis/SingleAxis/SingleAxisHandler.cs.meta | 11 + .../XCharts/Runtime/Component/Axis/XAxis.meta | 8 + .../Runtime/Component/Axis/XAxis/XAxis.cs | 34 + .../Component/Axis/XAxis/XAxis.cs.meta | 11 + .../Component/Axis/XAxis/XAxisHander.cs | 186 + .../Component/Axis/XAxis/XAxisHander.cs.meta | 11 + .../Runtime/Component/Axis/XAxis3D.meta | 8 + .../Runtime/Component/Axis/XAxis3D/XAxis3D.cs | 36 + .../Component/Axis/XAxis3D/XAxis3D.cs.meta | 11 + .../Component/Axis/XAxis3D/XAxis3DHander.cs | 190 + .../Axis/XAxis3D/XAxis3DHander.cs.meta | 11 + .../XCharts/Runtime/Component/Axis/YAxis.meta | 8 + .../Runtime/Component/Axis/YAxis/YAxis.cs | 32 + .../Component/Axis/YAxis/YAxis.cs.meta | 11 + .../Component/Axis/YAxis/YAxisHander.cs | 162 + .../Component/Axis/YAxis/YAxisHander.cs.meta | 11 + .../Runtime/Component/Axis/YAxis3D.meta | 8 + .../Runtime/Component/Axis/YAxis3D/YAxis3D.cs | 33 + .../Component/Axis/YAxis3D/YAxis3D.cs.meta | 11 + .../Component/Axis/YAxis3D/YAxis3DHander.cs | 176 + .../Axis/YAxis3D/YAxis3DHander.cs.meta | 11 + .../Runtime/Component/Axis/ZAxis3D.meta | 8 + .../Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs | 33 + .../Component/Axis/ZAxis3D/ZAxis3D.cs.meta | 11 + .../Component/Axis/ZAxis3D/ZAxis3DHander.cs | 198 + .../Axis/ZAxis3D/ZAxis3DHander.cs.meta | 11 + .../XCharts/Runtime/Component/Background.meta | 8 + .../Component/Background/Background.cs | 118 + .../Component/Background/Background.cs.meta | 11 + .../Component/Background/BackgroundHandler.cs | 56 + .../Background/BackgroundHandler.cs.meta | 11 + Assets/XCharts/Runtime/Component/Child.meta | 8 + .../Runtime/Component/Child/AreaStyle.cs | 132 + .../Runtime/Component/Child/AreaStyle.cs.meta | 11 + .../Runtime/Component/Child/ArrowStyle.cs | 92 + .../Component/Child/ArrowStyle.cs.meta | 11 + .../Runtime/Component/Child/BaseLine.cs | 82 + .../Runtime/Component/Child/BaseLine.cs.meta | 11 + .../Runtime/Component/Child/BorderStyle.cs | 92 + .../Component/Child/BorderStyle.cs.meta | 11 + .../Runtime/Component/Child/IconStyle.cs | 118 + .../Runtime/Component/Child/IconStyle.cs.meta | 11 + .../Runtime/Component/Child/ImageStyle.cs | 81 + .../Component/Child/ImageStyle.cs.meta | 11 + .../Runtime/Component/Child/ItemStyle.cs | 367 + .../Runtime/Component/Child/ItemStyle.cs.meta | 11 + .../Runtime/Component/Child/LevelStyle.cs | 57 + .../Component/Child/LevelStyle.cs.meta | 11 + .../Runtime/Component/Child/LineArrow.cs | 63 + .../Runtime/Component/Child/LineArrow.cs.meta | 11 + .../Runtime/Component/Child/LineStyle.cs | 285 + .../Runtime/Component/Child/LineStyle.cs.meta | 11 + .../Runtime/Component/Child/Location.cs | 423 + .../Runtime/Component/Child/Location.cs.meta | 11 + .../Runtime/Component/Child/MLValue.cs | 73 + .../Runtime/Component/Child/MLValue.cs.meta | 11 + .../Runtime/Component/Child/MarqueeStyle.cs | 62 + .../Component/Child/MarqueeStyle.cs.meta | 11 + .../Runtime/Component/Child/Padding.cs | 79 + .../Runtime/Component/Child/Padding.cs.meta | 11 + .../Runtime/Component/Child/SerieSymbl.cs | 198 + .../Component/Child/SerieSymbl.cs.meta | 11 + .../Runtime/Component/Child/StageColor.cs | 26 + .../Component/Child/StageColor.cs.meta | 11 + .../Runtime/Component/Child/SymbolStyle.cs | 230 + .../Component/Child/SymbolStyle.cs.meta | 11 + .../Runtime/Component/Child/TextLimit.cs | 150 + .../Runtime/Component/Child/TextLimit.cs.meta | 11 + .../Runtime/Component/Child/TextPadding.cs | 20 + .../Component/Child/TextPadding.cs.meta | 11 + .../Runtime/Component/Child/TextStyle.cs | 231 + .../Runtime/Component/Child/TextStyle.cs.meta | 11 + Assets/XCharts/Runtime/Component/Comment.meta | 8 + .../Runtime/Component/Comment/Comment.cs | 81 + .../Runtime/Component/Comment/Comment.cs.meta | 11 + .../Component/Comment/CommentHander.cs | 75 + .../Component/Comment/CommentHander.cs.meta | 11 + .../Runtime/Component/Comment/CommentItem.cs | 73 + .../Component/Comment/CommentItem.cs.meta | 11 + .../Component/Comment/CommentMarkStyle.cs | 27 + .../Comment/CommentMarkStyle.cs.meta | 11 + .../XCharts/Runtime/Component/DataZoom.meta | 8 + .../Runtime/Component/DataZoom/DataZoom.cs | 754 ++ .../Component/DataZoom/DataZoom.cs.meta | 11 + .../Component/DataZoom/DataZoomContext.cs | 31 + .../DataZoom/DataZoomContext.cs.meta | 11 + .../Component/DataZoom/DataZoomHandler.cs | 701 ++ .../DataZoom/DataZoomHandler.cs.meta | 11 + .../Component/DataZoom/DataZoomHelper.cs | 62 + .../Component/DataZoom/DataZoomHelper.cs.meta | 11 + Assets/XCharts/Runtime/Component/Debug.meta | 8 + .../Runtime/Component/Debug/DebugInfo.cs | 176 + .../Runtime/Component/Debug/DebugInfo.cs.meta | 11 + .../Runtime/Component/Interaction.meta | 8 + .../Component/Interaction/InteractData.cs | 294 + .../Interaction/InteractData.cs.meta | 11 + Assets/XCharts/Runtime/Component/Label.meta | 8 + .../Runtime/Component/Label/EndLabelStyle.cs | 17 + .../Component/Label/EndLabelStyle.cs.meta | 11 + .../Runtime/Component/Label/LabelLine.cs | 166 + .../Runtime/Component/Label/LabelLine.cs.meta | 11 + .../Runtime/Component/Label/LabelStyle.cs | 518 + .../Component/Label/LabelStyle.cs.meta | 11 + .../Component/Label/SerieLabelHelper.cs | 74 + .../Component/Label/SerieLabelHelper.cs.meta | 11 + Assets/XCharts/Runtime/Component/Legend.meta | 8 + .../Runtime/Component/Legend/Legend.cs | 458 + .../Runtime/Component/Legend/Legend.cs.meta | 11 + .../Runtime/Component/Legend/LegendContext.cs | 34 + .../Component/Legend/LegendContext.cs.meta | 11 + .../Runtime/Component/Legend/LegendHandler.cs | 288 + .../Component/Legend/LegendHandler.cs.meta | 11 + .../Runtime/Component/Legend/LegendHelper.cs | 311 + .../Component/Legend/LegendHelper.cs.meta | 11 + Assets/XCharts/Runtime/Component/Mark.meta | 8 + .../Runtime/Component/Mark/MarkArea.cs | 192 + .../Runtime/Component/Mark/MarkArea.cs.meta | 11 + .../Runtime/Component/Mark/MarkAreaHandler.cs | 195 + .../Component/Mark/MarkAreaHandler.cs.meta | 11 + .../Runtime/Component/Mark/MarkLine.cs | 267 + .../Runtime/Component/Mark/MarkLine.cs.meta | 11 + .../Runtime/Component/Mark/MarkLineHandler.cs | 326 + .../Component/Mark/MarkLineHandler.cs.meta | 11 + .../Runtime/Component/Mark/MarkLineHelper.cs | 52 + .../Component/Mark/MarkLineHelper.cs.meta | 11 + Assets/XCharts/Runtime/Component/Radar.meta | 8 + .../Runtime/Component/Radar/RadarCoord.cs | 493 + .../Component/Radar/RadarCoord.cs.meta | 11 + .../Component/Radar/RadarCoordContext.cs | 22 + .../Component/Radar/RadarCoordContext.cs.meta | 11 + .../Component/Radar/RadarCoordHandler.cs | 173 + .../Component/Radar/RadarCoordHandler.cs.meta | 11 + .../XCharts/Runtime/Component/Settings.meta | 8 + .../Runtime/Component/Settings/Settings.cs | 188 + .../Component/Settings/Settings.cs.meta | 11 + Assets/XCharts/Runtime/Component/State.meta | 8 + .../Runtime/Component/State/BlurStyle.cs | 13 + .../Runtime/Component/State/BlurStyle.cs.meta | 11 + .../Runtime/Component/State/EmphasisStyle.cs | 90 + .../Component/State/EmphasisStyle.cs.meta | 11 + .../Runtime/Component/State/SelectStyle.cs | 13 + .../Component/State/SelectStyle.cs.meta | 11 + .../Runtime/Component/State/StateStyle.cs | 126 + .../Component/State/StateStyle.cs.meta | 11 + Assets/XCharts/Runtime/Component/Title.meta | 8 + .../XCharts/Runtime/Component/Title/Title.cs | 105 + .../Runtime/Component/Title/Title.cs.meta | 11 + .../Runtime/Component/Title/TitleHandler.cs | 88 + .../Component/Title/TitleHandler.cs.meta | 11 + .../Runtime/Component/Title/TitleStyle.cs | 15 + .../Component/Title/TitleStyle.cs.meta | 11 + Assets/XCharts/Runtime/Component/Tooltip.meta | 8 + .../Runtime/Component/Tooltip/Tooltip.cs | 646 ++ .../Runtime/Component/Tooltip/Tooltip.cs.meta | 11 + .../Component/Tooltip/TooltipContext.cs | 27 + .../Component/Tooltip/TooltipContext.cs.meta | 11 + .../Component/Tooltip/TooltipHandler.cs | 861 ++ .../Component/Tooltip/TooltipHandler.cs.meta | 11 + .../Component/Tooltip/TooltipHelper.cs | 155 + .../Component/Tooltip/TooltipHelper.cs.meta | 11 + .../Runtime/Component/Tooltip/TooltipView.cs | 301 + .../Component/Tooltip/TooltipView.cs.meta | 11 + .../XCharts/Runtime/Component/VisualMap.meta | 8 + .../Runtime/Component/VisualMap/VisualMap.cs | 660 ++ .../Component/VisualMap/VisualMap.cs.meta | 11 + .../Component/VisualMap/VisualMapContext.cs | 21 + .../VisualMap/VisualMapContext.cs.meta | 11 + .../Component/VisualMap/VisualMapHandler.cs | 373 + .../VisualMap/VisualMapHandler.cs.meta | 11 + .../Component/VisualMap/VisualMapHelper.cs | 189 + .../VisualMap/VisualMapHelper.cs.meta | 11 + Assets/XCharts/Runtime/Coord.meta | 8 + Assets/XCharts/Runtime/Coord/Calendar.meta | 8 + .../Runtime/Coord/Calendar/CalendarCoord.cs | 19 + .../Coord/Calendar/CalendarCoord.cs.meta | 11 + .../Coord/Calendar/CalendarCoordHandler.cs | 9 + .../Calendar/CalendarCoordHandler.cs.meta | 11 + Assets/XCharts/Runtime/Coord/Grid.meta | 8 + .../XCharts/Runtime/Coord/Grid/GridCoord.cs | 337 + .../Runtime/Coord/Grid/GridCoord.cs.meta | 11 + .../Runtime/Coord/Grid/GridCoordContext.cs | 17 + .../Coord/Grid/GridCoordContext.cs.meta | 11 + .../Runtime/Coord/Grid/GridCoordHandler.cs | 89 + .../Coord/Grid/GridCoordHandler.cs.meta | 11 + .../Runtime/Coord/Grid/GridLayoutContext.cs | 12 + .../Coord/Grid/GridLayoutContext.cs.meta | 11 + .../Runtime/Coord/Grid/GridLayoutHandler.cs | 7 + .../Coord/Grid/GridLayoutHandler.cs.meta | 11 + .../XCharts/Runtime/Coord/Grid/XGridLayout.cs | 148 + .../Runtime/Coord/Grid/XGridLayout.cs.meta | 11 + Assets/XCharts/Runtime/Coord/Grid3D.meta | 8 + .../Runtime/Coord/Grid3D/GridCoord3D.cs | 270 + .../Runtime/Coord/Grid3D/GridCoord3D.cs.meta | 11 + .../Coord/Grid3D/GridCoord3DContext.cs | 23 + .../Coord/Grid3D/GridCoord3DContext.cs.meta | 11 + .../Coord/Grid3D/GridCoord3DHandler.cs | 79 + .../Coord/Grid3D/GridCoord3DHandler.cs.meta | 11 + Assets/XCharts/Runtime/Coord/Parallel.meta | 8 + .../Runtime/Coord/Parallel/ParallelCoord.cs | 127 + .../Coord/Parallel/ParallelCoord.cs.meta | 11 + .../Coord/Parallel/ParallelCoordContext.cs | 20 + .../Parallel/ParallelCoordContext.cs.meta | 11 + .../Coord/Parallel/ParallelCoordHandler.cs | 176 + .../Parallel/ParallelCoordHandler.cs.meta | 11 + Assets/XCharts/Runtime/Coord/Polar.meta | 8 + .../XCharts/Runtime/Coord/Polar/PolarCoord.cs | 83 + .../Runtime/Coord/Polar/PolarCoord.cs.meta | 11 + .../Runtime/Coord/Polar/PolarCoordContext.cs | 26 + .../Coord/Polar/PolarCoordContext.cs.meta | 11 + .../Runtime/Coord/Polar/PolarCoordHandler.cs | 49 + .../Coord/Polar/PolarCoordHandler.cs.meta | 11 + .../Runtime/Coord/Polar/PolarHelper.cs | 41 + .../Runtime/Coord/Polar/PolarHelper.cs.meta | 11 + Assets/XCharts/Runtime/Coord/SingleAxis.meta | 8 + .../Coord/SingleAxis/SingleAxisCoord.cs | 9 + .../Coord/SingleAxis/SingleAxisCoord.cs.meta | 11 + Assets/XCharts/Runtime/Helper.meta | 8 + Assets/XCharts/Runtime/Helper/CheckHelper.cs | 152 + .../Runtime/Helper/CheckHelper.cs.meta | 11 + .../XCharts/Runtime/Helper/FormatterHelper.cs | 390 + .../Runtime/Helper/FormatterHelper.cs.meta | 11 + Assets/XCharts/Runtime/I18n.meta | 8 + Assets/XCharts/Runtime/I18n/Lang.cs | 137 + Assets/XCharts/Runtime/I18n/Lang.cs.meta | 11 + Assets/XCharts/Runtime/Internal.meta | 8 + .../XCharts/Runtime/Internal/Attributes.meta | 8 + .../Attributes/ComponentHandlerAttribute.cs | 26 + .../ComponentHandlerAttribute.cs.meta | 11 + .../Attributes/CoordOptionsAttribute.cs | 42 + .../Attributes/CoordOptionsAttribute.cs.meta | 11 + .../Attributes/DefaultAnimationAttribute.cs | 22 + .../DefaultAnimationAttribute.cs.meta | 11 + .../Attributes/DefaultTooltipAttribute.cs | 17 + .../DefaultTooltipAttribute.cs.meta | 11 + .../Internal/Attributes/IgnoreDocAttribute.cs | 12 + .../Attributes/IgnoreDocAttribute.cs.meta | 11 + .../Internal/Attributes/ListForAttribute.cs | 15 + .../Attributes/ListForAttribute.cs.meta | 11 + .../Attributes/ListForComponentAttribute.cs | 11 + .../ListForComponentAttribute.cs.meta | 11 + .../Attributes/ListForSerieAttribute.cs | 11 + .../Attributes/ListForSerieAttribute.cs.meta | 11 + .../RequireChartComponentAttribute.cs | 28 + .../RequireChartComponentAttribute.cs.meta | 11 + .../Attributes/SerieComponentAttribute.cs | 84 + .../SerieComponentAttribute.cs.meta | 11 + .../Attributes/SerieConvertAttribute.cs | 50 + .../Attributes/SerieConvertAttribute.cs.meta | 11 + .../Attributes/SerieDataComponentAttribute.cs | 85 + .../SerieDataComponentAttribute.cs.meta | 11 + .../SerieDataExtraFieldAttribute.cs | 75 + .../SerieDataExtraFieldAttribute.cs.meta | 11 + .../Attributes/SerieHandlerAttribute.cs | 22 + .../Attributes/SerieHandlerAttribute.cs.meta | 11 + .../Internal/Attributes/SinceAttribute.cs | 15 + .../Attributes/SinceAttribute.cs.meta | 11 + .../XCharts/Runtime/Internal/BaseChart.API.cs | 781 ++ .../Runtime/Internal/BaseChart.API.cs.meta | 11 + .../Runtime/Internal/BaseChart.Component.cs | 500 + .../Internal/BaseChart.Component.cs.meta | 11 + .../Runtime/Internal/BaseChart.Custom.cs | 44 + .../Runtime/Internal/BaseChart.Custom.cs.meta | 11 + .../Runtime/Internal/BaseChart.Draw.cs | 134 + .../Runtime/Internal/BaseChart.Draw.cs.meta | 11 + .../Runtime/Internal/BaseChart.Serie.cs | 1170 ++ .../Runtime/Internal/BaseChart.Serie.cs.meta | 11 + Assets/XCharts/Runtime/Internal/BaseChart.cs | 802 ++ .../Runtime/Internal/BaseChart.cs.meta | 11 + .../XCharts/Runtime/Internal/BaseGraph.API.cs | 223 + .../Runtime/Internal/BaseGraph.API.cs.meta | 11 + Assets/XCharts/Runtime/Internal/BaseGraph.cs | 339 + .../Runtime/Internal/BaseGraph.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Basic.meta | 8 + .../Runtime/Internal/Basic/BaseSerie.cs | 110 + .../Runtime/Internal/Basic/BaseSerie.cs.meta | 11 + .../Runtime/Internal/Basic/ChildComponent.cs | 85 + .../Internal/Basic/ChildComponent.cs.meta | 11 + .../Runtime/Internal/Basic/CoordSystem.cs | 13 + .../Internal/Basic/CoordSystem.cs.meta | 11 + .../Runtime/Internal/Basic/MainComponent.cs | 129 + .../Internal/Basic/MainComponent.cs.meta | 11 + .../Internal/Basic/MainComponentContext.cs | 7 + .../Basic/MainComponentContext.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Data.meta | 8 + .../Runtime/Internal/Data/GraphData.cs | 520 + .../Runtime/Internal/Data/GraphData.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Misc.meta | 8 + .../Runtime/Internal/Misc/DelegateFunction.cs | 29 + .../Internal/Misc/DelegateFunction.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Misc/Enums.cs | 18 + .../Runtime/Internal/Misc/Enums.cs.meta | 11 + .../Internal/Misc/INeedSerieContainer.cs | 13 + .../Internal/Misc/INeedSerieContainer.cs.meta | 11 + .../Runtime/Internal/Misc/IPropertyChanged.cs | 10 + .../Internal/Misc/IPropertyChanged.cs.meta | 11 + .../Runtime/Internal/Misc/ISerieComponent.cs | 11 + .../Internal/Misc/ISerieComponent.cs.meta | 11 + .../Runtime/Internal/Misc/ISerieContainer.cs | 8 + .../Internal/Misc/ISerieContainer.cs.meta | 11 + .../Internal/Misc/ISerieDataComponent.cs | 10 + .../Internal/Misc/ISerieDataComponent.cs.meta | 11 + .../Runtime/Internal/Misc/ISimplifiedSerie.cs | 9 + .../Internal/Misc/ISimplifiedSerie.cs.meta | 11 + .../Runtime/Internal/Misc/ITooltipView.cs | 0 .../Internal/Misc/ITooltipView.cs.meta | 11 + .../Internal/Misc/IUpdateRuntimeData.cs | 11 + .../Internal/Misc/IUpdateRuntimeData.cs.meta | 11 + .../Runtime/Internal/Misc/SerieEventData.cs | 46 + .../Internal/Misc/SerieEventData.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Object.meta | 8 + .../Runtime/Internal/Object/ChartLabel.cs | 359 + .../Internal/Object/ChartLabel.cs.meta | 11 + .../Runtime/Internal/Object/ChartObject.cs | 14 + .../Internal/Object/ChartObject.cs.meta | 11 + .../Runtime/Internal/Object/ChartText.cs | 324 + .../Runtime/Internal/Object/ChartText.cs.meta | 11 + .../Runtime/Internal/Object/LegendItem.cs | 225 + .../Internal/Object/LegendItem.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Painter.cs | 72 + .../XCharts/Runtime/Internal/Painter.cs.meta | 11 + Assets/XCharts/Runtime/Internal/Pools.meta | 8 + .../Runtime/Internal/Pools/ListPool.cs | 35 + .../Runtime/Internal/Pools/ListPool.cs.meta | 11 + .../Runtime/Internal/Pools/ObjectPool.cs | 57 + .../Runtime/Internal/Pools/ObjectPool.cs.meta | 11 + .../Runtime/Internal/Pools/SerieDataPool.cs | 26 + .../Internal/Pools/SerieDataPool.cs.meta | 11 + .../Internal/Pools/SerieEventDataPool.cs | 34 + .../Internal/Pools/SerieEventDataPool.cs.meta | 11 + .../Runtime/Internal/Pools/SerieLabelPool.cs | 74 + .../Internal/Pools/SerieLabelPool.cs.meta | 11 + .../XCharts/Runtime/Internal/UIComponent.cs | 158 + .../Runtime/Internal/UIComponent.cs.meta | 11 + .../Runtime/Internal/UIComponentTheme.cs | 62 + .../Runtime/Internal/UIComponentTheme.cs.meta | 11 + .../XCharts/Runtime/Internal/Utilities.meta | 8 + .../Runtime/Internal/Utilities/ChartCached.cs | 258 + .../Internal/Utilities/ChartCached.cs.meta | 11 + .../Runtime/Internal/Utilities/ChartConst.cs | 11 + .../Internal/Utilities/ChartConst.cs.meta | 11 + .../Runtime/Internal/Utilities/ChartDrawer.cs | 215 + .../Internal/Utilities/ChartDrawer.cs.meta | 11 + .../Runtime/Internal/Utilities/ChartHelper.cs | 1118 ++ .../Internal/Utilities/ChartHelper.cs.meta | 11 + .../Internal/Utilities/ComponentHelper.cs | 75 + .../Utilities/ComponentHelper.cs.meta | 11 + .../Runtime/Internal/Utilities/DataHelper.cs | 110 + .../Internal/Utilities/DataHelper.cs.meta | 11 + .../Runtime/Internal/Utilities/InputHelper.cs | 111 + .../Internal/Utilities/InputHelper.cs.meta | 11 + .../Internal/Utilities/LayoutHelper.cs | 226 + .../Internal/Utilities/LayoutHelper.cs.meta | 11 + .../Runtime/Internal/Utilities/MathUtil.cs | 60 + .../Internal/Utilities/MathUtil.cs.meta | 11 + .../Runtime/Internal/Utilities/UIHelper.cs | 76 + .../Internal/Utilities/UIHelper.cs.meta | 11 + .../Runtime/Internal/XCResourcesImporter.cs | 168 + .../Internal/XCResourcesImporter.cs.meta | 11 + Assets/XCharts/Runtime/Internal/XCSettings.cs | 203 + .../Runtime/Internal/XCSettings.cs.meta | 11 + Assets/XCharts/Runtime/Internal/XCThemeMgr.cs | 160 + .../Runtime/Internal/XCThemeMgr.cs.meta | 11 + Assets/XCharts/Runtime/Internal/XChartsMgr.cs | 168 + .../Runtime/Internal/XChartsMgr.cs.meta | 11 + Assets/XCharts/Runtime/Serie.meta | 8 + Assets/XCharts/Runtime/Serie/Bar.meta | 8 + Assets/XCharts/Runtime/Serie/Bar/Bar.cs | 37 + Assets/XCharts/Runtime/Serie/Bar/Bar.cs.meta | 11 + .../Serie/Bar/BarHandler.PolarCoord.cs | 215 + .../Serie/Bar/BarHandler.PolarCoord.cs.meta | 11 + .../XCharts/Runtime/Serie/Bar/BarHandler.cs | 463 + .../Runtime/Serie/Bar/BarHandler.cs.meta | 11 + .../Runtime/Serie/Bar/SimplifiedBar.cs | 42 + .../Runtime/Serie/Bar/SimplifiedBar.cs.meta | 11 + .../Runtime/Serie/Bar/SimplifiedBarHandler.cs | 355 + .../Serie/Bar/SimplifiedBarHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Candlestick.meta | 8 + .../Runtime/Serie/Candlestick/Candlestick.cs | 34 + .../Serie/Candlestick/Candlestick.cs.meta | 11 + .../Serie/Candlestick/CandlestickHandler.cs | 266 + .../Candlestick/CandlestickHandler.cs.meta | 11 + .../Candlestick/SimplifiedCandlestick.cs | 41 + .../Candlestick/SimplifiedCandlestick.cs.meta | 11 + .../SimplifiedCandlestickHandler.cs | 265 + .../SimplifiedCandlestickHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Heatmap.meta | 8 + .../XCharts/Runtime/Serie/Heatmap/Heatmap.cs | 57 + .../Runtime/Serie/Heatmap/Heatmap.cs.meta | 11 + .../Heatmap/HeatmapHandler.PolarCoord.cs | 202 + .../Heatmap/HeatmapHandler.PolarCoord.cs.meta | 11 + .../Runtime/Serie/Heatmap/HeatmapHandler.cs | 515 + .../Serie/Heatmap/HeatmapHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Line.meta | 8 + Assets/XCharts/Runtime/Serie/Line/Line.cs | 36 + .../XCharts/Runtime/Serie/Line/Line.cs.meta | 11 + .../Serie/Line/LineHandler.GridCoord.cs | 406 + .../Serie/Line/LineHandler.GridCoord.cs.meta | 11 + .../Serie/Line/LineHandler.PolarCoord.cs | 283 + .../Serie/Line/LineHandler.PolarCoord.cs.meta | 11 + .../XCharts/Runtime/Serie/Line/LineHandler.cs | 123 + .../Runtime/Serie/Line/LineHandler.cs.meta | 11 + .../XCharts/Runtime/Serie/Line/LineHelper.cs | 622 ++ .../Runtime/Serie/Line/LineHelper.cs.meta | 11 + .../Runtime/Serie/Line/SimplifiedLine.cs | 42 + .../Runtime/Serie/Line/SimplifiedLine.cs.meta | 11 + .../Serie/Line/SimplifiedLineHandler.cs | 268 + .../Serie/Line/SimplifiedLineHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Parallel.meta | 8 + .../Runtime/Serie/Parallel/Parallel.cs | 37 + .../Runtime/Serie/Parallel/Parallel.cs.meta | 11 + .../Runtime/Serie/Parallel/ParallelHandler.cs | 148 + .../Serie/Parallel/ParallelHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Pie.meta | 8 + Assets/XCharts/Runtime/Serie/Pie/Pie.cs | 44 + Assets/XCharts/Runtime/Serie/Pie/Pie.cs.meta | 11 + .../XCharts/Runtime/Serie/Pie/PieHandler.cs | 736 ++ .../Runtime/Serie/Pie/PieHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Radar.meta | 8 + Assets/XCharts/Runtime/Serie/Radar/Radar.cs | 47 + .../XCharts/Runtime/Serie/Radar/Radar.cs.meta | 11 + .../Runtime/Serie/Radar/RadarHandler.cs | 561 + .../Runtime/Serie/Radar/RadarHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Ring.meta | 8 + Assets/XCharts/Runtime/Serie/Ring/Ring.cs | 57 + .../XCharts/Runtime/Serie/Ring/Ring.cs.meta | 11 + .../XCharts/Runtime/Serie/Ring/RingHandler.cs | 485 + .../Runtime/Serie/Ring/RingHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Scatter.meta | 8 + .../Runtime/Serie/Scatter/BaseScatter.cs | 9 + .../Runtime/Serie/Scatter/BaseScatter.cs.meta | 11 + .../Serie/Scatter/BaseScatterHandler.cs | 359 + .../Serie/Scatter/BaseScatterHandler.cs.meta | 11 + .../Runtime/Serie/Scatter/EffectScatter.cs | 28 + .../Serie/Scatter/EffectScatter.cs.meta | 11 + .../Serie/Scatter/EffectScatterHandler.cs | 26 + .../Scatter/EffectScatterHandler.cs.meta | 11 + .../XCharts/Runtime/Serie/Scatter/Scatter.cs | 28 + .../Runtime/Serie/Scatter/Scatter.cs.meta | 11 + .../Runtime/Serie/Scatter/ScatterHandler.cs | 6 + .../Serie/Scatter/ScatterHandler.cs.meta | 11 + .../Runtime/Serie/Serie.ExtraComponent.cs | 203 + .../Serie/Serie.ExtraComponent.cs.meta | 11 + Assets/XCharts/Runtime/Serie/Serie.cs | 2109 ++++ Assets/XCharts/Runtime/Serie/Serie.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SerieContext.cs | 142 + .../Runtime/Serie/SerieContext.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SerieData.cs | 802 ++ .../XCharts/Runtime/Serie/SerieData.cs.meta | 11 + .../XCharts/Runtime/Serie/SerieDataContext.cs | 85 + .../Runtime/Serie/SerieDataContext.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SerieDataLink.cs | 47 + .../Runtime/Serie/SerieDataLink.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SerieHandler.cs | 829 ++ .../Runtime/Serie/SerieHandler.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SerieHelper.cs | 1030 ++ .../XCharts/Runtime/Serie/SerieHelper.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SerieParams.cs | 26 + .../XCharts/Runtime/Serie/SerieParams.cs.meta | 11 + Assets/XCharts/Runtime/Serie/SeriesHelper.cs | 508 + .../Runtime/Serie/SeriesHelper.cs.meta | 11 + Assets/XCharts/Runtime/Theme.meta | 8 + Assets/XCharts/Runtime/Theme/AxisTheme.cs | 259 + .../XCharts/Runtime/Theme/AxisTheme.cs.meta | 11 + .../XCharts/Runtime/Theme/ComponentTheme.cs | 102 + .../Runtime/Theme/ComponentTheme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/DataZoomTheme.cs | 125 + .../Runtime/Theme/DataZoomTheme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/LegendTheme.cs | 36 + .../XCharts/Runtime/Theme/LegendTheme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/SerieTheme.cs | 128 + .../XCharts/Runtime/Theme/SerieTheme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/SubTitleTheme.cs | 25 + .../Runtime/Theme/SubTitleTheme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/Theme.cs | 405 + Assets/XCharts/Runtime/Theme/Theme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/ThemeStyle.cs | 241 + .../XCharts/Runtime/Theme/ThemeStyle.cs.meta | 11 + Assets/XCharts/Runtime/Theme/TitleTheme.cs | 24 + .../XCharts/Runtime/Theme/TitleTheme.cs.meta | 11 + Assets/XCharts/Runtime/Theme/TooltipTheme.cs | 118 + .../Runtime/Theme/TooltipTheme.cs.meta | 11 + .../XCharts/Runtime/Theme/VisualMapTheme.cs | 85 + .../Runtime/Theme/VisualMapTheme.cs.meta | 11 + Assets/XCharts/Runtime/Utilities.meta | 8 + Assets/XCharts/Runtime/Utilities/ColorUtil.cs | 31 + .../Runtime/Utilities/ColorUtil.cs.meta | 11 + .../XCharts/Runtime/Utilities/DateTimeUtil.cs | 255 + .../Runtime/Utilities/DateTimeUtil.cs.meta | 11 + .../Runtime/Utilities/DefineSymbolsUtil.cs | 131 + .../Utilities/DefineSymbolsUtil.cs.meta | 11 + Assets/XCharts/Runtime/Utilities/JsonUtil.cs | 73 + .../Runtime/Utilities/JsonUtil.cs.meta | 11 + .../XCharts/Runtime/Utilities/PropertyUtil.cs | 52 + .../Runtime/Utilities/PropertyUtil.cs.meta | 11 + .../Runtime/Utilities/ReflectionUtil.cs | 133 + .../Runtime/Utilities/ReflectionUtil.cs.meta | 11 + .../XCharts/Runtime/Utilities/RuntimeUtil.cs | 87 + .../Runtime/Utilities/RuntimeUtil.cs.meta | 11 + Assets/XCharts/Runtime/XCharts.Runtime.asmdef | 13 + .../Runtime/XCharts.Runtime.asmdef.meta | 7 + Assets/XCharts/Runtime/XLog.meta | 8 + Assets/XCharts/Runtime/XLog/XLog.cs | 340 + Assets/XCharts/Runtime/XLog/XLog.cs.meta | 11 + Assets/XCharts/Runtime/XUGL.meta | 8 + Assets/XCharts/Runtime/XUGL/SVG.meta | 8 + Assets/XCharts/Runtime/XUGL/SVG/SVG.cs | 39 + Assets/XCharts/Runtime/XUGL/SVG/SVG.cs.meta | 11 + Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs | 202 + .../XCharts/Runtime/XUGL/SVG/SVGPath.cs.meta | 11 + Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs | 36 + .../Runtime/XUGL/SVG/SVGPathSeg.cs.meta | 11 + .../Runtime/XUGL/SVG/SVGPathSegType.cs | 51 + .../Runtime/XUGL/SVG/SVGPathSegType.cs.meta | 11 + Assets/XCharts/Runtime/XUGL/UGL.cs | 2145 ++++ Assets/XCharts/Runtime/XUGL/UGL.cs.meta | 11 + Assets/XCharts/Runtime/XUGL/UGLExample.cs | 55 + .../XCharts/Runtime/XUGL/UGLExample.cs.meta | 11 + Assets/XCharts/Runtime/XUGL/UGLHelper.cs | 496 + Assets/XCharts/Runtime/XUGL/UGLHelper.cs.meta | 11 + Assets/XCharts/package.json | 27 + Assets/XCharts/package.json.meta | 7 + 862 files changed, 75666 insertions(+), 13 deletions(-) create mode 100644 Assets/Scenes/ChartTable.cs create mode 100644 Assets/Scenes/ChartTable.cs.meta create mode 100644 Assets/Scripts/LineChart.cs create mode 100644 Assets/Scripts/LineChart.cs.meta create mode 100644 Assets/XCharts.meta create mode 100644 Assets/XCharts/Editor.meta create mode 100644 Assets/XCharts/Editor/Attributes.meta create mode 100644 Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs create mode 100644 Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs.meta create mode 100644 Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs create mode 100644 Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs.meta create mode 100644 Assets/XCharts/Editor/Charts.meta create mode 100644 Assets/XCharts/Editor/Charts/BaseChartEditor.cs create mode 100644 Assets/XCharts/Editor/Charts/BaseChartEditor.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LineDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LineDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs create mode 100644 Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents.meta create mode 100644 Assets/XCharts/Editor/MainComponents/AxisEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/AxisEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/CommentEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/CommentEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/LegendEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/LegendEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/ThemeEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/ThemeEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/TitleEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/TitleEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/TooltipEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/TooltipEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs create mode 100644 Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs create mode 100644 Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series.meta create mode 100644 Assets/XCharts/Editor/Series/BarEditor.cs create mode 100644 Assets/XCharts/Editor/Series/BarEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/CandlestickEditor.cs create mode 100644 Assets/XCharts/Editor/Series/CandlestickEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/EffectScatterEditor.cs create mode 100644 Assets/XCharts/Editor/Series/EffectScatterEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/HeatmapEditor.cs create mode 100644 Assets/XCharts/Editor/Series/HeatmapEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/LineEditor.cs create mode 100644 Assets/XCharts/Editor/Series/LineEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/ParallelEditor.cs create mode 100644 Assets/XCharts/Editor/Series/ParallelEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/PieEditor.cs create mode 100644 Assets/XCharts/Editor/Series/PieEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/RadarEditor.cs create mode 100644 Assets/XCharts/Editor/Series/RadarEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/RingEditor.cs create mode 100644 Assets/XCharts/Editor/Series/RingEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/ScatterEditor.cs create mode 100644 Assets/XCharts/Editor/Series/ScatterEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SerieBaseEditor.cs create mode 100644 Assets/XCharts/Editor/Series/SerieBaseEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs create mode 100644 Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SerieEditor.cs create mode 100644 Assets/XCharts/Editor/Series/SerieEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SerieListEditor.cs create mode 100644 Assets/XCharts/Editor/Series/SerieListEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs create mode 100644 Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs create mode 100644 Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs create mode 100644 Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Utilities.meta create mode 100644 Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs create mode 100644 Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs.meta create mode 100644 Assets/XCharts/Editor/Utilities/EditorStyles.cs create mode 100644 Assets/XCharts/Editor/Utilities/EditorStyles.cs.meta create mode 100644 Assets/XCharts/Editor/Utilities/XChartsDaemon.cs create mode 100644 Assets/XCharts/Editor/Utilities/XChartsDaemon.cs.meta create mode 100644 Assets/XCharts/Editor/Windows.meta create mode 100644 Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs create mode 100644 Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Windows/XCSettingsEditor.cs create mode 100644 Assets/XCharts/Editor/Windows/XCSettingsEditor.cs.meta create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs.meta create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs.meta create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs.meta create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs.meta create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.cs create mode 100644 Assets/XCharts/Editor/Windows/XChartsEditor.cs.meta create mode 100644 Assets/XCharts/Editor/XCharts.Editor.asmdef create mode 100644 Assets/XCharts/Editor/XCharts.Editor.asmdef.meta create mode 100644 Assets/XCharts/Examples.meta create mode 100644 Assets/XCharts/Examples/Example00_CheatSheet.cs create mode 100644 Assets/XCharts/Examples/Example00_CheatSheet.cs.meta create mode 100644 Assets/XCharts/Examples/Example01_RandomData.cs create mode 100644 Assets/XCharts/Examples/Example01_RandomData.cs.meta create mode 100644 Assets/XCharts/Examples/Example02_ChartEvent.cs create mode 100644 Assets/XCharts/Examples/Example02_ChartEvent.cs.meta create mode 100644 Assets/XCharts/Examples/Example03_ChartAnimation.cs create mode 100644 Assets/XCharts/Examples/Example03_ChartAnimation.cs.meta create mode 100644 Assets/XCharts/Examples/Example04_DataZoom.cs create mode 100644 Assets/XCharts/Examples/Example04_DataZoom.cs.meta create mode 100644 Assets/XCharts/Examples/Example05_DynamicChart.cs create mode 100644 Assets/XCharts/Examples/Example05_DynamicChart.cs.meta create mode 100644 Assets/XCharts/Examples/Example10_LineChart.cs create mode 100644 Assets/XCharts/Examples/Example10_LineChart.cs.meta create mode 100644 Assets/XCharts/Examples/Example11_AddSinCurve.cs create mode 100644 Assets/XCharts/Examples/Example11_AddSinCurve.cs.meta create mode 100644 Assets/XCharts/Examples/Example12_CustomDrawing.cs create mode 100644 Assets/XCharts/Examples/Example12_CustomDrawing.cs.meta create mode 100644 Assets/XCharts/Examples/Example13_LineSimple.cs create mode 100644 Assets/XCharts/Examples/Example13_LineSimple.cs.meta create mode 100644 Assets/XCharts/Examples/Example20_BarChart.cs create mode 100644 Assets/XCharts/Examples/Example20_BarChart.cs.meta create mode 100644 Assets/XCharts/Examples/Example21_BarRace.cs create mode 100644 Assets/XCharts/Examples/Example21_BarRace.cs.meta create mode 100644 Assets/XCharts/Examples/Example30_PieChart.cs create mode 100644 Assets/XCharts/Examples/Example30_PieChart.cs.meta create mode 100644 Assets/XCharts/Examples/Example31_PieUpdateName.cs create mode 100644 Assets/XCharts/Examples/Example31_PieUpdateName.cs.meta create mode 100644 Assets/XCharts/Examples/Example40_Radar.cs create mode 100644 Assets/XCharts/Examples/Example40_Radar.cs.meta create mode 100644 Assets/XCharts/Examples/Example41_RadarUpdate.cs create mode 100644 Assets/XCharts/Examples/Example41_RadarUpdate.cs.meta create mode 100644 Assets/XCharts/Examples/Example50_Scatter.cs create mode 100644 Assets/XCharts/Examples/Example50_Scatter.cs.meta create mode 100644 Assets/XCharts/Examples/Example60_Heatmap.cs create mode 100644 Assets/XCharts/Examples/Example60_Heatmap.cs.meta create mode 100644 Assets/XCharts/Examples/Example80_Polar.cs create mode 100644 Assets/XCharts/Examples/Example80_Polar.cs.meta create mode 100644 Assets/XCharts/Examples/Example90_Candlestick.cs create mode 100644 Assets/XCharts/Examples/Example90_Candlestick.cs.meta create mode 100644 Assets/XCharts/Examples/Example_Test.cs create mode 100644 Assets/XCharts/Examples/Example_Test.cs.meta create mode 100644 Assets/XCharts/Examples/XCharts.Examples.asmdef create mode 100644 Assets/XCharts/Examples/XCharts.Examples.asmdef.meta create mode 100644 Assets/XCharts/LICENSE.md create mode 100644 Assets/XCharts/LICENSE.md.meta create mode 100644 Assets/XCharts/Plugins.meta create mode 100644 Assets/XCharts/Plugins/Download.jslib create mode 100644 Assets/XCharts/Plugins/Download.jslib.meta create mode 100644 Assets/XCharts/README-en.md create mode 100644 Assets/XCharts/README-en.md.meta create mode 100644 Assets/XCharts/README.md create mode 100644 Assets/XCharts/README.md.meta create mode 100644 Assets/XCharts/Resources.meta create mode 100644 Assets/XCharts/Resources/XCLang-EN.asset create mode 100644 Assets/XCharts/Resources/XCLang-EN.asset.meta create mode 100644 Assets/XCharts/Resources/XCLang-ZH.asset create mode 100644 Assets/XCharts/Resources/XCLang-ZH.asset.meta create mode 100644 Assets/XCharts/Resources/XCSettings.asset create mode 100644 Assets/XCharts/Resources/XCSettings.asset.meta create mode 100644 Assets/XCharts/Resources/XCTheme-Dark.asset create mode 100644 Assets/XCharts/Resources/XCTheme-Dark.asset.meta create mode 100644 Assets/XCharts/Resources/XCTheme-Default.asset create mode 100644 Assets/XCharts/Resources/XCTheme-Default.asset.meta create mode 100644 Assets/XCharts/Runtime.meta create mode 100644 Assets/XCharts/Runtime/Chart.meta create mode 100644 Assets/XCharts/Runtime/Chart/BarChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/BarChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/CandlestickChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/CandlestickChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/HeatmapChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/HeatmapChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/LineChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/LineChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/ParallelChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/ParallelChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/PieChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/PieChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/PolarChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/PolarChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/RadarChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/RadarChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/RingChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/RingChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/ScatterChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/ScatterChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs create mode 100644 Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Component.meta create mode 100644 Assets/XCharts/Runtime/Component/Animation.meta create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AngleAxis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/Axis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/Axis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisContext.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisName.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisName.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisTick.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/AxisTick.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/ParallelAxis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/RadiusAxis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/SingleAxis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis3D.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis3D.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/ZAxis3D.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Background.meta create mode 100644 Assets/XCharts/Runtime/Component/Background/Background.cs create mode 100644 Assets/XCharts/Runtime/Component/Background/Background.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/AreaStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/AreaStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/BaseLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/BaseLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/BorderStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/BorderStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/IconStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/IconStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/ImageStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/ImageStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/ItemStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/ItemStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/LevelStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/LevelStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/LineArrow.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/LineArrow.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/LineStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/LineStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/Location.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/Location.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/MLValue.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/MLValue.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/Padding.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/Padding.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/StageColor.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/StageColor.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/TextLimit.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/TextLimit.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/TextPadding.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/TextPadding.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Child/TextStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Child/TextStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Comment.meta create mode 100644 Assets/XCharts/Runtime/Component/Comment/Comment.cs create mode 100644 Assets/XCharts/Runtime/Component/Comment/Comment.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Comment/CommentHander.cs create mode 100644 Assets/XCharts/Runtime/Component/Comment/CommentHander.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Comment/CommentItem.cs create mode 100644 Assets/XCharts/Runtime/Component/Comment/CommentItem.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/DataZoom.meta create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Debug.meta create mode 100644 Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs create mode 100644 Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Interaction.meta create mode 100644 Assets/XCharts/Runtime/Component/Interaction/InteractData.cs create mode 100644 Assets/XCharts/Runtime/Component/Interaction/InteractData.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Label.meta create mode 100644 Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Label/LabelLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Label/LabelLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Label/LabelStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Label/LabelStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Legend.meta create mode 100644 Assets/XCharts/Runtime/Component/Legend/Legend.cs create mode 100644 Assets/XCharts/Runtime/Component/Legend/Legend.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Legend/LegendContext.cs create mode 100644 Assets/XCharts/Runtime/Component/Legend/LegendContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Mark.meta create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkArea.cs create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkArea.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Radar.meta create mode 100644 Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs create mode 100644 Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs create mode 100644 Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Settings.meta create mode 100644 Assets/XCharts/Runtime/Component/Settings/Settings.cs create mode 100644 Assets/XCharts/Runtime/Component/Settings/Settings.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/State.meta create mode 100644 Assets/XCharts/Runtime/Component/State/BlurStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/State/BlurStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/State/SelectStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/State/SelectStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/State/StateStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/State/StateStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Title.meta create mode 100644 Assets/XCharts/Runtime/Component/Title/Title.cs create mode 100644 Assets/XCharts/Runtime/Component/Title/Title.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Title/TitleHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Title/TitleHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Title/TitleStyle.cs create mode 100644 Assets/XCharts/Runtime/Component/Title/TitleStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Tooltip.meta create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs create mode 100644 Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/VisualMap.meta create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs create mode 100644 Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord.meta create mode 100644 Assets/XCharts/Runtime/Coord/Calendar.meta create mode 100644 Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs create mode 100644 Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs create mode 100644 Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs create mode 100644 Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Parallel.meta create mode 100644 Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs create mode 100644 Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs create mode 100644 Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs create mode 100644 Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Polar.meta create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs create mode 100644 Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Coord/SingleAxis.meta create mode 100644 Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs create mode 100644 Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Helper.meta create mode 100644 Assets/XCharts/Runtime/Helper/CheckHelper.cs create mode 100644 Assets/XCharts/Runtime/Helper/CheckHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Helper/FormatterHelper.cs create mode 100644 Assets/XCharts/Runtime/Helper/FormatterHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/I18n.meta create mode 100644 Assets/XCharts/Runtime/I18n/Lang.cs create mode 100644 Assets/XCharts/Runtime/I18n/Lang.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs create mode 100644 Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.API.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.API.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Component.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Component.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseChart.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseGraph.API.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseGraph.API.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/BaseGraph.cs create mode 100644 Assets/XCharts/Runtime/Internal/BaseGraph.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Basic.meta create mode 100644 Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs create mode 100644 Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs create mode 100644 Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs create mode 100644 Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs create mode 100644 Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs create mode 100644 Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Data.meta create mode 100644 Assets/XCharts/Runtime/Internal/Data/GraphData.cs create mode 100644 Assets/XCharts/Runtime/Internal/Data/GraphData.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/Enums.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/Enums.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs create mode 100644 Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Object.meta create mode 100644 Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs create mode 100644 Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Object/ChartObject.cs create mode 100644 Assets/XCharts/Runtime/Internal/Object/ChartObject.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Object/ChartText.cs create mode 100644 Assets/XCharts/Runtime/Internal/Object/ChartText.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Object/LegendItem.cs create mode 100644 Assets/XCharts/Runtime/Internal/Object/LegendItem.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Painter.cs create mode 100644 Assets/XCharts/Runtime/Internal/Painter.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Pools.meta create mode 100644 Assets/XCharts/Runtime/Internal/Pools/ListPool.cs create mode 100644 Assets/XCharts/Runtime/Internal/Pools/ListPool.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs create mode 100644 Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs create mode 100644 Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs create mode 100644 Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs create mode 100644 Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/UIComponent.cs create mode 100644 Assets/XCharts/Runtime/Internal/UIComponent.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/UIComponentTheme.cs create mode 100644 Assets/XCharts/Runtime/Internal/UIComponentTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs create mode 100644 Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs create mode 100644 Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/XCSettings.cs create mode 100644 Assets/XCharts/Runtime/Internal/XCSettings.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/XCThemeMgr.cs create mode 100644 Assets/XCharts/Runtime/Internal/XCThemeMgr.cs.meta create mode 100644 Assets/XCharts/Runtime/Internal/XChartsMgr.cs create mode 100644 Assets/XCharts/Runtime/Internal/XChartsMgr.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie.meta create mode 100644 Assets/XCharts/Runtime/Serie/Bar.meta create mode 100644 Assets/XCharts/Runtime/Serie/Bar/Bar.cs create mode 100644 Assets/XCharts/Runtime/Serie/Bar/Bar.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs create mode 100644 Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs create mode 100644 Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick.meta create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap.meta create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/Line.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/Line.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHelper.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/LineHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Parallel.meta create mode 100644 Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs create mode 100644 Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Pie.meta create mode 100644 Assets/XCharts/Runtime/Serie/Pie/Pie.cs create mode 100644 Assets/XCharts/Runtime/Serie/Pie/Pie.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Radar.meta create mode 100644 Assets/XCharts/Runtime/Serie/Radar/Radar.cs create mode 100644 Assets/XCharts/Runtime/Serie/Radar/Radar.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Ring.meta create mode 100644 Assets/XCharts/Runtime/Serie/Ring/Ring.cs create mode 100644 Assets/XCharts/Runtime/Serie/Ring/Ring.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs create mode 100644 Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/Serie.cs create mode 100644 Assets/XCharts/Runtime/Serie/Serie.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieContext.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieData.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieData.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieDataContext.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieDataContext.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieDataLink.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieDataLink.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieHandler.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieHandler.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieHelper.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SerieParams.cs create mode 100644 Assets/XCharts/Runtime/Serie/SerieParams.cs.meta create mode 100644 Assets/XCharts/Runtime/Serie/SeriesHelper.cs create mode 100644 Assets/XCharts/Runtime/Serie/SeriesHelper.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme.meta create mode 100644 Assets/XCharts/Runtime/Theme/AxisTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/AxisTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/ComponentTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/ComponentTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/DataZoomTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/DataZoomTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/LegendTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/LegendTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/SerieTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/SerieTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/SubTitleTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/SubTitleTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/Theme.cs create mode 100644 Assets/XCharts/Runtime/Theme/Theme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/ThemeStyle.cs create mode 100644 Assets/XCharts/Runtime/Theme/ThemeStyle.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/TitleTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/TitleTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/TooltipTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/TooltipTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Theme/VisualMapTheme.cs create mode 100644 Assets/XCharts/Runtime/Theme/VisualMapTheme.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities.meta create mode 100644 Assets/XCharts/Runtime/Utilities/ColorUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/ColorUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities/JsonUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/JsonUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities/PropertyUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/PropertyUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs create mode 100644 Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs.meta create mode 100644 Assets/XCharts/Runtime/XCharts.Runtime.asmdef create mode 100644 Assets/XCharts/Runtime/XCharts.Runtime.asmdef.meta create mode 100644 Assets/XCharts/Runtime/XLog.meta create mode 100644 Assets/XCharts/Runtime/XLog/XLog.cs create mode 100644 Assets/XCharts/Runtime/XLog/XLog.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL.meta create mode 100644 Assets/XCharts/Runtime/XUGL/SVG.meta create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVG.cs create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVG.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs create mode 100644 Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL/UGL.cs create mode 100644 Assets/XCharts/Runtime/XUGL/UGL.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL/UGLExample.cs create mode 100644 Assets/XCharts/Runtime/XUGL/UGLExample.cs.meta create mode 100644 Assets/XCharts/Runtime/XUGL/UGLHelper.cs create mode 100644 Assets/XCharts/Runtime/XUGL/UGLHelper.cs.meta create mode 100644 Assets/XCharts/package.json create mode 100644 Assets/XCharts/package.json.meta diff --git a/Assets/Scenes/ChartTable.cs b/Assets/Scenes/ChartTable.cs new file mode 100644 index 0000000..259e6cf --- /dev/null +++ b/Assets/Scenes/ChartTable.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using XCharts.Runtime; + +public class ChartTable : MonoBehaviour +{ + + public LineChart chart; + void Start() + { + + } + void Update() + { + + } +} diff --git a/Assets/Scenes/ChartTable.cs.meta b/Assets/Scenes/ChartTable.cs.meta new file mode 100644 index 0000000..66a95e0 --- /dev/null +++ b/Assets/Scenes/ChartTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67c1a80086b64da4cabdad54a59dbbb8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/xianchang.unity b/Assets/Scenes/xianchang.unity index fc944a1..b637486 100644 --- a/Assets/Scenes/xianchang.unity +++ b/Assets/Scenes/xianchang.unity @@ -123,6 +123,558 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &732773 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 732774} + - component: {fileID: 732776} + - component: {fileID: 732775} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &732774 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 732773} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2018747770} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &732775 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 732773} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &732776 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 732773} + m_CullTransparentMesh: 1 +--- !u!1 &2597108 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2597109} + - component: {fileID: 2597111} + - component: {fileID: 2597110} + m_Layer: 5 + m_Name: column0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2597109 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2597108} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 12087285} + - {fileID: 2011498645} + m_Father: {fileID: 1383321929} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &2597110 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2597108} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2597111 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2597108} + m_CullTransparentMesh: 1 +--- !u!1 &12087284 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 12087285} + - component: {fileID: 12087287} + - component: {fileID: 12087286} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &12087285 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 12087284} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2597109} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &12087286 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 12087284} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &12087287 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 12087284} + m_CullTransparentMesh: 1 +--- !u!1 &23854316 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 23854317} + - component: {fileID: 23854319} + - component: {fileID: 23854318} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &23854317 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 23854316} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1493526407} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 10, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &23854318 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 23854316} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 0 +--- !u!222 &23854319 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 23854316} + m_CullTransparentMesh: 1 +--- !u!1 &24076855 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 24076856} + - component: {fileID: 24076858} + - component: {fileID: 24076857} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &24076856 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24076855} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1903652412} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -58.5, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &24076857 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24076855} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &24076858 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24076855} + m_CullTransparentMesh: 1 +--- !u!1 &24261376 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 24261377} + - component: {fileID: 24261379} + - component: {fileID: 24261378} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &24261377 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24261376} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 878883520} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0, y: 0} + m_SizeDelta: {x: 97, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &24261378 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24261376} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 2025-12-0... +--- !u!222 &24261379 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24261376} + m_CullTransparentMesh: 1 +--- !u!1 &29422193 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 29422194} + - component: {fileID: 29422196} + - component: {fileID: 29422195} + m_Layer: 5 + m_Name: axis_4 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &29422194 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 29422193} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 584817190} + - {fileID: 1770427450} + m_Father: {fileID: 1528685111} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: 84} + m_SizeDelta: {x: 24, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &29422195 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 29422193} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &29422196 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 29422193} + m_CullTransparentMesh: 1 --- !u!1 &31259005 GameObject: m_ObjectHideFlags: 0 @@ -254,6 +806,72 @@ MeshCollider: m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 3680687292192122401, guid: b11e3115ad4164947957f5e35d5527cf, type: 3} +--- !u!1 &50599173 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 50599174} + - component: {fileID: 50599176} + - component: {fileID: 50599175} + m_Layer: 5 + m_Name: painter_5 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &50599174 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 50599173} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &50599175 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 50599173} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &50599176 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 50599173} + m_CullTransparentMesh: 1 --- !u!1 &67715185 GameObject: m_ObjectHideFlags: 0 @@ -337,6 +955,72 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 67715185} m_Mesh: {fileID: -7534162388597001602, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &75013130 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 75013131} + - component: {fileID: 75013133} + - component: {fileID: 75013132} + m_Layer: 5 + m_Name: painter_t + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &75013131 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 75013130} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 18 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &75013132 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 75013130} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &75013133 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 75013130} + m_CullTransparentMesh: 1 --- !u!1 &82777329 GameObject: m_ObjectHideFlags: 0 @@ -561,6 +1245,86 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 96211645} m_Mesh: {fileID: -978452405140492152, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &109621950 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 109621951} + - component: {fileID: 109621953} + - component: {fileID: 109621952} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &109621951 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 109621950} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 471345382} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &109621952 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 109621950} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 40 +--- !u!222 &109621953 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 109621950} + m_CullTransparentMesh: 1 --- !u!1 &127228036 GameObject: m_ObjectHideFlags: 0 @@ -687,6 +1451,7 @@ GameObject: - component: {fileID: 140014614} - component: {fileID: 140014615} - component: {fileID: 140014616} + - component: {fileID: 140014619} m_Layer: 0 m_Name: Manager m_TagString: Untagged @@ -896,6 +1661,101 @@ MonoBehaviour: deleteKey: 325 deleteDetectionRadius: 0.5 currentState: 0 + enableDynamicThickness: 1 + minThickness: 0.000005 + maxThickness: 0.0009 + closeDistance: 1 + farDistance: 10 +--- !u!114 &140014619 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 140014610} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7c0d98835e075144c986b47a2a969d32, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &148785228 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 148785229} + - component: {fileID: 148785231} + - component: {fileID: 148785230} + m_Layer: 5 + m_Name: YAxis0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &148785229 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 148785228} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1552550275} + - {fileID: 1549704675} + m_Father: {fileID: 1700330602} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 290, y: -150} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &148785230 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 148785228} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &148785231 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 148785228} + m_CullTransparentMesh: 1 --- !u!1 &154209944 GameObject: m_ObjectHideFlags: 0 @@ -1318,6 +2178,234 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 211242733} m_CullTransparentMesh: 1 +--- !u!1 &227398202 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 227398203} + - component: {fileID: 227398205} + - component: {fileID: 227398204} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &227398203 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 227398202} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1930752646} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &227398204 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 227398202} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &227398205 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 227398202} + m_CullTransparentMesh: 1 +--- !u!1 &231622059 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 231622060} + - component: {fileID: 231622062} + - component: {fileID: 231622061} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &231622060 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 231622059} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1136981718} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -58.5, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &231622061 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 231622059} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &231622062 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 231622059} + m_CullTransparentMesh: 1 +--- !u!1 &233661675 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 233661676} + - component: {fileID: 233661678} + - component: {fileID: 233661677} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &233661676 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 233661675} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1162711160} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -20, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &233661677 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 233661675} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &233661678 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 233661675} + m_CullTransparentMesh: 1 --- !u!1 &239612372 GameObject: m_ObjectHideFlags: 0 @@ -1464,6 +2552,84 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &263478111 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 263478112} + - component: {fileID: 263478114} + - component: {fileID: 263478113} + m_Layer: 5 + m_Name: datazoomstart + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &263478112 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 263478111} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 735697920} + - {fileID: 1699763816} + m_Father: {fileID: 1793713344} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -290, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &263478113 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 263478111} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &263478114 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 263478111} + m_CullTransparentMesh: 1 --- !u!1 &309747185 GameObject: m_ObjectHideFlags: 0 @@ -1576,6 +2742,240 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 309747185} m_Mesh: {fileID: 7746523985622863035, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &309858255 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 309858256} + - component: {fileID: 309858258} + - component: {fileID: 309858257} + m_Layer: 5 + m_Name: column1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &309858256 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 309858255} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1272543448} + - {fileID: 1987066720} + m_Father: {fileID: 1383321929} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &309858257 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 309858255} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &309858258 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 309858255} + m_CullTransparentMesh: 1 +--- !u!1 &322744744 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 322744745} + - component: {fileID: 322744747} + - component: {fileID: 322744746} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &322744745 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 322744744} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 817002998} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -20, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &322744746 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 322744744} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &322744747 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 322744744} + m_CullTransparentMesh: 1 +--- !u!1 &326403615 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 326403616} + - component: {fileID: 326403618} + - component: {fileID: 326403617} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &326403616 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 326403615} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 524176363} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 2, y: -0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &326403617 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 326403615} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &326403618 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 326403615} + m_CullTransparentMesh: 1 --- !u!1 &328649574 GameObject: m_ObjectHideFlags: 0 @@ -1711,6 +3111,82 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 328649574} m_CullTransparentMesh: 1 +--- !u!1 &368015770 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 368015771} + - component: {fileID: 368015773} + - component: {fileID: 368015772} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &368015771 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 368015770} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 964037858} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -58.5, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &368015772 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 368015770} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &368015773 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 368015770} + m_CullTransparentMesh: 1 --- !u!1001 &379506783 PrefabInstance: m_ObjectHideFlags: 0 @@ -1974,6 +3450,448 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 68.744, y: 123.343, z: 150.814} +--- !u!1 &400162282 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 400162283} + - component: {fileID: 400162285} + - component: {fileID: 400162284} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &400162283 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 400162282} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1471853573} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 10, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &400162284 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 400162282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 5 +--- !u!222 &400162285 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 400162282} + m_CullTransparentMesh: 1 +--- !u!1 &461711476 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 461711477} + - component: {fileID: 461711479} + - component: {fileID: 461711478} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &461711477 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 461711476} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1471853573} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -15, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &461711478 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 461711476} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &461711479 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 461711476} + m_CullTransparentMesh: 1 +--- !u!1 &471345381 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 471345382} + - component: {fileID: 471345384} + - component: {fileID: 471345383} + m_Layer: 5 + m_Name: axis_6 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &471345382 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 471345381} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 109621951} + - {fileID: 1621379198} + m_Father: {fileID: 1528685111} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: 84} + m_SizeDelta: {x: 24, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &471345383 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 471345381} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &471345384 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 471345381} + m_CullTransparentMesh: 1 +--- !u!1 &473898012 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 473898013} + - component: {fileID: 473898015} + - component: {fileID: 473898014} + m_Layer: 5 + m_Name: painter_4 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &473898013 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 473898012} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &473898014 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 473898012} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &473898015 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 473898012} + m_CullTransparentMesh: 1 +--- !u!1 &479140428 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 479140429} + - component: {fileID: 479140431} + - component: {fileID: 479140430} + m_Layer: 5 + m_Name: painter_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &479140429 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 479140428} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &479140430 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 479140428} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &479140431 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 479140428} + m_CullTransparentMesh: 1 +--- !u!1 &484223209 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 484223210} + - component: {fileID: 484223212} + - component: {fileID: 484223211} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &484223210 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 484223209} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2038604952} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -34, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &484223211 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 484223209} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &484223212 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 484223209} + m_CullTransparentMesh: 1 --- !u!1 &492161850 GameObject: m_ObjectHideFlags: 0 @@ -2422,6 +4340,84 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 517604011} m_Mesh: {fileID: 8418363027437880420, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &524176362 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 524176363} + - component: {fileID: 524176365} + - component: {fileID: 524176364} + m_Layer: 5 + m_Name: XAxis0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &524176363 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 524176362} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 326403616} + - {fileID: 1005432078} + m_Father: {fileID: 1700330602} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 290, y: -150} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &524176364 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 524176362} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &524176365 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 524176362} + m_CullTransparentMesh: 1 --- !u!1 &527703067 GameObject: m_ObjectHideFlags: 0 @@ -2499,6 +4495,72 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 527703067} m_CullTransparentMesh: 1 +--- !u!1 &543255132 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543255133} + - component: {fileID: 543255135} + - component: {fileID: 543255134} + m_Layer: 5 + m_Name: painter_3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &543255133 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 543255132} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &543255134 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 543255132} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &543255135 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 543255132} + m_CullTransparentMesh: 1 --- !u!1 &561287910 GameObject: m_ObjectHideFlags: 0 @@ -2611,6 +4673,124 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 561287910} m_Mesh: {fileID: -2198223755164019041, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &569790075 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 569790076} + m_Layer: 5 + m_Name: Title0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &569790076 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 569790075} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2038604952} + - {fileID: 2141516038} + m_Father: {fileID: 1746210670} + m_RootOrder: 13 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0, y: -9} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 1} +--- !u!1 &584817189 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 584817190} + - component: {fileID: 584817192} + - component: {fileID: 584817191} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &584817190 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584817189} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 29422194} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &584817191 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584817189} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 20 +--- !u!222 &584817192 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584817189} + m_CullTransparentMesh: 1 --- !u!1 &585553208 GameObject: m_ObjectHideFlags: 0 @@ -2813,6 +4993,230 @@ PrefabInstance: objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: a33e9b5279267db47a802d72bad630a4, type: 3} +--- !u!1 &595637039 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 595637040} + - component: {fileID: 595637042} + - component: {fileID: 595637041} + m_Layer: 5 + m_Name: painter_9 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &595637040 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595637039} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &595637041 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595637039} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &595637042 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595637039} + m_CullTransparentMesh: 1 +--- !u!1 &621286912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 621286913} + - component: {fileID: 621286915} + - component: {fileID: 621286914} + m_Layer: 5 + m_Name: datazoomend + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &621286913 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 621286912} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1265497240} + - {fileID: 1777657461} + m_Father: {fileID: 1793713344} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 290, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &621286914 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 621286912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &621286915 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 621286912} + m_CullTransparentMesh: 1 +--- !u!1 &622579335 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 622579336} + - component: {fileID: 622579338} + - component: {fileID: 622579337} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &622579336 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 622579335} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2038604952} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: -0, y: -0} + m_SizeDelta: {x: 48, y: 27} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &622579337 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 622579335} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.93333334, g: 0.94509804, b: 0.98039216, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 1 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: "\u6570\u636E" +--- !u!222 &622579338 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 622579335} + m_CullTransparentMesh: 1 --- !u!1001 &623316365 PrefabInstance: m_ObjectHideFlags: 0 @@ -2875,6 +5279,184 @@ Transform: m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} m_PrefabInstance: {fileID: 623316365} m_PrefabAsset: {fileID: 0} +--- !u!1 &624673606 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 624673607} + - component: {fileID: 624673609} + - component: {fileID: 624673608} + m_Layer: 5 + m_Name: painter_6 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &624673607 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 624673606} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &624673608 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 624673606} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &624673609 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 624673606} + m_CullTransparentMesh: 1 +--- !u!1 &649069855 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 649069856} + - component: {fileID: 649069858} + - component: {fileID: 649069857} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &649069856 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649069855} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1435914212} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -19.5, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &649069857 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649069855} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &649069858 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649069855} + m_CullTransparentMesh: 1 +--- !u!1 &654135559 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 654135560} + m_Layer: 5 + m_Name: label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &654135560 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 654135559} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1815209302} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &696718207 GameObject: m_ObjectHideFlags: 0 @@ -2943,6 +5525,198 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &715485977 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 715485978} + m_Layer: 5 + m_Name: title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &715485978 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 715485977} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1815209302} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &735697919 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 735697920} + - component: {fileID: 735697922} + - component: {fileID: 735697921} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &735697920 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 735697919} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 263478112} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &735697921 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 735697919} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &735697922 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 735697919} + m_CullTransparentMesh: 1 +--- !u!1 &744070008 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 744070009} + - component: {fileID: 744070011} + - component: {fileID: 744070010} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &744070009 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 744070008} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 878883520} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -58.5, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &744070010 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 744070008} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &744070011 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 744070008} + m_CullTransparentMesh: 1 --- !u!1 &754513119 stripped GameObject: m_CorrespondingSourceObject: {fileID: -2578546517966586142, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} @@ -3087,6 +5861,82 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 769986614} m_Mesh: {fileID: -9098487143386446186, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &777172745 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 777172746} + - component: {fileID: 777172748} + - component: {fileID: 777172747} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &777172746 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 777172745} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 821627687} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &777172747 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 777172745} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &777172748 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 777172745} + m_CullTransparentMesh: 1 --- !u!1 &809263219 GameObject: m_ObjectHideFlags: 0 @@ -3221,6 +6071,314 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 809263219} m_CullTransparentMesh: 1 +--- !u!1 &817002997 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 817002998} + - component: {fileID: 817003000} + - component: {fileID: 817002999} + m_Layer: 5 + m_Name: axis_3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &817002998 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817002997} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1313261739} + - {fileID: 322744745} + m_Father: {fileID: 1528685111} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: 36} + m_SizeDelta: {x: 24, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &817002999 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817002997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &817003000 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817002997} + m_CullTransparentMesh: 1 +--- !u!1 &821627686 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 821627687} + - component: {fileID: 821627689} + - component: {fileID: 821627688} + m_Layer: 5 + m_Name: info + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &821627687 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821627686} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1720930199} + - {fileID: 777172746} + m_Father: {fileID: 1204370577} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &821627688 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821627686} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1254902, g: 0.1254902, b: 0.1254902, a: 0.6666667} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &821627689 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821627686} + m_CullTransparentMesh: 1 +--- !u!1 &827190920 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 827190921} + - component: {fileID: 827190923} + - component: {fileID: 827190922} + m_Layer: 5 + m_Name: background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &827190921 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 827190920} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &827190922 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 827190920} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.0627451, g: 0.047058824, b: 0.16470589, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &827190923 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 827190920} + m_CullTransparentMesh: 1 +--- !u!1 &841051118 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 841051119} + - component: {fileID: 841051121} + - component: {fileID: 841051120} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &841051119 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 841051118} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2141516038} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &841051120 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 841051118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &841051121 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 841051118} + m_CullTransparentMesh: 1 --- !u!1 &845795817 GameObject: m_ObjectHideFlags: 0 @@ -3383,6 +6541,200 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 998.3075, y: 24.221384, z: 432.59982} m_Center: {x: 0, y: -12.110692, z: 0} +--- !u!1 &856399882 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 856399883} + m_Layer: 5 + m_Name: Tooltip0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &856399883 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 856399882} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1700330602} + - {fileID: 1059165247} + m_Father: {fileID: 1746210670} + m_RootOrder: 19 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &857602824 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 857602825} + - component: {fileID: 857602827} + - component: {fileID: 857602826} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &857602825 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 857602824} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2141516038} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 2, y: -0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &857602826 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 857602824} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 22 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 1 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &857602827 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 857602824} + m_CullTransparentMesh: 1 +--- !u!1 &866104419 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 866104420} + - component: {fileID: 866104422} + - component: {fileID: 866104421} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &866104420 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 866104419} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1493526407} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -15, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &866104421 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 866104419} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &866104422 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 866104419} + m_CullTransparentMesh: 1 --- !u!1 &871829731 GameObject: m_ObjectHideFlags: 0 @@ -3495,6 +6847,242 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 871829731} m_Mesh: {fileID: -5989272016515892068, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &878883519 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 878883520} + - component: {fileID: 878883522} + - component: {fileID: 878883521} + m_Layer: 5 + m_Name: axis_1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &878883520 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 878883519} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 24261377} + - {fileID: 744070009} + m_Father: {fileID: 1352785377} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -108.75001, y: -125} + m_SizeDelta: {x: 101, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &878883521 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 878883519} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &878883522 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 878883519} + m_CullTransparentMesh: 1 +--- !u!1 &881118540 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 881118541} + - component: {fileID: 881118543} + - component: {fileID: 881118542} + m_Layer: 5 + m_Name: axis_5 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &881118541 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 881118540} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2002422876} + - {fileID: 905960762} + m_Father: {fileID: 1528685111} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: 84} + m_SizeDelta: {x: 24, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &881118542 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 881118540} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &881118543 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 881118540} + m_CullTransparentMesh: 1 +--- !u!1 &893394085 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 893394086} + - component: {fileID: 893394088} + - component: {fileID: 893394087} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &893394086 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893394085} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1221574379} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0, y: 0} + m_SizeDelta: {x: 97, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &893394087 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893394085} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 2025-12-0... +--- !u!222 &893394088 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893394085} + m_CullTransparentMesh: 1 --- !u!1 &900714110 GameObject: m_ObjectHideFlags: 0 @@ -3607,6 +7195,224 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 900714110} m_Mesh: {fileID: 4328652657713109384, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &905960761 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 905960762} + - component: {fileID: 905960764} + - component: {fileID: 905960763} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &905960762 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 905960761} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 881118541} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -20, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &905960763 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 905960761} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &905960764 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 905960761} + m_CullTransparentMesh: 1 +--- !u!1 &921126632 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 921126633} + - component: {fileID: 921126635} + - component: {fileID: 921126634} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &921126633 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 921126632} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 953910092} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -20, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &921126634 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 921126632} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &921126635 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 921126632} + m_CullTransparentMesh: 1 +--- !u!1 &924295179 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 924295180} + - component: {fileID: 924295182} + - component: {fileID: 924295181} + m_Layer: 5 + m_Name: painter_b + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &924295180 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 924295179} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &924295181 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 924295179} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &924295182 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 924295179} + m_CullTransparentMesh: 1 --- !u!1 &925892940 GameObject: m_ObjectHideFlags: 0 @@ -3719,6 +7525,238 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 925892940} m_Mesh: {fileID: 3016026921303590564, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &953910091 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 953910092} + - component: {fileID: 953910094} + - component: {fileID: 953910093} + m_Layer: 5 + m_Name: axis_2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &953910092 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 953910091} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1084231790} + - {fileID: 921126633} + m_Father: {fileID: 1528685111} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: -12} + m_SizeDelta: {x: 24, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &953910093 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 953910091} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &953910094 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 953910091} + m_CullTransparentMesh: 1 +--- !u!1 &964037857 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 964037858} + - component: {fileID: 964037860} + - component: {fileID: 964037859} + m_Layer: 5 + m_Name: axis_4 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &964037858 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 964037857} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1262320472} + - {fileID: 368015771} + m_Father: {fileID: 1352785377} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 193.26424, y: -125} + m_SizeDelta: {x: 101, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &964037859 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 964037857} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &964037860 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 964037857} + m_CullTransparentMesh: 1 +--- !u!1 &1005432077 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1005432078} + - component: {fileID: 1005432080} + - component: {fileID: 1005432079} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1005432078 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1005432077} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 524176363} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1005432079 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1005432077} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1005432080 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1005432077} + m_CullTransparentMesh: 1 --- !u!1 &1021607066 GameObject: m_ObjectHideFlags: 0 @@ -4219,6 +8257,127 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 1.6609268, y: 9.14378, z: 2.3236628} m_Center: {x: -0.000015318394, y: -0.0000019073486, z: -0.3833654} +--- !u!1 &1059165246 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1059165247} + - component: {fileID: 1059165251} + - component: {fileID: 1059165250} + - component: {fileID: 1059165249} + - component: {fileID: 1059165248} + m_Layer: 5 + m_Name: view + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1059165247 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059165246} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1930752646} + - {fileID: 1383321929} + m_Father: {fileID: 856399883} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 290, y: -150} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1059165248 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059165246} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 10 + m_Right: 10 + m_Top: 10 + m_Bottom: 10 + m_ChildAlignment: 0 + m_Spacing: 0 + m_ChildForceExpandWidth: 0 + m_ChildForceExpandHeight: 0 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!114 &1059165249 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059165246} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} + m_Name: + m_EditorClassIdentifier: + m_EffectColor: {r: 1, g: 1, b: 1, a: 0} + m_EffectDistance: {x: 2, y: -2} + m_UseGraphicAlpha: 0 +--- !u!114 &1059165250 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059165246} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1059165251 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059165246} + m_CullTransparentMesh: 1 --- !u!1 &1075926901 stripped GameObject: m_CorrespondingSourceObject: {fileID: -3176553419678292264, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} @@ -4251,6 +8410,86 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 0.76161194, y: 0.7243271, z: 0.19907904} m_Center: {x: -0.000015258789, y: 0.0000038146973, z: -0.00000667572} +--- !u!1 &1084231789 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1084231790} + - component: {fileID: 1084231792} + - component: {fileID: 1084231791} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1084231790 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1084231789} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 953910092} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1084231791 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1084231789} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 10 +--- !u!222 &1084231792 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1084231789} + m_CullTransparentMesh: 1 --- !u!1 &1098063709 GameObject: m_ObjectHideFlags: 0 @@ -4282,6 +8521,162 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 15 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1136981717 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1136981718} + - component: {fileID: 1136981720} + - component: {fileID: 1136981719} + m_Layer: 5 + m_Name: axis_3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1136981718 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136981717} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1661193762} + - {fileID: 231622060} + m_Father: {fileID: 1352785377} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 92.59282, y: -125} + m_SizeDelta: {x: 101, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1136981719 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136981717} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1136981720 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136981717} + m_CullTransparentMesh: 1 +--- !u!1 &1162711159 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1162711160} + - component: {fileID: 1162711162} + - component: {fileID: 1162711161} + m_Layer: 5 + m_Name: axis_7 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1162711160 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1162711159} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1745768587} + - {fileID: 233661676} + m_Father: {fileID: 1528685111} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: 84} + m_SizeDelta: {x: 24, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1162711161 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1162711159} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1162711162 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1162711159} + m_CullTransparentMesh: 1 --- !u!1 &1172298543 GameObject: m_ObjectHideFlags: 0 @@ -4394,6 +8789,43 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1172298543} m_Mesh: {fileID: -8040977654764866309, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1204370576 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1204370577} + m_Layer: 5 + m_Name: debug + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1204370577 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1204370576} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 821627687} + m_Father: {fileID: 1746210670} + m_RootOrder: 20 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0, y: 1} --- !u!1 &1206943534 GameObject: m_ObjectHideFlags: 0 @@ -4529,6 +8961,84 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1206943534} m_CullTransparentMesh: 1 +--- !u!1 &1221574378 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1221574379} + - component: {fileID: 1221574381} + - component: {fileID: 1221574380} + m_Layer: 5 + m_Name: axis_2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1221574379 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1221574378} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 893394086} + - {fileID: 1255996233} + m_Father: {fileID: 1352785377} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -8.078598, y: -125} + m_SizeDelta: {x: 101, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1221574380 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1221574378} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1221574381 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1221574378} + m_CullTransparentMesh: 1 --- !u!1 &1245005135 GameObject: m_ObjectHideFlags: 0 @@ -4641,6 +9151,82 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1245005135} m_Mesh: {fileID: -90805422481815080, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1255996232 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1255996233} + - component: {fileID: 1255996235} + - component: {fileID: 1255996234} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1255996233 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1255996232} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1221574379} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -58.5, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1255996234 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1255996232} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1255996235 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1255996232} + m_CullTransparentMesh: 1 --- !u!1001 &1257179501 PrefabInstance: m_ObjectHideFlags: 0 @@ -4703,6 +9289,246 @@ Transform: m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: a33e9b5279267db47a802d72bad630a4, type: 3} m_PrefabInstance: {fileID: 1257179501} m_PrefabAsset: {fileID: 0} +--- !u!1 &1262320471 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1262320472} + - component: {fileID: 1262320474} + - component: {fileID: 1262320473} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1262320472 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1262320471} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 964037858} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0, y: 0} + m_SizeDelta: {x: 97, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1262320473 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1262320471} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 2025-12-0... +--- !u!222 &1262320474 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1262320471} + m_CullTransparentMesh: 1 +--- !u!1 &1265497239 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1265497240} + - component: {fileID: 1265497242} + - component: {fileID: 1265497241} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1265497240 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265497239} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 621286913} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 2, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &1265497241 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265497239} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1265497242 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265497239} + m_CullTransparentMesh: 1 +--- !u!1 &1272543447 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1272543448} + - component: {fileID: 1272543450} + - component: {fileID: 1272543449} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1272543448 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1272543447} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 309858256} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &1272543449 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1272543447} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1272543450 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1272543447} + m_CullTransparentMesh: 1 --- !u!1 &1287486846 stripped GameObject: m_CorrespondingSourceObject: {fileID: -8987690296473015402, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} @@ -4914,6 +9740,166 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1307015855} m_CullTransparentMesh: 1 +--- !u!1 &1309268912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1309268913} + - component: {fileID: 1309268915} + - component: {fileID: 1309268914} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1309268913 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1309268912} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1930752646} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 2, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &1309268914 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1309268912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1309268915 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1309268912} + m_CullTransparentMesh: 1 +--- !u!1 &1313261738 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1313261739} + - component: {fileID: 1313261741} + - component: {fileID: 1313261740} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1313261739 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1313261738} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 817002998} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1313261740 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1313261738} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 15 +--- !u!222 &1313261741 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1313261738} + m_CullTransparentMesh: 1 --- !u!1 &1336826371 GameObject: m_ObjectHideFlags: 0 @@ -5008,6 +9994,72 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1338641876 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1338641877} + - component: {fileID: 1338641879} + - component: {fileID: 1338641878} + m_Layer: 5 + m_Name: painter_8 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1338641877 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1338641876} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1338641878 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1338641876} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &1338641879 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1338641876} + m_CullTransparentMesh: 1 --- !u!1 &1348712103 GameObject: m_ObjectHideFlags: 0 @@ -5128,6 +10180,48 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1348712103} m_Mesh: {fileID: 112439578646246135, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1352785376 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1352785377} + m_Layer: 5 + m_Name: XAxis0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1352785377 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1352785376} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1903652412} + - {fileID: 878883520} + - {fileID: 1221574379} + - {fileID: 1136981718} + - {fileID: 964037858} + - {fileID: 1435914212} + m_Father: {fileID: 1746210670} + m_RootOrder: 15 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1359383647 GameObject: m_ObjectHideFlags: 0 @@ -5235,6 +10329,45 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1359383647} m_CullTransparentMesh: 1 +--- !u!1 &1383321928 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1383321929} + m_Layer: 5 + m_Name: item0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1383321929 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383321928} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2597109} + - {fileID: 309858256} + - {fileID: 2018747770} + m_Father: {fileID: 1059165247} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 25} + m_Pivot: {x: 0, y: 0.5} --- !u!1 &1396714429 stripped GameObject: m_CorrespondingSourceObject: {fileID: 8304324904651862010, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} @@ -5506,6 +10639,84 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 14 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1435914211 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1435914212} + - component: {fileID: 1435914214} + - component: {fileID: 1435914213} + m_Layer: 5 + m_Name: axis_5 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1435914212 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1435914211} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2021597840} + - {fileID: 649069856} + m_Father: {fileID: 1352785377} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 222.24547, y: -125} + m_SizeDelta: {x: 23, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1435914213 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1435914211} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1435914214 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1435914211} + m_CullTransparentMesh: 1 --- !u!1001 &1445656028 PrefabInstance: m_ObjectHideFlags: 0 @@ -5572,6 +10783,228 @@ PrefabInstance: m_RemovedComponents: - {fileID: 2399593117623680743, guid: 86a8666f9623b6b46b8f4b860a11546f, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 86a8666f9623b6b46b8f4b860a11546f, type: 3} +--- !u!1 &1471853572 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1471853573} + - component: {fileID: 1471853575} + - component: {fileID: 1471853574} + m_Layer: 5 + m_Name: axis_1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1471853573 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1471853572} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 400162283} + - {fileID: 461711477} + m_Father: {fileID: 1528685111} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: -60} + m_SizeDelta: {x: 14, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1471853574 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1471853572} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1471853575 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1471853572} + m_CullTransparentMesh: 1 +--- !u!1 &1486102860 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1486102861} + - component: {fileID: 1486102863} + - component: {fileID: 1486102862} + m_Layer: 5 + m_Name: painter_u + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1486102861 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1486102860} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 12 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1486102862 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1486102860} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &1486102863 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1486102860} + m_CullTransparentMesh: 1 +--- !u!1 &1493526406 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1493526407} + - component: {fileID: 1493526409} + - component: {fileID: 1493526408} + m_Layer: 5 + m_Name: axis_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1493526407 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1493526406} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 23854317} + - {fileID: 866104420} + m_Father: {fileID: 1528685111} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -524.2, y: -108} + m_SizeDelta: {x: 14, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1493526408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1493526406} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1493526409 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1493526406} + m_CullTransparentMesh: 1 --- !u!1 &1515177975 GameObject: m_ObjectHideFlags: 0 @@ -5684,6 +11117,338 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1515177975} m_Mesh: {fileID: 8419009533998702215, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1528685110 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1528685111} + m_Layer: 5 + m_Name: YAxis0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1528685111 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1528685110} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1493526407} + - {fileID: 1471853573} + - {fileID: 953910092} + - {fileID: 817002998} + - {fileID: 29422194} + - {fileID: 881118541} + - {fileID: 471345382} + - {fileID: 1162711160} + m_Father: {fileID: 1746210670} + m_RootOrder: 16 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1539026149 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1539026150} + - component: {fileID: 1539026152} + - component: {fileID: 1539026151} + m_Layer: 5 + m_Name: painter_2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1539026150 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1539026149} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1539026151 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1539026149} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &1539026152 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1539026149} + m_CullTransparentMesh: 1 +--- !u!1 &1549535168 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1549535169} + - component: {fileID: 1549535171} + - component: {fileID: 1549535170} + m_Layer: 5 + m_Name: painter_7 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1549535169 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549535168} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1549535170 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549535168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &1549535171 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549535168} + m_CullTransparentMesh: 1 +--- !u!1 &1549704674 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1549704675} + - component: {fileID: 1549704677} + - component: {fileID: 1549704676} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1549704675 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549704674} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 148785229} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1549704676 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549704674} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1549704677 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1549704674} + m_CullTransparentMesh: 1 +--- !u!1 &1552550274 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1552550275} + - component: {fileID: 1552550277} + - component: {fileID: 1552550276} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1552550275 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1552550274} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 148785229} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 2, y: -0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1552550276 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1552550274} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1552550277 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1552550274} + m_CullTransparentMesh: 1 --- !u!1 &1615636842 stripped GameObject: m_CorrespondingSourceObject: {fileID: -7848099655778327179, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} @@ -5716,6 +11481,242 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 0.7616043, y: 0.7243271, z: 0.19907904} m_Center: {x: 0, y: 0.0000038146973, z: -0.00000667572} +--- !u!1 &1621379197 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1621379198} + - component: {fileID: 1621379200} + - component: {fileID: 1621379199} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1621379198 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1621379197} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 471345382} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -20, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1621379199 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1621379197} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1621379200 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1621379197} + m_CullTransparentMesh: 1 +--- !u!1 &1636678457 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1636678458} + - component: {fileID: 1636678460} + - component: {fileID: 1636678459} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1636678458 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1636678457} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1903652412} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0, y: 0} + m_SizeDelta: {x: 97, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1636678459 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1636678457} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 2025-12-0... +--- !u!222 &1636678460 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1636678457} + m_CullTransparentMesh: 1 +--- !u!1 &1661193761 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1661193762} + - component: {fileID: 1661193764} + - component: {fileID: 1661193763} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1661193762 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1661193761} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1136981718} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0, y: 0} + m_SizeDelta: {x: 97, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1661193763 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1661193761} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 2025-12-0... +--- !u!222 &1661193764 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1661193761} + m_CullTransparentMesh: 1 --- !u!1 &1678785025 GameObject: m_ObjectHideFlags: 0 @@ -5828,6 +11829,120 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1678785025} m_Mesh: {fileID: -7072635575442064509, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1699763815 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1699763816} + - component: {fileID: 1699763818} + - component: {fileID: 1699763817} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1699763816 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1699763815} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 263478112} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1699763817 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1699763815} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1699763818 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1699763815} + m_CullTransparentMesh: 1 +--- !u!1 &1700330601 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1700330602} + m_Layer: 5 + m_Name: label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1700330602 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1700330601} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 524176363} + - {fileID: 148785229} + m_Father: {fileID: 856399883} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1720349207 GameObject: m_ObjectHideFlags: 0 @@ -5940,6 +12055,86 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1720349207} m_Mesh: {fileID: -1244137252518450476, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1720930198 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1720930199} + - component: {fileID: 1720930201} + - component: {fileID: 1720930200} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1720930199 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1720930198} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 821627687} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 2, y: -0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1720930200 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1720930198} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1720930201 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1720930198} + m_CullTransparentMesh: 1 --- !u!1 &1735319911 GameObject: m_ObjectHideFlags: 0 @@ -6052,6 +12247,2161 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1735319911} m_Mesh: {fileID: 8363381348644371785, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1745768586 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1745768587} + - component: {fileID: 1745768589} + - component: {fileID: 1745768588} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1745768587 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1745768586} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1162711160} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1745768588 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1745768586} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 40 +--- !u!222 &1745768589 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1745768586} + m_CullTransparentMesh: 1 +--- !u!1 &1746210669 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1746210670} + - component: {fileID: 1746210672} + - component: {fileID: 1746210671} + m_Layer: 5 + m_Name: LineChart_Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1746210670 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1746210669} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 827190921} + - {fileID: 924295180} + - {fileID: 479140429} + - {fileID: 2016687787} + - {fileID: 1539026150} + - {fileID: 543255133} + - {fileID: 473898013} + - {fileID: 50599174} + - {fileID: 624673607} + - {fileID: 1549535169} + - {fileID: 1338641877} + - {fileID: 595637040} + - {fileID: 1486102861} + - {fileID: 569790076} + - {fileID: 1815209302} + - {fileID: 1352785377} + - {fileID: 1528685111} + - {fileID: 1793713344} + - {fileID: 75013131} + - {fileID: 856399883} + - {fileID: 1204370577} + m_Father: {fileID: 1914064699} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -670, y: 390} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1746210671 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1746210669} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b4f38bd00b4648c448cabfc167538f7c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_EnableTextMeshPro: 0 + m_ChildNodeNames: + - painter_b + - painter_0 + - painter_1 + - painter_2 + - painter_3 + - painter_4 + - painter_5 + - painter_6 + - painter_7 + - painter_8 + - painter_9 + - painter_u + - painter_t + - debug + - serie_0 + - background + - Tooltip0 + - Title0 + - XAxis0 + - YAxis0 + - datazoom0 + m_ChartName: + m_Theme: + m_Show: 1 + m_SharedTheme: {fileID: 11400000, guid: 289d2fc7f4ce24f73b9ed8ec52639f72, type: 2} + m_TransparentBackground: 0 + m_EnableCustomTheme: 0 + m_CustomFont: {fileID: 0} + m_CustomBackgroundColor: + serializedVersion: 2 + rgba: 0 + m_CustomColorPalette: [] + m_Settings: + m_Show: 1 + m_MaxPainter: 10 + m_ReversePainter: 0 + m_BasePainterMaterial: {fileID: 0} + m_SeriePainterMaterial: {fileID: 0} + m_UpperPainterMaterial: {fileID: 0} + m_TopPainterMaterial: {fileID: 0} + m_LineSmoothStyle: 3 + m_LineSmoothness: 2 + m_LineSegmentDistance: 3 + m_CicleSmoothness: 2 + m_LegendIconLineWidth: 2 + m_LegendIconCornerRadius: + - 0.25 + - 0.25 + - 0.25 + - 0.25 + m_AxisMaxSplitNumber: 50 + m_DebugInfo: + m_Show: 1 + m_ShowDebugInfo: 0 + m_ShowAllChartObject: 0 + m_FoldSeries: 0 + m_LabelStyle: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0.1254902, g: 0.1254902, b: 0.1254902, a: 0.6666667} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FontSize: 18 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_ChartInited: 1 + m_AngleAxes: [] + m_Backgrounds: + - m_Show: 1 + m_Image: {fileID: 0} + m_ImageType: 1 + m_ImageColor: {r: 1, g: 1, b: 1, a: 1} + m_ImageWidth: 0 + m_ImageHeight: 0 + m_AutoColor: 1 + m_BorderStyle: + m_Show: 1 + m_BorderWidth: 0 + m_BorderColor: + serializedVersion: 2 + rgba: 0 + m_RoundedCorner: 1 + m_CornerRadius: + - 10 + - 10 + - 10 + - 10 + m_DataZooms: + - m_Enable: 1 + m_FilterMode: 3 + m_XAxisIndexs: 00000000 + m_YAxisIndexs: + m_SupportInside: 1 + m_SupportInsideScroll: 1 + m_SupportInsideDrag: 1 + m_SupportSlider: 1 + m_SupportMarquee: 0 + m_ShowDataShadow: 1 + m_ShowDetail: 0 + m_ZoomLock: 0 + m_FillerColor: + serializedVersion: 2 + rgba: 0 + m_BorderColor: + serializedVersion: 2 + rgba: 0 + m_BorderWidth: 0 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_Left: 10 + m_Right: 10 + m_Top: 0.9 + m_Bottom: 10 + m_RangeMode: 0 + m_Start: 0 + m_End: 70 + m_MinShowNum: 2 + m_ScrollSensitivity: 10 + m_Orient: 0 + m_LabelStyle: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 0.3 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_AreaStyle: + m_Show: 1 + m_Origin: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_Opacity: 0.3 + m_InnerFill: 0 + m_ToTop: 1 + m_MarqueeStyle: + m_Apply: 0 + m_RealRect: 0 + m_AreaStyle: + m_Show: 1 + m_Origin: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_Opacity: 0.6 + m_InnerFill: 0 + m_ToTop: 1 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_StartLock: 0 + m_EndLock: 0 + m_Grids: + - m_Show: 1 + m_LayoutIndex: -1 + m_Left: 0.11 + m_Right: 0.08 + m_Top: 0.22 + m_Bottom: 0.14 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_ShowBorder: 0 + m_BorderWidth: 0 + m_BorderColor: + serializedVersion: 2 + rgba: 0 + m_GridsLayout: [] + m_Legends: [] + m_MarkLines: [] + m_MarkAreas: [] + m_Polars: [] + m_Radars: [] + m_RadiusAxes: [] + m_Titles: + - m_Show: 1 + m_Text: "\u6570\u636E" + m_SubText: + m_LabelStyle: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_SubLabelStyle: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_ItemGap: 0 + m_Location: + m_Align: 2 + m_Left: 0 + m_Right: 0 + m_Top: 0.03 + m_Bottom: 0 + m_Tooltips: + - m_Show: 1 + m_Type: 4 + m_Trigger: 3 + m_TriggerOn: 0 + m_Position: 0 + m_ItemFormatter: + m_TitleFormatter: + m_Marker: "\u25CF" + m_FixedWidth: 0 + m_FixedHeight: 0 + m_MinWidth: 0 + m_MinHeight: 0 + m_NumericFormatter: + m_PaddingLeftRight: 10 + m_PaddingTopBottom: 10 + m_IgnoreDataShow: 0 + m_IgnoreDataDefaultContent: '-' + m_ShowContent: 1 + m_AlwayShowContent: 0 + m_Offset: {x: 18, y: -25} + m_BackgroundImage: {fileID: 0} + m_BackgroundType: 0 + m_BackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_BorderWidth: 2 + m_FixedX: 0 + m_FixedY: 0.7 + m_TitleHeight: 25 + m_ItemHeight: 25 + m_BorderColor: + serializedVersion: 2 + rgba: 16777215 + m_ColumnGapWidths: + - 15 + m_LineStyle: + m_Show: 1 + m_Type: 5 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_TitleLabelStyle: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 3 + m_ContentLabelStyles: + - m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 5 + m_Left: 0 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 0 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + - m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 20 + m_Left: 0 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 0 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 3 + - m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 0 + m_Left: 0 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 0 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 5 + m_VisualMaps: [] + m_XAxes: + - m_Show: 1 + m_Type: 1 + m_MinMaxType: 0 + m_GridIndex: 0 + m_PolarIndex: 0 + m_ParallelIndex: 0 + m_Position: 2 + m_Offset: 0 + m_Min: 0 + m_Max: 0 + m_SplitNumber: 0 + m_Interval: 0 + m_BoundaryGap: 1 + m_MaxCache: 0 + m_LogBase: 10 + m_LogBaseE: 0 + m_CeilRate: 0 + m_Inverse: 0 + m_Clockwise: 1 + m_InsertDataToHead: 0 + m_MinCategorySpacing: 0 + m_Icons: [] + m_Data: + - 2025-12-04 20:00:01.010 + - 2025-12-04 20:00:01.020 + - 2025-12-04 20:00:01.030 + - 2025-12-04 20:00:01.040 + - 2025-12-04 20:00:01.050 + - 2025-12-04 20:00:01.060 + - 2025-12-04 20:00:01.070 + - 2025-12-04 20:00:01.080 + - 2025-12-04 20:00:01.090 + - 2025-12-04 20:00:01.010 + - 2025-12-04 20:00:01.011 + - 2025-12-04 20:00:01.012 + - 2025-12-04 20:00:01.013 + - 2025-12-04 20:00:01.014 + - 2025-12-04 20:00:01.015 + - 2025-12-04 20:00:01.016 + - 2025-12-04 20:00:01.017 + - 2025-12-04 20:00:01.018 + - 2025-12-04 20:00:01.019 + - 2025-12-04 20:00:01.020 + m_AxisLine: + m_Show: 1 + m_LineStyle: + m_Show: 1 + m_Type: 5 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_OnZero: 1 + m_ShowArrow: 0 + m_Arrow: + m_Width: 10 + m_Height: 15 + m_Offset: 0 + m_Dent: 3 + m_Color: + serializedVersion: 2 + rgba: 0 + m_AxisName: + m_Show: 0 + m_Name: axisName + m_OnZero: 0 + m_LabelStyle: + m_Show: 1 + m_Position: 10 + m_AutoOffset: 0 + m_Offset: {x: 5, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_AxisTick: + m_Show: 1 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_AlignWithLabel: 0 + m_Inside: 0 + m_ShowStartTick: 1 + m_ShowEndTick: 1 + m_Distance: 0 + m_SplitNumber: 0 + m_AutoColor: 0 + m_AxisLabel: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 8 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_Interval: 0 + m_Inside: 0 + m_ShowAsPositiveNumber: 0 + m_OnZero: 0 + m_ShowStartLabel: 1 + m_ShowEndLabel: 1 + m_TextLimit: + m_Enable: 1 + m_MaxWidth: 0 + m_Gap: 1 + m_Suffix: ... + m_SplitLine: + m_Show: 0 + m_LineStyle: + m_Show: 1 + m_Type: 5 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_Interval: 0 + m_Distance: 0 + m_AutoColor: 0 + m_ShowStartLine: 1 + m_ShowEndLine: 1 + m_ShowZLine: 1 + m_SplitArea: + m_Show: 0 + m_Color: [] + m_Animation: + m_Show: 1 + m_Duration: 0 + m_UnscaledTime: 0 + m_MinorTick: + m_Show: 0 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_SplitNumber: 5 + m_AutoColor: 0 + m_MinorSplitLine: + m_Show: 0 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_Distance: 0 + m_AutoColor: 0 + m_IndicatorLabel: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: f2 + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_YAxes: + - m_Show: 1 + m_Type: 0 + m_MinMaxType: 0 + m_GridIndex: 0 + m_PolarIndex: 0 + m_ParallelIndex: 0 + m_Position: 0 + m_Offset: 0 + m_Min: 0 + m_Max: 0 + m_SplitNumber: 0 + m_Interval: 0 + m_BoundaryGap: 0 + m_MaxCache: 0 + m_LogBase: 10 + m_LogBaseE: 0 + m_CeilRate: 0 + m_Inverse: 0 + m_Clockwise: 1 + m_InsertDataToHead: 0 + m_MinCategorySpacing: 0 + m_Icons: [] + m_Data: [] + m_AxisLine: + m_Show: 1 + m_LineStyle: + m_Show: 1 + m_Type: 5 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_OnZero: 1 + m_ShowArrow: 0 + m_Arrow: + m_Width: 10 + m_Height: 15 + m_Offset: 0 + m_Dent: 3 + m_Color: + serializedVersion: 2 + rgba: 0 + m_AxisName: + m_Show: 0 + m_Name: axisName + m_OnZero: 0 + m_LabelStyle: + m_Show: 1 + m_Position: 10 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 22, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_AxisTick: + m_Show: 1 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_AlignWithLabel: 0 + m_Inside: 0 + m_ShowStartTick: 1 + m_ShowEndTick: 1 + m_Distance: 0 + m_SplitNumber: 0 + m_AutoColor: 0 + m_AxisLabel: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 8 + m_Formatter: + m_NumericFormatter: + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_Interval: 0 + m_Inside: 0 + m_ShowAsPositiveNumber: 0 + m_OnZero: 0 + m_ShowStartLabel: 1 + m_ShowEndLabel: 1 + m_TextLimit: + m_Enable: 0 + m_MaxWidth: 0 + m_Gap: 1 + m_Suffix: ... + m_SplitLine: + m_Show: 1 + m_LineStyle: + m_Show: 1 + m_Type: 5 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_Interval: 0 + m_Distance: 0 + m_AutoColor: 0 + m_ShowStartLine: 1 + m_ShowEndLine: 1 + m_ShowZLine: 1 + m_SplitArea: + m_Show: 0 + m_Color: [] + m_Animation: + m_Show: 1 + m_Duration: 0 + m_UnscaledTime: 0 + m_MinorTick: + m_Show: 0 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_SplitNumber: 5 + m_AutoColor: 0 + m_MinorSplitLine: + m_Show: 0 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_Distance: 0 + m_AutoColor: 0 + m_IndicatorLabel: + m_Show: 1 + m_Position: 0 + m_AutoOffset: 0 + m_Offset: {x: 0, y: 0, z: 0} + m_Rotate: 0 + m_AutoRotate: 0 + m_Distance: 0 + m_Formatter: + m_NumericFormatter: f2 + m_Width: 0 + m_Height: 0 + m_Icon: + m_Show: 0 + m_Layer: 0 + m_Align: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Width: 20 + m_Height: 20 + m_Offset: {x: 0, y: 0, z: 0} + m_AutoHideWhenLabelEmpty: 0 + m_Background: + m_Show: 1 + m_Sprite: {fileID: 0} + m_Type: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_Width: 0 + m_Height: 0 + m_TextPadding: + m_Show: 1 + m_Top: 0 + m_Right: 2 + m_Left: 2 + m_Bottom: 0 + m_TextStyle: + m_Show: 1 + m_Font: {fileID: 0} + m_AutoWrap: 0 + m_AutoAlign: 1 + m_Rotate: 0 + m_AutoColor: 0 + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_LineSpacing: 1 + m_Alignment: 4 + m_SingleAxes: [] + m_Parallels: [] + m_ParallelAxes: [] + m_Comments: [] + m_SerieBars: [] + m_SerieCandlesticks: [] + m_SerieEffectScatters: [] + m_SerieHeatmaps: [] + m_SerieLines: + - m_Index: 0 + m_Show: 1 + m_CoordSystem: GridCoord + m_SerieType: Line + m_SerieName: "\u6570\u636E" + m_State: 3 + m_ColorBy: 0 + m_Stack: + m_XAxisIndex: 0 + m_YAxisIndex: 0 + m_RadarIndex: 0 + m_VesselIndex: 0 + m_PolarIndex: 0 + m_SingleAxisIndex: 0 + m_ParallelIndex: 0 + m_GridIndex: -1 + m_MinShow: 0 + m_MaxShow: 0 + m_MaxCache: 0 + m_SampleDist: 0 + m_SampleType: 1 + m_SampleAverage: 0 + m_LineType: 0 + m_SmoothLimit: 0 + m_BarType: 0 + m_BarPercentStack: 0 + m_BarWidth: 0 + m_BarMaxWidth: 0 + m_BarGap: 0.1 + m_BarZebraWidth: 4 + m_BarZebraGap: 2 + m_Min: 0 + m_Max: 0 + m_MinSize: 0 + m_MaxSize: 1 + m_StartAngle: 0 + m_EndAngle: 0 + m_MinAngle: 0 + m_Clockwise: 1 + m_RoundCap: 0 + m_SplitNumber: 0 + m_ClickOffset: 1 + m_RoseType: 0 + m_Gap: 0 + m_Center: + - 0.5 + - 0.46 + m_Radius: + - 0 + - 0.28 + m_MinRadius: 0 + m_MinShowLabel: 0 + m_MinShowLabelValue: 0 + m_ShowDataDimension: 2 + m_ShowDataName: 0 + m_Clip: 0 + m_Ignore: 0 + m_IgnoreValue: 0 + m_IgnoreLineBreak: 0 + m_ShowAsPositiveNumber: 0 + m_Large: 1 + m_LargeThreshold: 200 + m_AvoidLabelOverlap: 0 + m_RadarType: 0 + m_PlaceHolder: 0 + m_DataSortType: 2 + m_Orient: 1 + m_Align: 0 + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_InsertDataToHead: 0 + m_RealtimeSort: 0 + m_LineStyle: + m_Show: 1 + m_Type: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_Width: 0 + m_Length: 0 + m_Opacity: 1 + m_DashLength: 4 + m_DotLength: 2 + m_GapLength: 2 + m_Symbol: + m_Show: 1 + m_Type: 3 + m_Size: 0 + m_Gap: 0 + m_Width: 0 + m_Height: 0 + m_Offset: {x: 0, y: 0} + m_Image: {fileID: 0} + m_ImageType: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_BorderWidth: 0 + m_EmptyColor: + serializedVersion: 2 + rgba: 0 + m_Size2: 0 + m_SizeType: 0 + m_DataIndex: 1 + m_DataScale: 1 + m_StartIndex: 0 + m_Interval: 0 + m_ForceShowLast: 0 + m_Repeat: 0 + m_MinSize: 0 + m_MaxSize: 0 + m_Animation: + m_Enable: 1 + m_Type: 0 + m_Easting: 0 + m_Threshold: 2000 + m_UnscaledTime: 0 + m_FadeIn: + m_Enable: 1 + m_Reverse: 0 + m_Delay: 0 + m_Duration: 1000 + m_Speed: 0 + m_FadeOut: + m_Enable: 1 + m_Reverse: 1 + m_Delay: 0 + m_Duration: 1000 + m_Speed: 0 + m_Change: + m_Enable: 1 + m_Reverse: 0 + m_Delay: 0 + m_Duration: 500 + m_Speed: 0 + m_Addition: + m_Enable: 1 + m_Reverse: 0 + m_Delay: 0 + m_Duration: 500 + m_Speed: 0 + m_Hiding: + m_Enable: 1 + m_Reverse: 0 + m_Delay: 0 + m_Duration: 500 + m_Speed: 0 + m_Interaction: + m_Enable: 1 + m_Reverse: 0 + m_Delay: 0 + m_Duration: 250 + m_Speed: 0 + m_Width: + m_Type: 0 + m_Value: 1.1 + m_Radius: + m_Type: 0 + m_Value: 1.5 + m_Offset: + m_Type: 1 + m_Value: 5 + m_ItemStyle: + m_Show: 1 + m_Color: + serializedVersion: 2 + rgba: 0 + m_Color0: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_ToColor2: + serializedVersion: 2 + rgba: 0 + m_MarkColor: + serializedVersion: 2 + rgba: 0 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_BackgroundWidth: 0 + m_CenterColor: + serializedVersion: 2 + rgba: 0 + m_CenterGap: 0 + m_BorderWidth: 0 + m_BorderGap: 0 + m_BorderColor: + serializedVersion: 2 + rgba: 0 + m_BorderColor0: + serializedVersion: 2 + rgba: 0 + m_BorderToColor: + serializedVersion: 2 + rgba: 0 + m_Opacity: 1 + m_ItemMarker: + m_ItemFormatter: + m_NumericFormatter: + m_CornerRadius: + - 0 + - 0 + - 0 + - 0 + m_Data: + - m_Index: 0 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 0 + - 10 + - m_Index: 1 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 15 + - m_Index: 2 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 8.7 + - m_Index: 3 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 10.2 + - m_Index: 4 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 15.3 + - m_Index: 5 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 6.8 + - m_Index: 6 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 5.1 + - m_Index: 7 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 1.5 + - m_Index: 8 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 8.3 + - m_Index: 9 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 15.3 + - m_Index: 10 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 6.8 + - m_Index: 11 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 5.1 + - m_Index: 12 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 1.5 + - m_Index: 13 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 3.3 + - m_Index: 14 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 15.3 + - m_Index: 15 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 6.8 + - m_Index: 16 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 5.1 + - m_Index: 17 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 1.5 + - m_Index: 18 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 8.3 + - m_Index: 19 + m_Name: + m_Id: + m_ParentId: + m_Ignore: 0 + m_Selected: 0 + m_Radius: 0 + m_State: 4 + m_ItemStyles: [] + m_Labels: [] + m_LabelLines: [] + m_Symbols: [] + m_LineStyles: [] + m_AreaStyles: [] + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_Data: + - 1 + - 15.8 + m_Links: [] + m_Labels: [] + m_LabelLines: [] + m_EndLabels: [] + m_LineArrows: [] + m_AreaStyles: + - m_Show: 1 + m_Origin: 0 + m_Color: + serializedVersion: 2 + rgba: 0 + m_ToColor: + serializedVersion: 2 + rgba: 0 + m_Opacity: 0.6 + m_InnerFill: 0 + m_ToTop: 1 + m_TitleStyles: [] + m_EmphasisStyles: [] + m_BlurStyles: [] + m_SelectStyles: [] + m_SeriePies: [] + m_SerieRadars: [] + m_SerieRings: [] + m_SerieScatters: [] + m_SerieParallels: [] + m_SerieSimplifiedLines: [] + m_SerieSimplifiedBars: [] + m_SerieSimplifiedCandlesticks: [] +--- !u!222 &1746210672 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1746210669} + m_CullTransparentMesh: 1 --- !u!1 &1767234962 GameObject: m_ObjectHideFlags: 0 @@ -6164,6 +14514,82 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1767234962} m_Mesh: {fileID: -2812740606670604321, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &1770427449 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1770427450} + - component: {fileID: 1770427452} + - component: {fileID: 1770427451} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1770427450 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1770427449} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 29422194} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -20, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1770427451 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1770427449} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1770427452 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1770427449} + m_CullTransparentMesh: 1 --- !u!1 &1774313872 GameObject: m_ObjectHideFlags: 0 @@ -6294,6 +14720,120 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 998.3075, y: 17.041107, z: 432.59982} m_Center: {x: 0, y: -8.520569, z: 0} +--- !u!1 &1777657460 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1777657461} + - component: {fileID: 1777657463} + - component: {fileID: 1777657462} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1777657461 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1777657460} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 621286913} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1777657462 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1777657460} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1777657463 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1777657460} + m_CullTransparentMesh: 1 +--- !u!1 &1793713343 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1793713344} + m_Layer: 5 + m_Name: datazoom0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1793713344 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1793713343} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 263478112} + - {fileID: 621286913} + m_Father: {fileID: 1746210670} + m_RootOrder: 17 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1001 &1808966011 PrefabInstance: m_ObjectHideFlags: 0 @@ -6351,6 +14891,44 @@ PrefabInstance: objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 3caeabc6ee00fd44682251a5e9c35557, type: 3} +--- !u!1 &1815209301 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1815209302} + m_Layer: 5 + m_Name: serie_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1815209302 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1815209301} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 654135560} + - {fileID: 715485978} + m_Father: {fileID: 1746210670} + m_RootOrder: 14 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1818029205 GameObject: m_ObjectHideFlags: 0 @@ -6540,6 +15118,84 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1843818177} m_CullTransparentMesh: 1 +--- !u!1 &1903652411 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1903652412} + - component: {fileID: 1903652414} + - component: {fileID: 1903652413} + m_Layer: 5 + m_Name: axis_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1903652412 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1903652411} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1636678458} + - {fileID: 24076856} + m_Father: {fileID: 1352785377} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -209.42143, y: -125} + m_SizeDelta: {x: 101, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1903652413 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1903652411} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1903652414 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1903652411} + m_CullTransparentMesh: 1 --- !u!1 &1914064695 GameObject: m_ObjectHideFlags: 0 @@ -6635,6 +15291,7 @@ RectTransform: m_Children: - {fileID: 1359383648} - {fileID: 1843818178} + - {fileID: 1746210670} m_Father: {fileID: 0} m_RootOrder: 13 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -6643,6 +15300,240 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!1 &1930752645 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1930752646} + - component: {fileID: 1930752648} + - component: {fileID: 1930752647} + m_Layer: 5 + m_Name: title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1930752646 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1930752645} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 227398203} + - {fileID: 1309268913} + m_Father: {fileID: 1059165247} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &1930752647 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1930752645} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1930752648 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1930752645} + m_CullTransparentMesh: 1 +--- !u!1 &1987066719 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1987066720} + - component: {fileID: 1987066722} + - component: {fileID: 1987066721} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1987066720 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1987066719} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 309858256} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1987066721 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1987066719} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1987066722 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1987066719} + m_CullTransparentMesh: 1 +--- !u!1 &2002422875 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2002422876} + - component: {fileID: 2002422878} + - component: {fileID: 2002422877} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2002422876 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2002422875} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 881118541} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &2002422877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2002422875} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: 40 +--- !u!222 &2002422878 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2002422875} + m_CullTransparentMesh: 1 --- !u!1001 &2006555949 PrefabInstance: m_ObjectHideFlags: 0 @@ -6700,6 +15591,226 @@ PrefabInstance: objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: b11e3115ad4164947957f5e35d5527cf, type: 3} +--- !u!1 &2011498644 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2011498645} + - component: {fileID: 2011498647} + - component: {fileID: 2011498646} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2011498645 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2011498644} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2597109} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2011498646 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2011498644} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2011498647 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2011498644} + m_CullTransparentMesh: 1 +--- !u!1 &2016687786 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2016687787} + - component: {fileID: 2016687789} + - component: {fileID: 2016687788} + m_Layer: 5 + m_Name: painter_1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &2016687787 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2016687786} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1746210670} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 580, y: 300} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2016687788 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2016687786} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01c85cd323a9f4dfb803470695bd0944, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!222 &2016687789 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2016687786} + m_CullTransparentMesh: 1 +--- !u!1 &2018747769 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2018747770} + - component: {fileID: 2018747772} + - component: {fileID: 2018747771} + m_Layer: 5 + m_Name: column2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2018747770 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2018747769} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 732774} + - {fileID: 2090344864} + m_Father: {fileID: 1383321929} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &2018747771 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2018747769} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2018747772 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2018747769} + m_CullTransparentMesh: 1 --- !u!1 &2020965148 GameObject: m_ObjectHideFlags: 0 @@ -6812,6 +15923,86 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2020965148} m_Mesh: {fileID: 2453063277419830229, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &2021597839 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2021597840} + - component: {fileID: 2021597842} + - component: {fileID: 2021597841} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2021597840 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021597839} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1435914212} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0, y: 0} + m_SizeDelta: {x: 19, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2021597841 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021597839} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: x5 +--- !u!222 &2021597842 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021597839} + m_CullTransparentMesh: 1 --- !u!1 &2033294235 GameObject: m_ObjectHideFlags: 0 @@ -6924,6 +16115,84 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2033294235} m_Mesh: {fileID: 3066481802794366244, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &2038604951 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2038604952} + - component: {fileID: 2038604954} + - component: {fileID: 2038604953} + m_Layer: 5 + m_Name: title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2038604952 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2038604951} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 622579336} + - {fileID: 484223210} + m_Father: {fileID: 569790076} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 52, y: 27} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &2038604953 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2038604951} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2038604954 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2038604951} + m_CullTransparentMesh: 1 --- !u!1001 &2044415685 PrefabInstance: m_ObjectHideFlags: 0 @@ -7491,6 +16760,82 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2085638279} m_Mesh: {fileID: -7674302450571782693, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} +--- !u!1 &2090344863 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2090344864} + - component: {fileID: 2090344866} + - component: {fileID: 2090344865} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2090344864 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2090344863} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2018747770} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2090344865 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2090344863} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2090344866 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2090344863} + m_CullTransparentMesh: 1 --- !u!1 &2096931755 stripped GameObject: m_CorrespondingSourceObject: {fileID: -8297187871677194394, guid: e908463f3f6b7e54cb2ffaa2e941c53e, type: 3} @@ -7905,6 +17250,84 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2117199854} m_CullTransparentMesh: 1 +--- !u!1 &2141516037 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2141516038} + - component: {fileID: 2141516040} + - component: {fileID: 2141516039} + m_Layer: 5 + m_Name: title_sub + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2141516038 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2141516037} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 857602825} + - {fileID: 841051119} + m_Father: {fileID: 569790076} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0, y: -24} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &2141516039 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2141516037} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61287841bdc4142caba8e77985cd8715, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2141516040 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2141516037} + m_CullTransparentMesh: 1 --- !u!33 &227398239710243840 MeshFilter: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Line/LineManager.cs b/Assets/Scripts/Line/LineManager.cs index e558e07..d452d2e 100644 --- a/Assets/Scripts/Line/LineManager.cs +++ b/Assets/Scripts/Line/LineManager.cs @@ -48,6 +48,7 @@ public class LineManager : MonoBehaviour } } + bool isshow; void Update() { if (Input.GetMouseButtonDown(2)) @@ -56,6 +57,12 @@ public class LineManager : MonoBehaviour point2 = null; PlayerPrefs.SetString("LineData", ""); } + if (allline >= Models.Count && !isshow) + { + isshow = true; + TipTexts.text = "ȫӣ"; + StartCoroutine(WaitHide(Tips)); + } } /// /// @@ -79,6 +86,7 @@ public class LineManager : MonoBehaviour { if (!lineData.Modelname.Contains(point.name)) { + allline++; lineData.Modelname.Add(point.name); string json = JsonConvert.SerializeObject(lineData); PlayerPrefs.SetString("LineData", json); @@ -97,9 +105,8 @@ public class LineManager : MonoBehaviour { if (!JudgmentLine(line, line2)) { - Tips.SetActive(true); - yield return new WaitForSeconds(2); - Tips.SetActive(false); + yield return null; + StartCoroutine(WaitHide(Tips)); } } @@ -195,6 +202,7 @@ public class LineManager : MonoBehaviour { if (!lineData.Modelname.Contains(Models[i].name)) { + allline++; lineData.Modelname.Add(Models[i].name); string json = JsonConvert.SerializeObject(lineData); PlayerPrefs.SetString("LineData", json); @@ -207,11 +215,19 @@ public class LineManager : MonoBehaviour return true; } } + int allline = 0; public void Init() { point2 = null; point1 = null; } + + IEnumerator WaitHide(GameObject game) + { + game.SetActive(true); + yield return new WaitForSeconds(1.5f); + game.SetActive(false); + } } public class LineData diff --git a/Assets/Scripts/Line/LineShowModel.cs b/Assets/Scripts/Line/LineShowModel.cs index e5023fc..926cf2d 100644 --- a/Assets/Scripts/Line/LineShowModel.cs +++ b/Assets/Scripts/Line/LineShowModel.cs @@ -63,6 +63,16 @@ public class LineShowModel : MonoBehaviour // ģʽⲿ public static LineShowModel Instance { get; private set; } + [Header("̬ϸ")] + public bool enableDynamicThickness = true; // ö̬ϸ + public float minThickness = 0.005f; // Сϸ + public float maxThickness = 0.02f; // ϸ + public float closeDistance = 1.0f; // Ϊ""ľֵ + public float farDistance = 10.0f; + + // ãڼ + private Camera mainCamera; + void Awake() { // õ @@ -74,6 +84,8 @@ public class LineShowModel : MonoBehaviour { Destroy(gameObject); } + + mainCamera = Camera.main; } void Update() @@ -94,8 +106,99 @@ public class LineShowModel : MonoBehaviour { ClearSnapHighlight(); } + + // ߵĶ̬ϸ + if (enableDynamicThickness && useCylinderWire) + { + UpdateAllWiresThickness(); + } + } + // ߵĶ̬ϸ + void UpdateAllWiresThickness() + { + if (mainCamera == null) return; + + foreach (GameObject wire in allWires) + { + if (wire == null) continue; + + LineShowModelCylinderWireData wireData = wire.GetComponent(); + if (wireData == null) continue; + + // еľ + Vector3 wireMidPoint = (wireData.startPoint + wireData.endPoint) / 2f; + float distanceToCamera = Vector3.Distance(wireMidPoint, mainCamera.transform.position); + + // ݾϸ + float targetThickness = CalculateDynamicThickness(distanceToCamera); + + // Բϸ + Transform cylinderTransform = wire.transform.Find("WireCylinder"); + if (cylinderTransform != null) + { + Vector3 currentScale = cylinderTransform.localScale; + cylinderTransform.localScale = new Vector3( + targetThickness, + currentScale.y, // ָ߶Ȳ + targetThickness + ); + + // + wireData.wireDiameter = targetThickness; + } + } + } + // ݾ㶯̬ϸ + float CalculateDynamicThickness(float distance) + { + // ʹԲֵϸ + float t = Mathf.InverseLerp(closeDistance, farDistance, distance); + t = Mathf.Clamp01(t); // ȷ0-1Χ + + // ԽԽϸʹ1-tתϵ + float thickness = Mathf.Lerp(minThickness, maxThickness, 1 - t); + + return thickness; + } + // ԾĴϸ㣨ѡ + float CalculateDynamicThicknessRelative(Vector3 start, Vector3 end, Vector3 cameraPos) + { + // е㵽ľ + Vector3 midPoint = (start + end) / 2f; + float distanceToCamera = Vector3.Distance(midPoint, cameraPos); + + // ߳ + float lineLength = Vector3.Distance(start, end); + + // ߳Ⱥۺϼ + float lengthFactor = Mathf.Clamp01(lineLength / farDistance); + float cameraFactor = Mathf.Clamp01(distanceToCamera / farDistance); + + // ۺأԽԽԽϸ + float combinedFactor = (lengthFactor + (1 - cameraFactor)) / 2f; + + return Mathf.Lerp(minThickness, maxThickness, 1 - combinedFactor); } + // ߵĶ̬ϸ + public void RefreshAllWiresThickness() + { + if (!enableDynamicThickness || !useCylinderWire) return; + + UpdateAllWiresThickness(); + } + + // ö̬ϸ + public void SetDynamicThicknessParams(float newMinThickness, float newMaxThickness, + float newCloseDistance, float newFarDistance) + { + minThickness = Mathf.Max(0.001f, newMinThickness); + maxThickness = Mathf.Max(minThickness, newMaxThickness); + closeDistance = Mathf.Max(0.1f, newCloseDistance); + farDistance = Mathf.Max(closeDistance + 0.1f, newFarDistance); + + RefreshAllWiresThickness(); + } void HandleInput() { if (Input.GetMouseButtonDown(0)) // @@ -319,7 +422,7 @@ public class LineShowModel : MonoBehaviour } } } - + // ޸Ԥߵĸ·Ҳ붯̬ϸ void UpdateCylinderPreviewWire(Vector3 start, Vector3 end) { Transform cylinderTransform = previewWireObject.transform.Find("PreviewCylinder"); @@ -340,15 +443,23 @@ public class LineShowModel : MonoBehaviour Vector3 midPoint = (start + end) / 2f; + // ԤߵĶ̬ϸ + float previewThickness = previewWireDiameter; + if (enableDynamicThickness && mainCamera != null) + { + float distanceToCamera = Vector3.Distance(midPoint, mainCamera.transform.position); + previewThickness = CalculateDynamicThickness(distanceToCamera); + } + // Բλúת cylinder.transform.position = midPoint; cylinder.transform.up = direction.normalized; - // Բ + // Բţʹö̬ϸ cylinder.transform.localScale = new Vector3( - previewWireDiameter, + previewThickness, distance / 2f, - previewWireDiameter + previewThickness ); } @@ -396,12 +507,23 @@ public class LineShowModel : MonoBehaviour return wireObject; } + + // ޸ߵĴʼʹö̬ϸ void CreateCylinderWire(GameObject wireObject, Vector3 start, Vector3 end) { GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder); cylinder.name = "WireCylinder"; cylinder.transform.SetParent(wireObject.transform); + // ʼ̬ϸ + Vector3 midPoint = (start + end) / 2f; + float initialThickness = cylinderWireDiameter; + if (enableDynamicThickness && mainCamera != null) + { + float distanceToCamera = Vector3.Distance(midPoint, mainCamera.transform.position); + initialThickness = CalculateDynamicThickness(distanceToCamera); + } + // ò if (cylinderWireMaterial != null) { @@ -417,17 +539,16 @@ public class LineShowModel : MonoBehaviour // ߵķ򡢳Ⱥе Vector3 direction = end - start; float distance = direction.magnitude; - Vector3 midPoint = (start + end) / 2f; // Բλúת cylinder.transform.position = midPoint; cylinder.transform.up = direction.normalized; - // Բ + // Բţʹö̬ϸ cylinder.transform.localScale = new Vector3( - cylinderWireDiameter, + initialThickness, distance / 2f, - cylinderWireDiameter + initialThickness ); // ѡײ diff --git a/Assets/Scripts/LineChart.cs b/Assets/Scripts/LineChart.cs new file mode 100644 index 0000000..f01db9b --- /dev/null +++ b/Assets/Scripts/LineChart.cs @@ -0,0 +1,28 @@ +using System.Collections; +using System.Collections.Generic; +using Unity.VisualScripting; +using UnityEngine; +using XCharts.Runtime; + +public class LineChart : MonoBehaviour +{ + + void Start() + { + LineChart chart = GetComponent(); + DataZoom dataZoom = chart.GetComponent(); + + dataZoom.enable = true; + dataZoom.supportInside = true; // ק͹ + dataZoom.supportSlider = true; // ʾײ + dataZoom.zoomLock = false; // + // óʼʾΧΪ50%100% + dataZoom.start = 50; + dataZoom.end = 100; + } + + void Update() + { + + } +} diff --git a/Assets/Scripts/LineChart.cs.meta b/Assets/Scripts/LineChart.cs.meta new file mode 100644 index 0000000..82f626c --- /dev/null +++ b/Assets/Scripts/LineChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08ae6d23a484eba43bb4f3162ed67cf1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SubjectToggle.cs b/Assets/Scripts/SubjectToggle.cs index 67ace29..c86c0d2 100644 --- a/Assets/Scripts/SubjectToggle.cs +++ b/Assets/Scripts/SubjectToggle.cs @@ -26,7 +26,7 @@ public class SubjectToggle : BaseItem, IPointerEnterHandler, IPointerExitHandler Bootstrap.Instance.uiManager.ShowPanel(this, E_UI_Layer.System, (panel) => { Bootstrap.Instance.eventCenter.EventTrigger(Enum_EventType.UpdateProgress, 0.5f); - Bootstrap.Instance.scenesManager.LoadSceneAsyn(this, "xianchang-TSQ1", () => + Bootstrap.Instance.scenesManager.LoadSceneAsyn(this, "xianchang", () => { Bootstrap.Instance.uiManager.ShowPanel(this, E_UI_Layer.Top, (panel) => { diff --git a/Assets/Scripts/UI/UIPanel/UI_TopTitlePanel.cs b/Assets/Scripts/UI/UIPanel/UI_TopTitlePanel.cs index e0ba88f..a6492bc 100644 --- a/Assets/Scripts/UI/UIPanel/UI_TopTitlePanel.cs +++ b/Assets/Scripts/UI/UIPanel/UI_TopTitlePanel.cs @@ -29,7 +29,7 @@ public class UI_TopTitlePanel : BasePanel { base.ShowMe(); Scene currentScene = SceneManager.GetActiveScene(); - if (currentScene.name == "xianchang-TSQ1") + if (currentScene.name == "xianchang") { AskBtn.gameObject.SetActive(true); ReturnBtn.gameObject.SetActive(true); diff --git a/Assets/XCharts.meta b/Assets/XCharts.meta new file mode 100644 index 0000000..5455863 --- /dev/null +++ b/Assets/XCharts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 27636d97cef7446ffba2e3036a207851 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor.meta b/Assets/XCharts/Editor.meta new file mode 100644 index 0000000..792fad1 --- /dev/null +++ b/Assets/XCharts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 98b750952a34c427693ac70f09008bae +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Attributes.meta b/Assets/XCharts/Editor/Attributes.meta new file mode 100644 index 0000000..b4b5c1a --- /dev/null +++ b/Assets/XCharts/Editor/Attributes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e7c19967ca244147b0fcbb129201b46 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs b/Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs new file mode 100644 index 0000000..3e5d460 --- /dev/null +++ b/Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace XCharts.Editor +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class ComponentEditorAttribute : Attribute + { + public readonly Type componentType; + + public ComponentEditorAttribute(Type componentType) + { + this.componentType = componentType; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs.meta b/Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs.meta new file mode 100644 index 0000000..5db1e6c --- /dev/null +++ b/Assets/XCharts/Editor/Attributes/ComponentEditorAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f433acf13ec404a6d91eb78352d18d4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs b/Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs new file mode 100644 index 0000000..c747be6 --- /dev/null +++ b/Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace XCharts.Editor +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class SerieEditorAttribute : Attribute + { + public readonly Type serieType; + + public SerieEditorAttribute(Type serieType) + { + this.serieType = serieType; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs.meta b/Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs.meta new file mode 100644 index 0000000..a94ad04 --- /dev/null +++ b/Assets/XCharts/Editor/Attributes/SerieEditorAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dcdc7a72224af419d96584fa40f822c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Charts.meta b/Assets/XCharts/Editor/Charts.meta new file mode 100644 index 0000000..412c017 --- /dev/null +++ b/Assets/XCharts/Editor/Charts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9e4407eed14ec4e518a373f4d8ae9b3c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Charts/BaseChartEditor.cs b/Assets/XCharts/Editor/Charts/BaseChartEditor.cs new file mode 100644 index 0000000..665e78f --- /dev/null +++ b/Assets/XCharts/Editor/Charts/BaseChartEditor.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomEditor(typeof(BaseChart), true)] + public class BaseChartEditor : UnityEditor.Editor + { + class Styles + { + public static readonly GUIContent btnAddSerie = new GUIContent("Add Serie", ""); + public static readonly GUIContent btnAddComponent = new GUIContent("Add Main Component", ""); + public static readonly GUIContent btnConvertXYAxis = new GUIContent("Convert XY Axis", ""); + public static readonly GUIContent btnRebuildChartObject = new GUIContent("Rebuild Chart Object", ""); + public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", ""); + public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", ""); + public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", ""); + } + protected BaseChart m_Chart; + protected SerializedProperty m_Script; + protected SerializedProperty m_EnableTextMeshPro; + protected SerializedProperty m_Settings; + protected SerializedProperty m_Theme; + protected SerializedProperty m_ChartName; + protected SerializedProperty m_DebugInfo; + protected SerializedProperty m_RaycastTarget; + + protected List m_Components = new List(); + protected List m_Series = new List(); + + private bool m_BaseFoldout; + + private bool m_CheckWarning = false; + private int m_LastComponentCount = 0; + private int m_LastSerieCount = 0; + private string m_VersionString = ""; + private StringBuilder sb = new StringBuilder(); + MainComponentListEditor m_ComponentList; + SerieListEditor m_SerieList; + + protected virtual void OnEnable() + { + if (target == null) return; + m_Chart = (BaseChart) target; + m_Script = serializedObject.FindProperty("m_Script"); + m_EnableTextMeshPro = serializedObject.FindProperty("m_EnableTextMeshPro"); + m_ChartName = serializedObject.FindProperty("m_ChartName"); + m_Theme = serializedObject.FindProperty("m_Theme"); + m_Settings = serializedObject.FindProperty("m_Settings"); + m_DebugInfo = serializedObject.FindProperty("m_DebugInfo"); + m_RaycastTarget = serializedObject.FindProperty("m_RaycastTarget"); + + RefreshComponent(); + m_ComponentList = new MainComponentListEditor(this); + m_ComponentList.Init(m_Chart, serializedObject, m_Components); + + RefreshSeries(); + m_SerieList = new SerieListEditor(this); + m_SerieList.Init(m_Chart, serializedObject, m_Series); + + m_VersionString = "v" + XChartsMgr.fullVersion; + if (m_EnableTextMeshPro.boolValue) + m_VersionString += "-tmp"; + } + + public List RefreshComponent() + { + m_Components.Clear(); + serializedObject.UpdateIfRequiredOrScript(); + foreach (var kv in m_Chart.typeListForComponent) + { + InitComponent(kv.Value.Name); + } + return m_Components; + } + + public List RefreshSeries() + { + m_Series.Clear(); + serializedObject.UpdateIfRequiredOrScript(); + foreach (var kv in m_Chart.typeListForSerie) + { + InitSerie(kv.Value.Name); + } + return m_Series; + } + + public override void OnInspectorGUI() + { + if (m_Chart == null && target == null) + { + base.OnInspectorGUI(); + return; + } + serializedObject.UpdateIfRequiredOrScript(); + if (m_LastComponentCount != m_Chart.components.Count) + { + m_LastComponentCount = m_Chart.components.Count; + RefreshComponent(); + m_ComponentList.UpdateComponentsProperty(m_Components); + + } + if (m_LastSerieCount != m_Chart.series.Count) + { + m_LastSerieCount = m_Chart.series.Count; + RefreshSeries(); + m_SerieList.UpdateSeriesProperty(m_Series); + } + OnStartInspectorGUI(); + OnDebugInspectorGUI(); + EditorGUILayout.Space(); + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void OnStartInspectorGUI() + { + ShowVersion(); + m_BaseFoldout = ChartEditorHelper.DrawHeader("Base", m_BaseFoldout, false, null, null); + if (m_BaseFoldout) + { + EditorGUILayout.PropertyField(m_Script); + EditorGUILayout.PropertyField(m_ChartName); + EditorGUILayout.PropertyField(m_RaycastTarget); + if (XChartsMgr.IsRepeatChartName(m_Chart, m_ChartName.stringValue)) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.HelpBox("chart name is repeated: " + m_ChartName.stringValue, MessageType.Error); + EditorGUILayout.EndHorizontal(); + } + } + EditorGUILayout.PropertyField(m_Theme); + EditorGUILayout.PropertyField(m_Settings); + m_ComponentList.OnGUI(); + m_SerieList.OnGUI(); + } + + protected virtual void OnDebugInspectorGUI() + { + EditorGUILayout.PropertyField(m_DebugInfo, true); + EditorGUILayout.Space(); + AddSerie(); + AddComponent(); + CheckWarning(); + } + + protected void PropertyComponnetList(SerializedProperty prop) + { + for (int i = 0; i < prop.arraySize; i++) + { + EditorGUILayout.PropertyField(prop.GetArrayElementAtIndex(i), true); + } + } + + private void InitComponent(string propName) + { + var prop = serializedObject.FindProperty(propName); + for (int i = 0; i < prop.arraySize; i++) + { + m_Components.Add(prop.GetArrayElementAtIndex(i)); + } + m_Components.Sort((a, b) => { return a.propertyPath.CompareTo(b.propertyPath); }); + } + + private void InitSerie(string propName) + { + var prop = serializedObject.FindProperty(propName); + for (int i = 0; i < prop.arraySize; i++) + { + m_Series.Add(prop.GetArrayElementAtIndex(i)); + } + m_Series.Sort(delegate(SerializedProperty a, SerializedProperty b) + { + var index1 = a.FindPropertyRelative("m_Index").intValue; + var index2 = b.FindPropertyRelative("m_Index").intValue; + return index1.CompareTo(index2); + }); + } + + private void ShowVersion() + { + EditorGUILayout.HelpBox(m_VersionString, MessageType.None); + } + + private void AddComponent() + { + if (GUILayout.Button(Styles.btnAddComponent)) + { + var menu = new GenericMenu(); + foreach (var type in GetMainComponentTypeNames()) + { + var title = ChartEditorHelper.GetContent(type.Name); + bool exists = !m_Chart.CanAddChartComponent(type); + if (!exists) + menu.AddItem(title, false, () => + { + m_ComponentList.AddChartComponent(type); + }); + else + { + menu.AddDisabledItem(title); + } + } + + menu.ShowAsContext(); + } + } + private void AddSerie() + { + if (GUILayout.Button(Styles.btnAddSerie)) + { + var menu = new GenericMenu(); + foreach (var type in GetSerieTypeNames()) + { + var title = ChartEditorHelper.GetContent(type.Name); + if (m_Chart.CanAddSerie(type)) + { + menu.AddItem(title, false, () => + { + m_SerieList.AddSerie(type); + }); + } + else + { + menu.AddDisabledItem(title); + } + } + menu.ShowAsContext(); + } + } + + private List GetMainComponentTypeNames() + { + var list = new List(); + var typeMap = RuntimeUtil.GetAllTypesDerivedFrom(); + foreach (var kvp in typeMap) + { + var type = kvp; + if (RuntimeUtil.HasSubclass(type)) continue; + + if (type.IsDefined(typeof(ComponentHandlerAttribute), false)) + { + var attribute = type.GetAttribute(); + if (attribute != null && attribute.handler != null) + list.Add(type); + } + else + { + list.Add(type); + } + } + list.Sort((a, b) => { return a.Name.CompareTo(b.Name); }); + return list; + } + private List GetSerieTypeNames() + { + var list = new List(); + var typeMap = RuntimeUtil.GetAllTypesDerivedFrom(); + foreach (var kvp in typeMap) + { + var type = kvp; + if (type.IsDefined(typeof(SerieHandlerAttribute), false)) + list.Add(type); + } + list.Sort((a, b) => { return a.Name.CompareTo(b.Name); }); + return list; + } + + private void CheckWarning() + { + if (m_Chart.HasChartComponent() && m_Chart.HasChartComponent()) + { + if (GUILayout.Button(Styles.btnConvertXYAxis)) + m_Chart.ConvertXYAxis(0); + } + if (GUILayout.Button(Styles.btnRebuildChartObject)) + { + m_Chart.RebuildChartObject(); + } + if (GUILayout.Button(Styles.btnSaveAsImage)) + { + m_Chart.SaveAsImage(); + } + if (m_CheckWarning) + { + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button(Styles.btnCheckWarning)) + { + m_CheckWarning = true; + m_Chart.CheckWarning(); + } + if (GUILayout.Button(Styles.btnHideWarning)) + { + m_CheckWarning = false; + } + EditorGUILayout.EndHorizontal(); + sb.Length = 0; + sb.AppendFormat("v{0}", XChartsMgr.fullVersion); + if (!string.IsNullOrEmpty(m_Chart.warningInfo)) + { + sb.AppendLine(); + sb.Append(m_Chart.warningInfo); + } + else + { + sb.AppendLine(); + sb.Append("Perfect! No warning!"); + } + EditorGUILayout.HelpBox(sb.ToString(), MessageType.Warning); + } + else + { + if (GUILayout.Button(Styles.btnCheckWarning)) + { + m_CheckWarning = true; + m_Chart.CheckWarning(); + } + + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Charts/BaseChartEditor.cs.meta b/Assets/XCharts/Editor/Charts/BaseChartEditor.cs.meta new file mode 100644 index 0000000..2916db0 --- /dev/null +++ b/Assets/XCharts/Editor/Charts/BaseChartEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7f1cff1e5bae244a872040086b1cfa8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents.meta b/Assets/XCharts/Editor/ChildComponents.meta new file mode 100644 index 0000000..b2aa5c5 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7861b681552cf4bc9b2c2f16d25c628c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs b/Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs new file mode 100644 index 0000000..ac09c04 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs @@ -0,0 +1,95 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(XCharts.Runtime.AnimationInfo), true)] + public class AnimationInfoDrawer : BasePropertyDrawer + { + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Enable", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Delay"); + PropertyField(prop, "m_Duration"); + PropertyField(prop, "m_Speed"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(XCharts.Runtime.AnimationChange), true)] + public class AnimationChangeDrawer : BasePropertyDrawer + { + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Enable", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Duration"); + PropertyField(prop, "m_Speed"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(XCharts.Runtime.AnimationAddition), true)] + public class AnimationAdditionDrawer : BasePropertyDrawer + { + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Enable", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Duration"); + PropertyField(prop, "m_Speed"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(XCharts.Runtime.AnimationInteraction), true)] + public class AnimationInteractionDrawer : BasePropertyDrawer + { + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Enable", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Duration"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Radius"); + PropertyField(prop, "m_Offset"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(AnimationStyle), true)] + public class AnimationDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Animation"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Enable", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Type"); + PropertyField(prop, "m_UnscaledTime"); + PropertyField(prop, "m_FadeIn"); + PropertyField(prop, "m_FadeOut"); + PropertyField(prop, "m_Change"); + PropertyField(prop, "m_Addition"); + PropertyField(prop, "m_Interaction"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs.meta new file mode 100644 index 0000000..5a9ff4d --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/AnimationDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 844042f92a581474ba0491427f3fd592 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs new file mode 100644 index 0000000..e5e8c80 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs @@ -0,0 +1,27 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(AreaStyle), true)] + public class AreaStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "AreaStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Origin"); + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_ToColor"); + PropertyField(prop, "m_Opacity"); + PropertyField(prop, "m_ToTop"); + PropertyField(prop, "m_InnerFill"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs.meta new file mode 100644 index 0000000..78e98cf --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/AreaStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c51fd822c8be44490832d81652d1aef5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs b/Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs new file mode 100644 index 0000000..726249a --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs @@ -0,0 +1,28 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(Background), true)] + public class BackgroundDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Background"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Image"); + PropertyField(prop, "m_ImageType"); + PropertyField(prop, "m_ImageColor"); + PropertyField(prop, "m_ImageWidth"); + PropertyField(prop, "m_ImageHeight"); + PropertyField(prop, "m_AutoColor"); + PropertyField(prop, "m_BorderStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs.meta new file mode 100644 index 0000000..110fd62 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/BackgroundDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88c83fad35bc544cab4106096d171189 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs b/Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs new file mode 100644 index 0000000..e557408 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace XCharts.Editor +{ + public delegate void DelegateMenuAction(Vector2 postion); + public class BasePropertyDrawer : PropertyDrawer + { + protected int m_Index; + protected int m_DataSize; + protected float m_DefaultWidth; + protected string m_DisplayName; + protected string m_KeyName; + protected Rect m_DrawRect; + protected Dictionary m_Heights = new Dictionary(); + protected Dictionary m_PropToggles = new Dictionary(); + protected Dictionary m_DataToggles = new Dictionary(); + + public virtual string ClassName { get { return ""; } } + public virtual List IngorePropertys { get { return new List { }; } } + + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + m_DrawRect = pos; + m_DrawRect.height = EditorGUIUtility.singleLineHeight; + m_DefaultWidth = pos.width; + var list = prop.displayName.Split(' '); + if (list.Length > 0) + { + if (!int.TryParse(list[list.Length - 1], out m_Index)) + { + m_Index = 0; + m_DisplayName = prop.displayName; + m_KeyName = prop.propertyPath + "_" + m_Index; + } + else + { + m_DisplayName = ClassName + " " + m_Index; + m_KeyName = prop.propertyPath + "_" + m_Index; + } + } + else + { + m_DisplayName = prop.displayName; + } + if (!m_PropToggles.ContainsKey(m_KeyName)) + { + m_PropToggles.Add(m_KeyName, false); + } + if (!m_DataToggles.ContainsKey(m_KeyName)) + { + m_DataToggles.Add(m_KeyName, false); + } + if (!m_Heights.ContainsKey(m_KeyName)) + { + m_Heights.Add(m_KeyName, 0); + } + else + { + m_Heights[m_KeyName] = 0; + } + } + + private string GetKeyName(SerializedProperty prop) + { + var index = 0; + var list = prop.displayName.Split(' '); + if (list.Length > 0) + { + int.TryParse(list[list.Length - 1], out index); + } + return prop.propertyPath + "_" + index; + } + + protected void AddHelpBox(string message, MessageType type = MessageType.Warning, int line = 2) + { + var offset = EditorGUI.indentLevel * ChartEditorHelper.INDENT_WIDTH; + EditorGUI.HelpBox(new Rect(m_DrawRect.x + offset, m_DrawRect.y, m_DrawRect.width - offset, EditorGUIUtility.singleLineHeight * line), message, type); + for (int i = 0; i < line; i++) + AddSingleLineHeight(); + } + + protected void AddSingleLineHeight() + { + m_Heights[m_KeyName] += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + m_DrawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + protected void AddHeight(float height) + { + m_Heights[m_KeyName] += height; + m_DrawRect.y += height; + } + + protected void PropertyListField(SerializedProperty prop, string relativePropName, bool showOrder = true) + { + if (IngorePropertys.Contains(relativePropName)) return; + var height = m_Heights[m_KeyName]; + var toggleKeyName = m_KeyName + relativePropName; + m_DataToggles[toggleKeyName] = ChartEditorHelper.MakeListWithFoldout(ref m_DrawRect, ref height, + prop.FindPropertyRelative(relativePropName), + m_DataToggles.ContainsKey(toggleKeyName) && m_DataToggles[toggleKeyName], showOrder, true); + m_Heights[m_KeyName] = height; + } + + protected void PropertyField(SerializedProperty prop, string relativePropName) + { + if (IngorePropertys.Contains(relativePropName)) return; + if (!ChartEditorHelper.PropertyField(ref m_DrawRect, m_Heights, m_KeyName, prop, relativePropName)) + { + Debug.LogError("PropertyField ERROR:" + prop.displayName + ", " + relativePropName); + } + } + + protected void PropertyFieldLimitMin(SerializedProperty prop, string relativePropName, float minValue) + { + if (IngorePropertys.Contains(relativePropName)) return; + if (!ChartEditorHelper.PropertyFieldWithMinValue(ref m_DrawRect, m_Heights, m_KeyName, prop, + relativePropName, minValue)) + { + Debug.LogError("PropertyField ERROR:" + prop.displayName + ", " + relativePropName); + } + } + protected void PropertyFieldLimitMax(SerializedProperty prop, string relativePropName, float maxValue) + { + if (IngorePropertys.Contains(relativePropName)) return; + if (!ChartEditorHelper.PropertyFieldWithMaxValue(ref m_DrawRect, m_Heights, m_KeyName, prop, + relativePropName, maxValue)) + { + Debug.LogError("PropertyField ERROR:" + prop.displayName + ", " + relativePropName); + } + } + + protected void PropertyField(SerializedProperty prop, SerializedProperty relativeProp) + { + if (!ChartEditorHelper.PropertyField(ref m_DrawRect, m_Heights, m_KeyName, relativeProp)) + { + Debug.LogError("PropertyField ERROR:" + prop.displayName + ", " + relativeProp); + } + } + + protected void PropertyTwoFiled(SerializedProperty prop, string relativeListProp, string labelName = null) + { + PropertyTwoFiled(prop, prop.FindPropertyRelative(relativeListProp), labelName); + } + protected void PropertyTwoFiled(SerializedProperty prop, SerializedProperty relativeListProp, + string labelName = null) + { + if (string.IsNullOrEmpty(labelName)) + { + labelName = relativeListProp.displayName; + } + ChartEditorHelper.MakeTwoField(ref m_DrawRect, m_DefaultWidth, relativeListProp, labelName); + m_Heights[m_KeyName] += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + protected bool MakeFoldout(SerializedProperty prop, string relativePropName) + { + if (string.IsNullOrEmpty(relativePropName)) + { + return ChartEditorHelper.MakeFoldout(ref m_DrawRect, m_Heights, m_PropToggles, m_KeyName, + m_DisplayName, null); + } + else + { + var relativeProp = prop.FindPropertyRelative(relativePropName); + return ChartEditorHelper.MakeFoldout(ref m_DrawRect, m_Heights, m_PropToggles, m_KeyName, + m_DisplayName, relativeProp); + } + } + protected bool MakeComponentFoldout(SerializedProperty prop, string relativePropName, bool relativePropEnable, + params HeaderMenuInfo[] menus) + { + if (string.IsNullOrEmpty(relativePropName)) + { + return ChartEditorHelper.MakeComponentFoldout(ref m_DrawRect, m_Heights, m_PropToggles, m_KeyName, + m_DisplayName, null, null, relativePropEnable, menus); + } + else + { + var relativeProp = prop.FindPropertyRelative(relativePropName); + return ChartEditorHelper.MakeComponentFoldout(ref m_DrawRect, m_Heights, m_PropToggles, m_KeyName, + m_DisplayName, relativeProp, null, relativePropEnable, menus); + } + } + + protected bool MakeComponentFoldout(SerializedProperty prop, string relativePropName, string relativePropName2, + bool relativePropEnable, params HeaderMenuInfo[] menus) + { + if (string.IsNullOrEmpty(relativePropName)) + { + return ChartEditorHelper.MakeComponentFoldout(ref m_DrawRect, m_Heights, m_PropToggles, m_KeyName, + m_DisplayName, null, null, relativePropEnable, menus); + } + else + { + var relativeProp = prop.FindPropertyRelative(relativePropName); + var relativeProp2 = prop.FindPropertyRelative(relativePropName2); + return ChartEditorHelper.MakeComponentFoldout(ref m_DrawRect, m_Heights, m_PropToggles, m_KeyName, + m_DisplayName, relativeProp, relativeProp2, relativePropEnable, menus); + } + } + + protected virtual void DrawExtendeds(SerializedProperty prop) { } + + public override float GetPropertyHeight(SerializedProperty prop, GUIContent label) + { + var key = GetKeyName(prop); + if (m_Heights.ContainsKey(key)) return m_Heights[key] + GetExtendedHeight(); + else return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + protected virtual float GetExtendedHeight() + { + return 0; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs.meta new file mode 100644 index 0000000..51f7228 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/BasePropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e5a04ce1f0a841b9b966a6d74de00e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs new file mode 100644 index 0000000..cca05d3 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(BorderStyle), true)] + public class BorderStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Border"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_BorderWidth"); + PropertyField(prop, "m_BorderColor"); + PropertyField(prop, "m_RoundedCorner"); + PropertyListField(prop, "m_CornerRadius", true); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs.meta new file mode 100644 index 0000000..e2f678a --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/BorderStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47a460215ec5e4ec0bc7f8122a44302a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs b/Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs new file mode 100644 index 0000000..a674af3 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs @@ -0,0 +1,26 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(CommentItem), true)] + public class CommentItemDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "CommentItem"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", "m_Content", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Content"); + PropertyField(prop, "m_Location"); + //PropertyField(prop, "m_MarkRect"); + //PropertyField(prop, "m_MarkStyle"); + PropertyField(prop, "m_LabelStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs.meta new file mode 100644 index 0000000..dc4966b --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/CommentItemDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d485d6a729a1449cdb5032f380fba70f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs new file mode 100644 index 0000000..126d7cd --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs @@ -0,0 +1,22 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(CommentMarkStyle), true)] + public class CommentMarkStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "MarkStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_LineStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs.meta new file mode 100644 index 0000000..d54117b --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/CommentMarkStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d74ed458b24774b129611ed816b6b6cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs b/Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs new file mode 100644 index 0000000..128f0f9 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs @@ -0,0 +1,160 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(ComponentTheme), true)] + public class ComponentThemeDrawer : BasePropertyDrawer + { + public override string ClassName { get { return ""; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; +#if dUI_TextMeshPro + PropertyField(prop, "m_TMPFont"); +#else + PropertyField(prop, "m_Font"); +#endif + PropertyField(prop, "m_FontSize"); + PropertyField(prop, "m_TextColor"); + DrawExtendeds(prop); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(BaseAxisTheme), true)] + public class BaseAxisThemeDrawer : ComponentThemeDrawer + { + public override string ClassName { get { return "Axis"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_LineType"); + PropertyField(prop, "m_LineWidth"); + PropertyField(prop, "m_LineLength"); + PropertyField(prop, "m_LineColor"); + PropertyField(prop, "m_SplitLineType"); + PropertyField(prop, "m_SplitLineWidth"); + PropertyField(prop, "m_SplitLineLength"); + PropertyField(prop, "m_SplitLineColor"); + PropertyField(prop, "m_TickWidth"); + PropertyField(prop, "m_TickLength"); + PropertyField(prop, "m_TickColor"); + PropertyField(prop, "m_SplitAreaColors"); + } + } + + [CustomPropertyDrawer(typeof(AxisTheme), true)] + public class AxisThemeDrawer : BaseAxisThemeDrawer + { + public override string ClassName { get { return "Axis"; } } + } + + [CustomPropertyDrawer(typeof(RadiusAxisTheme), true)] + public class RadiusAxisThemeDrawer : BaseAxisThemeDrawer + { + public override string ClassName { get { return "Radius Axis"; } } + public override List IngorePropertys + { + get + { + return new List + { + "m_TextBackgroundColor", + "m_LineLength", + "m_SplitLineLength", + }; + } + } + } + + [CustomPropertyDrawer(typeof(DataZoomTheme), true)] + public class DataZoomThemeDrawer : ComponentThemeDrawer + { + public override string ClassName { get { return "DataZoom"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_BackgroundColor"); + PropertyField(prop, "m_BorderWidth"); + PropertyField(prop, "m_BorderColor"); + PropertyField(prop, "m_DataLineWidth"); + PropertyField(prop, "m_DataLineColor"); + PropertyField(prop, "m_FillerColor"); + PropertyField(prop, "m_DataAreaColor"); + + } + } + + [CustomPropertyDrawer(typeof(LegendTheme), true)] + public class LegendThemeDrawer : ComponentThemeDrawer + { + public override string ClassName { get { return "Legend"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_UnableColor"); + } + } + + [CustomPropertyDrawer(typeof(TooltipTheme), true)] + public class TooltipThemeDrawer : ComponentThemeDrawer + { + public override string ClassName { get { return "Tooltip"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_LineType"); + PropertyField(prop, "m_LineWidth"); + PropertyField(prop, "m_LineColor"); + PropertyField(prop, "m_AreaColor"); + PropertyField(prop, "m_LabelTextColor"); + PropertyField(prop, "m_LabelBackgroundColor"); + } + } + + [CustomPropertyDrawer(typeof(VisualMapTheme), true)] + public class VisualMapThemeDrawer : ComponentThemeDrawer + { + public override string ClassName { get { return "VisualMap"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + // PropertyField(prop, "m_BorderWidth"); + // PropertyField(prop, "m_BorderColor"); + // PropertyField(prop, "m_BackgroundColor"); + } + } + + [CustomPropertyDrawer(typeof(SerieTheme), true)] + public class SerieThemeDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Serie"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_LineWidth"); + PropertyField(prop, "m_LineSymbolSize"); + PropertyField(prop, "m_ScatterSymbolSize"); + PropertyField(prop, "m_CandlestickColor"); + PropertyField(prop, "m_CandlestickColor0"); + PropertyField(prop, "m_CandlestickBorderColor"); + PropertyField(prop, "m_CandlestickBorderColor0"); + PropertyField(prop, "m_CandlestickBorderWidth"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs.meta new file mode 100644 index 0000000..5ddef75 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ComponentThemeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7937a2a7addd42299e960c5cfb75e34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs b/Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs new file mode 100644 index 0000000..af03faf --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(DebugInfo), true)] + public class DebugInfoDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Debug"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", false)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_FoldSeries"); + PropertyField(prop, "m_ShowDebugInfo"); + PropertyField(prop, "m_ShowAllChartObject"); + PropertyField(prop, "m_LabelStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs.meta new file mode 100644 index 0000000..34acfda --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/DebugInfoDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99bd61acea264400fb4747b17a2731e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs new file mode 100644 index 0000000..2f2e1a6 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(IconStyle), true)] + public class IconStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "IconStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Layer"); + PropertyField(prop, "m_Align"); + PropertyField(prop, "m_Sprite"); + PropertyField(prop, "m_Type"); + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + PropertyField(prop, "m_Offset"); + PropertyField(prop, "m_AutoHideWhenLabelEmpty"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs.meta new file mode 100644 index 0000000..fb19ace --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/IconStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9cae26ad61d224d8a97d41bdc52ec0b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs new file mode 100644 index 0000000..8ad71d4 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs @@ -0,0 +1,27 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(ImageStyle), true)] + public class ImageStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "ImageStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Sprite"); + PropertyField(prop, "m_Type"); + PropertyField(prop, "m_AutoColor"); + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs.meta new file mode 100644 index 0000000..6907fec --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ImageStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4649856b17dfd4f628eb975040fb791c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs new file mode 100644 index 0000000..1f77207 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs @@ -0,0 +1,40 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(ItemStyle), true)] + public class ItemStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "ItemStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", false)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_Color0"); + PropertyField(prop, "m_ToColor"); + PropertyField(prop, "m_ToColor2"); + PropertyField(prop, "m_MarkColor"); + PropertyField(prop, "m_BackgroundColor"); + PropertyField(prop, "m_BackgroundWidth"); + PropertyField(prop, "m_CenterColor"); + PropertyField(prop, "m_CenterGap"); + PropertyField(prop, "m_BorderWidth"); + PropertyField(prop, "m_BorderGap"); + PropertyField(prop, "m_BorderColor"); + PropertyField(prop, "m_BorderColor0"); + PropertyField(prop, "m_BorderToColor"); + PropertyField(prop, "m_Opacity"); + PropertyField(prop, "m_ItemMarker"); + PropertyField(prop, "m_ItemFormatter"); + PropertyField(prop, "m_NumericFormatter"); + PropertyListField(prop, "m_CornerRadius", true); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs.meta new file mode 100644 index 0000000..78d018c --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ItemStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f40830a3b05574467ad0d8873c6c8790 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs new file mode 100644 index 0000000..e26fd56 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs @@ -0,0 +1,31 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(LabelLine), true)] + public class LabelLineDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "LabelLine"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_LineType"); + PropertyField(prop, "m_LineColor"); + PropertyField(prop, "m_LineAngle"); + PropertyField(prop, "m_LineWidth"); + PropertyField(prop, "m_LineGap"); + PropertyField(prop, "m_LineLength1"); + PropertyField(prop, "m_LineLength2"); + PropertyField(prop, "m_LineEndX"); + PropertyField(prop, "m_StartSymbol"); + PropertyField(prop, "m_EndSymbol"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs.meta new file mode 100644 index 0000000..72f2076 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LabelLineDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 29a267a45c6e64454a982032947046c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs new file mode 100644 index 0000000..359da59 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs @@ -0,0 +1,41 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(LabelStyle), true)] + public class LabelStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Label"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Position"); + PropertyField(prop, "m_Formatter"); + PropertyField(prop, "m_NumericFormatter"); + PropertyField(prop, "m_AutoOffset"); + PropertyField(prop, "m_Offset"); + PropertyField(prop, "m_Distance"); + PropertyField(prop, "m_AutoRotate"); + PropertyField(prop, "m_Rotate"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + PropertyField(prop, "m_Icon"); + PropertyField(prop, "m_Background"); + PropertyField(prop, "m_TextStyle"); + PropertyField(prop, "m_TextPadding"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(EndLabelStyle), true)] + public class EndLabelStyleDrawer : LabelStyleDrawer + { + public override string ClassName { get { return "End Label"; } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs.meta new file mode 100644 index 0000000..390d611 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LabelStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: abd47f4015a9840b9acae8efb21db7c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs new file mode 100644 index 0000000..07833d9 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs @@ -0,0 +1,42 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(LevelStyle), true)] + public class LevelStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "LevelStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyListField(prop, "m_Levels"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(Level), true)] + public class LevelDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Level"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Depth", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Depth"); + PropertyField(prop, "m_Label"); + PropertyField(prop, "m_UpperLabel"); + PropertyField(prop, "m_LineStyle"); + PropertyField(prop, "m_ItemStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs.meta new file mode 100644 index 0000000..37b74c7 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LevelStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a1ff119a53a44e5abe2ef6f57816aa6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs new file mode 100644 index 0000000..031498f --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs @@ -0,0 +1,43 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(ArrowStyle), true)] + public class ArrowDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Arrow"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + PropertyField(prop, "m_Offset"); + PropertyField(prop, "m_Dent"); + PropertyField(prop, "m_Color"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(LineArrow), true)] + public class LineArrowStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "LineArrow"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Position"); + PropertyField(prop, "m_Arrow"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs.meta new file mode 100644 index 0000000..3c9213f --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LineArrowDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 817d27d232da94f6c9dab9e3d0c22631 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LineDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LineDrawer.cs new file mode 100644 index 0000000..dceb65c --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LineDrawer.cs @@ -0,0 +1,93 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(BaseLine), true)] + public class BaseLineDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Line"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + DrawExtendeds(prop); + PropertyField(prop, "m_LineStyle"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(AxisLine), true)] + public class AxisLineDrawer : BaseLineDrawer + { + public override string ClassName { get { return "AxisLine"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_OnZero"); + PropertyField(prop, "m_ShowArrow"); + PropertyField(prop, "m_Arrow"); + } + } + + [CustomPropertyDrawer(typeof(AxisSplitLine), true)] + public class AxisSplitLineDrawer : BaseLineDrawer + { + public override string ClassName { get { return "SplitLine"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_Interval"); + PropertyField(prop, "m_Distance"); + PropertyField(prop, "m_AutoColor"); + PropertyField(prop, "m_ShowStartLine"); + PropertyField(prop, "m_ShowEndLine"); + PropertyField(prop, "m_ShowZLine"); + } + } + + [CustomPropertyDrawer(typeof(AxisMinorSplitLine), true)] + public class AxisMinorSplitLineDrawer : BaseLineDrawer + { + public override string ClassName { get { return "MinorSplitLine"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + //PropertyField(prop, "m_Distance"); + //PropertyField(prop, "m_AutoColor"); + } + } + + [CustomPropertyDrawer(typeof(AxisTick), true)] + public class AxisTickDrawer : BaseLineDrawer + { + public override string ClassName { get { return "AxisTick"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_AlignWithLabel"); + PropertyField(prop, "m_Inside"); + PropertyField(prop, "m_ShowStartTick"); + PropertyField(prop, "m_ShowEndTick"); + PropertyField(prop, "m_SplitNumber"); + PropertyField(prop, "m_Distance"); + PropertyField(prop, "m_AutoColor"); + } + } + + [CustomPropertyDrawer(typeof(AxisMinorTick), true)] + public class AxisMinorTickDrawer : BaseLineDrawer + { + public override string ClassName { get { return "MinorTick"; } } + protected override void DrawExtendeds(SerializedProperty prop) + { + base.DrawExtendeds(prop); + PropertyField(prop, "m_SplitNumber"); + //PropertyField(prop, "m_AutoColor"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LineDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LineDrawer.cs.meta new file mode 100644 index 0000000..5830eeb --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LineDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e69f60c7d200439abcf3407c15f8c4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs new file mode 100644 index 0000000..7607c38 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs @@ -0,0 +1,31 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(LineStyle), true)] + public class LineStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "LineStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Type"); + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_ToColor"); + PropertyField(prop, "m_ToColor2"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Length"); + PropertyField(prop, "m_Opacity"); + PropertyField(prop, "m_DashLength"); + PropertyField(prop, "m_DotLength"); + PropertyField(prop, "m_GapLength"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs.meta new file mode 100644 index 0000000..e4bda69 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LineStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a36d5076e1414d619b53d1ef998806f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs b/Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs new file mode 100644 index 0000000..2cfe596 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(Location), true)] + public class LocationDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Location"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Align", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Top"); + PropertyField(prop, "m_Bottom"); + PropertyField(prop, "m_Left"); + PropertyField(prop, "m_Right"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs.meta new file mode 100644 index 0000000..4b0c2f5 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/LocationDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34092595791508d4b94b074a8788c388 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs b/Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs new file mode 100644 index 0000000..fe51564 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs @@ -0,0 +1,27 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + + [CustomPropertyDrawer(typeof(MLValue), true)] + public class MLValueDrawer : BasePropertyDrawer + { + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + Rect drawRect = pos; + drawRect.height = EditorGUIUtility.singleLineHeight; + SerializedProperty m_Percent = prop.FindPropertyRelative("m_Type"); + SerializedProperty m_Color = prop.FindPropertyRelative("m_Value"); + + ChartEditorHelper.MakeTwoField(ref drawRect, drawRect.width, m_Percent, m_Color, prop.displayName); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + public override float GetPropertyHeight(SerializedProperty prop, GUIContent label) + { + return 1 * EditorGUIUtility.singleLineHeight + 1 * EditorGUIUtility.standardVerticalSpacing; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs.meta new file mode 100644 index 0000000..e551a92 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/MLValueDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 364b6129b88e14605b1a1454b7bf876b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs new file mode 100644 index 0000000..28bf280 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(MarqueeStyle), true)] + public class MarqueeStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "MarqueeStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Apply"); + PropertyField(prop, "m_RealRect"); + PropertyField(prop, "m_LineStyle"); + PropertyField(prop, "m_AreaStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta new file mode 100644 index 0000000..e065930 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1a225478c2e14da3854aea28fb59882 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs b/Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs new file mode 100644 index 0000000..47fa5db --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(SerieSymbol), true)] + public class SerieSymbolDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Symbol"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + var type = (SymbolType)prop.FindPropertyRelative("m_Type").enumValueIndex; + PropertyField(prop, "m_Type"); + if (type == SymbolType.Custom) + { + AddHelpBox("Custom symbol only work in PictorialBar serie", MessageType.Warning); + PropertyField(prop, "m_Image"); + PropertyField(prop, "m_ImageType"); + PropertyField(prop, "m_Width"); + // PropertyField(prop, "m_Height"); + // PropertyField(prop, "m_Offset"); + } + PropertyField(prop, "m_Gap"); + PropertyField(prop, "m_SizeType"); + switch ((SymbolSizeType)prop.FindPropertyRelative("m_SizeType").enumValueIndex) + { + case SymbolSizeType.Custom: + PropertyField(prop, "m_Size"); + break; + case SymbolSizeType.FromData: + PropertyField(prop, "m_DataIndex"); + PropertyField(prop, "m_DataScale"); + PropertyField(prop, "m_MinSize"); + PropertyField(prop, "m_MaxSize"); + break; + case SymbolSizeType.Function: + break; + } + PropertyField(prop, "m_StartIndex"); + PropertyField(prop, "m_Interval"); + PropertyField(prop, "m_ForceShowLast"); + PropertyField(prop, "m_Repeat"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs.meta new file mode 100644 index 0000000..f780f98 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/SerieSymbolDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a164822bc0fd4e5291f00c5a4ee86f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs b/Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs new file mode 100644 index 0000000..4a7cab9 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs @@ -0,0 +1,38 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(Settings), true)] + public class SettingsDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Settings"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", false, new HeaderMenuInfo("Reset", () => + { + var chart = prop.serializedObject.targetObject as BaseChart; + chart.settings.Reset(); + }))) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_ReversePainter"); + PropertyField(prop, "m_MaxPainter"); + PropertyField(prop, "m_BasePainterMaterial"); + PropertyField(prop, "m_SeriePainterMaterial"); + PropertyField(prop, "m_UpperPainterMaterial"); + PropertyField(prop, "m_TopPainterMaterial"); + PropertyField(prop, "m_LineSmoothStyle"); + PropertyField(prop, "m_LineSmoothness"); + PropertyField(prop, "m_LineSegmentDistance"); + PropertyField(prop, "m_CicleSmoothness"); + PropertyField(prop, "m_AxisMaxSplitNumber"); + PropertyField(prop, "m_LegendIconLineWidth"); + PropertyListField(prop, "m_LegendIconCornerRadius", true); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs.meta new file mode 100644 index 0000000..11a0c6c --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/SettingsDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70536a1ba3af245e7ad3b11e97682d8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs new file mode 100644 index 0000000..31eee8f --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(StateStyle), true)] + public class StateStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "StateStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + OnCustomGUI(prop); + PropertyField(prop, "m_Symbol"); + PropertyField(prop, "m_ItemStyle"); + PropertyField(prop, "m_Label"); + PropertyField(prop, "m_LabelLine"); + PropertyField(prop, "m_LineStyle"); + PropertyField(prop, "m_AreaStyle"); + --EditorGUI.indentLevel; + } + } + + protected virtual void OnCustomGUI(SerializedProperty prop) { } + } + + [CustomPropertyDrawer(typeof(EmphasisStyle), true)] + public class EmphasisStyleDrawer : StateStyleDrawer + { + public override string ClassName { get { return "EmphasisStyle"; } } + protected override void OnCustomGUI(SerializedProperty prop) + { + PropertyField(prop, "m_Scale"); + PropertyField(prop, "m_Focus"); + PropertyField(prop, "m_BlurScope"); + } + } + + [CustomPropertyDrawer(typeof(BlurStyle), true)] + public class BlurStyleDrawer : StateStyleDrawer + { + public override string ClassName { get { return "BlurStyle"; } } + } + + [CustomPropertyDrawer(typeof(SelectStyle), true)] + public class SelectStyleDrawer : StateStyleDrawer + { + public override string ClassName { get { return "SelectStyle"; } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs.meta new file mode 100644 index 0000000..38f810f --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/StateStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3aad8ee99115742729ec5a963274fae0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs new file mode 100644 index 0000000..0ea8971 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(SymbolStyle), true)] + public class SymbolStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Symbol"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + var type = (SymbolType)prop.FindPropertyRelative("m_Type").enumValueIndex; + PropertyField(prop, "m_Type"); + if (type == SymbolType.Custom) + { + AddHelpBox("Custom Symbol only work in PictorialBar serie", MessageType.Warning); + PropertyField(prop, "m_Image"); + PropertyField(prop, "m_ImageType"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + } + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_Size"); + PropertyField(prop, "m_Size2"); + PropertyField(prop, "m_Gap"); + PropertyField(prop, "m_BorderWidth"); + PropertyField(prop, "m_EmptyColor"); + PropertyField(prop, "m_Offset"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs.meta new file mode 100644 index 0000000..f8294f2 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/SymbolStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72d557cf0b7134953b457ab973364520 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs b/Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs new file mode 100644 index 0000000..cbc0bbf --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs @@ -0,0 +1,24 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(TextLimit), true)] + public class TextLimitDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "TextLimit"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Enable", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_MaxWidth"); + PropertyField(prop, "m_Gap"); + PropertyField(prop, "m_Suffix"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs.meta new file mode 100644 index 0000000..708bf5d --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TextLimitDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 842d3986d1c1747d8b0668649e8b1a0e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs b/Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs new file mode 100644 index 0000000..0aa6c9f --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(Padding), true)] + public class PaddingDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Padding"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Top"); + PropertyField(prop, "m_Right"); + PropertyField(prop, "m_Bottom"); + PropertyField(prop, "m_Left"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(TextPadding), true)] + public class TextPaddingDrawer : PaddingDrawer + { + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs.meta new file mode 100644 index 0000000..dccf74b --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TextPaddingDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46cc25f4c9fc846938a06cf3b8fc75bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs new file mode 100644 index 0000000..d223972 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs @@ -0,0 +1,43 @@ +using UnityEditor; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(TextStyle), true)] + public class TextStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "TextStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; +#if dUI_TextMeshPro + PropertyField(prop, "m_TMPFont"); +#else + PropertyField(prop, "m_Font"); +#endif + PropertyField(prop, "m_Rotate"); + PropertyField(prop, "m_AutoColor"); + PropertyField(prop, "m_Color"); + PropertyField(prop, "m_FontSize"); + PropertyField(prop, "m_LineSpacing"); + PropertyField(prop, "m_Alignment"); + PropertyField(prop, "m_AutoAlign"); +#if dUI_TextMeshPro + PropertyField(prop, "m_TMPFontStyle"); + PropertyField(prop, "m_TMPSpriteAsset"); +#else + PropertyField(prop, "m_FontStyle"); + PropertyField(prop, "m_AutoWrap"); +#endif + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs.meta new file mode 100644 index 0000000..faa64c5 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TextStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f14c425fb2bff44f2bf9ddb8d6ff1741 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs b/Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs new file mode 100644 index 0000000..573fb02 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs @@ -0,0 +1,136 @@ +using System.IO; +using UnityEditor; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(ThemeStyle), true)] + public class ThemeStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Theme"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + var defaultWidth = pos.width; + var defaultX = pos.x; + var chart = prop.serializedObject.targetObject as BaseChart; + if (MakeComponentFoldout(prop, "m_Show", false, new HeaderMenuInfo("Reset|Reset to theme default color", () => + { + chart.theme.sharedTheme.ResetTheme(); + chart.RefreshAllComponent(); + }), new HeaderMenuInfo("Export|Export theme to asset for a new theme", () => + { + ExportThemeWindow.target = chart; + EditorWindow.GetWindow(typeof(ExportThemeWindow)); + }), new HeaderMenuInfo("Sync color to custom|Sync shared theme color to custom color", () => + { + chart.theme.SyncSharedThemeColorToCustom(); + }))) + { + ++EditorGUI.indentLevel; + var chartNameList = XCThemeMgr.GetAllThemeNames(); + var lastIndex = chartNameList.IndexOf(chart.theme.themeName); + var y = pos.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + var selectedIndex = EditorGUI.Popup(new Rect(pos.x, y, pos.width, EditorGUIUtility.singleLineHeight), + "Shared Theme", lastIndex, chartNameList.ToArray()); + AddSingleLineHeight(); + if (lastIndex != selectedIndex) + { + XCThemeMgr.SwitchTheme(chart, chartNameList[selectedIndex]); + } + PropertyField(prop, "m_SharedTheme"); + PropertyField(prop, "m_TransparentBackground"); + PropertyField(prop, "m_EnableCustomTheme"); + using(new EditorGUI.DisabledScope(!prop.FindPropertyRelative("m_EnableCustomTheme").boolValue)) + { + PropertyField(prop, "m_CustomBackgroundColor"); + PropertyField(prop, "m_CustomColorPalette"); + } + --EditorGUI.indentLevel; + } + } + + private void AddPropertyField(Rect pos, SerializedProperty prop, ref float y) + { + float height = EditorGUI.GetPropertyHeight(prop, new GUIContent(prop.displayName), true); + EditorGUI.PropertyField(new Rect(pos.x, y, pos.width, height), prop, true); + y += height + EditorGUIUtility.standardVerticalSpacing; + m_Heights[m_KeyName] += height + EditorGUIUtility.standardVerticalSpacing; + } + } + + public class ExportThemeWindow : UnityEditor.EditorWindow + { + public static BaseChart target; + private static ExportThemeWindow window; + private string m_ChartName; + static void Init() + { + window = (ExportThemeWindow) EditorWindow.GetWindow(typeof(ExportThemeWindow), false, "Export Theme", true); + window.minSize = new Vector2(600, 50); + window.maxSize = new Vector2(600, 50); + window.Show(); + } + + void OnInspectorUpdate() + { + Repaint(); + } + + private void OnGUI() + { + if (target == null) + { + Close(); + return; + } + GUILayout.Space(10); + GUILayout.Label("Input a new name for theme:"); + m_ChartName = GUILayout.TextField(m_ChartName); + + GUILayout.Space(10); + GUILayout.Label("Export path:"); + if (string.IsNullOrEmpty(m_ChartName)) + { + GUILayout.Label("Need input a new name."); + } + else + { + GUILayout.Label(XCThemeMgr.GetThemeAssetPath(m_ChartName)); + } + + GUILayout.Space(20); + if (GUILayout.Button("Export")) + { + if (string.IsNullOrEmpty(m_ChartName)) + { + ShowNotification(new GUIContent("ERROR:Need input a new name!")); + } + else if (XCThemeMgr.ContainsTheme(m_ChartName)) + { + ShowNotification(new GUIContent("ERROR:The name you entered is already in use!")); + } + else if (IsAssetsExist(XCThemeMgr.GetThemeAssetPath(m_ChartName))) + { + ShowNotification(new GUIContent("ERROR:The asset is exist! \npath=" + + XCThemeMgr.GetThemeAssetPath(m_ChartName))); + } + else + { + XCThemeMgr.ExportTheme(target.theme.sharedTheme, m_ChartName); + ShowNotification(new GUIContent("SUCCESS:The theme is exported. \npath=" + + XCThemeMgr.GetThemeAssetPath(m_ChartName))); + } + } + } + + private bool IsAssetsExist(string path) + { + return File.Exists(Application.dataPath + "/../" + path); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs.meta new file mode 100644 index 0000000..107eab6 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ThemeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 704e7c2793bca4050821c6e0756c8316 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs b/Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs new file mode 100644 index 0000000..66ad6d6 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs @@ -0,0 +1,12 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(TitleStyle), true)] + public class TitleStyleDrawer : LabelStyleDrawer + { + public override string ClassName { get { return "TitleStyle"; } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs.meta new file mode 100644 index 0000000..ad95f9b --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/TitleStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e451ee4c9f65a414784fd5fd9cad6ec1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs b/Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs new file mode 100644 index 0000000..74292ab --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(ViewControl), true)] + public class ViewControlDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "ViewControl"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Alpha"); + PropertyField(prop, "m_Beta"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs.meta b/Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs.meta new file mode 100644 index 0000000..02ebcb2 --- /dev/null +++ b/Assets/XCharts/Editor/ChildComponents/ViewControlDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faeb8611591ee4c038e88fdb5a67b5ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents.meta b/Assets/XCharts/Editor/MainComponents.meta new file mode 100644 index 0000000..6cc4e5e --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f98ff753316eb48d58325ecd996f2a1f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/AxisEditor.cs b/Assets/XCharts/Editor/MainComponents/AxisEditor.cs new file mode 100644 index 0000000..78199d6 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/AxisEditor.cs @@ -0,0 +1,237 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(Axis))] + public class AxisEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + var m_Type = baseProperty.FindPropertyRelative("m_Type"); + var m_LogBase = baseProperty.FindPropertyRelative("m_LogBase"); + var m_MinMaxType = baseProperty.FindPropertyRelative("m_MinMaxType"); + var type = (Axis.AxisType)m_Type.enumValueIndex; + EditorGUI.indentLevel++; + if (component is ParallelAxis) + { + PropertyField("m_ParallelIndex"); + } + else if (!(component is SingleAxis)) + { + PropertyField("m_GridIndex"); + PropertyField("m_PolarIndex"); + } + PropertyField("m_Type"); + PropertyField("m_Position"); + PropertyField("m_Offset"); + if (type == Axis.AxisType.Log) + { + PropertyField("m_LogBaseE"); + EditorGUI.BeginChangeCheck(); + PropertyField("m_LogBase"); + if (m_LogBase.floatValue <= 0 || m_LogBase.floatValue == 1) + { + m_LogBase.floatValue = 10; + } + EditorGUI.EndChangeCheck(); + } + if (type == Axis.AxisType.Value || type == Axis.AxisType.Time) + { + PropertyField("m_MinMaxType"); + Axis.AxisMinMaxType minMaxType = (Axis.AxisMinMaxType)m_MinMaxType.enumValueIndex; + switch (minMaxType) + { + case Axis.AxisMinMaxType.Default: + break; + case Axis.AxisMinMaxType.MinMax: + break; + case Axis.AxisMinMaxType.Custom: + EditorGUI.indentLevel++; + PropertyField("m_Min"); + PropertyField("m_Max"); + EditorGUI.indentLevel--; + break; + } + PropertyField("m_CeilRate"); + if (type == Axis.AxisType.Value) + { + PropertyField("m_Inverse"); + } + } + PropertyField("m_SplitNumber"); + if (type == Axis.AxisType.Category) + { + PropertyField("m_MaxCache"); + PropertyField("m_MinCategorySpacing"); + PropertyField("m_BoundaryGap"); + } + else + { + PropertyField("m_Interval"); + } + DrawExtendeds(); + if (type != Axis.AxisType.Category) + { + PropertyField("m_Animation"); + } + PropertyField("m_AxisLine"); + PropertyField("m_AxisName"); + PropertyField("m_AxisTick"); + PropertyField("m_AxisLabel"); + PropertyField("m_SplitLine"); + PropertyField("m_SplitArea"); + PropertyField("m_IndicatorLabel"); + if (type != Axis.AxisType.Category) + { + PropertyField("m_MinorTick"); + PropertyField("m_MinorSplitLine"); + } + PropertyListField("m_Icons", true); + if (type == Axis.AxisType.Category) + { + PropertyListField("m_Data", true, new HeaderMenuInfo("Import ECharts Axis Data", () => + { + PraseExternalDataEditor.UpdateData(chart, null, component as Axis, false); + PraseExternalDataEditor.ShowWindow(); + })); + } + EditorGUI.indentLevel--; + } + } + + [ComponentEditor(typeof(XAxis))] + public class XAxisEditor : AxisEditor { } + + [ComponentEditor(typeof(YAxis))] + public class YAxisEditor : AxisEditor { } + + [ComponentEditor(typeof(XAxis3D))] + public class XAxis3DEditor : AxisEditor { } + + [ComponentEditor(typeof(YAxis3D))] + public class YAxis3DEditor : AxisEditor { } + + [ComponentEditor(typeof(ZAxis3D))] + public class ZAxis3DEditor : AxisEditor { } + + [ComponentEditor(typeof(SingleAxis))] + public class SingleAxisEditor : AxisEditor + { + protected override void DrawExtendeds() + { + base.DrawExtendeds(); + PropertyField("m_Orient"); + PropertyField("m_Left"); + PropertyField("m_Right"); + PropertyField("m_Top"); + PropertyField("m_Bottom"); + PropertyField("m_Width"); + PropertyField("m_Height"); + } + } + + [ComponentEditor(typeof(AngleAxis))] + public class AngleAxisEditor : AxisEditor + { + protected override void DrawExtendeds() + { + base.DrawExtendeds(); + PropertyField("m_StartAngle"); + PropertyField("m_Clockwise"); + } + } + + [ComponentEditor(typeof(RadiusAxis))] + public class RadiusAxisEditor : AxisEditor { } + + [ComponentEditor(typeof(ParallelAxis))] + public class ParallelAxisEditor : AxisEditor { } + + [CustomPropertyDrawer(typeof(AxisLabel), true)] + public class AxisLabelDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "AxisLabel"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Inside"); + PropertyField(prop, "m_Interval"); + + PropertyField(prop, "m_ShowAsPositiveNumber"); + PropertyField(prop, "m_OnZero"); + PropertyField(prop, "m_ShowStartLabel"); + PropertyField(prop, "m_ShowEndLabel"); + + PropertyField(prop, "m_Rotate"); + PropertyField(prop, "m_Offset"); + PropertyField(prop, "m_Distance"); + PropertyField(prop, "m_Formatter"); + PropertyField(prop, "m_NumericFormatter"); + PropertyField(prop, "m_Width"); + PropertyField(prop, "m_Height"); + PropertyField(prop, "m_Icon"); + PropertyField(prop, "m_Background"); + PropertyField(prop, "m_TextStyle"); + PropertyField(prop, "m_TextPadding"); + PropertyField(prop, "m_TextLimit"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(AxisName), true)] + public class AxisNameDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "AxisName"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Name"); + PropertyField(prop, "m_OnZero"); + PropertyField(prop, "m_LabelStyle"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(AxisSplitArea), true)] + public class AxisSplitAreaDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "SplitArea"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Color"); + --EditorGUI.indentLevel; + } + } + } + + [CustomPropertyDrawer(typeof(AxisAnimation), true)] + public class AxisAnimationDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Animation"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_UnscaledTime"); + PropertyField(prop, "m_Duration"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/AxisEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/AxisEditor.cs.meta new file mode 100644 index 0000000..5ffd445 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/AxisEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e6d7780afa9b49aa9081bf55d301955 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs b/Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs new file mode 100644 index 0000000..41570a2 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs @@ -0,0 +1,21 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(Background))] + internal sealed class BackgroundEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + + ++EditorGUI.indentLevel; + PropertyField("m_Image"); + PropertyField("m_ImageType"); + PropertyField("m_ImageColor"); + PropertyField("m_AutoColor"); + PropertyField("m_BorderStyle"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs.meta new file mode 100644 index 0000000..96466f3 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/BackgroundEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 89d95a9a994ad4b4692832e9a548e9e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs b/Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs new file mode 100644 index 0000000..ef129ed --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using UnityEngine.Assertions; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class BaseGraphEditor : UnityEditor.Editor + { + class Styles + { + public static readonly GUIContent btnAddComponent = new GUIContent("Add Main Component", ""); + public static readonly GUIContent btnRebuildChartObject = new GUIContent("Rebuild Object", ""); + public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", ""); + public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", ""); + public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", ""); + } + public BaseGraph m_BaseGraph; + + public static T AddUIComponent(string chartName) where T : BaseGraph + { + return XChartsEditor.AddGraph(chartName); + } + + protected Dictionary m_Properties = new Dictionary(); + + protected virtual void OnEnable() + { + m_Properties.Clear(); + m_BaseGraph = (BaseGraph)target; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + PropertyField("m_Script"); + + OnStartInspectorGUI(); + OnDebugInspectorGUI(); + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void OnStartInspectorGUI() { } + + protected virtual void OnDebugInspectorGUI() + { + EditorGUILayout.Space(); + OnDebugStartInspectorGUI(); + OnDebugEndInspectorGUI(); + } + + protected virtual void OnDebugStartInspectorGUI() { } + protected virtual void OnDebugEndInspectorGUI() { } + + protected void PropertyField(string name) + { + if (!m_Properties.ContainsKey(name)) + { + var prop = serializedObject.FindProperty(name); + if (prop == null) + { + Debug.LogError("Property " + name + " not found!"); + return; + } + m_Properties.Add(name, prop); + } + EditorGUILayout.PropertyField(m_Properties[name]); + } + + protected void PropertyField(SerializedProperty property) + { + Assert.IsNotNull(property); + var title = ChartEditorHelper.GetContent(property.displayName); + PropertyField(property, title); + } + + protected void PropertyField(SerializedProperty property, GUIContent title) + { + EditorGUILayout.PropertyField(property, title); + } + + protected void PropertyListField(string relativePropName, bool showOrder = true, params HeaderMenuInfo[] menus) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var height = 0f; + var prop = FindProperty(relativePropName); + prop.isExpanded = ChartEditorHelper.MakeListWithFoldout(ref m_DrawRect, ref height, + prop, prop.isExpanded, showOrder, true, menus); + if (prop.isExpanded) + { + GUILayoutUtility.GetRect(1f, height - 17); + } + } + + protected void PropertyTwoFiled(string relativePropName) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var prop = FindProperty(relativePropName); + ChartEditorHelper.MakeTwoField(ref m_DrawRect, m_DrawRect.width, prop, prop.displayName); + } + + protected SerializedProperty FindProperty(string path) + { + if (!m_Properties.ContainsKey(path)) + { + m_Properties.Add(path, serializedObject.FindProperty(path)); + } + return m_Properties[path]; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs.meta new file mode 100644 index 0000000..cc9f93e --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/BaseGraphEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88786092000154d359c1aa954ce664f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/CommentEditor.cs b/Assets/XCharts/Editor/MainComponents/CommentEditor.cs new file mode 100644 index 0000000..922e44a --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/CommentEditor.cs @@ -0,0 +1,18 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(Comment))] + public class CommentEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_LabelStyle"); + //PropertyField("m_MarkStyle"); + PropertyListField("m_Items", true); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/CommentEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/CommentEditor.cs.meta new file mode 100644 index 0000000..8c2401f --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/CommentEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2364066bf3174aa39b79020266ce72d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs b/Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs new file mode 100644 index 0000000..10f0fdb --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs @@ -0,0 +1,65 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(DataZoom))] + public class DataZoomEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + var m_SupportInside = baseProperty.FindPropertyRelative("m_SupportInside"); + var m_SupportSlider = baseProperty.FindPropertyRelative("m_SupportSlider"); + var m_SupportMarquee = baseProperty.FindPropertyRelative("m_SupportMarquee"); + var m_Start = baseProperty.FindPropertyRelative("m_Start"); + var m_End = baseProperty.FindPropertyRelative("m_End"); + var m_MinShowNum = baseProperty.FindPropertyRelative("m_MinShowNum"); + ++EditorGUI.indentLevel; + PropertyField("m_Orient"); + PropertyField("m_SupportInside"); + if (m_SupportInside.boolValue) + { + PropertyField("m_SupportInsideScroll"); + PropertyField("m_SupportInsideDrag"); + } + PropertyField(m_SupportSlider); + PropertyField(m_SupportMarquee); + PropertyField("m_ZoomLock"); + PropertyField("m_ScrollSensitivity"); + PropertyField("m_RangeMode"); + PropertyField(m_Start); + PropertyField(m_End); + PropertyField("m_StartLock"); + PropertyField("m_EndLock"); + PropertyField(m_MinShowNum); + if (m_Start.floatValue < 0) m_Start.floatValue = 0; + if (m_End.floatValue > 100) m_End.floatValue = 100; + if (m_MinShowNum.intValue < 0) m_MinShowNum.intValue = 0; + if (m_SupportSlider.boolValue) + { + PropertyField("m_ShowDataShadow"); + PropertyField("m_ShowDetail"); + PropertyField("m_BackgroundColor"); + PropertyField("m_BorderWidth"); + PropertyField("m_BorderColor"); + PropertyField("m_FillerColor"); + PropertyField("m_Left"); + PropertyField("m_Right"); + PropertyField("m_Top"); + PropertyField("m_Bottom"); + PropertyField("m_LineStyle"); + PropertyField("m_AreaStyle"); + PropertyField("m_LabelStyle"); + PropertyListField("m_XAxisIndexs", true); + PropertyListField("m_YAxisIndexs", true); + } + else + { + PropertyListField("m_XAxisIndexs", true); + PropertyListField("m_YAxisIndexs", true); + } + PropertyField("m_MarqueeStyle"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs.meta new file mode 100644 index 0000000..78d0550 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/DataZoomEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 06bc176df52bf4953b8d46254523d2ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs b/Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs new file mode 100644 index 0000000..4dd20aa --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(GridCoord3D))] + public class GridCoord3DEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_Left"); + PropertyField("m_Bottom"); + PropertyField("m_BoxWidth"); + PropertyField("m_BoxHeight"); + PropertyField("m_BoxDepth"); + PropertyField("m_XYExchanged"); + PropertyField("m_ShowBorder"); + PropertyField("m_ViewControl"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs.meta new file mode 100644 index 0000000..32b3c33 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/GridCoord3DEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9a4a8a30b1124c4e996e234d5717a07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs b/Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs new file mode 100644 index 0000000..adb60fd --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(GridCoord))] + public class GridCoordEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + var layoutIndex = baseProperty.FindPropertyRelative("m_LayoutIndex").intValue; + PropertyField("m_LayoutIndex"); + PropertyField("m_Left"); + PropertyField("m_Right"); + PropertyField("m_Top"); + PropertyField("m_Bottom"); + PropertyField("m_BackgroundColor"); + PropertyField("m_ShowBorder"); + PropertyField("m_BorderWidth"); + PropertyField("m_BorderColor"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs.meta new file mode 100644 index 0000000..7df3d86 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/GridCoordEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb28a0ae5edd34b63ae9cbce0986585b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs b/Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs new file mode 100644 index 0000000..b20aea5 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(GridLayout))] + public class GridLayoutEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_Left"); + PropertyField("m_Right"); + PropertyField("m_Top"); + PropertyField("m_Bottom"); + PropertyField("m_Row"); + PropertyField("m_Column"); + PropertyField("m_Spacing"); + PropertyField("m_Inverse"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs.meta new file mode 100644 index 0000000..54b6ae7 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/GridLayoutEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4288bf299494d43d497436ace4b7a5a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/LegendEditor.cs b/Assets/XCharts/Editor/MainComponents/LegendEditor.cs new file mode 100644 index 0000000..e65bad0 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/LegendEditor.cs @@ -0,0 +1,32 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(Legend))] + public class LegendEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_IconType"); + PropertyField("m_ItemWidth"); + PropertyField("m_ItemHeight"); + PropertyField("m_ItemGap"); + PropertyField("m_ItemAutoColor"); + PropertyField("m_ItemOpacity"); + PropertyField("m_SelectedMode"); + PropertyField("m_Orient"); + PropertyField("m_Location"); + PropertyField("m_LabelStyle"); + PropertyField("m_TextLimit"); + PropertyField("m_Background"); + PropertyField("m_Padding"); + PropertyListField("m_Icons"); + PropertyListField("m_Colors"); + PropertyListField("m_Positions"); + PropertyListField("m_Data"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/LegendEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/LegendEditor.cs.meta new file mode 100644 index 0000000..8429e30 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/LegendEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ef040b104aa2452f80d91c7c33775c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs b/Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs new file mode 100644 index 0000000..eb7b9b0 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs @@ -0,0 +1,116 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.Assertions; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class MainComponentBaseEditor + { + protected const string MORE = "More"; + protected bool m_MoreFoldout = false; + public BaseChart chart { get; private set; } + public MainComponent component { get; private set; } + + public SerializedProperty baseProperty; + public SerializedProperty showProperty; + + internal void Init(BaseChart chart, MainComponent target, SerializedProperty property, UnityEditor.Editor inspector) + { + this.chart = chart; + this.component = target; + this.baseProperty = property; + showProperty = baseProperty.FindPropertyRelative("m_Show"); + if (showProperty == null) + showProperty = baseProperty.FindPropertyRelative("m_Enable"); + OnEnable(); + } + + public virtual void OnEnable() + { } + + public virtual void OnDisable() + { } + + internal void OnInternalInspectorGUI() + { + OnInspectorGUI(); + EditorGUILayout.Space(); + } + + public virtual void OnInspectorGUI() + { } + + protected virtual void DrawExtendeds() + { } + + public virtual string GetDisplayTitle() + { + var num = chart.GetChartComponentNum(component.GetType()); + if (num > 1) + return ObjectNames.NicifyVariableName(component.GetType().Name) + " " + component.index; + else + return ObjectNames.NicifyVariableName(component.GetType().Name); + } + + protected SerializedProperty FindProperty(string path) + { + return baseProperty.FindPropertyRelative(path); + } + + protected void PropertyField(string path) + { + var property = FindProperty(path); + if (property != null) + { + var title = ChartEditorHelper.GetContent(property.displayName); + PropertyField(property, title); + } + else + { + Debug.LogError("Property not exist:" + baseProperty.propertyPath + "," + path); + } + } + + protected void PropertyFiledMore(System.Action action) + { + m_MoreFoldout = ChartEditorHelper.DrawHeader(MORE, m_MoreFoldout, false, null, null); + if (m_MoreFoldout) + { + if (action != null) action(); + } + } + + protected void PropertyField(SerializedProperty property) + { + Assert.IsNotNull(property); + var title = ChartEditorHelper.GetContent(property.displayName); + PropertyField(property, title); + } + + protected void PropertyField(SerializedProperty property, GUIContent title) + { + EditorGUILayout.PropertyField(property, title); + } + + protected void PropertyListField(string relativePropName, bool showOrder = true, params HeaderMenuInfo[] menus) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var height = 0f; + var prop = FindProperty(relativePropName); + prop.isExpanded = ChartEditorHelper.MakeListWithFoldout(ref m_DrawRect, ref height, + prop, prop.isExpanded, showOrder, true, menus); + if (prop.isExpanded) + { + GUILayoutUtility.GetRect(1f, height - 17); + } + } + + protected void PropertyTwoFiled(string relativePropName) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var prop = FindProperty(relativePropName); + ChartEditorHelper.MakeTwoField(ref m_DrawRect, m_DrawRect.width, prop, prop.displayName); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs.meta new file mode 100644 index 0000000..c0299b8 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MainComponentBaseEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2909950e65ad44c2eb617a8b75845431 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs b/Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs new file mode 100644 index 0000000..05e2896 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs @@ -0,0 +1,8 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class MainComponentEditor : MainComponentBaseEditor + where T : MainComponent + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs.meta new file mode 100644 index 0000000..6e9b172 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MainComponentEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 276997094e92e4b6590591727cb21349 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs b/Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs new file mode 100644 index 0000000..065ea98 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.Assertions; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public sealed class MainComponentListEditor + { + public BaseChart chart { get; private set; } + BaseChartEditor m_BaseEditor; + + //SerializedObject m_SerializedObject; + List m_ComponentsProperty; + SerializedProperty m_EnableProperty; + + Dictionary m_EditorTypes; + List m_Editors; + + public MainComponentListEditor(BaseChartEditor editor) + { + Assert.IsNotNull(editor); + m_BaseEditor = editor; + } + + public void Init(BaseChart chart, SerializedObject serializedObject, List componentProps) + { + Assert.IsNotNull(chart); + + this.chart = chart; + m_ComponentsProperty = componentProps; + + Assert.IsNotNull(m_ComponentsProperty); + + m_Editors = new List(); + m_EditorTypes = new Dictionary(); + + var editorTypes = RuntimeUtil.GetAllTypesDerivedFrom() + .Where(t => t.IsDefined(typeof(ComponentEditorAttribute), false) && !t.IsAbstract); + foreach (var editorType in editorTypes) + { + var attribute = editorType.GetAttribute(); + m_EditorTypes.Add(attribute.componentType, editorType); + } + + RefreshEditors(); + } + + public void UpdateComponentsProperty(List componentProps) + { + m_ComponentsProperty = componentProps; + RefreshEditors(); + } + + public void Clear() + { + if (m_Editors == null) + return; + + foreach (var editor in m_Editors) + editor.OnDisable(); + + m_Editors.Clear(); + m_EditorTypes.Clear(); + } + + public void OnGUI() + { + if (chart == null) + return; + + for (int i = 0; i < m_Editors.Count; i++) + { + var editor = m_Editors[i]; + string title = editor.GetDisplayTitle(); + int id = i; + + bool displayContent = ChartEditorHelper.DrawHeader( + title, + editor.baseProperty, + editor.showProperty, + () => { if (EditorUtility.DisplayDialog("", "Sure reset " + editor.component.GetType().Name + "?", "Yes", "Cancel")) ResetComponentEditor(id); }, + () => { if (EditorUtility.DisplayDialog("", "Sure remove " + editor.component.GetType().Name + "?", "Yes", "Cancel")) RemoveComponentEditor(id); }, + () => { Application.OpenURL("https://xcharts-team.github.io/docs/configuration/#" + editor.component.GetType().Name.ToLower()); } + ); + if (displayContent) + { + editor.OnInternalInspectorGUI(); + } + } + + if (m_Editors.Count == 0) + { + EditorGUILayout.HelpBox("No componnet.", MessageType.Info); + } + } + + void RefreshEditors() + { + foreach (var editor in m_Editors) + editor.OnDisable(); + + m_Editors.Clear(); + var count = Mathf.Min(chart.components.Count, m_ComponentsProperty.Count); + for (int i = 0; i < count; i++) + { + if (chart.components[i] != null) + { + CreateEditor(chart.components[i], m_ComponentsProperty[i]); + } + } + } + + void CreateEditor(MainComponent component, SerializedProperty property, int index = -1) + { + + var settingsType = component.GetType(); + Type editorType; + + if (!m_EditorTypes.TryGetValue(settingsType, out editorType)) + editorType = typeof(MainComponentBaseEditor); + var editor = (MainComponentBaseEditor)Activator.CreateInstance(editorType); + editor.Init(chart, component, property, m_BaseEditor); + + if (index < 0) + m_Editors.Add(editor); + else + m_Editors[index] = editor; + } + + public void AddChartComponent(Type type) + { + var component = chart.AddChartComponent(type); + if (component != null) + { + if (component is YAxis) + { + var yAxis = component as YAxis; + if (yAxis.index == 1) + { + yAxis.position = Axis.AxisPosition.Right; + } + } + else if (component is XAxis) + { + var xAxis = component as XAxis; + if (xAxis.index == 1) + { + xAxis.position = Axis.AxisPosition.Top; + } + } + } + m_ComponentsProperty = m_BaseEditor.RefreshComponent(); + RefreshEditors(); + EditorUtility.SetDirty(chart); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + private void ResetComponentEditor(int id) + { + m_Editors[id].component.Reset(); + EditorUtility.SetDirty(chart); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + private void RemoveComponentEditor(int id) + { + m_Editors[id].OnDisable(); + chart.RemoveChartComponent(m_Editors[id].component); + m_Editors.RemoveAt(id); + chart.RebuildChartObject(); + m_ComponentsProperty = m_BaseEditor.RefreshComponent(); + RefreshEditors(); + EditorUtility.SetDirty(chart); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs.meta new file mode 100644 index 0000000..5810b63 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MainComponentListEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62cc000ee006f492aadb05138ac6fe87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs b/Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs new file mode 100644 index 0000000..43e2894 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs @@ -0,0 +1,55 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(MarkArea))] + public class MarkAreaEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_SerieIndex"); + PropertyField("m_Text"); + PropertyField("m_ItemStyle"); + PropertyField("m_Label"); + PropertyField("m_Start"); + PropertyField("m_End"); + --EditorGUI.indentLevel; + } + } + + [CustomPropertyDrawer(typeof(MarkAreaData), true)] + public class MarkAreaDataDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "MarkAreaData"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + var type = (MarkAreaType) (prop.FindPropertyRelative("m_Type")).enumValueIndex; + PropertyField(prop, "m_Type"); + PropertyField(prop, "m_Name"); + switch (type) + { + case MarkAreaType.None: + PropertyField(prop, "m_XPosition"); + PropertyField(prop, "m_YPosition"); + PropertyField(prop, "m_XValue"); + PropertyField(prop, "m_YValue"); + break; + case MarkAreaType.Min: + case MarkAreaType.Max: + case MarkAreaType.Average: + case MarkAreaType.Median: + PropertyField(prop, "m_Dimension"); + break; + } + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs.meta new file mode 100644 index 0000000..fd786ea --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MarkAreaEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83cc6f39a078f4ecfb2c3da09b116355 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs b/Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs new file mode 100644 index 0000000..34e5bd9 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs @@ -0,0 +1,60 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(MarkLine))] + public class MarkLineEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_SerieIndex"); + PropertyField("m_OnTop"); + PropertyField("m_Animation"); + PropertyListField("m_Data", true); + --EditorGUI.indentLevel; + } + } + + [CustomPropertyDrawer(typeof(MarkLineData), true)] + public class MarkLineDataDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "MarkLineData"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + var type = (MarkLineType) (prop.FindPropertyRelative("m_Type")).enumValueIndex; + var group = prop.FindPropertyRelative("m_Group").intValue; + PropertyField(prop, "m_Type"); + PropertyField(prop, "m_Name"); + switch (type) + { + case MarkLineType.None: + PropertyField(prop, "m_XPosition"); + PropertyField(prop, "m_YPosition"); + PropertyField(prop, "m_XValue"); + PropertyField(prop, "m_YValue"); + break; + case MarkLineType.Min: + case MarkLineType.Max: + case MarkLineType.Average: + case MarkLineType.Median: + PropertyField(prop, "m_Dimension"); + break; + } + PropertyField(prop, "m_Group"); + if (group > 0 && type == MarkLineType.None) PropertyField(prop, "m_ZeroPosition"); + PropertyField(prop, "m_LineStyle"); + PropertyField(prop, "m_StartSymbol"); + PropertyField(prop, "m_EndSymbol"); + PropertyField(prop, "m_Label"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs.meta new file mode 100644 index 0000000..0793155 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/MarkLineEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 341fcecf4884e47519a2aff6defb30de +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs b/Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs new file mode 100644 index 0000000..3a216f3 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs @@ -0,0 +1,21 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(ParallelCoord))] + public class ParallelCoordEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_Orient"); + PropertyField("m_Left"); + PropertyField("m_Right"); + PropertyField("m_Top"); + PropertyField("m_Bottom"); + PropertyField("m_BackgroundColor"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs.meta new file mode 100644 index 0000000..339b121 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/ParallelCoordEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3d4d8d4d5c4b4197b021a25a7125390 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs b/Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs new file mode 100644 index 0000000..bb94c95 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs @@ -0,0 +1,19 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(PolarCoord))] + public class PolarCoordEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyTwoFiled("m_Center"); + PropertyTwoFiled("m_Radius"); + PropertyField("m_BackgroundColor"); + PropertyField("m_IndicatorLabelOffset"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs.meta new file mode 100644 index 0000000..aeb2e65 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/PolarCoordEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f488cf12ded545de8737a2cde8bfead +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs b/Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs new file mode 100644 index 0000000..8caf952 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs @@ -0,0 +1,52 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(RadarCoord))] + public class RadarCoordEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_GridIndex"); + PropertyField("m_Shape"); + PropertyField("m_PositionType"); + PropertyTwoFiled("m_Center"); + PropertyField("m_Radius"); + PropertyField("m_SplitNumber"); + PropertyField("m_StartAngle"); + PropertyField("m_CeilRate"); + PropertyField("m_IsAxisTooltip"); + PropertyField("m_OutRangeColor"); + PropertyField("m_ConnectCenter"); + PropertyField("m_LineGradient"); + PropertyField("m_AxisLine"); + PropertyField("m_AxisName"); + PropertyField("m_SplitLine"); + PropertyField("m_SplitArea"); + PropertyListField("m_IndicatorList"); + --EditorGUI.indentLevel; + } + } + + [CustomPropertyDrawer(typeof(RadarCoord.Indicator), true)] + public class RadarIndicatorDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Indicator"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Name"); + PropertyField(prop, "m_Min"); + PropertyField(prop, "m_Max"); + PropertyTwoFiled(prop, "m_Range"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs.meta new file mode 100644 index 0000000..484b250 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/RadarCoordEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f1bfbd3f054624d42b854bcac720a58b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/ThemeEditor.cs b/Assets/XCharts/Editor/MainComponents/ThemeEditor.cs new file mode 100644 index 0000000..76012ef --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/ThemeEditor.cs @@ -0,0 +1,64 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Editor +{ + [CustomEditor(typeof(Theme))] + public class ThemeEditor : UnityEditor.Editor + { + static class Styles + { + internal static GUIContent btnReset = new GUIContent("Reset to Default", "Reset to default theme"); + internal static GUIContent btnSyncFontToSubTheme = new GUIContent("Sync Font to Sub Theme", "Sync main theme font to sub theme font"); + internal static GUIContent btnSyncFontFromSetting = new GUIContent("Sync Font from Setting", "Sync main theme font and sub theme font from XCSetting font"); + } + + private Theme m_Theme; + + void OnEnable() + { + m_Theme = target as Theme; + } + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + if (GUILayout.Button(Styles.btnReset)) + { + if (EditorUtility.DisplayDialog(Styles.btnReset.text, Styles.btnReset.tooltip, "Yes", "Cancel")) + { + m_Theme.ResetTheme(); + Debug.Log("XCharts: Reset Finish."); + } + } + if (GUILayout.Button(Styles.btnSyncFontFromSetting)) + { + if (EditorUtility.DisplayDialog(Styles.btnSyncFontFromSetting.text, Styles.btnSyncFontFromSetting.tooltip, "Yes", "Cancel")) + { + m_Theme.common.font = XCSettings.font; + m_Theme.SyncFontToSubComponent(); +#if dUI_TextMeshPro + m_Theme.common.tmpFont = XCSettings.tmpFont; + m_Theme.SyncTMPFontToSubComponent(); +#endif + Debug.Log("XCharts: Sync Finish."); + } + } + if (GUILayout.Button(Styles.btnSyncFontToSubTheme)) + { + if (EditorUtility.DisplayDialog(Styles.btnSyncFontToSubTheme.text, Styles.btnSyncFontToSubTheme.tooltip, "Yes", "Cancel")) + { + m_Theme.SyncFontToSubComponent(); +#if dUI_TextMeshPro + m_Theme.SyncTMPFontToSubComponent(); +#endif + Debug.Log("XCharts: Sync Finish."); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/ThemeEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/ThemeEditor.cs.meta new file mode 100644 index 0000000..b14f798 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/ThemeEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7856321df80e646c99317e95964991bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/TitleEditor.cs b/Assets/XCharts/Editor/MainComponents/TitleEditor.cs new file mode 100644 index 0000000..2a22c05 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/TitleEditor.cs @@ -0,0 +1,21 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(Title))] + public class TitleEditor : MainComponentEditor + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_Text"); + PropertyField("m_SubText"); + PropertyField("m_ItemGap"); + PropertyField("m_Location"); + PropertyField("m_LabelStyle"); + PropertyField("m_SubLabelStyle"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/TitleEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/TitleEditor.cs.meta new file mode 100644 index 0000000..7780391 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/TitleEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c50c4d317d9274b32a5f137db0d66038 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/TooltipEditor.cs b/Assets/XCharts/Editor/MainComponents/TooltipEditor.cs new file mode 100644 index 0000000..b76b1c3 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/TooltipEditor.cs @@ -0,0 +1,49 @@ +using UnityEditor; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(Tooltip))] + public class TooltipEditor : MainComponentEditor<Tooltip> + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_Type"); + PropertyField("m_Trigger"); + PropertyField("m_TriggerOn"); + PropertyField("m_Position"); + PropertyField("m_FixedX"); + PropertyField("m_FixedY"); + PropertyField("m_Offset"); + PropertyField("m_ShowContent"); + PropertyField("m_AlwayShowContent"); + PropertyField("m_TitleFormatter"); + PropertyField("m_ItemFormatter"); + PropertyField("m_NumericFormatter"); + PropertyFiledMore(() => + { + PropertyField("m_TitleHeight"); + PropertyField("m_ItemHeight"); + PropertyField("m_Marker"); + PropertyField("m_BorderWidth"); + PropertyField("m_BorderColor"); + PropertyField("m_PaddingLeftRight"); + PropertyField("m_PaddingTopBottom"); + PropertyField("m_BackgroundImage"); + PropertyField("m_BackgroundType"); + PropertyField("m_BackgroundColor"); + PropertyField("m_FixedWidth"); + PropertyField("m_FixedHeight"); + PropertyField("m_MinWidth"); + PropertyField("m_MinHeight"); + PropertyField("m_IgnoreDataDefaultContent"); + }); + PropertyField("m_LineStyle"); + PropertyField("m_TitleLabelStyle"); + PropertyListField("m_ColumnGapWidths"); + PropertyListField("m_ContentLabelStyles"); + --EditorGUI.indentLevel; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/TooltipEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/TooltipEditor.cs.meta new file mode 100644 index 0000000..0952d61 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/TooltipEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9179fea7bb2354601acce0feb82f8b17 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs b/Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs new file mode 100644 index 0000000..b942a2b --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using UnityEngine.Assertions; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class UIComponentEditor : UnityEditor.Editor + { + class Styles + { + public static readonly GUIContent btnAddComponent = new GUIContent("Add Main Component", ""); + public static readonly GUIContent btnRebuildChartObject = new GUIContent("Rebuild Object", ""); + public static readonly GUIContent btnSaveAsImage = new GUIContent("Save As Image", ""); + public static readonly GUIContent btnCheckWarning = new GUIContent("Check Warning", ""); + public static readonly GUIContent btnHideWarning = new GUIContent("Hide Warning", ""); + } + public UIComponent m_UIComponent; + + public static T AddUIComponent<T>(string chartName) where T : UIComponent + { + return XChartsEditor.AddGraph<T>(chartName); + } + + protected Dictionary<string, SerializedProperty> m_Properties = new Dictionary<string, SerializedProperty>(); + + protected virtual void OnEnable() + { + m_Properties.Clear(); + m_UIComponent = (UIComponent) target; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + PropertyField("m_Script"); + + OnStartInspectorGUI(); + OnDebugInspectorGUI(); + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void OnStartInspectorGUI() { } + + protected virtual void OnDebugInspectorGUI() + { + EditorGUILayout.Space(); + PropertyField("m_DebugModel"); + OnDebugStartInspectorGUI(); + if (GUILayout.Button(Styles.btnRebuildChartObject)) + { + m_UIComponent.RebuildChartObject(); + } + if (GUILayout.Button(Styles.btnSaveAsImage)) + { + m_UIComponent.SaveAsImage(); + } + OnDebugEndInspectorGUI(); + } + + protected virtual void OnDebugStartInspectorGUI() { } + protected virtual void OnDebugEndInspectorGUI() { } + + protected void PropertyField(string name) + { + if (!m_Properties.ContainsKey(name)) + { + var prop = serializedObject.FindProperty(name); + if (prop == null) + { + Debug.LogError("Property " + name + " not found!"); + return; + } + m_Properties.Add(name, prop); + } + EditorGUILayout.PropertyField(m_Properties[name]); + } + + protected void PropertyField(SerializedProperty property) + { + Assert.IsNotNull(property); + var title = ChartEditorHelper.GetContent(property.displayName); + PropertyField(property, title); + } + + protected void PropertyField(SerializedProperty property, GUIContent title) + { + EditorGUILayout.PropertyField(property, title); + } + + protected void PropertyListField(string relativePropName, bool showOrder = true, params HeaderMenuInfo[] menus) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var height = 0f; + var prop = FindProperty(relativePropName); + prop.isExpanded = ChartEditorHelper.MakeListWithFoldout(ref m_DrawRect, ref height, + prop, prop.isExpanded, showOrder, true, menus); + if (prop.isExpanded) + { + GUILayoutUtility.GetRect(1f, height - 17); + } + } + + protected void PropertyTwoFiled(string relativePropName) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var prop = FindProperty(relativePropName); + ChartEditorHelper.MakeTwoField(ref m_DrawRect, m_DrawRect.width, prop, prop.displayName); + } + + protected SerializedProperty FindProperty(string path) + { + if (!m_Properties.ContainsKey(path)) + { + m_Properties.Add(path, serializedObject.FindProperty(path)); + } + return m_Properties[path]; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs.meta new file mode 100644 index 0000000..72da37b --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/UIComponentEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d226759112b0d463b8fba4830762893c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs b/Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs new file mode 100644 index 0000000..d06d614 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(UIComponentTheme), true)] + public class UIComponentThemeDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Theme"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_SharedTheme"); + PropertyField(prop, "m_TransparentBackground"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs.meta b/Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs.meta new file mode 100644 index 0000000..75ad5ab --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/UIComponentThemeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dca2d7a2ed994420182384c2efa48c0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs b/Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs new file mode 100644 index 0000000..059f3c4 --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs @@ -0,0 +1,64 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [ComponentEditor(typeof(VisualMap))] + public class VisualMapEditor : MainComponentEditor<VisualMap> + { + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + var type = (VisualMap.Type) baseProperty.FindPropertyRelative("m_Type").enumValueIndex; + var isPiece = type == VisualMap.Type.Piecewise; + PropertyField("m_Type"); + PropertyField("m_SerieIndex"); + PropertyField("m_AutoMinMax"); + PropertyField("m_Min"); + PropertyField("m_Max"); + PropertyField("m_SplitNumber"); + PropertyField("m_Dimension"); + PropertyField("m_WorkOnLine"); + PropertyField("m_WorkOnArea"); + PropertyField("m_ShowUI"); + if (baseProperty.FindPropertyRelative("m_ShowUI").boolValue) + { + PropertyField("m_SelectedMode"); + PropertyTwoFiled("m_Range"); + PropertyTwoFiled("m_Text"); + PropertyTwoFiled("m_TextGap"); + PropertyField("m_HoverLink"); + PropertyField("m_Calculable"); + PropertyField("m_ItemWidth"); + PropertyField("m_ItemHeight"); + if (isPiece) PropertyField("m_ItemGap"); + PropertyField("m_BorderWidth"); + PropertyField("m_Orient"); + PropertyField("m_Location"); + } + PropertyListField("m_OutOfRange"); + PropertyListField("m_InRange"); + --EditorGUI.indentLevel; + } + } + + [CustomPropertyDrawer(typeof(VisualMapRange), true)] + public class VisualMapRangeDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Range"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeFoldout(prop, "m_Color")) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Min"); + PropertyField(prop, "m_Max"); + PropertyField(prop, "m_Label"); + PropertyField(prop, "m_Color"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs.meta b/Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs.meta new file mode 100644 index 0000000..288d90a --- /dev/null +++ b/Assets/XCharts/Editor/MainComponents/VisualMapEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38b6413ab74484d6599bebbca7f5d437 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series.meta b/Assets/XCharts/Editor/Series.meta new file mode 100644 index 0000000..9afafc7 --- /dev/null +++ b/Assets/XCharts/Editor/Series.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 303691ade88f04660abab870b613cc3a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/BarEditor.cs b/Assets/XCharts/Editor/Series/BarEditor.cs new file mode 100644 index 0000000..d1ec01b --- /dev/null +++ b/Assets/XCharts/Editor/Series/BarEditor.cs @@ -0,0 +1,63 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Bar))] + public class BarEditor : SerieEditor<Bar> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_ColorBy"); + PropertyField("m_Stack"); + if (serie.IsUseCoord<PolarCoord>()) + { + PropertyField("m_PolarIndex"); + } + else + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + } + PropertyField("m_BarType"); + PropertyField("m_BarWidth"); + PropertyField("m_BarGap"); + PropertyField("m_BarMaxWidth"); + PropertyField("m_RealtimeSort"); + if(serie.useSortData) + { + PropertyField("m_DataSortType"); + } + if (serie.IsUseCoord<PolarCoord>()) + { + PropertyField("m_RoundCap"); + } + else + { + PropertyField("m_BarPercentStack"); + if (serie.barType == BarType.Zebra) + { + PropertyField("m_BarZebraWidth"); + PropertyField("m_BarZebraGap"); + } + } + PropertyField("m_Clip"); + PropertyFiledMore(() => + { + PropertyFieldLimitMin("m_MinShow", 0); + PropertyFieldLimitMin("m_MaxShow", 0); + PropertyFieldLimitMin("m_MaxCache", 0); + PropertyField("m_Ignore"); + PropertyField("m_IgnoreValue"); + PropertyField("m_IgnoreLineBreak"); + PropertyField("m_ShowAsPositiveNumber"); + PropertyField("m_Large"); + PropertyField("m_LargeThreshold"); + PropertyField("m_PlaceHolder"); + PropertyField("m_MinShowLabel"); + PropertyField("m_MinShowLabelValue"); + }); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/BarEditor.cs.meta b/Assets/XCharts/Editor/Series/BarEditor.cs.meta new file mode 100644 index 0000000..afc8184 --- /dev/null +++ b/Assets/XCharts/Editor/Series/BarEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b924d2e0412243769e4ac6ee8bd5fa6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/CandlestickEditor.cs b/Assets/XCharts/Editor/Series/CandlestickEditor.cs new file mode 100644 index 0000000..06fff6f --- /dev/null +++ b/Assets/XCharts/Editor/Series/CandlestickEditor.cs @@ -0,0 +1,26 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Candlestick))] + public class CandlestickEditor : SerieEditor<Candlestick> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_ColorBy"); + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + PropertyFieldLimitMin("m_MinShow", 0); + PropertyFieldLimitMin("m_MaxShow", 0); + PropertyFieldLimitMin("m_MaxCache", 0); + PropertyField("m_BarWidth"); + PropertyField("m_Clip"); + PropertyField("m_ShowAsPositiveNumber"); + PropertyField("m_Large"); + PropertyField("m_LargeThreshold"); + + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/CandlestickEditor.cs.meta b/Assets/XCharts/Editor/Series/CandlestickEditor.cs.meta new file mode 100644 index 0000000..e10eb32 --- /dev/null +++ b/Assets/XCharts/Editor/Series/CandlestickEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 713afd224d3194435b9559720035a5fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/EffectScatterEditor.cs b/Assets/XCharts/Editor/Series/EffectScatterEditor.cs new file mode 100644 index 0000000..b686744 --- /dev/null +++ b/Assets/XCharts/Editor/Series/EffectScatterEditor.cs @@ -0,0 +1,26 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(EffectScatter))] + public class EffectScatterEditor : SerieEditor<EffectScatter> + { + public override void OnCustomInspectorGUI() + { + if (serie.IsUseCoord<SingleAxisCoord>()) + { + PropertyField("m_SingleAxisIndex"); + } + else + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + } + PropertyField("m_Clip"); + PropertyField("m_Symbol"); + + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/EffectScatterEditor.cs.meta b/Assets/XCharts/Editor/Series/EffectScatterEditor.cs.meta new file mode 100644 index 0000000..a94c3a7 --- /dev/null +++ b/Assets/XCharts/Editor/Series/EffectScatterEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc8fce25209234fa3b6c3cb79d02b47e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/HeatmapEditor.cs b/Assets/XCharts/Editor/Series/HeatmapEditor.cs new file mode 100644 index 0000000..4ac1bd5 --- /dev/null +++ b/Assets/XCharts/Editor/Series/HeatmapEditor.cs @@ -0,0 +1,30 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Heatmap))] + public class HeatmapEditor : SerieEditor<Heatmap> + { + public override void OnCustomInspectorGUI() + { + if (serie.IsUseCoord<PolarCoord>()) + { + PropertyField("m_PolarIndex"); + } + else + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + } + PropertyField("m_HeatmapType"); + PropertyField("m_Ignore"); + PropertyField("m_IgnoreValue"); + PropertyField("m_MaxCache"); + + + PropertyField("m_Symbol"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/HeatmapEditor.cs.meta b/Assets/XCharts/Editor/Series/HeatmapEditor.cs.meta new file mode 100644 index 0000000..f630b51 --- /dev/null +++ b/Assets/XCharts/Editor/Series/HeatmapEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3137acf1aff4f4cd29af0d3c3ca78bca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/LineEditor.cs b/Assets/XCharts/Editor/Series/LineEditor.cs new file mode 100644 index 0000000..a68b6f4 --- /dev/null +++ b/Assets/XCharts/Editor/Series/LineEditor.cs @@ -0,0 +1,47 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Line))] + public class LineEditor : SerieEditor<Line> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_Stack"); + if (serie.IsUseCoord<PolarCoord>()) + { + PropertyField("m_PolarIndex"); + } + else + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + } + PropertyField("m_LineType"); + if (serie.lineType == LineType.Smooth) + { + PropertyField("m_SmoothLimit"); + } + PropertyField("m_Clip"); + PropertyFiledMore(() => + { + PropertyFieldLimitMin("m_MinShow", 0); + PropertyFieldLimitMin("m_MaxShow", 0); + PropertyFieldLimitMin("m_MaxCache", 0); + PropertyField("m_SampleDist"); + PropertyField("m_SampleType"); + PropertyField("m_SampleAverage"); + PropertyField("m_Ignore"); + PropertyField("m_IgnoreValue"); + PropertyField("m_IgnoreLineBreak"); + PropertyField("m_ShowAsPositiveNumber"); + PropertyField("m_Large"); + PropertyField("m_LargeThreshold"); + }); + PropertyField("m_Symbol"); + PropertyField("m_LineStyle"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/LineEditor.cs.meta b/Assets/XCharts/Editor/Series/LineEditor.cs.meta new file mode 100644 index 0000000..c817b49 --- /dev/null +++ b/Assets/XCharts/Editor/Series/LineEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 065fbd043ce6e40609cfb52114aaaa6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/ParallelEditor.cs b/Assets/XCharts/Editor/Series/ParallelEditor.cs new file mode 100644 index 0000000..c29aff6 --- /dev/null +++ b/Assets/XCharts/Editor/Series/ParallelEditor.cs @@ -0,0 +1,17 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Parallel))] + public class ParallelEditor : SerieEditor<Parallel> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_ColorBy"); + PropertyField("m_ParallelIndex"); + PropertyField("m_LineType"); + PropertyField("m_LineStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/ParallelEditor.cs.meta b/Assets/XCharts/Editor/Series/ParallelEditor.cs.meta new file mode 100644 index 0000000..b368384 --- /dev/null +++ b/Assets/XCharts/Editor/Series/ParallelEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01af34b37a31d4876bd2d9ca7687ccd1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/PieEditor.cs b/Assets/XCharts/Editor/Series/PieEditor.cs new file mode 100644 index 0000000..54fe463 --- /dev/null +++ b/Assets/XCharts/Editor/Series/PieEditor.cs @@ -0,0 +1,33 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Pie))] + public class PieEditor : SerieEditor<Pie> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_GridIndex"); + PropertyField("m_RoseType"); + PropertyField("m_Gap"); + PropertyTwoFiled("m_Center"); + PropertyTwoFiled("m_Radius"); + PropertyField("m_AvoidLabelOverlap"); + PropertyFiledMore(() => + { + PropertyField("m_MaxCache"); + PropertyField("m_MinAngle"); + PropertyField("m_MinRadius"); + PropertyField("m_RoundCap"); + PropertyField("m_Ignore"); + PropertyField("m_IgnoreValue"); + PropertyField("m_ClickOffset"); + PropertyField("m_RadiusGradient"); + PropertyField("m_MinShowLabel"); + PropertyField("m_MinShowLabelValue"); + }); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/PieEditor.cs.meta b/Assets/XCharts/Editor/Series/PieEditor.cs.meta new file mode 100644 index 0000000..b0486f5 --- /dev/null +++ b/Assets/XCharts/Editor/Series/PieEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e7ae042a30a3433d8ae63a82bf37278 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/RadarEditor.cs b/Assets/XCharts/Editor/Series/RadarEditor.cs new file mode 100644 index 0000000..aee5f2e --- /dev/null +++ b/Assets/XCharts/Editor/Series/RadarEditor.cs @@ -0,0 +1,22 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Radar))] + public class RadarEditor : SerieEditor<Radar> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_ColorBy"); + PropertyField("m_RadarType"); + PropertyField("m_RadarIndex"); + PropertyField("m_MaxCache"); + PropertyField("m_Smooth"); + + PropertyField("m_Symbol"); + PropertyField("m_LineStyle"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/RadarEditor.cs.meta b/Assets/XCharts/Editor/Series/RadarEditor.cs.meta new file mode 100644 index 0000000..179183d --- /dev/null +++ b/Assets/XCharts/Editor/Series/RadarEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b6a9ab6dd1ea4e3a98bef73e90d42a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/RingEditor.cs b/Assets/XCharts/Editor/Series/RingEditor.cs new file mode 100644 index 0000000..5721c7e --- /dev/null +++ b/Assets/XCharts/Editor/Series/RingEditor.cs @@ -0,0 +1,25 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Ring))] + public class RingEditor : SerieEditor<Ring> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_GridIndex"); + PropertyTwoFiled("m_Center"); + PropertyTwoFiled("m_Radius"); + PropertyField("m_StartAngle"); + PropertyField("m_Gap"); + PropertyField("m_MaxCache"); + PropertyField("m_RoundCap"); + PropertyField("m_Clockwise"); + PropertyField("m_AvoidLabelOverlap"); + PropertyField("m_RadiusGradient"); + + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/RingEditor.cs.meta b/Assets/XCharts/Editor/Series/RingEditor.cs.meta new file mode 100644 index 0000000..4664e80 --- /dev/null +++ b/Assets/XCharts/Editor/Series/RingEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 647f8e564429a4b76833d3b428f5ab13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/ScatterEditor.cs b/Assets/XCharts/Editor/Series/ScatterEditor.cs new file mode 100644 index 0000000..5519bcb --- /dev/null +++ b/Assets/XCharts/Editor/Series/ScatterEditor.cs @@ -0,0 +1,27 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(Scatter))] + public class ScatterEditor : SerieEditor<Scatter> + { + public override void OnCustomInspectorGUI() + { + if (serie.IsUseCoord<SingleAxisCoord>()) + { + PropertyField("m_SingleAxisIndex"); + } + else + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + } + PropertyField("m_MaxCache"); + PropertyField("m_Clip"); + + PropertyField("m_Symbol"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/ScatterEditor.cs.meta b/Assets/XCharts/Editor/Series/ScatterEditor.cs.meta new file mode 100644 index 0000000..7b59778 --- /dev/null +++ b/Assets/XCharts/Editor/Series/ScatterEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3dfef7780c8cc412f87d00e437c94715 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SerieBaseEditor.cs b/Assets/XCharts/Editor/Series/SerieBaseEditor.cs new file mode 100644 index 0000000..48e5966 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieBaseEditor.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using UnityEngine.Assertions; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class SerieBaseEditor + { + public BaseChart chart { get; private set; } + public Serie serie { get; private set; } + + //Editor m_Inspector; + internal SerializedProperty baseProperty; + internal SerializedProperty showProperty; + internal List<HeaderMenuInfo> menus = new List<HeaderMenuInfo>(); + internal List<HeaderMenuInfo> serieDataMenus = new List<HeaderMenuInfo>(); + protected Dictionary<string, Type> m_CoordOptionsDic; + protected List<string> m_CoordOptionsNames; + private string m_DisplayName; + + internal void Init(BaseChart chart, Serie target, SerializedProperty property, UnityEditor.Editor inspector) + { + this.chart = chart; + this.serie = target; + this.baseProperty = property; + m_DisplayName = string.Format("Serie {0}: {1}", serie.index, serie.GetType().Name); + //m_Inspector = inspector; + showProperty = baseProperty.FindPropertyRelative("m_Show"); + if (showProperty == null) + showProperty = baseProperty.FindPropertyRelative("m_Enable"); + OnEnable(); + + if (serie.GetType().IsDefined(typeof(CoordOptionsAttribute), false)) + { + var attribute = serie.GetType().GetAttribute<CoordOptionsAttribute>(); + m_CoordOptionsDic = new Dictionary<string, Type>(); + m_CoordOptionsNames = new List<string>(); + if (attribute.type0 != null) + { + m_CoordOptionsDic[attribute.type0.Name] = attribute.type0; + m_CoordOptionsNames.Add(attribute.type0.Name); + } + if (attribute.type1 != null) + { + m_CoordOptionsDic[attribute.type1.Name] = attribute.type1; + m_CoordOptionsNames.Add(attribute.type1.Name); + } + if (attribute.type2 != null) + { + m_CoordOptionsDic[attribute.type2.Name] = attribute.type2; + m_CoordOptionsNames.Add(attribute.type2.Name); + } + if (attribute.type3 != null) + { + m_CoordOptionsDic[attribute.type3.Name] = attribute.type3; + m_CoordOptionsNames.Add(attribute.type3.Name); + } + } + } + + public virtual void OnEnable() + { } + + public virtual void OnDisable() + { } + + internal void OnInternalInspectorGUI() + { + OnInspectorGUI(); + EditorGUILayout.Space(); + } + + public virtual void OnInspectorGUI() + { } + + protected virtual void DrawExtendeds() + { } + + public virtual string GetDisplayTitle() + { + // var title = string.Format("serie {0}: {1}", serie.index, serie.GetType().Name); + // return ObjectNames.NicifyVariableName(title); + return m_DisplayName; + } + + internal SerializedProperty FindProperty(string path) + { + return baseProperty.FindPropertyRelative(path); + } + + protected SerializedProperty PropertyField(string path) + { + Assert.IsNotNull(path); + var property = FindProperty(path); + Assert.IsNotNull(property, "Can't find:" + path); + var title = ChartEditorHelper.GetContent(property.displayName); + PropertyField(property, title); + return property; + } + + protected void PropertyField(SerializedProperty property) + { + Assert.IsNotNull(property); + var title = ChartEditorHelper.GetContent(property.displayName); + PropertyField(property, title); + } + + protected void PropertyField(SerializedProperty property, GUIContent title) + { + EditorGUILayout.PropertyField(property, title); + } + + protected void PropertyListField(string relativePropName, bool showOrder = true) + { + //TODO: + PropertyField(relativePropName); + } + + protected void PropertyTwoFiled(string relativePropName) + { + var m_DrawRect = GUILayoutUtility.GetRect(1f, 17f); + var prop = FindProperty(relativePropName); + ChartEditorHelper.MakeTwoField(ref m_DrawRect, m_DrawRect.width, prop, prop.displayName); + } + protected void PropertyFieldLimitMin(string relativePropName, double min) + { + var prop = PropertyField(relativePropName); + switch (prop.propertyType) + { + case SerializedPropertyType.Float: + if (prop.floatValue < min) + prop.floatValue = (float) min; + break; + case SerializedPropertyType.Integer: + if (prop.intValue < min) + prop.intValue = (int) min; + break; + } + + } + protected void PropertyFieldLimitMax(string relativePropName, int max) + { + var prop = PropertyField(relativePropName); + switch (prop.propertyType) + { + case SerializedPropertyType.Float: + if (prop.floatValue > max) + prop.floatValue = (float) max; + break; + case SerializedPropertyType.Integer: + if (prop.intValue > max) + prop.intValue = (int) max; + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SerieBaseEditor.cs.meta b/Assets/XCharts/Editor/Series/SerieBaseEditor.cs.meta new file mode 100644 index 0000000..fad64c3 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieBaseEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 991edc42b5abf429b8062fd202278a4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs b/Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs new file mode 100644 index 0000000..5b62955 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs @@ -0,0 +1,24 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(SerieDataLink), true)] + public class SerieDataLinkDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "Link"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Source"); + PropertyField(prop, "m_Target"); + PropertyField(prop, "m_Value"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs.meta b/Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs.meta new file mode 100644 index 0000000..3c8a0ce --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieDataLinkDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 877ff0f4c473d47f29e7e7e3a3eaf53b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SerieEditor.cs b/Assets/XCharts/Editor/Series/SerieEditor.cs new file mode 100644 index 0000000..3eee17f --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieEditor.cs @@ -0,0 +1,362 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class SerieEditor<T> : SerieBaseEditor where T : Serie + { + protected const string MORE = "More"; + protected bool m_MoreFoldout = false; + private bool m_DataFoldout = false; + private bool m_DataComponentFoldout = true; + private Dictionary<int, bool> m_DataElementFoldout = new Dictionary<int, bool>(); + private bool m_LinksFoldout = false; + private Dictionary<int, bool> m_LinksElementFoldout = new Dictionary<int, bool>(); + + public override void OnInspectorGUI() + { + ++EditorGUI.indentLevel; + PropertyField("m_SerieName"); + if (m_CoordOptionsNames != null && m_CoordOptionsNames.Count > 1) + { + var index = m_CoordOptionsNames.IndexOf(serie.coordSystem); + var selectedIndex = EditorGUILayout.Popup("Coord System", index, m_CoordOptionsNames.ToArray()); + if (selectedIndex != index) + { + var typeName = m_CoordOptionsNames[selectedIndex]; + serie.coordSystem = m_CoordOptionsDic[typeName].Name; + } + } + PropertyField("m_State"); + OnCustomInspectorGUI(); + OnExtraInspectorGUI(); + PropertyFieldData(); + OnEndCustomInspectorGUI(); + --EditorGUI.indentLevel; + } + + public virtual void OnCustomInspectorGUI() + { } + + public virtual void OnEndCustomInspectorGUI() + { } + + private void OnExtraInspectorGUI() + { + foreach (var kv in Serie.extraComponentMap) + { + var prop = FindProperty(kv.Value); + if (prop.arraySize > 0) + PropertyField(prop.GetArrayElementAtIndex(0)); + } + } + + private HeaderMenuInfo headMenuInfo = new HeaderMenuInfo("Import ECharts Data", null); + + private void HeadMenuInfoCallback() + { + PraseExternalDataEditor.UpdateData(chart, serie, null, false); + PraseExternalDataEditor.ShowWindow(); + } + + private void PropertyFieldData() + { + headMenuInfo.action = HeadMenuInfoCallback; + m_DataFoldout = ChartEditorHelper.DrawHeader("Data", m_DataFoldout, false, null, null, headMenuInfo); + if (!m_DataFoldout) return; + EditorGUI.indentLevel++; + var m_Datas = FindProperty("m_Data"); + var m_DataDimension = FindProperty("m_ShowDataDimension"); + var m_ShowDataName = FindProperty("m_ShowDataName"); + PropertyField(m_ShowDataName); + PropertyField(m_DataDimension); + var listSize = m_Datas.arraySize; + listSize = EditorGUILayout.IntField("Size", listSize); + if (listSize < 0) listSize = 0; + if (m_DataDimension.intValue < 1) m_DataDimension.intValue = 1; + int dimension = m_DataDimension.intValue; + bool showName = m_ShowDataName.boolValue; + if (listSize != m_Datas.arraySize) + { + while (listSize > m_Datas.arraySize) m_Datas.arraySize++; + while (listSize < m_Datas.arraySize) m_Datas.arraySize--; + serie.ResetDataIndex(); + } + if (listSize > 30) // && !XCSettings.editorShowAllListData) + { + int num = listSize > 10 ? 10 : listSize; + for (int i = 0; i < num; i++) + { + DrawSerieData(dimension, m_Datas, i, showName); + } + if (num >= 10) + { + ChartEditorHelper.DrawHeader("... ", false, false, null, null); + DrawSerieData(dimension, m_Datas, listSize - 1, showName); + } + } + else + { + for (int i = 0; i < m_Datas.arraySize; i++) + { + DrawSerieData(dimension, m_Datas, i, showName); + } + } + EditorGUI.indentLevel--; + } + + private HeaderMenuInfo linkHeadMenuInfo = new HeaderMenuInfo("Import ECharts Link", null); + + private void LinkHeadMenuInfoCallback() + { + PraseExternalDataEditor.UpdateData(chart, serie, null, false); + PraseExternalDataEditor.ShowWindow(); + } + + protected void PropertyFieldLinks() + { + linkHeadMenuInfo.action = LinkHeadMenuInfoCallback; + m_LinksFoldout = ChartEditorHelper.DrawHeader("Links", m_LinksFoldout, false, null, null, linkHeadMenuInfo); + if (!m_LinksFoldout) return; + EditorGUI.indentLevel++; + var m_Links = FindProperty("m_Links"); + var listSize = m_Links.arraySize; + listSize = EditorGUILayout.IntField("Size", listSize); + if (listSize < 0) listSize = 0; + if (listSize != m_Links.arraySize) + { + while (listSize > m_Links.arraySize) m_Links.arraySize++; + while (listSize < m_Links.arraySize) m_Links.arraySize--; + } + if (listSize > 30) // && !XCSettings.editorShowAllListData) + { + int num = listSize > 10 ? 10 : listSize; + for (int i = 0; i < num; i++) + { + DrawSerieDataLink(m_Links, i); + } + if (num >= 10) + { + ChartEditorHelper.DrawHeader("... ", false, false, null, null); + DrawSerieDataLink(m_Links, listSize - 1); + } + } + else + { + for (int i = 0; i < m_Links.arraySize; i++) + { + DrawSerieDataLink(m_Links, i); + } + } + EditorGUI.indentLevel--; + } + + protected void PropertyFiledMore(System.Action action) + { + m_MoreFoldout = ChartEditorHelper.DrawHeader(MORE, m_MoreFoldout, false, null, null); + if (m_MoreFoldout) + { + if (action != null) action(); + } + } + + private void DrawSerieDataHeader(Rect drawRect, HeaderCallbackContext context) + { + var serieData = context.serieData; + var fieldCount = context.fieldCount; + var showName = context.showName; + var index = context.index; + var dimension = context.dimension; + + //drawRect.width -= 2f; + var maxX = drawRect.xMax; + var currentWidth = drawRect.width; + var lastX = drawRect.x; + var lastWid = drawRect.width; + var lastFieldWid = EditorGUIUtility.fieldWidth; + var lastLabelWid = EditorGUIUtility.labelWidth; + var sereName = serieData.FindPropertyRelative("m_Name"); + var data = serieData.FindPropertyRelative("m_Data"); +#if UNITY_2019_3_OR_NEWER + var gap = 2; + var namegap = 3; + var buttomLength = 30; +#else + var gap = 0; + var namegap = 0; + var buttomLength = 30; +#endif + if (showName) + { + buttomLength += 12; + } + if (fieldCount <= 1) + { + while (2 > data.arraySize) + { + var value = data.arraySize == 0 ? index : 0; + data.arraySize++; + data.GetArrayElementAtIndex(data.arraySize - 1).floatValue = value; + } + SerializedProperty element = data.GetArrayElementAtIndex(1); + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * 15 + gap; + drawRect.x = startX; + drawRect.xMax = maxX - buttomLength; + EditorGUI.PropertyField(drawRect, element, GUIContent.none); + } + else + { + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * 15 + gap; + var dataWidTotal = currentWidth - (startX + 20.5f + 1) - buttomLength; + var dataWid = dataWidTotal / fieldCount; + var xWid = dataWid - 0; + for (int i = 0; i < dimension; i++) + { + var dataCount = i < 1 ? 2 : i + 1; + while (dataCount > data.arraySize) + { + var value = data.arraySize == 0 ? index : 0; + data.arraySize++; + data.GetArrayElementAtIndex(data.arraySize - 1).floatValue = value; + } + drawRect.x = startX + i * xWid; + drawRect.width = dataWid + 25; + SerializedProperty element = data.GetArrayElementAtIndex(dimension <= 1 ? 1 : i); + EditorGUI.PropertyField(drawRect, element, GUIContent.none); + } + if (showName) + { + drawRect.x = startX + (fieldCount - 1) * xWid; + drawRect.width = dataWid + 40 + dimension * namegap - 2.5f; + EditorGUI.PropertyField(drawRect, sereName, GUIContent.none); + } + drawRect.x = lastX; + drawRect.width = lastWid; + ChartEditorHelper.UpDownAddDeleteButton(drawRect, context.listProp, index); + EditorGUIUtility.fieldWidth = lastFieldWid; + EditorGUIUtility.labelWidth = lastLabelWid; + } + } + + private void DrawSerieData(int dimension, SerializedProperty m_Datas, int index, bool showName) + { + bool flag; + if (!m_DataElementFoldout.TryGetValue(index, out flag)) + { + flag = false; + m_DataElementFoldout[index] = false; + } + var fieldCount = dimension + (showName ? 1 : 0); + var serieData = m_Datas.GetArrayElementAtIndex(index); + var dataIndex = serieData.FindPropertyRelative("m_Index").intValue; + var callbackContext = new HeaderCallbackContext() + { + serieData = serieData, + fieldCount = fieldCount, + showName = showName, + index = index, + dimension = dimension, + listProp = m_Datas + }; + m_DataElementFoldout[index] = ChartEditorHelper.DrawSerieDataHeader("SerieData " + dataIndex, flag, false, null, callbackContext, DrawSerieDataHeader); + if (m_DataElementFoldout[index]) + { + if (!(serie is ISimplifiedSerie)) + DrawSerieDataDetail(m_Datas, index); + } + } + + private void DrawSerieDataDetail(SerializedProperty m_Datas, int index) + { + EditorGUI.indentLevel++; + var serieData = m_Datas.GetArrayElementAtIndex(index); + PropertyField(serieData.FindPropertyRelative("m_Name")); + //PropertyField(serieData.FindPropertyRelative("m_State")); + if (serie.GetType().IsDefined(typeof(SerieDataExtraFieldAttribute), false)) + { + var attribute = serie.GetType().GetAttribute<SerieDataExtraFieldAttribute>(); + foreach (var field in attribute.fields) + { + PropertyField(serieData.FindPropertyRelative(field)); + } + } + + serieDataMenus.Clear(); + if (serie.GetType().IsDefined(typeof(SerieDataComponentAttribute), false)) + { + var attribute = serie.GetType().GetAttribute<SerieDataComponentAttribute>(); + foreach (var type in attribute.types) + { + var size = serieData.FindPropertyRelative(SerieData.extraComponentMap[type]).arraySize; + serieDataMenus.Add(new HeaderMenuInfo("Add " + type.Name, () => + { + serie.GetSerieData(index).EnsureComponent(type); + EditorUtility.SetDirty(chart); + }, size == 0)); + } + foreach (var type in attribute.types) + { + var size = serieData.FindPropertyRelative(SerieData.extraComponentMap[type]).arraySize; + serieDataMenus.Add(new HeaderMenuInfo("Remove " + type.Name, () => + { + serie.GetSerieData(index).RemoveComponent(type); + EditorUtility.SetDirty(chart); + }, size > 0)); + } + } + serieDataMenus.Add(new HeaderMenuInfo("Remove All", () => + { + serie.GetSerieData(index).RemoveAllComponent(); + }, true)); + m_DataComponentFoldout = ChartEditorHelper.DrawHeader("Component", m_DataComponentFoldout, false, null, null, serieDataMenus); + if (m_DataComponentFoldout) + { + foreach (var kv in SerieData.extraComponentMap) + { + var prop = serieData.FindPropertyRelative(kv.Value); + if (prop.arraySize > 0) + PropertyField(prop.GetArrayElementAtIndex(0)); + } + } + EditorGUI.indentLevel--; + } + + private void DrawSerieDataLink(SerializedProperty m_Datas, int index) + { + bool flag; + if (!m_LinksElementFoldout.TryGetValue(index, out flag)) + { + flag = false; + m_LinksElementFoldout[index] = false; + } + var dataLink = m_Datas.GetArrayElementAtIndex(index); + m_LinksElementFoldout[index] = ChartEditorHelper.DrawHeader("Link " + index, flag, false, null, + delegate (Rect drawRect) + { + var sourceIndex = dataLink.FindPropertyRelative("m_Source"); + var targetIndex = dataLink.FindPropertyRelative("m_Target"); + var value = dataLink.FindPropertyRelative("m_Value"); + var hig = ChartEditorHelper.MakeThreeField(ref drawRect, drawRect.width, sourceIndex, targetIndex, value, ""); + var btnRect = drawRect; + btnRect.y -= hig; + ChartEditorHelper.UpDownAddDeleteButton(btnRect, m_Datas, index); + }); + if (m_LinksElementFoldout[index]) + { + DrawSerieDataLinkDetail(m_Datas, index); + } + } + + private void DrawSerieDataLinkDetail(SerializedProperty m_Links, int index) + { + EditorGUI.indentLevel++; + var dataLink = m_Links.GetArrayElementAtIndex(index); + PropertyField(dataLink.FindPropertyRelative("m_Source")); + PropertyField(dataLink.FindPropertyRelative("m_Target")); + PropertyField(dataLink.FindPropertyRelative("m_Value")); + EditorGUI.indentLevel--; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SerieEditor.cs.meta b/Assets/XCharts/Editor/Series/SerieEditor.cs.meta new file mode 100644 index 0000000..58ee781 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a03d2daaabd8465398b1ff06a9889cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SerieListEditor.cs b/Assets/XCharts/Editor/Series/SerieListEditor.cs new file mode 100644 index 0000000..406845b --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieListEditor.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine.Assertions; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public sealed class SerieListEditor + { + public BaseChart chart { get; private set; } + BaseChartEditor m_BaseEditor; + + SerializedObject m_SerializedObject; + List<SerializedProperty> m_SeriesProperty; + SerializedProperty m_EnableProperty; + + Dictionary<Type, Type> m_EditorTypes; + List<SerieBaseEditor> m_Editors; + private bool m_SerieFoldout; + + public SerieListEditor(BaseChartEditor editor) + { + Assert.IsNotNull(editor); + m_BaseEditor = editor; + } + + public void Init(BaseChart chart, SerializedObject serializedObject, List<SerializedProperty> componentProps) + { + Assert.IsNotNull(chart); + Assert.IsNotNull(serializedObject); + + this.chart = chart; + m_SerializedObject = serializedObject; + m_SeriesProperty = componentProps; + + m_Editors = new List<SerieBaseEditor>(); + m_EditorTypes = new Dictionary<Type, Type>(); + + var editorTypes = RuntimeUtil.GetAllTypesDerivedFrom<SerieBaseEditor>() + .Where(t => t.IsDefined(typeof(SerieEditorAttribute), false) && !t.IsAbstract); + foreach (var editorType in editorTypes) + { + var attribute = editorType.GetAttribute<SerieEditorAttribute>(); + m_EditorTypes.Add(attribute.serieType, editorType); + } + + RefreshEditors(); + } + + public void UpdateSeriesProperty(List<SerializedProperty> componentProps) + { + m_SeriesProperty = componentProps; + RefreshEditors(); + } + + public void Clear() + { + if (m_Editors == null) + return; + + foreach (var editor in m_Editors) + editor.OnDisable(); + + m_Editors.Clear(); + m_EditorTypes.Clear(); + } + + public void OnGUI() + { + if (chart == null) + return; + if (chart.debug.foldSeries) + { + m_SerieFoldout = ChartEditorHelper.DrawHeader("Series", m_SerieFoldout, false, null, null); + if (m_SerieFoldout) + { + DrawSeries(); + } + } + else + { + DrawSeries(); + } + } + + void DrawSeries() + { + for (int i = 0; i < m_Editors.Count; i++) + { + var editor = m_Editors[i]; + string title = editor.GetDisplayTitle(); + bool displayContent = ChartEditorHelper.DrawHeader( + title, + editor.baseProperty, + editor.showProperty, + editor.menus); + if (displayContent) + { + editor.OnInternalInspectorGUI(); + } + } + if (m_Editors.Count <= 0) + { + EditorGUILayout.HelpBox("No serie.", MessageType.Info); + } + } + + void RefreshEditors() + { + m_SerializedObject.UpdateIfRequiredOrScript(); + foreach (var editor in m_Editors) + editor.OnDisable(); + + m_Editors.Clear(); + + for (int i = 0; i < chart.series.Count; i++) + { + var serie = chart.series[i]; + if (serie != null) + { + CreateEditor(serie, m_SeriesProperty[i]); + } + } + } + + void CreateEditor(Serie serie, SerializedProperty property, int index = -1) + { + var id = index >= 0 ? index : m_Editors.Count; + var settingsType = serie.GetType(); + Type editorType; + + if (!m_EditorTypes.TryGetValue(settingsType, out editorType)) + editorType = typeof(SerieBaseEditor); + var editor = (SerieBaseEditor) Activator.CreateInstance(editorType); + editor.Init(chart, serie, property, m_BaseEditor); + editor.menus.Clear(); + editor.menus.Add(new HeaderMenuInfo("Clone", () => + { + CloneSerie(editor.serie); + })); + editor.menus.Add(new HeaderMenuInfo("Remove", () => + { + if (EditorUtility.DisplayDialog("", "Sure remove serie?", "Yes", "Cancel")) + RemoveSerieEditor(id); + })); + editor.menus.Add(new HeaderMenuInfo("Move Down", () => + { + if (chart.MoveDownSerie(id)) + { + m_SeriesProperty = m_BaseEditor.RefreshSeries(); + RefreshEditors(); + } + })); + editor.menus.Add(new HeaderMenuInfo("Move Up", () => + { + if (chart.MoveUpSerie(id)) + { + m_SeriesProperty = m_BaseEditor.RefreshSeries(); + RefreshEditors(); + } + })); + editor.menus.Add(new HeaderMenuInfo("Reset Data Index", () => + { + if (chart.ResetDataIndex(id)) + { + RefreshEditors(); + } + })); + foreach (var type in GetConvertToSerie(editor.serie.GetType())) + { + editor.menus.Add(new HeaderMenuInfo("Convert to " + type.Name, () => + { + ConvertSerie(editor.serie, type); + })); + } + if (editor.serie.GetType().IsDefined(typeof(SerieComponentAttribute), false)) + { + var attribute = editor.serie.GetType().GetAttribute<SerieComponentAttribute>(); + foreach (var type in attribute.types) + { + var size = editor.FindProperty(Serie.extraComponentMap[type]).arraySize; + editor.menus.Add(new HeaderMenuInfo("Add " + type.Name, () => + { + editor.serie.EnsureComponent(type); + RefreshEditors(); + chart.RefreshAllComponent(); + EditorUtility.SetDirty(chart); + }, size == 0)); + } + foreach (var type in attribute.types) + { + var size = editor.FindProperty(Serie.extraComponentMap[type]).arraySize; + editor.menus.Add(new HeaderMenuInfo("Remove " + type.Name, () => + { + editor.serie.RemoveComponent(type); + RefreshEditors(); + chart.RefreshAllComponent(); + EditorUtility.SetDirty(chart); + }, size > 0)); + } + } + if (index < 0) + m_Editors.Add(editor); + else + m_Editors[index] = editor; + } + + public void AddSerie(Type type) + { + m_SerializedObject.Update(); + var serieName = chart.GenerateDefaultSerieName(); + type.InvokeMember("AddDefaultSerie", + BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, + new object[] { chart, serieName }); + m_SerializedObject.Update(); + m_SerializedObject.ApplyModifiedProperties(); + m_SeriesProperty = m_BaseEditor.RefreshSeries(); + RefreshEditors(); + EditorUtility.SetDirty(chart); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + public void ConvertSerie(Serie serie, Type type) + { + chart.ConvertSerie(serie, type); + m_SeriesProperty = m_BaseEditor.RefreshSeries(); + RefreshEditors(); + } + + public void CloneSerie(Serie serie) + { + var newSerie = serie.Clone(); + newSerie.serieName = chart.GenerateDefaultSerieName(); + chart.InsertSerie(newSerie); + m_SeriesProperty = m_BaseEditor.RefreshSeries(); + RefreshEditors(); + } + + private void RemoveSerieEditor(int id) + { + m_Editors[id].OnDisable(); + chart.RemoveSerie(m_Editors[id].serie); + m_Editors.RemoveAt(id); + m_SerializedObject.Update(); + m_SerializedObject.ApplyModifiedProperties(); + m_SeriesProperty = m_BaseEditor.RefreshSeries(); + RefreshEditors(); + EditorUtility.SetDirty(chart); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + private List<Type> GetConvertToSerie(Type serie) + { + var list = new List<Type>(); + var typeMap = RuntimeUtil.GetAllTypesDerivedFrom<Serie>(); + foreach (var kvp in typeMap) + { + var type = kvp; + if (type.IsDefined(typeof(SerieConvertAttribute), false)) + { + var attribute = type.GetAttribute<SerieConvertAttribute>(); + if (attribute != null && attribute.Contains(serie)) + list.Add(type); + } + } + list.Sort((a, b) => { return a.Name.CompareTo(b.Name); }); + return list; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SerieListEditor.cs.meta b/Assets/XCharts/Editor/Series/SerieListEditor.cs.meta new file mode 100644 index 0000000..f8d7bf2 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SerieListEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5fbdf672386ce40bb803a8bb3bb2a3ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs b/Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs new file mode 100644 index 0000000..c09d3ee --- /dev/null +++ b/Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs @@ -0,0 +1,19 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(SimplifiedBar))] + public class SimplifiedBarEditor : SerieEditor<SimplifiedBar> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + PropertyField("m_BarWidth"); + PropertyField("m_BarGap"); + PropertyField("m_Clip"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs.meta b/Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs.meta new file mode 100644 index 0000000..a1b72e9 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SimplifiedBarEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99f8e53a5ab7c49e6b87aedee03cf856 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs b/Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs new file mode 100644 index 0000000..d180a57 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs @@ -0,0 +1,17 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(SimplifiedCandlestick))] + public class SimplifiedCandlestickEditor : SerieEditor<SimplifiedCandlestick> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + PropertyField("m_BarWidth"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs.meta b/Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs.meta new file mode 100644 index 0000000..9562375 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SimplifiedCandlestickEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 73d22e02d33e948d6981d537ba1f680e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs b/Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs new file mode 100644 index 0000000..b5be0c2 --- /dev/null +++ b/Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs @@ -0,0 +1,19 @@ +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [SerieEditor(typeof(SimplifiedLine))] + public class LineHPEditor : SerieEditor<SimplifiedLine> + { + public override void OnCustomInspectorGUI() + { + PropertyField("m_XAxisIndex"); + PropertyField("m_YAxisIndex"); + PropertyField("m_LineType"); + //PropertyField("m_Clip"); + PropertyField("m_LineStyle"); + PropertyField("m_ItemStyle"); + PropertyField("m_Animation"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs.meta b/Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs.meta new file mode 100644 index 0000000..4e85e3d --- /dev/null +++ b/Assets/XCharts/Editor/Series/SimplifiedLineEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cedf2a45756cd415cb5a74f3188ebd72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Utilities.meta b/Assets/XCharts/Editor/Utilities.meta new file mode 100644 index 0000000..51eefcd --- /dev/null +++ b/Assets/XCharts/Editor/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4c4e4069901c4c3089878f483167df8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs b/Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs new file mode 100644 index 0000000..27ffdaf --- /dev/null +++ b/Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs @@ -0,0 +1,802 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class HeaderCallbackContext + { + public int fieldCount = 0; + public SerializedProperty serieData; + public bool showName; + public int index; + public int dimension; + public SerializedProperty listProp; + } + + public class HeaderMenuInfo + { + public string name; + public Action action; + public bool enable = true; + + public HeaderMenuInfo() { } + public HeaderMenuInfo(string name, Action action) + { + this.name = name; + this.action = action; + } + public HeaderMenuInfo(string name, Action action, bool enable) + { + this.name = name; + this.action = action; + this.enable = enable; + } + } + + public static class ChartEditorHelper + { + public const float HEADER_HEIGHT = 17f; + public const float FOLDOUT_WIDTH = 13f; +#if UNITY_2019_3_OR_NEWER + public const float INDENT_WIDTH = 15; + public const float BOOL_WIDTH = 15; + public const float ARROW_WIDTH = 20; + public const float GAP_WIDTH = 2; + public const float DIFF_WIDTH = 0; +#else + public const float INDENT_WIDTH = 15; + public const float BOOL_WIDTH = 15; + public const float ARROW_WIDTH = 14f; + public const float GAP_WIDTH = 0; + public const float DIFF_WIDTH = 1; +#endif + public const float ICON_WIDHT = 10; + public const float ICON_GAP = 0; + static Dictionary<string, GUIContent> s_GUIContentCache; + + static ChartEditorHelper() + { + s_GUIContentCache = new Dictionary<string, GUIContent>(); + } + + public static void SecondField(Rect drawRect, SerializedProperty prop) + { + RectOffset offset = new RectOffset(-(int)EditorGUIUtility.labelWidth, 0, 0, 0); + drawRect = offset.Add(drawRect); + EditorGUI.PropertyField(drawRect, prop, GUIContent.none); + drawRect = offset.Remove(drawRect); + } + + public static void MakeTwoField(ref Rect drawRect, float rectWidth, SerializedProperty arrayProp, + string name) + { + while (arrayProp.arraySize < 2) arrayProp.arraySize++; + var prop1 = arrayProp.GetArrayElementAtIndex(0); + var prop2 = arrayProp.GetArrayElementAtIndex(1); + MakeTwoField(ref drawRect, rectWidth, prop1, prop2, name); + } + + public static void MakeDivideList(ref Rect drawRect, float rectWidth, SerializedProperty arrayProp, + string name, int showNum) + { + while (arrayProp.arraySize < showNum) arrayProp.arraySize++; + EditorGUI.LabelField(drawRect, name); +#if UNITY_2019_3_OR_NEWER + var gap = 2; +#else + var gap = 0; +#endif + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + gap; + var dataWidTotal = (rectWidth - (startX + INDENT_WIDTH + 1)); + EditorGUI.DrawRect(new Rect(startX, drawRect.y, dataWidTotal, drawRect.height), Color.grey); + var dataWid = dataWidTotal / showNum; + var xWid = dataWid - gap; + for (int i = 0; i < 1; i++) + { + drawRect.x = startX + i * xWid; + drawRect.width = dataWid + (EditorGUI.indentLevel - 2) * 40.5f; + EditorGUI.PropertyField(drawRect, arrayProp.GetArrayElementAtIndex(i), GUIContent.none); + } + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + public static void MakeTwoField(ref Rect drawRect, float rectWidth, SerializedProperty prop1, + SerializedProperty prop2, string name) + { + EditorGUI.LabelField(drawRect, name); + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + GAP_WIDTH; + var diff = 12 + EditorGUI.indentLevel * 14; + var offset = diff - INDENT_WIDTH; + var tempWidth = (rectWidth - startX + diff) / 2; + var centerXRect = new Rect(startX, drawRect.y, tempWidth, drawRect.height - 1); + var centerYRect = new Rect(centerXRect.x + tempWidth - offset + 3.4f, drawRect.y, tempWidth - 1, drawRect.height - 1); + EditorGUI.PropertyField(centerXRect, prop1, GUIContent.none); + EditorGUI.PropertyField(centerYRect, prop2, GUIContent.none); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + public static float MakeThreeField(ref Rect drawRect, float rectWidth, SerializedProperty prop1, + SerializedProperty prop2, SerializedProperty prop3, string name, bool btnSpacing = true) + { + EditorGUI.LabelField(drawRect, name); + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + GAP_WIDTH; + var diff = 13f + EditorGUI.indentLevel * 14; + var offset = diff - INDENT_WIDTH; + var tempWidth = (rectWidth - startX + diff - (btnSpacing ? (ICON_WIDHT + ICON_GAP) * 4 : 0)) / 3 + 8.5f; + var centerXRect = new Rect(startX, drawRect.y, tempWidth, drawRect.height - 1); + var centerYRect = new Rect(centerXRect.x + tempWidth - offset, drawRect.y, tempWidth - 1, drawRect.height - 1); + var centerZRect = new Rect(centerYRect.x + tempWidth - offset, drawRect.y, tempWidth - 1, drawRect.height - 1); + EditorGUI.PropertyField(centerXRect, prop1, GUIContent.none); + EditorGUI.PropertyField(centerYRect, prop2, GUIContent.none); + EditorGUI.PropertyField(centerZRect, prop3, GUIContent.none); + var hig = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + drawRect.y += hig; + return hig; + } + + public static void MakeVector2(ref Rect drawRect, float rectWidth, SerializedProperty prop, string name) + { + EditorGUI.LabelField(drawRect, name); + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + GAP_WIDTH; + var diff = 14 + EditorGUI.indentLevel * 14; + var offset = diff - INDENT_WIDTH; + var tempWidth = (rectWidth - startX + diff) / 2; + var centerXRect = new Rect(startX, drawRect.y, tempWidth, drawRect.height); + var centerYRect = new Rect(centerXRect.x + tempWidth - offset, drawRect.y, tempWidth, drawRect.height); + var x = EditorGUI.FloatField(centerXRect, prop.vector3Value.x); + var y = EditorGUI.FloatField(centerYRect, prop.vector3Value.y); + prop.vector3Value = new Vector3(x, y); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + } + + public static bool MakeFoldout(ref Rect drawRect, ref bool moduleToggle, string content, + SerializedProperty prop = null, bool bold = false) + { + float defaultWidth = drawRect.width; + float defaultX = drawRect.x; + var style = bold ? EditorCustomStyles.foldoutStyle : UnityEditor.EditorStyles.foldout; + drawRect.width = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH; + moduleToggle = EditorGUI.Foldout(drawRect, moduleToggle, content, true, style); + MakeBool(drawRect, prop); + drawRect.width = defaultWidth; + drawRect.x = defaultX; + return moduleToggle; + } + + public static bool MakeFoldout(ref Rect drawRect, Dictionary<string, float> heights, + Dictionary<string, bool> moduleToggle, string key, string content, SerializedProperty prop, bool bold = false) + { + float defaultWidth = drawRect.width; + float defaultX = drawRect.x; + var style = bold ? EditorCustomStyles.foldoutStyle : UnityEditor.EditorStyles.foldout; + drawRect.width = EditorGUIUtility.labelWidth; + moduleToggle[key] = EditorGUI.Foldout(drawRect, moduleToggle[key], content, true, style); + if (prop != null) + { + if (prop.propertyType == SerializedPropertyType.Boolean) + { + MakeBool(drawRect, prop); + } + else + { + drawRect.x = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + ARROW_WIDTH; + drawRect.width = defaultWidth - drawRect.x + ARROW_WIDTH - 2; + EditorGUI.PropertyField(drawRect, prop, GUIContent.none); + } + } + + drawRect.width = defaultWidth; + drawRect.x = defaultX; + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + heights[key] += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + return moduleToggle[key]; + } + public static bool MakeComponentFoldout(ref Rect drawRect, Dictionary<string, float> heights, + Dictionary<string, bool> moduleToggle, string key, string content, SerializedProperty prop, + SerializedProperty prop2, bool propEnable, params HeaderMenuInfo[] menus) + { + var sourRect = drawRect; + float defaultWidth = drawRect.width; + float defaultX = drawRect.x; + float headerHeight = DrawSplitterAndBackground(drawRect); + + drawRect.width = EditorGUIUtility.labelWidth; + + moduleToggle[key] = EditorGUI.Foldout(drawRect, moduleToggle[key], content, true, EditorStyles.foldout); + if (prop != null) + { + if (prop.propertyType == SerializedPropertyType.Boolean) + { + if (!propEnable) + using (new EditorGUI.DisabledScope(true)) + MakeBool(drawRect, prop); + else + MakeBool(drawRect, prop); + if (prop2 != null && !moduleToggle[key]) + { + drawRect.x = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + ARROW_WIDTH + BOOL_WIDTH; + drawRect.width = defaultWidth - drawRect.x + ARROW_WIDTH; + EditorGUI.PropertyField(drawRect, prop2, GUIContent.none); + } + } + else + { + drawRect.x = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + ARROW_WIDTH; + drawRect.width = defaultWidth - drawRect.x + ARROW_WIDTH - 2; + EditorGUI.PropertyField(drawRect, prop, GUIContent.none); + } + } + DrawMenu(sourRect, menus); + drawRect.width = defaultWidth; + drawRect.x = defaultX; + drawRect.y += headerHeight; + heights[key] += headerHeight; + return moduleToggle[key]; + } + + public static void MakeBool(Rect drawRect, SerializedProperty boolProp, int index = 0, string name = null) + { + float defaultWidth = drawRect.width; + float defaultX = drawRect.x; + float boolWidth = index * (BOOL_WIDTH + GAP_WIDTH); + drawRect.x = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + ARROW_WIDTH + boolWidth; + drawRect.width = (EditorGUI.indentLevel + 1) * BOOL_WIDTH + index * 110; + if (boolProp != null) + { + EditorGUI.PropertyField(drawRect, boolProp, GUIContent.none); + if (!string.IsNullOrEmpty(name)) + { + drawRect.x += BOOL_WIDTH; + drawRect.width = 200; + EditorGUI.LabelField(drawRect, name); + } + } + drawRect.width = defaultWidth; + drawRect.x = defaultX; + } + + public static bool MakeFoldout(ref Rect drawRect, ref float height, ref Dictionary<string, bool> moduleToggle, + SerializedProperty prop, string moduleName, string showPropName, bool bold = false) + { + var relativeProp = prop.FindPropertyRelative(showPropName); + var flag = MakeFoldout(ref drawRect, ref moduleToggle, prop, moduleName, relativeProp, bold); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + return flag; + } + + public static bool MakeFoldout(ref Rect drawRect, ref Dictionary<string, bool> moduleToggle, SerializedProperty prop, + string moduleName, SerializedProperty showProp = null, bool bold = false) + { + var key = prop.propertyPath; + if (!moduleToggle.ContainsKey(key)) + { + moduleToggle.Add(key, false); + } + var toggle = moduleToggle[key]; + + float defaultWidth = drawRect.width; + float defaultX = drawRect.x; +#if UNITY_2019_3_OR_NEWER + drawRect.width = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH; +#else + drawRect.width = EditorGUIUtility.labelWidth; +#endif + var displayName = string.IsNullOrEmpty(moduleName) ? prop.displayName : moduleName; + var foldoutStyle = bold ? EditorCustomStyles.foldoutStyle : UnityEditor.EditorStyles.foldout; + toggle = EditorGUI.Foldout(drawRect, toggle, displayName, true, foldoutStyle); + + if (moduleToggle[key] != toggle) + { + moduleToggle[key] = toggle; + } + if (showProp != null) + { + drawRect.x = EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + ARROW_WIDTH; + if (showProp.propertyType == SerializedPropertyType.Boolean) + { + drawRect.width = (EditorGUI.indentLevel + 1) * BOOL_WIDTH; + } + else + { + drawRect.width = defaultWidth - drawRect.x + ARROW_WIDTH - GAP_WIDTH; + } + EditorGUI.PropertyField(drawRect, showProp, GUIContent.none); + } + drawRect.width = defaultWidth; + drawRect.x = defaultX; + return toggle; + } + + public static bool MakeListWithFoldout(ref Rect drawRect, SerializedProperty listProp, bool foldout, + bool showOrder, bool showSize, params HeaderMenuInfo[] menus) + { + var height = 0f; + return MakeListWithFoldout(ref drawRect, ref height, listProp, foldout, showOrder, showSize, menus); + } + + public static bool MakeListWithFoldout(ref Rect drawRect, ref float height, SerializedProperty listProp, + bool foldout, bool showOrder, bool showSize, params HeaderMenuInfo[] menus) + { + var rawWidth = drawRect.width; + var headerHeight = DrawSplitterAndBackground(drawRect); + var foldoutRect = drawRect; + foldoutRect.xMax -= 10; + bool flag = EditorGUI.Foldout(foldoutRect, foldout, listProp.displayName, true); + if (!flag) + { + var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + GAP_WIDTH; + var sizeRect = new Rect(startX, drawRect.y + 1f, (EditorGUI.indentLevel + 1) * 15, drawRect.height - 1); + EditorGUI.IntField(sizeRect, GUIContent.none, listProp.arraySize); + DrawMenu(drawRect, menus); + } + height += headerHeight; + drawRect.y += headerHeight; + drawRect.width = rawWidth; + if (flag) + { + MakeList(ref drawRect, ref height, listProp, showOrder, showSize); + } + return flag; + } + + public static void MakeList(ref Rect drawRect, SerializedProperty listProp, bool showOrder = false, + bool showSize = true) + { + var height = 0f; + MakeList(ref drawRect, ref height, listProp, showOrder, showSize); + } + + public static void MakeList(ref Rect drawRect, ref float height, SerializedProperty listProp, + bool showOrder = false, bool showSize = true) + { + EditorGUI.indentLevel++; + var listSize = listProp.arraySize; + if (showSize) + { + var headerHeight = DrawSplitterAndBackground(drawRect); + if (showOrder) + { + var elementRect = new Rect(drawRect.x, drawRect.y, drawRect.width - ICON_WIDHT + 2, drawRect.height); + var oldColor = GUI.contentColor; + GUI.contentColor = Color.black; + GUI.contentColor = oldColor; + listSize = listProp.arraySize; + listSize = EditorGUI.IntField(elementRect, "Size", listSize); + } + else + { + listSize = EditorGUI.IntField(drawRect, "Size", listSize); + } + if (listSize < 0) listSize = 0; + drawRect.y += headerHeight; + height += headerHeight; + + if (listSize != listProp.arraySize) + { + while (listSize > listProp.arraySize) listProp.arraySize++; + while (listSize < listProp.arraySize) listProp.arraySize--; + } + } + if (listSize > 30 && !XCSettings.editorShowAllListData) + { + SerializedProperty element; + int num = listSize > 10 ? 10 : listSize; + for (int i = 0; i < num; i++) + { + element = listProp.GetArrayElementAtIndex(i); + DrawSplitterAndBackground(drawRect); + EditorGUI.PropertyField(drawRect, element, new GUIContent("Element " + i)); + drawRect.y += EditorGUI.GetPropertyHeight(element); + height += EditorGUI.GetPropertyHeight(element); + } + if (num >= 10) + { + EditorGUI.LabelField(drawRect, "..."); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + element = listProp.GetArrayElementAtIndex(listSize - 1); + DrawSplitterAndBackground(drawRect); + EditorGUI.PropertyField(drawRect, element, new GUIContent("Element " + (listSize - 1))); + drawRect.y += EditorGUI.GetPropertyHeight(element) + EditorGUIUtility.standardVerticalSpacing; + height += EditorGUI.GetPropertyHeight(element) + EditorGUIUtility.standardVerticalSpacing; + } + } + else + { + for (int i = 0; i < listProp.arraySize; i++) + { + SerializedProperty element = listProp.GetArrayElementAtIndex(i); + DrawSplitterAndBackground(drawRect); + if (showOrder) + { + var isSerie = "Serie".Equals(element.type); + var elementRect = isSerie ? + new Rect(drawRect.x, drawRect.y, drawRect.width + INDENT_WIDTH - 2 * ICON_GAP, drawRect.height) : + new Rect(drawRect.x, drawRect.y, drawRect.width - 4 * ICON_WIDHT, drawRect.height); + EditorGUI.PropertyField(elementRect, element, new GUIContent("Element " + i)); + UpDownAddDeleteButton(drawRect, listProp, i); + drawRect.y += EditorGUI.GetPropertyHeight(element); + height += EditorGUI.GetPropertyHeight(element); + } + else + { + EditorGUI.PropertyField(drawRect, element, new GUIContent("Element " + i)); + drawRect.y += EditorGUI.GetPropertyHeight(element); + height += EditorGUI.GetPropertyHeight(element); + } + } + } + EditorGUI.indentLevel--; + } + + public static void UpDownAddDeleteButton(Rect drawRect, SerializedProperty listProp, int i) + { + var temp = INDENT_WIDTH + GAP_WIDTH + ICON_GAP; + var iconRect = new Rect(drawRect.width - 4 * ICON_WIDHT + temp, drawRect.y, ICON_WIDHT, drawRect.height); + var oldColor = GUI.contentColor; + GUI.contentColor = Color.black; + if (GUI.Button(iconRect, EditorCustomStyles.iconUp, EditorCustomStyles.invisibleButton)) + { + if (i > 0) listProp.MoveArrayElement(i, i - 1); + } + iconRect = new Rect(drawRect.width - 3 * ICON_WIDHT + temp, drawRect.y, ICON_WIDHT, drawRect.height); + if (GUI.Button(iconRect, EditorCustomStyles.iconDown, EditorCustomStyles.invisibleButton)) + { + if (i < listProp.arraySize - 1) listProp.MoveArrayElement(i, i + 1); + } + iconRect = new Rect(drawRect.width - 2 * ICON_WIDHT + temp, drawRect.y, ICON_WIDHT, drawRect.height); + if (GUI.Button(iconRect, EditorCustomStyles.iconAdd, EditorCustomStyles.invisibleButton)) + { + if (i < listProp.arraySize && i >= 0) listProp.InsertArrayElementAtIndex(i); + } + iconRect = new Rect(drawRect.width - ICON_WIDHT + temp, drawRect.y, ICON_WIDHT, drawRect.height); + if (GUI.Button(iconRect, EditorCustomStyles.iconRemove, EditorCustomStyles.invisibleButton)) + { + if (i < listProp.arraySize && i >= 0) listProp.DeleteArrayElementAtIndex(i); + } + GUI.contentColor = oldColor; + } + + public static bool PropertyField(ref Rect drawRect, Dictionary<string, float> heights, string key, + SerializedProperty prop) + { + if (prop == null) return false; + EditorGUI.PropertyField(drawRect, prop, true); + var hig = EditorGUI.GetPropertyHeight(prop); + drawRect.y += hig; + heights[key] += hig; + return true; + } + + public static bool PropertyFieldWithMinValue(ref Rect drawRect, Dictionary<string, float> heights, string key, + SerializedProperty prop, float minValue) + { + if (prop == null) return false; + EditorGUI.PropertyField(drawRect, prop, true); + if (prop.propertyType == SerializedPropertyType.Float && prop.floatValue < minValue) + prop.floatValue = minValue; + if (prop.propertyType == SerializedPropertyType.Integer && prop.intValue < minValue) + prop.intValue = (int)minValue; + var hig = EditorGUI.GetPropertyHeight(prop); + drawRect.y += hig; + heights[key] += hig; + return true; + } + + public static bool PropertyFieldWithMaxValue(ref Rect drawRect, Dictionary<string, float> heights, string key, + SerializedProperty prop, float maxValue) + { + if (prop == null) return false; + EditorGUI.PropertyField(drawRect, prop, true); + if (prop.propertyType == SerializedPropertyType.Float && prop.floatValue > maxValue) + prop.floatValue = maxValue; + if (prop.propertyType == SerializedPropertyType.Integer && prop.intValue > maxValue) + prop.intValue = (int)maxValue; + var hig = EditorGUI.GetPropertyHeight(prop); + drawRect.y += hig; + heights[key] += hig; + return true; + } + + public static bool PropertyField(ref Rect drawRect, Dictionary<string, float> heights, string key, + SerializedProperty parentProp, string relativeName) + { + return PropertyField(ref drawRect, heights, key, parentProp.FindPropertyRelative(relativeName)); + } + public static bool PropertyFieldWithMinValue(ref Rect drawRect, Dictionary<string, float> heights, string key, + SerializedProperty parentProp, string relativeName, float minValue) + { + var relativeProp = parentProp.FindPropertyRelative(relativeName); + return PropertyFieldWithMinValue(ref drawRect, heights, key, relativeProp, minValue); + } + public static bool PropertyFieldWithMaxValue(ref Rect drawRect, Dictionary<string, float> heights, string key, + SerializedProperty parentProp, string relativeName, float maxValue) + { + var relativeProp = parentProp.FindPropertyRelative(relativeName); + return PropertyFieldWithMaxValue(ref drawRect, heights, key, relativeProp, maxValue); + } + + public static GUIContent GetContent(string textAndTooltip) + { + if (string.IsNullOrEmpty(textAndTooltip)) + return GUIContent.none; + + GUIContent content; + + if (!s_GUIContentCache.TryGetValue(textAndTooltip, out content)) + { + var s = textAndTooltip.Split('|'); + content = new GUIContent(s[0]); + + if (s.Length > 1 && !string.IsNullOrEmpty(s[1])) + content.tooltip = s[1]; + + s_GUIContentCache.Add(textAndTooltip, content); + } + + return content; + } + + public static void DrawSplitter() + { + var rect = GUILayoutUtility.GetRect(1f, 1f); + rect.xMin = 0f; + rect.width += 4f; + DrawSplitter(rect); + } + public static void DrawSplitter(Rect rect) + { + if (Event.current.type != EventType.Repaint) + return; + EditorGUI.DrawRect(rect, EditorCustomStyles.splitter); + } + + public static float DrawSplitterAndBackground(Rect drawRect, bool drawBackground = false) + { + float defaultWidth = drawRect.width; + float defaultX = drawRect.x; + + var splitRect = drawRect; + splitRect.y = drawRect.y; + splitRect.x = EditorGUI.indentLevel * INDENT_WIDTH + 4; + splitRect.xMax = drawRect.xMax; + splitRect.height = 1f; + + DrawSplitter(splitRect); + + if (drawBackground) + { + var bgRect = drawRect; + bgRect.y = drawRect.y; + bgRect.x -= 10 - EditorGUI.indentLevel * INDENT_WIDTH; + bgRect.xMax = drawRect.xMax; + bgRect.height = HEADER_HEIGHT + (EditorGUI.indentLevel < 1 ? 2 : 0); + EditorGUI.DrawRect(bgRect, EditorCustomStyles.headerBackground); + } + return HEADER_HEIGHT; + } + + public static bool DrawHeader(string title, bool state, bool drawBackground, SerializedProperty activeField, + Action<Rect> drawCallback, params HeaderMenuInfo[] menus) + { + var rect = GUILayoutUtility.GetRect(1f, HEADER_HEIGHT); + var labelRect = DrawHeaderInternal(rect, title, ref state, drawBackground, activeField); + DrawMenu(rect, menus); + if (drawCallback != null) + { + drawCallback(rect); + } + var e = Event.current; + if (e.type == EventType.MouseDown) + { + if (labelRect.Contains(e.mousePosition)) + { + if (e.button == 0) + { + state = !state; + e.Use(); + } + } + } + return state; + } + + public static bool DrawSerieDataHeader(string title, bool state, bool drawBackground, SerializedProperty activeField, + HeaderCallbackContext context, Action<Rect, HeaderCallbackContext> drawCallback, params HeaderMenuInfo[] menus) + { + var rect = GUILayoutUtility.GetRect(1f, HEADER_HEIGHT); + var labelRect = DrawHeaderInternal(rect, title, ref state, drawBackground, activeField); + DrawMenu(rect, menus); + if (drawCallback != null) + { + drawCallback(rect, context); + } + var e = Event.current; + if (e.type == EventType.MouseDown) + { + if (labelRect.Contains(e.mousePosition)) + { + if (e.button == 0) + { + state = !state; + e.Use(); + } + } + } + return state; + } + + internal static bool DrawHeader(string title, bool state, bool drawBackground, SerializedProperty activeField, + Action<Rect> drawCallback, List<HeaderMenuInfo> menus) + { + var rect = GUILayoutUtility.GetRect(1f, HEADER_HEIGHT); + var labelRect = DrawHeaderInternal(rect, title, ref state, drawBackground, activeField); + DrawMenu(rect, menus); + if (drawCallback != null) + { + drawCallback(rect); + } + var e = Event.current; + if (e.type == EventType.MouseDown) + { + if (labelRect.Contains(e.mousePosition)) + { + if (e.button == 0) + { + state = !state; + e.Use(); + } + } + } + return state; + } + + private static Rect DrawHeaderInternal(Rect rect, string title, ref bool state, bool drawBackground, SerializedProperty activeField) + { + var splitRect = rect; + splitRect.x = EditorGUI.indentLevel * INDENT_WIDTH + 4; + splitRect.xMax = rect.xMax; + splitRect.height = 1f; + + var backgroundRect = rect; + backgroundRect.x = splitRect.x; + backgroundRect.xMax = rect.xMax; + + var labelRect = rect; + labelRect.xMin += 0f; + labelRect.xMax -= 35f; + + var foldoutRect = rect; + //foldoutRect.x -= 12f - EditorGUI.indentLevel * INDENT_WIDTH ; + foldoutRect.x = rect.x - FOLDOUT_WIDTH + EditorGUI.indentLevel * INDENT_WIDTH + DIFF_WIDTH; + foldoutRect.y += 1f; + foldoutRect.width = FOLDOUT_WIDTH; + foldoutRect.height = FOLDOUT_WIDTH; + + DrawSplitter(splitRect); + if (drawBackground) + EditorGUI.DrawRect(backgroundRect, EditorCustomStyles.headerBackground); + if (!string.IsNullOrEmpty(title)) + EditorGUI.LabelField(labelRect, GetContent(title)); + state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout); + if (activeField != null) + { + var toggleRect = backgroundRect; + toggleRect.x = rect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * INDENT_WIDTH + GAP_WIDTH; + toggleRect.y += 1f; + toggleRect.width = 13f; + toggleRect.height = 13f; + activeField.boolValue = GUI.Toggle(toggleRect, activeField.boolValue, GUIContent.none); + } + return labelRect; + } + + internal static bool DrawHeader(string title, SerializedProperty group, SerializedProperty activeField, + Action resetAction, Action removeAction, Action docAction) + { + if (group == null) return false; + group.isExpanded = DrawHeader(title, group.isExpanded, false, activeField, null, + new HeaderMenuInfo("Reset", resetAction), + new HeaderMenuInfo("Remove", removeAction), + new HeaderMenuInfo("HelpDoc", docAction)); + return group.isExpanded; + } + + internal static bool DrawHeader(string title, SerializedProperty group, SerializedProperty activeField, + params HeaderMenuInfo[] menus) + { + group.isExpanded = DrawHeader(title, group.isExpanded, false, activeField, null, menus); + return group.isExpanded; + } + + internal static bool DrawHeader(string title, SerializedProperty group, SerializedProperty activeField, + List<HeaderMenuInfo> menus) + { + group.isExpanded = DrawHeader(title, group.isExpanded, false, activeField, null, menus); + return group.isExpanded; + } + + internal static void DrawMenu(Rect parentRect, params HeaderMenuInfo[] menus) + { + if (menus == null || menus.Length <= 0) return; + var menuIcon = EditorCustomStyles.paneOptionsIcon; + var menuRect = new Rect(parentRect.xMax - menuIcon.width, parentRect.y + 2f, + menuIcon.width, menuIcon.height); + GUI.DrawTexture(menuRect, menuIcon); + var e = Event.current; + if (e.type == EventType.MouseDown) + { + if (menuRect.Contains(e.mousePosition)) + { + ShowHeaderContextMenu(new Vector2(menuRect.x, menuRect.yMax), menus); + e.Use(); + } + else if (parentRect.Contains(e.mousePosition)) + { + if (e.button != 0) + { + ShowHeaderContextMenu(e.mousePosition, menus); + e.Use(); + } + } + } + } + + internal static void DrawMenu(Rect parentRect, List<HeaderMenuInfo> menus) + { + if (menus == null || menus.Count <= 0) return; + var menuIcon = EditorCustomStyles.paneOptionsIcon; + var menuRect = new Rect(parentRect.xMax - menuIcon.width, parentRect.y + 2f, + menuIcon.width, menuIcon.height); + GUI.DrawTexture(menuRect, menuIcon); + var e = Event.current; + if (e.type == EventType.MouseDown) + { + if (menuRect.Contains(e.mousePosition)) + { + ShowHeaderContextMenu(new Vector2(menuRect.x, menuRect.yMax), menus); + e.Use(); + } + else if (parentRect.Contains(e.mousePosition)) + { + if (e.button != 0) + { + ShowHeaderContextMenu(e.mousePosition, menus); + e.Use(); + } + } + } + } + + static void ShowHeaderContextMenu(Vector2 position, params HeaderMenuInfo[] menus) + { + if (menus == null || menus.Length <= 0) return; + var menu = new GenericMenu(); + foreach (var info in menus) + { + if (info.enable) + menu.AddItem(GetContent(info.name), false, () => info.action()); + else + menu.AddDisabledItem(GetContent(info.name)); + } + menu.DropDown(new Rect(position, Vector2.zero)); + } + static void ShowHeaderContextMenu(Vector2 position, List<HeaderMenuInfo> menus) + { + if (menus == null || menus.Count <= 0) return; + var menu = new GenericMenu(); + foreach (var info in menus) + { + if (info.enable) + menu.AddItem(GetContent(info.name), false, () => info.action()); + else + menu.AddDisabledItem(GetContent(info.name)); + } + menu.DropDown(new Rect(position, Vector2.zero)); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs.meta b/Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs.meta new file mode 100644 index 0000000..570f008 --- /dev/null +++ b/Assets/XCharts/Editor/Utilities/ChartEditorHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd22466b776d93c4cb0b252ee510cc7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Utilities/EditorStyles.cs b/Assets/XCharts/Editor/Utilities/EditorStyles.cs new file mode 100644 index 0000000..602a269 --- /dev/null +++ b/Assets/XCharts/Editor/Utilities/EditorStyles.cs @@ -0,0 +1,32 @@ +using UnityEditor; +using UnityEngine; + +namespace XCharts.Editor +{ + public class EditorCustomStyles + { + static readonly Color splitterDark = new Color(0.12f, 0.12f, 0.12f, 0.5f); + static readonly Color splitterLight = new Color(0.6f, 0.6f, 0.6f, 0.5f); + static readonly Texture2D paneOptionsIconDark = (Texture2D) EditorGUIUtility.Load("Builtin Skins/DarkSkin/Images/pane options.png"); + static readonly Texture2D paneOptionsIconLight = (Texture2D) EditorGUIUtility.Load("Builtin Skins/LightSkin/Images/pane options.png"); + static readonly Color headerBackgroundDark = new Color(0.1f, 0.1f, 0.1f, 0.2f); + static readonly Color headerBackgroundLight = new Color(1f, 1f, 1f, 0.2f); + + public static readonly GUIStyle headerStyle = UnityEditor.EditorStyles.boldLabel; + public static readonly GUIStyle foldoutStyle = new GUIStyle(UnityEditor.EditorStyles.foldout) + { + font = headerStyle.font, + fontStyle = headerStyle.fontStyle, + }; + public static readonly GUIContent iconAdd = new GUIContent("+", "Add"); + public static readonly GUIContent iconRemove = new GUIContent("-", "Remove"); + public static readonly GUIContent iconUp = new GUIContent("↑", "Up"); + public static readonly GUIContent iconDown = new GUIContent("↓", "Down"); + public static readonly GUIStyle invisibleButton = "InvisibleButton"; + public static readonly GUIStyle smallTickbox = new GUIStyle("ShurikenToggle"); + + public static Color splitter { get { return EditorGUIUtility.isProSkin ? splitterDark : splitterLight; } } + public static Texture2D paneOptionsIcon { get { return EditorGUIUtility.isProSkin ? paneOptionsIconDark : paneOptionsIconLight; } } + public static Color headerBackground { get { return EditorGUIUtility.isProSkin ? headerBackgroundDark : headerBackgroundLight; } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Utilities/EditorStyles.cs.meta b/Assets/XCharts/Editor/Utilities/EditorStyles.cs.meta new file mode 100644 index 0000000..7e7555c --- /dev/null +++ b/Assets/XCharts/Editor/Utilities/EditorStyles.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f91656ebb897d40d49795e4701f255f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Utilities/XChartsDaemon.cs b/Assets/XCharts/Editor/Utilities/XChartsDaemon.cs new file mode 100644 index 0000000..6b3b322 --- /dev/null +++ b/Assets/XCharts/Editor/Utilities/XChartsDaemon.cs @@ -0,0 +1,90 @@ +using System.IO; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + internal static class XChartsDaemon + { + public class XChartsAssetPostprocessor : AssetPostprocessor + { + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, + string[] movedFromAssetsPaths) + { + foreach (var assetPath in importedAssets) + { + CheckAddedAsset(assetPath); + } + foreach (var assetPath in deletedAssets) + { + CheckDeletedAsset(assetPath); + } + } + } + + public static void CheckAddedAsset(string assetPath) + { + var fileName = Path.GetFileName(assetPath); + if (fileName.Equals("XCSettings.asset")) + { + CheckAsmdef(); + XCThemeMgr.ReloadThemeList(); + } + else if (IsThemeAsset(assetPath)) + { + var theme = AssetDatabase.LoadAssetAtPath<Theme>(assetPath); + if (XCSettings.AddCustomTheme(theme)) + { + XCThemeMgr.ReloadThemeList(); + } + } + } + + public static void CheckAsmdef() + { +#if UNITY_2017_1_OR_NEWER +#if dUI_TextMeshPro + XChartsEditor.CheckAsmdefTmpReference(true); +#else + XChartsEditor.CheckAsmdefTmpReference(false); +#endif +#elif UNITY_2019_1_OR_NEWER +#if INPUT_SYSTEM_ENABLED + XChartsEditor.CheckAsmdefInputSystemReference(true); +#else + XChartsEditor.CheckAsmdefInputSystemReference(false); +#endif +#endif + } + + public static void CheckDeletedAsset(string assetPath) + { + if (!IsThemeAsset(assetPath)) return; + if (XCSettings.Instance == null) return; + var themes = XCSettings.customThemes; + var changed = false; + + for (int i = themes.Count - 1; i >= 0; i--) + { + if (themes[i] == null) + { + themes.RemoveAt(i); + changed = true; + } + } + if (changed) + { + XCThemeMgr.ReloadThemeList(); + } + } + + private static bool IsThemeAsset(string assetPath) + { + if (!assetPath.EndsWith(".asset")) return false; + var assetName = Path.GetFileNameWithoutExtension(assetPath); + if (!assetName.StartsWith(XCSettings.THEME_ASSET_NAME_PREFIX)) return false; + return true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Utilities/XChartsDaemon.cs.meta b/Assets/XCharts/Editor/Utilities/XChartsDaemon.cs.meta new file mode 100644 index 0000000..3841cc5 --- /dev/null +++ b/Assets/XCharts/Editor/Utilities/XChartsDaemon.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 036a714dab7744d76849114f5bcf59a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows.meta b/Assets/XCharts/Editor/Windows.meta new file mode 100644 index 0000000..6e597ab --- /dev/null +++ b/Assets/XCharts/Editor/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac8865193d4f548d2aaf66163c4192d9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs b/Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs new file mode 100644 index 0000000..d7e47f0 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + public class PraseExternalDataEditor : UnityEditor.EditorWindow + { + [SerializeField] private int m_DataDimension = 1; + [SerializeField] private double m_DefaultYValue = 0; + private static BaseChart s_Chart; + private static Serie s_Serie; + private static Axis s_Axis; + private static bool s_LinksData; + private static PraseExternalDataEditor window; + private static string inputJsonText = ""; + + public static void ShowWindow() + { + window = GetWindow<PraseExternalDataEditor>(); + window.titleContent = new GUIContent("PraseExternalData"); + window.minSize = new Vector2(450, 550); + window.Focus(); + window.Show(); + } + + public static void UpdateData(BaseChart chart, Serie serie, Axis axis, bool linksData) + { + s_Chart = chart; + s_Serie = serie; + s_Axis = axis; + s_LinksData = linksData; + inputJsonText = UnityEngine.GUIUtility.systemCopyBuffer; + } + + void OnInspectorUpdate() + { + Repaint(); + } + + private void OnGUI() + { + if (s_Chart == null) + { + Close(); + return; + } + EditorGUILayout.LabelField("Input external data (echarts data):"); + m_DataDimension = EditorGUILayout.IntField("Data Dimension", m_DataDimension); + if (m_DataDimension < 1) + m_DataDimension = 1; + else if (m_DataDimension == 2) + m_DefaultYValue = EditorGUILayout.DoubleField("Default Y Value", m_DefaultYValue); + inputJsonText = EditorGUILayout.TextArea(inputJsonText, GUILayout.Height(400)); + if (GUILayout.Button("Try Add")) + { + if (s_Serie != null) + { + if (!ParseArrayData(s_Serie, inputJsonText)) + { + if (ParseJsonData(s_Serie, inputJsonText)) + inputJsonText = ""; + } + else + { + inputJsonText = ""; + } + } + else if (s_Axis != null) + { + if (!ParseArrayData(s_Axis, inputJsonText)) + { + if (ParseJsonData(s_Axis, inputJsonText)) + inputJsonText = ""; + } + else + { + inputJsonText = ""; + } + } + } + } + + private bool ParseArrayData(Axis axis, string arrayData) + { + arrayData = arrayData.Trim(); + if (!arrayData.StartsWith("data: Array")) return false; + axis.data.Clear(); + var list = arrayData.Split('\n'); + for (int i = 1; i < list.Length; i++) + { + var temp = list[i].Split(':'); + if (temp.Length == 2) + { + var category = temp[1].Replace("\"", "").Trim(); + axis.data.Add(category); + } + } + axis.SetAllDirty(); + return true; + } + + private bool ParseArrayData(Serie serie, string arrayData) + { + arrayData = arrayData.Trim(); + if (!arrayData.StartsWith("data: Array")) return false; + if (s_LinksData) serie.ClearLinks(); + else serie.ClearData(); + var list = arrayData.Split('\n'); + for (int i = 1; i < list.Length; i++) + { + var temp = list[i].Split(':'); + if (temp.Length == 2) + { + var strvalue = temp[1].Replace("\"", "").Trim(); + var value = 0d; + var flag = double.TryParse(strvalue, out value); + if (flag) + { + serie.AddYData(value); + } + } + } + serie.SetAllDirty(); + return true; + } + + private bool ParseJsonData(Axis axis, string jsonData) + { + if (!CheckJsonData(ref jsonData)) return false; + axis.data.Clear(); + string[] datas = jsonData.Split(','); + for (int i = 0; i < datas.Length; i++) + { + var txt = datas[i].Trim().Replace("[", "").Replace("]", ""); + var value = 0d; + if (!double.TryParse(txt, out value)) + axis.data.Add(txt.Replace("\'", "").Replace("\"", "")); + } + axis.SetAllDirty(); + return true; + } + + /// <summary> + /// 从json中导入数据 + /// </summary> + /// <param name="jsonData"></param> + private bool ParseJsonData(Serie serie, string jsonData) + { + if (!CheckJsonData(ref jsonData)) return false; + if (s_LinksData) serie.ClearLinks(); + else serie.ClearData(); + if (jsonData.IndexOf("],") > -1 || jsonData.IndexOf("] ,") > -1) + { + string[] datas = jsonData.Split(new string[] { "],", "] ," }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < datas.Length; i++) + { + var data = datas[i].Replace("[", "").Replace("]", "").Split(new char[] { '[', ',' }, StringSplitOptions.RemoveEmptyEntries); + var serieData = new SerieData(); + double value = 0; + if (data.Length == 2 && !double.TryParse(data[0], out value)) + { + double.TryParse(data[1], out value); + if (m_DataDimension == 2) + serieData.data = new List<double>() { i, m_DefaultYValue, value }; + else + serieData.data = new List<double>() { i, value }; + serieData.name = data[0].Replace("\"", "").Trim(); + } + else + { + for (int j = 0; j < data.Length; j++) + { + var txt = data[j].Trim().Replace("]", ""); + var flag = double.TryParse(txt, out value); + if (flag) + { + serieData.data.Add(value); + } + else serieData.name = txt.Replace("\"", "").Trim(); + } + } + serie.AddSerieData(serieData); + } + } + else if (jsonData.IndexOf("value") > -1 && jsonData.IndexOf("name") > -1) + { + string[] datas = jsonData.Split(new string[] { "},", "} ,", "}" }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < datas.Length; i++) + { + var arr = datas[i].Replace("{", "").Split(','); + var serieData = new SerieData(); + foreach (var a in arr) + { + if (a.StartsWith("value:")) + { + double value = double.Parse(a.Substring(6, a.Length - 6)); + if (m_DataDimension == 2) + serieData.data = new List<double>() { i, m_DefaultYValue, value }; + else + serieData.data = new List<double>() { i, value }; + } + else if (a.StartsWith("name:")) + { + string name = a.Substring(6, a.Length - 6 - 1); + serieData.name = name; + } + else if (a.StartsWith("selected:")) + { + string selected = a.Substring(9, a.Length - 9); + serieData.selected = bool.Parse(selected); + } + } + serie.AddSerieData(serieData); + } + } + else + { + string[] datas = jsonData.Split(','); + for (int i = 0; i < datas.Length; i++) + { + double value; + var flag = double.TryParse(datas[i].Trim(), out value); + if (flag) + { + var serieData = new SerieData(); + if (m_DataDimension == 2) + serieData.data = new List<double>() { i, m_DefaultYValue, value }; + else + serieData.data = new List<double>() { i, value }; + serie.AddSerieData(serieData); + } + } + } + serie.SetAllDirty(); + return true; + } + + private static bool CheckJsonData(ref string jsonData) + { + if (string.IsNullOrEmpty(jsonData)) return false; + jsonData = jsonData.Replace("\r\n", ""); + jsonData = jsonData.Replace(" ", ""); + jsonData = jsonData.Replace("\n", ""); + int startIndex = jsonData.IndexOf("["); + int endIndex = jsonData.LastIndexOf("]"); + if (startIndex == -1 || endIndex == -1) + { + Debug.LogError("json data need include in [ ]"); + return false; + } + jsonData = jsonData.Substring(startIndex + 1, endIndex - startIndex - 1); + return true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs.meta b/Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs.meta new file mode 100644 index 0000000..045abf1 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/PraseExternalDataEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b41bbccd77d88460aba5bcf81b4920ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/XCSettingsEditor.cs b/Assets/XCharts/Editor/Windows/XCSettingsEditor.cs new file mode 100644 index 0000000..c03ef5b --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XCSettingsEditor.cs @@ -0,0 +1,59 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomEditor(typeof(XCSettings))] + public class XCSettingsEditor : UnityEditor.Editor + { + internal class Styles + { + public static readonly GUIContent defaultFontAssetLabel = new GUIContent("Default Font Asset", "The Font Asset that will be assigned by default to newly created text objects when no Font Asset is specified."); + public static readonly GUIContent defaultFontAssetPathLabel = new GUIContent("Path: Resources/", "The relative path to a Resources folder where the Font Assets and Material Presets are located.\nExample \"Fonts & Materials/\""); + } + } + +#if UNITY_2018_3_OR_NEWER + class XCResourceImporterProvider : SettingsProvider + { + XCResourcesImporter m_ResourceImporter; + + public XCResourceImporterProvider() : base("Project/XCharts", SettingsScope.Project) + { } + + public override void OnGUI(string searchContext) + { + if (m_ResourceImporter == null) + m_ResourceImporter = new XCResourcesImporter(); + + m_ResourceImporter.OnGUI(); + } + + public override void OnDeactivate() + { + if (m_ResourceImporter != null) + m_ResourceImporter.OnDestroy(); + } + + static UnityEngine.Object GetSettings() + { + return Resources.Load<XCSettings>("XCSettings"); + } + + [SettingsProviderGroup] + static SettingsProvider[] CreateXCSettingsProvider() + { + var providers = new System.Collections.Generic.List<SettingsProvider> { new XCResourceImporterProvider() }; + if (GetSettings() != null) + { + var provider = new AssetSettingsProvider("Project/XCharts/Settings", GetSettings); + provider.PopulateSearchKeywordsFromGUIContentProperties<XCSettingsEditor.Styles>(); + providers.Add(provider); + } + + return providers.ToArray(); + } + } +#endif +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/XCSettingsEditor.cs.meta b/Assets/XCharts/Editor/Windows/XCSettingsEditor.cs.meta new file mode 100644 index 0000000..dedbd2d --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XCSettingsEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a1acb5e9cc3740aabbaaccd4ec9b8b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs b/Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs new file mode 100644 index 0000000..727fc01 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +using ADB = UnityEditor.AssetDatabase; + +namespace XCharts.Editor +{ + public partial class XChartsEditor + { + [MenuItem("XCharts/BarChart/Baisc Column", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Baisc Column", priority = 45)] + public static void AddBarChart() + { + AddChart<BarChart>("BarChart"); + } + + [MenuItem("XCharts/BarChart/Zebra Column", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Zebra Column", priority = 45)] + public static void AddBarChart_ZebraColumn() + { + var chart = AddChart<BarChart>("BarChart", "Zebra Column"); + chart.DefaultZebraColumnChart(); + } + + [MenuItem("XCharts/BarChart/Capsule Column", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Capsule Column", priority = 45)] + public static void AddBarChart_CapsuleColumn() + { + var chart = AddChart<BarChart>("BarChart", "Capsule Column"); + chart.DefaultCapsuleColumnChart(); + } + + [MenuItem("XCharts/BarChart/Grouped Column", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Grouped Column", priority = 45)] + public static void AddBarChart_GroupedColumn() + { + var chart = AddChart<BarChart>("BarChart", "Grouped Column"); + chart.DefaultGroupedColumnChart(); + } + + [MenuItem("XCharts/BarChart/Stacked Column", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Stacked Column", priority = 45)] + public static void AddBarChart_StackedColumn() + { + var chart = AddChart<BarChart>("BarChart", "Stacked Column"); + chart.DefaultStackedColumnChart(); + } + + [MenuItem("XCharts/BarChart/Percent Column", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Percent Column", priority = 45)] + public static void AddBarChart_PercentColumn() + { + var chart = AddChart<BarChart>("BarChart", "Percent Column"); + chart.DefaultPercentColumnChart(); + } + + [MenuItem("XCharts/BarChart/Baisc Bar", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Baisc Bar", priority = 45)] + public static void AddBarChart_BasicBar() + { + var chart = AddChart<BarChart>("BarChart"); + chart.DefaultBarChart(); + } + + [MenuItem("XCharts/BarChart/Zebra Bar", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Zebra Bar", priority = 45)] + public static void AddBarChart_ZebraBar() + { + var chart = AddChart<BarChart>("BarChart", "Zebra Bar"); + chart.DefaultZebraBarChart(); + } + + [MenuItem("XCharts/BarChart/Capsule Bar", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Capsule Bar", priority = 45)] + public static void AddBarChart_CapsuleBar() + { + var chart = AddChart<BarChart>("BarChart", "Capsule Bar"); + chart.DefaultCapsuleBarChart(); + } + + [MenuItem("XCharts/BarChart/Grouped Bar", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Grouped Bar", priority = 45)] + public static void AddBarChart_GroupedBar() + { + var chart = AddChart<BarChart>("BarChart", "Grouped Bar"); + chart.DefaultGroupedBarChart(); + } + + [MenuItem("XCharts/BarChart/Stacked Bar", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Stacked Bar", priority = 45)] + public static void AddBarChart_StackedBar() + { + var chart = AddChart<BarChart>("BarChart", "Stacked Bar"); + chart.DefaultStackedBarChart(); + } + + [MenuItem("XCharts/BarChart/Percent Bar", priority = 45)] + [MenuItem("GameObject/UI/XCharts/BarChart/Percent Bar", priority = 45)] + public static void AddBarChart_PercentBar() + { + var chart = AddChart<BarChart>("BarChart", "Percent Bar"); + chart.DefaultPercentBarChart(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs.meta b/Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs.meta new file mode 100644 index 0000000..b0b5959 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.BarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56ff28653cd1148dc857e65c4440cf74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs b/Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs new file mode 100644 index 0000000..4405df2 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +using ADB = UnityEditor.AssetDatabase; + +namespace XCharts.Editor +{ + public partial class XChartsEditor + { + [MenuItem("XCharts/LineChart/Basic Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Basic Line", priority = 44)] + public static void AddLineChart() + { + AddChart<LineChart>("LineChart"); + } + + [MenuItem("XCharts/LineChart/Area Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Area Line", priority = 44)] + public static void AddLineChart_Area() + { + var chart = AddChart<LineChart>("LineChart_Area", "Area Line"); + chart.DefaultAreaLineChart(); + } + + [MenuItem("XCharts/LineChart/Smooth Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Smooth Line", priority = 44)] + public static void AddLineChart_Smooth() + { + var chart = AddChart<LineChart>("LineChart_Smooth", "Smooth Line"); + chart.DefaultSmoothLineChart(); + } + + [MenuItem("XCharts/LineChart/Smooth Area", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Smooth Area Line", priority = 44)] + public static void AddLineChart_SmoothArea() + { + var chart = AddChart<LineChart>("LineChart_SmoothArea", "Smooth Area Line"); + chart.DefaultSmoothAreaLineChart(); + } + + [MenuItem("XCharts/LineChart/Stack Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Stack Line", priority = 44)] + public static void AddLineChart_Stack() + { + var chart = AddChart<LineChart>("LineChart_Stack", "Stack Line"); + chart.DefaultStackLineChart(); + } + + [MenuItem("XCharts/LineChart/Stack Area Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Stack Area Line", priority = 44)] + public static void AddLineChart_StackArea() + { + var chart = AddChart<LineChart>("LineChart_StackArea", "Stack Area Line"); + chart.DefaultStackAreaLineChart(); + } + + [MenuItem("XCharts/LineChart/Step Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Step Line", priority = 44)] + public static void AddLineChart_Step() + { + var chart = AddChart<LineChart>("LineChart_Step", "Step Line"); + chart.DefaultStepLineChart(); + } + + [MenuItem("XCharts/LineChart/Dashed Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Dashed Line", priority = 44)] + public static void AddLineChart_Dash() + { + var chart = AddChart<LineChart>("LineChart_Dashed", "Dashed Line"); + chart.DefaultDashLineChart(); + } + + [MenuItem("XCharts/LineChart/Time Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Time Line", priority = 44)] + public static void AddLineChart_Time() + { + var chart = AddChart<LineChart>("LineChart_Time", "Time Line"); + chart.DefaultTimeLineChart(); + } + + [MenuItem("XCharts/LineChart/Log Line", priority = 44)] + [MenuItem("GameObject/UI/XCharts/LineChart/Log Line", priority = 44)] + public static void AddLineChart_Log() + { + var chart = AddChart<LineChart>("LineChart_Log", "Log Line"); + chart.DefaultLogLineChart(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs.meta b/Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs.meta new file mode 100644 index 0000000..6501302 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.LineChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5036b2e279753473a906aaaf8b369d44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs b/Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs new file mode 100644 index 0000000..b4c2403 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +using ADB = UnityEditor.AssetDatabase; + +namespace XCharts.Editor +{ + public partial class XChartsEditor + { + [MenuItem("XCharts/PieChart/Pie", priority = 46)] + [MenuItem("GameObject/UI/XCharts/PieChart/Pie", priority = 46)] + public static void AddPieChart() + { + AddChart<PieChart>("PieChart"); + } + + [MenuItem("XCharts/PieChart/Pie With Label", priority = 46)] + [MenuItem("GameObject/UI/XCharts/PieChart/Pie With Label", priority = 46)] + public static void AddPieChart_WithLabel() + { + var chart = AddChart<PieChart>("PieChart"); + chart.DefaultLabelPieChart(); + } + + [MenuItem("XCharts/PieChart/Donut", priority = 46)] + [MenuItem("GameObject/UI/XCharts/PieChart/Donut", priority = 46)] + public static void AddPieChart_Donut() + { + var chart = AddChart<PieChart>("PieChart"); + chart.DefaultDonutPieChart(); + } + + [MenuItem("XCharts/PieChart/Donut With Label", priority = 46)] + [MenuItem("GameObject/UI/XCharts/PieChart/Donut With Label", priority = 46)] + public static void AddPieChart_DonutWithLabel() + { + var chart = AddChart<PieChart>("PieChart"); + chart.DefaultLabelDonutPieChart(); + } + + [MenuItem("XCharts/PieChart/Radius Rose", priority = 46)] + [MenuItem("GameObject/UI/XCharts/PieChart/Radius Rose", priority = 46)] + public static void AddPieChart_RadiusRose() + { + var chart = AddChart<PieChart>("PieChart"); + chart.DefaultRadiusRosePieChart(); + } + + [MenuItem("XCharts/PieChart/Area Rose", priority = 46)] + [MenuItem("GameObject/UI/XCharts/PieChart/Area Rose", priority = 46)] + public static void AddPieChart_AreaRose() + { + var chart = AddChart<PieChart>("PieChart"); + chart.DefaultAreaRosePieChart(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs.meta b/Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs.meta new file mode 100644 index 0000000..7051a18 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.PieChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 75bcae53ea72749418444c00f6281a3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs b/Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs new file mode 100644 index 0000000..70cb228 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +using ADB = UnityEditor.AssetDatabase; + +namespace XCharts.Editor +{ + public partial class XChartsEditor + { + [MenuItem("XCharts/PolarChart/Line", priority = 54)] + [MenuItem("GameObject/UI/XCharts/PolarChart/Line", priority = 54)] + public static void PolarChart() + { + AddChart<PolarChart>("PolarChart"); + } + + [MenuItem("XCharts/PolarChart/Radial Bar", priority = 54)] + [MenuItem("GameObject/UI/XCharts/PolarChart/Radial Bar", priority = 54)] + public static void PolarChart_RadialBar() + { + var chart = AddChart<PolarChart>("PolarChart"); + chart.DefaultRadialBarPolarChart(); + } + + [MenuItem("XCharts/PolarChart/Tangential Bar", priority = 54)] + [MenuItem("GameObject/UI/XCharts/PolarChart/Tangential Bar", priority = 54)] + public static void PolarChart_TangentialBar() + { + var chart = AddChart<PolarChart>("PolarChart"); + chart.DefaultTangentialBarPolarChart(); + } + + [MenuItem("XCharts/PolarChart/Heatmap", priority = 54)] + [MenuItem("GameObject/UI/XCharts/PolarChart/Heatmap", priority = 54)] + public static void PolarChart_Heatmap() + { + var chart = AddChart<PolarChart>("PolarChart"); + chart.DefaultHeatmapPolarChart(); + } + + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs.meta b/Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs.meta new file mode 100644 index 0000000..8d51951 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.PolarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4cf6923e1dcc04bb4a077d53bf7b6a0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.cs b/Assets/XCharts/Editor/Windows/XChartsEditor.cs new file mode 100644 index 0000000..a4c3736 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +using ADB = UnityEditor.AssetDatabase; + + +namespace XCharts.Editor +{ + public partial class XChartsEditor : UnityEditor.Editor + { + private static Transform GetParent() + { + GameObject selectObj = Selection.activeGameObject; + if (selectObj == null) + { +#if UNITY_2023_1_OR_NEWER + var canvas = UnityEngine.Object.FindFirstObjectByType<Canvas>(); +#else + var canvas = GameObject.FindObjectOfType<Canvas>(); +#endif + if (canvas != null) return canvas.transform; + else + { + var canvasObject = new GameObject(); + canvasObject.name = "Canvas"; + canvas = canvasObject.AddComponent<Canvas>(); + canvas.renderMode = RenderMode.ScreenSpaceCamera; + var mainCamera = GameObject.FindGameObjectWithTag("MainCamera"); + canvas.worldCamera = mainCamera == null ? null : mainCamera.GetComponent<Camera>(); + canvasObject.AddComponent<CanvasScaler>(); + canvasObject.AddComponent<GraphicRaycaster>(); + if (GameObject.Find("EventSystem") == null) + { + var eventSystem = new GameObject(); + eventSystem.name = "EventSystem"; + eventSystem.AddComponent<EventSystem>(); + eventSystem.AddComponent<StandaloneInputModule>(); + } + return canvas.transform; + } + } + else + { + return selectObj.transform; + } + } + + private static string GetName(Transform parent, string name) + { + if (parent.Find(name) == null) return name; + for (int i = 1; i <= 10; i++) + { + var newName = string.Format("{0} ({1})", name, i); + if (parent.Find(newName) == null) + { + return newName; + } + } + return name; + } + + public static T AddChart<T>(string chartName, string titleName = null) where T : BaseChart + { + XCThemeMgr.CheckReloadTheme(); + var chart = AddGraph<T>(chartName); + if (!string.IsNullOrEmpty(titleName)) + { + var title = chart.GetChartComponent<Title>(); + title.text = titleName; + } + return chart; + } + + public static T AddGraph<T>(string graphName) where T : Graphic + { + var parent = GetParent(); + if (parent == null) return null; + XCThemeMgr.CheckReloadTheme(); + var obj = new GameObject(); + obj.name = GetName(parent, graphName); + obj.layer = LayerMask.NameToLayer("UI"); + var t = obj.AddComponent<T>(); + obj.transform.SetParent(parent); + obj.transform.localScale = Vector3.one; + obj.transform.localPosition = Vector3.zero; + obj.transform.localRotation = Quaternion.Euler(0, 0, 0); + var rect = obj.GetComponent<RectTransform>(); + rect.anchorMin = new Vector2(0.5f, 0.5f); + rect.anchorMax = new Vector2(0.5f, 0.5f); + rect.pivot = new Vector2(0.5f, 0.5f); + Selection.activeGameObject = obj; + EditorUtility.SetDirty(obj); + return t; + } + + [MenuItem("XCharts/EmptyChart", priority = 43)] + [MenuItem("GameObject/UI/XCharts/EmptyChart", priority = 43)] + public static void AddBaseChart() + { + AddChart<BaseChart>("EmptyChart"); + } + + [MenuItem("XCharts/RadarChart/Polygon Radar", priority = 47)] + [MenuItem("GameObject/UI/XCharts/RadarChart/Polygon Radar", priority = 47)] + public static void AddRadarChart() + { + AddChart<RadarChart>("RadarChart"); + } + + [MenuItem("XCharts/RadarChart/Cirle Radar", priority = 47)] + [MenuItem("GameObject/UI/XCharts/RadarChart/Cirle Radar", priority = 47)] + public static void AddRadarChart_CirleRadar() + { + var chart = AddChart<RadarChart>("RadarChart"); + chart.DefaultCircleRadarChart(); + } + + [MenuItem("XCharts/ScatterChart/Scatter", priority = 48)] + [MenuItem("GameObject/UI/XCharts/ScatterChart/Scatter", priority = 48)] + public static void AddScatterChart() + { + AddChart<ScatterChart>("ScatterChart"); + } + + [MenuItem("XCharts/ScatterChart/Bubble", priority = 48)] + [MenuItem("GameObject/UI/XCharts/ScatterChart/Bubble", priority = 48)] + public static void AddScatterChart_Bubble() + { + var chart = AddChart<ScatterChart>("ScatterChart"); + chart.DefaultBubbleChart(); + } + + [MenuItem("XCharts/HeatmapChart/Heatmap", priority = 49)] + [MenuItem("GameObject/UI/XCharts/HeatmapChart/Heatmap", priority = 49)] + public static void AddHeatmapChart() + { + AddChart<HeatmapChart>("HeatmapChart"); + } + + [MenuItem("XCharts/HeatmapChart/Count Heatmap", priority = 49)] + [MenuItem("GameObject/UI/XCharts/HeatmapChart/Count Heatmap", priority = 49)] + public static void AddHeatmapChart_Count() + { + var chart = AddChart<HeatmapChart>("HeatmapChart"); + chart.DefaultCountHeatmapChart(); + } + + [MenuItem("XCharts/RingChart/Ring", priority = 51)] + [MenuItem("GameObject/UI/XCharts/RingChart/Ring", priority = 51)] + public static void AddRingChart() + { + AddChart<RingChart>("RingChart"); + } + + [MenuItem("XCharts/RingChart/Multiple Ring", priority = 51)] + [MenuItem("GameObject/UI/XCharts/RingChart/Multiple Ring", priority = 51)] + public static void AddRingChart_MultiRing() + { + var chart = AddChart<RingChart>("RingChart"); + chart.DefaultMultipleRingChart(); + } + + [MenuItem("XCharts/CandlestickChart/Candlestick", priority = 54)] + [MenuItem("GameObject/UI/XCharts/CandlestickChart/Candlestick", priority = 54)] + public static void CandlestickChart() + { + AddChart<CandlestickChart>("CandlestickChart"); + } + + [MenuItem("XCharts/ParallelChart/Parallel", priority = 55)] + [MenuItem("GameObject/UI/XCharts/ParallelChart/Parallel", priority = 55)] + public static void ParallelChart() + { + AddChart<ParallelChart>("ParallelChart"); + } + + [MenuItem("XCharts/SimplifiedChart/Line", priority = 56)] + [MenuItem("GameObject/UI/XCharts/SimplifiedChart/Line", priority = 56)] + public static void SimplifiedLineChart() + { + AddChart<SimplifiedLineChart>("SimplifiedLineChart"); + } + + [MenuItem("XCharts/SimplifiedChart/Bar", priority = 57)] + [MenuItem("GameObject/UI/XCharts/SimplifiedChart/Bar", priority = 57)] + public static void SimplifiedBarChart() + { + AddChart<SimplifiedBarChart>("SimplifiedBarChart"); + } + + [MenuItem("XCharts/SimplifiedChart/Candlestick", priority = 58)] + [MenuItem("GameObject/UI/XCharts/SimplifiedChart/Candlestick", priority = 58)] + public static void SimplifiedCandlestickChart() + { + AddChart<SimplifiedCandlestickChart>("SimplifiedCandlestickChart"); + } + + [MenuItem("XCharts/Themes Reload")] + public static void ReloadTheme() + { + XCThemeMgr.ReloadThemeList(); + } + + #region Text mesh pro support +#if UNITY_2017_1_OR_NEWER + const string SYMBOL_TMP = "dUI_TextMeshPro"; + const string ASMDEF_TMP = "Unity.TextMeshPro"; + +#if !dUI_TextMeshPro + [MenuItem("XCharts/TextMeshPro Enable")] +#endif + public static void EnableTextMeshPro() + { + if (!IsSpecifyAssemblyExist(ASMDEF_TMP)) + { + Debug.LogError("TextMeshPro is not in the project, please import TextMeshPro package first."); + return; + } + if (EditorUtility.DisplayDialog("TextMeshPro Enable", "TextMeshPro is disabled, do you want to enable it?", "Yes", "Cancel")) + { + DefineSymbolsUtil.AddGlobalDefine(SYMBOL_TMP); + XChartsMgr.RemoveAllChartObject(); + CheckAsmdefTmpReference(true); + } + } + +#if dUI_TextMeshPro + [MenuItem("XCharts/TextMeshPro Disable")] +#endif + public static void DisableTextMeshPro() + { + if (EditorUtility.DisplayDialog("TextMeshPro Disable", "TextMeshPro is enabled, do you want to disable it?", "Yes", "Cancel")) + { + CheckAsmdefTmpReference(false); + DefineSymbolsUtil.RemoveGlobalDefine(SYMBOL_TMP); + XChartsMgr.RemoveAllChartObject(); + } + } + + public static void CheckAsmdefTmpReference(bool enable) + { + if (enable) + { + InsertSpecifyReferenceIntoAssembly(Platform.Editor, ASMDEF_TMP); + InsertSpecifyReferenceIntoAssembly(Platform.Runtime, ASMDEF_TMP); + } + else + { + RemoveSpecifyReferenceFromAssembly(Platform.Editor, ASMDEF_TMP); + RemoveSpecifyReferenceFromAssembly(Platform.Runtime, ASMDEF_TMP); + } + } +#endif + #endregion + + #region InputSystem Support +#if UNITY_2019_1_OR_NEWER + //As InputSystem is released in 2019.1+ ,when unity version is 2019.1+ , enable InputSystem Support + const string SYMBOL_I_S = "INPUT_SYSTEM_ENABLED"; + const string ASMDEF_I_S = "Unity.InputSystem"; + +#if !INPUT_SYSTEM_ENABLED + [MenuItem("XCharts/InputSystem Enable")] +#endif + public static void EnableInputSystem() + { + if (!IsSpecifyAssemblyExist(ASMDEF_I_S)) + { + Debug.LogError("InputSystem is not in the project, please import InputSystem package first."); + return; + } + if (EditorUtility.DisplayDialog("InputSystem Enable", "InputSystem is disabled, do you want to enable it?", "Yes", "Cancel")) + { + CheckAsmdefInputSystemReference(true); + DefineSymbolsUtil.AddGlobalDefine(SYMBOL_I_S); + } + } + +#if INPUT_SYSTEM_ENABLED + [MenuItem("XCharts/InputSystem Disable")] +#endif + public static void DisableInputSystem() + { + if (EditorUtility.DisplayDialog("InputSystem Disable", "InputSystem is enabled, do you want to disable it?", "Yes", "Cancel")) + { + CheckAsmdefInputSystemReference(false); + DefineSymbolsUtil.RemoveGlobalDefine(SYMBOL_I_S); + } + } + + public static void CheckAsmdefInputSystemReference(bool enable) + { + if(enable) + { + InsertSpecifyReferenceIntoAssembly(Platform.Editor, ASMDEF_I_S); + InsertSpecifyReferenceIntoAssembly(Platform.Runtime, ASMDEF_I_S); + } + else + { + RemoveSpecifyReferenceFromAssembly(Platform.Editor, ASMDEF_I_S); + RemoveSpecifyReferenceFromAssembly(Platform.Runtime, ASMDEF_I_S); + } + } +#endif + #endregion + + #region Assistant members +#if UNITY_2017_1_OR_NEWER + // as text mesh pro is released in 2017.1, so we may use these function and types in 2017.1 or later + private static void InsertSpecifyReferenceIntoAssembly(Platform platform, string reference) + { + var file = GetPackageAssemblyDefinitionPath(platform); + var content = File.ReadAllText(file); + var data = new AssemblyDefinitionData(); + EditorJsonUtility.FromJsonOverwrite(content, data); + if (!data.references.Contains(reference)) + { + data.references.Add(reference); + var json = EditorJsonUtility.ToJson(data, true); + File.WriteAllText(file, json); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } + + private static void RemoveSpecifyReferenceFromAssembly(Platform platform, string reference) + { + var file = GetPackageAssemblyDefinitionPath(platform); + var content = File.ReadAllText(file); + var data = new AssemblyDefinitionData(); + EditorJsonUtility.FromJsonOverwrite(content, data); + if (data.references.Contains(reference)) + { + data.references.Remove(reference); + var json = EditorJsonUtility.ToJson(data, true); + File.WriteAllText(file, json); + } + } + + public enum Platform { Editor, Runtime } + public static string GetPackageAssemblyDefinitionPath(Platform platform) + { + var p = platform == Platform.Editor ? "Editor" : "Runtime"; + var f = "XCharts." + p + ".asmdef"; + var sub = Path.Combine(p, f); + string packagePath = Path.GetFullPath("Packages/com.monitor1394.xcharts"); + if (!Directory.Exists(packagePath)) + { + packagePath = ADB.FindAssets("t:Script") + .Where(v => Path.GetFileNameWithoutExtension(ADB.GUIDToAssetPath(v)) == "XChartsMgr") + .Select(id => ADB.GUIDToAssetPath(id)) + .FirstOrDefault(); + packagePath = Path.GetDirectoryName(packagePath); + packagePath = packagePath.Substring(0, packagePath.LastIndexOf("Runtime")); + } + return Path.Combine(packagePath, sub); + } + + public static bool IsSpecifyAssemblyExist(string name) + { +#if UNITY_2018_1_OR_NEWER + foreach (var assembly in UnityEditor.Compilation.CompilationPipeline.GetAssemblies(UnityEditor.Compilation.AssembliesType.Player)) + { + if (assembly.name.Equals(name)) return true; + } +#elif UNITY_2017_3_OR_NEWER + foreach (var assembly in UnityEditor.Compilation.CompilationPipeline.GetAssemblies()) + { + if (assembly.name.Equals(name)) return true; + } +#endif + return false; + } + + [Serializable] + class AssemblyDefinitionData + { +#pragma warning disable 649 + public string name; + public List<string> references; + public List<string> includePlatforms; + public List<string> excludePlatforms; + public bool allowUnsafeCode; + public bool overrideReferences; + public List<string> precompiledReferences; + public bool autoReferenced; + public List<string> defineConstraints; + public List<string> versionDefines; + public bool noEngineReferences; +#pragma warning restore 649 + } +#endif + #endregion + + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/Windows/XChartsEditor.cs.meta b/Assets/XCharts/Editor/Windows/XChartsEditor.cs.meta new file mode 100644 index 0000000..00e4a88 --- /dev/null +++ b/Assets/XCharts/Editor/Windows/XChartsEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 941beb76fdaa64a27a2df6561893157e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/XCharts.Editor.asmdef b/Assets/XCharts/Editor/XCharts.Editor.asmdef new file mode 100644 index 0000000..4371b9b --- /dev/null +++ b/Assets/XCharts/Editor/XCharts.Editor.asmdef @@ -0,0 +1,17 @@ +{ + "name": "XCharts.Editor", + "references": [ + "XCharts.Runtime" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/XCharts.Editor.asmdef.meta b/Assets/XCharts/Editor/XCharts.Editor.asmdef.meta new file mode 100644 index 0000000..a0fad90 --- /dev/null +++ b/Assets/XCharts/Editor/XCharts.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9639efc34ea6e4056830a23233b99b16 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples.meta b/Assets/XCharts/Examples.meta new file mode 100644 index 0000000..574f1ff --- /dev/null +++ b/Assets/XCharts/Examples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0cfb5d7eeb260491b9d2545237eab7ce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example00_CheatSheet.cs b/Assets/XCharts/Examples/Example00_CheatSheet.cs new file mode 100644 index 0000000..defa7e7 --- /dev/null +++ b/Assets/XCharts/Examples/Example00_CheatSheet.cs @@ -0,0 +1,313 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [RequireComponent(typeof(LineChart))] + public class Example00_CheatSheet : MonoBehaviour + { + private LineChart chart; + private float speed = 100f; + + private void OnEnable() + { + StartCoroutine(CheatSheet()); + } + + IEnumerator CheatSheet() + { + StartCoroutine(InitChart()); + while (true) + { + StartCoroutine(ComponentTitle()); + yield return new WaitForSeconds(2); + StartCoroutine(ComponentAxis()); + yield return new WaitForSeconds(2); + StartCoroutine(ComponentGrid()); + yield return new WaitForSeconds(2); + StartCoroutine(ComponentSerie()); + yield return new WaitForSeconds(4); + StartCoroutine(ComponentLegend()); + yield return new WaitForSeconds(4); + StartCoroutine(ComponentTheme()); + yield return new WaitForSeconds(4); + StartCoroutine(ComponentDataZoom()); + yield return new WaitForSeconds(5); + StartCoroutine(ComponentVisualMap()); + yield return new WaitForSeconds(3); + } + } + + IEnumerator InitChart() + { + chart = gameObject.GetComponent<LineChart>(); + + chart.EnsureChartComponent<Title>().show = true; + chart.EnsureChartComponent<Title>().text = "术语解析-组件"; + + var grid = chart.EnsureChartComponent<GridCoord>(); + grid.bottom = 30; + grid.right = 30; + grid.left = 50; + grid.top = 80; + + chart.RemoveChartComponent<VisualMap>(); + + chart.RemoveData(); + + chart.AddSerie<Bar>("Bar"); + chart.AddSerie<Line>("Line"); + + for (int i = 0; i < 8; i++) + { + chart.AddXAxisData("x" + (i + 1)); + chart.AddData(0, Random.Range(10, 100)); + chart.AddData(1, Random.Range(30, 100)); + } + yield return null; + } + + IEnumerator ComponentTitle() + { + chart.EnsureChartComponent<Title>().text = "术语解析 - 组件"; + chart.EnsureChartComponent<Title>().subText = "Title 标题:可指定主标题和子标题"; + chart.EnsureChartComponent<XAxis>().show = true; + chart.EnsureChartComponent<YAxis>().show = true; + chart.EnsureChartComponent<Legend>().show = false; + chart.series[0].show = false; + chart.series[1].show = false; + + for (int i = 0; i < 4; i++) + { + chart.EnsureChartComponent<Title>().show = !chart.EnsureChartComponent<Title>().show; + chart.RefreshChart(); + yield return new WaitForSeconds(0.2f); + } + chart.EnsureChartComponent<Title>().show = true; + chart.RefreshChart(); + } + + IEnumerator ComponentAxis() + { + chart.EnsureChartComponent<Title>().subText = "Axis 坐标轴:配置X和Y轴的轴线、刻度、标签等样式外观配置"; + chart.series[0].show = false; + chart.series[1].show = false; + var xAxis = chart.EnsureChartComponent<XAxis>(); + var yAxis = chart.EnsureChartComponent<YAxis>(); + for (int i = 0; i < 4; i++) + { + xAxis.show = !xAxis.show; + yAxis.show = !yAxis.show; + chart.RefreshChart(); + yield return new WaitForSeconds(0.2f); + } + xAxis.show = true; + yAxis.show = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + } + + IEnumerator ComponentGrid() + { + chart.EnsureChartComponent<Title>().subText = "Grid 网格:调整坐标系边距和颜色等"; + var grid = chart.EnsureChartComponent<GridCoord>(); + for (int i = 0; i < 4; i++) + { + grid.backgroundColor = i % 2 == 0 ? Color.clear : Color.grey; + chart.RefreshChart(); + yield return new WaitForSeconds(0.2f); + } + grid.backgroundColor = Color.clear; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + } + + IEnumerator ComponentSerie() + { + chart.EnsureChartComponent<Title>().subText = "Serie 系列:调整坐标系边距和颜色等"; + chart.series[0].show = true; + chart.series[1].show = true; + chart.AnimationReset(); + chart.RefreshChart(); + yield return new WaitForSeconds(1.2f); + for (int i = 0; i < 4; i++) + { + chart.series[0].show = !chart.series[0].show; + chart.series[1].show = !chart.series[1].show; + chart.RefreshChart(); + yield return new WaitForSeconds(0.2f); + } + chart.series[0].show = true; + chart.series[1].show = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + } + + IEnumerator ComponentLegend() + { + chart.EnsureChartComponent<Title>().subText = "Legend 图例:展示不同系列的名字和颜色,可控制系列显示等"; + var legend = chart.EnsureChartComponent<Legend>(); + legend.show = true; + var grid = chart.EnsureChartComponent<GridCoord>(); + grid.top = 80; + legend.location.top = 50; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + for (int i = 0; i < 4; i++) + { + legend.show = !legend.show; + chart.RefreshChart(); + yield return new WaitForSeconds(0.2f); + } + legend.show = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + chart.ClickLegendButton(0, "Line", false); + yield return new WaitForSeconds(0.2f); + chart.ClickLegendButton(0, "Line", true); + yield return new WaitForSeconds(0.5f); + + chart.ClickLegendButton(1, "Bar", false); + yield return new WaitForSeconds(0.2f); + chart.ClickLegendButton(1, "Bar", true); + yield return new WaitForSeconds(0.5f); + } + + IEnumerator ComponentTheme() + { + chart.EnsureChartComponent<Title>().subText = "Theme 主题:可从全局上配置图表的颜色、字体等效果,支持默认主题切换"; + yield return new WaitForSeconds(1f); + chart.EnsureChartComponent<Title>().subText = "Theme 主题:Light主题"; + chart.UpdateTheme(ThemeType.Light); + yield return new WaitForSeconds(1f); + chart.EnsureChartComponent<Title>().subText = "Theme 主题:Dark主题"; + chart.UpdateTheme(ThemeType.Dark); + yield return new WaitForSeconds(1f); + chart.EnsureChartComponent<Title>().subText = "Theme 主题:Default主题"; + chart.UpdateTheme(ThemeType.Default); + yield return new WaitForSeconds(1f); + } + + IEnumerator ComponentDataZoom() + { + chart.EnsureChartComponent<Title>().subText = "DataZoom 区域缩放:可通过拖、拽、缩小、放大来观察细节数据"; + var grid = chart.EnsureChartComponent<GridCoord>(); + grid.bottom = 70; + + var dataZoom = chart.EnsureChartComponent<DataZoom>(); + dataZoom.enable = true; + dataZoom.supportInside = true; + dataZoom.supportSlider = true; + dataZoom.start = 0; + dataZoom.end = 100; + + chart.RefreshChart(); + for (int i = 0; i < 4; i++) + { + dataZoom.supportSlider = !dataZoom.supportSlider; + chart.RefreshChart(); + yield return new WaitForSeconds(0.2f); + } + dataZoom.supportSlider = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + while (dataZoom.start < 40) + { + dataZoom.start += speed * Time.deltaTime * 0.8f; + chart.RefreshDataZoom(); + chart.RefreshChart(); + yield return null; + } + while (dataZoom.end > 60) + { + dataZoom.end -= speed * Time.deltaTime * 0.8f; + chart.RefreshDataZoom(); + chart.RefreshChart(); + yield return null; + } + while (dataZoom.start > 0) + { + dataZoom.start -= speed * Time.deltaTime * 0.8f; + dataZoom.end -= speed * Time.deltaTime * 0.8f; + chart.RefreshDataZoom(); + chart.RefreshChart(); + yield return null; + } + while (dataZoom.end < 100) + { + dataZoom.start += speed * Time.deltaTime * 0.8f; + dataZoom.end += speed * Time.deltaTime * 0.8f; + chart.RefreshDataZoom(); + chart.RefreshChart(); + yield return null; + } + while (dataZoom.start > 0 || dataZoom.end < 100) + { + dataZoom.start -= speed * Time.deltaTime * 0.8f; + dataZoom.end += speed * Time.deltaTime * 0.8f; + chart.RefreshDataZoom(); + chart.RefreshChart(); + yield return null; + } + } + + IEnumerator ComponentVisualMap() + { + chart.EnsureChartComponent<Title>().subText = "VisualMap 视觉映射:可从全局上配置图表的颜色、字体等效果,支持默认主题切换"; + + var visualMap = chart.EnsureChartComponent<VisualMap>(); + visualMap.show = true; + visualMap.showUI = true; + visualMap.orient = Orient.Vertical; + visualMap.calculable = true; + visualMap.min = 0; + visualMap.max = 100; + visualMap.range[0] = 0; + visualMap.range[1] = 100; + + var colors = new List<string> + { + "#313695", + "#4575b4", + "#74add1", + "#abd9e9", + "#e0f3f8", + "#ffffbf", + "#fee090", + "#fdae61", + "#f46d43", + "#d73027", + "#a50026" + }; + visualMap.AddColors(colors); + var grid = chart.EnsureChartComponent<GridCoord>(); + grid.left = 80; + grid.bottom = 100; + chart.RefreshChart(); + + yield return new WaitForSeconds(1f); + while (visualMap.rangeMin < 40) + { + visualMap.rangeMin += speed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + while (visualMap.rangeMax > 60) + { + visualMap.rangeMax -= speed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + while (visualMap.rangeMin > 0 || visualMap.rangeMax < 100) + { + visualMap.rangeMin -= speed * Time.deltaTime; + visualMap.rangeMax += speed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example00_CheatSheet.cs.meta b/Assets/XCharts/Examples/Example00_CheatSheet.cs.meta new file mode 100644 index 0000000..2cfe1ac --- /dev/null +++ b/Assets/XCharts/Examples/Example00_CheatSheet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 677b2673e728a4e308f26a5a9b236277 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example01_RandomData.cs b/Assets/XCharts/Examples/Example01_RandomData.cs new file mode 100644 index 0000000..80eb128 --- /dev/null +++ b/Assets/XCharts/Examples/Example01_RandomData.cs @@ -0,0 +1,238 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [RequireComponent(typeof(BaseChart))] + public class Example01_RandomData : MonoBehaviour + { + public bool loopAdd = false; + public float loopAddTime = 1f; + public bool loopUpdate = false; + public float loopUpadteTime = 1f; + public int maxCache = 0; + public bool insertDataToHead = false; + + BaseChart chart; + float lastAddTime; + float lastUpdateTime; + int dataCount; + + int lastMaxCache = 0; + bool lastInsertDataToHead = false; + + void Awake() + { + chart = gameObject.GetComponent<BaseChart>(); + chart.onInit = () => + { + dataCount = chart.GetSerie(0).dataCount; + SetMaxCache(maxCache); + SetInsertDataToHead(insertDataToHead); + lastMaxCache = maxCache; + lastInsertDataToHead = insertDataToHead; + }; + } + + void SetMaxCache(int maxCache) + { + chart.SetMaxCache(maxCache); + } + + void SetInsertDataToHead(bool insertDataToHead) + { + foreach (var serie in chart.series) + serie.insertDataToHead = insertDataToHead; + + var coms = chart.GetChartComponents<XAxis>(); + if (coms != null) + { + foreach (var com in coms) + { + var axis = com as XAxis; + if (axis.type == Axis.AxisType.Category) + { + axis.insertDataToHead = insertDataToHead; + Debug.LogError("axis:" + axis + "," + insertDataToHead); + } + } + } + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + AddData(); + } + else if (Input.GetKeyDown(KeyCode.U)) + { + UpdateData(); + } + else if (Input.GetKeyDown(KeyCode.C)) + { + chart.ClearData(); + } + if (lastMaxCache != maxCache) + { + lastMaxCache = maxCache; + SetMaxCache(maxCache); + } + if (lastInsertDataToHead != insertDataToHead) + { + lastInsertDataToHead = insertDataToHead; + SetInsertDataToHead(insertDataToHead); + } + lastAddTime += Time.deltaTime; + if (loopAdd && lastAddTime >= loopAddTime) + { + lastAddTime = 0; + AddData(); + } + + lastUpdateTime += Time.deltaTime; + if (loopUpdate && lastUpdateTime >= loopUpadteTime) + { + lastUpdateTime = 0; + UpdateData(); + } + } + + void AddData() + { + if (chart is HeatmapChart) + { + var xAxis = chart.GetChartComponent<XAxis>(); + var yAxis = chart.GetChartComponent<YAxis>(); + if (xAxis != null && yAxis != null) + { + chart.AddXAxisData((xAxis.GetAddedDataCount() + 1).ToString()); + for (int i = 0; i < yAxis.data.Count; i++) + { + chart.AddData(0, xAxis.GetAddedDataCount() - 1, i, Random.Range(10, 90)); + } + } + return; + } + else + { + AddXAxisData(); + var xAxis = chart.GetChartComponent<XAxis>(); + foreach (var serie in chart.series) + { + AddSerieRandomData(serie, xAxis); + } + } + } + + void AddXAxisData() + { + var xAxes = chart.GetChartComponents<XAxis>(); + foreach (var com in xAxes) + { + var xAxis = com as XAxis; + if (xAxis.type == Axis.AxisType.Category) + { + chart.AddXAxisData("x" + (xAxis.GetAddedDataCount() + 1), xAxis.index); + } + } + } + + void UpdateData() + { + foreach (var serie in chart.series) + { + UpdateSerieRandomData(serie); + } + } + + void AddSerieRandomData(Serie serie, XAxis xAxis) + { + if (serie is Line || serie is Bar || serie is Scatter || serie is EffectScatter) + { + if (xAxis.type == Axis.AxisType.Category) + { + chart.AddData(serie.index, Random.Range(10, 90), "data" + serie.dataCount); + } + else + { + if (serie is Line) + chart.AddData(serie.index, dataCount++, Random.Range(10, 90), "data" + serie.dataCount); + else + chart.AddData(serie.index, Random.Range(10, 90), Random.Range(10, 90), "data" + serie.dataCount); + } + } + else if (serie is Ring) + { + chart.AddData(serie.index, Random.Range(10, 90), 100, "data" + serie.dataCount); + } + else if (serie is Radar) + { + var list = new System.Collections.Generic.List<double>(); + for (int i = 0; i < 5; i++) + list.Add(Random.Range(10, 90)); + chart.AddData(serie.index, list, "data" + serie.dataCount); + } + else if (serie is Candlestick) + { + var open = Random.Range(20, 60); + var close = Random.Range(40, 90); + var lowest = Random.Range(0, 50); + var heighest = Random.Range(50, 100); + chart.AddData(serie.index, serie.dataCount, open, close, lowest, heighest); + } + else if (serie is Heatmap) + { + var yAxis = chart.GetChartComponent<YAxis>(serie.yAxisIndex); + for (int i = 0; i < yAxis.data.Count; i++) + { + chart.AddData(serie.index, xAxis.GetAddedDataCount() - 1, i, Random.Range(0, 150)); + } + } + else + { + chart.AddData(serie.index, Random.Range(10, 90), "data" + serie.dataCount); + } + } + + void UpdateSerieRandomData(Serie serie) + { + var index = Random.Range(0, serie.dataCount); + if (serie is Ring) + { + chart.UpdateData(serie.index, index, 0, Random.Range(10, 90)); + } + else if (serie is Radar) + { + var dimension = Random.Range(0, 5); + chart.UpdateData(serie.index, index, dimension, Random.Range(10, 90)); + } + else if (serie is Heatmap) + { + var xAxis = chart.GetChartComponent<XAxis>(); + var yAxis = chart.GetChartComponent<YAxis>(); + var xIndex = Random.Range(0, xAxis.data.Count); + var yIndex = Random.Range(0, yAxis.data.Count); + chart.UpdateData(serie.index, xIndex, yIndex, Random.Range(10, 90)); + } + else if (serie is Candlestick) + { + var open = Random.Range(20, 60); + var close = Random.Range(40, 90); + var lowest = Random.Range(0, 50); + var heighest = Random.Range(50, 100); + chart.UpdateData(serie.index, index, new List<double> { open, close, lowest, heighest }); + } + else + { + chart.UpdateData(serie.index, index, Random.Range(10, 90)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example01_RandomData.cs.meta b/Assets/XCharts/Examples/Example01_RandomData.cs.meta new file mode 100644 index 0000000..e06c78b --- /dev/null +++ b/Assets/XCharts/Examples/Example01_RandomData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c7cdc29e9a8040fdbc7100c3325e9ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example02_ChartEvent.cs b/Assets/XCharts/Examples/Example02_ChartEvent.cs new file mode 100644 index 0000000..51bbfaa --- /dev/null +++ b/Assets/XCharts/Examples/Example02_ChartEvent.cs @@ -0,0 +1,113 @@ +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XCharts.Runtime; +using XUGL; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [RequireComponent(typeof(BaseChart))] + public class Example02_ChartEvent : MonoBehaviour + { + BaseChart chart; + + void Awake() + { + chart = gameObject.GetComponent<BaseChart>(); + + chart.onPointerEnter = OnPointerEnter; + chart.onPointerExit = OnPointerExit; + chart.onPointerDown = OnPointerDown; + chart.onPointerUp = OnPointerUp; + chart.onPointerClick = OnPointerClick; + chart.onScroll = OnScroll; + + chart.onSerieClick = OnSerieClick; + chart.onSerieEnter = OnSerieEnter; + chart.onSerieExit = OnSerieExit; + + chart.onDraw = OnDraw; + chart.onDrawBeforeSerie = OnDrawBeforeSerie; + chart.onDrawAfterSerie = OnDrawAfterSerie; + chart.onDrawTop = OnDrawTop; + } + + void OnPointerEnter(PointerEventData eventData, BaseGraph chart) + { + Debug.Log("enter:" + chart); + } + + void OnPointerExit(PointerEventData eventData, BaseGraph chart) + { + Debug.Log("exit:" + chart); + } + + void OnPointerDown(PointerEventData eventData, BaseGraph chart) + { + Debug.Log("down:" + chart); + } + + void OnPointerUp(PointerEventData eventData, BaseGraph chart) + { + Debug.Log("up:" + chart); + } + + void OnPointerClick(PointerEventData eventData, BaseGraph chart) + { + Debug.Log("click:" + chart); + } + + void OnScroll(PointerEventData eventData, BaseGraph chart) + { + Debug.Log("scroll:" + chart); + } + + void OnSerieClick(SerieEventData data) + { + Debug.Log("OnSerieClick: " + data.serieIndex + " " + data.dataIndex + " " + data.dimension + " " + data.value); + } + + void OnSerieEnter(SerieEventData data) + { + Debug.Log("OnSerieEnter: " + data.serieIndex + " " + data.dataIndex + " " + data.dimension + " " + data.value); + } + + void OnSerieExit(SerieEventData data) + { + Debug.Log("OnSerieExit: " + data.serieIndex + " " + data.dataIndex + " " + data.dimension + " " + data.value); + } + + void OnDraw(VertexHelper vh) + { + //Debug.Log("OnDraw"); + } + + void OnDrawBeforeSerie(VertexHelper vh, Serie serie) + { + //Debug.Log("OnDrawBeforeSerie: " + serie.index); + } + + void OnDrawAfterSerie(VertexHelper vh, Serie serie) + { + //Debug.Log("OnDrawAfterSerie: " + serie.index); + if (serie.index != 0) return; + var dataPoints = serie.context.dataPoints; + if (dataPoints.Count > 4) + { + var pos = dataPoints[3]; + var grid = chart.GetChartComponent<GridCoord>(); + var zeroPos = new Vector3(grid.context.x, grid.context.y); + var startPos = new Vector3(pos.x, zeroPos.y); + var endPos = new Vector3(pos.x, zeroPos.y + grid.context.height); + UGL.DrawLine(vh, startPos, endPos, chart.theme.serie.lineWidth, Color.blue); + UGL.DrawCricle(vh, pos, 5, Color.blue); + } + } + + void OnDrawTop(VertexHelper vh) + { + //Debug.Log("OnDrawTop"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example02_ChartEvent.cs.meta b/Assets/XCharts/Examples/Example02_ChartEvent.cs.meta new file mode 100644 index 0000000..a57357a --- /dev/null +++ b/Assets/XCharts/Examples/Example02_ChartEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c549dc496cd86467e8286252906562cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example03_ChartAnimation.cs b/Assets/XCharts/Examples/Example03_ChartAnimation.cs new file mode 100644 index 0000000..708eea1 --- /dev/null +++ b/Assets/XCharts/Examples/Example03_ChartAnimation.cs @@ -0,0 +1,38 @@ +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example03_ChartAnimation : MonoBehaviour + { + BaseChart chart; + + void Awake() + { + chart = gameObject.GetComponent<BaseChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<BarChart>(); + chart.Init(); + } + var serie = chart.GetSerie(0); + serie.animation.enable = true; + //自定义每个数据项的渐入延时 + serie.animation.fadeIn.delayFunction = CustomFadeInDelay; + //自定义每个数据项的渐入时长 + serie.animation.fadeIn.durationFunction = CustomFadeInDuration; + } + + float CustomFadeInDelay(int dataIndex) + { + return dataIndex * 1000; + } + + float CustomFadeInDuration(int dataIndex) + { + return dataIndex * 1000 + 1000; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example03_ChartAnimation.cs.meta b/Assets/XCharts/Examples/Example03_ChartAnimation.cs.meta new file mode 100644 index 0000000..93e6d6b --- /dev/null +++ b/Assets/XCharts/Examples/Example03_ChartAnimation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6258ca3b055714eac92804f501011b53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example04_DataZoom.cs b/Assets/XCharts/Examples/Example04_DataZoom.cs new file mode 100644 index 0000000..b555b29 --- /dev/null +++ b/Assets/XCharts/Examples/Example04_DataZoom.cs @@ -0,0 +1,50 @@ +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example04_DataZoom : MonoBehaviour + { + BaseChart chart; + + void Awake() + { + chart = gameObject.GetComponent<BaseChart>(); + if (chart == null) return; + var dataZoom = chart.GetChartComponent<DataZoom>(); + if (dataZoom == null) return; + dataZoom.marqueeStyle.onStart = OnMarqueeStart; + dataZoom.marqueeStyle.onEnd = OnMarqueeEnd; + dataZoom.marqueeStyle.onGoing = OnMarquee; + } + + void OnMarqueeStart(DataZoom dataZoom) + { + //Debug.Log("OnMarqueeStart:" + dataZoom); + } + + void OnMarquee(DataZoom dataZoom) + { + //Debug.Log("OnMarquee:" + dataZoom); + } + + void OnMarqueeEnd(DataZoom dataZoom) + { + //Debug.Log("OnMarqueeEnd:" + dataZoom); + var serie = chart.GetSerie(0); + foreach (var serieData in serie.data) + { + if (dataZoom.IsInMarqueeArea(serieData)) + { + serieData.EnsureComponent<ItemStyle>().color = Color.red; + } + else + { + serieData.EnsureComponent<ItemStyle>().color = Color.clear; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example04_DataZoom.cs.meta b/Assets/XCharts/Examples/Example04_DataZoom.cs.meta new file mode 100644 index 0000000..e0a5838 --- /dev/null +++ b/Assets/XCharts/Examples/Example04_DataZoom.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2cc0ca220d904377984528de6214b97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example05_DynamicChart.cs b/Assets/XCharts/Examples/Example05_DynamicChart.cs new file mode 100644 index 0000000..234ed59 --- /dev/null +++ b/Assets/XCharts/Examples/Example05_DynamicChart.cs @@ -0,0 +1,95 @@ +using UnityEngine; +using XCharts.Runtime; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example05_DynamicChart : MonoBehaviour + { + BaseChart chart; + + void Awake() { } + + void Update() + { + if (Input.GetKeyDown(KeyCode.P)) + { + AddPieChart("Dynamic PieChart"); + } + if (Input.GetKeyDown(KeyCode.L)) + { + AddLineChart("Dynamic LineChart"); + } + } + + GameObject CreateChartObject(string chartName) + { + for (int i = transform.childCount - 1; i >= 0; i--) + { + Destroy(transform.GetChild(i).gameObject); + } + var chartObject = new GameObject(); + chartObject.name = chartName; + chartObject.transform.SetParent(transform); + chartObject.transform.localScale = Vector3.one; + chartObject.transform.localPosition = Vector3.zero; + return chartObject; + } + + void AddPieChart(string chartName) + { + var chartObject = CreateChartObject(chartName); + var chart = chartObject.AddComponent<PieChart>(); + chart.SetSize(580, 300); + + chart.EnsureChartComponent<Title>().show = true; + chart.EnsureChartComponent<Title>().text = chartName; + + chart.EnsureChartComponent<Tooltip>().show = true; + chart.EnsureChartComponent<Legend>().show = true; + + chart.RemoveData(); + chart.AddSerie<Pie>(); + + for (int i = 0; i < 3; i++) + { + chart.AddData(0, Random.Range(10, 20), "pie" + (i + 1)); + } + } + + void AddLineChart(string chartName) + { + var chartObject = CreateChartObject(chartName); + var chart = chartObject.AddComponent<PieChart>(); + chart.SetSize(580, 300); + + chart.EnsureChartComponent<Title>().show = true; + chart.EnsureChartComponent<Title>().text = chartName; + + chart.EnsureChartComponent<Legend>().show = false; + + var tooltip = chart.EnsureChartComponent<Tooltip>(); + tooltip.trigger = Tooltip.Trigger.Axis; + + var xAxis = chart.EnsureChartComponent<XAxis>(); + var yAxis = chart.EnsureChartComponent<YAxis>(); + xAxis.splitNumber = 10; + xAxis.boundaryGap = true; + xAxis.show = true; + yAxis.show = true; + xAxis.type = Axis.AxisType.Category; + yAxis.type = Axis.AxisType.Value; + + chart.RemoveData(); + chart.AddSerie<Line>(); + + for (int i = 0; i < 10; i++) + { + chart.AddXAxisData("x" + (i + 1)); + chart.AddData(0, Random.Range(10, 100)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example05_DynamicChart.cs.meta b/Assets/XCharts/Examples/Example05_DynamicChart.cs.meta new file mode 100644 index 0000000..f60ff2e --- /dev/null +++ b/Assets/XCharts/Examples/Example05_DynamicChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c3dbcd4fb120c4508b7bba52b41fbdb9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example10_LineChart.cs b/Assets/XCharts/Examples/Example10_LineChart.cs new file mode 100644 index 0000000..1814d64 --- /dev/null +++ b/Assets/XCharts/Examples/Example10_LineChart.cs @@ -0,0 +1,260 @@ +using System.Collections; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example10_LineChart : MonoBehaviour + { + private LineChart chart; + private Serie serie; + private int m_DataNum = 8; + + private void OnEnable() + { + StartCoroutine(PieDemo()); + } + + IEnumerator PieDemo() + { + while (true) + { + StartCoroutine(AddSimpleLine()); + yield return new WaitForSeconds(2); + StartCoroutine(ChangeLineType()); + yield return new WaitForSeconds(8); + StartCoroutine(LineAreaStyleSettings()); + yield return new WaitForSeconds(5); + StartCoroutine(LineArrowSettings()); + yield return new WaitForSeconds(2); + StartCoroutine(LineSymbolSettings()); + yield return new WaitForSeconds(7); + StartCoroutine(LineLabelSettings()); + yield return new WaitForSeconds(3); + StartCoroutine(LineMutilSerie()); + yield return new WaitForSeconds(5); + } + } + + IEnumerator AddSimpleLine() + { + chart = gameObject.GetComponent<LineChart>(); + if (chart == null){ + chart = gameObject.AddComponent<LineChart>(); + chart.Init(); + } + chart.GetChartComponent<Title>().text = "LineChart - 折线图"; + chart.GetChartComponent<Title>().subText = "普通折线图"; + + var yAxis = chart.GetChartComponent<YAxis>(); + yAxis.minMaxType = Axis.AxisMinMaxType.Custom; + yAxis.min = 0; + yAxis.max = 100; + + chart.RemoveData(); + serie = chart.AddSerie<Line>("Line"); + + for (int i = 0; i < m_DataNum; i++) + { + chart.AddXAxisData("x" + (i + 1)); + chart.AddData(0, UnityEngine.Random.Range(30, 90)); + } + yield return new WaitForSeconds(1); + } + + IEnumerator ChangeLineType() + { + chart.GetChartComponent<Title>().subText = "LineTyle - 曲线图"; + serie.lineType = LineType.Smooth; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "LineTyle - 阶梯线图"; + serie.lineType = LineType.StepStart; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + serie.lineType = LineType.StepMiddle; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + serie.lineType = LineType.StepEnd; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "LineTyle - 虚线"; + serie.lineStyle.type = LineStyle.Type.Dashed; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "LineTyle - 点线"; + serie.lineStyle.type = LineStyle.Type.Dotted; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "LineTyle - 点划线"; + serie.lineStyle.type = LineStyle.Type.DashDot; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "LineTyle - 双点划线"; + serie.lineStyle.type = LineStyle.Type.DashDotDot; + chart.RefreshChart(); + + serie.lineType = LineType.Normal; + chart.RefreshChart(); + } + + IEnumerator LineAreaStyleSettings() + { + chart.GetChartComponent<Title>().subText = "AreaStyle 面积图"; + + serie.EnsureComponent<AreaStyle>(); + serie.areaStyle.show = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + + chart.GetChartComponent<Title>().subText = "AreaStyle 面积图"; + serie.lineType = LineType.Smooth; + serie.areaStyle.show = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1f); + + chart.GetChartComponent<Title>().subText = "AreaStyle 面积图 - 调整透明度"; + while (serie.areaStyle.opacity > 0.4) + { + serie.areaStyle.opacity -= 0.6f * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "AreaStyle 面积图 - 渐变"; + serie.areaStyle.toColor = Color.white; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + + IEnumerator LineArrowSettings() + { + chart.GetChartComponent<Title>().subText = "LineArrow 头部箭头"; + chart.GetSerie(0).EnsureComponent<LineArrow>(); + serie.lineArrow.show = true; + serie.lineArrow.position = LineArrow.Position.Start; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "LineArrow 尾部箭头"; + serie.lineArrow.position = LineArrow.Position.End; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + serie.lineArrow.show = false; + } + + /// <summary> + /// SerieSymbol 相关设置 + /// </summary> + /// <returns></returns> + IEnumerator LineSymbolSettings() + { + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记"; + while (serie.symbol.size < 5) + { + serie.symbol.size += 2.5f * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记 - 空心圆"; + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记 - 实心圆"; + serie.symbol.type = SymbolType.Circle; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记 - 三角形"; + serie.symbol.type = SymbolType.Triangle; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记 - 正方形"; + serie.symbol.type = SymbolType.Rect; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记 - 菱形"; + serie.symbol.type = SymbolType.Diamond; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + chart.GetChartComponent<Title>().subText = "SerieSymbol 图形标记"; + serie.symbol.type = SymbolType.EmptyCircle; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + + /// <summary> + /// SerieLabel相关配置 + /// </summary> + /// <returns></returns> + IEnumerator LineLabelSettings() + { + chart.GetChartComponent<Title>().subText = "SerieLabel 文本标签"; + serie.EnsureComponent<LabelStyle>(); + chart.RefreshChart(); + while (serie.label.offset[1] < 20) + { + serie.label.offset = new Vector3(serie.label.offset.x, serie.label.offset.y + 20f * Time.deltaTime); + chart.RefreshChart(); + yield return null; + } + yield return new WaitForSeconds(1); + + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + serie.label.textStyle.color = Color.white; + serie.label.background.color = Color.grey; + serie.labelDirty = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + serie.label.show = false; + chart.RefreshChart(); + } + + /// <summary> + /// 添加多条线图 + /// </summary> + /// <returns></returns> + IEnumerator LineMutilSerie() + { + chart.GetChartComponent<Title>().subText = "多系列"; + var serie2 = chart.AddSerie<Line>("Line2"); + serie2.lineType = LineType.Normal; + for (int i = 0; i < m_DataNum; i++) + { + chart.AddData(1, UnityEngine.Random.Range(30, 90)); + } + yield return new WaitForSeconds(1); + + var serie3 = chart.AddSerie<Line>("Line3"); + serie3.lineType = LineType.Normal; + for (int i = 0; i < m_DataNum; i++) + { + chart.AddData(2, UnityEngine.Random.Range(30, 90)); + } + yield return new WaitForSeconds(1); + + var yAxis = chart.GetChartComponent<YAxis>(); + yAxis.minMaxType = Axis.AxisMinMaxType.Default; + chart.GetChartComponent<Title>().subText = "多系列 - 堆叠"; + serie.stack = "samename"; + serie2.stack = "samename"; + serie3.stack = "samename"; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example10_LineChart.cs.meta b/Assets/XCharts/Examples/Example10_LineChart.cs.meta new file mode 100644 index 0000000..b2fcde5 --- /dev/null +++ b/Assets/XCharts/Examples/Example10_LineChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6155c7e0df4504ebfaf0c671ae200197 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example11_AddSinCurve.cs b/Assets/XCharts/Examples/Example11_AddSinCurve.cs new file mode 100644 index 0000000..dac854c --- /dev/null +++ b/Assets/XCharts/Examples/Example11_AddSinCurve.cs @@ -0,0 +1,62 @@ +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example11_AddSinCurve : MonoBehaviour + { + private float time; + public int angle; + private LineChart chart; + + void Awake() + { + chart = gameObject.GetComponent<LineChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<LineChart>(); + chart.Init(); + } + chart.EnsureChartComponent<Title>().show = true; + chart.EnsureChartComponent<Title>().text = "Sin Curve"; + + chart.EnsureChartComponent<Tooltip>().show = true; + chart.EnsureChartComponent<Legend>().show = false; + + var xAxis = chart.EnsureChartComponent<XAxis>(); + var yAxis = chart.EnsureChartComponent<YAxis>(); + + xAxis.show = true; + yAxis.show = true; + + xAxis.type = Axis.AxisType.Value; + yAxis.type = Axis.AxisType.Value; + + xAxis.boundaryGap = false; + xAxis.maxCache = 0; + chart.series[0].maxCache = 0; + + chart.RemoveData(); + + var serie = chart.AddSerie<Line>(); + serie.symbol.show = false; + serie.lineType = LineType.Normal; + for (angle = 0; angle < 1080; angle++) + { + float xvalue = Mathf.PI / 180 * angle; + float yvalue = Mathf.Sin(xvalue); + chart.AddData(0, xvalue, yvalue); + } + } + + void Update() + { + if (angle > 3000) return; + angle++; + float xvalue = Mathf.PI / 180 * angle; + float yvalue = Mathf.Sin(xvalue); + chart.AddData(0, xvalue, yvalue); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example11_AddSinCurve.cs.meta b/Assets/XCharts/Examples/Example11_AddSinCurve.cs.meta new file mode 100644 index 0000000..1f7901b --- /dev/null +++ b/Assets/XCharts/Examples/Example11_AddSinCurve.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b380753d3cb4149c4a3a65a1816e0cc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example12_CustomDrawing.cs b/Assets/XCharts/Examples/Example12_CustomDrawing.cs new file mode 100644 index 0000000..ace1318 --- /dev/null +++ b/Assets/XCharts/Examples/Example12_CustomDrawing.cs @@ -0,0 +1,41 @@ +using UnityEngine; +using UnityEngine.UI; +using XCharts.Runtime; +using XUGL; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example12_CustomDrawing : MonoBehaviour + { + LineChart chart; + void Awake() + { + chart = gameObject.GetComponent<LineChart>(); + if (chart == null) return; + + chart.onDraw = delegate(VertexHelper vh) { }; + // or + chart.onDrawBeforeSerie = delegate(VertexHelper vh, Serie serie) { }; + // or + chart.onDrawAfterSerie = delegate(VertexHelper vh, Serie serie) + { + if (serie.index != 0) return; + var dataPoints = serie.context.dataPoints; + if (dataPoints.Count > 0) + { + var pos = dataPoints[3]; + var grid = chart.GetChartComponent<GridCoord>(); + var zeroPos = new Vector3(grid.context.x, grid.context.y); + var startPos = new Vector3(pos.x, zeroPos.y); + var endPos = new Vector3(pos.x, zeroPos.y + grid.context.height); + UGL.DrawLine(vh, startPos, endPos, chart.theme.serie.lineWidth, Color.blue); + UGL.DrawCricle(vh, pos, 5, Color.blue); + } + }; + // or + chart.onDrawTop = delegate(VertexHelper vh) { }; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example12_CustomDrawing.cs.meta b/Assets/XCharts/Examples/Example12_CustomDrawing.cs.meta new file mode 100644 index 0000000..c81c939 --- /dev/null +++ b/Assets/XCharts/Examples/Example12_CustomDrawing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da550ad36be5f442e96ad021cc10ca68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example13_LineSimple.cs b/Assets/XCharts/Examples/Example13_LineSimple.cs new file mode 100644 index 0000000..0a78a41 --- /dev/null +++ b/Assets/XCharts/Examples/Example13_LineSimple.cs @@ -0,0 +1,61 @@ +using UnityEngine; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example13_LineSimple : MonoBehaviour + { + void Awake() + { + AddData(); + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + AddData(); + } + } + + void AddData() + { + var chart = gameObject.GetComponent<LineChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<LineChart>(); + chart.Init(); + } + chart.EnsureChartComponent<Title>().show = true; + chart.EnsureChartComponent<Title>().text = "Line Simple"; + + chart.EnsureChartComponent<Tooltip>().show = true; + chart.EnsureChartComponent<Legend>().show = false; + + var xAxis = chart.EnsureChartComponent<XAxis>(); + var yAxis = chart.EnsureChartComponent<YAxis>(); + xAxis.show = true; + yAxis.show = true; + xAxis.type = Axis.AxisType.Category; + yAxis.type = Axis.AxisType.Value; + + xAxis.splitNumber = 10; + xAxis.boundaryGap = true; + + chart.RemoveData(); + chart.AddSerie<Line>(); + chart.AddSerie<Line>(); + for (int i = 0; i < 20; i++) + { + chart.AddXAxisData("x" + i); + chart.AddData(0, Random.Range(10, 20)); + chart.AddData(1, Random.Range(10, 20)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example13_LineSimple.cs.meta b/Assets/XCharts/Examples/Example13_LineSimple.cs.meta new file mode 100644 index 0000000..4587607 --- /dev/null +++ b/Assets/XCharts/Examples/Example13_LineSimple.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6d0f65efd8e14ebdafa172e0ccbd562 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example20_BarChart.cs b/Assets/XCharts/Examples/Example20_BarChart.cs new file mode 100644 index 0000000..fcebaa8 --- /dev/null +++ b/Assets/XCharts/Examples/Example20_BarChart.cs @@ -0,0 +1,158 @@ +using System.Collections; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example20_BarChart : MonoBehaviour + { + private BarChart chart; + private Serie serie, serie2; + private int m_DataNum = 5; + + private void OnEnable() + { + StartCoroutine(PieDemo()); + } + + IEnumerator PieDemo() + { + while (true) + { + StartCoroutine(AddSimpleBar()); + yield return new WaitForSeconds(2); + StartCoroutine(BarMutilSerie()); + yield return new WaitForSeconds(3); + StartCoroutine(ZebraBar()); + yield return new WaitForSeconds(3); + StartCoroutine(SameBarAndNotStack()); + yield return new WaitForSeconds(3); + StartCoroutine(SameBarAndStack()); + yield return new WaitForSeconds(3); + StartCoroutine(SameBarAndPercentStack()); + yield return new WaitForSeconds(10); + } + } + + IEnumerator AddSimpleBar() + { + chart = gameObject.GetComponent<BarChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<BarChart>(); + chart.Init(); + } + chart.EnsureChartComponent<Title>().text = "BarChart - 柱状图"; + chart.EnsureChartComponent<Title>().subText = "普通柱状图"; + + var yAxis = chart.EnsureChartComponent<YAxis>(); + yAxis.minMaxType = Axis.AxisMinMaxType.Default; + + chart.RemoveData(); + serie = chart.AddSerie<Bar>("Bar1"); + + for (int i = 0; i < m_DataNum; i++) + { + chart.AddXAxisData("x" + (i + 1)); + chart.AddData(0, UnityEngine.Random.Range(30, 90)); + } + yield return new WaitForSeconds(1); + } + + IEnumerator BarMutilSerie() + { + chart.EnsureChartComponent<Title>().subText = "多条柱状图"; + + float now = serie.barWidth - 0.35f; + while (serie.barWidth > 0.35f) + { + serie.barWidth -= now * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + + serie2 = chart.AddSerie<Bar>("Bar2"); + serie2.lineType = LineType.Normal; + serie2.barWidth = 0.35f; + for (int i = 0; i < m_DataNum; i++) + { + chart.AddData(1, UnityEngine.Random.Range(20, 90)); + } + yield return new WaitForSeconds(1); + } + + IEnumerator ZebraBar() + { + chart.EnsureChartComponent<Title>().subText = "斑马柱状图"; + serie.barType = BarType.Zebra; + serie2.barType = BarType.Zebra; + serie.barZebraWidth = serie.barZebraGap = 4; + serie2.barZebraWidth = serie2.barZebraGap = 4; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + + IEnumerator SameBarAndNotStack() + { + chart.EnsureChartComponent<Title>().subText = "非堆叠同柱"; + serie.barType = serie2.barType = BarType.Normal; + serie.stack = ""; + serie2.stack = ""; + serie.barGap = -1; + serie2.barGap = -1; + yield return new WaitForSeconds(1); + } + + IEnumerator SameBarAndStack() + { + chart.EnsureChartComponent<Title>().subText = "堆叠同柱"; + serie.barType = serie2.barType = BarType.Normal; + serie.stack = "samename"; + serie2.stack = "samename"; + yield return new WaitForSeconds(1); + float now = 0.6f - serie.barWidth; + while (serie.barWidth < 0.6f) + { + serie.barWidth += now * Time.deltaTime; + serie2.barWidth += now * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + serie.barWidth = serie2.barWidth; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + + IEnumerator SameBarAndPercentStack() + { + chart.EnsureChartComponent<Title>().subText = "百分比堆叠同柱"; + serie.barType = serie2.barType = BarType.Normal; + serie.stack = "samename"; + serie2.stack = "samename"; + + serie.barPercentStack = true; + if (null == serie.label) + { + serie.EnsureComponent<LabelStyle>(); + } + serie.label.show = true; + serie.label.position = LabelStyle.Position.Center; + serie.label.textStyle.color = Color.white; + serie.label.formatter = "{d:f0}%"; + + if (null == serie2.label) + { + serie2.EnsureComponent<LabelStyle>(); + } + serie2.label.show = true; + serie2.label.position = LabelStyle.Position.Center; + serie2.label.textStyle.color = Color.white; + serie2.label.formatter = "{d:f0}%"; + serie2.labelDirty = true; + + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example20_BarChart.cs.meta b/Assets/XCharts/Examples/Example20_BarChart.cs.meta new file mode 100644 index 0000000..c5ef6fd --- /dev/null +++ b/Assets/XCharts/Examples/Example20_BarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03916f7ca858b446883197ae17e50f16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example21_BarRace.cs b/Assets/XCharts/Examples/Example21_BarRace.cs new file mode 100644 index 0000000..c14e3ff --- /dev/null +++ b/Assets/XCharts/Examples/Example21_BarRace.cs @@ -0,0 +1,46 @@ +using System.Collections; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example21_BarRace : MonoBehaviour + { + private BarChart chart; + private float lastTime; + + void Awake() + { + chart = gameObject.GetComponent<BarChart>(); + chart.ClearData(); + for (int i = 0; i < 5; i++) + { + chart.AddYAxisData("y" + i); + chart.AddData(0, Random.Range(0, 200)); + } + } + + void Update() + { + if (Time.time - lastTime >= 3f) + { + lastTime = Time.time; + UpdateData(); + } + } + + void UpdateData() + { + var serie = chart.GetSerie(0); + + for (int i = 0; i < serie.dataCount; i++) + { + if (Random.Range(0, 1f) > 0.9f) + chart.UpdateData(0, i, chart.GetData(0, i) + Mathf.Round(Random.Range(0, 2000))); + else + chart.UpdateData(0, i, chart.GetData(0, i) + Mathf.Round(Random.Range(0, 200))); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example21_BarRace.cs.meta b/Assets/XCharts/Examples/Example21_BarRace.cs.meta new file mode 100644 index 0000000..70e9a19 --- /dev/null +++ b/Assets/XCharts/Examples/Example21_BarRace.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9842ca7fe07044666950b6f53ef65fdb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example30_PieChart.cs b/Assets/XCharts/Examples/Example30_PieChart.cs new file mode 100644 index 0000000..ae80b60 --- /dev/null +++ b/Assets/XCharts/Examples/Example30_PieChart.cs @@ -0,0 +1,207 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.EventSystems; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example30_PieChart : MonoBehaviour + { + private PieChart chart; + private Serie serie, serie1; + private float m_RadiusSpeed = 100f; + private float m_CenterSpeed = 1f; + + private void OnEnable() + { + StartCoroutine(PieDemo()); + } + + IEnumerator PieDemo() + { + while (true) + { + StartCoroutine(PieAdd()); + yield return new WaitForSeconds(2); + StartCoroutine(PieShowLabel()); + yield return new WaitForSeconds(4); + StartCoroutine(Doughnut()); + yield return new WaitForSeconds(3); + StartCoroutine(DoublePie()); + yield return new WaitForSeconds(2); + StartCoroutine(RosePie()); + yield return new WaitForSeconds(5); + } + } + + IEnumerator PieAdd() + { + chart = gameObject.GetComponent<PieChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<PieChart>(); + chart.Init(); + } + yield return null; + chart.GetChartComponent<Title>().text = "PieChart - 饼图"; + chart.GetChartComponent<Title>().subText = "基础饼图"; + + var legend = chart.EnsureChartComponent<Legend>(); + legend.show = true; + legend.location.align = Location.Align.TopLeft; + legend.location.top = 60; + legend.location.left = 2; + legend.itemWidth = 70; + legend.itemHeight = 20; + legend.orient = Orient.Vertical; + + chart.RemoveData(); + serie = chart.AddSerie<Pie>("访问来源"); + serie.radius[0] = 0; + serie.radius[1] = 110; + serie.center[0] = 0.5f; + serie.center[1] = 0.4f; + chart.AddData(0, 335, "直接访问"); + chart.AddData(0, 310, "邮件营销"); + chart.AddData(0, 243, "联盟广告"); + chart.AddData(0, 135, "视频广告"); + chart.AddData(0, 1548, "搜索引擎"); + + chart.onSerieClick = delegate (SerieEventData data) + { + + }; + yield return new WaitForSeconds(1); + } + + IEnumerator PieShowLabel() + { + chart.EnsureChartComponent<Title>().subText = "显示文本标签"; + + serie.EnsureComponent<LabelStyle>(); + serie.label.show = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + serie.labelLine.lineType = LabelLine.LineType.Curves; + chart.RefreshChart(); + + yield return new WaitForSeconds(1); + serie.labelLine.lineType = LabelLine.LineType.HorizontalLine; + chart.RefreshChart(); + + yield return new WaitForSeconds(1); + serie.labelLine.lineType = LabelLine.LineType.BrokenLine; + chart.RefreshChart(); + + yield return new WaitForSeconds(1); + serie.labelLine.show = false; + chart.RefreshChart(); + } + + IEnumerator Doughnut() + { + chart.EnsureChartComponent<Title>().subText = "圆环图"; + serie.radius[0] = 2f; + while (serie.radius[0] < serie.radius[1] * 0.7f) + { + serie.radius[0] += m_RadiusSpeed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + serie.gap = 1f; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + serie.data[0].selected = true; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + + serie.gap = 0f; + serie.data[0].selected = false; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + + IEnumerator DoublePie() + { + chart.EnsureChartComponent<Title>().subText = "多图组合"; + serie1 = chart.AddSerie<Pie>("访问来源2"); + chart.AddData(1, 335, "直达"); + chart.AddData(1, 679, "营销广告"); + chart.AddData(1, 1548, "搜索引擎"); + serie1.radius[0] = 0; + serie1.radius[1] = 2f; + serie1.center[0] = 0.5f; + serie1.center[1] = 0.4f; + chart.RefreshChart(); + while (serie1.radius[1] < serie.radius[0] * 0.75f) + { + serie1.radius[1] += m_RadiusSpeed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + if (null == serie.label) + { + serie.EnsureComponent<LabelStyle>(); + } + if (null == serie1.label) + { + serie1.EnsureComponent<LabelStyle>(); + } + serie1.label.show = true; + serie1.label.position = LabelStyle.Position.Inside; + serie1.label.textStyle.color = Color.white; + serie1.label.textStyle.fontSize = 14; + + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + + IEnumerator RosePie() + { + chart.EnsureChartComponent<Title>().subText = "玫瑰图"; + chart.EnsureChartComponent<Legend>().show = false; + serie1.ClearData(); + serie.ClearData(); + serie1.radius = serie.radius = new float[2] { 0, 80 }; + serie1.label.position = LabelStyle.Position.Outside; + serie1.labelLine.lineType = LabelLine.LineType.Curves; + serie1.label.textStyle.color = Color.clear; + for (int i = 0; i < 2; i++) + { + chart.AddData(i, 10, "rose1"); + chart.AddData(i, 5, "rose2"); + chart.AddData(i, 15, "rose3"); + chart.AddData(i, 25, "rose4"); + chart.AddData(i, 20, "rose5"); + chart.AddData(i, 35, "rose6"); + chart.AddData(i, 30, "rose7"); + chart.AddData(i, 40, "rose8"); + } + + while (serie.center[0] > 0.25f || serie1.center[0] < 0.7f) + { + if (serie.center[0] > 0.25f) serie.center[0] -= m_CenterSpeed * Time.deltaTime; + if (serie1.center[0] < 0.7f) serie1.center[0] += m_CenterSpeed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + yield return new WaitForSeconds(1); + while (serie.radius[0] > 3f) + { + serie.radius[0] -= m_RadiusSpeed * Time.deltaTime; + serie1.radius[0] -= m_RadiusSpeed * Time.deltaTime; + chart.RefreshChart(); + yield return null; + } + + serie.radius[0] = 0; + serie1.radius[0] = 0; + serie.pieRoseType = RoseType.Area; + serie1.pieRoseType = RoseType.Radius; + chart.RefreshChart(); + yield return new WaitForSeconds(1); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example30_PieChart.cs.meta b/Assets/XCharts/Examples/Example30_PieChart.cs.meta new file mode 100644 index 0000000..fb74aa5 --- /dev/null +++ b/Assets/XCharts/Examples/Example30_PieChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0b8649d38981b4b5bbdf16e8f303fa1e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example31_PieUpdateName.cs b/Assets/XCharts/Examples/Example31_PieUpdateName.cs new file mode 100644 index 0000000..d4449ce --- /dev/null +++ b/Assets/XCharts/Examples/Example31_PieUpdateName.cs @@ -0,0 +1,77 @@ +using UnityEngine; +using XCharts.Runtime; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example31_PieUpdateName : MonoBehaviour + { + PieChart chart; + + void Awake() + { + chart = gameObject.GetComponent<PieChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<PieChart>(); + chart.Init(); + } + var serieIndex = 0; + var serie = chart.GetSerie(serieIndex); + if (serie == null) return; + serie.EnsureComponent<LabelStyle>(); + serie.label.show = true; + serie.label.position = LabelStyle.Position.Outside; + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + ClearAndAddData(); + //UpdateDataName(); + //UpdateDataName(); + } + } + + void UpdateDataName() + { + var serieIndex = 0; + var serie = chart.GetSerie(serieIndex); + if (serie == null) return; + for (int i = 0; i < serie.dataCount; i++) + { + var value = Random.Range(10, 100); + chart.UpdateData(serieIndex, i, value); + chart.UpdateDataName(serieIndex, i, "value=" + value); + } + } + + void ResetSameName() + { + var serieIndex = 0; + var serie = chart.GetSerie(serieIndex); + if (serie == null) return; + for (int i = 0; i < serie.dataCount; i++) + { + chart.UpdateDataName(serieIndex, i, "piename"); + } + } + + void ClearAndAddData() + { + var serieIndex = 0; + var serie = chart.GetSerie(serieIndex); + if (serie == null) return; + int count = serie.dataCount; + serie.ClearData(); + for (int i = 0; i < count; i++) + { + chart.AddData(0, Random.Range(0, 100), "pie" + i); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example31_PieUpdateName.cs.meta b/Assets/XCharts/Examples/Example31_PieUpdateName.cs.meta new file mode 100644 index 0000000..2afd8a2 --- /dev/null +++ b/Assets/XCharts/Examples/Example31_PieUpdateName.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41195ee7a652f4ef79c22c365d314621 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example40_Radar.cs b/Assets/XCharts/Examples/Example40_Radar.cs new file mode 100644 index 0000000..fc9dfaf --- /dev/null +++ b/Assets/XCharts/Examples/Example40_Radar.cs @@ -0,0 +1,139 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + public class Example40_Radar : MonoBehaviour + { + private RadarChart chart; + private Serie serie, serie1; + void Awake() + { + LoopDemo(); + } + + private void OnEnable() + { + LoopDemo(); + } + + void LoopDemo() + { + StopAllCoroutines(); + StartCoroutine(RadarDemo()); + } + + IEnumerator RadarDemo() + { + StartCoroutine(RadarAdd()); + yield return new WaitForSeconds(2); + StartCoroutine(RadarUpdate()); + yield return new WaitForSeconds(2); + StartCoroutine(RadarAddMultiple()); + yield return new WaitForSeconds(2); + LoopDemo(); + } + + IEnumerator RadarAdd() + { + chart = gameObject.GetComponent<RadarChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<RadarChart>(); + chart.Init(); + } + + chart.RemoveChartComponents<RadarCoord>(); + chart.RemoveData(); + + chart.GetChartComponent<Title>().text = "RadarChart - 雷达图"; + chart.GetChartComponent<Title>().subText = ""; + + var legend = chart.GetChartComponent<Legend>(); + legend.show = true; + legend.location.align = Location.Align.TopLeft; + legend.location.top = 60; + legend.location.left = 2; + legend.itemWidth = 70; + legend.itemHeight = 20; + legend.orient = Orient.Vertical; + + var radarCoord = chart.AddChartComponent<RadarCoord>(); + radarCoord.shape = RadarCoord.Shape.Polygon; + radarCoord.center[0] = 0.5f; + radarCoord.center[1] = 0.4f; + radarCoord.radius = 0.4f; + + radarCoord.AddIndicator("indicator1", 0, 100); + radarCoord.AddIndicator("indicator2", 0, 100); + radarCoord.AddIndicator("indicator3", 0, 100); + radarCoord.AddIndicator("indicator4", 0, 100); + radarCoord.AddIndicator("indicator5", 0, 100); + + serie = chart.AddSerie<Radar>("test"); + serie.radarIndex = 0; + chart.AddData(0, new List<double> { 10, 20, 60, 40, 20 }, "data1"); + chart.AddData(0, new List<double> { 40, 60, 90, 80, 70 }, "data2"); + yield return new WaitForSeconds(1); + } + + IEnumerator RadarUpdate() + { + var radarCoord = chart.GetChartComponent<RadarCoord>(); + radarCoord.UpdateIndicator(0, "new1", 0, 100); + chart.UpdateData(0, 0, new List<double> { 15, 30, 50, 60, 50 }); + chart.UpdateDataName(0, 0, "new1"); + yield return new WaitForSeconds(1); + } + + IEnumerator RadarAddMultiple() + { + chart.RemoveChartComponents<RadarCoord>(); + chart.RemoveData(); + + chart.GetChartComponent<Title>().text = "RadarChart - 多雷达图"; + chart.GetChartComponent<Title>().subText = ""; + + var legend = chart.GetChartComponent<Legend>(); + legend.show = true; + legend.location.align = Location.Align.TopLeft; + legend.location.top = 60; + legend.location.left = 2; + legend.itemWidth = 70; + legend.itemHeight = 20; + legend.orient = Orient.Vertical; + + var radarCoord = chart.AddChartComponent<RadarCoord>(); + radarCoord.shape = RadarCoord.Shape.Polygon; + radarCoord.center[0] = 0.25f; + radarCoord.center[1] = 0.4f; + radarCoord.radius = 0.25f; + for (int i = 1; i <= 5; i++) + { + radarCoord.AddIndicator("radar1" + i, 0, 100); + } + + var radarCoord2 = chart.AddChartComponent<RadarCoord>(); + radarCoord2.shape = RadarCoord.Shape.Polygon; + radarCoord2.center[0] = 0.75f; + radarCoord2.center[1] = 0.4f; + radarCoord2.radius = 0.25f; + for (int i = 1; i <= 5; i++) + { + radarCoord2.AddIndicator("radar2" + i, 0, 100); + } + + serie = chart.AddSerie<Radar>("test1"); + serie.radarIndex = 0; + chart.AddData(0, new List<double> { 10, 20, 60, 40, 20 }, "data1"); + + serie1 = chart.AddSerie<Radar>("test2"); + serie1.radarIndex = 1; + chart.AddData(1, new List<double> { 10, 20, 60, 40, 20 }, "data2"); + yield return new WaitForSeconds(1); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example40_Radar.cs.meta b/Assets/XCharts/Examples/Example40_Radar.cs.meta new file mode 100644 index 0000000..2421445 --- /dev/null +++ b/Assets/XCharts/Examples/Example40_Radar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95a60d7e7a0fc41ecaec5f48823b70bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example41_RadarUpdate.cs b/Assets/XCharts/Examples/Example41_RadarUpdate.cs new file mode 100644 index 0000000..03d23db --- /dev/null +++ b/Assets/XCharts/Examples/Example41_RadarUpdate.cs @@ -0,0 +1,76 @@ +using UnityEngine; +using XCharts.Runtime; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example41_RadarUpdate : MonoBehaviour + { + RadarChart chart; + int count = 0; + double max = 0; + + void Awake() + { + chart = gameObject.GetComponent<RadarChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<RadarChart>(); + chart.Init(); + } + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + UpdateData(); + count++; + } + UpdateMax(); + } + + void UpdateData() + { + var serieIndex = 0; + var serie = chart.GetSerie(serieIndex); + if (serie == null) return; + if (serie.radarType == RadarType.Multiple) + { + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.GetSerieData(i); + for (int j = 0; j < serieData.data.Count; j++) + { + var value = Random.Range(10, 100); + chart.UpdateData(serieIndex, i, j, value); + } + } + } + else + { + for (int i = 0; i < serie.dataCount; i++) + { + var value = Random.Range(10, 100); + chart.UpdateData(serieIndex, i, value); + } + } + chart.GetChartComponent<Title>().subText = "max:" + serie.context.dataMax; + } + + void UpdateMax() + { + var serieIndex = 0; + var serie = chart.GetSerie(serieIndex); + if (serie == null) return; + if (serie.context.dataMax != max) + { + chart.GetChartComponent<Title>().subText = "max:" + serie.context.dataMax; + max = serie.context.dataMax; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example41_RadarUpdate.cs.meta b/Assets/XCharts/Examples/Example41_RadarUpdate.cs.meta new file mode 100644 index 0000000..b0a750e --- /dev/null +++ b/Assets/XCharts/Examples/Example41_RadarUpdate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a2ad6907bd5045ec920b4f0e359535e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example50_Scatter.cs b/Assets/XCharts/Examples/Example50_Scatter.cs new file mode 100644 index 0000000..63d377f --- /dev/null +++ b/Assets/XCharts/Examples/Example50_Scatter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example50_Scatter : MonoBehaviour + { + private ScatterChart chart; + + void Awake() + { + chart = gameObject.GetComponent<ScatterChart>(); + if (chart == null) return; + foreach (var serie in chart.series) + { + serie.symbol.sizeFunction = SymbolSize; + } + } + + float SymbolSize(float defaultSize, SerieData serieData) + { + return defaultSize; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example50_Scatter.cs.meta b/Assets/XCharts/Examples/Example50_Scatter.cs.meta new file mode 100644 index 0000000..62269c7 --- /dev/null +++ b/Assets/XCharts/Examples/Example50_Scatter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e6c9b864ab644b45ae93df3878ab1dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example60_Heatmap.cs b/Assets/XCharts/Examples/Example60_Heatmap.cs new file mode 100644 index 0000000..3c06639 --- /dev/null +++ b/Assets/XCharts/Examples/Example60_Heatmap.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example60_Heatmap : MonoBehaviour + { + private HeatmapChart chart; + + void Awake() + { + chart = gameObject.GetComponent<HeatmapChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<HeatmapChart>(); + chart.Init(); + } + chart.GetChartComponent<Title>().text = "HeatmapChart"; + chart.GetChartComponent<Tooltip>().type = Tooltip.Type.None; + + var grid = chart.GetChartComponent<GridCoord>(); + grid.left = 100; + grid.right = 60; + grid.bottom = 60; + + var xAxis = chart.GetChartComponent<XAxis>(); + var yAxis = chart.GetChartComponent<YAxis>(); + //目前只支持Category + xAxis.type = Axis.AxisType.Category; + yAxis.type = Axis.AxisType.Category; + + xAxis.boundaryGap = true; + xAxis.boundaryGap = true; + + xAxis.splitNumber = 10; + yAxis.splitNumber = 10; + + //清空数据重新添加 + chart.RemoveData(); + var serie = chart.AddSerie<Heatmap>("serie1"); + + //设置样式 + serie.itemStyle.show = true; + serie.itemStyle.borderWidth = 1; + serie.itemStyle.borderColor = Color.clear; + + //设置高亮样式 + var emphasisStyle = serie.EnsureComponent<EmphasisStyle>(); + emphasisStyle.itemStyle.show = true; + emphasisStyle.itemStyle.borderWidth = 1; + emphasisStyle.itemStyle.borderColor = Color.black; + + //设置视觉映射组件 + var visualMap = chart.GetChartComponent<VisualMap>(); + visualMap.max = 10; + visualMap.range[0] = 0f; + visualMap.range[1] = 10f; + visualMap.orient = Orient.Vertical; + visualMap.calculable = true; + visualMap.location.align = Location.Align.BottomLeft; + visualMap.location.bottom = 100; + visualMap.location.left = 30; + + //清空颜色重新添加 + + var heatmapGridWid = 10f; + int xSplitNumber = (int) (grid.context.width / heatmapGridWid); + int ySplitNumber = (int) (grid.context.height / heatmapGridWid); + var colors = new List<string> + { + "#313695", + "#4575b4", + "#74add1", + "#abd9e9", + "#e0f3f8", + "#ffffbf", + "#fee090", + "#fdae61", + "#f46d43", + "#d73027", + "#a50026" + }; + visualMap.AddColors(colors); + //添加xAxis的数据 + for (int i = 0; i < xSplitNumber; i++) + { + chart.AddXAxisData((i + 1).ToString()); + } + //添加yAxis的数据 + for (int i = 0; i < ySplitNumber; i++) + { + chart.AddYAxisData((i + 1).ToString()); + } + for (int i = 0; i < xSplitNumber; i++) + { + for (int j = 0; j < ySplitNumber; j++) + { + var value = 0f; + var rate = Random.Range(0, 101); + if (rate > 70) value = Random.Range(8f, 10f); + else value = Random.Range(1f, 8f); + var list = new List<double> { i, j, value }; + //至少是一个三位数据:(x,y,value) + chart.AddData(0, list); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example60_Heatmap.cs.meta b/Assets/XCharts/Examples/Example60_Heatmap.cs.meta new file mode 100644 index 0000000..9ec94e3 --- /dev/null +++ b/Assets/XCharts/Examples/Example60_Heatmap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e702e0ac05be84dbe9622180d4f6ef71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example80_Polar.cs b/Assets/XCharts/Examples/Example80_Polar.cs new file mode 100644 index 0000000..3b9865b --- /dev/null +++ b/Assets/XCharts/Examples/Example80_Polar.cs @@ -0,0 +1,55 @@ +using UnityEngine; +using XCharts.Runtime; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example80_Polar : MonoBehaviour + { + private BaseChart chart; + private float updateTime; + + void Awake() + { + chart = gameObject.GetComponent<BaseChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<BaseChart>(); + chart.Init(); + } + chart.EnsureChartComponent<PolarCoord>(); + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + AddData(); + } + } + + void AddData() + { + chart.RemoveData(); + chart.GetChartComponent<Tooltip>().type = Tooltip.Type.Cross; + var angleAxis = chart.GetChartComponent<AngleAxis>(); + angleAxis.type = Axis.AxisType.Value; + angleAxis.minMaxType = Axis.AxisMinMaxType.Custom; + angleAxis.min = 0; + angleAxis.max = 360; + angleAxis.startAngle = Random.Range(0, 90); + chart.AddSerie<Line>("line1"); + + var rate = Random.Range(1, 4); + for (int i = 0; i <= 360; i++) + { + var t = i / 180f * Mathf.PI; + var r = Mathf.Sin(2 * t) * Mathf.Cos(2 * t) * rate; + chart.AddData(0, Mathf.Abs(r), i); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example80_Polar.cs.meta b/Assets/XCharts/Examples/Example80_Polar.cs.meta new file mode 100644 index 0000000..ea734cf --- /dev/null +++ b/Assets/XCharts/Examples/Example80_Polar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ca29783da761a4e0e9c5204d5b24b610 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example90_Candlestick.cs b/Assets/XCharts/Examples/Example90_Candlestick.cs new file mode 100644 index 0000000..acde88d --- /dev/null +++ b/Assets/XCharts/Examples/Example90_Candlestick.cs @@ -0,0 +1,69 @@ +using UnityEngine; +using XCharts.Runtime; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example90_Candlestick : MonoBehaviour + { + private CandlestickChart chart; + private float updateTime; + public int dataCount = 100; + + void Awake() + { + chart = gameObject.GetComponent<CandlestickChart>(); + if (chart == null) + { + chart = gameObject.AddComponent<CandlestickChart>(); + chart.Init(); + } + AddData(); + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + AddData(); + } + } + + void AddData() + { + chart.ClearData(); + + var xValue = System.DateTime.Now; + var baseValue = Random.Range(0f, 1f) * 12000; + var boxVals = new float[4]; + var dayRange = 12; + + for (int i = 0; i < dataCount; i++) + { + baseValue = baseValue + Random.Range(0f, 1f) * 30 - 10; + for (int j = 0; j < 4; j++) + { + boxVals[j] = (Random.Range(0f, 1f) - 0.5f) * dayRange + baseValue; + } + System.Array.Sort(boxVals); + var openIdx = Mathf.RoundToInt(Random.Range(0f, 1f) * 3); + var closeIdx = Mathf.RoundToInt(Random.Range(0f, 1f) * 2); + if (openIdx == closeIdx) + { + closeIdx++; + } + //var volumn = boxVals[3]*(1000+Random.Range(0f,1f) * 500); + var open = boxVals[openIdx]; + var close = boxVals[closeIdx]; + var lowest = boxVals[0]; + var heighest = boxVals[3]; + + chart.AddXAxisData(i.ToString()); + chart.AddData(0, i, open, close, lowest, heighest); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example90_Candlestick.cs.meta b/Assets/XCharts/Examples/Example90_Candlestick.cs.meta new file mode 100644 index 0000000..189a372 --- /dev/null +++ b/Assets/XCharts/Examples/Example90_Candlestick.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69c7f3bf337c843888b4a7031eef1027 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/Example_Test.cs b/Assets/XCharts/Examples/Example_Test.cs new file mode 100644 index 0000000..31f70f3 --- /dev/null +++ b/Assets/XCharts/Examples/Example_Test.cs @@ -0,0 +1,47 @@ +using UnityEngine; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example_Test : MonoBehaviour + { + BaseChart chart; + + void Awake() + { + chart = gameObject.GetComponent<BaseChart>(); + } + + void Update() + { + if (Input.GetKeyDown(KeyCode.Space)) + { + AddData(); + } + else if (Input.GetKeyDown(KeyCode.R)) + { + chart.AnimationReset(); + chart.AnimationFadeIn(); + } + else if (Input.GetKeyDown(KeyCode.U)) + { + chart.UpdateData(0, 2, 99); + } + else if (Input.GetKeyDown(KeyCode.C)) + { + chart.UpdateData(0, 2, 22); + } + } + + void AddData() + { + chart.AnimationReset(); + chart.AnimationFadeOut(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/Example_Test.cs.meta b/Assets/XCharts/Examples/Example_Test.cs.meta new file mode 100644 index 0000000..7ee7459 --- /dev/null +++ b/Assets/XCharts/Examples/Example_Test.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bac63bf58d06d47be8e1759189fbd9ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Examples/XCharts.Examples.asmdef b/Assets/XCharts/Examples/XCharts.Examples.asmdef new file mode 100644 index 0000000..2428dbe --- /dev/null +++ b/Assets/XCharts/Examples/XCharts.Examples.asmdef @@ -0,0 +1,15 @@ +{ + "name": "XCharts.Examples", + "references": [ + "XCharts.Runtime" + ], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [] +} \ No newline at end of file diff --git a/Assets/XCharts/Examples/XCharts.Examples.asmdef.meta b/Assets/XCharts/Examples/XCharts.Examples.asmdef.meta new file mode 100644 index 0000000..454b078 --- /dev/null +++ b/Assets/XCharts/Examples/XCharts.Examples.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9ca8daef375784f86b76407e76c9045a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/LICENSE.md b/Assets/XCharts/LICENSE.md new file mode 100644 index 0000000..c0a7457 --- /dev/null +++ b/Assets/XCharts/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-present, monitor1394 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Assets/XCharts/LICENSE.md.meta b/Assets/XCharts/LICENSE.md.meta new file mode 100644 index 0000000..6a70383 --- /dev/null +++ b/Assets/XCharts/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: effab8d087eba4ef1957a08a3607a0b1 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Plugins.meta b/Assets/XCharts/Plugins.meta new file mode 100644 index 0000000..a60003b --- /dev/null +++ b/Assets/XCharts/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6558d1464a47441d18df18c4a403b2f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Plugins/Download.jslib b/Assets/XCharts/Plugins/Download.jslib new file mode 100644 index 0000000..cc5b888 --- /dev/null +++ b/Assets/XCharts/Plugins/Download.jslib @@ -0,0 +1,24 @@ +mergeInto(LibraryManager.library, { + Download: function (str, fn) { + var msg = UTF8ToString(str); + var fname = UTF8ToString(fn); + function fixBinary(bin) { + var length = bin.length; + var buf = new ArrayBuffer(length); + var arr = new Uint8Array(buf); + for (var i = 0; i < length; i++) { + arr[i] = bin.charCodeAt(i); + } + return buf; + } + var binary = fixBinary(atob(msg)); + var data = new Blob([binary]); + var link = document.createElement('a'); + link.download = fname; + link.href = URL.createObjectURL(data); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } +}); + diff --git a/Assets/XCharts/Plugins/Download.jslib.meta b/Assets/XCharts/Plugins/Download.jslib.meta new file mode 100644 index 0000000..c2c8647 --- /dev/null +++ b/Assets/XCharts/Plugins/Download.jslib.meta @@ -0,0 +1,34 @@ +fileFormatVersion: 2 +guid: 821b9cd60f13648a396c76481da2191c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + isOverridable: 0 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Facebook: WebGL + second: + enabled: 1 + settings: {} + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/README-en.md b/Assets/XCharts/README-en.md new file mode 100644 index 0000000..4d034bd --- /dev/null +++ b/Assets/XCharts/README-en.md @@ -0,0 +1,115 @@ +<h2 align="center">XCharts</h2> +<p align="center"> +A powerful, easy-to-use, configurable charting and data visualization library for Unity.<br/>Unity数据可视化图表插件。<br/> +<a href="https://github.com/XCharts-Team/XCharts">中文文档</a> +</p> +<p align="center"> + <a href="https://github.com/XCharts-Team/XCharts/blob/master/LICENSE"> + <img src="https://img.shields.io/github/license/XCharts-Team/XCharts"></img> + </a> + <a href="https://github.com/XCharts-Team/XCharts/releases"> + <img src="https://img.shields.io/github/v/release/XCharts-Team/XCharts?include_prereleases"></img> + </a> + <a href=""> + <img src="https://img.shields.io/github/repo-size/monitor1394/unity-ugui-xcharts"></img> + </a> + <a href=""> + <img src="https://img.shields.io/github/languages/code-size/monitor1394/unity-ugui-xcharts"></img> + </a> + <a href=""> + <img src="https://img.shields.io/badge/Unity-5.6+-green"></img> + </a> + <a href=""> + <img src="https://img.shields.io/badge/TextMeshPro-YES-green"></img> + </a> +</p> +<p align="center"> + <a href=""> + <img src="https://img.shields.io/github/stars/XCharts-Team/XCharts?style=social"></img> + </a> + <a href=""> + <img src="https://img.shields.io/github/forks/XCharts-Team/XCharts?style=social"></img> + </a> + <a href=""> + <img src="https://img.shields.io/github/issues-closed/XCharts-Team/XCharts?color=green&label=%20%20%20%20issues&logoColor=green&style=social"></img> + </a> +</p> + +![XCharts](Documentation~/zh/img/xcharts.png) + +## Overview + +A powerful and easy-to-use data visualization library for Unity. It supports more than ten built-in charts, including line, bar, pie, radar, scatter, heatmap, ring, candlestick, polar, parallel coordinates, as well as extension charts such as 3d pie, 3d bar, 3d pyramid, funnel, gauge, liquid, pictorialbar, gantt, treemap, sankey, line3d and graph chart. + +## Key Features + +- __Pure Code Rendering__: Charts are rendered with pure code, eliminating the need for extra texture or shader resources. +- __Visual Configuration__: Configure parameters visually with real-time preview and support for dynamic configuration and data adjustments at runtime. +- __High Customizability__: Themes and configuration parameters can be adjusted as needed, with support for custom drawing and callbacks. +- __Built-in and Extension Charts__: Supports a variety of chart types, including 3D charts and special chart types like gauges and treemaps. +- __Multiple Chart Combinations__: Combine multiple charts of the same or different types within a single instance. +- __Various Coordinate Systems__: Supports different coordinate systems such as Cartesian, polar, and single axes. +- __Rich Components__: Includes titles, legends, tooltips, and more. +- __Custom Drawing__: Utilize a powerful API for custom drawing of points, lines, and other graphics. +- __Large Data Rendering__: Capable of rendering tens of thousands of data points with support for sampling rendering. +- __Custom Themes__: Customize themes and use the included light and dark default themes. +- __Animations and Interactions__: Supports various animations and interactions for a dynamic user experience. +- __Third-Party Extensions__: Integrates with TextMeshPro and the New Input System. +- __Version and Compatibility__: Compatible with all Unity versions above 5.6 and runs on all platforms. + +## Documentation + +- [XCharts3.0 Homepage](https://xcharts-team.github.io/en) +- [XCharts3.0 Tutorial](Documentation~/en/tutorial01.md) +- [XCharts3.0 API](Documentation~/en/api.md) +- [XCharts3.0 FAQ](Documentation~/en/faq.md) +- [XCharts3.0 Configurate](Documentation~/en/configuration.md) +- [XCharts3.0 Changelog](Documentation~/en/changelog.md) +- [XCharts3.0 Support](Documentation~/en/support.md) + +## Screenshots + +![buildinchart](Documentation~/en/img/readme_buildinchart.png) + +![extendchart](Documentation~/en/img/readme_extendchart.png) + +## Important Notes + +- `XCharts3.0` is not fully compatible with `XCharts2.0`. Upgrading to 3.0 may require code adjustments and reconfiguration of some charts. +- `XCharts2.0` is in the maintenance phase with only critical bug fixes applied. +- While XCharts supports Unity 5.6 and above, compatibility issues may arise due to limited testing. +- This repository contains only the `XCharts` source code. For demos, visit the [XCharts-Demo](https://github.com/XCharts-Team/XCharts-Demo) repo or the [Online Demo](https://xcharts-team.github.io/en/examples/). + +## Getting Started + +1. Import the `XCharts` unitypackage or source code into your Unity project. +2. Create a chart by right-clicking in the `Hierarchy` view and selecting `UI->XCharts->LineChart`. +3. Adjust component parameters in the `Inspector` to see real-time effects in the `Game` view. +4. For more details, refer to the [5-minute tutorial](Documentation~/en/tutorial01.md). + +## Branch Information + +- __master__ indicates the development branch. The latest changes and new features are first committed to the `master` branch, and after some time from the `master` branch `merge` to the `3.0` branch, and the `release` version. +- __3.0__ Stable branch of XCharts 3.0. It is generally updated once a month, with the latest changes from the `master` branch `merge`, and the `release` version is released. +- __2.0__ A stable branch of XCharts 2.0. With Demo, currently no longer maintenance, only to modify serious bugs. +- __2.0-upm__ Stable UMP branch of XCharts 2.0. Only the Package part is included without Demo. It is dedicated to the UMP and is not maintained. +- __1.0__ Stable branch of XCharts 1.0. With Demo, no maintenance. +- __1.0-upm__ stable UMP branch of XCharts 1.0. No Demo, no maintenance. + +## FAQ + +- __Is XCharts free to use?__ Yes, XCharts is free under the MIT license and includes value-added VIP services. +- __Does XCharts support dynamic data addition and modification?__ Yes, but data must be parsed or retrieved by the user. +- __Does this plugin work on platforms other than Unity?__ No, it is designed for Unity only. + +## Changelog + +- [Changelog](Documentation~/en/changelog.md) + +## Licenses + +- XCharts is released under the [MIT License](https://github.com/XCharts-Team/XCharts/blob/master/LICENSE.md). + +## Contact + +- For more information or support, contact us at `monitor1394@gmail.com`. diff --git a/Assets/XCharts/README-en.md.meta b/Assets/XCharts/README-en.md.meta new file mode 100644 index 0000000..66c33d2 --- /dev/null +++ b/Assets/XCharts/README-en.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7c7e32dee55f747fdba157f6230f52b2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/README.md b/Assets/XCharts/README.md new file mode 100644 index 0000000..0997d37 --- /dev/null +++ b/Assets/XCharts/README.md @@ -0,0 +1,152 @@ + +<h2 align="center">XCharts</h2> +<p align="center"> +A powerful, easy-to-use, configurable charting and data visualization library for Unity.<br/>Unity数据可视化图表插件。<br/> +<a href="README-en.md">English README</a> +</p> +<p align="center"> + <a href="https://github.com/XCharts-Team/XCharts/blob/master/LICENSE"> + <img src="https://img.shields.io/github/license/XCharts-Team/XCharts"></img> + </a> + <a href="https://github.com/XCharts-Team/XCharts/releases"> + <img src="https://img.shields.io/github/v/release/XCharts-Team/XCharts?include_prereleases"></img> + </a> + <a href="https://github.com/XCharts-Team/XCharts"> + <img src="https://img.shields.io/github/repo-size/monitor1394/unity-ugui-xcharts"></img> + </a> + <a href="https://github.com/XCharts-Team/XCharts"> + <img src="https://img.shields.io/github/languages/code-size/monitor1394/unity-ugui-xcharts"></img> + </a> + <a href="https://xcharts-team.github.io/docs/tutorial01"> + <img src="https://img.shields.io/badge/Unity-5.6+-green"></img> + </a> + <a href="https://xcharts-team.github.io/docs/tutorial01"> + <img src="https://img.shields.io/badge/TextMeshPro-YES-green"></img> + </a> +</p> +<p align="center"> + <a href="https://github.com/XCharts-Team/XCharts/stargazers"> + <img src="https://img.shields.io/github/stars/XCharts-Team/XCharts?style=social"></img> + </a> + <a href="https://github.com/XCharts-Team/XCharts/forks"> + <img src="https://img.shields.io/github/forks/XCharts-Team/XCharts?style=social"></img> + </a> + <a href="https://github.com/XCharts-Team/XCharts/issues"> + <img src="https://img.shields.io/github/issues-closed/XCharts-Team/XCharts?color=green&label=%20%20%20%20issues&logoColor=green&style=social"></img> + </a> +</p> + +![XCharts](Documentation~/zh/img/xcharts.png) + +XCharts 是一款基于 UGUI 的功能强大、简单易用的 Unity 数据可视化图表插件。它提供了丰富的图表类型和灵活的配置选项,帮助开发者快速实现专业级的数据可视化效果。支持折线图、柱状图、饼图、雷达图、散点图、热力图、环形图、K线图、极坐标、平行坐标等十多种常用的内置图表。提供3D饼图、3D柱图、3D金字塔、漏斗图、仪表盘、水位图、象形柱图、甘特图、矩形树图、桑基图、3D折线图、关系图等十多种高级扩展图表。 + +[XCharts 官方主页](https://xcharts-team.github.io) +[XCharts 在线示例](https://xcharts-team.github.io/examples) + +[XCharts 教程:5分钟上手 XCharts](Documentation~/zh/tutorial01.md) +[XCharts API文档](Documentation~/zh/api.md) +[XCharts 常见问题](Documentation~/zh/faq.md) +[XCharts 配置项手册](Documentation~/zh/configuration.md) +[XCharts 更新日志](Documentation~/zh/changelog.md) +[XCharts 订阅服务](Documentation~/zh/support.md) + +## 特性 + +- __纯代码绘制__:图表完全通过代码生成,无需额外贴图或 Shader 资源,轻量高效。 +- __可视化配置__:提供直观的参数配置界面,支持实时预览效果,并可在运行时动态修改配置和数据。 +- __高度定制化__:支持从主题、组件到数据项的全面参数设置,同时允许通过代码自定义绘制逻辑、回调函数及图表实现。 +- __多内置图表__:支持线图、柱状图、饼图、雷达图、散点图、热力图、环形图、K线图、极坐标、平行坐标等多种常用的内置图表。 +- __多扩展图表__:支持3D柱图、3D饼图、漏斗图、金字塔、仪表盘、水位图、象形柱图、甘特图、矩形树图、桑基图、3D折线图、关系图等多种高级扩展图表,满足复杂数据可视化需求。 +- __多扩展组件__:支持多种实用 UI 组件,如表格、统计数值、滑动条、进度条等,增强图表交互性。 +- __多图表组合__:支持在同一图表中组合显示多个相同或不同类型的图表,满足复杂场景需求。 +- __多种坐标系__:支持直角坐标系、极坐标系、单轴等多种坐标系,适应不同数据展示需求。 +- __丰富的组件__:提供标题、图例、提示框、标线、标域、数据区域缩放、视觉映射等常用组件,提升图表可读性。 +- __多样式线图__:支持直线、曲线、虚线、面积图、阶梯线图等多种线图样式,满足不同数据趋势展示需求。 +- __多样式柱图__:支持并列柱图、堆叠柱图、堆积百分比柱图、斑马柱图、胶囊柱图等多种柱状图样式。 +- __多样式饼图__:支持环形图、玫瑰图、环形玫瑰图等多种饼图样式,直观展示数据占比。 +- __自定义绘制__:提供强大的绘图 API,支持自定义绘制点、线、面等图形,满足个性化需求。 +- __大数据绘制__:支持万级数据量绘制,优化性能表现;支持采样绘制,进一步提升大数据场景下的性能。 +- __自定义主题__:支持主题定制、导入和导出,内置明暗两种默认主题,轻松适配不同应用场景。 +- __动画和交互__:支持渐入、渐出、变更、新增等多种动画效果,以及数据筛选、视图缩放、细节展示等交互操作,提升用户体验。 +- __第三方扩展__:无缝集成TexMeshPro和New Input System,扩展功能兼容性。 +- __版本和兼容__:支持 Unity 5.6 及以上版本,兼容全平台运行。 + +## 截图 + +![内置图表](Documentation~/zh/img/readme_buildinchart.png) + +![扩展图表](Documentation~/zh/img/readme_extendchart.png) + +## 使用 + +- 导入`XCharts`的`unitypackage`或者源码到项目。建议也导入`XCharts`守护程序 [XCharts-Daemon](https://github.com/XCharts-Team/XCharts-Daemon)。 +- 在`Hierarchy`视图下右键选择`XCharts->LineChart`,即可创建一个默认的折线图。 +- 用`Inspector`视图下的`Add Serie`和`Add Main Component`按钮可以添加`Serie`和`组件`。 +- 在`Inspector`视图下可以调整各个组件的参数,`Game`视图可看到实时效果。 +- 更多细节,请看[【XCharts教程:5分钟上手教程】](Documentation~/zh/tutorial01.md)。 +- 首次使用,建议先认真看一遍教程。 + +## 常见问题 (FAQ) + +- __XCharts 可以免费使用吗?__ + XCharts 基于 MIT 协议,核心功能完全免费。您也可以订阅 VIP 服务,享受更多高级功能和专属技术支持。 + +- __XCharts 支持代码动态添加和修改数据吗?__ + 是的,XCharts 提供了丰富的数据操作接口,支持代码动态修改配置和数据。但数据来源(如 Excel 或数据库)需要您自行解析后调用 XCharts 接口添加到图表中。 + +- __XCharts 支持哪些平台?__ + XCharts 专为 Unity 平台设计,支持 Unity 5.6 及以上版本。理论上,任何支持 UGUI 的 Unity 版本均可运行 XCharts。目前不支持 Winform 或 WPF 等其他平台。 + +- __如何解决锯齿问题?XCharts 支持多大的数据量?__ + XCharts 基于 UGUI 实现,因此 UGUI 的常见问题(如锯齿、Mesh 顶点数限制)在 XCharts 中也会存在。 + - __锯齿问题__:可通过调整抗锯齿设置或使用更高分辨率解决。 + - __数据量限制__:单条折线图(Line)支持约 2 万数据点,开启采样后可支持更多数据,但会消耗更多 CPU 资源。 + 更多解决方案请参考 [问答 16](Documentation~/zh/faq.md) 和 [问答 27](Documentation~/zh/faq.md)。 + +- __哪里可以查看 Demo?__ + 本仓库仅包含 XCharts 源码,Demo 示例请访问 [XCharts-Demo](https://github.com/XCharts-Team/XCharts-Demo) 仓库。您也可以在浏览器中查看 [在线 Demo](https://xcharts-team.github.io/examples/)。 + +## 日志 + +- 各版本的详细更新日志请查看 [更新日志](Documentation~/zh/changelog.md) + +## 扩展 + +- __[XCharts](https://github.com/XCharts-Team/XCharts)__ 核心功能,完全开源免费 +- __[XCharts-Daemon](https://github.com/XCharts-Team/XCharts-Daemon)__ 守护程序,确保XCharts更新时的编译正常 +- __[XCharts-Demo](https://github.com/XCharts-Team/XCharts-Demo)__ 官方示例(不包含扩展图表的示例) +- __[XCharts-Pro](https://github.com/XCharts-Team/XCharts-Pro)__ 专业版,包含所有扩展图表和扩展组件(需订阅 SVIP) +- __[XCharts-Pro-Demo](https://github.com/XCharts-Team/XCharts-Pro-Demo)__ 专业版官方示例(需订阅 SVIP) +- __[XCharts-UI](https://github.com/XCharts-Team/XCharts-UI)__ 扩展UI组件(需订阅 VIP) +- __[XCharts-Bar3DChart](https://github.com/XCharts-Team/XCharts-Bar3DChart)__ 3D柱图(需订阅 VIP) +- __[XCharts-FunnelChart](https://github.com/XCharts-Team/XCharts-FunnelChart)__ 漏斗图(需订阅 VIP) +- __[XCharts-GanttChart](https://github.com/XCharts-Team/XCharts-GanttChart)__ 甘特图(需订阅 VIP) +- __[XCharts-GaugeChart](https://github.com/XCharts-Team/XCharts-GaugeChart)__ 仪表盘(需订阅 VIP) +- __[XCharts-LiquidChart](https://github.com/XCharts-Team/XCharts-LiquidChart)__ 水位图(需订阅 VIP) +- __[XCharts-PictorialBarChart](https://github.com/XCharts-Team/XCharts-PictorialBarChart)__ 象形住图(需订阅 VIP) +- __[XCharts-Pie3DChart](https://github.com/XCharts-Team/XCharts-Pie3DChart)__ 3D饼图(需订阅 VIP) +- __[XCharts-PyramidChart](https://github.com/XCharts-Team/XCharts-PyramidChart)__ 3D金字塔(需订阅 VIP) +- __[XCharts-TreemapChart](https://github.com/XCharts-Team/XCharts-TreemapChart)__ 矩形树图(需订阅 VIP) +- __[XCharts-SankeyChart](https://github.com/XCharts-Team/XCharts-SankeyChart)__ 桑基图(需订阅 VIP) +- __[XCharts-Line3DChart](https://github.com/XCharts-Team/XCharts-Line3DChart)__ 3D折线图(需订阅 VIP) +- __[XCharts-GraphChart](https://github.com/XCharts-Team/XCharts-GraphChart)__ 关系图(需订阅 VIP) + +## 许可 + +- __[MIT License](https://github.com/XCharts-Team/XCharts/blob/master/LICENSE.md)__:XCharts 核心库基于 MIT 协议,允许免费商用和二次开发。 + +- __扩展功能授权__:扩展图表和高级功能需订阅 VIP 或 SVIP 服务获得使用许可。 + +## 订阅 + +- __核心功能免费__:XCharts 核心库基于 MIT 协议完全开源,可免费使用。 +- __增值服务__:为满足多样化需求,我们提供多种订阅服务,详情请查看 [订阅详情](Documentation~/zh/support.md)。 +- __灵活选择__:订阅非强制,不影响核心功能使用。 +- __按年付费__:订阅服务按年计费,到期后可选择续订。中断订阅后,将无法享受更新和技术支持服务。 + +## 其他 + +- 邮箱:`monitor1394@gmail.com` +- QQ群:XCharts交流群(`202030963`) +- VIP群:XCharts VIP群(`867291970`) +- 支持与合作:[订阅与支持](Documentation~/zh/support.md) diff --git a/Assets/XCharts/README.md.meta b/Assets/XCharts/README.md.meta new file mode 100644 index 0000000..760d09c --- /dev/null +++ b/Assets/XCharts/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 393c8e8ab781b4041b141f93eb407380 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Resources.meta b/Assets/XCharts/Resources.meta new file mode 100644 index 0000000..f5614c7 --- /dev/null +++ b/Assets/XCharts/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0e3168b99564b477a83640c24b713f0c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Resources/XCLang-EN.asset b/Assets/XCharts/Resources/XCLang-EN.asset new file mode 100644 index 0000000..05e55c4 --- /dev/null +++ b/Assets/XCharts/Resources/XCLang-EN.asset @@ -0,0 +1,57 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b65fc8b25febc4b9e8acb500d16770b2, type: 3} + m_Name: XCLang-EN + m_EditorClassIdentifier: + langName: EN + time: + months: + - January + - February + - March + - April + - May + - June + - July + - August + - September + - October + - November + - December + monthAbbr: + - Jan + - Feb + - Mar + - Apr + - May + - Jun + - Jul + - Aug + - Sep + - Oct + - Nov + - Dec + dayOfWeek: + - Sunday + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + dayOfWeekAbbr: + - Sun + - Mon + - Tue + - Wed + - Thu + - Fri + - Sat diff --git a/Assets/XCharts/Resources/XCLang-EN.asset.meta b/Assets/XCharts/Resources/XCLang-EN.asset.meta new file mode 100644 index 0000000..e0f7d44 --- /dev/null +++ b/Assets/XCharts/Resources/XCLang-EN.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cfc5541268f414098950441fd8b6f4a7 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Resources/XCLang-ZH.asset b/Assets/XCharts/Resources/XCLang-ZH.asset new file mode 100644 index 0000000..2e53acc --- /dev/null +++ b/Assets/XCharts/Resources/XCLang-ZH.asset @@ -0,0 +1,89 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b65fc8b25febc4b9e8acb500d16770b2, type: 3} + m_Name: XCLang-ZH + m_EditorClassIdentifier: + langName: ZH + time: + months: + - "\u4E00\u6708" + - "\u4E8C\u6708" + - "\u4E09\u6708" + - "\u56DB\u6708" + - "\u4E94\u6708" + - "\u516D\u6708" + - "\u4E03\u6708" + - "\u516B\u6708" + - "\u4E5D\u6708" + - "\u5341\u6708" + - "\u5341\u4E00\u6708" + - "\u5341\u4E8C\u6708" + monthAbbr: + - "1\u6708" + - "2\u6708" + - "3\u6708" + - "4\u6708" + - "5\u6708" + - "6\u6708" + - "7\u6708" + - "8\u6708" + - "9\u6708" + - "10\u6708" + - "11\u6708" + - "12\u6708" + dayOfMonth: + - "1\u65E5" + - "2\u65E5" + - "3\u65E5" + - "4\u65E5" + - "5\u65E5" + - "6\u65E5" + - "7\u65E5" + - "8\u65E5" + - "9\u65E5" + - "10\u65E5" + - "11\u65E5" + - "12\u65E5" + - "13\u65E5" + - "14\u65E5" + - "15\u65E5" + - "16\u65E5" + - "17\u65E5" + - "18\u65E5" + - "19\u65E5" + - "20\u65E5" + - "21\u65E5" + - "22\u65E5" + - "23\u65E5" + - "24\u65E5" + - "25\u65E5" + - "26\u65E5" + - "27\u65E5" + - "28\u65E5" + - "29\u65E5" + - "30\u65E5" + - "31\u65E5" + dayOfWeek: + - "\u661F\u671F\u65E5" + - "\u661F\u671F\u4E00" + - "\u661F\u671F\u4E8C" + - "\u661F\u671F\u4E09" + - "\u661F\u671F\u56DB" + - "\u661F\u671F\u4E94" + - "\u661F\u671F\u516D" + dayOfWeekAbbr: + - "\u65E5" + - "\u4E00" + - "\u4E8C" + - "\u4E09" + - "\u56DB" + - "\u4E94" + - "\u516D" diff --git a/Assets/XCharts/Resources/XCLang-ZH.asset.meta b/Assets/XCharts/Resources/XCLang-ZH.asset.meta new file mode 100644 index 0000000..f638b16 --- /dev/null +++ b/Assets/XCharts/Resources/XCLang-ZH.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79b252423c47d4cf380e489ed55e05d4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Resources/XCSettings.asset b/Assets/XCharts/Resources/XCSettings.asset new file mode 100644 index 0000000..07e20fb --- /dev/null +++ b/Assets/XCharts/Resources/XCSettings.asset @@ -0,0 +1,52 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3694d869548264b718bdfc6c8009dcf1, type: 3} + m_Name: XCSettings + m_EditorClassIdentifier: + m_Lang: {fileID: 11400000, guid: 79b252423c47d4cf380e489ed55e05d4, type: 2} + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSizeLv1: 24 + m_FontSizeLv2: 22 + m_FontSizeLv3: 20 + m_FontSizeLv4: 18 + m_AxisLineType: 0 + m_AxisLineWidth: 0.8 + m_AxisSplitLineType: 0 + m_AxisSplitLineWidth: 0.8 + m_AxisTickWidth: 0.8 + m_AxisTickLength: 5 + m_GaugeAxisLineWidth: 15 + m_GaugeAxisSplitLineWidth: 0.8 + m_GaugeAxisSplitLineLength: 15 + m_GaugeAxisTickWidth: 0.8 + m_GaugeAxisTickLength: 5 + m_TootipLineWidth: 0.8 + m_DataZoomBorderWidth: 0.5 + m_DataZoomDataLineWidth: 0.5 + m_VisualMapBorderWidth: 0 + m_SerieLineWidth: 1.8 + m_SerieLineSymbolSize: 5 + m_SerieScatterSymbolSize: 20 + m_SerieSelectedRate: 1.3 + m_SerieCandlestickBorderWidth: 1 + m_EditorShowAllListData: 0 + m_MaxPainter: 10 + m_LineSmoothStyle: 3 + m_LineSmoothness: 2 + m_LineSegmentDistance: 3 + m_CicleSmoothness: 2 + m_VisualMapTriangeLen: 20 + m_CustomThemes: + - {fileID: 11400000, guid: 289d2fc7f4ce24f73b9ed8ec52639f72, type: 2} + - {fileID: 11400000, guid: e1dc23a10de1e4c5dbfbaf74c4dfd218, type: 2} + - {fileID: 11400000, guid: f917f38ce737f4563a377883dccaff8f, type: 2} + - {fileID: 11400000, guid: 376d15d5e9b694d75965c837a0fe1222, type: 2} diff --git a/Assets/XCharts/Resources/XCSettings.asset.meta b/Assets/XCharts/Resources/XCSettings.asset.meta new file mode 100644 index 0000000..c83763d --- /dev/null +++ b/Assets/XCharts/Resources/XCSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 676e1e322123d4fe2a761de3ef14235f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Resources/XCTheme-Dark.asset b/Assets/XCharts/Resources/XCTheme-Dark.asset new file mode 100644 index 0000000..63d2600 --- /dev/null +++ b/Assets/XCharts/Resources/XCTheme-Dark.asset @@ -0,0 +1,203 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c59330ca0f4443b69f06b890a44f32e, type: 3} + m_Name: XCTheme-Dark + m_EditorClassIdentifier: + m_ThemeType: 2 + m_ThemeName: Dark + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_ContrastColor: + serializedVersion: 2 + rgba: 0 + m_BackgroundColor: + serializedVersion: 2 + rgba: 4280945680 + m_ColorPalette: + - serializedVersion: 2 + rgba: 4294939209 + - serializedVersion: 2 + rgba: 4289920892 + - serializedVersion: 2 + rgba: 4284538365 + - serializedVersion: 2 + rgba: 4285951743 + - serializedVersion: 2 + rgba: 4294564184 + - serializedVersion: 2 + rgba: 4287741957 + - serializedVersion: 2 + rgba: 4282747647 + - serializedVersion: 2 + rgba: 4293085325 + - serializedVersion: 2 + rgba: 4294932957 + m_Common: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 20 + m_Title: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.93333334, g: 0.94509804, b: 0.98039216, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 24 + m_SubTitle: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 22 + m_Legend: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 20 + m_UnableColor: {r: 0.8, g: 0.8, b: 0.8, a: 1} + m_Axis: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 18 + m_LineType: 0 + m_LineWidth: 0.8 + m_LineLength: 0 + m_LineColor: + serializedVersion: 2 + rgba: 4291737785 + m_SplitLineType: 0 + m_SplitLineWidth: 0.8 + m_SplitLineLength: 0 + m_SplitLineColor: + serializedVersion: 2 + rgba: 4283647816 + m_TickWidth: 0.8 + m_TickLength: 5 + m_TickColor: + serializedVersion: 2 + rgba: 4291737785 + m_SplitAreaColors: + - serializedVersion: 2 + rgba: 100663295 + - serializedVersion: 2 + rgba: 218103807 + m_Gauge: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 18 + m_LineType: 0 + m_LineWidth: 15 + m_LineLength: 0 + m_LineColor: + serializedVersion: 2 + rgba: 4291737785 + m_SplitLineType: 0 + m_SplitLineWidth: 0.8 + m_SplitLineLength: 15 + m_SplitLineColor: + serializedVersion: 2 + rgba: 4294967295 + m_TickWidth: 0.8 + m_TickLength: 5 + m_TickColor: + serializedVersion: 2 + rgba: 4294967295 + m_SplitAreaColors: + - serializedVersion: 2 + rgba: 100663295 + - serializedVersion: 2 + rgba: 218103807 + m_BarBackgroundColor: + serializedVersion: 2 + rgba: 4291348680 + m_StageColor: + - m_Percent: 0.2 + m_Color: + serializedVersion: 2 + rgba: 4289644433 + - m_Percent: 0.8 + m_Color: + serializedVersion: 2 + rgba: 4288579171 + - m_Percent: 1 + m_Color: + serializedVersion: 2 + rgba: 4281415106 + m_Tooltip: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_TextBackgroundColor: {r: 0.31764707, g: 0.31764707, b: 0.31764707, a: 0.78431374} + m_FontSize: 22 + m_LineType: 0 + m_LineWidth: 0.8 + m_LineColor: + serializedVersion: 2 + rgba: 4293848814 + m_AreaColor: + serializedVersion: 2 + rgba: 542200145 + m_LabelTextColor: + serializedVersion: 2 + rgba: 4294967295 + m_LabelBackgroundColor: + serializedVersion: 2 + rgba: 4289177511 + m_DataZoom: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 20 + m_BorderWidth: 0.5 + m_DataLineWidth: 0.5 + m_FillerColor: + serializedVersion: 2 + rgba: 869180295 + m_BorderColor: + serializedVersion: 2 + rgba: 4287262833 + m_DataLineColor: + serializedVersion: 2 + rgba: 4287262833 + m_DataAreaColor: + serializedVersion: 2 + rgba: 4287262833 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_VisualMap: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.7254902, g: 0.72156864, b: 0.80784315, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 18 + m_BorderWidth: 0 + m_BorderColor: + serializedVersion: 2 + rgba: 4291611852 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_TriangeLen: 20 + m_Serie: + m_LineWidth: 1.8 + m_LineSymbolSize: 5 + m_ScatterSymbolSize: 20 + m_CandlestickColor: + serializedVersion: 2 + rgba: 4283846390 + m_CandlestickColor0: + serializedVersion: 2 + rgba: 4287818324 + m_CandlestickBorderWidth: 1 + m_CandlestickBorderColor: + serializedVersion: 2 + rgba: 4283846390 + m_CandlestickBorderColor0: + serializedVersion: 2 + rgba: 4287818324 diff --git a/Assets/XCharts/Resources/XCTheme-Dark.asset.meta b/Assets/XCharts/Resources/XCTheme-Dark.asset.meta new file mode 100644 index 0000000..c6e8422 --- /dev/null +++ b/Assets/XCharts/Resources/XCTheme-Dark.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 289d2fc7f4ce24f73b9ed8ec52639f72 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Resources/XCTheme-Default.asset b/Assets/XCharts/Resources/XCTheme-Default.asset new file mode 100644 index 0000000..53fae3e --- /dev/null +++ b/Assets/XCharts/Resources/XCTheme-Default.asset @@ -0,0 +1,160 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c59330ca0f4443b69f06b890a44f32e, type: 3} + m_Name: XCTheme-Default + m_EditorClassIdentifier: + m_ThemeType: 0 + m_ThemeName: Default + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_ContrastColor: + serializedVersion: 2 + rgba: 0 + m_BackgroundColor: + serializedVersion: 2 + rgba: 4294967295 + m_ColorPalette: + - serializedVersion: 2 + rgba: 4291194964 + - serializedVersion: 2 + rgba: 4285910161 + - serializedVersion: 2 + rgba: 4284008698 + - serializedVersion: 2 + rgba: 4284901102 + - serializedVersion: 2 + rgba: 4292788339 + - serializedVersion: 2 + rgba: 4285702715 + - serializedVersion: 2 + rgba: 4283598076 + - serializedVersion: 2 + rgba: 4290011290 + - serializedVersion: 2 + rgba: 4291591402 + m_Common: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.31764707, g: 0.3019608, b: 0.3019608, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 20 + m_Title: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.31764707, g: 0.3019608, b: 0.3019608, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 24 + m_SubTitle: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.5882353, g: 0.5882353, b: 0.5882353, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 22 + m_Legend: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.31764707, g: 0.3019608, b: 0.3019608, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 20 + m_UnableColor: {r: 0.8, g: 0.8, b: 0.8, a: 1} + m_Axis: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.31764707, g: 0.3019608, b: 0.3019608, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 18 + m_LineType: 0 + m_LineWidth: 0.8 + m_LineLength: 0 + m_LineColor: + serializedVersion: 2 + rgba: 4283256145 + m_SplitLineType: 0 + m_SplitLineWidth: 0.8 + m_SplitLineLength: 0 + m_SplitLineColor: + serializedVersion: 2 + rgba: 542200145 + m_TickWidth: 0.8 + m_TickLength: 5 + m_TickColor: + serializedVersion: 2 + rgba: 4283256145 + m_SplitAreaColors: + - serializedVersion: 2 + rgba: 1308293882 + - serializedVersion: 2 + rgba: 1305004232 + m_Tooltip: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_TextBackgroundColor: {r: 1, g: 1, b: 1, a: 1} + m_FontSize: 20 + m_LineType: 0 + m_LineWidth: 0.8 + m_LineColor: + serializedVersion: 2 + rgba: 1680419113 + m_AreaColor: + serializedVersion: 2 + rgba: 542200145 + m_LabelTextColor: + serializedVersion: 2 + rgba: 4294967295 + m_LabelBackgroundColor: + serializedVersion: 2 + rgba: 4280887593 + m_DataZoom: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 20 + m_BorderWidth: 0.5 + m_DataLineWidth: 0.5 + m_FillerColor: + serializedVersion: 2 + rgba: 1858910119 + m_BorderColor: + serializedVersion: 2 + rgba: 4292730333 + m_DataLineColor: + serializedVersion: 2 + rgba: 4283712815 + m_DataAreaColor: + serializedVersion: 2 + rgba: 1431586095 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_VisualMap: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_TextColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_TextBackgroundColor: {r: 0, g: 0, b: 0, a: 0} + m_FontSize: 18 + m_BorderWidth: 0 + m_BorderColor: + serializedVersion: 2 + rgba: 4291611852 + m_BackgroundColor: + serializedVersion: 2 + rgba: 0 + m_TriangeLen: 20 + m_Serie: + m_LineWidth: 1.8 + m_LineSymbolSize: 5 + m_ScatterSymbolSize: 20 + m_CandlestickColor: + serializedVersion: 2 + rgba: 4283716843 + m_CandlestickColor0: + serializedVersion: 2 + rgba: 4284658247 + m_CandlestickBorderWidth: 1 + m_CandlestickBorderColor: + serializedVersion: 2 + rgba: 4283716843 + m_CandlestickBorderColor0: + serializedVersion: 2 + rgba: 4284658247 diff --git a/Assets/XCharts/Resources/XCTheme-Default.asset.meta b/Assets/XCharts/Resources/XCTheme-Default.asset.meta new file mode 100644 index 0000000..059fff7 --- /dev/null +++ b/Assets/XCharts/Resources/XCTheme-Default.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e1dc23a10de1e4c5dbfbaf74c4dfd218 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime.meta b/Assets/XCharts/Runtime.meta new file mode 100644 index 0000000..bfcc193 --- /dev/null +++ b/Assets/XCharts/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b33410c335fd5440483c5cabb05c3e5d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart.meta b/Assets/XCharts/Runtime/Chart.meta new file mode 100644 index 0000000..8fe8fd6 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 900b05585ba864df1aa05dcdb36b324b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/BarChart.cs b/Assets/XCharts/Runtime/Chart/BarChart.cs new file mode 100644 index 0000000..7cd9c9a --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/BarChart.cs @@ -0,0 +1,178 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Bar chart shows different data through the height of a bar, which is used in rectangular coordinate with at least 1 category axis. + /// || 柱状图(或称条形图)是一种通过柱形的高度(横向的情况下则是宽度)来表现数据大小的一种常用图表类型。 + /// </summary> + [AddComponentMenu("XCharts/BarChart", 14)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class BarChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + EnsureChartComponent<XAxis>(); + EnsureChartComponent<YAxis>(); + + RemoveData(); + Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + for (int i = 0; i < 5; i++) + { + AddXAxisData("x" + (i + 1)); + } + } + + /// <summary> + /// default zebra column chart. + /// || 斑马柱状图。 + /// </summary> + public void DefaultZebraColumnChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.barType = BarType.Zebra; + } + + /// <summary> + /// default capsule column chart. + /// || 胶囊柱状图。 + /// </summary> + public void DefaultCapsuleColumnChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.barType = BarType.Capsule; + } + + /// <summary> + /// default grouped column chart. + /// || 默认分组柱状图。 + /// </summary> + public void DefaultGroupedColumnChart() + { + CheckChartInit(); + Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + } + + /// <summary> + /// default stacked column chart. + /// || 默认堆叠分组柱状图。 + /// </summary> + public void DefaultStackedColumnChart() + { + CheckChartInit(); + var serie1 = GetSerie(0); + serie1.stack = "stack1"; + var serie2 = Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie2.stack = "stack1"; + } + + /// <summary> + /// default percent column chart. + /// || 默认百分比柱状图。 + /// </summary> + public void DefaultPercentColumnChart() + { + CheckChartInit(); + var serie1 = GetSerie(0); + serie1.stack = "stack1"; + serie1.barPercentStack = true; + var serie2 = Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie2.stack = "stack1"; + serie2.barPercentStack = true; + } + + /// <summary> + /// default bar chart. + /// || 默认条形图。 + /// </summary> + public void DefaultBarChart() + { + CheckChartInit(); + CovertColumnToBar(this); + } + + /// <summary> + /// default zebra bar chart. + /// || 默认斑马条形图。 + /// </summary> + public void DefaultZebraBarChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.barType = BarType.Zebra; + CovertColumnToBar(this); + } + + /// <summary> + /// default capsule bar chart. + /// || 默认胶囊条形图。 + /// </summary> + public void DefaultCapsuleBarChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.barType = BarType.Capsule; + CovertColumnToBar(this); + } + + /// <summary> + /// default grouped bar chart. + /// || 默认分组条形图。 + /// </summary> + public void DefaultGroupedBarChart() + { + CheckChartInit(); + Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + CovertColumnToBar(this); + } + + /// <summary> + /// default stacked bar chart. + /// || 默认堆叠条形图。 + /// </summary> + public void DefaultStackedBarChart() + { + CheckChartInit(); + var serie1 = GetSerie(0); + serie1.stack = "stack1"; + var serie2 = Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie2.stack = "stack1"; + CovertColumnToBar(this); + } + + /// <summary> + /// default percent bar chart. + /// || 默认百分比条形图。 + /// </summary> + public void DefaultPercentBarChart() + { + CheckChartInit(); + var serie1 = GetSerie(0); + serie1.stack = "stack1"; + serie1.barPercentStack = true; + var serie2 = Bar.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie2.stack = "stack1"; + serie2.barPercentStack = true; + CovertColumnToBar(this); + } + + private static void CovertColumnToBar(BarChart chart) + { + chart.ConvertXYAxis(0); + var xAxis = chart.GetChartComponent<XAxis>(); + xAxis.axisLine.show = false; + xAxis.axisTick.show = false; + + var yAxis = chart.GetChartComponent<YAxis>(); + yAxis.axisTick.alignWithLabel = true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/BarChart.cs.meta b/Assets/XCharts/Runtime/Chart/BarChart.cs.meta new file mode 100644 index 0000000..76be77d --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/BarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 535d2697503c2a94a887354e22a5414d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/CandlestickChart.cs b/Assets/XCharts/Runtime/Chart/CandlestickChart.cs new file mode 100644 index 0000000..8da694b --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/CandlestickChart.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// A candlestick chart is a style of financial chart used to describe price movements of a security, derivative, or currency. + /// || 蜡烛图,也叫K线图,用于描述证券、衍生品或货币的价格走势的一种金融图表样式。 + /// </summary> + [AddComponentMenu("XCharts/CandlestickChart", 23)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class CandlestickChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + EnsureChartComponent<XAxis>(); + EnsureChartComponent<YAxis>(); + + RemoveData(); + var serie = Candlestick.AddDefaultSerie(this, GenerateDefaultSerieName()); + for (int i = 0; i < serie.dataCount; i++) + { + AddXAxisData("x" + (i + 1)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/CandlestickChart.cs.meta b/Assets/XCharts/Runtime/Chart/CandlestickChart.cs.meta new file mode 100644 index 0000000..359211c --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/CandlestickChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b64f0bb738cc4acfa72fff2c30212b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/HeatmapChart.cs b/Assets/XCharts/Runtime/Chart/HeatmapChart.cs new file mode 100644 index 0000000..44cc76f --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/HeatmapChart.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Heat map mainly use colors to represent values, which must be used along with visualMap component. + /// It can be used in either rectangular coordinate or geographic coordinate. But the behaviour on them are quite different. Rectangular coordinate must have two categories to use it. + /// ||热力图主要通过颜色去表现数值的大小,必须要配合 visualMap 组件使用。 + /// 可以应用在直角坐标系以及地理坐标系上,这两个坐标系上的表现形式相差很大,直角坐标系上必须要使用两个类目轴。 + /// </summary> + [AddComponentMenu("XCharts/HeatmapChart", 18)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class HeatmapChart : BaseChart + { + protected override void DefaultChart() + { + var grid = EnsureChartComponent<GridCoord>(); + grid.UpdateRuntimeData(this); + grid.left = 0.12f; + + var heatmapGridWid = 18f; + int xSplitNumber = (int)(grid.context.width / heatmapGridWid); + int ySplitNumber = (int)(grid.context.height / heatmapGridWid); + + var xAxis = EnsureChartComponent<XAxis>(); + xAxis.type = Axis.AxisType.Category; + xAxis.splitLine.show = false; + xAxis.boundaryGap = true; + xAxis.splitNumber = xSplitNumber / 2; + + var yAxis = EnsureChartComponent<YAxis>(); + yAxis.type = Axis.AxisType.Category; + yAxis.splitLine.show = false; + yAxis.boundaryGap = true; + yAxis.splitNumber = ySplitNumber; + RemoveData(); + + Heatmap.AddDefaultSerie(this, GenerateDefaultSerieName()); + + var visualMap = EnsureChartComponent<VisualMap>(); + visualMap.autoMinMax = true; + visualMap.orient = Orient.Vertical; + visualMap.calculable = true; + visualMap.location.align = Location.Align.BottomLeft; + visualMap.location.bottom = 100; + visualMap.location.left = 30; + var colors = new List<string> + { + "#313695", + "#4575b4", + "#74add1", + "#abd9e9", + "#e0f3f8", + "#ffffbf", + "#fee090", + "#fdae61", + "#f46d43", + "#d73027", + "#a50026" + }; + visualMap.AddColors(colors); + for (int i = 0; i < xSplitNumber; i++) + { + xAxis.data.Add((i + 1).ToString()); + } + for (int i = 0; i < ySplitNumber; i++) + { + yAxis.data.Add((i + 1).ToString()); + } + for (int i = 0; i < xSplitNumber; i++) + { + for (int j = 0; j < ySplitNumber; j++) + { + var value = Random.Range(0, 150); + var list = new List<double> { i, j, value }; + AddData(0, list); + } + } + } + + /// <summary> + /// default count heatmap chart. + /// || 默认计数热力图。 + /// </summary> + public void DefaultCountHeatmapChart() + { + CheckChartInit(); + + var serie = GetSerie<Heatmap>(0); + serie.heatmapType = HeatmapType.Count; + var xAxis = GetChartComponent<XAxis>(); + xAxis.type = Axis.AxisType.Value; + xAxis.splitNumber = 4; + + var yAxis = GetChartComponent<YAxis>(); + yAxis.type = Axis.AxisType.Value; + yAxis.splitNumber = 2; + + serie.ClearData(); + for (int i = 0; i < 100; i++) + { + var x = UnityEngine.Random.Range(0, 100); + var y = UnityEngine.Random.Range(0, 100); + AddData(0, x, y); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/HeatmapChart.cs.meta b/Assets/XCharts/Runtime/Chart/HeatmapChart.cs.meta new file mode 100644 index 0000000..0cceaa5 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/HeatmapChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 31aa03cd4ce594c239ae746791b3b59f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/LineChart.cs b/Assets/XCharts/Runtime/Chart/LineChart.cs new file mode 100644 index 0000000..a051226 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/LineChart.cs @@ -0,0 +1,146 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Line chart relates all the data points symbol by broken lines, which is used to show the trend of data changing. + /// It could be used in both rectangular coordinate andpolar coordinate. + /// ||折线图是用折线将各个数据点标志连接起来的图表,用于展现数据的变化趋势。可用于直角坐标系和极坐标系上。 + /// 设置 areaStyle 后可以绘制面积图。 + /// </summary> + [AddComponentMenu("XCharts/LineChart", 13)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class LineChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + EnsureChartComponent<XAxis>(); + EnsureChartComponent<YAxis>(); + + RemoveData(); + Line.AddDefaultSerie(this, GenerateDefaultSerieName()); + for (int i = 0; i < 5; i++) + { + AddXAxisData("x" + (i + 1)); + } + } + + /// <summary> + /// default area line chart. + /// || 默认面积折线图。 + /// </summary> + public void DefaultAreaLineChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.EnsureComponent<AreaStyle>(); + } + + /// <summary> + /// default smooth line chart. + /// || 默认平滑折线图。 + /// </summary> + public void DefaultSmoothLineChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.lineType = LineType.Smooth; + } + + /// <summary> + /// default smooth area line chart. + /// || 默认平滑面积折线图。 + /// </summary> + public void DefaultSmoothAreaLineChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.EnsureComponent<AreaStyle>(); + serie.lineType = LineType.Smooth; + } + + /// <summary> + /// default stack line chart. + /// || 默认堆叠折线图。 + /// </summary> + public void DefaultStackLineChart() + { + CheckChartInit(); + var serie1 = GetSerie(0); + if (serie1 == null) return; + serie1.stack = "stack1"; + var serie2 = Line.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie2.stack = "stack1"; + } + + /// <summary> + /// default stack area line chart. + /// || 默认堆叠面积折线图。 + /// </summary> + public void DefaultStackAreaLineChart() + { + CheckChartInit(); + var serie1 = GetSerie(0); + if (serie1 == null) return; + serie1.EnsureComponent<AreaStyle>(); + serie1.stack = "stack1"; + var serie2 = Line.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie2.EnsureComponent<AreaStyle>(); + serie2.stack = "stack1"; + } + + /// <summary> + /// default step line chart. + /// || 默认阶梯折线图。 + /// </summary> + public void DefaultStepLineChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.lineType = LineType.StepMiddle; + } + + /// <summary> + /// default dash line chart. + /// || 默认虚线折线图。 + /// </summary> + public void DefaultDashLineChart() + { + CheckChartInit(); + var serie = GetSerie(0); + if (serie == null) return; + serie.lineType = LineType.Normal; + serie.lineStyle.type = LineStyle.Type.Dashed; + } + + /// <summary> + /// default time line chart. + /// || 默认时间折线图。 + /// </summary> + public void DefaultTimeLineChart() + { + CheckChartInit(); + var xAxis = GetChartComponent<XAxis>(); + xAxis.type = Axis.AxisType.Time; + } + + /// <summary> + /// default logarithmic line chart. + /// || 默认对数轴折线图。 + /// </summary> + public void DefaultLogLineChart() + { + CheckChartInit(); + var yAxis = GetChartComponent<YAxis>(); + yAxis.type = Axis.AxisType.Log; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/LineChart.cs.meta b/Assets/XCharts/Runtime/Chart/LineChart.cs.meta new file mode 100644 index 0000000..f345666 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/LineChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4f38bd00b4648c448cabfc167538f7c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/ParallelChart.cs b/Assets/XCharts/Runtime/Chart/ParallelChart.cs new file mode 100644 index 0000000..ff9aee9 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/ParallelChart.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Parallel Coordinates is a common way of visualizing high-dimensional geometry and analyzing multivariate data. + /// || 平行坐标系,通过绘制垂直于坐标轴的平行线来显示数据的一种可视化图表。 + /// </summary> + [AddComponentMenu("XCharts/ParallelChart", 25)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class ParallelChart : BaseChart + { + protected override void DefaultChart() + { + RemoveData(); + AddChartComponent<ParallelCoord>(); + + for (int i = 0; i < 3; i++) + { + var valueAxis = AddChartComponent<ParallelAxis>(); + valueAxis.type = Axis.AxisType.Value; + } + var categoryAxis = AddChartComponent<ParallelAxis>(); + categoryAxis.type = Axis.AxisType.Category; + categoryAxis.position = Axis.AxisPosition.Right; + categoryAxis.data = new List<string>() { "x1", "x2", "x3", "x4", "x5" }; + + Parallel.AddDefaultSerie(this, GenerateDefaultSerieName()); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/ParallelChart.cs.meta b/Assets/XCharts/Runtime/Chart/ParallelChart.cs.meta new file mode 100644 index 0000000..e2e2f61 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/ParallelChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 161753d0d6ce541c89483f8c3a21343f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/PieChart.cs b/Assets/XCharts/Runtime/Chart/PieChart.cs new file mode 100644 index 0000000..ec48aa2 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/PieChart.cs @@ -0,0 +1,89 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The pie chart is mainly used for showing proportion of different categories. Each arc length represents the proportion of data quantity. + /// || 饼图主要用于显示不同类目占比的情况,通过弧长来反映数据的大小占比。 + /// </summary> + [AddComponentMenu("XCharts/PieChart", 15)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class PieChart : BaseChart + { + protected override void DefaultChart() + { + var legend = EnsureChartComponent<Legend>(); + legend.show = true; + + RemoveData(); + Pie.AddDefaultSerie(this, GenerateDefaultSerieName()); + } + + /// <summary> + /// default label pie chart. + /// || 默认带标签饼图。 + /// </summary> + public void DefaultLabelPieChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.EnsureComponent<LabelStyle>(); + serie.EnsureComponent<LabelLine>(); + } + + /// <summary> + /// default donut pie chart. + /// || 默认甜甜圈饼图。 + /// </summary> + public void DefaultDonutPieChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.radius[0] = 0.20f; + serie.radius[1] = 0.28f; + } + + /// <summary> + /// default label donut pie chart. + /// || 默认带标签甜甜圈饼图。 + /// </summary> + public void DefaultLabelDonutPieChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.radius[0] = 0.20f; + serie.radius[1] = 0.28f; + serie.EnsureComponent<LabelStyle>(); + serie.EnsureComponent<LabelLine>(); + } + + /// <summary> + /// default rose pie chart. + /// || 默认玫瑰饼图。 + /// </summary> + public void DefaultRadiusRosePieChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.pieRoseType = RoseType.Radius; + serie.EnsureComponent<LabelStyle>(); + serie.EnsureComponent<LabelLine>(); + } + + /// <summary> + /// default area rose pie chart. + /// || 默认面积玫瑰饼图。 + /// </summary> + public void DefaultAreaRosePieChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.pieRoseType = RoseType.Area; + serie.EnsureComponent<LabelStyle>(); + serie.EnsureComponent<LabelLine>(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/PieChart.cs.meta b/Assets/XCharts/Runtime/Chart/PieChart.cs.meta new file mode 100644 index 0000000..a2a53c5 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/PieChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d44276ba809fd92408b296835f6f7658 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/PolarChart.cs b/Assets/XCharts/Runtime/Chart/PolarChart.cs new file mode 100644 index 0000000..0d93196 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/PolarChart.cs @@ -0,0 +1,168 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + /// <summary> + /// Polar coordinates are usually used in a circular layout. + /// || 极坐标系,可以用于散点图和折线图。 + /// </summary> + [AddComponentMenu("XCharts/PolarChart", 23)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class PolarChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<PolarCoord>(); + EnsureChartComponent<AngleAxis>(); + var radiusAxis = EnsureChartComponent<RadiusAxis>(); + radiusAxis.axisLabel.show = false; + + var tooltip = EnsureChartComponent<Tooltip>(); + tooltip.type = Tooltip.Type.Cross; + tooltip.trigger = Tooltip.Trigger.Axis; + + RemoveData(); + var serie = Line.AddDefaultSerie(this, GenerateDefaultSerieName()); + serie.SetCoord<PolarCoord>(); + serie.ClearData(); + serie.symbol.show = false; + for (int i = 0; i <= 360; i++) + { + var t = i / 180f * Mathf.PI; + var r = Mathf.Sin(2 * t) * Mathf.Cos(2 * t) * 2; + AddData(0, Mathf.Abs(r), i); + } + } + + /// <summary> + /// default radial bar polar chart. + /// || 默认径向柱状极坐标图。 + /// </summary> + public void DefaultRadialBarPolarChart() + { + CheckChartInit(); + RemoveData(); + + var polarCoord = GetChartComponent<PolarCoord>(); + polarCoord.radius[0] = 20; + + var categorys = new string[] { "a", "b", "c", "d" }; + var radiusAxis = GetChartComponent<RadiusAxis>(); + radiusAxis.splitNumber = 4; + + var angleAxis = GetChartComponent<AngleAxis>(); + angleAxis.type = Axis.AxisType.Category; + angleAxis.startAngle = 75; + angleAxis.boundaryGap = true; + angleAxis.splitLine.show = false; + + foreach (var category in categorys) + angleAxis.AddData(category); + + var serie = AddSerie<Bar>(GenerateDefaultSerieName()); + serie.SetCoord<PolarCoord>(); + serie.ClearData(); + serie.symbol.show = false; + for (int i = 0; i < categorys.Length; i++) + { + var x = UnityEngine.Random.Range(0f, 4f); + var y = i; + AddData(0, x, y, categorys[i]); + } + } + + /// <summary> + /// default tangential bar polar chart. + /// || 默认切向柱状极坐标图。 + /// </summary> + public void DefaultTangentialBarPolarChart() + { + CheckChartInit(); + RemoveData(); + + var polarCoord = GetChartComponent<PolarCoord>(); + polarCoord.radius[0] = 20; + + var categorys = new string[] { "a", "b", "c", "d" }; + var radiusAxis = GetChartComponent<RadiusAxis>(); + radiusAxis.type = Axis.AxisType.Category; + radiusAxis.splitNumber = 4; + radiusAxis.boundaryGap = true; + + var angleAxis = GetChartComponent<AngleAxis>(); + angleAxis.type = Axis.AxisType.Value; + radiusAxis.splitNumber = 12; + angleAxis.startAngle = 75; + angleAxis.max = 4; + + foreach (var category in categorys) + radiusAxis.AddData(category); + + var serie = AddSerie<Bar>(GenerateDefaultSerieName()); + serie.SetCoord<PolarCoord>(); + serie.ClearData(); + serie.symbol.show = false; + for (int i = 0; i < categorys.Length; i++) + { + var x = UnityEngine.Random.Range(0f, 4f); + var y = i; + AddData(0, y, x, categorys[i]); + } + } + + /// <summary> + /// default heatmap polar chart. + /// || 默认极坐标色块图。 + /// </summary> + public void DefaultHeatmapPolarChart() + { + CheckChartInit(); + RemoveData(); + + var visualMap = EnsureChartComponent<VisualMap>(); + var colors = new List<string> { "#BAE7FF", "#1890FF", "#1028ff" }; + visualMap.AddColors(colors); + visualMap.autoMinMax = true; + + var polarCoord = GetChartComponent<PolarCoord>(); + polarCoord.radius[0] = 20; + + var categorys = new string[] { "a", "b", "c", "d" }; + var radiusAxis = GetChartComponent<RadiusAxis>(); + radiusAxis.type = Axis.AxisType.Category; + radiusAxis.splitNumber = 4; + radiusAxis.boundaryGap = true; + + var angleAxis = GetChartComponent<AngleAxis>(); + angleAxis.type = Axis.AxisType.Category; + angleAxis.boundaryGap = true; + angleAxis.splitNumber = 24; + angleAxis.startAngle = 75; + angleAxis.max = 4; + + foreach (var category in categorys) + radiusAxis.AddData(category); + + for (int i = 0; i < 24; i++) + { + angleAxis.AddData(i + "h"); + } + + var serie = AddSerie<Heatmap>(GenerateDefaultSerieName()); + serie.SetCoord<PolarCoord>(); + serie.ClearData(); + serie.symbol.show = false; + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 24; y++) + { + AddData(0, x, y, UnityEngine.Random.Range(0f, 4f)); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/PolarChart.cs.meta b/Assets/XCharts/Runtime/Chart/PolarChart.cs.meta new file mode 100644 index 0000000..29ae5a6 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/PolarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 574bcbd917fc148e8bb8735acda07f77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/RadarChart.cs b/Assets/XCharts/Runtime/Chart/RadarChart.cs new file mode 100644 index 0000000..ea314bb --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/RadarChart.cs @@ -0,0 +1,35 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Radar chart is mainly used to show multi-variable data, such as the analysis of a football player's varied attributes. It relies radar component. + /// || 雷达图主要用于显示多变量的数据,例如足球运动员的各项属性分析。依赖雷达组件。 + /// </summary> + [AddComponentMenu("XCharts/RadarChart", 16)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class RadarChart : BaseChart + { + protected override void DefaultChart() + { + RemoveData(); + RemoveChartComponents<RadarCoord>(); + AddChartComponent<RadarCoord>(); + Radar.AddDefaultSerie(this, GenerateDefaultSerieName()); + } + + /// <summary> + /// default circle radar chart. + /// || 默认圆形雷达图。 + /// </summary> + public void DefaultCircleRadarChart() + { + CheckChartInit(); + var radarCoord = GetChartComponent<RadarCoord>(); + radarCoord.shape = RadarCoord.Shape.Circle; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/RadarChart.cs.meta b/Assets/XCharts/Runtime/Chart/RadarChart.cs.meta new file mode 100644 index 0000000..2e638a3 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/RadarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2231a0d3e3a5b043b074f6739be4a86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/RingChart.cs b/Assets/XCharts/Runtime/Chart/RingChart.cs new file mode 100644 index 0000000..078a461 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/RingChart.cs @@ -0,0 +1,36 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Ring chart is mainly used to show the proportion of each item and the relationship between the items. + /// || 环形图主要用于显示每一项的比例以及各项之间的关系。 + /// </summary> + [AddComponentMenu("XCharts/RingChart", 20)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class RingChart : BaseChart + { + protected override void DefaultChart() + { + GetChartComponent<Tooltip>().type = Tooltip.Type.Line; + RemoveData(); + Ring.AddDefaultSerie(this, GenerateDefaultSerieName()); + } + + /// <summary> + /// default multiple ring chart. + /// || 默认多圆环图。 + /// </summary> + public void DefaultMultipleRingChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.label.show = false; + AddData(0, UnityEngine.Random.Range(30, 90), 100, "data2"); + AddData(0, UnityEngine.Random.Range(30, 90), 100, "data3"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/RingChart.cs.meta b/Assets/XCharts/Runtime/Chart/RingChart.cs.meta new file mode 100644 index 0000000..a46c903 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/RingChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ad8949f652ee4376a4a4fe5cb32029f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/ScatterChart.cs b/Assets/XCharts/Runtime/Chart/ScatterChart.cs new file mode 100644 index 0000000..96fa6d1 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/ScatterChart.cs @@ -0,0 +1,48 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Scatter chart is mainly used to show the relationship between two data dimensions. + /// || 散点图主要用于展现两个数据维度之间的关系。 + /// </summary> + [AddComponentMenu("XCharts/ScatterChart", 17)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class ScatterChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + + var xAxis = EnsureChartComponent<XAxis>(); + xAxis.type = Axis.AxisType.Value; + xAxis.boundaryGap = false; + + var yAxis = EnsureChartComponent<YAxis>(); + yAxis.type = Axis.AxisType.Value; + yAxis.boundaryGap = false; + + RemoveData(); + Scatter.AddDefaultSerie(this, GenerateDefaultSerieName()); + } + + /// <summary> + /// default bubble chart. + /// || 默认气泡图。 + /// </summary> + public void DefaultBubbleChart() + { + CheckChartInit(); + var serie = GetSerie(0); + serie.itemStyle.borderWidth = 2f; + serie.itemStyle.borderColor = theme.GetColor(0); + serie.itemStyle.opacity = 0.35f; + serie.symbol.sizeType = SymbolSizeType.FromData; + serie.symbol.dataScale = 0.3f; + serie.symbol.maxSize = 30f; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/ScatterChart.cs.meta b/Assets/XCharts/Runtime/Chart/ScatterChart.cs.meta new file mode 100644 index 0000000..efbd890 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/ScatterChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf16aac0bd6c24a8da75846c34c5193e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs b/Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs new file mode 100644 index 0000000..3f0d271 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// A simplified bar chart is a simplified mode of a bar chart that provides better performance by simplifying components and configurations. + /// || 简化柱状图是柱状图的简化模式,通过简化组件和配置,拥有更好的性能。 + /// </summary> + [AddComponentMenu("XCharts/SimplifiedBarChart", 27)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class SimplifiedBarChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + EnsureChartComponent<XAxis>(); + EnsureChartComponent<YAxis>(); + + RemoveData(); + SimplifiedBar.AddDefaultSerie(this, GenerateDefaultSerieName()); + for (int i = 0; i < GetSerie(0).dataCount; i++) + { + AddXAxisData("x" + (i + 1)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs.meta b/Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs.meta new file mode 100644 index 0000000..7dcf138 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/SimplifiedBarChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa86c3bbf8877409c9d45716fbaf92f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs b/Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs new file mode 100644 index 0000000..4f4535f --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// A simplified candlestick chart is a simplified mode of a bar chart that provides better performance by simplifying components and configurations. + /// || 简化K线图是K线图的简化模式,通过简化组件和配置,拥有更好的性能。 + /// </summary> + [AddComponentMenu("XCharts/SimplifiedCandlestickChart", 28)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class SimplifiedCandlestickChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + EnsureChartComponent<XAxis>(); + EnsureChartComponent<YAxis>(); + + RemoveData(); + SimplifiedCandlestick.AddDefaultSerie(this, GenerateDefaultSerieName()); + for (int i = 0; i < GetSerie(0).dataCount; i++) + { + AddXAxisData("x" + (i + 1)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs.meta b/Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs.meta new file mode 100644 index 0000000..26ff0a3 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/SimplifiedCandlestickChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6dcc9bd1ca8344d938f386e6b32e8946 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs b/Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs new file mode 100644 index 0000000..4a14a15 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// A simplified line chart is a simplified mode of a bar chart that provides better performance by simplifying components and configurations. + /// || 简化折线图是折线图的简化模式,通过简化组件和配置,拥有更好的性能。 + /// </summary> + [AddComponentMenu("XCharts/SimplifiedLineChart", 26)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + [HelpURL("https://xcharts-team.github.io/docs/configuration")] + public class SimplifiedLineChart : BaseChart + { + protected override void DefaultChart() + { + EnsureChartComponent<GridCoord>(); + EnsureChartComponent<XAxis>(); + EnsureChartComponent<YAxis>(); + + RemoveData(); + SimplifiedLine.AddDefaultSerie(this, GenerateDefaultSerieName()); + for (int i = 0; i < GetSerie(0).dataCount; i++) + { + AddXAxisData("x" + (i + 1)); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs.meta b/Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs.meta new file mode 100644 index 0000000..0ab5605 --- /dev/null +++ b/Assets/XCharts/Runtime/Chart/SimplifiedLineChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8233997c1b324ecd875a03af4d90972 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component.meta b/Assets/XCharts/Runtime/Component.meta new file mode 100644 index 0000000..73353b5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3be5f1d3b129a47dd8e41cffe3b8e428 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Animation.meta b/Assets/XCharts/Runtime/Component/Animation.meta new file mode 100644 index 0000000..4c94e65 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9f827513754e8436bbc63e64c5b5e6c3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs b/Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs new file mode 100644 index 0000000..d87c7d0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs @@ -0,0 +1,496 @@ + +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the animation info. + /// ||动画配置参数。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationInfo + { + [SerializeField][Since("v3.8.0")] private bool m_Enable = true; + [SerializeField][Since("v3.8.0")] private bool m_Reverse = false; + [SerializeField][Since("v3.8.0")] private float m_Delay = 0; + [SerializeField][Since("v3.8.0")] private float m_Duration = 1000; + [SerializeField][Since("v3.14.0")] private float m_Speed = 0; + public AnimationInfoContext context = new AnimationInfoContext(); + + /// <summary> + /// whether enable animation. + /// ||是否开启动画效果。 + /// </summary> + public bool enable { get { return m_Enable; } set { m_Enable = value; } } + /// <summary> + /// whether enable reverse animation. + /// ||是否开启反向动画效果。 + /// </summary> + public bool reverse { get { return m_Reverse; } set { m_Reverse = value; } } + /// <summary> + /// the delay time before animation start. + /// ||动画开始前的延迟时间。 + /// </summary> + public float delay { get { return m_Delay; } set { m_Delay = value; } } + /// <summary> + /// the duration of animation. Default is used to calculate the speed of animation. It can also be specified by speed. + /// ||动画的时长。默认用于计算动画的速度。也可以通过speed指定速度。 + /// </summary> + public float duration { get { return m_Duration; } set { m_Duration = value; } } + /// <summary> + /// the speed of animation. When speed is specified, duration will be invalid. Default is 0, which means no speed specified. + /// ||动画的速度。当指定speed时,duration将失效。默认为0,表示不指定速度。 + /// </summary> + public float speed { get { return m_Speed; } set { m_Speed = value; } } + /// <summary> + /// the callback function of animation start. + /// ||动画开始的回调。 + /// </summary> + public Action OnAnimationStart { get; set; } + /// <summary> + /// the callback function of animation end. + /// ||动画结束的回调。 + /// </summary> + public Action OnAnimationEnd { get; set; } + + /// <summary> + /// the delegate function of animation delay. + /// ||动画延迟的委托函数。 + /// </summary> + public AnimationDelayFunction delayFunction { get; set; } + /// <summary> + /// the delegate function of animation duration. + /// ||动画时长的委托函数。 + /// </summary> + public AnimationDurationFunction durationFunction { get; set; } + + /// <summary> + /// Reset animation. + /// ||重置动画。 + /// </summary> + public void Reset() + { + if (!enable) return; + context.init = false; + context.start = false; + context.pause = false; + context.end = false; + context.startTime = 0; + context.currProgress = 0; + context.destProgress = 0; + context.totalProgress = 0; + context.sizeProgress = 0; + context.currPointIndex = 0; + context.currPoint = Vector3.zero; + context.destPoint = Vector3.zero; + context.dataCurrProgress.Clear(); + context.dataDestProgress.Clear(); + } + + /// <summary> + /// Start animation. + /// ||开始动画。 + /// </summary> + /// <param name="reset">是否重置上一次的参数</param> + public void Start(bool reset = true) + { + if (!enable) return; + if (context.start) + { + context.pause = false; + return; + } + context.init = false; + context.start = true; + context.end = false; + context.pause = false; + context.startTime = Time.time; + if (reset) + { + context.currProgress = 0; + context.destProgress = 1; + context.totalProgress = 0; + context.sizeProgress = 0; + context.dataCurrProgress.Clear(); + context.dataDestProgress.Clear(); + } + if (OnAnimationStart != null) + { + OnAnimationStart(); + } + } + + /// <summary> + /// Pause animation. + /// ||暂停动画。 + /// </summary> + public void Pause() + { + if (!enable) return; + if (!context.start || context.end) return; + context.pause = true; + } + + /// <summary> + /// Resume animation. + /// ||恢复动画。 + /// </summary> + public void Resume() + { + if (!enable) return; + if (!context.pause) return; + context.pause = false; + } + + /// <summary> + /// End animation. + /// ||结束动画。 + /// </summary> + public void End() + { + if (!enable) return; + if (!context.start || context.end) return; + context.init = false; + context.start = false; + context.end = true; + context.currPointIndex = context.destPointIndex; + context.startTime = Time.time; + if (OnAnimationEnd != null) + { + OnAnimationEnd(); + } + } + + /// <summary> + /// Initialize animation. + /// ||初始化动画。 + /// </summary> + /// <param name="curr">当前进度</param> + /// <param name="dest">目标进度</param> + /// <param name="totalPointIndex">目标索引</param> + /// <returns></returns> + public bool Init(float curr, float dest, int totalPointIndex) + { + if (!enable || !context.start) return false; + context.totalProgress = dest - curr; + context.destPointIndex = totalPointIndex; + if (reverse) + { + if (!context.init) context.currProgress = dest; + context.destProgress = curr; + } + else + { + if (!context.init) context.currProgress = curr; + context.destProgress = dest; + } + context.init = true; + return true; + } + + /// <summary> + /// Whether animation is finish. + /// ||动画是否结束。 + /// </summary> + public bool IsFinish() + { + if (!context.start) return true; + if (context.end) return true; + if (context.pause) return false; + if (!context.init) return false; + return m_Reverse ? context.currProgress <= context.destProgress + : context.currProgress >= context.destProgress; + } + + /// <summary> + /// Whether animation is in delay. + /// ||动画是否在延迟中。 + /// </summary> + public bool IsInDelay() + { + if (!context.start) + return false; + else + return m_Delay > 0 && Time.time - context.startTime < m_Delay / 1000; + } + + /// <summary> + /// Whether animation is in index delay. + /// ||动画是否在索引延迟中。 + /// </summary> + /// <param name="dataIndex"></param> + /// <returns></returns> + public bool IsInIndexDelay(int dataIndex) + { + if (context.start) + return Time.time - context.startTime < GetIndexDelay(dataIndex) / 1000f; + else + return false; + } + + /// <summary> + /// Get animation delay. + /// ||获取动画延迟。 + /// </summary> + /// <param name="dataIndex"></param> + /// <returns></returns> + public float GetIndexDelay(int dataIndex) + { + if (!context.start) return 0; + if (delayFunction != null) + return delayFunction(dataIndex); + return delay; + } + + internal float GetCurrAnimationDuration(int dataIndex = -1) + { + if (dataIndex >= 0) + { + if (context.start && durationFunction != null) + return durationFunction(dataIndex) / 1000f; + } + return m_Duration > 0 ? m_Duration / 1000 : 1f; + } + + internal void SetDataCurrProgress(int index, float state) + { + context.dataCurrProgress[index] = state; + } + + + internal float GetDataCurrProgress(int index, float initValue, float destValue, ref bool isBarEnd) + { + if (IsInDelay()) + { + isBarEnd = false; + return initValue; + } + var c1 = !context.dataCurrProgress.ContainsKey(index); + var c2 = !context.dataDestProgress.ContainsKey(index); + if (c1 || c2) + { + if (c1) + context.dataCurrProgress.Add(index, initValue); + + if (c2) + context.dataDestProgress.Add(index, destValue); + + isBarEnd = false; + } + else + { + isBarEnd = context.dataCurrProgress[index] == context.dataDestProgress[index]; + } + return context.dataCurrProgress[index]; + } + + internal void CheckProgress(double total, bool m_UnscaledTime) + { + if (!context.start || !context.init || context.pause) return; + if (IsInDelay()) return; + var delta = GetDelta(total, m_UnscaledTime); + if (reverse) + { + context.currProgress -= delta; + if (context.currProgress <= context.destProgress) + { + context.currProgress = context.destProgress; + End(); + } + } + else + { + context.currProgress += delta; + if (context.currProgress >= context.destProgress) + { + context.currProgress = context.destProgress; + End(); + } + } + } + + internal float CheckItemProgress(int dataIndex, float destProgress, ref bool isEnd, float startProgress, bool m_UnscaledTime) + { + if (m_Reverse) + { + var temp = startProgress; + startProgress = destProgress; + destProgress = temp; + } + var currHig = GetDataCurrProgress(dataIndex, startProgress, destProgress, ref isEnd); + if (IsFinish()) + { + return destProgress; + } + else if (IsInDelay() || IsInIndexDelay(dataIndex)) + { + return startProgress; + } + else if (context.pause) + { + return currHig; + } + else + { + var delta = GetDelta(destProgress - startProgress, m_UnscaledTime); + currHig += delta; + if (reverse) + { + if ((destProgress > 0 && currHig <= 0) || (destProgress < 0 && currHig >= 0)) + { + currHig = 0; + isEnd = true; + } + } + else + { + if ((destProgress - startProgress > 0 && currHig > destProgress) || + (destProgress - startProgress < 0 && currHig < destProgress)) + { + currHig = destProgress; + isEnd = true; + } + } + SetDataCurrProgress(dataIndex, currHig); + return currHig; + } + } + + internal void CheckSymbol(float dest, bool m_UnscaledTime) + { + if (!context.start || !context.init || context.pause) return; + + if (IsInDelay()) + return; + + var delta = GetDelta(dest, m_UnscaledTime); + if (reverse) + { + context.sizeProgress -= delta; + if (context.sizeProgress < 0) + context.sizeProgress = 0; + } + else + { + context.sizeProgress += delta; + if (context.sizeProgress > dest) + context.sizeProgress = dest; + } + } + + private float GetDelta(double total, bool unscaledTime) + { + if (m_Speed > 0) + { + context.currDuration = (float)total / m_Speed; + return (float)(m_Speed * (unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime)); + } + else + { + context.currDuration = 0; + return (float)(total / GetCurrAnimationDuration() * (unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime)); + } + } + } + + /// <summary> + /// Fade in animation. + /// ||淡入动画。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationFadeIn : AnimationInfo + { + } + + /// <summary> + /// Fade out animation. + /// ||淡出动画。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationFadeOut : AnimationInfo + { + } + + /// <summary> + /// Data change animation. + /// ||数据变更动画。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationChange : AnimationInfo + { + } + + /// <summary> + /// Data addition animation. + /// ||数据新增动画。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationAddition : AnimationInfo + { + } + + /// <summary> + /// Data hiding animation. + /// ||数据隐藏动画。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationHiding : AnimationInfo + { + } + + /// <summary> + /// Interactive animation of charts. + /// ||交互动画。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class AnimationInteraction : AnimationInfo + { + [SerializeField][Since("v3.8.0")] private MLValue m_Width = new MLValue(1.1f); + [SerializeField][Since("v3.8.0")] private MLValue m_Radius = new MLValue(1.1f); + [SerializeField][Since("v3.8.0")] private MLValue m_Offset = new MLValue(MLValue.Type.Absolute, 5f); + + /// <summary> + /// the mlvalue of width. + /// ||宽度的多样式数值。 + /// </summary> + public MLValue width { get { return m_Width; } set { m_Width = value; } } + /// <summary> + /// the mlvalue of radius. + /// ||半径的多样式数值。 + /// </summary> + public MLValue radius { get { return m_Radius; } set { m_Radius = value; } } + /// <summary> + /// the mlvalue of offset. Such as the offset of the pie chart when the sector is selected. + /// ||交互的多样式数值。如饼图的扇形选中时的偏移。 + /// </summary> + public MLValue offset { get { return m_Offset; } set { m_Offset = value; } } + + public float GetRadius(float radius) + { + return m_Radius.GetValue(radius); + } + + public float GetWidth(float width) + { + return m_Width.GetValue(width); + } + + public float GetOffset(float total) + { + return m_Offset.GetValue(total); + } + + public float GetOffset() + { + return m_Offset.value; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs.meta b/Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs.meta new file mode 100644 index 0000000..ed254f0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae54b92d6276445ac9524b598bfb6e84 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs b/Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs new file mode 100644 index 0000000..1ff1bef --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public sealed class AnimationInfoContext + { + public bool init; + public bool start; + public bool pause; + public bool end; + public float startTime; + public float currProgress; + public float destProgress; + public float totalProgress; + public float sizeProgress; + public int currPointIndex; + public int destPointIndex; + public float currDuration; + public Vector3 currPoint; + public Vector3 destPoint; + public Dictionary<int, float> dataCurrProgress = new Dictionary<int, float>(); + public Dictionary<int, float> dataDestProgress = new Dictionary<int, float>(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs.meta b/Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs.meta new file mode 100644 index 0000000..3d6a871 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationInfoContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 023c7390605c34a72b13f5db7a647f06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs b/Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs new file mode 100644 index 0000000..a14980c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs @@ -0,0 +1,613 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public enum AnimationType + { + /// <summary> + /// he default. An animation playback mode will be selected according to the actual situation. + /// ||默认。内部会根据实际情况选择一种动画播放方式。 + /// </summary> + Default, + /// <summary> + /// Play the animation from left to right. + /// ||从左往右播放动画。 + /// </summary> + LeftToRight, + /// <summary> + /// Play the animation from bottom to top. + /// ||从下往上播放动画。 + /// </summary> + BottomToTop, + /// <summary> + /// Play animations from the inside out. + /// ||由内到外播放动画。 + /// </summary> + InsideOut, + /// <summary> + /// Play the animation along the path. + /// ||沿着路径播放动画。当折线图从左到右无序或有折返时,可以使用该模式。 + /// </summary> + AlongPath, + /// <summary> + /// Play the animation clockwise. + /// ||顺时针播放动画。 + /// </summary> + Clockwise, + } + + public enum AnimationEasing + { + Linear, + } + + /// <summary> + /// the animation of serie. support animation type: fadeIn, fadeOut, change, addition. + /// ||动画组件,用于控制图表的动画播放。支持配置五种动画表现:FadeIn(渐入动画),FadeOut(渐出动画),Change(变更动画),Addition(新增动画),Interaction(交互动画)。 + /// 按作用的对象可以分为两类:SerieAnimation(系列动画)和DataAnimation(数据动画)。 + /// </summary> + [System.Serializable] + public class AnimationStyle : ChildComponent + { + [SerializeField] private bool m_Enable = true; + [SerializeField] private AnimationType m_Type; + [SerializeField] private AnimationEasing m_Easting; + [SerializeField] private int m_Threshold = 2000; + [SerializeField][Since("v3.4.0")] private bool m_UnscaledTime; + [SerializeField][Since("v3.8.0")] private AnimationFadeIn m_FadeIn = new AnimationFadeIn(); + [SerializeField][Since("v3.8.0")] private AnimationFadeOut m_FadeOut = new AnimationFadeOut() { reverse = true }; + [SerializeField][Since("v3.8.0")] private AnimationChange m_Change = new AnimationChange() { duration = 500 }; + [SerializeField][Since("v3.8.0")] private AnimationAddition m_Addition = new AnimationAddition() { duration = 500 }; + [SerializeField][Since("v3.8.0")] private AnimationHiding m_Hiding = new AnimationHiding() { duration = 500 }; + [SerializeField][Since("v3.8.0")] private AnimationInteraction m_Interaction = new AnimationInteraction() { duration = 250 }; + + [Obsolete("Use animation.fadeIn.delayFunction instead.", true)] + public AnimationDelayFunction fadeInDelayFunction; + [Obsolete("Use animation.fadeIn.durationFunction instead.", true)] + public AnimationDurationFunction fadeInDurationFunction; + [Obsolete("Use animation.fadeOut.delayFunction instead.", true)] + public AnimationDelayFunction fadeOutDelayFunction; + [Obsolete("Use animation.fadeOut.durationFunction instead.", true)] + public AnimationDurationFunction fadeOutDurationFunction; + [Obsolete("Use animation.fadeIn.OnAnimationEnd() instead.", true)] + public Action fadeInFinishCallback { get; set; } + [Obsolete("Use animation.fadeOut.OnAnimationEnd() instead.", true)] + public Action fadeOutFinishCallback { get; set; } + public AnimationStyleContext context = new AnimationStyleContext(); + + /// <summary> + /// Whether to enable animation. + /// ||是否开启动画效果。 + /// </summary> + public bool enable { get { return m_Enable; } set { m_Enable = value; } } + /// <summary> + /// The type of animation. + /// ||动画类型。 + /// </summary> + public AnimationType type + { + get { return m_Type; } + set + { + m_Type = value; + if (m_Type != AnimationType.Default) + { + context.type = m_Type; + } + } + } + /// <summary> + /// Whether to set graphic number threshold to animation. Animation will be disabled when graphic number is larger than threshold. + /// ||是否开启动画的阈值,当单个系列显示的图形数量大于这个阈值时会关闭动画。 + /// </summary> + public int threshold { get { return m_Threshold; } set { m_Threshold = value; } } + /// <summary> + /// Animation updates independently of Time.timeScale. + /// ||动画是否受TimeScaled的影响。默认为 false 受TimeScaled的影响。 + /// </summary> + public bool unscaledTime { get { return m_UnscaledTime; } set { m_UnscaledTime = value; } } + /// <summary> + /// Fade in animation configuration. + /// ||渐入动画配置。 + /// </summary> + public AnimationFadeIn fadeIn { get { return m_FadeIn; } } + /// <summary> + /// Fade out animation configuration. + /// ||渐出动画配置。 + /// </summary> + public AnimationFadeOut fadeOut { get { return m_FadeOut; } } + /// <summary> + /// Update data animation configuration. + /// ||数据变更动画配置。 + /// </summary> + public AnimationChange change { get { return m_Change; } } + /// <summary> + /// Add data animation configuration. + /// ||数据新增动画配置。 + /// </summary> + public AnimationAddition addition { get { return m_Addition; } } + /// <summary> + /// Data hiding animation configuration. + /// ||数据隐藏动画配置。 + /// </summary> + public AnimationHiding hiding { get { return m_Hiding; } } + /// <summary> + /// Interaction animation configuration. + /// ||交互动画配置。 + /// </summary> + public AnimationInteraction interaction { get { return m_Interaction; } } + + private Vector3 m_LinePathLastPos; + private List<AnimationInfo> m_Animations; + private List<AnimationInfo> animations + { + get + { + if (m_Animations == null) + { + m_Animations = new List<AnimationInfo>(); + m_Animations.Add(m_FadeIn); + m_Animations.Add(m_FadeOut); + m_Animations.Add(m_Change); + m_Animations.Add(m_Addition); + m_Animations.Add(m_Hiding); + } + return m_Animations; + } + } + + /// <summary> + /// The actived animation. + /// ||当前激活的动画。 + /// </summary> + public AnimationInfo activedAnimation + { + get + { + foreach (var anim in animations) + { + if (anim.context.start) return anim; + } + return null; + } + } + + /// <summary> + /// Start fadein animation. + /// ||开始渐入动画。 + /// </summary> + public void FadeIn() + { + if (m_FadeOut.context.start) return; + m_FadeIn.Start(); + } + + /// <summary> + /// Restart the actived animation. + /// ||重启当前激活的动画。 + /// </summary> + public void Restart() + { + var anim = activedAnimation; + Reset(); + if (anim != null) + { + anim.Start(); + } + } + + /// <summary> + /// Start fadeout animation. + /// ||开始渐出动画。 + /// </summary> + public void FadeOut() + { + m_FadeOut.Start(); + } + + /// <summary> + /// Start additon animation. + /// ||开始数据新增动画。 + /// </summary> + public void Addition() + { + if (!enable) return; + if (!m_FadeIn.context.start && !m_FadeOut.context.start) + { + m_Addition.Start(false); + } + } + + /// <summary> + /// Pause all animations. + /// ||暂停所有动画。 + /// </summary> + public void Pause() + { + foreach (var anim in animations) + { + anim.Pause(); + } + } + + /// <summary> + /// Resume all animations. + /// ||恢复所有动画。 + /// </summary> + public void Resume() + { + foreach (var anim in animations) + { + anim.Resume(); + } + } + + /// <summary> + /// Reset all animations. + /// </summary> + public void Reset() + { + foreach (var anim in animations) + { + anim.Reset(); + } + } + + /// <summary> + /// Initialize animation configuration. + /// ||初始化动画配置。 + /// </summary> + /// <param name="curr">当前进度</param> + /// <param name="dest">目标进度</param> + public void InitProgress(float curr, float dest) + { + var anim = activedAnimation; + if (anim == null) return; + var isAddedAnim = anim is AnimationAddition; + if (IsSerieAnimation()) + { + if (isAddedAnim) + { + anim.Init(anim.context.currPointIndex, dest, (int)dest - 1); + } + else + { + m_Addition.context.currPointIndex = (int)dest - 1; + anim.Init(curr, dest, (int)dest - 1); + } + } + else + { + anim.Init(curr, dest, 0); + } + } + + /// <summary> + /// Initialize animation configuration. + /// ||初始化动画配置。 + /// </summary> + /// <param name="paths">路径坐标点列表</param> + /// <param name="isY">是Y轴还是X轴</param> + public void InitProgress(List<Vector3> paths, bool isY) + { + if (paths.Count < 1) return; + var anim = activedAnimation; + if (anim == null) + { + m_Addition.context.currPointIndex = paths.Count - 1; + return; + } + var isAddedAnim = anim is AnimationAddition; + var startIndex = 0; + if (isAddedAnim) + { + startIndex = anim.context.currPointIndex == paths.Count - 1 ? + paths.Count - 2 : + anim.context.currPointIndex; + if (startIndex < 0 || startIndex >= paths.Count - 1) return; + } + else + { + m_Addition.context.currPointIndex = paths.Count - 1; + } + var sp = paths[startIndex]; + var ep = paths[paths.Count - 1]; + var currDetailProgress = isY ? sp.y : sp.x; + var totalDetailProgress = isY ? ep.y : ep.x; + if (context.type == AnimationType.AlongPath) + { + currDetailProgress = 0; + totalDetailProgress = 0; + var lp = sp; + for (int i = 1; i < paths.Count; i++) + { + var np = paths[i]; + totalDetailProgress += Vector3.Distance(np, lp); + lp = np; + if (startIndex > 0 && i == startIndex) + currDetailProgress = totalDetailProgress; + } + m_LinePathLastPos = sp; + context.currentPathDistance = 0; + } + if (sp == anim.context.currPoint && ep == anim.context.destPoint) + { + return; + } + + if (anim.Init(currDetailProgress, totalDetailProgress, paths.Count - 1)) + { + anim.context.currPoint = sp; + anim.context.destPoint = ep; + } + } + + public bool IsEnd() + { + foreach (var animation in animations) + { + if (animation.context.start) + return animation.context.end; + } + return m_FadeIn.context.end; + } + + + public bool IsFinish() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + return true; +#endif + if (!m_Enable) + return true; + var animation = activedAnimation; + if (animation != null && animation.context.end) + { + return true; + } + if (IsSerieAnimation()) + { + if (m_FadeOut.context.start) + { + return m_FadeOut.context.currProgress <= m_FadeOut.context.destProgress; + } + else if (m_Addition.context.start) + { + return m_Addition.context.currProgress >= m_Addition.context.destProgress; + } + else + { + return m_FadeIn.context.currProgress >= m_FadeIn.context.destProgress; + } + } + else if (IsDataAnimation()) + { + if (animation == null) return true; + else return animation.context.end; + } + return true; + } + + public bool IsInDelay() + { + var anim = activedAnimation; + if (anim != null) + return anim.IsInDelay(); + return false; + } + + /// <summary> + /// whther animaiton is data animation. BottomToTop and InsideOut are data animation. + /// ||是否为数据动画。BottomToTop和InsideOut类型的为数据动画。 + /// </summary> + public bool IsDataAnimation() + { + return context.type == AnimationType.BottomToTop || context.type == AnimationType.InsideOut; + } + + /// <summary> + /// whther animaiton is serie animation. LeftToRight, AlongPath and Clockwise are serie animation. + /// ||是否为系列动画。LeftToRight、AlongPath和Clockwise类型的为系列动画。 + /// </summary> + public bool IsSerieAnimation() + { + return context.type == AnimationType.LeftToRight || + context.type == AnimationType.AlongPath || context.type == AnimationType.Clockwise; + } + + public bool CheckDetailBreak(float detail) + { + if (!IsSerieAnimation()) + return false; + foreach (var animation in animations) + { + if (animation.context.start) + return !IsFinish() && detail > animation.context.currProgress; + } + return false; + } + + public bool CheckDetailBreak(Vector3 pos, bool isYAxis) + { + if (!IsSerieAnimation()) + return false; + + if (IsFinish()) + return false; + + if (context.type == AnimationType.AlongPath) + { + context.currentPathDistance += Vector3.Distance(pos, m_LinePathLastPos); + m_LinePathLastPos = pos; + return CheckDetailBreak(context.currentPathDistance); + } + else + { + if (isYAxis) + return pos.y > GetCurrDetail(); + else + return pos.x > GetCurrDetail(); + } + } + + public void CheckProgress() + { + if (IsDataAnimation() && context.isAllItemAnimationEnd) + { + foreach (var animation in animations) + { + animation.End(); + } + return; + } + foreach (var animation in animations) + { + animation.CheckProgress(animation.context.totalProgress, m_UnscaledTime); + } + } + + public void CheckProgress(double total) + { + if (IsFinish()) + return; + foreach (var animation in animations) + { + animation.CheckProgress(total, m_UnscaledTime); + } + } + + internal float CheckItemProgress(int dataIndex, float destProgress, ref bool isEnd, float startProgress = 0) + { + isEnd = false; + var anim = activedAnimation; + if (anim == null) + { + isEnd = true; + return destProgress; + } + return anim.CheckItemProgress(dataIndex, destProgress, ref isEnd, startProgress, m_UnscaledTime); + } + + public void CheckSymbol(float dest) + { + m_FadeIn.CheckSymbol(dest, m_UnscaledTime); + m_FadeOut.CheckSymbol(dest, m_UnscaledTime); + } + + public float GetSysmbolSize(float dest) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + return dest; +#endif + if (!enable) + return dest; + + if (IsEnd()) + return m_FadeOut.context.start ? 0 : dest; + + return m_FadeOut.context.start ? m_FadeOut.context.sizeProgress : m_FadeIn.context.sizeProgress; + } + + public float GetCurrDetail() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + { + foreach (var animation in animations) + { + if (animation.context.start) + return animation.context.destProgress; + } + } +#endif + foreach (var animation in animations) + { + if (animation.context.start) + return animation.context.currProgress; + } + return m_FadeIn.context.currProgress; + } + + public float GetCurrRate() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + return 1; +#endif + if (!enable || IsEnd()) + return 1; + return m_FadeOut.context.start ? m_FadeOut.context.currProgress : m_FadeIn.context.currProgress; + } + + public int GetCurrIndex() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + return -1; +#endif + if (!enable) + return -1; + var anim = activedAnimation; + if (anim == null) + return -1; + return (int)anim.context.currProgress; + } + + public float GetChangeDuration() + { + if (m_Enable && m_Change.enable) + return m_Change.context.currDuration > 0 ? m_Change.context.currDuration : m_Change.duration; + else + return 0; + } + + public float GetAdditionDuration() + { + if (m_Enable && m_Addition.enable) + return m_Addition.context.currDuration > 0 ? m_Addition.context.currDuration : m_Addition.duration; + else + return 0; + } + + public float GetInteractionDuration() + { + if (m_Enable && m_Interaction.enable) + return m_Interaction.context.currDuration > 0 ? m_Interaction.context.currDuration : m_Interaction.duration; + else + return 0; + } + + public float GetInteractionRadius(float radius) + { + if (m_Enable && m_Interaction.enable) + return m_Interaction.GetRadius(radius); + else + return radius; + } + + public bool HasFadeOut() + { + return enable && m_FadeOut.context.end; + } + + public bool IsFadeIn() + { + return enable && m_FadeIn.context.start; + } + + public bool IsFadeOut() + { + return enable && m_FadeOut.context.start; + } + + public bool CanCheckInteract() + { + return enable && interaction.enable + && !IsFadeIn() && !IsFadeOut(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs.meta b/Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs.meta new file mode 100644 index 0000000..87c8cd8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e31c30f2ef61c48718a626f93307ce92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs new file mode 100644 index 0000000..f49244f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public struct AnimationStyleContext + { + public AnimationType type; + public bool enableSerieDataAddedAnimation; + public float currentPathDistance; + public bool isAllItemAnimationEnd; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs.meta b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs.meta new file mode 100644 index 0000000..572eb5b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3dc504960589413fa6a76267067775c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs new file mode 100644 index 0000000..568ab86 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs @@ -0,0 +1,77 @@ +using UnityEngine; +using XUGL; + +namespace XCharts.Runtime +{ + public static class AnimationStyleHelper + { + public static float CheckDataAnimation(BaseChart chart, Serie serie, int dataIndex, float destProgress, float startPorgress = 0) + { + if (!serie.animation.IsDataAnimation()) + { + serie.animation.context.isAllItemAnimationEnd = false; + return destProgress; + } + if (serie.animation.IsFinish()) + { + serie.animation.context.isAllItemAnimationEnd = false; + return destProgress; + } + var isDataAnimationEnd = true; + var currHig = serie.animation.CheckItemProgress(dataIndex, destProgress, ref isDataAnimationEnd, startPorgress); + if (!isDataAnimationEnd) + { + serie.animation.context.isAllItemAnimationEnd = false; + } + return currHig; + } + + public static void UpdateSerieAnimation(Serie serie) + { + var serieType = serie.GetType(); + var animationType = AnimationType.LeftToRight; + var enableSerieDataAnimation = true; + if (serieType.IsDefined(typeof(DefaultAnimationAttribute), false)) + { + var attribute = serieType.GetAttribute<DefaultAnimationAttribute>(); + animationType = attribute.type; + enableSerieDataAnimation = attribute.enableSerieDataAddedAnimation; + } + UpdateAnimationType(serie.animation, animationType, enableSerieDataAnimation); + } + + public static void UpdateAnimationType(AnimationStyle animation, AnimationType defaultType, bool enableSerieDataAnimation) + { + animation.context.type = animation.type == AnimationType.Default ? + defaultType : + animation.type; + animation.context.enableSerieDataAddedAnimation = enableSerieDataAnimation; + } + + public static bool GetAnimationPosition(AnimationStyle animation, bool isY, Vector3 lp, Vector3 cp, float progress, ref Vector3 ip, ref float rate) + { + if (animation.context.type == AnimationType.AlongPath) + { + var dist = Vector3.Distance(lp, cp); + rate = (dist - animation.context.currentPathDistance + animation.GetCurrDetail()) / dist; + ip = Vector3.Lerp(lp, cp, rate); + return true; + } + else + { + var startPos = isY ? new Vector3(-10000, progress) : new Vector3(progress, -10000); + var endPos = isY ? new Vector3(10000, progress) : new Vector3(progress, 10000); + + if (UGLHelper.GetIntersection(lp, cp, startPos, endPos, ref ip)) + { + rate = Vector3.Distance(lp, ip) / Vector3.Distance(lp, cp); + return true; + } + else + { + return false; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs.meta b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs.meta new file mode 100644 index 0000000..eed1235 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Animation/AnimationStyleHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54cadaee0856b4f7085787fd450eec37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis.meta b/Assets/XCharts/Runtime/Component/Axis.meta new file mode 100644 index 0000000..61797b1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 194a62edf7ec2484fa2eebbf5bde3e95 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AngleAxis.meta b/Assets/XCharts/Runtime/Component/Axis/AngleAxis.meta new file mode 100644 index 0000000..0b8a740 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AngleAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 28b88ca3453f04fbdb23a53b5bcb4bf7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs new file mode 100644 index 0000000..08929c2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Angle axis of Polar Coordinate. + /// ||极坐标系的角度轴。 + /// </summary> + [System.Serializable] + [RequireChartComponent(typeof(PolarCoord))] + [ComponentHandler(typeof(AngleAxisHandler), true)] + public class AngleAxis : Axis + { + [SerializeField] private float m_StartAngle = 0; + + /// <summary> + /// Starting angle of axis. 0 degrees by default, standing for right position of center. + /// ||起始刻度的角度,默认为 0 度,即圆心的正右方。 + /// </summary> + public float startAngle + { + get { return m_StartAngle; } + set { if (PropertyUtil.SetStruct(ref m_StartAngle, value)) SetAllDirty(); } + } + + public float GetValueAngle(float value) + { + return (value + context.startAngle + 360) % 360; + } + + public float GetValueAngle(double value) + { + return (float) (value + context.startAngle + 360) % 360; + } + + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Value; + m_SplitNumber = 12; + m_StartAngle = 0; + m_BoundaryGap = false; + m_Data = new List<string>(12); + splitLine.show = true; + splitLine.lineStyle.type = LineStyle.Type.Solid; + axisLabel.textLimit.enable = false; + minMaxType = AxisMinMaxType.Custom; + min = 0; + max = 360; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs.meta new file mode 100644 index 0000000..ea0bbb0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 787015be923a74e1da4000c7abc2dcdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs new file mode 100644 index 0000000..a96a7b5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs @@ -0,0 +1,174 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class AngleAxisHandler : AxisHandler<AngleAxis> + { + public override void InitComponent() + { + InitAngleAxis(component); + } + + public override void Update() + { + component.context.startAngle = 90 - component.startAngle; + UpdateAxisMinMaxValue(component); + UpdatePointerValue(component); + } + + public override void DrawBase(VertexHelper vh) + { + DrawAngleAxis(vh, component); + } + + private void UpdateAxisMinMaxValue(AngleAxis axis, bool updateChart = true) + { + if (axis.IsCategory() || !axis.show) return; + double tempMinValue = 0; + double tempMaxValue = 0; + SeriesHelper.GetYMinMaxValue(chart, axis.polarIndex, axis.inverse, out tempMinValue, + out tempMaxValue, true); + AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true); + if (tempMinValue != axis.context.minValue || tempMaxValue != axis.context.maxValue) + { + axis.UpdateMinMaxValue(tempMinValue, tempMaxValue); + axis.context.offset = 0; + axis.context.lastCheckInverse = axis.inverse; + UpdateAxisTickValueList(axis); + + if (updateChart) + { + UpdateAxisLabelText(axis); + chart.RefreshChart(); + } + } + } + + internal void UpdateAxisLabelText(AngleAxis axis) + { + var runtimeWidth = 360; + if (axis.context.labelObjectList.Count <= 0) + InitAngleAxis(axis); + else + UpdateLabelText(axis, runtimeWidth, null, false); + } + + private void InitAngleAxis(AngleAxis axis) + { + var polar = chart.GetChartComponent<PolarCoord>(axis.polarIndex); + if (polar == null) return; + PolarHelper.UpdatePolarCenter(polar, chart.chartPosition, chart.chartWidth, chart.chartHeight); + var radius = polar.context.outsideRadius; + axis.context.labelObjectList.Clear(); + axis.context.startAngle = 90 - axis.startAngle; + + string objName = component.GetType().Name + axis.index; + var axisObj = ChartHelper.AddObject(objName, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + axisObj.transform.localPosition = Vector3.zero; + axisObj.SetActive(axis.show); + axisObj.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(axisObj); + var splitNumber = AxisHelper.GetSplitNumber(axis, radius, null); + var totalAngle = axis.context.startAngle; + var total = 360; + var cenPos = polar.context.center; + var txtHig = axis.axisLabel.textStyle.GetFontSize(chart.theme.axis) + 2; + var margin = axis.axisLabel.distance + axis.axisTick.GetLength(chart.theme.axis.tickLength); + var isCategory = axis.IsCategory(); + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series); + for (int i = 0; i < splitNumber; i++) + { + float scaleAngle = AxisHelper.GetScaleWidth(axis, total, i + 1, null); + bool inside = axis.axisLabel.inside; + var labelName = AxisHelper.GetLabelName(axis, total, i, axis.context.minValue, axis.context.maxValue, + null, isPercentStack); + var label = ChartHelper.AddAxisLabelObject(splitNumber, i, objName + i, axisObj.transform, + new Vector2(scaleAngle, txtHig), axis, + chart.theme.axis, labelName, Color.clear); + label.text.SetAlignment(axis.axisLabel.textStyle.GetAlignment(TextAnchor.MiddleCenter)); + var pos = ChartHelper.GetPos(cenPos, radius + margin, + isCategory ? (totalAngle + scaleAngle / 2) : totalAngle, true); + AxisHelper.AdjustCircleLabelPos(label, pos, cenPos, txtHig, Vector3.zero); + if (i == 0) axis.axisLabel.SetRelatedText(label.text, scaleAngle); + axis.context.labelObjectList.Add(label); + + totalAngle += scaleAngle; + } + } + + private void DrawAngleAxis(VertexHelper vh, AngleAxis angleAxis) + { + var polar = chart.GetChartComponent<PolarCoord>(angleAxis.polarIndex); + var radius = polar.context.outsideRadius; + var cenPos = polar.context.center; + var total = 360; + var size = AxisHelper.GetScaleNumber(angleAxis, total, null); + var currAngle = angleAxis.context.startAngle; + var tickWidth = angleAxis.axisTick.GetWidth(chart.theme.axis.tickWidth); + var tickLength = angleAxis.axisTick.GetLength(chart.theme.axis.tickLength); + var tickColor = angleAxis.axisTick.GetColor(chart.theme.axis.lineColor); + var lineColor = angleAxis.axisLine.GetColor(chart.theme.axis.lineColor); + var splitLineColor = angleAxis.splitLine.GetColor(chart.theme.axis.splitLineColor); + for (int i = 1; i < size; i++) + { + var scaleWidth = AxisHelper.GetScaleWidth(angleAxis, total, i); + var pos1 = ChartHelper.GetPos(cenPos, polar.context.insideRadius, currAngle, true); + var pos2 = ChartHelper.GetPos(cenPos, polar.context.outsideRadius, currAngle, true); + if (angleAxis.show && angleAxis.splitLine.show) + { + if (angleAxis.splitLine.NeedShow(i - 1, size - 1)) + { + var lineWidth = angleAxis.splitLine.GetWidth(chart.theme.axis.splitLineWidth); + UGL.DrawLine(vh, pos1, pos2, lineWidth, splitLineColor); + } + } + if (angleAxis.show && angleAxis.axisTick.show) + { + if ((i == 1 && angleAxis.axisTick.showStartTick) || + (i == size - 1 && angleAxis.axisTick.showEndTick) || + (i > 1 && i < size - 1)) + { + var tickY = radius + tickLength; + var tickPos = ChartHelper.GetPos(cenPos, tickY, currAngle, true); + UGL.DrawLine(vh, pos2, tickPos, tickWidth, tickColor); + } + } + currAngle += scaleWidth; + } + if (angleAxis.show && angleAxis.axisLine.show) + { + var lineWidth = angleAxis.axisLine.GetWidth(chart.theme.axis.lineWidth); + var outsideRaidus = radius + lineWidth * 2; + UGL.DrawDoughnut(vh, cenPos, radius, outsideRaidus, lineColor, ColorUtil.clearColor32); + if (polar.context.insideRadius > 0) + { + radius = polar.context.insideRadius; + outsideRaidus = radius + lineWidth * 2; + UGL.DrawDoughnut(vh, cenPos, radius, outsideRaidus, lineColor, ColorUtil.clearColor32); + } + } + } + + protected override void UpdatePointerValue(Axis axis) + { + var polar = chart.GetChartComponent<PolarCoord>(axis.polarIndex); + if (polar == null) + return; + + if (!polar.context.isPointerEnter) + { + axis.context.pointerValue = double.PositiveInfinity; + return; + } + + var dir = (chart.pointerPos - new Vector2(polar.context.center.x, polar.context.center.y)).normalized; + var angle = ChartHelper.GetAngle360(Vector2.up, dir); + axis.context.pointerValue = (angle - component.context.startAngle + 360) % 360; + axis.context.pointerLabelPosition = polar.context.center + new Vector3(dir.x, dir.y) * (polar.context.outsideRadius + polar.indicatorLabelOffset); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs.meta new file mode 100644 index 0000000..b3408f6 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AngleAxis/AngleAxisHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83f228c42435c4619943a2f187c98e7b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/Axis.cs b/Assets/XCharts/Runtime/Component/Axis/Axis.cs new file mode 100644 index 0000000..0d048a1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/Axis.cs @@ -0,0 +1,971 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The axis in rectangular coordinate. + /// ||直角坐标系的坐标轴组件。 + /// </summary> + [System.Serializable] + public class Axis : MainComponent + { + /// <summary> + /// the type of axis. + /// ||坐标轴类型。 + /// </summary> + public enum AxisType + { + /// <summary> + /// Numerical axis, suitable for continuous data. + /// ||数值轴。适用于连续数据。 + /// </summary> + Value, + /// <summary> + /// Category axis, suitable for discrete category data. Data should only be set via data for this type. + /// ||类目轴。适用于离散的类目数据,为该类型时必须通过 data 设置类目数据。serie的数据第0维数据对应坐标轴data的index。 + /// </summary> + Category, + /// <summary> + /// Log axis, suitable for log data. + /// ||对数轴。适用于对数数据。 + /// </summary> + Log, + /// <summary> + /// Time axis, suitable for continuous time series data. + /// ||时间轴。适用于连续的时序数据。 + /// </summary> + Time + } + + /// <summary> + /// the type of axis min and max value. + /// ||坐标轴最大最小刻度显示类型。 + /// </summary> + public enum AxisMinMaxType + { + /// <summary> + /// 0 - maximum. + /// ||0-最大值。 + /// </summary> + Default, + /// <summary> + /// minimum - maximum. + /// ||最小值-最大值。 + /// </summary> + MinMax, + /// <summary> + /// Customize the minimum and maximum. + /// ||自定义最小值最大值。 + /// </summary> + Custom, + /// <summary> + /// [since("v3.7.0")]minimum - maximum, automatically calculate the appropriate values. + /// ||[since("v3.7.0")]最小值-最大值。自动计算合适的值。 + /// </summary> + MinMaxAuto, + } + /// <summary> + /// the position of axis in grid. + /// ||坐标轴在Grid中的位置 + /// </summary> + public enum AxisPosition + { + Left, + Right, + Bottom, + Top, + Center + } + + [SerializeField] protected bool m_Show = true; + [SerializeField] protected Axis.AxisType m_Type; + [SerializeField] protected Axis.AxisMinMaxType m_MinMaxType; + [SerializeField] protected int m_GridIndex; + [SerializeField] protected int m_PolarIndex; + [SerializeField] protected int m_ParallelIndex; + [SerializeField] protected Axis.AxisPosition m_Position; + [SerializeField] protected float m_Offset; + [SerializeField] protected double m_Min; + [SerializeField] protected double m_Max; + [SerializeField] protected int m_SplitNumber = 0; + [SerializeField] protected double m_Interval = 0; + [SerializeField] protected bool m_BoundaryGap = true; + [SerializeField] protected int m_MaxCache = 0; + [SerializeField] protected float m_LogBase = 10; + [SerializeField] protected bool m_LogBaseE = false; + [SerializeField] protected double m_CeilRate = 0; + [SerializeField] protected bool m_Inverse = false; + [SerializeField] private bool m_Clockwise = true; + [SerializeField] private bool m_InsertDataToHead; + [SerializeField][Since("v3.11.0")] private float m_MinCategorySpacing = 0; + [SerializeField] protected List<Sprite> m_Icons = new List<Sprite>(); + [SerializeField] protected List<string> m_Data = new List<string>(); + [SerializeField] protected AxisLine m_AxisLine = AxisLine.defaultAxisLine; + [SerializeField] protected AxisName m_AxisName = AxisName.defaultAxisName; + [SerializeField] protected AxisTick m_AxisTick = AxisTick.defaultTick; + [SerializeField] protected AxisLabel m_AxisLabel = AxisLabel.defaultAxisLabel; + [SerializeField] protected AxisSplitLine m_SplitLine = AxisSplitLine.defaultSplitLine; + [SerializeField] protected AxisSplitArea m_SplitArea = AxisSplitArea.defaultSplitArea; + [SerializeField] protected AxisAnimation m_Animation = new AxisAnimation(); + [SerializeField][Since("v3.2.0")] protected AxisMinorTick m_MinorTick = AxisMinorTick.defaultMinorTick; + [SerializeField][Since("v3.2.0")] protected AxisMinorSplitLine m_MinorSplitLine = AxisMinorSplitLine.defaultMinorSplitLine; + [SerializeField][Since("v3.4.0")] protected LabelStyle m_IndicatorLabel = new LabelStyle() { numericFormatter = "f2" }; + + public AxisContext context = new AxisContext(); + + /// <summary> + /// Whether to show axis. + /// ||是否显示坐标轴。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetAllDirty(); } + } + /// <summary> + /// the type of axis. + /// ||坐标轴类型。 + /// </summary> + public AxisType type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetAllDirty(); } + } + /// <summary> + /// the type of axis minmax. + /// ||坐标轴刻度最大最小值显示类型。 + /// </summary> + public AxisMinMaxType minMaxType + { + get { return m_MinMaxType; } + set { if (PropertyUtil.SetStruct(ref m_MinMaxType, value)) SetAllDirty(); } + } + /// <summary> + /// The index of the grid on which the axis are located, by default, is in the first grid. + /// ||坐标轴所在的 grid 的索引,默认位于第一个 grid。 + /// </summary> + public int gridIndex + { + get { return m_GridIndex; } + set { if (PropertyUtil.SetStruct(ref m_GridIndex, value)) SetAllDirty(); } + } + /// <summary> + /// The index of the polar on which the axis are located, by default, is in the first polar. + /// ||坐标轴所在的 ploar 的索引,默认位于第一个 polar。 + /// </summary> + public int polarIndex + { + get { return m_PolarIndex; } + set { if (PropertyUtil.SetStruct(ref m_PolarIndex, value)) SetAllDirty(); } + } + /// <summary> + /// The index of the parallel on which the axis are located, by default, is in the first parallel. + /// ||坐标轴所在的 parallel 的索引,默认位于第一个 parallel。 + /// </summary> + public int parallelIndex + { + get { return m_ParallelIndex; } + set { if (PropertyUtil.SetStruct(ref m_ParallelIndex, value)) SetAllDirty(); } + } + /// <summary> + /// the position of axis in grid. + /// ||坐标轴在Grid中的位置。 + /// </summary> + public AxisPosition position + { + get { return m_Position; } + set { if (PropertyUtil.SetStruct(ref m_Position, value)) SetAllDirty(); } + } + /// <summary> + /// the offset of axis from the default position. Useful when the same position has multiple axes. + /// ||坐标轴相对默认位置的偏移。在相同position有多个坐标轴时有用。 + /// </summary> + public float offset + { + get { return m_Offset; } + set { if (PropertyUtil.SetStruct(ref m_Offset, value)) SetAllDirty(); } + } + /// <summary> + /// The minimun value of axis.Valid when `minMaxType` is `Custom` + /// ||设定的坐标轴刻度最小值,当minMaxType为Custom时有效。 + /// </summary> + public double min + { + get { return m_Min; } + set { if (PropertyUtil.SetStruct(ref m_Min, value)) SetAllDirty(); } + } + /// <summary> + /// The maximum value of axis.Valid when `minMaxType` is `Custom` + /// ||设定的坐标轴刻度最大值,当minMaxType为Custom时有效。 + /// </summary> + public double max + { + get { return m_Max; } + set { if (PropertyUtil.SetStruct(ref m_Max, value)) SetAllDirty(); } + } + /// <summary> + /// Number of segments that the axis is split into. + /// ||坐标轴的期望的分割段数。默认为0表示自动分割。 + /// </summary> + public int splitNumber + { + get { return m_SplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value)) SetAllDirty(); } + } + /// <summary> + /// Compulsively set segmentation interval for axis.This is unavailable for category axis. + /// ||强制设置坐标轴分割间隔。无法在类目轴中使用。 + /// </summary> + public double interval + { + get { return m_Interval; } + set { if (PropertyUtil.SetStruct(ref m_Interval, value)) SetAllDirty(); } + } + /// <summary> + /// The boundary gap on both sides of a coordinate axis, which is valid only for category axis with type: 'Category'. + /// ||坐标轴两边是否留白。只对类目轴有效。 + /// </summary> + public bool boundaryGap + { + get { return IsCategory() ? m_BoundaryGap : false; } + set { if (PropertyUtil.SetStruct(ref m_BoundaryGap, value)) SetAllDirty(); } + } + /// <summary> + /// Base of logarithm, which is valid only for numeric axes with type: 'Log'. + /// ||对数轴的底数,只在对数轴(type:'Log')中有效。 + /// </summary> + public float logBase + { + get { return m_LogBase; } + set + { + if (value <= 0 || value == 1) value = 10; + if (PropertyUtil.SetStruct(ref m_LogBase, value)) SetAllDirty(); + } + } + /// <summary> + /// On the log axis, if base e is the natural number, and is true, logBase fails. + /// ||对数轴是否以自然数 e 为底数,为 true 时 logBase 失效。 + /// </summary> + public bool logBaseE + { + get { return m_LogBaseE; } + set { if (PropertyUtil.SetStruct(ref m_LogBaseE, value)) SetAllDirty(); } + } + /// <summary> + /// The max number of axis data cache. + /// ||The first data will be remove when the size of axis data is larger then maxCache. + /// ||可缓存的最大数据量。默认为0没有限制,大于0时超过指定值会移除旧数据再插入新数据。 + /// </summary> + public int maxCache + { + get { return m_MaxCache; } + set { if (PropertyUtil.SetStruct(ref m_MaxCache, value < 0 ? 0 : value)) SetAllDirty(); } + } + /// <summary> + /// The ratio of maximum and minimum values rounded upward. The default is 0, which is automatically calculated. + /// ||最大最小值向上取整的倍率。默认为0时自动计算。 + /// </summary> + public double ceilRate + { + get { return m_CeilRate; } + set { if (PropertyUtil.SetStruct(ref m_CeilRate, value < 0 ? 0 : value)) SetAllDirty(); } + } + /// <summary> + /// Whether the axis are reversed or not. Invalid in `Category` axis. + /// ||是否反向坐标轴。在类目轴中无效。 + /// </summary> + public bool inverse + { + get { return m_Inverse; } + set { if (m_Type == AxisType.Value && PropertyUtil.SetStruct(ref m_Inverse, value)) SetAllDirty(); } + } + /// <summary> + /// Whether the positive position of axis is in clockwise. True for clockwise by default. + /// ||刻度增长是否按顺时针,默认顺时针。 + /// </summary> + public bool clockwise + { + get { return m_Clockwise; } + set { if (PropertyUtil.SetStruct(ref m_Clockwise, value)) SetAllDirty(); } + } + /// <summary> + /// Category data, available in type: 'Category' axis. + /// ||类目数据,在类目轴(type: 'category')中有效。 + /// </summary> + public List<string> data + { + get { return m_Data; } + set { if (value != null) { m_Data = value; SetAllDirty(); } } + } + /// <summary> + /// 类目数据对应的图标。 + /// </summary> + public List<Sprite> icons + { + get { return m_Icons; } + set { if (value != null) { m_Icons = value; SetAllDirty(); } } + } + /// <summary> + /// axis Line. + /// ||坐标轴轴线。 + /// </summary> + public AxisLine axisLine + { + get { return m_AxisLine; } + set { if (value != null) { m_AxisLine = value; SetVerticesDirty(); } } + } + /// <summary> + /// axis name. + /// ||坐标轴名称。 + /// </summary> + public AxisName axisName + { + get { return m_AxisName; } + set { if (value != null) { m_AxisName = value; SetComponentDirty(); } } + } + /// <summary> + /// axis tick. + /// ||坐标轴刻度。 + /// </summary> + public AxisTick axisTick + { + get { return m_AxisTick; } + set { if (value != null) { m_AxisTick = value; SetVerticesDirty(); } } + } + /// <summary> + /// axis label. + /// ||坐标轴刻度标签。 + /// </summary> + public AxisLabel axisLabel + { + get { return m_AxisLabel; } + set { if (value != null) { m_AxisLabel = value; SetComponentDirty(); } } + } + /// <summary> + /// axis split line. + /// ||坐标轴分割线。 + /// </summary> + public AxisSplitLine splitLine + { + get { return m_SplitLine; } + set { if (value != null) { m_SplitLine = value; SetVerticesDirty(); } } + } + /// <summary> + /// axis split area. + /// ||坐标轴分割区域。 + /// </summary> + public AxisSplitArea splitArea + { + get { return m_SplitArea; } + set { if (value != null) { m_SplitArea = value; SetVerticesDirty(); } } + } + /// <summary> + /// axis minor tick. + /// ||坐标轴次刻度。 + /// </summary> + public AxisMinorTick minorTick + { + get { return m_MinorTick; } + set { if (value != null) { m_MinorTick = value; SetVerticesDirty(); } } + } + /// <summary> + /// axis minor split line. + /// ||坐标轴次分割线。 + /// </summary> + public AxisMinorSplitLine minorSplitLine + { + get { return m_MinorSplitLine; } + set { if (value != null) { m_MinorSplitLine = value; SetVerticesDirty(); } } + } + /// <summary> + /// Style of axis tooltip indicator label. + /// ||指示器文本的样式。Tooltip为Cross时使用。 + /// </summary> + public LabelStyle indicatorLabel + { + get { return m_IndicatorLabel; } + set { if (value != null) { m_IndicatorLabel = value; SetComponentDirty(); } } + } + /// <summary> + /// animation of axis. + /// ||坐标轴动画。 + /// </summary> + public AxisAnimation animation + { + get { return m_Animation; } + set { if (value != null) { m_Animation = value; SetComponentDirty(); } } + } + /// <summary> + /// Whether to add new data at the head or at the end of the list. + /// ||添加新数据时是在列表的头部还是尾部加入。 + /// </summary> + public bool insertDataToHead + { + get { return m_InsertDataToHead; } + set { if (PropertyUtil.SetStruct(ref m_InsertDataToHead, value)) SetAllDirty(); } + } + /// <summary> + /// The minimum spacing between categories. + /// ||类目之间的最小间距。 + /// </summary> + public float minCategorySpacing + { + get { return m_MinCategorySpacing; } + set { if (PropertyUtil.SetStruct(ref m_MinCategorySpacing, value)) SetAllDirty(); } + } + + public override bool vertsDirty + { + get + { + return m_VertsDirty || + axisLine.anyDirty || + axisTick.anyDirty || + splitLine.anyDirty || + splitArea.anyDirty || + minorTick.anyDirty || + minorSplitLine.anyDirty; + } + } + + public override bool componentDirty + { + get + { + return m_ComponentDirty || + axisName.anyDirty || + axisLabel.anyDirty || + indicatorLabel.anyDirty; + } + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + axisName.ClearComponentDirty(); + axisLabel.ClearComponentDirty(); + indicatorLabel.ClearComponentDirty(); + } + + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + axisLabel.ClearVerticesDirty(); + axisLine.ClearVerticesDirty(); + axisTick.ClearVerticesDirty(); + splitLine.ClearVerticesDirty(); + splitArea.ClearVerticesDirty(); + minorTick.ClearVerticesDirty(); + minorSplitLine.ClearVerticesDirty(); + indicatorLabel.ClearVerticesDirty(); + } + + public override void SetComponentDirty() + { + context.isNeedUpdateFilterData = true; + base.SetComponentDirty(); + } + + /// <summary> + /// 重置状态。 + /// </summary> + public override void ResetStatus() + { + context.minValue = 0; + context.maxValue = 0; + context.destMinValue = 0; + context.destMaxValue = 0; + } + + public Axis Clone() + { + var axis = new Axis(); + axis.show = show; + axis.type = type; + axis.gridIndex = 0; + axis.minMaxType = minMaxType; + axis.min = min; + axis.max = max; + axis.splitNumber = splitNumber; + axis.interval = interval; + axis.boundaryGap = boundaryGap; + axis.maxCache = maxCache; + axis.logBase = logBase; + axis.logBaseE = logBaseE; + axis.ceilRate = ceilRate; + axis.insertDataToHead = insertDataToHead; + axis.axisLine = axisLine.Clone(); + axis.axisName = axisName.Clone(); + axis.axisTick = axisTick.Clone(); + axis.axisLabel = axisLabel.Clone(); + axis.splitLine = splitLine.Clone(); + axis.splitArea = splitArea.Clone(); + axis.minorTick = minorTick.Clone(); + axis.minorSplitLine = minorSplitLine.Clone(); + axis.indicatorLabel = indicatorLabel.Clone(); + axis.animation = animation.Clone(); + axis.icons = new List<Sprite>(); + axis.data = new List<string>(); + ChartHelper.CopyList(axis.data, data); + return axis; + } + + public void Copy(Axis axis) + { + show = axis.show; + type = axis.type; + minMaxType = axis.minMaxType; + gridIndex = axis.gridIndex; + min = axis.min; + max = axis.max; + splitNumber = axis.splitNumber; + interval = axis.interval; + boundaryGap = axis.boundaryGap; + maxCache = axis.maxCache; + logBase = axis.logBase; + logBaseE = axis.logBaseE; + ceilRate = axis.ceilRate; + insertDataToHead = axis.insertDataToHead; + axisLine.Copy(axis.axisLine); + axisName.Copy(axis.axisName); + axisTick.Copy(axis.axisTick); + axisLabel.Copy(axis.axisLabel); + splitLine.Copy(axis.splitLine); + splitArea.Copy(axis.splitArea); + minorTick.Copy(axis.minorTick); + minorSplitLine.Copy(axis.minorSplitLine); + indicatorLabel.Copy(axis.indicatorLabel); + animation.Copy(axis.animation); + ChartHelper.CopyList(data, axis.data); + ChartHelper.CopyList<Sprite>(icons, axis.icons); + } + + /// <summary> + /// 清空类目数据 + /// </summary> + public override void ClearData() + { + m_Data.Clear(); + m_Icons.Clear(); + context.Clear(); + SetAllDirty(); + } + + /// <summary> + /// 是否为类目轴。 + /// </summary> + /// <returns></returns> + public bool IsCategory() + { + return m_Type == AxisType.Category; + } + + /// <summary> + /// 是否为数值轴。 + /// </summary> + /// <returns></returns> + public bool IsValue() + { + return m_Type == AxisType.Value; + } + + /// <summary> + /// 是否为对数轴。 + /// </summary> + /// <returns></returns> + public bool IsLog() + { + return m_Type == AxisType.Log; + } + + /// <summary> + /// 是否为时间轴。 + /// </summary> + public bool IsTime() + { + return m_Type == AxisType.Time; + } + + public bool IsLeft() + { + return m_Position == AxisPosition.Left; + } + + public bool IsRight() + { + return m_Position == AxisPosition.Right; + } + + public bool IsTop() + { + return m_Position == AxisPosition.Top; + } + + public bool IsBottom() + { + return m_Position == AxisPosition.Bottom; + } + + public bool IsNeedShowLabel(int index, int total = 0) + { + if (total == 0) + { + total = context.labelValueList.Count; + } + return axisLabel.IsNeedShowLabel(index, total); + } + + public void SetNeedUpdateFilterData() + { + context.isNeedUpdateFilterData = true; + } + + /// <summary> + /// 添加一个类目到类目数据列表 + /// </summary> + /// <param name="category"></param> + public void AddData(string category) + { + if (maxCache > 0) + { + if (context.addedDataCount < m_Data.Count) + context.addedDataCount = m_Data.Count; + while (m_Data.Count >= maxCache) + { + RemoveData(m_InsertDataToHead ? m_Data.Count - 1 : 0); + } + } + context.addedDataCount++; + if (m_InsertDataToHead) + m_Data.Insert(0, category); + else + m_Data.Add(category); + + SetAllDirty(); + } + + /// <summary> + /// get the history data count. + /// ||获得添加过的历史数据总数 + /// </summary> + /// <returns></returns> + public int GetAddedDataCount() + { + return context.addedDataCount < m_Data.Count ? m_Data.Count : context.addedDataCount; + } + + public void RemoveData(int dataIndex) + { + context.isNeedUpdateFilterData = true; + m_Data.RemoveAt(dataIndex); + } + + /// <summary> + /// 更新类目数据 + /// </summary> + /// <param name="index"></param> + /// <param name="category"></param> + public void UpdateData(int index, string category) + { + if (index >= 0 && index < m_Data.Count) + { + m_Data[index] = category; + SetComponentDirty(); + } + } + + /// <summary> + /// 添加图标 + /// </summary> + /// <param name="icon"></param> + public void AddIcon(Sprite icon) + { + if (maxCache > 0) + { + while (m_Icons.Count > maxCache) + { + m_Icons.RemoveAt(m_InsertDataToHead ? m_Icons.Count - 1 : 0); + } + } + if (m_InsertDataToHead) m_Icons.Insert(0, icon); + else m_Icons.Add(icon); + SetAllDirty(); + } + + /// <summary> + /// 更新图标 + /// </summary> + /// <param name="index"></param> + /// <param name="icon"></param> + public void UpdateIcon(int index, Sprite icon) + { + if (index >= 0 && index < m_Icons.Count) + { + m_Icons[index] = icon; + SetComponentDirty(); + } + } + + /// <summary> + /// 获得指定索引的类目数据 + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public string GetData(int index) + { + if (index >= 0 && index < m_Data.Count) + return m_Data[index]; + else + return null; + } + + /// <summary> + /// 获得在dataZoom范围内指定索引的类目数据 + /// </summary> + /// <param name="index">类目数据索引</param> + /// <param name="dataZoom">区域缩放</param> + /// <returns></returns> + public string GetData(int index, DataZoom dataZoom) + { + var showData = GetDataList(dataZoom); + if (index >= 0 && index < showData.Count) + return showData[index]; + else + return ""; + } + + public Sprite GetIcon(int index) + { + if (index >= 0 && index < m_Icons.Count) + return m_Icons[index]; + else + return null; + } + + /// <summary> + /// 获得值在坐标轴上的距离 + /// </summary> + /// <param name="value"></param> + /// <param name="axisLength"></param> + /// <returns></returns> + public float GetDistance(double value, float axisLength = 0) + { + if (context.minMaxRange == 0) + return 0; + if (axisLength == 0) + { + axisLength = context.length; + } + + if (IsCategory() && boundaryGap) + { + var each = axisLength / data.Count; + return (float)(each * (value + 0.5f)); + } + else if (IsLog()) + { + var logValue = GetLogValue(value); + var logMin = GetLogValue(context.minValue); + var logMax = GetLogValue(context.maxValue); + return axisLength * (float)((logValue - logMin) / (logMax - logMin)); + } + else + { + return axisLength * (float)((value - context.minValue) / context.minMaxRange); + } + } + + public float GetValueLength(double value, float axisLength) + { + if (context.minMaxRange > 0) + { + return axisLength * ((float)(value / context.minMaxRange)); + } + else + { + return 0; + } + } + + /// <summary> + /// 获得指定区域缩放的类目数据列表 + /// </summary> + /// <param name="dataZoom">区域缩放</param> + /// <returns></returns> + internal List<string> GetDataList(DataZoom dataZoom) + { + if (dataZoom != null && dataZoom.enable && dataZoom.IsContainsAxis(this)) + { + UpdateFilterData(dataZoom); + return context.filterData; + } + else + { + return m_Data.Count > 0 ? m_Data : context.runtimeData; + } + } + + internal List<string> GetDataList() + { + return m_Data.Count > 0 ? m_Data : context.runtimeData; + } + + /// <summary> + /// 更新dataZoom对应的类目数据列表 + /// </summary> + /// <param name="dataZoom"></param> + internal void UpdateFilterData(DataZoom dataZoom) + { + if (dataZoom != null && dataZoom.enable && dataZoom.IsContainsAxis(this)) + { + var data = GetDataList(); + context.UpdateFilterData(data, dataZoom); + } + } + + /// <summary> + /// 获得类目数据个数 + /// </summary> + /// <param name="dataZoom"></param> + /// <returns></returns> + internal int GetDataCount(DataZoom dataZoom) + { + return IsCategory() ? GetDataList(dataZoom).Count : 0; + } + + internal Vector3 GetLabelObjectPosition(int index) + { + if (context.labelObjectList != null && index < context.labelObjectList.Count) + return context.labelObjectList[index].GetPosition(); + else + return Vector3.zero; + } + + internal void UpdateMinMaxValue(double minValue, double maxValue, bool needAnimation = false) + { + if (needAnimation) + { + if (context.lastMinValue == 0 && context.lastMaxValue == 0) + { + context.minValue = minValue; + context.maxValue = maxValue; + } + context.lastMinValue = context.minValue; + context.lastMaxValue = context.maxValue; + context.destMinValue = minValue; + context.destMaxValue = maxValue; + } + else + { + context.minValue = minValue; + context.maxValue = maxValue; + context.destMinValue = minValue; + context.destMaxValue = maxValue; + } + double tempRange = maxValue - minValue; + if (context.minMaxRange != tempRange) + { + context.minMaxRange = tempRange; + if (type == Axis.AxisType.Value && interval > 0) + { + SetComponentDirty(); + } + } + } + + public float GetLogValue(double value) + { + if (value <= 0 || value == 1) + return 0; + else + return logBaseE ? (float)Math.Log(value) : (float)Math.Log(value, logBase); + } + + public double GetLogMinIndex() + { + if (context.minValue <= 0 || context.minValue == 1) + return 0; + return logBaseE ? + Math.Log(context.minValue) : + Math.Log(context.minValue, logBase); + } + + public double GetLogMaxIndex() + { + if (context.maxValue <= 0 || context.maxValue == 1) + return 0; + return logBaseE ? + Math.Log(context.maxValue) : + Math.Log(context.maxValue, logBase); + } + + public double GetLabelValue(int index) + { + if (index < 0) + return context.minValue; + else if (index > context.labelValueList.Count - 1) + return context.maxValue; + else + return context.labelValueList[index]; + } + + public double GetLastLabelValue() + { + if (context.labelValueList.Count > 0) + return context.labelValueList[context.labelValueList.Count - 1]; + else + return 0; + } + + public void UpdateZeroOffset(float axisLength) + { + context.offset = context.minValue > 0 || context.minMaxRange == 0 ? + 0 : + (context.maxValue < 0 ? + axisLength : + (float)(Math.Abs(context.minValue) * (axisLength / (Math.Abs(context.minValue) + Math.Abs(context.maxValue)))) + ); + } + + public Vector3 GetCategoryPosition(int categoryIndex, int dataCount = 0) + { + if (dataCount <= 0) + { + dataCount = data.Count; + } + if (IsCategory() && dataCount > 0) + { + Vector3 pos; + if (boundaryGap) + { + var each = context.length / dataCount; + pos = context.start + context.dire * (each * (categoryIndex + 0.5f)); + } + else + { + var each = context.length / (dataCount - 1); + pos = context.start + context.dire * (each * categoryIndex); + } + if (axisLabel.distance != 0) + { + if (this is YAxis) + { + pos.x = GetLabelObjectPosition(0).x; + } + else + { + pos.y = GetLabelObjectPosition(0).y; + } + } + return pos; + } + else + { + return Vector3.zero; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/Axis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/Axis.cs.meta new file mode 100644 index 0000000..c86a648 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/Axis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5c29555575e04db98ee243c3b17f0ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs b/Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs new file mode 100644 index 0000000..bd3be5d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs @@ -0,0 +1,160 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + public static class Axis3DHelper + { + public static Vector3 Get3DGridPosition(GridCoord3D grid, XAxis3D xAxis, YAxis3D yAxis, ZAxis3D zAxis, double xValue, double yValue, double zValue) + { + var x = xAxis.GetDistance(xValue); + var y = yAxis.GetDistance(yValue); + var z = zAxis.GetDistance(zValue); + + var dest = grid.context.pointA; + dest += xAxis.context.dire * x; + dest += yAxis.context.dire * y; + dest += zAxis.context.dire * z; + return dest; + } + + public static Vector3 Get3DGridPosition(GridCoord3D grid, XAxis3D xAxis, YAxis3D yAxis, double xValue, double yValue) + { + var x = xAxis.GetDistance(xValue); + var y = yAxis.GetDistance(yValue); + + var dest = grid.context.pointA; + dest += xAxis.context.dire * x; + dest += yAxis.context.dire * y; + return dest; + } + + internal static void DrawAxisTick(VertexHelper vh, Axis axis, AxisTheme theme, DataZoom dataZoom, + Vector3 start, Vector3 end, Vector3 relativedDire) + { + var tickLength = axis.axisTick.GetLength(theme.tickLength); + var axisLength = Vector3.Distance(start, end); + var axisDire = (end - start).normalized; + + if (axis.position == Axis.AxisPosition.Right) + { + relativedDire = -relativedDire; + } + + if (AxisHelper.NeedShowSplit(axis)) + { + var size = AxisHelper.GetScaleNumber(axis, axisLength, dataZoom); + if (axis.IsTime()) + { + size += 1; + if (!ChartHelper.IsEquals(axis.GetLastLabelValue(), axis.context.maxValue)) + size += 1; + } + var tickWidth = axis.axisTick.GetWidth(theme.tickWidth); + var tickColor = axis.axisTick.GetColor(theme.tickColor); + var current = start; + for (int i = 0; i < size; i++) + { + var scaleWidth = AxisHelper.GetScaleWidth(axis, axisLength, i + 1, dataZoom); + var hideTick = (i == 0 && (!axis.axisTick.showStartTick || axis.axisTick.alignWithLabel)) || + (i == size - 1 && !axis.axisTick.showEndTick); + if (axis.axisTick.show && !hideTick) + { + UGL.DrawLine(vh, current, current + relativedDire * tickLength, tickWidth, tickColor); + } + current += axisDire * scaleWidth; + } + } + if (axis.show && axis.axisLine.show && axis.axisLine.showArrow) + { + + } + } + + public static void DrawAxisSplit(VertexHelper vh, Axis axis, AxisTheme theme, DataZoom dataZoom, + Vector3 start, Vector3 end, Axis relativedAxis) + { + if (relativedAxis == null) return; + var axisLength = Vector3.Distance(start, end); + var axisDire = (end - start).normalized; + var splitLength = relativedAxis.context.length; + var relativeDire = relativedAxis.context.dire; + var axisLineWidth = axis.axisLine.GetWidth(theme.lineWidth); + splitLength -= axisLineWidth; + var lineColor = axis.splitLine.GetColor(theme.splitLineColor); + var lineWidth = axis.splitLine.GetWidth(theme.lineWidth); + var lineType = axis.splitLine.GetType(theme.splitLineType); + + var size = AxisHelper.GetScaleNumber(axis, axisLength, dataZoom); + if (axis.IsTime()) + { + size += 1; + if (!ChartHelper.IsEquals(axis.GetLastLabelValue(), axis.context.maxValue)) + size += 1; + } + + var current = start; + for (int i = 0; i < size; i++) + { + var scaleWidth = AxisHelper.GetScaleWidth(axis, axisLength, axis.IsTime() ? i : i + 1, dataZoom); + if (axis.boundaryGap && axis.axisTick.alignWithLabel) + current -= axisDire * scaleWidth / 2; + + if (axis.splitArea.show && i <= size - 1) + { + var p1 = current; + var p2 = current + relativeDire * splitLength; + var p3 = p2 + axisDire * scaleWidth; + var p4 = p1 + axisDire * scaleWidth; + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, axis.splitArea.GetColor(i, theme)); + } + if (axis.splitLine.show) + { + if (axis.splitLine.NeedShow(i, size)) + { + if (relativedAxis == null || !relativedAxis.axisLine.show + || (Vector3.Distance(current, relativedAxis.context.start) > 0.5f && Vector3.Distance(current, relativedAxis.context.end) > 0.5f)) + { + ChartDrawer.DrawLineStyle(vh, + lineType, + lineWidth, + current, + current + relativeDire * splitLength, + lineColor); + } + } + } + current += axisDire * scaleWidth; + } + } + + public static Vector3 GetLabelPosition(int i, Axis axis, Axis relativedAxis, AxisTheme theme, float scaleWid) + { + var axisStart = axis.context.start; + var axisEnd = axis.context.end; + var axisDire = axis.context.dire; + var relativedDire = relativedAxis != null ? relativedAxis.context.dire : Vector3.zero; + var axisLength = Vector3.Distance(axisStart, axisEnd); + var inside = axis.axisLabel.inside; + var fontSize = axis.axisLabel.textStyle.GetFontSize(theme); + var current = axis.offset; + + if (axis.position == Axis.AxisPosition.Right) + { + relativedDire = -relativedDire; + } + + if (axis.IsTime() || axis.IsValue()) + { + scaleWid = axis.context.minMaxRange != 0 ? + axis.GetDistance(axis.GetLabelValue(i), axisLength) : + 0; + } + + return axisStart + axisDire * scaleWid + axis.axisLabel.offset - relativedDire * (axis.axisLabel.distance + fontSize / 2); + } + + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs.meta b/Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs.meta new file mode 100644 index 0000000..5d5ac29 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/Axis3DHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52469636872044a81a291bb00b71a140 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs b/Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs new file mode 100644 index 0000000..51f9ca1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs @@ -0,0 +1,63 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// animation style of axis. + /// ||坐标轴动画配置。 + /// </summary> + [System.Serializable] + [Since("v3.9.0")] + public class AxisAnimation : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private float m_Duration; + [SerializeField] private bool m_UnscaledTime; + + /// <summary> + /// whether to enable animation. + /// ||是否开启动画。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } + /// <summary> + /// the duration of animation (ms). When it is set to 0, the animation duration will be automatically calculated according to the serie. + /// ||动画时长(ms)。 默认设置为0时,会自动获取serie的动画时长。 + /// </summary> + public float duration + { + get { return m_Duration; } + set { if (PropertyUtil.SetStruct(ref m_Duration, value)) SetComponentDirty(); } + } + /// <summary> + /// Animation updates independently of Time.timeScale. + /// ||动画是否受TimeScaled的影响。默认为 false 受TimeScaled的影响。 + /// </summary> + public bool unscaledTime + { + get { return m_UnscaledTime; } + set { if (PropertyUtil.SetStruct(ref m_UnscaledTime, value)) SetComponentDirty(); } + } + + public AxisAnimation Clone() + { + var animation = new AxisAnimation + { + show = show, + duration = duration, + unscaledTime = unscaledTime + }; + return animation; + } + + public void Copy(AxisAnimation animation) + { + show = animation.show; + duration = animation.duration; + unscaledTime = animation.unscaledTime; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs.meta new file mode 100644 index 0000000..1d82827 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisAnimation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecce90a24f1e64ce2affa51992d56ac4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisContext.cs b/Assets/XCharts/Runtime/Component/Axis/AxisContext.cs new file mode 100644 index 0000000..11e4e36 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisContext.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class AxisContext : MainComponentContext + { + public Orient orient; + /// <summary> + /// 坐标轴的起点X + /// </summary> + public float x; + /// <summary> + /// 坐标轴的起点Y + /// </summary> + public float y; + public Vector3 start; + public Vector3 end; + public Vector3 dire; + /// <summary> + /// 坐标轴原点X + /// </summary> + public float zeroX; + /// <summary> + /// 坐标轴原点Y + /// </summary> + public float zeroY; + public float width; + public float height; + public float length; + public Vector3 position; + public float left; + public float right; + public float bottom; + public float top; + /// <summary> + /// the current minimun value. + /// ||当前最小值。 + /// </summary> + public double minValue; + public double lastMinValue { get; internal set; } + public double destMinValue { get; internal set; } + /// <summary> + /// the current maximum value. + /// ||当前最大值。 + /// </summary> + public double maxValue; + public double lastMaxValue { get; internal set; } + public double destMaxValue { get; internal set; } + public bool needAnimation { get; internal set; } + /// <summary> + /// the offset of zero position. + /// ||坐标轴原点在坐标轴的偏移。 + /// </summary> + public float offset; + public double minMaxRange; + /// <summary> + /// the tick value of value axis. + /// ||数值轴时每个tick的数值。 + /// </summary> + public double tickValue; + public float scaleWidth; + public float startAngle; + public double pointerValue; + public Vector3 pointerLabelPosition; + public double axisTooltipValue; + public TextAnchor aligment; + public List<string> runtimeData { get { return m_RuntimeData; } } + public List<double> labelValueList { get { return m_LabelValueList; } } + public List<ChartLabel> labelObjectList { get { return m_AxisLabelList; } } + public List<int> sortedDataIndices { get { return m_SortedDataIndices; } } + public int dataZoomStartIndex; + /// <summary> + /// 添加过的历史数据总数 + /// </summary> + public int addedDataCount; + + internal List<string> filterData; + internal bool lastCheckInverse; + internal bool isNeedUpdateFilterData; + + private int filterStart; + private int filterEnd; + private int filterMinShow; + + private List<ChartLabel> m_AxisLabelList = new List<ChartLabel>(); + private List<double> m_LabelValueList = new List<double>(); + private List<string> m_RuntimeData = new List<string>(); + private List<int> m_SortedDataIndices = new List<int>(); + + internal void Clear() + { + addedDataCount = 0; + m_RuntimeData.Clear(); + } + + private List<string> m_EmptyFliter = new List<string>(); + /// <summary> + /// 更新dataZoom对应的类目数据列表 + /// </summary> + /// <param name="dataZoom"></param> + internal void UpdateFilterData(List<string> data, DataZoom dataZoom) + { + int start = 0, end = 0; + var range = Mathf.RoundToInt(data.Count * (dataZoom.end - dataZoom.start) / 100); + if (range <= 0) + range = 1; + + if (dataZoom.context.invert) + { + end = Mathf.RoundToInt(data.Count * dataZoom.end / 100); + start = end - range; + if (start < 0) start = 0; + } + else + { + start = Mathf.RoundToInt(data.Count * dataZoom.start / 100); + end = start + range; + if (end > data.Count) end = data.Count; + } + + if (start != filterStart || + end != filterEnd || + dataZoom.minShowNum != filterMinShow || + isNeedUpdateFilterData) + { + filterStart = start; + filterEnd = end; + filterMinShow = dataZoom.minShowNum; + isNeedUpdateFilterData = false; + + if (data.Count > 0) + { + if (range < dataZoom.minShowNum) + { + if (dataZoom.minShowNum > data.Count) + range = data.Count; + else + range = dataZoom.minShowNum; + } + if (range > data.Count - start) + start = data.Count - range; + if (start >= 0) + { + dataZoomStartIndex = start; + filterData = data.GetRange(start, range); + } + else + { + dataZoomStartIndex = 0; + filterData = data; + } + } + else + { + dataZoomStartIndex = 0; + filterData = data; + } + } + else if (end == 0) + { + dataZoomStartIndex = 0; + filterData = m_EmptyFliter; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisContext.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisContext.cs.meta new file mode 100644 index 0000000..5f2226c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6525f065fa5d04663ab7026a3467f56e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs b/Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs new file mode 100644 index 0000000..f77f51b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs @@ -0,0 +1,1251 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XCharts.Runtime; +using XUGL; + +namespace XCharts +{ + public abstract class AxisHandler<T> : MainComponentHandler + where T : Axis + { + private static readonly string s_DefaultAxisName = "name"; + private double m_LastInterval = double.MinValue; + private int m_LastSplitNumber = int.MinValue; + public T component { get; internal set; } + + internal override void SetComponent(MainComponent component) + { + this.component = (T)component; + } + + protected virtual Vector3 GetLabelPosition(float scaleWid, int i) + { + return Vector3.zero; + } + + internal virtual float GetAxisLineXOrY() + { + return 0; + } + + protected virtual Orient orient { get; set; } + + protected virtual void UpdatePointerValue(Axis axis) + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (grid == null) + return; + if (!grid.context.isPointerEnter) + { + axis.context.pointerValue = double.PositiveInfinity; + } + else + { + var lastPointerValue = axis.context.pointerValue; + if (axis.IsCategory()) + { + var dataZoom = chart.GetDataZoomOfAxis(axis); + var dataCount = chart.series.Count > 0 ? chart.series[0].GetDataList(dataZoom).Count : 0; + var local = chart.pointerPos; + if (axis is YAxis) + { + float splitWid = AxisHelper.GetDataWidth(axis, grid.context.height, dataCount, dataZoom); + for (int j = 0; j < axis.GetDataCount(dataZoom); j++) + { + float pY = grid.context.y + j * splitWid; + if ((axis.boundaryGap && (local.y > pY && local.y <= pY + splitWid)) || + (!axis.boundaryGap && (local.y > pY - splitWid / 2 && local.y <= pY + splitWid / 2))) + { + axis.context.pointerValue = j; + axis.context.pointerLabelPosition = axis.GetCategoryPosition(j, dataCount); + if (j != lastPointerValue) + { + if (chart.onAxisPointerValueChanged != null) + chart.onAxisPointerValueChanged(axis, j); + } + break; + } + } + } + else + { + float splitWid = AxisHelper.GetDataWidth(axis, grid.context.width, dataCount, dataZoom); + for (int j = 0; j < axis.GetDataCount(dataZoom); j++) + { + float pX = grid.context.x + j * splitWid; + if ((axis.boundaryGap && (local.x > pX && local.x <= pX + splitWid)) || + (!axis.boundaryGap && (local.x > pX - splitWid / 2 && local.x <= pX + splitWid / 2))) + { + axis.context.pointerValue = j; + axis.context.pointerLabelPosition = axis.GetCategoryPosition(j, dataCount); + if (j != lastPointerValue) + { + if (chart.onAxisPointerValueChanged != null) + chart.onAxisPointerValueChanged(axis, j); + } + break; + } + } + } + } + else + { + if (axis is YAxis) + { + var yRate = axis.context.minMaxRange / grid.context.height; + var yValue = yRate * (chart.pointerPos.y - grid.context.y - axis.context.offset); + if (axis.context.minValue > 0) + yValue += axis.context.minValue; + + var labelX = axis.GetLabelObjectPosition(0).x; + axis.context.pointerValue = yValue; + axis.context.pointerLabelPosition = new Vector3(labelX, chart.pointerPos.y); + if (yValue != lastPointerValue) + { + if (chart.onAxisPointerValueChanged != null) + chart.onAxisPointerValueChanged(axis, yValue); + } + } + else + { + double xValue; + if (axis.IsLog()) + { + var logBase = axis.logBase; + var minLog = Math.Log(axis.context.minValue, logBase); + var maxLog = Math.Log(axis.context.maxValue, logBase); + var logRange = maxLog - minLog; + var pointerLog = minLog + logRange * (chart.pointerPos.x - grid.context.x - axis.context.offset) / grid.context.width; + xValue = Math.Pow(logBase, pointerLog); + } + else + { + var xRate = axis.context.minMaxRange / grid.context.width; + xValue = xRate * (chart.pointerPos.x - grid.context.x - axis.context.offset); + if (axis.context.minValue > 0) + xValue += axis.context.minValue; + } + var labelY = axis.GetLabelObjectPosition(0).y; + axis.context.pointerValue = xValue; + axis.context.pointerLabelPosition = new Vector3(chart.pointerPos.x, labelY); + if (xValue != lastPointerValue) + { + if (chart.onAxisPointerValueChanged != null) + chart.onAxisPointerValueChanged(axis, xValue); + } + } + } + } + } + + internal void UpdateAxisMinMaxValue(int axisIndex, Axis axis, bool cancelAnimation = false) + { + if (!axis.show) + return; + + if (axis.IsCategory()) + { + axis.context.minValue = 0; + axis.context.maxValue = axis.data.Count > 0 ? axis.data.Count - 1 : SeriesHelper.GetMaxSerieDataCount(chart.series) - 1; + axis.context.minMaxRange = axis.context.maxValue; + if (chart.HasRealtimeSortSerie(axis.gridIndex)) + { + UpdateAxisLabelText(axis); + } + return; + } + + double tempMinValue; + double tempMaxValue; + axis.context.needAnimation = Application.isPlaying && axis.animation.show; + chart.GetSeriesMinMaxValue(axis, axisIndex, out tempMinValue, out tempMaxValue); + + var dataZoom = chart.GetDataZoomOfAxis(axis); + if (dataZoom != null && dataZoom.enable) + { + if (axis is XAxis) + dataZoom.SetXAxisIndexValueInfo(axisIndex, ref tempMinValue, ref tempMaxValue); + else + dataZoom.SetYAxisIndexValueInfo(axisIndex, ref tempMinValue, ref tempMaxValue); + } + + if (tempMinValue != axis.context.destMinValue || + tempMaxValue != axis.context.destMaxValue || + m_LastInterval != axis.interval || + m_LastSplitNumber != axis.splitNumber) + { + m_LastSplitNumber = axis.splitNumber; + m_LastInterval = axis.interval; + axis.UpdateMinMaxValue(tempMinValue, tempMaxValue, !cancelAnimation && axis.context.needAnimation); + axis.context.offset = 0; + axis.context.lastCheckInverse = axis.inverse; + UpdateAxisTickValueList(axis); + + if (tempMinValue != 0 || tempMaxValue != 0) + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (grid != null && axis is XAxis && axis.IsValue()) + { + axis.UpdateZeroOffset(grid.context.width); + } + if (grid != null && axis is YAxis && axis.IsValue()) + { + axis.UpdateZeroOffset(grid.context.height); + } + } + + UpdateAxisLabelText(axis); + chart.RefreshChart(); + } + + if (!cancelAnimation && axis.context.needAnimation && (axis.context.minValue != axis.context.destMinValue || axis.context.maxValue != axis.context.destMaxValue)) + { + var duration = axis.animation.duration == 0 + ? SeriesHelper.GetMinAnimationDuration(chart.series) / 1000f + : axis.animation.duration / 1000f; + var deltaTime = axis.animation.unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime; + var minDiff = axis.context.destMinValue - axis.context.lastMinValue; + var maxDiff = axis.context.destMaxValue - axis.context.lastMaxValue; + var minDelta = minDiff / duration * deltaTime; + var maxDelta = maxDiff / duration * deltaTime; + axis.context.minValue += minDelta; + axis.context.maxValue += maxDelta; + if ((minDiff > 0 && axis.context.minValue > axis.context.destMinValue) + || (minDiff < 0 && axis.context.minValue < axis.context.destMinValue)) + { + axis.context.minValue = axis.context.destMinValue; + axis.context.lastMinValue = axis.context.destMinValue; + } + if ((maxDiff > 0 && axis.context.maxValue > axis.context.destMaxValue) + || (maxDiff < 0 && axis.context.maxValue < axis.context.destMaxValue)) + { + axis.context.maxValue = axis.context.destMaxValue; + axis.context.lastMaxValue = axis.context.destMaxValue; + } + axis.context.minMaxRange = axis.context.maxValue - axis.context.minValue; + UpdateAxisTickValueList(axis); + UpdateAxisLabelText(axis); + chart.RefreshChart(); + } + } + + internal virtual void UpdateAxisLabelText(Axis axis) + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (grid == null || axis == null) + return; + + float runtimeWidth = axis is XAxis ? grid.context.width : grid.context.height; + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series); + var dataZoom = chart.GetDataZoomOfAxis(axis); + + UpdateLabelText(axis, runtimeWidth, dataZoom, isPercentStack); + } + + internal void UpdateLabelText(Axis axis, float coordinateWidth, DataZoom dataZoom, bool forcePercent) + { + var context = axis.context; + var destMaxValue = context.destMaxValue; + var destMinValue = context.destMinValue; + var isCategory = axis.IsCategory(); + var serie = chart.GetSerie(0); + if (isCategory && serie != null && serie.useSortData) + { + var showData = serie.GetDataList(dataZoom, true); + var isChanged = CheckSortedDataChanged(axis, showData); + if (isChanged) + { + for (int i = 0; i < context.labelObjectList.Count; i++) + { + if (context.labelObjectList[i] != null) + { + var index = i < showData.Count ? showData[i].index : i; + var text = AxisHelper.GetLabelName(axis, coordinateWidth, index, destMinValue, destMaxValue, dataZoom, forcePercent, i); + context.labelObjectList[i].SetText(text); + } + } + SaveSortedDataIndex(axis, showData); + } + } + else + { + for (int i = 0; i < context.labelObjectList.Count; i++) + { + if (context.labelObjectList[i] != null) + { + var text = AxisHelper.GetLabelName(axis, coordinateWidth, i, destMinValue, destMaxValue, dataZoom, forcePercent); + context.labelObjectList[i].SetText(text); + } + } + } + } + + private bool CheckSortedDataChanged(Axis axis, List<SerieData> dataList) + { + if (dataList.Count != axis.context.sortedDataIndices.Count) return true; + for (int i = 0; i < dataList.Count; i++) + { + if (dataList[i].index != axis.context.sortedDataIndices[i]) return true; + } + return false; + } + + private void SaveSortedDataIndex(Axis axis, List<SerieData> dataList) + { + axis.context.sortedDataIndices.Clear(); + for (int i = 0; i < dataList.Count; i++) + { + axis.context.sortedDataIndices.Add(dataList[i].index); + } + } + + internal void UpdateAxisTickValueList(Axis axis) + { + if (axis.IsTime()) + { + var lastCount = axis.context.labelValueList.Count; + axis.context.tickValue = DateTimeUtil.UpdateTimeAxisDateTimeList(axis.context.labelValueList, + (int)axis.context.minValue, (int)axis.context.maxValue, axis.splitNumber); + + if (axis.context.labelValueList.Count != lastCount) + axis.SetAllDirty(); + } + else if (axis.IsValue()) + { + var list = axis.context.labelValueList; + var lastCount = list.Count; + list.Clear(); + + var range = axis.context.maxValue - axis.context.minValue; + if (range <= 0) + return; + + double tick = axis.interval; + + if (axis.interval == 0) + { + if (range >= double.MaxValue / 2) + { + tick = range / 4; + } + else if (axis.splitNumber > 0) + { + tick = range / axis.splitNumber; + } + else + { + var each = GetTick(range); + tick = each; + if (range / 4 % each == 0) + tick = range / 4; + else if (range / tick > 8) + tick = 2 * each; + else if (range / tick < 4) + tick = each / 2; + } + } + var value = 0d; + axis.context.tickValue = tick; + if (Mathf.Approximately((float)(axis.context.minValue % tick), 0)) + { + value = axis.context.minValue; + } + else + { + list.Add(axis.context.minValue); + value = Math.Ceiling(axis.context.minValue / tick) * tick; + } + var maxSplitNumber = chart.settings.axisMaxSplitNumber; + while (value <= axis.context.maxValue) + { + list.Add(value); + value += tick; + + if (maxSplitNumber > 0 && list.Count > maxSplitNumber) + break; + } + if (!ChartHelper.IsEquals(axis.context.maxValue, list[list.Count - 1])) + { + list.Add(axis.context.maxValue); + } + if (lastCount != list.Count) + { + axis.SetAllDirty(); + } + } + } + + private static double GetTick(double max) + { + if (max <= 1) return max / 5; + if (max > 1 && max < 10) return 1; + var bigger = Math.Ceiling(Math.Abs(max)); + int n = 1; + while (bigger / (Mathf.Pow(10, n)) > 10) + { + n++; + } + return Math.Pow(10, n); + } + + internal void CheckValueLabelActive(Axis axis, int i, ChartLabel label, Vector3 pos) + { + if (!axis.show || !axis.axisLabel.show) + { + label.SetTextActive(false); + return; + } + if (axis.IsValue()) + { + if (orient == Orient.Horizonal) + { + if (i == 0) + { + var dist = GetLabelPosition(0, 1).x - pos.x; + label.SetTextActive(axis.IsNeedShowLabel(i) && dist > label.text.GetPreferredWidth()); + } + else if (i == axis.context.labelValueList.Count - 1) + { + var dist = pos.x - GetLabelPosition(0, i - 1).x; + label.SetTextActive(axis.IsNeedShowLabel(i) && dist > label.text.GetPreferredWidth()); + } + } + else + { + if (i == 0) + { + var dist = GetLabelPosition(0, 1).y - pos.y; + label.SetTextActive(axis.IsNeedShowLabel(i) && dist > label.text.GetPreferredHeight()); + } + else if (i == axis.context.labelValueList.Count - 1) + { + var dist = pos.y - GetLabelPosition(0, i - 1).y; + label.SetTextActive(axis.IsNeedShowLabel(i) && dist > label.text.GetPreferredHeight()); + } + } + } + } + + protected void InitAxis3D(Axis relativedAxis, Orient orient) + { + Axis axis = component; + var axisLength = (axis.context.end - axis.context.start).magnitude; + if (axisLength == 0) return; + chart.InitAxisRuntimeData(axis); + UpdateAxisMinMaxValue(axis.index, axis, true); + + var objName = ChartCached.GetComponentObjectName(axis); + var axisObj = ChartHelper.AddObject(objName, + chart.transform, + chart.chartMinAnchor, + chart.chartMaxAnchor, + chart.chartPivot, + chart.chartSizeDelta, -1, chart.childrenNodeNames); + + axisObj.SetActive(axis.show); + axisObj.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(axisObj); + + axis.gameObject = axisObj; + axis.context.labelObjectList.Clear(); + + if (!axis.show) + return; + + var axisLabelTextStyle = axis.axisLabel.textStyle; + var dataZoom = chart.GetDataZoomOfAxis(axis); + var splitNumber = AxisHelper.GetScaleNumber(axis, axisLength, dataZoom); + var totalWidth = 0f; + var eachWidth = AxisHelper.GetEachWidth(axis, axisLength, dataZoom); + var gapWidth = axis.boundaryGap ? eachWidth / 2 : 0; + + var textWidth = axis.axisLabel.width > 0 ? + axis.axisLabel.width : + AxisHelper.GetScaleWidth(axis, axisLength, 0, dataZoom); + + var textHeight = axis.axisLabel.height > 0 ? + axis.axisLabel.height : + 20f; + + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series); + var inside = axis.axisLabel.inside; + var defaultAlignment = orient == Orient.Horizonal ? TextAnchor.MiddleCenter : + ((inside && axis.IsLeft()) || (!inside && axis.IsRight()) ? + TextAnchor.MiddleLeft : + TextAnchor.MiddleRight); + if (axis.IsCategory() && axis.boundaryGap) + splitNumber -= 1; + axis.context.aligment = defaultAlignment; + var sortSerie = chart.GetRealtimeSortSerie(axis.gridIndex); + if (sortSerie != null) + { + SerieHelper.UpdateSerieRuntimeFilterData(sortSerie); + } + var showData = sortSerie != null ? sortSerie.GetDataList(dataZoom, true) : null; + + for (int i = 0; i < splitNumber; i++) + { + var labelWidth = AxisHelper.GetScaleWidth(axis, axisLength, i + 1, dataZoom); + var sortIndex = sortSerie != null ? (i < showData.Count ? showData[i].index : i) : i; + var labelName = AxisHelper.GetLabelName(axis, axisLength, sortIndex, + axis.context.destMinValue, + axis.context.destMaxValue, + dataZoom, isPercentStack, i); + + var label = ChartHelper.AddAxisLabelObject(splitNumber, i, + ChartCached.GetAxisLabelName(i), + axisObj.transform, + new Vector2(textWidth, textHeight), + axis, chart.theme.axis, labelName, + Color.clear, + defaultAlignment, + chart.theme.GetColor(i)); + + if (i == 0) + axis.axisLabel.SetRelatedText(label.text, labelWidth); + + var pos = GetLabelPosition(totalWidth + gapWidth, i); + label.SetPosition(pos); + axis.context.labelObjectList.Add(label); + + totalWidth += labelWidth; + } + if (axis.axisName.show) + { + ChartLabel label = null; + var offset = axis.axisName.labelStyle.offset; + var autoColor = axis.axisLine.GetColor(chart.theme.axis.lineColor); + switch (axis.axisName.labelStyle.position) + { + case LabelStyle.Position.Start: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.context.start + offset); + break; + + case LabelStyle.Position.Middle: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition((axis.context.start + axis.context.end) / 2 + offset); + break; + + default: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.context.end + offset); + break; + } + } + } + + protected void InitAxis(Axis relativedAxis, Orient orient, + float axisStartX, float axisStartY, float axisLength, float relativedLength) + { + Axis axis = component; + chart.InitAxisRuntimeData(axis); + UpdateAxisMinMaxValue(axis.index, axis, true); + + var objName = ChartCached.GetComponentObjectName(axis); + var axisObj = ChartHelper.AddObject(objName, + chart.transform, + chart.chartMinAnchor, + chart.chartMaxAnchor, + chart.chartPivot, + chart.chartSizeDelta, -1, chart.childrenNodeNames); + + axisObj.SetActive(axis.show); + axisObj.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(axisObj); + + axis.gameObject = axisObj; + axis.context.labelObjectList.Clear(); + + if (!axis.show) + return; + + var axisLabelTextStyle = axis.axisLabel.textStyle; + var dataZoom = chart.GetDataZoomOfAxis(axis); + var splitNumber = AxisHelper.GetScaleNumber(axis, axisLength, dataZoom); + var totalWidth = 0f; + var eachWidth = AxisHelper.GetEachWidth(axis, axisLength, dataZoom); + var gapWidth = axis.boundaryGap ? eachWidth / 2 : 0; + + var textWidth = axis.axisLabel.width > 0 ? + axis.axisLabel.width : + (orient == Orient.Horizonal ? + AxisHelper.GetScaleWidth(axis, axisLength, 0, dataZoom) : + (axisStartX - chart.chartX) + ); + + var textHeight = axis.axisLabel.height > 0 ? + axis.axisLabel.height : + 20f; + + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series); + var inside = axis.axisLabel.inside; + var defaultAlignment = orient == Orient.Horizonal ? TextAnchor.MiddleCenter : + ((inside && axis.IsLeft()) || (!inside && axis.IsRight()) ? + TextAnchor.MiddleLeft : + TextAnchor.MiddleRight); + if (axis.IsCategory() && axis.boundaryGap) + splitNumber -= 1; + axis.context.aligment = defaultAlignment; + var sortSerie = chart.GetRealtimeSortSerie(axis.gridIndex); + if (sortSerie != null) + { + SerieHelper.UpdateSerieRuntimeFilterData(sortSerie); + } + var showData = sortSerie != null ? sortSerie.GetDataList(dataZoom, true) : null; + for (int i = 0; i < splitNumber; i++) + { + var labelWidth = AxisHelper.GetScaleWidth(axis, axisLength, i + 1, dataZoom); + var sortIndex = sortSerie != null ? (i < showData.Count ? showData[i].index : i) : i; + var labelName = AxisHelper.GetLabelName(axis, axisLength, sortIndex, + axis.context.destMinValue, + axis.context.destMaxValue, + dataZoom, isPercentStack, i); + + var label = ChartHelper.AddAxisLabelObject(splitNumber, i, + ChartCached.GetAxisLabelName(i), + axisObj.transform, + new Vector2(textWidth, textHeight), + axis, chart.theme.axis, labelName, + Color.clear, + defaultAlignment, + chart.theme.GetColor(i)); + + if (i == 0) + axis.axisLabel.SetRelatedText(label.text, labelWidth); + + var pos = GetLabelPosition(totalWidth + gapWidth, i); + label.SetPosition(pos); + CheckValueLabelActive(axis, i, label, pos); + + axis.context.labelObjectList.Add(label); + + totalWidth += labelWidth; + } + if (axis.axisName.show) + { + ChartLabel label; + var relativedDist = relativedAxis == null ? 0 : relativedAxis.context.offset; + var zeroPos = new Vector3(axisStartX, axisStartY + relativedDist); + var offset = axis.axisName.labelStyle.offset; + var autoColor = axis.axisLine.GetColor(chart.theme.axis.lineColor); + if (orient == Orient.Horizonal) + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + var posY = !axis.axisName.onZero && grid != null ? grid.context.y : GetAxisLineXOrY() + offset.y; + switch (axis.axisName.labelStyle.position) + { + case LabelStyle.Position.Start: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleRight); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.position == Axis.AxisPosition.Top ? + new Vector2(zeroPos.x - offset.x, axisStartY + relativedLength + offset.y + axis.offset) : + new Vector2(zeroPos.x - offset.x, posY + offset.y)); + break; + + case LabelStyle.Position.Middle: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.position == Axis.AxisPosition.Top ? + new Vector2(axisStartX + axisLength / 2 + offset.x, axisStartY + relativedLength - offset.y + axis.offset) : + new Vector2(axisStartX + axisLength / 2 + offset.x, posY + offset.y)); + break; + + default: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleLeft); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.position == Axis.AxisPosition.Top ? + new Vector2(axisStartX + axisLength + offset.x, axisStartY + relativedLength + offset.y + axis.offset) : + new Vector2(axisStartX + axisLength + offset.x, posY + offset.y)); + break; + } + } + else + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + var posX = !axis.axisName.onZero && grid != null ? grid.context.x : GetAxisLineXOrY() + offset.x; + switch (axis.axisName.labelStyle.position) + { + case LabelStyle.Position.Start: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.position == Axis.AxisPosition.Right ? + new Vector2(axisStartX + relativedLength + offset.x + axis.offset, axisStartY - offset.y) : + new Vector2(posX + offset.x, axisStartY - offset.y)); + break; + + case LabelStyle.Position.Middle: + + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.position == Axis.AxisPosition.Right ? + new Vector2(axisStartX + relativedLength - offset.x + axis.offset, axisStartY + axisLength / 2 + offset.y) : + new Vector2(posX + offset.x, axisStartY + axisLength / 2 + offset.y)); + break; + + default: + //LabelStyle.Position + label = ChartHelper.AddChartLabel(s_DefaultAxisName, axisObj.transform, axis.axisName.labelStyle, + chart.theme.axis, axis.axisName.name, autoColor, TextAnchor.MiddleCenter); + label.SetActive(axis.axisName.labelStyle.show, true); + label.SetPosition(axis.position == Axis.AxisPosition.Right ? + new Vector2(axisStartX + relativedLength + offset.x + axis.offset, axisStartY + axisLength + offset.y) : + new Vector2(posX + offset.x, axisStartY + axisLength + offset.y)); + break; + } + } + } + } + + internal static Vector3 GetLabelPosition(int i, Orient orient, Axis axis, Axis relativedAxis, AxisTheme theme, + float scaleWid, float axisStartX, float axisStartY, float axisLength, float relativedLength) + { + var inside = axis.axisLabel.inside; + var fontSize = axis.axisLabel.textStyle.GetFontSize(theme); + var current = axis.offset; + + if (axis.IsTime() || axis.IsValue()) + { + scaleWid = axis.context.minMaxRange != 0 ? + axis.GetDistance(axis.GetLabelValue(i), axisLength) : + 0; + } + + if (orient == Orient.Horizonal) + { + if (axis.axisLabel.onZero && relativedAxis != null) + axisStartY += relativedAxis.context.offset; + + if (axis.IsTop()) + axisStartY += relativedLength; + + if ((inside && axis.IsBottom()) || (!inside && axis.IsTop())) + current += axisStartY + axis.axisLabel.distance + fontSize / 2; + else + current += axisStartY - axis.axisLabel.distance - fontSize / 2; + + return new Vector3(axisStartX + scaleWid, current) + axis.axisLabel.offset; + } + else + { + if (axis.axisLabel.onZero && relativedAxis != null) + axisStartX += relativedAxis.context.offset; + + if (axis.IsRight()) + axisStartX += relativedLength; + + if ((inside && axis.IsLeft()) || (!inside && axis.IsRight())) + current += axisStartX + axis.axisLabel.distance; + else + current += axisStartX - axis.axisLabel.distance; + + return new Vector3(current, axisStartY + scaleWid) + axis.axisLabel.offset; + } + } + + internal static void DrawAxisLine(VertexHelper vh, Axis axis, AxisTheme theme, Orient orient, + float startX, float startY, float axisLength) + { + var inverse = axis.IsValue() && axis.inverse; + var offset = AxisHelper.GetAxisLineArrowOffset(axis); + + var lineWidth = axis.axisLine.GetWidth(theme.lineWidth); + var lineType = axis.axisLine.GetType(theme.lineType); + var lineColor = axis.axisLine.GetColor(theme.lineColor); + + if (orient == Orient.Horizonal) + { + var left = new Vector3(startX - lineWidth - (inverse ? offset : 0), startY); + var right = new Vector3(startX + axisLength + lineWidth + (!inverse ? offset : 0), startY); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, left, right, lineColor); + } + else + { + var bottom = new Vector3(startX, startY - lineWidth - (inverse ? offset : 0)); + var top = new Vector3(startX, startY + axisLength + lineWidth + (!inverse ? offset : 0)); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, bottom, top, lineColor); + } + } + + internal static void DrawAxisTick(VertexHelper vh, Axis axis, AxisTheme theme, DataZoom dataZoom, + Orient orient, float startX, float startY, float axisLength) + { + var lineWidth = axis.axisLine.GetWidth(theme.lineWidth); + var tickLength = axis.axisTick.GetLength(theme.tickLength); + + if (AxisHelper.NeedShowSplit(axis)) + { + var size = AxisHelper.GetScaleNumber(axis, axisLength, dataZoom); + var tickWidth = axis.axisTick.GetWidth(theme.tickWidth); + var tickColor = axis.axisTick.GetColor(theme.tickColor); + var current = orient == Orient.Horizonal ? startX : startY; + var maxAxisXY = current + axisLength; + var lastTickX = current; + var lastTickY = current; + var minorTickSplitNumber = axis.minorTick.splitNumber <= 0 ? 5 : axis.minorTick.splitNumber; + var minorTickDistance = axis.GetValueLength(axis.context.tickValue / minorTickSplitNumber, axisLength); + var minorTickColor = axis.minorTick.GetColor(theme.tickColor); + var minorTickWidth = axis.minorTick.GetWidth(theme.tickWidth); + var minorTickLength = axis.minorTick.GetLength(theme.tickLength * 0.6f); + var minorStartIndex = axis.IsTime() ? 0 : 1; + var isLogAxis = axis.IsLog(); + for (int i = 0; i < size; i++) + { + var scaleWidth = AxisHelper.GetScaleWidth(axis, axisLength, i + 1, dataZoom); + var hideTick = (i == 0 && (!axis.axisTick.showStartTick || axis.axisTick.alignWithLabel)) || + (i == size - 1 && !axis.axisTick.showEndTick); + if (axis.axisTick.show) + { + if (orient == Orient.Horizonal) + { + float pX = axis.IsTime() ? + (startX + axis.GetDistance(axis.GetLabelValue(i), axisLength)) : + current; + + if (axis.boundaryGap && axis.axisTick.alignWithLabel) + pX -= scaleWidth / 2; + + var sY = 0f; + var eY = 0f; + var mY = 0f; + if ((axis.axisTick.inside && axis.IsBottom()) || + (!axis.axisTick.inside && axis.IsTop())) + { + sY = startY + lineWidth; + eY = sY + tickLength; + mY = sY + minorTickLength; + } + else + { + sY = startY - lineWidth; + eY = sY - tickLength; + mY = sY - minorTickLength; + } + if (!hideTick) + UGL.DrawLine(vh, new Vector3(pX, sY), new Vector3(pX, eY), tickWidth, tickColor); + if (axis.minorTick.show && i >= minorStartIndex && (minorTickDistance > 0 || isLogAxis)) + { + if (isLogAxis) + { + var count = 0; + var logRange = (axis.logBase - 1f); + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + var tickTotal = lastTickX + minorTickDistance; + while (tickTotal < current && count < minorTickSplitNumber - 1) + { + UGL.DrawLine(vh, new Vector3(tickTotal, sY), new Vector3(tickTotal, mY), minorTickWidth, minorTickColor); + count++; + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + tickTotal = lastTickX + minorTickDistance; + } + } + else if (lastTickX <= axis.context.zeroX || (i == minorStartIndex && pX > axis.context.zeroX)) + { + var tickTotal = pX - minorTickDistance; + while (tickTotal > lastTickX) + { + UGL.DrawLine(vh, new Vector3(tickTotal, sY), new Vector3(tickTotal, mY), minorTickWidth, minorTickColor); + tickTotal -= minorTickDistance; + } + } + else + { + var tickTotal = lastTickX + minorTickDistance; + while (tickTotal < pX) + { + UGL.DrawLine(vh, new Vector3(tickTotal, sY), new Vector3(tickTotal, mY), minorTickWidth, minorTickColor); + tickTotal += minorTickDistance; + } + } + if (i == size - 1) + { + var tickTotal = pX + minorTickDistance; + while (tickTotal < maxAxisXY) + { + UGL.DrawLine(vh, new Vector3(tickTotal, sY), new Vector3(tickTotal, mY), minorTickWidth, minorTickColor); + tickTotal += minorTickDistance; + } + } + } + lastTickX = pX; + } + else + { + float pY = axis.IsTime() ? + (startY + axis.GetDistance(axis.GetLabelValue(i), axisLength)) : + current; + + if (axis.boundaryGap && axis.axisTick.alignWithLabel) + pY -= scaleWidth / 2; + + var sX = 0f; + var eX = 0f; + var mX = 0f; + if ((axis.axisTick.inside && axis.IsLeft()) || + (!axis.axisTick.inside && axis.IsRight())) + { + sX = startX + lineWidth; + eX = sX + tickLength; + mX = sX + minorTickLength; + } + else + { + sX = startX - lineWidth; + eX = sX - tickLength; + mX = sX - minorTickLength; + } + if (!hideTick) + UGL.DrawLine(vh, new Vector3(sX, pY), new Vector3(eX, pY), tickWidth, tickColor); + if (axis.minorTick.show && i >= minorStartIndex && (minorTickDistance > 0 || isLogAxis)) + { + if (isLogAxis) + { + var count = 0; + var logRange = (axis.logBase - 1f); + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + var tickTotal = lastTickY + minorTickDistance; + while (tickTotal < current && count < minorTickSplitNumber - 1) + { + UGL.DrawLine(vh, new Vector3(sX, tickTotal), new Vector3(mX, tickTotal), minorTickWidth, minorTickColor); + count++; + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + tickTotal = lastTickY + minorTickDistance; + } + } + else if (lastTickY <= axis.context.zeroY || (i == minorStartIndex && pY > axis.context.zeroY)) + { + var tickTotal = pY - minorTickDistance; + while (tickTotal > lastTickY) + { + + UGL.DrawLine(vh, new Vector3(sX, tickTotal), new Vector3(mX, tickTotal), minorTickWidth, minorTickColor); + tickTotal -= minorTickDistance; + } + } + else + { + var tickTotal = lastTickY + minorTickDistance; + while (tickTotal < pY) + { + + UGL.DrawLine(vh, new Vector3(sX, tickTotal), new Vector3(mX, tickTotal), minorTickWidth, minorTickColor); + tickTotal += minorTickDistance; + } + } + if (i == size - 1) + { + var tickTotal = pY + minorTickDistance; + while (tickTotal < maxAxisXY) + { + UGL.DrawLine(vh, new Vector3(sX, tickTotal), new Vector3(mX, tickTotal), minorTickWidth, minorTickColor); + tickTotal += minorTickDistance; + } + } + } + lastTickY = pY; + } + } + current += scaleWidth; + } + } + if (axis.show && axis.axisLine.show && axis.axisLine.showArrow) + { + var lineY = startY + axis.offset; + var inverse = axis.IsValue() && axis.inverse; + var axisArrow = axis.axisLine.arrow; + if (orient == Orient.Horizonal) + { + if (inverse) + { + var startPos = new Vector3(startX + axisLength, lineY); + var arrowPos = new Vector3(startX, lineY); + UGL.DrawArrow(vh, startPos, arrowPos, axisArrow.width, axisArrow.height, + axisArrow.offset, axisArrow.dent, + axisArrow.GetColor(axis.axisLine.GetColor(theme.lineColor))); + } + else + { + var arrowPosX = startX + axisLength + lineWidth; + var startPos = new Vector3(startX, lineY); + var arrowPos = new Vector3(arrowPosX, lineY); + UGL.DrawArrow(vh, startPos, arrowPos, axisArrow.width, axisArrow.height, + axisArrow.offset, axisArrow.dent, + axisArrow.GetColor(axis.axisLine.GetColor(theme.lineColor))); + } + } + else + { + if (inverse) + { + var startPos = new Vector3(startX, startY + axisLength); + var arrowPos = new Vector3(startX, startY); + UGL.DrawArrow(vh, startPos, arrowPos, axisArrow.width, axisArrow.height, + axisArrow.offset, axisArrow.dent, + axisArrow.GetColor(axis.axisLine.GetColor(theme.lineColor))); + } + else + { + var startPos = new Vector3(startX, startY); + var arrowPos = new Vector3(startX, startY + axisLength + lineWidth); + UGL.DrawArrow(vh, startPos, arrowPos, axisArrow.width, axisArrow.height, + axisArrow.offset, axisArrow.dent, + axisArrow.GetColor(axis.axisLine.GetColor(theme.lineColor))); + } + } + } + } + + protected void DrawAxisSplit(VertexHelper vh, AxisTheme theme, DataZoom dataZoom, + Orient orient, float startX, float startY, float axisLength, float splitLength, + Axis relativedAxis = null) + { + Axis axis = component; + var axisLineWidth = axis.axisLine.GetWidth(theme.lineWidth); + splitLength -= axisLineWidth; + var lineColor = axis.splitLine.GetColor(theme.splitLineColor); + var lineWidth = axis.splitLine.GetWidth(theme.lineWidth); + var lineType = axis.splitLine.GetType(theme.splitLineType); + + var size = AxisHelper.GetScaleNumber(axis, axisLength, dataZoom); + if (axis.IsTime()) + { + size += 1; + if (!ChartHelper.IsEquals(axis.GetLastLabelValue(), axis.context.maxValue)) + size += 1; + } + + var current = orient == Orient.Horizonal ? startX : startY; + var maxAxisXY = current + axisLength; + var lastSplitX = 0f; + var lastSplitY = 0f; + var minorTickSplitNumber = axis.minorTick.splitNumber <= 0 ? 5 : axis.minorTick.splitNumber; + var minorTickDistance = axis.GetValueLength(axis.context.tickValue / minorTickSplitNumber, axisLength); + var minorSplitLineColor = axis.minorSplitLine.GetColor(theme.minorSplitLineColor); + var minorLineWidth = axis.minorSplitLine.GetWidth(theme.lineWidth); + var minorLineType = axis.minorSplitLine.GetType(theme.splitLineType); + var minorStartIndex = axis.IsTime() ? 0 : 1; + var isLogAxis = axis.IsLog(); + for (int i = 0; i < size; i++) + { + var scaleWidth = AxisHelper.GetScaleWidth(axis, axisLength, axis.IsTime() ? i : i + 1, dataZoom); + if (axis.boundaryGap && axis.axisTick.alignWithLabel) + current -= scaleWidth / 2; + + if (axis.splitArea.show && i <= size - 1) + { + if (orient == Orient.Horizonal) + { + UGL.DrawQuadrilateral(vh, + new Vector2(current, startY), + new Vector2(current, startY + splitLength), + new Vector2(current + scaleWidth, startY + splitLength), + new Vector2(current + scaleWidth, startY), + axis.splitArea.GetColor(i, theme)); + } + else + { + UGL.DrawQuadrilateral(vh, + new Vector2(startX, current), + new Vector2(startX + splitLength, current), + new Vector2(startX + splitLength, current + scaleWidth), + new Vector2(startX, current + scaleWidth), + axis.splitArea.GetColor(i, theme)); + } + } + if (axis.splitLine.show) + { + if (axis.splitLine.NeedShow(i, size)) + { + if (orient == Orient.Horizonal) + { + if (relativedAxis == null || !relativedAxis.axisLine.show || !MathUtil.Approximately(current, relativedAxis.context.x)) + { + ChartDrawer.DrawLineStyle(vh, + lineType, + lineWidth, + new Vector3(current, startY), + new Vector3(current, startY + splitLength), + lineColor); + } + if (axis.minorSplitLine.show && i >= minorStartIndex && (minorTickDistance > 0 || isLogAxis)) + { + if (isLogAxis) + { + var count = 0; + var logRange = axis.logBase - 1f; + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + var tickTotal = lastSplitX + minorTickDistance; + while (tickTotal < current && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(tickTotal, startY), + new Vector3(tickTotal, startY + splitLength), + minorSplitLineColor); + count++; + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + tickTotal = lastSplitX + minorTickDistance; + } + } + else if (lastSplitX <= axis.context.zeroX || (i == minorStartIndex && current > axis.context.zeroX)) + { + var tickTotal = current - minorTickDistance; + var count = 0; + while (tickTotal > lastSplitX && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(tickTotal, startY), + new Vector3(tickTotal, startY + splitLength), + minorSplitLineColor); + count++; + tickTotal -= minorTickDistance; + } + } + else + { + var tickTotal = lastSplitX + minorTickDistance; + var count = 0; + while (tickTotal < current && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(tickTotal, startY), + new Vector3(tickTotal, startY + splitLength), + minorSplitLineColor); + count++; + tickTotal += minorTickDistance; + } + } + if (i == size - 1) + { + var tickTotal = current + minorTickDistance; + var count = 0; + while (tickTotal < maxAxisXY && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(tickTotal, startY), + new Vector3(tickTotal, startY + splitLength), + minorSplitLineColor); + count++; + tickTotal += minorTickDistance; + } + } + } + lastSplitX = current; + } + else + { + if (relativedAxis == null || !relativedAxis.axisLine.show || !MathUtil.Approximately(current, relativedAxis.context.y)) + { + ChartDrawer.DrawLineStyle(vh, + lineType, + lineWidth, + new Vector3(startX, current), + new Vector3(startX + splitLength, current), + lineColor); + } + if (axis.minorSplitLine.show && i >= minorStartIndex && (minorTickDistance > 0 || isLogAxis)) + { + if (isLogAxis) + { + var count = 0; + var logRange = (axis.logBase - 1f); + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + var tickTotal = lastSplitY + minorTickDistance; + while (tickTotal < current && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(startX, tickTotal), + new Vector3(startX + splitLength, tickTotal), + minorSplitLineColor); + count++; + minorTickDistance = scaleWidth * axis.GetLogValue(1 + (count + 1) * logRange / minorTickSplitNumber); + tickTotal = lastSplitY + minorTickDistance; + } + } + else if (lastSplitY <= axis.context.zeroY || (i == minorStartIndex && current > axis.context.zeroY)) + { + var tickTotal = current - minorTickDistance; + var count = 0; + while (tickTotal > lastSplitY && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(startX, tickTotal), + new Vector3(startX + splitLength, tickTotal), + minorSplitLineColor); + count++; + tickTotal -= minorTickDistance; + } + } + else + { + var tickTotal = lastSplitY + minorTickDistance; + var count = 0; + while (tickTotal < current && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(startX, tickTotal), + new Vector3(startX + splitLength, tickTotal), + minorSplitLineColor); + count++; + tickTotal += minorTickDistance; + } + } + if (i == size - 1) + { + var tickTotal = current + minorTickDistance; + var count = 0; + while (tickTotal < maxAxisXY && count < minorTickSplitNumber - 1) + { + ChartDrawer.DrawLineStyle(vh, + minorLineType, + minorLineWidth, + new Vector3(startX, tickTotal), + new Vector3(startX + splitLength, tickTotal), + minorSplitLineColor); + count++; + tickTotal += minorTickDistance; + } + } + } + lastSplitY = current; + } + } + } + current += scaleWidth; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs.meta new file mode 100644 index 0000000..319549f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0babef8a2708b4745bbb0a0648913a35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs b/Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs new file mode 100644 index 0000000..03fe5fd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs @@ -0,0 +1,648 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class AxisHelper + { + + /// <summary> + /// 包含箭头偏移的轴线长度 + /// </summary> + /// <param name="axis"></param> + /// <returns></returns> + public static float GetAxisLineArrowOffset(Axis axis) + { + if (axis.axisLine.show && axis.axisLine.showArrow && axis.axisLine.arrow.offset > 0) + { + return axis.axisLine.arrow.offset; + } + return 0; + } + + /// <summary> + /// 获得分割网格个数,包含次刻度 + /// </summary> + /// <param name="axis"></param> + /// <returns></returns> + public static int GetTotalSplitGridNum(Axis axis) + { + if (axis.IsCategory()) + return axis.data.Count; + else + { + var splitNum = axis.splitNumber <= 0 ? GetSplitNumber(axis, 0, null) : axis.splitNumber; + return splitNum * axis.minorTick.splitNumber; + } + } + + /// <summary> + /// 获得分割段数 + /// </summary> + /// <param name="dataZoom"></param> + /// <returns></returns> + public static int GetSplitNumber(Axis axis, float coordinateWid, DataZoom dataZoom) + { + if (axis.type == Axis.AxisType.Value) + { + return axis.context.labelValueList.Count - 1; + } + else if (axis.type == Axis.AxisType.Time) + { + return axis.context.labelValueList.Count; + } + else if (axis.type == Axis.AxisType.Log) + { + return axis.splitNumber > 0 ? axis.splitNumber : 4; + } + else if (axis.type == Axis.AxisType.Category) + { + int dataCount = axis.GetDataList(dataZoom).Count; + if (!axis.boundaryGap) + dataCount -= 1; + if (dataCount <= 0) + dataCount = 1; + + if (axis.splitNumber <= 0) + { + var eachWid = coordinateWid / dataCount; + + var min = axis.minCategorySpacing > 0 + ? axis.minCategorySpacing + : (Mathf.Abs(axis.context.dire.y) < 0.01 ? 80 : 20); + if (eachWid > min) return dataCount; + var tick = Mathf.CeilToInt(min / eachWid); + return tick <= 1 ? dataCount : (int)(dataCount / tick); + } + else + { + if (axis.splitNumber <= 0 || axis.splitNumber > dataCount) + return dataCount; + if (dataCount >= axis.splitNumber * 2) + return axis.splitNumber; + else + return dataCount; + } + } + return 0; + } + + /// <summary> + /// 获得一个类目数据在坐标系中代表的宽度 + /// </summary> + /// <param name="coordinateWidth"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public static float GetDataWidth(Axis axis, float coordinateWidth, int dataCount, DataZoom dataZoom) + { + if (dataCount < 1) + dataCount = 1; + if (axis.IsValue()) + return dataCount > 1 ? coordinateWidth / (dataCount - 1) : coordinateWidth; + var categoryCount = axis.GetDataCount(dataZoom); + int segment = (axis.boundaryGap ? categoryCount : categoryCount - 1); + segment = segment <= 0 ? dataCount : segment; + if (segment <= 0) + segment = 1; + + return coordinateWidth / segment; + } + + /// <summary> + /// 获得标签显示的名称 + /// </summary> + /// <param name="index"></param> + /// <param name="minValue"></param> + /// <param name="maxValue"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public static string GetLabelName(Axis axis, float coordinateWidth, int index, double minValue, double maxValue, + DataZoom dataZoom, bool forcePercent, int sortIndex = -1) + { + int split = GetSplitNumber(axis, coordinateWidth, dataZoom); + if (sortIndex == -1) sortIndex = index; + if (axis.type == Axis.AxisType.Value) + { + if (minValue == 0 && maxValue == 0) + maxValue = axis.max != 0 ? axis.max : 1; + double value = 0; + if (forcePercent) + maxValue = 100; + + value = axis.GetLabelValue(index); + if (axis.inverse) + { + value = -value; + minValue = -minValue; + maxValue = -maxValue; + } + if (forcePercent) + return string.Format("{0}%", (int)value); + else + return axis.axisLabel.GetFormatterContent(sortIndex, axis.context.labelValueList.Count, value, minValue, maxValue); + } + else if (axis.type == Axis.AxisType.Log) + { + double value = axis.logBaseE ? + System.Math.Exp(axis.GetLogMinIndex() + index) : + System.Math.Pow(axis.logBase, axis.GetLogMinIndex() + index); + if (axis.inverse) + { + value = -value; + minValue = -minValue; + maxValue = -maxValue; + } + return axis.axisLabel.GetFormatterContent(sortIndex, 0, value, minValue, maxValue, true); + } + else if (axis.type == Axis.AxisType.Time) + { + if (minValue == 0 && maxValue == 0) + return string.Empty; + if (index > axis.context.labelValueList.Count - 1) + return string.Empty; + + var value = axis.GetLabelValue(index); + return axis.axisLabel.GetFormatterDateTime(sortIndex, axis.context.labelValueList.Count, value, minValue, maxValue); + } + var showData = axis.GetDataList(dataZoom); + int dataCount = showData.Count; + if (dataCount <= 0) + return ""; + int rate = axis.boundaryGap ? (dataCount / split) : (dataCount - 1) / split; + if (rate == 0) rate = 1; + if (axis.insertDataToHead) + { + if (index > 0) + { + var residue = dataCount - 1 - split * rate; + var newIndex = residue + (index - 1) * rate; + if (newIndex < 0) + newIndex = 0; + return axis.axisLabel.GetFormatterContent(sortIndex, dataCount, showData[newIndex]); + } + else + { + if (axis.boundaryGap && coordinateWidth / dataCount > 5) + return string.Empty; + else + return axis.axisLabel.GetFormatterContent(sortIndex, dataCount, showData[0]); + } + } + else + { + int newIndex = index * rate; + if (newIndex < dataCount) + { + return axis.axisLabel.GetFormatterContent(sortIndex, dataCount, showData[newIndex]); + } + else + { + var diff = newIndex - dataCount; + if (axis.boundaryGap && ((diff > 0 && diff / rate < 0.4f) || dataCount >= axis.data.Count)) + return string.Empty; + else + return axis.axisLabel.GetFormatterContent(sortIndex, dataCount, showData[dataCount - 1]); + } + } + } + + /// <summary> + /// 获得分割线条数 + /// </summary> + /// <param name="dataZoom"></param> + /// <returns></returns> + public static int GetScaleNumber(Axis axis, float coordinateWidth, DataZoom dataZoom = null) + { + int splitNum = GetSplitNumber(axis, coordinateWidth, dataZoom); + if (splitNum == 0) + return 0; + + if (axis.IsCategory()) + { + var dataCount = axis.GetDataList(dataZoom).Count; + var scaleNum = 0; + + if (axis.boundaryGap) + { + scaleNum = dataCount > 1 && dataCount % splitNum == 0 ? + splitNum + 1 : + splitNum + 2; + } + else + { + scaleNum = splitNum + 1; + } + return scaleNum; + } + else if (axis.IsTime()) + return splitNum; + else + return splitNum + 1; + } + + /// <summary> + /// 获得分割段宽度 + /// </summary> + /// <param name="coordinateWidth"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public static float GetScaleWidth(Axis axis, float coordinateWidth, int index, DataZoom dataZoom = null) + { + if (index < 0) + return 0; + if (axis.IsTime() || axis.IsValue()) + { + var value = axis.GetLabelValue(index); + var lastValue = axis.GetLabelValue(index - 1); + var width = axis.context.minMaxRange == 0 ? 0 : + (float)(coordinateWidth * ((value - lastValue) / axis.context.minMaxRange)); + return width; + } + else + { + int num = GetScaleNumber(axis, coordinateWidth, dataZoom); + int splitNum = GetSplitNumber(axis, coordinateWidth, dataZoom); + if (num <= 0) + num = 1; + var data = axis.GetDataList(dataZoom); + if (axis.IsCategory() && data.Count > 0 && splitNum > 0) + { + var count = axis.boundaryGap ? data.Count : data.Count - 1; + int tick = count / splitNum; + if (count <= 0) + return 0; + + var each = coordinateWidth / count; + if (axis.insertDataToHead) + { + var max = axis.boundaryGap ? splitNum : splitNum - 1; + if (index == 1) + { + if (axis.axisTick.alignWithLabel) + return each * tick; + else + return coordinateWidth - each * tick * max; + } + else + { + if (count < splitNum) + return each; + else + return each * (count / splitNum); + } + } + else + { + var max = axis.boundaryGap ? num - 1 : num; + if (index >= max) + { + if (axis.axisTick.alignWithLabel) + return each * tick; + else + return coordinateWidth - each * tick * (index - 1); + } + else + { + if (count < splitNum) + return each; + else + return each * (count / splitNum); + } + } + } + else + { + if (splitNum <= 0) + return 0; + else + return coordinateWidth / splitNum; + } + } + } + + public static float GetEachWidth(Axis axis, float coordinateWidth, DataZoom dataZoom = null) + { + var data = axis.GetDataList(dataZoom); + if (data.Count > 0) + { + var count = axis.boundaryGap ? data.Count : data.Count - 1; + return count > 0 ? coordinateWidth / count : coordinateWidth; + } + else + { + int num = GetScaleNumber(axis, coordinateWidth, dataZoom) - 1; + return num > 0 ? coordinateWidth / num : coordinateWidth; + } + } + + /// <summary> + /// 调整最大最小值 + /// </summary> + /// <param name="minValue"></param> + /// <param name="maxValue"></param> + public static void AdjustMinMaxValue(Axis axis, ref double minValue, ref double maxValue, bool needFormat, double ceilRate = 0) + { + if (axis.type == Axis.AxisType.Log) + { + int minSplit = 0; + int maxSplit = 0; + maxValue = ChartHelper.GetMaxLogValue(maxValue, axis.logBase, axis.logBaseE, out maxSplit); + minValue = ChartHelper.GetMinLogValue(minValue, axis.logBase, axis.logBaseE, out minSplit); + + var splitNumber = maxSplit + minSplit; + if (splitNumber > 15) + splitNumber = 15; + axis.splitNumber = splitNumber; + return; + } + if (axis.type == Axis.AxisType.Time) { } + else if (axis.minMaxType == Axis.AxisMinMaxType.Custom) + { + if (axis.min != 0 || axis.max != 0) + { + if (axis.inverse) + { + minValue = -axis.max; + maxValue = -axis.min; + } + else + { + minValue = axis.min; + maxValue = axis.max; + } + } + } + else + { + if (ceilRate == 0) ceilRate = axis.ceilRate; + switch (axis.minMaxType) + { + case Axis.AxisMinMaxType.Default: + + if (minValue == 0 && maxValue == 0) { } + else if (minValue > 0 && maxValue > 0) + { + minValue = 0; + maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, ceilRate) : maxValue; + } + else if (minValue < 0 && maxValue < 0) + { + minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, ceilRate) : minValue; + maxValue = 0; + } + else + { + minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, ceilRate) : minValue; + maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, ceilRate) : maxValue; + } + break; + + case Axis.AxisMinMaxType.MinMax: + if (ceilRate != 0) + { + minValue = ChartHelper.GetMinCeilRate(minValue, ceilRate); + maxValue = ChartHelper.GetMaxCeilRate(maxValue, ceilRate); + } + break; + + case Axis.AxisMinMaxType.MinMaxAuto: + minValue = needFormat ? ChartHelper.GetMinDivisibleValue(minValue, ceilRate) : minValue; + maxValue = needFormat ? ChartHelper.GetMaxDivisibleValue(maxValue, ceilRate) : maxValue; + break; + } + } + } + + public static bool NeedShowSplit(Axis axis) + { + if (!axis.show) + return false; + if (axis.IsCategory() && axis.GetDataList().Count <= 0) + return false; + else + return true; + } + + public static void AdjustCircleLabelPos(ChartLabel txt, Vector3 pos, Vector3 cenPos, float txtHig, Vector3 offset) + { + var txtWidth = txt.text.GetPreferredWidth(); + var sizeDelta = new Vector2(txtWidth, txt.text.GetPreferredHeight()); + txt.text.SetSizeDelta(sizeDelta); + var diff = pos.x - cenPos.x; + if (diff < -1f) //left + { + pos = new Vector3(pos.x - txtWidth / 2, pos.y); + } + else if (diff > 1f) //right + { + pos = new Vector3(pos.x + txtWidth / 2, pos.y); + } + else + { + float y = pos.y > cenPos.y ? pos.y + txtHig / 2 : pos.y - txtHig / 2; + pos = new Vector3(pos.x, y); + } + txt.SetPosition(pos + offset); + } + + public static void AdjustRadiusAxisLabelPos(ChartLabel txt, Vector3 pos, Vector3 cenPos, float txtHig, Vector3 offset) + { + var txtWidth = txt.text.GetPreferredWidth(); + var sizeDelta = new Vector2(txtWidth, txt.text.GetPreferredHeight()); + txt.text.SetSizeDelta(sizeDelta); + var diff = pos.y - cenPos.y; + if (diff > 20f) //left + { + pos = new Vector3(pos.x - txtWidth / 2, pos.y); + } + else if (diff < -20f) //right + { + pos = new Vector3(pos.x + txtWidth / 2, pos.y); + } + else + { + float y = pos.y > cenPos.y ? pos.y + txtHig / 2 : pos.y - txtHig / 2; + pos = new Vector3(pos.x, y); + } + txt.SetPosition(pos); + } + + public static float GetAxisPosition(GridCoord grid, Axis axis, double value, int dataCount = 0, DataZoom dataZoom = null) + { + var gridHeight = axis is YAxis ? grid.context.height : grid.context.width; + var gridXY = axis is YAxis ? grid.context.y : grid.context.x; + if (axis.IsCategory()) + { + if (dataCount == 0) dataCount = axis.data.Count; + var categoryIndex = (int)value; + var scaleWid = AxisHelper.GetDataWidth(axis, gridHeight, dataCount, dataZoom); + float startY = gridXY + (axis.boundaryGap ? scaleWid / 2 : 0); + return startY + scaleWid * categoryIndex; + } + else + { + var yDataHig = (axis.context.minMaxRange == 0) ? 0f : + (float)((value - axis.context.minValue) / axis.context.minMaxRange * gridHeight); + return gridXY + yDataHig; + } + } + + public static double GetAxisPositionValue(GridCoord grid, Axis axis, Vector3 pos) + { + if (axis is YAxis) + return GetAxisPositionValue(pos.y, grid.context.height, axis.context.minMaxRange, grid.context.y, axis.context.offset); + else if (axis is XAxis) + return GetAxisPositionValue(pos.x, grid.context.width, axis.context.minMaxRange, grid.context.x, axis.context.offset); + else + return 0; + } + + public static double GetAxisPositionValue(float xy, float axisLength, double axisRange, float axisStart, float axisOffset) + { + var yRate = axisRange / axisLength; + return yRate * (xy - axisStart - axisOffset); + } + + /// <summary> + /// 获得数值value在坐标轴上的坐标位置 + /// </summary> + /// <param name="grid"></param> + /// <param name="axis"></param> + /// <param name="scaleWidth"></param> + /// <param name="value"></param> + /// <returns></returns> + public static float GetAxisValuePosition(GridCoord grid, Axis axis, float scaleWidth, double value) + { + return GetAxisPositionInternal(grid, axis, scaleWidth, value, true, false); + } + + /// <summary> + /// 获得数值value在坐标轴上相对起点的距离 + /// </summary> + /// <param name="grid"></param> + /// <param name="axis"></param> + /// <param name="scaleWidth"></param> + /// <param name="value"></param> + /// <returns></returns> + public static float GetAxisValueDistance(GridCoord grid, Axis axis, float scaleWidth, double value) + { + return GetAxisPositionInternal(grid, axis, scaleWidth, value, false, false); + } + + /// <summary> + /// 获得数值value在坐标轴上对应的长度 + /// </summary> + /// <param name="grid"></param> + /// <param name="axis"></param> + /// <param name="scaleWidth"></param> + /// <param name="value"></param> + /// <returns></returns> + public static float GetAxisValueLength(GridCoord grid, Axis axis, float scaleWidth, double value) + { + return GetAxisPositionInternal(grid, axis, scaleWidth, value, false, true); + } + + /// <summary> + /// 获得数值value在坐标轴上对应的split索引 + /// </summary> + /// <param name="axis"></param> + /// <param name="value"></param> + /// <returns></returns> + public static int GetAxisValueSplitIndex(Axis axis, double value, bool checkMaxCache, int totalSplitNumber = -1) + { + if (axis.IsCategory()) + { + if (checkMaxCache) + return axis.maxCache > 0 ? (int)value - (axis.GetAddedDataCount() - axis.data.Count) : (int)value; + else + return (int)value; + } + else + { + if (value == axis.context.minValue) + return 0; + else + { + if (totalSplitNumber == -1) + totalSplitNumber = GetTotalSplitGridNum(axis); + if (axis.minMaxType == Axis.AxisMinMaxType.Custom) + return Mathf.CeilToInt(((float)((value - axis.min) / axis.max) * totalSplitNumber) - 1); + else + return Mathf.CeilToInt(((float)((value - axis.context.minValue) / axis.context.minMaxRange) * totalSplitNumber) - 1); + } + } + } + + private static float GetAxisPositionInternal(GridCoord grid, Axis axis, float scaleWidth, double value, bool includeGridXY, bool realLength) + { + var isY = axis is YAxis; + var gridHeight = isY ? grid.context.height : grid.context.width; + var gridXY = isY ? grid.context.y : grid.context.x; + + if (axis.IsLog()) + { + var minIndex = axis.GetLogMinIndex(); + var nowIndex = axis.GetLogValue(value); + return includeGridXY ? + (float)(gridXY + (nowIndex - minIndex) / axis.splitNumber * gridHeight) : + (float)((nowIndex - minIndex) / axis.splitNumber * gridHeight); + } + else if (axis.IsCategory()) + { + var categoryIndex = (int)value; + return includeGridXY ? + gridXY + (axis.boundaryGap ? scaleWidth / 2 : 0) + scaleWidth * categoryIndex : + (axis.boundaryGap ? scaleWidth / 2 : 0) + scaleWidth * categoryIndex; + } + else + { + var yDataHig = 0f; + if (axis.context.minMaxRange != 0) + { + if (realLength) + yDataHig = (float)(value * gridHeight / axis.context.minMaxRange); + else + yDataHig = (float)((value - axis.context.minValue) / axis.context.minMaxRange * gridHeight); + } + return includeGridXY ? + gridXY + yDataHig : + yDataHig; + } + } + + public static float GetAxisXOrY(GridCoord grid, Axis axis, Axis relativedAxis) + { + if (axis is XAxis) + return GetXAxisXOrY(grid, axis, relativedAxis); + else if (axis is YAxis) + return GetYAxisXOrY(grid, axis, relativedAxis); + else if (axis is SingleAxis) + return axis.context.y + axis.offset; + else if (axis is ParallelAxis) + return axis.context.y; + else + return axis.context.x; + } + + public static float GetXAxisXOrY(GridCoord grid, Axis xAxis, Axis relativedAxis) + { + var startY = grid.context.y + xAxis.offset; + if (xAxis.IsTop()) + startY += grid.context.height; + else if (xAxis.axisLine.onZero && relativedAxis != null && relativedAxis.IsValue() + && relativedAxis.gridIndex == xAxis.gridIndex) + startY += relativedAxis.context.offset; + return startY; + } + + public static float GetYAxisXOrY(GridCoord grid, Axis yAxis, Axis relativedAxis) + { + var startX = grid.context.x + yAxis.offset; + if (yAxis.IsRight()) + startX += grid.context.width; + else if (yAxis.axisLine.onZero && relativedAxis != null && relativedAxis.IsValue() + && relativedAxis.gridIndex == yAxis.gridIndex) + startX += relativedAxis.context.offset; + return startX; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs.meta new file mode 100644 index 0000000..ab5a3bc --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 566e3426780cc4339a1fb92d9604d21f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs b/Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs new file mode 100644 index 0000000..6479a14 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs @@ -0,0 +1,185 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to axis label. + /// ||坐标轴刻度标签的相关设置。 + /// </summary> + [Serializable] + public class AxisLabel : LabelStyle + { + [SerializeField] private int m_Interval = 0; + [SerializeField] private bool m_Inside = false; + [SerializeField] private bool m_ShowAsPositiveNumber = false; + [SerializeField] private bool m_OnZero = false; + [SerializeField] private bool m_ShowStartLabel = true; + [SerializeField] private bool m_ShowEndLabel = true; + [SerializeField] private TextLimit m_TextLimit = new TextLimit(); + + /// <summary> + /// The display interval of the axis label. + /// ||坐标轴刻度标签的显示间隔,在类目轴中有效。0表示显示所有标签,1表示隔一个隔显示一个标签,以此类推。 + /// </summary> + public int interval + { + get { return m_Interval; } + set { if (PropertyUtil.SetStruct(ref m_Interval, value)) SetComponentDirty(); } + } + /// <summary> + /// Set this to true so the axis labels face the inside direction. + /// ||刻度标签是否朝内,默认朝外。 + /// </summary> + public bool inside + { + get { return m_Inside; } + set { if (PropertyUtil.SetStruct(ref m_Inside, value)) SetComponentDirty(); } + } + /// <summary> + /// Show negative number as positive number. + /// ||将负数数值显示为正数。一般和`Serie`的`showAsPositiveNumber`配合使用。 + /// </summary> + public bool showAsPositiveNumber + { + get { return m_ShowAsPositiveNumber; } + set { if (PropertyUtil.SetStruct(ref m_ShowAsPositiveNumber, value)) SetComponentDirty(); } + } + + /// <summary> + /// 刻度标签显示在0刻度上。 + /// </summary> + public bool onZero + { + get { return m_OnZero; } + set { if (PropertyUtil.SetStruct(ref m_OnZero, value)) SetComponentDirty(); } + } + /// <summary> + /// Whether to display the first label. + /// ||是否显示第一个文本。 + /// </summary> + public bool showStartLabel + { + get { return m_ShowStartLabel; } + set { if (PropertyUtil.SetStruct(ref m_ShowStartLabel, value)) SetComponentDirty(); } + } + /// <summary> + /// Whether to display the last label. + /// ||是否显示最后一个文本。 + /// </summary> + public bool showEndLabel + { + get { return m_ShowEndLabel; } + set { if (PropertyUtil.SetStruct(ref m_ShowEndLabel, value)) SetComponentDirty(); } + } + /// <summary> + /// 文本限制。 + /// </summary> + public TextLimit textLimit + { + get { return m_TextLimit; } + set { if (value != null) { m_TextLimit = value; SetComponentDirty(); } } + } + + public override bool componentDirty { get { return m_ComponentDirty || m_TextLimit.componentDirty; } } + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + textLimit.ClearComponentDirty(); + } + + public static AxisLabel defaultAxisLabel + { + get + { + return new AxisLabel() + { + m_Show = true, + m_Interval = 0, + m_Inside = false, + m_Distance = 8, + m_TextStyle = new TextStyle(), + }; + } + } + + public new AxisLabel Clone() + { + var axisLabel = new AxisLabel + { + show = show, + formatter = formatter, + interval = interval, + inside = inside, + distance = distance, + numericFormatter = numericFormatter, + width = width, + height = height, + showStartLabel = showStartLabel, + showEndLabel = showEndLabel, + textLimit = textLimit.Clone() + }; + axisLabel.textStyle.Copy(textStyle); + return axisLabel; + } + + public void Copy(AxisLabel axisLabel) + { + show = axisLabel.show; + formatter = axisLabel.formatter; + interval = axisLabel.interval; + inside = axisLabel.inside; + distance = axisLabel.distance; + numericFormatter = axisLabel.numericFormatter; + width = axisLabel.width; + height = axisLabel.height; + showStartLabel = axisLabel.showStartLabel; + showEndLabel = axisLabel.showEndLabel; + textLimit.Copy(axisLabel.textLimit); + textStyle.Copy(axisLabel.textStyle); + } + + public void SetRelatedText(ChartText txt, float labelWidth) + { + m_TextLimit.SetRelatedText(txt, labelWidth); + } + + public override string GetFormatterContent(int labelIndex, int totalIndex, string category) + { + if (string.IsNullOrEmpty(category)) + return GetFormatterFunctionContent(labelIndex, category, category); + + if (string.IsNullOrEmpty(m_Formatter)) + { + return GetFormatterFunctionContent(labelIndex, category, m_TextLimit.GetLimitContent(category)); + } + else + { + var content = m_Formatter; + FormatterHelper.ReplaceAxisLabelContent(ref content, category, labelIndex, totalIndex); + return GetFormatterFunctionContent(labelIndex, category, m_TextLimit.GetLimitContent(content)); + } + } + + public override string GetFormatterContent(int labelIndex, int totalIndex, double value, double minValue, double maxValue, bool isLog = false) + { + if (showAsPositiveNumber && value < 0) + { + value = Math.Abs(value); + } + return base.GetFormatterContent(labelIndex, totalIndex, value, minValue, maxValue, isLog); + } + + public bool IsNeedShowLabel(int index, int total) + { + var labelShow = show && (interval == 0 || index % (interval + 1) == 0); + if (labelShow) + { + if (!showStartLabel && index == 0) labelShow = false; + else if (!showEndLabel && index == total - 1) labelShow = false; + } + return labelShow; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs.meta new file mode 100644 index 0000000..a735687 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisLabel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 051f9473d1beb4e0bb35aa1600cb44bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisLine.cs b/Assets/XCharts/Runtime/Component/Axis/AxisLine.cs new file mode 100644 index 0000000..acf91d1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisLine.cs @@ -0,0 +1,77 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to axis line. + /// ||坐标轴轴线。 + /// </summary> + [System.Serializable] + public class AxisLine : BaseLine + { + [SerializeField] private bool m_OnZero; + [SerializeField] private bool m_ShowArrow; + [SerializeField] private ArrowStyle m_Arrow = new ArrowStyle(); + + /// <summary> + /// When mutiple axes exists, this option can be used to specify which axis can be "onZero" to. + /// ||X 轴或者 Y 轴的轴线是否在另一个轴的 0 刻度上,只有在另一个轴为数值轴且包含 0 刻度时有效。 + /// </summary> + public bool onZero + { + get { return m_OnZero; } + set { if (PropertyUtil.SetStruct(ref m_OnZero, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show the arrow symbol of axis. + /// ||是否显示箭头。 + /// </summary> + public bool showArrow + { + get { return m_ShowArrow; } + set { if (PropertyUtil.SetStruct(ref m_ShowArrow, value)) SetVerticesDirty(); } + } + /// <summary> + /// the arrow of line. + /// ||轴线箭头。 + /// </summary> + public ArrowStyle arrow + { + get { return m_Arrow; } + set { if (PropertyUtil.SetClass(ref m_Arrow, value)) SetVerticesDirty(); } + } + public static AxisLine defaultAxisLine + { + get + { + var axisLine = new AxisLine + { + m_Show = true, + m_OnZero = true, + m_ShowArrow = false, + m_Arrow = new ArrowStyle(), + m_LineStyle = new LineStyle(LineStyle.Type.None), + }; + return axisLine; + } + } + + public AxisLine Clone() + { + var axisLine = new AxisLine(); + axisLine.show = show; + axisLine.onZero = onZero; + axisLine.showArrow = showArrow; + axisLine.arrow = arrow.Clone(); + return axisLine; + } + + public void Copy(AxisLine axisLine) + { + base.Copy(axisLine); + onZero = axisLine.onZero; + showArrow = axisLine.showArrow; + arrow.Copy(axisLine.arrow); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisLine.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisLine.cs.meta new file mode 100644 index 0000000..7551798 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2748c2a8789724709aa76f6056eb708d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs b/Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs new file mode 100644 index 0000000..1fff97d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs @@ -0,0 +1,62 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Minor split line of axis in grid area. + /// ||坐标轴在 grid 区域中的次分隔线。次分割线会对齐次刻度线 minorTick。 + /// </summary> + [Serializable] + [Since("v3.2.0")] + public class AxisMinorSplitLine : BaseLine + { + [SerializeField] private float m_Distance; + [SerializeField] private bool m_AutoColor; + + /// <summary> + /// The distance between the split line and axis line. + /// ||刻度线与轴线的距离。 + /// </summary> + public float distance { get { return m_Distance; } set { m_Distance = value; } } + /// <summary> + /// auto color. + /// ||自动设置颜色。 + /// </summary> + public bool autoColor { get { return m_AutoColor; } set { m_AutoColor = value; } } + + public override bool vertsDirty { get { return m_VertsDirty || m_LineStyle.anyDirty; } } + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + m_LineStyle.ClearVerticesDirty(); + } + public static AxisMinorSplitLine defaultMinorSplitLine + { + get + { + return new AxisMinorSplitLine() + { + m_Show = false, + }; + } + } + + public AxisMinorSplitLine Clone() + { + var axisSplitLine = new AxisMinorSplitLine(); + axisSplitLine.show = show; + axisSplitLine.distance = distance; + axisSplitLine.autoColor = autoColor; + axisSplitLine.lineStyle = lineStyle.Clone(); + return axisSplitLine; + } + + public void Copy(AxisMinorSplitLine splitLine) + { + base.Copy(splitLine); + distance = splitLine.distance; + autoColor = splitLine.autoColor; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs.meta new file mode 100644 index 0000000..d1400a2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisMinorSplitLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7be5a277811c64887a121d7711929aab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs b/Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs new file mode 100644 index 0000000..26c321c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs @@ -0,0 +1,63 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to axis minor tick. + /// ||坐标轴次刻度相关设置。注意:次刻度无法在类目轴中使用。 + /// </summary> + [System.Serializable] + [Since("v3.2.0")] + public class AxisMinorTick : BaseLine + { + [SerializeField] protected int m_SplitNumber = 5; + [SerializeField] private bool m_AutoColor; + + /// <summary> + /// Number of segments that the axis is split into. + /// ||分隔线之间分割的刻度数。 + /// </summary> + public int splitNumber + { + get { return m_SplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value)) SetAllDirty(); } + } + public bool autoColor { get { return m_AutoColor; } set { m_AutoColor = value; } } + + public override bool vertsDirty { get { return m_VertsDirty || m_LineStyle.anyDirty; } } + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + m_LineStyle.ClearVerticesDirty(); + } + public static AxisMinorTick defaultMinorTick + { + get + { + var tick = new AxisMinorTick + { + m_Show = false + }; + return tick; + } + } + + public AxisMinorTick Clone() + { + var axisTick = new AxisMinorTick(); + axisTick.show = show; + axisTick.splitNumber = splitNumber; + axisTick.autoColor = autoColor; + axisTick.lineStyle = lineStyle.Clone(); + return axisTick; + } + + public void Copy(AxisMinorTick axisTick) + { + show = axisTick.show; + splitNumber = axisTick.splitNumber; + autoColor = axisTick.autoColor; + lineStyle.Copy(axisTick.lineStyle); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs.meta new file mode 100644 index 0000000..6dda4b2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisMinorTick.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3bea237f1eccc409ba2635e6f4ca609c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisName.cs b/Assets/XCharts/Runtime/Component/Axis/AxisName.cs new file mode 100644 index 0000000..ca06c16 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisName.cs @@ -0,0 +1,86 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the name of axis. + /// ||坐标轴名称。 + /// </summary> + [Serializable] + public class AxisName : ChildComponent + { + [SerializeField] private bool m_Show; + [SerializeField] private string m_Name; + [SerializeField][Since("v3.1.0")] private bool m_OnZero; + [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle(); + + /// <summary> + /// Whether to show axis name. + /// ||是否显示坐标轴名称。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } + /// <summary> + /// the name of axis. + /// ||坐标轴名称。 + /// </summary> + public string name + { + get { return m_Name; } + set { if (PropertyUtil.SetClass(ref m_Name, value)) SetComponentDirty(); } + } + /// <summary> + /// Whether the axis name position are the same with 0 position of YAxis. + /// ||坐标轴名称的位置是否保持和Y轴0刻度一致。 + /// </summary> + public bool onZero + { + get { return m_OnZero; } + set { if (PropertyUtil.SetStruct(ref m_OnZero, value)) SetComponentDirty(); } + } + /// <summary> + /// The text style of axis name. + /// ||文本样式。 + /// </summary> + public LabelStyle labelStyle + { + get { return m_LabelStyle; } + set { if (PropertyUtil.SetClass(ref m_LabelStyle, value)) SetComponentDirty(); } + } + + public static AxisName defaultAxisName + { + get + { + var axisName = new AxisName() + { + m_Show = false, + m_Name = "axisName", + m_LabelStyle = new LabelStyle() + }; + axisName.labelStyle.position = LabelStyle.Position.End; + return axisName; + } + } + + public AxisName Clone() + { + var axisName = new AxisName(); + axisName.show = show; + axisName.name = name; + axisName.m_LabelStyle.Copy(m_LabelStyle); + return axisName; + } + + public void Copy(AxisName axisName) + { + show = axisName.show; + name = axisName.name; + m_LabelStyle.Copy(axisName.labelStyle); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisName.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisName.cs.meta new file mode 100644 index 0000000..e217ba8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisName.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 878555ba3c6b1479f94f38185700531e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs b/Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs new file mode 100644 index 0000000..1faff43 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Split area of axis in grid area, not shown by default. + /// ||坐标轴在 grid 区域中的分隔区域,默认不显示。 + /// </summary> + [Serializable] + public class AxisSplitArea : ChildComponent + { + [SerializeField] private bool m_Show; + [SerializeField] private List<Color32> m_Color; + + /// <summary> + /// Set this to true to show the splitArea. + /// ||是否显示分隔区域。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// Color of split area. SplitArea color could also be set in color array, + /// which the split lines would take as their colors in turns. + /// Dark and light colors in turns are used by default. + /// ||分隔区域颜色。分隔区域会按数组中颜色的顺序依次循环设置颜色。默认是一个深浅的间隔色。 + /// </summary> + public List<Color32> color + { + get { return m_Color; } + set { if (value != null) { m_Color = value; SetVerticesDirty(); } } + } + + public static AxisSplitArea defaultSplitArea + { + get + { + return new AxisSplitArea() + { + m_Show = false, + m_Color = new List<Color32>() { } + }; + } + } + + public AxisSplitArea Clone() + { + var axisSplitArea = new AxisSplitArea(); + axisSplitArea.show = show; + axisSplitArea.color = new List<Color32>(); + ChartHelper.CopyList(axisSplitArea.color, color); + return axisSplitArea; + } + + public void Copy(AxisSplitArea splitArea) + { + show = splitArea.show; + color.Clear(); + ChartHelper.CopyList(color, splitArea.color); + } + + public Color32 GetColor(int index, BaseAxisTheme theme) + { + if (color.Count > 0) + { + var i = index % color.Count; + return color[i]; + } + else + { + var i = index % theme.splitAreaColors.Count; + return theme.splitAreaColors[i]; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs.meta new file mode 100644 index 0000000..76dbcd5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisSplitArea.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18702fd7797054670af64546b7304bb4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs b/Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs new file mode 100644 index 0000000..3b2a1fe --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs @@ -0,0 +1,112 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Split line of axis in grid area. + /// ||坐标轴在 grid 区域中的分隔线。 + /// </summary> + [Serializable] + public class AxisSplitLine : BaseLine + { + [SerializeField] private int m_Interval; + [SerializeField] private float m_Distance; + [SerializeField] private bool m_AutoColor; + [SerializeField][Since("v3.3.0")] private bool m_ShowStartLine = true; + [SerializeField][Since("v3.3.0")] private bool m_ShowEndLine = true; + [SerializeField][Since("v3.11.0")] private bool m_ShowZLine = true; + + /// <summary> + /// The distance between the split line and axis line. + /// ||刻度线与轴线的距离。 + /// </summary> + public float distance { get { return m_Distance; } set { m_Distance = value; } } + /// <summary> + /// auto color. + /// ||自动设置颜色。 + /// </summary> + public bool autoColor { get { return m_AutoColor; } set { m_AutoColor = value; } } + /// <summary> + /// Interval of Axis splitLine. + /// ||坐标轴分隔线的显示间隔。 + /// </summary> + public int interval + { + get { return m_Interval; } + set { if (PropertyUtil.SetStruct(ref m_Interval, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show the first split line. + /// ||是否显示第一条分割线。 + /// </summary> + public bool showStartLine + { + get { return m_ShowStartLine; } + set { if (PropertyUtil.SetStruct(ref m_ShowStartLine, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show the last split line. + /// ||是否显示最后一条分割线。 + /// </summary> + public bool showEndLine + { + get { return m_ShowEndLine; } + set { if (PropertyUtil.SetStruct(ref m_ShowEndLine, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show the Z axis part of the split line. Generally used for 3D coordinate systems. + /// ||是否显示Z轴部分分割线。一般用于3D坐标系。 + /// </summary> + public bool showZLine + { + get { return m_ShowZLine; } + set { if (PropertyUtil.SetStruct(ref m_ShowZLine, value)) SetVerticesDirty(); } + } + + public override bool vertsDirty { get { return m_VertsDirty || m_LineStyle.anyDirty; } } + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + m_LineStyle.ClearVerticesDirty(); + } + public static AxisSplitLine defaultSplitLine + { + get + { + return new AxisSplitLine() + { + m_Show = false, + }; + } + } + + public AxisSplitLine Clone() + { + var axisSplitLine = new AxisSplitLine(); + axisSplitLine.show = show; + axisSplitLine.interval = interval; + axisSplitLine.showStartLine = showStartLine; + axisSplitLine.showEndLine = showEndLine; + axisSplitLine.lineStyle = lineStyle.Clone(); + return axisSplitLine; + } + + public void Copy(AxisSplitLine splitLine) + { + base.Copy(splitLine); + interval = splitLine.interval; + showStartLine = splitLine.showStartLine; + showEndLine = splitLine.showEndLine; + } + + internal bool NeedShow(int index, int total) + { + if (!show) return false; + if (interval != 0 && index % (interval + 1) != 0) return false; + if (!showStartLine && index == 0) return false; + if (!showEndLine && index == total - 1) return false; + return true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs.meta new file mode 100644 index 0000000..4b8c3e4 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisSplitLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3da942a7a6bea44e2998ed993c0641ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisTick.cs b/Assets/XCharts/Runtime/Component/Axis/AxisTick.cs new file mode 100644 index 0000000..eb1f059 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisTick.cs @@ -0,0 +1,110 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to axis tick. + /// ||坐标轴刻度相关设置。 + /// </summary> + [System.Serializable] + public class AxisTick : BaseLine + { + [SerializeField] private bool m_AlignWithLabel; + [SerializeField] private bool m_Inside; + [SerializeField] private bool m_ShowStartTick; + [SerializeField] private bool m_ShowEndTick; + [SerializeField] private float m_Distance; + [SerializeField] protected int m_SplitNumber = 0; + [SerializeField] private bool m_AutoColor; + + /// <summary> + /// The distance between the tick line and axis line. + /// ||刻度线与轴线的距离。 + /// </summary> + public float distance { get { return m_Distance; } set { m_Distance = value; } } + + /// <summary> + /// Align axis tick with label, which is available only when boundaryGap is set to be true in category axis. + /// ||类目轴中在 boundaryGap 为 true 的时候有效,可以保证刻度线和标签对齐。 + /// </summary> + public bool alignWithLabel + { + get { return m_AlignWithLabel; } + set { if (PropertyUtil.SetStruct(ref m_AlignWithLabel, value)) SetVerticesDirty(); } + } + /// <summary> + /// Set this to true so the axis labels face the inside direction. + /// ||坐标轴刻度是否朝内,默认朝外。 + /// </summary> + public bool inside + { + get { return m_Inside; } + set { if (PropertyUtil.SetStruct(ref m_Inside, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to display the first tick. + /// ||是否显示第一个刻度。 + /// </summary> + public bool showStartTick + { + get { return m_ShowStartTick; } + set { if (PropertyUtil.SetStruct(ref m_ShowStartTick, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to display the last tick. + /// ||是否显示最后一个刻度。 + /// </summary> + public bool showEndTick + { + get { return m_ShowEndTick; } + set { if (PropertyUtil.SetStruct(ref m_ShowEndTick, value)) SetVerticesDirty(); } + } + /// <summary> + /// Number of segments that the axis is split into. + /// ||分隔线之间分割的刻度数。 + /// </summary> + public int splitNumber + { + get { return m_SplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value)) SetAllDirty(); } + } + public bool autoColor { get { return m_AutoColor; } set { m_AutoColor = value; } } + + public static AxisTick defaultTick + { + get + { + var tick = new AxisTick + { + m_Show = true, + m_AlignWithLabel = false, + m_Inside = false, + m_ShowStartTick = true, + m_ShowEndTick = true + }; + return tick; + } + } + + public AxisTick Clone() + { + var axisTick = new AxisTick(); + axisTick.show = show; + axisTick.alignWithLabel = alignWithLabel; + axisTick.inside = inside; + axisTick.showStartTick = showStartTick; + axisTick.showEndTick = showEndTick; + axisTick.lineStyle = lineStyle.Clone(); + return axisTick; + } + + public void Copy(AxisTick axisTick) + { + show = axisTick.show; + alignWithLabel = axisTick.alignWithLabel; + inside = axisTick.inside; + showStartTick = axisTick.showStartTick; + showEndTick = axisTick.showEndTick; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/AxisTick.cs.meta b/Assets/XCharts/Runtime/Component/Axis/AxisTick.cs.meta new file mode 100644 index 0000000..c4fdfad --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/AxisTick.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60278762ed892450d85e27b7df8f997e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/ParallelAxis.meta b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis.meta new file mode 100644 index 0000000..7734a7d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 24693180b2a2e41b2ab4025b2bbebf01 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs new file mode 100644 index 0000000..7fac3ab --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [RequireChartComponent(typeof(ParallelCoord))] + [ComponentHandler(typeof(ParallelAxisHander), true)] + public class ParallelAxis : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Value; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = true; + m_Position = AxisPosition.Bottom; + m_Offset = 0; + m_Data = new List<string>() { "x1", "x2", "x3", "x4", "x5" }; + m_Icons = new List<Sprite>(5); + splitLine.show = false; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = true; + axisName.labelStyle.offset = new Vector3(0, 25, 0); + } + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs.meta new file mode 100644 index 0000000..4dd9fa4 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7bc01c54f4d6485389fd57c37810c74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs new file mode 100644 index 0000000..be43113 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs @@ -0,0 +1,168 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class ParallelAxisHander : AxisHandler<ParallelAxis> + { + private Orient m_Orient; + private ParallelCoord m_Parallel; + + protected override Orient orient { get { return m_Orient; } } + + public override void InitComponent() + { + InitParallelAxis(component); + } + + public override void Update() + { + UpdateContext(component); + } + + public override void DrawBase(VertexHelper vh) + { + UpdateContext(component); + DrawParallelAxisSplit(vh, component); + DrawParallelAxisLine(vh, component); + DrawParallelAxisTick(vh, component); + } + + private void UpdateContext(ParallelAxis axis) + { + var parallel = chart.GetChartComponent<ParallelCoord>(axis.parallelIndex); + if (parallel == null) + return; + + m_Orient = parallel.orient; + m_Parallel = parallel; + var axisCount = chart.GetChartComponentNum<ParallelAxis>(); + + if (m_Orient == Orient.Horizonal) + { + var each = axisCount > 1 ? parallel.context.height / (axisCount - 1) : 0; + axis.context.x = parallel.context.x; + axis.context.y = parallel.context.y + (axis.index) * each; + axis.context.width = parallel.context.width; + axis.context.length = parallel.context.width; + } + else + { + var each = axisCount > 1 ? parallel.context.width / (axisCount - 1) : 0; + axis.context.x = parallel.context.x + (axis.index) * each; + axis.context.y = parallel.context.y; + axis.context.width = parallel.context.height; + axis.context.length = parallel.context.height; + } + axis.context.orient = m_Orient; + axis.context.height = 0; + axis.context.position = new Vector3(axis.context.x, axis.context.y); + } + + private void InitParallelAxis(ParallelAxis axis) + { + var theme = chart.theme; + var xAxisIndex = axis.index; + axis.painter = chart.painter; + axis.refreshComponent = delegate() + { + UpdateContext(axis); + InitAxis(null, + m_Orient, + axis.context.x, + axis.context.y, + axis.context.width, + axis.context.height); + }; + axis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(component, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + if (m_Parallel == null) + return Vector3.zero; + + return GetLabelPosition(i, m_Orient, component, null, + chart.theme.axis, + scaleWid, + component.context.x, + component.context.y, + component.context.width, + component.context.height); + } + + private void DrawParallelAxisSplit(VertexHelper vh, ParallelAxis axis) + { + if (AxisHelper.NeedShowSplit(axis)) + { + if (m_Parallel == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(axis); + DrawAxisSplit(vh, chart.theme.axis, dataZoom, + m_Orient, + axis.context.x, + axis.context.y, + axis.context.width, + axis.context.height); + } + } + + private void DrawParallelAxisTick(VertexHelper vh, ParallelAxis axis) + { + if (AxisHelper.NeedShowSplit(axis)) + { + if (m_Parallel == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(axis); + + DrawAxisTick(vh, axis, chart.theme.axis, dataZoom, + m_Orient, + axis.context.x, + axis.context.y, + axis.context.width); + } + } + + private void DrawParallelAxisLine(VertexHelper vh, ParallelAxis axis) + { + if (axis.show && axis.axisLine.show) + { + if (m_Parallel == null) + return; + + DrawAxisLine(vh, axis, + chart.theme.axis, + m_Orient, + axis.context.x, + axis.context.y, + axis.context.width); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.x; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs.meta b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs.meta new file mode 100644 index 0000000..5b82d2f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ParallelAxis/ParallelAxisHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26ab25bf702c54ad38461c91ba1451af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/RadiusAxis.meta b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis.meta new file mode 100644 index 0000000..a418bc2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7c8971958a94d47e68f7ebdff5872b71 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs new file mode 100644 index 0000000..ef949b3 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + /// <summary> + /// Radial axis of polar coordinate. + /// ||极坐标系的径向轴。 + /// </summary> + [System.Serializable] + [RequireChartComponent(typeof(PolarCoord))] + [ComponentHandler(typeof(RadiusAxisHandler), true)] + public class RadiusAxis : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Value; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 5; + m_BoundaryGap = false; + m_Data = new List<string>(5); + splitLine.show = true; + splitLine.lineStyle.type = LineStyle.Type.Solid; + axisLabel.textLimit.enable = false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs.meta new file mode 100644 index 0000000..d9c487e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f6429398a27934726ba49d387d681728 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs new file mode 100644 index 0000000..036665e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs @@ -0,0 +1,217 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class RadiusAxisHandler : AxisHandler<RadiusAxis> + { + public override void InitComponent() + { + InitRadiusAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component); + UpdatePointerValue(component); + } + + public override void DrawBase(VertexHelper vh) + { + DrawRadiusAxis(vh, component); + } + + protected override void UpdatePointerValue(Axis axis) + { + if (axis == null) + return; + var polar = chart.GetChartComponent<PolarCoord>(axis.polarIndex); + if (polar == null) + return; + + if (!polar.context.isPointerEnter) + { + axis.context.pointerValue = double.PositiveInfinity; + return; + } + + var angleAxis = ComponentHelper.GetAngleAxis(chart.components, polar.index); + if (angleAxis == null) + return; + + var dist = Vector3.Distance(chart.pointerPos, polar.context.center); + axis.context.pointerValue = axis.context.minValue + (dist / polar.context.radius) * axis.context.minMaxRange; + axis.context.pointerLabelPosition = GetLabelPosition(polar, axis, angleAxis.context.startAngle, dist); + } + + private void UpdateAxisMinMaxValue(RadiusAxis axis, bool updateChart = true) + { + if (axis == null) return; + if (axis.IsCategory() || !axis.show) return; + double tempMinValue; + double tempMaxValue; + SeriesHelper.GetXMinMaxValue(chart, axis.polarIndex, axis.inverse, out tempMinValue, + out tempMaxValue, true); + AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true); + if (tempMinValue != axis.context.minValue || tempMaxValue != axis.context.maxValue) + { + axis.UpdateMinMaxValue(tempMinValue, tempMaxValue); + axis.context.offset = 0; + axis.context.lastCheckInverse = axis.inverse; + UpdateAxisTickValueList(axis); + + if (updateChart) + { + UpdateAxisLabelText(axis); + chart.RefreshChart(); + } + } + } + + internal void UpdateAxisLabelText(RadiusAxis axis) + { + if (axis == null) + return; + var polar = chart.GetChartComponent<PolarCoord>(axis.polarIndex); + if (axis.context.labelObjectList.Count <= 0) + InitRadiusAxis(axis); + else + { + UpdateLabelText(axis, polar.context.radius, null, false); + } + } + + private void InitRadiusAxis(RadiusAxis axis) + { + var polar = chart.GetChartComponent<PolarCoord>(axis.index); + if (polar == null) + return; + + var angleAxis = ComponentHelper.GetAngleAxis(chart.components, polar.index); + if (angleAxis == null) + return; + + PolarHelper.UpdatePolarCenter(polar, chart.chartPosition, chart.chartWidth, chart.chartHeight); + axis.context.labelObjectList.Clear(); + var radius = polar.context.outsideRadius - polar.context.insideRadius; + var objName = component.GetType().Name + axis.index; + var axisObj = ChartHelper.AddObject(objName, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + axisObj.transform.localPosition = Vector3.zero; + axisObj.SetActive(axis.show && axis.axisLabel.show); + axisObj.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(axisObj); + var textStyle = axis.axisLabel.textStyle; + var splitNumber = AxisHelper.GetScaleNumber(axis, radius, null); + var totalWidth = polar.context.insideRadius; + var txtHig = textStyle.GetFontSize(chart.theme.axis) + 2; + for (int i = 0; i < splitNumber; i++) + { + var labelWidth = AxisHelper.GetScaleWidth(axis, radius, i + 1, null); + var inside = axis.axisLabel.inside; + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series); + var labelName = AxisHelper.GetLabelName(axis, radius, i, axis.context.minValue, axis.context.maxValue, + null, isPercentStack); + var label = ChartHelper.AddAxisLabelObject(splitNumber, i, objName + i, axisObj.transform, + new Vector2(labelWidth, txtHig), axis, chart.theme.axis, labelName, Color.clear); + + if (i == 0) + axis.axisLabel.SetRelatedText(label.text, labelWidth); + + label.text.SetAlignment(textStyle.GetAlignment(TextAnchor.MiddleCenter)); + label.SetText(labelName); + label.SetPosition(GetLabelPosition(polar, axis, angleAxis.context.startAngle, totalWidth)); + label.SetActive(true, true); + label.SetTextActive(true); + + axis.context.labelObjectList.Add(label); + + totalWidth += labelWidth; + } + } + + private Vector3 GetLabelPosition(PolarCoord polar, Axis axis, float startAngle, float totalWidth) + { + var cenPos = polar.context.center; + var dire = ChartHelper.GetDire(startAngle, true).normalized; + var tickLength = axis.axisTick.GetLength(chart.theme.axis.tickLength); + var tickVector = ChartHelper.GetVertialDire(dire) * + (tickLength + axis.axisLabel.distance); + if (axis.IsCategory()) + { + totalWidth += polar.context.radius / axis.data.Count / 2; + } + return ChartHelper.GetPos(cenPos, totalWidth, startAngle, true) + tickVector; + } + + private void DrawRadiusAxis(VertexHelper vh, RadiusAxis radiusAxis) + { + if (radiusAxis == null) + return; + + var polar = chart.GetChartComponent<PolarCoord>(radiusAxis.polarIndex); + if (polar == null) + return; + + var angleAxis = ComponentHelper.GetAngleAxis(chart.components, polar.index); + if (angleAxis == null) + return; + + var startAngle = angleAxis.context.startAngle; + var radius = polar.context.radius; + var cenPos = polar.context.center; + var size = AxisHelper.GetScaleNumber(radiusAxis, radius, null); + var totalWidth = polar.context.insideRadius; + var dire = ChartHelper.GetDire(startAngle, true).normalized; + var tickWidth = radiusAxis.axisTick.GetWidth(chart.theme.axis.tickWidth); + var tickLength = radiusAxis.axisTick.GetLength(chart.theme.axis.tickLength); + var tickVetor = ChartHelper.GetVertialDire(dire) * tickLength; + for (int i = 0; i < size; i++) + { + var scaleWidth = AxisHelper.GetScaleWidth(radiusAxis, radius, i + 1); + var pos = ChartHelper.GetPos(cenPos, totalWidth + tickWidth, startAngle, true); + if (radiusAxis.show && radiusAxis.splitLine.show) + { + if (CanDrawSplitLine(angleAxis, i, size) && radiusAxis.splitLine.NeedShow(i, size)) + { + var outsideRaidus = totalWidth + radiusAxis.splitLine.GetWidth(chart.theme.axis.splitLineWidth) * 2; + var splitLineColor = radiusAxis.splitLine.GetColor(chart.theme.axis.splitLineColor); + UGL.DrawDoughnut(vh, cenPos, totalWidth, outsideRaidus, splitLineColor, ColorUtil.clearColor32); + } + } + if (radiusAxis.show && radiusAxis.axisTick.show) + { + if ((i == 0 && radiusAxis.axisTick.showStartTick) || + (i == size && radiusAxis.axisTick.showEndTick) || + (i > 0 && i < size)) + { + UGL.DrawLine(vh, pos, pos + tickVetor, tickWidth, chart.theme.axis.lineColor); + } + } + totalWidth += scaleWidth; + } + if (radiusAxis.show && radiusAxis.axisLine.show) + { + var lineStartPos = polar.context.center + dire * polar.context.insideRadius; + var lineEndPos = polar.context.center + dire * (polar.context.outsideRadius + 2 * tickWidth); + var lineWidth = radiusAxis.axisLine.GetWidth(chart.theme.axis.lineWidth); + UGL.DrawLine(vh, lineStartPos, lineEndPos, lineWidth, chart.theme.axis.lineColor); + } + } + + private bool CanDrawSplitLine(AngleAxis angleAxis, int i, int size) + { + if (angleAxis.axisLine.show) + { + return i != size - 1 && i != 0; + } + else + { + return true; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs.meta b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs.meta new file mode 100644 index 0000000..5319e69 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/RadiusAxis/RadiusAxisHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f2cb79bfe30c4f14a3117f9f30ed3bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/SingleAxis.meta b/Assets/XCharts/Runtime/Component/Axis/SingleAxis.meta new file mode 100644 index 0000000..09c6e70 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/SingleAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17498717d39c14b43a91c67401407410 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs new file mode 100644 index 0000000..5b2e89e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs @@ -0,0 +1,162 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Single axis. + /// ||单轴。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(SingleAxisHander), true)] + public class SingleAxis : Axis, IUpdateRuntimeData + { + [SerializeField] protected Orient m_Orient = Orient.Horizonal; + [SerializeField] private float m_Left = 0.1f; + [SerializeField] private float m_Right = 0.1f; + [SerializeField] private float m_Top = 0f; + [SerializeField] private float m_Bottom = 0.2f; + [SerializeField] private float m_Width = 0; + [SerializeField] private float m_Height = 50; + + /// <summary> + /// Orientation of the axis. By default, it's 'Horizontal'. You can set it to be 'Vertical' to make a vertical axis. + /// ||坐标轴朝向。默认为水平朝向。 + /// </summary> + public Orient orient + { + get { return m_Orient; } + set { if (PropertyUtil.SetStruct(ref m_Orient, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the left side of the container. + /// ||组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the right side of the container. + /// ||组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the top side of the container. + /// ||组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the bottom side of the container. + /// ||组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetAllDirty(); } + } + /// <summary> + /// width of axis. + /// ||坐标轴宽。 + /// </summary> + public float width + { + get { return m_Width; } + set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetAllDirty(); } + } + /// <summary> + /// height of axis. + /// ||坐标轴高。 + /// </summary> + public float height + { + get { return m_Height; } + set { if (PropertyUtil.SetStruct(ref m_Height, value)) SetAllDirty(); } + } + + public void UpdateRuntimeData(BaseChart chart) + { + var chartX = chart.chartX; + var chartY = chart.chartY; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + context.left = left <= 1 ? left * chartWidth : left; + context.bottom = bottom <= 1 ? bottom * chartHeight : bottom; + context.top = top <= 1 ? top * chartHeight : top; + context.right = right <= 1 ? right * chartWidth : right; + + context.height = height <= 1 ? height * chartHeight : height; + + if (m_Orient == Orient.Horizonal) + { + context.width = width == 0 ? + chartWidth - context.left - context.right : + (width <= 1 ? chartWidth * width : width); + } + else + { + context.width = width == 0 ? + chartHeight - context.top - context.bottom : + (width <= 1 ? chartHeight * width : width); + } + + if (context.left != 0 && context.right == 0) + context.x = chartX + context.left; + else if (context.left == 0 && context.right != 0) + context.x = chartX + chartWidth - context.right - context.width; + else + context.x = chartX + context.left; + + if (context.bottom != 0 && context.top == 0) + context.y = chartY + context.bottom; + else if (context.bottom == 0 && context.top != 0) + context.y = chartY + chartHeight - context.top - context.height; + else + context.y = chartY + context.bottom; + + context.start = new Vector3(context.x, context.y); + if (m_Orient == Orient.Horizonal) + context.end = new Vector3(context.x + context.width, context.y); + else + context.end = new Vector3(context.x, context.y + context.height); + context.length = (context.end - context.start).magnitude; + context.position = new Vector3(context.x, context.y); + } + + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Category; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = true; + m_Position = AxisPosition.Bottom; + m_Offset = 0; + + m_Left = 0.1f; + m_Right = 0.1f; + m_Top = 0; + m_Bottom = 0.2f; + m_Width = 0; + m_Height = 50; + + m_Data = new List<string>() { "x1", "x2", "x3", "x4", "x5" }; + m_Icons = new List<Sprite>(5); + splitLine.show = false; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = true; + axisTick.showStartTick = true; + axisTick.showEndTick = true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs.meta new file mode 100644 index 0000000..1fe393d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aeb871d6555744e609bd651306c601a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs new file mode 100644 index 0000000..46aa997 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs @@ -0,0 +1,122 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class SingleAxisHander : AxisHandler<SingleAxis> + { + protected override Orient orient { get { return component.orient; } } + + public override void InitComponent() + { + InitXAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component.index, component); + UpdatePointerValue(component); + } + + public override void DrawBase(VertexHelper vh) + { + DrawSingleAxisSplit(vh, component); + DrawSingleAxisLine(vh, component); + DrawSingleAxisTick(vh, component); + } + + private void InitXAxis(SingleAxis axis) + { + var theme = chart.theme; + var xAxisIndex = axis.index; + axis.painter = chart.painter; + axis.refreshComponent = delegate() + { + axis.UpdateRuntimeData(chart); + + InitAxis(null, + axis.orient, + axis.context.x, + axis.context.y, + axis.context.width, + axis.context.height); + }; + axis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(component, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + return GetLabelPosition(i, component.orient, component, null, + chart.theme.axis, + scaleWid, + component.context.x, + component.context.y, + component.context.width, + component.context.height); + } + + private void DrawSingleAxisSplit(VertexHelper vh, SingleAxis axis) + { + if (AxisHelper.NeedShowSplit(axis)) + { + var dataZoom = chart.GetDataZoomOfAxis(axis); + DrawAxisSplit(vh, chart.theme.axis, dataZoom, + axis.orient, + axis.context.x, + axis.context.y, + axis.context.width, + axis.context.height); + } + } + + private void DrawSingleAxisTick(VertexHelper vh, SingleAxis axis) + { + if (AxisHelper.NeedShowSplit(axis)) + { + var dataZoom = chart.GetDataZoomOfAxis(axis); + DrawAxisTick(vh, axis, chart.theme.axis, dataZoom, + axis.orient, + axis.context.x, + axis.context.y, + axis.context.width); + } + } + + private void DrawSingleAxisLine(VertexHelper vh, SingleAxis axis) + { + if (axis.show && axis.axisLine.show) + { + DrawAxisLine(vh, axis, + chart.theme.axis, + axis.orient, + axis.context.x, + GetAxisLineXOrY(), + axis.context.width); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.y + component.offset; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs.meta b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs.meta new file mode 100644 index 0000000..59d803e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/SingleAxis/SingleAxisHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50b3514e3079543ea9000d21d809cad3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis.meta b/Assets/XCharts/Runtime/Component/Axis/XAxis.meta new file mode 100644 index 0000000..e35422e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5e50f8f0f8bb406b99fb32d6b5c7769 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs new file mode 100644 index 0000000..c4d5742 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The x axis in cartesian(rectangular) coordinate. + /// ||直角坐标系 grid 中的 x 轴。 + /// </summary> + [System.Serializable] + [RequireChartComponent(typeof(GridCoord))] + [ComponentHandler(typeof(XAxisHander), true)] + public class XAxis : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Category; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = true; + m_Position = AxisPosition.Bottom; + m_Offset = 0; + m_Data = new List<string>() { "x1", "x2", "x3", "x4", "x5" }; + m_Icons = new List<Sprite>(5); + splitLine.show = false; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = true; + axisName.labelStyle.offset = new Vector3(5, 0, 0); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs.meta new file mode 100644 index 0000000..11c03fd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a71be0d36b9745c2894e598b3d9188a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs new file mode 100644 index 0000000..eb30656 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs @@ -0,0 +1,186 @@ +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.EventSystems; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class XAxisHander : AxisHandler<XAxis> + { + protected override Orient orient { get { return Orient.Horizonal; } } + + public override void InitComponent() + { + InitXAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component.index, component); + if (!chart.isTriggerOnClick) + { + UpdatePointerValue(component); + } + } + + public override void OnPointerClick(PointerEventData eventData) + { + base.OnPointerClick(eventData); + if (chart.isTriggerOnClick) + { + UpdatePointerValue(component); + } + } + + public override void OnPointerExit(PointerEventData eventData) + { + base.OnPointerExit(eventData); + if (chart.isTriggerOnClick) + { + component.context.pointerValue = double.PositiveInfinity; + } + } + + public override void DrawBase(VertexHelper vh) + { + UpdatePosition(component); + DrawXAxisSplit(vh, component); + DrawXAxisLine(vh, component); + DrawXAxisTick(vh, component); + } + + private void UpdatePosition(XAxis axis) + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (grid != null) + { + var relativedAxis = chart.GetChartComponent<YAxis>(axis.gridIndex); + axis.context.x = grid.context.x; + axis.context.y = AxisHelper.GetXAxisXOrY(grid, axis, relativedAxis); + axis.context.start = new Vector3(grid.context.x, axis.context.y); + axis.context.end = new Vector3(grid.context.x + grid.context.width, axis.context.y); + var vec = axis.context.end - axis.context.start; + axis.context.dire = vec.normalized; + axis.context.length = vec.magnitude; + axis.context.zeroY = grid.context.y; + axis.context.zeroX = grid.context.x + axis.context.offset; + } + } + + private void InitXAxis(XAxis xAxis) + { + var theme = chart.theme; + var xAxisIndex = xAxis.index; + xAxis.painter = chart.painter; + xAxis.refreshComponent = delegate() + { + var grid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + if (grid != null) + { + var yAxis = chart.GetChartComponent<YAxis>(xAxis.index); + InitAxis(yAxis, + orient, + grid.context.x, + grid.context.y, + grid.context.width, + grid.context.height); + } + }; + xAxis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(component, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + var grid = chart.GetChartComponent<GridCoord>(component.gridIndex); + if (grid == null) + return Vector3.zero; + + var yAxis = chart.GetChartComponent<YAxis>(component.index); + return GetLabelPosition(i, Orient.Horizonal, component, yAxis, + chart.theme.axis, + scaleWid, + grid.context.x, + grid.context.y, + grid.context.width, + grid.context.height); + } + + private void DrawXAxisSplit(VertexHelper vh, XAxis xAxis) + { + if (AxisHelper.NeedShowSplit(xAxis)) + { + var grid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + if (grid == null) + return; + + var relativedAxis = chart.GetChartComponent<YAxis>(xAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + + DrawAxisSplit(vh, chart.theme.axis, dataZoom, + Orient.Horizonal, + grid.context.x, + grid.context.y, + grid.context.width, + grid.context.height, + relativedAxis); + } + } + + private void DrawXAxisTick(VertexHelper vh, XAxis xAxis) + { + if (AxisHelper.NeedShowSplit(xAxis)) + { + var grid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + if (grid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + + DrawAxisTick(vh, xAxis, chart.theme.axis, dataZoom, + Orient.Horizonal, + grid.context.x, + GetAxisLineXOrY(), + grid.context.width); + } + } + + private void DrawXAxisLine(VertexHelper vh, XAxis xAxis) + { + if (xAxis.show && xAxis.axisLine.show) + { + var grid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + if (grid == null) + return; + + DrawAxisLine(vh, xAxis, chart.theme.axis, + Orient.Horizonal, + grid.context.x, + GetAxisLineXOrY(), + grid.context.width); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.y; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs.meta b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs.meta new file mode 100644 index 0000000..79717bd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis/XAxisHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7818e1175663412196de53f19b5ac08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis3D.meta b/Assets/XCharts/Runtime/Component/Axis/XAxis3D.meta new file mode 100644 index 0000000..52962a4 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis3D.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6350e9983955e49c5b48704d3866cbfe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs new file mode 100644 index 0000000..e7f931c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The x axis in cartesian(rectangular) coordinate. + /// ||直角坐标系 grid 中的 x 轴。 + /// </summary> + [Since("v3.11.0")] + [System.Serializable] + [RequireChartComponent(typeof(GridCoord3D))] + [ComponentHandler(typeof(XAxis3DHander), true)] + public class XAxis3D : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Category; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = true; + m_Position = AxisPosition.Bottom; + m_Offset = 0; + m_Data = new List<string>() { "x1", "x2", "x3", "x4", "x5" }; + m_Icons = new List<Sprite>(5); + splitLine.show = false; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = true; + axisName.name = "X"; + axisName.labelStyle.position = LabelStyle.Position.Middle; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs.meta b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs.meta new file mode 100644 index 0000000..b4a328e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9129bca9c2a864e1ea337d7eb74d1024 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs new file mode 100644 index 0000000..2c53f60 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs @@ -0,0 +1,190 @@ +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.EventSystems; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class XAxis3DHander : AxisHandler<XAxis3D> + { + protected override Orient orient { get { return Orient.Horizonal; } } + + public override void InitComponent() + { + InitXAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component.index, component); + if (!chart.isTriggerOnClick) + { + UpdatePointerValue(component); + } + } + + public override void OnPointerClick(PointerEventData eventData) + { + base.OnPointerClick(eventData); + if (chart.isTriggerOnClick) + { + UpdatePointerValue(component); + } + } + + public override void OnPointerExit(PointerEventData eventData) + { + base.OnPointerExit(eventData); + if (chart.isTriggerOnClick) + { + component.context.pointerValue = double.PositiveInfinity; + } + } + + public override void DrawBase(VertexHelper vh) + { + UpdatePosition(component); + DrawXAxisSplit(vh, component); + DrawXAxisLine(vh, component); + DrawXAxisTick(vh, component); + } + + private void UpdatePosition(XAxis3D axis) + { + var grid = chart.GetChartComponent<GridCoord3D>(axis.gridIndex); + if (grid != null) + { + if (axis.position == Axis.AxisPosition.Right || axis.position == Axis.AxisPosition.Top) + { + axis.context.start = grid.xyExchanged ? grid.context.pointD : grid.context.pointB; + axis.context.end = grid.context.pointC; + } + else + { + axis.context.start = grid.context.pointA; + axis.context.end = grid.xyExchanged ? grid.context.pointB : grid.context.pointD; + } + var vect = axis.context.end - axis.context.start; + axis.context.x = axis.context.start.x; + axis.context.y = axis.context.start.y; + axis.context.dire = vect.normalized; + axis.context.length = vect.magnitude; + } + } + + private void InitXAxis(XAxis3D xAxis) + { + var theme = chart.theme; + var xAxisIndex = xAxis.index; + xAxis.painter = chart.painter; + xAxis.refreshComponent = delegate () + { + var yAxis = chart.GetChartComponent<YAxis3D>(xAxis.index); + InitAxis3D(yAxis, orient); + }; + xAxis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(component, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + var yAxis = chart.GetChartComponent<YAxis3D>(component.index); + return Axis3DHelper.GetLabelPosition(i, component, yAxis, chart.theme.axis, scaleWid); + } + + private void DrawXAxisSplit(VertexHelper vh, XAxis3D xAxis) + { + if (AxisHelper.NeedShowSplit(xAxis)) + { + var grid = chart.GetChartComponent<GridCoord3D>(xAxis.gridIndex); + var relativedAxis = chart.GetChartComponent<YAxis3D>(xAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + var isLeft = grid.IsLeft(); + if (grid.xyExchanged) + { + Axis3DHelper.DrawAxisSplit(vh, xAxis, chart.theme.axis, dataZoom, + grid.context.pointA, + grid.context.pointB, + relativedAxis); + if (xAxis.splitLine.showZLine) + { + var relativedAxis2 = chart.GetChartComponent<ZAxis3D>(xAxis.gridIndex); + Axis3DHelper.DrawAxisSplit(vh, xAxis, chart.theme.axis, dataZoom, + isLeft ? grid.context.pointD : grid.context.pointA, + isLeft ? grid.context.pointC : grid.context.pointB, + relativedAxis2); + } + } + else + { + Axis3DHelper.DrawAxisSplit(vh, xAxis, chart.theme.axis, dataZoom, + grid.context.pointA, + grid.context.pointD, + relativedAxis); + if (xAxis.splitLine.showZLine) + { + var relativedAxis2 = chart.GetChartComponent<ZAxis3D>(xAxis.gridIndex); + Axis3DHelper.DrawAxisSplit(vh, xAxis, chart.theme.axis, dataZoom, + grid.context.pointB, + grid.context.pointC, + relativedAxis2); + } + } + } + } + + private void DrawXAxisTick(VertexHelper vh, XAxis3D xAxis) + { + if (AxisHelper.NeedShowSplit(xAxis)) + { + var grid = chart.GetChartComponent<GridCoord3D>(xAxis.gridIndex); + if (grid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + var relativedAxis = chart.GetChartComponent<YAxis3D>(xAxis.gridIndex); + Axis3DHelper.DrawAxisTick(vh, xAxis, chart.theme.axis, dataZoom, + xAxis.context.start, + xAxis.context.end, + -relativedAxis.context.dire); + } + } + + private void DrawXAxisLine(VertexHelper vh, XAxis3D axis) + { + if (axis.show && axis.axisLine.show) + { + var theme = chart.theme.axis; + var lineWidth = axis.axisLine.GetWidth(theme.lineWidth); + var lineType = axis.axisLine.GetType(theme.lineType); + var lineColor = axis.axisLine.GetColor(theme.lineColor); + + var start = axis.context.start; + var end = axis.context.end; + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, start, end, lineColor); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.y; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs.meta b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs.meta new file mode 100644 index 0000000..20521e3 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/XAxis3D/XAxis3DHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc1147481a423494d963df29b423f3a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis.meta b/Assets/XCharts/Runtime/Component/Axis/YAxis.meta new file mode 100644 index 0000000..ef2fe9c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69f8ba8fcc7d84b12b42f837f9f2b94b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs new file mode 100644 index 0000000..9569c58 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The x axis in cartesian(rectangular) coordinate. + /// ||直角坐标系 grid 中的 y 轴。 + /// </summary> + [System.Serializable] + [RequireChartComponent(typeof(GridCoord), typeof(XAxis))] + [ComponentHandler(typeof(YAxisHander), true)] + public class YAxis : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Value; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = false; + m_Position = AxisPosition.Left; + m_Data = new List<string>(5); + splitLine.show = true; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = false; + axisTick.showStartTick = true; + axisName.labelStyle.offset = new Vector3(0, 22, 0); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs.meta b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs.meta new file mode 100644 index 0000000..2e38816 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ac60b8329f7a45c3898c7539d78f091 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs new file mode 100644 index 0000000..2474264 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs @@ -0,0 +1,162 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class YAxisHander : AxisHandler<YAxis> + { + protected override Orient orient { get { return Orient.Vertical; } } + + public override void InitComponent() + { + InitYAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component.index, component); + UpdatePointerValue(component); + } + + public override void DrawBase(VertexHelper vh) + { + UpdatePosition(component); + DrawYAxisSplit(vh, component.index, component); + DrawYAxisLine(vh, component.index, component); + DrawYAxisTick(vh, component.index, component); + } + + private void UpdatePosition(YAxis axis) + { + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (grid != null) + { + var relativedAxis = chart.GetChartComponent<XAxis>(axis.gridIndex); + axis.context.x = AxisHelper.GetYAxisXOrY(grid, axis, relativedAxis); + axis.context.y = grid.context.y; + axis.context.start = new Vector3(axis.context.x, grid.context.y); + axis.context.end = new Vector3(axis.context.x, grid.context.y + grid.context.height); + var vect = axis.context.end - axis.context.start; + axis.context.dire = vect.normalized; + axis.context.length = vect.magnitude; + axis.context.zeroX = axis.context.x; + axis.context.zeroY = axis.context.y + axis.context.offset; + } + } + + private void InitYAxis(YAxis yAxis) + { + var theme = chart.theme; + var yAxisIndex = yAxis.index; + yAxis.painter = chart.painter; + yAxis.refreshComponent = delegate() + { + var grid = chart.GetChartComponent<GridCoord>(yAxis.gridIndex); + if (grid != null) + { + var xAxis = chart.GetChartComponent<YAxis>(yAxis.index); + InitAxis(xAxis, + orient, + grid.context.x, + grid.context.y, + grid.context.height, + grid.context.width); + } + }; + yAxis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(axis, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + var grid = chart.GetChartComponent<GridCoord>(component.gridIndex); + if (grid == null) + return Vector3.zero; + + var xAxis = chart.GetChartComponent<XAxis>(component.index); + return GetLabelPosition(i, Orient.Vertical, component, xAxis, + chart.theme.axis, + scaleWid, + grid.context.x, + grid.context.y, + grid.context.height, + grid.context.width); + } + + private void DrawYAxisSplit(VertexHelper vh, int yAxisIndex, YAxis yAxis) + { + if (AxisHelper.NeedShowSplit(yAxis)) + { + var grid = chart.GetChartComponent<GridCoord>(yAxis.gridIndex); + if (grid == null) + return; + var relativedAxis = chart.GetChartComponent<XAxis>(yAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + DrawAxisSplit(vh, chart.theme.axis, dataZoom, + Orient.Vertical, + grid.context.x, + grid.context.y, + grid.context.height, + grid.context.width, + relativedAxis); + } + } + + private void DrawYAxisTick(VertexHelper vh, int yAxisIndex, YAxis yAxis) + { + if (AxisHelper.NeedShowSplit(yAxis)) + { + var grid = chart.GetChartComponent<GridCoord>(yAxis.gridIndex); + if (grid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + + DrawAxisTick(vh, yAxis, chart.theme.axis, dataZoom, + Orient.Vertical, + GetAxisLineXOrY(), + grid.context.y, + grid.context.height); + } + } + + private void DrawYAxisLine(VertexHelper vh, int yAxisIndex, YAxis yAxis) + { + if (yAxis.show && yAxis.axisLine.show) + { + var grid = chart.GetChartComponent<GridCoord>(yAxis.gridIndex); + if (grid == null) + return; + + DrawAxisLine(vh, yAxis, chart.theme.axis, + Orient.Vertical, + GetAxisLineXOrY(), + grid.context.y, + grid.context.height); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.x; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs.meta b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs.meta new file mode 100644 index 0000000..0ea465d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis/YAxisHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f09b5dcb5fcc54583bcd7946f18dfa48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis3D.meta b/Assets/XCharts/Runtime/Component/Axis/YAxis3D.meta new file mode 100644 index 0000000..c3f1993 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis3D.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aa26616789b6b4903aae479a4c552b89 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs new file mode 100644 index 0000000..d212c68 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + /// <summary> + /// The x axis in cartesian(rectangular) coordinate. + /// ||直角坐标系 grid 中的 y 轴。 + /// </summary> + [Since("v3.11.0")] + [System.Serializable] + [RequireChartComponent(typeof(GridCoord3D), typeof(XAxis3D))] + [ComponentHandler(typeof(YAxis3DHander), true)] + public class YAxis3D : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Value; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = false; + m_Position = AxisPosition.Left; + m_Data = new List<string>(5); + splitLine.show = true; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = false; + axisTick.showStartTick = true; + axisName.name = "Y"; + axisName.labelStyle.position = LabelStyle.Position.Middle; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs.meta b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs.meta new file mode 100644 index 0000000..68c9681 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a3cb4a6657aaf473bbae7162eb189cc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs new file mode 100644 index 0000000..703a3d9 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs @@ -0,0 +1,176 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class YAxis3DHander : AxisHandler<YAxis3D> + { + protected override Orient orient { get { return Orient.Vertical; } } + + public override void InitComponent() + { + InitYAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component.index, component); + UpdatePointerValue(component); + } + + public override void DrawBase(VertexHelper vh) + { + UpdatePosition(component); + DrawYAxisSplit(vh, component.index, component); + DrawYAxisLine(vh, component.index, component); + DrawYAxisTick(vh, component.index, component); + } + + private void UpdatePosition(YAxis3D axis) + { + var grid = chart.GetChartComponent<GridCoord3D>(axis.gridIndex); + if (grid != null) + { + if (axis.position == Axis.AxisPosition.Right) + { + axis.context.start = grid.xyExchanged ? grid.context.pointB : grid.context.pointD; + axis.context.end = grid.context.pointC; + } + else + { + axis.context.start = grid.context.pointA; + axis.context.end = grid.xyExchanged ? grid.context.pointD : grid.context.pointB; + } + axis.context.x = axis.context.start.x; + axis.context.y = axis.context.start.y; + var vect = axis.context.end - axis.context.start; + axis.context.dire = vect.normalized; + axis.context.length = vect.magnitude; + } + } + + private void InitYAxis(YAxis3D yAxis) + { + var theme = chart.theme; + var yAxisIndex = yAxis.index; + yAxis.painter = chart.painter; + yAxis.refreshComponent = delegate () + { + var grid = chart.GetChartComponent<GridCoord3D>(yAxis.gridIndex); + if (grid != null) + { + var xAxis = chart.GetChartComponent<YAxis3D>(yAxis.index); + InitAxis3D(xAxis, orient); + } + }; + yAxis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(axis, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + var xAxis = chart.GetChartComponent<XAxis3D>(component.index); + return Axis3DHelper.GetLabelPosition(i, component, xAxis, chart.theme.axis, scaleWid); + } + + private void DrawYAxisSplit(VertexHelper vh, int yAxisIndex, YAxis3D yAxis) + { + if (AxisHelper.NeedShowSplit(yAxis)) + { + var grid = chart.GetChartComponent<GridCoord3D>(yAxis.gridIndex); + var relativedAxis = chart.GetChartComponent<XAxis3D>(yAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + var isLeft = grid.IsLeft(); + if (grid.xyExchanged) + { + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + grid.context.pointA, + grid.context.pointD, + relativedAxis); + if (yAxis.splitLine.showZLine) + { + var relativedAxis2 = chart.GetChartComponent<ZAxis3D>(yAxis.gridIndex); + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + grid.context.pointB, grid.context.pointC, relativedAxis2); + } + } + else + { + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + grid.context.pointA, + grid.context.pointB, + relativedAxis); + if (yAxis.splitLine.showZLine) + { + var relativedAxis2 = chart.GetChartComponent<ZAxis3D>(yAxis.gridIndex); + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + isLeft ? grid.context.pointD : grid.context.pointA, + isLeft ? grid.context.pointC : grid.context.pointB, + relativedAxis2); + } + } + } + } + + private void DrawYAxisTick(VertexHelper vh, int yAxisIndex, YAxis3D yAxis) + { + if (AxisHelper.NeedShowSplit(yAxis)) + { + var grid = chart.GetChartComponent<GridCoord3D>(yAxis.gridIndex); + if (grid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + var relativedAxis = chart.GetChartComponent<XAxis3D>(yAxis.gridIndex); + + Axis3DHelper.DrawAxisTick(vh, yAxis, chart.theme.axis, dataZoom, + yAxis.context.start, + yAxis.context.end, + -relativedAxis.context.dire); + } + } + + private void DrawYAxisLine(VertexHelper vh, int axisIndex, YAxis3D axis) + { + if (axis.show && axis.axisLine.show) + { + var grid = chart.GetChartComponent<GridCoord3D>(axis.gridIndex); + if (grid == null) + return; + + var theme = chart.theme.axis; + + var lineWidth = axis.axisLine.GetWidth(theme.lineWidth); + var lineType = axis.axisLine.GetType(theme.lineType); + var lineColor = axis.axisLine.GetColor(theme.lineColor); + + var start = axis.context.start; + var end = axis.context.end; + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, start, end, lineColor); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.x; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs.meta b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs.meta new file mode 100644 index 0000000..0433326 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/YAxis3D/YAxis3DHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56b4be734c61645e1bf91c22a6e3da6c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/ZAxis3D.meta b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D.meta new file mode 100644 index 0000000..a8fde68 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 378448672ed084b0798c7ad343314693 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs new file mode 100644 index 0000000..e4f3e01 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + /// <summary> + /// The x axis in cartesian(rectangular) coordinate. + /// ||直角坐标系 grid 中的 y 轴。 + /// </summary> + [Since("v3.11.0")] + [System.Serializable] + [RequireChartComponent(typeof(GridCoord3D), typeof(XAxis3D))] + [ComponentHandler(typeof(ZAxis3DHander), true)] + public class ZAxis3D : Axis + { + public override void SetDefaultValue() + { + m_Show = true; + m_Type = AxisType.Value; + m_Min = 0; + m_Max = 0; + m_SplitNumber = 0; + m_BoundaryGap = false; + m_Position = AxisPosition.Left; + m_Data = new List<string>(5); + splitLine.show = true; + splitLine.lineStyle.type = LineStyle.Type.None; + axisLabel.textLimit.enable = false; + axisTick.showStartTick = true; + axisName.name = "Z"; + axisName.labelStyle.position = LabelStyle.Position.Middle; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs.meta b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs.meta new file mode 100644 index 0000000..428821d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65aa8ae88610c431ebdab86935af2379 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs new file mode 100644 index 0000000..e6e9257 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs @@ -0,0 +1,198 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class ZAxis3DHander : AxisHandler<ZAxis3D> + { + protected override Orient orient { get { return Orient.Vertical; } } + + public override void InitComponent() + { + InitYAxis(component); + } + + public override void Update() + { + UpdateAxisMinMaxValue(component.index, component); + UpdatePointerValue(component); + } + + public override void DrawBase(VertexHelper vh) + { + UpdatePosition(component); + DrawZAxisSplit(vh, component.index, component); + DrawZAxisLine(vh, component.index, component); + DrawZAxisTick(vh, component.index, component); + } + + private void UpdatePosition(ZAxis3D axis) + { + var grid = chart.GetChartComponent<GridCoord3D>(axis.gridIndex); + if (grid != null) + { + if (grid.context.pointB.x < grid.context.pointA.x) + { + axis.context.start = grid.context.pointD; + axis.context.end = grid.context.pointH; + } + else if (axis.position == Axis.AxisPosition.Center) + { + axis.context.start = grid.context.pointB; + axis.context.end = grid.context.pointF; + } + else if (axis.position == Axis.AxisPosition.Right) + { + axis.context.start = grid.context.pointC; + axis.context.end = grid.context.pointG; + } + else + { + axis.context.start = grid.context.pointA; + axis.context.end = grid.context.pointE; + } + axis.context.x = axis.context.start.x; + axis.context.y = axis.context.start.y; + var vect = axis.context.end - axis.context.start; + axis.context.dire = vect.normalized; + axis.context.length = vect.magnitude; + } + } + + private void InitYAxis(ZAxis3D yAxis) + { + var theme = chart.theme; + var yAxisIndex = yAxis.index; + yAxis.painter = chart.painter; + yAxis.refreshComponent = delegate () + { + var grid = chart.GetChartComponent<GridCoord3D>(yAxis.gridIndex); + if (grid != null) + { + var relativedAxis = chart.GetChartComponent<ZAxis3D>(yAxis.index); + InitAxis3D(relativedAxis, orient); + } + }; + yAxis.refreshComponent(); + } + + internal override void UpdateAxisLabelText(Axis axis) + { + base.UpdateAxisLabelText(axis); + if (axis.IsTime() || axis.IsValue()) + { + for (int i = 0; i < axis.context.labelObjectList.Count; i++) + { + var label = axis.context.labelObjectList[i]; + if (label != null) + { + var pos = GetLabelPosition(0, i); + label.SetPosition(pos); + CheckValueLabelActive(axis, i, label, pos); + } + } + } + } + + protected override Vector3 GetLabelPosition(float scaleWid, int i) + { + var grid = chart.GetChartComponent<GridCoord3D>(component.gridIndex); + if (grid == null) + return Vector3.zero; + + var yAxis = chart.GetChartComponent<XAxis3D>(component.index); + return Axis3DHelper.GetLabelPosition(i, component, yAxis, + chart.theme.axis, + scaleWid); + } + + private void DrawZAxisSplit(VertexHelper vh, int yAxisIndex, ZAxis3D yAxis) + { + if (AxisHelper.NeedShowSplit(yAxis)) + { + var grid = chart.GetChartComponent<GridCoord3D>(yAxis.gridIndex); + if (grid == null) + return; + + var isLeft = grid.IsLeft(); + if (grid.xyExchanged) + { + var relativedAxis = chart.GetChartComponent<XAxis3D>(yAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + isLeft ? grid.context.pointD : grid.context.pointA, + isLeft ? grid.context.pointH : grid.context.pointE, + relativedAxis); + if (yAxis.splitLine.showZLine) + { + var relativedAxis2 = chart.GetChartComponent<YAxis3D>(yAxis.gridIndex); + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + grid.context.pointB, + grid.context.pointF, + relativedAxis2); + } + } + else + { + var relativedAxis = chart.GetChartComponent<YAxis3D>(yAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + isLeft ? grid.context.pointD : grid.context.pointA, + isLeft ? grid.context.pointH : grid.context.pointE, + relativedAxis); + if (yAxis.splitLine.showZLine) + { + var relativedAxis2 = chart.GetChartComponent<XAxis3D>(yAxis.gridIndex); + Axis3DHelper.DrawAxisSplit(vh, yAxis, chart.theme.axis, dataZoom, + grid.context.pointB, + grid.context.pointF, + relativedAxis2); + } + } + } + } + + private void DrawZAxisTick(VertexHelper vh, int yAxisIndex, ZAxis3D zAxis) + { + if (AxisHelper.NeedShowSplit(zAxis)) + { + var grid = chart.GetChartComponent<GridCoord3D>(zAxis.gridIndex); + if (grid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(zAxis); + var relativedDire = grid.context.pointA - grid.context.pointB; + Axis3DHelper.DrawAxisTick(vh, zAxis, chart.theme.axis, dataZoom, + zAxis.context.start, + zAxis.context.end, + relativedDire.normalized); + } + } + + private void DrawZAxisLine(VertexHelper vh, int axisIndex, ZAxis3D axis) + { + if (axis.show && axis.axisLine.show) + { + var grid = chart.GetChartComponent<GridCoord3D>(axis.gridIndex); + if (grid == null) + return; + + var theme = chart.theme.axis; + + var lineWidth = axis.axisLine.GetWidth(theme.lineWidth); + var lineType = axis.axisLine.GetType(theme.lineType); + var lineColor = axis.axisLine.GetColor(theme.lineColor); + + var start = axis.context.start; + var end = axis.context.end; + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, start, end, lineColor); + } + } + + internal override float GetAxisLineXOrY() + { + return component.context.x; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs.meta b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs.meta new file mode 100644 index 0000000..3ea9afe --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Axis/ZAxis3D/ZAxis3DHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67fb4be32885d4915979719c676aac5a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Background.meta b/Assets/XCharts/Runtime/Component/Background.meta new file mode 100644 index 0000000..15e2676 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Background.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7db6fdcbbbfd148f58ff7a1f1a569d51 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Background/Background.cs b/Assets/XCharts/Runtime/Component/Background/Background.cs new file mode 100644 index 0000000..c6ee38f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Background/Background.cs @@ -0,0 +1,118 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// Background component. + /// ||背景组件。 + /// </summary> + [Serializable] + [DisallowMultipleComponent] + [ComponentHandler(typeof(BackgroundHandler), false, 0)] + public class Background : MainComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private Sprite m_Image; + [SerializeField] private Image.Type m_ImageType; + [SerializeField] private Color m_ImageColor = Color.white; + [SerializeField][Since("v3.10.0")] private float m_ImageWidth = 0; + [SerializeField][Since("v3.10.0")] private float m_ImageHeight = 0; + [SerializeField] private bool m_AutoColor = true; + [SerializeField][Since("v3.10.0")] private BorderStyle m_BorderStyle = new BorderStyle(); + + /// <summary> + /// Whether to enable the background component. + /// ||是否启用背景组件。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } + /// <summary> + /// the image of background. + /// ||背景图。 + /// </summary> + public Sprite image + { + get { return m_Image; } + set { if (PropertyUtil.SetClass(ref m_Image, value)) SetComponentDirty(); } + } + + /// <summary> + /// the fill type of background image. + /// ||背景图填充类型。 + /// </summary> + public Image.Type imageType + { + get { return m_ImageType; } + set { if (PropertyUtil.SetStruct(ref m_ImageType, value)) SetComponentDirty(); } + } + + /// <summary> + /// 背景图颜色。 + /// </summary> + public Color imageColor + { + get { return m_ImageColor; } + set { if (PropertyUtil.SetColor(ref m_ImageColor, value)) SetComponentDirty(); } + } + + /// <summary> + /// the width of background image. + /// ||背景图宽度。 + /// </summary> + public float imageWidth + { + get { return m_ImageWidth; } + set { if (PropertyUtil.SetStruct(ref m_ImageWidth, value)) SetComponentDirty(); } + } + + /// <summary> + /// the height of background image. + /// ||背景图高度。 + /// </summary> + public float imageHeight + { + get { return m_ImageHeight; } + set { if (PropertyUtil.SetStruct(ref m_ImageHeight, value)) SetComponentDirty(); } + } + + /// <summary> + /// Whether to use theme background color for component color when the background component is on. + /// ||当background组件开启时,是否自动使用主题背景色作为backgrounnd组件的颜色。当设置为false时,用imageColor作为颜色。 + /// </summary> + public bool autoColor + { + get { return m_AutoColor; } + set { if (PropertyUtil.SetStruct(ref m_AutoColor, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the border style of background. + /// ||背景边框样式。 + /// </summary> + public BorderStyle borderStyle + { + get { return m_BorderStyle; } + set { if (PropertyUtil.SetClass(ref m_BorderStyle, value)) SetComponentDirty(); } + } + + /// <summary> + /// the rect of background. + /// ||背景的矩形区域。 + /// </summary> + public Rect rect { get; set; } + + public override void SetDefaultValue() + { + m_Show = true; + m_Image = null; + m_ImageType = Image.Type.Sliced; + m_ImageColor = Color.white; + m_AutoColor = true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Background/Background.cs.meta b/Assets/XCharts/Runtime/Component/Background/Background.cs.meta new file mode 100644 index 0000000..e531dbc --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Background/Background.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 524f7df5241cc4379ae241a73d5b2ff2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs b/Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs new file mode 100644 index 0000000..b79ccd6 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs @@ -0,0 +1,56 @@ +using System; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class BackgroundHandler : MainComponentHandler<Background> + { + private readonly string s_BackgroundObjectName = "background"; + public override void InitComponent() + { + component.painter = chart.painter; + component.refreshComponent = delegate () + { + var backgroundObj = ChartHelper.AddObject(s_BackgroundObjectName, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + component.gameObject = backgroundObj; + backgroundObj.hideFlags = chart.chartHideFlags; + + var backgroundImage = ChartHelper.EnsureComponent<Image>(backgroundObj); + ChartHelper.UpdateRectTransform(backgroundObj, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); + backgroundImage.sprite = component.image; + backgroundImage.type = component.imageType; + backgroundImage.color = chart.theme.GetBackgroundColor(component); + + backgroundObj.transform.SetSiblingIndex(0); + backgroundObj.SetActive(component.show && component.image != null); + }; + component.refreshComponent(); + } + + public override void Update() + { + if (component.gameObject != null && component.gameObject.transform.GetSiblingIndex() != 0) + component.gameObject.transform.SetSiblingIndex(0); + } + + public override void DrawBase(VertexHelper vh) + { + if (!component.show) + return; + if (component.image != null) + return; + + var backgroundColor = chart.theme.GetBackgroundColor(component); + var borderWidth = component.borderStyle.GetRuntimeBorderWidth(); + var borderColor = component.borderStyle.GetRuntimeBorderColor(); + var cornerRadius = component.borderStyle.GetRuntimeCornerRadius(); + UGL.DrawRoundRectangleWithBorder(vh, chart.chartRect, backgroundColor, backgroundColor, cornerRadius, + borderWidth, borderColor, 0, 1f); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs.meta b/Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs.meta new file mode 100644 index 0000000..89ce4b6 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Background/BackgroundHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f1cb3d1a2aa224bbe84eef2681cf3df4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child.meta b/Assets/XCharts/Runtime/Component/Child.meta new file mode 100644 index 0000000..3f52f89 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20d31ade0390641698e6b846b4294b74 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/AreaStyle.cs b/Assets/XCharts/Runtime/Component/Child/AreaStyle.cs new file mode 100644 index 0000000..9cd9a18 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/AreaStyle.cs @@ -0,0 +1,132 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The style of area. + /// ||区域填充样式。 + /// </summary> + [System.Serializable] + public class AreaStyle : ChildComponent, ISerieComponent, ISerieDataComponent + { + /// <summary> + /// Origin position of area. + /// ||图形区域的起始位置。默认情况下,图形会从坐标轴轴线到数据间进行填充。如果需要填充的区域是坐标轴最大值到数据间,或者坐标轴最小值到数据间,则可以通过这个配置项进行设置。 + /// </summary> + public enum AreaOrigin + { + /// <summary> + /// to fill between axis line to data. + /// ||填充坐标轴轴线到数据间的区域。 + /// </summary> + Auto, + /// <summary> + /// to fill between min axis value (when not inverse) to data. + /// ||填充坐标轴底部到数据间的区域。 + /// </summary> + Start, + /// <summary> + /// to fill between max axis value (when not inverse) to data. + /// ||填充坐标轴顶部到数据间的区域。 + /// </summary> + End + } + + [SerializeField] private bool m_Show = true; + [SerializeField] private AreaStyle.AreaOrigin m_Origin; + [SerializeField] private Color32 m_Color; + [SerializeField] private Color32 m_ToColor; + [SerializeField][Range(0, 1)] private float m_Opacity = 0.6f; + [SerializeField][Since("v3.2.0")] private bool m_InnerFill; + [SerializeField][Since("v3.6.0")] private bool m_ToTop = true; + + /// <summary> + /// Set this to false to prevent the areafrom showing. + /// ||是否显示区域填充。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// the origin of area. + /// ||区域填充的起始位置。 + /// </summary> + public AreaOrigin origin + { + get { return m_Origin; } + set { if (PropertyUtil.SetStruct(ref m_Origin, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of area,default use serie color. + /// ||区域填充的颜色,如果toColor不是默认值,则表示渐变色的起点颜色。 + /// </summary> + public Color32 color + { + get { return m_Color; } + set { if (PropertyUtil.SetColor(ref m_Color, value)) SetVerticesDirty(); } + } + /// <summary> + /// Gradient color, start color to toColor. + /// ||渐变色的终点颜色。 + /// </summary> + public Color32 toColor + { + get { return m_ToColor; } + set { if (PropertyUtil.SetColor(ref m_ToColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// Opacity of the component. Supports value from 0 to 1, and the component will not be drawn when set to 0. + /// ||图形透明度。支持从 0 到 1 的数字,为 0 时不绘制该图形。 + /// </summary> + public float opacity + { + get { return m_Opacity; } + set { if (PropertyUtil.SetStruct(ref m_Opacity, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to fill only polygonal areas. Currently, only convex polygons are supported. + /// ||是否只填充多边形区域。目前只支持凸多边形。 + /// </summary> + public bool innerFill + { + get { return m_InnerFill; } + set { if (PropertyUtil.SetStruct(ref m_InnerFill, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to fill the gradient color to the top. The default is true, which means that the gradient color is filled to the top. + /// If it is false, the gradient color is filled to the actual position. + /// ||渐变色是到顶部还是到实际位置。默认为true到顶部。 + /// </summary> + public bool toTop + { + get { return m_ToTop; } + set { if (PropertyUtil.SetStruct(ref m_ToTop, value)) SetVerticesDirty(); } + } + + public Color32 GetColor() + { + if (m_Opacity == 1) + return m_Color; + + var color = m_Color; + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetColor(Color32 themeColor) + { + if (!ChartHelper.IsClearColor(color)) + { + return GetColor(); + } + else + { + var color = themeColor; + color.a = (byte) (color.a * opacity); + return color; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/AreaStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/AreaStyle.cs.meta new file mode 100644 index 0000000..53d0f68 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/AreaStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec0d95a9298bb4c159dcae36020beec9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs b/Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs new file mode 100644 index 0000000..24e74c1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs @@ -0,0 +1,92 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// </summary> + [Serializable] + public class ArrowStyle : ChildComponent + { + [SerializeField] private float m_Width = 10; + [SerializeField] private float m_Height = 15; + [SerializeField] private float m_Offset = 0; + [SerializeField] private float m_Dent = 3; + [SerializeField] private Color32 m_Color = Color.clear; + + /// <summary> + /// The widht of arrow. + /// ||箭头宽。 + /// </summary> + public float width + { + get { return m_Width; } + set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetVerticesDirty(); } + } + /// <summary> + /// The height of arrow. + /// ||箭头高。 + /// </summary> + public float height + { + get { return m_Height; } + set { if (PropertyUtil.SetStruct(ref m_Height, value)) SetVerticesDirty(); } + } + /// <summary> + /// The offset of arrow. + /// ||箭头偏移。 + /// </summary> + public float offset + { + get { return m_Offset; } + set { if (PropertyUtil.SetStruct(ref m_Offset, value)) SetVerticesDirty(); } + } + /// <summary> + /// The dent of arrow. + /// ||箭头的凹度。 + /// </summary> + public float dent + { + get { return m_Dent; } + set { if (PropertyUtil.SetStruct(ref m_Dent, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the color of arrow. + /// ||箭头颜色。 + /// </summary> + public Color32 color + { + get { return m_Color; } + set { if (PropertyUtil.SetColor(ref m_Color, value)) SetVerticesDirty(); } + } + + public ArrowStyle Clone() + { + var arrow = new ArrowStyle(); + arrow.width = width; + arrow.height = height; + arrow.offset = offset; + arrow.dent = dent; + arrow.color = color; + return arrow; + } + + public void Copy(ArrowStyle arrow) + { + width = arrow.width; + height = arrow.height; + offset = arrow.offset; + dent = arrow.dent; + color = arrow.color; + } + + public Color32 GetColor(Color32 defaultColor) + { + if (ChartHelper.IsClearColor(color)) + return defaultColor; + else + return color; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs.meta new file mode 100644 index 0000000..002958e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/ArrowStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2232b812c68f042d29c44863e38d0417 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/BaseLine.cs b/Assets/XCharts/Runtime/Component/Child/BaseLine.cs new file mode 100644 index 0000000..5f022ed --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/BaseLine.cs @@ -0,0 +1,82 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to base line. + /// ||线条基础配置。 + /// </summary> + [System.Serializable] + public class BaseLine : ChildComponent + { + [SerializeField] protected bool m_Show; + [SerializeField] protected LineStyle m_LineStyle = new LineStyle(); + + /// <summary> + /// Set this to false to prevent the axis line from showing. + /// ||是否显示坐标轴轴线。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// 线条样式 + /// </summary> + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (value != null) { m_LineStyle = value; SetVerticesDirty(); } } + } + + public static BaseLine defaultBaseLine + { + get + { + var axisLine = new BaseLine + { + m_Show = true, + m_LineStyle = new LineStyle() + }; + return axisLine; + } + } + + public BaseLine() + { + lineStyle = new LineStyle(); + } + + public BaseLine(bool show) : base() + { + m_Show = show; + } + + public void Copy(BaseLine axisLine) + { + show = axisLine.show; + lineStyle.Copy(axisLine.lineStyle); + } + + public LineStyle.Type GetType(LineStyle.Type themeType) + { + return lineStyle.GetType(themeType); + } + + public float GetWidth(float themeWidth) + { + return lineStyle.GetWidth(themeWidth); + } + + public float GetLength(float themeLength) + { + return lineStyle.GetLength(themeLength); + } + + public Color32 GetColor(Color32 themeColor) + { + return lineStyle.GetColor(themeColor); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/BaseLine.cs.meta b/Assets/XCharts/Runtime/Component/Child/BaseLine.cs.meta new file mode 100644 index 0000000..de0a9fa --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/BaseLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4c431b00ccffe4db4b61179b6df06eb2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/BorderStyle.cs b/Assets/XCharts/Runtime/Component/Child/BorderStyle.cs new file mode 100644 index 0000000..2a828ad --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/BorderStyle.cs @@ -0,0 +1,92 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// The style of border. + /// ||边框样式。 + /// </summary> + [System.Serializable] + [Since("v3.10.0")] + public class BorderStyle : ChildComponent + { + [SerializeField] private bool m_Show = false; + [SerializeField] private float m_BorderWidth; + [SerializeField] private Color32 m_BorderColor; + [SerializeField] private bool m_RoundedCorner = true; + [SerializeField] private float[] m_CornerRadius = new float[] { 0, 0, 0, 0 }; + + /// <summary> + /// whether the border is visible. + /// ||是否显示边框。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetAllDirty(); } + } + + /// <summary> + /// the width of border. + /// ||边框宽度。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetAllDirty(); } + } + + /// <summary> + /// the color of border. + /// ||边框颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetAllDirty(); } + } + + /// <summary> + /// whether the border is rounded corner. + /// ||是否显示圆角。 + /// </summary> + public bool roundedCorner + { + get { return m_RoundedCorner; } + set { if (PropertyUtil.SetStruct(ref m_RoundedCorner, value)) SetAllDirty(); } + } + + /// <summary> + /// The radius of rounded corner. Its unit is px. Use array to respectively specify the 4 corner radiuses((clockwise upper left, + /// upper right, bottom right and bottom left)). When is set to (1,1,1,1), all corners are rounded. + /// ||圆角半径。用数组分别指定4个圆角半径(顺时针左上,右上,右下,左下)。当为(1,1,1,1)时为全圆角。 + /// </summary> + public float[] cornerRadius + { + get { return m_CornerRadius; } + set { if (PropertyUtil.SetClass(ref m_CornerRadius, value)) SetAllDirty(); } + } + + public float GetRuntimeBorderWidth() + { + return m_Show ? m_BorderWidth : 0; + } + + public Color32 GetRuntimeBorderColor() + { + return m_Show ? m_BorderColor : ColorUtil.clearColor32; + } + + public float[] GetRuntimeCornerRadius() + { + return m_Show && roundedCorner ? m_CornerRadius : null; + } + + public bool IsCricle() + { + return roundedCorner && m_CornerRadius[0] == 1 && m_CornerRadius[1] == 1 && + m_CornerRadius[2] == 1 && m_CornerRadius[3] == 1; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/BorderStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/BorderStyle.cs.meta new file mode 100644 index 0000000..90570ca --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/BorderStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a756cb373aab4292b93a0597fc4e82c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/IconStyle.cs b/Assets/XCharts/Runtime/Component/Child/IconStyle.cs new file mode 100644 index 0000000..ede0afe --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/IconStyle.cs @@ -0,0 +1,118 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class IconStyle : ChildComponent + { + public enum Layer + { + /// <summary> + /// The icon is display under the label text. + /// 图标在标签文字下 + /// </summary> + UnderText, + /// <summary> + /// The icon is display above the label text. + /// 图标在标签文字上 + /// </summary> + AboveText + } + + [SerializeField] private bool m_Show = false; + [SerializeField] private Layer m_Layer; + [SerializeField] private Align m_Align = Align.Left; + [SerializeField] private Sprite m_Sprite; + [SerializeField] private Image.Type m_Type; + [SerializeField] private Color m_Color = Color.white; + [SerializeField] private float m_Width = 20; + [SerializeField] private float m_Height = 20; + [SerializeField] private Vector3 m_Offset; + [SerializeField] private bool m_AutoHideWhenLabelEmpty = false; + + public void Reset() + { + m_Show = false; + m_Layer = Layer.UnderText; + m_Sprite = null; + m_Color = Color.white; + m_Width = 20; + m_Height = 20; + m_Offset = Vector3.zero; + m_AutoHideWhenLabelEmpty = false; + } + /// <summary> + /// Whether the data icon is show. + /// ||是否显示图标。 + /// </summary> + public bool show { get { return m_Show; } set { m_Show = value; } } + /// <summary> + /// 显示在上层还是在下层。 + /// </summary> + public Layer layer { get { return m_Layer; } set { m_Layer = value; } } + /// <summary> + /// The image of icon. + /// ||图标的图片。 + /// </summary> + public Sprite sprite { get { return m_Sprite; } set { m_Sprite = value; } } + /// <summary> + /// How to display the icon. + /// ||图片的显示类型。 + /// </summary> + public Image.Type type { get { return m_Type; } set { m_Type = value; } } + /// <summary> + /// 图标颜色。 + /// </summary> + public Color color { get { return m_Color; } set { m_Color = value; } } + /// <summary> + /// 图标宽。 + /// </summary> + public float width { get { return m_Width; } set { m_Width = value; } } + /// <summary> + /// 图标高。 + /// </summary> + public float height { get { return m_Height; } set { m_Height = value; } } + /// <summary> + /// 图标偏移。 + /// </summary> + public Vector3 offset { get { return m_Offset; } set { m_Offset = value; } } + /// <summary> + /// 水平方向对齐方式。 + /// </summary> + public Align align { get { return m_Align; } set { m_Align = value; } } + /// <summary> + /// 当label内容为空时是否自动隐藏图标 + /// </summary> + public bool autoHideWhenLabelEmpty { get { return m_AutoHideWhenLabelEmpty; } set { m_AutoHideWhenLabelEmpty = value; } } + public IconStyle Clone() + { + var iconStyle = new IconStyle(); + iconStyle.show = show; + iconStyle.layer = layer; + iconStyle.sprite = sprite; + iconStyle.type = type; + iconStyle.color = color; + iconStyle.width = width; + iconStyle.height = height; + iconStyle.offset = offset; + iconStyle.align = align; + iconStyle.autoHideWhenLabelEmpty = autoHideWhenLabelEmpty; + return iconStyle; + } + + public void Copy(IconStyle iconStyle) + { + show = iconStyle.show; + layer = iconStyle.layer; + sprite = iconStyle.sprite; + type = iconStyle.type; + color = iconStyle.color; + width = iconStyle.width; + height = iconStyle.height; + offset = iconStyle.offset; + align = iconStyle.align; + autoHideWhenLabelEmpty = iconStyle.autoHideWhenLabelEmpty; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/IconStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/IconStyle.cs.meta new file mode 100644 index 0000000..53609da --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/IconStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82c4d360f7b5b4ee7845e9bbe611c8a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/ImageStyle.cs b/Assets/XCharts/Runtime/Component/Child/ImageStyle.cs new file mode 100644 index 0000000..645c2c2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/ImageStyle.cs @@ -0,0 +1,81 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class ImageStyle : ChildComponent, ISerieComponent, ISerieDataComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private Sprite m_Sprite; + [SerializeField] private Image.Type m_Type; + [SerializeField] private bool m_AutoColor; + [SerializeField] private Color m_Color = Color.clear; + [SerializeField] private float m_Width = 0; + [SerializeField] private float m_Height = 0; + + public void Reset() + { + m_Show = false; + m_Type = Image.Type.Simple; + m_Sprite = null; + m_AutoColor = false; + m_Color = Color.white; + m_Width = 0; + m_Height = 0; + } + + /// <summary> + /// Whether the data icon is show. + /// ||是否显示图标。 + /// </summary> + public bool show { get { return m_Show; } set { m_Show = value; } } + /// <summary> + /// The image of icon. + /// ||图标的图片。 + /// </summary> + public Sprite sprite { get { return m_Sprite; } set { m_Sprite = value; } } + /// <summary> + /// How to display the image. + /// ||图片的显示类型。 + /// </summary> + public Image.Type type { get { return m_Type; } set { m_Type = value; } } + /// <summary> + /// 是否自动颜色。 + /// </summary> + public bool autoColor { get { return m_AutoColor; } set { m_AutoColor = value; } } + /// <summary> + /// 图标颜色。 + /// </summary> + public Color color { get { return m_Color; } set { m_Color = value; } } + /// <summary> + /// 图标宽。 + /// </summary> + public float width { get { return m_Width; } set { m_Width = value; } } + /// <summary> + /// 图标高。 + /// </summary> + public float height { get { return m_Height; } set { m_Height = value; } } + public ImageStyle Clone() + { + var imageStyle = new ImageStyle(); + imageStyle.type = type; + imageStyle.sprite = sprite; + imageStyle.autoColor = autoColor; + imageStyle.color = color; + imageStyle.width = width; + imageStyle.height = height; + return imageStyle; + } + + public void Copy(ImageStyle imageStyle) + { + type = imageStyle.type; + sprite = imageStyle.sprite; + autoColor = imageStyle.autoColor; + color = imageStyle.color; + width = imageStyle.width; + height = imageStyle.height; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/ImageStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/ImageStyle.cs.meta new file mode 100644 index 0000000..8e9356e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/ImageStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5a76d1129783c4f55b0773da2eda9b67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/ItemStyle.cs b/Assets/XCharts/Runtime/Component/Child/ItemStyle.cs new file mode 100644 index 0000000..8fe5100 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/ItemStyle.cs @@ -0,0 +1,367 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// 图形样式。 + /// </summary> + [System.Serializable] + public class ItemStyle : ChildComponent, ISerieDataComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private Color32 m_Color; + [SerializeField] private Color32 m_Color0; + [SerializeField] private Color32 m_ToColor; + [SerializeField] private Color32 m_ToColor2; + [SerializeField][Since("v3.6.0")] private Color32 m_MarkColor; + [SerializeField] private Color32 m_BackgroundColor; + [SerializeField] private float m_BackgroundWidth; + [SerializeField] private Color32 m_CenterColor; + [SerializeField] private float m_CenterGap; + [SerializeField] private float m_BorderWidth = 0; + [SerializeField] private float m_BorderGap = 0; + [SerializeField] private Color32 m_BorderColor; + [SerializeField] private Color32 m_BorderColor0; + [SerializeField] private Color32 m_BorderToColor; + [SerializeField][Range(0, 1)] private float m_Opacity = 1; + [SerializeField] private string m_ItemMarker; + [SerializeField] private string m_ItemFormatter; + [SerializeField] private string m_NumericFormatter = ""; + [SerializeField] private float[] m_CornerRadius = new float[] { 0, 0, 0, 0 }; + + public void Reset() + { + m_Show = false; + m_Color = Color.clear; + m_Color0 = Color.clear; + m_ToColor = Color.clear; + m_ToColor2 = Color.clear; + m_MarkColor = Color.clear; + m_BackgroundColor = Color.clear; + m_BackgroundWidth = 0; + m_CenterColor = Color.clear; + m_CenterGap = 0; + m_BorderWidth = 0; + m_BorderGap = 0; + m_BorderColor = Color.clear; + m_BorderColor0 = Color.clear; + m_BorderToColor = Color.clear; + m_Opacity = 1; + m_ItemFormatter = null; + m_ItemMarker = null; + m_NumericFormatter = ""; + if (m_CornerRadius == null) + { + m_CornerRadius = new float[] { 0, 0, 0, 0 }; + } + else + { + for (int i = 0; i < m_CornerRadius.Length; i++) + m_CornerRadius[i] = 0; + } + } + + /// <summary> + /// 是否启用。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// 数据项颜色。 + /// </summary> + public Color32 color + { + get { return m_Color; } + set { if (PropertyUtil.SetColor(ref m_Color, value)) SetVerticesDirty(); } + } + /// <summary> + /// 数据项颜色。 + /// </summary> + public Color32 color0 + { + get { return m_Color0; } + set { if (PropertyUtil.SetColor(ref m_Color0, value)) SetVerticesDirty(); } + } + /// <summary> + /// Gradient color1. + /// ||渐变色的颜色1。 + /// </summary> + public Color32 toColor + { + get { return m_ToColor; } + set { if (PropertyUtil.SetColor(ref m_ToColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// Gradient color2.Only valid in line diagrams. + /// ||渐变色的颜色2。只在折线图中有效。 + /// </summary> + public Color32 toColor2 + { + get { return m_ToColor2; } + set { if (PropertyUtil.SetColor(ref m_ToColor2, value)) SetVerticesDirty(); } + } + /// <summary> + /// Serie's mark color. It is only used to display Legend and Tooltip, and does not affect the drawing color. The default value is clear. + /// ||Serie的标识颜色。仅用于Legend和Tooltip的展示,不影响绘制颜色,默认为clear。 + /// </summary> + public Color32 markColor + { + get { return m_MarkColor; } + set { if (PropertyUtil.SetStruct(ref m_MarkColor, value)) { SetAllDirty(); } } + } + /// <summary> + /// 数据项背景颜色。 + /// </summary> + public Color32 backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_BackgroundColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// 数据项背景宽度。 + /// </summary> + public float backgroundWidth + { + get { return m_BackgroundWidth; } + set { if (PropertyUtil.SetStruct(ref m_BackgroundWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// 中心区域颜色。 + /// </summary> + public Color32 centerColor + { + get { return m_CenterColor; } + set { if (PropertyUtil.SetColor(ref m_CenterColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// 中心区域间隙。 + /// </summary> + public float centerGap + { + get { return m_CenterGap; } + set { if (PropertyUtil.SetStruct(ref m_CenterGap, value)) SetVerticesDirty(); } + } + /// <summary> + /// 边框的颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// 边框的颜色。 + /// </summary> + public Color32 borderColor0 + { + get { return m_BorderColor0; } + set { if (PropertyUtil.SetColor(ref m_BorderColor0, value)) SetVerticesDirty(); } + } + /// <summary> + /// 边框的渐变色。 + /// </summary> + public Color32 borderToColor + { + get { return m_BorderToColor; } + set { if (PropertyUtil.SetColor(ref m_BorderToColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// 边框宽。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// 边框间隙。 + /// </summary> + public float borderGap + { + get { return m_BorderGap; } + set { if (PropertyUtil.SetStruct(ref m_BorderGap, value)) SetVerticesDirty(); } + } + /// <summary> + /// 透明度。支持从 0 到 1 的数字,为 0 时不绘制该图形。 + /// </summary> + public float opacity + { + get { return m_Opacity; } + set { if (PropertyUtil.SetStruct(ref m_Opacity, value)) SetVerticesDirty(); } + } + /// <summary> + /// 提示框单项的字符串模版格式器。具体配置参考`Tooltip`的`formatter` + /// </summary> + public string itemFormatter + { + get { return m_ItemFormatter; } + set { if (PropertyUtil.SetClass(ref m_ItemFormatter, value)) SetVerticesDirty(); } + } + /// <summary> + /// 提示框单项的字符标志。用在Tooltip中。 + /// </summary> + public string itemMarker + { + get { return m_ItemMarker; } + set { if (PropertyUtil.SetClass(ref m_ItemMarker, value)) SetVerticesDirty(); } + } + /// <summary> + /// Standard number and date format string. Used to format a Double value or a DateTime date as a string. + /// numericFormatter is used as an argument to either `Double.ToString ()` or `DateTime.ToString()`. <br /> + /// The number format uses the Axx format: A is a single-character format specifier that supports C currency, + /// D decimal, E exponent, F fixed-point number, G regular, N digit, P percentage, R round trip, and X hexadecimal. + /// xx is precision specification, from 0-99. E.g. F1, E2<br /> + /// Date format: Starts with `date`, which is used to format DateTime. Common date formats are: + /// yyyy year, MM month, dd day, HH hour, mm minute, ss second, fff millisecond. For example: date:yyyy-MM-dd HH:mm:ss<br /> + /// Time format: Starts with `time`, which is used to format TimeSpan. Common time formats are: + /// d day, HH hour, mm minute, ss second, fffffff fractional part. + /// Only the version of Unity2018 or later can support formatting, and the characters inside should be escaped. + /// For example: time:HH\:mm\:ss<br /> + /// number format reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings<br/> + /// date format reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings<br/> + /// Note: The date and time formats are only supported by 'v3.12.0' or later.<br/> + /// ||标准数字和日期格式字符串。用于将Double数值或DateTime日期格式化显示为字符串。numericFormatter用来作为Double.ToString()或DateTime.ToString()的参数。<br/> + /// 数字格式使用Axx的形式:A是格式说明符的单字符,支持C货币、D十进制、E指数、F定点数、G常规、N数字、P百分比、R往返、X十六进制的。xx是精度说明,从0-99。如:F1, E2<br/> + /// 日期格式:以`date`开头,用来格式化DateTime,常见格式有:yyyy年,MM月,dd日,HH时,mm分,ss秒,fff毫秒。如:date:yyyy-MM-dd HH:mm:ss<br/> + /// 时间格式:以`time`开头,用来格式化TimeSpan,常见格式有:d日,HH时,mm分,ss秒,fffffff小数部分。 + /// 需要Unity2018以上版本才支持格式化,并且里面的字符要转义。如:time:d\.HH\:mm\:ss<br/> + /// 数值格式化参考:https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/standard-numeric-format-strings <br/> + /// 日期格式化参考:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/standard-date-and-time-format-strings <br/> + /// 时间格式化参考:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/standard-timespan-format-strings <br/> + /// 注意:date和time格式需要`v3.12.0`以上版本才支持。 + /// </summary> + public string numericFormatter + { + get { return m_NumericFormatter; } + set { if (PropertyUtil.SetClass(ref m_NumericFormatter, value)) SetComponentDirty(); } + } + /// <summary> + /// The radius of rounded corner. Its unit is px. Use array to respectively specify the 4 corner radiuses((clockwise upper left, upper right, bottom right and bottom left)). + /// ||圆角半径。用数组分别指定4个圆角半径(顺时针左上,右上,右下,左下)。 + /// </summary> + public float[] cornerRadius + { + get { return m_CornerRadius; } + set { if (PropertyUtil.SetClass(ref m_CornerRadius, value, true)) SetVerticesDirty(); } + } + + public Color32 GetColor() + { + if (m_Opacity == 1 || m_Color.a == 0) + return m_Color; + + var color = m_Color; + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetToColor() + { + if (m_Opacity == 1 || m_ToColor.a == 0) + return m_ToColor; + + var color = m_ToColor; + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetColor0() + { + if (m_Opacity == 1 || m_Color0.a == 0) + return m_Color0; + + var color = m_Color0; + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetColor(Color32 defaultColor) + { + var color = ChartHelper.IsClearColor(m_Color) ? defaultColor : m_Color; + + if (m_Opacity == 1 || color.a == 0) + return color; + + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetColor0(Color32 defaultColor) + { + var color = ChartHelper.IsClearColor(m_Color0) ? defaultColor : m_Color0; + + if (m_Opacity == 1 || color.a == 0) + return color; + + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetBorderColor(Color32 defaultColor) + { + var color = ChartHelper.IsClearColor(m_BorderColor) ? defaultColor : m_BorderColor; + + if (m_Opacity == 1 || color.a == 0) + return color; + + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public Color32 GetBorderColor0(Color32 defaultColor) + { + var color = ChartHelper.IsClearColor(m_BorderColor0) ? defaultColor : m_BorderColor0; + + if (m_Opacity == 1 || color.a == 0) + return color; + + color.a = (byte) (color.a * m_Opacity); + return color; + } + + public bool IsNeedGradient() + { + return !ChartHelper.IsClearColor(m_ToColor) || !ChartHelper.IsClearColor(m_ToColor2); + } + + public Color32 GetGradientColor(float value, Color32 defaultColor) + { + if (!IsNeedGradient()) + return ChartConst.clearColor32; + + value = Mathf.Clamp01(value); + var startColor = ChartHelper.IsClearColor(m_Color) ? defaultColor : m_Color; + Color32 color; + + if (!ChartHelper.IsClearColor(m_ToColor2)) + { + if (value <= 0.5f) + color = Color32.Lerp(startColor, m_ToColor, 2 * value); + else + color = Color32.Lerp(m_ToColor, m_ToColor2, 2 * (value - 0.5f)); + } + else + { + color = Color32.Lerp(startColor, m_ToColor, value); + } + if (m_Opacity != 1) + { + color.a = (byte) (color.a * m_Opacity); + } + return color; + } + + public bool IsNeedCorner() + { + if (m_CornerRadius == null) return false; + foreach (var value in m_CornerRadius) + { + if (value != 0) return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/ItemStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/ItemStyle.cs.meta new file mode 100644 index 0000000..83e8a6f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/ItemStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ca9b30f9779c4a16b60cc21334828b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/LevelStyle.cs b/Assets/XCharts/Runtime/Component/Child/LevelStyle.cs new file mode 100644 index 0000000..3bb64cd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/LevelStyle.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class Level : ChildComponent + { + [SerializeField][Since("v3.10.0")] private int m_Depth = 0; + [SerializeField] private LabelStyle m_Label = new LabelStyle(); + [SerializeField] private LabelStyle m_UpperLabel = new LabelStyle(); + [SerializeField][Since("v3.10.0")] private LineStyle m_LineStyle = new LineStyle(); + [SerializeField] private ItemStyle m_ItemStyle = new ItemStyle(); + + /// <summary> + /// the depth of level. + /// ||层级深度。 + /// </summary> + public int depth { get { return m_Depth; } set { m_Depth = value; } } + /// <summary> + /// the label style of level. + /// ||文本标签样式。 + /// </summary> + public LabelStyle label { get { return m_Label; } } + /// <summary> + /// the upper label style of level. + /// ||上方的文本标签样式。 + /// </summary> + public LabelStyle upperLabel { get { return m_UpperLabel; } } + /// <summary> + /// the line style of level. + /// ||线条样式。 + /// </summary> + public LineStyle lineStyle { get { return m_LineStyle; } } + /// <summary> + /// the item style of level. + /// ||数据项样式。 + /// </summary> + public ItemStyle itemStyle { get { return m_ItemStyle; } } + } + + [System.Serializable] + public class LevelStyle : ChildComponent + { + [SerializeField] private bool m_Show = false; + [SerializeField] private List<Level> m_Levels = new List<Level>() { new Level() }; + + /// <summary> + /// 是否启用LevelStyle + /// </summary> + public bool show { get { return m_Show; } set { m_Show = value; } } + /// <summary> + /// 各层节点对应的配置。当enableLevels为true时生效,levels[0]对应的第一层的配置,levels[1]对应第二层,依次类推。当levels中没有对应层时用默认的设置。 + /// </summary> + public List<Level> levels { get { return m_Levels; } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/LevelStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/LevelStyle.cs.meta new file mode 100644 index 0000000..1193963 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/LevelStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3760e89d324d7413d95a2ac1d434a546 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/LineArrow.cs b/Assets/XCharts/Runtime/Component/Child/LineArrow.cs new file mode 100644 index 0000000..2c9a229 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/LineArrow.cs @@ -0,0 +1,63 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// </summary> + [Serializable] + public class LineArrow : ChildComponent, ISerieComponent + { + public enum Position + { + /// <summary> + /// 末端箭头 + /// </summary> + End, + /// <summary> + /// 头端箭头 + /// </summary> + Start + } + + [SerializeField] private bool m_Show; + [SerializeField] private Position m_Position; + [SerializeField] + private ArrowStyle m_Arrow = new ArrowStyle() + { + width = 10, + height = 15, + offset = 0, + dent = 3 + }; + + /// <summary> + /// Whether to show the arrow. + /// ||是否显示箭头。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// The position of arrow. + /// ||箭头位置。 + /// </summary> + public Position position + { + get { return m_Position; } + set { if (PropertyUtil.SetStruct(ref m_Position, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the arrow of line. + /// ||箭头。 + /// </summary> + public ArrowStyle arrow + { + get { return m_Arrow; } + set { if (PropertyUtil.SetClass(ref m_Arrow, value)) SetVerticesDirty(); } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/LineArrow.cs.meta b/Assets/XCharts/Runtime/Component/Child/LineArrow.cs.meta new file mode 100644 index 0000000..ee3425a --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/LineArrow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f2455acb3ba34409896bf03ddba593e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/LineStyle.cs b/Assets/XCharts/Runtime/Component/Child/LineStyle.cs new file mode 100644 index 0000000..de61bc6 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/LineStyle.cs @@ -0,0 +1,285 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The style of line. + /// ||线条样式。 + /// 注: 修改 lineStyle 中的颜色不会影响图例颜色,如果需要图例颜色和折线图颜色一致,需修改 itemStyle.color,线条颜色默认也会取该颜色。 + /// toColor,toColor2可设置水平方向的渐变,如需要设置垂直方向的渐变,可使用VisualMap。 + /// </summary> + [System.Serializable] + public class LineStyle : ChildComponent, ISerieDataComponent + { + /// <summary> + /// 线的类型。 + /// </summary> + public enum Type + { + /// <summary> + /// 实线 + /// </summary> + Solid, + /// <summary> + /// 虚线 + /// </summary> + Dashed, + /// <summary> + /// 点线 + /// </summary> + Dotted, + /// <summary> + /// 点划线 + /// </summary> + DashDot, + /// <summary> + /// 双点划线 + /// </summary> + DashDotDot, + None, + } + + [SerializeField] private bool m_Show = true; + [SerializeField] private Type m_Type = Type.Solid; + [SerializeField] private Color32 m_Color; + [SerializeField] private Color32 m_ToColor; + [SerializeField] private Color32 m_ToColor2; + [SerializeField] private float m_Width = 0; + [SerializeField] private float m_Length = 0; + [SerializeField][Range(0, 1)] private float m_Opacity = 1; + [SerializeField][Since("v3.8.1")] private float m_DashLength = 4; + [SerializeField][Since("v3.8.1")] private float m_DotLength = 2; + [SerializeField][Since("v3.8.1")] private float m_GapLength = 2; + + /// <summary> + /// Whether show line. + /// ||是否显示线条。当作为子组件,它的父组件有参数控制是否显示时,改参数无效。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// the type of line. + /// ||线的类型。 + /// </summary> + public Type type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of line, default use serie color. + /// ||线的颜色。 + /// </summary> + public Color32 color + { + get { return m_Color; } + set { if (PropertyUtil.SetColor(ref m_Color, value)) SetVerticesDirty(); } + } + /// <summary> + /// the middle color of line, default use serie color. + /// ||线的渐变颜色(需要水平方向渐变时)。 + /// </summary> + public Color32 toColor + { + get { return m_ToColor; } + set { if (PropertyUtil.SetColor(ref m_ToColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the end color of line, default use serie color. + /// ||线的渐变颜色2(需要水平方向三个渐变色的渐变时)。 + /// </summary> + public Color32 toColor2 + { + get { return m_ToColor2; } + set { if (PropertyUtil.SetColor(ref m_ToColor2, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of line. + /// ||线宽。 + /// </summary> + public float width + { + get { return m_Width; } + set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetVerticesDirty(); } + } + /// <summary> + /// the length of line. + /// ||线长。 + /// </summary> + public float length + { + get { return m_Length; } + set { if (PropertyUtil.SetStruct(ref m_Length, value)) SetVerticesDirty(); } + } + /// <summary> + /// Opacity of the line. Supports value from 0 to 1, and the line will not be drawn when set to 0. + /// ||线的透明度。支持从 0 到 1 的数字,为 0 时不绘制该图形。 + /// </summary> + public float opacity + { + get { return m_Opacity; } + set { if (PropertyUtil.SetStruct(ref m_Opacity, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the length of dash line. default value is 0, which means the length of dash line is 12 times of line width. + /// Represents a multiple of the number of segments in a line chart. + /// ||虚线的长度。默认0时为线条宽度的12倍。在折线图中代表分割段数的倍数。 + /// </summary> + public float dashLength + { + get { return m_DashLength; } + set { if (PropertyUtil.SetStruct(ref m_DashLength, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the length of dot line. default value is 0, which means the length of dot line is 2 times of line width. + /// Represents a multiple of the number of segments in a line chart. + /// ||点线的长度。默认0时为线条宽度的3倍。在折线图中代表分割段数的倍数。 + /// </summary> + public float dotLength + { + get { return m_DotLength; } + set { if (PropertyUtil.SetStruct(ref m_DotLength, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the length of gap line. default value is 0, which means the length of gap line is 3 times of line width. + /// Represents a multiple of the number of segments in a line chart. + /// ||点线的长度。默认0时为线条宽度的3倍。在折线图中代表分割段数的倍数。 + /// </summary> + public float gapLength + { + get { return m_GapLength; } + set { if (PropertyUtil.SetStruct(ref m_GapLength, value)) SetVerticesDirty(); } + } + + public LineStyle() + { } + + public LineStyle(float width) + { + this.width = width; + } + + public LineStyle(LineStyle.Type type) + { + this.type = type; + } + + public LineStyle(LineStyle.Type type, float width) + { + this.type = type; + this.width = width; + } + + public LineStyle Clone() + { + var lineStyle = new LineStyle(); + lineStyle.show = show; + lineStyle.type = type; + lineStyle.color = color; + lineStyle.toColor = toColor; + lineStyle.toColor2 = toColor2; + lineStyle.width = width; + lineStyle.opacity = opacity; + lineStyle.dashLength = dashLength; + lineStyle.dotLength = dotLength; + lineStyle.gapLength = gapLength; + return lineStyle; + } + + public void Copy(LineStyle lineStyle) + { + show = lineStyle.show; + type = lineStyle.type; + color = lineStyle.color; + toColor = lineStyle.toColor; + toColor2 = lineStyle.toColor2; + width = lineStyle.width; + opacity = lineStyle.opacity; + dashLength = lineStyle.dashLength; + dotLength = lineStyle.dotLength; + gapLength = lineStyle.gapLength; + } + + public bool IsNotSolidLine() + { + return type != Type.Solid && type != Type.None; + } + + public Color32 GetColor() + { + if (m_Opacity == 1) + return m_Color; + + var color = m_Color; + color.a = (byte)(color.a * m_Opacity); + return color; + } + + public bool IsNeedGradient() + { + return !ChartHelper.IsClearColor(m_ToColor) || !ChartHelper.IsClearColor(m_ToColor2); + } + + public Color32 GetGradientColor(float value, Color32 defaultColor) + { + var color = ChartConst.clearColor32; + if (!IsNeedGradient()) + return color; + + value = Mathf.Clamp01(value); + var startColor = ChartHelper.IsClearColor(m_Color) ? defaultColor : m_Color; + + if (!ChartHelper.IsClearColor(m_ToColor2)) + { + if (value <= 0.5f) + color = Color32.Lerp(startColor, m_ToColor, 2 * value); + else + color = Color32.Lerp(m_ToColor, m_ToColor2, 2 * (value - 0.5f)); + } + else + { + color = Color32.Lerp(startColor, m_ToColor, value); + } + if (m_Opacity != 1) + { + color.a = (byte)(color.a * m_Opacity); + } + return color; + } + + public Type GetType(Type themeType) + { + return type == Type.None ? themeType : type; + } + + public float GetWidth(float themeWidth) + { + return width == 0 ? themeWidth : width; + } + + public float GetLength(float themeLength) + { + return length == 0 ? themeLength : length; + } + + public Color32 GetColor(Color32 themeColor) + { + if (!ChartHelper.IsClearColor(color)) + { + return GetColor(); + } + else + { + var color = themeColor; + color.a = (byte)(color.a * opacity); + return color; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/LineStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/LineStyle.cs.meta new file mode 100644 index 0000000..2856b89 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/LineStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 092f08a2daa4b4013a72ffc3c9a18f85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/Location.cs b/Assets/XCharts/Runtime/Component/Child/Location.cs new file mode 100644 index 0000000..ea010f2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/Location.cs @@ -0,0 +1,423 @@ +using System; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + /// <summary> + /// Location type. Quick to set the general location. + /// ||位置类型。通过Align快速设置大体位置,再通过left,right,top,bottom微调具体位置。 + /// </summary> + [Serializable] + public class Location : ChildComponent, IPropertyChanged + { + /// <summary> + /// 对齐方式 + /// </summary> + public enum Align + { + TopLeft, + TopRight, + TopCenter, + BottomLeft, + BottomRight, + BottomCenter, + Center, + CenterLeft, + CenterRight + } + + [SerializeField] private Align m_Align = Align.TopCenter; + [SerializeField] private float m_Left; + [SerializeField] private float m_Right; + [SerializeField] private float m_Top; + [SerializeField] private float m_Bottom; + + private TextAnchor m_TextAlignment; +#if dUI_TextMeshPro + private TextAlignmentOptions m_TMPTextAlignment; +#endif + private Vector2 m_AnchorMin; + private Vector2 m_AnchorMax; + private Vector2 m_Pivot; + + /// <summary> + /// 对齐方式。 + /// </summary> + public Align align + { + get { return m_Align; } + set { if (PropertyUtil.SetStruct(ref m_Align, value)) { SetComponentDirty(); UpdateAlign(); } } + } + /// <summary> + /// Distance between component and the left side of the container. + /// ||离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) { SetComponentDirty(); UpdateAlign(); } } + } + /// <summary> + /// Distance between component and the left side of the container. + /// ||离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) { SetComponentDirty(); UpdateAlign(); } } + } + /// <summary> + /// Distance between component and the left side of the container. + /// ||离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) { SetComponentDirty(); UpdateAlign(); } } + } + /// <summary> + /// Distance between component and the left side of the container. + /// ||离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) { SetComponentDirty(); UpdateAlign(); } } + } + + /// <summary> + /// the anchor of text. + /// ||Location对应的Anchor锚点 + /// </summary> + public TextAnchor runtimeTextAlignment { get { return m_TextAlignment; } } + +#if dUI_TextMeshPro + public TextAlignmentOptions runtimeTMPTextAlignment { get { return m_TMPTextAlignment; } } +#endif + /// <summary> + /// the minimum achor. + /// ||Location对应的anchorMin。 + /// </summary> + public Vector2 runtimeAnchorMin { get { return m_AnchorMin; } } + /// <summary> + /// the maximun achor. + /// ||Location对应的anchorMax. + /// ||</summary> + public Vector2 runtimeAnchorMax { get { return m_AnchorMax; } } + /// <summary> + /// the povot. + /// ||Loation对应的中心点。 + /// </summary> + public Vector2 runtimePivot { get { return m_Pivot; } } + public float runtimeLeft { get; private set; } + public float runtimeRight { get; private set; } + public float runtimeBottom { get; private set; } + public float runtimeTop { get; private set; } + + public static Location defaultLeft + { + get + { + return new Location() + { + align = Align.CenterLeft, + left = 0.03f, + right = 0, + top = 0, + bottom = 0 + }; + } + } + + public static Location defaultRight + { + get + { + return new Location() + { + align = Align.CenterRight, + left = 0, + right = 0.03f, + top = 0, + bottom = 0 + }; + } + } + + public static Location defaultTop + { + get + { + return new Location() + { + align = Align.TopCenter, + left = 0, + right = 0, + top = 0.03f, + bottom = 0 + }; + } + } + + public static Location defaultBottom + { + get + { + return new Location() + { + align = Align.BottomCenter, + left = 0, + right = 0, + top = 0, + bottom = 0.03f + }; + } + } + + private void UpdateAlign() + { + switch (m_Align) + { + case Align.BottomCenter: + m_TextAlignment = TextAnchor.LowerCenter; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.Bottom; +#endif + m_AnchorMin = new Vector2(0.5f, 0); + m_AnchorMax = new Vector2(0.5f, 0); + m_Pivot = new Vector2(0.5f, 0); + break; + case Align.BottomLeft: + m_TextAlignment = TextAnchor.LowerLeft; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.BottomLeft; +#endif + m_AnchorMin = new Vector2(0, 0); + m_AnchorMax = new Vector2(0, 0); + m_Pivot = new Vector2(0, 0); + break; + case Align.BottomRight: + m_TextAlignment = TextAnchor.LowerRight; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.BottomRight; +#endif + m_AnchorMin = new Vector2(1, 0); + m_AnchorMax = new Vector2(1, 0); + m_Pivot = new Vector2(1, 0); + break; + case Align.Center: + m_TextAlignment = TextAnchor.MiddleCenter; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.Center; +#endif + m_AnchorMin = new Vector2(0.5f, 0.5f); + m_AnchorMax = new Vector2(0.5f, 0.5f); + m_Pivot = new Vector2(0.5f, 0.5f); + break; + case Align.CenterLeft: + m_TextAlignment = TextAnchor.MiddleLeft; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.Left; +#endif + m_AnchorMin = new Vector2(0, 0.5f); + m_AnchorMax = new Vector2(0, 0.5f); + m_Pivot = new Vector2(0, 0.5f); + break; + case Align.CenterRight: + m_TextAlignment = TextAnchor.MiddleRight; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.Right; +#endif + m_AnchorMin = new Vector2(1, 0.5f); + m_AnchorMax = new Vector2(1, 0.5f); + m_Pivot = new Vector2(1, 0.5f); + break; + case Align.TopCenter: + m_TextAlignment = TextAnchor.UpperCenter; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.Top; +#endif + m_AnchorMin = new Vector2(0.5f, 1); + m_AnchorMax = new Vector2(0.5f, 1); + m_Pivot = new Vector2(0.5f, 1); + break; + case Align.TopLeft: + m_TextAlignment = TextAnchor.UpperLeft; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.TopLeft; +#endif + m_AnchorMin = new Vector2(0, 1); + m_AnchorMax = new Vector2(0, 1); + m_Pivot = new Vector2(0, 1); + break; + case Align.TopRight: + m_TextAlignment = TextAnchor.UpperRight; +#if dUI_TextMeshPro + m_TMPTextAlignment = TextAlignmentOptions.TopRight; +#endif + m_AnchorMin = new Vector2(1, 1); + m_AnchorMax = new Vector2(1, 1); + m_Pivot = new Vector2(1, 1); + break; + default: + break; + } + } + + public bool IsBottom() + { + switch (m_Align) + { + case Align.BottomCenter: + case Align.BottomLeft: + case Align.BottomRight: + + return true; + default: + return false; + } + } + + public bool IsTop() + { + switch (m_Align) + { + case Align.TopCenter: + case Align.TopLeft: + case Align.TopRight: + return true; + default: + return false; + } + } + + public bool IsCenter() + { + switch (m_Align) + { + case Align.Center: + case Align.CenterLeft: + case Align.CenterRight: + return true; + default: + return false; + } + } + + public void UpdateRuntimeData(float chartWidth, float chartHeight) + { + runtimeLeft = left <= 1 ? left * chartWidth : left; + runtimeRight = right <= 1 ? right * chartWidth : right; + runtimeTop = top <= 1 ? top * chartHeight : top; + runtimeBottom = bottom <= 1 ? bottom * chartHeight : bottom; + } + + /// <summary> + /// 返回在坐标系中的具体位置 + /// </summary> + /// <param name="chartWidth"></param> + /// <param name="chartHeight"></param> + /// <returns></returns> + public Vector3 GetPosition(float chartWidth, float chartHeight) + { + UpdateRuntimeData(chartWidth, chartHeight); + switch (align) + { + case Align.BottomCenter: + return new Vector3(chartWidth / 2, runtimeBottom); + case Align.BottomLeft: + return new Vector3(runtimeLeft, runtimeBottom); + case Align.BottomRight: + return new Vector3(chartWidth - runtimeRight, runtimeBottom); + case Align.Center: + return new Vector3(chartWidth / 2, chartHeight / 2); + case Align.CenterLeft: + return new Vector3(runtimeLeft, chartHeight / 2); + case Align.CenterRight: + return new Vector3(chartWidth - runtimeRight, chartHeight / 2); + case Align.TopCenter: + return new Vector3(chartWidth / 2, chartHeight - runtimeTop); + case Align.TopLeft: + return new Vector3(runtimeLeft, chartHeight - runtimeTop); + case Align.TopRight: + return new Vector3(chartWidth - runtimeRight, chartHeight - runtimeTop); + default: + return Vector2.zero; + } + } + + public Rect GetRect(float graphX, float graphY, float graphWidth, float graphHeight, float rectWidth, float rectHeight) + { + UpdateRuntimeData(graphWidth, graphWidth); + + float x, y, width, height; + + width = rectWidth == 0 ? graphWidth - runtimeLeft - runtimeRight : rectWidth; + height = rectHeight == 0 ? graphHeight - runtimeBottom - runtimeTop : rectHeight; + + switch (align) + { + case Align.BottomCenter: + x = graphX + runtimeLeft + (graphWidth - runtimeLeft - runtimeRight - width) / 2; + y = graphY + runtimeBottom; + break; + + case Align.BottomLeft: + x = graphX + runtimeLeft; + y = graphY + runtimeBottom; + break; + + case Align.BottomRight: + x = graphX + graphWidth - runtimeRight - width; + y = graphY + runtimeBottom; + break; + + case Align.Center: + x = graphX + runtimeLeft + (graphWidth - runtimeLeft - runtimeRight - width) / 2; + y = graphY + runtimeBottom + (graphHeight - runtimeBottom - runtimeTop - height) / 2; + break; + + case Align.CenterLeft: + x = graphX + runtimeLeft; + y = graphY + runtimeBottom + (graphHeight - runtimeBottom - runtimeTop - height) / 2; + break; + + case Align.CenterRight: + x = graphX + graphWidth - runtimeRight - width; + y = graphY + runtimeBottom + (graphHeight - runtimeBottom - runtimeTop - height) / 2; + break; + + case Align.TopCenter: + x = graphX + runtimeLeft + (graphWidth - runtimeLeft - runtimeRight - width) / 2; + y = graphY + graphHeight - runtimeTop - height; + break; + + case Align.TopLeft: + x = graphX + runtimeLeft; + y = graphY + graphHeight - runtimeTop - height; + break; + + case Align.TopRight: + x = graphX + graphWidth - runtimeRight - width; + y = graphY + graphHeight - runtimeTop - height; + break; + + default: + return new Rect(0, 0, 0, 0); + } + return new Rect(x, y, width, height); + } + + + /// <summary> + /// 属性变更时更新textAnchor,minAnchor,maxAnchor,pivot + /// </summary> + public void OnChanged() + { + UpdateAlign(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/Location.cs.meta b/Assets/XCharts/Runtime/Component/Child/Location.cs.meta new file mode 100644 index 0000000..b209c7c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/Location.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7922ce86a6b0f4813a7f34e004b92e9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/MLValue.cs b/Assets/XCharts/Runtime/Component/Child/MLValue.cs new file mode 100644 index 0000000..42451c4 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/MLValue.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// 多样式数值。 + /// </summary> + [Since("v3.8.0")] + [System.Serializable] + public class MLValue : ChildComponent + { + /// <summary> + /// the type of value. + /// ||数值类型。 + /// </summary> + public enum Type + { + /// <summary> + /// Percent value form. + /// ||百分比形式。 + /// </summary> + Percent, + /// <summary> + /// Absolute value form. + /// ||绝对值形式。 + /// </summary> + Absolute, + /// <summary> + /// Extra value form. + /// ||额外形式。 + /// </summary> + Extra + } + [SerializeField] private Type m_Type; + [SerializeField] private float m_Value; + + public Type type { get { return m_Type; } set { m_Type = value; } } + public float value { get { return m_Value; } set { m_Value = value; } } + + public MLValue(float value) + { + m_Type = Type.Percent; + m_Value = value; + } + + public MLValue(Type type, float value) + { + m_Type = type; + m_Value = value; + } + + /// <summary> + /// Get the value by type. + /// ||根据类型获取值。 + /// </summary> + /// <param name="total">默认值</param> + /// <returns></returns> + public float GetValue(float total) + { + switch (m_Type) + { + case Type.Percent: + return m_Value * total; + case Type.Absolute: + return m_Value; + case Type.Extra: + return total + m_Value; + default: return 0; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/MLValue.cs.meta b/Assets/XCharts/Runtime/Component/Child/MLValue.cs.meta new file mode 100644 index 0000000..194febe --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/MLValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10f15d7e58cf24fa6a1986793ba2a36b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs b/Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs new file mode 100644 index 0000000..30e12a8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Marquee style. It can be used for the DataZoom component. + /// 选取框样式。可用于DataZoom组件。 + /// </summary> + [Since("v3.5.0")] + [System.Serializable] + public class MarqueeStyle : ChildComponent + { + [SerializeField][Since("v3.5.0")] private bool m_Apply = false; + [SerializeField][Since("v3.5.0")] private bool m_RealRect = false; + [SerializeField][Since("v3.5.0")] private AreaStyle m_AreaStyle = new AreaStyle(); + [SerializeField][Since("v3.5.0")] private LineStyle m_LineStyle = new LineStyle(); + + protected Action<DataZoom> m_OnStart; + protected Action<DataZoom> m_OnGoing; + protected Action<DataZoom> m_OnEnd; + + /// <summary> + /// The area style of marquee. + /// ||选取框区域填充样式。 + /// </summary> + public AreaStyle areaStyle { get { return m_AreaStyle; } set { m_AreaStyle = value; } } + /// <summary> + /// The line style of marquee border. + /// ||选取框区域边框样式。 + /// </summary> + public LineStyle lineStyle { get { return m_LineStyle; } set { m_LineStyle = value; } } + /// <summary> + /// Check whether the scope is applied to the DataZoom. + /// If this parameter is set to true, the range after the selection is complete is the DataZoom selection range. + /// ||选取框范围是否应用到DataZoom上。当为true时,框选结束后的范围即为DataZoom的选择范围。 + /// </summary> + public bool apply { get { return m_Apply; } set { m_Apply = value; } } + /// <summary> + /// Whether to select the actual box selection area. When true, + /// the actual range between the mouse's actual point and the end point is used as the box selection area. + /// ||是否选取实际框选区域。当为true时,以鼠标的其实点和结束点间的实际范围作为框选区域。 + /// </summary> + public bool realRect { get { return m_RealRect; } set { m_RealRect = value; } } + /// <summary> + /// Customize the callback to the start of the selection of the checkbox. + /// ||自定义选取框开始选取时的回调。 + /// </summary> + public Action<DataZoom> onStart { set { m_OnStart = value; } get { return m_OnStart; } } + /// <summary> + /// Custom checkboxes select ongoing callbacks. + /// ||自定义选取框选取进行时的回调。 + /// </summary> + public Action<DataZoom> onGoing { set { m_OnStart = value; } get { return m_OnStart; } } + /// <summary> + /// Customize the callback at the end of the selection. + /// ||自定义选取框结束选取时的回调。 + /// </summary> + public Action<DataZoom> onEnd { set { m_OnEnd = value; } get { return m_OnEnd; } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs.meta new file mode 100644 index 0000000..5c804f3 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/MarqueeStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: effa7d6629485469d91d41f896b9de8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/Padding.cs b/Assets/XCharts/Runtime/Component/Child/Padding.cs new file mode 100644 index 0000000..3752e68 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/Padding.cs @@ -0,0 +1,79 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// padding setting of item or text. + /// ||边距设置。 + /// </summary> + [Serializable] + public class Padding : ChildComponent + { + [SerializeField] protected bool m_Show = true; + [SerializeField] protected float m_Top = 0; + [SerializeField] protected float m_Right = 2f; + [SerializeField] protected float m_Left = 2f; + [SerializeField] protected float m_Bottom = 0; + + public Padding() { } + + public Padding(float top, float right, float bottom, float left) + { + SetPadding(top, right, bottom, left); + } + + public void SetPadding(float top, float right, float bottom, float left) + { + m_Top = top;; + m_Right = right; + m_Bottom = bottom; + m_Left = left; + } + /// <summary> + /// show padding. + /// 是否显示。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } + /// <summary> + /// padding of top. + /// ||顶部间距。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetComponentDirty(); } + } + /// <summary> + /// padding of right. + /// ||右部间距。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetComponentDirty(); } + } + /// <summary> + /// padding of bottom. + /// ||底部间距。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetComponentDirty(); } + } + /// <summary> + /// padding of left. + /// ||左边间距。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetComponentDirty(); } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/Padding.cs.meta b/Assets/XCharts/Runtime/Component/Child/Padding.cs.meta new file mode 100644 index 0000000..9b9851f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/Padding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4249907274734533ba65edb14987472 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs b/Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs new file mode 100644 index 0000000..f4fdb33 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// The way to get serie symbol size. + /// ||获取标记图形大小的方式。 + /// </summary> + public enum SymbolSizeType + { + /// <summary> + /// Specify constant for symbol size. + /// ||自定义大小。 + /// </summary> + Custom, + /// <summary> + /// Specify the dataIndex and dataScale to calculate symbol size. + /// ||通过 dataIndex 从数据中获取,再乘以一个比例系数 dataScale 。 + /// </summary> + FromData, + /// <summary> + /// Specify function for symbol size. + /// ||通过委托函数获取。 + /// </summary> + Function, + } + + /// <summary> + /// 系列数据项的标记的图形 + /// </summary> + [System.Serializable] + public class SerieSymbol : SymbolStyle, ISerieDataComponent + { + [SerializeField] private SymbolSizeType m_SizeType = SymbolSizeType.Custom; + [SerializeField] private int m_DataIndex = 1; + [SerializeField] private float m_DataScale = 1; + [SerializeField] private SymbolSizeFunction m_SizeFunction; + [SerializeField] private int m_StartIndex; + [SerializeField] private int m_Interval; + [SerializeField] private bool m_ForceShowLast = false; + [SerializeField] private bool m_Repeat = false; + [SerializeField][Since("v3.3.0")] private float m_MinSize = 0f; + [SerializeField][Since("v3.3.0")] private float m_MaxSize = 0f; + + public override void Reset() + { + base.Reset(); + m_SizeType = SymbolSizeType.Custom; + m_DataIndex = 1; + m_DataScale = 1; + m_SizeFunction = null; + m_StartIndex = 0; + m_Interval = 0; + m_ForceShowLast = false; + m_Repeat = false; + m_MinSize = 0f; + m_MaxSize = 0f; + } + + /// <summary> + /// the type of symbol size. + /// ||标记图形的大小获取方式。 + /// </summary> + public SymbolSizeType sizeType + { + get { return m_SizeType; } + set { if (PropertyUtil.SetStruct(ref m_SizeType, value)) SetVerticesDirty(); } + } + /// <summary> + /// whitch data index is when the sizeType assined as FromData. + /// ||当sizeType指定为FromData时,指定的数据源索引。 + /// </summary> + public int dataIndex + { + get { return m_DataIndex; } + set { if (PropertyUtil.SetStruct(ref m_DataIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// the scale of data when sizeType assined as FromData. + /// ||当sizeType指定为FromData时,指定的倍数系数。 + /// </summary> + public float dataScale + { + get { return m_DataScale; } + set { if (PropertyUtil.SetStruct(ref m_DataScale, value)) SetVerticesDirty(); } + } + /// <summary> + /// the function of size when sizeType assined as Function. + /// ||当sizeType指定为Function时,指定的委托函数。 + /// </summary> + public SymbolSizeFunction sizeFunction + { + get { return m_SizeFunction; } + set { if (PropertyUtil.SetClass(ref m_SizeFunction, value)) SetVerticesDirty(); } + } + /// <summary> + /// the index start to show symbol. + /// ||开始显示图形标记的索引。 + /// </summary> + public int startIndex + { + get { return m_StartIndex; } + set { if (PropertyUtil.SetStruct(ref m_StartIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// the interval of show symbol. + /// ||显示图形标记的间隔。0表示显示所有标签,1表示隔一个隔显示一个标签,以此类推。 + /// </summary> + public int interval + { + get { return m_Interval; } + set { if (PropertyUtil.SetStruct(ref m_Interval, value)) SetVerticesDirty(); } + } + /// <summary> + /// whether to show the last symbol. + /// ||是否强制显示最后一个图形标记。 + /// </summary> + public bool forceShowLast + { + get { return m_ForceShowLast; } + set { if (PropertyUtil.SetStruct(ref m_ForceShowLast, value)) SetVerticesDirty(); } + } + /// <summary> + /// 图形是否重复。 + /// </summary> + public bool repeat + { + get { return m_Repeat; } + set { if (PropertyUtil.SetStruct(ref m_Repeat, value)) SetAllDirty(); } + } + /// <summary> + /// Minimum symbol size. + /// ||图形最小尺寸。只在sizeType为SymbolSizeType.FromData时有效。 + /// </summary> + public float minSize + { + get { return m_MinSize; } + set { if (PropertyUtil.SetStruct(ref m_MinSize, value)) SetVerticesDirty(); } + } + /// <summary> + /// Maximum symbol size. + /// ||图形最大尺寸。只在sizeType为SymbolSizeType.FromData时有效。 + /// </summary> + public float maxSize + { + get { return m_MaxSize; } + set { if (PropertyUtil.SetStruct(ref m_MaxSize, value)) SetVerticesDirty(); } + } + + /// <summary> + /// 根据指定的sizeType获得标记的大小 + /// </summary> + public float GetSize(SerieData serieData, float themeSize) + { + switch (m_SizeType) + { + case SymbolSizeType.Custom: + return size == 0 ? themeSize : size; + case SymbolSizeType.FromData: + if (serieData != null && dataIndex >= 0 && dataIndex < serieData.data.Count) + { + var value = (float) serieData.data[dataIndex] * m_DataScale; + if (m_MinSize != 0 && value < m_MinSize) value = m_MinSize; + if (m_MaxSize != 0 && value > m_MaxSize) value = m_MaxSize; + return value; + } + else + { + return size == 0 ? themeSize : size; + } + case SymbolSizeType.Function: + if (sizeFunction != null) return sizeFunction(themeSize, serieData); + else return size == 0 ? themeSize : size; + default: + return size == 0 ? themeSize : size; + } + } + + public bool ShowSymbol(int dataIndex, int dataCount) + { + if (!show) + return false; + + if (dataIndex < startIndex) + return false; + + if (m_Interval <= 0) + return true; + + if (m_ForceShowLast && dataIndex == dataCount - 1) + return true; + + return (dataIndex - startIndex) % (m_Interval + 1) == 0; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs.meta b/Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs.meta new file mode 100644 index 0000000..ccbf533 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/SerieSymbl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd2852f4c46ae4dbd8c105e62dcce9a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/StageColor.cs b/Assets/XCharts/Runtime/Component/Child/StageColor.cs new file mode 100644 index 0000000..592d971 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/StageColor.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class StageColor : ChildComponent + { + [SerializeField] private float m_Percent; + [SerializeField] private Color32 m_Color; + /// <summary> + /// 结束位置百分比。 + /// </summary> + public float percent { get { return m_Percent; } set { m_Percent = value; } } + /// <summary> + /// 颜色。 + /// </summary> + public Color32 color { get { return m_Color; } set { m_Color = value; } } + + public StageColor(float percent, Color32 color) + { + m_Percent = percent; + m_Color = color; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/StageColor.cs.meta b/Assets/XCharts/Runtime/Component/Child/StageColor.cs.meta new file mode 100644 index 0000000..4be512d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/StageColor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d40f9dfbc90e744858784753e0d7109d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs b/Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs new file mode 100644 index 0000000..54e7a63 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs @@ -0,0 +1,230 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// the type of symbol. + /// ||标记图形的类型。 + /// </summary> + public enum SymbolType + { + /// <summary> + /// 不显示标记。 + /// </summary> + None, + /// <summary> + /// 自定义标记。 + /// </summary> + Custom, + /// <summary> + /// 圆形。 + /// </summary> + Circle, + /// <summary> + /// 空心圆。 + /// </summary> + EmptyCircle, + /// <summary> + /// 正方形。可通过设置`itemStyle`的`cornerRadius`变成圆角矩形。 + /// </summary> + Rect, + /// <summary> + /// 空心正方形。 + /// </summary> + EmptyRect, + /// <summary> + /// 三角形。 + /// </summary> + Triangle, + /// <summary> + /// 空心三角形。 + /// </summary> + EmptyTriangle, + /// <summary> + /// 菱形。 + /// </summary> + Diamond, + /// <summary> + /// 空心菱形。 + /// </summary> + EmptyDiamond, + /// <summary> + /// 箭头。 + /// </summary> + Arrow, + /// <summary> + /// 空心箭头。 + /// </summary> + EmptyArrow, + /// <summary> + /// 加号。 + /// </summary> + Plus, + /// <summary> + /// 减号。 + /// </summary> + Minus, + } + + /// <summary> + /// 系列数据项的标记的图形 + /// </summary> + [System.Serializable] + public class SymbolStyle : ChildComponent + { + [SerializeField] protected bool m_Show = true; + [SerializeField] protected SymbolType m_Type = SymbolType.EmptyCircle; + [SerializeField] protected float m_Size = 0f; + [SerializeField] protected float m_Gap = 0; + [SerializeField] protected float m_Width = 0f; + [SerializeField] protected float m_Height = 0f; + [SerializeField] protected Vector2 m_Offset = Vector2.zero; + [SerializeField] protected Sprite m_Image; + [SerializeField] protected Image.Type m_ImageType; + [SerializeField] protected Color32 m_Color; + [SerializeField][Since("v3.13.0")] protected float m_BorderWidth = 0f; + [SerializeField][Since("v3.13.0")] protected Color32 m_EmptyColor; + [SerializeField][Since("v3.13.0")] protected float m_Size2 = 0f; + + public virtual void Reset() + { + m_Show = false; + m_Type = SymbolType.EmptyCircle; + m_Size = 0f; + m_Size2 = 0f; + m_Gap = 0; + m_Width = 0f; + m_Height = 0f; + m_Offset = Vector2.zero; + m_Image = null; + m_ImageType = Image.Type.Simple; + } + + /// <summary> + /// Whether the symbol is showed. + /// ||是否显示标记。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetAllDirty(); } + } + /// <summary> + /// the type of symbol. + /// ||标记类型。 + /// </summary> + public SymbolType type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetVerticesDirty(); } + } + /// <summary> + /// the size of symbol. + /// ||标记的大小。 + /// </summary> + public float size + { + get { return m_Size; } + set { if (PropertyUtil.SetStruct(ref m_Size, value)) SetVerticesDirty(); } + } + /// <summary> + /// the size of symbol. + /// ||标记的大小。当为Rect时,size2表示高度。 + /// </summary> + public float size2 + { + get { return m_Size2; } + set { if (PropertyUtil.SetStruct(ref m_Size2, value)) SetVerticesDirty(); } + } + /// <summary> + /// the gap of symbol and line segment. + /// ||图形标记和线条的间隙距离。 + /// </summary> + public float gap + { + get { return m_Gap; } + set { if (PropertyUtil.SetStruct(ref m_Gap, value)) SetVerticesDirty(); } + } + /// <summary> + /// 图形的宽。 + /// </summary> + public float width + { + get { return m_Width; } + set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetAllDirty(); } + } + /// <summary> + /// 图形的高。 + /// </summary> + public float height + { + get { return m_Height; } + set { if (PropertyUtil.SetStruct(ref m_Height, value)) SetAllDirty(); } + } + /// <summary> + /// 自定义的标记图形。 + /// </summary> + public Sprite image + { + get { return m_Image; } + set { if (PropertyUtil.SetClass(ref m_Image, value)) SetAllDirty(); } + } + /// <summary> + /// the fill type of image. + /// ||图形填充类型。 + /// </summary> + public Image.Type imageType + { + get { return m_ImageType; } + set { if (PropertyUtil.SetStruct(ref m_ImageType, value)) SetAllDirty(); } + } + /// <summary> + /// 图形的偏移。 + /// </summary> + public Vector2 offset + { + get { return m_Offset; } + set { if (PropertyUtil.SetStruct(ref m_Offset, value)) SetAllDirty(); } + } + /// <summary> + /// 图形的颜色。 + /// </summary> + public Color32 color + { + get { return m_Color; } + set { if (PropertyUtil.SetStruct(ref m_Color, value)) SetAllDirty(); } + } + /// <summary> + /// the border width of symbol. + /// ||图形的边框宽度。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetAllDirty(); } + } + /// <summary> + /// the color of empty symbol. + /// ||空心图形的颜色。 + /// </summary> + public Color32 emptyColor + { + get { return m_EmptyColor; } + set { if (PropertyUtil.SetStruct(ref m_EmptyColor, value)) SetAllDirty(); } + } + public Vector3 offset3 { get { return new Vector3(m_Offset.x, m_Offset.y, 0); } } + private List<float> m_AnimationSize = new List<float>() { 0, 5, 10 }; + /// <summary> + /// the setting for effect scatter. + /// ||带有涟漪特效动画的散点图的动画参数。 + /// </summary> + public List<float> animationSize { get { return m_AnimationSize; } } + + public Color32 GetColor(Color32 defaultColor) + { + return ChartHelper.IsClearColor(m_Color) ? defaultColor : m_Color; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs.meta new file mode 100644 index 0000000..5913032 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/SymbolStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 837d37f4d6f614b38bef9f075a64b6dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/TextLimit.cs b/Assets/XCharts/Runtime/Component/Child/TextLimit.cs new file mode 100644 index 0000000..d01e492 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/TextLimit.cs @@ -0,0 +1,150 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// Text character limitation and adaptation component. When the length of the text exceeds the set length, + /// it is cropped and suffixes are appended to the end.Only valid in the category axis. + /// ||文本字符限制和自适应。当文本长度超过设定的长度时进行裁剪,并将后缀附加在最后。 + /// 只在类目轴中有效。 + /// </summary> + [Serializable] + public class TextLimit : ChildComponent + { + [SerializeField] private bool m_Enable = false; + [SerializeField] private float m_MaxWidth = 0; + [SerializeField] private float m_Gap = 1; + [SerializeField] private string m_Suffix = "..."; + + /// <summary> + /// Whether to enable text limit. + /// ||是否启用文本自适应。 + /// [default:true] + /// </summary> + public bool enable + { + get { return m_Enable; } + set { if (PropertyUtil.SetStruct(ref m_Enable, value)) SetComponentDirty(); } + } + /// <summary> + /// Set the maximum width. A default of 0 indicates automatic fetch; otherwise, custom. + /// ||Clipping occurs when the width of the text is greater than this value. + /// ||设定最大宽度。默认为0表示自动获取,否则表示自定义。当文本的宽度大于该值进行裁剪。 + /// </summary> + public float maxWidth + { + get { return m_MaxWidth; } + set { if (PropertyUtil.SetStruct(ref m_MaxWidth, value)) SetComponentDirty(); } + } + /// <summary> + /// White pixel distance at both ends. + /// ||两边留白像素距离。 + /// [default:10f] + /// </summary> + public float gap + { + get { return m_Gap; } + set { if (PropertyUtil.SetStruct(ref m_Gap, value)) SetComponentDirty(); } + } + /// <summary> + /// Suffixes when the length exceeds. + /// ||长度超出时的后缀。 + /// [default: "..."] + /// </summary> + public string suffix + { + get { return m_Suffix; } + set { if (PropertyUtil.SetClass(ref m_Suffix, value)) SetComponentDirty(); } + } + + private ChartText m_RelatedText; + private float m_RelatedTextWidth = 0; + + public TextLimit Clone() + { + var textLimit = new TextLimit(); + textLimit.enable = enable; + textLimit.maxWidth = maxWidth; + textLimit.gap = gap; + textLimit.suffix = suffix; + return textLimit; + } + + public void Copy(TextLimit textLimit) + { + enable = textLimit.enable; + maxWidth = textLimit.maxWidth; + gap = textLimit.gap; + suffix = textLimit.suffix; + } + + public void SetRelatedText(ChartText txt, float labelWidth) + { + m_RelatedText = txt; + m_RelatedTextWidth = labelWidth; + } + + public string GetLimitContent(string content) + { + float checkWidth = m_MaxWidth > 0 ? m_MaxWidth : m_RelatedTextWidth; + if (m_RelatedText == null || checkWidth <= 0) + { + return content; + } + else + { + if (m_Enable) + { + float len = m_RelatedText.GetPreferredWidth(content); + float suffixLen = m_RelatedText.GetPreferredWidth(suffix); + if (len >= checkWidth - m_Gap * 2) + { + return content.Substring(0, GetAdaptLength(content, suffixLen)) + suffix; + } + else + { + return content; + } + } + else + { + return content; + } + } + } + + private int GetAdaptLength(string content, float suffixLen) + { + int start = 0; + int middle = content.Length / 2; + int end = content.Length; + float checkWidth = m_MaxWidth > 0 ? m_MaxWidth : m_RelatedTextWidth; + + float limit = checkWidth - m_Gap * 2 - suffixLen; + if (limit < 0) + return 0; + + float len = 0; + while (len != limit && middle != start) + { + len = m_RelatedText.GetPreferredWidth(content.Substring(0, middle)); + if (len < limit) + { + start = middle; + } + else if (len > limit) + { + end = middle; + } + else + { + break; + } + middle = (start + end) / 2; + } + return middle; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/TextLimit.cs.meta b/Assets/XCharts/Runtime/Component/Child/TextLimit.cs.meta new file mode 100644 index 0000000..da76507 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/TextLimit.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f49509a5de044535b1dd3f192f7008c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/TextPadding.cs b/Assets/XCharts/Runtime/Component/Child/TextPadding.cs new file mode 100644 index 0000000..25a2b58 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/TextPadding.cs @@ -0,0 +1,20 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to text. + /// ||文本的内边距设置。 + /// </summary> + [Serializable] + public class TextPadding : Padding + { + public TextPadding() { } + + public TextPadding(float top, float right, float bottom, float left) + { + SetPadding(top, right, bottom, left); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/TextPadding.cs.meta b/Assets/XCharts/Runtime/Component/Child/TextPadding.cs.meta new file mode 100644 index 0000000..cff9472 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/TextPadding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 407bba126a0854199a4686b44cc9407e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Child/TextStyle.cs b/Assets/XCharts/Runtime/Component/Child/TextStyle.cs new file mode 100644 index 0000000..1f2c55e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/TextStyle.cs @@ -0,0 +1,231 @@ +using System; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + /// <summary> + /// Settings related to text. + /// ||文本的相关设置。 + /// </summary> + [Serializable] + public class TextStyle : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private Font m_Font; + [SerializeField] private bool m_AutoWrap = false; + [SerializeField] private bool m_AutoAlign = true; + [SerializeField] private float m_Rotate = 0; + [SerializeField] private bool m_AutoColor = false; + [SerializeField] private Color m_Color = Color.clear; + [SerializeField] private int m_FontSize = 0; + [SerializeField] private FontStyle m_FontStyle = FontStyle.Normal; + [SerializeField] private float m_LineSpacing = 1f; + [SerializeField] private TextAnchor m_Alignment = TextAnchor.MiddleCenter; +#if dUI_TextMeshPro + [SerializeField] private TMP_FontAsset m_TMPFont; + [SerializeField] private FontStyles m_TMPFontStyle = FontStyles.Normal; + [SerializeField][Since("v3.1.0")] private TMP_SpriteAsset m_TMPSpriteAsset; +#endif + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } + /// <summary> + /// Rotation of text. + /// ||文本的旋转。 + /// [default: `0f`] + /// </summary> + public float rotate + { + get { return m_Rotate; } + set { if (PropertyUtil.SetStruct(ref m_Rotate, value)) SetComponentDirty(); } + } + /// <summary> + /// 是否开启自动颜色。当开启时,会自动设置颜色。 + /// </summary> + public bool autoColor + { + get { return m_AutoColor; } + set { if (PropertyUtil.SetStruct(ref m_AutoColor, value)) SetAllDirty(); } + } + /// <summary> + /// the color of text. + /// ||文本的颜色。 + /// [default: `Color.clear`] + /// </summary> + public Color color + { + get { return m_Color; } + set { if (PropertyUtil.SetColor(ref m_Color, value)) SetComponentDirty(); } + } + /// <summary> + /// the font of text. When `null`, the theme's font is used by default. + /// ||文本字体。 + /// [default: null] + /// </summary> + public Font font + { + get { return m_Font; } + set { if (PropertyUtil.SetClass(ref m_Font, value)) SetComponentDirty(); } + } + /// <summary> + /// font size. + /// ||文本字体大小。 + /// [default: 18] + /// </summary> + public int fontSize + { + get { return m_FontSize; } + set { if (PropertyUtil.SetStruct(ref m_FontSize, value)) SetComponentDirty(); } + } + /// <summary> + /// font style. + /// ||文本字体的风格。 + /// [default: FontStyle.Normal] + /// </summary> + public FontStyle fontStyle + { + get { return m_FontStyle; } + set { if (PropertyUtil.SetStruct(ref m_FontStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// text line spacing. + /// ||行间距。 + /// [default: 1f] + /// </summary> + public float lineSpacing + { + get { return m_LineSpacing; } + set { if (PropertyUtil.SetStruct(ref m_LineSpacing, value)) SetComponentDirty(); } + } + /// <summary> + /// 是否自动换行。 + /// </summary> + public bool autoWrap + { + get { return m_AutoWrap; } + set { if (PropertyUtil.SetStruct(ref m_AutoWrap, value)) SetComponentDirty(); } + } + /// <summary> + /// 文本是否让系统自动选对齐方式。为false时才会用alignment。 + /// </summary> + public bool autoAlign + { + get { return m_AutoAlign; } + set { if (PropertyUtil.SetStruct(ref m_AutoAlign, value)) SetComponentDirty(); } + } + /// <summary> + /// 对齐方式。 + /// </summary> + public TextAnchor alignment + { + get { return m_Alignment; } + set { if (PropertyUtil.SetStruct(ref m_Alignment, value)) SetComponentDirty(); } + } +#if dUI_TextMeshPro + /// <summary> + /// the font of textmeshpro. + /// ||TextMeshPro字体。 + /// </summary> + public TMP_FontAsset tmpFont + { + get { return m_TMPFont; } + set { if (PropertyUtil.SetClass(ref m_TMPFont, value)) SetComponentDirty(); } + } + /// <summary> + /// the font style of TextMeshPro. + /// ||TextMeshPro字体类型。 + /// </summary> + public FontStyles tmpFontStyle + { + get { return m_TMPFontStyle; } + set { if (PropertyUtil.SetStruct(ref m_TMPFontStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// the sprite asset of TextMeshPro. + /// ||TextMeshPro的Sprite Asset。 + /// </summary> + public TMP_SpriteAsset tmpSpriteAsset + { + get { return m_TMPSpriteAsset; } + set { if (PropertyUtil.SetClass(ref m_TMPSpriteAsset, value)) SetComponentDirty(); } + } +#endif + + public TextStyle() { } + + public TextStyle(int fontSize) + { + this.fontSize = fontSize; + } + + public TextStyle(int fontSize, FontStyle fontStyle) + { + this.fontSize = fontSize; + this.fontStyle = fontStyle; + } + + public TextStyle(int fontSize, FontStyle fontStyle, Color color) + { + this.fontSize = fontSize; + this.fontStyle = fontStyle; + this.color = color; + } + + public TextStyle(int fontSize, FontStyle fontStyle, Color color, int rorate) + { + this.fontSize = fontSize; + this.fontStyle = fontStyle; + this.color = color; + this.rotate = rotate; + } + + public void Copy(TextStyle textStyle) + { + font = textStyle.font; + rotate = textStyle.rotate; + color = textStyle.color; + fontSize = textStyle.fontSize; + fontStyle = textStyle.fontStyle; + lineSpacing = textStyle.lineSpacing; + alignment = textStyle.alignment; + autoWrap = textStyle.autoWrap; + autoAlign = textStyle.autoAlign; +#if dUI_TextMeshPro + m_TMPFont = textStyle.tmpFont; + m_TMPFontStyle = textStyle.tmpFontStyle; + m_TMPSpriteAsset = textStyle.tmpSpriteAsset; +#endif + } + + public void UpdateAlignmentByLocation(Location location) + { + m_Alignment = location.runtimeTextAlignment; + } + + public Color GetColor(Color defaultColor) + { + if (ChartHelper.IsClearColor(color)) + return defaultColor; + else + return color; + } + + public int GetFontSize(ComponentTheme defaultTheme) + { + if (fontSize == 0) + return defaultTheme.fontSize; + else + return fontSize; + } + + public TextAnchor GetAlignment(TextAnchor defaultAlignment) + { + return m_AutoAlign ? defaultAlignment : alignment; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Child/TextStyle.cs.meta b/Assets/XCharts/Runtime/Component/Child/TextStyle.cs.meta new file mode 100644 index 0000000..c429b3b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Child/TextStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8f6b652968894ab195666501dda672c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Comment.meta b/Assets/XCharts/Runtime/Component/Comment.meta new file mode 100644 index 0000000..27242bf --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81fe767917cd3492a9f587f5d5e3a037 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Comment/Comment.cs b/Assets/XCharts/Runtime/Component/Comment/Comment.cs new file mode 100644 index 0000000..dffabb5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/Comment.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// comment of chart. + /// ||图表注解组件。 + /// </summary> + [Serializable] + [ComponentHandler(typeof(CommentHander), true)] + public class Comment : MainComponent, IPropertyChanged + { + [SerializeField] private bool m_Show = true; + [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle(); + [SerializeField] private CommentMarkStyle m_MarkStyle; + [SerializeField] private List<CommentItem> m_Items = new List<CommentItem>() { new CommentItem() }; + + /// <summary> + /// Set this to false to prevent the comment from showing. + /// ||是否显示注解组件。 + /// </summary> + public bool show { get { return m_Show; } set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } } + /// <summary> + /// The items of comment. + /// ||注解项。每个注解组件可以设置多个注解项。 + /// </summary> + public List<CommentItem> items { get { return m_Items; } set { m_Items = value; SetComponentDirty(); } } + /// <summary> + /// The text style of all comments. + /// ||所有组件的文本样式。 + /// </summary> + public LabelStyle labelStyle + { + get { return m_LabelStyle; } + set { if (PropertyUtil.SetClass(ref m_LabelStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// The text style of all comments. + /// ||所有组件的文本样式。 + /// </summary> + public CommentMarkStyle markStyle + { + get { return m_MarkStyle; } + set { if (PropertyUtil.SetClass(ref m_MarkStyle, value)) SetVerticesDirty(); } + } + + public LabelStyle GetLabelStyle(int index) + { + if (index >= 0 && index < items.Count) + { + var labelStyle = items[index].labelStyle; + if (labelStyle.show) return labelStyle; + } + return m_LabelStyle; + } + + public CommentMarkStyle GetMarkStyle(int index) + { + if (index >= 0 && index < items.Count) + { + var markStyle = items[index].markStyle; + if (markStyle.show) return markStyle; + } + return m_MarkStyle; + } + + /// <summary> + /// Callback handling when parameters change. + /// ||参数变更时的回调处理。 + /// </summary> + public void OnChanged() + { + foreach (var item in items) + { + item.location.OnChanged(); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Comment/Comment.cs.meta b/Assets/XCharts/Runtime/Component/Comment/Comment.cs.meta new file mode 100644 index 0000000..3a9af59 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/Comment.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec99dd6b13a3b4e9789d007f23ffa499 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Comment/CommentHander.cs b/Assets/XCharts/Runtime/Component/Comment/CommentHander.cs new file mode 100644 index 0000000..7520aa2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/CommentHander.cs @@ -0,0 +1,75 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class CommentHander : MainComponentHandler<Comment> + { + private static readonly string s_CommentObjectName = "comment"; + + public override void InitComponent() + { + var comment = component; + comment.OnChanged(); + comment.painter = null; + comment.refreshComponent = delegate() + { + var objName = ChartCached.GetComponentObjectName(comment); + var commentObj = ChartHelper.AddObject(objName, + chart.transform, + chart.chartMinAnchor, + chart.chartMaxAnchor, + chart.chartPivot, + chart.chartSizeDelta, -1, chart.childrenNodeNames); + + commentObj.SetActive(comment.show); + commentObj.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(commentObj); + for (int i = 0; i < comment.items.Count; i++) + { + var item = comment.items[i]; + var labelStyle = comment.GetLabelStyle(i); + var labelPos = chart.chartPosition + item.location.GetPosition(chart.chartWidth, chart.chartHeight); + var label = ChartHelper.AddChartLabel(s_CommentObjectName + i, commentObj.transform, labelStyle, chart.theme.common, + GetContent(item), Color.clear, TextAnchor.MiddleCenter); + label.SetActive(comment.show && item.show, true); + label.SetPosition(labelPos); + label.text.SetLocalPosition(labelStyle.offset); + item.labelObject = label; + } + }; + comment.refreshComponent(); + } + + private string GetContent(CommentItem item) + { + if (item.content.IndexOf("{") >= 0) + { + var content = item.content; + FormatterHelper.ReplaceContent(ref content, -1, item.labelStyle.numericFormatter, null, chart); + return content; + } + else + { + return item.content; + } + } + + public override void DrawUpper(VertexHelper vh) + { + for (int i = 0; i < component.items.Count; i++) + { + var item = component.items[i]; + var markStyle = component.GetMarkStyle(i); + if (markStyle == null || !markStyle.show) continue; + var color = ChartHelper.IsClearColor(markStyle.lineStyle.color) ? + chart.theme.axis.splitLineColor : + markStyle.lineStyle.color; + var width = markStyle.lineStyle.width == 0 ? 1 : markStyle.lineStyle.width; + UGL.DrawBorder(vh, item.markRect, width, color); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Comment/CommentHander.cs.meta b/Assets/XCharts/Runtime/Component/Comment/CommentHander.cs.meta new file mode 100644 index 0000000..a1e30c8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/CommentHander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45362c4eed0e54d2880f2ed359ce9385 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Comment/CommentItem.cs b/Assets/XCharts/Runtime/Component/Comment/CommentItem.cs new file mode 100644 index 0000000..1036328 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/CommentItem.cs @@ -0,0 +1,73 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// comment of chart. + /// ||注解项。 + /// </summary> + [Serializable] + public class CommentItem : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private string m_Content = "comment"; + [SerializeField] private Rect m_MarkRect; + [SerializeField] private CommentMarkStyle m_MarkStyle = new CommentMarkStyle() { show = false }; + [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle() { show = false }; + [SerializeField][Since("v3.5.0")] private Location m_Location = new Location() { align = Location.Align.TopLeft, top = 0.125f }; + + public ChartLabel labelObject { get; set; } + + + /// <summary> + /// Set this to false to prevent this comment item from showing. + /// ||是否显示当前注解项。 + /// </summary> + public bool show { get { return m_Show; } set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } } + /// <summary> + /// content of comment. + /// ||注解的文本内容。支持模板参数,可以参考Tooltip的itemFormatter。 + /// </summary> + public string content + { + get { return m_Content; } + set + { + if (PropertyUtil.SetClass(ref m_Content, value)) + { + if (labelObject != null) labelObject.SetText(value); + else SetComponentDirty(); + } + } + } + /// <summary> + /// the mark rect of comment. + /// ||注解区域。 + /// </summary> + public Rect markRect { get { return m_MarkRect; } set { if (PropertyUtil.SetStruct(ref m_MarkRect, value)) SetVerticesDirty(); } } + /// <summary> + /// the mark rect style. + /// ||注解标记区域样式。 + /// </summary> + public CommentMarkStyle markStyle { get { return m_MarkStyle; } set { if (PropertyUtil.SetClass(ref m_MarkStyle, value)) SetVerticesDirty(); } } + /// <summary> + /// The text style of all comments. + /// ||注解项的文本样式。 + /// </summary> + public LabelStyle labelStyle + { + get { return m_LabelStyle; } + set { if (PropertyUtil.SetClass(ref m_LabelStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// The location of comment. + /// ||Comment显示的位置。 + /// </summary> + public Location location + { + get { return m_Location; } + set { if (PropertyUtil.SetClass(ref m_Location, value)) SetComponentDirty(); } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Comment/CommentItem.cs.meta b/Assets/XCharts/Runtime/Component/Comment/CommentItem.cs.meta new file mode 100644 index 0000000..29b3cd2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/CommentItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f082815b255e546019b6b43ac20bf4cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs b/Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs new file mode 100644 index 0000000..b2bc76b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs @@ -0,0 +1,27 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the comment mark style. + /// ||注解项区域样式。 + /// </summary> + [Serializable] + public class CommentMarkStyle : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private LineStyle m_LineStyle; + + /// <summary> + /// Set this to false to prevent this comment item from showing. + /// ||是否显示当前注解项。 + /// </summary> + public bool show { get { return m_Show; } set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } } + /// <summary> + /// line style of comment mark area. + /// ||线条样式。 + /// </summary> + public LineStyle lineStyle { get { return m_LineStyle; } set { if (PropertyUtil.SetClass(ref m_LineStyle, value)) SetVerticesDirty(); } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs.meta b/Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs.meta new file mode 100644 index 0000000..9e4c5dd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Comment/CommentMarkStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 764734b787d72455782bf75bb38e465e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/DataZoom.meta b/Assets/XCharts/Runtime/Component/DataZoom.meta new file mode 100644 index 0000000..c51556f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a017b088954fb499eae363f4182fbeed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs b/Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs new file mode 100644 index 0000000..fd2f60f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs @@ -0,0 +1,754 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// DataZoom component is used for zooming a specific area, + /// which enables user to investigate data in detail, + /// or get an overview of the data, or get rid of outlier points. + /// ||DataZoom 组件 用于区域缩放,从而能自由关注细节的数据信息,或者概览数据整体,或者去除离群点的影响。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(DataZoomHandler), true)] + public class DataZoom : MainComponent, IUpdateRuntimeData + { + /// <summary> + /// Generally dataZoom component zoom or roam coordinate system through data filtering + /// and set the windows of axes internally. + /// Its behaviours vary according to filtering mode settings. + /// ||dataZoom 的运行原理是通过 数据过滤 来达到 数据窗口缩放 的效果。数据过滤模式的设置不同,效果也不同。 + /// </summary> + public enum FilterMode + { + /// <summary> + /// data that outside the window will be filtered, which may lead to some changes of windows of other axes. + /// For each data item, it will be filtered if one of the relevant dimensions is out of the window. + /// ||当前数据窗口外的数据,被 过滤掉。即 会 影响其他轴的数据范围。每个数据项,只要有一个维度在数据窗口外,整个数据项就会被过滤掉。 + /// </summary> + Filter, + /// <summary> + /// data that outside the window will be filtered, which may lead to some changes of windows of other axes. + /// For each data item, it will be filtered only if all of the relevant dimensions are out of the same side of the window. + /// ||当前数据窗口外的数据,被 过滤掉。即 会 影响其他轴的数据范围。每个数据项,只有当全部维度都在数据窗口同侧外部,整个数据项才会被过滤掉。 + /// </summary> + WeakFilter, + /// <summary> + /// data that outside the window will be set to NaN, which will not lead to changes of windows of other axes. + /// ||当前数据窗口外的数据,被 设置为空。即 不会 影响其他轴的数据范围。 + /// </summary> + Empty, + /// <summary> + /// Do not filter data. + /// ||不过滤数据,只改变数轴范围。 + /// </summary> + None + } + /// <summary> + /// The value type of start and end.取值类型 + /// </summary> + public enum RangeMode + { + //Value, + /// <summary> + /// percent value. + /// ||百分比。 + /// </summary> + Percent + } + + [SerializeField] private bool m_Enable = true; + [SerializeField] private FilterMode m_FilterMode; + [SerializeField] private List<int> m_XAxisIndexs = new List<int>() { 0 }; + [SerializeField] private List<int> m_YAxisIndexs = new List<int>() { }; + [SerializeField] private bool m_SupportInside; + [SerializeField] private bool m_SupportInsideScroll = true; + [SerializeField] private bool m_SupportInsideDrag = true; + [SerializeField] private bool m_SupportSlider; + [SerializeField] private bool m_SupportMarquee; + [SerializeField] private bool m_ShowDataShadow; + [SerializeField] private bool m_ShowDetail; + [SerializeField] private bool m_ZoomLock; + //[SerializeField] private bool m_Realtime; + [SerializeField] protected Color32 m_FillerColor; + [SerializeField] protected Color32 m_BorderColor; + [SerializeField] protected float m_BorderWidth; + [SerializeField] protected Color32 m_BackgroundColor; + [SerializeField] private float m_Left; + [SerializeField] private float m_Right; + [SerializeField] private float m_Top; + [SerializeField] private float m_Bottom; + [SerializeField] private RangeMode m_RangeMode; + [SerializeField] private float m_Start; + [SerializeField] private float m_End; + [SerializeField] private int m_MinShowNum = 2; + [Range(1f, 20f)] + [SerializeField] private float m_ScrollSensitivity = 1.1f; + [SerializeField] private Orient m_Orient = Orient.Horizonal; + [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle(); + [SerializeField] private LineStyle m_LineStyle = new LineStyle(LineStyle.Type.Solid); + [SerializeField] private AreaStyle m_AreaStyle = new AreaStyle(); + [SerializeField][Since("v3.5.0")] private MarqueeStyle m_MarqueeStyle = new MarqueeStyle(); + [SerializeField][Since("v3.6.0")] private bool m_StartLock; + [SerializeField][Since("v3.6.0")] private bool m_EndLock; + + public DataZoomContext context = new DataZoomContext(); + private CustomDataZoomStartEndFunction m_StartEndFunction; + + /// <summary> + /// Whether to show dataZoom. + /// ||是否显示缩放区域。 + /// </summary> + public bool enable + { + get { return m_Enable; } + set { if (PropertyUtil.SetStruct(ref m_Enable, value)) SetVerticesDirty(); } + } + /// <summary> + /// The mode of data filter. + /// ||数据过滤类型。 + /// </summary> + public FilterMode filterMode + { + get { return m_FilterMode; } + set { if (PropertyUtil.SetStruct(ref m_FilterMode, value)) SetVerticesDirty(); } + } + /// <summary> + /// Specify which xAxis is controlled by the dataZoom. + /// ||控制的 x 轴索引列表。 + /// </summary> + public List<int> xAxisIndexs + { + get { return m_XAxisIndexs; } + set { if (PropertyUtil.SetClass(ref m_XAxisIndexs, value)) SetVerticesDirty(); } + } + /// <summary> + /// Specify which yAxis is controlled by the dataZoom. + /// ||控制的 y 轴索引列表。 + /// </summary> + public List<int> yAxisIndexs + { + get { return m_YAxisIndexs; } + set { if (PropertyUtil.SetClass(ref m_YAxisIndexs, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether built-in support is supported. + /// Built into the coordinate system to allow the user to zoom in and out of the coordinate system by mouse dragging, + /// mouse wheel, finger swiping (on the touch screen). + /// ||是否支持内置。内置于坐标系中,使用户可以在坐标系上通过鼠标拖拽、鼠标滚轮、手指滑动(触屏上)来缩放或漫游坐标系。 + /// </summary> + public bool supportInside + { + get { return m_SupportInside; } + set { if (PropertyUtil.SetStruct(ref m_SupportInside, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether inside scrolling is supported. + /// ||是否支持坐标系内滚动 + /// </summary> + public bool supportInsideScroll + { + get { return m_SupportInsideScroll; } + set { if (PropertyUtil.SetStruct(ref m_SupportInsideScroll, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether insde drag is supported. + /// ||是否支持坐标系内拖拽 + /// </summary> + public bool supportInsideDrag + { + get { return m_SupportInsideDrag; } + set { if (PropertyUtil.SetStruct(ref m_SupportInsideDrag, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether a slider is supported. There are separate sliders on which the user zooms or roams. + /// ||是否支持滑动条。有单独的滑动条,用户在滑动条上进行缩放或漫游。 + /// </summary> + public bool supportSlider + { + get { return m_SupportSlider; } + set { if (PropertyUtil.SetStruct(ref m_SupportSlider, value)) SetVerticesDirty(); } + } + /// <summary> + /// Supported Box Selected. Provides a marquee for scaling the data area. + /// ||是否支持框选。提供一个选框进行数据区域缩放。 + /// </summary> + public bool supportMarquee + { + get { return m_SupportMarquee; } + set { if (PropertyUtil.SetStruct(ref m_SupportMarquee, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show data shadow, to indicate the data tendency in brief. + /// ||是否显示数据阴影。数据阴影可以简单地反应数据走势。 + /// </summary> + public bool showDataShadow + { + get { return m_ShowDataShadow; } + set { if (PropertyUtil.SetStruct(ref m_ShowDataShadow, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show detail, that is, show the detailed data information when dragging. + /// ||是否显示detail,即拖拽时候显示详细数值信息。 + /// </summary> + public bool showDetail + { + get { return m_ShowDetail; } + set { if (PropertyUtil.SetStruct(ref m_ShowDetail, value)) SetVerticesDirty(); } + } + /// <summary> + /// Specify whether to lock the size of window (selected area). + /// ||是否锁定选择区域(或叫做数据窗口)的大小。 + /// 如果设置为 true 则锁定选择区域的大小,也就是说,只能平移,不能缩放。 + /// </summary> + public bool zoomLock + { + get { return m_ZoomLock; } + set { if (PropertyUtil.SetStruct(ref m_ZoomLock, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show data shadow in dataZoom-silder component, to indicate the data tendency in brief. + /// ||拖动时,是否实时更新系列的视图。如果设置为 false,则只在拖拽结束的时候更新。默认为true,暂不支持修改。 + /// </summary> + public bool realtime { get { return true; } } + /// <summary> + /// The background color of the component. + /// ||组件的背景颜色。 + /// </summary> + public Color backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetStruct(ref m_BackgroundColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of dataZoom data area. + /// ||数据区域颜色。 + /// </summary> + public Color32 fillerColor + { + get { return m_FillerColor; } + set { if (PropertyUtil.SetColor(ref m_FillerColor, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the color of dataZoom border. + /// ||边框颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetComponentDirty(); } + } + /// <summary> + /// 边框宽。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetComponentDirty(); } + } + /// <summary> + /// Distance between dataZoom component and the bottom side of the container. + /// bottom value is a instant pixel value like 10 or float value [0-1]. + /// ||组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetVerticesDirty(); } + } + /// <summary> + /// Distance between dataZoom component and the top side of the container. + /// top value is a instant pixel value like 10 or float value [0-1]. + /// ||组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetVerticesDirty(); } + } + /// <summary> + /// Distance between dataZoom component and the left side of the container. + /// left value is a instant pixel value like 10 or float value [0-1]. + /// ||组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetVerticesDirty(); } + } + /// <summary> + /// Distance between dataZoom component and the right side of the container. + /// right value is a instant pixel value like 10 or float value [0-1]. + /// ||组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetVerticesDirty(); } + } + /// <summary> + /// Use absolute value or percent value in DataZoom.start and DataZoom.end. + /// ||取绝对值还是百分比。 + /// </summary> + public RangeMode rangeMode + { + get { return m_RangeMode; } + set { if (PropertyUtil.SetStruct(ref m_RangeMode, value)) SetVerticesDirty(); } + } + /// <summary> + /// The start percentage of the window out of the data extent, in the range of 0 ~ 100. + /// ||数据窗口范围的起始百分比。范围是:0 ~ 100。 + /// </summary> + public float start + { + get { return m_Start; } + set { m_Start = value; if (m_Start < 0) m_Start = 0; if (m_Start > 100) m_Start = 100; SetVerticesDirty(); } + } + /// <summary> + /// Lock start value. + /// ||固定起始值,不让改变。 + /// </summary> + public bool startLock + { + get { return m_StartLock; } + set { if (PropertyUtil.SetStruct(ref m_StartLock, value)) SetVerticesDirty(); } + } + /// <summary> + /// Lock end value. + /// ||固定结束值,不让改变。 + /// </summary> + public bool endLock + { + get { return m_EndLock; } + set { if (PropertyUtil.SetStruct(ref m_EndLock, value)) SetVerticesDirty(); } + } + /// <summary> + /// The end percentage of the window out of the data extent, in the range of 0 ~ 100. + /// ||数据窗口范围的结束百分比。范围是:0 ~ 100。 + /// </summary> + public float end + { + get { return m_End; } + set { m_End = value; if (m_End < 0) m_End = 0; if (m_End > 100) m_End = 100; SetVerticesDirty(); } + } + /// <summary> + /// Minimum number of display data. Minimum number of data displayed when DataZoom is enlarged to maximum. + /// ||最小显示数据个数。当DataZoom放大到最大时,最小显示的数据个数。 + /// </summary> + public int minShowNum + { + get { return m_MinShowNum; } + set { if (PropertyUtil.SetStruct(ref m_MinShowNum, value)) SetVerticesDirty(); } + } + /// <summary> + /// The sensitivity of dataZoom scroll. + /// The larger the number, the more sensitive it is. + /// ||缩放区域组件的敏感度。值越高每次缩放所代表的数据越多。 + /// </summary> + public float scrollSensitivity + { + get { return m_ScrollSensitivity; } + set { if (PropertyUtil.SetStruct(ref m_ScrollSensitivity, value)) SetVerticesDirty(); } + } + /// <summary> + /// Specify whether the layout of dataZoom component is horizontal or vertical. What's more, + /// it indicates whether the horizontal axis or vertical axis is controlled by default in catesian coordinate system. + /// ||布局方式是横还是竖。不仅是布局方式,对于直角坐标系而言,也决定了,缺省情况控制横向数轴还是纵向数轴。 + /// </summary> + public Orient orient + { + get { return m_Orient; } + set { if (PropertyUtil.SetStruct(ref m_Orient, value)) SetVerticesDirty(); } + } + /// <summary> + /// label style. + /// ||文本标签格式。 + /// </summary> + public LabelStyle labelStyle + { + get { return m_LabelStyle; } + set { if (PropertyUtil.SetClass(ref m_LabelStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// 阴影线条样式。 + /// </summary> + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (PropertyUtil.SetClass(ref m_LineStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// 阴影填充样式。 + /// </summary> + public AreaStyle areaStyle + { + get { return m_AreaStyle; } + set { if (PropertyUtil.SetClass(ref m_AreaStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// 选取框样式。 + /// </summary> + public MarqueeStyle marqueeStyle + { + get { return m_MarqueeStyle; } + set { if (PropertyUtil.SetClass(ref m_MarqueeStyle, value)) SetAllDirty(); } + } + /// <summary> + /// start和end变更委托。 + /// </summary> + public CustomDataZoomStartEndFunction startEndFunction { get { return m_StartEndFunction; } set { m_StartEndFunction = value; } } + + class AxisIndexValueInfo + { + public double rawMin; + public double rawMax; + public double min; + public double max; + } + private Dictionary<int, AxisIndexValueInfo> m_XAxisIndexInfos = new Dictionary<int, AxisIndexValueInfo>(); + private Dictionary<int, AxisIndexValueInfo> m_YAxisIndexInfos = new Dictionary<int, AxisIndexValueInfo>(); + + /// <summary> + /// The start label. + /// ||组件的开始信息文本。 + /// </summary> + private ChartLabel m_StartLabel { get; set; } + /// <summary> + /// The end label. + /// ||组件的结束信息文本。 + /// </summary> + private ChartLabel m_EndLabel { get; set; } + + public override void SetDefaultValue() + { + supportInside = true; + supportSlider = true; + filterMode = FilterMode.None; + xAxisIndexs = new List<int>() { 0 }; + yAxisIndexs = new List<int>() { }; + showDataShadow = true; + showDetail = false; + zoomLock = false; + m_Bottom = 10; + m_Left = 10; + m_Right = 10; + m_Top = 0.9f; + rangeMode = RangeMode.Percent; + start = 30; + end = 70; + m_Orient = Orient.Horizonal; + m_ScrollSensitivity = 10; + m_LabelStyle = new LabelStyle(); + m_LineStyle = new LineStyle(LineStyle.Type.Solid) + { + opacity = 0.3f + }; + m_AreaStyle = new AreaStyle() + { + show = true, + opacity = 0.3f + }; + m_MarqueeStyle = new MarqueeStyle(); + } + + /// <summary> + /// 给定的坐标是否在缩放区域内 + /// </summary> + /// <param name="pos"></param> + /// <param name="startX"></param> + /// <param name="width"></param> + /// <returns></returns> + public bool IsInZoom(Vector2 pos) + { + if (pos.x < context.x - 1 || pos.x > context.x + context.width + 1 || + pos.y < context.y - 1 || pos.y > context.y + context.height + 1) + { + return false; + } + return true; + } + + /// <summary> + /// 给定的坐标是否在选中区域内 + /// </summary> + /// <param name="pos"></param> + /// <returns></returns> + public bool IsInSelectedZoom(Vector2 pos) + { + switch (m_Orient) + { + case Orient.Horizonal: + var start = context.x + context.width * m_Start / 100; + var end = context.x + context.width * m_End / 100; + return ChartHelper.IsInRect(pos, start, end, context.y, context.y + context.height); + case Orient.Vertical: + start = context.y + context.height * m_Start / 100; + end = context.y + context.height * m_End / 100; + return ChartHelper.IsInRect(pos, context.x, context.x + context.width, start, end); + default: + return false; + } + } + + public bool IsInSelectedZoom(int totalIndex, int index, bool invert) + { + if (totalIndex <= 0) + return false; + + var tstart = invert ? 100 - end : start; + var tend = invert ? 100 - start : end; + var range = Mathf.RoundToInt(totalIndex * (tend - tstart) / 100); + var min = Mathf.FloorToInt(totalIndex * tstart / 100); + var max = Mathf.CeilToInt(totalIndex * tend / 100); + if (min == 0) max = min + range; + if (max == totalIndex) min = max - range; + var flag = index >= min && index < min + range; + return flag; + } + + /// <summary> + /// 给定的坐标是否在开始活动条触发区域内 + /// </summary> + /// <param name="pos"></param> + /// <param name="startX"></param> + /// <param name="width"></param> + /// <returns></returns> + public bool IsInStartZoom(Vector2 pos) + { + switch (m_Orient) + { + case Orient.Horizonal: + var start = context.x + context.width * m_Start / 100; + return ChartHelper.IsInRect(pos, start - 10, start + 10, context.y, context.y + context.height); + case Orient.Vertical: + start = context.y + context.height * m_Start / 100; + return ChartHelper.IsInRect(pos, context.x, context.x + context.width, start - 10, start + 10); + default: + return false; + } + } + + /// <summary> + /// 给定的坐标是否在结束活动条触发区域内 + /// </summary> + /// <param name="pos"></param> + /// <param name="startX"></param> + /// <param name="width"></param> + /// <returns></returns> + public bool IsInEndZoom(Vector2 pos) + { + switch (m_Orient) + { + case Orient.Horizonal: + var end = context.x + context.width * m_End / 100; + return ChartHelper.IsInRect(pos, end - 10, end + 10, context.y, context.y + context.height); + case Orient.Vertical: + end = context.y + context.height * m_End / 100; + return ChartHelper.IsInRect(pos, context.x, context.x + context.width, end - 10, end + 10); + default: + return false; + } + } + + public bool IsInMarqueeArea(SerieData serieData) + { + return IsInMarqueeArea(serieData.context.position); + } + + public bool IsInMarqueeArea(Vector2 pos) + { + if (!supportMarquee) return false; + if (context.marqueeRect.width >= 0) + { + return context.marqueeRect.Contains(pos); + } + else + { + var rect = context.marqueeRect; + return (new Rect(rect.x + rect.width, rect.y, -rect.width, rect.height)).Contains(pos); + } + } + + public bool IsContainsAxis(Axis axis) + { + if (axis == null) + return false; + else if (axis is XAxis) + return xAxisIndexs.Contains(axis.index); + else if (axis is YAxis) + return yAxisIndexs.Contains(axis.index); + else + return false; + } + public bool IsContainsXAxis(int index) + { + return xAxisIndexs != null && xAxisIndexs.Contains(index); + } + + public bool IsContainsYAxis(int index) + { + return yAxisIndexs != null && yAxisIndexs.Contains(index); + } + + public Color32 GetFillerColor(Color32 themeColor) + { + if (ChartHelper.IsClearColor(fillerColor)) + return themeColor; + else + return fillerColor; + } + + public Color32 GetBackgroundColor(Color32 themeColor) + { + if (ChartHelper.IsClearColor(backgroundColor)) + return themeColor; + else + return backgroundColor; + } + public Color32 GetBorderColor(Color32 themeColor) + { + if (ChartHelper.IsClearColor(borderColor)) + return themeColor; + else + return borderColor; + } + + /// <summary> + /// 是否显示文本 + /// </summary> + /// <param name="flag"></param> + internal void SetLabelActive(bool flag) + { + m_StartLabel.SetActive(flag); + m_EndLabel.SetActive(flag); + } + + /// <summary> + /// 设置开始文本内容 + /// </summary> + /// <param name="text"></param> + internal void SetStartLabelText(string text) + { + if (m_StartLabel != null) m_StartLabel.SetText(text); + } + + /// <summary> + /// 设置结束文本内容 + /// </summary> + /// <param name="text"></param> + internal void SetEndLabelText(string text) + { + if (m_EndLabel != null) m_EndLabel.SetText(text); + } + + internal void SetStartLabel(ChartLabel startLabel) + { + m_StartLabel = startLabel; + } + + internal void SetEndLabel(ChartLabel endLabel) + { + m_EndLabel = endLabel; + } + + internal void UpdateStartLabelPosition(Vector3 pos) + { + if (m_StartLabel != null) m_StartLabel.SetPosition(pos); + } + + internal void UpdateEndLabelPosition(Vector3 pos) + { + if (m_EndLabel != null) m_EndLabel.SetPosition(pos); + } + + public void UpdateRuntimeData(BaseChart chart) + { + var chartX = chart.chartX; + var chartY = chart.chartY; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + var runtimeLeft = left <= 1 ? left * chartWidth : left; + var runtimeBottom = bottom <= 1 ? bottom * chartHeight : bottom; + var runtimeTop = top <= 1 ? top * chartHeight : top; + var runtimeRight = right <= 1 ? right * chartWidth : right; + context.x = chartX + runtimeLeft; + context.y = chartY + runtimeBottom; + context.width = chartWidth - runtimeLeft - runtimeRight; + context.height = chartHeight - runtimeTop - runtimeBottom; + } + + internal void SetXAxisIndexValueInfo(int xAxisIndex, ref double min, ref double max) + { + AxisIndexValueInfo info; + if (!m_XAxisIndexInfos.TryGetValue(xAxisIndex, out info)) + { + info = new AxisIndexValueInfo(); + m_XAxisIndexInfos[xAxisIndex] = info; + } + info.rawMin = min; + info.rawMax = max; + info.min = min + (max - min) * start / 100; + info.max = min + (max - min) * end / 100; + min = info.min; + max = info.max; + } + + internal void SetYAxisIndexValueInfo(int yAxisIndex, ref double min, ref double max) + { + AxisIndexValueInfo info; + if (!m_YAxisIndexInfos.TryGetValue(yAxisIndex, out info)) + { + info = new AxisIndexValueInfo(); + m_YAxisIndexInfos[yAxisIndex] = info; + } + info.rawMin = min; + info.rawMax = max; + info.min = min + (max - min) * start / 100; + info.max = min + (max - min) * end / 100; + min = info.min; + max = info.max; + } + + internal bool IsXAxisIndexValue(int axisIndex) + { + return m_XAxisIndexInfos.ContainsKey(axisIndex); + } + + internal bool IsYAxisIndexValue(int axisIndex) + { + return m_YAxisIndexInfos.ContainsKey(axisIndex); + } + + internal void GetXAxisIndexValue(int axisIndex, out double min, out double max) + { + AxisIndexValueInfo info; + if (m_XAxisIndexInfos.TryGetValue(axisIndex, out info)) + { + var range = info.rawMax - info.rawMin; + min = info.rawMin + range * m_Start / 100; + max = info.rawMin + range * m_End / 100; + } + else + { + min = 0; + max = 0; + } + } + internal void GetYAxisIndexValue(int axisIndex, out double min, out double max) + { + AxisIndexValueInfo info; + if (m_YAxisIndexInfos.TryGetValue(axisIndex, out info)) + { + var range = info.rawMax - info.rawMin; + min = info.rawMin + range * m_Start / 100; + max = info.rawMin + range * m_End / 100; + } + else + { + min = 0; + max = 0; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs.meta b/Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs.meta new file mode 100644 index 0000000..1f760ce --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoom.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc01046451b8f406896eb1a5c50433db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs new file mode 100644 index 0000000..d856ead --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class DataZoomContext : MainComponentContext + { + public float x { get; internal set; } + public float y { get; internal set; } + public float width { get; internal set; } + public float height { get; internal set; } + public bool isDrag { get; internal set; } + public bool isCoordinateDrag { get; internal set; } + public bool isStartDrag { get; internal set; } + public bool isEndDrag { get; internal set; } + /// <summary> + /// 运行时实际范围的开始值 + /// </summary> + public double startValue { get; set; } + /// <summary> + /// 运行时实际范围的结束值 + /// </summary> + public double endValue { get; set; } + public bool invert { get; set; } + + public bool isMarqueeDrag { get; set; } + public Vector3 marqueeStartPos { get; set; } + public Vector3 marqueeEndPos { get; set; } + public Rect marqueeRect { get; set; } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs.meta b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs.meta new file mode 100644 index 0000000..0bf1d45 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 899bbe0691c1c450c99f775d8d5f38c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs new file mode 100644 index 0000000..14b7890 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs @@ -0,0 +1,701 @@ +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class DataZoomHandler : MainComponentHandler<DataZoom> + { + private static readonly string s_DefaultDataZoom = "datazoom"; + private Vector2 m_LastTouchPos0; + private Vector2 m_LastTouchPos1; + private bool m_CheckDataZoomLabel; + private float m_DataZoomLastStartIndex; + private float m_DataZoomLastEndIndex; + private float m_LastStart; + private float m_LastEnd; + + public override void InitComponent() + { + var dataZoom = component; + dataZoom.painter = chart.m_PainterUpper; + dataZoom.refreshComponent = delegate () + { + var dataZoomObject = ChartHelper.AddObject(s_DefaultDataZoom + dataZoom.index, chart.transform, + chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + dataZoom.gameObject = dataZoomObject; + dataZoomObject.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(dataZoomObject); + + var startLabel = ChartHelper.AddChartLabel(s_DefaultDataZoom + "start", dataZoomObject.transform, + dataZoom.labelStyle, chart.theme.dataZoom, "", Color.clear, TextAnchor.MiddleRight); + startLabel.gameObject.SetActive(true); + + var endLabel = ChartHelper.AddChartLabel(s_DefaultDataZoom + "end", dataZoomObject.transform, + dataZoom.labelStyle, chart.theme.dataZoom, "", Color.clear, TextAnchor.MiddleLeft); + endLabel.gameObject.SetActive(true); + + dataZoom.SetStartLabel(startLabel); + dataZoom.SetEndLabel(endLabel); + dataZoom.SetLabelActive(false); + + foreach (var index in dataZoom.xAxisIndexs) + { + var xAxis = chart.GetChartComponent<XAxis>(index); + if (xAxis != null) + { + xAxis.UpdateFilterData(dataZoom); + } + } + + foreach (var serie in chart.series) + { + SerieHelper.UpdateFilterData(serie, dataZoom); + } + }; + dataZoom.refreshComponent(); + } + public override void Update() + { + CheckDataZoomScale(component); + CheckDataZoomLabel(component); + if (m_LastStart != component.start || m_LastEnd != component.end) + { + UpdateDataZoomRange(component, component.start, component.end); + } + } + + public override void DrawUpper(VertexHelper vh) + { + if (chart == null) + return; + + var dataZoom = component; + switch (dataZoom.orient) + { + case Orient.Horizonal: + DrawHorizonalDataZoomSlider(vh, dataZoom); + DrawMarquee(vh, dataZoom); + break; + case Orient.Vertical: + DrawVerticalDataZoomSlider(vh, dataZoom); + DrawMarquee(vh, dataZoom); + break; + } + } + + public override void OnBeginDrag(PointerEventData eventData) + { + if (chart == null) + return; + + if (Input.touchCount > 1) + return; + + var dataZoom = component; + if (!dataZoom.enable) + return; + + Vector2 pos; + if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) + return; + + var grid = chart.GetGridOfDataZoom(dataZoom); + if (dataZoom.supportInside && dataZoom.supportInsideDrag) + { + if (grid.Contains(pos)) + { + dataZoom.context.isCoordinateDrag = true; + } + } + if (dataZoom.supportMarquee) + { + dataZoom.context.isMarqueeDrag = true; + dataZoom.context.marqueeStartPos = pos; + dataZoom.context.marqueeEndPos = pos; + + if (dataZoom.marqueeStyle.realRect) + dataZoom.context.marqueeRect = new Rect(pos.x, pos.y, 0, 0); + else + dataZoom.context.marqueeRect = new Rect(pos.x, grid.context.y, 0, grid.context.height); + + if (dataZoom.marqueeStyle.onStart != null) + { + dataZoom.marqueeStyle.onStart(dataZoom); + } + return; + } + if (dataZoom.supportSlider) + { + if (!dataZoom.zoomLock) + { + if (dataZoom.IsInStartZoom(pos)) + { + dataZoom.context.isStartDrag = true; + } + else if (dataZoom.IsInEndZoom(pos)) + { + dataZoom.context.isEndDrag = true; + } + else if (dataZoom.IsInSelectedZoom(pos)) + { + dataZoom.context.isDrag = true; + } + } + else if (dataZoom.IsInSelectedZoom(pos)) + { + dataZoom.context.isDrag = true; + } + } + } + + public override void OnDrag(PointerEventData eventData) + { + if (chart == null) + return; + if (Input.touchCount > 1) + return; + + var dataZoom = component; + var grid = chart.GetGridOfDataZoom(dataZoom); + if (dataZoom.supportMarquee) + { + Vector2 pos; + if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) + return; + + dataZoom.context.marqueeEndPos = pos; + var oldRect = dataZoom.context.marqueeRect; + var rectWidth = pos.x - dataZoom.context.marqueeStartPos.x; + if (dataZoom.marqueeStyle.realRect) + dataZoom.context.marqueeRect = Rect.MinMaxRect(dataZoom.context.marqueeStartPos.x, pos.y, pos.x, dataZoom.context.marqueeStartPos.y); + else + dataZoom.context.marqueeRect = new Rect(oldRect.x, oldRect.y, rectWidth, oldRect.height); + + dataZoom.SetVerticesDirty(); + if (dataZoom.marqueeStyle.onGoing != null) + dataZoom.marqueeStyle.onGoing(dataZoom); + return; + } + else + { + switch (dataZoom.orient) + { + case Orient.Horizonal: + var deltaPercent = eventData.delta.x / grid.context.width * 100; + OnDragInside(dataZoom, deltaPercent); + OnDragSlider(dataZoom, deltaPercent); + break; + case Orient.Vertical: + deltaPercent = eventData.delta.y / grid.context.height * 100; + OnDragInside(dataZoom, deltaPercent); + OnDragSlider(dataZoom, deltaPercent); + break; + } + } + } + + public override void OnEndDrag(PointerEventData eventData) + { + if (chart == null) + return; + + var dataZoom = component; + + if (dataZoom.supportMarquee) + { + dataZoom.context.isMarqueeDrag = false; + if (dataZoom.marqueeStyle.apply) + { + var grid = chart.GetGridOfDataZoom(dataZoom); + var start = (dataZoom.context.marqueeRect.x - grid.context.x) / grid.context.width * 100; + var end = (dataZoom.context.marqueeRect.x - grid.context.x + dataZoom.context.marqueeRect.width) / grid.context.width * 100; + UpdateDataZoomRange(dataZoom, start, end); + } + if (dataZoom.marqueeStyle.onEnd != null) + { + dataZoom.marqueeStyle.onEnd(dataZoom); + } + return; + } + if (dataZoom.context.isDrag || dataZoom.context.isStartDrag || dataZoom.context.isEndDrag || + dataZoom.context.isCoordinateDrag) + { + chart.RefreshChart(); + } + dataZoom.context.isDrag = false; + dataZoom.context.isCoordinateDrag = false; + dataZoom.context.isStartDrag = false; + dataZoom.context.isEndDrag = false; + } + public override void OnPointerDown(PointerEventData eventData) + { + if (chart == null) + return; + if (Input.touchCount > 1) + return; + + Vector2 localPos; + if (!chart.ScreenPointToChartPoint(eventData.position, out localPos)) + return; + + var dataZoom = component; + var grid = chart.GetGridOfDataZoom(dataZoom); + if (dataZoom.IsInStartZoom(localPos) || + dataZoom.IsInEndZoom(localPos)) + { + return; + } + + if (dataZoom.IsInZoom(localPos) && + !dataZoom.IsInSelectedZoom(localPos)) + { + var pointerX = localPos.x; + var selectWidth = grid.context.width * (dataZoom.end - dataZoom.start) / 100; + var startX = pointerX - selectWidth / 2; + var endX = pointerX + selectWidth / 2; + if (startX < grid.context.x) + { + startX = grid.context.x; + endX = grid.context.x + selectWidth; + } + else if (endX > grid.context.x + grid.context.width) + { + endX = grid.context.x + grid.context.width; + startX = grid.context.x + grid.context.width - selectWidth; + } + var start = (startX - grid.context.x) / grid.context.width * 100; + var end = (endX - grid.context.x) / grid.context.width * 100; + UpdateDataZoomRange(dataZoom, start, end); + } + } + + public override void OnScroll(PointerEventData eventData) + { + if (chart == null) + return; + if (Input.touchCount > 1) + return; + + var dataZoom = component; + if (!dataZoom.enable || dataZoom.zoomLock) + return; + + Vector2 pos; + if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) + return; + + var grid = chart.GetGridOfDataZoom(dataZoom); + if ((dataZoom.supportInside && dataZoom.supportInsideScroll && grid.Contains(pos)) || + dataZoom.IsInZoom(pos)) + { + ScaleDataZoom(dataZoom, eventData.scrollDelta.y * dataZoom.scrollSensitivity); + } + } + + private void OnDragInside(DataZoom dataZoom, float deltaPercent) + { + if (deltaPercent == 0) + return; + if (Input.touchCount > 1) + return; + if (!dataZoom.supportInside || !dataZoom.supportInsideDrag) + return; + if (!dataZoom.context.isCoordinateDrag) + return; + + var diff = dataZoom.end - dataZoom.start; + if (deltaPercent > 0) + { + if (dataZoom.start > 0) + { + var start = dataZoom.start - deltaPercent; + if (start < 0) start = 0; + var end = start + diff; + UpdateDataZoomRange(dataZoom, start, end); + } + } + else + { + if (dataZoom.end < 100) + { + var end = dataZoom.end - deltaPercent; + if (end > 100) end = 100; + var start = end - diff; + UpdateDataZoomRange(dataZoom, start, end); + } + } + } + + private void OnDragSlider(DataZoom dataZoom, float deltaPercent) + { + if (Input.touchCount > 1) + return; + if (!dataZoom.supportSlider) + return; + + if (dataZoom.context.isStartDrag) + { + var start = dataZoom.start + deltaPercent; + if (start > dataZoom.end) + { + start = dataZoom.end; + dataZoom.context.isEndDrag = true; + dataZoom.context.isStartDrag = false; + } + UpdateDataZoomRange(dataZoom, start, dataZoom.end); + } + else if (dataZoom.context.isEndDrag) + { + var end = dataZoom.end + deltaPercent; + if (end < dataZoom.start) + { + end = dataZoom.start; + dataZoom.context.isStartDrag = true; + dataZoom.context.isEndDrag = false; + } + UpdateDataZoomRange(dataZoom, dataZoom.start, end); + } + else if (dataZoom.context.isDrag) + { + if (deltaPercent > 0) + { + if (dataZoom.end + deltaPercent > 100) deltaPercent = 100 - dataZoom.end; + } + else + { + if (dataZoom.start + deltaPercent < 0) deltaPercent = -dataZoom.start; + } + UpdateDataZoomRange(dataZoom, dataZoom.start + deltaPercent, dataZoom.end + deltaPercent); + } + } + + private void ScaleDataZoom(DataZoom dataZoom, float delta) + { + var grid = chart.GetGridOfDataZoom(dataZoom); + var deltaPercent = dataZoom.orient == Orient.Horizonal ? + Mathf.Abs(delta / grid.context.width * 100) : + Mathf.Abs(delta / grid.context.height * 100); + if (delta > 0) + { + if (dataZoom.end <= dataZoom.start) + return; + UpdateDataZoomRange(dataZoom, dataZoom.start + deltaPercent, dataZoom.end - deltaPercent); + } + else + { + UpdateDataZoomRange(dataZoom, dataZoom.start - deltaPercent, dataZoom.end + deltaPercent); + } + } + + public void UpdateDataZoomRange(DataZoom dataZoom, float start, float end) + { + if (end > 100) + end = 100; + + if (start < 0) + start = 0; + + if (end < start) + end = start; + if (dataZoom.startEndFunction != null) + dataZoom.startEndFunction(ref start, ref end); + + if (!dataZoom.startLock) + dataZoom.start = start; + if (!dataZoom.endLock) + dataZoom.end = end; + m_LastStart = dataZoom.start; + m_LastEnd = dataZoom.end; + if (dataZoom.realtime) + { + chart.OnDataZoomRangeChanged(dataZoom); + chart.RefreshChart(); + } + } + + public void RefreshDataZoomLabel() + { + m_CheckDataZoomLabel = true; + } + + private void CheckDataZoomScale(DataZoom dataZoom) + { + if (!dataZoom.enable || dataZoom.zoomLock || !dataZoom.supportInside || !dataZoom.supportInsideDrag) + return; + + if (Input.touchCount == 2) + { + var touch0 = Input.GetTouch(0); + var touch1 = Input.GetTouch(1); + if (touch1.phase == TouchPhase.Began) + { + m_LastTouchPos0 = touch0.position; + m_LastTouchPos1 = touch1.position; + } + else if (touch0.phase == TouchPhase.Moved || touch1.phase == TouchPhase.Moved) + { + var tempPos0 = touch0.position; + var tempPos1 = touch1.position; + var currDist = Vector2.Distance(tempPos0, tempPos1); + var lastDist = Vector2.Distance(m_LastTouchPos0, m_LastTouchPos1); + var delta = (currDist - lastDist); + ScaleDataZoom(dataZoom, delta / dataZoom.scrollSensitivity); + m_LastTouchPos0 = tempPos0; + m_LastTouchPos1 = tempPos1; + } + } + } + + private void CheckDataZoomLabel(DataZoom dataZoom) + { + if (dataZoom.enable && dataZoom.supportSlider && dataZoom.showDetail) + { + Vector2 local; + if (!chart.ScreenPointToChartPoint(Input.mousePosition, out local)) + { + dataZoom.SetLabelActive(false); + return; + } + if (dataZoom.IsInSelectedZoom(local) || + dataZoom.IsInStartZoom(local) || + dataZoom.IsInEndZoom(local)) + { + dataZoom.SetLabelActive(true); + RefreshDataZoomLabel(); + } + else + { + dataZoom.SetLabelActive(false); + } + } + if (m_CheckDataZoomLabel && dataZoom.xAxisIndexs.Count > 0) + { + m_CheckDataZoomLabel = false; + var xAxis = chart.GetChartComponent<XAxis>(dataZoom.xAxisIndexs[0]); + var startIndex = (int)((xAxis.data.Count - 1) * dataZoom.start / 100); + var endIndex = (int)((xAxis.data.Count - 1) * dataZoom.end / 100); + + if (m_DataZoomLastStartIndex != startIndex || m_DataZoomLastEndIndex != endIndex) + { + m_DataZoomLastStartIndex = startIndex; + m_DataZoomLastEndIndex = endIndex; + if (xAxis.data.Count > 0) + { + dataZoom.SetStartLabelText(xAxis.data[startIndex]); + dataZoom.SetEndLabelText(xAxis.data[endIndex]); + } + else if (xAxis.IsTime()) + { + //TODO: + dataZoom.SetStartLabelText(""); + dataZoom.SetEndLabelText(""); + } + xAxis.SetAllDirty(); + } + var start = dataZoom.context.x + dataZoom.context.width * dataZoom.start / 100; + var end = dataZoom.context.x + dataZoom.context.width * dataZoom.end / 100; + var hig = dataZoom.context.height; + dataZoom.UpdateStartLabelPosition(new Vector3(start - 10, chart.chartY + dataZoom.bottom + hig / 2)); + dataZoom.UpdateEndLabelPosition(new Vector3(end + 10, chart.chartY + dataZoom.bottom + hig / 2)); + } + } + + private void DrawHorizonalDataZoomSlider(VertexHelper vh, DataZoom dataZoom) + { + if (!dataZoom.enable || !dataZoom.supportSlider) + return; + var p1 = new Vector3(dataZoom.context.x, dataZoom.context.y); + var p2 = new Vector3(dataZoom.context.x, dataZoom.context.y + dataZoom.context.height); + var p3 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y + dataZoom.context.height); + var p4 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y); + + var lineColor = dataZoom.lineStyle.GetColor(chart.theme.dataZoom.dataLineColor); + var lineWidth = dataZoom.lineStyle.GetWidth(chart.theme.dataZoom.dataLineWidth); + var borderWidth = dataZoom.borderWidth == 0 ? chart.theme.dataZoom.borderWidth : dataZoom.borderWidth; + var borderColor = dataZoom.GetBorderColor(chart.theme.dataZoom.borderColor); + var backgroundColor = dataZoom.GetBackgroundColor(chart.theme.dataZoom.backgroundColor); + var areaColor = dataZoom.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor); + + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, backgroundColor); + + var centerPos = new Vector3(dataZoom.context.x + dataZoom.context.width / 2, + dataZoom.context.y + dataZoom.context.height / 2); + UGL.DrawBorder(vh, centerPos, dataZoom.context.width, dataZoom.context.height, borderWidth, borderColor); + if (dataZoom.showDataShadow && chart.series.Count > 0) + { + Serie serie = chart.series[0]; + Axis axis = chart.GetChartComponent<YAxis>(0); + var showData = serie.GetDataList(null); + float scaleWid = dataZoom.context.width / (showData.Count - 1); + Vector3 lp = Vector3.zero; + Vector3 np = Vector3.zero; + double minValue = 0; + double maxValue = 0; + SeriesHelper.GetYMinMaxValue(chart, 0, axis.inverse, out minValue, out maxValue, false, false); + AxisHelper.AdjustMinMaxValue(axis, ref minValue, ref maxValue, true); + + int rate = 1; + var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist; + var maxCount = showData.Count; + if (sampleDist > 0) + rate = (int)((maxCount - serie.minShow) / (dataZoom.context.width / sampleDist)); + if (rate < 1) + rate = 1; + + var totalAverage = serie.sampleAverage > 0 ? serie.sampleAverage : + DataHelper.DataAverage(ref showData, serie.sampleType, serie.minShow, maxCount, rate); + var dataChanging = false; + var animationDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + + for (int i = 0; i < maxCount; i += rate) + { + double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i, + dataAddDuration, animationDuration, ref dataChanging, axis, unscaledTime); + float pX = dataZoom.context.x + i * scaleWid; + float dataHig = (float)((maxValue - minValue) == 0 ? 0 : + (value - minValue) / (maxValue - minValue) * dataZoom.context.height); + np = new Vector3(pX, chart.chartY + dataZoom.bottom + dataHig); + if (i > 0) + { + UGL.DrawLine(vh, lp, np, lineWidth, lineColor); + Vector3 alp = new Vector3(lp.x, lp.y - lineWidth); + Vector3 anp = new Vector3(np.x, np.y - lineWidth); + + Vector3 tnp = new Vector3(np.x, chart.chartY + dataZoom.bottom + lineWidth); + Vector3 tlp = new Vector3(lp.x, chart.chartY + dataZoom.bottom + lineWidth); + UGL.DrawQuadrilateral(vh, alp, anp, tnp, tlp, areaColor); + } + lp = np; + } + if (dataChanging) + { + chart.RefreshTopPainter(); + } + } + switch (dataZoom.rangeMode) + { + case DataZoom.RangeMode.Percent: + var start = dataZoom.context.x + dataZoom.context.width * dataZoom.start / 100; + var end = dataZoom.context.x + dataZoom.context.width * dataZoom.end / 100; + var fillerColor = dataZoom.GetFillerColor(chart.theme.dataZoom.fillerColor); + + p1 = new Vector2(start, dataZoom.context.y); + p2 = new Vector2(start, dataZoom.context.y + dataZoom.context.height); + p3 = new Vector2(end, dataZoom.context.y + dataZoom.context.height); + p4 = new Vector2(end, dataZoom.context.y); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, fillerColor); + UGL.DrawLine(vh, p1, p2, lineWidth, fillerColor); + UGL.DrawLine(vh, p3, p4, lineWidth, fillerColor); + break; + } + } + + private void DrawVerticalDataZoomSlider(VertexHelper vh, DataZoom dataZoom) + { + if (!dataZoom.enable || !dataZoom.supportSlider) + return; + + var p1 = new Vector3(dataZoom.context.x, dataZoom.context.y); + var p2 = new Vector3(dataZoom.context.x, dataZoom.context.y + dataZoom.context.height); + var p3 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y + dataZoom.context.height); + var p4 = new Vector3(dataZoom.context.x + dataZoom.context.width, dataZoom.context.y); + var lineColor = dataZoom.lineStyle.GetColor(chart.theme.dataZoom.dataLineColor); + var lineWidth = dataZoom.lineStyle.GetWidth(chart.theme.dataZoom.dataLineWidth); + var borderWidth = dataZoom.borderWidth == 0 ? chart.theme.dataZoom.borderWidth : dataZoom.borderWidth; + var borderColor = dataZoom.GetBorderColor(chart.theme.dataZoom.borderColor); + var backgroundColor = dataZoom.GetBackgroundColor(chart.theme.dataZoom.backgroundColor); + var areaColor = dataZoom.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor); + + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, backgroundColor); + var centerPos = new Vector3(dataZoom.context.x + dataZoom.context.width / 2, + dataZoom.context.y + dataZoom.context.height / 2); + UGL.DrawBorder(vh, centerPos, dataZoom.context.width, dataZoom.context.height, borderWidth, borderColor); + + if (dataZoom.showDataShadow && chart.series.Count > 0) + { + Serie serie = chart.series[0]; + Axis axis = chart.GetChartComponent<YAxis>(0); + var showData = serie.GetDataList(null); + float scaleWid = dataZoom.context.height / (showData.Count - 1); + Vector3 lp = Vector3.zero; + Vector3 np = Vector3.zero; + double minValue = 0; + double maxValue = 0; + SeriesHelper.GetYMinMaxValue(chart, 0, axis.inverse, out minValue, out maxValue); + AxisHelper.AdjustMinMaxValue(axis, ref minValue, ref maxValue, true); + + int rate = 1; + var sampleDist = serie.sampleDist < 2 ? 2 : serie.sampleDist; + var maxCount = showData.Count; + if (sampleDist > 0) + rate = (int)((maxCount - serie.minShow) / (dataZoom.context.height / sampleDist)); + if (rate < 1) + rate = 1; + + var totalAverage = serie.sampleAverage > 0 ? serie.sampleAverage : + DataHelper.DataAverage(ref showData, serie.sampleType, serie.minShow, maxCount, rate); + var dataChanging = false; + var animationDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + + for (int i = 0; i < maxCount; i += rate) + { + double value = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, maxCount, totalAverage, i, + dataAddDuration, animationDuration, ref dataChanging, axis, unscaledTime); + float pY = dataZoom.context.y + i * scaleWid; + float dataHig = (maxValue - minValue) == 0 ? 0 : + (float)((value - minValue) / (maxValue - minValue) * dataZoom.context.width); + np = new Vector3(chart.chartX + chart.chartWidth - dataZoom.right - dataHig, pY); + if (i > 0) + { + UGL.DrawLine(vh, lp, np, lineWidth, lineColor); + Vector3 alp = new Vector3(lp.x, lp.y - lineWidth); + Vector3 anp = new Vector3(np.x, np.y - lineWidth); + + Vector3 tnp = new Vector3(np.x, chart.chartY + dataZoom.bottom + lineWidth); + Vector3 tlp = new Vector3(lp.x, chart.chartY + dataZoom.bottom + lineWidth); + UGL.DrawQuadrilateral(vh, alp, anp, tnp, tlp, areaColor); + } + lp = np; + } + if (dataChanging) + { + chart.RefreshTopPainter(); + } + } + switch (dataZoom.rangeMode) + { + case DataZoom.RangeMode.Percent: + var start = dataZoom.context.y + dataZoom.context.height * dataZoom.start / 100; + var end = dataZoom.context.y + dataZoom.context.height * dataZoom.end / 100; + var fillerColor = dataZoom.GetFillerColor(chart.theme.dataZoom.fillerColor); + + p1 = new Vector2(dataZoom.context.x, start); + p2 = new Vector2(dataZoom.context.x + dataZoom.context.width, start); + p3 = new Vector2(dataZoom.context.x + dataZoom.context.width, end); + p4 = new Vector2(dataZoom.context.x, end); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, fillerColor); + UGL.DrawLine(vh, p1, p2, lineWidth, fillerColor); + UGL.DrawLine(vh, p3, p4, lineWidth, fillerColor); + break; + } + } + + private void DrawMarquee(VertexHelper vh, DataZoom dataZoom) + { + if (!dataZoom.enable || !dataZoom.supportMarquee) + return; + var areaColor = dataZoom.marqueeStyle.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor); + UGL.DrawRectangle(vh, dataZoom.context.marqueeRect, areaColor); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs.meta b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs.meta new file mode 100644 index 0000000..5f39e61 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f980f43c96a748e0913a1a0054ecd9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs new file mode 100644 index 0000000..c7f8b81 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs @@ -0,0 +1,62 @@ +namespace XCharts.Runtime +{ + public static class DataZoomHelper + { + public static void UpdateDataZoomRuntimeStartEndValue(DataZoom dataZoom, Serie serie) + { + if (dataZoom == null || serie == null) + return; + + double min = 0; + double max = 0; + SerieHelper.GetMinMaxData(serie, out min, out max, null); + dataZoom.context.startValue = min + (max - min) * dataZoom.start / 100; + dataZoom.context.endValue = min + (max - min) * dataZoom.end / 100; + } + + public static void UpdateDataZoomRuntimeStartEndValue<T>(BaseChart chart) where T : Serie + { + foreach (var component in chart.components) + { + if (component is DataZoom) + { + var dataZoom = component as DataZoom; + if (!dataZoom.enable) + continue; + + double min = double.MaxValue; + double max = double.MinValue; + foreach (var serie in chart.series) + { + if (!serie.show || !(serie is T)) + continue; + if (!dataZoom.IsContainsXAxis(serie.xAxisIndex)) + continue; + + var axis = chart.GetChartComponent<XAxis>(serie.xAxisIndex); + + if (axis.minMaxType == Axis.AxisMinMaxType.Custom) + { + if (axis.min < min) + min = axis.min; + if (axis.max > max) + max = axis.max; + } + else + { + double serieMinValue = 0; + double serieMaxValue = 0; + SerieHelper.GetMinMaxData(serie, out serieMinValue, out serieMaxValue, null, 2); + if (serieMinValue < min) + min = serieMinValue; + if (serieMaxValue > max) + max = serieMaxValue; + } + } + dataZoom.context.startValue = min + (max - min) * dataZoom.start / 100; + dataZoom.context.endValue = min + (max - min) * dataZoom.end / 100; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs.meta b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs.meta new file mode 100644 index 0000000..070730e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/DataZoom/DataZoomHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3cc7a61abc3a74004a079f796e51dfc9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Debug.meta b/Assets/XCharts/Runtime/Component/Debug.meta new file mode 100644 index 0000000..aefd3c3 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Debug.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 76abe02f90a34419dbd45292ed7000d6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs b/Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs new file mode 100644 index 0000000..e7aa084 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + public class DebugInfo + { +#pragma warning disable 0414 + [SerializeField] private bool m_Show = true; +#pragma warning restore 0414 + [SerializeField] private bool m_ShowDebugInfo = false; + [SerializeField] protected bool m_ShowAllChartObject = false; + [SerializeField] protected bool m_FoldSeries = false; + [SerializeField] + private LabelStyle m_LabelStyle = new LabelStyle() + { + background = new ImageStyle() + { + color = new Color32(32, 32, 32, 170) + }, + textStyle = new TextStyle() + { + fontSize = 18, + color = Color.white + } + }; + + private static StringBuilder s_Sb = new StringBuilder(); + + private static readonly float INTERVAL = 0.2f; + private static readonly float MAXCACHE = 20; + private int m_FrameCount = 0; + private float m_LastTime = 0f; + private float m_LastCheckShowTime = 0f; + private int m_LastRefreshCount = 0; + private BaseChart m_Chart; + private ChartLabel m_Label; + private List<float> m_FpsList = new List<float>(); + + /// <summary> + /// Whether show debug component. + /// ||是否显示Debug组件。 + /// </summary> + public bool show { get { return m_Show; } set { m_Show = value; } } + /// <summary> + /// Whether show children components of chart in hierarchy view. + /// ||是否在Hierarchy试图显示所有chart下的节点。 + /// </summary> + public bool showAllChartObject { get { return m_ShowAllChartObject; } set { m_ShowAllChartObject = value; } } + /// <summary> + /// Whether to fold series in inspector view. + /// ||是否在Inspector上折叠Serie。 + /// </summary> + public bool foldSeries { get { return m_FoldSeries; } set { m_FoldSeries = value; } } + /// <summary> + /// frame rate. + /// ||当前帧率。 + /// </summary> + public float fps { get; private set; } + /// <summary> + /// The average frame rate. + /// ||平均帧率。 + /// </summary> + public float avgFps { get; private set; } + /// <summary> + /// The fefresh count of chart per second. + /// ||图表每秒刷新次数。 + /// </summary> + public int refreshCount { get; internal set; } + internal int clickChartCount { get; set; } + + public void Init(BaseChart chart) + { + m_Chart = chart; + m_Label = AddDebugInfoObject("debug", chart.transform, m_LabelStyle, chart.theme, chart.childrenNodeNames); + } + + public void Update() + { + if (clickChartCount > 2) + { + m_ShowDebugInfo = !m_ShowDebugInfo; + ChartHelper.SetActive(m_Label.transform, m_ShowDebugInfo); + clickChartCount = 0; + m_LastCheckShowTime = Time.realtimeSinceStartup; + return; + } + if (Time.realtimeSinceStartup - m_LastCheckShowTime > 0.5f) + { + m_LastCheckShowTime = Time.realtimeSinceStartup; + clickChartCount = 0; + } + if (!m_ShowDebugInfo || m_Label == null) + return; + + m_FrameCount++; + if (Time.realtimeSinceStartup - m_LastTime >= INTERVAL) + { + fps = m_FrameCount / (Time.realtimeSinceStartup - m_LastTime); + m_FrameCount = 0; + m_LastTime = Time.realtimeSinceStartup; + if (m_LastRefreshCount == refreshCount) + { + m_LastRefreshCount = 0; + refreshCount = 0; + } + m_LastRefreshCount = refreshCount; + if (m_FpsList.Count > MAXCACHE) + { + m_FpsList.RemoveAt(0); + } + m_FpsList.Add(fps); + avgFps = GetAvg(m_FpsList); + if (m_Label != null) + { + s_Sb.Length = 0; + s_Sb.AppendFormat("v{0}\n", XChartsMgr.version); + s_Sb.AppendFormat("fps : {0:f0} / {1:f0}\n", fps, avgFps); + s_Sb.AppendFormat("draw : {0}\n", refreshCount); + + var dataCount = m_Chart.GetAllSerieDataCount(); + SetValueWithKInfo(s_Sb, "data", dataCount); + + var vertCount = 0; + foreach (var serie in m_Chart.series) + vertCount += serie.context.vertCount; + + SetValueWithKInfo(s_Sb, "b-vert", m_Chart.m_BasePainterVertCount); + SetValueWithKInfo(s_Sb, "s-vert", vertCount); + SetValueWithKInfo(s_Sb, "t-vert", m_Chart.m_TopPainterVertCount, false); + + m_Label.SetText(s_Sb.ToString()); + } + } + } + + private static void SetValueWithKInfo(StringBuilder s_Sb, string key, int value, bool newLine = true) + { + if (value >= 1000) + s_Sb.AppendFormat("{0} : {1:f1}k", key, value * 0.001f); + else + s_Sb.AppendFormat("{0} : {1}", key, value); + if (newLine) + s_Sb.Append("\n"); + } + + private static float GetAvg(List<float> list) + { + var total = 0f; + foreach (var v in list) total += v; + return total / list.Count; + } + + private ChartLabel AddDebugInfoObject(string name, Transform parent, LabelStyle labelStyle, + ThemeStyle theme, List<string> childrenNodeNames) + { + var anchorMax = new Vector2(0, 1); + var anchorMin = new Vector2(0, 1); + var pivot = new Vector2(0, 1); + var sizeDelta = new Vector2(100, 100); + + var labelGameObject = ChartHelper.AddObject(name, parent, anchorMin, anchorMax, pivot, sizeDelta, -1, childrenNodeNames); + labelGameObject.transform.SetAsLastSibling(); + labelGameObject.hideFlags = m_Chart.chartHideFlags; + ChartHelper.SetActive(labelGameObject, m_ShowDebugInfo); + + var label = ChartHelper.AddChartLabel("info", labelGameObject.transform, labelStyle, theme.common, + "", Color.clear, TextAnchor.UpperLeft); + label.SetActive(labelStyle.show); + return label; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs.meta b/Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs.meta new file mode 100644 index 0000000..be8f760 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Debug/DebugInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6accb0ff71304b56a019db8ee3139d9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Interaction.meta b/Assets/XCharts/Runtime/Component/Interaction.meta new file mode 100644 index 0000000..9dd74cb --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Interaction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8445ec442e5314aa891cbbd6d4d966c4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Interaction/InteractData.cs b/Assets/XCharts/Runtime/Component/Interaction/InteractData.cs new file mode 100644 index 0000000..23f210d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Interaction/InteractData.cs @@ -0,0 +1,294 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + public class InteractData + { + private float m_PreviousValue = 0; + private float m_CurrentValue = float.NaN; + private float m_TargetValue = float.NaN; + private Vector3 m_PreviousPosition = Vector3.one; + private Vector3 m_TargetPosition = Vector3.one; + private Color32 m_PreviousColor = ColorUtil.clearColor32; + private Color32 m_TargetColor = ColorUtil.clearColor32; + private Color32 m_PreviousToColor = ColorUtil.clearColor32; + private Color32 m_TargetToColor = ColorUtil.clearColor32; + private float m_UpdateTime = 0; + private bool m_UpdateFlag = false; + private bool m_ValueEnable = false; + + internal float targetVaue { get { return m_TargetValue; } } + internal float previousValue { get { return m_PreviousValue; } } + internal bool valueEnable { get { return m_ValueEnable; } } + internal bool updateFlag { get { return m_UpdateFlag; } } + + public override string ToString() + { + return string.Format("m_PreviousValue:{0},m_TargetValue:{1},m_UpdateTime:{2},m_UpdateFlag:{3},m_ValueEnable:{4},m_PreviousPosition:{5},m_TargetPosition:{6}", + m_PreviousValue, m_TargetValue, m_UpdateTime, m_UpdateFlag, m_ValueEnable, m_PreviousPosition, m_TargetPosition); + } + + public void SetValue(ref bool needInteract, float value, bool highlight, float rate = 1.3f) + { + value = highlight && rate != 0 ? value * rate : value; + SetValue(ref needInteract, value); + } + + public void SetValue(ref bool needInteract, float value, bool previousValueZero = false) + { + if (m_TargetValue != value) + { + needInteract = true; + if (!m_ValueEnable) + m_PreviousValue = previousValueZero ? 0 : value; + else + m_PreviousValue = m_CurrentValue; + UpdateStart(); + m_TargetValue = value; + } + else if (m_UpdateFlag) + { + needInteract = true; + } + } + + public void SetPosition(ref bool needInteract, Vector3 pos) + { + if (m_TargetPosition != pos) + { + needInteract = true; + UpdateStart(); + m_PreviousPosition = m_TargetPosition == Vector3.one ? pos : m_TargetPosition; + m_TargetPosition = pos; + } + } + + public void SetColor(ref bool needInteract, Color32 color) + { + if (!ChartHelper.IsValueEqualsColor(color, m_TargetColor)) + { + needInteract = true; + UpdateStart(); + m_PreviousColor = ChartHelper.IsClearColor(m_TargetColor) ? color : m_TargetColor; + m_TargetColor = color; + } + else if (m_UpdateFlag) + { + needInteract = true; + } + } + public void SetColor(ref bool needInteract, Color32 color, Color32 toColor) + { + SetColor(ref needInteract, color); + if (!ChartHelper.IsValueEqualsColor(toColor, m_TargetToColor)) + { + needInteract = true; + UpdateStart(); + m_PreviousToColor = ChartHelper.IsClearColor(m_TargetToColor) ? color : m_TargetToColor; + m_TargetToColor = toColor; + } + } + + public void SetValueAndColor(ref bool needInteract, float value, Color32 color) + { + SetValue(ref needInteract, value); + SetColor(ref needInteract, color); + } + + public void SetValueAndColor(ref bool needInteract, float value, Color32 color, Color32 toColor) + { + SetValue(ref needInteract, value); + SetColor(ref needInteract, color, toColor); + } + + public bool TryGetValue(ref float value, ref bool interacting, float animationDuration = 250) + { + if (!IsValueEnable() || animationDuration == 0) + return false; + if (float.IsNaN(m_TargetValue)) + return false; + if (m_UpdateFlag && !float.IsNaN(m_PreviousValue)) + { + var rate = GetRate(animationDuration); + if (rate < 1) + { + interacting = true; + value = Mathf.Lerp(m_PreviousValue, m_TargetValue, rate); + m_CurrentValue = value; + return true; + } + else + { + UpdateEnd(); + } + } + value = m_TargetValue; + return true; + } + + public bool TryGetPosition(ref Vector3 pos, ref bool interacting, float animationDuration = 250) + { + if (!IsValueEnable() || animationDuration == 0) + return false; + if (m_TargetPosition == Vector3.one) + { + return false; + } + if (m_UpdateFlag && m_PreviousPosition != Vector3.one) + { + var rate = GetRate(animationDuration); + if (rate < 1) + { + interacting = true; + pos = Vector3.Lerp(m_PreviousPosition, m_TargetPosition, rate); + return true; + } + else + { + UpdateEnd(); + } + } + pos = m_TargetPosition; + return true; + } + + public bool TryGetColor(ref Color32 color, ref bool interacting, float animationDuration = 250) + { + if (!IsValueEnable() || animationDuration == 0) + return false; + if (m_UpdateFlag) + { + var rate = GetRate(animationDuration); + if (rate < 1) + { + interacting = true; + color = Color32.Lerp(m_PreviousColor, m_TargetColor, rate); + return true; + } + else + { + UpdateEnd(); + } + } + color = m_TargetColor; + return true; + } + + public bool TryGetColor(ref Color32 color, ref Color32 toColor, ref bool interacting, float animationDuration = 250) + { + if (!IsValueEnable() || animationDuration == 0) + return false; + if (m_UpdateFlag) + { + var rate = GetRate(animationDuration); + if (rate < 1) + { + interacting = true; + color = Color32.Lerp(m_PreviousColor, m_TargetColor, rate); + toColor = Color32.Lerp(m_PreviousToColor, m_TargetToColor, rate); + return true; + } + else + { + UpdateEnd(); + } + } + color = m_TargetColor; + toColor = m_TargetToColor; + return true; + } + public bool TryGetValueAndColor(ref float value, ref Color32 color, ref Color32 toColor, ref bool interacting, float animationDuration = 250) + { + if (!IsValueEnable() || animationDuration == 0) + return false; + if (float.IsNaN(m_TargetValue)) + return false; + if (m_UpdateFlag && !float.IsNaN(m_PreviousValue)) + { + var rate = GetRate(animationDuration); + if (rate < 1) + { + interacting = true; + value = Mathf.Lerp(m_PreviousValue, m_TargetValue, rate); + color = Color32.Lerp(m_PreviousColor, m_TargetColor, rate); + toColor = Color32.Lerp(m_PreviousToColor, m_TargetToColor, rate); + m_CurrentValue = value; + return true; + } + else + { + UpdateEnd(); + } + } + value = m_TargetValue; + color = m_TargetColor; + toColor = m_TargetToColor; + return true; + } + + private float GetRate(float animationDuration) + { + var time = Time.time - m_UpdateTime; + var total = animationDuration / 1000; + var rate = time / total; + if (rate > 1) rate = 1; + return rate; + } + + private void UpdateStart() + { + m_ValueEnable = true; + m_UpdateFlag = true; + m_UpdateTime = Time.time; + } + + private void UpdateEnd() + { + if (!m_UpdateFlag) return; + m_UpdateFlag = false; + m_PreviousColor = m_TargetColor; + m_PreviousToColor = m_TargetToColor; + m_PreviousValue = m_TargetValue; + m_CurrentValue = m_TargetValue; + m_PreviousPosition = m_TargetPosition; + } + + public bool TryGetValueAndColor(ref float value, ref Vector3 pos, ref Color32 color, ref Color32 toColor, ref bool interacting, float animationDuration = 250) + { + var flag = TryGetValueAndColor(ref value, ref color, ref toColor, ref interacting, animationDuration); + flag |= TryGetPosition(ref pos, ref interacting, animationDuration); + return flag; + } + + public bool TryGetValueAndColor(ref float value, ref Vector3 pos, ref bool interacting, float animationDuration = 250) + { + var flag = TryGetValue(ref value, ref interacting, animationDuration); + flag |= TryGetPosition(ref pos, ref interacting, animationDuration); + return flag; + } + + public void Reset() + { + m_UpdateFlag = false; + m_ValueEnable = false; + m_TargetValue = float.NaN; + m_PreviousValue = float.NaN; + m_CurrentValue = float.NaN; + m_PreviousPosition = Vector3.one; + m_TargetPosition = Vector3.one; + m_TargetColor = ColorUtil.clearColor32; + m_TargetToColor = ColorUtil.clearColor32; + m_PreviousColor = ColorUtil.clearColor32; + m_PreviousToColor = ColorUtil.clearColor32; + } + + private bool IsValueEnable() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + return false; +#endif + return m_ValueEnable; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Interaction/InteractData.cs.meta b/Assets/XCharts/Runtime/Component/Interaction/InteractData.cs.meta new file mode 100644 index 0000000..4e09507 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Interaction/InteractData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42f150814cce84d66b931eed0a07d4ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Label.meta b/Assets/XCharts/Runtime/Component/Label.meta new file mode 100644 index 0000000..401c628 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ad378dd158b5d438a87405d35a3a6546 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs b/Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs new file mode 100644 index 0000000..ecdd7e9 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs @@ -0,0 +1,17 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class EndLabelStyle : LabelStyle + { + public EndLabelStyle() + { + m_Offset = new Vector3(5, 0, 0); + m_TextStyle.alignment = TextAnchor.MiddleLeft; + m_NumericFormatter = "f0"; + m_Formatter = "{a}:{c}"; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs.meta b/Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs.meta new file mode 100644 index 0000000..deca912 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/EndLabelStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3ca55f3ab0314339ae171c8ac07c4e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Label/LabelLine.cs b/Assets/XCharts/Runtime/Component/Label/LabelLine.cs new file mode 100644 index 0000000..4f668e5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/LabelLine.cs @@ -0,0 +1,166 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// 标签的引导线 + /// </summary> + [System.Serializable] + public class LabelLine : ChildComponent, ISerieComponent, ISerieDataComponent + { + /// <summary> + /// 标签视觉引导线类型 + /// </summary> + public enum LineType + { + /// <summary> + /// 折线 + /// </summary> + BrokenLine, + /// <summary> + /// 曲线 + /// </summary> + Curves, + /// <summary> + /// 水平线 + /// </summary> + HorizontalLine + } + + [SerializeField] private bool m_Show = true; + [SerializeField] private LineType m_LineType = LineType.BrokenLine; + [SerializeField] private Color32 m_LineColor = ChartConst.clearColor32; + [SerializeField] private float m_LineAngle = 60; + [SerializeField] private float m_LineWidth = 1.0f; + [SerializeField] private float m_LineGap = 1.0f; + [SerializeField] private float m_LineLength1 = 25f; + [SerializeField] private float m_LineLength2 = 15f; + [SerializeField][Since("v3.8.0")] private float m_LineEndX = 0f; + [SerializeField] private SymbolStyle m_StartSymbol = new SymbolStyle() { show = false, type = SymbolType.Circle, size = 3 }; + [SerializeField] private SymbolStyle m_EndSymbol = new SymbolStyle() { show = false, type = SymbolType.Circle, size = 3 }; + + public void Reset() + { + m_Show = false; + m_LineType = LineType.BrokenLine; + m_LineColor = Color.clear; + m_LineAngle = 60; + m_LineWidth = 1.0f; + m_LineGap = 1.0f; + m_LineLength1 = 25f; + m_LineLength2 = 15f; + m_LineEndX = 0; + } + + /// <summary> + /// Whether the label line is showed. + /// ||是否显示视觉引导线。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetAllDirty(); } + } + /// <summary> + /// the type of visual guide line. + /// ||视觉引导线类型。 + /// </summary> + public LineType lineType + { + get { return m_LineType; } + set { if (PropertyUtil.SetStruct(ref m_LineType, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of visual guild line. + /// ||视觉引导线颜色。默认和serie一致取自调色板。 + /// </summary> + public Color32 lineColor + { + get { return m_LineColor; } + set { if (PropertyUtil.SetStruct(ref m_LineColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the angle of visual guild line. Valid for broken line and curve line. Invalid in Pie. + /// ||视觉引导线的固定角度。对折线和曲线有效。在Pie中无效。 + /// </summary> + public float lineAngle + { + get { return m_LineAngle; } + set { if (PropertyUtil.SetStruct(ref m_LineAngle, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of visual guild line. + /// ||视觉引导线的宽度。 + /// </summary> + public float lineWidth + { + get { return m_LineWidth; } + set { if (PropertyUtil.SetStruct(ref m_LineWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the gap of container and guild line. + /// ||视觉引导线和容器的间距。 + /// </summary> + public float lineGap + { + get { return m_LineGap; } + set { if (PropertyUtil.SetStruct(ref m_LineGap, value)) SetVerticesDirty(); } + } + /// <summary> + /// The length of the first segment of visual guide line. + /// ||视觉引导线第一段的长度。 + /// </summary> + public float lineLength1 + { + get { return m_LineLength1; } + set { if (PropertyUtil.SetStruct(ref m_LineLength1, value)) SetVerticesDirty(); } + } + /// <summary> + /// The length of the second segment of visual guide line. + /// ||视觉引导线第二段的长度。 + /// </summary> + public float lineLength2 + { + get { return m_LineLength2; } + set { if (PropertyUtil.SetStruct(ref m_LineLength2, value)) SetVerticesDirty(); } + } + /// <summary> + /// The fixed x position of the end point of visual guide line. + /// ||视觉引导线结束点的固定x位置。当不为0时,会代替lineLength2设定引导线的x位置。 + /// </summary> + public float lineEndX + { + get { return m_LineEndX; } + set { if (PropertyUtil.SetStruct(ref m_LineEndX, value)) SetVerticesDirty(); } + } + /// <summary> + /// The symbol of the start point of labelline. + /// ||起始点的图形标记。 + /// </summary> + public SymbolStyle startSymbol + { + get { return m_StartSymbol; } + set { if (PropertyUtil.SetClass(ref m_StartSymbol, value)) SetVerticesDirty(); } + } + /// <summary> + /// The symbol of the end point of labelline. + /// ||结束点的图形标记。 + /// </summary> + public SymbolStyle endSymbol + { + get { return m_EndSymbol; } + set { if (PropertyUtil.SetClass(ref m_EndSymbol, value)) SetVerticesDirty(); } + } + + public Vector3 GetStartSymbolOffset() + { + return m_StartSymbol != null && m_StartSymbol.show ? m_StartSymbol.offset3 : Vector3.zero; + } + + public Vector3 GetEndSymbolOffset() + { + return m_EndSymbol != null && m_EndSymbol.show ? m_EndSymbol.offset3 : Vector3.zero; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Label/LabelLine.cs.meta b/Assets/XCharts/Runtime/Component/Label/LabelLine.cs.meta new file mode 100644 index 0000000..2f3d51f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/LabelLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c3977205b6f14d8a97ae32177691580 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Label/LabelStyle.cs b/Assets/XCharts/Runtime/Component/Label/LabelStyle.cs new file mode 100644 index 0000000..d6ed487 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/LabelStyle.cs @@ -0,0 +1,518 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Text label of chart, to explain some data information about graphic item like value, name and so on. + /// ||图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。 + /// </summary> + [System.Serializable] + public class LabelStyle : ChildComponent, ISerieComponent, ISerieDataComponent + { + /// <summary> + /// The position of label. + /// ||标签的位置。 + /// </summary> + public enum Position + { + Default, + /// <summary> + /// Outside of sectors of pie chart, which relates to corresponding sector through visual guide line. + /// ||饼图扇区外侧,通过视觉引导线连到相应的扇区。 + /// </summary> + Outside, + /// <summary> + /// Inside the sectors of pie chart. + /// ||饼图扇区内部。 + /// </summary> + Inside, + /// <summary> + /// In the center of pie chart. + /// ||在饼图中心位置。 + /// </summary> + Center, + /// <summary> + /// top of symbol. + /// ||图形标志的顶部。 + /// </summary> + Top, + /// <summary> + /// the bottom of symbol. + /// ||图形标志的底部。 + /// </summary> + Bottom, + /// <summary> + /// the left of symbol. + /// ||图形标志的左边。 + /// </summary> + Left, + /// <summary> + /// the right of symbol. + /// ||图形标志的右边。 + /// </summary> + Right, + /// <summary> + /// the start of line. + /// ||线的起始点。 + /// </summary> + Start, + /// <summary> + /// the middle of line. + /// ||线的中点。 + /// </summary> + Middle, + /// <summary> + /// the end of line. + /// ||线的结束点。 + /// </summary> + End + } + + [SerializeField] protected bool m_Show = true; + [SerializeField] Position m_Position = Position.Default; + [SerializeField] protected bool m_AutoOffset = false; + [SerializeField] protected Vector3 m_Offset; + [SerializeField] protected float m_Rotate; + [SerializeField][Since("v3.6.0")] protected bool m_AutoRotate = false; + [SerializeField] protected float m_Distance; + [SerializeField] protected string m_Formatter; + [SerializeField] protected string m_NumericFormatter = ""; + [SerializeField] protected float m_Width = 0; + [SerializeField] protected float m_Height = 0; + + [SerializeField] protected IconStyle m_Icon = new IconStyle(); + [SerializeField] protected ImageStyle m_Background = new ImageStyle(); + [SerializeField] protected TextPadding m_TextPadding = new TextPadding(); + [SerializeField] protected TextStyle m_TextStyle = new TextStyle(); + protected LabelFormatterFunction m_FormatterFunction; + + public void Reset() + { + m_Show = false; + m_Position = Position.Default; + m_Offset = Vector3.zero; + m_Distance = 0; + m_Rotate = 0; + m_Width = 0; + m_Height = 0; + m_NumericFormatter = ""; + m_AutoOffset = false; + } + + /// <summary> + /// Whether the label is showed. + /// ||是否显示文本标签。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetAllDirty(); } + } + /// <summary> + /// The position of label. + /// ||标签的位置。 + /// </summary> + public Position position + { + get { return m_Position; } + set { if (PropertyUtil.SetStruct(ref m_Position, value)) SetAllDirty(); } + } + /// <summary> + /// label content string template formatter. \n line wrapping is supported. Formatters for some components will not take effect. <br /> + /// Template placeholder have the following, some of which apply only to fixed components: <br /> + /// `{.}` : indicates the dot mark. <br /> + /// `{a}` : indicates the series name. <br /> + /// `{b}` : category value of x axis or data name. <br /> + /// `{c}` : data value. <br /> + /// `{d}` : percentage. <br /> + /// `{e}` : indicates the data name. <br /> + /// `{f}` : data sum. <br /> + /// `{g}` : indicates the total number of data. <br /> + /// `{h}` : hexadecimal color value. <br /> + /// `{y}` : category value of y axis. <br /> + /// `{value}` : the value of the axis or legend. <br /> + /// `{index}` : the index of the axis. <br /> + /// The following placeholder apply to `UITable` components: <br /> + /// `{name}` : indicates the row name of the table. <br /> + /// `{index}` : indicates the row number of the table. <br /> + /// The following placeholder apply to `UIStatistc` components: <br /> + /// `{title}` : title text. <br /> + /// `{dd}` : day. <br /> + /// `{hh}` : hours. <br /> + /// `{mm}` : minutes. <br /> + /// `{ss}` : second. <br /> + /// `{fff}` : milliseconds. <br /> + /// `{d}` : day. <br /> + /// `{h}` : hours. <br /> + /// `{m}` : minutes. <br /> + /// `{s}` : second. <br /> + /// `{f}` : milliseconds. <br /> + /// Example :{b}:{c}<br /> + /// ||标签内容字符串模版格式器。支持用 \n 换行。部分组件的格式器会不生效。<br/> + /// 模板通配符有以下这些,部分只适用于固定的组件:<br/> + /// `{.}`:圆点标记。<br/> + /// `{a}`:系列名。<br/> + /// `{b}`:X轴类目名或数据名。<br/> + /// `{c}`:数据值。<br/> + /// `{d}`:百分比。<br/> + /// `{e}`:数据名。<br/> + /// `{f}`:数据和。<br/> + /// `{g}`:数据总个数。<br/> + /// `{h}`:十六进制颜色值。<br/> + /// `{y}`:Y轴的类目名。<br/> + /// `{value}`:坐标轴或图例的值。<br/> + /// `{index}`:坐标轴编号。<br/> + /// 以下通配符适用UITable组件:<br/> + /// `{name}`: 表格的行名。<br/> + /// `{index}`:表格的行号。<br/> + /// 以下通配符适用UIStatistc组件:<br/> + /// `{title}`:标题文本。<br/> + /// `{dd}`:天。<br/> + /// `{hh}`:小时。<br/> + /// `{mm}`:分钟。<br/> + /// `{ss}`:秒。<br/> + /// `{fff}`:毫秒。<br/> + /// `{d}`:天。<br/> + /// `{h}`:小时。<br/> + /// `{m}`:分钟。<br/> + /// `{s}`:秒。<br/> + /// `{f}`:毫秒。<br/> + /// 示例:“{b}:{c}” + /// </summary> + public string formatter + { + get { return m_Formatter; } + set { if (PropertyUtil.SetClass(ref m_Formatter, value)) SetComponentDirty(); } + } + /// <summary> + /// Standard number and date format string. Used to format a Double value or a DateTime date as a string. + /// numericFormatter is used as an argument to either `Double.ToString ()` or `DateTime.ToString()`. <br /> + /// The number format uses the Axx format: A is a single-character format specifier that supports C currency, + /// D decimal, E exponent, F fixed-point number, G regular, N digit, P percentage, R round trip, and X hexadecimal. + /// xx is precision specification, from 0-99. E.g. F1, E2<br /> + /// Date format: Starts with `date`, which is used to format DateTime. Common date formats are: + /// yyyy year, MM month, dd day, HH hour, mm minute, ss second, fff millisecond. For example: date:yyyy-MM-dd HH:mm:ss<br /> + /// Time format: Starts with `time`, which is used to format TimeSpan. Common time formats are: + /// d day, HH hour, mm minute, ss second, fffffff fractional part. + /// Only the version of Unity2018 or later can support formatting, and the characters inside should be escaped. + /// For example: time:HH\:mm\:ss<br /> + /// number format reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings<br/> + /// date format reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings<br/> + /// Note: The date and time formats are only supported by 'v3.12.0' or later.<br/> + /// ||标准数字和日期格式字符串。用于将Double数值或DateTime日期格式化显示为字符串。numericFormatter用来作为Double.ToString()或DateTime.ToString()的参数。<br/> + /// 数字格式使用Axx的形式:A是格式说明符的单字符,支持C货币、D十进制、E指数、F定点数、G常规、N数字、P百分比、R往返、X十六进制的。xx是精度说明,从0-99。如:F1, E2<br/> + /// 日期格式:以`date`开头,用来格式化DateTime,常见格式有:yyyy年,MM月,dd日,HH时,mm分,ss秒,fff毫秒。如:date:yyyy-MM-dd HH:mm:ss<br/> + /// 时间格式:以`time`开头,用来格式化TimeSpan,常见格式有:d日,HH时,mm分,ss秒,fffffff小数部分。 + /// 需要Unity2018以上版本才支持格式化,并且里面的字符要转义。如:time:d\.HH\:mm\:ss<br/> + /// 数值格式化参考:https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/standard-numeric-format-strings <br/> + /// 日期格式化参考:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/standard-date-and-time-format-strings <br/> + /// 时间格式化参考:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/standard-timespan-format-strings <br/> + /// 注意:date和time格式需要`v3.12.0`以上版本才支持。 + /// </summary> + public string numericFormatter + { + get { return m_NumericFormatter; } + set { if (PropertyUtil.SetClass(ref m_NumericFormatter, value)) SetComponentDirty(); } + } + /// <summary> + /// offset to the host graphic element. + /// ||距离图形元素的偏移 + /// </summary> + public Vector3 offset + { + get { return m_Offset; } + set { if (PropertyUtil.SetStruct(ref m_Offset, value)) SetAllDirty(); } + } + /// <summary> + /// Rotation of label. + /// ||文本的旋转。 + /// </summary> + public float rotate + { + get { return m_Rotate; } + set { if (PropertyUtil.SetStruct(ref m_Rotate, value)) SetComponentDirty(); } + } + /// <summary> + /// auto rotate of label. + /// ||是否自动旋转。 + /// </summary> + public bool autoRotate + { + get { return m_AutoRotate; } + set { if (PropertyUtil.SetStruct(ref m_AutoRotate, value)) SetComponentDirty(); } + } + /// <summary> + /// the distance of label to axis line. + /// ||距离轴线的距离。 + /// </summary> + public float distance + { + get { return m_Distance; } + set { if (PropertyUtil.SetStruct(ref m_Distance, value)) SetAllDirty(); } + } + /// <summary> + /// the width of label. If set as default value 0, it means than the label width auto set as the text width. + /// ||标签的宽度。一般不用指定,不指定时则自动是文字的宽度。 + /// </summary> + public float width + { + get { return m_Width; } + set { if (PropertyUtil.SetStruct(ref m_Width, value)) SetComponentDirty(); } + } + /// <summary> + /// the height of label. If set as default value 0, it means than the label height auto set as the text height. + /// ||标签的高度。一般不用指定,不指定时则自动是文字的高度。 + /// </summary> + public float height + { + get { return m_Height; } + set { if (PropertyUtil.SetStruct(ref m_Height, value)) SetComponentDirty(); } + } + /// <summary> + /// the text padding of label. + /// ||文本的边距。 + /// </summary> + public TextPadding textPadding + { + get { return m_TextPadding; } + set { if (PropertyUtil.SetClass(ref m_TextPadding, value)) SetComponentDirty(); } + } + /// <summary> + /// Whether to automatically offset. When turned on, the Y offset will automatically determine the opening of the curve to determine whether to offset up or down. + /// ||是否开启自动偏移。当开启时,Y的偏移会自动判断曲线的开口来决定向上还是向下偏移。 + /// </summary> + public bool autoOffset + { + get { return m_AutoOffset; } + set { if (PropertyUtil.SetStruct(ref m_AutoOffset, value)) SetAllDirty(); } + } + /// <summary> + /// the sytle of background. + /// ||背景图样式。 + /// </summary> + public ImageStyle background + { + get { return m_Background; } + set { if (PropertyUtil.SetClass(ref m_Background, value)) SetAllDirty(); } + } + /// <summary> + /// the sytle of icon. + /// ||图标样式。 + /// </summary> + public IconStyle icon + { + get { return m_Icon; } + set { if (PropertyUtil.SetClass(ref m_Icon, value)) SetAllDirty(); } + } + /// <summary> + /// the sytle of text. + /// ||文本样式。 + /// </summary> + public TextStyle textStyle + { + get { return m_TextStyle; } + set { if (PropertyUtil.SetClass(ref m_TextStyle, value)) SetAllDirty(); } + } + /// <summary> + /// the formatter function of label, which supports string template and callback function. + /// ||标签的文本格式化函数,支持字符串模版和回调函数。 + /// </summary> + public LabelFormatterFunction formatterFunction + { + get { return m_FormatterFunction; } + set { m_FormatterFunction = value; } + } + /// <summary> + /// whether the label is inside. + /// ||是否在内部。 + /// </summary> + public bool IsInside() + { + return m_Position == Position.Inside || m_Position == Position.Center; + } + + public bool IsDefaultPosition(Position position) + { + return m_Position == Position.Default || m_Position == position; + } + + public bool IsAutoSize() + { + return width == 0 && height == 0; + } + + public Vector3 GetOffset(float radius) + { + var x = ChartHelper.GetActualValue(m_Offset.x, radius); + var y = ChartHelper.GetActualValue(m_Offset.y, radius); + var z = ChartHelper.GetActualValue(m_Offset.z, radius); + return new Vector3(x, y, z); + } + + public Color GetColor(Color defaultColor) + { + if (ChartHelper.IsClearColor(textStyle.color)) + { + return IsInside() ? Color.black : defaultColor; + } + else + { + return textStyle.color; + } + } + + public virtual LabelStyle Clone() + { + var label = new LabelStyle(); + label.m_Show = m_Show; + label.m_Position = m_Position; + label.m_Offset = m_Offset; + label.m_Rotate = m_Rotate; + label.m_Distance = m_Distance; + label.m_Formatter = m_Formatter; + label.m_Width = m_Width; + label.m_Height = m_Height; + label.m_NumericFormatter = m_NumericFormatter; + label.m_AutoOffset = m_AutoOffset; + label.m_Icon.Copy(m_Icon); + label.m_Background.Copy(m_Background); + label.m_TextPadding = m_TextPadding; + label.m_TextStyle.Copy(m_TextStyle); + return label; + } + + public virtual void Copy(LabelStyle label) + { + m_Show = label.m_Show; + m_Position = label.m_Position; + m_Offset = label.m_Offset; + m_Rotate = label.m_Rotate; + m_Distance = label.m_Distance; + m_Formatter = label.m_Formatter; + m_Width = label.m_Width; + m_Height = label.m_Height; + m_NumericFormatter = label.m_NumericFormatter; + m_AutoOffset = label.m_AutoOffset; + m_Icon.Copy(label.m_Icon); + m_Background.Copy(label.m_Background); + m_TextPadding = label.m_TextPadding; + m_TextStyle.Copy(label.m_TextStyle); + } + + public virtual string GetFormatterContent(int labelIndex, int totalIndex, string category) + { + if (string.IsNullOrEmpty(category)) + return GetFormatterFunctionContent(labelIndex, category, category); + + if (string.IsNullOrEmpty(m_Formatter)) + { + return GetFormatterFunctionContent(labelIndex, category, category); + } + else + { + var content = m_Formatter; + FormatterHelper.ReplaceAxisLabelContent(ref content, category, labelIndex, totalIndex); + return GetFormatterFunctionContent(labelIndex, category, category); + } + } + + public virtual string GetFormatterContent(int labelIndex, int totalIndex, double value, double minValue, double maxValue, bool isLog = false) + { + var newNumericFormatter = numericFormatter; + if (value == 0 && !DateTimeUtil.IsDateOrTimeRegex(newNumericFormatter)) + { + newNumericFormatter = "f0"; + } + else if (string.IsNullOrEmpty(newNumericFormatter) && !isLog) + { + if (Math.Abs(maxValue) >= Math.Abs(minValue)) + { + newNumericFormatter = MathUtil.IsInteger(maxValue) ? "0.#" : "f" + MathUtil.GetPrecision(maxValue); + } + else + { + newNumericFormatter = MathUtil.IsInteger(minValue) ? "0.#" : "f" + MathUtil.GetPrecision(minValue); + } + } + if (string.IsNullOrEmpty(m_Formatter)) + { + if (isLog) + { + return GetFormatterFunctionContent(labelIndex, value, ChartCached.NumberToStr(value, newNumericFormatter)); + } + if (minValue >= -1 && minValue <= 1 && maxValue >= -1 && maxValue <= 1) + { + int minAcc = MathUtil.GetPrecision(minValue); + int maxAcc = MathUtil.GetPrecision(maxValue); + int curAcc = MathUtil.GetPrecision(value); + int acc = Mathf.Max(Mathf.Max(minAcc, maxAcc), curAcc); + return GetFormatterFunctionContent(labelIndex, value, ChartCached.FloatToStr(value, newNumericFormatter, acc)); + } + return GetFormatterFunctionContent(labelIndex, value, ChartCached.NumberToStr(value, newNumericFormatter)); + } + else + { + var content = m_Formatter; + FormatterHelper.ReplaceAxisLabelContent(ref content, newNumericFormatter, value, labelIndex, totalIndex); + return GetFormatterFunctionContent(labelIndex, value, content); + } + } + + private static bool isDateFormatter = false; + private static string newFormatter = null; + public string GetFormatterDateTime(int labelIndex, int totalIndex, double value, double minValue, double maxValue) + { + var timestamp = (int)value; + var dateTime = DateTimeUtil.GetDateTime(timestamp); + var dateString = string.Empty; + if (string.IsNullOrEmpty(numericFormatter) || numericFormatter.Equals("f2")) + { + dateString = DateTimeUtil.GetDateTimeFormatString(dateTime, maxValue - minValue); + } + else + { + try + { + if (DateTimeUtil.IsDateOrTimeRegex(numericFormatter, ref isDateFormatter, ref newFormatter)) + { + if (isDateFormatter) + dateString = ChartCached.NumberToDateStr(timestamp, newFormatter); + else + dateString = ChartCached.NumberToTimeStr(timestamp, newFormatter); + } + else + { + dateString = dateTime.ToString(numericFormatter); + } + } + catch + { + XLog.Warning("not support datetime formatter:" + numericFormatter); + } + } + if (!string.IsNullOrEmpty(m_Formatter)) + { + var content = m_Formatter; + FormatterHelper.ReplaceAxisLabelContent(ref content, dateString, labelIndex, totalIndex); + return GetFormatterFunctionContent(labelIndex, value, content); + } + else + { + return GetFormatterFunctionContent(labelIndex, value, dateString); + } + } + + protected string GetFormatterFunctionContent(int labelIndex, string category, string currentContent) + { + return m_FormatterFunction == null ? currentContent : + m_FormatterFunction(labelIndex, labelIndex, category, currentContent); + } + + protected string GetFormatterFunctionContent(int labelIndex, double value, string currentContent) + { + return m_FormatterFunction == null ? currentContent : + m_FormatterFunction(labelIndex, value, null, currentContent); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Label/LabelStyle.cs.meta b/Assets/XCharts/Runtime/Component/Label/LabelStyle.cs.meta new file mode 100644 index 0000000..9f25d62 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/LabelStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0b2c690f282f04752898422894f61738 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs b/Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs new file mode 100644 index 0000000..b05ffaa --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class SerieLabelHelper + { + + public static Color GetLabelColor(Serie serie, ThemeStyle theme, int index) + { + if (serie.label != null && !ChartHelper.IsClearColor(serie.label.textStyle.color)) + { + return serie.label.textStyle.color; + } + else + { + return theme.GetColor(index); + } + } + + public static bool CanShowLabel(Serie serie, SerieData serieData, LabelStyle label, int dimesion) + { + return serie.show && serieData.context.canShowLabel && !serie.IsIgnoreValue(serieData, dimesion); + } + + public static string GetFormatterContent(Serie serie, SerieData serieData, + double dataValue, double dataTotal, LabelStyle serieLabel, Color color, BaseChart chart = null) + { + if (serieLabel == null) + { + serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + } + var numericFormatter = serieLabel == null ? "" : serieLabel.numericFormatter; + var serieName = serie.serieName; + var dataName = serieData != null ? serieData.name : null; + if (string.IsNullOrEmpty(serieLabel.formatter)) + { + var currentContent = ChartCached.NumberToStr(dataValue, numericFormatter); + if (serieLabel.formatterFunction == null) + return currentContent; + else + return serieLabel.formatterFunction(serieData.index, dataValue, null, currentContent); + } + else + { + var content = serieLabel.formatter; + FormatterHelper.ReplaceSerieLabelContent(ref content, numericFormatter, serie.dataCount, dataValue, + dataTotal, serieName, dataName, dataName, color, serieData, chart, serie.index); + if (serieLabel.formatterFunction == null) + return content; + else + return serieLabel.formatterFunction(serieData.index, dataValue, null, content); + } + } + + public static void SetGaugeLabelText(Serie serie) + { + var serieData = serie.GetSerieData(0); + if (serieData == null) return; + if (serieData.labelObject == null) return; + var label = SerieHelper.GetSerieLabel(serie, serieData); + if (label == null) return; + var value = serieData.GetData(1); + var total = serie.max; + var content = SerieLabelHelper.GetFormatterContent(serie, serieData, value, total, null, Color.clear); + serieData.labelObject.SetText(content); + serieData.labelObject.SetPosition(serie.context.center + label.offset); + if (!ChartHelper.IsClearColor(label.textStyle.color)) + { + serieData.labelObject.text.SetColor(label.textStyle.color); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs.meta b/Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs.meta new file mode 100644 index 0000000..a0d6d81 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Label/SerieLabelHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 654a13ef33a064e4fbf078742f397b20 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Legend.meta b/Assets/XCharts/Runtime/Component/Legend.meta new file mode 100644 index 0000000..cbd5c63 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5bf1d7d1b565e45b6aacd4a261ddef9f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Legend/Legend.cs b/Assets/XCharts/Runtime/Component/Legend/Legend.cs new file mode 100644 index 0000000..5e34113 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/Legend.cs @@ -0,0 +1,458 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Legend component.The legend component shows different sets of tags, colors, and names. + /// You can control which series are not displayed by clicking on the legend. + /// ||图例组件。 + /// 图例组件展现了不同系列的标记,颜色和名字。可以通过点击图例控制哪些系列不显示。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(LegendHandler), true)] + public class Legend : MainComponent, IPropertyChanged + { + public enum Type + { + /// <summary> + /// 自动匹配。 + /// </summary> + Auto, + /// <summary> + /// 自定义图标。 + /// </summary> + Custom, + /// <summary> + /// 空心圆。 + /// </summary> + EmptyCircle, + /// <summary> + /// 圆形。 + /// </summary> + Circle, + /// <summary> + /// 正方形。可通过Setting的legendIconCornerRadius参数调整圆角。 + /// </summary> + Rect, + /// <summary> + /// 三角形。 + /// </summary> + Triangle, + /// <summary> + /// 菱形。 + /// </summary> + Diamond, + /// <summary> + /// 烛台(可用于K线图)。 + /// </summary> + Candlestick, + } + /// <summary> + /// Selected mode of legend, which controls whether series can be toggled displaying by clicking legends. + /// ||图例选择的模式,控制是否可以通过点击图例改变系列的显示状态。默认开启图例选择,可以设成 None 关闭。 + /// </summary> + public enum SelectedMode + { + /// <summary> + /// 多选。 + /// </summary> + Multiple, + /// <summary> + /// 单选。 + /// </summary> + Single, + /// <summary> + /// 无法选择。 + /// </summary> + None + } + + [SerializeField] private bool m_Show = true; + [SerializeField] private Type m_IconType = Type.Auto; + [SerializeField] private SelectedMode m_SelectedMode = SelectedMode.Multiple; + [SerializeField] private Orient m_Orient = Orient.Horizonal; + [SerializeField] private Location m_Location = new Location() { align = Location.Align.TopCenter, top = 0.125f }; + [SerializeField] private float m_ItemWidth = 25.0f; + [SerializeField] private float m_ItemHeight = 12.0f; + [SerializeField] private float m_ItemGap = 10f; + [SerializeField] private bool m_ItemAutoColor = true; + [SerializeField] private float m_ItemOpacity = 1; + [SerializeField] private string m_Formatter; + [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle(); + [SerializeField][Since("v3.10.0")] private TextLimit m_TextLimit = new TextLimit(); + [SerializeField] private List<string> m_Data = new List<string>(); + [SerializeField] private List<Sprite> m_Icons = new List<Sprite>(); + [SerializeField] private List<Color> m_Colors = new List<Color>(); + [SerializeField][Since("v3.1.0")] protected ImageStyle m_Background = new ImageStyle() { show = false }; + [SerializeField][Since("v3.1.0")] protected Padding m_Padding = new Padding(); + [SerializeField][Since("v3.6.0")] private List<Vector3> m_Positions = new List<Vector3>(); + + public LegendContext context = new LegendContext(); + + /// <summary> + /// Whether to show legend component. + /// ||是否显示图例组件。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } + /// <summary> + /// Type of legend. + /// ||图例类型。 + /// </summary> + public Type iconType + { + get { return m_IconType; } + set { if (PropertyUtil.SetStruct(ref m_IconType, value)) SetAllDirty(); } + } + /// <summary> + /// Selected mode of legend, which controls whether series can be toggled displaying by clicking legends. + /// ||选择模式。控制是否可以通过点击图例改变系列的显示状态。默认开启图例选择,可以设成 None 关闭。 + /// </summary> + public SelectedMode selectedMode + { + get { return m_SelectedMode; } + set { if (PropertyUtil.SetStruct(ref m_SelectedMode, value)) SetComponentDirty(); } + } + /// <summary> + /// Specify whether the layout of legend component is horizontal or vertical. + /// ||布局方式是横还是竖。 + /// </summary> + public Orient orient + { + get { return m_Orient; } + set { if (PropertyUtil.SetStruct(ref m_Orient, value)) SetComponentDirty(); } + } + /// <summary> + /// The location of legend. + /// ||图例显示的位置。 + /// </summary> + public Location location + { + get { return m_Location; } + set { if (PropertyUtil.SetClass(ref m_Location, value)) SetComponentDirty(); } + } + /// <summary> + /// Image width of legend symbol. + /// ||图例标记的图形宽度。 + /// </summary> + public float itemWidth + { + get { return m_ItemWidth; } + set { if (PropertyUtil.SetStruct(ref m_ItemWidth, value)) SetComponentDirty(); } + } + /// <summary> + /// Image height of legend symbol. + /// ||图例标记的图形高度。 + /// </summary> + public float itemHeight + { + get { return m_ItemHeight; } + set { if (PropertyUtil.SetStruct(ref m_ItemHeight, value)) SetComponentDirty(); } + } + /// <summary> + /// The distance between each legend, horizontal distance in horizontal layout, and vertical distance in vertical layout. + /// ||图例每项之间的间隔。横向布局时为水平间隔,纵向布局时为纵向间隔。 + /// </summary> + public float itemGap + { + get { return m_ItemGap; } + set { if (PropertyUtil.SetStruct(ref m_ItemGap, value)) SetComponentDirty(); } + } + /// <summary> + /// Whether the legend symbol matches the color automatically. + /// ||图例标记的图形是否自动匹配颜色。 + /// </summary> + public bool itemAutoColor + { + get { return m_ItemAutoColor; } + set { if (PropertyUtil.SetStruct(ref m_ItemAutoColor, value)) SetComponentDirty(); } + } + /// <summary> + /// the opacity of item color. + /// ||图例标记的图形的颜色透明度。 + /// </summary> + public float itemOpacity + { + get { return m_ItemOpacity; } + set { if (PropertyUtil.SetStruct(ref m_ItemOpacity, value)) SetComponentDirty(); } + } + /// <summary> + /// No longer used, the use of LabelStyle.formatter instead. + /// ||不再使用,使用LabelStyle.formatter代替。 + /// </summary> + [Obsolete("Use LabelStyle.formatter instead.", false)] + public string formatter + { + get { return m_Formatter; } + set { if (PropertyUtil.SetClass(ref m_Formatter, value)) SetComponentDirty(); } + } + /// <summary> + /// the style of text. + /// ||文本样式。 + /// </summary> + public LabelStyle labelStyle + { + get { return m_LabelStyle; } + set { if (PropertyUtil.SetClass(ref m_LabelStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// the limit of text. + /// ||文本限制。 + /// </summary> + public TextLimit textLimit + { + get { return m_TextLimit; } + set { if (value != null) { m_TextLimit = value; SetComponentDirty(); } } + } + /// <summary> + /// the sytle of background. + /// ||背景图样式。 + /// </summary> + public ImageStyle background + { + get { return m_Background; } + set { if (PropertyUtil.SetClass(ref m_Background, value)) SetAllDirty(); } + } + /// <summary> + /// the paddinng of item and background. + /// ||图例标记和背景的间距。 + /// </summary> + public Padding padding + { + get { return m_Padding; } + set { if (PropertyUtil.SetClass(ref m_Padding, value)) SetAllDirty(); } + } + /// <summary> + /// Data array of legend. An array item is usually a name representing string. (If it is a pie chart, + /// it could also be the name of a single data in the pie chart) of a series. + /// If data is not specified, it will be auto collected from series. + /// ||图例的数据数组。数组项通常为一个字符串,每一项代表一个系列的 name(如果是饼图,也可以是饼图单个数据的 name)。 + /// 如果 data 没有被指定,会自动从当前系列中获取。指定data时里面的数据项和serie匹配时才会生效。 + /// </summary> + public List<string> data + { + get { return m_Data; } + set { if (value != null) { m_Data = value; SetComponentDirty(); } } + } + /// <summary> + /// 自定义的图例标记图形。 + /// </summary> + public List<Sprite> icons + { + get { return m_Icons; } + set { if (value != null) { m_Icons = value; SetComponentDirty(); } } + } + /// <summary> + /// the colors of legend item. + /// ||图例标记的颜色列表。 + /// </summary> + public List<Color> colors + { + get { return m_Colors; } + set { if (value != null) { m_Colors = value; SetAllDirty(); } } + } + /// <summary> + /// the custom positions of legend item. + /// ||图例标记的自定义位置列表。 + /// </summary> + public List<Vector3> positions + { + get { return m_Positions; } + set { if (value != null) { m_Positions = value; SetAllDirty(); } } + } + /// <summary> + /// 图表是否需要刷新(图例组件不需要刷新图表) + /// </summary> + public override bool vertsDirty { get { return false; } } + /// <summary> + /// 组件是否需要刷新 + /// </summary> + public override bool componentDirty + { + get { return m_ComponentDirty || location.componentDirty || labelStyle.componentDirty || textLimit.componentDirty; } + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + location.ClearComponentDirty(); + labelStyle.ClearComponentDirty(); + textLimit.ClearComponentDirty(); + } + + /// <summary> + /// Clear legend data. + /// ||清空。 + /// </summary> + public override void ClearData() + { + m_Data.Clear(); + SetComponentDirty(); + } + + /// <summary> + /// Whether include in legend data by the specified name. + /// ||是否包括由指定名字的图例 + /// </summary> + /// <param name="name"></param> + /// <returns></returns> + public bool ContainsData(string name) + { + return m_Data.Contains(name); + } + + /// <summary> + /// Removes the legend with the specified name. + /// ||移除指定名字的图例。 + /// </summary> + /// <param name="name"></param> + public void RemoveData(string name) + { + if (m_Data.Contains(name)) + { + m_Data.Remove(name); + SetComponentDirty(); + } + } + + /// <summary> + /// Add legend data. + /// ||添加图例。 + /// </summary> + /// <param name="name"></param> + public void AddData(string name) + { + if (!m_Data.Contains(name) && !string.IsNullOrEmpty(name)) + { + m_Data.Add(name); + SetComponentDirty(); + } + } + + /// <summary> + /// Gets the legend for the specified index. + /// ||获得指定索引的图例。 + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public string GetData(int index) + { + if (index >= 0 && index < m_Data.Count) + { + return m_Data[index]; + } + return null; + } + + /// <summary> + /// Gets the index of the specified legend. + /// ||获得指定图例的索引。 + /// </summary> + /// <param name="legendName"></param> + /// <returns></returns> + public int GetIndex(string legendName) + { + return m_Data.IndexOf(legendName); + } + + /// <summary> + /// Remove all legend buttons. + /// ||移除所有图例按钮。 + /// </summary> + public void RemoveButton() + { + context.buttonList.Clear(); + } + + /// <summary> + /// Bind buttons to legends. + /// ||给图例绑定按钮。 + /// </summary> + /// <param name="name"></param> + /// <param name="btn"></param> + /// <param name="total"></param> + public void SetButton(string name, LegendItem item, int total) + { + context.buttonList[name] = item; + int index = context.buttonList.Values.Count; + item.SetIconActive(iconType == Type.Custom); + item.SetActive(show); + } + + /// <summary> + /// Update the legend button color. + /// ||更新图例按钮颜色。 + /// </summary> + /// <param name="name"></param> + /// <param name="color"></param> + public void UpdateButtonColor(string name, Color color) + { + if (context.buttonList.ContainsKey(name)) + { + context.buttonList[name].SetIconColor(color); + } + } + + /// <summary> + /// Update the text color of legend. + /// ||更新图例文字颜色。 + /// </summary> + /// <param name="name"></param> + /// <param name="color"></param> + public void UpdateContentColor(string name, Color color) + { + if (context.buttonList.ContainsKey(name)) + { + context.buttonList[name].SetContentColor(color); + } + } + + /// <summary> + /// Gets the legend button for the specified index. + /// ||获得指定索引的图例按钮。 + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public Sprite GetIcon(int index) + { + if (index >= 0 && index < m_Icons.Count) + { + return m_Icons[index]; + } + else + { + return null; + } + } + + public Color GetColor(int index) + { + if (index >= 0 && index < m_Colors.Count) + return m_Colors[index]; + else + return Color.white; + } + + public Vector3 GetPosition(int index, Vector3 defaultPos) + { + if (index >= 0 && index < m_Positions.Count) + return m_Positions[index]; + else + return defaultPos; + } + + /// <summary> + /// Callback handling when parameters change. + /// ||参数变更时的回调处理。 + /// </summary> + public void OnChanged() + { + m_Location.OnChanged(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Legend/Legend.cs.meta b/Assets/XCharts/Runtime/Component/Legend/Legend.cs.meta new file mode 100644 index 0000000..c0193d1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/Legend.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c53210fe487d047b6a51bacc0d3e7a71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Legend/LegendContext.cs b/Assets/XCharts/Runtime/Component/Legend/LegendContext.cs new file mode 100644 index 0000000..69c89c5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/LegendContext.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + + public class LegendContext : MainComponentContext + { + /// <summary> + /// 运行时图例的总宽度 + /// </summary> + public float width { get; internal set; } + /// <summary> + /// 运行时图例的总高度 + /// </summary> + public float height { get; internal set; } + public Vector2 center { get; internal set; } + /// <summary> + /// the button list of legend. + /// ||图例按钮列表。 + /// </summary> + internal Dictionary<string, LegendItem> buttonList = new Dictionary<string, LegendItem>(); + /// <summary> + /// 多列时每列的宽度 + /// </summary> + internal Dictionary<int, float> eachWidthDict = new Dictionary<int, float>(); + /// <summary> + /// 单列高度 + /// </summary> + internal float eachHeight { get; set; } + public Image background { get; set; } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Legend/LegendContext.cs.meta b/Assets/XCharts/Runtime/Component/Legend/LegendContext.cs.meta new file mode 100644 index 0000000..59d2e50 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/LegendContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c62c17c9d5b2b4a0fb260103c3ceb5ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs b/Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs new file mode 100644 index 0000000..f098c92 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs @@ -0,0 +1,288 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class LegendHandler : MainComponentHandler<Legend> + { + private static readonly string s_LegendObjectName = "legend"; + private static readonly char[] s_NameSplit = new char[] { '_' }; + + public override void InitComponent() + { + InitLegend(component); + } + + public override void CheckComponent(System.Text.StringBuilder sb) + { + var legend = component; + if (ChartHelper.IsColorAlphaZero(legend.labelStyle.textStyle.color)) + sb.AppendFormat("warning:legend{0}->textStyle->color alpha is 0\n", legend.index); + var serieNameList = SeriesHelper.GetLegalSerieNameList(chart.series); + if (serieNameList.Count == 0) + sb.AppendFormat("warning:legend{0} need serie.serieName or serieData.name not empty\n", legend.index); + foreach (var category in legend.data) + { + if (!serieNameList.Contains(category)) + { + sb.AppendFormat("warning:legend{0} [{1}] is invalid, must be one of serie.serieName or serieData.name\n", + legend.index, category); + } + } + } + public override void DrawTop(VertexHelper vh) + { + DrawLegend(vh); + } + + public override void OnSerieDataUpdate(int serieIndex) + { +#pragma warning disable 0618 + if (FormatterHelper.NeedFormat(component.formatter) || FormatterHelper.NeedFormat(component.labelStyle.formatter)) + component.refreshComponent(); +#pragma warning restore 0618 + } + + private void InitLegend(Legend legend) + { + legend.painter = null; + legend.refreshComponent = delegate () + { + legend.OnChanged(); + var legendObject = ChartHelper.AddObject(s_LegendObjectName + legend.index, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + legend.gameObject = legendObject; + legendObject.hideFlags = chart.chartHideFlags; + //ChartHelper.DestoryGameObjectByMatch(legendObject.transform, "_"); + SeriesHelper.UpdateSerieNameList(chart, ref chart.m_LegendRealShowName); + legend.context.background = ChartHelper.AddIcon("background", legendObject.transform, 0, 0); + legend.context.background.transform.SetSiblingIndex(0); + ChartHelper.SetBackground(legend.context.background, legend.background); + List<string> datas; + if (legend.show && legend.data.Count > 0) + { + datas = new List<string>(); + foreach (var data in legend.data) + { + if (chart.m_LegendRealShowName.Contains(data) || chart.IsSerieName(data)) + datas.Add(data); + } + } + else + { + datas = chart.m_LegendRealShowName; + } + int totalLegend = 0; + for (int i = 0; i < datas.Count; i++) + { + if (!SeriesHelper.IsLegalLegendName(datas[i])) continue; + totalLegend++; + } + legend.RemoveButton(); + ChartHelper.HideAllObject(legendObject); + if (!legend.show) return; + var textLimitInitFlag = false; + var isAnySerieColorByData = SeriesHelper.IsAnyColorByDataSerie(chart.series); + for (int i = 0; i < datas.Count; i++) + { + if (!SeriesHelper.IsLegalLegendName(datas[i])) continue; + string legendName = datas[i]; + var serieIndex = isAnySerieColorByData ? 0 : i; + var dataIndex = isAnySerieColorByData ? i : 0; + var legendContent = GetFormatterContent(legend, dataIndex, datas[i], serieIndex); + if (legend.textLimit.enable) + legendContent = legend.textLimit.GetLimitContent(legendContent); + var readIndex = chart.m_LegendRealShowName.IndexOf(datas[i]); + var active = chart.IsActiveByLegend(datas[i]); + var bgColor = LegendHelper.GetIconColor(chart, legend, readIndex, datas[i], active); + bgColor.a = legend.itemOpacity; + var item = LegendHelper.AddLegendItem(chart, legend, i, legendName, legendObject.transform, chart.theme, + legendContent, bgColor, active, readIndex); + legend.SetButton(legendName, item, totalLegend); + if (!textLimitInitFlag && legend.textLimit.enable) + { + textLimitInitFlag = true; + legend.textLimit.SetRelatedText(item.text, legend.itemWidth); + } + ChartHelper.ClearEventListener(item.button.gameObject); + ChartHelper.AddEventListener(item.button.gameObject, EventTriggerType.PointerDown, (data) => + { + if (data.selectedObject == null || legend.selectedMode == Legend.SelectedMode.None) return; + var temp = data.selectedObject.name.Split(s_NameSplit, 2); + string selectedName = temp[1]; + int clickedIndex = int.Parse(temp[0]); + if (legend.selectedMode == Legend.SelectedMode.Multiple) + { + OnLegendButtonClick(legend, clickedIndex, selectedName, !chart.IsActiveByLegend(selectedName)); + } + else + { + var btnList = legend.context.buttonList.Values.ToArray(); + if (btnList.Length == 1) + { + OnLegendButtonClick(legend, 0, selectedName, !chart.IsActiveByLegend(selectedName)); + } + else + { + for (int n = 0; n < btnList.Length; n++) + { + temp = btnList[n].name.Split(s_NameSplit, 2); + selectedName = btnList[n].legendName; + var index = btnList[n].index; + OnLegendButtonClick(legend, n, selectedName, index == clickedIndex ? true : false); + } + } + } + }); + ChartHelper.AddEventListener(item.button.gameObject, EventTriggerType.PointerEnter, (data) => + { + if (item.button == null) return; + var temp = item.button.name.Split(s_NameSplit, 2); + string selectedName = temp[1]; + int index = int.Parse(temp[0]); + OnLegendButtonEnter(legend, index, selectedName); + }); + ChartHelper.AddEventListener(item.button.gameObject, EventTriggerType.PointerExit, (data) => + { + if (item.button == null) return; + var temp = item.button.name.Split(s_NameSplit, 2); + string selectedName = temp[1]; + int index = int.Parse(temp[0]); + OnLegendButtonExit(legend, index, selectedName); + }); + } + LegendHelper.ResetItemPosition(legend, chart.chartPosition, chart.chartWidth, chart.chartHeight); + }; + legend.refreshComponent(); + } + + private string GetFormatterContent(Legend legend, int dataIndex, string category, int serieIndex) + { +#pragma warning disable 0618 + if (string.IsNullOrEmpty(legend.formatter) && string.IsNullOrEmpty(legend.labelStyle.formatter)) + return category; + else + { + var formatter = string.IsNullOrEmpty(legend.labelStyle.formatter) ? legend.formatter : legend.labelStyle.formatter; + var content = formatter.Replace("{name}", category); + content = content.Replace("{value}", category); + var serie = chart.GetSerie(serieIndex); + FormatterHelper.ReplaceContent(ref content, dataIndex, legend.labelStyle.numericFormatter, serie, chart, category); + return content; + } +#pragma warning restore 0618 + } + + private void OnLegendButtonClick(Legend legend, int index, string legendName, bool show) + { + chart.OnLegendButtonClick(index, legendName, show); + if (chart.onLegendClick != null) + chart.onLegendClick(legend, index, legendName, show); + } + + private void OnLegendButtonEnter(Legend legend, int index, string legendName) + { + chart.OnLegendButtonEnter(index, legendName); + if (chart.onLegendEnter != null) + chart.onLegendEnter(legend, index, legendName); + } + + private void OnLegendButtonExit(Legend legend, int index, string legendName) + { + chart.OnLegendButtonExit(index, legendName); + if (chart.onLegendExit != null) + chart.onLegendExit(legend, index, legendName); + } + + private void DrawLegend(VertexHelper vh) + { + if (chart.series.Count == 0) return; + var legend = component; + if (!legend.show) return; + if (legend.iconType == Legend.Type.Custom) return; + foreach (var kv in legend.context.buttonList) + { + var item = kv.Value; + var rect = item.GetIconRect(); + var radius = Mathf.Min(rect.width, rect.height) / 2; + var color = item.GetIconColor(); + var iconType = legend.iconType; + if (legend.iconType == Legend.Type.Auto) + { + var serie = chart.GetSerie(item.legendName); + if (serie != null) + { + if (serie is Line || serie is SimplifiedLine) + { + var sp = new Vector3(rect.center.x - rect.width / 2, rect.center.y); + var ep = new Vector3(rect.center.x + rect.width / 2, rect.center.y); + UGL.DrawLine(vh, sp, ep, chart.settings.legendIconLineWidth, color); + if (!serie.symbol.show) continue; + switch (serie.symbol.type) + { + case SymbolType.None: + continue; + case SymbolType.Circle: + iconType = Legend.Type.Circle; + break; + case SymbolType.Diamond: + iconType = Legend.Type.Diamond; + break; + case SymbolType.EmptyCircle: + iconType = Legend.Type.EmptyCircle; + break; + case SymbolType.Rect: + iconType = Legend.Type.Rect; + break; + case SymbolType.Triangle: + iconType = Legend.Type.Triangle; + break; + } + } + else + { + iconType = Legend.Type.Rect; + } + } + else + { + iconType = Legend.Type.Rect; + } + } + switch (iconType) + { + case Legend.Type.Rect: + var cornerRadius = chart.settings.legendIconCornerRadius; + UGL.DrawRoundRectangle(vh, rect.center, rect.width, rect.height, color, color, + 0, cornerRadius, false, 0.5f); + break; + case Legend.Type.Circle: + UGL.DrawCricle(vh, rect.center, radius, color); + break; + case Legend.Type.Diamond: + UGL.DrawDiamond(vh, rect.center, radius, color); + break; + case Legend.Type.EmptyCircle: + var backgroundColor = chart.GetChartBackgroundColor(); + UGL.DrawEmptyCricle(vh, rect.center, radius, 2 * chart.settings.legendIconLineWidth, + color, color, backgroundColor, 1f); + break; + case Legend.Type.Triangle: + UGL.DrawTriangle(vh, rect.center, 1.2f * radius, color); + break; + case Legend.Type.Candlestick: + UGL.DrawRoundRectangle(vh, rect.center, rect.width / 2, rect.height / 2, color, color, + 0, null, false, 0.5f); + UGL.DrawLine(vh, new Vector3(rect.center.x, rect.center.y - rect.height / 2), + new Vector3(rect.center.x, rect.center.y + rect.height / 2), 1, color); + break; + } + } + } + } +} diff --git a/Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs.meta b/Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs.meta new file mode 100644 index 0000000..3d332f8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/LegendHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d05c7e75b9d3c4a839099bf152752af1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs b/Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs new file mode 100644 index 0000000..ffc55f3 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs @@ -0,0 +1,311 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public static class LegendHelper + { + public static Color GetContentColor(BaseChart chart, int legendIndex, string legendName, Legend legend, ThemeStyle theme, bool active) + { + var textStyle = legend.labelStyle.textStyle; + if (active) + { + if (legend.labelStyle.textStyle.autoColor) + return SeriesHelper.GetNameColor(chart, legendIndex, legendName); + else + return !ChartHelper.IsClearColor(textStyle.color) ? textStyle.color : theme.legend.textColor; + } + else return theme.legend.unableColor; + } + + public static Color GetIconColor(BaseChart chart, Legend legend, int readIndex, string legendName, bool active) + { + if (active) + { + if (legend.itemAutoColor) + { + return SeriesHelper.GetNameColor(chart, readIndex, legendName); + } + else + return legend.GetColor(readIndex); + } + else return chart.theme.legend.unableColor; + } + + public static LegendItem AddLegendItem(BaseChart chart, Legend legend, int i, string legendName, Transform parent, + ThemeStyle theme, string content, Color itemColor, bool active, int legendIndex) + { + var objName = i + "_" + legendName; + var anchorMin = new Vector2(0, 0.5f); + var anchorMax = new Vector2(0, 0.5f); + var pivot = new Vector2(0, 0.5f); + var sizeDelta = new Vector2(100, 30); + var iconSizeDelta = new Vector2(legend.itemWidth, legend.itemHeight); + var textStyle = legend.labelStyle.textStyle; + var contentColor = GetContentColor(chart, legendIndex, legendName, legend, theme, active); + + var objAnchorMin = new Vector2(0, 1); + var objAnchorMax = new Vector2(0, 1); + var objPivot = new Vector2(0, 1); + var btnObj = ChartHelper.AddObject(objName, parent, objAnchorMin, objAnchorMax, objPivot, sizeDelta, -1, chart.childrenNodeNames); + var iconObj = ChartHelper.AddObject("icon", btnObj.transform, anchorMin, anchorMax, pivot, iconSizeDelta); + var img = ChartHelper.EnsureComponent<Image>(btnObj); + img.color = Color.clear; + img.raycastTarget = true; + ChartHelper.EnsureComponent<Button>(btnObj); + ChartHelper.EnsureComponent<Image>(iconObj); + + var label = ChartHelper.AddChartLabel("content", btnObj.transform, legend.labelStyle, theme.legend, + content, contentColor, TextAnchor.MiddleLeft); + label.SetActive(true, true); + + var item = new LegendItem(); + item.index = i; + item.name = objName; + item.legendName = legendName; + item.SetObject(btnObj); + item.SetIconSize(legend.itemWidth, legend.itemHeight); + item.SetIconColor(itemColor); + item.SetIconImage(legend.GetIcon(i)); + item.SetContentPosition(legend.labelStyle.offset); + item.SetContent(content); + //item.SetBackground(legend.background); + return item; + } + + public static void SetLegendBackground(Legend legend, ImageStyle style) + { + var background = legend.context.background; + if (background == null) return; + ChartHelper.SetActive(background, style.show); + if (!style.show) return; + var rect = background.gameObject.GetComponent<RectTransform>(); + rect.localPosition = legend.context.center; + rect.sizeDelta = new Vector2(legend.context.width, legend.context.height); + ChartHelper.SetBackground(background, style); + } + + public static void ResetItemPosition(Legend legend, Vector3 chartPos, float chartWidth, float chartHeight) + { + legend.location.UpdateRuntimeData(chartWidth, chartHeight); + var startX = 0f; + var startY = 0f; + var legendMaxWidth = chartWidth - legend.location.runtimeLeft - legend.location.runtimeRight; + var legendMaxHeight = chartHeight - legend.location.runtimeTop - legend.location.runtimeBottom; + UpdateLegendWidthAndHeight(legend, legendMaxWidth, legendMaxHeight); + var legendRuntimeWidth = legend.context.width; + var legendRuntimeHeight = legend.context.height; + var isVertical = legend.orient == Orient.Vertical; + switch (legend.location.align) + { + case Location.Align.TopCenter: + startX = chartPos.x + chartWidth / 2 - legendRuntimeWidth / 2; + startY = chartPos.y + chartHeight - legend.location.runtimeTop; + break; + case Location.Align.TopLeft: + startX = chartPos.x + legend.location.runtimeLeft; + startY = chartPos.y + chartHeight - legend.location.runtimeTop; + break; + case Location.Align.TopRight: + startX = chartPos.x + chartWidth - legendRuntimeWidth - legend.location.runtimeRight; + startY = chartPos.y + chartHeight - legend.location.runtimeTop; + break; + case Location.Align.Center: + startX = chartPos.x + chartWidth / 2 - legendRuntimeWidth / 2; + startY = chartPos.y + chartHeight / 2 + legendRuntimeHeight / 2; + break; + case Location.Align.CenterLeft: + startX = chartPos.x + legend.location.runtimeLeft; + startY = chartPos.y + chartHeight / 2 + legendRuntimeHeight / 2; + break; + case Location.Align.CenterRight: + startX = chartPos.x + chartWidth - legendRuntimeWidth - legend.location.runtimeRight; + startY = chartPos.y + chartHeight / 2 + legendRuntimeHeight / 2; + break; + case Location.Align.BottomCenter: + startX = chartPos.x + chartWidth / 2 - legendRuntimeWidth / 2; + startY = chartPos.y + legendRuntimeHeight + legend.location.runtimeBottom; + break; + case Location.Align.BottomLeft: + startX = chartPos.x + legend.location.runtimeLeft; + startY = chartPos.y + legendRuntimeHeight + legend.location.runtimeBottom; + break; + case Location.Align.BottomRight: + startX = chartPos.x + chartWidth - legendRuntimeWidth - legend.location.runtimeRight; + startY = chartPos.y + legendRuntimeHeight + legend.location.runtimeBottom; + break; + } + if (!legend.padding.show) + { + legend.context.center = new Vector2(startX + legend.context.width / 2, startY - legend.context.height / 2); + } + else + { + legend.context.center = new Vector2(startX + legend.context.width / 2 - legend.padding.left, + startY - legend.context.height / 2 + legend.padding.top); + } + + if (isVertical) SetVerticalItemPosition(legend, legendMaxHeight, startX, startY); + else SetHorizonalItemPosition(legend, legendMaxWidth, startX, startY); + SetLegendBackground(legend, legend.background); + } + + private static void SetVerticalItemPosition(Legend legend, float legendMaxHeight, float startX, float startY) + { + var currHeight = 0f; + var offsetX = 0f; + var row = 0; + var index = 0; + foreach (var kv in legend.context.buttonList) + { + var item = kv.Value; + if (currHeight + item.height > legendMaxHeight) + { + currHeight = 0; + offsetX += legend.context.eachWidthDict[row]; + row++; + } + item.SetPosition(legend.GetPosition(index++, new Vector3(startX + offsetX, startY - currHeight))); + currHeight += item.height + legend.itemGap; + } + } + private static void SetHorizonalItemPosition(Legend legend, float legendMaxWidth, float startX, float startY) + { + var currWidth = 0f; + var offsetY = 0f; + var index = 0; + foreach (var kv in legend.context.buttonList) + { + var item = kv.Value; + if (currWidth + item.width > legendMaxWidth) + { + currWidth = 0; + offsetY += legend.context.eachHeight; + } + item.SetPosition(legend.GetPosition(index++, new Vector3(startX + currWidth, startY - offsetY))); + currWidth += item.width + legend.itemGap; + } + } + + private static void UpdateLegendWidthAndHeight(Legend legend, float maxWidth, float maxHeight) + { + var width = 0f; + var height = 0f; + var realHeight = 0f; + var realWidth = 0f; + legend.context.eachWidthDict.Clear(); + legend.context.eachHeight = 0; + if (legend.orient == Orient.Horizonal) + { + foreach (var kv in legend.context.buttonList) + { + if (width + kv.Value.width > maxWidth) + { + realWidth = width - legend.itemGap; + realHeight += height + legend.itemGap; + if (legend.context.eachHeight < height + legend.itemGap) + { + legend.context.eachHeight = height + legend.itemGap; + } + height = 0; + width = 0; + } + width += kv.Value.width + legend.itemGap; + if (kv.Value.height > height) + height = kv.Value.height; + } + width -= legend.itemGap; + legend.context.height = realHeight + height; + legend.context.width = realWidth > 0 ? realWidth : width; + } + else + { + var row = 0; + foreach (var kv in legend.context.buttonList) + { + if (height + kv.Value.height > maxHeight) + { + realHeight = height - legend.itemGap; + realWidth += width + legend.itemGap; + legend.context.eachWidthDict[row] = width + legend.itemGap; + row++; + height = 0; + width = 0; + } + height += kv.Value.height + legend.itemGap; + if (kv.Value.width > width) + width = kv.Value.width; + } + height -= legend.itemGap; + legend.context.height = realHeight > 0 ? realHeight : height; + legend.context.width = realWidth + width; + } + if (legend.padding.show) + { + legend.context.width += legend.padding.left + legend.padding.right; + legend.context.height += legend.padding.top + legend.padding.bottom; + } + } + + private static bool IsBeyondWidth(Legend legend, float maxWidth) + { + var totalWidth = 0f; + foreach (var kv in legend.context.buttonList) + { + var item = kv.Value; + totalWidth += item.width + legend.itemGap; + if (totalWidth > maxWidth) return true; + } + return false; + } + + public static bool CheckDataShow(Serie serie, string legendName, bool show) + { + bool needShow = false; + if (legendName.Equals(serie.serieName)) + { + serie.show = show; + serie.highlight = false; + if (serie.show) needShow = true; + } + else + { + foreach (var data in serie.data) + { + if (legendName.Equals(data.name)) + { + data.show = show; + data.context.highlight = false; + if (data.show) needShow = true; + } + } + } + return needShow; + } + + public static int CheckDataHighlighted(Serie serie, string legendName, bool heighlight) + { + var highlightedDataIndex = 0; + if (legendName.Equals(serie.serieName)) + { + serie.highlight = heighlight; + } + else + { + foreach (var data in serie.data) + { + if (legendName.Equals(data.name)) + { + data.context.highlight = heighlight; + if (data.context.highlight) + { + highlightedDataIndex = data.index; + } + } + } + } + return highlightedDataIndex; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs.meta b/Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs.meta new file mode 100644 index 0000000..18e7596 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Legend/LegendHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc1c14527667e4475a275768371b3b9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Mark.meta b/Assets/XCharts/Runtime/Component/Mark.meta new file mode 100644 index 0000000..57971b8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69bae12c156de4372a9680df180e91df +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkArea.cs b/Assets/XCharts/Runtime/Component/Mark/MarkArea.cs new file mode 100644 index 0000000..577295b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkArea.cs @@ -0,0 +1,192 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// 标域类型 + /// </summary> + public enum MarkAreaType + { + None, + /// <summary> + /// 最小值。 + /// </summary> + Min, + /// <summary> + /// 最大值。 + /// </summary> + Max, + /// <summary> + /// 平均值。 + /// </summary> + Average, + /// <summary> + /// 中位数。 + /// </summary> + Median + } + + /// <summary> + /// Used to mark an area in chart. For example, mark a time interval. + /// ||图表标域,常用于标记图表中某个范围的数据。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(MarkAreaHandler), true)] + public class MarkArea : MainComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private string m_Text = ""; + [SerializeField] private int m_SerieIndex = 0; + [SerializeField] private MarkAreaData m_Start = new MarkAreaData(); + [SerializeField] private MarkAreaData m_End = new MarkAreaData(); + [SerializeField] private ItemStyle m_ItemStyle = new ItemStyle(); + [SerializeField] private LabelStyle m_Label = new LabelStyle(); + public ChartLabel runtimeLabel { get; internal set; } + public Vector3 runtimeLabelPosition { get; internal set; } + public Rect runtimeRect { get; internal set; } + /// <summary> + /// 是否显示标域。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// The text of markArea. + /// 标域显示的文本。 + /// </summary> + public string text + { + get { return m_Text; } + set { if (PropertyUtil.SetClass(ref m_Text, value)) SetComponentDirty(); } + } + /// <summary> + /// Serie index of markArea. + /// 标域影响的Serie索引。 + /// </summary> + public int serieIndex + { + get { return m_SerieIndex; } + set { if (PropertyUtil.SetStruct(ref m_SerieIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// 标域范围的起始数据。 + /// </summary> + public MarkAreaData start + { + get { return m_Start; } + set { if (PropertyUtil.SetClass(ref m_Start, value)) SetVerticesDirty(); } + } + /// <summary> + /// 标域范围的结束数据。 + /// </summary> + public MarkAreaData end + { + get { return m_End; } + set { if (PropertyUtil.SetClass(ref m_End, value)) SetVerticesDirty(); } + } + /// <summary> + /// 标域样式。 + /// </summary> + public ItemStyle itemStyle + { + get { return m_ItemStyle; } + set { if (PropertyUtil.SetClass(ref m_ItemStyle, value)) SetVerticesDirty(); } + } + /// <summary> + /// 标域文本样式。 + /// </summary> + public LabelStyle label + { + get { return m_Label; } + set { if (PropertyUtil.SetClass(ref m_Label, value)) SetComponentDirty(); } + } + public override void SetDefaultValue() + { + m_ItemStyle = new ItemStyle(); + m_ItemStyle.opacity = 0.6f; + m_Label = new LabelStyle(); + m_Label.show = true; + } + } + + /// <summary> + /// 标域的数据。 + /// </summary> + [System.Serializable] + public class MarkAreaData : ChildComponent + { + [SerializeField] private MarkAreaType m_Type = MarkAreaType.None; + [SerializeField] private string m_Name; + [SerializeField] private int m_Dimension = 1; + [SerializeField] private float m_XPosition; + [SerializeField] private float m_YPosition; + [SerializeField] private double m_XValue; + [SerializeField] private double m_YValue; + public double runtimeValue { get; internal set; } + /// <summary> + /// Name of the marker, which will display as a label. + /// ||标注名称。会作为文字显示。 + /// </summary> + public string name + { + get { return m_Name; } + set { if (PropertyUtil.SetClass(ref m_Name, value)) SetVerticesDirty(); } + } + /// <summary> + /// Special markArea types, are used to label maximum value, minimum value and so on. + /// ||特殊的标域类型,用于标注最大值最小值等。 + /// </summary> + public MarkAreaType type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetVerticesDirty(); } + } + /// <summary> + /// From which dimension of data to calculate the maximum and minimum value and so on. + /// ||从哪个维度的数据计算最大最小值等。 + /// </summary> + public int dimension + { + get { return m_Dimension; } + set { if (PropertyUtil.SetStruct(ref m_Dimension, value)) SetVerticesDirty(); } + } + /// <summary> + /// The x coordinate relative to the origin, in pixels. + /// ||相对原点的 x 坐标,单位像素。当type为None时有效。 + /// </summary> + public float xPosition + { + get { return m_XPosition; } + set { if (PropertyUtil.SetStruct(ref m_XPosition, value)) SetVerticesDirty(); } + } + /// <summary> + /// The y coordinate relative to the origin, in pixels. + /// ||相对原点的 y 坐标,单位像素。当type为None时有效。 + /// </summary> + public float yPosition + { + get { return m_YPosition; } + set { if (PropertyUtil.SetStruct(ref m_YPosition, value)) SetVerticesDirty(); } + } + /// <summary> + /// The value specified on the X-axis. A value specified when the X-axis is the category axis represents the index of the category axis data, otherwise a specific value. + /// ||X轴上的指定值。当X轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 + /// </summary> + public double xValue + { + get { return m_XValue; } + set { if (PropertyUtil.SetStruct(ref m_XValue, value)) SetVerticesDirty(); } + } + /// <summary> + /// That's the value on the Y-axis. The value specified when the Y axis is the category axis represents the index of the category axis data, otherwise the specific value. + /// ||Y轴上的指定值。当Y轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 + /// </summary> + public double yValue + { + get { return m_YValue; } + set { if (PropertyUtil.SetStruct(ref m_YValue, value)) SetVerticesDirty(); } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkArea.cs.meta b/Assets/XCharts/Runtime/Component/Mark/MarkArea.cs.meta new file mode 100644 index 0000000..3173d1c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkArea.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c7f98347a0d54e1c82866b041a473ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs b/Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs new file mode 100644 index 0000000..df8eb1f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs @@ -0,0 +1,195 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class MarkAreaHandler : MainComponentHandler<MarkArea> + { + private GameObject m_MarkLineLabelRoot; + private bool m_NeedUpdateLabelPosition; + + public override void InitComponent() + { + m_MarkLineLabelRoot = ChartHelper.AddObject("markarea" + component.index, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + m_MarkLineLabelRoot.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(m_MarkLineLabelRoot); + InitMarkArea(component); + } + + public override void DrawBase(VertexHelper vh) + { + DrawMarkArea(vh, component); + } + + public override void Update() + { + if (m_NeedUpdateLabelPosition) + { + m_NeedUpdateLabelPosition = false; + if (component.runtimeLabel != null) + { + component.runtimeLabel.SetPosition(component.runtimeLabelPosition); + } + } + } + + private void InitMarkArea(MarkArea markArea) + { + markArea.painter = chart.m_PainterUpper; + markArea.refreshComponent = delegate() + { + var label = ChartHelper.AddChartLabel("label", m_MarkLineLabelRoot.transform, markArea.label, chart.theme.axis, + component.text, Color.clear, TextAnchor.MiddleCenter); + UpdateRuntimeData(component); + label.SetActive(markArea.label.show, true); + label.SetPosition(component.runtimeLabelPosition); + label.SetText(component.text); + markArea.runtimeLabel = label; + }; + markArea.refreshComponent(); + } + + private void DrawMarkArea(VertexHelper vh, MarkArea markArea) + { + if (!markArea.show) return; + var serie = chart.GetSerie(markArea.serieIndex); + if (serie == null || !serie.show || !markArea.show) return; + + UpdateRuntimeData(markArea); + + var colorIndex = chart.GetLegendRealShowNameIndex(serie.legendName); + var serieColor = SerieHelper.GetLineColor(serie, null, chart.theme, colorIndex, SerieState.Normal); + var areaColor = markArea.itemStyle.GetColor(serieColor); + UGL.DrawRectangle(vh, markArea.runtimeRect, areaColor, areaColor); + } + + private void UpdateRuntimeData(MarkArea markArea) + { + var serie = chart.GetSerie(markArea.serieIndex); + if (serie == null || !serie.show || !markArea.show) return; + var yAxis = chart.GetChartComponent<YAxis>(serie.yAxisIndex); + var xAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex); + var grid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + var showData = serie.GetDataList(dataZoom); + + var lt = GetPosition(markArea.start, serie, dataZoom, xAxis, yAxis, grid, showData, true); + var rb = GetPosition(markArea.end, serie, dataZoom, xAxis, yAxis, grid, showData, false); + var lb = new Vector3(lt.x, rb.y); + + markArea.runtimeRect = new Rect(lb.x, lb.y, rb.x - lb.x, lt.y - lb.y); + UpdateLabelPosition(markArea); + } + + private void UpdateLabelPosition(MarkArea markArea) + { + if (!markArea.label.show) return; + m_NeedUpdateLabelPosition = true; + var rect = markArea.runtimeRect; + switch (markArea.label.position) + { + case LabelStyle.Position.Center: + markArea.runtimeLabelPosition = rect.center; + break; + case LabelStyle.Position.Left: + markArea.runtimeLabelPosition = rect.center + new Vector2(rect.width / 2, 0); + break; + case LabelStyle.Position.Right: + markArea.runtimeLabelPosition = rect.center - new Vector2(rect.width / 2, 0); + break; + case LabelStyle.Position.Top: + markArea.runtimeLabelPosition = rect.center + new Vector2(0, rect.height / 2); + break; + case LabelStyle.Position.Bottom: + markArea.runtimeLabelPosition = rect.center - new Vector2(0, rect.height / 2); + break; + default: + markArea.runtimeLabelPosition = rect.center + new Vector2(0, rect.height / 2); + break; + } + markArea.runtimeLabelPosition += markArea.label.offset; + } + + private Vector3 GetPosition(MarkAreaData data, Serie serie, DataZoom dataZoom, XAxis xAxis, YAxis yAxis, + GridCoord grid, List<SerieData> showData, bool start) + { + var pos = Vector3.zero; + switch (data.type) + { + case MarkAreaType.Min: + data.runtimeValue = SerieHelper.GetMinData(serie, data.dimension, dataZoom); + return GetPosition(xAxis, yAxis, grid, data.runtimeValue, start); + case MarkAreaType.Max: + data.runtimeValue = SerieHelper.GetMaxData(serie, data.dimension, dataZoom); + return GetPosition(xAxis, yAxis, grid, data.runtimeValue, start); + case MarkAreaType.Average: + data.runtimeValue = SerieHelper.GetAverageData(serie, data.dimension, dataZoom); + return GetPosition(xAxis, yAxis, grid, data.runtimeValue, start); + case MarkAreaType.Median: + data.runtimeValue = SerieHelper.GetMedianData(serie, data.dimension, dataZoom); + return GetPosition(xAxis, yAxis, grid, data.runtimeValue, start); + case MarkAreaType.None: + if (data.xPosition != 0 || data.yPosition != 0) + { + var pX = grid.context.x + data.xPosition; + var pY = grid.context.y + data.yPosition; + return new Vector3(pX, pY); + } + else if (data.yValue != 0) + { + data.runtimeValue = data.yValue; + if (yAxis.IsCategory()) + { + var pY = AxisHelper.GetAxisPosition(grid, yAxis, data.yValue, showData.Count, dataZoom); + return start ? + new Vector3(grid.context.x, pY) : + new Vector3(grid.context.x + grid.context.width, pY); + } + else + { + return GetPosition(xAxis, yAxis, grid, data.runtimeValue, start); + } + } + else + { + data.runtimeValue = data.xValue; + if (xAxis.IsCategory()) + { + var pX = AxisHelper.GetAxisPosition(grid, xAxis, data.xValue, showData.Count, dataZoom); + return start ? new Vector3(pX, grid.context.y + grid.context.height) : + new Vector3(pX, grid.context.y); + } + else + { + return GetPosition(xAxis, yAxis, grid, data.xValue, start); + } + } + default: + break; + } + return pos; + } + + private Vector3 GetPosition(Axis xAxis, Axis yAxis, GridCoord grid, double value, bool start) + { + if (yAxis.IsCategory()) + { + var pX = AxisHelper.GetAxisPosition(grid, xAxis, value); + return start ? + new Vector3(pX, grid.context.y + grid.context.height) : + new Vector3(pX, grid.context.y); + } + else + { + var pY = AxisHelper.GetAxisPosition(grid, yAxis, value); + return start ? + new Vector3(grid.context.x, pY + grid.context.height) : + new Vector3(grid.context.x + grid.context.width, pY); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs.meta b/Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs.meta new file mode 100644 index 0000000..534da72 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkAreaHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5ffb2d23b0574e6eb5805a2f3783081 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkLine.cs b/Assets/XCharts/Runtime/Component/Mark/MarkLine.cs new file mode 100644 index 0000000..47f1e2b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkLine.cs @@ -0,0 +1,267 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// 标线类型 + /// </summary> + public enum MarkLineType + { + None, + /// <summary> + /// 最小值。 + /// </summary> + Min, + /// <summary> + /// 最大值。 + /// </summary> + Max, + /// <summary> + /// 平均值。 + /// </summary> + Average, + /// <summary> + /// 中位数。 + /// </summary> + Median + } + + /// <summary> + /// Use a line in the chart to illustrate. + /// ||图表标线。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(MarkLineHandler), true)] + public class MarkLine : MainComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private int m_SerieIndex = 0; + [SerializeField][Since("v3.9.0")] private bool m_OnTop = true; + [SerializeField] private AnimationStyle m_Animation = new AnimationStyle(); + [SerializeField] private List<MarkLineData> m_Data = new List<MarkLineData>(); + + /// <summary> + /// Whether to display the marking line. + /// ||是否显示标线。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// The serie index of markLine. + /// ||标线影响的Serie索引。 + /// </summary> + public int serieIndex + { + get { return m_SerieIndex; } + set { if (PropertyUtil.SetStruct(ref m_SerieIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// whether the markline is on top. + /// ||是否在最上层。 + /// </summary> + public bool onTop + { + get { return m_OnTop; } + set { if (PropertyUtil.SetStruct(ref m_OnTop, value)) SetVerticesDirty(); } + } + /// <summary> + /// The animation of markline. + /// ||标线的动画样式。 + /// </summary> + public AnimationStyle animation + { + get { return m_Animation; } + set { if (PropertyUtil.SetClass(ref m_Animation, value)) SetVerticesDirty(); } + } + /// <summary> + /// A list of marked data. When the group of data item is 0, each data item represents a line; + /// When the group is not 0, two data items of the same group represent the starting point and + /// the ending point of the line respectively to form a line. In this case, the relevant style + /// parameters of the line are the parameters of the starting point. + /// ||标线的数据列表。当数据项的group为0时,每个数据项表示一条标线;当group不为0时,相同group的两个数据项分别表 + /// 示标线的起始点和终止点来组成一条标线,此时标线的相关样式参数取起始点的参数。 + /// </summary> + public List<MarkLineData> data + { + get { return m_Data; } + set { if (PropertyUtil.SetClass(ref m_Data, value)) SetVerticesDirty(); } + } + + public override void SetDefaultValue() + { + data.Clear(); + var item = new MarkLineData(); + item.name = "average"; + item.type = MarkLineType.Average; + item.lineStyle.type = LineStyle.Type.Dashed; + item.lineStyle.color = Color.clear; + item.startSymbol.show = true; + item.startSymbol.type = SymbolType.Circle; + item.startSymbol.size = 4; + item.endSymbol.show = true; + item.endSymbol.type = SymbolType.Arrow; + item.endSymbol.size = 5; + item.label.show = true; + item.label.numericFormatter = "f1"; + item.label.formatter = "{c}"; + data.Add(item); + } + } + /// <summary> + /// Data of marking line. + /// ||图表标线的数据。 + /// </summary> + [System.Serializable] + public class MarkLineData : ChildComponent + { + [SerializeField] private MarkLineType m_Type = MarkLineType.None; + [SerializeField] private string m_Name; + [SerializeField] private int m_Dimension = 1; + [SerializeField] private float m_XPosition; + [SerializeField] private float m_YPosition; + [SerializeField] private double m_XValue; + [SerializeField] private double m_YValue; + [SerializeField] private int m_Group = 0; + [SerializeField] private bool m_ZeroPosition = false; + + [SerializeField] private SymbolStyle m_StartSymbol = new SymbolStyle(); + [SerializeField] private SymbolStyle m_EndSymbol = new SymbolStyle(); + [SerializeField] private LineStyle m_LineStyle = new LineStyle(); + [SerializeField] private LabelStyle m_Label = new LabelStyle(); + //[SerializeField] private Emphasis m_Emphasis = new Emphasis(); + + public Vector3 runtimeStartPosition { get; internal set; } + public Vector3 runtimeEndPosition { get; internal set; } + public Vector3 runtimeCurrentEndPosition { get; internal set; } + public ChartLabel runtimeLabel { get; internal set; } + public double runtimeValue { get; internal set; } + public bool runtimeInGrid { get; internal set; } + + /// <summary> + /// Name of the marker, which will display as a label. + /// ||标线名称,将会作为文字显示。label的formatter可通过{b}显示名称,通过{c}显示数值。 + /// </summary> + public string name + { + get { return m_Name; } + set { if (PropertyUtil.SetClass(ref m_Name, value)) SetVerticesDirty(); } + } + /// <summary> + /// Special label types, are used to label maximum value, minimum value and so on. + /// ||特殊的标线类型,用于标注最大值最小值等。 + /// </summary> + public MarkLineType type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetVerticesDirty(); } + } + /// <summary> + /// From which dimension of data to calculate the maximum and minimum value and so on. + /// ||从哪个维度的数据计算最大最小值等。 + /// </summary> + public int dimension + { + get { return m_Dimension; } + set { if (PropertyUtil.SetStruct(ref m_Dimension, value)) SetVerticesDirty(); } + } + /// <summary> + /// The x coordinate relative to the origin, in pixels. + /// ||相对原点的 x 坐标,单位像素。当type为None时有效。 + /// </summary> + public float xPosition + { + get { return m_XPosition; } + set { if (PropertyUtil.SetStruct(ref m_XPosition, value)) SetVerticesDirty(); } + } + /// <summary> + /// The y coordinate relative to the origin, in pixels. + /// ||相对原点的 y 坐标,单位像素。当type为None时有效。 + /// </summary> + public float yPosition + { + get { return m_YPosition; } + set { if (PropertyUtil.SetStruct(ref m_YPosition, value)) SetVerticesDirty(); } + } + /// <summary> + /// The value specified on the X-axis. A value specified when the X-axis is the category axis represents the index of the category axis data, otherwise a specific value. + /// ||X轴上的指定值。当X轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 + /// </summary> + public double xValue + { + get { return m_XValue; } + set { if (PropertyUtil.SetStruct(ref m_XValue, value)) SetVerticesDirty(); } + } + /// <summary> + /// That's the value on the Y-axis. The value specified when the Y axis is the category axis represents the index of the category axis data, otherwise the specific value. + /// ||Y轴上的指定值。当Y轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 + /// </summary> + public double yValue + { + get { return m_YValue; } + set { if (PropertyUtil.SetStruct(ref m_YValue, value)) SetVerticesDirty(); } + } + /// <summary> + /// Grouping. When the group is not 0, it means that this data is the starting point or end point of the marking line. Data consistent with the group form a marking line. + /// ||分组。当group不为0时,表示这个data是标线的起点或终点,group一致的data组成一条标线。 + /// </summary> + public int group + { + get { return m_Group; } + set { if (PropertyUtil.SetStruct(ref m_Group, value)) SetVerticesDirty(); } + } + /// <summary> + /// Is the origin of the coordinate system. + /// ||是否为坐标系原点。 + /// </summary> + public bool zeroPosition + { + get { return m_ZeroPosition; } + set { if (PropertyUtil.SetStruct(ref m_ZeroPosition, value)) SetVerticesDirty(); } + } + /// <summary> + /// The symbol of the start point of markline. + /// ||起始点的图形标记。 + /// </summary> + public SymbolStyle startSymbol + { + get { return m_StartSymbol; } + set { if (PropertyUtil.SetClass(ref m_StartSymbol, value)) SetVerticesDirty(); } + } + /// <summary> + /// The symbol of the end point of markline. + /// ||结束点的图形标记。 + /// </summary> + public SymbolStyle endSymbol + { + get { return m_EndSymbol; } + set { if (PropertyUtil.SetClass(ref m_EndSymbol, value)) SetVerticesDirty(); } + } + /// <summary> + /// The line style of markline. + /// ||标线样式。 + /// </summary> + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (PropertyUtil.SetClass(ref m_LineStyle, value)) SetVerticesDirty(); } + } + /// <summary> + /// Text styles of label. You can set position to Start, Middle, and End to display text in different locations. + /// ||文本样式。可设置position为Start、Middle和End在不同的位置显示文本。 + /// </summary> + public LabelStyle label + { + get { return m_Label; } + set { if (PropertyUtil.SetClass(ref m_Label, value)) SetVerticesDirty(); } + } + // public Emphasis emphasis + // { + // get { return m_Emphasis; } + // set { if (PropertyUtil.SetClass(ref m_Emphasis, value)) SetVerticesDirty(); } + // } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkLine.cs.meta b/Assets/XCharts/Runtime/Component/Mark/MarkLine.cs.meta new file mode 100644 index 0000000..5bf2ec6 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e728b47a96c74b3f986d9abe3b03934 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs b/Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs new file mode 100644 index 0000000..ccf6cf1 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs @@ -0,0 +1,326 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class MarkLineHandler : MainComponentHandler<MarkLine> + { + private GameObject m_MarkLineLabelRoot; + private bool m_RefreshLabel = false; + + public override void InitComponent() + { + m_MarkLineLabelRoot = ChartHelper.AddObject("markline", chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + m_MarkLineLabelRoot.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(m_MarkLineLabelRoot); + InitMarkLine(component); + } + + public override void DrawBase(VertexHelper vh) + { + if (!component.onTop) + DrawMarkLine(vh, component); + } + + public override void DrawUpper(VertexHelper vh) + { + if (component.onTop) + DrawMarkLine(vh, component); + } + + public override void Update() + { + if (m_RefreshLabel) + { + m_RefreshLabel = false; + var serie = chart.GetSerie(component.serieIndex); + if (!serie.show || !component.show) return; + foreach (var data in component.data) + { + if (data.runtimeLabel != null) + { + var pos = MarkLineHelper.GetLabelPosition(data); + data.runtimeLabel.SetActive(data.label.show && data.runtimeInGrid); + data.runtimeLabel.SetPosition(pos); + data.runtimeLabel.SetText(MarkLineHelper.GetFormatterContent(serie, data)); + } + } + } + } + + private void InitMarkLine(MarkLine markLine) + { + var serie = chart.GetSerie(markLine.serieIndex); + if (!serie.show || !markLine.show) return; + ResetTempMarkLineGroupData(markLine); + var serieColor = (Color)chart.GetItemColor(serie); + if (m_TempGroupData.Count > 0) + { + foreach (var kv in m_TempGroupData) + { + if (kv.Value.Count >= 2) + { + var data = kv.Value[0]; + InitMarkLineLabel(serie, data, serieColor); + } + } + } + foreach (var data in markLine.data) + { + if (data.group != 0) continue; + InitMarkLineLabel(serie, data, serieColor); + } + } + + private void InitMarkLineLabel(Serie serie, MarkLineData data, Color serieColor) + { + data.painter = chart.m_PainterUpper; + data.refreshComponent = delegate () + { + var textName = string.Format("markLine_{0}_{1}", component.index, data.index); + var content = MarkLineHelper.GetFormatterContent(serie, data); + var label = ChartHelper.AddChartLabel(textName, m_MarkLineLabelRoot.transform, data.label, chart.theme.axis, + content, Color.clear, TextAnchor.MiddleCenter); + var pos = MarkLineHelper.GetLabelPosition(data); + label.SetIconActive(false); + label.SetActive(false, true); + label.SetPosition(pos); + data.runtimeLabel = label; + }; + data.refreshComponent(); + } + + private Dictionary<int, List<MarkLineData>> m_TempGroupData = new Dictionary<int, List<MarkLineData>>(); + private void DrawMarkLine(VertexHelper vh, MarkLine markLine) + { + var serie = chart.GetSerie(markLine.serieIndex); + if (!serie.show || !markLine.show) return; + if (markLine.data.Count == 0) return; + var yAxis = chart.GetChartComponent<YAxis>(serie.yAxisIndex); + var xAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex); + var grid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + var animation = markLine.animation; + var showData = serie.GetDataList(dataZoom); + var sp = Vector3.zero; + var ep = Vector3.zero; + var colorIndex = chart.GetLegendRealShowNameIndex(serie.serieName); + var serieColor = SerieHelper.GetLineColor(serie, null, chart.theme, colorIndex, SerieState.Normal); + animation.InitProgress(0, 1f); + ResetTempMarkLineGroupData(markLine); + if (m_TempGroupData.Count > 0) + { + foreach (var kv in m_TempGroupData) + { + if (kv.Value.Count >= 2) + { + sp = GetSinglePos(xAxis, yAxis, grid, serie, dataZoom, kv.Value[0], showData.Count); + ep = GetSinglePos(xAxis, yAxis, grid, serie, dataZoom, kv.Value[1], showData.Count); + kv.Value[0].runtimeStartPosition = sp; + kv.Value[1].runtimeEndPosition = ep; + DrawMakLineData(vh, kv.Value[0], animation, serie, grid, serieColor, sp, ep); + } + } + } + foreach (var data in markLine.data) + { + if (data.group != 0) continue; + switch (data.type) + { + case MarkLineType.Min: + data.runtimeValue = SerieHelper.GetMinData(serie, data.dimension, dataZoom); + GetStartEndPos(yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.Max: + data.runtimeValue = SerieHelper.GetMaxData(serie, data.dimension, dataZoom); + GetStartEndPos(yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.Average: + data.runtimeValue = SerieHelper.GetAverageData(serie, data.dimension, dataZoom); + GetStartEndPos(yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.Median: + data.runtimeValue = SerieHelper.GetMedianData(serie, data.dimension, dataZoom); + GetStartEndPos(yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.None: + if (data.xPosition != 0) + { + data.runtimeValue = data.xPosition; + var pX = grid.context.x + data.xPosition; + sp = new Vector3(pX, grid.context.y); + ep = new Vector3(pX, grid.context.y + grid.context.height); + } + else if (data.yPosition != 0) + { + data.runtimeValue = data.yPosition; + var pY = grid.context.y + data.yPosition; + sp = new Vector3(grid.context.x, pY); + ep = new Vector3(grid.context.x + grid.context.width, pY); + } + else if (data.yValue != 0 || (data.xValue == 0 && data.yValue == 0 && yAxis.IsValue())) + { + data.runtimeValue = data.yValue; + if (yAxis.IsCategory()) + { + var pY = AxisHelper.GetAxisPosition(grid, yAxis, data.yValue, showData.Count, dataZoom); + sp = new Vector3(grid.context.x, pY); + ep = new Vector3(grid.context.x + grid.context.width, pY); + } + else + { + GetStartEndPos(yAxis, grid, data.yValue, ref sp, ref ep); + } + } + else + { + data.runtimeValue = data.xValue; + if (xAxis.IsCategory()) + { + var pX = AxisHelper.GetAxisPosition(grid, xAxis, data.xValue, showData.Count, dataZoom); + sp = new Vector3(pX, grid.context.y); + ep = new Vector3(pX, grid.context.y + grid.context.height); + } + else + { + GetStartEndPos(xAxis, grid, data.xValue, ref sp, ref ep); + } + } + break; + default: + break; + } + data.runtimeStartPosition = sp; + data.runtimeEndPosition = ep; + DrawMakLineData(vh, data, animation, serie, grid, serieColor, sp, ep); + } + if (!animation.IsFinish()) + { + animation.CheckProgress(1f); + chart.RefreshTopPainter(); + } + } + + private void ResetTempMarkLineGroupData(MarkLine markLine) + { + m_TempGroupData.Clear(); + for (int i = 0; i < markLine.data.Count; i++) + { + var data = markLine.data[i]; + data.index = i; + if (data.group == 0) continue; + if (!m_TempGroupData.ContainsKey(data.group)) + { + m_TempGroupData[data.group] = new List<MarkLineData>(); + } + m_TempGroupData[data.group].Add(data); + } + } + + private void DrawMakLineData(VertexHelper vh, MarkLineData data, AnimationStyle animation, Serie serie, + GridCoord grid, Color32 serieColor, Vector3 sp, Vector3 ep) + { + if (!animation.IsFinish()) + ep = Vector3.Lerp(sp, ep, animation.GetCurrDetail()); + if ((!chart.IsInChart(sp) && !chart.IsInChart(ep)) || + (serie.clip && !grid.Contains(sp) && !grid.Contains(ep))) + { + data.runtimeInGrid = false; + m_RefreshLabel = true; + return; + } + data.runtimeCurrentEndPosition = ep; + if (sp != Vector3.zero || ep != Vector3.zero) + { + data.runtimeInGrid = true; + m_RefreshLabel = true; + chart.ClampInChart(ref sp); + chart.ClampInChart(ref ep); + var theme = chart.theme.axis; + var lineColor = ChartHelper.IsClearColor(data.lineStyle.color) ? serieColor : data.lineStyle.color; + var lineWidth = data.lineStyle.width == 0 ? theme.lineWidth : data.lineStyle.width; + ChartDrawer.DrawLineStyle(vh, data.lineStyle, sp, ep, lineWidth, LineStyle.Type.Dashed, lineColor, lineColor); + if (data.startSymbol != null && data.startSymbol.show) + { + DrawMarkLineSymbol(vh, data.startSymbol, serie, grid, chart.theme, sp, sp, lineColor); + } + if (data.endSymbol != null && data.endSymbol.show) + { + DrawMarkLineSymbol(vh, data.endSymbol, serie, grid, chart.theme, ep, sp, lineColor); + } + } + } + + private void DrawMarkLineSymbol(VertexHelper vh, SymbolStyle symbol, Serie serie, GridCoord grid, ThemeStyle theme, + Vector3 pos, Vector3 startPos, Color32 lineColor) + { + float tickness = 0f; + float[] cornerRadius = null; + Color32 borderColor; + SerieHelper.GetSymbolInfo(out borderColor, out tickness, out cornerRadius, serie, null, chart.theme); + chart.DrawClipSymbol(vh, symbol.type, symbol.size, tickness, pos, lineColor, lineColor, + ColorUtil.clearColor32, borderColor, symbol.gap, serie.clip, cornerRadius, grid, startPos); + } + + private void GetStartEndPos(Axis xAxis, GridCoord grid, double value, ref Vector3 sp, ref Vector3 ep) + { + if (xAxis is YAxis) + { + var pY = AxisHelper.GetAxisPosition(grid, xAxis, value); + sp = new Vector3(grid.context.x, pY); + ep = new Vector3(grid.context.x + grid.context.width, pY); + } + else + { + var pX = AxisHelper.GetAxisPosition(grid, xAxis, value); + sp = new Vector3(pX, grid.context.y); + ep = new Vector3(pX, grid.context.y + grid.context.height); + } + } + + private float GetAxisPosition(GridCoord grid, Axis axis, DataZoom dataZoom, int dataCount, double value) + { + return AxisHelper.GetAxisPosition(grid, axis, value, dataCount, dataZoom); + } + + private Vector3 GetSinglePos(Axis xAxis, Axis yAxis, GridCoord grid, Serie serie, DataZoom dataZoom, MarkLineData data, + int serieDataCount) + { + switch (data.type) + { + case MarkLineType.Min: + var serieData = SerieHelper.GetMinSerieData(serie, data.dimension, dataZoom); + data.runtimeValue = serieData.GetData(data.dimension); + var pX = GetAxisPosition(grid, xAxis, dataZoom, serieDataCount, serieData.index); + var pY = GetAxisPosition(grid, yAxis, dataZoom, serieDataCount, data.runtimeValue); + return new Vector3(pX, pY); + case MarkLineType.Max: + serieData = SerieHelper.GetMaxSerieData(serie, data.dimension, dataZoom); + data.runtimeValue = serieData.GetData(data.dimension); + pX = GetAxisPosition(grid, xAxis, dataZoom, serieDataCount, serieData.index); + pY = GetAxisPosition(grid, yAxis, dataZoom, serieDataCount, data.runtimeValue); + return new Vector3(pX, pY); + case MarkLineType.None: + if (data.zeroPosition) + { + data.runtimeValue = 0; + return grid.context.position; + } + else + { + pX = data.xPosition != 0 ? grid.context.x + data.xPosition : + GetAxisPosition(grid, xAxis, dataZoom, serieDataCount, data.xValue); + pY = data.yPosition != 0 ? grid.context.y + data.yPosition : + GetAxisPosition(grid, yAxis, dataZoom, serieDataCount, data.yValue); + data.runtimeValue = data.yValue; + return new Vector3(pX, pY); + } + default: + return grid.context.position; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs.meta b/Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs.meta new file mode 100644 index 0000000..0b0c3dd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkLineHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faa35bab8fc6e42d5b5d19731c1a20a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs b/Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs new file mode 100644 index 0000000..4a1c5dc --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs @@ -0,0 +1,52 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + internal static class MarkLineHelper + { + public static string GetFormatterContent(Serie serie, MarkLineData data) + { + var serieLabel = data.label; + var numericFormatter = serieLabel.numericFormatter; + if (string.IsNullOrEmpty(serieLabel.formatter)) + { + var content = ChartCached.NumberToStr(data.runtimeValue, numericFormatter); + return serieLabel.formatterFunction == null? content: + serieLabel.formatterFunction(data.index, data.runtimeValue, null, content); + } + else + { + var content = serieLabel.formatter; + FormatterHelper.ReplaceSerieLabelContent(ref content, numericFormatter, serie.dataCount, data.runtimeValue, + 0, serie.serieName, data.name, data.name, Color.clear, null); + return serieLabel.formatterFunction == null? content: + serieLabel.formatterFunction(data.index, data.runtimeValue, null, content); + } + } + + public static Vector3 GetLabelPosition(MarkLineData data) + { + if (!data.label.show) return Vector3.zero; + var dir = (data.runtimeEndPosition - data.runtimeStartPosition).normalized; + var horizontal = Mathf.Abs(Vector3.Dot(dir, Vector3.right)) == 1; + var labelWidth = data.runtimeLabel == null ? 50 : data.runtimeLabel.GetTextWidth(); + var labelHeight = data.runtimeLabel == null ? 20 : data.runtimeLabel.GetTextHeight(); + switch (data.label.position) + { + case LabelStyle.Position.Start: + if (data.runtimeStartPosition == Vector3.zero) return Vector3.zero; + if (horizontal) return data.runtimeStartPosition + data.label.offset + labelWidth / 2 * Vector3.left; + else return data.runtimeStartPosition + data.label.offset + labelHeight / 2 * Vector3.down; + case LabelStyle.Position.Middle: + if (data.runtimeCurrentEndPosition == Vector3.zero) return Vector3.zero; + var center = (data.runtimeStartPosition + data.runtimeCurrentEndPosition) / 2; + if (horizontal) return center + data.label.offset + labelHeight / 2 * Vector3.up; + else return center + data.label.offset + labelWidth / 2 * Vector3.right; + default: + if (data.runtimeCurrentEndPosition == Vector3.zero) return Vector3.zero; + if (horizontal) return data.runtimeCurrentEndPosition + data.label.offset + labelWidth / 2 * Vector3.right; + else return data.runtimeCurrentEndPosition + data.label.offset + labelHeight / 2 * Vector3.up; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs.meta b/Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs.meta new file mode 100644 index 0000000..e95ca4b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Mark/MarkLineHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b472a7e4755b74fb6a3ec2c410650833 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Radar.meta b/Assets/XCharts/Runtime/Component/Radar.meta new file mode 100644 index 0000000..eae8071 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fb4a3817487149f680a509a5247105e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs b/Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs new file mode 100644 index 0000000..222a240 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs @@ -0,0 +1,493 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// Radar coordinate conponnet for radar charts. + /// 雷达图坐标系组件,只适用于雷达图。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(RadarCoordHandler), true)] + [CoordOptions(typeof(RadarCoord))] + public class RadarCoord : CoordSystem, ISerieContainer + { + /// <summary> + /// Radar render type, in which 'Polygon' and 'Circle' are supported. + /// ||雷达图绘制类型,支持 'Polygon' 和 'Circle'。 + /// </summary> + public enum Shape + { + Polygon, + Circle + } + /// <summary> + /// The position type of radar. + /// ||显示位置。 + /// </summary> + public enum PositionType + { + /// <summary> + /// Display at the vertex. + /// ||显示在顶点处。 + /// </summary> + Vertice, + /// <summary> + /// Display at the middle of line. + /// ||显示在两者之间。 + /// </summary> + Between, + } + /// <summary> + /// Indicator of radar chart, which is used to assign multiple variables(dimensions) in radar chart. + /// ||雷达图的指示器,用来指定雷达图中的多个变量(维度)。 + /// </summary> + [System.Serializable] + public class Indicator + { + [SerializeField] private string m_Name; + [SerializeField] private double m_Max; + [SerializeField] private double m_Min; + [SerializeField] private double[] m_Range = new double[2] { 0, 0 }; + + /// <summary> + /// The name of indicator. + /// ||指示器名称。 + /// </summary> + public string name { get { return m_Name; } set { m_Name = value; } } + /// <summary> + /// The maximum value of indicator, with default value of 0, but we recommend to set it manually. + /// ||指示器的最大值,默认为 0 无限制。 + /// </summary> + public double max { get { return m_Max; } set { m_Max = value; } } + /// <summary> + /// The minimum value of indicator, with default value of 0. + /// ||指示器的最小值,默认为 0 无限制。 + /// </summary> + public double min { get { return m_Min; } set { m_Min = value; } } + /// <summary> + /// the text conponent of indicator. + /// ||指示器的文本组件。 + /// </summary> + public Text text { get; set; } + /// <summary> + /// Normal range. When the value is outside this range, the display color is automatically changed. + /// ||正常值范围。当数值不在这个范围时,会自动变更显示颜色。 + /// </summary> + public double[] range + { + get { return m_Range; } + set { if (value != null && value.Length == 2) { m_Range = value; } } + } + + public bool IsInRange(double value) + { + if (m_Range == null || m_Range.Length < 2) return true; + if (m_Range[0] != 0 || m_Range[1] != 0) + return value >= m_Range[0] && value <= m_Range[1]; + else + return true; + } + } + + [SerializeField] private bool m_Show; + [SerializeField] private Shape m_Shape; + [SerializeField] private float m_Radius = 100; + [SerializeField] private int m_SplitNumber = 5; + [SerializeField] private float[] m_Center = new float[2] { 0.5f, 0.5f }; + [SerializeField] private AxisLine m_AxisLine = AxisLine.defaultAxisLine; + [SerializeField] private AxisName m_AxisName = AxisName.defaultAxisName; + [SerializeField] private AxisSplitLine m_SplitLine = AxisSplitLine.defaultSplitLine; + [SerializeField] private AxisSplitArea m_SplitArea = AxisSplitArea.defaultSplitArea; + [SerializeField] private bool m_Indicator = true; + [SerializeField] private PositionType m_PositionType = PositionType.Vertice; + [SerializeField] private float m_IndicatorGap = 10; + [SerializeField] private double m_CeilRate = 0; + [SerializeField] private bool m_IsAxisTooltip; + [SerializeField] private Color32 m_OutRangeColor = Color.red; + [SerializeField] private bool m_ConnectCenter = false; + [SerializeField] private bool m_LineGradient = true; + [SerializeField][Since("v3.4.0")] private float m_StartAngle; + [SerializeField][Since("v3.8.0")] private int m_GridIndex = -1; + [SerializeField] private List<Indicator> m_IndicatorList = new List<Indicator>(); + + public RadarCoordContext context = new RadarCoordContext(); + + /// <summary> + /// [default:true] + /// Set this to false to prevent the radar from showing. + /// ||是否显示雷达坐标系组件。 + /// </summary> + public bool show { get { return m_Show; } set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } } + /// <summary> + /// Index of layout component that serie uses. Default is -1 means not use layout, otherwise use the first layout component. + /// ||所使用的 layout 组件的 index。 默认为-1不指定index, 当为大于或等于0时, 为第一个layout组件的第index个格子。 + /// </summary> + public int gridIndex + { + get { return m_GridIndex; } + set { if (PropertyUtil.SetStruct(ref m_GridIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// Radar render type, in which 'Polygon' and 'Circle' are supported. + /// ||雷达图绘制类型,支持 'Polygon' 和 'Circle'。 + /// </summary> + public Shape shape + { + get { return m_Shape; } + set { if (PropertyUtil.SetStruct(ref m_Shape, value)) SetAllDirty(); } + } + /// <summary> + /// the radius of radar. + /// ||雷达图的半径。 + /// </summary> + public float radius + { + get { return m_Radius; } + set { if (PropertyUtil.SetStruct(ref m_Radius, value)) SetAllDirty(); } + } + /// <summary> + /// Segments of indicator axis. + /// ||指示器轴的分割段数。 + /// </summary> + public int splitNumber + { + get { return m_SplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value)) SetAllDirty(); } + } + /// <summary> + /// the center of radar chart. + /// ||雷达图的中心点。数组的第一项是横坐标,第二项是纵坐标。 + /// 当值为0-1之间时表示百分比,设置成百分比时第一项是相对于容器宽度,第二项是相对于容器高度。 + /// </summary> + public float[] center + { + get { return m_Center; } + set { if (value != null) { m_Center = value; SetAllDirty(); } } + } + /// <summary> + /// axis line. + /// ||轴线。 + /// </summary> + public AxisLine axisLine + { + get { return m_AxisLine; } + set { if (PropertyUtil.SetClass(ref m_AxisLine, value, true)) SetAllDirty(); } + } + /// <summary> + /// Name options for radar indicators. + /// ||雷达图每个指示器名称的配置项。 + /// </summary> + public AxisName axisName + { + get { return m_AxisName; } + set { if (PropertyUtil.SetClass(ref m_AxisName, value, true)) SetAllDirty(); } + } + /// <summary> + /// split line. + /// ||分割线。 + /// </summary> + public AxisSplitLine splitLine + { + get { return m_SplitLine; } + set { if (PropertyUtil.SetClass(ref m_SplitLine, value, true)) SetAllDirty(); } + } + /// <summary> + /// Split area of axis in grid area. + /// ||分割区域。 + /// </summary> + public AxisSplitArea splitArea + { + get { return m_SplitArea; } + set { if (PropertyUtil.SetClass(ref m_SplitArea, value, true)) SetAllDirty(); } + } + /// <summary> + /// Whether to show indicator. + /// ||是否显示指示器。 + /// </summary> + public bool indicator + { + get { return m_Indicator; } + set { if (PropertyUtil.SetStruct(ref m_Indicator, value)) SetComponentDirty(); } + } + /// <summary> + /// The gap of indicator and radar. + /// ||指示器和雷达的间距。 + /// </summary> + public float indicatorGap + { + get { return m_IndicatorGap; } + set { if (PropertyUtil.SetStruct(ref m_IndicatorGap, value)) SetComponentDirty(); } + } + /// <summary> + /// The ratio of maximum and minimum values rounded upward. The default is 0, which is automatically calculated. + /// ||最大最小值向上取整的倍率。默认为0时自动计算。 + /// </summary> + public double ceilRate + { + get { return m_CeilRate; } + set { if (PropertyUtil.SetStruct(ref m_CeilRate, value < 0 ? 0 : value)) SetAllDirty(); } + } + /// <summary> + /// 是否Tooltip显示轴线上的所有数据。 + /// </summary> + public bool isAxisTooltip + { + get { return m_IsAxisTooltip; } + set { if (PropertyUtil.SetStruct(ref m_IsAxisTooltip, value)) SetAllDirty(); } + } + /// <summary> + /// The position type of indicator. + /// ||显示位置类型。 + /// </summary> + public PositionType positionType + { + get { return m_PositionType; } + set { if (PropertyUtil.SetStruct(ref m_PositionType, value)) SetAllDirty(); } + } + /// <summary> + /// The color displayed when data out of range. + /// ||数值超出范围时显示的颜色。 + /// </summary> + public Color32 outRangeColor + { + get { return m_OutRangeColor; } + set { if (PropertyUtil.SetStruct(ref m_OutRangeColor, value)) SetAllDirty(); } + } + /// <summary> + /// Whether serie data connect to radar center with line. + /// ||数值是否连线到中心点。 + /// </summary> + public bool connectCenter + { + get { return m_ConnectCenter; } + set { if (PropertyUtil.SetStruct(ref m_ConnectCenter, value)) SetAllDirty(); } + } + /// <summary> + /// Whether need gradient for data line. + /// ||数值线段是否需要渐变。 + /// </summary> + public bool lineGradient + { + get { return m_LineGradient; } + set { if (PropertyUtil.SetStruct(ref m_LineGradient, value)) SetAllDirty(); } + } + /// <summary> + /// 起始角度。和时钟一样,12点钟位置是0度,顺时针到360度。 + /// </summary> + public float startAngle + { + get { return m_StartAngle; } + set { if (PropertyUtil.SetStruct(ref m_StartAngle, value)) SetVerticesDirty(); } + } + /// <summary> + /// the indicator list. + /// ||指示器列表。 + /// </summary> + public List<Indicator> indicatorList { get { return m_IndicatorList; } } + + public bool IsPointerEnter() + { + return context.isPointerEnter; + } + + public override void SetDefaultValue() + { + m_Show = true; + m_GridIndex = -1; + m_Shape = Shape.Polygon; + m_Radius = 0.35f; + m_SplitNumber = 5; + m_Indicator = true; + m_IndicatorList = new List<Indicator>(5) + { + new Indicator() { name = "indicator1", max = 0 }, + new Indicator() { name = "indicator2", max = 0 }, + new Indicator() { name = "indicator3", max = 0 }, + new Indicator() { name = "indicator4", max = 0 }, + new Indicator() { name = "indicator5", max = 0 }, + }; + center[0] = 0.5f; + center[1] = 0.4f; + splitLine.show = true; + splitArea.show = true; + axisName.show = true; + axisName.name = null; + } + + private bool IsEqualsIndicatorList(List<Indicator> indicators1, List<Indicator> indicators2) + { + if (indicators1.Count != indicators2.Count) return false; + for (int i = 0; i < indicators1.Count; i++) + { + var indicator1 = indicators1[i]; + var indicator2 = indicators2[i]; + if (!indicator1.Equals(indicator2)) return false; + } + return true; + } + + public bool IsInIndicatorRange(int index, double value) + { + var indicator = GetIndicator(index); + return indicator == null ? true : indicator.IsInRange(value); + } + + public double GetIndicatorMin(int index) + { + if (index >= 0 && index < m_IndicatorList.Count) + { + return m_IndicatorList[index].min; + } + return 0; + } + public double GetIndicatorMax(int index) + { + if (index >= 0 && index < m_IndicatorList.Count) + { + return m_IndicatorList[index].max; + } + return 0; + } + + internal void UpdateRadarCenter(BaseChart chart) + { + if (center.Length < 2) return; + var chartPosition = chart.chartPosition; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + if (gridIndex >= 0) + { + var layout = chart.GetChartComponent<GridLayout>(0); + if (layout != null) + { + layout.UpdateRuntimeData(chart); + layout.UpdateGridContext(gridIndex, ref chartPosition, ref chartWidth, ref chartHeight); + } + } + var centerX = center[0] <= 1 ? chartWidth * center[0] : center[0]; + var centerY = center[1] <= 1 ? chartHeight * center[1] : center[1]; + context.center = chartPosition + new Vector3(centerX, centerY); + if (radius <= 0) + { + context.radius = 0; + } + else if (radius <= 1) + { + context.radius = Mathf.Min(chartWidth, chartHeight) * radius; + } + else + { + context.radius = radius; + } + if (shape == RadarCoord.Shape.Polygon && positionType == PositionType.Between) + { + var angle = Mathf.PI / indicatorList.Count; + context.dataRadius = context.radius * Mathf.Cos(angle); + } + else + { + context.dataRadius = context.radius; + } + } + + public Vector3 GetIndicatorPosition(int index) + { + int indicatorNum = indicatorList.Count; + var angle = 0f; + switch (positionType) + { + case PositionType.Vertice: + angle = 2 * Mathf.PI / indicatorNum * index; + break; + case PositionType.Between: + angle = 2 * Mathf.PI / indicatorNum * (index + 0.5f); + break; + } + angle += startAngle * Mathf.PI / 180; + var x = context.center.x + (context.radius + indicatorGap) * Mathf.Sin(angle); + var y = context.center.y + (context.radius + indicatorGap) * Mathf.Cos(angle); + return new Vector3(x, y); + } + + public void AddIndicator(RadarCoord.Indicator indicator) + { + indicatorList.Add(indicator); + SetAllDirty(); + } + + public RadarCoord.Indicator AddIndicator(string name, double min, double max) + { + var indicator = new RadarCoord.Indicator(); + indicator.name = name; + indicator.min = min; + indicator.max = max; + indicatorList.Add(indicator); + SetAllDirty(); + return indicator; + } + + [Since("v3.3.0")] + public void AddIndicatorList(List<string> nameList, double min = 0, double max = 0) + { + foreach (var name in nameList) + AddIndicator(name, min, max); + } + + public bool UpdateIndicator(int indicatorIndex, string name, double min, double max) + { + var indicator = GetIndicator(indicatorIndex); + if (indicator == null) return false; + indicator.name = name; + indicator.min = min; + indicator.max = max; + SetAllDirty(); + return true; + } + + public RadarCoord.Indicator GetIndicator(int indicatorIndex) + { + if (indicatorIndex < 0 || indicatorIndex > indicatorList.Count - 1) return null; + return indicatorList[indicatorIndex]; + } + + public string GetIndicatorName(int indicatorIndex) + { + var indicator = GetIndicator(indicatorIndex); + if (indicator == null) return string.Empty; + return indicator.name; + } + + public override void ClearData() + { + indicatorList.Clear(); + } + + public string GetFormatterIndicatorContent(int indicatorIndex, int totalIndex) + { + var indicator = GetIndicator(indicatorIndex); + if (indicator == null) + return string.Empty; + else + return GetFormatterIndicatorContent(indicator.name, indicatorIndex, totalIndex); + } + + public string GetFormatterIndicatorContent(string indicatorName, int index, int totalIndex) + { + if (string.IsNullOrEmpty(indicatorName)) + return indicatorName; + + if (string.IsNullOrEmpty(m_AxisName.labelStyle.formatter)) + { + return indicatorName; + } + else + { + var content = m_AxisName.labelStyle.formatter; + FormatterHelper.ReplaceAxisLabelContent(ref content, indicatorName, index, totalIndex); + return content; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs.meta b/Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs.meta new file mode 100644 index 0000000..6d3d5aa --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar/RadarCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 876512c564bd144be99d0acbe079cf8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs b/Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs new file mode 100644 index 0000000..e30da0c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public class RadarCoordContext : MainComponentContext + { + /// <summary> + /// the center position of radar in container. + /// ||雷达图在容器中的具体中心点。 + /// </summary> + public Vector3 center { get; internal set; } + /// <summary> + /// the true radius of radar. + /// ||雷达图的运行时实际半径。 + /// </summary> + public float radius { get; internal set; } + public float dataRadius { get; internal set; } + public bool isPointerEnter { get; set; } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs.meta b/Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs.meta new file mode 100644 index 0000000..147702f --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar/RadarCoordContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f7419e8466e048cb9689ab85d20e4de +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs b/Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs new file mode 100644 index 0000000..abf5b43 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class RadarCoordHandler : MainComponentHandler<RadarCoord> + { + private const string INDICATOR_TEXT = "indicator"; + + public override void InitComponent() + { + InitRadarCoord(component); + } + + public override void Update() + { + base.Update(); + if (!chart.isPointerInChart) + { + component.context.isPointerEnter = false; + return; + } + var radar = component; + radar.context.isPointerEnter = radar.show && + Vector3.Distance(radar.context.center, chart.pointerPos) <= radar.context.radius; + } + + public override void DrawBase(VertexHelper vh) + { + DrawRadarCoord(vh, component); + } + + private void InitRadarCoord(RadarCoord radar) + { + float txtHig = 20; + radar.painter = chart.GetPainter(radar.index); + radar.refreshComponent = delegate() + { + radar.UpdateRadarCenter(chart); + var radarObject = ChartHelper.AddObject("Radar" + radar.index, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + radar.gameObject = radarObject; + radar.gameObject.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(radarObject.transform, INDICATOR_TEXT); + for (int i = 0; i < radar.indicatorList.Count; i++) + { + var indicator = radar.indicatorList[i]; + var pos = radar.GetIndicatorPosition(i); + var objName = INDICATOR_TEXT + "_" + i; + var content = radar.GetFormatterIndicatorContent(i, radar.indicatorList.Count); + var label = ChartHelper.AddChartLabel(objName, radarObject.transform, radar.axisName.labelStyle, + chart.theme.common, content, Color.clear, TextAnchor.MiddleCenter); + label.SetActive(radar.axisName.show && radar.indicator && radar.axisName.labelStyle.show, true); + AxisHelper.AdjustCircleLabelPos(label, pos, radar.context.center, txtHig, radar.axisName.labelStyle.offset); + } + chart.RefreshBasePainter(); + }; + radar.refreshComponent.Invoke(); + } + + private void DrawRadarCoord(VertexHelper vh, RadarCoord radar) + { + if (!radar.show) return; + radar.UpdateRadarCenter(chart); + if (radar.shape == RadarCoord.Shape.Circle) + { + DrawCricleRadar(vh, radar); + } + else + { + DrawPolygonRadar(vh, radar); + } + } + + private void DrawCricleRadar(VertexHelper vh, RadarCoord radar) + { + float insideRadius = 0, outsideRadius = 0; + float block = radar.context.radius / radar.splitNumber; + int indicatorNum = radar.indicatorList.Count; + Vector3 p = radar.context.center; + Vector3 p1; + float angle = 2 * Mathf.PI / indicatorNum; + var lineColor = radar.axisLine.GetColor(chart.theme.axis.splitLineColor); + var lineWidth = radar.axisLine.GetWidth(chart.theme.axis.lineWidth); + var lineType = radar.axisLine.GetType(chart.theme.axis.lineType); + var splitLineColor = radar.splitLine.GetColor(chart.theme.axis.splitLineColor); + var splitLineWidth = radar.splitLine.GetWidth(chart.theme.axis.splitLineWidth); + splitLineWidth *= 2f; + for (int i = 0; i < radar.splitNumber; i++) + { + var color = radar.splitArea.GetColor(i, chart.theme.axis); + outsideRadius = insideRadius + block; + if (radar.splitArea.show) + { + UGL.DrawDoughnut(vh, p, insideRadius, outsideRadius, color, Color.clear, + 0, 360, chart.settings.cicleSmoothness); + } + if (radar.splitLine.show) + { + UGL.DrawEmptyCricle(vh, p, outsideRadius, splitLineWidth, splitLineColor, + Color.clear, chart.settings.cicleSmoothness); + } + insideRadius = outsideRadius; + } + if (radar.axisLine.show) + { + for (int j = 0; j <= indicatorNum; j++) + { + float currAngle = j * angle; + p1 = new Vector3(p.x + outsideRadius * Mathf.Sin(currAngle), + p.y + outsideRadius * Mathf.Cos(currAngle)); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, p, p1, lineColor); + } + } + } + + private void DrawPolygonRadar(VertexHelper vh, RadarCoord radar) + { + float insideRadius = 0, outsideRadius = 0; + float block = radar.context.radius / radar.splitNumber; + int indicatorNum = radar.indicatorList.Count; + Vector3 p1, p2, p3, p4; + Vector3 p = radar.context.center; + var startAngle = radar.startAngle * Mathf.PI / 180; + var angle = 2 * Mathf.PI / indicatorNum; + var lineColor = radar.axisLine.GetColor(chart.theme.axis.splitLineColor); + var lineWidth = radar.axisLine.GetWidth(chart.theme.axis.lineWidth); + var lineType = radar.axisLine.GetType(chart.theme.axis.lineType); + var splitLineColor = radar.splitLine.GetColor(chart.theme.axis.splitLineColor); + var splitLineWidth = radar.splitLine.GetWidth(chart.theme.axis.splitLineWidth); + var splitLineType = radar.splitLine.GetType(chart.theme.axis.splitLineType); + for (int i = 0; i < radar.splitNumber; i++) + { + var color = radar.splitArea.GetColor(i, chart.theme.axis); + outsideRadius = insideRadius + block; + p1 = new Vector3(p.x + insideRadius * Mathf.Sin(startAngle), p.y + insideRadius * Mathf.Cos(startAngle)); + p2 = new Vector3(p.x + outsideRadius * Mathf.Sin(startAngle), p.y + outsideRadius * Mathf.Cos(startAngle)); + for (int j = 0; j <= indicatorNum; j++) + { + float currAngle = startAngle + j * angle; + p3 = new Vector3(p.x + outsideRadius * Mathf.Sin(currAngle), + p.y + outsideRadius * Mathf.Cos(currAngle)); + p4 = new Vector3(p.x + insideRadius * Mathf.Sin(currAngle), + p.y + insideRadius * Mathf.Cos(currAngle)); + if (radar.splitArea.show) + { + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, color); + } + if (radar.splitLine.NeedShow(i, radar.splitNumber)) + { + ChartDrawer.DrawLineStyle(vh, splitLineType, splitLineWidth, p2, p3, splitLineColor); + } + p1 = p4; + p2 = p3; + } + insideRadius = outsideRadius; + } + if (radar.axisLine.show) + { + for (int j = 0; j <= indicatorNum; j++) + { + float currAngle = startAngle + j * angle; + p3 = new Vector3(p.x + outsideRadius * Mathf.Sin(currAngle), + p.y + outsideRadius * Mathf.Cos(currAngle)); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, p, p3, lineColor); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs.meta b/Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs.meta new file mode 100644 index 0000000..1c042d0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Radar/RadarCoordHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27622e3c95fec42daafff901970daf8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Settings.meta b/Assets/XCharts/Runtime/Component/Settings.meta new file mode 100644 index 0000000..d09a812 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Settings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 592a52c7f32a046c689bd54aae7eff59 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Settings/Settings.cs b/Assets/XCharts/Runtime/Component/Settings/Settings.cs new file mode 100644 index 0000000..2a1f81e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Settings/Settings.cs @@ -0,0 +1,188 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Global parameter setting component. The default value can be used in general, and can be adjusted when necessary. + /// ||全局参数设置组件。一般情况下可使用默认值,当有需要时可进行调整。 + /// </summary> + [Serializable] + public class Settings : MainComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField][Range(1, 20)] protected int m_MaxPainter = 10; + [SerializeField] protected bool m_ReversePainter = false; + [SerializeField] protected Material m_BasePainterMaterial; + [SerializeField] protected Material m_SeriePainterMaterial; + [SerializeField] protected Material m_UpperPainterMaterial; + [SerializeField] protected Material m_TopPainterMaterial; + [SerializeField][Range(1, 10)] protected float m_LineSmoothStyle = 2.5f; + [SerializeField][Range(1f, 20)] protected float m_LineSmoothness = 2f; + [SerializeField][Range(0.5f, 20)] protected float m_LineSegmentDistance = 3f; + [SerializeField][Range(1, 10)] protected float m_CicleSmoothness = 2f; + [SerializeField] protected float m_LegendIconLineWidth = 2; + [SerializeField] private float[] m_LegendIconCornerRadius = new float[] { 0.25f, 0.25f, 0.25f, 0.25f }; + [SerializeField][Since("v3.1.0")] protected float m_AxisMaxSplitNumber = 50; + + public bool show { get { return m_Show; } } + /// <summary> + /// max painter. + /// ||设定的painter数量。 + /// </summary> + public int maxPainter + { + get { return m_MaxPainter; } + set { if (PropertyUtil.SetStruct(ref m_MaxPainter, value < 0 ? 1 : value)) SetVerticesDirty(); } + } + /// <summary> + /// Painter是否逆序。逆序时index大的serie最先绘制。 + /// </summary> + public bool reversePainter + { + get { return m_ReversePainter; } + set { if (PropertyUtil.SetStruct(ref m_ReversePainter, value)) SetVerticesDirty(); } + } + /// <summary> + /// Base Pointer 材质球,设置后会影响Axis等。 + /// </summary> + public Material basePainterMaterial + { + get { return m_BasePainterMaterial; } + set { if (PropertyUtil.SetClass(ref m_BasePainterMaterial, value)) SetComponentDirty(); } + } + /// <summary> + /// Serie Pointer 材质球,设置后会影响所有Serie。 + /// </summary> + public Material seriePainterMaterial + { + get { return m_SeriePainterMaterial; } + set { if (PropertyUtil.SetClass(ref m_SeriePainterMaterial, value)) SetComponentDirty(); } + } + /// <summary> + /// Top Pointer 材质球。 + /// </summary> + public Material topPainterMaterial + { + get { return m_TopPainterMaterial; } + set { if (PropertyUtil.SetClass(ref m_TopPainterMaterial, value)) SetComponentDirty(); } + } + /// <summary> + /// Upper Pointer 材质球。 + /// </summary> + public Material upperPainterMaterial + { + get { return m_UpperPainterMaterial; } + set { if (PropertyUtil.SetClass(ref m_UpperPainterMaterial, value)) SetComponentDirty(); } + } + /// <summary> + /// Curve smoothing factor. By adjusting the smoothing coefficient, the curvature of the curve can be changed, + /// and different curves with slightly different appearance can be obtained. + /// ||曲线平滑系数。通过调整平滑系数可以改变曲线的曲率,得到外观稍微有变化的不同曲线。 + /// </summary> + public float lineSmoothStyle + { + get { return m_LineSmoothStyle; } + set { if (PropertyUtil.SetStruct(ref m_LineSmoothStyle, value < 0 ? 1f : value)) SetVerticesDirty(); } + } + /// <summary> + /// Smoothness of curve. The smaller the value, the smoother the curve, but the number of vertices will increase. + /// ||When the area with gradient is filled, the larger the value, the worse the transition effect. + /// ||曲线平滑度。值越小曲线越平滑,但顶点数也会随之增加。当开启有渐变的区域填充时,数值越大渐变过渡效果越差。 + /// </summary> + public float lineSmoothness + { + get { return m_LineSmoothness; } + set { if (PropertyUtil.SetStruct(ref m_LineSmoothness, value < 0 ? 1f : value)) SetVerticesDirty(); } + } + /// <summary> + /// The partition distance of a line segment. A line in a normal line chart is made up of many segments, + /// the number of which is determined by the change in value. The smaller the number of segments, + /// the higher the number of vertices. When the area with gradient is filled, the larger the value, the worse the transition effect. + /// ||线段的分割距离。普通折线图的线是由很多线段组成,段数由该数值决定。值越小段数越多,但顶点数也会随之增加。当开启有渐变的区域填充时,数值越大渐变过渡效果越差。 + /// </summary> + public float lineSegmentDistance + { + get { return m_LineSegmentDistance; } + set { if (PropertyUtil.SetStruct(ref m_LineSegmentDistance, value < 0 ? 1f : value)) SetVerticesDirty(); } + } + /// <summary> + /// the smoothess of cricle. + /// ||圆形的平滑度。数越小圆越平滑,但顶点数也会随之增加。 + /// </summary> + public float cicleSmoothness + { + get { return m_CicleSmoothness; } + set { if (PropertyUtil.SetStruct(ref m_CicleSmoothness, value < 0 ? 1f : value)) SetVerticesDirty(); } + } + + /// <summary> + /// the width of line serie legend. + /// ||Line类型图例图标的线条宽度。 + /// </summary> + public float legendIconLineWidth + { + get { return m_LegendIconLineWidth; } + set { if (PropertyUtil.SetStruct(ref m_LegendIconLineWidth, value)) SetVerticesDirty(); } + } + + /// <summary> + /// The radius of rounded corner. Its unit is px. Use array to respectively specify the 4 corner radiuses((clockwise upper left, upper right, bottom right and bottom left)). + /// ||图例圆角半径。用数组分别指定4个圆角半径(顺时针左上,右上,右下,左下)。 + /// </summary> + public float[] legendIconCornerRadius + { + get { return m_LegendIconCornerRadius; } + set { if (PropertyUtil.SetClass(ref m_LegendIconCornerRadius, value, true)) SetVerticesDirty(); } + } + + /// <summary> + /// the max splitnumber of axis. + /// ||坐标轴最大分隔段数。段数过大时可能会生成较多的label节点。 + /// </summary> + public float axisMaxSplitNumber + { + get { return m_AxisMaxSplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_AxisMaxSplitNumber, value)) SetVerticesDirty(); } + } + + public void Copy(Settings settings) + { + m_ReversePainter = settings.reversePainter; + m_MaxPainter = settings.maxPainter; + m_BasePainterMaterial = settings.basePainterMaterial; + m_SeriePainterMaterial = settings.seriePainterMaterial; + m_UpperPainterMaterial = settings.upperPainterMaterial; + m_TopPainterMaterial = settings.topPainterMaterial; + m_LineSmoothStyle = settings.lineSmoothStyle; + m_LineSmoothness = settings.lineSmoothness; + m_LineSegmentDistance = settings.lineSegmentDistance; + m_CicleSmoothness = settings.cicleSmoothness; + m_LegendIconLineWidth = settings.legendIconLineWidth; + ChartHelper.CopyArray(m_LegendIconCornerRadius, settings.legendIconCornerRadius); + } + + public override void Reset() + { + Copy(DefaultSettings); + } + + public static Settings DefaultSettings + { + get + { + return new Settings() + { + m_ReversePainter = false, + m_MaxPainter = XCSettings.maxPainter, + m_LineSmoothStyle = XCSettings.lineSmoothStyle, + m_LineSmoothness = XCSettings.lineSmoothness, + m_LineSegmentDistance = XCSettings.lineSegmentDistance, + m_CicleSmoothness = XCSettings.cicleSmoothness, + m_LegendIconLineWidth = 2, + m_LegendIconCornerRadius = new float[] { 0.25f, 0.25f, 0.25f, 0.25f } + }; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Settings/Settings.cs.meta b/Assets/XCharts/Runtime/Component/Settings/Settings.cs.meta new file mode 100644 index 0000000..b064a39 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Settings/Settings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e57c4afa48c2455b8a91b20eca25321 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/State.meta b/Assets/XCharts/Runtime/Component/State.meta new file mode 100644 index 0000000..1e539fd --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ca1088963feb54117bce8be6bceb64de +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/State/BlurStyle.cs b/Assets/XCharts/Runtime/Component/State/BlurStyle.cs new file mode 100644 index 0000000..fea2cd0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/BlurStyle.cs @@ -0,0 +1,13 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Configurations of blur state. + /// ||淡出状态样式。 + /// </summary> + [System.Serializable] + [Since("v3.2.0")] + public class BlurStyle : StateStyle, ISerieComponent, ISerieDataComponent + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/State/BlurStyle.cs.meta b/Assets/XCharts/Runtime/Component/State/BlurStyle.cs.meta new file mode 100644 index 0000000..545be0c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/BlurStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e3f901db80454f89800a84977289535 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs b/Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs new file mode 100644 index 0000000..19c6e62 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs @@ -0,0 +1,90 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Configurations of emphasis state. + /// ||高亮状态样式。 + /// </summary> + [System.Serializable] + [Since("v3.2.0")] + public class EmphasisStyle : StateStyle, ISerieComponent, ISerieDataComponent + { + /// <summary> + /// focus type. + /// ||聚焦类型。 + /// </summary> + public enum FocusType + { + /// <summary> + /// Do not fade out other data, it's by default. + /// ||不淡出其它图形,默认使用该配置。 + /// </summary> + None, + /// <summary> + /// Only focus (not fade out) the element of the currently highlighted data. + /// ||只聚焦(不淡出)当前高亮的数据的图形。 + /// </summary> + Self, + /// <summary> + /// Focus on all elements of the series which the currently highlighted data belongs to. + /// ||聚焦当前高亮的数据所在的系列的所有图形。 + /// </summary> + Series + } + /// <summary> + /// blur scope. + /// ||淡出范围。 + /// </summary> + public enum BlurScope + { + /// <summary> + /// coordinate system. + /// ||淡出范围为坐标系,默认使用该配置。 + /// </summary> + GridCoord, + /// <summary> + /// series. + /// ||淡出范围为系列。 + /// </summary> + Series, + /// <summary> + /// global. + /// ||淡出范围为全局。 + /// </summary> + Global + } + + [SerializeField] private float m_Scale = 1.1f; + [SerializeField] private FocusType m_Focus = FocusType.None; + [SerializeField] private BlurScope m_BlurScope = BlurScope.GridCoord; + + /// <summary> + /// Whether to scale to highlight the data in emphasis state. + /// ||高亮时的缩放倍数。 + /// </summary> + public float scale + { + get { return m_Scale; } + set { if (PropertyUtil.SetStruct(ref m_Scale, value)) SetVerticesDirty(); } + } + /// <summary> + /// When the data is highlighted, whether to fade out of other data to focus the highlighted. + /// ||在高亮图形时,是否淡出其它数据的图形已达到聚焦的效果。 + /// </summary> + public FocusType focus + { + get { return m_Focus; } + set { if (PropertyUtil.SetStruct(ref m_Focus, value)) SetVerticesDirty(); } + } + /// <summary> + /// The range of fade out when focus is enabled. + /// ||在开启focus的时候,可以通过blurScope配置淡出的范围。 + /// </summary> + public BlurScope blurScope + { + get { return m_BlurScope; } + set { if (PropertyUtil.SetStruct(ref m_BlurScope, value)) SetVerticesDirty(); } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs.meta b/Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs.meta new file mode 100644 index 0000000..c75a637 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/EmphasisStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91a31f424478042418811c32bb8aa2d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/State/SelectStyle.cs b/Assets/XCharts/Runtime/Component/State/SelectStyle.cs new file mode 100644 index 0000000..c72eb72 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/SelectStyle.cs @@ -0,0 +1,13 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Configurations of select state. + /// ||选中状态样式。 + /// </summary> + [System.Serializable] + [Since("v3.2.0")] + public class SelectStyle : StateStyle, ISerieComponent, ISerieDataComponent + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/State/SelectStyle.cs.meta b/Assets/XCharts/Runtime/Component/State/SelectStyle.cs.meta new file mode 100644 index 0000000..c4a851c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/SelectStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 986a9b6da6fdd48c49a9b665450dd605 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/State/StateStyle.cs b/Assets/XCharts/Runtime/Component/State/StateStyle.cs new file mode 100644 index 0000000..66f0441 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/StateStyle.cs @@ -0,0 +1,126 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the state style of serie. + /// ||Serie的状态样式。Serie的状态有正常,高亮,淡出,选中四种状态。 + /// </summary> + [System.Serializable] + [Since("v3.2.0")] + public class StateStyle : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private LabelStyle m_Label = new LabelStyle(); + [SerializeField] private LabelLine m_LabelLine = new LabelLine(); + [SerializeField] private ItemStyle m_ItemStyle = new ItemStyle(); + [SerializeField] private LineStyle m_LineStyle = new LineStyle(); + [SerializeField] private AreaStyle m_AreaStyle = new AreaStyle(); + [SerializeField] private SerieSymbol m_Symbol = new SerieSymbol(); + + public void Reset() + { + m_Show = false; + m_Label.Reset(); + m_LabelLine.Reset(); + m_ItemStyle.Reset(); + m_Symbol.Reset(); + } + + /// <summary> + /// 是否启用高亮样式。 + /// </summary> + public bool show + { + get { return m_Show; } + set { m_Show = value; } + } + /// <summary> + /// 图形文本标签。 + /// </summary> + public LabelStyle label + { + get { return m_Label; } + set { if (PropertyUtil.SetClass(ref m_Label, value, true)) SetAllDirty(); } + } + /// <summary> + /// 图形文本引导线样式。 + /// </summary> + public LabelLine labelLine + { + get { return m_LabelLine; } + set { if (PropertyUtil.SetClass(ref m_LabelLine, value, true)) SetAllDirty(); } + } + /// <summary> + /// 图形样式。 + /// </summary> + public ItemStyle itemStyle + { + get { return m_ItemStyle; } + set { if (PropertyUtil.SetClass(ref m_ItemStyle, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// 折线样式。 + /// </summary> + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (PropertyUtil.SetClass(ref m_LineStyle, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// 区域样式。 + /// </summary> + public AreaStyle areaStyle + { + get { return m_AreaStyle; } + set { if (PropertyUtil.SetClass(ref m_AreaStyle, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// 标记样式。 + /// </summary> + public SerieSymbol symbol + { + get { return m_Symbol; } + set { if (PropertyUtil.SetClass(ref m_Symbol, value, true)) SetVerticesDirty(); } + } + + public override bool vertsDirty + { + get + { + return m_VertsDirty || + m_Label.vertsDirty || + m_ItemStyle.vertsDirty || + m_LineStyle.vertsDirty || + m_AreaStyle.vertsDirty || + m_Symbol.vertsDirty; + } + } + + public override bool componentDirty + { + get + { + return m_ComponentDirty || + m_Label.componentDirty; + } + } + + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + m_Label.ClearVerticesDirty(); + m_ItemStyle.ClearVerticesDirty(); + m_LineStyle.ClearVerticesDirty(); + m_AreaStyle.ClearVerticesDirty(); + m_Symbol.ClearVerticesDirty(); + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + m_Label.ClearComponentDirty(); + m_Symbol.ClearComponentDirty(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/State/StateStyle.cs.meta b/Assets/XCharts/Runtime/Component/State/StateStyle.cs.meta new file mode 100644 index 0000000..fed3381 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/State/StateStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 921539f841914493a90f748c6c6662dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Title.meta b/Assets/XCharts/Runtime/Component/Title.meta new file mode 100644 index 0000000..71e29ea --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cea6be3fa2a9e4ae6be4b3fd882f7352 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Title/Title.cs b/Assets/XCharts/Runtime/Component/Title/Title.cs new file mode 100644 index 0000000..2ed6833 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title/Title.cs @@ -0,0 +1,105 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Title component, including main title and subtitle. + /// ||标题组件,包含主标题和副标题。 + /// </summary> + [Serializable] + [ComponentHandler(typeof(TitleHander), true)] + public class Title : MainComponent, IPropertyChanged + { + [SerializeField] private bool m_Show = true; + [SerializeField] private string m_Text = "Chart Title"; + [SerializeField] private string m_SubText = ""; + [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle(); + [SerializeField] private LabelStyle m_SubLabelStyle = new LabelStyle(); + [SerializeField] private float m_ItemGap = 0; + [SerializeField] private Location m_Location = Location.defaultTop; + + /// <summary> + /// [default:true] + /// Set this to false to prevent the title from showing. + /// ||是否显示标题组件。 + /// </summary> + public bool show { get { return m_Show; } set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetComponentDirty(); } } + /// <summary> + /// The main title text, supporting \n for newlines. + /// ||主标题文本,支持使用 \n 换行。 + /// </summary> + public string text { get { return m_Text; } set { if (PropertyUtil.SetClass(ref m_Text, value)) SetComponentDirty(); } } + /// <summary> + /// The text style of main title. + /// ||主标题文本样式。 + /// </summary> + public LabelStyle labelStyle + { + get { return m_LabelStyle; } + set { if (PropertyUtil.SetClass(ref m_LabelStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// Subtitle text, supporting for \n for newlines. + /// ||副标题文本,支持使用 \n 换行。 + /// </summary> + public string subText + { + get { return m_SubText; } + set { if (PropertyUtil.SetClass(ref m_SubText, value)) SetComponentDirty(); } + } + /// <summary> + /// The text style of sub title. + /// ||副标题文本样式。 + /// </summary> + public LabelStyle subLabelStyle + { + get { return m_SubLabelStyle; } + set { if (PropertyUtil.SetClass(ref m_SubLabelStyle, value)) SetComponentDirty(); } + } + /// <summary> + /// [default:8] + /// The gap between the main title and subtitle. + /// ||主副标题之间的间距。 + /// </summary> + public float itemGap + { + get { return m_ItemGap; } + set { if (PropertyUtil.SetStruct(ref m_ItemGap, value)) SetComponentDirty(); } + } + /// <summary> + /// The location of title component. + /// ||标题显示位置。 + /// </summary> + public Location location + { + get { return m_Location; } + set { if (PropertyUtil.SetClass(ref m_Location, value)) SetComponentDirty(); } + } + + public override bool vertsDirty { get { return false; } } + public override bool componentDirty + { + get + { + return m_ComponentDirty || + location.componentDirty || + m_LabelStyle.componentDirty || + m_SubLabelStyle.componentDirty; + } + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + location.ClearComponentDirty(); + m_LabelStyle.ClearComponentDirty(); + m_SubLabelStyle.ClearComponentDirty(); + } + + public void OnChanged() + { + m_Location.OnChanged(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Title/Title.cs.meta b/Assets/XCharts/Runtime/Component/Title/Title.cs.meta new file mode 100644 index 0000000..1d57b56 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title/Title.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c4f5a39710624b94a3d015eb552f53a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Title/TitleHandler.cs b/Assets/XCharts/Runtime/Component/Title/TitleHandler.cs new file mode 100644 index 0000000..9f789ab --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title/TitleHandler.cs @@ -0,0 +1,88 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class TitleHander : MainComponentHandler<Title> + { + private static readonly string s_TitleObjectName = "title"; + private static readonly string s_SubTitleObjectName = "title_sub"; + private ChartLabel m_LabelObject; + private ChartLabel m_SubLabelObject; + + public override void InitComponent() + { + var title = component; + title.painter = null; + title.refreshComponent = delegate() + { + title.OnChanged(); + var anchorMin = title.location.runtimeAnchorMin; + var anchorMax = title.location.runtimeAnchorMax; + var pivot = title.location.runtimePivot; + var objName = ChartCached.GetComponentObjectName(title); + var titleObject = ChartHelper.AddObject(objName, chart.transform, anchorMin, anchorMax, + pivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + title.gameObject = titleObject; + title.gameObject.transform.SetSiblingIndex(chart.m_PainterUpper.transform.GetSiblingIndex() + 1); + anchorMin = title.location.runtimeAnchorMin; + anchorMax = title.location.runtimeAnchorMax; + pivot = title.location.runtimePivot; + var fontSize = title.labelStyle.textStyle.GetFontSize(chart.theme.title); + ChartHelper.UpdateRectTransform(titleObject, anchorMin, anchorMax, pivot, new Vector2(chart.chartWidth, chart.chartHeight)); + var titlePosition = chart.GetTitlePosition(title); + var subTitlePosition = -new Vector3(0, fontSize + title.itemGap, 0); + + titleObject.transform.localPosition = titlePosition; + titleObject.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(titleObject); + + m_LabelObject = ChartHelper.AddChartLabel(s_TitleObjectName, titleObject.transform, title.labelStyle, chart.theme.title, + GetTitleText(title), Color.clear, title.location.runtimeTextAlignment); + m_LabelObject.SetActive(title.show && title.labelStyle.show, true); + + m_SubLabelObject = ChartHelper.AddChartLabel(s_SubTitleObjectName, titleObject.transform, title.subLabelStyle, chart.theme.subTitle, + GetSubTitleText(title), Color.clear, title.location.runtimeTextAlignment); + m_SubLabelObject.SetActive(title.show && title.subLabelStyle.show, true); + m_SubLabelObject.transform.localPosition = subTitlePosition + title.subLabelStyle.offset; + }; + title.refreshComponent(); + } + + public override void OnSerieDataUpdate(int serieIndex) + { + if (m_LabelObject != null && FormatterHelper.NeedFormat(component.text)) + m_LabelObject.SetText(GetTitleText(component)); + if (m_SubLabelObject != null && FormatterHelper.NeedFormat(component.subText)) + m_SubLabelObject.SetText(GetSubTitleText(component)); + } + + private string GetTitleText(Title title) + { + if (FormatterHelper.NeedFormat(title.text)) + { + var content = title.text; + FormatterHelper.ReplaceContent(ref content, -1, title.labelStyle.numericFormatter, null, chart); + return content; + } + else + { + return title.text; + } + } + + private string GetSubTitleText(Title title) + { + if (FormatterHelper.NeedFormat(title.subText)) + { + var content = title.subText; + FormatterHelper.ReplaceContent(ref content, -1, title.subLabelStyle.numericFormatter, null, chart); + return content; + } + else + { + return title.subText; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Title/TitleHandler.cs.meta b/Assets/XCharts/Runtime/Component/Title/TitleHandler.cs.meta new file mode 100644 index 0000000..bc42e0d --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title/TitleHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbe3062b7770040e6b4a98026f0ad044 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Title/TitleStyle.cs b/Assets/XCharts/Runtime/Component/Title/TitleStyle.cs new file mode 100644 index 0000000..9b405ab --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title/TitleStyle.cs @@ -0,0 +1,15 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the title of serie. + /// ||标题相关设置。 + /// </summary> + [Serializable] + public class TitleStyle : LabelStyle, ISerieDataComponent, ISerieComponent + { + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Title/TitleStyle.cs.meta b/Assets/XCharts/Runtime/Component/Title/TitleStyle.cs.meta new file mode 100644 index 0000000..1a44da0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Title/TitleStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd97375f7d84f4fd18dab048c465cdd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Tooltip.meta b/Assets/XCharts/Runtime/Component/Tooltip.meta new file mode 100644 index 0000000..7161b85 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17e248f354e9b4e3fa75170f7919e297 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs b/Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs new file mode 100644 index 0000000..e17247b --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs @@ -0,0 +1,646 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// Tooltip component. + /// ||提示框组件。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(TooltipHandler), true)] + public class Tooltip : MainComponent + { + /// <summary> + /// Indicator type. + /// ||指示器类型。 + /// </summary> + public enum Type + { + /// <summary> + /// line indicator. + /// ||直线指示器 + /// </summary> + Line, + /// <summary> + /// shadow crosshair indicator. + /// ||阴影指示器 + /// </summary> + Shadow, + /// <summary> + /// no indicator displayed. + /// ||无指示器 + /// </summary> + None, + /// <summary> + /// crosshair indicator, which is actually the shortcut of enable two axisPointers of two orthometric axes. + /// ||十字准星指示器。坐标轴显示Label和交叉线。 + /// </summary> + Cross, + /// <summary> + /// Auto select indicator according to serie type. + /// ||根据serie的类型自动选择显示指示器。 + /// </summary> + Auto + } + + /// <summary> + /// Trigger strategy. + /// ||触发类型。 + /// </summary> + public enum Trigger + { + /// <summary> + /// Triggered by data item, which is mainly used for charts that don't have a category axis like scatter charts or pie charts. + /// ||数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用。 + /// </summary> + Item, + /// <summary> + /// Triggered by axes, which is mainly used for charts that have category axes, like bar charts or line charts. + /// ||坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用。 + /// </summary> + Axis, + /// <summary> + /// Trigger nothing. + /// ||什么都不触发。 + /// </summary> + None, + /// <summary> + /// Auto select trigger according to serie type. + /// ||根据serie的类型自动选择触发类型。 + /// </summary> + Auto + } + /// <summary> + /// the condition of trigger tooltip. + /// ||触发条件。 + /// </summary> + public enum TriggerOn + { + /// <summary> + /// Trigger when mouse move. + /// ||鼠标移动时触发。 + /// </summary> + MouseMove, + /// <summary> + /// Trigger when mouse click. + /// ||鼠标点击时触发。 + /// </summary> + Click, + } + /// <summary> + /// Position type. + /// ||坐标类型。 + /// </summary> + public enum Position + { + /// <summary> + /// Auto. The mobile platform is displayed at the top, and the non-mobile platform follows the mouse position. + /// ||自适应。移动平台靠顶部显示,非移动平台跟随鼠标位置。 + /// </summary> + Auto, + /// <summary> + /// Custom. Fully customize display position (x,y). + /// ||自定义。完全自定义显示位置(x,y)。 + /// </summary> + Custom, + /// <summary> + /// Just fix the coordinate X. Y follows the mouse position. + /// ||只固定坐标X。Y跟随鼠标位置。 + /// </summary> + FixedX, + /// <summary> + /// Just fix the coordinate Y. X follows the mouse position. + /// ||只固定坐标Y。X跟随鼠标位置。 + FixedY + } + + [SerializeField] private bool m_Show = true; + [SerializeField] private Type m_Type = Type.Auto; + [SerializeField] private Trigger m_Trigger = Trigger.Auto; + [SerializeField][Since("v3.11.0")] private TriggerOn m_TriggerOn = TriggerOn.MouseMove; + [SerializeField][Since("v3.3.0")] private Position m_Position = Position.Auto; + [SerializeField] private string m_ItemFormatter; + [SerializeField] private string m_TitleFormatter; + [SerializeField] private string m_Marker = "●"; + [SerializeField] private float m_FixedWidth = 0; + [SerializeField] private float m_FixedHeight = 0; + [SerializeField] private float m_MinWidth = 0; + [SerializeField] private float m_MinHeight = 0; + [SerializeField] private string m_NumericFormatter = ""; + [SerializeField] private int m_PaddingLeftRight = 10; + [SerializeField] private int m_PaddingTopBottom = 10; + [SerializeField] private bool m_IgnoreDataShow = false; + [SerializeField] private string m_IgnoreDataDefaultContent = "-"; + [SerializeField] private bool m_ShowContent = true; + [SerializeField] private bool m_AlwayShowContent = false; + [SerializeField] private Vector2 m_Offset = new Vector2(18f, -25f); + [SerializeField] private Sprite m_BackgroundImage; + [SerializeField] private Image.Type m_BackgroundType = Image.Type.Simple; + [SerializeField] private Color m_BackgroundColor; + [SerializeField] private float m_BorderWidth = 2f; + [SerializeField] private float m_FixedX = 0f; + [SerializeField] private float m_FixedY = 0.7f; + [SerializeField] private float m_TitleHeight = 25f; + [SerializeField] private float m_ItemHeight = 25f; + [SerializeField] private Color32 m_BorderColor = new Color32(230, 230, 230, 255); + [SerializeField][Since("v3.14.0")] private List<float> m_ColumnGapWidths = new List<float>{15}; + [SerializeField] private LineStyle m_LineStyle = new LineStyle(LineStyle.Type.None); + [SerializeField] + private LabelStyle m_TitleLabelStyle = new LabelStyle() + { + textStyle = new TextStyle() { alignment = TextAnchor.MiddleLeft } + }; + [SerializeField] + private List<LabelStyle> m_ContentLabelStyles = new List<LabelStyle>() + { + new LabelStyle() { textPadding = new TextPadding(0, 5, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleCenter } }, + new LabelStyle() { textPadding = new TextPadding(0, 20, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleLeft } }, + new LabelStyle() { textPadding = new TextPadding(0, 0, 0, 0), textStyle = new TextStyle() { alignment = TextAnchor.MiddleRight } } + }; + + public TooltipContext context = new TooltipContext(); + public TooltipView view; + + /// <summary> + /// the callback of tooltip click index. + /// ||Tooltip为Click触发时,点击的X轴索引的回调。 + /// </summary> + public System.Action<int> onClickIndex { get; set; } + + /// <summary> + /// Whether to show the tooltip component. + /// ||是否显示提示框组件。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) { SetAllDirty(); SetActive(value); } } + } + /// <summary> + /// Indicator type. + /// ||提示框指示器类型。 + /// </summary> + public Type type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetAllDirty(); } + } + /// <summary> + /// Type of triggering. + /// ||触发类型。 + /// </summary> + public Trigger trigger + { + get { return m_Trigger; } + set { if (PropertyUtil.SetStruct(ref m_Trigger, value)) SetAllDirty(); } + } + /// <summary> + /// Condition of trigger tooltip. + /// ||触发条件。 + /// </summary> + public TriggerOn triggerOn + { + get { return m_TriggerOn; } + set { if (PropertyUtil.SetStruct(ref m_TriggerOn, value)) SetAllDirty(); } + } + /// <summary> + /// Type of position. + /// ||显示位置类型。 + /// </summary> + public Position position + { + get { return m_Position; } + set { if (PropertyUtil.SetStruct(ref m_Position, value)) SetAllDirty(); } + } + /// <summary> + /// String template formatter for tooltip title content. \n line wrapping is supported. The placeholder {i} can be set separately to indicate that title is ignored and not displayed. + /// Template variables are {.}, {a}, {b}, {c}, {d}, {e}, {f}, and {g}. <br /> + /// {.} is the dot of the corresponding color of serie currently indicated or index 0. <br /> + /// {a} is the series name name of serie currently indicated or index 0. <br /> + /// {b} is the name of the serie data item serieData currently indicated or index 0, or the category value (such as the X-axis of a line chart). <br /> + /// {c} is the value of the serie y-dimension (dimesion is 1) currently indicated or index is 0. <br /> + /// {d} is the serie y-dimensional (dimesion 1) percentage value of the currently indicated or index 0, note without the % sign. <br /> + /// {e} is the name of the serie data item serieData currently indicated or whose index is 0. <br /> + /// {h} is the hexadecimal color value of serieData for the serie data item currently indicated or index 0. <br /> + /// {f} is the sum of data. <br /> + /// {g} indicates the total number of data. <br /> + /// {y} is category value of y axis. <br /> + /// {.1} represents a dot of the corresponding color with serie specified as index 1. <br /> + /// The 1 in {a1}, {b1}, {c1} represents serie where index is specified as 1. <br /> + /// {c1:2} represents the third data of the current indicator data item in serie with index 1 (one data item has multiple data, index 2 represents the third data). <br /> + /// {c1:2-2} represents the third data of serie third data item with index 1 (that is, the number of data items must be specified when specifying the number of data items). <br /> + /// {d1:2:f2} indicates that a format string with a single value is f2 (numericFormatter is used if no value is specified). <br /> + /// {d:0.##} indicates that the format string with a value specified alone is 0.## # (for percentages, preserving a 2-digit significant number while avoiding the "100.00%" situation with f2). <br /> + /// example: "{a}, {c}", "{a1}, {c1: f1}", "{a1}, {c1:0: f1}", "{a1}, {c1:1-1: f1}" + /// ||提示框标题内容的字符串模版格式器。支持用 \n 换行。可以单独设置占位符{i}表示忽略不显示title。 + /// 模板变量有{.}、{a}、{b}、{c}、{d}、{e}、{f}、{g}。<br/> + /// {.}为当前所指示或index为0的serie的对应颜色的圆点。<br/> + /// {a}为当前所指示或index为0的serie的系列名name。<br/> + /// {b}为当前所指示或index为0的serie的数据项serieData的name,或者类目值(如折线图的X轴)。<br/> + /// {c}为当前所指示或index为0的serie的y维(dimesion为1)的数值。<br/> + /// {d}为当前所指示或index为0的serie的y维(dimesion为1)百分比值,注意不带%号。<br/> + /// {e}为当前所指示或index为0的serie的数据项serieData的name。<br/> + /// {h}为当前所指示或index为0的serie的数据项serieData的十六进制颜色值。<br/> + /// {f}为数据总和。<br/> + /// {g}为数据总个数。<br/> + /// {y}为value所对应的y轴的类目值。<br/> + /// {.1}表示指定index为1的serie对应颜色的圆点。<br/> + /// {a1}、{b1}、{c1}中的1表示指定index为1的serie。<br/> + /// {c1:2}表示索引为1的serie的当前指示数据项的第3个数据(一个数据项有多个数据,index为2表示第3个数据)。<br/> + /// {c1:2-2}表示索引为1的serie的第3个数据项的第3个数据(也就是要指定第几个数据项时必须要指定第几个数据)。<br/> + /// {d1:2:f2}表示单独指定了数值的格式化字符串为f2(不指定时用numericFormatter)。<br/> + /// {d:0.##} 表示单独指定了数值的格式化字符串为 0.## (用于百分比,保留2位有效数同时又能避免使用 f2 而出现的类似于"100.00%"的情况 )。<br/> + /// 示例:"{a}:{c}"、"{a1}:{c1:f1}"、"{a1}:{c1:0:f1}"、"{a1}:{c1:1-1:f1}" + /// </summary> + public string titleFormatter { get { return m_TitleFormatter; } set { m_TitleFormatter = value; } } + /// <summary> + /// a string template formatter for a single Serie or data item content. Support for wrapping lines with \n. + /// Template variables are {.}, {a}, {b}, {c}, {d}.<br/> + /// {.} is the dot of the corresponding color of a Serie that is currently indicated or whose index is 0.<br/> + /// {a} is the series name of the serie that is currently indicated or whose index is 0.<br/> + /// {b} is the name of the data item serieData that is currently indicated or whose index is 0, or a category value (such as the X-axis of a line chart).<br/> + /// {c} is the value of a Y-dimension (dimesion is 1) from a Serie that is currently indicated or whose index is 0.<br/> + /// {d} is the percentage value of Y-dimensions (dimesion is 1) from serie that is currently indicated or whose index is 0, with no % sign.<br/> + /// {e} is the name of the data item serieData that is currently indicated or whose index is 0.<br/> + /// {f} is sum of data.<br/> + /// {y} is category value of y axis.<br/> + /// {.1} represents a dot from serie corresponding color that specifies index as 1.<br/> + /// 1 in {a1}, {b1}, {c1} represents a serie that specifies an index of 1.<br/> + /// {c1:2} represents the third data from serie's current indication data item indexed to 1 (a data item has multiple data, index 2 represents the third data).<br/> + /// {c1:2-2} represents the third data item from serie's third data item indexed to 1 (i.e., which data item must be specified to specify).<br/> + /// {d1:2: F2} indicates that a formatted string with a value specified separately is F2 (numericFormatter is used when numericFormatter is not specified).<br/> + /// {d:0.##} indicates that a formatted string with a value specified separately is 0.## (used for percentage, reserved 2 valid digits while avoiding the situation similar to "100.00%" when using f2 ).<br/> + /// Example: "{a}, {c}", "{a1}, {c1: f1}", "{a1}, {c1:0: f1}", "{a1} : {c1:1-1: f1}"<br/> + /// ||提示框单个serie或数据项内容的字符串模版格式器。支持用 \n 换行。用|来表示多个列的分隔。 + /// 模板变量有{.}、{a}、{b}、{c}、{d}、{e}、{f}、{g}。<br/> + /// {i}或-表示忽略当前项。 + /// {.}为当前所指示的serie或数据项的对应颜色的圆点。<br/> + /// {a}为当前所指示的serie或数据项的系列名name。<br/> + /// {b}为当前所指示的serie或数据项的数据项serieData的name,或者类目值(如折线图的X轴)。<br/> + /// {c}为当前所指示的serie或数据项的y维(dimesion为1)的数值。<br/> + /// {d}为当前所指示的serie或数据项的y维(dimesion为1)百分比值,注意不带%号。<br/> + /// {e}为当前所指示的serie或数据项的数据项serieData的name。<br/> + /// {f}为当前所指示的serie的默认维度的数据总和。<br/> + /// {g}为当前所指示的serie的数据总个数。<br/> + /// {h}为当前所指示的serie的十六进制颜色值。<br/> + /// {y}为当前所指示的serie的y轴的类目值。<br/> + /// {c0}表示当前数据项维度为0的数据。<br/> + /// {c1}表示当前数据项维度为1的数据。<br/> + /// {d3}表示维度3的数据的百分比。它的分母是默认维度(一般是1维度)数据。<br/> + /// |表示多个列的分隔。<br/> + /// 示例:"{i}", "{.}|{a}|{c}", "{.}|{b}|{c2:f2}", "{.}|{b}|{y}" + /// </summary> + public string itemFormatter { get { return m_ItemFormatter; } set { m_ItemFormatter = value; } } + /// <summary> + /// Standard number and date format string. Used to format a Double value or a DateTime date as a string. + /// numericFormatter is used as an argument to either `Double.ToString ()` or `DateTime.ToString()`. <br /> + /// The number format uses the Axx format: A is a single-character format specifier that supports C currency, + /// D decimal, E exponent, F fixed-point number, G regular, N digit, P percentage, R round trip, and X hexadecimal. + /// xx is precision specification, from 0-99. E.g. F1, E2<br /> + /// Date format: Starts with `date`, which is used to format DateTime. Common date formats are: + /// yyyy year, MM month, dd day, HH hour, mm minute, ss second, fff millisecond. For example: date:yyyy-MM-dd HH:mm:ss<br /> + /// Time format: Starts with `time`, which is used to format TimeSpan. Common time formats are: + /// d day, HH hour, mm minute, ss second, fffffff fractional part. + /// Only the version of Unity2018 or later can support formatting, and the characters inside should be escaped. + /// For example: time:HH\:mm\:ss<br /> + /// number format reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings<br/> + /// date format reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings<br/> + /// Note: The date and time formats are only supported by 'v3.12.0' or later.<br/> + /// ||标准数字和日期格式字符串。用于将Double数值或DateTime日期格式化显示为字符串。numericFormatter用来作为Double.ToString()或DateTime.ToString()的参数。<br/> + /// 数字格式使用Axx的形式:A是格式说明符的单字符,支持C货币、D十进制、E指数、F定点数、G常规、N数字、P百分比、R往返、X十六进制的。xx是精度说明,从0-99。如:F1, E2<br/> + /// 日期格式:以`date`开头,用来格式化DateTime,常见格式有:yyyy年,MM月,dd日,HH时,mm分,ss秒,fff毫秒。如:date:yyyy-MM-dd HH:mm:ss<br/> + /// 时间格式:以`time`开头,用来格式化TimeSpan,常见格式有:d日,HH时,mm分,ss秒,fffffff小数部分。 + /// 需要Unity2018以上版本才支持格式化,并且里面的字符要转义。如:time:d\.HH\:mm\:ss<br/> + /// 数值格式化参考:https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/standard-numeric-format-strings <br/> + /// 日期格式化参考:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/standard-date-and-time-format-strings <br/> + /// 时间格式化参考:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/standard-timespan-format-strings <br/> + /// 注意:date和time格式需要`v3.12.0`以上版本才支持。 + /// </summary> + public string numericFormatter + { + get { return m_NumericFormatter; } + set { if (PropertyUtil.SetClass(ref m_NumericFormatter, value)) SetComponentDirty(); } + } + /// <summary> + /// the marker of serie. + /// ||serie的符号标志。 + /// </summary> + public string marker { get { return m_Marker; } set { m_Marker = value; } } + /// <summary> + /// Fixed width. Higher priority than minWidth. + /// ||固定宽度。比 minWidth 优先。 + /// </summary> + public float fixedWidth { get { return m_FixedWidth; } set { m_FixedWidth = value; } } + /// <summary> + /// Fixed height. Higher priority than minHeight. + /// ||固定高度。比 minHeight 优先。 + /// </summary> + public float fixedHeight { get { return m_FixedHeight; } set { m_FixedHeight = value; } } + /// <summary> + /// Minimum width. If fixedWidth has a value, get fixedWidth first. + /// ||最小宽度。如若 fixedWidth 设有值,优先取 fixedWidth。 + /// </summary> + public float minWidth { get { return m_MinWidth; } set { m_MinWidth = value; } } + /// <summary> + /// Minimum height. If fixedHeight has a value, take priority over fixedHeight. + /// ||最小高度。如若 fixedHeight 设有值,优先取 fixedHeight。 + /// </summary> + public float minHeight { get { return m_MinHeight; } set { m_MinHeight = value; } } + /// <summary> + /// the text padding of left and right. defaut:5. + /// ||左右边距。 + /// </summary> + public int paddingLeftRight { get { return m_PaddingLeftRight; } set { m_PaddingLeftRight = value; } } + /// <summary> + /// the text padding of top and bottom. defaut:5. + /// ||上下边距。 + /// </summary> + public int paddingTopBottom { get { return m_PaddingTopBottom; } set { m_PaddingTopBottom = value; } } + /// <summary> + /// Whether to show ignored data on tooltip. + /// ||是否显示忽略数据在tooltip上。 + /// </summary> + public bool ignoreDataShow { get { return m_IgnoreDataShow; } set { m_IgnoreDataShow = value; } } + /// <summary> + /// The default display character information for ignored data. + /// ||被忽略数据的默认显示字符信息。如果设置为空,则表示完全不显示忽略数据。 + /// </summary> + public string ignoreDataDefaultContent { get { return m_IgnoreDataDefaultContent; } set { m_IgnoreDataDefaultContent = value; } } + /// <summary> + /// The background image of tooltip. + /// ||提示框的背景图片。 + /// </summary> + public Sprite backgroundImage { get { return m_BackgroundImage; } set { m_BackgroundImage = value; SetComponentDirty(); } } + /// <summary> + /// The background type of tooltip. + /// ||提示框的背景图片显示类型。 + /// </summary> + public Image.Type backgroundType { get { return m_BackgroundType; } set { m_BackgroundType = value; SetComponentDirty(); } } + /// <summary> + /// The background color of tooltip. + /// ||提示框的背景颜色。 + /// </summary> + public Color backgroundColor { get { return m_BackgroundColor; } set { m_BackgroundColor = value; SetComponentDirty(); } } + /// <summary> + /// Whether to trigger after always display. + /// ||是否触发后一直显示提示框浮层。 + /// </summary> + public bool alwayShowContent { get { return m_AlwayShowContent; } set { m_AlwayShowContent = value; } } + /// <summary> + /// Whether to show the tooltip floating layer, whose default value is true. + /// It should be configurated to be false, if you only need tooltip to trigger the event or show the axisPointer without content. + /// ||是否显示提示框浮层,默认显示。只需tooltip触发事件或显示axisPointer而不需要显示内容时可配置该项为false。 + /// </summary> + public bool showContent { get { return m_ShowContent; } set { m_ShowContent = value; } } + /// <summary> + /// The position offset of tooltip relative to the mouse position. + /// ||提示框相对于鼠标位置的偏移。 + /// </summary> + public Vector2 offset { get { return m_Offset; } set { m_Offset = value; } } + /// <summary> + /// the width of tooltip border. + /// ||边框线宽。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of tooltip border. + /// ||边框颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the x positionn of fixedX. + /// ||固定X位置的坐标。 + /// </summary> + public float fixedX + { + get { return m_FixedX; } + set { if (PropertyUtil.SetStruct(ref m_FixedX, value)) SetVerticesDirty(); } + } + /// <summary> + /// the y position of fixedY. + /// ||固定Y位置的坐标。 + /// </summary> + public float fixedY + { + get { return m_FixedY; } + set { if (PropertyUtil.SetStruct(ref m_FixedY, value)) SetVerticesDirty(); } + } + /// <summary> + /// height of title text. + /// ||标题文本的高。 + /// </summary> + public float titleHeight + { + get { return m_TitleHeight; } + set { if (PropertyUtil.SetStruct(ref m_TitleHeight, value)) SetComponentDirty(); } + } + /// <summary> + /// height of content text. + /// ||数据项文本的高。 + /// </summary> + public float itemHeight + { + get { return m_ItemHeight; } + set { if (PropertyUtil.SetStruct(ref m_ItemHeight, value)) SetComponentDirty(); } + } + /// <summary> + /// the column gap width of content. When there is only one column, it only represents the gap width of the second column. + /// ||内容部分的列间距。当只有一列时,只表示第二列的间距。 + /// </summary> + public List<float> columnGapWidths + { + get { return m_ColumnGapWidths; } + set { if (value != null) { m_ColumnGapWidths = value; SetComponentDirty(); } } + } + /// <summary> + /// the textstyle of title. + /// ||标题的文本样式。 + /// </summary> + public LabelStyle titleLabelStyle + { + get { return m_TitleLabelStyle; } + set { if (value != null) { m_TitleLabelStyle = value; SetComponentDirty(); } } + } + /// <summary> + /// the column text style list of content. The first represents the text style of the first column, and so on. + /// ||内容部分的列文本样式列表。第一个表示第一列的文本样式,以此类推。 + /// </summary> + public List<LabelStyle> contentLabelStyles + { + get { return m_ContentLabelStyles; } + set { if (value != null) { m_ContentLabelStyles = value; SetComponentDirty(); } } + } + + /// <summary> + /// the line style of indicator line. + /// ||指示线样式。 + /// </summary> + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (value != null) m_LineStyle = value; SetComponentDirty(); } + } + + /// <summary> + /// 组件是否需要刷新 + /// </summary> + public override bool componentDirty + { + get { return m_ComponentDirty || lineStyle.componentDirty; } + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + lineStyle.ClearComponentDirty(); + } + /// <summary> + /// 当前提示框所指示的Serie索引(目前只对散点图有效)。 + /// </summary> + public Dictionary<int, List<int>> runtimeSerieIndex = new Dictionary<int, List<int>>(); + /// <summary> + /// The data index currently indicated by Tooltip. + /// ||当前提示框所指示的数据项索引。 + /// </summary> + public List<int> runtimeDataIndex { get { return m_RuntimeDateIndex; } internal set { m_RuntimeDateIndex = value; } } + private List<int> m_RuntimeDateIndex = new List<int>() { -1, -1 }; + + /// <summary> + /// Keep Tooltiop displayed at the top. + /// ||保持Tooltiop显示在最顶上 + /// </summary> + public void KeepTop() + { + gameObject.transform.SetAsLastSibling(); + } + + public override void ClearData() + { + ClearValue(); + } + + /// <summary> + /// 清除提示框指示数据 + /// </summary> + internal void ClearValue() + { + for (int i = 0; i < runtimeDataIndex.Count; i++) runtimeDataIndex[i] = -1; + } + + /// <summary> + /// 提示框是否显示 + /// </summary> + /// <returns></returns> + public bool IsActive() + { + return gameObject != null && gameObject.activeInHierarchy; + } + + /// <summary> + /// 设置Tooltip组件是否显示 + /// </summary> + /// <param name="flag"></param> + public void SetActive(bool flag) + { + if (gameObject && gameObject.activeInHierarchy != flag) + { + gameObject.SetActive(alwayShowContent ? true : flag); + } + SetContentActive(flag); + } + + /// <summary> + /// 设置文本框是否显示 + /// </summary> + /// <param name="flag"></param> + public void SetContentActive(bool flag) + { + if (view == null) + return; + + view.SetActive(alwayShowContent ? true : flag); + } + + /// <summary> + /// 当前提示框是否选中数据项 + /// </summary> + /// <returns></returns> + public bool IsSelected() + { + foreach (var index in runtimeDataIndex) + if (index >= 0) return true; + return false; + } + + /// <summary> + /// 指定索引的数据项是否被提示框选中 + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public bool IsSelected(int index) + { + foreach (var temp in runtimeDataIndex) + if (temp == index) return true; + return false; + } + + public void ClearSerieDataIndex() + { + foreach (var kv in runtimeSerieIndex) + { + kv.Value.Clear(); + } + } + + public void AddSerieDataIndex(int serieIndex, int dataIndex) + { + if (!runtimeSerieIndex.ContainsKey(serieIndex)) + { + runtimeSerieIndex[serieIndex] = new List<int>(); + } + runtimeSerieIndex[serieIndex].Add(dataIndex); + } + + public bool isAnySerieDataIndex() + { + foreach (var kv in runtimeSerieIndex) + { + if (kv.Value.Count > 0) return true; + } + return false; + } + + public bool IsTriggerItem() + { + return trigger == Trigger.Auto ? context.trigger == Trigger.Item : trigger == Trigger.Item; + } + + public bool IsTriggerAxis() + { + return trigger == Trigger.Auto ? context.trigger == Trigger.Axis : trigger == Trigger.Axis; + } + + public LabelStyle GetContentLabelStyle(int index) + { + if (m_ContentLabelStyles.Count == 0) + return null; + + if (index < 0) + index = 0; + else if (index > m_ContentLabelStyles.Count - 1) + index = m_ContentLabelStyles.Count - 1; + + return m_ContentLabelStyles[index]; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs.meta b/Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs.meta new file mode 100644 index 0000000..441a06e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/Tooltip.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dff3b0d6d38ee49838f054d30ab9b733 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs b/Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs new file mode 100644 index 0000000..b7e090e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + public class TooltipData + { + public string title; + public List<SerieParams> param = new List<SerieParams>(); + } + + public class TooltipContext + { + public Vector2 pointer; + public float width; + public float height; + public float angle; + public int xAxisClickIndex = -1; + public Tooltip.Type type; + public Tooltip.Trigger trigger; + public TooltipData data = new TooltipData(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs.meta b/Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs.meta new file mode 100644 index 0000000..2dd6ed0 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7324ce36c9b2c475bb18abd6618b107c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs new file mode 100644 index 0000000..3ff95e7 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs @@ -0,0 +1,861 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class TooltipHandler : MainComponentHandler<Tooltip> + { + private Dictionary<string, ChartLabel> m_IndicatorLabels = new Dictionary<string, ChartLabel>(); + private GameObject m_LabelRoot; + private ISerieContainer m_PointerContainer; + + public override void InitComponent() + { + InitTooltip(component); + } + + public override void BeforceSerieUpdate() + { + UpdateTooltipData(component); + } + + public override void Update() + { + UpdateTooltip(component); + UpdateTooltipIndicatorLabelText(component); + if (component.view != null) + component.view.Update(); + } + + public override void DrawUpper(VertexHelper vh) + { + DrawTooltipIndicator(vh, component); + } + + public override void OnPointerExit(PointerEventData eventData) + { + base.OnPointerExit(eventData); + if (chart.isTriggerOnClick) + { + component.context.xAxisClickIndex = -1; + } + } + + private void InitTooltip(Tooltip tooltip) + { + tooltip.painter = chart.m_PainterUpper; + tooltip.refreshComponent = delegate () + { + var objName = ChartCached.GetComponentObjectName(tooltip); + tooltip.gameObject = ChartHelper.AddObject(objName, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + var tooltipObject = tooltip.gameObject; + tooltipObject.transform.localPosition = Vector3.zero; + tooltipObject.hideFlags = chart.chartHideFlags; + var parent = tooltipObject.transform; + ChartHelper.HideAllObject(tooltipObject.transform); + + tooltip.view = TooltipView.CreateView(tooltip, chart.theme, parent); + tooltip.SetActive(false); + + m_LabelRoot = ChartHelper.AddObject("label", tooltip.gameObject.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); + m_LabelRoot.transform.SetSiblingIndex(0); + ChartHelper.HideAllObject(m_LabelRoot); + m_IndicatorLabels.Clear(); + foreach (var com in chart.components) + { + if (com is Axis) + { + var axis = com as Axis; + var aligment = (com is AngleAxis) ? TextAnchor.MiddleCenter : axis.context.aligment; + var labelName = ChartCached.GetComponentObjectName(axis); + var item = ChartHelper.AddTooltipIndicatorLabel(component, labelName, m_LabelRoot.transform, + chart.theme, aligment, axis.indicatorLabel); + item.SetActive(false); + m_IndicatorLabels[labelName] = item; + } + } + chart.isTriggerOnClick = tooltip.triggerOn == Tooltip.TriggerOn.Click; + }; + tooltip.refreshComponent(); + } + + private ChartLabel GetIndicatorLabel(Axis axis) + { + if (m_LabelRoot == null) return null; + var key = ChartCached.GetComponentObjectName(axis); + if (m_IndicatorLabels.ContainsKey(key)) + { + return m_IndicatorLabels[key]; + } + else + { + var item = ChartHelper.AddTooltipIndicatorLabel(component, key, m_LabelRoot.transform, + chart.theme, TextAnchor.MiddleCenter, axis.indicatorLabel); + m_IndicatorLabels[key] = item; + return item; + } + } + + private void UpdateTooltipData(Tooltip tooltip) + { + m_ShowTooltip = false; + if (tooltip.trigger == Tooltip.Trigger.None) return; + chart.isTriggerOnClick = tooltip.triggerOn == Tooltip.TriggerOn.Click; + + if ((tooltip.show && chart.isPointerInChart) && + ((tooltip.triggerOn == Tooltip.TriggerOn.Click && chart.isPointerClick) || + (tooltip.triggerOn == Tooltip.TriggerOn.MouseMove)) + ) + { + for (int i = chart.series.Count - 1; i >= 0; i--) + { + var serie = chart.series[i]; + if (!(serie is INeedSerieContainer)) + { + m_ShowTooltip = true; + m_ContainerSeries = null; + return; + } + } + m_ContainerSeries = ListPool<Serie>.Get(); + UpdatePointerContainerAndSeriesAndTooltip(tooltip, ref m_ContainerSeries); + if (m_ContainerSeries.Count > 0) + { + m_ShowTooltip = true; + return; + } + } + + if (!m_ShowTooltip && tooltip.IsActive()) + { + tooltip.ClearValue(); + tooltip.SetActive(false); + component.context.xAxisClickIndex = -1; + chart.pointerClickEventData = null; + } + } + + private bool m_ShowTooltip; + private List<Serie> m_ContainerSeries; + private void UpdateTooltip(Tooltip tooltip) + { + if (!m_ShowTooltip) + { + if (m_ContainerSeries != null) + { + ListPool<Serie>.Release(m_ContainerSeries); + m_ContainerSeries = null; + } + return; + } + + var anyTrigger = false; + for (int i = chart.series.Count - 1; i >= 0; i--) + { + var serie = chart.series[i]; + if (!(serie is INeedSerieContainer)) + { + if (SetSerieTooltip(tooltip, serie)) + { + anyTrigger = true; + chart.RefreshTopPainter(); + break; + } + } + } + if (!anyTrigger && m_ContainerSeries == null) + { + m_ContainerSeries = ListPool<Serie>.Get(); + UpdatePointerContainerAndSeriesAndTooltip(tooltip, ref m_ContainerSeries); + } + if (m_ContainerSeries != null) + { + if (!SetSerieTooltip(tooltip, m_ContainerSeries)) + m_ShowTooltip = false; + else + anyTrigger = true; + ListPool<Serie>.Release(m_ContainerSeries); + m_ContainerSeries = null; + } + if (!m_ShowTooltip || !anyTrigger) + { + if (tooltip.context.type == Tooltip.Type.Cross && m_PointerContainer != null && m_PointerContainer.IsPointerEnter()) + { + m_ShowTooltip = true; + tooltip.SetActive(true); + tooltip.SetContentActive(false); + } + else + { + m_ShowTooltip = false; + tooltip.SetActive(false); + chart.pointerClickEventData = null; + } + } + else + { + chart.RefreshUpperPainter(); + } + } + + private void UpdateTooltipIndicatorLabelText(Tooltip tooltip) + { + if (!tooltip.show) return; + if (tooltip.context.type == Tooltip.Type.None) return; + if (m_PointerContainer != null) + { + if (tooltip.context.type == Tooltip.Type.Cross) + { + if (m_PointerContainer is GridCoord) + { + var grid = m_PointerContainer as GridCoord; + ChartHelper.HideAllObject(m_LabelRoot); + foreach (var component in chart.components) + { + if (component is XAxis || component is YAxis) + { + var axis = component as Axis; + if (axis.gridIndex == grid.index) + { + var label = GetIndicatorLabel(axis); + SetTooltipIndicatorLabel(tooltip, axis, label); + } + } + } + } + else if (m_PointerContainer is PolarCoord) + { + var polar = m_PointerContainer as PolarCoord; + ChartHelper.HideAllObject(m_LabelRoot); + foreach (var component in chart.components) + { + if (component is AngleAxis || component is RadiusAxis) + { + var axis = component as Axis; + if (axis.polarIndex == polar.index) + { + var label = GetIndicatorLabel(axis); + SetTooltipIndicatorLabel(tooltip, axis, label); + } + } + } + } + } + } + } + + private void SetTooltipIndicatorLabel(Tooltip tooltip, Axis axis, ChartLabel label) + { + if (label == null) return; + if (double.IsNaN(axis.context.pointerValue)) return; + label.SetActive(true, true); + label.SetTextActive(true); + label.SetPosition(axis.context.pointerLabelPosition + axis.indicatorLabel.offset); + + if (axis.IsCategory()) + { + var index = (int)axis.context.pointerValue; + var dataZoom = chart.GetDataZoomOfAxis(axis); + var category = axis.GetData(index, dataZoom); + label.SetText(axis.indicatorLabel.GetFormatterContent(index, 0, category)); + } + else if (axis.IsTime()) + { + label.SetText(axis.indicatorLabel.GetFormatterDateTime(0, 0, axis.context.pointerValue, axis.context.minValue, axis.context.maxValue)); + } + else + { + label.SetText(axis.indicatorLabel.GetFormatterContent(0, 0, axis.context.pointerValue, axis.context.minValue, axis.context.maxValue, axis.IsLog())); + } + var textColor = axis.axisLabel.textStyle.GetColor(chart.theme.axis.textColor); + if (ChartHelper.IsClearColor(axis.indicatorLabel.background.color)) + label.color = textColor; + else + label.color = axis.indicatorLabel.background.color; + + if (ChartHelper.IsClearColor(axis.indicatorLabel.textStyle.color)) + label.SetTextColor(Color.white); + else + label.SetTextColor(axis.indicatorLabel.textStyle.color); + } + + private void UpdatePointerContainerAndSeriesAndTooltip(Tooltip tooltip, ref List<Serie> list) + { + list.Clear(); + m_PointerContainer = null; + var updateTooltipTypeAndTrigger = false; + for (int i = chart.components.Count - 1; i >= 0; i--) + { + var component = chart.components[i]; + if (component is ISerieContainer) + { + var container = component as ISerieContainer; + if (container.IsPointerEnter()) + { + foreach (var serie in chart.series) + { + if (serie is INeedSerieContainer && + (serie as INeedSerieContainer).containterInstanceId == component.instanceId && + !serie.placeHolder) + { + if (!updateTooltipTypeAndTrigger) + { + updateTooltipTypeAndTrigger = true; + tooltip.context.type = tooltip.type == Tooltip.Type.Auto ? + serie.context.tooltipType : tooltip.type; + tooltip.context.trigger = tooltip.trigger == Tooltip.Trigger.Auto ? + serie.context.tooltipTrigger : tooltip.trigger; + } + var isTriggerAxis = tooltip.IsTriggerAxis(); + var inchart = true; + if (container is GridCoord) + { + var xAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex); + var yAxis = chart.GetChartComponent<YAxis>(serie.yAxisIndex); + inchart = UpdateAxisPointerDataIndex(serie, xAxis, yAxis, container as GridCoord, isTriggerAxis); + } + else if (container is PolarCoord) + { + var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, container.index); + tooltip.context.angle = (float)m_AngleAxis.context.pointerValue; + } + if (inchart) + { + list.Add(serie); + if (!isTriggerAxis) + { + chart.RefreshTopPainter(); + } + } + } + } + m_PointerContainer = container; + } + } + } + } + + private bool UpdateAxisPointerDataIndex(Serie serie, XAxis xAxis, YAxis yAxis, GridCoord grid, bool isTriggerAxis) + { + serie.context.pointerAxisDataIndexs.Clear(); + if (xAxis == null || yAxis == null) return false; + var flag = true; + if (serie is Heatmap) + { + GetSerieDataByXYAxis(serie, xAxis, yAxis); + } + else if (yAxis.IsCategory() && !xAxis.IsCategory()) + { + if (isTriggerAxis) + { + var index = serie.context.dataZoomStartIndex + (int)yAxis.context.pointerValue; + if(serie.useSortData) index = yAxis.context.sortedDataIndices[index]; + serie.context.pointerEnter = true; + serie.context.pointerAxisDataIndexs.Add(index); + serie.context.pointerItemDataIndex = index; + yAxis.context.axisTooltipValue = index; + } + } + else if (yAxis.IsTime()) + { + serie.context.pointerEnter = true; + if (isTriggerAxis) + GetSerieDataIndexByAxis(serie, yAxis, grid); + else + GetSerieDataIndexByItem(serie, yAxis, grid); + } + else if (xAxis.IsCategory()) + { + if (isTriggerAxis) + { + var index = serie.context.dataZoomStartIndex + (int)xAxis.context.pointerValue; + if(serie.useSortData) index = xAxis.context.sortedDataIndices[index]; + if (chart.isTriggerOnClick) + { + if (serie.insertDataToHead) + index = index + (serie.context.totalDataIndex - serie.context.clickTotalDataIndex); + else if (serie.context.totalDataIndex >= serie.dataCount) + index = index - (serie.context.totalDataIndex - serie.context.clickTotalDataIndex); + if (index < 0 || index >= serie.dataCount) + { + index = -1; + flag = false; + } + } + if (component.context.xAxisClickIndex != index) + { + component.context.xAxisClickIndex = index; + if (component.onClickIndex != null) + { + component.onClickIndex(index); + } + } + serie.context.pointerEnter = true; + serie.context.pointerAxisDataIndexs.Add(index); + serie.context.pointerItemDataIndex = index; + xAxis.context.axisTooltipValue = index; + } + } + else + { + if (isTriggerAxis) + { + serie.context.pointerEnter = true; + GetSerieDataIndexByAxis(serie, xAxis, grid); + } + else + { + GetSerieDataIndexByItem(serie, xAxis, grid); + } + } + return flag; + } + + private void GetSerieDataByXYAxis(Serie serie, Axis xAxis, Axis yAxis) + { + var xAxisIndex = AxisHelper.GetAxisValueSplitIndex(xAxis, xAxis.context.pointerValue, false); + var yAxisIndex = AxisHelper.GetAxisValueSplitIndex(yAxis, yAxis.context.pointerValue, false); + serie.context.pointerItemDataIndex = -1; + if (serie is Heatmap) + { + var heatmap = serie as Heatmap; + if (heatmap.heatmapType == HeatmapType.Count) + { + serie.context.pointerItemDataIndex = HeatmapHandler.GetGridKey(xAxisIndex, yAxisIndex); + return; + } + } + foreach (var serieData in serie.data) + { + var x = AxisHelper.GetAxisValueSplitIndex(xAxis, serieData.GetData(0), true); + var y = AxisHelper.GetAxisValueSplitIndex(yAxis, serieData.GetData(1), true); + if (xAxisIndex == x && y == yAxisIndex) + { + serie.context.pointerItemDataIndex = serieData.index; + break; + } + } + } + + private void GetSerieDataIndexByAxis(Serie serie, Axis axis, GridCoord grid, int dimension = 0) + { + var currValue = 0d; + var lastValue = 0d; + var nextValue = 0d; + var axisValue = axis.context.pointerValue; + var isTimeAxis = axis.IsTime(); + var dataCount = serie.dataCount; + var themeSymbolSize = chart.theme.serie.scatterSymbolSize; + var data = serie.data; + if (!isTimeAxis)// || serie.useSortData) + { + serie.context.sortedData.Clear(); + for (int i = 0; i < dataCount; i++) + { + var serieData = serie.data[i]; + serie.context.sortedData.Add(serieData); + } + serie.context.sortedData.Sort(delegate (SerieData a, SerieData b) + { + return a.GetData(dimension).CompareTo(b.GetData(dimension)); + }); + data = serie.context.sortedData; + } + serie.context.pointerAxisDataIndexs.Clear(); + for (int i = 0; i < dataCount; i++) + { + var serieData = data[i]; + currValue = serieData.GetData(dimension); + if (i == 0 && i + 1 < dataCount) + { + nextValue = data[i + 1].GetData(dimension); + if (axisValue <= currValue + (nextValue - currValue) / 2) + { + serie.context.pointerAxisDataIndexs.Add(serieData.index); + break; + } + } + else if (i == dataCount - 1) + { + if (axisValue > lastValue + (currValue - lastValue) / 2) + { + serie.context.pointerAxisDataIndexs.Add(serieData.index); + break; + } + } + else if (i + 1 < dataCount) + { + nextValue = data[i + 1].GetData(dimension); + if (axisValue > (currValue - (currValue - lastValue) / 2) && axisValue <= currValue + (nextValue - currValue) / 2) + { + serie.context.pointerAxisDataIndexs.Add(serieData.index); + break; + } + } + lastValue = currValue; + } + if (serie.context.pointerAxisDataIndexs.Count > 0) + { + var index = serie.context.pointerAxisDataIndexs[0]; + serie.context.pointerItemDataIndex = index; + axis.context.axisTooltipValue = serie.GetSerieData(index).GetData(dimension); + } + else + { + serie.context.pointerItemDataIndex = -1; + axis.context.axisTooltipValue = 0; + } + } + + private void GetSerieDataIndexByItem(Serie serie, Axis axis, GridCoord grid, int dimension = 0) + { + if (serie.context.pointerItemDataIndex >= 0) + { + axis.context.axisTooltipValue = serie.GetSerieData(serie.context.pointerItemDataIndex).GetData(dimension); + } + else if (component.type == Tooltip.Type.Cross) + { + axis.context.axisTooltipValue = axis.context.pointerValue; + } + else + { + axis.context.axisTooltipValue = 0; + } + } + + private bool SetSerieTooltip(Tooltip tooltip, Serie serie) + { + if (serie.context.pointerItemDataIndex < 0) return false; + tooltip.context.type = tooltip.type == Tooltip.Type.Auto ? serie.context.tooltipType : tooltip.type; + tooltip.context.trigger = tooltip.trigger == Tooltip.Trigger.Auto ? serie.context.tooltipTrigger : tooltip.trigger; + if (tooltip.context.trigger == Tooltip.Trigger.None) return false; + tooltip.context.data.param.Clear(); + tooltip.context.data.title = serie.serieName; + tooltip.context.pointer = GetTooltipPointerPos(); + + serie.handler.UpdateTooltipSerieParams(serie.context.pointerItemDataIndex, false, null, + tooltip.marker, tooltip.itemFormatter, tooltip.numericFormatter, tooltip.ignoreDataDefaultContent, + ref tooltip.context.data.param, + ref tooltip.context.data.title); + TooltipHelper.ResetTooltipParamsByItemFormatter(tooltip, chart); + + tooltip.SetActive(m_ShowTooltip); + tooltip.view.Refresh(); + TooltipHelper.LimitInRect(chart, tooltip, chart.chartRect); + return true; + } + + private Vector2 GetTooltipPointerPos() + { + if (chart.isTriggerOnClick && chart.isPointerClick) + return chart.clickPos; + else + return chart.pointerPos; + } + + private bool SetSerieTooltip(Tooltip tooltip, List<Serie> series) + { + if (tooltip.context.trigger == Tooltip.Trigger.None) + return false; + + if (series.Count <= 0) + return false; + + string category = null; + var showCategory = false; + var isTriggerByAxis = false; + var isTriggerByItem = tooltip.context.trigger == Tooltip.Trigger.Item; + var dataIndex = -1; + var timestamp = -1; + double axisRange = 0; + tooltip.context.data.param.Clear(); + tooltip.context.pointer = GetTooltipPointerPos(); + if (m_PointerContainer is GridCoord) + { + GetAxisCategory(m_PointerContainer.index, ref dataIndex, ref category, ref timestamp, ref axisRange); + if (tooltip.context.trigger == Tooltip.Trigger.Axis) + { + isTriggerByAxis = true; + if (series.Count <= 1) + { + showCategory = true; + tooltip.context.data.title = series[0].serieName; + } + else + { + tooltip.context.data.title = category; + } + } + else if (tooltip.context.trigger == Tooltip.Trigger.Item) + { + isTriggerByItem = true; + showCategory = series.Count <= 1; + } + } + + var triggerSerieCount = 0; + for (int i = 0; i < series.Count; i++) + { + var serie = series[i]; + if (!serie.show) continue; + if (isTriggerByItem && serie.context.pointerItemDataIndex < 0) continue; + triggerSerieCount++; + serie.context.isTriggerByAxis = isTriggerByAxis; + if (isTriggerByAxis && dataIndex >= 0 && serie.context.pointerItemDataIndex < 0) + serie.context.pointerItemDataIndex = dataIndex; + if (timestamp >= 0) + { + showCategory = false; + var serieData = serie.GetSerieData(serie.context.pointerItemDataIndex); + if (serieData != null) + { + tooltip.context.data.title = DateTimeUtil.GetDefaultDateTimeString((int)serieData.GetData(0), axisRange); + } + } + serie.handler.UpdateTooltipSerieParams(dataIndex, showCategory, category, + tooltip.marker, tooltip.itemFormatter, tooltip.numericFormatter, + tooltip.ignoreDataDefaultContent, + ref tooltip.context.data.param, + ref tooltip.context.data.title); + } + if (triggerSerieCount <= 0) + { + return false; + } + TooltipHelper.ResetTooltipParamsByItemFormatter(tooltip, chart); + if (tooltip.context.data.param.Count > 0 || !string.IsNullOrEmpty(tooltip.context.data.title)) + { + tooltip.SetActive(m_ShowTooltip); + if (tooltip.view != null) + tooltip.view.Refresh(); + TooltipHelper.LimitInRect(chart, tooltip, chart.chartRect); + return true; + } + return false; + } + + private bool GetAxisCategory(int gridIndex, ref int dataIndex, ref string category, ref int timestamp, ref double axisRange) + { + foreach (var component in chart.components) + { + if (component is Axis) + { + var axis = component as Axis; + if (axis.gridIndex == gridIndex) + { + if (axis.IsCategory()) + { + dataIndex = double.IsNaN(axis.context.pointerValue) + ? axis.context.dataZoomStartIndex + : (int)axis.context.axisTooltipValue; + category = axis.GetData(dataIndex); + return true; + } + else if (axis.IsTime()) + { + timestamp = (int)axis.context.pointerValue; + axisRange = axis.context.minMaxRange; + } + } + } + } + return false; + } + + private void DrawTooltipIndicator(VertexHelper vh, Tooltip tooltip) + { + if (!tooltip.show) return; + if (tooltip.context.type == Tooltip.Type.None) return; + if (!IsAnySerieNeedTooltip()) return; + if (m_PointerContainer is GridCoord) + { + var grid = m_PointerContainer as GridCoord; + if (!grid.context.isPointerEnter) return; + if (IsYCategoryOfGrid(grid.index)) + DrawYAxisIndicator(vh, tooltip, grid); + else + DrawXAxisIndicator(vh, tooltip, grid); + } + else if (m_PointerContainer is PolarCoord) + { + DrawPolarIndicator(vh, tooltip, m_PointerContainer as PolarCoord); + } + } + + private bool IsYCategoryOfGrid(int gridIndex) + { + foreach (var component in chart.GetChartComponents<YAxis>()) + { + var yAxis = component as YAxis; + if (yAxis.gridIndex == gridIndex && !yAxis.IsCategory()) return false; + } + foreach (var component in chart.GetChartComponents<XAxis>()) + { + var xAxis = component as XAxis; + if (xAxis.gridIndex == gridIndex && xAxis.IsCategory()) return false; + } + return true; + } + + private void DrawXAxisIndicator(VertexHelper vh, Tooltip tooltip, GridCoord grid) + { + var xAxes = chart.GetChartComponents<XAxis>(); + var lineType = tooltip.lineStyle.GetType(chart.theme.tooltip.lineType); + var lineWidth = tooltip.lineStyle.GetWidth(chart.theme.tooltip.lineWidth); + foreach (var component in xAxes) + { + var xAxis = component as XAxis; + if (xAxis.gridIndex == grid.index) + { + if (double.IsInfinity(xAxis.context.pointerValue)) + continue; + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + int dataCount = chart.series.Count > 0 ? chart.series[0].GetDataList(dataZoom).Count : 0; + float splitWidth = AxisHelper.GetDataWidth(xAxis, grid.context.width, dataCount, dataZoom); + switch (tooltip.context.type) + { + case Tooltip.Type.Cross: + case Tooltip.Type.Line: + float pX = grid.context.x; + pX += xAxis.IsCategory() ? + (float)(xAxis.context.pointerValue * splitWidth + (xAxis.boundaryGap ? splitWidth / 2 : 0)) : + xAxis.GetDistance(xAxis.context.axisTooltipValue, grid.context.width); + if (pX < grid.context.x) + break; + Vector2 sp = new Vector2(pX, grid.context.y); + Vector2 ep = new Vector2(pX, grid.context.y + grid.context.height); + var lineColor = TooltipHelper.GetLineColor(tooltip, chart.theme.tooltip.lineColor); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor); + if (tooltip.context.type == Tooltip.Type.Cross) + { + sp = new Vector2(grid.context.x, chart.pointerPos.y); + ep = new Vector2(grid.context.x + grid.context.width, chart.pointerPos.y); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor); + } + break; + case Tooltip.Type.Shadow: + if (xAxis.IsCategory() && !double.IsInfinity(xAxis.context.pointerValue)) + { + float tooltipSplitWid = splitWidth < 1 ? 1 : splitWidth; + pX = (float)(grid.context.x + splitWidth * xAxis.context.pointerValue - + (xAxis.boundaryGap ? 0 : splitWidth / 2)); + if (pX < grid.context.x) + break; + float pY = grid.context.y + grid.context.height; + Vector3 p1 = chart.ClampInGrid(grid, new Vector3(pX, grid.context.y)); + Vector3 p2 = chart.ClampInGrid(grid, new Vector3(pX, pY)); + Vector3 p3 = chart.ClampInGrid(grid, new Vector3(pX + tooltipSplitWid, pY)); + Vector3 p4 = chart.ClampInGrid(grid, new Vector3(pX + tooltipSplitWid, grid.context.y)); + var areaColor = TooltipHelper.GetLineColor(tooltip, chart.theme.tooltip.areaColor); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, areaColor); + } + break; + } + } + } + } + + private bool IsAnySerieNeedTooltip() + { + foreach (var serie in chart.series) + { + if (serie.context.pointerEnter) return true; + } + return false; + } + private void DrawYAxisIndicator(VertexHelper vh, Tooltip tooltip, GridCoord grid) + { + var yAxes = chart.GetChartComponents<YAxis>(); + var lineType = tooltip.lineStyle.GetType(chart.theme.tooltip.lineType); + var lineWidth = tooltip.lineStyle.GetWidth(chart.theme.tooltip.lineWidth); + foreach (var component in yAxes) + { + var yAxis = component as YAxis; + if (yAxis.gridIndex == grid.index) + { + if (double.IsInfinity(yAxis.context.pointerValue)) + continue; + var dataZoom = chart.GetDataZoomOfAxis(yAxis); + int dataCount = chart.series.Count > 0 ? chart.series[0].GetDataList(dataZoom).Count : 0; + float splitWidth = AxisHelper.GetDataWidth(yAxis, grid.context.height, dataCount, dataZoom); + switch (tooltip.context.type) + { + case Tooltip.Type.Cross: + case Tooltip.Type.Line: + float pY = (float)(grid.context.y + yAxis.context.pointerValue * splitWidth + + (yAxis.boundaryGap ? splitWidth / 2 : 0)); + if (pY < grid.context.y) + break; + Vector2 sp = new Vector2(grid.context.x, pY); + Vector2 ep = new Vector2(grid.context.x + grid.context.width, pY); + var lineColor = TooltipHelper.GetLineColor(tooltip, chart.theme.tooltip.lineColor); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor); + if (tooltip.context.type == Tooltip.Type.Cross) + { + sp = new Vector2(chart.pointerPos.x, grid.context.y); + ep = new Vector2(chart.pointerPos.x, grid.context.y + grid.context.height); + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor); + } + break; + case Tooltip.Type.Shadow: + if (yAxis.IsCategory()) + { + float tooltipSplitWid = splitWidth < 1 ? 1 : splitWidth; + float pX = grid.context.x + grid.context.width; + pY = (float)(grid.context.y + splitWidth * yAxis.context.pointerValue - + (yAxis.boundaryGap ? 0 : splitWidth / 2)); + if (pY < grid.context.y) + break; + Vector3 p1 = new Vector3(grid.context.x, pY); + Vector3 p2 = new Vector3(grid.context.x, pY + tooltipSplitWid); + Vector3 p3 = new Vector3(pX, pY + tooltipSplitWid); + Vector3 p4 = new Vector3(pX, pY); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, chart.theme.tooltip.areaColor); + } + break; + } + } + } + } + + private void DrawPolarIndicator(VertexHelper vh, Tooltip tooltip, PolarCoord m_Polar) + { + if (tooltip.context.angle < 0) return; + var theme = chart.theme; + var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, m_Polar.index); + var lineColor = TooltipHelper.GetLineColor(tooltip, theme.tooltip.lineColor); + var lineType = tooltip.lineStyle.GetType(theme.tooltip.lineType); + var lineWidth = tooltip.lineStyle.GetWidth(theme.tooltip.lineWidth); + var cenPos = m_Polar.context.center; + var radius = m_Polar.context.outsideRadius; + var tooltipAngle = m_AngleAxis.GetValueAngle(tooltip.context.angle); + + var sp = ChartHelper.GetPos(m_Polar.context.center, m_Polar.context.insideRadius, tooltipAngle, true); + var ep = ChartHelper.GetPos(m_Polar.context.center, m_Polar.context.outsideRadius, tooltipAngle, true); + + switch (tooltip.context.type) + { + case Tooltip.Type.Cross: + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor); + var dist = Vector2.Distance(chart.pointerPos, cenPos); + if (dist > radius) dist = radius; + var outsideRaidus = dist + tooltip.lineStyle.GetWidth(theme.tooltip.lineWidth) * 2; + UGL.DrawDoughnut(vh, cenPos, dist, outsideRaidus, lineColor, Color.clear); + break; + case Tooltip.Type.Line: + ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor); + break; + case Tooltip.Type.Shadow: + UGL.DrawSector(vh, cenPos, radius, lineColor, tooltipAngle - 2, tooltipAngle + 2, chart.settings.cicleSmoothness); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs.meta b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs.meta new file mode 100644 index 0000000..7fb5702 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d25a5b5e3d6f45b8a06b94e33792087 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs new file mode 100644 index 0000000..467dce8 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs @@ -0,0 +1,155 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class TooltipHelper + { + internal static void ResetTooltipParamsByItemFormatter(Tooltip tooltip, BaseChart chart) + { + if (!string.IsNullOrEmpty(tooltip.titleFormatter)) + { + if (IsIgnoreFormatter(tooltip.titleFormatter)) + { + tooltip.context.data.title = string.Empty; + } + else + { + tooltip.context.data.title = tooltip.titleFormatter; + FormatterHelper.ReplaceContent(ref tooltip.context.data.title, -1, + tooltip.numericFormatter, null, chart); + } + } + for (int i = tooltip.context.data.param.Count - 1; i >= 0; i--) + { + var param = tooltip.context.data.param[i]; + if (IsIgnoreFormatter(param.itemFormatter)) + { + tooltip.context.data.param.RemoveAt(i); + } + } + foreach (var param in tooltip.context.data.param) + { + if (!string.IsNullOrEmpty(param.itemFormatter)) + { + param.columns.Clear(); + var content = param.itemFormatter; + FormatterHelper.ReplaceSerieLabelContent(ref content, + param.numericFormatter, + param.dataCount, + param.value, + param.total, + param.serieName, + param.category, + param.serieData.name, + param.color, + param.serieData, + chart, + param.serieIndex); + foreach (var item in content.Split('|')) + { + param.columns.Add(item); + } + } + } + } + + public static bool IsIgnoreFormatter(string itemFormatter) + { + return "-".Equals(itemFormatter) ||"{i}".Equals(itemFormatter, StringComparison.CurrentCultureIgnoreCase); + } + + public static void LimitInRect(BaseChart chart, Tooltip tooltip, Rect chartRect) + { + if (tooltip.view == null) + return; + + var pos = tooltip.view.GetTargetPos(); + if (pos.x + tooltip.context.width > chartRect.x + chartRect.width) + { + pos.x = tooltip.context.pointer.x - tooltip.context.width - tooltip.offset.x; + } + else if (pos.x < chartRect.x) + { + pos.x = tooltip.context.pointer.x - tooltip.context.width + Mathf.Abs(tooltip.offset.x); + } + if (pos.y - tooltip.context.height < chartRect.y) + { + pos.y = chartRect.y + tooltip.context.height; + } + if (pos.y > chartRect.y + chartRect.height) + pos.y = chartRect.y + chartRect.height; + var screenGap = 10; + var screenPos = chart.LocalPointToScreenPoint(pos); + if (screenPos.x < screenGap) + pos.x += Mathf.Abs(screenPos.x) + screenGap; + if (screenPos.x + tooltip.context.width > Screen.width - screenGap) + pos.x -= Mathf.Abs(screenPos.x + tooltip.context.width - Screen.width) + screenGap; + + if (screenPos.y < tooltip.context.height + screenGap) + pos.y += Mathf.Abs(screenPos.y - tooltip.context.height) + screenGap; + if (screenPos.y > Screen.height - screenGap) + pos.y -= Mathf.Abs(screenPos.y - Screen.height) + screenGap; + + UpdateContentPos(tooltip, pos, chartRect); + } + + /// <summary> + /// 更新文本框位置 + /// </summary> + /// <param name="pos"></param> + private static void UpdateContentPos(Tooltip tooltip, Vector2 pos, Rect chartRect) + { + if (tooltip.view != null) + { + var width = chartRect.width; + var height = chartRect.height; + switch (tooltip.position) + { + case Tooltip.Position.Auto: +#if UNITY_ANDROID || UNITY_IOS + if (tooltip.fixedY == 0) pos.y = chartRect.x + ChartHelper.GetActualValue(0.7f, height); + else pos.y = chartRect.y + ChartHelper.GetActualValue(tooltip.fixedY, height); +#endif + break; + case Tooltip.Position.Custom: + pos = new Vector2(chartRect.x, chartRect.y); + pos.x += ChartHelper.GetActualValue(tooltip.fixedX, width); + pos.y += ChartHelper.GetActualValue(tooltip.fixedY, height); + break; + case Tooltip.Position.FixedX: + pos = new Vector2(chartRect.x, pos.y); + pos.x += ChartHelper.GetActualValue(tooltip.fixedX, width); + break; + case Tooltip.Position.FixedY: + pos = new Vector2(pos.x, chartRect.y); + pos.y += ChartHelper.GetActualValue(tooltip.fixedY, height); + break; + } + tooltip.view.UpdatePosition(pos); + } + } + + public static string GetItemNumericFormatter(Tooltip tooltip, Serie serie, SerieData serieData) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (!string.IsNullOrEmpty(itemStyle.numericFormatter)) return itemStyle.numericFormatter; + else return tooltip.numericFormatter; + } + + public static Color32 GetLineColor(Tooltip tooltip, Color32 defaultColor) + { + var lineStyle = tooltip.lineStyle; + if (!ChartHelper.IsClearColor(lineStyle.color)) + { + return lineStyle.GetColor(); + } + else + { + var color = defaultColor; + ChartHelper.SetColorOpacity(ref color, lineStyle.opacity); + return color; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs.meta b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs.meta new file mode 100644 index 0000000..169f2a5 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 184e190de6da6486b8b4d333a302477a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs b/Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs new file mode 100644 index 0000000..0ae7870 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs @@ -0,0 +1,301 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + public class TooltipViewItem + { + public GameObject gameObject; + public List<ChartLabel> columns = new List<ChartLabel>(); + } + public class TooltipView + { + private static Vector2 anchorMax = new Vector2(0, 1); + private static Vector2 anchorMin = new Vector2(0, 1); + private static Vector2 pivot = new Vector2(0, 1); + private static Vector2 v2_0_05 = new Vector2(0, 0.5f); + + public Tooltip tooltip; + public ComponentTheme theme; + public GameObject gameObject; + public Transform transform; + public Image background; + public Outline border; + public VerticalLayoutGroup layout; + public ChartLabel title; + private List<TooltipViewItem> m_Items = new List<TooltipViewItem>(); + private List<float> m_ColumnMaxWidth = new List<float>(); + private bool m_Active = false; + private Vector3 m_TargetPos; + private Vector3 m_CurrentVelocity; + + public void Update() + { + if (!m_Active) + return; + transform.localPosition = Vector3.SmoothDamp(transform.localPosition, m_TargetPos, ref m_CurrentVelocity, 0.08f); + } + + public Vector3 GetCurrentPos() + { + return transform.localPosition; + } + + public Vector3 GetTargetPos() + { + return m_TargetPos; + } + + public void UpdatePosition(Vector3 pos) + { + m_TargetPos = pos; + } + + public void SetActive(bool flag) + { + m_Active = flag && tooltip.showContent; + ChartHelper.SetActive(gameObject, m_Active); + if (!flag) + { + m_ColumnMaxWidth.Clear(); + foreach (var item in m_Items) + item.gameObject.SetActive(false); + } + } + + public void Refresh() + { + if (tooltip == null) return; + var data = tooltip.context.data; + var ignoreColumn = string.IsNullOrEmpty(tooltip.ignoreDataDefaultContent); + + var titleActive = !string.IsNullOrEmpty(data.title); + ChartHelper.SetActive(title, titleActive); + title.SetText(data.title); + + for (int i = 0; i < data.param.Count; i++) + { + var item = GetItem(i); + var param = data.param[i]; + if (param.columns.Count <= 0 || (ignoreColumn && param.ignore)) + { + item.gameObject.SetActive(false); + continue; + } + item.gameObject.SetActive(true); + for (int j = 0; j < param.columns.Count; j++) + { + var column = GetItemColumn(item, j, j == 0 && IsSecondaryMark(param, param.columns[j])); + column.SetActive(true); + column.SetText(param.columns[j]); + + if (j == 0) + { + var labelStyle = tooltip.GetContentLabelStyle(j); + if (labelStyle != null && ChartHelper.IsClearColor(labelStyle.textStyle.color)) + column.text.SetColor(param.color); + } + + if (j >= m_ColumnMaxWidth.Count) + m_ColumnMaxWidth.Add(0); + + var columnWidth = column.text.GetPreferredWidth() + GetTooltipColumnGapWidth(tooltip, j); + if (m_ColumnMaxWidth[j] < columnWidth) + m_ColumnMaxWidth[j] = columnWidth; + } + for (int j = param.columns.Count; j < item.columns.Count; j++) + { + item.columns[j].SetActive(false); + } + } + for (int i = data.param.Count; i < m_Items.Count; i++) + { + m_Items[i].gameObject.SetActive(false); + } + ResetSize(); + UpdatePosition(tooltip.context.pointer + tooltip.offset); + tooltip.gameObject.transform.SetAsLastSibling(); + } + + private static float GetTooltipColumnGapWidth(Tooltip tooltip, int index) + { + if (tooltip == null || tooltip.columnGapWidths.Count == 0) return 0; + if (tooltip.columnGapWidths.Count == 1) return index == 1 ? tooltip.columnGapWidths[0] : 0; + if (index < tooltip.columnGapWidths.Count) + { + return tooltip.columnGapWidths[index]; + } + return 0; + } + + private static bool IsSecondaryMark(SerieParams sp, string mark) + { + return sp.isSecondaryMark && mark == sp.marker; + } + + private void ResetSize() + { + var maxHig = 0f; + var maxWid = 0f; + if (tooltip.fixedWidth > 0) + { + maxWid = tooltip.fixedWidth; + } + else + { + maxWid = TotalMaxWidth(); + var titleWid = title.GetTextWidth(); + if (maxWid < titleWid) + maxWid = titleWid; + } + + if (tooltip.fixedHeight > 0) + { + maxHig = tooltip.fixedHeight; + } + else + { + if (!string.IsNullOrEmpty(title.text.GetText())) + maxHig += tooltip.titleHeight; + maxHig += tooltip.itemHeight * tooltip.context.data.param.Count; + maxHig += tooltip.paddingTopBottom * 2; + } + + if (tooltip.minWidth > 0 && maxWid < tooltip.minWidth) + maxWid = tooltip.minWidth; + + if (tooltip.minHeight > 0 && maxHig < tooltip.minHeight) + maxHig = tooltip.minHeight; + + for (int i = 0; i < m_Items.Count; i++) + { + var item = m_Items[i]; + item.gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(maxWid, tooltip.itemHeight); + var xPos = 0f; + for (int j = 0; j < m_ColumnMaxWidth.Count; j++) + { + if (j >= item.columns.Count) break; + var deltaX = j == m_ColumnMaxWidth.Count - 1 ? maxWid - xPos : m_ColumnMaxWidth[j]; + item.columns[j].text.SetSizeDelta(new Vector2(deltaX, tooltip.itemHeight)); + item.columns[j].SetSize(deltaX, tooltip.itemHeight); + item.columns[j].SetRectPosition(new Vector3(xPos, 0)); + xPos += m_ColumnMaxWidth[j]; + } + } + tooltip.context.width = maxWid + tooltip.paddingLeftRight * 2; + tooltip.context.height = maxHig; + background.GetComponent<RectTransform>().sizeDelta = new Vector2(tooltip.context.width, tooltip.context.height); + } + + private float TotalMaxWidth() + { + var total = 0f; + foreach (var max in m_ColumnMaxWidth) + total += max; + return total; + } + + private TooltipViewItem GetItem(int i) + { + if (i < 0) i = 0; + if (i < m_Items.Count) + { + return m_Items[i]; + } + else + { + var item = CreateViewItem(i, gameObject.transform, tooltip, theme); + m_Items.Add(item); + return item; + } + } + + private ChartLabel GetItemColumn(TooltipViewItem item, int i, bool isSecondaryMark = false) + { + if (i < 0) i = 0; + ChartLabel column; + if (i < item.columns.Count) + { + column = item.columns[i]; + } + else + { + column = CreateViewItemColumn(i, item.gameObject.transform, tooltip, theme); + item.columns.Add(column); + } + if (isSecondaryMark) + { + column.text.text.fontSize = (int)(tooltip.GetContentLabelStyle(i).textStyle.fontSize * 0.6f); + } + return column; + } + + public static TooltipView CreateView(Tooltip tooltip, ThemeStyle theme, Transform parent) + { + var view = new TooltipView(); + view.tooltip = tooltip; + view.theme = theme.tooltip; + + view.gameObject = ChartHelper.AddObject("view", parent, anchorMin, anchorMax, pivot, Vector3.zero); + view.gameObject.transform.localPosition = Vector3.zero; + view.transform = view.gameObject.transform; + + view.background = ChartHelper.EnsureComponent<Image>(view.gameObject); + view.background.sprite = tooltip.backgroundImage; + view.background.type = tooltip.backgroundType; + view.background.color = ChartHelper.IsClearColor(tooltip.backgroundColor) ? + Color.white : tooltip.backgroundColor; + + view.border = ChartHelper.EnsureComponent<Outline>(view.gameObject); + view.border.enabled = tooltip.borderWidth > 0; + view.border.useGraphicAlpha = false; + view.border.effectColor = tooltip.borderColor; + view.border.effectDistance = new Vector2(tooltip.borderWidth, -tooltip.borderWidth); + + view.layout = ChartHelper.EnsureComponent<VerticalLayoutGroup>(view.gameObject); + view.layout.childControlHeight = false; + view.layout.childControlWidth = false; + view.layout.childForceExpandHeight = false; + view.layout.childForceExpandWidth = false; + view.layout.padding = new RectOffset(tooltip.paddingLeftRight, + tooltip.paddingLeftRight, + tooltip.paddingTopBottom, + tooltip.paddingTopBottom); + + view.title = ChartHelper.AddChartLabel("title", view.gameObject.transform, tooltip.titleLabelStyle, theme.tooltip, + "", Color.clear, TextAnchor.MiddleLeft); + view.title.gameObject.SetActive(true); + + var item = CreateViewItem(0, view.gameObject.transform, tooltip, theme.tooltip); + view.m_Items.Add(item); + + view.Refresh(); + + return view; + } + + private static TooltipViewItem CreateViewItem(int i, Transform parent, Tooltip tooltip, ComponentTheme theme) + { + GameObject item1 = ChartHelper.AddObject("item" + i, parent, anchorMin, anchorMax, v2_0_05, Vector3.zero); + + var item = new TooltipViewItem(); + item.gameObject = item1; + item.columns.Add(CreateViewItemColumn(0, item1.transform, tooltip, theme)); + item.columns.Add(CreateViewItemColumn(1, item1.transform, tooltip, theme)); + item.columns.Add(CreateViewItemColumn(2, item1.transform, tooltip, theme)); + return item; + } + + private static ChartLabel CreateViewItemColumn(int i, Transform parent, Tooltip tooltip, ComponentTheme theme) + { + var labelStyle = tooltip.GetContentLabelStyle(i); + labelStyle.textStyle.autoAlign = false; + var label = ChartHelper.AddChartLabel("column" + i, parent, labelStyle, theme, + "", Color.clear, TextAnchor.MiddleLeft, true); + return label; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs.meta b/Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs.meta new file mode 100644 index 0000000..bdc9a34 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Tooltip/TooltipView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20bbaf6c402824f5d8abcaf1cee57865 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/VisualMap.meta b/Assets/XCharts/Runtime/Component/VisualMap.meta new file mode 100644 index 0000000..1077d02 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c21848eac9668493db23591788d54bf0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs b/Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs new file mode 100644 index 0000000..756b8d9 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs @@ -0,0 +1,660 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class VisualMapRange : ChildComponent + { + [SerializeField] private double m_Min; + [SerializeField] private double m_Max; + [SerializeField] private string m_Label; + [SerializeField] private Color32 m_Color; + + /// <summary> + /// 范围最小值 + /// </summary> + public double min { get { return m_Min; } set { m_Min = value; } } + /// <summary> + /// 范围最大值 + /// </summary> + public double max { get { return m_Max; } set { m_Max = value; } } + /// <summary> + /// 文字描述 + /// </summary> + public string label { get { return m_Label; } set { m_Label = value; } } + /// <summary> + /// 颜色 + /// </summary> + public Color32 color { get { return m_Color; } set { m_Color = value; } } + + public bool Contains(double value, double minMaxRange) + { + if (m_Min == 0 && m_Max == 0) return false; + var cmin = System.Math.Abs(m_Min) < 1 ? minMaxRange * m_Min : m_Min; + var cmax = System.Math.Abs(m_Max) < 1 ? minMaxRange * m_Max : m_Max; + return value >= cmin && value < cmax; + } + } + + /// <summary> + /// VisualMap component. Mapping data to visual elements such as colors. + /// ||视觉映射组件。用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道)。 + /// </summary> + [System.Serializable] + [ComponentHandler(typeof(VisualMapHandler), true)] + public class VisualMap : MainComponent + { + /// <summary> + /// 类型。分为连续型和分段型。 + /// </summary> + public enum Type + { + /// <summary> + /// 连续型。 + /// </summary> + Continuous, + /// <summary> + /// 分段型。 + /// </summary> + Piecewise + } + + /// <summary> + /// 选择模式 + /// </summary> + public enum SelectedMode + { + /// <summary> + /// 多选。 + /// </summary> + Multiple, + /// <summary> + /// 单选。 + /// </summary> + Single + } + + [SerializeField] private bool m_Show = true; + [SerializeField] private bool m_ShowUI = false; + [SerializeField] private Type m_Type = Type.Continuous; + [SerializeField] private SelectedMode m_SelectedMode = SelectedMode.Multiple; + [SerializeField] private int m_SerieIndex = 0; + [SerializeField] private double m_Min = 0; + [SerializeField] private double m_Max = 0; + + [SerializeField] private double[] m_Range = new double[2] { 0, 0 }; + [SerializeField] private string[] m_Text = new string[2] { "", "" }; + [SerializeField] private float[] m_TextGap = new float[2] { 10f, 10f }; + [SerializeField] private int m_SplitNumber = 5; + [SerializeField] private bool m_Calculable = false; + [SerializeField] private bool m_Realtime = true; + [SerializeField] private float m_ItemWidth = 20f; + [SerializeField] private float m_ItemHeight = 140f; + [SerializeField] private float m_ItemGap = 10f; + [SerializeField] private float m_BorderWidth = 0; + [SerializeField] private int m_Dimension = -1; + [SerializeField] private bool m_HoverLink = true; + [SerializeField] private bool m_AutoMinMax = true; + [SerializeField] private Orient m_Orient = Orient.Horizonal; + [SerializeField] private Location m_Location = Location.defaultLeft; + [SerializeField] private bool m_WorkOnLine = true; + [SerializeField] private bool m_WorkOnArea = false; + + [SerializeField] private List<VisualMapRange> m_OutOfRange = new List<VisualMapRange>() { new VisualMapRange() { color = Color.gray } }; + [SerializeField] private List<VisualMapRange> m_InRange = new List<VisualMapRange>(); + + public VisualMapContext context = new VisualMapContext(); + + /// <summary> + /// Whether to enable components. + /// ||组件是否生效。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to display components. If set to false, it will not show up, but the data mapping function still exists. + /// ||是否显示组件。如果设置为 false,不会显示,但是数据映射的功能还存在。 + /// </summary> + public bool showUI + { + get { return m_ShowUI; } + set { if (PropertyUtil.SetStruct(ref m_ShowUI, value)) SetVerticesDirty(); } + } + /// <summary> + /// the type of visualmap component. + /// ||组件类型。 + /// </summary> + public Type type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetVerticesDirty(); } + } + /// <summary> + /// the selected mode for Piecewise visualMap. + /// ||选择模式。 + /// </summary> + public SelectedMode selectedMode + { + get { return m_SelectedMode; } + set { if (PropertyUtil.SetStruct(ref m_SelectedMode, value)) SetVerticesDirty(); } + } + /// <summary> + /// the serie index of visualMap. + /// ||影响的serie索引。 + /// </summary> + public int serieIndex + { + get { return m_SerieIndex; } + set { if (PropertyUtil.SetStruct(ref m_SerieIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// The minimum allowed. 'min' must be user specified. [visualmap.min, visualmap.max] forms the "domain" of the visualMap. + /// || + /// 允许的最小值。`autoMinMax`为`false`时必须指定。[visualMap.min, visualMap.max] 形成了视觉映射的『定义域』。 + /// </summary> + public double min + { + get { return m_Min; } + set { if (PropertyUtil.SetStruct(ref m_Min, value)) SetVerticesDirty(); } + } + /// <summary> + /// The maximum allowed. 'max' must be user specified. [visualmap.min, visualmap.max] forms the "domain" of the visualMap. + /// || + /// 允许的最大值。`autoMinMax`为`false`时必须指定。[visualMap.min, visualMax.max] 形成了视觉映射的『定义域』。 + /// </summary> + public double max + { + get { return m_Max; } + set { m_Max = (value < min ? min + 1 : value); SetVerticesDirty(); } + } + /// <summary> + /// Specifies the position of the numeric value corresponding to the handle. Range should be within the range of [min,max]. + /// || + /// 指定手柄对应数值的位置。range 应在[min,max]范围内。 + /// </summary> + public double[] range { get { return m_Range; } } + /// <summary> + /// Text on both ends. + /// ||两端的文本,如 ['High', 'Low']。 + /// </summary> + public string[] text { get { return m_Text; } } + /// <summary> + /// The distance between the two text bodies. + /// ||两端文字主体之间的距离,单位为px。 + /// </summary> + public float[] textGap { get { return m_TextGap; } } + /// <summary> + /// For continuous data, it is automatically evenly divided into several segments + /// and automatically matches the size of inRange color list when the default is 0. + /// || + /// 对于连续型数据,自动平均切分成几段,默认为0时自动匹配inRange颜色列表大小。 + /// </summary> + public int splitNumber + { + get { return m_SplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether the handle used for dragging is displayed (the handle can be dragged to adjust the selected range). + /// || + /// 是否显示拖拽用的手柄(手柄能拖拽调整选中范围)。 + /// </summary> + public bool calculable + { + get { return m_Calculable; } + set { if (PropertyUtil.SetStruct(ref m_Calculable, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to update in real time while dragging. + /// || + /// 拖拽时,是否实时更新。 + /// </summary> + public bool realtime + { + get { return m_Realtime; } + set { if (PropertyUtil.SetStruct(ref m_Realtime, value)) SetVerticesDirty(); } + } + /// <summary> + /// The width of the figure, that is, the width of the color bar. + /// || + /// 图形的宽度,即颜色条的宽度。 + /// </summary> + public float itemWidth + { + get { return m_ItemWidth; } + set { if (PropertyUtil.SetStruct(ref m_ItemWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// The height of the figure, that is, the height of the color bar. + /// || + /// 图形的高度,即颜色条的高度。 + /// </summary> + public float itemHeight + { + get { return m_ItemHeight; } + set { if (PropertyUtil.SetStruct(ref m_ItemHeight, value)) SetVerticesDirty(); } + } + /// <summary> + /// 每个图元之间的间隔距离。 + /// </summary> + public float itemGap + { + get { return m_ItemGap; } + set { if (PropertyUtil.SetStruct(ref m_ItemGap, value)) SetVerticesDirty(); } + } + /// <summary> + /// Border line width. + /// || + /// 边框线宽,单位px。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// Specifies "which dimension" of the data to map to the visual element. "Data" is series.data. + /// ||Starting at 1, the default is 0 to take the last dimension in data. + /// || + /// 指定用数据的『哪个维度』,映射到视觉元素上。『数据』即 series.data。从1开始,默认为0取 data 中最后一个维度。 + /// </summary> + public int dimension + { + get { return m_Dimension; } + set { if (PropertyUtil.SetStruct(ref m_Dimension, value)) SetVerticesDirty(); } + } + /// <summary> + /// When the hoverLink function is turned on, when the mouse hovers over the visualMap component, + /// the corresponding value of the mouse position is highlighted in the corresponding graphic element in the diagram. + /// ||Conversely, when the mouse hovers over a graphic element in a diagram, + /// the corresponding value of the visualMap component is triangulated in the corresponding position. + /// || + /// 打开 hoverLink 功能时,鼠标悬浮到 visualMap 组件上时,鼠标位置对应的数值 在 图表中对应的图形元素,会高亮。 + /// 反之,鼠标悬浮到图表中的图形元素上时,在 visualMap 组件的相应位置会有三角提示其所对应的数值。 + /// </summary> + public bool hoverLink + { + get { return m_HoverLink; } + set { if (PropertyUtil.SetStruct(ref m_HoverLink, value)) SetVerticesDirty(); } + } + /// <summary> + /// Automatically set min, Max value + /// 自动设置min,max的值 + /// </summary> + public bool autoMinMax + { + get { return m_AutoMinMax; } + set { if (PropertyUtil.SetStruct(ref m_AutoMinMax, value)) SetVerticesDirty(); } + } + /// <summary> + /// Specify whether the layout of component is horizontal or vertical. + /// || + /// 布局方式是横还是竖。 + /// </summary> + public Orient orient + { + get { return m_Orient; } + set { if (PropertyUtil.SetStruct(ref m_Orient, value)) SetVerticesDirty(); } + } + /// <summary> + /// The location of component. + /// ||组件显示的位置。 + /// </summary> + public Location location + { + get { return m_Location; } + set { if (PropertyUtil.SetClass(ref m_Location, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether the visualmap is work on linestyle of linechart. + /// ||组件是否对LineChart的LineStyle有效。 + /// </summary> + public bool workOnLine + { + get { return m_WorkOnLine; } + set { if (PropertyUtil.SetStruct(ref m_WorkOnLine, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether the visualmap is work on areaStyle of linechart. + /// ||组件是否对LineChart的AreaStyle有效。 + /// </summary> + public bool workOnArea + { + get { return m_WorkOnArea; } + set { if (PropertyUtil.SetStruct(ref m_WorkOnArea, value)) SetVerticesDirty(); } + } + /// <summary> + /// Defines a visual color outside of the selected range. + /// ||定义 在选中范围外 的视觉颜色。 + /// </summary> + public List<VisualMapRange> outOfRange + { + get { return m_OutOfRange; } + set { if (value != null) { m_OutOfRange = value; SetVerticesDirty(); } } + } + /// <summary> + /// 分段式每一段的相关配置。 + /// </summary> + public List<VisualMapRange> inRange + { + get { return m_InRange; } + set { if (value != null) { m_InRange = value; SetVerticesDirty(); } } + } + + public override bool vertsDirty { get { return m_VertsDirty || location.anyDirty; } } + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + location.ClearVerticesDirty(); + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + location.ClearComponentDirty(); + } + + public double rangeMin + { + get + { + if (m_Range[0] == 0 && m_Range[1] == 0) return min; + else if (m_Range[0] < min || m_Range[0] > max) return min; + else return m_Range[0]; + } + set + { + if (value >= min && value <= m_Range[1]) m_Range[0] = value; + } + } + + public double rangeMax + { + get + { + if (m_Range[0] == 0 && m_Range[1] == 0) return max; + if (m_Range[1] >= m_Range[0] && m_Range[1] < max) return m_Range[1]; + else return max; + } + set + { + if (value >= m_Range[0] && value <= max) m_Range[1] = value; + } + } + + public float runtimeRangeMinHeight { get { return (float)((rangeMin - min) / (max - min) * itemHeight); } } + public float runtimeRangeMaxHeight { get { return (float)((rangeMax - min) / (max - min) * itemHeight); } } + + public void AddColors(List<Color32> colors) + { + m_InRange.Clear(); + foreach (var color in colors) + { + m_InRange.Add(new VisualMapRange() + { + color = color + }); + } + } + + public void AddColors(List<string> colors) + { + m_InRange.Clear(); + foreach (var str in colors) + { + m_InRange.Add(new VisualMapRange() + { + color = ThemeStyle.GetColor(str) + }); + } + } + + public Color32 GetColor(double xValue, double yValue, double zValue, byte alpha = 255) + { + Color32 color; + if (m_Dimension == 0) + { + color = GetColor(xValue); + } + else if (m_Dimension == 1) + { + color = GetColor(yValue); + } + else + { + color = GetColor(zValue); + } + color.a = alpha; + return color; + } + + public Color32 GetColor(double value) + { + int index = GetIndex(value); + if (index == -1) + { + if (m_OutOfRange.Count > 0) + return m_OutOfRange[0].color; + else + return ChartConst.clearColor32; + } + + if (m_Type == VisualMap.Type.Piecewise) + { + return m_InRange[index].color; + } + else + { + int splitNumber = m_InRange.Count; + var diff = (m_Max - m_Min) / (splitNumber - 1); + var nowMin = m_Min + index * diff; + var rate = (value - nowMin) / diff; + if (index == splitNumber - 1) + return m_InRange[index].color; + else + return Color32.Lerp(m_InRange[index].color, m_InRange[index + 1].color, (float)rate); + } + } + + private bool IsNeedPieceColor(double value, out int index) + { + bool flag = false; + index = -1; + for (int i = 0; i < m_InRange.Count; i++) + { + var range = m_InRange[i]; + if (range.min != 0 || range.max != 0) + { + flag = true; + if (range.Contains(value, max - min)) + { + index = i; + return true; + } + } + } + return flag; + } + + private Color32 GetPiecesColor(double value) + { + foreach (var piece in m_InRange) + { + if (piece.Contains(value, max - min)) + { + return piece.color; + } + } + if (m_OutOfRange.Count > 0) + return m_OutOfRange[0].color; + else + return ChartConst.clearColor32; + } + + public int GetIndex(double value) + { + int splitNumber = m_InRange.Count; + if (splitNumber <= 0) + return -1; + var index = -1; + if (IsNeedPieceColor(value, out index)) + { + return index; + } + value = MathUtil.Clamp(value, m_Min, m_Max); + + var diff = (m_Max - m_Min) / (splitNumber - 1); + + for (int i = 0; i < splitNumber; i++) + { + if (value <= m_Min + (i + 1) * diff) + { + index = i; + break; + } + } + return index; + } + + public bool IsPiecewise() + { + return m_Type == VisualMap.Type.Piecewise; + } + + public bool IsInSelectedValue(double value) + { + if (context.pointerIndex < 0) + return true; + else + return context.pointerIndex == GetIndex(value); + } + + public double GetValue(Vector3 pos, Rect chartRect) + { + var vertical = orient == Orient.Vertical; + var centerPos = new Vector3(chartRect.x, chartRect.y) + location.GetPosition(chartRect.width, chartRect.height); + var pos1 = centerPos + (vertical ? Vector3.down : Vector3.left) * itemHeight / 2; + var pos2 = centerPos + (vertical ? Vector3.up : Vector3.right) * itemHeight / 2; + + if (vertical) + { + if (pos.y < pos1.y) + return min; + else if (pos.y > pos2.y) + return max; + else + return min + (pos.y - pos1.y) / (pos2.y - pos1.y) * (max - min); + } + else + { + if (pos.x < pos1.x) + return min; + else if (pos.x > pos2.x) + return max; + else + return min + (pos.x - pos1.x) / (pos2.x - pos1.x) * (max - min); + } + } + + public bool IsInRect(Vector3 local, Rect chartRect, float triangleLen = 20) + { + var centerPos = new Vector3(chartRect.x, chartRect.y) + location.GetPosition(chartRect.width, chartRect.height); + var diff = calculable ? triangleLen : 0; + + if (local.x >= centerPos.x - itemWidth / 2 - diff && + local.x <= centerPos.x + itemWidth / 2 + diff && + local.y >= centerPos.y - itemHeight / 2 - diff && + local.y <= centerPos.y + itemHeight / 2 + diff) + { + return true; + } + else + { + return false; + } + } + + public bool IsInRangeRect(Vector3 local, Rect chartRect) + { + var centerPos = new Vector3(chartRect.x, chartRect.y) + location.GetPosition(chartRect.width, chartRect.height); + + if (orient == Orient.Vertical) + { + var pos1 = centerPos + Vector3.down * itemHeight / 2; + + return local.x >= centerPos.x - itemWidth / 2 && + local.x <= centerPos.x + itemWidth / 2 && + local.y >= pos1.y + runtimeRangeMinHeight && + local.y <= pos1.y + runtimeRangeMaxHeight; + } + else + { + var pos1 = centerPos + Vector3.left * itemHeight / 2; + return local.x >= pos1.x + runtimeRangeMinHeight && + local.x <= pos1.x + runtimeRangeMaxHeight && + local.y >= centerPos.y - itemWidth / 2 && + local.y <= centerPos.y + itemWidth / 2; + } + } + + public bool IsInRangeMinRect(Vector3 local, Rect chartRect, float triangleLen) + { + var centerPos = new Vector3(chartRect.x, chartRect.y) + location.GetPosition(chartRect.width, chartRect.height); + + if (orient == Orient.Vertical) + { + var radius = triangleLen / 2; + var pos1 = centerPos + Vector3.down * itemHeight / 2; + var cpos = new Vector3(pos1.x + itemWidth / 2 + radius, pos1.y + runtimeRangeMinHeight - radius); + + return local.x >= cpos.x - radius && + local.x <= cpos.x + radius && + local.y >= cpos.y - radius && + local.y <= cpos.y + radius; + } + else + { + var radius = triangleLen / 2; + var pos1 = centerPos + Vector3.left * itemHeight / 2; + var cpos = new Vector3(pos1.x + runtimeRangeMinHeight, pos1.y + itemWidth / 2 + radius); + + return local.x >= cpos.x - radius && + local.x <= cpos.x + radius && + local.y >= cpos.y - radius && + local.y <= cpos.y + radius; + } + } + + public bool IsInRangeMaxRect(Vector3 local, Rect chartRect, float triangleLen) + { + var centerPos = new Vector3(chartRect.x, chartRect.y) + location.GetPosition(chartRect.width, chartRect.height); + + if (orient == Orient.Vertical) + { + var radius = triangleLen / 2; + var pos1 = centerPos + Vector3.down * itemHeight / 2; + var cpos = new Vector3(pos1.x + itemWidth / 2 + radius, pos1.y + runtimeRangeMaxHeight + radius); + + return local.x >= cpos.x - radius && + local.x <= cpos.x + radius && + local.y >= cpos.y - radius && + local.y <= cpos.y + radius; + } + else + { + var radius = triangleLen / 2; + var pos1 = centerPos + Vector3.left * itemHeight / 2; + var cpos = new Vector3(pos1.x + runtimeRangeMaxHeight + radius, pos1.y + itemWidth / 2 + radius); + + return local.x >= cpos.x - radius && + local.x <= cpos.x + radius && + local.y >= cpos.y - radius && + local.y <= cpos.y + radius; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs.meta b/Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs.meta new file mode 100644 index 0000000..63b8bc2 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a684cb32850c4df6aa39ed4fa5efb3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs new file mode 100644 index 0000000..afba42a --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class VisualMapContext : MainComponentContext + { + /// <summary> + /// 鼠标悬停选中的index + /// </summary> + public int pointerIndex { get; set; } + public double pointerValue { get; set; } + public bool minDrag { get; internal set; } + public bool maxDrag { get; internal set; } + public double min { get; set; } + public double max { get; set; } + + internal List<Color32> inRangeColors = new List<Color32>(); + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs.meta b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs.meta new file mode 100644 index 0000000..b2eea4c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b81ad95b4747442daa716953c7c02638 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs new file mode 100644 index 0000000..32089ea --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs @@ -0,0 +1,373 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class VisualMapHandler : MainComponentHandler<VisualMap> + { + public override void OnBeginDrag(PointerEventData eventData) + { + OnDragVisualMapStart(component); + } + public override void OnDrag(PointerEventData eventData) + { + OnDragVisualMap(component); + } + + public override void OnEndDrag(PointerEventData eventData) + { + OnDragVisualMapEnd(component); + } + + public override void Update() + { + CheckVisualMap(component); + } + + public override void DrawBase(VertexHelper vh) + { + var visualMap = component; + if (!visualMap.show || !visualMap.showUI) return; + switch (visualMap.type) + { + case VisualMap.Type.Continuous: + DrawContinuousVisualMap(vh, visualMap); + break; + case VisualMap.Type.Piecewise: + //DrawPiecewiseVisualMap(vh, visualMap); + break; + } + } + + private void CheckVisualMap(VisualMap visualMap) + { + if (visualMap == null || !visualMap.show) + return; + + if (chart.canvas == null) + return; + + Vector2 local; + if (!chart.ScreenPointToChartPoint(Input.mousePosition, out local)) + { + if (visualMap.context.pointerIndex >= 0) + { + visualMap.context.pointerIndex = -1; + chart.RefreshChart(); + } + return; + } + + if (local.x < chart.chartX || + local.x > chart.chartX + chart.chartWidth || + local.y < chart.chartY || + local.y > chart.chartY + chart.chartHeight || + !visualMap.IsInRangeRect(local, chart.chartRect)) + { + if (visualMap.context.pointerIndex >= 0) + { + visualMap.context.pointerIndex = -1; + chart.RefreshChart(); + } + return; + } + + var pos1 = Vector3.zero; + var pos2 = Vector3.zero; + var halfHig = visualMap.itemHeight / 2; + var centerPos = chart.chartPosition + visualMap.location.GetPosition(chart.chartWidth, chart.chartHeight); + var selectedIndex = -1; + double value = 0; + + switch (visualMap.orient) + { + case Orient.Horizonal: + pos1 = centerPos + Vector3.left * halfHig; + pos2 = centerPos + Vector3.right * halfHig; + value = visualMap.min + (local.x - pos1.x) / (pos2.x - pos1.x) * (visualMap.max - visualMap.min); + selectedIndex = visualMap.GetIndex(value); + break; + + case Orient.Vertical: + pos1 = centerPos + Vector3.down * halfHig; + pos2 = centerPos + Vector3.up * halfHig; + value = visualMap.min + (local.y - pos1.y) / (pos2.y - pos1.y) * (visualMap.max - visualMap.min); + selectedIndex = visualMap.GetIndex(value); + break; + } + + visualMap.context.pointerValue = value; + visualMap.context.pointerIndex = selectedIndex; + chart.RefreshChart(); + } + + private void DrawContinuousVisualMap(VertexHelper vh, VisualMap visualMap) + { + var centerPos = chart.chartPosition + visualMap.location.GetPosition(chart.chartWidth, chart.chartHeight); + var pos1 = Vector3.zero; + var pos2 = Vector3.zero; + var dir = Vector3.zero; + var halfWid = visualMap.itemWidth / 2; + var halfHig = visualMap.itemHeight / 2; + var xRadius = 0f; + var yRadius = 0f; + var splitNum = visualMap.inRange.Count; + var splitWid = visualMap.itemHeight / (splitNum - 1); + var isVertical = false; + var colors = visualMap.inRange; + var triangeLen = chart.theme.visualMap.triangeLen; + + switch (visualMap.orient) + { + case Orient.Horizonal: + pos1 = centerPos + Vector3.left * halfHig; + pos2 = centerPos + Vector3.right * halfHig; + dir = Vector3.right; + xRadius = splitWid / 2; + yRadius = halfWid; + isVertical = false; + if (visualMap.calculable) + { + var p0 = pos1 + Vector3.right * visualMap.runtimeRangeMinHeight; + var p1 = p0 + Vector3.up * halfWid; + var p2 = p0 + Vector3.up * (halfWid + triangeLen); + var p3 = p2 + Vector3.left * triangeLen; + var color = visualMap.GetColor(visualMap.rangeMin); + UGL.DrawTriangle(vh, p1, p2, p3, color); + p0 = pos1 + Vector3.right * visualMap.runtimeRangeMaxHeight; + p1 = p0 + Vector3.up * halfWid; + p2 = p0 + Vector3.up * (halfWid + triangeLen); + p3 = p2 + Vector3.right * triangeLen; + color = visualMap.GetColor(visualMap.rangeMax); + UGL.DrawTriangle(vh, p1, p2, p3, color); + } + break; + + case Orient.Vertical: + pos1 = centerPos + Vector3.down * halfHig; + pos2 = centerPos + Vector3.up * halfHig; + dir = Vector3.up; + xRadius = halfWid; + yRadius = splitWid / 2; + isVertical = true; + if (visualMap.calculable) + { + var p0 = pos1 + Vector3.up * visualMap.runtimeRangeMinHeight; + var p1 = p0 + Vector3.right * halfWid; + var p2 = p0 + Vector3.right * (halfWid + triangeLen); + var p3 = p2 + Vector3.down * triangeLen; + var color = visualMap.GetColor(visualMap.rangeMin); + UGL.DrawTriangle(vh, p1, p2, p3, color); + p0 = pos1 + Vector3.up * visualMap.runtimeRangeMaxHeight; + p1 = p0 + Vector3.right * halfWid; + p2 = p0 + Vector3.right * (halfWid + triangeLen); + p3 = p2 + Vector3.up * triangeLen; + color = visualMap.GetColor(visualMap.rangeMax); + UGL.DrawTriangle(vh, p1, p2, p3, color); + } + break; + } + if (visualMap.calculable && + (visualMap.rangeMin > visualMap.min || visualMap.rangeMax < visualMap.max)) + { + var rangeMin = visualMap.rangeMin; + var rangeMax = visualMap.rangeMax; + var diff = (visualMap.max - visualMap.min) / (splitNum - 1); + for (int i = 1; i < splitNum; i++) + { + var splitMin = visualMap.min + (i - 1) * diff; + var splitMax = splitMin + diff; + if (rangeMin > splitMax || rangeMax < splitMin) + { + continue; + } + else if (rangeMin <= splitMin && rangeMax >= splitMax) + { + var splitPos = pos1 + dir * (i - 1 + 0.5f) * splitWid; + var startColor = colors[i - 1].color; + var toColor = visualMap.IsPiecewise() ? startColor : colors[i].color; + UGL.DrawRectangle(vh, splitPos, xRadius, yRadius, startColor, toColor, isVertical); + } + else if (rangeMin > splitMin && rangeMax >= splitMax) + { + var p0 = pos1 + dir * visualMap.runtimeRangeMinHeight; + var splitMaxPos = pos1 + dir * i * splitWid; + var splitPos = p0 + (splitMaxPos - p0) / 2; + var startColor = visualMap.GetColor(visualMap.rangeMin); + var toColor = visualMap.IsPiecewise() ? startColor : colors[i].color; + var yRadius1 = Vector3.Distance(p0, splitMaxPos) / 2; + + if (visualMap.orient == Orient.Vertical) + UGL.DrawRectangle(vh, splitPos, xRadius, yRadius1, startColor, toColor, isVertical); + else + UGL.DrawRectangle(vh, splitPos, yRadius1, yRadius, startColor, toColor, isVertical); + } + else if (rangeMax < splitMax && rangeMin <= splitMin) + { + var p0 = pos1 + dir * visualMap.runtimeRangeMaxHeight; + var splitMinPos = pos1 + dir * (i - 1) * splitWid; + var splitPos = splitMinPos + (p0 - splitMinPos) / 2; + var startColor = colors[i - 1].color; + var toColor = visualMap.IsPiecewise() ? startColor : visualMap.GetColor(visualMap.rangeMax); + var yRadius1 = Vector3.Distance(p0, splitMinPos) / 2; + + if (visualMap.orient == Orient.Vertical) + UGL.DrawRectangle(vh, splitPos, xRadius, yRadius1, startColor, toColor, isVertical); + else + UGL.DrawRectangle(vh, splitPos, yRadius1, yRadius, startColor, toColor, isVertical); + } + else + { + var p0 = pos1 + dir * visualMap.runtimeRangeMinHeight; + var p1 = pos1 + dir * visualMap.runtimeRangeMaxHeight; + var splitPos = (p0 + p1) / 2; + var startColor = visualMap.GetColor(visualMap.rangeMin); + var toColor = visualMap.GetColor(visualMap.rangeMax); + var yRadius1 = Vector3.Distance(p0, p1) / 2; + + if (visualMap.orient == Orient.Vertical) + UGL.DrawRectangle(vh, splitPos, xRadius, yRadius1, startColor, toColor, isVertical); + else + UGL.DrawRectangle(vh, splitPos, yRadius1, yRadius, startColor, toColor, isVertical); + } + } + } + else + { + for (int i = 1; i < splitNum; i++) + { + var splitPos = pos1 + dir * (i - 1 + 0.5f) * splitWid; + var startColor = colors[i - 1].color; + var toColor = visualMap.IsPiecewise() ? startColor : colors[i].color; + UGL.DrawRectangle(vh, splitPos, xRadius, yRadius, startColor, toColor, isVertical); + } + } + + if (visualMap.rangeMin > visualMap.min) + { + var p0 = pos1 + dir * visualMap.runtimeRangeMinHeight; + UGL.DrawRectangle(vh, pos1, p0, visualMap.itemWidth / 2, chart.theme.visualMap.backgroundColor); + } + if (visualMap.rangeMax < visualMap.max) + { + var p1 = pos1 + dir * visualMap.runtimeRangeMaxHeight; + UGL.DrawRectangle(vh, p1, pos2, visualMap.itemWidth / 2, chart.theme.visualMap.backgroundColor); + } + + if (visualMap.hoverLink) + { + if (visualMap.context.pointerIndex >= 0) + { + var p0 = pos1 + dir * visualMap.runtimeRangeMinHeight; + var p1 = pos1 + dir * visualMap.runtimeRangeMaxHeight; + var pointerPos = chart.pointerPos; + + if (visualMap.orient == Orient.Vertical) + { + var p2 = new Vector3(centerPos.x + halfWid, Mathf.Clamp(pointerPos.y + (triangeLen / 2), p0.y, p1.y)); + var p3 = new Vector3(centerPos.x + halfWid, Mathf.Clamp(pointerPos.y - (triangeLen / 2), p0.y, p1.y)); + var p4 = new Vector3(centerPos.x + halfWid + triangeLen / 2, pointerPos.y); + UGL.DrawTriangle(vh, p2, p3, p4, colors[visualMap.context.pointerIndex].color); + } + else + { + var p2 = new Vector3(Mathf.Clamp(pointerPos.x + (triangeLen / 2), p0.x, p1.x), centerPos.y + halfWid); + var p3 = new Vector3(Mathf.Clamp(pointerPos.x - (triangeLen / 2), p0.x, p1.x), centerPos.y + halfWid); + var p4 = new Vector3(pointerPos.x, centerPos.y + halfWid + triangeLen / 2); + UGL.DrawTriangle(vh, p2, p3, p4, colors[visualMap.context.pointerIndex].color); + } + } + } + } + + private void DrawPiecewiseVisualMap(VertexHelper vh, VisualMap visualMap) + { + var centerPos = chart.chartPosition + visualMap.location.GetPosition(chart.chartWidth, chart.chartHeight); + var pos1 = Vector3.zero; + var pos2 = Vector3.zero; + var dir = Vector3.zero; + var halfWid = visualMap.itemWidth / 2; + var halfHig = visualMap.itemHeight / 2; + + switch (visualMap.orient) + { + case Orient.Horizonal: + for (int i = 0; i < visualMap.inRange.Count; i++) + { + var piece = visualMap.inRange[i]; + } + break; + + case Orient.Vertical: + var each = visualMap.itemHeight + visualMap.itemGap; + for (int i = 0; i < visualMap.inRange.Count; i++) + { + var piece = visualMap.inRange[i]; + var pos = new Vector3(centerPos.x, centerPos.y - each * i); + UGL.DrawRectangle(vh, pos, halfWid, halfHig, piece.color); + } + break; + } + } + + private void OnDragVisualMapStart(VisualMap visualMap) + { + if (!visualMap.show || !visualMap.showUI || !visualMap.calculable) + return; + + var inMinRect = visualMap.IsInRangeMinRect(chart.pointerPos, chart.chartRect, chart.theme.visualMap.triangeLen); + var inMaxRect = visualMap.IsInRangeMaxRect(chart.pointerPos, chart.chartRect, chart.theme.visualMap.triangeLen); + + if (inMinRect || inMaxRect) + { + if (inMinRect) + { + visualMap.context.minDrag = true; + } + else + { + visualMap.context.maxDrag = true; + } + } + } + + private void OnDragVisualMap(VisualMap visualMap) + { + if (!visualMap.show || !visualMap.showUI || !visualMap.calculable) + return; + + if (!visualMap.context.minDrag && !visualMap.context.maxDrag) + return; + + var value = visualMap.GetValue(chart.pointerPos, chart.chartRect); + if (visualMap.context.minDrag) + { + visualMap.rangeMin = value; + } + else + { + visualMap.rangeMax = value; + } + chart.RefreshChart(); + } + + private void OnDragVisualMapEnd(VisualMap visualMap) + { + if (!visualMap.show || !visualMap.showUI || !visualMap.calculable) + return; + + if (visualMap.context.minDrag || visualMap.context.maxDrag) + { + chart.RefreshChart(); + visualMap.context.minDrag = false; + visualMap.context.maxDrag = false; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs.meta b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs.meta new file mode 100644 index 0000000..022611e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8db5a57b5961a493db94ac8974238d18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs new file mode 100644 index 0000000..406b2bf --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs @@ -0,0 +1,189 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class VisualMapHelper + { + public static void AutoSetLineMinMax(VisualMap visualMap, Serie serie, bool isY, Axis axis, Axis relativedAxis) + { + if (!IsNeedGradient(visualMap) || !visualMap.autoMinMax) + return; + + double min = 0; + double max = 0; + var xAxis = isY ? relativedAxis : axis; + var yAxis = isY ? axis : relativedAxis; + if (visualMap.dimension == 0) + { + min = xAxis.IsCategory() ? 0 : xAxis.context.minValue; + max = xAxis.IsCategory() ? serie.dataCount - 1 : xAxis.context.maxValue; + SetMinMax(visualMap, min, max); + } + else + { + min = yAxis.IsCategory() ? 0 : yAxis.context.minValue; + max = yAxis.IsCategory() ? serie.dataCount - 1 : yAxis.context.maxValue; + SetMinMax(visualMap, min, max); + } + } + + public static void SetMinMax(VisualMap visualMap, double min, double max) + { + if ((visualMap.min != min || visualMap.max != max)) + { + if (max >= min) + { + visualMap.min = min; + visualMap.max = max; + } + else + { + throw new Exception("SetMinMax:max < min:" + min + "," + max); + } + } + } + + public static void GetLineGradientColor(VisualMap visualMap, float xValue, float yValue, + out Color32 startColor, out Color32 toColor) + { + startColor = ChartConst.clearColor32; + toColor = ChartConst.clearColor32; + if (visualMap.dimension == 0) + { + startColor = visualMap.IsPiecewise() ? visualMap.GetColor(xValue) : visualMap.GetColor(xValue - 1); + toColor = visualMap.IsPiecewise() ? startColor : visualMap.GetColor(xValue); + } + else + { + startColor = visualMap.IsPiecewise() ? visualMap.GetColor(yValue) : visualMap.GetColor(yValue - 1); + toColor = visualMap.IsPiecewise() ? startColor : visualMap.GetColor(yValue); + } + } + + public static Color32 GetLineGradientColor(VisualMap visualMap, Vector3 pos, GridCoord grid, Axis axis, + Axis relativedAxis, Color32 defaultColor) + { + double value = 0; + double min = 0; + double max = 0; + + if (visualMap.dimension == 0) + { + min = axis.context.minValue; + max = axis.context.maxValue; + if (axis.IsCategory() && axis.boundaryGap) + { + float startX = grid.context.x + axis.context.scaleWidth / 2; + value = (min + (pos.x - startX) / (grid.context.width - axis.context.scaleWidth) * (max - min)); + if (visualMap.IsPiecewise()) + value = (int) value; + } + else + { + value = min + (pos.x - grid.context.x) / grid.context.width * (max - min); + } + } + else + { + min = relativedAxis.context.minValue; + max = relativedAxis.context.maxValue; + if (relativedAxis.IsCategory() && relativedAxis.boundaryGap) + { + float startY = grid.context.y + relativedAxis.context.scaleWidth / 2; + value = (min + (pos.y - startY) / (grid.context.height - relativedAxis.context.scaleWidth) * (max - min)); + if (visualMap.IsPiecewise()) + value = (int) value; + } + else + { + value = min + (pos.y - grid.context.y) / grid.context.height * (max - min); + } + } + + var color = visualMap.GetColor(value); + if (ChartHelper.IsClearColor(color)) + { + return defaultColor; + } + else + { + if (color.a != 0) + color.a = defaultColor.a; + + return color; + } + } + + public static Color32 GetItemStyleGradientColor(ItemStyle itemStyle, Vector3 pos, BaseChart chart, + Axis axis, Color32 defaultColor) + { + var min = axis.context.minValue; + var max = axis.context.maxValue; + var grid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + var value = min + (pos.x - grid.context.x) / grid.context.width * (max - min); + var rate = (value - min) / (max - min); + var color = itemStyle.GetGradientColor((float) rate, defaultColor); + + if (ChartHelper.IsClearColor(color)) + return defaultColor; + else + return color; + } + + public static Color32 GetLineStyleGradientColor(LineStyle lineStyle, Vector3 pos, GridCoord grid, + Axis axis, Color32 defaultColor) + { + var min = axis.context.minValue; + var max = axis.context.maxValue; + var value = min + (pos.x - grid.context.x) / grid.context.width * (max - min); + var rate = (value - min) / (max - min); + var color = lineStyle.GetGradientColor((float) rate, defaultColor); + + if (ChartHelper.IsClearColor(color)) + return defaultColor; + else + return color; + } + + public static bool IsNeedGradient(VisualMap visualMap) + { + if (visualMap == null) + return false; + if (!visualMap.show || (!visualMap.workOnLine && !visualMap.workOnArea)) + return false; + if (visualMap.inRange.Count <= 0) + return false; + return true; + } + public static bool IsNeedLineGradient(VisualMap visualMap) + { + if (visualMap == null) + return false; + if (!visualMap.show || !visualMap.workOnLine) + return false; + if (visualMap.inRange.Count <= 0) + return false; + return true; + } + public static bool IsNeedAreaGradient(VisualMap visualMap) + { + if (visualMap == null) + return false; + if (!visualMap.show || !visualMap.workOnArea) + return false; + if (visualMap.inRange.Count <= 0) + return false; + return true; + } + + public static int GetDimension(VisualMap visualMap, int defaultDimension) + { + if (visualMap == null || !visualMap.show) + return defaultDimension; + + return visualMap != null && visualMap.dimension >= 0 ? + visualMap.dimension : defaultDimension; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs.meta b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs.meta new file mode 100644 index 0000000..7b98866 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/VisualMap/VisualMapHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eddf18450477b4502804d13fa724e45d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord.meta b/Assets/XCharts/Runtime/Coord.meta new file mode 100644 index 0000000..fe2fbe1 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 19968e8512641421f82ca8213ca6a907 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Calendar.meta b/Assets/XCharts/Runtime/Coord/Calendar.meta new file mode 100644 index 0000000..e62b633 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Calendar.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: da5534d24de514e54911c0efb7b7b2ba +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs new file mode 100644 index 0000000..28318ab --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs @@ -0,0 +1,19 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + [ComponentHandler(typeof(CalendarCoordHandler), true)] + public class CalendarCoord : CoordSystem, IUpdateRuntimeData, ISerieContainer + { + public bool IsPointerEnter() + { + return false; + } + + public void UpdateRuntimeData(BaseChart chart) + { + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs.meta b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs.meta new file mode 100644 index 0000000..457c73a --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eccc042d880064df8a2a99be68969918 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs new file mode 100644 index 0000000..69eb333 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs @@ -0,0 +1,9 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class CalendarCoordHandler : MainComponentHandler<CalendarCoord> + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs.meta b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs.meta new file mode 100644 index 0000000..6825dad --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Calendar/CalendarCoordHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57a6c8647580846888712d387da72d1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid.meta b/Assets/XCharts/Runtime/Coord/Grid.meta new file mode 100644 index 0000000..a6c4710 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7d09a247055fa44dcab4eb4c61401a9b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs b/Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs new file mode 100644 index 0000000..b460025 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// Grid component. + /// ||Drawing grid in rectangular coordinate. Line chart, bar chart, and scatter chart can be drawn in grid. + /// ||网格组件。 + /// 直角坐标系内绘图网格。可以在网格上绘制折线图,柱状图,散点图。 + /// </summary> + [Serializable] + [ComponentHandler(typeof(GridCoordHandler), true)] + public class GridCoord : CoordSystem, IUpdateRuntimeData, ISerieContainer + { + [SerializeField] private bool m_Show = true; + [SerializeField][Since("v3.8.0")] private int m_LayoutIndex = -1; + [SerializeField] private float m_Left = 0.11f; + [SerializeField] private float m_Right = 0.08f; + [SerializeField] private float m_Top = 0.22f; + [SerializeField] private float m_Bottom = 0.14f; + [SerializeField] private Color32 m_BackgroundColor; + [SerializeField] private bool m_ShowBorder = false; + [SerializeField] private float m_BorderWidth = 0f; + [SerializeField] private Color32 m_BorderColor; + + public GridCoordContext context = new GridCoordContext(); + + /// <summary> + /// Whether to show the grid in rectangular coordinate. + /// ||是否显示直角坐标系网格。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// The index of the grid layout component to which the grid belongs. + /// The default is -1, which means that it does not belong to any grid layout component. + /// When this value is set, the left, right, top, and bottom properties will be invalid. + /// ||网格所属的网格布局组件的索引。默认为-1,表示不属于任何网格布局组件。当设置了该值时,left、right、top、bottom属性将失效。 + /// </summary> + public int layoutIndex + { + get { return m_LayoutIndex; } + set { if (PropertyUtil.SetStruct(ref m_LayoutIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// Distance between grid component and the left side of the container. + /// ||grid 组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the right side of the container. + /// ||grid 组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the top side of the container. + /// ||grid 组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the bottom side of the container. + /// ||grid 组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetAllDirty(); } + } + /// <summary> + /// Background color of grid, which is transparent by default. + /// ||网格背景色,默认透明。 + /// </summary> + public Color32 backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_BackgroundColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show the grid border. + /// ||是否显示网格边框。 + /// </summary> + public bool showBorder + { + get { return m_ShowBorder; } + set { if (PropertyUtil.SetStruct(ref m_ShowBorder, value)) SetVerticesDirty(); } + } + /// <summary> + /// Border width of grid. + /// ||网格边框宽。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// The color of grid border. + /// ||网格边框颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetVerticesDirty(); } + } + + public void UpdateRuntimeData(BaseChart chart) + { + var chartX = chart.chartX; + var chartY = chart.chartY; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + if (layoutIndex >= 0) + { + var layout = chart.GetChartComponent<GridLayout>(layoutIndex); + if (layout != null) + { + layout.UpdateRuntimeData(chart); + layout.UpdateGridContext(index, ref chartX, ref chartY, ref chartWidth, ref chartHeight); + } + } + var actualLeft = left <= 1 ? left * chartWidth : left; + var actualBottom = bottom <= 1 ? bottom * chartHeight : bottom; + var actualTop = top <= 1 ? top * chartHeight : top; + var actualRight = right <= 1 ? right * chartWidth : right; + context.x = chartX + actualLeft; + context.y = chartY + actualBottom; + context.width = chartWidth - actualLeft - actualRight; + context.height = chartHeight - actualTop - actualBottom; + context.position = new Vector3(context.x, context.y); + context.center = new Vector3(context.x + context.width / 2, context.y + context.height / 2); + } + + /// <summary> + /// Whether the pointer is in the grid. + /// ||指针是否在网格内。 + /// </summary> + /// <returns></returns> + public bool IsPointerEnter() + { + return context.isPointerEnter; + } + + /// <summary> + /// Whether the given position is in the grid. + /// ||给定的位置是否在网格内。 + /// </summary> + /// <param name="pos"></param> + /// <returns></returns> + public bool Contains(Vector3 pos) + { + return Contains(pos.x, pos.y); + } + + /// <summary> + /// Whether the given position is in the grid. + /// ||给定的位置是否在网格内。 + /// </summary> + /// <param name="pos"></param> + /// <param name="isYAxis"></param> + /// <returns></returns> + [Since("v3.7.0")] + public bool Contains(Vector3 pos, bool isYAxis) + { + return isYAxis ? ContainsY(pos.y) : ContainsX(pos.x); + } + + /// <summary> + /// Whether the given position is in the grid. + /// ||给定的位置是否在网格内。 + /// </summary> + /// <param name="x"></param> + /// <param name="y"></param> + /// <returns></returns> + public bool Contains(float x, float y) + { + return ContainsX(x) && ContainsY(y); + } + + /// <summary> + /// Whether the given x is in the grid. + /// ||给定的x是否在网格内。 + /// </summary> + /// <param name="x"></param> + /// <returns></returns> + [Since("v3.7.0")] + public bool ContainsX(float x) + { + return x >= context.x - 0.01f && x <= context.x + context.width + 0.01f; + } + + /// <summary> + /// Whether the given y is in the grid. + /// ||给定的y是否在网格内。 + /// </summary> + /// <param name="y"></param> + /// <returns></returns> + [Since("v3.7.0")] + public bool ContainsY(float y) + { + return y >= context.y - 0.01f && y <= context.y + context.height + 0.01f; + } + + /// <summary> + /// Clamp the position of pos to the grid. + /// ||将位置限制在网格内。 + /// </summary> + /// <param name="pos"></param> + [Since("v3.7.0")] + public void Clamp(ref Vector3 pos) + { + ClampX(ref pos); + ClampY(ref pos); + } + + /// <summary> + /// Clamp the x position of pos to the grid. + /// ||将位置的X限制在网格内。 + /// </summary> + /// <param name="pos"></param> + [Since("v3.7.0")] + public void ClampX(ref Vector3 pos) + { + if (pos.x < context.x) pos.x = context.x; + else if (pos.x > context.x + context.width) pos.x = context.x + context.width; + } + + /// <summary> + /// Clamp the y position of pos to the grid. + /// ||将位置的Y限制在网格内。 + /// </summary> + /// <param name="pos"></param> + [Since("v3.7.0")] + public void ClampY(ref Vector3 pos) + { + if (pos.y < context.y) pos.y = context.y; + else if (pos.y > context.y + context.height) pos.y = context.y + context.height; + } + + /// <summary> + /// Determines whether a given line segment will not intersect the Grid boundary at all. + /// ||判断给定的线段是否与Grid边界是否完全不会相交。 + /// </summary> + /// <param name="sp"></param> + /// <param name="ep"></param> + /// <returns></returns> + [Since("v3.10.0")] + public bool NotAnyIntersect(Vector3 sp, Vector3 ep) + { + if (sp.x < context.x && ep.x < context.x) + return true; + if (sp.x > context.x + context.width && ep.x > context.x + context.width) + return true; + if (sp.y < context.y && ep.y < context.y) + return true; + if (sp.y > context.y + context.height && ep.y > context.y + context.height) + return true; + return false; + } + + /// <summary> + /// 给定的线段和Grid边界的交点 + /// </summary> + /// <param name="sp"></param> + /// <param name="ep"></param> + /// <returns></returns> + public bool BoundaryPoint(Vector3 sp, Vector3 ep, ref Vector3 point) + { + if (Contains(sp) && Contains(ep)) + return false; + if (sp.x < context.x && ep.x < context.x) + return false; + if (sp.x > context.x + context.width && ep.x > context.x + context.width) + return false; + if (sp.y < context.y && ep.y < context.y) + return false; + if (sp.y > context.y + context.height && ep.y > context.y + context.height) + return false; + var lb = new Vector3(context.x, context.y); + var lt = new Vector3(context.x, context.y + context.height); + var rt = new Vector3(context.x + context.width, context.y + context.height); + var rb = new Vector3(context.x + context.width, context.y); + if (UGLHelper.GetIntersection(sp, ep, rb, rt, ref point)) + return true; + if (UGLHelper.GetIntersection(sp, ep, lt, rt, ref point)) + return true; + if (UGLHelper.GetIntersection(sp, ep, lb, rb, ref point)) + return true; + if (UGLHelper.GetIntersection(sp, ep, lb, lt, ref point)) + return true; + return false; + } + + /// <summary> + /// 给定的线段和Grid边界的交点 + /// </summary> + /// <param name="sp"></param> + /// <param name="ep"></param> + /// <returns></returns> + public bool BoundaryPoint(Vector3 sp, Vector3 ep, ref List<Vector3> point) + { + if (Contains(sp) && Contains(ep)) + return false; + var lb = new Vector3(context.x, context.y); + var lt = new Vector3(context.x, context.y + context.height); + var rt = new Vector3(context.x + context.width, context.y + context.height); + var rb = new Vector3(context.x + context.width, context.y); + var flag = false; + if (UGLHelper.GetIntersection(sp, ep, lb, lt, ref point)) + flag = true; + if (UGLHelper.GetIntersection(sp, ep, lt, rt, ref point)) + flag = true; + if (UGLHelper.GetIntersection(sp, ep, lb, rb, ref point)) + flag = true; + if (UGLHelper.GetIntersection(sp, ep, rb, rt, ref point)) + flag = true; + return flag; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs.meta b/Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs.meta new file mode 100644 index 0000000..9c3e968 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f99c74cc7f2c44bfcae9f5c40e6b7c46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs b/Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs new file mode 100644 index 0000000..5f794d7 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class GridCoordContext : MainComponentContext + { + public float x; + public float y; + public float width; + public float height; + public Vector3 position; + public Vector3 center; + public bool isPointerEnter; + public List<ChartLabel> endLabelList = new List<ChartLabel>(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs.meta b/Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs.meta new file mode 100644 index 0000000..13f0b39 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridCoordContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a7c139761fe64d93be4c65619a0fd38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs b/Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs new file mode 100644 index 0000000..b38b769 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs @@ -0,0 +1,89 @@ +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class GridCoordHandler : MainComponentHandler<GridCoord> + { + public override void InitComponent() + { + var grid = component; + grid.painter = chart.painter; + grid.refreshComponent = delegate() + { + grid.UpdateRuntimeData(chart); + chart.OnCoordinateChanged(); + }; + grid.refreshComponent(); + } + + public override void CheckComponent(StringBuilder sb) + { + var grid = component; + if (grid.left >= chart.chartWidth) + sb.Append("warning:grid->left > chartWidth\n"); + if (grid.right >= chart.chartWidth) + sb.Append("warning:grid->right > chartWidth\n"); + if (grid.top >= chart.chartHeight) + sb.Append("warning:grid->top > chartHeight\n"); + if (grid.bottom >= chart.chartHeight) + sb.Append("warning:grid->bottom > chartHeight\n"); + if (grid.left + grid.right >= chart.chartWidth) + sb.Append("warning:grid.left + grid.right > chartWidth\n"); + if (grid.top + grid.bottom >= chart.chartHeight) + sb.Append("warning:grid.top + grid.bottom > chartHeight\n"); + } + + public override void Update() + { + if (chart.isPointerInChart) + { + component.context.isPointerEnter = component.Contains(chart.pointerPos); + } + else + { + component.context.isPointerEnter = false; + } + } + + public override void DrawBase(VertexHelper vh) + { + if (!SeriesHelper.IsAnyClipSerie(chart.series)) + { + DrawCoord(vh, component); + } + } + public override void DrawUpper(VertexHelper vh) + { + if (SeriesHelper.IsAnyClipSerie(chart.series)) + { + DrawCoord(vh, component); + } + } + + private void DrawCoord(VertexHelper vh, GridCoord grid) + { + if (!grid.show) return; + if (!ChartHelper.IsClearColor(grid.backgroundColor)) + { + var p1 = new Vector2(grid.context.x, grid.context.y); + var p2 = new Vector2(grid.context.x, grid.context.y + grid.context.height); + var p3 = new Vector2(grid.context.x + grid.context.width, grid.context.y + grid.context.height); + var p4 = new Vector2(grid.context.x + grid.context.width, grid.context.y); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, grid.backgroundColor); + } + if (grid.showBorder) + { + var borderWidth = grid.borderWidth == 0 ? chart.theme.axis.lineWidth * 2 : grid.borderWidth; + var borderColor = ChartHelper.IsClearColor(grid.borderColor) ? + chart.theme.axis.lineColor : + grid.borderColor; + UGL.DrawBorder(vh, grid.context.center, grid.context.width - borderWidth, + grid.context.height - borderWidth, borderWidth, borderColor); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs.meta b/Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs.meta new file mode 100644 index 0000000..ed91ce7 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridCoordHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e22a9b603e57459f97040b285f3936a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs new file mode 100644 index 0000000..bc107ba --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs @@ -0,0 +1,12 @@ +namespace XCharts.Runtime +{ + public class GridLayoutContext : MainComponentContext + { + public float x; + public float y; + public float width; + public float height; + public float eachWidth; + public float eachHeight; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs.meta b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs.meta new file mode 100644 index 0000000..20a5868 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7265c042ebd33458eb12c112d46d9a60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs new file mode 100644 index 0000000..3df8623 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs @@ -0,0 +1,7 @@ +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class GridLayoutHandler : MainComponentHandler<GridLayout> + { + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs.meta b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs.meta new file mode 100644 index 0000000..87a7842 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/GridLayoutHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b1c1f0fa475b484286b0b2c688dc3c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs b/Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs new file mode 100644 index 0000000..a3f20b0 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs @@ -0,0 +1,148 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Grid layout component. Used to manage the layout of multiple `GridCoord`, and the number of rows and columns of the grid can be controlled by `row` and `column`. + /// ||网格布局组件。用于管理多个`GridCoord`的布局,可以通过`row`和`column`来控制网格的行列数。 + /// </summary> + [Since("v3.8.0")] + [Serializable] + [ComponentHandler(typeof(GridLayoutHandler), true)] + public class GridLayout : MainComponent, IUpdateRuntimeData + { + [SerializeField] private bool m_Show = true; + [SerializeField] private float m_Left = 0.1f; + [SerializeField] private float m_Right = 0.08f; + [SerializeField] private float m_Top = 0.22f; + [SerializeField] private float m_Bottom = 0.12f; + [SerializeField] private int m_Row = 2; + [SerializeField] private int m_Column = 2; + [SerializeField] private Vector2 m_Spacing = Vector2.zero; + [SerializeField] protected bool m_Inverse = false; + + public GridLayoutContext context = new GridLayoutContext(); + + /// <summary> + /// Whether to show the grid in rectangular coordinate. + /// ||是否显示直角坐标系网格。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// Distance between grid component and the left side of the container. + /// ||grid 组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the right side of the container. + /// ||grid 组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the top side of the container. + /// ||grid 组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the bottom side of the container. + /// ||grid 组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetAllDirty(); } + } + /// <summary> + /// the row count of grid layout. + /// ||网格布局的行数。 + /// </summary> + public int row + { + get { return m_Row; } + set { if (PropertyUtil.SetStruct(ref m_Row, value)) SetAllDirty(); } + } + /// <summary> + /// the column count of grid layout. + /// ||网格布局的列数。 + /// </summary> + public int column + { + get { return m_Column; } + set { if (PropertyUtil.SetStruct(ref m_Column, value)) SetAllDirty(); } + } + /// <summary> + /// the spacing of grid layout. + /// ||网格布局的间距。 + /// </summary> + public Vector2 spacing + { + get { return m_Spacing; } + set { if (PropertyUtil.SetStruct(ref m_Spacing, value)) SetAllDirty(); } + } + /// <summary> + /// Whether to inverse the grid layout. + /// ||是否反转网格布局。 + /// </summary> + public bool inverse + { + get { return m_Inverse; } + set { if (PropertyUtil.SetStruct(ref m_Inverse, value)) SetAllDirty(); } + } + + public void UpdateRuntimeData(BaseChart chart) + { + var chartX = chart.chartX; + var chartY = chart.chartY; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + var actualLeft = left <= 1 ? left * chartWidth : left; + var actualBottom = bottom <= 1 ? bottom * chartHeight : bottom; + var actualTop = top <= 1 ? top * chartHeight : top; + var actualRight = right <= 1 ? right * chartWidth : right; + context.x = chartX + actualLeft; + context.y = chartY + actualBottom; + context.width = chartWidth - actualLeft - actualRight; + context.height = chartHeight - actualTop - actualBottom; + context.eachWidth = (context.width - spacing.x * (column - 1)) / column; + context.eachHeight = (context.height - spacing.y * (row - 1)) / row; + } + + internal void UpdateGridContext(int index, ref float x, ref float y, ref float width, ref float height) + { + var row = index / m_Column; + var column = index % m_Column; + + x = context.x + column * (context.eachWidth + spacing.x); + if(m_Inverse) + y = context.y + row * (context.eachHeight + spacing.y); + else + y = context.y + context.height - (row + 1) * context.eachHeight - row * spacing.y; + width = context.eachWidth; + height = context.eachHeight; + } + + internal void UpdateGridContext(int index, ref Vector3 position, ref float width, ref float height) + { + float x = 0, y = 0; + UpdateGridContext(index, ref x, ref y, ref width, ref height); + position = new Vector3(x, y); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs.meta b/Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs.meta new file mode 100644 index 0000000..84093da --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid/XGridLayout.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 691ae1f57760b4a948c94f3faa0ef6f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid3D.meta b/Assets/XCharts/Runtime/Coord/Grid3D.meta new file mode 100644 index 0000000..08f4746 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 14f9081aa22ba4bcb9cdbfbb95c7221e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs new file mode 100644 index 0000000..04f2431 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// View control component in 3D coordinate system. + /// ||3D视角控制组件。 + /// </summary> + [Since("v3.11.0")] + [Serializable] + public class ViewControl : ChildComponent + { + [SerializeField][Range(-90, 180)] private float m_Alpha = 90f; + [SerializeField][Range(-90, 90)] private float m_Beta = 55f; + + /// <summary> + /// The angle of the view in the x-z plane. + /// ||视角在x-z平面的角度。 + /// </summary> + public float alpha + { + get { return m_Alpha; } + set { if (PropertyUtil.SetStruct(ref m_Alpha, value)) SetVerticesDirty(); } + } + + /// <summary> + /// The angle of the view in the y-z plane. + /// ||视角在y-z平面的角度。 + /// </summary> + public float beta + { + get { return m_Beta; } + set { if (PropertyUtil.SetStruct(ref m_Beta, value)) SetVerticesDirty(); } + } + } + + /// <summary> + /// Grid component. + /// ||Drawing grid in rectangular coordinate. Line chart, bar chart, and scatter chart can be drawn in grid. + /// ||3D网格组件。 + /// 3D直角坐标系内绘图网格。可以在网格上绘制3D折线图,3D柱状图,3D散点图。 + /// </summary> + [Serializable] + [ComponentHandler(typeof(GridCoord3DHandler), true)] + public class GridCoord3D : CoordSystem, IUpdateRuntimeData, ISerieContainer + { + [SerializeField] private bool m_Show = true; + [SerializeField] private float m_Left = 0.15f; + [SerializeField] private float m_Right = 0.2f; + [SerializeField] private float m_Top = 0.3f; + [SerializeField] private float m_Bottom = 0.15f; + [SerializeField] private bool m_ShowBorder = false; + [SerializeField] private float m_BoxWidth = 0.55f; + [SerializeField] private float m_BoxHeight = 0.4f; + [SerializeField] private float m_BoxDepth = 0.2f; + [SerializeField] private bool m_XYExchanged = false; + [SerializeField] private ViewControl m_ViewControl = new ViewControl(); + + public GridCoord3DContext context = new GridCoord3DContext(); + + /// <summary> + /// Whether to show the grid in rectangular coordinate. + /// ||是否显示直角坐标系网格。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// Distance between grid component and the left side of the container. + /// ||grid 组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the right side of the container. + /// ||grid 组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the top side of the container. + /// ||grid 组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the bottom side of the container. + /// ||grid 组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetAllDirty(); } + } + /// <summary> + /// Whether to show the grid border. + /// ||是否显示网格边框。 + /// </summary> + public bool showBorder + { + get { return m_ShowBorder; } + set { if (PropertyUtil.SetStruct(ref m_ShowBorder, value)) SetVerticesDirty(); } + } + /// <summary> + /// The width of the box in the coordinate system. + /// ||坐标系的宽度。 + /// </summary> + public float boxWidth + { + get { return m_BoxWidth; } + set { if (PropertyUtil.SetStruct(ref m_BoxWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// The height of the box in the coordinate system. + /// ||坐标系的高度。 + /// </summary> + public float boxHeight + { + get { return m_BoxHeight; } + set { if (PropertyUtil.SetStruct(ref m_BoxHeight, value)) SetVerticesDirty(); } + } + /// <summary> + /// The depth of the box in the coordinate system. + /// ||坐标系的深度。 + /// </summary> + public float boxDepth + { + get { return m_BoxDepth; } + set { if (PropertyUtil.SetStruct(ref m_BoxDepth, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to exchange the x and y axes. + /// ||是否交换x和y轴。 + /// </summary> + public bool xyExchanged + { + get { return m_XYExchanged; } + set { if (PropertyUtil.SetStruct(ref m_XYExchanged, value)) SetVerticesDirty(); } + } + /// <summary> + /// View control component in 3D coordinate system. + /// ||3D视角控制组件。 + /// </summary> + public ViewControl viewControl + { + get { return m_ViewControl; } + //set { if (PropertyUtil.SetClass(ref m_ViewControl, value)) SetVerticesDirty(); } + } + + public void UpdateRuntimeData(BaseChart chart) + { + var chartX = chart.chartX; + var chartY = chart.chartY; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + var actualLeft = left <= 1 ? left * chartWidth : left; + var actualBottom = bottom <= 1 ? bottom * chartHeight : bottom; + var actualBoxWidth = m_BoxWidth <= 1 ? m_BoxWidth * chartWidth : m_BoxWidth; + var actualBoxHeight = m_BoxHeight <= 1 ? m_BoxHeight * chartHeight : m_BoxHeight; + var actualBoxDepth = m_BoxDepth <= 1 ? m_BoxDepth * chartWidth : m_BoxDepth; + context.x = chartX + actualLeft; + context.y = chartY + actualBottom; + context.pointA.x = context.x; + context.pointA.y = context.y; + + var angle = m_ViewControl.alpha * Mathf.Deg2Rad; + context.pointD.x = context.x + actualBoxWidth * Mathf.Sin(angle); + context.pointD.y = context.y - actualBoxWidth * Mathf.Cos(angle); + + angle = (90 - m_ViewControl.beta) * Mathf.Deg2Rad; + context.pointB.x = context.x + actualBoxDepth * Mathf.Cos(angle); + context.pointB.y = context.y + actualBoxDepth * Mathf.Sin(angle); + + context.pointC = context.pointB + (context.pointD - context.pointA); + + context.pointE.x = context.pointA.x; + context.pointE.y = context.pointA.y + actualBoxHeight; + + var diff = context.pointE - context.pointA; + context.pointF = context.pointB + diff; + context.pointG = context.pointC + diff; + context.pointH = context.pointD + diff; + + var minX = Mathf.Min(context.pointA.x, context.pointB.x, context.pointC.x, context.pointD.x, context.pointE.x, context.pointF.x, context.pointG.x, context.pointH.x); + var minY = Mathf.Min(context.pointA.y, context.pointB.y, context.pointC.y, context.pointD.y, context.pointE.y, context.pointF.y, context.pointG.y, context.pointH.y); + var maxX = Mathf.Max(context.pointA.x, context.pointB.x, context.pointC.x, context.pointD.x, context.pointE.x, context.pointF.x, context.pointG.x, context.pointH.x); + var maxY = Mathf.Max(context.pointA.y, context.pointB.y, context.pointC.y, context.pointD.y, context.pointE.y, context.pointF.y, context.pointG.y, context.pointH.y); + + context.maxRect.x = minX; + context.maxRect.y = minY; + context.maxRect.width = maxX - minX; + context.maxRect.height = maxY - minY; + } + + /// <summary> + /// The opening of the coordinate system faces to the left. + /// 坐标系开口朝向左边。 + /// </summary> + /// <returns></returns> + public bool IsLeft() + { + return context.pointB.x < context.pointA.x; + } + + /// <summary> + /// Whether the pointer is in the grid. + /// ||指针是否在网格内。 + /// </summary> + /// <returns></returns> + public bool IsPointerEnter() + { + return context.isPointerEnter; + } + + /// <summary> + /// Whether the given position is in the grid. + /// ||给定的位置是否在网格内。 + /// </summary> + /// <param name="pos"></param> + /// <returns></returns> + public bool Contains(Vector3 pos) + { + if (!context.maxRect.Contains(pos)) return false; + if (UGLHelper.IsPointInPolygon(pos, context.pointA, context.pointB, context.pointC, context.pointD)) return true; + if (UGLHelper.IsPointInPolygon(pos, context.pointB, context.pointF, context.pointG, context.pointC)) return true; + if (IsLeft()) + if (UGLHelper.IsPointInPolygon(pos, context.pointC, context.pointG, context.pointH, context.pointD)) return true; + else + if (UGLHelper.IsPointInPolygon(pos, context.pointA, context.pointE, context.pointF, context.pointB)) return true; + return false; + } + + /// <summary> + /// Clamp the position of pos to the grid. + /// ||将位置限制在网格内。 + /// </summary> + /// <param name="pos"></param> + public void Clamp(ref Vector3 pos) + { + //TODO: + } + + /// <summary> + /// Determines whether a given line segment will not intersect the Grid boundary at all. + /// ||判断给定的线段是否与Grid边界是否完全不会相交。 + /// </summary> + /// <param name="sp"></param> + /// <param name="ep"></param> + /// <returns></returns> + public bool NotAnyIntersect(Vector3 sp, Vector3 ep) + { + //TODO: + return false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs.meta b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs.meta new file mode 100644 index 0000000..9094e1c --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95f8c8c3492e54987af59175d94f8761 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs new file mode 100644 index 0000000..1b134f1 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class GridCoord3DContext : MainComponentContext + { + public float x; + public float y; + public Rect maxRect = new Rect(0, 0, 0, 0); + public bool isPointerEnter; + public List<ChartLabel> endLabelList = new List<ChartLabel>(); + //public Vector3 position = Vector3.zero; + public Vector3 pointA = Vector3.zero; + public Vector3 pointB = Vector3.zero; + public Vector3 pointC = Vector3.zero; + public Vector3 pointD = Vector3.zero; + public Vector3 pointE = Vector3.zero; + public Vector3 pointF = Vector3.zero; + public Vector3 pointG = Vector3.zero; + public Vector3 pointH = Vector3.zero; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs.meta b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs.meta new file mode 100644 index 0000000..7e53fdc --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4a0f1dda078b4877bfabe3c16815498 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs new file mode 100644 index 0000000..5168d36 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs @@ -0,0 +1,79 @@ +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class GridCoord3DHandler : MainComponentHandler<GridCoord3D> + { + public override void InitComponent() + { + var grid = component; + grid.painter = chart.painter; + grid.refreshComponent = delegate () + { + grid.UpdateRuntimeData(chart); + chart.OnCoordinateChanged(); + }; + grid.refreshComponent(); + } + + public override void CheckComponent(StringBuilder sb) + { + var grid = component; + if (grid.left >= chart.chartWidth) + sb.Append("warning:grid->left > chartWidth\n"); + if (grid.right >= chart.chartWidth) + sb.Append("warning:grid->right > chartWidth\n"); + if (grid.top >= chart.chartHeight) + sb.Append("warning:grid->top > chartHeight\n"); + if (grid.bottom >= chart.chartHeight) + sb.Append("warning:grid->bottom > chartHeight\n"); + if (grid.left + grid.right >= chart.chartWidth) + sb.Append("warning:grid.left + grid.right > chartWidth\n"); + if (grid.top + grid.bottom >= chart.chartHeight) + sb.Append("warning:grid.top + grid.bottom > chartHeight\n"); + } + + public override void Update() + { + if (chart.isPointerInChart) + { + component.context.isPointerEnter = component.Contains(chart.pointerPos); + } + else + { + component.context.isPointerEnter = false; + } + } + + public override void DrawUpper(VertexHelper vh) + { + DrawCoord(vh, component); + } + + private void DrawCoord(VertexHelper vh, GridCoord3D grid) + { + if (!grid.show) return; + if (grid.showBorder) + { + var borderWidth = chart.theme.axis.lineWidth; + var borderColor = chart.theme.axis.lineColor; + if (grid.IsLeft()) + { + UGL.DrawLine(vh, grid.context.pointA, grid.context.pointE, borderWidth, borderColor); + UGL.DrawLine(vh, grid.context.pointE, grid.context.pointF, borderWidth, borderColor); + UGL.DrawLine(vh, grid.context.pointE, grid.context.pointH, borderWidth, borderColor); + } + else + { + UGL.DrawLine(vh, grid.context.pointD, grid.context.pointH, borderWidth, borderColor); + UGL.DrawLine(vh, grid.context.pointE, grid.context.pointH, borderWidth, borderColor); + UGL.DrawLine(vh, grid.context.pointG, grid.context.pointH, borderWidth, borderColor); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs.meta b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs.meta new file mode 100644 index 0000000..0fab114 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Grid3D/GridCoord3DHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 92481e92b90724f46b3a7e8c585e12e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Parallel.meta b/Assets/XCharts/Runtime/Coord/Parallel.meta new file mode 100644 index 0000000..081c678 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 30b1519a34fcc4ca3a56834527584719 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs new file mode 100644 index 0000000..2628a59 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs @@ -0,0 +1,127 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Grid component. + /// ||Drawing grid in rectangular coordinate. Line chart, bar chart, and scatter chart can be drawn in grid. + /// ||网格组件。 + /// 直角坐标系内绘图网格。可以在网格上绘制折线图,柱状图,散点图。 + /// </summary> + [Serializable] + [ComponentHandler(typeof(ParallelCoordHandler), true)] + public class ParallelCoord : CoordSystem, IUpdateRuntimeData, ISerieContainer + { + [SerializeField] private bool m_Show = true; + [SerializeField] protected Orient m_Orient = Orient.Vertical; + [SerializeField] private float m_Left = 0.1f; + [SerializeField] private float m_Right = 0.08f; + [SerializeField] private float m_Top = 0.22f; + [SerializeField] private float m_Bottom = 0.12f; + [SerializeField] private Color m_BackgroundColor; + + public ParallelCoordContext context = new ParallelCoordContext(); + + /// <summary> + /// Whether to show the grid in rectangular coordinate. + /// ||是否显示直角坐标系网格。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// Orientation of the axis. By default, it's 'Vertical'. You can set it to be 'Horizonal' to make a vertical axis. + /// ||坐标轴朝向。默认为垂直朝向。 + /// </summary> + public Orient orient + { + get { return m_Orient; } + set { if (PropertyUtil.SetStruct(ref m_Orient, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the left side of the container. + /// ||grid 组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the right side of the container. + /// ||grid 组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the top side of the container. + /// ||grid 组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between grid component and the bottom side of the container. + /// ||grid 组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetAllDirty(); } + } + /// <summary> + /// Background color of grid, which is transparent by default. + /// ||网格背景色,默认透明。 + /// </summary> + public Color backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_BackgroundColor, value)) SetVerticesDirty(); } + } + + public bool IsPointerEnter() + { + return context.runtimeIsPointerEnter; + } + + public void UpdateRuntimeData(BaseChart chart) + { + var chartX = chart.chartX; + var chartY = chart.chartY; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + context.left = left <= 1 ? left * chartWidth : left; + context.bottom = bottom <= 1 ? bottom * chartHeight : bottom; + context.top = top <= 1 ? top * chartHeight : top; + context.right = right <= 1 ? right * chartWidth : right; + context.x = chartX + context.left; + context.y = chartY + context.bottom; + context.width = chartWidth - context.left - context.right; + context.height = chartHeight - context.top - context.bottom; + context.position = new Vector3(context.x, context.y); + } + + public bool Contains(Vector3 pos) + { + return Contains(pos.x, pos.y); + } + + public bool Contains(float x, float y) + { + if (x < context.x - 1 || x > context.x + context.width + 1 || + y < context.y - 1 || y > context.y + context.height + 1) + { + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs.meta b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs.meta new file mode 100644 index 0000000..273d285 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a7be31c76736845a9b2c92a7b8051290 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs new file mode 100644 index 0000000..1d545f9 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class ParallelCoordContext : MainComponentContext + { + public float x; + public float y; + public float width; + public float height; + public Vector3 position; + public float left; + public float right; + public float bottom; + public float top; + public bool runtimeIsPointerEnter; + internal List<ParallelAxis> parallelAxes = new List<ParallelAxis>(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs.meta b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs.meta new file mode 100644 index 0000000..ac0ce5f --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14f338556609b48568d2504a1b153be7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs new file mode 100644 index 0000000..05e1b60 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class ParallelCoordHandler : MainComponentHandler<ParallelCoord> + { + private Dictionary<int, double> m_SerieDimMin = new Dictionary<int, double>(); + private Dictionary<int, double> m_SerieDimMax = new Dictionary<int, double>(); + private double m_LastInterval; + private int m_LastSplitNumber; + + public override void InitComponent() + { + var grid = component; + grid.painter = chart.painter; + grid.refreshComponent = delegate() + { + grid.UpdateRuntimeData(chart); + chart.OnCoordinateChanged(); + }; + grid.refreshComponent(); + } + + public override void CheckComponent(StringBuilder sb) + { + var grid = component; + if (grid.left >= chart.chartWidth) + sb.Append("warning:grid->left > chartWidth\n"); + if (grid.right >= chart.chartWidth) + sb.Append("warning:grid->right > chartWidth\n"); + if (grid.top >= chart.chartHeight) + sb.Append("warning:grid->top > chartHeight\n"); + if (grid.bottom >= chart.chartHeight) + sb.Append("warning:grid->bottom > chartHeight\n"); + if (grid.left + grid.right >= chart.chartWidth) + sb.Append("warning:grid.left + grid.right > chartWidth\n"); + if (grid.top + grid.bottom >= chart.chartHeight) + sb.Append("warning:grid.top + grid.bottom > chartHeight\n"); + } + + public override void Update() + { + UpdatePointerEnter(); + UpdateParallelAxisMinMaxValue(); + } + + public override void DrawBase(VertexHelper vh) + { + if (!SeriesHelper.IsAnyClipSerie(chart.series)) + { + DrawCoord(vh); + } + } + public override void DrawUpper(VertexHelper vh) + { + if (SeriesHelper.IsAnyClipSerie(chart.series)) + { + DrawCoord(vh); + } + } + + private void DrawCoord(VertexHelper vh) + { + var grid = component; + if (grid.show && !ChartHelper.IsClearColor(grid.backgroundColor)) + { + var p1 = new Vector2(grid.context.x, grid.context.y); + var p2 = new Vector2(grid.context.x, grid.context.y + grid.context.height); + var p3 = new Vector2(grid.context.x + grid.context.width, grid.context.y + grid.context.height); + var p4 = new Vector2(grid.context.x + grid.context.width, grid.context.y); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, grid.backgroundColor); + } + } + + private void UpdatePointerEnter() + { + if (chart.isPointerInChart) + component.context.runtimeIsPointerEnter = component.Contains(chart.pointerPos); + else + component.context.runtimeIsPointerEnter = false; + } + + private void UpdateParallelAxisMinMaxValue() + { + var list = chart.GetChartComponents<ParallelAxis>(); + if (list.Count != component.context.parallelAxes.Count) + { + component.context.parallelAxes.Clear(); + foreach (var com in chart.GetChartComponents<ParallelAxis>()) + { + var axis = com as ParallelAxis; + if (axis.parallelIndex == component.index) + component.context.parallelAxes.Add(axis); + } + } + m_SerieDimMin.Clear(); + m_SerieDimMax.Clear(); + foreach (var serie in chart.series) + { + if ((serie is Parallel) && serie.parallelIndex == component.index) + { + foreach (var serieData in serie.data) + { + for (int i = 0; i < serieData.data.Count; i++) + { + var value = serieData.data[i]; + if (!m_SerieDimMin.ContainsKey(i)) + m_SerieDimMin[i] = value; + else if (m_SerieDimMin[i] > value) + m_SerieDimMin[i] = value; + + if (!m_SerieDimMax.ContainsKey(i)) + m_SerieDimMax[i] = value; + else if (m_SerieDimMax[i] < value) + m_SerieDimMax[i] = value; + } + } + } + } + for (int i = 0; i < component.context.parallelAxes.Count; i++) + { + var axis = component.context.parallelAxes[i]; + if (axis.IsCategory()) + { + m_SerieDimMax[i] = axis.data.Count > 0 ? axis.data.Count - 1 : 0; + m_SerieDimMin[i] = 0; + } + else if (axis.minMaxType == Axis.AxisMinMaxType.Custom) + { + m_SerieDimMin[i] = axis.min; + m_SerieDimMax[i] = axis.max; + } + else if (m_SerieDimMax.ContainsKey(i)) + { + + var tempMinValue = m_SerieDimMin[i]; + var tempMaxValue = m_SerieDimMax[i]; + AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true); + m_SerieDimMin[i] = tempMinValue; + m_SerieDimMax[i] = tempMaxValue; + } + } + for (int i = 0; i < component.context.parallelAxes.Count; i++) + { + if (m_SerieDimMax.ContainsKey(i)) + { + var axis = component.context.parallelAxes[i]; + var tempMinValue = m_SerieDimMin[i]; + var tempMaxValue = m_SerieDimMax[i]; + + if (tempMinValue != axis.context.minValue || + tempMaxValue != axis.context.maxValue || + m_LastInterval != axis.interval || + m_LastSplitNumber != axis.splitNumber) + { + m_LastSplitNumber = axis.splitNumber; + m_LastInterval = axis.interval; + + axis.UpdateMinMaxValue(tempMinValue, tempMaxValue); + axis.context.offset = 0; + axis.context.lastCheckInverse = axis.inverse; + + (axis.handler as ParallelAxisHander).UpdateAxisTickValueList(axis); + (axis.handler as ParallelAxisHander).UpdateAxisLabelText(axis); + chart.RefreshChart(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs.meta b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs.meta new file mode 100644 index 0000000..e7ee712 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Parallel/ParallelCoordHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb7323519e00e4916a9c42c5faa36a38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Polar.meta b/Assets/XCharts/Runtime/Coord/Polar.meta new file mode 100644 index 0000000..d1b31bf --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43b3734481ac34ff89708f2edfa473ca +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs b/Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs new file mode 100644 index 0000000..58db2fd --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs @@ -0,0 +1,83 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Polar coordinate can be used in scatter and line chart. Every polar coordinate has an angleAxis and a radiusAxis. + /// ||极坐标系组件。 + /// 极坐标系,可以用于散点图和折线图。每个极坐标系拥有一个角度轴和一个半径轴。 + /// </summary> + [Serializable] + [ComponentHandler(typeof(PolarCoordHandler), true)] + public class PolarCoord : CoordSystem, ISerieContainer + { + [SerializeField] private bool m_Show = true; + [SerializeField] private float[] m_Center = new float[2] { 0.5f, 0.44f }; + [SerializeField] private float[] m_Radius = new float[2] { 0, 0.31f }; + [SerializeField] private Color m_BackgroundColor; + [SerializeField][Since("v3.8.0")] private float m_IndicatorLabelOffset = 30f; + + public PolarCoordContext context = new PolarCoordContext(); + + /// <summary> + /// Whether to show the polor component. + /// ||是否显示极坐标。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// <summary> + /// The center of ploar. The center[0] is the x-coordinate, and the center[1] is the y-coordinate. + /// When value between 0 and 1 represents a percentage relative to the chart. + /// ||极坐标的中心点。数组的第一项是横坐标,第二项是纵坐标。 + /// 当值为0-1之间时表示百分比,设置成百分比时第一项是相对于容器宽度,第二项是相对于容器高度。 + /// </summary> + public float[] center + { + get { return m_Center; } + set { if (value != null) { m_Center = value; SetAllDirty(); } } + } + /// <summary> + /// the radius of polar. + /// ||半径。radius[0]表示内径,radius[1]表示外径。 + /// </summary> + public float[] radius + { + get { return m_Radius; } + set { if (value != null && value.Length == 2) { m_Radius = value; SetAllDirty(); } } + } + /// <summary> + /// Background color of polar, which is transparent by default. + /// ||极坐标的背景色,默认透明。 + /// </summary> + public Color backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_BackgroundColor, value)) SetVerticesDirty(); } + } + + /// <summary> + /// The offset of indicator label. + /// ||指示器标签的偏移量。 + /// </summary> + public float indicatorLabelOffset + { + get { return m_IndicatorLabelOffset; } + set { if (PropertyUtil.SetStruct(ref m_IndicatorLabelOffset, value)) SetVerticesDirty(); } + } + + public bool IsPointerEnter() + { + return context.isPointerEnter; + } + + public bool Contains(Vector3 pos) + { + var dist = Vector3.Distance(pos, context.center); + return dist >= context.insideRadius && dist <= context.outsideRadius; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs.meta b/Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs.meta new file mode 100644 index 0000000..03d8791 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec567fac460994411a8aadcb5e0f9b68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs new file mode 100644 index 0000000..028ce6c --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs @@ -0,0 +1,26 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class PolarCoordContext : MainComponentContext + { + /// <summary> + /// the center position of polar in container. + /// ||极坐标在容器中的具体中心点。 + /// </summary> + public Vector3 center; + public float radius; + /// <summary> + /// the true radius of polar. + /// ||极坐标的运行时实际内半径。 + /// </summary> + public float insideRadius; + /// <summary> + /// the true radius of polar. + /// ||极坐标的运行时实际外半径。 + /// </summary> + public float outsideRadius; + public bool isPointerEnter; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs.meta b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs.meta new file mode 100644 index 0000000..e448702 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2eaaaa315fbae4fc3a9976f51a1396b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs new file mode 100644 index 0000000..182c8be --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs @@ -0,0 +1,49 @@ +using System; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class PolarCoordHandler : MainComponentHandler<PolarCoord> + { + public override void Update() + { + base.Update(); + PolarHelper.UpdatePolarCenter(component, chart.chartPosition, chart.chartWidth, chart.chartHeight); + + if (chart.isPointerInChart) + component.context.isPointerEnter = component.Contains(chart.pointerPos); + else + component.context.isPointerEnter = false; + } + + public override void DrawBase(VertexHelper vh) + { + DrawPolar(vh, component); + } + + private void DrawPolar(VertexHelper vh, PolarCoord polar) + { + PolarHelper.UpdatePolarCenter(polar, chart.chartPosition, chart.chartWidth, chart.chartHeight); + if (polar.show && !ChartHelper.IsClearColor(polar.backgroundColor)) + { + if (polar.context.insideRadius > 0) + { + UGL.DrawDoughnut(vh, polar.context.center, + polar.context.insideRadius, + polar.context.outsideRadius, + polar.backgroundColor, + ColorUtil.clearColor32); + } + else + { + UGL.DrawCricle(vh, polar.context.center, + polar.context.outsideRadius, + polar.backgroundColor); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs.meta b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs.meta new file mode 100644 index 0000000..d3e4351 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarCoordHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af4b941946def4928b416260dec7ac9b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs b/Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs new file mode 100644 index 0000000..f80f521 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs @@ -0,0 +1,41 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + internal static class PolarHelper + { + public static void UpdatePolarCenter(PolarCoord polar, Vector3 chartPosition, float chartWidth, float chartHeight) + { + if (polar.center.Length < 2) return; + var centerX = polar.center[0] <= 1 ? chartWidth * polar.center[0] : polar.center[0]; + var centerY = polar.center[1] <= 1 ? chartHeight * polar.center[1] : polar.center[1]; + var minWidth = Mathf.Min(chartWidth, chartHeight); + + polar.context.center = chartPosition + new Vector3(centerX, centerY); + polar.context.insideRadius = polar.context.outsideRadius = 0; + if (polar.radius.Length >= 2) + { + polar.context.insideRadius = ChartHelper.GetActualValue(polar.radius[0], minWidth, 1); + polar.context.outsideRadius = ChartHelper.GetActualValue(polar.radius[1], minWidth, 1); + } + else if (polar.radius.Length >= 1) + { + polar.context.outsideRadius = ChartHelper.GetActualValue(polar.radius[0], minWidth, 1); + } + polar.context.radius = polar.context.outsideRadius - polar.context.insideRadius; + } + + public static Vector3 UpdatePolarAngleAndPos(PolarCoord polar, AngleAxis angleAxis, RadiusAxis radiusAxis, SerieData serieData) + { + var value = serieData.GetData(0); + var angle = angleAxis.GetValueAngle(serieData.GetData(1)); + var radius = polar.context.insideRadius + radiusAxis.GetValueLength(value, polar.context.radius); + + angle = (angle + 360) % 360; + serieData.context.angle = angle; + serieData.context.position = ChartHelper.GetPos(polar.context.center, radius, angle, true); + + return serieData.context.position; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs.meta b/Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs.meta new file mode 100644 index 0000000..8ec1778 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/Polar/PolarHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: feb363cc2ae0846b89612143ce4535ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/SingleAxis.meta b/Assets/XCharts/Runtime/Coord/SingleAxis.meta new file mode 100644 index 0000000..a460d59 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/SingleAxis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 102d61482a6f946cc82f228c88369dfd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs b/Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs new file mode 100644 index 0000000..990ebe3 --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs @@ -0,0 +1,9 @@ +using System; + +namespace XCharts.Runtime +{ + [Serializable] + [ComponentHandler(null)] + public class SingleAxisCoord : CoordSystem + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs.meta b/Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs.meta new file mode 100644 index 0000000..84b8cde --- /dev/null +++ b/Assets/XCharts/Runtime/Coord/SingleAxis/SingleAxisCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3e972d6eb5bc45e1ba7b2c5740474fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Helper.meta b/Assets/XCharts/Runtime/Helper.meta new file mode 100644 index 0000000..f8b14dc --- /dev/null +++ b/Assets/XCharts/Runtime/Helper.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 58d150a402b5e4bfcbec6a28cba7ed44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Helper/CheckHelper.cs b/Assets/XCharts/Runtime/Helper/CheckHelper.cs new file mode 100644 index 0000000..f070e5e --- /dev/null +++ b/Assets/XCharts/Runtime/Helper/CheckHelper.cs @@ -0,0 +1,152 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class CheckHelper + { + private static bool IsColorAlphaZero(Color color) + { + return !ChartHelper.IsClearColor(color) && color.a == 0; + } + + public static string CheckChart(BaseGraph chart) + { + if (chart is BaseChart) return CheckChart((BaseChart) chart); + else return string.Empty; + } + + public static string CheckChart(BaseChart chart) + { + var sb = ChartHelper.sb; + sb.Length = 0; + CheckName(chart, sb); + CheckSize(chart, sb); + CheckTheme(chart, sb); + CheckTitle(chart, sb); + CheckLegend(chart, sb); + CheckGrid(chart, sb); + CheckSerie(chart, sb); + return sb.ToString(); + } + + private static void CheckName(BaseChart chart, StringBuilder sb) + { + if (string.IsNullOrEmpty(chart.chartName)) return; + var list = XChartsMgr.GetCharts(chart.chartName); + if (list.Count > 1) + { + sb.AppendFormat("warning:chart name is repeated: {0}\n", chart.chartName); + } + } + + private static void CheckSize(BaseChart chart, StringBuilder sb) + { + if (chart.chartWidth == 0 || chart.chartHeight == 0) + { + sb.Append("warning:chart width or height is 0\n"); + } + } + + private static void CheckTheme(BaseChart chart, StringBuilder sb) + { + var theme = chart.theme; + theme.CheckWarning(sb); + } + + private static void CheckTitle(BaseChart chart, StringBuilder sb) + { + // foreach (var title in chart.titles) + // { + // if (!title.show) return; + // if (string.IsNullOrEmpty(title.text)) sb.AppendFormat("warning:title{0}->text is null\n", title.index); + // if (IsColorAlphaZero(title.textStyle.color)) + // sb.AppendFormat("warning:title{0}->textStyle->color alpha is 0\n", title.index); + // if (IsColorAlphaZero(title.subTextStyle.color)) + // sb.AppendFormat("warning:title{0}->subTextStyle->color alpha is 0\n", title.index); + // } + } + + private static void CheckLegend(BaseChart chart, StringBuilder sb) { } + + private static void CheckGrid(BaseChart chart, StringBuilder sb) { } + + private static void CheckSerie(BaseChart chart, StringBuilder sb) + { + var allDataIsEmpty = true; + var allDataIsZero = true; + var allSerieIsHide = true; + var set = new HashSet<int>(); + foreach (var serie in chart.series) + { + if (serie.show) allSerieIsHide = false; + if (serie.dataCount > 0) + { + allDataIsEmpty = false; + var dataIndexError = 0; + set.Clear(); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.GetSerieData(i); + if (set.Contains(serieData.index)) + { + dataIndexError++; + } + else + { + set.Add(serieData.index); + } + for (int j = 1; j < serieData.data.Count; j++) + { + if (serieData.GetData(j) != 0) + { + allDataIsZero = false; + break; + } + } + } + var dataCount = serie.GetSerieData(0).data.Count; + if (serie.showDataDimension > 1 && serie.showDataDimension != dataCount) + { + sb.AppendFormat("warning:serie {0} serieData.data.count[{1}] not match showDataDimension[{2}]\n", serie.index, dataCount, serie.showDataDimension); + } + if (dataIndexError > 0) + { + sb.AppendFormat("error: data index error, count={0}/{1}\n", dataIndexError, serie.dataCount); + } + } + else + { + sb.AppendFormat("warning:serie {0} no data\n", serie.index); + } + if (IsColorAlphaZero(serie.itemStyle.color)) + sb.AppendFormat("warning:serie {0} itemStyle->color alpha is 0\n", serie.index); + if (serie.itemStyle.opacity == 0) + sb.AppendFormat("warning:serie {0} itemStyle->opacity is 0\n", serie.index); + if (serie.itemStyle.borderWidth != 0 && IsColorAlphaZero(serie.itemStyle.borderColor)) + sb.AppendFormat("warning:serie {0} itemStyle->borderColor alpha is 0\n", serie.index); + if (serie is Line) + { + if (serie.lineStyle.opacity == 0) + sb.AppendFormat("warning:serie {0} lineStyle->opacity is 0\n", serie.index); + if (IsColorAlphaZero(serie.lineStyle.color)) + sb.AppendFormat("warning:serie {0} lineStyle->color alpha is 0\n", serie.index); + } + else if (serie is Pie) + { + if (serie.radius.Length >= 2 && serie.radius[1] == 0) + sb.AppendFormat("warning:serie {0} radius[1] is 0\n", serie.index); + } + else if (serie is Scatter || serie is EffectScatter) + { + if (!serie.symbol.show) + sb.AppendFormat("warning:serie {0} symbol type is None\n", serie.index); + } + } + if (allDataIsEmpty) sb.Append("warning:all serie data is empty\n"); + if (!allDataIsEmpty && allDataIsZero) sb.Append("warning:all serie data is 0\n"); + if (allSerieIsHide) sb.Append("warning:all serie is hide\n"); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Helper/CheckHelper.cs.meta b/Assets/XCharts/Runtime/Helper/CheckHelper.cs.meta new file mode 100644 index 0000000..56e1a2f --- /dev/null +++ b/Assets/XCharts/Runtime/Helper/CheckHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09a50ff0a7fdb4174b4dc2d28fc08b6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Helper/FormatterHelper.cs b/Assets/XCharts/Runtime/Helper/FormatterHelper.cs new file mode 100644 index 0000000..57ae9d4 --- /dev/null +++ b/Assets/XCharts/Runtime/Helper/FormatterHelper.cs @@ -0,0 +1,390 @@ +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class FormatterHelper + { + public const string PH_NN = "\n"; + private static Regex s_Regex = new Regex(@"{([a-h|.|y]\d*)(:\d+(-\d+)?)?(:[c-g|x|p|r]\d*|:0\.#*)?}", RegexOptions.IgnoreCase); + private static Regex s_RegexSub = new Regex(@"(0\.#*)|(\d+-\d+)|(\w+)|(\.)", RegexOptions.IgnoreCase); + private static Regex s_RegexN = new Regex(@"^\d+", RegexOptions.IgnoreCase); + private static Regex s_RegexN_N = new Regex(@"\d+-\d+", RegexOptions.IgnoreCase); + private static Regex s_RegexFn = new Regex(@"[c-g|x|p|r]\d*|0\.#*", RegexOptions.IgnoreCase); + private static Regex s_RegexNewLine = new Regex(@"[\\|/]+n|</br>|<br>|<br/>", RegexOptions.IgnoreCase); + private static Regex s_RegexForAxisLabel = new Regex(@"{value(:[c-g|x|p|r]\d*)?}", RegexOptions.IgnoreCase); + private static Regex s_RegexSubForAxisLabel = new Regex(@"(value)|([c-g|x|p|r]\d*)", RegexOptions.IgnoreCase); + private static Regex s_RegexForSerieLabel = new Regex(@"{[a-h|\.|y]\d*(:[c-g|x|p|r]\d*)?}", RegexOptions.IgnoreCase); + private static Regex s_RegexSubForSerieLabel = new Regex(@"(\.)|([a-h|y]\d*)|([c-g|x|p|r]\d*)", RegexOptions.IgnoreCase); + private static Regex s_RegexForAxisIndex = new Regex(@"\{(-?)index([+-]\d+)?\}", RegexOptions.IgnoreCase); + + public static bool NeedFormat(string content) + { + return !string.IsNullOrEmpty(content) && content.IndexOf('{') >= 0; + } + + /// <summary> + /// 替换字符串中的通配符,支持的通配符有{.}、{a}、{b}、{c}、{d}、{e}、{f}、{g}、{h}、{y}。 + /// </summary> + /// <param name="content">要替换的字符串</param> + /// <param name="dataIndex">选中的数据项serieData索引</param> + /// <param name="numericFormatter">默认的数字格式化</param> + /// <param name="serie">选中的serie</param> + /// <param name="series">所有serie</param> + /// <param name="theme">用来获取指定index的颜色</param> + /// <param name="category">选中的类目,一般用在折线图和柱状图</param> + /// <returns></returns> + public static bool ReplaceContent(ref string content, int dataIndex, string numericFormatter, Serie serie, + BaseChart chart, string colorName = null) + { + var foundDot = false; + var mc = s_Regex.Matches(content); + if (dataIndex < 0) + { + dataIndex = serie != null ? serie.context.pointerItemDataIndex : 0; + } + foreach (var m in mc) + { + var old = m.ToString(); + var args = s_RegexSub.Matches(m.ToString()); + var argsCount = args.Count; + if (argsCount <= 0) continue; + int targetIndex = 0; + char p = GetSerieIndex(args[0].ToString(), ref targetIndex); + if (targetIndex >= 0) + { + serie = chart.GetSerie(targetIndex); + if (serie == null) continue; + } + else if (serie != null) + { + targetIndex = serie.index; + } + else + { + serie = chart.GetSerie(0); + targetIndex = 0; + } + if (serie == null) continue; + if (p == '.' || p == 'h' || p == 'H') + { + var bIndex = dataIndex; + if (argsCount >= 2) + { + var args1Str = args[1].ToString(); + if (s_RegexN.IsMatch(args1Str)) bIndex = int.Parse(args1Str); + } + var color = string.IsNullOrEmpty(colorName) ? + (Color)chart.GetMarkColor(serie, serie.GetSerieData(bIndex)) : + SeriesHelper.GetNameColor(chart, bIndex, colorName); + if (p == '.') + { + content = content.Replace(old, ChartCached.ColorToDotStr(color)); + foundDot = true; + } + else + { + content = content.Replace(old, "#" + ChartCached.ColorToStr(color)); + } + } + else if (p == 'a' || p == 'A') + { + if (argsCount == 1) + { + content = content.Replace(old, serie.serieName); + } + } + else if (p == 'b' || p == 'B' || p == 'e' || p == 'E') + { + var bIndex = dataIndex; + if (argsCount >= 2) + { + var args1Str = args[1].ToString(); + if (s_RegexN.IsMatch(args1Str)) bIndex = int.Parse(args1Str); + } + var needCategory = p != 'e' && p != 'E' && serie.defaultColorBy != SerieColorBy.Data; + if (needCategory) + { + var category = chart.GetTooltipCategory(serie); + content = content.Replace(old, category); + } + else + { + var serieData = serie.GetSerieData(bIndex); + content = content.Replace(old, serieData.name); + } + } + else if (p == 'g' || p == 'G') + { + content = content.Replace(old, ChartCached.NumberToStr(serie.dataCount, "")); + } + else if (p == 'y' || p == 'Y') + { + if (chart != null) + { + var yAxis = chart.GetChartComponent<YAxis>(0); + if (yAxis != null) + { + var bIndex = dataIndex; + if (argsCount >= 2) + { + var args1Str = args[1].ToString(); + if (s_RegexN.IsMatch(args1Str)) bIndex = int.Parse(args1Str); + if (s_RegexFn.IsMatch(args1Str)) numericFormatter = args1Str; + } + if (yAxis.IsCategory()) + { + var yCategory = yAxis.GetData(bIndex); + content = content.Replace(old, yCategory); + } + else + { + var value = yAxis.context.pointerValue; + content = content.Replace(old, ChartCached.FloatToStr(value, numericFormatter)); + } + } + } + } + else if (p == 'c' || p == 'C' || p == 'd' || p == 'D' || p == 'f' || p == 'f') + { + var isPercent = p == 'd' || p == 'D'; + var isTotal = p == 'f' || p == 'F'; + var bIndex = dataIndex; + var dimensionIndex = -1; + if (argsCount >= 2) + { + var args1Str = args[1].ToString(); + if (s_RegexFn.IsMatch(args1Str)) + { + numericFormatter = args1Str; + } + else if (s_RegexN_N.IsMatch(args1Str)) + { + var temp = args1Str.Split('-'); + bIndex = int.Parse(temp[0]); + dimensionIndex = int.Parse(temp[1]); + } + else if (s_RegexN.IsMatch(args1Str)) + { + dimensionIndex = int.Parse(args1Str); + } + else + { + Debug.LogError("unmatch:" + args1Str); + continue; + } + } + if (argsCount >= 3) + { + numericFormatter = args[2].ToString(); + } + if (dimensionIndex == -1) dimensionIndex = 1; + if (numericFormatter == string.Empty) + { + numericFormatter = SerieHelper.GetNumericFormatter(serie, serie.GetSerieData(bIndex), ""); + } + var value = serie.GetData(bIndex, dimensionIndex); + var ignore = serie.IsIgnoreIndex(bIndex); + if (isPercent) + { + var total = serie.GetDataTotal(dimensionIndex, serie.GetSerieData(bIndex)); + var percent = total == 0 ? 0 : value / total * 100; + content = content.Replace(old, ChartCached.FloatToStr(percent, numericFormatter)); + } + else if (isTotal) + { + var total = serie.GetDataTotal(dimensionIndex, serie.GetSerieData(bIndex)); + content = content.Replace(old, ChartCached.FloatToStr(total, numericFormatter)); + } + else + { + if (ignore) + content = content.Replace(old, "-"); + else + content = content.Replace(old, ChartCached.FloatToStr(value, numericFormatter)); + } + } + } + content = s_RegexNewLine.Replace(content, PH_NN); + return foundDot; + } + + public static void ReplaceSerieLabelContent(ref string content, string numericFormatter, int dataCount, double value, double total, + string serieName, string category, string dataName, Color color, SerieData serieData, BaseChart chart = null, int serieIndex = 0) + { + var mc = s_RegexForSerieLabel.Matches(content); + foreach (var m in mc) + { + var old = m.ToString(); + var args = s_RegexSubForSerieLabel.Matches(old); + var argsCount = args.Count; + if (argsCount <= 0) continue; + var pstr = args[0].ToString(); + var p = pstr.ElementAt(0); + var pIndex = -1; + if (pstr.Length > 1) + { + int.TryParse(pstr.Substring(1, pstr.Length - 1), out pIndex); + } + if (argsCount >= 2) + { + numericFormatter = args[1].ToString(); + } + if (p == '.') + { + content = content.Replace(old, ChartCached.ColorToDotStr(color)); + } + else if (p == 'a' || p == 'A') + { + content = content.Replace(old, serieName); + } + else if (p == 'b' || p == 'B') + { + content = content.Replace(old, category); + } + else if (p == 'e' || p == 'E') + { + content = content.Replace(old, dataName); + } + else if (p == 'd' || p == 'D') + { + if (serieData != null && serieData.ignore) + content = content.Replace(old, "-"); + else + { + var rate = pIndex >= 0 && serieData != null ? + (value == 0 ? 0 : serieData.GetData(pIndex) / value * 100) : + (total == 0 ? 0 : value / total * 100); + content = content.Replace(old, ChartCached.NumberToStr(rate, numericFormatter)); + } + } + else if (p == 'c' || p == 'C') + { + if (serieData != null && serieData.ignore) + content = content.Replace(old, "-"); + else if (serieData != null && pIndex >= 0) + content = content.Replace(old, ChartCached.NumberToStr(serieData.GetData(pIndex), numericFormatter)); + else + content = content.Replace(old, ChartCached.NumberToStr(value, numericFormatter)); + } + else if (p == 'f' || p == 'f') + { + if (pIndex != 1 && chart != null) + { + var serie = chart.GetSerie(serieIndex); + if (serie != null) + { + total = serie.GetDataTotal(pIndex, serieData); + } + } + content = content.Replace(old, ChartCached.NumberToStr(total, numericFormatter)); + } + else if (p == 'g' || p == 'G') + { + content = content.Replace(old, ChartCached.NumberToStr(dataCount, numericFormatter)); + } + else if (p == 'h' || p == 'H') + { + content = content.Replace(old, "#" + ChartCached.ColorToStr(color)); + } + else if (p == 'y' || p == 'Y') + { + if (chart != null) + { + var yAxis = chart.GetChartComponent<YAxis>(0); + if (yAxis != null) + { + if (yAxis.IsCategory()) + { + var yCategory = yAxis.GetData(pIndex >= 0 ? pIndex : (int)value); + content = content.Replace(old, yCategory); + } + else + { + content = content.Replace(old, ChartCached.NumberToStr(value, numericFormatter)); + } + } + } + } + } + content = TrimAndReplaceLine(content); + } + + private static char GetSerieIndex(string strType, ref int index) + { + index = -1; + if (strType.Length > 1) + { + if (!int.TryParse(strType.Substring(1), out index)) + { + index = -1; + } + } + return strType.ElementAt(0); + } + + public static string TrimAndReplaceLine(StringBuilder sb) + { + return TrimAndReplaceLine(sb.ToString()); + } + + public static string TrimAndReplaceLine(string content) + { + return s_RegexNewLine.Replace(content.Trim(), PH_NN); + } + + public static void ReplaceAxisLabelContent(ref string content, string numericFormatter, double value, int index, int totalIndex) + { + var mc = s_RegexForAxisLabel.Matches(content); + foreach (var m in mc) + { + var old = m.ToString(); + var args = s_RegexSubForAxisLabel.Matches(m.ToString()); + var argsCount = args.Count; + if (argsCount <= 0) continue; + if (argsCount >= 2) + { + numericFormatter = args[1].ToString(); + } + content = content.Replace(old, ChartCached.FloatToStr(value, numericFormatter)); + } + ReplaceIndexContent(ref content, index, totalIndex); + content = TrimAndReplaceLine(content); + } + + public static void ReplaceAxisLabelContent(ref string content, string value, int index, int totalIndex) + { + var mc = s_RegexForAxisLabel.Matches(content); + foreach (var m in mc) + { + var old = m.ToString(); + var args = s_RegexSubForAxisLabel.Matches(m.ToString()); + var argsCount = args.Count; + if (argsCount <= 0) continue; + content = content.Replace(old, value); + } + ReplaceIndexContent(ref content, index, totalIndex); + content = TrimAndReplaceLine(content); + } + + public static void ReplaceIndexContent(ref string content, int currIndex, int totalIndex) + { + if (totalIndex <= 0) return; + content = s_RegexForAxisIndex.Replace(content, (match) => + { + bool isNegative = match.Groups[1].Value == "-"; + int offset = 0; + int parsedOffset = 0; + if (match.Groups[2].Success && + int.TryParse(match.Groups[2].Value, out parsedOffset)) + { + offset = parsedOffset; + } + int baseValue = isNegative ? totalIndex - currIndex : currIndex + 1; + return (baseValue + offset).ToString(); + }); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Helper/FormatterHelper.cs.meta b/Assets/XCharts/Runtime/Helper/FormatterHelper.cs.meta new file mode 100644 index 0000000..6c2e5d3 --- /dev/null +++ b/Assets/XCharts/Runtime/Helper/FormatterHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fddcb81df44148ed86496564b120261 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/I18n.meta b/Assets/XCharts/Runtime/I18n.meta new file mode 100644 index 0000000..3486925 --- /dev/null +++ b/Assets/XCharts/Runtime/I18n.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3091670d5958a4fbaa9024b5cda31f1d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/I18n/Lang.cs b/Assets/XCharts/Runtime/I18n/Lang.cs new file mode 100644 index 0000000..e89b88d --- /dev/null +++ b/Assets/XCharts/Runtime/I18n/Lang.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Language. + /// ||国际化语言表。 + /// </summary> + [Serializable] + [CreateAssetMenu(menuName = "XCharts/Export Lang")] + public class Lang : ScriptableObject + { + public string langName = "EN"; + public LangTime time = new LangTime(); + public LangCandlestick candlestick = new LangCandlestick(); + + public string GetMonthAbbr(int month) + { + if (month < 1 && month > 12) return month.ToString(); + else return time.monthAbbr[month - 1]; + } + + public string GetDay(int day) + { + day = day - 1; + if (day >= 0 && day < time.dayOfMonth.Count - 1) + return time.dayOfMonth[day]; + else + return day.ToString(); + } + + public string GetCandlestickDimensionName(int i) + { + if (i >= 0 && i < candlestick.dimensionNames.Count) + return candlestick.dimensionNames[i]; + else + return string.Empty; + } + } + + [Serializable] + public class LangTime + { + public List<string> months = new List<string>() + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + }; + public List<string> monthAbbr = new List<string>() + { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + public List<string> dayOfMonth = new List<string>() + { + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31" + }; + public List<string> dayOfWeek = new List<string>() + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + }; + public List<string> dayOfWeekAbbr = new List<string>() + { + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + }; + } + + [Serializable] + public class LangCandlestick + { + public List<string> dimensionNames = new List<string>() { "open", "close", "lowest", "highest" }; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/I18n/Lang.cs.meta b/Assets/XCharts/Runtime/I18n/Lang.cs.meta new file mode 100644 index 0000000..1f35cfa --- /dev/null +++ b/Assets/XCharts/Runtime/I18n/Lang.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b65fc8b25febc4b9e8acb500d16770b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal.meta b/Assets/XCharts/Runtime/Internal.meta new file mode 100644 index 0000000..791d17b --- /dev/null +++ b/Assets/XCharts/Runtime/Internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 750348e0c6842d74e872391f6ea942da +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes.meta b/Assets/XCharts/Runtime/Internal/Attributes.meta new file mode 100644 index 0000000..d27d0ee --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aa2d903c5b18c41f78b61bd01f1512f3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs new file mode 100644 index 0000000..a3a82a6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs @@ -0,0 +1,26 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class ComponentHandlerAttribute : Attribute + { + public readonly Type handler; + public readonly bool allowMultiple = true; + public readonly int order = 3; + + public ComponentHandlerAttribute(Type handler, int order = 3) + { + this.handler = handler; + this.allowMultiple = true; + this.order = order; + } + + public ComponentHandlerAttribute(Type handler, bool allowMultiple, int order = 3) + { + this.handler = handler; + this.allowMultiple = allowMultiple; + this.order = order; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs.meta new file mode 100644 index 0000000..0cabefc --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ComponentHandlerAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 396f8e713effb49fa8757d45944e7d30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs new file mode 100644 index 0000000..b1040ca --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs @@ -0,0 +1,42 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class CoordOptionsAttribute : Attribute + { + public readonly Type type0; + public readonly Type type1; + public readonly Type type2; + public readonly Type type3; + + public CoordOptionsAttribute(Type coord) + { + type0 = coord; + } + public CoordOptionsAttribute(Type coord, Type coord2) + { + type0 = coord; + type1 = coord2; + } + public CoordOptionsAttribute(Type coord, Type coord2, Type coord3) + { + type0 = coord; + type1 = coord2; + type2 = coord3; + } + public CoordOptionsAttribute(Type coord, Type coord2, Type coord3, Type coord4) + { + type0 = coord; + type1 = coord2; + type2 = coord3; + type3 = coord4; + } + + public bool Contains<T>() where T : CoordSystem + { + var type = typeof(T); + return (type == type0 || type == type1 || type == type2 || type == type3); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs.meta new file mode 100644 index 0000000..faba950 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/CoordOptionsAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c03247521a944507bcdb1bcfbbc6006 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs new file mode 100644 index 0000000..f8b0646 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class DefaultAnimationAttribute : Attribute + { + public readonly AnimationType type; + public readonly bool enableSerieDataAddedAnimation = true; + + public DefaultAnimationAttribute(AnimationType handler) + { + this.type = handler; + } + + public DefaultAnimationAttribute(AnimationType handler, bool enableSerieDataAddedAnimation) + { + this.type = handler; + this.enableSerieDataAddedAnimation = enableSerieDataAddedAnimation; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs.meta new file mode 100644 index 0000000..8b15da5 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/DefaultAnimationAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b25b7b1d8388945d4bf78e54f094470f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs new file mode 100644 index 0000000..c5eaff3 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class DefaultTooltipAttribute : Attribute + { + public readonly Tooltip.Type type; + public readonly Tooltip.Trigger trigger; + + public DefaultTooltipAttribute(Tooltip.Type type, Tooltip.Trigger trigger) + { + this.type = type; + this.trigger = trigger; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs.meta new file mode 100644 index 0000000..0aa0432 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/DefaultTooltipAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a994dc47021bb4031ba6cf23eaf82e7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs new file mode 100644 index 0000000..dbbc307 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public class IgnoreDoc : Attribute + { + public IgnoreDoc() + { + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs.meta new file mode 100644 index 0000000..aab5242 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/IgnoreDocAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd89bf9e568d34de089f71258f2bd211 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs new file mode 100644 index 0000000..0a96ce6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public class ListFor : Attribute + { + public readonly Type type; + + public ListFor(Type type) + { + this.type = type; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs.meta new file mode 100644 index 0000000..d4d2d6e --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ListForAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34edd91ec3857490fa2f04c620e44299 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs new file mode 100644 index 0000000..52a9881 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public sealed class ListForComponent : ListFor + { + public ListForComponent(Type type) : base(type) + { } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs.meta new file mode 100644 index 0000000..dca1752 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ListForComponentAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 529bcbd6bb69b4aac905c44451077ca5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs new file mode 100644 index 0000000..07bcf23 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public sealed class ListForSerie : ListFor + { + public ListForSerie(Type type) : base(type) + { } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs.meta new file mode 100644 index 0000000..f5a2afd --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/ListForSerieAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2723e22555ab04116892a8c7d5c75fbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs new file mode 100644 index 0000000..551bb9a --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs @@ -0,0 +1,28 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class RequireChartComponentAttribute : Attribute + { + public readonly Type type0; + public readonly Type type1; + public readonly Type type2; + + public RequireChartComponentAttribute(Type requiredComponent) + { + type0 = requiredComponent; + } + public RequireChartComponentAttribute(Type requiredComponent, Type requiredComponent2) + { + type0 = requiredComponent; + type1 = requiredComponent2; + } + public RequireChartComponentAttribute(Type requiredComponent, Type requiredComponent2, Type requiredComponent3) + { + type0 = requiredComponent; + type1 = requiredComponent2; + type2 = requiredComponent3; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs.meta new file mode 100644 index 0000000..3ad3719 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/RequireChartComponentAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f27bf434cb8045a6b5d02930f8df479 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs new file mode 100644 index 0000000..7a8890f --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + /// <summary> + /// The attribute for serie component. + /// ||可添加到Serie的组件。 + /// </summary> + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class SerieComponentAttribute : Attribute + { + public readonly List<Type> types = new List<Type>(); + + public SerieComponentAttribute() + { } + public SerieComponentAttribute(Type type1) + { + AddType(type1); + } + public SerieComponentAttribute(Type type1, Type type2) + { + AddType(type1); + AddType(type2); + } + public SerieComponentAttribute(Type type1, Type type2, Type type3) + { + AddType(type1); + AddType(type2); + AddType(type3); + } + public SerieComponentAttribute(Type type1, Type type2, Type type3, Type type4) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + } + public SerieComponentAttribute(Type type1, Type type2, Type type3, Type type4, Type type5) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + AddType(type5); + } + public SerieComponentAttribute(Type type1, Type type2, Type type3, Type type4, Type type5, Type type6) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + AddType(type5); + AddType(type6); + } + public SerieComponentAttribute(Type type1, Type type2, Type type3, Type type4, Type type5, Type type6, Type type7) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + AddType(type5); + AddType(type6); + AddType(type7); + } + + private void AddType(Type type) + { + if (!Serie.extraComponentMap.ContainsKey(type)) + throw new ArgumentException("Serie not support extra component:" + type); + types.Add(type); + } + + public bool Contains<T>() where T : ISerieComponent + { + return Contains(typeof(T)); + } + + public bool Contains(Type type) + { + return types.Contains(type); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs.meta new file mode 100644 index 0000000..99dd27c --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieComponentAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d61861a0f45f43af8915ae23cc326e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs new file mode 100644 index 0000000..2661d34 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs @@ -0,0 +1,50 @@ +using System; + +namespace XCharts.Runtime +{ + /// <summary> + /// The attribute for which serie types can be converted to. + /// ||可转化为哪些Serie类型。 + /// </summary> + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class SerieConvertAttribute : Attribute + { + public readonly Type type0; + public readonly Type type1; + public readonly Type type2; + public readonly Type type3; + + public SerieConvertAttribute(Type serie) + { + type0 = serie; + } + public SerieConvertAttribute(Type serie, Type serie2) + { + type0 = serie; + type1 = serie2; + } + public SerieConvertAttribute(Type serie, Type serie2, Type serie3) + { + type0 = serie; + type1 = serie2; + type2 = serie3; + } + public SerieConvertAttribute(Type serie, Type serie2, Type serie3, Type serie4) + { + type0 = serie; + type1 = serie2; + type2 = serie3; + type3 = serie4; + } + + public bool Contains<T>() where T : Serie + { + return Contains(typeof(T)); + } + + public bool Contains(Type type) + { + return (type == type0 || type == type1 || type == type2 || type == type3); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs.meta new file mode 100644 index 0000000..b4de61a --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieConvertAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74af4595d38cb43ca8f11348cc979137 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs new file mode 100644 index 0000000..bd6428e --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + /// <summary> + /// The attribute for serie data component. + /// ||可添加到SerieData的组件。 + /// </summary> + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class SerieDataComponentAttribute : Attribute + { + public readonly List<Type> types = new List<Type>(); + + public SerieDataComponentAttribute() + { + } + public SerieDataComponentAttribute(Type type1) + { + AddType(type1); + } + public SerieDataComponentAttribute(Type type1, Type type2) + { + AddType(type1); + AddType(type2); + } + public SerieDataComponentAttribute(Type type1, Type type2, Type type3) + { + AddType(type1); + AddType(type2); + AddType(type3); + } + public SerieDataComponentAttribute(Type type1, Type type2, Type type3, Type type4) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + } + public SerieDataComponentAttribute(Type type1, Type type2, Type type3, Type type4, Type type5) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + AddType(type5); + } + public SerieDataComponentAttribute(Type type1, Type type2, Type type3, Type type4, Type type5, Type type6) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + AddType(type5); + AddType(type6); + } + public SerieDataComponentAttribute(Type type1, Type type2, Type type3, Type type4, Type type5, Type type6, Type type7) + { + AddType(type1); + AddType(type2); + AddType(type3); + AddType(type4); + AddType(type5); + AddType(type6); + AddType(type7); + } + + private void AddType(Type type) + { + if (!SerieData.extraComponentMap.ContainsKey(type)) + throw new ArgumentException("SerieData not support extra component:" + type); + types.Add(type); + } + + public bool Contains<T>() where T : ISerieComponent + { + return Contains(typeof(T)); + } + + public bool Contains(Type type) + { + return types.Contains(type); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs.meta new file mode 100644 index 0000000..43120ee --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataComponentAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a77e2e342c09c4c6b95a0094ad0fcffc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs new file mode 100644 index 0000000..12e2c3f --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class SerieDataExtraFieldAttribute : Attribute + { + public readonly List<string> fields = new List<string>(); + + public SerieDataExtraFieldAttribute() + { } + public SerieDataExtraFieldAttribute(string field1) + { + AddFiled(field1); + } + public SerieDataExtraFieldAttribute(string field1, string field2) + { + AddFiled(field1); + AddFiled(field2); + } + public SerieDataExtraFieldAttribute(string field1, string field2, string field3) + { + AddFiled(field1); + AddFiled(field2); + AddFiled(field3); + } + public SerieDataExtraFieldAttribute(string field1, string field2, string field3, string field4) + { + AddFiled(field1); + AddFiled(field2); + AddFiled(field3); + AddFiled(field4); + } + public SerieDataExtraFieldAttribute(string field1, string field2, string field3, string field4, string field5) + { + AddFiled(field1); + AddFiled(field2); + AddFiled(field3); + AddFiled(field4); + AddFiled(field5); + } + public SerieDataExtraFieldAttribute(string field1, string field2, string field3, string field4, string field5, string field6) + { + AddFiled(field1); + AddFiled(field2); + AddFiled(field3); + AddFiled(field4); + AddFiled(field5); + AddFiled(field6); + } + public SerieDataExtraFieldAttribute(string field1, string field2, string field3, string field4, string field5, string field6, string field7) + { + AddFiled(field1); + AddFiled(field2); + AddFiled(field3); + AddFiled(field4); + AddFiled(field5); + AddFiled(field6); + AddFiled(field7); + } + + private void AddFiled(string field) + { + if (!SerieData.extraFieldList.Contains(field)) + throw new ArgumentException("SerieData not support field:" + field); + fields.Add(field); + } + + public bool Contains(string field) + { + return fields.Contains(field); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs.meta new file mode 100644 index 0000000..216bf90 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieDataExtraFieldAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8b0cc5a1c11e497abb7e32c7d14b25f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs new file mode 100644 index 0000000..1bd21e7 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class SerieHandlerAttribute : Attribute + { + public readonly Type handler; + public readonly bool allowMultiple = true; + + public SerieHandlerAttribute(Type handler) + { + this.handler = handler; + this.allowMultiple = true; + } + public SerieHandlerAttribute(Type handler, bool allowMultiple) + { + this.handler = handler; + this.allowMultiple = allowMultiple; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs.meta new file mode 100644 index 0000000..2dfda00 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SerieHandlerAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 810e22da460074d639f56dd860d9f5d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs b/Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs new file mode 100644 index 0000000..1e3499e --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace XCharts.Runtime +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class Since : Attribute + { + public readonly string version; + + public Since(string version) + { + this.version = version; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs.meta b/Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs.meta new file mode 100644 index 0000000..783b859 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Attributes/SinceAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04c4c3fba4de2404d9c715eeff4a707c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.API.cs b/Assets/XCharts/Runtime/Internal/BaseChart.API.cs new file mode 100644 index 0000000..7c744a2 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.API.cs @@ -0,0 +1,781 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// The base class of all charts. + /// ||所有Chart的基类。 + /// </summary> + public partial class BaseChart + { + /// <summary> + /// The name of chart. + /// ||</summary> + public string chartName + { + get { return m_ChartName; } + set + { + if (!string.IsNullOrEmpty(value) && XChartsMgr.ContainsChart(value)) + { + Debug.LogError("chartName repeated:" + value); + } + else + { + m_ChartName = value; + } + } + } + /// <summary> + /// The theme. + /// ||</summary> + public ThemeStyle theme { get { return m_Theme; } set { m_Theme = value; } } + /// <summary> + /// Global parameter setting component. + /// ||全局设置组件。 + /// </summary> + public Settings settings { get { return m_Settings; } } + /// <summary> + /// The x of chart. + /// ||图表的X + /// </summary> + public float chartX { get { return m_ChartX; } } + /// <summary> + /// The y of chart. + /// ||图表的Y + /// </summary> + public float chartY { get { return m_ChartY; } } + /// <summary> + /// The width of chart. + /// ||图表的宽 + /// </summary> + public float chartWidth { get { return m_ChartWidth; } } + /// <summary> + /// The height of chart. + /// ||图表的高 + /// </summary> + public float chartHeight { get { return m_ChartHeight; } } + public Vector2 chartMinAnchor { get { return m_ChartMinAnchor; } } + public Vector2 chartMaxAnchor { get { return m_ChartMaxAnchor; } } + public Vector2 chartPivot { get { return m_ChartPivot; } } + public Vector2 chartSizeDelta { get { return m_ChartSizeDelta; } } + /// <summary> + /// The position of chart. + /// ||图表的左下角起始坐标。 + /// </summary> + public Vector3 chartPosition { get { return m_ChartPosition; } } + public Rect chartRect { get { return m_ChartRect; } } + /// <summary> + /// The callback function of chart init. + /// ||图表的初始化完成回调。 + /// </summary> + public Action onInit { set { m_OnInit = value; } } + /// <summary> + /// The callback function of chart update. + /// ||图表的Update回调。 + /// </summary> + public Action onUpdate { set { m_OnUpdate = value; } } + /// <summary> + /// 自定义绘制回调。在绘制Serie前调用。 + /// </summary> + public Action<VertexHelper> onDraw { set { m_OnDrawBase = value; } } + /// <summary> + /// 自定义Serie绘制回调。在每个Serie绘制完前调用。 + /// </summary> + public Action<VertexHelper, Serie> onDrawBeforeSerie { set { m_OnDrawSerieBefore = value; } } + /// <summary> + /// 自定义Serie绘制回调。在每个Serie绘制完后调用。 + /// </summary> + public Action<VertexHelper, Serie> onDrawAfterSerie { set { m_OnDrawSerieAfter = value; } } + /// <summary> + /// 自定义Upper层绘制回调。在绘制Tooltip前调用。 + /// </summary> + public Action<VertexHelper> onDrawUpper { set { m_OnDrawUpper = value; } } + /// <summary> + /// 自定义Top层绘制回调。在绘制Tooltip前调用。 + /// </summary> + public Action<VertexHelper> onDrawTop { set { m_OnDrawTop = value; } } + /// <summary> + /// 自定义仪表盘指针绘制委托。 + /// </summary> + public CustomDrawGaugePointerFunction customDrawGaugePointerFunction { set { m_CustomDrawGaugePointerFunction = value; } get { return m_CustomDrawGaugePointerFunction; } } + /// <summary> + /// the callback function of pointer click serie. + /// ||鼠标点击Serie回调。 + /// </summary> + [Since("v3.6.0")] + public Action<SerieEventData> onSerieClick { set { m_OnSerieClick = value; m_ForceOpenRaycastTarget = true; } get { return m_OnSerieClick; } } + /// <summary> + /// the callback function of pointer down serie. + /// ||鼠标按下Serie回调。 + /// </summary> + [Since("v3.6.0")] + public Action<SerieEventData> onSerieDown { set { m_OnSerieDown = value; m_ForceOpenRaycastTarget = true; } get { return m_OnSerieDown; } } + /// <summary> + /// the callback function of pointer enter serie. + /// ||鼠标进入Serie回调。 + /// </summary> + [Since("v3.6.0")] + public Action<SerieEventData> onSerieEnter { set { m_OnSerieEnter = value; m_ForceOpenRaycastTarget = true; } get { return m_OnSerieEnter; } } + /// <summary> + /// the callback function of pointer exit serie. + /// ||鼠标离开Serie回调。 + /// </summary> + [Since("v3.6.0")] + public Action<SerieEventData> onSerieExit { set { m_OnSerieExit = value; m_ForceOpenRaycastTarget = true; } get { return m_OnSerieExit; } } + /// <summary> + /// the callback function of pointer click pie area. + /// ||点击饼图区域回调。参数:PointerEventData,SerieIndex,SerieDataIndex + /// </summary> + [Obsolete("Use \"onSerieClick\" instead", true)] + public Action<PointerEventData, int, int> onPointerClickPie { get; set; } + /// <summary> + /// the callback function of pointer enter pie area. + /// ||鼠标进入和离开饼图区域回调,SerieDataIndex为-1时表示离开。参数:PointerEventData,SerieIndex,SerieDataIndex + /// </summary> + [Since("v3.3.0")] + [Obsolete("Use \"onSerieEnter\" instead", true)] + public Action<int, int> onPointerEnterPie { set { m_OnPointerEnterPie = value; m_ForceOpenRaycastTarget = true; } get { return m_OnPointerEnterPie; } } + /// <summary> + /// the callback function of click bar. + /// ||点击柱形图柱条回调。参数:eventData, dataIndex + /// </summary> + [Obsolete("Use \"onSerieClick\" instead", true)] + public Action<PointerEventData, int> onPointerClickBar { get; set; } + /// <summary> + /// 坐标轴变更数据索引时回调。参数:axis, dataIndex/dataValue + /// </summary> + public Action<Axis, double> onAxisPointerValueChanged { set { m_OnAxisPointerValueChanged = value; } get { return m_OnAxisPointerValueChanged; } } + /// <summary> + /// the callback function of click legend. + /// ||点击图例按钮回调。参数:legendIndex, legendName, show + /// </summary> + public Action<Legend, int, string, bool> onLegendClick { set { m_OnLegendClick = value; } internal get { return m_OnLegendClick; } } + /// <summary> + /// the callback function of enter legend. + /// ||鼠标进入图例回调。参数:legendIndex, legendName + /// </summary> + public Action<Legend, int, string> onLegendEnter { set { m_OnLegendEnter = value; } internal get { return m_OnLegendEnter; } } + /// <summary> + /// the callback function of exit legend. + /// ||鼠标退出图例回调。参数:legendIndex, legendName + /// </summary> + public Action<Legend, int, string> onLegendExit { set { m_OnLegendExit = value; } internal get { return m_OnLegendExit; } } + + public void Init(bool defaultChart = true) + { + if (defaultChart) + { + OnInit(); + DefaultChart(); + } + else + { + OnBeforeSerialize(); + } + } + + /// <summary> + /// Redraw chart in next frame. + /// ||在下一帧刷新整个图表。 + /// </summary> + public void RefreshChart() + { + m_RefreshChart = true; + if (m_Painter) m_Painter.Refresh(); + foreach (var painter in m_PainterList) painter.Refresh(); + if (m_PainterUpper) m_PainterUpper.Refresh(); + if (m_PainterTop) m_PainterTop.Refresh(); + } + + public override void RefreshGraph() + { + RefreshChart(); + } + + /// <summary> + /// Redraw chart serie in next frame. + /// ||在下一帧刷新图表的指定serie。 + /// </summary> + public void RefreshChart(int serieIndex) + { + RefreshPainter(GetSerie(serieIndex)); + } + + /// <summary> + /// Redraw chart serie in next frame. + /// ||在下一帧刷新图表的指定serie。 + /// </summary> + public void RefreshChart(Serie serie) + { + if (serie == null) return; + // serie.ResetInteract(); + RefreshPainter(serie); + } + + /// <summary> + /// Clear all components and series data. Note: serie only empties the data and does not remove serie. + /// ||清空所有组件和Serie的数据。注意:Serie只是清空数据,不会移除Serie。 + /// </summary> + public virtual void ClearData() + { + ClearSerieData(); + ClearSerieLinks(); + ClearComponentData(); + } + + /// <summary> + /// Clear the data of all series. + /// ||清空所有serie的数据。 + /// </summary> + [Since("v3.4.0")] + public virtual void ClearSerieData() + { + foreach (var serie in m_Series) + serie.ClearData(); + m_CheckAnimation = false; + RefreshChart(); + } + + /// <summary> + /// Clear the link data of all series. + /// ||清空所有serie的link数据。 + /// </summary> + [Since("v3.10.0")] + public virtual void ClearSerieLinks() + { + foreach (var serie in m_Series) + serie.ClearLinks(); + m_CheckAnimation = false; + RefreshChart(); + } + + /// <summary> + /// Clear the data of all components. + /// ||清空所有组件的数据。 + /// </summary> + [Since("v3.4.0")] + public virtual void ClearComponentData() + { + foreach (var component in m_Components) + component.ClearData(); + m_CheckAnimation = false; + RefreshChart(); + } + + /// <summary> + /// Empty all component data and remove all series. Use the chart again and again to tell the truth. + /// Note: The component only clears the data part, and the parameters are retained and not reset. + /// ||清空所有组件数据,并移除所有Serie。一般在图表重新初始化时使用。 + /// 注意:组件只清空数据部分,参数会保留不会被重置。 + /// </summary> + public virtual void RemoveData() + { + foreach (var component in m_Components) + component.ClearData(); + m_Series.Clear(); + m_SerieHandlers.Clear(); + m_CheckAnimation = false; + RefreshChart(); + } + + /// <summary> + /// Remove all of them Serie. This interface is used when Serie needs to be removed only, and RemoveData() is generally used in other cases. + /// ||移除所有的Serie。当确认只需要移除Serie时使用该接口,其他情况下一般用RemoveData()。 + /// </summary> + [Since("v3.2.0")] + public virtual void RemoveAllSerie() + { + m_Series.Clear(); + m_SerieHandlers.Clear(); + m_CheckAnimation = false; + RefreshChart(); + } + + /// <summary> + /// Remove legend and serie by name. + /// ||清除指定系列名称的数据。 + /// </summary> + /// <param name="serieName">the name of serie</param> + public virtual void RemoveData(string serieName) + { + RemoveSerie(serieName); + foreach (var component in m_Components) + { + if (component is Legend) + { + var legend = component as Legend; + legend.RemoveData(serieName); + } + } + RefreshChart(); + } + + public virtual void UpdateLegendColor(string legendName, bool active) + { + var legendIndex = m_LegendRealShowName.IndexOf(legendName); + if (legendIndex >= 0) + { + foreach (var component in m_Components) + { + if (component is Legend) + { + var legend = component as Legend; + var iconColor = LegendHelper.GetIconColor(this, legend, legendIndex, legendName, active); + var contentColor = LegendHelper.GetContentColor(this, legendIndex, legendName, legend, m_Theme, active); + legend.UpdateButtonColor(legendName, iconColor); + legend.UpdateContentColor(legendName, contentColor); + } + } + } + } + + /// <summary> + /// Whether serie is activated. + /// ||获得指定图例名字的系列是否显示。 + /// </summary> + /// <param name="legendName"></param> + /// <returns></returns> + public virtual bool IsActiveByLegend(string legendName) + { + foreach (var serie in m_Series) + { + if (serie.show && legendName.Equals(serie.serieName)) + { + return true; + } + else + { + foreach (var serieData in serie.data) + { + if (serieData.show && legendName.Equals(serieData.name)) + { + return true; + } + } + } + + } + return false; + } + + /// <summary> + /// Update chart theme. + /// ||切换内置主题。 + /// </summary> + /// <param name="theme">theme</param> + public bool UpdateTheme(ThemeType theme) + { + if (theme == ThemeType.Custom) + { + Debug.LogError("UpdateTheme: not support switch to Custom theme."); + return false; + } + if (m_Theme.sharedTheme == null) + m_Theme.sharedTheme = XCThemeMgr.GetTheme(ThemeType.Default); + m_Theme.sharedTheme.CopyTheme(theme); + return true; + } + + /// <summary> + /// Update chart theme info. + /// ||切换图表主题。 + /// </summary> + /// <param name="theme">theme</param> + public void UpdateTheme(Theme theme) + { + m_Theme.sharedTheme = theme; + SetAllComponentDirty(); +#if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty(this); +#endif + } + + /// <summary> + /// Whether enable serie animations. + /// ||是否启用Serie动画。 + /// </summary> + /// <param name="flag"></param> + public void AnimationEnable(bool flag) + { + foreach (var serie in m_Series) serie.AnimationEnable(flag); + } + + /// <summary> + /// Start all serie fadein animations. + /// ||开始所有Serie的渐入动画。 + /// </summary> + /// <param name="reset">reset animation</param> + public void AnimationFadeIn(bool reset = true) + { + if (reset) AnimationReset(); + foreach (var serie in m_Series) serie.AnimationFadeIn(); + } + + /// <summary> + /// Start all serie fadeout animations. + /// ||开始所有Serie的渐出动画。 + /// </summary> + public void AnimationFadeOut() + { + foreach (var serie in m_Series) serie.AnimationFadeOut(); + } + + /// <summary> + /// Pause all animations. + /// ||暂停所有Serie的动画。 + /// </summary> + public void AnimationPause() + { + foreach (var serie in m_Series) serie.AnimationPause(); + } + + /// <summary> + /// Resume all animations. + /// ||继续所有Serie的动画。 + /// </summary> + public void AnimationResume() + { + foreach (var serie in m_Series) serie.AnimationResume(); + } + + /// <summary> + /// Reset all animations. + /// ||重置所有Serie的动画。 + /// </summary> + public void AnimationReset() + { + foreach (var serie in m_Series) serie.AnimationReset(); + } + + /// <summary> + /// 点击图例按钮 + /// </summary> + /// <param name="legendIndex">图例按钮索引</param> + /// <param name="legendName">图例按钮名称</param> + /// <param name="show">显示还是隐藏</param> + public void ClickLegendButton(int legendIndex, string legendName, bool show) + { + OnLegendButtonClick(legendIndex, legendName, show); + RefreshChart(); + } + + /// <summary> + /// 坐标是否在图表范围内 + /// </summary> + /// <param name="local"></param> + /// <returns></returns> + public bool IsInChart(Vector2 local) + { + return IsInChart(local.x, local.y); + } + + public bool IsInChart(float x, float y) + { + if (x < m_ChartX || x > m_ChartX + m_ChartWidth || + y < m_ChartY || y > m_ChartY + m_ChartHeight) + { + return false; + } + return true; + } + + public void ClampInChart(ref Vector3 pos) + { + if (!IsInChart(pos.x, pos.y)) + { + if (pos.x < m_ChartX) pos.x = m_ChartX; + if (pos.x > m_ChartX + m_ChartWidth) pos.x = m_ChartX + m_ChartWidth; + if (pos.y < m_ChartY) pos.y = m_ChartY; + if (pos.y > m_ChartY + m_ChartHeight) pos.y = m_ChartY + m_ChartHeight; + } + } + + public Vector3 ClampInGrid(GridCoord grid, Vector3 pos) + { + if (grid.Contains(pos)) return pos; + else + { + // var pos = new Vector3(pos.x, pos.y); + if (pos.x < grid.context.x) pos.x = grid.context.x; + if (pos.x > grid.context.x + grid.context.width) pos.x = grid.context.x + grid.context.width; + if (pos.y < grid.context.y) pos.y = grid.context.y; + if (pos.y > grid.context.y + grid.context.height) pos.y = grid.context.y + grid.context.height; + return pos; + } + } + + /// <summary> + /// 转换X轴和Y轴的配置 + /// </summary> + /// <param name="index">坐标轴索引,0或1</param> + public void ConvertXYAxis(int index) + { + List<MainComponent> m_XAxes; + List<MainComponent> m_YAxes; + m_ComponentMaps.TryGetValue(typeof(XAxis), out m_XAxes); + m_ComponentMaps.TryGetValue(typeof(YAxis), out m_YAxes); + if (index >= 0 && index <= 1) + { + var xAxis = m_XAxes[index] as XAxis; + var yAxis = m_YAxes[index] as YAxis; + var tempX = xAxis.Clone(); + xAxis.Copy(yAxis); + yAxis.Copy(tempX); + xAxis.context.offset = 0; + yAxis.context.offset = 0; + xAxis.context.minValue = 0; + xAxis.context.maxValue = 0; + yAxis.context.minValue = 0; + yAxis.context.maxValue = 0; + ResetChartStatus(); + RefreshChart(); + } + } + + /// <summary> + /// 在下一帧刷新DataZoom + /// </summary> + public void RefreshDataZoom() + { + foreach (var handler in m_ComponentHandlers) + { + if (handler is DataZoomHandler) + { + (handler as DataZoomHandler).RefreshDataZoomLabel(); + } + } + } + + /// <summary> + /// 设置可缓存的最大数据量。当数据量超过该值时,会自动删除第一个值再加入最新值。 + /// </summary> + public void SetMaxCache(int maxCache) + { + foreach (var serie in m_Series) + serie.maxCache = maxCache; + foreach (var component in m_Components) + { + if (component is Axis) + { + (component as Axis).maxCache = maxCache; + } + } + } + + /// <summary> + /// set insert data to head. + /// ||设置数据插入到头部。 + /// </summary> + /// <param name="insertDataToHead"></param> + [Since("v3.11.0")] + public void SetInsertDataToHead(bool insertDataToHead) + { + foreach (var serie in m_Series) + serie.insertDataToHead = insertDataToHead; + + var coms = GetChartComponents<XAxis>(); + foreach (var com in coms) + { + var axis = com as XAxis; + if (axis.type == Axis.AxisType.Category) + axis.insertDataToHead = insertDataToHead; + } + } + + public Vector3 GetTitlePosition(Title title) + { + return chartPosition + title.location.GetPosition(chartWidth, chartHeight); + } + + public int GetLegendRealShowNameIndex(string name) + { + return m_LegendRealShowName.IndexOf(name); + } + + public Color32 GetLegendRealShowNameColor(string name) + { + var index = GetLegendRealShowNameIndex(name); + return theme.GetColor(index); + } + + /// <summary> + /// 设置Base Painter的材质球 + /// </summary> + /// <param name="material"></param> + public void SetBasePainterMaterial(Material material) + { + settings.basePainterMaterial = material; + if (m_Painter != null) + { + m_Painter.material = material; + } + } + + /// <summary> + /// 设置Serie Painter的材质球 + /// </summary> + /// <param name="material"></param> + public void SetSeriePainterMaterial(Material material) + { + settings.basePainterMaterial = material; + if (m_PainterList != null) + { + foreach (var painter in m_PainterList) + painter.material = material; + } + } + + /// <summary> + /// 设置Upper Painter的材质球 + /// </summary> + /// <param name="material"></param> + public void SetUpperPainterMaterial(Material material) + { + settings.upperPainterMaterial = material; + if (m_PainterUpper != null) + { + m_PainterUpper.material = material; + } + } + + /// <summary> + /// 设置Top Painter的材质球 + /// </summary> + /// <param name="material"></param> + public void SetTopPainterMaterial(Material material) + { + settings.topPainterMaterial = material; + if (m_PainterTop != null) + { + m_PainterTop.material = material; + } + } + + private Background m_Background; + public Color32 GetChartBackgroundColor() + { + if (m_Background == null) m_Background = GetChartComponent<Background>(); + //var background = GetChartComponent<Background>(); + return theme.GetBackgroundColor(m_Background); + } + + /// <summary> + /// 获得Serie的标识颜色。 + /// </summary> + /// <param name="serie"></param> + /// <param name="serieData"></param> + /// <returns></returns> + [Since("v3.4.0")] + public Color32 GetMarkColor(Serie serie, SerieData serieData) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (ChartHelper.IsClearColor(itemStyle.markColor)) + { + return GetItemColor(serie, serieData); + } + else + { + return itemStyle.markColor; + } + } + + public Color32 GetItemColor(Serie serie, SerieData serieData) + { + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, m_Theme); + return color; + } + + public Color32 GetItemColor(Serie serie, SerieData serieData, int colorIndex) + { + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, m_Theme, colorIndex); + return color; + } + + public Color32 GetItemColor(Serie serie) + { + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, serie, null, m_Theme); + return color; + } + + /// <summary> + /// trigger tooltip by data index. + /// ||尝试触发指定数据项的Tooltip. + /// </summary> + /// <param name="dataIndex">数据项索引</param> + /// <param name="serieIndex">Serie索引,默认为第0个Serie</param> + /// <returns></returns> + [Since("v3.7.0")] + public bool TriggerTooltip(int dataIndex, int serieIndex = 0) + { + var serie = GetSerie(serieIndex); + if (serie == null) return false; + var dataPoints = serie.context.dataPoints; + var dataPoint = Vector3.zero; + if (dataPoints.Count == 0) + { + if (serie.dataCount == 0) return false; + dataIndex = dataIndex % serie.dataCount; + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) return false; + dataPoint = serie.GetSerieData(dataIndex).context.position; + } + else + { + dataIndex = dataIndex % dataPoints.Count; + dataPoint = dataPoints[dataIndex]; + } + return TriggerTooltip(dataPoint); + } + + /// <summary> + /// trigger tooltip by chart local position. + /// ||在指定的位置尝试触发Tooltip. + /// </summary> + /// <param name="localPosition"></param> + /// <returns></returns> + [Since("v3.7.0")] + public bool TriggerTooltip(Vector3 localPosition) + { + var screenPoint = LocalPointToScreenPoint(localPosition); + var eventData = new PointerEventData(EventSystem.current); + eventData.position = screenPoint; + OnPointerEnter(eventData); + return true; + } + + /// <summary> + /// cancel tooltip. + /// ||取消Tooltip. + /// </summary> + [Since("v3.7.0")] + public void CancelTooltip() + { + pointerMoveEventData = null; + pointerClickEventData = null; + var tooltip = GetChartComponent<Tooltip>(); + if (tooltip != null) + { + tooltip.SetActive(false); + } + } + + /// <summary> + /// reset chart status. When some parameters are set, due to the animation effect, the chart status may not be correct. + /// ||重置图表状态。当设置某些参数后,由于动画影响,可能导致图表状态不正确,此时可以调用该接口重置图表状态。 + /// </summary> + [Since("v3.10.0")] + public void ResetChartStatus() + { + foreach (var component in m_Components) component.ResetStatus(); + foreach (var handler in m_SerieHandlers) handler.ForceUpdateSerieContext(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.API.cs.meta b/Assets/XCharts/Runtime/Internal/BaseChart.API.cs.meta new file mode 100644 index 0000000..fcc2101 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.API.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62d2f81e569a4477aab2091dc0b8dba7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Component.cs b/Assets/XCharts/Runtime/Internal/BaseChart.Component.cs new file mode 100644 index 0000000..c4555cf --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Component.cs @@ -0,0 +1,500 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public partial class BaseChart + { + public bool TryAddChartComponent<T>() where T : MainComponent + { + return TryAddChartComponent(typeof(T)); + } + + public bool TryAddChartComponent(Type type) + { + if (CanAddChartComponent(type)) + { + AddChartComponent(type); + return true; + } + else + { + return false; + } + } + + public bool TryAddChartComponent<T>(out T component) where T : MainComponent + { + var type = typeof(T); + if (CanAddChartComponent(type)) + { + component = AddChartComponent(type) as T; + return true; + } + else + { + component = null; + return false; + } + } + + public T AddChartComponent<T>() where T : MainComponent + { + return (T)AddChartComponent(typeof(T)); + } + + public T AddChartComponentWhenNoExist<T>() where T : MainComponent + { + if (HasChartComponent<T>()) return null; + return AddChartComponent<T>(); + } + + public MainComponent AddChartComponent(Type type) + { + InitListForFieldInfos(); + if (!CanAddChartComponent(type)) + { + Debug.LogError("XCharts ERROR: CanAddChartComponent:" + type.Name); + return null; + } + CheckAddRequireChartComponent(type); + var component = Activator.CreateInstance(type) as MainComponent; + if (component == null) + { + Debug.LogError("XCharts ERROR: CanAddChartComponent:" + type.Name); + return null; + } + component.SetDefaultValue(); + if (component is IUpdateRuntimeData) + (component as IUpdateRuntimeData).UpdateRuntimeData(this); + AddComponent(component); + m_Components.Sort(); + CreateComponentHandler(component); +#if UNITY_EDITOR && UNITY_2019_1_OR_NEWER + UnityEditor.EditorUtility.SetDirty(this); + OnBeforeSerialize(); +#endif + return component; + } + + private void AddComponent(MainComponent component) + { + var type = component.GetType(); + m_Components.Add(component); + List<MainComponent> list; + if (!m_ComponentMaps.TryGetValue(type, out list)) + { + list = new List<MainComponent>(); + m_ComponentMaps[type] = list; + } + component.index = list.Count; + list.Add(component); + m_Components.Sort((a, b) => { return a.GetType().Name.CompareTo(b.GetType().Name); }); + } + + private void CheckAddRequireChartComponent(Type type) + { + if (Attribute.IsDefined(type, typeof(RequireChartComponentAttribute))) + { + foreach (var obj in type.GetCustomAttributes(typeof(RequireChartComponentAttribute), false)) + { + var attribute = obj as RequireChartComponentAttribute; + if (attribute.type0 != null && !HasChartComponent(attribute.type0)) + AddChartComponent(attribute.type0); + if (attribute.type1 != null && !HasChartComponent(attribute.type1)) + AddChartComponent(attribute.type1); + if (attribute.type2 != null && !HasChartComponent(attribute.type2)) + AddChartComponent(attribute.type2); + } + } + } + + private void CreateComponentHandler(MainComponent component) + { + if (!component.GetType().IsDefined(typeof(ComponentHandlerAttribute), false)) + { + Debug.LogError("MainComponent no Handler:" + component.GetType()); + return; + } + var attrubte = component.GetType().GetAttribute<ComponentHandlerAttribute>(); + if (attrubte.handler == null) + return; + + var handler = (MainComponentHandler)Activator.CreateInstance(attrubte.handler); + handler.attribute = attrubte; + handler.chart = this; + handler.order = attrubte.order; + handler.SetComponent(component); + component.handler = handler; + m_ComponentHandlers.Add(handler); + m_ComponentHandlers.Sort((a, b) => { return a.order.CompareTo(b.order); }); + } + + public bool RemoveChartComponent<T>(int index = 0) + where T : MainComponent + { + return RemoveChartComponent(typeof(T), index); + } + + public int RemoveChartComponents<T>() + where T : MainComponent + { + return RemoveChartComponents(typeof(T)); + } + + public void RemoveAllChartComponent() + { + m_Components.Clear(); + InitComponentHandlers(); + } + + public bool RemoveChartComponent(Type type, int index = 0) + { + MainComponent toRemove = null; + for (int i = 0; i < m_Components.Count; i++) + { + if (m_Components[i].GetType() == type && m_Components[i].index == index) + { + toRemove = m_Components[i]; + break; + } + } + return RemoveChartComponent(toRemove); + } + + public int RemoveChartComponents(Type type) + { + int count = 0; + for (int i = m_Components.Count - 1; i > 0; i--) + { + if (m_Components[i].GetType() == type) + { + RemoveChartComponent(m_Components[i]); + count++; + } + } + return count; + } + + public bool RemoveChartComponent(MainComponent component) + { + if (component == null) return false; + if (m_Components.Remove(component)) + { + if (component.gameObject != null) + ChartHelper.SetActive(component.gameObject, false); +#if UNITY_EDITOR && UNITY_2019_1_OR_NEWER + UnityEditor.EditorUtility.SetDirty(this); + OnBeforeSerialize(); +#endif + InitComponentHandlers(); + RefreshChart(); + return true; + } + return false; + } + + public bool CanAddChartComponent(Type type) + { + if (!type.IsSubclassOf(typeof(MainComponent))) return false; + if (!m_TypeListForComponent.ContainsKey(type)) return false; + if (CanMultipleComponent(type)) return !HasChartComponent(type); + else return true; + } + + public bool HasChartComponent<T>() + where T : MainComponent + { + return HasChartComponent(typeof(T)); + } + + public bool HasChartComponent(Type type) + { + foreach (var component in m_Components) + { + if (component == null) continue; + if (component.GetType() == type) + return true; + } + return false; + } + + public bool CanMultipleComponent(Type type) + { + return Attribute.IsDefined(type, typeof(DisallowMultipleComponent)); + } + + public int GetChartComponentNum<T>() where T : MainComponent + { + return GetChartComponentNum(typeof(T)); + } + + private static List<MainComponent> list; + public int GetChartComponentNum(Type type) + { + if (m_ComponentMaps.TryGetValue(type, out list)) + return list.Count; + else + return 0; + } + + public T GetChartComponent<T>(int index = 0) where T : MainComponent + { + foreach (var component in m_Components) + { + if (component is T && component.index == index) + return component as T; + } + return null; + } + + public List<MainComponent> GetChartComponents<T>() where T : MainComponent + { + var type = typeof(T); + if (m_ComponentMaps.ContainsKey(type)) + return m_ComponentMaps[type]; + else + return null; + } + + [Obsolete("'GetOrAddChartComponent' is obsolete, Use 'EnsureChartComponent' instead.")] + public T GetOrAddChartComponent<T>() where T : MainComponent + { + var component = GetChartComponent<T>(); + if (component == null) + return AddChartComponent<T>(); + else + return component; + } + + /// <summary> + /// Ensure the chart has the component, if not, add it. + /// Note: it may fail to add. + /// ||确保图表有该组件,如果没有则添加。注意:有可能添加不成功。 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>component, or null if add failed.</returns> + [Since("v3.6.0")] + public T EnsureChartComponent<T>() where T : MainComponent + { + var component = GetChartComponent<T>(); + if (component == null) + return AddChartComponent<T>(); + else + return component; + } + + public bool TryGetChartComponent<T>(out T component, int index = 0) + where T : MainComponent + { + component = null; + foreach (var com in m_Components) + { + if (com is T && com.index == index) + { + component = (T)com; + return true; + } + } + return false; + } + public GridCoord GetGrid(Vector2 local) + { + List<MainComponent> list; + if (m_ComponentMaps.TryGetValue(typeof(GridCoord), out list)) + { + foreach (var component in list) + { + var grid = component as GridCoord; + if (grid.Contains(local)) return grid; + } + } + return null; + } + + public GridCoord GetGridOfDataZoom(DataZoom dataZoom) + { + GridCoord grid = null; + if (dataZoom.xAxisIndexs != null && dataZoom.xAxisIndexs.Count > 0) + { + for (int i = 0; i < dataZoom.xAxisIndexs.Count; i++) + { + var xAxis = GetChartComponent<XAxis>(dataZoom.xAxisIndexs[i]); + var tempGrid = GetChartComponent<GridCoord>(xAxis.gridIndex); + if (tempGrid.IsPointerEnter()) + { + grid = tempGrid; + break; + } + } + } + else if (dataZoom.yAxisIndexs != null && dataZoom.yAxisIndexs.Count > 0) + { + for (int i = 0; i < dataZoom.yAxisIndexs.Count; i++) + { + var yAxis = GetChartComponent<YAxis>(dataZoom.yAxisIndexs[i]); + var tempGrid = GetChartComponent<GridCoord>(yAxis.gridIndex); + if (tempGrid.IsPointerEnter()) + { + grid = tempGrid; + break; + } + } + } + if (grid == null) return GetChartComponent<GridCoord>(); + else return grid; + } + + public DataZoom GetDataZoomOfAxis(Axis axis) + { + foreach (var component in m_Components) + { + if (component is DataZoom) + { + var dataZoom = component as DataZoom; + if (!dataZoom.enable) continue; + if (dataZoom.IsContainsAxis(axis)) return dataZoom; + } + } + return null; + } + + public VisualMap GetVisualMapOfSerie(Serie serie) + { + foreach (var component in m_Components) + { + if (component is VisualMap) + { + var visualMap = component as VisualMap; + if (visualMap.serieIndex == serie.index) return visualMap; + } + } + return null; + } + + public void GetDataZoomOfSerie(Serie serie, out DataZoom xDataZoom, out DataZoom yDataZoom) + { + xDataZoom = null; + yDataZoom = null; + if (serie == null) return; + foreach (var component in m_Components) + { + if (component is DataZoom) + { + var dataZoom = component as DataZoom; + if (!dataZoom.enable) continue; + if (dataZoom.IsContainsXAxis(serie.xAxisIndex)) + { + xDataZoom = dataZoom; + } + if (dataZoom.IsContainsYAxis(serie.yAxisIndex)) + { + yDataZoom = dataZoom; + } + } + } + } + + public DataZoom GetXDataZoomOfSerie(Serie serie) + { + if (serie == null) return null; + foreach (var component in m_Components) + { + if (component is DataZoom) + { + var dataZoom = component as DataZoom; + if (!dataZoom.enable) continue; + if (dataZoom.IsContainsXAxis(serie.xAxisIndex)) + return dataZoom; + } + } + return null; + } + + /// <summary> + /// reutrn true when all the show axis is `Value` type. + /// ||纯数值坐标轴(数值轴或对数轴)。 + /// </summary> + public bool IsAllAxisValue() + { + foreach (var component in m_Components) + { + if (component is Axis) + { + var axis = component as Axis; + if (axis.show && !axis.IsValue() && !axis.IsLog() && !axis.IsTime()) return false; + } + } + return true; + } + + /// <summary> + /// 纯类目轴。 + /// </summary> + public bool IsAllAxisCategory() + { + foreach (var component in m_Components) + { + if (component is Axis) + { + var axis = component as Axis; + if (axis.show && !axis.IsCategory()) return false; + } + } + return true; + } + + public bool IsInAnyGrid(Vector2 local) + { + List<MainComponent> list; + if (m_ComponentMaps.TryGetValue(typeof(GridCoord), out list)) + { + foreach (var grid in list) + { + if ((grid as GridCoord).Contains(local)) return true; + } + } + return false; + } + + internal string GetTooltipCategory(Serie serie) + { + var xAxis = GetChartComponent<XAxis>(serie.xAxisIndex); + var yAxis = GetChartComponent<YAxis>(serie.yAxisIndex); + if (yAxis.IsCategory()) + { + return yAxis.GetData(serie.context.pointerItemDataIndex); + } + else if (xAxis.IsCategory()) + { + return xAxis.GetData(serie.context.pointerItemDataIndex); + } + return null; + } + + internal bool GetSerieGridCoordAxis(Serie serie, out Axis axis, out Axis relativedAxis) + { + var yAxis = GetChartComponent<YAxis>(serie.yAxisIndex); + var xAxis = GetChartComponent<XAxis>(serie.xAxisIndex); + if (xAxis == null || yAxis == null) + { + axis = null; + relativedAxis = null; + return false; + } + var isY = yAxis.IsCategory() && !xAxis.IsCategory(); + if (isY) + { + axis = yAxis; + relativedAxis = GetChartComponent<XAxis>(serie.xAxisIndex); + } + else + { + axis = GetChartComponent<XAxis>(serie.xAxisIndex); + relativedAxis = yAxis; + } + return isY; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Component.cs.meta b/Assets/XCharts/Runtime/Internal/BaseChart.Component.cs.meta new file mode 100644 index 0000000..430d2a9 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Component.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: abbf9c9160e2c45c4a873a7da09672be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs b/Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs new file mode 100644 index 0000000..675cc0f --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public partial class BaseChart + { + public virtual void InitAxisRuntimeData(Axis axis) { } + + public virtual void GetSeriesMinMaxValue(Axis axis, int axisIndex, out double tempMinValue, out double tempMaxValue) + { + var needAnimationData = !axis.context.needAnimation; + if (axis is XAxis3D) + { + SeriesHelper.GetXMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData); + } + else if (axis is ZAxis3D) + { + SeriesHelper.GetZMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData); + } + else if (axis is YAxis3D) + { + SeriesHelper.GetYMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData); + } + else if (IsAllAxisValue()) + { + if (axis is XAxis) + { + SeriesHelper.GetXMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData); + } + else + { + SeriesHelper.GetYMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData); + } + } + else + { + SeriesHelper.GetYMinMaxValue(this, axisIndex, axis.inverse, out tempMinValue, out tempMaxValue, false, false, needAnimationData); + } + AxisHelper.AdjustMinMaxValue(axis, ref tempMinValue, ref tempMaxValue, true); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs.meta b/Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs.meta new file mode 100644 index 0000000..4d9b1cc --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Custom.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae62083fadc854bcc8c8312f84c6d166 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs b/Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs new file mode 100644 index 0000000..aef6238 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs @@ -0,0 +1,134 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + public partial class BaseChart + { + public void DrawClipPolygon(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, + Color32 color, bool clip, GridCoord grid) + { + DrawClipPolygon(vh, p1, p2, p3, p4, color, color, clip, grid); + } + + public void DrawClipPolygon(VertexHelper vh, Vector3 p, float radius, Color32 color, + bool clip, bool vertical, GridCoord grid) + { + if (!IsInChart(p)) return; + if (!clip || (clip && (grid.Contains(p)))) + UGL.DrawSquare(vh, p, radius, color); + } + + public void DrawClipPolygon(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, + Color32 startColor, Color32 toColor, bool clip, GridCoord grid) + { + ClampInChart(ref p1); + ClampInChart(ref p2); + ClampInChart(ref p3); + ClampInChart(ref p4); + if (clip) + { + p1 = ClampInGrid(grid, p1); + p2 = ClampInGrid(grid, p2); + p3 = ClampInGrid(grid, p3); + p4 = ClampInGrid(grid, p4); + } + if (!clip || (clip && (grid.Contains(p1) && grid.Contains(p2) && grid.Contains(p3) && + grid.Contains(p4)))) + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, startColor, toColor); + } + + public void DrawClipPolygon(VertexHelper vh, ref Vector3 p1, ref Vector3 p2, ref Vector3 p3, ref Vector3 p4, + Color32 startColor, Color32 toColor, bool clip, GridCoord grid) + { + ClampInChart(ref p1); + ClampInChart(ref p2); + ClampInChart(ref p3); + ClampInChart(ref p4); + if (clip) + { + p1 = ClampInGrid(grid, p1); + p2 = ClampInGrid(grid, p2); + p3 = ClampInGrid(grid, p3); + p4 = ClampInGrid(grid, p4); + } + if (!clip || + (clip && grid.Contains(p1) && grid.Contains(p2) && grid.Contains(p3) && + grid.Contains(p4))) + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, startColor, toColor); + } + + public void DrawClipTriangle(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Color32 color, + bool clip, GridCoord grid) + { + DrawClipTriangle(vh, p1, p2, p3, color, color, color, clip, grid); + } + + public void DrawClipTriangle(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Color32 color, + Color32 color2, Color32 color3, bool clip, GridCoord grid) + { + if (!IsInChart(p1) || !IsInChart(p2) || !IsInChart(p3)) return; + if (!clip || (clip && (grid.Contains(p1) || grid.Contains(p2) || grid.Contains(p3)))) + UGL.DrawTriangle(vh, p1, p2, p3, color, color2, color3); + } + + public void DrawClipLine(VertexHelper vh, Vector3 p1, Vector3 p2, float size, Color32 color, + bool clip, GridCoord grid) + { + if (!IsInChart(p1) || !IsInChart(p2)) return; + if (!clip || (clip && (grid.Contains(p1) || grid.Contains(p2)))) + UGL.DrawLine(vh, p1, p2, size, color); + } + + public void DrawClipSymbol(VertexHelper vh, SymbolType type, float symbolSize, float tickness, + Vector3 pos, Color32 color, Color32 toColor, Color32 emptyColor, Color32 borderColor, float gap, + bool clip, float[] cornerRadius, GridCoord grid, Vector3 startPos, float symbolSize2 = 0) + { + if (!IsInChart(pos)) return; + if (!clip || (clip && (grid.Contains(pos)))) + DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, emptyColor, borderColor, + gap, cornerRadius, startPos, symbolSize2); + } + + public void DrawClipZebraLine(VertexHelper vh, Vector3 p1, Vector3 p2, float size, float zebraWidth, + float zebraGap, Color32 color, Color32 toColor, bool clip, GridCoord grid, float maxDistance) + { + ClampInChart(ref p1); + ClampInChart(ref p2); + UGL.DrawZebraLine(vh, p1, p2, size, zebraWidth, zebraGap, color, toColor, maxDistance); + } + + public void DrawSymbol(VertexHelper vh, SymbolType type, float symbolSize, float tickness, + Vector3 pos, Color32 color, Color32 toColor, Color32 emptyColor, Color32 borderColor, + float gap, float[] cornerRadius, float symbolSize2 = 0) + { + DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, emptyColor, borderColor, + gap, cornerRadius, Vector3.zero, symbolSize2); + } + + public void DrawSymbol(VertexHelper vh, SymbolType type, float symbolSize, float tickness, + Vector3 pos, Color32 color, Color32 toColor, Color32 emptyColor, Color32 borderColor, + float gap, float[] cornerRadius, Vector3 startPos, float symbolSize2 = 0) + { + var backgroundColor = GetChartBackgroundColor(); + if (ChartHelper.IsClearColor(emptyColor)) + emptyColor = backgroundColor; + var smoothness = settings.cicleSmoothness; + ChartDrawer.DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, gap, + cornerRadius, emptyColor, backgroundColor, borderColor, smoothness, startPos, symbolSize2); + } + + public Color32 GetXLerpColor(Color32 areaColor, Color32 areaToColor, Vector3 pos, GridCoord grid) + { + if (ChartHelper.IsValueEqualsColor(areaColor, areaToColor)) return areaColor; + return Color32.Lerp(areaToColor, areaColor, (pos.y - grid.context.y) / grid.context.height); + } + + public Color32 GetYLerpColor(Color32 areaColor, Color32 areaToColor, Vector3 pos, GridCoord grid) + { + if (ChartHelper.IsValueEqualsColor(areaColor, areaToColor)) return areaColor; + return Color32.Lerp(areaToColor, areaColor, (pos.x - grid.context.x) / grid.context.width); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs.meta b/Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs.meta new file mode 100644 index 0000000..26dffb0 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Draw.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 781bfba23eace44fcbbf9ee6924da32b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs b/Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs new file mode 100644 index 0000000..4063d5f --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs @@ -0,0 +1,1170 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; + +namespace XCharts.Runtime +{ + public partial class BaseChart + { + public T AddSerie<T>(string serieName = null, bool show = true, bool addToHead = false) where T : Serie + { + if (!CanAddSerie<T>()) return null; + var index = -1; + var serie = InsertSerie(index, typeof(T), serieName, show, addToHead) as T; + CreateSerieHandler(serie); + return serie; + } + + public T InsertSerie<T>(int index, string serieName = null, bool show = true) where T : Serie + { + if (!CanAddSerie<T>()) return null; + var serie = InsertSerie(index, typeof(T), serieName, show) as T; + InitSerieHandlers(); + return serie; + } + + public void InsertSerie(Serie serie, int index = -1, bool addToHead = false) + { + serie.AnimationRestart(); + AnimationStyleHelper.UpdateSerieAnimation(serie); + if (addToHead) m_Series.Insert(0, serie); + else if (index >= 0) m_Series.Insert(index, serie); + else m_Series.Add(serie); + ResetSeriesIndex(); + SeriesHelper.UpdateSerieNameList(this, ref m_LegendRealShowName); + } + + public bool MoveUpSerie(int serieIndex) + { + if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return false; + if (serieIndex == 0) return false; + var up = GetSerie(serieIndex - 1); + var temp = GetSerie(serieIndex); + m_Series[serieIndex - 1] = temp; + m_Series[serieIndex] = up; + ResetSeriesIndex(); + InitSerieHandlers(); + RefreshChart(); + return true; + } + + public bool MoveDownSerie(int serieIndex) + { + if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return false; + if (serieIndex == m_Series.Count - 1) return false; + var down = GetSerie(serieIndex + 1); + var temp = GetSerie(serieIndex); + m_Series[serieIndex + 1] = temp; + m_Series[serieIndex] = down; + ResetSeriesIndex(); + InitSerieHandlers(); + RefreshChart(); + return true; + } + + /// <summary> + /// 重置serie的数据项索引。避免数据项索引异常。 + /// </summary> + /// <param name="serieIndex"></param> + public bool ResetDataIndex(int serieIndex) + { + var serie = GetSerie(serieIndex); + if (serie != null) + return serie.ResetDataIndex(); + return false; + } + + public bool CanAddSerie<T>() where T : Serie + { + return CanAddSerie(typeof(T)); + } + + public bool CanAddSerie(Type type) + { + return m_TypeListForSerie.ContainsKey(type); + } + + public bool HasSerie<T>() where T : Serie + { + return HasSerie(typeof(T)); + } + + public bool HasSerie(Type type) + { + if (!type.IsSubclassOf(typeof(Serie))) return false; + foreach (var serie in m_Series) + { + if (serie.GetType() == type) + return true; + } + return false; + } + + public bool HasRealtimeSortSerie(int gridIndex) + { + foreach (var serie in m_Series) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (serie.useSortData) + return true; + } + return false; + } + + public Serie GetRealtimeSortSerie(int gridIndex) + { + foreach (var serie in m_Series) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (serie.useSortData) + return serie; + } + return null; + } + + public T GetSerie<T>() where T : Serie + { + foreach (var serie in m_Series) + { + if (serie is T) return serie as T; + } + return null; + } + + public Serie GetSerie(string serieName) + { + foreach (var serie in m_Series) + { + if (string.IsNullOrEmpty(serie.serieName)) + { + if (string.IsNullOrEmpty(serieName)) return serie; + } + else if (serie.serieName.Equals(serieName)) + { + return serie; + } + } + return null; + } + + public Serie GetSerie(int serieIndex) + { + if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return null; + return m_Series[serieIndex]; + } + + public T GetSerie<T>(int serieIndex) where T : Serie + { + if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return null; + return m_Series[serieIndex] as T; + } + + public void RemoveSerie(string serieName) + { + for (int i = m_Series.Count - 1; i >= 0; i--) + { + var serie = m_Series[i]; + if (string.IsNullOrEmpty(serieName)) + { + if (string.IsNullOrEmpty(serie.serieName)) + RemoveSerie(serie); + } + else if (serieName.Equals(serie.serieName)) + { + RemoveSerie(serie); + } + } + } + + public void RemoveSerie(int serieIndex) + { + if (serieIndex < 0 || serieIndex > m_Series.Count - 1) return; + RemoveSerie(m_Series[serieIndex]); + } + + public void RemoveSerie<T>() where T : Serie + { + for (int i = m_Series.Count - 1; i >= 0; i--) + { + var serie = m_Series[i]; + if (serie is T) + RemoveSerie(serie); + } + } + + public void RemoveSerie(Serie serie) + { + serie.OnRemove(); + m_SerieHandlers.Remove(serie.handler); + m_Series.Remove(serie); + RefreshChart(); + } + + public bool ConvertSerie<T>(Serie serie) where T : Serie + { + return ConvertSerie(serie, typeof(T)); + } + + public bool ConvertSerie(Serie serie, Type type) + { + try + { + var newSerie = type.InvokeMember("ConvertSerie", + BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, + new object[] { serie }) as Serie; + return ReplaceSerie(serie, newSerie); + } + catch + { + Debug.LogError(string.Format("ConvertSerie Failed: can't found {0}.ConvertSerie(Serie serie)", type.Name)); + return false; + } + } + + public bool ReplaceSerie(Serie oldSerie, Serie newSerie) + { + if (oldSerie == null || newSerie == null) + return false; + + var index = m_Series.IndexOf(oldSerie); + if (index < 0) + return false; + AnimationStyleHelper.UpdateSerieAnimation(newSerie); + oldSerie.OnRemove(); + m_Series.RemoveAt(index); + m_Series.Insert(index, newSerie); + ResetSeriesIndex(); + InitSerieHandlers(); + RefreshAllComponent(); + RefreshChart(); + return true; + } + + /// <summary> + /// Add a data to serie. + /// ||If serieName doesn't exist in legend,will be add to legend. + /// ||添加一个数据到指定的系列中。 + /// </summary> + /// <param name="serieName">the name of serie</param> + /// <param name="data">the data to add</param> + /// <param name="dataName">the name of data</param> + /// <param name="dataId">the unique id of data</param> + /// <returns>Returns True on success</returns> + public SerieData AddData(string serieName, double data, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieName); + if (serie != null) + { + var serieData = serie.AddYData(data, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + /// <summary> + /// Add a data to serie. + /// ||添加一个数据到指定的系列中。 + /// </summary> + /// <param name="serieIndex">the index of serie</param> + /// <param name="data">the data to add</param> + /// <param name="dataName">the name of data</param> + /// <param name="dataId">the unique id of data</param> + /// <returns>Returns True on success</returns> + public SerieData AddData(int serieIndex, double data, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + var serieData = serie.AddYData(data, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + /// <summary> + /// Add an arbitray dimension data to serie,such as (x,y,z,...). + /// ||添加多维数据(x,y,z...)到指定的系列中。 + /// </summary> + /// <param name="serieName">the name of serie</param> + /// <param name="multidimensionalData">the (x,y,z,...) data</param> + /// <param name="dataName">the name of data</param> + /// <param name="dataId">the unique id of data</param> + /// <returns>Returns True on success</returns> + public SerieData AddData(string serieName, List<double> multidimensionalData, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieName); + if (serie != null) + { + var serieData = serie.AddData(multidimensionalData, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + /// <summary> + /// Add an arbitray dimension data to serie,such as (x,y,z,...). + /// ||添加多维数据(x,y,z...)到指定的系列中。 + /// </summary> + /// <param name="serieIndex">the index of serie,index starts at 0</param> + /// <param name="multidimensionalData">the (x,y,z,...) data</param> + /// <param name="dataName">the name of data</param> + /// <param name="dataId">the unique id of data</param> + /// <returns>Returns True on success</returns> + public SerieData AddData(int serieIndex, List<double> multidimensionalData, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + var serieData = serie.AddData(multidimensionalData, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + [Since("v3.4.0")] + /// <summary> + /// Add an arbitray dimension data to serie,such as (x,y,z,...). + /// ||添加多维数据(x,y,z...)到指定的系列中。 + /// </summary> + /// <param name="serieIndex">the index of serie</param> + /// <param name="multidimensionalData">the (x,y,z,...) data</param> + /// <returns></returns> + public SerieData AddData(int serieIndex, params double[] multidimensionalData) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + var serieData = serie.AddData(multidimensionalData); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + [Since("v3.4.0")] + /// <summary> + /// Add an arbitray dimension data to serie,such as (x,y,z,...). + /// ||添加多维数据(x,y,z...)到指定的系列中。 + /// </summary> + /// <param name="serieName">the name of serie</param> + /// <param name="multidimensionalData">the (x,y,z,...) data</param> + /// <returns></returns> + public SerieData AddData(string serieName, params double[] multidimensionalData) + { + var serie = GetSerie(serieName); + if (serie != null) + { + var serieData = serie.AddData(multidimensionalData); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + /// <summary> + /// Add a (x,y) data to serie. + /// ||添加(x,y)数据到指定系列中。 + /// </summary> + /// <param name="serieName">the name of serie</param> + /// <param name="xValue">x data</param> + /// <param name="yValue">y data</param> + /// <param name="dataName">the name of data</param> + /// <param name="dataId">the unique id of data</param> + /// <returns>Returns True on success</returns> + public SerieData AddData(string serieName, double xValue, double yValue, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieName); + if (serie != null) + { + var serieData = serie.AddXYData(xValue, yValue, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + /// <summary> + /// Add a (x,y) data to serie. + /// ||添加(x,y)数据到指定系列中。 + /// </summary> + /// <param name="serieIndex">the index of serie</param> + /// <param name="xValue">x data</param> + /// <param name="yValue">y data</param> + /// <param name="dataName">the name of data</param> + /// <param name="dataId">the unique id of data</param> + /// <returns>Returns True on success</returns> + public SerieData AddData(int serieIndex, double xValue, double yValue, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + var serieData = serie.AddXYData(xValue, yValue, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + /// <summary> + /// Add a (time,y) data to serie. + /// ||添加(time,y)数据到指定的系列中。 + /// </summary> + /// <param name="serieName"></param> + /// <param name="time"></param> + /// <param name="yValue"></param> + /// <param name="dataName"></param> + /// <param name="dataId"></param> + /// <returns></returns> + public SerieData AddData(string serieName, DateTime time, double yValue, string dataName = null, string dataId = null) + { + var xValue = DateTimeUtil.GetTimestamp(time); + return AddData(serieName, xValue, yValue, dataName, dataId); + } + + /// <summary> + /// Add a (time,y) data to serie. + /// ||添加(time,y)数据到指定的系列中。 + /// </summary> + /// <param name="serieIndex"></param> + /// <param name="time"></param> + /// <param name="yValue"></param> + /// <param name="dataName"></param> + /// <param name="dataId"></param> + /// <returns></returns> + public SerieData AddData(int serieIndex, DateTime time, double yValue, string dataName = null, string dataId = null) + { + var xValue = DateTimeUtil.GetTimestamp(time); + return AddData(serieIndex, xValue, yValue, dataName, dataId); + } + + public SerieData AddData(int serieIndex, double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + var serieData = serie.AddData(indexOrTimestamp, open, close, lowest, heighest, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + public SerieData AddData(string serieName, double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null) + { + var serie = GetSerie(serieName); + if (serie != null) + { + var serieData = serie.AddData(indexOrTimestamp, open, close, lowest, heighest, dataName, dataId); + RefreshPainter(serie.painter); + return serieData; + } + return null; + } + + /// <summary> + /// Add a link data to serie. + /// ||添加一个关系图的关系数据。 + /// </summary> + /// <param name="serieIndex">the index of serie</param> + /// <param name="sourceId">the source id of link</param> + /// <param name="targetId">the target id of link</param> + /// <param name="value">the value of link</param> + /// <returns></returns> + public SerieDataLink AddLink(int serieIndex, string sourceId, string targetId, double value = 0) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + var link = serie.AddLink(sourceId, targetId, value); + RefreshPainter(serie.painter); + return link; + } + return null; + } + + /// <summary> + /// Update serie data by serie name. + /// ||更新指定系列中的指定索引数据。 + /// </summary> + /// <param name="serieName">the name of serie</param> + /// <param name="dataIndex">the index of data</param> + /// <param name="value">the data will be update</param> + public bool UpdateData(string serieName, int dataIndex, double value) + { + var serie = GetSerie(serieName); + if (serie != null) + { + if (serie.UpdateYData(dataIndex, value)) + { + RefreshPainter(serie); + return true; + } + else + { + return false; + } + } + return false; + } + + /// <summary> + /// Update serie data by serie index. + /// ||更新指定系列中的指定索引数据。 + /// </summary> + /// <param name="serieIndex">the index of serie</param> + /// <param name="dataIndex">the index of data</param> + /// <param name="value">the data will be update</param> + public bool UpdateData(int serieIndex, int dataIndex, double value) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + if (serie.UpdateYData(dataIndex, value)) + { + RefreshPainter(serie); + return true; + } + else + { + return false; + } + } + return false; + } + + /// <summary> + /// 更新指定系列指定索引的数据项的多维数据。 + /// </summary> + /// <param name="serieName"></param> + /// <param name="dataIndex"></param> + /// <param name="multidimensionalData">一个数据项的多维数据列表,而不是多个数据项的数据</param> + public bool UpdateData(string serieName, int dataIndex, List<double> multidimensionalData) + { + var serie = GetSerie(serieName); + if (serie != null) + { + if (serie.UpdateData(dataIndex, multidimensionalData)) + { + RefreshPainter(serie); + return true; + } + else + { + return false; + } + } + return false; + } + + /// <summary> + /// 更新指定系列指定索引的数据项的多维数据。 + /// </summary> + /// <param name="serieIndex"></param> + /// <param name="dataIndex"></param> + /// <param name="multidimensionalData">一个数据项的多维数据列表,而不是多个数据项的数据</param> + public bool UpdateData(int serieIndex, int dataIndex, List<double> multidimensionalData) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + if (serie.UpdateData(dataIndex, multidimensionalData)) + { + RefreshPainter(serie); + return true; + } + else + { + return false; + } + } + return false; + } + + /// <summary> + /// 更新指定系列指定索引指定维数的数据。维数从0开始。 + /// </summary> + /// <param name="serieName"></param> + /// <param name="dataIndex"></param> + /// <param name="dimension">指定维数,从0开始</param> + /// <param name="value"></param> + public bool UpdateData(string serieName, int dataIndex, int dimension, double value) + { + var serie = GetSerie(serieName); + if (serie != null) + { + if (serie.UpdateData(dataIndex, dimension, value)) + { + RefreshPainter(serie); + return true; + } + else + { + return false; + } + } + return false; + } + + /// <summary> + /// 更新指定系列指定索引指定维数的数据。维数从0开始。 + /// </summary> + /// <param name="serieIndex"></param> + /// <param name="dataIndex"></param> + /// <param name="dimension">指定维数,从0开始</param> + /// <param name="value"></param> + public bool UpdateData(int serieIndex, int dataIndex, int dimension, double value) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + if (serie.UpdateData(dataIndex, dimension, value)) + { + RefreshPainter(serie); + return true; + } + else + { + return false; + } + } + return false; + } + + /// <summary> + /// Update serie data name. + /// ||更新指定系列中的指定索引数据名称。 + /// </summary> + /// <param name="serieName"></param> + /// <param name="dataIndex"></param> + /// <param name="dataName"></param> + public bool UpdateDataName(string serieName, int dataIndex, string dataName) + { + var serie = GetSerie(serieName); + if (serie != null) + { + return serie.UpdateDataName(dataIndex, dataName); + } + return false; + } + + /// <summary> + /// Update serie data name. + /// ||更新指定系列中的指定索引数据名称。 + /// </summary> + /// <param name="serieIndex"></param> + /// <param name="dataName"></param> + /// <param name="dataIndex"></param> + public bool UpdateDataName(int serieIndex, int dataIndex, string dataName) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + return serie.UpdateDataName(dataIndex, dataName); + } + return false; + } + + public double GetData(string serieName, int dataIndex, int dimension = 1) + { + var serie = GetSerie(serieName); + if (serie != null) + { + return serie.GetData(dataIndex, dimension); + } + return 0; + } + + public double GetData(int serieIndex, int dataIndex, int dimension = 1) + { + var serie = GetSerie(serieIndex); + if (serie != null) + { + return serie.GetData(dataIndex, dimension); + } + return 0; + } + + public int GetAllSerieDataCount() + { + var total = 0; + foreach (var serie in m_Series) + total += serie.dataCount; + return total; + } + + /// <summary> + /// Whether to show serie. + /// ||设置指定系列是否显示。 + /// </summary> + /// <param name="serieName">the name of serie</param> + /// <param name="active">Active or not</param> + public void SetSerieActive(string serieName, bool active) + { + var serie = GetSerie(serieName); + if (serie != null) + SetSerieActive(serie, active); + } + + /// <summary> + /// Whether to show serie. + /// ||设置指定系列是否显示。 + /// </summary> + /// <param name="serieIndex">the index of serie</param> + /// <param name="active">Active or not</param> + public void SetSerieActive(int serieIndex, bool active) + { + var serie = GetSerie(serieIndex); + if (serie != null) + SetSerieActive(serie, active); + } + + public void SetSerieActive(Serie serie, bool active) + { + serie.show = active; + serie.RefreshLabel(); + serie.AnimationReset(); + if (active) serie.AnimationFadeIn(); + UpdateLegendColor(serie.serieName, active); + } + + /// <summary> + /// Add a category data to xAxis. + /// ||添加一个类目数据到指定的x轴。 + /// </summary> + /// <param name="category">the category data</param> + /// <param name="xAxisIndex">which xAxis should category add to</param> + public void AddXAxisData(string category, int xAxisIndex = 0) + { + var xAxis = GetChartComponent<XAxis>(xAxisIndex); + if (xAxis != null) + { + xAxis.AddData(category); + } + } + + /// <summary> + /// Update category data. + /// ||更新X轴类目数据。 + /// </summary> + /// <param name="index">the index of category data</param> + /// <param name="category"></param> + /// <param name="xAxisIndex">which xAxis index to update to</param> + public void UpdateXAxisData(int index, string category, int xAxisIndex = 0) + { + var xAxis = GetChartComponent<XAxis>(xAxisIndex); + if (xAxis != null) + { + xAxis.UpdateData(index, category); + } + } + + /// <summary> + /// Add an icon to xAxis. + /// ||添加一个图标到指定的x轴。 + /// </summary> + /// <param name="icon"></param> + /// <param name="xAxisIndex"></param> + public void AddXAxisIcon(Sprite icon, int xAxisIndex = 0) + { + var xAxis = GetChartComponent<XAxis>(xAxisIndex); + if (xAxis != null) + { + xAxis.AddIcon(icon); + } + } + + /// <summary> + /// Update xAxis icon. + /// ||更新X轴图标。 + /// </summary> + /// <param name="index"></param> + /// <param name="icon"></param> + /// <param name="xAxisIndex"></param> + public void UpdateXAxisIcon(int index, Sprite icon, int xAxisIndex = 0) + { + var xAxis = GetChartComponent<XAxis>(xAxisIndex); + if (xAxis != null) + { + xAxis.UpdateIcon(index, icon); + } + } + + /// <summary> + /// Add a category data to yAxis. + /// ||添加一个类目数据到指定的y轴。 + /// </summary> + /// <param name="category">the category data</param> + /// <param name="yAxisIndex">which yAxis should category add to</param> + public void AddYAxisData(string category, int yAxisIndex = 0) + { + var yAxis = GetChartComponent<YAxis>(yAxisIndex); + if (yAxis != null) + { + yAxis.AddData(category); + } + } + + /// <summary> + /// Update category data. + /// ||更新Y轴类目数据。 + /// </summary> + /// <param name="index">the index of category data</param> + /// <param name="category"></param> + /// <param name="yAxisIndex">which yAxis index to update to</param> + public void UpdateYAxisData(int index, string category, int yAxisIndex = 0) + { + var yAxis = GetChartComponent<YAxis>(yAxisIndex); + if (yAxis != null) + { + yAxis.UpdateData(index, category); + } + } + + /// <summary> + /// Add an icon to yAxis. + /// ||添加一个图标到指定的y轴。 + /// </summary> + /// <param name="icon"></param> + /// <param name="yAxisIndex"></param> + public void AddYAxisIcon(Sprite icon, int yAxisIndex = 0) + { + var yAxis = GetChartComponent<YAxis>(yAxisIndex); + if (yAxis != null) + { + yAxis.AddIcon(icon); + } + } + + /// <summary> + /// 更新Y轴图标。 + /// </summary> + /// <param name="index"></param> + /// <param name="icon"></param> + /// <param name="yAxisIndex"></param> + public void UpdateYAxisIcon(int index, Sprite icon, int yAxisIndex = 0) + { + var yAxis = GetChartComponent<YAxis>(yAxisIndex); + if (yAxis != null) + { + yAxis.UpdateIcon(index, icon); + } + } + + public float GetSerieBarGap<T>(int gridIndex) where T : Serie + { + float gap = 0f; + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + if (serie.show && serie is T) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (serie.barGap != 0) + { + gap = serie.barGap; + } + } + } + return gap; + } + + public double GetSerieSameStackTotalValue<T>(string stack, int dataIndex, int gridIndex) where T : Serie + { + if (string.IsNullOrEmpty(stack)) return 0; + double total = 0; + foreach (var serie in m_Series) + { + if (serie is T) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (stack.Equals(serie.stack)) + { + total += serie.data[dataIndex].data[1]; + } + } + } + return total; + } + + public int GetSerieBarRealCount<T>(int gridIndex) where T : Serie + { + var count = 0; + barStackSet.Clear(); + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + if (!serie.show) continue; + if (serie is T) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (!string.IsNullOrEmpty(serie.stack)) + { + if (barStackSet.Contains(serie.stack)) continue; + barStackSet.Add(serie.stack); + } + count++; + + } + } + return count; + } + + private bool CheckSerieGridIndex(Serie serie, int gridIndex) + { + if (gridIndex >= 0) + { + if (serie.xAxisIndex >= 0 && serie.xAxisIndex < m_XAxes.Count) + { + var xAxis = m_XAxes[serie.xAxisIndex]; + if (xAxis.gridIndex != gridIndex) return false; + } + if (serie.yAxisIndex >= 0 && serie.yAxisIndex < m_YAxes.Count) + { + var yAxis = m_YAxes[serie.yAxisIndex]; + if (yAxis.gridIndex != gridIndex) return false; + } + } + return true; + } + + private HashSet<string> barStackSet = new HashSet<string>(); + public float GetSerieTotalWidth<T>(float categoryWidth, float gap, int realBarCount, int gridIndex) where T : Serie + { + float total = 0; + float lastGap = 0; + barStackSet.Clear(); + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + if (!serie.show) continue; + if (serie is T) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (!string.IsNullOrEmpty(serie.stack)) + { + if (barStackSet.Contains(serie.stack)) continue; + barStackSet.Add(serie.stack); + } + var width = GetStackBarWidth<T>(categoryWidth, serie, realBarCount); + if (gap == -1) + { + if (width > total) total = width; + } + else + { + lastGap = ChartHelper.GetActualValue(gap, width); + total += width; + total += lastGap; + } + } + } + if (total > 0 && gap != -1) total -= lastGap; + return total; + } + + public float GetSerieTotalGap<T>(float categoryWidth, float gap, int index, int gridIndex) where T : Serie + { + if (index <= 0) return 0; + var total = 0f; + var count = 0; + var totalRealBarCount = GetSerieBarRealCount<T>(gridIndex); + barStackSet.Clear(); + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + if (!serie.show) continue; + if (serie is T) + { + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (!string.IsNullOrEmpty(serie.stack)) + { + if (barStackSet.Contains(serie.stack)) continue; + barStackSet.Add(serie.stack); + } + var width = GetStackBarWidth<T>(categoryWidth, serie, totalRealBarCount); + if (gap == -1) + { + if (width > total) total = width; + } + else + { + total += width + ChartHelper.GetActualValue(gap, width); + } + if (count + 1 >= index) + break; + else + count++; + } + } + return total; + } + + private float GetStackBarWidth<T>(float categoryWidth, Serie now, int realBarCount) where T : Serie + { + if (string.IsNullOrEmpty(now.stack)) return now.GetBarWidth(categoryWidth, realBarCount); + float barWidth = 0; + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + if ((serie is T) && + serie.show && now.stack.Equals(serie.stack)) + { + if (serie.barWidth > barWidth) barWidth = serie.barWidth; + } + } + if (barWidth == 0) + { + var width = ChartHelper.GetActualValue(0.6f, categoryWidth); + if (realBarCount == 0) + return width < 1 ? categoryWidth : width; + else + return width / realBarCount; + } + else + return ChartHelper.GetActualValue(barWidth, categoryWidth); + } + + private List<string> tempList = new List<string>(); + public int GetSerieIndexIfStack<T>(Serie currSerie, int gridIndex) where T : Serie + { + tempList.Clear(); + int index = 0; + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + if (!serie.show) continue; + if (!(serie is T)) continue; + if (!CheckSerieGridIndex(serie, gridIndex)) continue; + if (string.IsNullOrEmpty(serie.stack)) + { + if (serie.index == currSerie.index) return index; + tempList.Add(string.Empty); + index++; + } + else + { + if (!tempList.Contains(serie.stack)) + { + if (serie.index == currSerie.index) return index; + tempList.Add(serie.stack); + index++; + } + else + { + if (serie.index == currSerie.index) return tempList.IndexOf(serie.stack); + } + } + } + return 0; + } + + internal void InitSerieHandlers() + { + m_SerieHandlers.Clear(); + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + serie.index = i; + CreateSerieHandler(serie); + } + } + + private void CreateSerieHandler(Serie serie) + { + if (serie == null) + throw new ArgumentNullException("serie is null"); + + if (serie.GetType().IsDefined(typeof(DefaultTooltipAttribute), false)) + { + var attribute1 = serie.GetType().GetAttribute<DefaultTooltipAttribute>(); + if (attribute1 != null) + { + serie.context.tooltipTrigger = attribute1.trigger; + serie.context.tooltipType = attribute1.type; + } + } + if (!serie.GetType().IsDefined(typeof(SerieHandlerAttribute), false)) + { + Debug.LogError("Serie no Handler:" + serie.GetType()); + return; + } + var attribute = serie.GetType().GetAttribute<SerieHandlerAttribute>(); + var handler = (SerieHandler)Activator.CreateInstance(attribute.handler); + handler.attribute = attribute; + handler.chart = this; + handler.defaultDimension = 1; + handler.SetSerie(serie); + serie.handler = handler; + m_SerieHandlers.Add(handler); + } + + private Serie InsertSerie(int index, Type type, string serieName, bool show = true, bool addToHead = false) + { + CheckAddRequireChartComponent(type); + var serie = Activator.CreateInstance(type) as Serie; + serie.show = show; + serie.serieName = serieName; + serie.serieType = type.Name; + serie.index = m_Series.Count; + + if (type == typeof(Scatter)) + { + serie.symbol.show = true; + serie.symbol.type = SymbolType.Circle; + } + else if (type == typeof(Line)) + { + serie.symbol.show = true; + serie.symbol.type = SymbolType.EmptyCircle; + } + else if (type == typeof(Heatmap)) + { + serie.symbol.show = true; + serie.symbol.type = SymbolType.Rect; + } + else + { + serie.symbol.show = false; + } + InsertSerie(serie, index, addToHead); + return serie; + } + + private void ResetSeriesIndex() + { +#if UNITY_EDITOR && UNITY_2019_1_OR_NEWER + UnityEditor.EditorUtility.SetDirty(this); +#endif + for (int i = 0; i < m_Series.Count; i++) + { + m_Series[i].index = i; + } + } + + private void AddSerieAfterDeserialize(Serie serie) + { + serie.OnAfterDeserialize(); + m_Series.Add(serie); + } + + public string GenerateDefaultSerieName() + { + return "serie" + m_Series.Count; + } + + public bool IsSerieName(string name) + { + if (string.IsNullOrEmpty(name)) + return false; + foreach (var serie in m_Series) + { + if (name.Equals(serie.serieName)) + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs.meta b/Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs.meta new file mode 100644 index 0000000..841dd30 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.Serie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70fab7deef662441eaaee4d6ddd43295 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.cs b/Assets/XCharts/Runtime/Internal/BaseChart.cs new file mode 100644 index 0000000..3c8caab --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.cs @@ -0,0 +1,802 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [AddComponentMenu("XCharts/EmptyChart", 10)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform), typeof(CanvasRenderer))] + [DisallowMultipleComponent] + public partial class BaseChart : BaseGraph, ISerializationCallbackReceiver + { + [SerializeField] protected string m_ChartName; + [SerializeField] protected ThemeStyle m_Theme = new ThemeStyle(); + [SerializeField] protected Settings m_Settings; + [SerializeField] protected DebugInfo m_DebugInfo = new DebugInfo(); + [SerializeField] protected bool m_ChartInited = false; + +#pragma warning disable 0414 + [SerializeField][ListForComponent(typeof(AngleAxis))] private List<AngleAxis> m_AngleAxes = new List<AngleAxis>(); + [SerializeField][ListForComponent(typeof(Background))] private List<Background> m_Backgrounds = new List<Background>(); + [SerializeField][ListForComponent(typeof(DataZoom))] private List<DataZoom> m_DataZooms = new List<DataZoom>(); + [SerializeField][ListForComponent(typeof(GridCoord))] private List<GridCoord> m_Grids = new List<GridCoord>(); + [SerializeField][ListForComponent(typeof(GridLayout))] private List<GridLayout> m_GridsLayout = new List<GridLayout>(); + + [SerializeField][ListForComponent(typeof(Legend))] private List<Legend> m_Legends = new List<Legend>(); + [SerializeField][ListForComponent(typeof(MarkLine))] private List<MarkLine> m_MarkLines = new List<MarkLine>(); + [SerializeField][ListForComponent(typeof(MarkArea))] private List<MarkArea> m_MarkAreas = new List<MarkArea>(); + [SerializeField][ListForComponent(typeof(PolarCoord))] private List<PolarCoord> m_Polars = new List<PolarCoord>(); + [SerializeField][ListForComponent(typeof(RadarCoord))] private List<RadarCoord> m_Radars = new List<RadarCoord>(); + [SerializeField][ListForComponent(typeof(RadiusAxis))] private List<RadiusAxis> m_RadiusAxes = new List<RadiusAxis>(); + [SerializeField][ListForComponent(typeof(Title))] private List<Title> m_Titles = new List<Title>(); + [SerializeField][ListForComponent(typeof(Tooltip))] private List<Tooltip> m_Tooltips = new List<Tooltip>(); + [SerializeField][ListForComponent(typeof(VisualMap))] private List<VisualMap> m_VisualMaps = new List<VisualMap>(); + [SerializeField][ListForComponent(typeof(XAxis))] private List<XAxis> m_XAxes = new List<XAxis>(); + [SerializeField][ListForComponent(typeof(YAxis))] private List<YAxis> m_YAxes = new List<YAxis>(); + [SerializeField][ListForComponent(typeof(SingleAxis))] private List<SingleAxis> m_SingleAxes = new List<SingleAxis>(); + [SerializeField][ListForComponent(typeof(ParallelCoord))] private List<ParallelCoord> m_Parallels = new List<ParallelCoord>(); + [SerializeField][ListForComponent(typeof(ParallelAxis))] private List<ParallelAxis> m_ParallelAxes = new List<ParallelAxis>(); + [SerializeField][ListForComponent(typeof(Comment))] private List<Comment> m_Comments = new List<Comment>(); + + [SerializeField][ListForSerie(typeof(Bar))] private List<Bar> m_SerieBars = new List<Bar>(); + [SerializeField][ListForSerie(typeof(Candlestick))] private List<Candlestick> m_SerieCandlesticks = new List<Candlestick>(); + [SerializeField][ListForSerie(typeof(EffectScatter))] private List<EffectScatter> m_SerieEffectScatters = new List<EffectScatter>(); + [SerializeField][ListForSerie(typeof(Heatmap))] private List<Heatmap> m_SerieHeatmaps = new List<Heatmap>(); + [SerializeField][ListForSerie(typeof(Line))] private List<Line> m_SerieLines = new List<Line>(); + [SerializeField][ListForSerie(typeof(Pie))] private List<Pie> m_SeriePies = new List<Pie>(); + [SerializeField][ListForSerie(typeof(Radar))] private List<Radar> m_SerieRadars = new List<Radar>(); + [SerializeField][ListForSerie(typeof(Ring))] private List<Ring> m_SerieRings = new List<Ring>(); + [SerializeField][ListForSerie(typeof(Scatter))] private List<Scatter> m_SerieScatters = new List<Scatter>(); + [SerializeField][ListForSerie(typeof(Parallel))] private List<Parallel> m_SerieParallels = new List<Parallel>(); + [SerializeField][ListForSerie(typeof(SimplifiedLine))] private List<SimplifiedLine> m_SerieSimplifiedLines = new List<SimplifiedLine>(); + [SerializeField][ListForSerie(typeof(SimplifiedBar))] private List<SimplifiedBar> m_SerieSimplifiedBars = new List<SimplifiedBar>(); + [SerializeField][ListForSerie(typeof(SimplifiedCandlestick))] private List<SimplifiedCandlestick> m_SerieSimplifiedCandlesticks = new List<SimplifiedCandlestick>(); +#pragma warning restore 0414 + protected List<Serie> m_Series = new List<Serie>(); + protected List<MainComponent> m_Components = new List<MainComponent>(); + + protected Dictionary<Type, FieldInfo> m_TypeListForComponent = new Dictionary<Type, FieldInfo>(); + protected Dictionary<Type, FieldInfo> m_TypeListForSerie = new Dictionary<Type, FieldInfo>(); + + protected Dictionary<Type, List<MainComponent>> m_ComponentMaps = new Dictionary<Type, List<MainComponent>>(); + + public Dictionary<Type, FieldInfo> typeListForComponent { get { return m_TypeListForComponent; } } + public Dictionary<Type, FieldInfo> typeListForSerie { get { return m_TypeListForSerie; } } + public List<MainComponent> components { get { return m_Components; } } + + public List<Serie> series { get { return m_Series; } } + public DebugInfo debug { get { return m_DebugInfo; } } + public override HideFlags chartHideFlags { get { return m_DebugInfo.showAllChartObject ? HideFlags.None : HideFlags.HideInHierarchy; } } + + protected float m_ChartWidth; + protected float m_ChartHeight; + protected float m_ChartX; + protected float m_ChartY; + protected Vector3 m_ChartPosition = Vector3.zero; + protected Vector2 m_ChartMinAnchor; + protected Vector2 m_ChartMaxAnchor; + protected Vector2 m_ChartPivot; + protected Vector2 m_ChartSizeDelta; + + protected Rect m_ChartRect = new Rect(0, 0, 0, 0); + protected Action m_OnInit; + protected Action m_OnUpdate; + protected Action<VertexHelper> m_OnDrawBase; + protected Action<VertexHelper> m_OnDrawUpper; + protected Action<VertexHelper> m_OnDrawTop; + protected Action<VertexHelper, Serie> m_OnDrawSerieBefore; + protected Action<VertexHelper, Serie> m_OnDrawSerieAfter; + protected Action<SerieEventData> m_OnSerieClick; + protected Action<SerieEventData> m_OnSerieDown; + protected Action<SerieEventData> m_OnSerieEnter; + protected Action<SerieEventData> m_OnSerieExit; + protected Action<int, int> m_OnPointerEnterPie; + protected Action<Axis, double> m_OnAxisPointerValueChanged; + protected Action<Legend, int, string, bool> m_OnLegendClick; + protected Action<Legend, int, string> m_OnLegendEnter; + protected Action<Legend, int, string> m_OnLegendExit; + + protected CustomDrawGaugePointerFunction m_CustomDrawGaugePointerFunction; + + internal bool m_CheckAnimation = false; + internal protected List<string> m_LegendRealShowName = new List<string>(); + protected List<Painter> m_PainterList = new List<Painter>(); + internal Painter m_PainterUpper; + internal Painter m_PainterTop; + internal int m_BasePainterVertCount; + internal int m_UpperPainterVertCount; + internal int m_TopPainterVertCount; + + private ThemeType m_CheckTheme = 0; + protected List<MainComponentHandler> m_ComponentHandlers = new List<MainComponentHandler>(); + protected List<SerieHandler> m_SerieHandlers = new List<SerieHandler>(); + + protected virtual void DefaultChart() { } + + protected override void InitComponent() + { + base.InitComponent(); + SeriesHelper.UpdateSerieNameList(this, ref m_LegendRealShowName); + foreach (var handler in m_ComponentHandlers) + { + handler.InitComponent(); + handler.inited = true; + } + foreach (var handler in m_SerieHandlers) + { + handler.InitComponent(); + handler.inited = true; + } + m_DebugInfo.Init(this); + } + + protected override void Awake() + { + if (m_Settings == null) + m_Settings = Settings.DefaultSettings; + CheckTheme(true); + base.Awake(); + InitComponentHandlers(); + InitSerieHandlers(); + AnimationReset(); + AnimationFadeIn(); + XChartsMgr.AddChart(this); + } + + protected void OnInit() + { + RemoveAllChartComponent(); + OnBeforeSerialize(); + + EnsureChartComponent<Title>(); + EnsureChartComponent<Tooltip>(); + EnsureChartComponent<Title>().text = GetType().Name; + + var background = EnsureChartComponent<Background>(); + background.borderStyle.show = true; + background.borderStyle.cornerRadius = new float[] { 10, 10, 10, 10 }; + + if (m_Theme.sharedTheme != null) + m_Theme.sharedTheme.CopyTheme(ThemeType.Default); + else + m_Theme.sharedTheme = XCThemeMgr.GetTheme(ThemeType.Default); + + var sizeDelta = rectTransform.sizeDelta; + if (sizeDelta.x < 580 && sizeDelta.y < 300) + { + m_GraphWidth = 580; + m_GraphHeight = 300; + m_ChartWidth = m_GraphWidth; + m_ChartHeight = m_GraphHeight; + rectTransform.sizeDelta = new Vector2(m_ChartWidth, m_ChartHeight); + UpdateSize(); + } + ChartHelper.HideAllObject(transform); + m_ChartInited = true; + if (m_OnInit != null) + m_OnInit(); + } + + protected void CheckChartInit() + { + if (!m_ChartInited) + { + OnInit(); + DefaultChart(); + } + } + +#if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + OnInit(); + DefaultChart(); + Awake(); + } + + protected override void OnValidate() + { + base.OnValidate(); + foreach (var handler in m_SerieHandlers) handler.ForceUpdateSerieContext(); + } +#endif + + protected override void Start() + { + RefreshChart(); + } + + protected override void Update() + { + CheckTheme(); + base.Update(); + CheckPainter(); + CheckRefreshChart(); + Internal_CheckAnimation(); + foreach (var handler in m_SerieHandlers) handler.BeforeUpdate(); + foreach (var handler in m_ComponentHandlers) handler.BeforceSerieUpdate(); + foreach (var handler in m_SerieHandlers) handler.Update(); + foreach (var handler in m_ComponentHandlers) + { + if (!handler.inited) + { + handler.InitComponent(); + handler.inited = true; + } + handler.Update(); + } + foreach (var handler in m_SerieHandlers) handler.AfterUpdate(); + + m_DebugInfo.Update(); + if (m_OnUpdate != null) + m_OnUpdate(); + } + + public Painter GetPainter(int index) + { + if (index >= 0 && index < m_PainterList.Count) + { + return m_PainterList[index]; + } + return null; + } + + public void RefreshBasePainter() + { + m_Painter.Refresh(); + } + public void RefreshTopPainter() + { + m_PainterTop.Refresh(); + } + + public void RefreshUpperPainter() + { + m_PainterUpper.Refresh(); + } + + public void RefreshPainter(int index) + { + var painter = GetPainter(index); + RefreshPainter(painter); + } + + public void RefreshPainter(Serie serie) + { + if (serie == null) return; + RefreshPainter(GetPainterIndexBySerie(serie)); + } + + internal override void RefreshPainter(Painter painter) + { + base.RefreshPainter(painter); + if (painter != null && painter.type == Painter.Type.Serie) + { + m_PainterUpper.Refresh(); + } + } + + public void SetPainterActive(int index, bool flag) + { + var painter = GetPainter(index); + if (painter == null) return; + painter.SetActive(flag, m_DebugInfo.showAllChartObject); + } + + protected virtual void CheckTheme(bool firstInit = false) + { + if (m_Theme.sharedTheme == null) + { + m_Theme.sharedTheme = XCThemeMgr.GetTheme(ThemeType.Default); + } + if (firstInit) + { + m_CheckTheme = m_Theme.themeType; + } + if (m_Theme.sharedTheme != null && m_CheckTheme != m_Theme.themeType) + { + m_CheckTheme = m_Theme.themeType; + m_Theme.sharedTheme.CopyTheme(m_CheckTheme); +#if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty(this); +#endif + SetAllComponentDirty(); + OnThemeChanged(); + } + } + protected override void CheckComponent() + { + base.CheckComponent(); + if (m_Theme.anyDirty) + { + if (m_Theme.componentDirty) + { + SetAllComponentDirty(); + } + if (m_Theme.vertsDirty) RefreshChart(); + m_Theme.ClearDirty(); + } + foreach (var com in m_Components) + CheckComponentDirty(com); + } + + protected void CheckComponentDirty(MainComponent component) + { + if (component == null) return; + if (component.anyDirty) + { + if (component.componentDirty) + { + if (component.refreshComponent != null) + component.refreshComponent.Invoke(); + else + component.handler.InitComponent(); + } + if (component.vertsDirty) + { + if (component.painter != null) + { + RefreshPainter(component.painter); + } + } + component.ClearDirty(); + } + } + + protected override void SetAllComponentDirty() + { + base.SetAllComponentDirty(); + m_Theme.SetAllDirty(); + foreach (var com in m_Components) com.SetAllDirty(); + foreach (var handler in m_SerieHandlers) handler.InitComponent(); + m_RefreshChart = true; + } + + protected override void OnDestroy() + { + base.OnDestroy(); + XChartsMgr.RemoveChart(chartName); + for (int i = transform.childCount - 1; i >= 0; i--) + { + DestroyImmediate(transform.GetChild(i).gameObject); + } + } + + protected virtual void CheckPainter() + { + for (int i = 0; i < m_Series.Count; i++) + { + var serie = m_Series[i]; + serie.index = i; + SetPainterActive(i, true); + } + if (m_PainterTop != null && transform.childCount - 3 != m_PainterTop.transform.GetSiblingIndex()) + { + m_PainterTop.transform.SetSiblingIndex(transform.childCount - 3); + } + } + + protected override void InitPainter() + { + base.InitPainter(); + if (settings == null) return; + m_Painter.material = settings.basePainterMaterial; + m_PainterList.Clear(); + var sizeDelta = new Vector2(m_GraphWidth, m_GraphHeight); + for (int i = 0; i < settings.maxPainter; i++) + { + var index = settings.reversePainter ? settings.maxPainter - 1 - i : i; + var painter = ChartHelper.AddPainterObject("painter_" + index, transform, m_GraphMinAnchor, + m_GraphMaxAnchor, m_GraphPivot, sizeDelta, chartHideFlags, 2 + index, m_ChildNodeNames); + painter.index = m_PainterList.Count; + painter.type = Painter.Type.Serie; + painter.onPopulateMesh = OnDrawPainterSerie; + painter.SetActive(false, m_DebugInfo.showAllChartObject); + painter.material = settings.seriePainterMaterial; + painter.transform.SetSiblingIndex(index + 1); + m_PainterList.Add(painter); + } + m_PainterUpper = ChartHelper.AddPainterObject("painter_u", transform, m_GraphMinAnchor, + m_GraphMaxAnchor, m_GraphPivot, sizeDelta, chartHideFlags, 2 + settings.maxPainter, m_ChildNodeNames); + m_PainterUpper.type = Painter.Type.Top; + m_PainterUpper.onPopulateMesh = OnDrawPainterUpper; + m_PainterUpper.SetActive(true, m_DebugInfo.showAllChartObject); + m_PainterUpper.material = settings.topPainterMaterial; + m_PainterUpper.transform.SetSiblingIndex(settings.maxPainter + 1); + + m_PainterTop = ChartHelper.AddPainterObject("painter_t", transform, m_GraphMinAnchor, + m_GraphMaxAnchor, m_GraphPivot, sizeDelta, chartHideFlags, 2 + settings.maxPainter, m_ChildNodeNames); + m_PainterTop.type = Painter.Type.Top; + m_PainterTop.onPopulateMesh = OnDrawPainterTop; + m_PainterTop.SetActive(true, m_DebugInfo.showAllChartObject); + m_PainterTop.material = settings.topPainterMaterial; + m_PainterTop.transform.SetSiblingIndex(settings.maxPainter + 1); + } + + internal void InitComponentHandlers() + { + m_ComponentHandlers.Clear(); + m_Components.Sort(); + m_ComponentMaps.Clear(); + foreach (var component in m_Components) + { + var type = component.GetType(); + List<MainComponent> list; + if (!m_ComponentMaps.TryGetValue(type, out list)) + { + list = new List<MainComponent>(); + m_ComponentMaps[type] = list; + } + component.index = list.Count; + list.Add(component); + CreateComponentHandler(component); + } + } + + protected override void CheckRefreshChart() + { + if (m_Painter == null) return; + if (m_RefreshChart) + { + CheckRefreshPainter(); + m_RefreshChart = false; + } + } + + protected override void CheckRefreshPainter() + { + if (m_Painter == null) return; + m_Painter.CheckRefresh(); + foreach (var painter in m_PainterList) painter.CheckRefresh(); + if (m_PainterUpper != null) m_PainterUpper.CheckRefresh(); + if (m_PainterTop != null) m_PainterTop.CheckRefresh(); + } + + public void Internal_CheckAnimation() + { + if (!m_CheckAnimation) + { + m_CheckAnimation = true; + AnimationFadeIn(); + } + } + + protected override void OnSizeChanged() + { + base.OnSizeChanged(); + m_ChartWidth = m_GraphWidth; + m_ChartHeight = m_GraphHeight; + m_ChartX = m_GraphX; + m_ChartY = m_GraphY; + m_ChartPosition = m_GraphPosition; + m_ChartMinAnchor = m_GraphMinAnchor; + m_ChartMaxAnchor = m_GraphMaxAnchor; + m_ChartPivot = m_GraphPivot; + m_ChartSizeDelta = m_GraphSizeDelta; + m_ChartRect = m_GraphRect; + SetAllComponentDirty(); + OnCoordinateChanged(); + RefreshChart(); + } + + internal virtual void OnSerieDataUpdate(int serieIndex) + { + foreach (var handler in m_ComponentHandlers) handler.OnSerieDataUpdate(serieIndex); + } + + internal virtual void OnCoordinateChanged() + { + foreach (var component in m_Components) + { + if (component is Axis) + component.SetAllDirty(); + if (component is IUpdateRuntimeData) + (component as IUpdateRuntimeData).UpdateRuntimeData(this); + } + } + + protected override void OnLocalPositionChanged() + { + Background background; + if (TryGetChartComponent<Background>(out background)) + background.SetAllDirty(); + } + + protected virtual void OnThemeChanged() { } + + public virtual void OnDataZoomRangeChanged(DataZoom dataZoom) + { + foreach (var index in dataZoom.xAxisIndexs) + { + var axis = GetChartComponent<XAxis>(index); + if (axis != null && axis.show) axis.SetAllDirty(); + } + foreach (var index in dataZoom.yAxisIndexs) + { + var axis = GetChartComponent<YAxis>(index); + if (axis != null && axis.show) axis.SetAllDirty(); + } + } + + public override void OnPointerClick(PointerEventData eventData) + { + m_DebugInfo.clickChartCount++; + base.OnPointerClick(eventData); + foreach (var handler in m_SerieHandlers) handler.OnPointerClick(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnPointerClick(eventData); + } + + public override void OnPointerDown(PointerEventData eventData) + { + base.OnPointerDown(eventData); + foreach (var handler in m_SerieHandlers) handler.OnPointerDown(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnPointerDown(eventData); + } + + public override void OnPointerUp(PointerEventData eventData) + { + base.OnPointerUp(eventData); + foreach (var handler in m_SerieHandlers) handler.OnPointerUp(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnPointerUp(eventData); + } + + public override void OnPointerEnter(PointerEventData eventData) + { + base.OnPointerEnter(eventData); + foreach (var handler in m_SerieHandlers) handler.OnPointerEnter(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnPointerEnter(eventData); + } + + public override void OnPointerExit(PointerEventData eventData) + { + base.OnPointerExit(eventData); + foreach (var handler in m_SerieHandlers) handler.OnPointerExit(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnPointerExit(eventData); + } + + public override void OnBeginDrag(PointerEventData eventData) + { + base.OnBeginDrag(eventData); + foreach (var handler in m_SerieHandlers) handler.OnBeginDrag(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnBeginDrag(eventData); + } + + public override void OnDrag(PointerEventData eventData) + { + base.OnDrag(eventData); + foreach (var handler in m_SerieHandlers) handler.OnDrag(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnDrag(eventData); + } + + public override void OnEndDrag(PointerEventData eventData) + { + base.OnEndDrag(eventData); + foreach (var handler in m_SerieHandlers) handler.OnEndDrag(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnEndDrag(eventData); + } + + public override void OnScroll(PointerEventData eventData) + { + base.OnScroll(eventData); + foreach (var handler in m_SerieHandlers) handler.OnScroll(eventData); + foreach (var handler in m_ComponentHandlers) handler.OnScroll(eventData); + } + + public virtual void OnLegendButtonClick(int index, string legendName, bool show) + { + foreach (var handler in m_SerieHandlers) + handler.OnLegendButtonClick(index, legendName, show); + } + + public virtual void OnLegendButtonEnter(int index, string legendName) + { + foreach (var handler in m_SerieHandlers) + handler.OnLegendButtonEnter(index, legendName); + } + + public virtual void OnLegendButtonExit(int index, string legendName) + { + foreach (var handler in m_SerieHandlers) + handler.OnLegendButtonExit(index, legendName); + } + + protected override void OnDrawPainterBase(VertexHelper vh, Painter painter) + { + vh.Clear(); + DrawBackground(vh); + DrawPainterBase(vh); + foreach (var handler in m_ComponentHandlers) handler.DrawBase(vh); + foreach (var handler in m_SerieHandlers) handler.DrawBase(vh); + if (m_OnDrawBase != null) + { + m_OnDrawBase(vh); + } + m_BasePainterVertCount = vh.currentVertCount; + } + + protected virtual void OnDrawPainterSerie(VertexHelper vh, Painter painter) + { + vh.Clear(); + var maxPainter = settings.maxPainter; + var maxSeries = m_Series.Count; + var rate = Mathf.CeilToInt(maxSeries * 1.0f / maxPainter); + m_PainterUpper.Refresh(); + m_PainterTop.Refresh(); + m_DebugInfo.refreshCount++; + for (int i = painter.index * rate; i < (painter.index + 1) * rate && i < maxSeries; i++) + { + var serie = m_Series[i]; + serie.context.colorIndex = GetLegendRealShowNameIndex(serie.legendName); + serie.context.dataPoints.Clear(); + serie.context.dataIndexs.Clear(); + serie.context.dataIgnores.Clear(); + serie.animation.context.isAllItemAnimationEnd = true; + if (serie.show && !serie.animation.HasFadeOut()) + { + if (m_OnDrawSerieBefore != null) + { + m_OnDrawSerieBefore.Invoke(vh, serie); + } + DrawPainterSerie(vh, serie); + if (i >= 0 && i < m_SerieHandlers.Count) + { + var handler = m_SerieHandlers[i]; + handler.DrawSerie(vh); + handler.RefreshLabelNextFrame(); + } + if (m_OnDrawSerieAfter != null) + { + m_OnDrawSerieAfter(vh, serie); + } + } + serie.context.vertCount = vh.currentVertCount; + } + } + + protected virtual void OnDrawPainterUpper(VertexHelper vh, Painter painter) + { + vh.Clear(); + DrawPainterUpper(vh); + foreach (var draw in m_ComponentHandlers) draw.DrawUpper(vh); + if (m_OnDrawUpper != null) + { + m_OnDrawUpper(vh); + } + m_UpperPainterVertCount = vh.currentVertCount; + } + + protected virtual void OnDrawPainterTop(VertexHelper vh, Painter painter) + { + vh.Clear(); + DrawPainterTop(vh); + foreach (var draw in m_ComponentHandlers) draw.DrawTop(vh); + if (m_OnDrawTop != null) + { + m_OnDrawTop(vh); + } + m_TopPainterVertCount = vh.currentVertCount; + } + + protected virtual void DrawPainterSerie(VertexHelper vh, Serie serie) { } + + protected virtual void DrawPainterUpper(VertexHelper vh) + { + foreach (var handler in m_SerieHandlers) + handler.DrawUpper(vh); + } + + protected virtual void DrawPainterTop(VertexHelper vh) + { + foreach (var handler in m_SerieHandlers) + handler.DrawTop(vh); + } + + protected virtual void DrawBackground(VertexHelper vh) + { + var background = GetChartComponent<Background>(); + if (background != null && background.show) + return; + Vector3 p1 = new Vector3(chartX, chartY + chartHeight); + Vector3 p2 = new Vector3(chartX + chartWidth, chartY + chartHeight); + Vector3 p3 = new Vector3(chartX + chartWidth, chartY); + Vector3 p4 = new Vector3(chartX, chartY); + UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, theme.backgroundColor); + } + + protected int GetPainterIndexBySerie(Serie serie) + { + var maxPainter = settings.maxPainter; + var maxSeries = m_Series.Count; + if (maxPainter >= maxSeries) return serie.index; + else + { + var rate = Mathf.CeilToInt(maxSeries * 1.0f / maxPainter); + return serie.index / rate; + } + } + + private void InitListForFieldInfos() + { + if (m_TypeListForSerie.Count != 0 || m_TypeListForComponent.Count != 0) return; + m_TypeListForComponent.Clear(); + m_TypeListForSerie.Clear(); + var fileds1 = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + var fileds2 = GetType().BaseType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + var list = ListPool<FieldInfo>.Get(); + list.AddRange(fileds1); + list.AddRange(fileds2); + foreach (var field in list) + { + var attribute1 = field.GetAttribute<ListForSerie>(false); + if (attribute1 != null) + m_TypeListForSerie.Add(attribute1.type, field); + + var attribute2 = field.GetAttribute<ListForComponent>(false); + if (attribute2 != null) + m_TypeListForComponent.Add(attribute2.type, field); + } + ListPool<FieldInfo>.Release(list); + } + + public void OnBeforeSerialize() + { +#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER + if (!UnityEditor.EditorUtility.IsDirty(this)) + return; + UnityEditor.EditorUtility.ClearDirty(this); +#endif + InitListForFieldInfos(); + foreach (var kv in m_TypeListForSerie) + { + ReflectionUtil.InvokeListClear(this, kv.Value); + } + foreach (var kv in m_TypeListForComponent) + { + ReflectionUtil.InvokeListClear(this, kv.Value); + } + foreach (var component in m_Components) + { + FieldInfo field; + if (m_TypeListForComponent.TryGetValue(component.GetType(), out field)) + ReflectionUtil.InvokeListAdd(this, field, component); + else + Debug.LogError("No ListForComponent:" + component.GetType()); + } + foreach (var serie in m_Series) + { + FieldInfo field; + serie.OnBeforeSerialize(); + if (m_TypeListForSerie.TryGetValue(serie.GetType(), out field)) + ReflectionUtil.InvokeListAdd(this, field, serie); + else + Debug.LogError("No ListForSerie:" + serie.GetType()); + } + } + + public void OnAfterDeserialize() + { + InitListForFieldInfos(); + m_Components.Clear(); + m_Series.Clear(); + foreach (var kv in m_TypeListForComponent) + { + ReflectionUtil.InvokeListAddTo<MainComponent>(this, kv.Value, AddComponent); + } + foreach (var kv in m_TypeListForSerie) + { + ReflectionUtil.InvokeListAddTo<Serie>(this, kv.Value, AddSerieAfterDeserialize); + } + m_Series.Sort(); + m_Components.Sort(); + InitComponentHandlers(); + InitSerieHandlers(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.cs.meta b/Assets/XCharts/Runtime/Internal/BaseChart.cs.meta new file mode 100644 index 0000000..25be7da --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseChart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5053a63a1ebdfe4f8972f194156c3d3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseGraph.API.cs b/Assets/XCharts/Runtime/Internal/BaseGraph.API.cs new file mode 100644 index 0000000..90c9adc --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseGraph.API.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections; +using UnityEngine; +using UnityEngine.EventSystems; + +namespace XCharts.Runtime +{ + /// <summary> + /// The base class of all graphs or components. + /// ||所有图形的基类。 + /// </summary> + public partial class BaseGraph + { + /// <summary> + /// The x of graph. + /// ||图形的X + /// </summary> + public float graphX { get { return m_GraphX; } } + /// <summary> + /// The y of graph. + /// ||图形的Y + /// </summary> + public float graphY { get { return m_GraphY; } } + /// <summary> + /// The width of graph. + /// ||图形的宽 + /// </summary> + public float graphWidth { get { return m_GraphWidth; } } + /// <summary> + /// The height of graph. + /// ||图形的高 + /// </summary> + public float graphHeight { get { return m_GraphHeight; } } + /// <summary> + /// The position of graph. + /// ||图形的左下角起始坐标。 + /// </summary> + public Vector3 graphPosition { get { return m_GraphPosition; } } + public Rect graphRect { get { return m_GraphRect; } } + public Vector2 graphSizeDelta { get { return m_GraphSizeDelta; } } + public Vector2 graphPivot { get { return m_GraphPivot; } } + public Vector2 graphMinAnchor { get { return m_GraphMinAnchor; } } + public Vector2 graphMaxAnchor { get { return m_GraphMaxAnchor; } } + public Vector2 graphAnchoredPosition { get { return m_GraphAnchoredPosition; } } + /// <summary> + /// The postion of pointer move. + /// ||鼠标位置。 + /// </summary> + public Vector2 pointerPos { get; protected set; } + public Vector2 clickPos { get; protected set; } + /// <summary> + /// Whether the mouse pointer is in the chart. + /// ||鼠标是否在图表内。 + /// </summary> + public bool isPointerInChart { get { return pointerMoveEventData != null; } } + /// <summary> + /// Whether the mouse click the chart. + /// ||鼠标是否点击了图表。 + /// </summary> + public bool isPointerClick { get { return pointerClickEventData != null; } } + /// <summary> + /// 警告信息。 + /// </summary> + public string warningInfo { get; protected set; } + /// <summary> + /// 强制开启鼠标事件检测。 + /// </summary> + public bool forceOpenRaycastTarget { get { return m_ForceOpenRaycastTarget; } set { m_ForceOpenRaycastTarget = value; } } + /// <summary> + /// 鼠标点击回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onPointerClick { set { m_OnPointerClick = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标按下回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onPointerDown { set { m_OnPointerDown = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标弹起回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onPointerUp { set { m_OnPointerUp = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标进入回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onPointerEnter { set { m_OnPointerEnter = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标退出回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onPointerExit { set { m_OnPointerExit = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标开始拖拽回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onBeginDrag { set { m_OnBeginDrag = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标拖拽回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onDrag { set { m_OnDrag = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标结束拖拽回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onEndDrag { set { m_OnEndDrag = value; m_ForceOpenRaycastTarget = true; } } + /// <summary> + /// 鼠标滚动回调。 + /// </summary> + public Action<PointerEventData, BaseGraph> onScroll { set { m_OnScroll = value; m_ForceOpenRaycastTarget = true; } } + + /// <summary> + /// 设置图形的宽高(在非stretch pivot下才有效,其他情况需要自己调整RectTransform) + /// </summary> + /// <param name="width"></param> + /// <param name="height"></param> + public virtual void SetSize(float width, float height) + { + if (LayerHelper.IsFixedWidthHeight(rectTransform)) + { + rectTransform.sizeDelta = new Vector2(width, height); + } + else + { + Debug.LogError("Can't set size on stretch pivot,you need to modify rectTransform by yourself."); + } + } + + /// <summary> + /// 重新初始化Painter + /// </summary> + public void SetPainterDirty() + { + m_PainerDirty = true; + } + + /// <summary> + /// Redraw graph in next frame. + /// ||在下一帧刷新图形。 + /// </summary> + public virtual void RefreshGraph() + { + m_RefreshChart = true; + } + + public void RefreshAllComponent() + { + SetAllComponentDirty(); + RefreshGraph(); + } + + /// <summary> + /// 检测警告信息。 + /// </summary> + /// <returns></returns> + public string CheckWarning() + { + warningInfo = CheckHelper.CheckChart(this); + return warningInfo; + } + + /// <summary> + /// 移除并重新创建所有图表的Object。 + /// </summary> + public void RebuildChartObject() + { + ChartHelper.DestoryGameObjectByMatch(transform, m_ChildNodeNames); + //SetAllComponentDirty(); + } + + public bool ScreenPointToChartPoint(Vector2 screenPoint, out Vector2 chartPoint) + { +#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX + var relative = Display.RelativeMouseAt(screenPoint); + if (relative != Vector3.zero) + screenPoint = relative; +#endif + var cam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; + if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, + screenPoint, cam, out chartPoint)) + { + return false; + } + return true; + } + + /// <summary> + /// chart local point to screen point. + /// ||图表内坐标转屏幕坐标。 + /// </summary> + /// <param name="localPoint">图表内的坐标</param> + /// <returns>屏幕坐标</returns> + [Since("v3.7.0")] + public Vector2 LocalPointToScreenPoint(Vector2 localPoint) + { + var cam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; + var wordPoint = rectTransform.TransformPoint(localPoint); + return RectTransformUtility.WorldToScreenPoint(cam, wordPoint); + } + + /// <summary> + /// chart local point to world point. + /// ||图表内坐标转世界坐标。 + /// </summary> + /// <param name="localPoint">图表内的坐标</param> + /// <returns>世界坐标</returns> + [Since("v3.7.0")] + public Vector2 LocalPointToWorldPoint(Vector2 localPoint) + { + return rectTransform.TransformPoint(localPoint); + } + + /// <summary> + /// 保存图表为图片。 + /// </summary> + /// <param name="imageType">type of image: png, jpg, exr</param> + /// <param name="savePath">save path</param> + public void SaveAsImage(string imageType = "png", string savePath = "") + { + StartCoroutine(SaveAsImageSync(imageType, savePath)); + } + + private IEnumerator SaveAsImageSync(string imageType, string path) + { + yield return new WaitForEndOfFrame(); + ChartHelper.SaveAsImage(rectTransform, canvas, imageType, path); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseGraph.API.cs.meta b/Assets/XCharts/Runtime/Internal/BaseGraph.API.cs.meta new file mode 100644 index 0000000..26a83ab --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseGraph.API.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46b27d174989044f3b63eaf0c3b21fcd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/BaseGraph.cs b/Assets/XCharts/Runtime/Internal/BaseGraph.cs new file mode 100644 index 0000000..8f0f765 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseGraph.cs @@ -0,0 +1,339 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +#if INPUT_SYSTEM_ENABLED +using Input = XCharts.Runtime.InputHelper; +#endif + +namespace XCharts.Runtime +{ + [RequireComponent(typeof(CanvasRenderer))] + public partial class BaseGraph : MaskableGraphic, IPointerDownHandler, IPointerUpHandler, + IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IPointerClickHandler, + IDragHandler, IEndDragHandler, IScrollHandler + { + [SerializeField] protected bool m_EnableTextMeshPro = false; + [SerializeField] protected List<string> m_ChildNodeNames = new List<string>(); + + protected Painter m_Painter; + protected int m_SiblingIndex; + + protected float m_GraphWidth; + protected float m_GraphHeight; + protected float m_GraphX; + protected float m_GraphY; + protected Vector3 m_GraphPosition = Vector3.zero; + protected Vector2 m_GraphMinAnchor; + protected Vector2 m_GraphMaxAnchor; + protected Vector2 m_GraphPivot; + protected Vector2 m_GraphSizeDelta; + protected Vector2 m_GraphAnchoredPosition; + protected Rect m_GraphRect = new Rect(0, 0, 0, 0); + protected bool m_RefreshChart = false; + protected bool m_ForceOpenRaycastTarget; + protected bool m_IsControlledByLayout = false; + protected bool m_PainerDirty = false; + protected bool m_IsOnValidate = false; + protected Vector3 m_LastLocalPosition; + internal PointerEventData pointerMoveEventData; + internal PointerEventData pointerClickEventData; + internal bool isTriggerOnClick = false; + + protected Action<PointerEventData, BaseGraph> m_OnPointerClick; + protected Action<PointerEventData, BaseGraph> m_OnPointerDown; + protected Action<PointerEventData, BaseGraph> m_OnPointerUp; + protected Action<PointerEventData, BaseGraph> m_OnPointerEnter; + protected Action<PointerEventData, BaseGraph> m_OnPointerExit; + protected Action<PointerEventData, BaseGraph> m_OnBeginDrag; + protected Action<PointerEventData, BaseGraph> m_OnDrag; + protected Action<PointerEventData, BaseGraph> m_OnEndDrag; + protected Action<PointerEventData, BaseGraph> m_OnScroll; + + public virtual HideFlags chartHideFlags { get { return HideFlags.None; } } + + private ScrollRect m_ScrollRect; + private Vector2 m_PointerDownPos; + + public Painter painter { get { return m_Painter; } } + public List<string> childrenNodeNames { get { return m_ChildNodeNames; } } + public bool isDragingClick { get; set; } + + protected virtual void InitComponent() + { + InitPainter(); + } + + protected override void Awake() + { + CheckTextMeshPro(); + m_SiblingIndex = 0; + m_LastLocalPosition = transform.localPosition; + UpdateSize(); + InitComponent(); + CheckIsInScrollRect(); + } + + protected override void Start() + { + m_RefreshChart = true; + } + + protected virtual void Update() + { + CheckSize(); + if (m_IsOnValidate) + { + m_IsOnValidate = false; + CheckTextMeshPro(); + InitComponent(); + RefreshGraph(); + } + else + { + CheckComponent(); + } + CheckPointerPos(); + CheckRefreshChart(); + CheckRefreshPainter(); + } + + protected virtual void SetAllComponentDirty() + { +#if UNITY_EDITOR + if (!Application.isPlaying) + { + m_IsOnValidate = true; + } +#endif + m_PainerDirty = true; + } + + protected virtual void CheckComponent() + { + if (m_PainerDirty) + { + InitPainter(); + m_PainerDirty = false; + } + } + + private void CheckTextMeshPro() + { +#if dUI_TextMeshPro + var enableTextMeshPro = true; +#else + var enableTextMeshPro = false; +#endif + if (m_EnableTextMeshPro != enableTextMeshPro) + { + m_EnableTextMeshPro = enableTextMeshPro; + RebuildChartObject(); + } + } + +#if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + } + + protected override void OnValidate() + { + base.OnValidate(); + m_IsOnValidate = true; + } +#endif + + protected override void OnDestroy() + { + base.OnDestroy(); + for (int i = transform.childCount - 1; i >= 0; i--) + { + DestroyImmediate(transform.GetChild(i).gameObject); + } + } + + protected override void OnPopulateMesh(VertexHelper vh) + { + vh.Clear(); + } + + protected virtual void InitPainter() + { + m_Painter = ChartHelper.AddPainterObject("painter_b", transform, m_GraphMinAnchor, + m_GraphMaxAnchor, m_GraphPivot, new Vector2(m_GraphWidth, m_GraphHeight), chartHideFlags, 1, m_ChildNodeNames); + m_Painter.type = Painter.Type.Base; + m_Painter.onPopulateMesh = OnDrawPainterBase; + m_Painter.transform.SetSiblingIndex(0); + } + + private void CheckSize() + { + var currWidth = rectTransform.rect.width; + var currHeight = rectTransform.rect.height; + + if (m_GraphWidth == 0 && m_GraphHeight == 0 && (currWidth != 0 || currHeight != 0)) + { + Awake(); + } + + if (m_GraphWidth != currWidth || + m_GraphHeight != currHeight || + m_GraphMinAnchor != rectTransform.anchorMin || + m_GraphMaxAnchor != rectTransform.anchorMax || + m_GraphAnchoredPosition != rectTransform.anchoredPosition) + { + UpdateSize(); + } + if (!ChartHelper.IsValueEqualsVector3(m_LastLocalPosition, transform.localPosition)) + { + m_LastLocalPosition = transform.localPosition; + OnLocalPositionChanged(); + } + } + + protected void UpdateSize() + { + m_GraphWidth = rectTransform.rect.width; + m_GraphHeight = rectTransform.rect.height; + + m_GraphMaxAnchor = rectTransform.anchorMax; + m_GraphMinAnchor = rectTransform.anchorMin; + m_GraphSizeDelta = rectTransform.sizeDelta; + m_GraphAnchoredPosition = rectTransform.anchoredPosition; + + rectTransform.pivot = LayerHelper.ResetChartPositionAndPivot(m_GraphMinAnchor, m_GraphMaxAnchor, + m_GraphWidth, m_GraphHeight, ref m_GraphX, ref m_GraphY); + m_GraphPivot = rectTransform.pivot; + + m_GraphRect.x = m_GraphX; + m_GraphRect.y = m_GraphY; + m_GraphRect.width = m_GraphWidth; + m_GraphRect.height = m_GraphHeight; + m_GraphPosition.x = m_GraphX; + m_GraphPosition.y = m_GraphY; + + OnSizeChanged(); + } + + private void CheckPointerPos() + { + if (canvas == null) return; + if (pointerMoveEventData != null) + { + pointerPos = MousePos2ChartPos(pointerMoveEventData.position); + } + } + + private Vector2 MousePos2ChartPos(Vector2 mousePos) + { + Vector2 local; + if (!ScreenPointToChartPoint(mousePos, out local)) + { + return Vector2.zero; + } + else + { + return local; + } + } + + protected virtual void CheckIsInScrollRect() + { + m_ScrollRect = GetComponentInParent<ScrollRect>(); + } + + protected virtual void CheckRefreshChart() + { + if (m_RefreshChart && m_Painter != null) + { + m_Painter.Refresh(); + m_RefreshChart = false; + } + } + + protected virtual void CheckRefreshPainter() + { + if (m_Painter == null) return; + m_Painter.CheckRefresh(); + } + + internal virtual void RefreshPainter(Painter painter) + { + if (painter == null) return; + painter.Refresh(); + } + + protected virtual void OnSizeChanged() + { + m_RefreshChart = true; + } + + protected virtual void OnLocalPositionChanged() { } + + protected virtual void OnDrawPainterBase(VertexHelper vh, Painter painter) + { + DrawPainterBase(vh); + } + + protected virtual void DrawPainterBase(VertexHelper vh) { } + + public virtual void OnPointerClick(PointerEventData eventData) + { + pointerClickEventData = eventData; + clickPos = MousePos2ChartPos(pointerClickEventData.position); + if (m_OnPointerClick != null) m_OnPointerClick(eventData, this); + } + + public virtual void OnPointerDown(PointerEventData eventData) + { + m_PointerDownPos = eventData.position; + if (m_OnPointerDown != null) m_OnPointerDown(eventData, this); + } + + public virtual void OnPointerUp(PointerEventData eventData) + { + isDragingClick = Vector2.Distance(eventData.position, m_PointerDownPos) > 6; + if (m_OnPointerUp != null) m_OnPointerUp(eventData, this); + } + + public virtual void OnPointerEnter(PointerEventData eventData) + { + pointerMoveEventData = eventData; + if (m_OnPointerEnter != null) m_OnPointerEnter(eventData, this); + } + + public virtual void OnPointerExit(PointerEventData eventData) + { + pointerMoveEventData = null; + pointerClickEventData = null; + if (m_OnPointerExit != null) m_OnPointerExit(eventData, this); + } + + public virtual void OnBeginDrag(PointerEventData eventData) + { + if (m_ScrollRect != null) m_ScrollRect.OnBeginDrag(eventData); + if (m_OnBeginDrag != null) m_OnBeginDrag(eventData, this); + } + + public virtual void OnEndDrag(PointerEventData eventData) + { + if (m_ScrollRect != null) m_ScrollRect.OnEndDrag(eventData); + if (m_OnEndDrag != null) m_OnEndDrag(eventData, this); + } + + public virtual void OnDrag(PointerEventData eventData) + { + if (m_ScrollRect != null) m_ScrollRect.OnDrag(eventData); + if (m_OnDrag != null) m_OnDrag(eventData, this); + } + + public virtual void OnScroll(PointerEventData eventData) + { + if (m_ScrollRect != null) m_ScrollRect.OnScroll(eventData); + if (m_OnScroll != null) m_OnScroll(eventData, this); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/BaseGraph.cs.meta b/Assets/XCharts/Runtime/Internal/BaseGraph.cs.meta new file mode 100644 index 0000000..fb3c60e --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/BaseGraph.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f059825ead3b4a7da7f1fbcebbf545e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Basic.meta b/Assets/XCharts/Runtime/Internal/Basic.meta new file mode 100644 index 0000000..e554ca9 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31e8b0503e55d41f0bf3baab818d0dfa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs b/Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs new file mode 100644 index 0000000..16ade89 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public abstract class BaseSerie + { + public virtual bool vertsDirty { get { return m_VertsDirty; } } + public virtual bool componentDirty { get { return m_ComponentDirty; } } + + public virtual SerieColorBy defaultColorBy { get { return SerieColorBy.Serie; } } + public virtual bool titleJustForSerie { get { return false; } } + public virtual bool useSortData { get { return false; } } + public virtual bool multiDimensionLabel { get { return false; } } + public bool anyDirty { get { return vertsDirty || componentDirty; } } + public Painter painter { get { return m_Painter; } set { m_Painter = value; } } + public Action refreshComponent { get; set; } + public GameObject gameObject { get; set; } + + [NonSerialized] protected bool m_VertsDirty; + [NonSerialized] protected bool m_ComponentDirty; + [NonSerialized] protected Painter m_Painter; + [NonSerialized] public SerieContext context = new SerieContext(); + [NonSerialized] public InteractData interact = new InteractData(); + + public SerieHandler handler { get; set; } + + + + public static void ClearVerticesDirty(ChildComponent component) + { + if (component != null) + component.ClearVerticesDirty(); + } + + public static void ClearComponentDirty(ChildComponent component) + { + if (component != null) + component.ClearComponentDirty(); + } + + public static bool IsVertsDirty(ChildComponent component) + { + return component == null?false : component.vertsDirty; + } + + public static bool IsComponentDirty(ChildComponent component) + { + return component == null?false : component.componentDirty; + } + + public virtual void SetVerticesDirty() + { + m_VertsDirty = true; + } + + public virtual void ClearVerticesDirty() + { + m_VertsDirty = false; + } + + public virtual void SetComponentDirty() + { + m_ComponentDirty = true; + } + + public virtual void ClearComponentDirty() + { + m_ComponentDirty = false; + } + + public virtual void ClearData() { } + + public virtual void ClearDirty() + { + ClearVerticesDirty(); + ClearComponentDirty(); + } + + public virtual void SetAllDirty() + { + SetVerticesDirty(); + SetComponentDirty(); + } + + public virtual void OnRemove() + { + if (handler != null) + handler.RemoveComponent(); + } + + public virtual void OnDataUpdate() { } + + public virtual void OnBeforeSerialize() { } + + public virtual void OnAfterDeserialize() + { + OnDataUpdate(); + } + + public void RefreshLabel() + { + if (handler != null) + handler.RefreshLabelNextFrame(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs.meta b/Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs.meta new file mode 100644 index 0000000..7280559 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/BaseSerie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28d00b46c33234f0ab88a5756f63679b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs b/Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs new file mode 100644 index 0000000..ce89da2 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs @@ -0,0 +1,85 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class ChildComponent + { + public virtual int index { get; set; } + + [NonSerialized] protected bool m_VertsDirty; + [NonSerialized] protected bool m_ComponentDirty; + [NonSerialized] protected Painter m_Painter; + + /// <summary> + /// 图表重绘标记。 + /// </summary> + public virtual bool vertsDirty { get { return m_VertsDirty; } } + /// <summary> + /// 组件重新初始化标记。 + /// </summary> + public virtual bool componentDirty { get { return m_ComponentDirty; } } + /// <summary> + /// 需要重绘图表或重新初始化组件。 + /// </summary> + public bool anyDirty { get { return vertsDirty || componentDirty; } } + public Painter painter { get { return m_Painter; } set { m_Painter = value; } } + public Action refreshComponent { get; set; } + public GameObject gameObject { get; set; } + + public static void ClearVerticesDirty(ChildComponent component) + { + if (component != null) + component.ClearVerticesDirty(); + } + + public static void ClearComponentDirty(ChildComponent component) + { + if (component != null) + component.ClearComponentDirty(); + } + + public static bool IsVertsDirty(ChildComponent component) + { + return component == null?false : component.vertsDirty; + } + + public static bool IsComponentDirty(ChildComponent component) + { + return component == null?false : component.componentDirty; + } + + public virtual void SetVerticesDirty() + { + m_VertsDirty = true; + } + + public virtual void ClearVerticesDirty() + { + m_VertsDirty = false; + } + + public virtual void SetComponentDirty() + { + m_ComponentDirty = true; + } + + public virtual void ClearComponentDirty() + { + m_ComponentDirty = false; + } + + public virtual void ClearDirty() + { + ClearVerticesDirty(); + ClearComponentDirty(); + } + + public virtual void SetAllDirty() + { + SetVerticesDirty(); + SetComponentDirty(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs.meta b/Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs.meta new file mode 100644 index 0000000..8ffda00 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/ChildComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 358324a6b44cb4b35b4393ecf2458993 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs b/Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs new file mode 100644 index 0000000..6f01f47 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs @@ -0,0 +1,13 @@ +using System; + +namespace XCharts.Runtime +{ + /// <summary> + /// Coordinate system component. + /// || + /// 坐标系系统。 + /// </summary> + [Serializable] + public abstract class CoordSystem : MainComponent + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs.meta b/Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs.meta new file mode 100644 index 0000000..c74fa3d --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/CoordSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60a0ecce780d64885aa5875dae39aa03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs b/Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs new file mode 100644 index 0000000..843b704 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs @@ -0,0 +1,129 @@ +using System; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class MainComponent : IComparable + { + public int instanceId { get { return index; } } + public int index { get; internal set; } + protected bool m_VertsDirty; + protected bool m_ComponentDirty; + protected Painter m_Painter; + + /// <summary> + /// 图表重绘标记。 + /// </summary> + public virtual bool vertsDirty { get { return m_VertsDirty; } } + /// <summary> + /// 组件重新初始化标记。 + /// </summary> + public virtual bool componentDirty { get { return m_ComponentDirty; } } + /// <summary> + /// 需要重绘图表或重新初始化组件。 + /// </summary> + public bool anyDirty { get { return vertsDirty || componentDirty; } } + public Painter painter { get { return m_Painter; } set { m_Painter = value; } } + public Action refreshComponent { get; set; } + public GameObject gameObject { get; set; } + internal MainComponentHandler handler { get; set; } + + public virtual void SetVerticesDirty() + { + m_VertsDirty = true; + } + + public virtual void ClearVerticesDirty() + { + m_VertsDirty = false; + } + + public virtual void SetComponentDirty() + { + m_ComponentDirty = true; + } + + public virtual void ClearComponentDirty() + { + m_ComponentDirty = false; + } + + public virtual void Reset() { } + + public virtual void ResetStatus() { } + + public virtual void ClearData() { } + + public virtual void ClearDirty() + { + ClearVerticesDirty(); + ClearComponentDirty(); + } + + public virtual void SetAllDirty() + { + SetVerticesDirty(); + SetComponentDirty(); + } + + public virtual void SetDefaultValue() { } + + public virtual void OnRemove() + { + if (handler != null) + handler.RemoveComponent(); + } + + public int CompareTo(object obj) + { + var flag = GetType().Name.CompareTo(obj.GetType().Name); + if (flag == 0) + return index.CompareTo((obj as MainComponent).index); + else + return flag; + } + } + + public abstract class MainComponentHandler + { + public int order { get; internal set; } + public BaseChart chart { get; internal set; } + public ComponentHandlerAttribute attribute { get; internal set; } + public bool inited { get; internal set; } + + public virtual void InitComponent() { } + public virtual void RemoveComponent() { } + public virtual void CheckComponent(StringBuilder sb) { } + public virtual void BeforceSerieUpdate() { } + public virtual void Update() { } + public virtual void DrawBase(VertexHelper vh) { } + public virtual void DrawUpper(VertexHelper vh) { } + public virtual void DrawTop(VertexHelper vh) { } + public virtual void OnSerieDataUpdate(int serieIndex) { } + public virtual void OnPointerClick(PointerEventData eventData) { } + public virtual void OnPointerDown(PointerEventData eventData) { } + public virtual void OnPointerUp(PointerEventData eventData) { } + public virtual void OnPointerEnter(PointerEventData eventData) { } + public virtual void OnPointerExit(PointerEventData eventData) { } + public virtual void OnDrag(PointerEventData eventData) { } + public virtual void OnBeginDrag(PointerEventData eventData) { } + public virtual void OnEndDrag(PointerEventData eventData) { } + public virtual void OnScroll(PointerEventData eventData) { } + internal abstract void SetComponent(MainComponent component); + } + + public abstract class MainComponentHandler<T> : MainComponentHandler + where T : MainComponent + { + public T component { get; internal set; } + + internal override void SetComponent(MainComponent component) + { + this.component = (T)component; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs.meta b/Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs.meta new file mode 100644 index 0000000..7bd0ad6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/MainComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45df06cc65b1844bab6fe52ef5d782e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs b/Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs new file mode 100644 index 0000000..02e10f7 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs @@ -0,0 +1,7 @@ +namespace XCharts.Runtime +{ + public class MainComponentContext + { + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs.meta b/Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs.meta new file mode 100644 index 0000000..d4f9308 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Basic/MainComponentContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2f85d9a16a84b474993b84d0e705bbcf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Data.meta b/Assets/XCharts/Runtime/Internal/Data.meta new file mode 100644 index 0000000..2715837 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fcb8d4f1ad56b432f8a8eae9fa5941b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Data/GraphData.cs b/Assets/XCharts/Runtime/Internal/Data/GraphData.cs new file mode 100644 index 0000000..269f4ef --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Data/GraphData.cs @@ -0,0 +1,520 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the data struct of graph. + /// ||数据结构-图。 + /// </summary> + public class GraphData + { + public bool directed; + public List<GraphNode> nodes = new List<GraphNode>(); + public List<GraphEdge> edges = new List<GraphEdge>(); + + public Dictionary<string, GraphNode> nodeMap = new Dictionary<string, GraphNode>(); + public Dictionary<string, GraphEdge> edgeMap = new Dictionary<string, GraphEdge>(); + + public GraphData(bool directed) + { + this.directed = directed; + } + + public void Clear() + { + nodes.Clear(); + edges.Clear(); + nodeMap.Clear(); + edgeMap.Clear(); + } + + public void Refresh() + { + foreach (var node in nodes) + { + node.depth = GetNodeDepth(node); + } + } + + public static double GetNodesTotalValue(List<GraphNode> nodes) + { + double totalValue = 0; + foreach (var node in nodes) + { + if (node.IsAnyInEdgesExpanded()) + { + totalValue += node.totalValues; + } + } + return totalValue; + } + + public static int GetExpandedNodesCount(List<GraphNode> nodes) + { + int count = 0; + foreach (var node in nodes) + { + if (node.IsAnyInEdgesExpanded()) + { + count++; + } + } + return count; + } + + public List<List<GraphNode>> GetDepthNodes() + { + List<List<GraphNode>> depthNodes = new List<List<GraphNode>>(); + var maxDepth = GetMaxDepth(); + for (int i = 0; i <= maxDepth; i++) + { + depthNodes.Add(new List<GraphNode>()); + } + foreach (var node in nodes) + { + if (node.inDegree == 0) + { + depthNodes[0].Add(node); + } + else + { + int deep = GetNodeDepth(node); + depthNodes[maxDepth - deep].Add(node); + } + } + return depthNodes; + } + + public List<GraphNode> GetRootNodes() + { + List<GraphNode> rootNodes = new List<GraphNode>(); + foreach (var node in nodes) + { + if (node.inDegree == 0) + { + rootNodes.Add(node); + } + } + return rootNodes; + } + + public int GetMaxDepth() + { + int maxDepth = 0; + foreach (var node in nodes) + { + int deep = GetNodeDepth(node); + if (deep > maxDepth) + { + maxDepth = deep; + } + } + return maxDepth; + } + + // public int GetNodeDepth(GraphNode node) + // { + // int depth = 0; + // GetNodeDepth(node, ref depth); + // return depth; + // } + + // public void GetNodeDepth(GraphNode node, ref int depth, int recursiveCount = 0) + // { + // if (recursiveCount > 50) + // { + // XLog.Error("GraphData.GetNodeDeep(): recursiveCount > 50, maybe graph is ring"); + // return; + // } + // if (node.inDegree == 0) + // { + // return; + // } + // else + // { + // depth += 1; + // foreach (var edge in node.inEdges) + // { + // GetNodeDepth(edge.node1, ref depth, recursiveCount + 1); + // } + // } + // } + + public int GetNodeDepth(GraphNode node, int recursiveCount = 0) + { + if (recursiveCount > 50) + { + XLog.Error("GraphData.GetNodeDeep(): recursiveCount > 50, maybe graph is ring"); + return 0; + } + int depth = 0; + if (node.outDegree == 0) + { + return depth; + } + else + { + foreach (var edge in node.outEdges) + { + int otherDeep = GetNodeDepth(edge.node2, recursiveCount + 1); + if (otherDeep > depth) + { + depth = otherDeep; + } + } + return depth + 1; + } + } + + + + public GraphNode GetNode(string nodeId) + { + if (nodeMap.ContainsKey(nodeId)) + { + return nodeMap[nodeId]; + } + else + { + return null; + } + } + + public GraphEdge GetEdge(string nodeId1, string nodeId2) + { + if (directed) + { + return edgeMap[nodeId1 + "_" + nodeId2]; + } + else + { + var key = nodeId1 + "_" + nodeId2; + if (edgeMap.ContainsKey(key)) + { + return edgeMap[key]; + } + else + { + key = nodeId2 + "_" + nodeId1; + if (edgeMap.ContainsKey(key)) + { + return edgeMap[key]; + } + else + { + return null; + } + } + } + } + + public GraphNode AddNode(string nodeId, string nodeName, int dataIndex, double value) + { + if (nodeMap.ContainsKey(nodeId)) + { + return nodeMap[nodeId]; + } + else + { + GraphNode node = new GraphNode(nodeId, nodeName, dataIndex); + node.hostGraph = this; + nodeMap.Add(nodeId, node); + nodes.Add(node); + return node; + } + } + + public GraphEdge AddEdge(string nodeId1, string nodeId2, double value) + { + GraphNode node1, node2; + if (!nodeMap.TryGetValue(nodeId1, out node1)) + { + XLog.Warning("GraphData.AddEdge(): " + nodeId1 + " not exist"); + return null; + } + if (!nodeMap.TryGetValue(nodeId2, out node2)) + { + XLog.Warning("GraphData.AddEdge(): " + nodeId2 + " not exist"); + return null; + } + if (node1 == null) + { + XLog.Warning("GraphData.AddEdge(): node1 is null"); + return null; + } + if (node2 == null) + { + XLog.Warning("GraphData.AddEdge(): node2 is null"); + return null; + } + if (directed && node1 == node2) + { + XLog.Warning("GraphData.AddEdge(): node1 == node2:" + node1); + return null; + } + string edgeKey = nodeId1 + "_" + nodeId2; + if (edgeMap.ContainsKey(edgeKey)) + { + return edgeMap[edgeKey]; + } + else + { + GraphEdge edge = new GraphEdge(node1, node2, value); + edge.key = edgeKey; + edge.hostGraph = this; + + if (directed) + { + node1.outEdges.Add(edge); + node2.inEdges.Add(edge); + } + node1.edges.Add(edge); + if (node1 != node2) + { + node2.edges.Add(edge); + } + + edgeMap.Add(edgeKey, edge); + edges.Add(edge); + return edge; + } + } + + public void EachNode(System.Action<GraphNode> onEach) + { + if (onEach == null) return; + foreach (var node in nodes) + { + onEach(node); + } + } + + public void BreadthFirstTraverse(GraphNode startNode, System.Action<GraphNode> onTraverse) + { + if (startNode == null) return; + foreach (var node in nodes) + { + node.visited = false; + } + + onTraverse(startNode); + startNode.visited = true; + + Queue<GraphNode> queue = new Queue<GraphNode>(); + queue.Enqueue(startNode); + while (queue.Count > 0) + { + var currentNode = queue.Dequeue(); + foreach (var edge in currentNode.edges) + { + var otherNode = edge.node1 == currentNode ? edge.node2 : edge.node1; + if (!otherNode.visited) + { + onTraverse(otherNode); + otherNode.visited = true; + queue.Enqueue(otherNode); + } + } + } + } + + public void DeepFirstTraverse(GraphNode startNode, System.Action<GraphNode> onTraverse) + { + if (startNode == null) return; + foreach (var node in nodes) + { + node.visited = false; + } + + Stack<GraphNode> stack = new Stack<GraphNode>(); + stack.Push(startNode); + while (stack.Count > 0) + { + var currentNode = stack.Pop(); + if (!currentNode.visited) + { + onTraverse(currentNode); + currentNode.visited = true; + } + foreach (var edge in currentNode.edges) + { + var otherNode = edge.node1 == currentNode ? edge.node2 : edge.node1; + if (!otherNode.visited) + { + stack.Push(otherNode); + } + } + } + } + + public void ExpandNode(string nodeId, bool flag) + { + var node = GetNode(nodeId); + if (node != null) + { + node.Expand(flag); + } + } + + public void ExpandAllNodes(bool flag, int level = -1) + { + foreach (var node in nodes) + { + if (level < 0 || node.level == level) + { + node.Expand(flag); + } + } + } + + public bool IsAllNodeInZeroPosition() + { + foreach (var node in nodes) + { + if (node.position != Vector3.zero) return false; + } + return true; + } + } + + /// <summary> + /// The node of graph. + /// ||图的节点。 + /// </summary> + public class GraphNode + { + public string id; + public string name; + public double value; + public List<GraphEdge> edges = new List<GraphEdge>(); + public List<GraphEdge> inEdges = new List<GraphEdge>(); + public List<GraphEdge> outEdges = new List<GraphEdge>(); + public GraphData hostGraph; + public int dataIndex; + public bool visited; + public int depth = -1; + public bool expand = true; + public int level = 0; + public Vector3 position = Vector3.zero; + public Vector3 pp = Vector3.zero; + public float weight; + public float repulsion; + + public GraphNode(string id, string name, int dataIndex) + { + this.id = id; + this.name = name; + this.dataIndex = dataIndex; + } + + public int degree { get { return edges.Count; } } + + public int inDegree { get { return inEdges.Count; } } + + public int outDegree { get { return outEdges.Count; } } + + public double totalValues + { + get + { + double totalValue = 0; + if (inEdges.Count == 0) + { + foreach (var edge in outEdges) + { + totalValue += edge.value; + } + } + else + { + foreach (var edge in inEdges) + { + totalValue += edge.value; + } + } + return totalValue; + } + } + public override string ToString() + { + return name; + } + + public bool IsAllInEdgesCollapsed() + { + if (inEdges.Count == 0) return false; + foreach (var edge in inEdges) + { + if (!edge.expand) return false; + } + return true; + } + + public bool IsAnyInEdgesExpanded() + { + if (inEdges.Count == 0) return true; + foreach (var edge in inEdges) + { + if (edge.expand) return true; + } + return false; + } + + public void Expand(bool flag) + { + if (expand == flag) return; + expand = flag; + foreach (var edge in outEdges) + { + edge.expand = flag; + } + } + } + + /// <summary> + /// The edge of graph. + /// ||图的边。 + /// </summary> + public class GraphEdge + { + public string key; + public GraphNode node1; + public GraphNode node2; + public double value; + public GraphData hostGraph; + + public List<Vector3> upPoints = new List<Vector3>(); + public List<Vector3> downPoints = new List<Vector3>(); + public float width; + public float distance; + public bool highlight; + public bool expand = true; + + public GraphEdge(GraphNode node1, GraphNode node2, double value) + { + this.node1 = node1; + this.node2 = node2; + this.value = value; + } + + public bool IsPointInEdge(Vector2 point) + { + if (upPoints.Count == 0 || downPoints.Count == 0) return false; + var lastCount = upPoints.Count - 1; + if (point.x < upPoints[0].x || point.x > upPoints[lastCount].x) return false; + if (point.y > upPoints[0].y && point.y > upPoints[lastCount].y) return false; + if (point.y < downPoints[0].y && point.y < downPoints[lastCount].y) return false; + + for (int i = 0; i < upPoints.Count - 1; i++) + { + var diff = point.x - upPoints[i].x; + if (diff <= 0) + { + return point.y < upPoints[i].y && point.y > downPoints[i].y; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Data/GraphData.cs.meta b/Assets/XCharts/Runtime/Internal/Data/GraphData.cs.meta new file mode 100644 index 0000000..bb2ee87 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Data/GraphData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d8951dd10b1247c9baf8515b3a22771 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc.meta b/Assets/XCharts/Runtime/Internal/Misc.meta new file mode 100644 index 0000000..89f5d7b --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7c3110bdc66d84fd6b59aa8c6843f5e3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs b/Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs new file mode 100644 index 0000000..0ec6fd5 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// The delegate function for LabelStyle‘s formatter. + /// ||SerieLabel的formatter自定义委托。 + /// </summary> + /// <param name="dataIndex">数据索引</param> + /// <param name="value">数值</param> + /// <param name="category">类目</param> + /// <param name="content">当前内容</param> + /// <returns>最终显示的文本内容</returns> + public delegate string LabelFormatterFunction(int dataIndex, double value, string category, string content); + public delegate float AnimationDelayFunction(int dataIndex); + public delegate float AnimationDurationFunction(int dataIndex); + /// <summary> + /// 获取标记大小的回调。 + /// </summary> + public delegate float SymbolSizeFunction(float defaultSize, SerieData serieData); + public delegate void CustomDrawGaugePointerFunction(VertexHelper vh, int serieIndex, int dataIndex, float currentAngle); + /// <summary> + /// DataZoom的start和end变更时的委托方法。 + /// </summary> + /// <param name="start"></param> + /// <param name="end"></param> + public delegate void CustomDataZoomStartEndFunction(ref float start, ref float end); +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs.meta new file mode 100644 index 0000000..27b82fb --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/DelegateFunction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d9ec774e2e5b4d9ba407a27f60b6d71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/Enums.cs b/Assets/XCharts/Runtime/Internal/Misc/Enums.cs new file mode 100644 index 0000000..c6761c2 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/Enums.cs @@ -0,0 +1,18 @@ +namespace XCharts.Runtime +{ + /// <summary> + /// the layout is horizontal or vertical. + /// ||垂直还是水平布局方式。 + /// </summary> + public enum Orient + { + /// <summary> + /// 水平 + /// </summary> + Horizonal, + /// <summary> + /// 垂直 + /// </summary> + Vertical + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/Enums.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/Enums.cs.meta new file mode 100644 index 0000000..e27ae4c --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/Enums.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 124ff8824480945229e367921154d13a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs b/Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs new file mode 100644 index 0000000..c979d5d --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs @@ -0,0 +1,13 @@ +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public interface INeedSerieContainer + { + int containerIndex { get; } + int containterInstanceId { get; } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs.meta new file mode 100644 index 0000000..f2dd8fc --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/INeedSerieContainer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69c33f4520abf483585632a17268a9a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs b/Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs new file mode 100644 index 0000000..34f2e35 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs @@ -0,0 +1,10 @@ +namespace XCharts.Runtime +{ + /// <summary> + /// 属性变更接口 + /// </summary> + public interface IPropertyChanged + { + void OnChanged(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs.meta new file mode 100644 index 0000000..6521200 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/IPropertyChanged.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24f32e2d632f08245ae885545f14a2a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs b/Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs new file mode 100644 index 0000000..03d7fd0 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs @@ -0,0 +1,11 @@ +namespace XCharts.Runtime +{ + /// <summary> + /// The interface for serie component. + /// ||可用于Serie的组件。 + /// </summary> + public interface ISerieComponent + { + bool show { get; set; } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs.meta new file mode 100644 index 0000000..d9d9a3c --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISerieComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20d76dbb8ca234b439951f6e72826c43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs b/Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs new file mode 100644 index 0000000..53300a5 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs @@ -0,0 +1,8 @@ +namespace XCharts.Runtime +{ + public interface ISerieContainer + { + int index { get; } + bool IsPointerEnter(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs.meta new file mode 100644 index 0000000..2a7b0d5 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISerieContainer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d02195c119c14384903aa94daf21a1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs b/Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs new file mode 100644 index 0000000..35c1187 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs @@ -0,0 +1,10 @@ +namespace XCharts.Runtime +{ + /// <summary> + /// The interface for serie data component. + /// ||可用于SerieData的组件。 + /// </summary> + public interface ISerieDataComponent + { + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs.meta new file mode 100644 index 0000000..6e31f40 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISerieDataComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34105fa92849e42abab6320ce3ea540f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs b/Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs new file mode 100644 index 0000000..20afab9 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs @@ -0,0 +1,9 @@ +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public interface ISimplifiedSerie + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs.meta new file mode 100644 index 0000000..81f5854 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ISimplifiedSerie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd917380f26ed4fb393092a4017f9907 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs b/Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs.meta new file mode 100644 index 0000000..fecfc56 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/ITooltipView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5ec8a82e2f9043c5b2e0b880fb024b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs b/Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs new file mode 100644 index 0000000..cf4c019 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs @@ -0,0 +1,11 @@ +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public interface IUpdateRuntimeData + { + void UpdateRuntimeData(BaseChart chart); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs.meta new file mode 100644 index 0000000..15b89d6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/IUpdateRuntimeData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07abc4a18196a426a96d1ed14cbc7bf0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs b/Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs new file mode 100644 index 0000000..ea4ef1d --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs @@ -0,0 +1,46 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the data of serie event. + /// ||serie事件的数据。 + /// </summary> + public class SerieEventData + { + /// <summary> + /// the position of pointer in chart. + /// ||鼠标在chart中的位置。 + /// </summary> + public Vector3 pointerPos { get; set; } + /// <summary> + /// the index of serie in chart.series. + /// ||在chart.series中的索引。 + /// </summary> + public int serieIndex { get; set; } + /// <summary> + /// the index of data in serie.data. + /// ||在serie.data中的索引。 + /// </summary> + public int dataIndex { get; set; } + /// <summary> + /// the dimension of data. + /// ||数据的维度。 + /// </summary> + public int dimension { get; set; } + /// <summary> + /// the value of data. + /// ||数据的值。 + /// </summary> + public double value { get; set; } + + public void Reset() + { + serieIndex = -1; + dataIndex = -1; + dimension = -1; + value = 0; + pointerPos = Vector3.zero; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs.meta b/Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs.meta new file mode 100644 index 0000000..8d23ec0 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Misc/SerieEventData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdfaa773d93294d78b2fb4b8f42708a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Object.meta b/Assets/XCharts/Runtime/Internal/Object.meta new file mode 100644 index 0000000..011588c --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d225ec4fe992405d91714722649cc93 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs b/Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs new file mode 100644 index 0000000..c43a6c9 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs @@ -0,0 +1,359 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public class ChartLabel : Image + { + [SerializeField] private ChartText m_LabelText; + + private bool m_HideIconIfTextEmpty = false; + private bool m_AutoSize = true; + private float m_PaddingLeft = 0; + private float m_PaddingRight = 0; + private float m_PaddingTop = 0; + private float m_PaddingBottom = 0; + private float m_Width = 0; + private float m_Height = 0; + private RectTransform m_TextRect; + private RectTransform m_IconRect; + private RectTransform m_ObjectRect; + private Vector3 m_IconOffest; + private Align m_Align = Align.Left; + private Image m_IconImage; + private bool m_Active = true; + + public Image icon + { + get { return m_IconImage; } + set { SetIcon(value); } + } + public ChartText text + { + get { return m_LabelText; } + set + { + m_LabelText = value; + if (value != null) m_TextRect = m_LabelText.gameObject.GetComponent<RectTransform>(); + } + } + + public bool hideIconIfTextEmpty { set { m_HideIconIfTextEmpty = value; } } + public bool isIconActive { get; private set; } + public bool isAnimationEnd { get; internal set; } + public Rect rect { get; set; } + + internal RectTransform objectRect + { + get + { + if (m_ObjectRect == null) + m_ObjectRect = gameObject.GetComponent<RectTransform>(); + return m_ObjectRect; + } + } + + protected override void Awake() + { + raycastTarget = false; + m_Active = ChartHelper.IsActiveByScale(gameObject); + } + + public void SetTextPadding(TextPadding padding) + { + m_PaddingLeft = padding.left; + m_PaddingRight = padding.right; + m_PaddingTop = padding.top; + m_PaddingBottom = padding.bottom; + UpdatePadding(); + } + public void SetPadding(float[] padding) + { + if (padding.Length >= 4) + { + m_PaddingLeft = padding[3]; + m_PaddingRight = padding[1]; + m_PaddingTop = padding[0]; + m_PaddingBottom = padding[2]; + } + else if (padding.Length >= 2) + { + m_PaddingLeft = padding[1]; + m_PaddingRight = padding[1]; + m_PaddingTop = padding[0]; + m_PaddingBottom = padding[0]; + } + else if (padding.Length == 1) + { + m_PaddingLeft = padding[0]; + m_PaddingRight = padding[0]; + m_PaddingTop = padding[0]; + m_PaddingBottom = padding[0]; + } + UpdatePadding(); + } + + public void SetIcon(Image image) + { + m_IconImage = image; + if (image != null) + { + m_IconRect = m_IconImage.GetComponent<RectTransform>(); + } + } + + public float GetWidth() + { + return m_Width; + } + + public float GetHeight() + { + return m_Height; + } + + public void SetSize(float width, float height) + { + this.m_Width = width; + this.m_Height = height; + m_AutoSize = width == 0 && height == 0; + objectRect.sizeDelta = new Vector2(width, height); + } + + public void SetIconSprite(Sprite sprite) + { + if (m_IconImage != null) m_IconImage.sprite = sprite; + } + + public void SetIconSize(float width, float height) + { + if (m_IconRect != null) m_IconRect.sizeDelta = new Vector3(width, height); + } + + public void UpdateIcon(IconStyle iconStyle, Sprite sprite = null, Color color = default(Color)) + { + if (m_IconImage == null || iconStyle == null) + return; + + SetIconActive(iconStyle.show); + if (iconStyle.show) + { + m_IconImage.sprite = sprite == null ? iconStyle.sprite : sprite; + m_IconImage.color = ChartHelper.IsClearColor(iconStyle.color) ? color : iconStyle.color; + m_IconImage.type = iconStyle.type; + m_IconRect.sizeDelta = new Vector2(iconStyle.width, iconStyle.height); + m_IconOffest = iconStyle.offset; + m_Align = iconStyle.align; + m_HideIconIfTextEmpty = iconStyle.autoHideWhenLabelEmpty; + AdjustIconPos(); + if (iconStyle.layer == IconStyle.Layer.UnderText) + m_IconRect.SetSiblingIndex(0); + else + m_IconRect.SetSiblingIndex(transform.childCount - 1); + } + } + + public float GetTextWidth() + { + if (m_TextRect) return m_TextRect.sizeDelta.x; + else return 0; + } + + public float GetTextHeight() + { + if (m_TextRect) return m_TextRect.sizeDelta.y; + return 0; + } + + public void SetTextColor(Color color) + { + if (m_LabelText != null) m_LabelText.SetColor(color); + } + + public void SetRotate(float rotate) + { + transform.localEulerAngles = new Vector3(0, 0, rotate); + } + + public void SetTextRotate(float rotate) + { + if (m_LabelText != null) m_LabelText.SetLocalEulerAngles(new Vector3(0, 0, rotate)); + } + + public void SetPosition(Vector3 position) + { + transform.localPosition = position; + } + + public void SetRectPosition(Vector3 position) + { + objectRect.anchoredPosition3D = position; + } + + public Vector3 GetPosition() + { + return transform.localPosition; + } + + public bool IsActiveByScale() + { + return m_Active; + } + + public void SetActive(bool flag, bool force = false) + { + if (m_Active == flag && !force) return; + if (ChartHelper.SetActive(gameObject, flag)) + { + m_Active = flag; + } + } + + public void SetTextActive(bool flag) + { + if (m_LabelText != null) m_LabelText.SetActive(flag); + } + + public void SetIconActive(bool flag) + { + isIconActive = flag; + if (m_IconImage) ChartHelper.SetActive(m_IconImage, flag); + } + + public bool SetText(string text) + { + if (m_TextRect == null || m_LabelText == null) + return false; + + if (text == null) + text = ""; + if (!m_LabelText.GetText().Equals(text)) + { + m_LabelText.SetText(text); + if (m_AutoSize) + { + var newSize = string.IsNullOrEmpty(text) ? Vector2.zero : + new Vector2(m_LabelText.GetPreferredWidth(), + m_LabelText.GetPreferredHeight()); + var sizeChange = newSize.x != m_TextRect.sizeDelta.x || newSize.y != m_TextRect.sizeDelta.y; + this.m_Width = newSize.x; + this.m_Height = newSize.y; + if (sizeChange) + { + m_TextRect.sizeDelta = newSize; + UpdateSize(); + UpdatePadding(); + AdjustIconPos(); + } + return sizeChange; + } + AdjustIconPos(); + if (m_HideIconIfTextEmpty && isIconActive) + { + SetIconActive(!string.IsNullOrEmpty(text)); + } + } + return false; + } + + private void UpdateSize() + { + if (m_AutoSize) + { + var sizeDelta = m_TextRect.sizeDelta; + m_Width = sizeDelta.x + m_PaddingLeft + m_PaddingRight; + m_Height = sizeDelta.y + m_PaddingTop + m_PaddingBottom; + objectRect.sizeDelta = new Vector2(m_Width, m_Height); + } + } + + private void UpdatePadding() + { + if (m_TextRect == null) return; + switch (text.alignment) + { + case TextAnchor.LowerLeft: + m_TextRect.anchoredPosition = new Vector2(m_PaddingLeft, m_PaddingBottom); + break; + case TextAnchor.UpperLeft: + m_TextRect.anchoredPosition = new Vector2(m_PaddingLeft, -m_PaddingTop); + break; + case TextAnchor.MiddleLeft: + m_TextRect.anchoredPosition = new Vector2(m_PaddingLeft, m_Height / 2 - m_PaddingTop - m_TextRect.sizeDelta.y / 2); + break; + case TextAnchor.LowerRight: + m_TextRect.anchoredPosition = new Vector2(-m_PaddingRight, m_PaddingBottom); + break; + case TextAnchor.UpperRight: + m_TextRect.anchoredPosition = new Vector2(-m_PaddingRight, -m_PaddingTop); + break; + case TextAnchor.MiddleRight: + m_TextRect.anchoredPosition = new Vector2(-m_PaddingRight, m_Height / 2 - m_PaddingTop - m_TextRect.sizeDelta.y / 2); + break; + case TextAnchor.LowerCenter: + m_TextRect.anchoredPosition = new Vector2(-(m_Width / 2 - m_PaddingLeft - m_TextRect.sizeDelta.x / 2), m_PaddingBottom); + break; + case TextAnchor.UpperCenter: + m_TextRect.anchoredPosition = new Vector2(-(m_Width / 2 - m_PaddingLeft - m_TextRect.sizeDelta.x / 2), -m_PaddingTop); + break; + case TextAnchor.MiddleCenter: + m_TextRect.anchoredPosition = new Vector2(-(m_Width / 2 - m_PaddingLeft - m_TextRect.sizeDelta.x / 2), m_Height / 2 - m_PaddingTop - m_TextRect.sizeDelta.y / 2); + break; + default: + break; + } + } + + private void AdjustIconPos() + { + if (m_IconImage && m_IconRect && m_LabelText != null && m_TextRect != null) + { + var iconX = 0f; + switch (m_Align) + { + case Align.Left: + switch (m_LabelText.alignment) + { + case TextAnchor.LowerLeft: + case TextAnchor.UpperLeft: + case TextAnchor.MiddleLeft: + iconX = -m_TextRect.sizeDelta.x / 2 - m_IconRect.sizeDelta.x / 2; + break; + case TextAnchor.LowerRight: + case TextAnchor.UpperRight: + case TextAnchor.MiddleRight: + iconX = m_TextRect.sizeDelta.x / 2 - m_LabelText.GetPreferredWidth() - m_IconRect.sizeDelta.x / 2; + break; + case TextAnchor.LowerCenter: + case TextAnchor.UpperCenter: + case TextAnchor.MiddleCenter: + iconX = -m_LabelText.GetPreferredWidth() / 2 - m_IconRect.sizeDelta.x / 2; + break; + } + break; + case Align.Right: + switch (m_LabelText.alignment) + { + case TextAnchor.LowerLeft: + case TextAnchor.UpperLeft: + case TextAnchor.MiddleLeft: + iconX = m_TextRect.sizeDelta.x / 2 + m_IconRect.sizeDelta.x / 2; + break; + case TextAnchor.LowerRight: + case TextAnchor.UpperRight: + case TextAnchor.MiddleRight: + iconX = m_IconRect.sizeDelta.x / 2; + break; + case TextAnchor.LowerCenter: + case TextAnchor.UpperCenter: + case TextAnchor.MiddleCenter: + iconX = m_LabelText.GetPreferredWidth() / 2 + m_IconRect.sizeDelta.x / 2; + break; + } + break; + } + m_IconRect.anchoredPosition = m_IconOffest + new Vector3(iconX, 0); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs.meta b/Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs.meta new file mode 100644 index 0000000..d673cbb --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/ChartLabel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61287841bdc4142caba8e77985cd8715 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Object/ChartObject.cs b/Assets/XCharts/Runtime/Internal/Object/ChartObject.cs new file mode 100644 index 0000000..143e120 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/ChartObject.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + public class ChartObject + { + protected GameObject m_GameObject; + + public virtual void Destroy() + { + GameObject.Destroy(m_GameObject); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Object/ChartObject.cs.meta b/Assets/XCharts/Runtime/Internal/Object/ChartObject.cs.meta new file mode 100644 index 0000000..183b9e7 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/ChartObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fe3102b0eea042938d30af910ca86d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Object/ChartText.cs b/Assets/XCharts/Runtime/Internal/Object/ChartText.cs new file mode 100644 index 0000000..205a611 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/ChartText.cs @@ -0,0 +1,324 @@ +using UnityEngine; +using UnityEngine.UI; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + [System.Serializable] + public class ChartText + { + private Text m_Text; + private TextAnchor m_TextAlignment; + public Text text + { + get { return m_Text; } + set { m_Text = value; } + } +#if dUI_TextMeshPro + private TextMeshProUGUI m_TMPText; + public TextMeshProUGUI tmpText { get { return m_TMPText; } set { m_TMPText = value; } } +#endif + public GameObject gameObject + { + get + { +#if dUI_TextMeshPro + if (m_TMPText != null) return m_TMPText.gameObject; +#else + if (m_Text != null) return m_Text.gameObject; +#endif + return null; + } + } + + public TextAnchor alignment + { + get + { + return m_TextAlignment; + } + set + { + SetAlignment(alignment); + } + } + + public ChartText() + { } + + public ChartText(GameObject textParent) + { +#if dUI_TextMeshPro + m_TMPText = textParent.GetComponentInChildren<TextMeshProUGUI>(); + if (m_TMPText == null) + { + Debug.LogError("can't find TextMeshProUGUI component:" + textParent); + } +#else + m_Text = textParent.GetComponentInChildren<Text>(); + if (m_Text == null) + { + Debug.LogError("can't find Text component:" + textParent); + } +#endif + } + + public void SetFontSize(float fontSize) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.fontSize = fontSize; +#else + if (m_Text != null) m_Text.fontSize = (int)fontSize; +#endif + } + + public void SetText(string text) + { + if (text == null) text = string.Empty; + else text = text.Replace("\\n", "\n"); +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.text = text; +#else + if (m_Text != null) m_Text.text = text; +#endif + } + + public string GetText() + { +#if dUI_TextMeshPro + if (m_TMPText != null) return m_TMPText.text; +#else + if (m_Text != null) return m_Text.text; +#endif + return string.Empty; + } + + public void SetColor(Color color) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.color = color; +#else + if (m_Text != null) m_Text.color = color; +#endif + } + + public Color GetColor() + { +#if dUI_TextMeshPro + if (m_TMPText != null) return m_TMPText.color; +#else + if (m_Text != null) return m_Text.color; +#endif + return Color.clear; + } + + public void SetLineSpacing(float lineSpacing) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.lineSpacing = lineSpacing; +#else + if (m_Text != null) m_Text.lineSpacing = lineSpacing; +#endif + } + + public void SetActive(bool flag) + { +#if dUI_TextMeshPro + //m_TMPText.gameObject.SetActive(flag); + if (m_TMPText != null) ChartHelper.SetActive(m_TMPText.gameObject, flag); +#else + //m_Text.gameObject.SetActive(flag); + if (m_Text != null) ChartHelper.SetActive(m_Text.gameObject, flag); +#endif + } + + public void SetLocalPosition(Vector3 position) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.transform.localPosition = position; +#else + if (m_Text != null) m_Text.transform.localPosition = position; +#endif + } + + public void SetRectPosition(Vector3 position) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.GetComponent<RectTransform>().anchoredPosition3D = position; +#else + if (m_Text != null) m_Text.GetComponent<RectTransform>().anchoredPosition3D = position; +#endif + } + + public void SetSizeDelta(Vector2 sizeDelta) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.GetComponent<RectTransform>().sizeDelta = sizeDelta; +#else + if (m_Text != null) m_Text.GetComponent<RectTransform>().sizeDelta = sizeDelta; +#endif + } + + public void SetLocalEulerAngles(Vector3 position) + { +#if dUI_TextMeshPro + if (m_TMPText != null) m_TMPText.transform.localEulerAngles = position; +#else + if (m_Text != null) m_Text.transform.localEulerAngles = position; +#endif + } + + public void SetAlignment(TextAnchor alignment) + { + m_TextAlignment = alignment; +#if dUI_TextMeshPro + if (m_TMPText == null) return; + switch (alignment) + { + case TextAnchor.LowerCenter: + m_TMPText.alignment = TextAlignmentOptions.Bottom; + break; + case TextAnchor.LowerLeft: + m_TMPText.alignment = TextAlignmentOptions.BottomLeft; + break; + case TextAnchor.LowerRight: + m_TMPText.alignment = TextAlignmentOptions.BottomRight; + break; + case TextAnchor.MiddleCenter: + m_TMPText.alignment = TextAlignmentOptions.Midline; + break; + case TextAnchor.MiddleLeft: + m_TMPText.alignment = TextAlignmentOptions.MidlineLeft; + break; + case TextAnchor.MiddleRight: + m_TMPText.alignment = TextAlignmentOptions.MidlineRight; + break; + case TextAnchor.UpperCenter: + m_TMPText.alignment = TextAlignmentOptions.Top; + break; + case TextAnchor.UpperLeft: + m_TMPText.alignment = TextAlignmentOptions.TopLeft; + break; + case TextAnchor.UpperRight: + m_TMPText.alignment = TextAlignmentOptions.TopRight; + break; + default: + m_TMPText.alignment = TextAlignmentOptions.Midline; + break; + } +#else + if (m_Text != null) m_Text.alignment = alignment; +#endif + } + + public void SetFont(Font font) + { + if (m_Text) m_Text.font = font; + } + + public void SetFontStyle(FontStyle fontStyle) + { +#if dUI_TextMeshPro + if (m_TMPText == null) return; + switch (fontStyle) + { + case FontStyle.Normal: + m_TMPText.fontStyle = FontStyles.Normal; + break; + case FontStyle.Bold: + m_TMPText.fontStyle = FontStyles.Bold; + break; + case FontStyle.BoldAndItalic: + m_TMPText.fontStyle = FontStyles.Bold | FontStyles.Italic; + break; + case FontStyle.Italic: + m_TMPText.fontStyle = FontStyles.Italic; + break; + } +#else + if (m_Text != null) m_Text.fontStyle = fontStyle; +#endif + } + + public void SetFontAndSizeAndStyle(TextStyle textStyle, ComponentTheme theme) + { +#if dUI_TextMeshPro + if (m_TMPText == null) return; + m_TMPText.font = textStyle.tmpFont == null ? theme.tmpFont : textStyle.tmpFont; + m_TMPText.fontSize = textStyle.fontSize == 0 ? theme.fontSize : textStyle.fontSize; + m_TMPText.fontStyle = textStyle.tmpFontStyle; +#else + if (m_Text != null) + { + m_Text.font = textStyle.font == null ? theme.font : textStyle.font; + m_Text.fontSize = textStyle.fontSize == 0 ? theme.fontSize : textStyle.fontSize; + m_Text.fontStyle = textStyle.fontStyle; + } +#endif + } + + public float GetPreferredWidth(string content) + { +#if dUI_TextMeshPro + if (m_TMPText != null && !string.IsNullOrEmpty(content)) + { + return m_TMPText.GetPreferredValues(content).x; + } +#else + if (m_Text != null && !string.IsNullOrEmpty(content)) + { + var tg = m_Text.cachedTextGeneratorForLayout; + var setting = m_Text.GetGenerationSettings(Vector2.zero); + return tg.GetPreferredWidth(content, setting) / m_Text.pixelsPerUnit; + } +#endif + return 0; + } + + public float GetPreferredWidth() + { +#if dUI_TextMeshPro + if (m_TMPText != null) return m_TMPText.preferredWidth; +#else + if (m_Text != null) return m_Text.preferredWidth; +#endif + return 0; + } + public float GetPreferredHeight() + { +#if dUI_TextMeshPro + if (m_TMPText != null) return m_TMPText.preferredHeight; +#else + if (m_Text != null) return m_Text.preferredHeight; +#endif + return 0; + } + + public string GetPreferredText(string content, string suffix, float maxWidth) + { + var sourWid = GetPreferredWidth(content); + if (sourWid < maxWidth) return content; + var suffixWid = GetPreferredWidth(suffix); + var textWid = maxWidth - 1.3f * suffixWid; + for (int i = content.Length; i > 0; i--) + { + var temp = content.Substring(0, i); + if (GetPreferredWidth(temp) < textWid) + { + return temp + suffix; + } + } + return string.Empty; + } + +#if dUI_TextMeshPro + + public void SetFont(TMP_FontAsset font) + { + if (m_TMPText != null) m_TMPText.font = font; + } +#endif + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Object/ChartText.cs.meta b/Assets/XCharts/Runtime/Internal/Object/ChartText.cs.meta new file mode 100644 index 0000000..6b1b33d --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/ChartText.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e2466c1fe5874bea8373b071405a930 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Object/LegendItem.cs b/Assets/XCharts/Runtime/Internal/Object/LegendItem.cs new file mode 100644 index 0000000..cd0c875 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/LegendItem.cs @@ -0,0 +1,225 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public class LegendItem + { + private int m_Index; + private string m_Name; + private string m_LegendName; + private GameObject m_GameObject; + private Button m_Button; + private Image m_Icon; + private ChartText m_Text; + private Image m_Background; + private Image m_TextBackground; + private RectTransform m_Rect; + private RectTransform m_IconRect; + private RectTransform m_TextRect; + private RectTransform m_TextBackgroundRect; + private float m_Gap = 0f; + private float m_LabelPaddingLeftRight = 0f; + private float m_LabelPaddingTopBottom = 0f; + private bool m_LabelAutoSize = true; + + public int index { get { return m_Index; } set { m_Index = value; } } + public string name { get { return m_Name; } set { m_Name = value; } } + public string legendName { get { return m_LegendName; } set { m_LegendName = value; } } + public GameObject gameObject { get { return m_GameObject; } } + public Button button { get { return m_Button; } } + public ChartText text { get { return m_Text; } } + + public float width + { + get + { + if (m_IconRect && m_TextBackgroundRect) + { + return m_IconRect.sizeDelta.x + m_Gap + m_TextBackgroundRect.sizeDelta.x; + } + else + { + return 0; + } + } + } + + public float height + { + get + { + if (m_IconRect && m_TextBackgroundRect) + { + return Mathf.Max(m_IconRect.sizeDelta.y, m_TextBackgroundRect.sizeDelta.y); + } + else + { + return m_Text.GetPreferredHeight(); + } + } + } + + public void SetObject(GameObject obj) + { + m_GameObject = obj; + m_Button = obj.GetComponent<Button>(); + m_Rect = obj.GetComponent<RectTransform>(); + m_Icon = obj.transform.Find("icon").gameObject.GetComponent<Image>(); + m_Background = obj.GetComponent<Image>(); + m_TextBackground = obj.transform.Find("content").gameObject.GetComponent<Image>(); + m_Text = new ChartText(obj); + m_IconRect = m_Icon.gameObject.GetComponent<RectTransform>(); + m_TextRect = m_Text.gameObject.GetComponent<RectTransform>(); + m_TextBackgroundRect = m_TextBackground.gameObject.GetComponent<RectTransform>(); + } + + public void SetButton(Button button) + { + m_Button = button; + } + + public void SetIcon(Image icon) + { + m_Icon = icon; + } + + public void SetText(ChartText text) + { + m_Text = text; + } + + public void SetTextBackground(Image image) + { + m_TextBackground = image; + } + + public void SetIconSize(float width, float height) + { + if (m_IconRect) + { + m_IconRect.sizeDelta = new Vector2(width, height); + } + } + + public Rect GetIconRect() + { + if (m_GameObject && m_IconRect) + { + var pos = m_GameObject.transform.localPosition; + var sizeDelta = m_IconRect.sizeDelta; + var y = pos.y - (m_Rect.sizeDelta.y - sizeDelta.y) / 2 - sizeDelta.y; + return new Rect(pos.x, y, m_IconRect.sizeDelta.x, m_IconRect.sizeDelta.y); + } + else + { + return Rect.zero; + } + } + + public Color GetIconColor() + { + if (m_Icon) return m_Icon.color; + else return Color.clear; + } + + public void SetIconColor(Color color) + { + if (m_Icon) + { + m_Icon.color = color; + } + } + + public void SetIconImage(Sprite image) + { + if (m_Icon) + { + m_Icon.sprite = image; + } + } + + public void SetIconActive(bool active) + { + if (m_Icon) + { + m_Icon.gameObject.SetActive(active); + } + } + + public void SetContentColor(Color color) + { + if (m_Text != null) + { + m_Text.SetColor(color); + } + } + + public void SetContentBackgroundColor(Color color) + { + if (m_TextBackground) + { + m_TextBackground.color = color; + } + } + + public void SetContentPosition(Vector3 offset) + { + m_Gap = offset.x; + if (m_TextBackgroundRect) + { + var posX = m_IconRect.sizeDelta.x + offset.x; + m_TextBackgroundRect.anchoredPosition3D = new Vector3(posX, offset.y, 0); + } + } + + public bool SetContent(string content) + { + if (m_Text == null) return false; + if (!m_Text.GetText().Equals(content)) + { + m_Text.SetText(content); + if (m_LabelAutoSize) + { + var newSize = string.IsNullOrEmpty(content) ? Vector2.zero : + new Vector2(m_Text.GetPreferredWidth(), m_Text.GetPreferredHeight()); + var sizeChange = newSize.x != m_TextRect.sizeDelta.x || newSize.y != m_TextRect.sizeDelta.y; + if (sizeChange) + { + m_TextRect.sizeDelta = newSize; + m_TextRect.anchoredPosition3D = new Vector3(m_LabelPaddingLeftRight, 0); + m_TextBackgroundRect.sizeDelta = new Vector2(m_Text.GetPreferredWidth() + m_LabelPaddingLeftRight * 2, + m_Text.GetPreferredHeight() + m_LabelPaddingTopBottom * 2 - 4); + + } + m_Rect.sizeDelta = new Vector3(width, height); + return sizeChange; + } + } + m_Rect.sizeDelta = new Vector3(width, height); + return false; + } + + public void SetPosition(Vector3 position) + { + if (m_GameObject) + { + m_GameObject.transform.localPosition = position; + } + } + + public void SetActive(bool active) + { + if (m_GameObject) + { + m_GameObject.SetActive(active); + } + } + + public void SetBackground(ImageStyle imageStyle) + { + ChartHelper.SetBackground(m_Background, imageStyle); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Object/LegendItem.cs.meta b/Assets/XCharts/Runtime/Internal/Object/LegendItem.cs.meta new file mode 100644 index 0000000..e7a5204 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Object/LegendItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e5abcb8f339f41f5b3680ecdab67509 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Painter.cs b/Assets/XCharts/Runtime/Internal/Painter.cs new file mode 100644 index 0000000..e0c33fe --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Painter.cs @@ -0,0 +1,72 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [RequireComponent(typeof(CanvasRenderer))] + public class Painter : MaskableGraphic + { + public enum Type + { + Base, + Serie, + Top + } + protected int m_Index = -1; + protected Type m_Type = Type.Base; + protected bool m_Refresh; + protected Action<VertexHelper, Painter> m_OnPopulateMesh; + + public Action<VertexHelper, Painter> onPopulateMesh { set { m_OnPopulateMesh = value; } } + public int index { get { return m_Index; } set { m_Index = value; } } + public Type type { get { return m_Type; } set { m_Type = value; } } + public void Refresh() + { + if (null == this || gameObject == null) return; + if (!gameObject.activeSelf) return; + m_Refresh = true; + } + + public void Init() + { + raycastTarget = false; + } + + public void SetActive(bool flag, bool isDebugMode = false) + { + if (gameObject.activeInHierarchy != flag) + { + gameObject.SetActive(flag); + } + var hideFlags = flag && isDebugMode ? HideFlags.None : HideFlags.HideInHierarchy; + if (gameObject.hideFlags != hideFlags) + { + gameObject.hideFlags = hideFlags; + } + } + + protected override void Awake() + { + Init(); + } + + public void CheckRefresh() + { + if (m_Refresh && gameObject.activeSelf) + { + m_Refresh = false; + SetVerticesDirty(); + } + } + + protected override void OnPopulateMesh(VertexHelper vh) + { + vh.Clear(); + if (m_OnPopulateMesh != null) + { + m_OnPopulateMesh(vh, this); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Painter.cs.meta b/Assets/XCharts/Runtime/Internal/Painter.cs.meta new file mode 100644 index 0000000..60597fa --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Painter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01c85cd323a9f4dfb803470695bd0944 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Pools.meta b/Assets/XCharts/Runtime/Internal/Pools.meta new file mode 100644 index 0000000..49e7df6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 576ce681815d348d0a2abbbadf3dd9f7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Pools/ListPool.cs b/Assets/XCharts/Runtime/Internal/Pools/ListPool.cs new file mode 100644 index 0000000..37efc52 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/ListPool.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + public static class ListPool<T> + { + private static readonly ObjectPool<List<T>> s_ListPool = new ObjectPool<List<T>>(OnGet, OnClear); + static void OnGet(List<T> l) + { + if (l.Capacity < 50) + { + l.Capacity = 50; + } + } + static void OnClear(List<T> l) + { + l.Clear(); + } + + public static List<T> Get() + { + return s_ListPool.Get(); + } + + public static void Release(List<T> toRelease) + { + s_ListPool.Release(toRelease); + } + + public static void ClearAll() + { + s_ListPool.ClearAll(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Pools/ListPool.cs.meta b/Assets/XCharts/Runtime/Internal/Pools/ListPool.cs.meta new file mode 100644 index 0000000..7807cc9 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/ListPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02c30457469c746dc96f00f24cb6e1c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs b/Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs new file mode 100644 index 0000000..9dfb045 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Events; + +namespace XCharts.Runtime +{ + public class ObjectPool<T> where T : new() + { + private readonly bool m_NewIfEmpty = true; + private readonly Stack<T> m_Stack = new Stack<T>(); + private readonly UnityAction<T> m_ActionOnGet; + private readonly UnityAction<T> m_ActionOnRelease; + + public int countAll { get; private set; } + public int countActive { get { return countAll - countInactive; } } + public int countInactive { get { return m_Stack.Count; } } + + public ObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease, bool newIfEmpty = true) + { + m_NewIfEmpty = newIfEmpty; + m_ActionOnGet = actionOnGet; + m_ActionOnRelease = actionOnRelease; + } + + public T Get() + { + T element; + if (m_Stack.Count == 0) + { + if (!m_NewIfEmpty) return default(T); + element = new T(); + countAll++; + } + else + { + element = m_Stack.Pop(); + } + if (m_ActionOnGet != null) + m_ActionOnGet(element); + return element; + } + + public void Release(T element) + { + if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element)) + Debug.LogError("Internal error. Trying to destroy object that is already released to pool."); + if (m_ActionOnRelease != null) + m_ActionOnRelease(element); + m_Stack.Push(element); + } + + public void ClearAll() + { + m_Stack.Clear(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs.meta b/Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs.meta new file mode 100644 index 0000000..58927ca --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/ObjectPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09e67988253cb4f568b82d52b4113797 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs b/Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs new file mode 100644 index 0000000..9380580 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + internal static class SerieDataPool + { + private static readonly ObjectPool<SerieData> s_ListPool = new ObjectPool<SerieData>(null, OnClear); + + static void OnGet(SerieData serieData) { } + + static void OnClear(SerieData serieData) + { + serieData.Reset(); + } + + public static SerieData Get() + { + return s_ListPool.Get(); + } + + public static void Release(SerieData toRelease) + { + s_ListPool.Release(toRelease); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs.meta b/Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs.meta new file mode 100644 index 0000000..5bd4740 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/SerieDataPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faf4da15b01d74648bd13f73125e27bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs b/Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs new file mode 100644 index 0000000..d0367ae --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs @@ -0,0 +1,34 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class SerieEventDataPool + { + private static readonly ObjectPool<SerieEventData> s_ListPool = new ObjectPool<SerieEventData>(null, OnClear); + + static void OnGet(SerieEventData data) + { + } + + static void OnClear(SerieEventData data) + { + data.Reset(); + } + + public static SerieEventData Get(Vector3 pos, int serieIndex, int dataIndex, int dimension, double value) + { + var data = s_ListPool.Get(); + data.serieIndex = serieIndex; + data.dataIndex = dataIndex; + data.pointerPos = pos; + data.dimension = dimension; + data.value = value; + return data; + } + + public static void Release(SerieEventData toRelease) + { + s_ListPool.Release(toRelease); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs.meta b/Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs.meta new file mode 100644 index 0000000..40316cc --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/SerieEventDataPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30d123dd5c38446f18183f50336322bb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs b/Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs new file mode 100644 index 0000000..b4e1aeb --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class SerieLabelPool + { + private static readonly Stack<GameObject> m_Stack = new Stack<GameObject>(200); + private static Dictionary<int, bool> m_ReleaseDic = new Dictionary<int, bool>(1000); + + public static GameObject Get(string name, Transform parent, LabelStyle label, Color color, + float iconWidth, float iconHeight, ThemeStyle theme) + { + GameObject element; + if (m_Stack.Count == 0 || !Application.isPlaying) + { + element = CreateSerieLabel(name, parent, label, color, iconWidth, iconHeight, theme); + } + else + { + element = m_Stack.Pop(); + if (element == null) + { + element = CreateSerieLabel(name, parent, label, color, iconWidth, iconHeight, theme); + } + m_ReleaseDic.Remove(element.GetInstanceID()); + element.name = name; + element.transform.SetParent(parent); + var text = new ChartText(element); + text.SetColor(color); + text.SetFontAndSizeAndStyle(label.textStyle, theme.common); + ChartHelper.SetActive(element, true); + } + element.transform.localEulerAngles = new Vector3(0, 0, label.rotate); + return element; + } + + public static void Release(GameObject element) + { + if (element == null) return; + ChartHelper.SetActive(element, false); + if (!Application.isPlaying) return; + if (!m_ReleaseDic.ContainsKey(element.GetInstanceID())) + { + m_Stack.Push(element); + m_ReleaseDic.Add(element.GetInstanceID(), true); + } + } + + public static void ReleaseAll(Transform parent) + { + int count = parent.childCount; + for (int i = 0; i < count; i++) + { + Release(parent.GetChild(i).gameObject); + } + } + + public static void ClearAll() + { + m_Stack.Clear(); + m_ReleaseDic.Clear(); + } + + private static GameObject CreateSerieLabel(string name, Transform parent, LabelStyle labelStyle, Color color, + float iconWidth, float iconHeight, ThemeStyle theme) + { + var label = ChartHelper.AddChartLabel(name, parent, labelStyle, theme.common, + "", color, TextAnchor.MiddleCenter); + label.SetActive(labelStyle.show, true); + return label.gameObject; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs.meta b/Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs.meta new file mode 100644 index 0000000..8d5c752 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Pools/SerieLabelPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e960aeb14c09844e3bdcdc4138af0761 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/UIComponent.cs b/Assets/XCharts/Runtime/Internal/UIComponent.cs new file mode 100644 index 0000000..43916ee --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/UIComponent.cs @@ -0,0 +1,158 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// UI组件基类。 + /// </summary> + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + public class UIComponent : BaseGraph + { + [SerializeField] private bool m_DebugModel = false; + [SerializeField] protected UIComponentTheme m_Theme = new UIComponentTheme(); + [SerializeField] protected Background m_Background = new Background() { show = true }; + + protected bool m_DataDirty; + private ThemeType m_CheckTheme = 0; + + public override HideFlags chartHideFlags { get { return m_DebugModel ? HideFlags.None : HideFlags.HideInHierarchy; } } + public UIComponentTheme theme { get { return m_Theme; } set { m_Theme = value; } } + /// <summary> + /// 背景样式。 + /// </summary> + public Background background { get { return m_Background; } set { m_Background = value; color = Color.white; } } + /// <summary> + /// Update chart theme. + /// ||切换内置主题。 + /// </summary> + /// <param name="theme">theme</param> + public bool UpdateTheme(ThemeType theme) + { + if (theme == ThemeType.Custom) + { + Debug.LogError("UpdateTheme: not support switch to Custom theme."); + return false; + } + if (m_Theme.sharedTheme == null) + m_Theme.sharedTheme = XCThemeMgr.GetTheme(ThemeType.Default); + m_Theme.sharedTheme.CopyTheme(theme); + m_Theme.SetAllDirty(); + return true; + } + + [Since("v3.9.0")] + public void SetDataDirty() + { + m_DataDirty = true; + m_RefreshChart = true; + } + + public override void SetAllDirty() + { + base.SetAllDirty(); + SetDataDirty(); + } + + public override void SetVerticesDirty() + { + base.SetVerticesDirty(); + m_RefreshChart = true; + } + + protected override void InitComponent() + { + base.InitComponent(); + if (m_Theme.sharedTheme == null) + m_Theme.sharedTheme = XCThemeMgr.GetTheme(ThemeType.Default); + UIHelper.InitBackground(this); + } + + protected override void CheckComponent() + { + base.CheckComponent(); + if (m_Theme.anyDirty) + { + if (m_Theme.componentDirty) + { + SetAllComponentDirty(); + } + if (m_Theme.vertsDirty) RefreshGraph(); + m_Theme.ClearDirty(); + } + } + + protected override void SetAllComponentDirty() + { + base.SetAllComponentDirty(); + InitComponent(); + } + + protected override void OnDrawPainterBase(VertexHelper vh, Painter painter) + { + vh.Clear(); + UIHelper.DrawBackground(vh, this); + } + + protected override void Awake() + { + CheckTheme(true); + base.Awake(); + } + + protected override void Update() + { + base.Update(); + if (m_DataDirty) + { + m_DataDirty = false; + DataDirty(); + } + } + +#if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + Awake(); + } + + protected override void OnValidate() + { + base.OnValidate(); + } +#endif + + protected virtual void DataDirty() + { + } + + protected virtual void CheckTheme(bool firstInit = false) + { + if (m_Theme.sharedTheme == null) + { + m_Theme.sharedTheme = XCThemeMgr.GetTheme(ThemeType.Default); + } + if (firstInit) + { + m_CheckTheme = m_Theme.themeType; + } + if (m_Theme.sharedTheme != null && m_CheckTheme != m_Theme.themeType) + { + m_CheckTheme = m_Theme.themeType; + m_Theme.sharedTheme.CopyTheme(m_CheckTheme); +#if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty(this); +#endif + SetAllDirty(); + SetAllComponentDirty(); + OnThemeChanged(); + } + } + + protected virtual void OnThemeChanged() { } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/UIComponent.cs.meta b/Assets/XCharts/Runtime/Internal/UIComponent.cs.meta new file mode 100644 index 0000000..9c155b1 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/UIComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb30814b19a9d4c1d800ae89e4537a8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/UIComponentTheme.cs b/Assets/XCharts/Runtime/Internal/UIComponentTheme.cs new file mode 100644 index 0000000..7d2d2e0 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/UIComponentTheme.cs @@ -0,0 +1,62 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [Serializable] + public class UIComponentTheme : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private Theme m_SharedTheme; + [SerializeField] private bool m_TransparentBackground = false; + + public bool show { get { return m_Show; } } + /// <summary> + /// the theme of chart. + /// ||主题类型。 + /// </summary> + public ThemeType themeType + { + get { return sharedTheme.themeType; } + } + /// <summary> + /// theme name. + /// ||主题名字。 + /// </summary> + public string themeName + { + get { return sharedTheme.themeName; } + } + /// <summary> + /// the asset of theme. + /// ||主题配置。 + /// </summary> + public Theme sharedTheme + { + get { return m_SharedTheme; } + set { m_SharedTheme = value; SetAllDirty(); } + } + /// <summary> + /// the background color of chart. + /// ||背景颜色。 + /// </summary> + public Color32 backgroundColor + { + get + { + if (m_TransparentBackground) return ColorUtil.clearColor32; + else if (sharedTheme != null) return sharedTheme.backgroundColor; + else return ColorUtil.clearColor32; + } + } + + public Color32 GetBackgroundColor(Background background) + { + if (background != null && background.show && !background.autoColor) + return background.imageColor; + else + return backgroundColor; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/UIComponentTheme.cs.meta b/Assets/XCharts/Runtime/Internal/UIComponentTheme.cs.meta new file mode 100644 index 0000000..c4032b0 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/UIComponentTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 259f5ef4039524e15a7f88578635e907 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities.meta b/Assets/XCharts/Runtime/Internal/Utilities.meta new file mode 100644 index 0000000..6ceb052 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd10f45b4e7714b7687abf5f2f016993 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs b/Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs new file mode 100644 index 0000000..38ed92e --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class ChartCached + { + private const string NUMERIC_FORMATTER_D = "D"; + private const string NUMERIC_FORMATTER_d = "d"; + private const string NUMERIC_FORMATTER_X = "X"; + private const string NUMERIC_FORMATTER_x = "x"; + private static readonly string s_DefaultAxis = "axis_"; + private static CultureInfo ci = GetDefaultCultureInfo(); // "en-us", "zh-cn", "ar-iq", "de-de" + private static Dictionary<Color, string> s_ColorToStr = new Dictionary<Color, string>(100); + private static Dictionary<int, string> s_SerieLabelName = new Dictionary<int, string>(1000); + private static Dictionary<Color, string> s_ColorDotStr = new Dictionary<Color, string>(100); + private static Dictionary<Type, Dictionary<int, string>> s_ComponentObjectName = new Dictionary<Type, Dictionary<int, string>>(); + private static Dictionary<int, string> s_AxisLabelName = new Dictionary<int, string>(); + private static Dictionary<Type, string> s_TypeName = new Dictionary<Type, string>(); + + private static Dictionary<double, Dictionary<string, string>> s_NumberToStr = new Dictionary<double, Dictionary<string, string>>(); + private static Dictionary<int, Dictionary<string, string>> s_PrecisionToStr = new Dictionary<int, Dictionary<string, string>>(); + private static Dictionary<string, Dictionary<int, string>> s_StringIntDict = new Dictionary<string, Dictionary<int, string>>(); + private static Dictionary<double, DateTime> s_TimestampToDateTimeDict = new Dictionary<double, DateTime>(); + private static Dictionary<double, TimeSpan> s_NumberToTimeSpanDict = new Dictionary<double, TimeSpan>(); + + private static CultureInfo GetDefaultCultureInfo() + { + try + { + return new CultureInfo("en-us"); + } + catch (Exception) + { + return CultureInfo.InvariantCulture; + } + } + + public static string FloatToStr(double value, string numericFormatter = "F", int precision = 0) + { + if (precision > 0 && numericFormatter.Length == 1) + { + if (!s_PrecisionToStr.ContainsKey(precision)) + { + s_PrecisionToStr[precision] = new Dictionary<string, string>(); + } + if (!s_PrecisionToStr[precision].ContainsKey(numericFormatter)) + { + s_PrecisionToStr[precision][numericFormatter] = numericFormatter + precision; + } + return NumberToStr(value, s_PrecisionToStr[precision][numericFormatter]); + } + else + { + return NumberToStr(value, numericFormatter); + } + } + + public static string NumberToStr(double value, string formatter) + { + if (!s_NumberToStr.ContainsKey(value)) + { + s_NumberToStr[value] = new Dictionary<string, string>(); + } + if (!s_NumberToStr[value].ContainsKey(formatter)) + { + bool isDateFormatter = false; + string newFormatter = null; + if (string.IsNullOrEmpty(formatter)) + { + s_NumberToStr[value][formatter] = value.ToString(); + } + else if (DateTimeUtil.IsDateOrTimeRegex(formatter,ref isDateFormatter, ref newFormatter)) + { + if(isDateFormatter) + s_NumberToStr[value][formatter] = NumberToDateStr(value, newFormatter); + else + s_NumberToStr[value][formatter] = NumberToTimeStr(value, newFormatter); + } + else if (formatter.StartsWith(NUMERIC_FORMATTER_D) || + formatter.StartsWith(NUMERIC_FORMATTER_d) || + formatter.StartsWith(NUMERIC_FORMATTER_X) || + formatter.StartsWith(NUMERIC_FORMATTER_x) + ) + { + s_NumberToStr[value][formatter] = ((int)value).ToString(formatter, ci); + } + else + { + s_NumberToStr[value][formatter] = value.ToString(formatter, ci); + } + } + return s_NumberToStr[value][formatter]; + } + + public static string IntToStr(int value, string numericFormatter = "") + { + return NumberToStr(value, numericFormatter); + } + + public static string NumberToDateStr(double timestamp, string formatter) + { + var dt = NumberToDateTime(timestamp); + try + { + return dt.ToString(formatter, ci); + } + catch (Exception) + { + XLog.LogError("Not support DateTime format: " + formatter); + return timestamp.ToString(); + } + } + + public static string NumberToTimeStr(double timestamp, string formatter) + { + try + { + var ts = NumberToTimeSpan(timestamp); +#if UNITY_2018_3_OR_NEWER + return ts.ToString(formatter, ci); +#else + return ts.ToString(); +#endif + } + catch (Exception) + { + XLog.LogError("Not support TimeSpan format: " + formatter); + return timestamp.ToString(); + } + } + + public static DateTime NumberToDateTime(double timestamp) + { + if (!s_TimestampToDateTimeDict.ContainsKey(timestamp)) + { + s_TimestampToDateTimeDict[timestamp] = DateTimeUtil.GetDateTime(timestamp); + } + return s_TimestampToDateTimeDict[timestamp]; + } + + public static TimeSpan NumberToTimeSpan(double timestamp) + { + if(!s_NumberToTimeSpanDict.ContainsKey(timestamp)) + { + s_NumberToTimeSpanDict[timestamp] = TimeSpan.FromSeconds(timestamp); + } + return s_NumberToTimeSpanDict[timestamp]; + } + + public static string ColorToStr(Color color) + { + if (s_ColorToStr.ContainsKey(color)) + { + return s_ColorToStr[color]; + } + else + { + s_ColorToStr[color] = ColorUtility.ToHtmlStringRGBA(color); + return s_ColorToStr[color]; + } + } + + public static string ColorToDotStr(Color color) + { + if (!s_ColorDotStr.ContainsKey(color)) + { + s_ColorDotStr[color] = "<color=#" + ColorToStr(color) + ">●</color>"; + } + return s_ColorDotStr[color]; + } + + public static string GetSerieLabelName(string prefix, int i, int j) + { + int key = i * 10000000 + j; + if (s_SerieLabelName.ContainsKey(key)) + { + return s_SerieLabelName[key]; + } + else + { + string name = prefix + "_" + i + "_" + j; + s_SerieLabelName[key] = name; + return name; + } + } + + public static string GetString(string prefix, int suffix) + { + if (!s_StringIntDict.ContainsKey(prefix)) + { + s_StringIntDict[prefix] = new Dictionary<int, string>(); + } + if (!s_StringIntDict[prefix].ContainsKey(suffix)) + { + s_StringIntDict[prefix][suffix] = prefix + suffix; + } + return s_StringIntDict[prefix][suffix]; + } + + internal static string GetComponentObjectName(MainComponent component) + { + Dictionary<int, string> dict; + var type = component.GetType(); + if (s_ComponentObjectName.TryGetValue(type, out dict)) + { + string name; + if (!dict.TryGetValue(component.index, out name)) + { + name = GetTypeName(type) + component.index; + dict[component.index] = name; + } + return name; + } + else + { + var name = GetTypeName(type) + component.index; + dict = new Dictionary<int, string>(); + dict.Add(component.index, name); + s_ComponentObjectName[type] = dict; + return name; + } + } + + internal static string GetAxisLabelName(int index) + { + string name; + if (!s_AxisLabelName.TryGetValue(index, out name)) + { + name = s_DefaultAxis + index; + s_AxisLabelName[index] = name; + return name; + } + else + { + return name; + } + } + + internal static string GetTypeName<T>() + { + return GetTypeName(typeof(T)); + } + + internal static string GetTypeName(Type type) + { + if (s_TypeName.ContainsKey(type)) return s_TypeName[type]; + else + { + var name = type.Name; + s_TypeName[type] = name; + return name; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs.meta new file mode 100644 index 0000000..7d4496e --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartCached.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 403191b8caeb44430b89d9f3260c4a76 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs b/Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs new file mode 100644 index 0000000..2b0eeb6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class ChartConst + { + public static readonly Color32 clearColor32 = new Color32(0, 0, 0, 0); + public static readonly Color32 greyColor32 = new Color32(128, 128, 128, 255); + public static readonly Color clearColor = Color.clear; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs.meta new file mode 100644 index 0000000..f932d45 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartConst.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e19d8fc0680be46b5ac9babf7dd9fe27 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs b/Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs new file mode 100644 index 0000000..baffa0f --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + public static class ChartDrawer + { + public static void DrawSymbol(VertexHelper vh, SymbolType type, float symbolSize, float tickness, + Vector3 pos, Color32 color, Color32 toColor, float gap, float[] cornerRadius, + Color32 emptyColor, Color32 backgroundColor, Color32 borderColor, float smoothness, + Vector3 startPos, float symbolSize2 = 0f) + { + switch (type) + { + case SymbolType.None: + break; + case SymbolType.Circle: + if (gap > 0) + { + UGL.DrawDoughnut(vh, pos, symbolSize, symbolSize + gap, backgroundColor, backgroundColor, color, smoothness); + } + else + { + if (tickness > 0 && !ChartHelper.IsClearColor(borderColor)) + UGL.DrawDoughnut(vh, pos, symbolSize, symbolSize + tickness, borderColor, borderColor, color, smoothness); + else + UGL.DrawCricle(vh, pos, symbolSize, color, toColor, smoothness); + } + break; + case SymbolType.EmptyCircle: + if (tickness == 0) tickness = 4f; + if (gap > 0) + { + UGL.DrawCricle(vh, pos, symbolSize + gap, backgroundColor, smoothness); + UGL.DrawEmptyCricle(vh, pos, symbolSize, tickness, color, color, emptyColor, smoothness); + } + else + { + UGL.DrawEmptyCricle(vh, pos, symbolSize, tickness, color, color, emptyColor, smoothness); + } + break; + case SymbolType.Rect: + if (symbolSize2 > 0 && symbolSize2 != symbolSize) + { + UGL.DrawRectangle(vh, pos, symbolSize, symbolSize2, color, toColor); + } + else + { + if (gap > 0) + { + UGL.DrawSquare(vh, pos, symbolSize + gap, backgroundColor); + UGL.DrawSquare(vh, pos, symbolSize, color, toColor); + } + else + { + if (tickness > 0) + { + UGL.DrawRoundRectangle(vh, pos, symbolSize * 2, symbolSize * 2, color, color, 0, cornerRadius, true); + UGL.DrawBorder(vh, pos, symbolSize, symbolSize, tickness, borderColor, 0, cornerRadius); + } + else + UGL.DrawRoundRectangle(vh, pos, symbolSize * 2, symbolSize * 2, color, color, 0, cornerRadius, true); + } + } + break; + case SymbolType.EmptyRect: + if (tickness == 0) tickness = 4f; + if (gap > 0) + { + UGL.DrawSquare(vh, pos, symbolSize + gap, backgroundColor); + UGL.DrawBorder(vh, pos, symbolSize * 2, symbolSize * 2, tickness, color); + } + else + { + UGL.DrawBorder(vh, pos, symbolSize * 2 - tickness * 2, symbolSize * 2 - tickness * 2, tickness, color); + } + break; + case SymbolType.Triangle: + case SymbolType.EmptyTriangle: + if (gap > 0) + { + UGL.DrawEmptyTriangle(vh, pos, symbolSize * 1.4f + gap * 2, gap * 2, backgroundColor); + } + if (type == SymbolType.EmptyTriangle) + { + if (tickness == 0) tickness = 4f; + UGL.DrawEmptyTriangle(vh, pos, symbolSize * 1.4f, tickness * 2f, color, emptyColor); + } + else + { + UGL.DrawTriangle(vh, pos, symbolSize * 1.4f, color, toColor); + } + break; + case SymbolType.Diamond: + case SymbolType.EmptyDiamond: + var xRadius = symbolSize; + var yRadius = symbolSize * 1.5f; + if (gap > 0) + { + UGL.DrawEmptyDiamond(vh, pos, xRadius + gap, yRadius + gap, gap, backgroundColor); + } + if (type == SymbolType.EmptyDiamond) + { + if (tickness == 0) tickness = 4f; + UGL.DrawEmptyDiamond(vh, pos, xRadius, yRadius, tickness, color, emptyColor); + } + else + { + UGL.DrawDiamond(vh, pos, xRadius, yRadius, color, toColor); + } + break; + case SymbolType.Arrow: + case SymbolType.EmptyArrow: + var arrowWidth = symbolSize * 2; + var arrowHeight = arrowWidth * 1.5f; + var arrowOffset = 0; + var arrowDent = arrowWidth / 3.3f; + if (gap > 0) + { + arrowWidth = (symbolSize + gap) * 2; + arrowHeight = arrowWidth * 1.5f; + arrowOffset = 0; + arrowDent = arrowWidth / 3.3f; + var dir = (pos - startPos).normalized; + var sharpPos = pos + gap * dir; + UGL.DrawArrow(vh, startPos, sharpPos, arrowWidth, arrowHeight, + arrowOffset, arrowDent, backgroundColor); + } + arrowWidth = symbolSize * 2; + arrowHeight = arrowWidth * 1.5f; + arrowOffset = 0; + arrowDent = arrowWidth / 3.3f; + UGL.DrawArrow(vh, startPos, pos, arrowWidth, arrowHeight, + arrowOffset, arrowDent, color); + if (type == SymbolType.EmptyArrow) + { + if (tickness == 0) tickness = 4f; + arrowWidth = (symbolSize - tickness) * 2; + arrowHeight = arrowWidth * 1.5f; + arrowOffset = 0; + arrowDent = arrowWidth / 3.3f; + var dir = (pos - startPos).normalized; + var sharpPos = pos - tickness * dir; + UGL.DrawArrow(vh, startPos, sharpPos, arrowWidth, arrowHeight, + arrowOffset, arrowDent, backgroundColor); + } + break; + case SymbolType.Plus: + if (gap > 0) + { + UGL.DrawPlus(vh, pos, symbolSize + gap, tickness + gap, backgroundColor); + } + UGL.DrawPlus(vh, pos, symbolSize, tickness, color); + break; + case SymbolType.Minus: + if (gap > 0) + { + UGL.DrawMinus(vh, pos, symbolSize + gap, tickness + gap, backgroundColor); + } + UGL.DrawMinus(vh, pos, symbolSize, tickness, color); + break; + } + } + + public static void DrawLineStyle(VertexHelper vh, LineStyle lineStyle, Vector3 startPos, Vector3 endPos, + Color32 defaultColor, float themeWidth, LineStyle.Type themeType) + { + var type = lineStyle.GetType(themeType); + var width = lineStyle.GetWidth(themeWidth); + var color = lineStyle.GetColor(defaultColor); + DrawLineStyle(vh, type, width, startPos, endPos, color, color); + } + + public static void DrawLineStyle(VertexHelper vh, LineStyle lineStyle, Vector3 startPos, Vector3 endPos, + float themeWidth, LineStyle.Type themeType, Color32 defaultColor, Color32 defaultToColor) + { + var type = lineStyle.GetType(themeType); + var width = lineStyle.GetWidth(themeWidth); + var color = lineStyle.GetColor(defaultColor); + var toColor = ChartHelper.IsClearColor(defaultToColor) ? color : defaultToColor; + DrawLineStyle(vh, type, width, startPos, endPos, color, toColor); + } + + public static void DrawLineStyle(VertexHelper vh, LineStyle.Type lineType, float lineWidth, + Vector3 startPos, Vector3 endPos, Color32 color) + { + DrawLineStyle(vh, lineType, lineWidth, startPos, endPos, color, color); + } + + public static void DrawLineStyle(VertexHelper vh, LineStyle.Type lineType, float lineWidth, + Vector3 startPos, Vector3 endPos, Color32 color, Color32 toColor) + { + switch (lineType) + { + case LineStyle.Type.Dashed: + UGL.DrawDashLine(vh, startPos, endPos, lineWidth, color, toColor); + break; + case LineStyle.Type.Dotted: + UGL.DrawDotLine(vh, startPos, endPos, lineWidth, color, toColor); + break; + case LineStyle.Type.Solid: + UGL.DrawLine(vh, startPos, endPos, lineWidth, color, toColor); + break; + case LineStyle.Type.DashDot: + UGL.DrawDashDotLine(vh, startPos, endPos, lineWidth, color); + break; + case LineStyle.Type.DashDotDot: + UGL.DrawDashDotDotLine(vh, startPos, endPos, lineWidth, color); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs.meta new file mode 100644 index 0000000..64d094a --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 712f08d71f1bf4ab6a1785526bcd5c30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs b/Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs new file mode 100644 index 0000000..4f862da --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs @@ -0,0 +1,1118 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +#if dUI_TextMeshPro +using TMPro; +#endif +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace XCharts.Runtime +{ + public static class ChartHelper + { + private static StringBuilder s_Builder = new StringBuilder(); + private static Vector3 s_DefaultIngoreDataVector3 = Vector3.zero; + + public static StringBuilder sb { get { return s_Builder; } } + public static Vector3 ignoreVector3 { get { return s_DefaultIngoreDataVector3; } } + + public static bool IsIngore(Vector3 pos) + { + return pos == s_DefaultIngoreDataVector3; + } + public static string Cancat(string str1, string str2) + { + s_Builder.Length = 0; + s_Builder.Append(str1).Append(str2); + return s_Builder.ToString(); + } + + public static string Cancat(string str1, int i) + { + s_Builder.Length = 0; + s_Builder.Append(str1).Append(ChartCached.IntToStr(i)); + return s_Builder.ToString(); + } + + public static bool IsActiveByScale(GameObject gameObject) + { + if (gameObject == null) return false; + return IsActiveByScale(gameObject.transform); + } + + public static bool IsActiveByScale(Image image) + { + if (image == null) return false; + return IsActiveByScale(image.gameObject); + } + + public static bool IsActiveByScale(Transform transform) + { + return transform.localScale != Vector3.zero; + } + + public static bool SetActive(GameObject gameObject, bool active) + { + if (gameObject == null) return false; + return SetActive(gameObject.transform, active); + } + + public static bool SetActive(Image image, bool active) + { + if (image == null) return false; + return SetActive(image.gameObject, active); + } + + public static bool SetActive(Text text, bool active) + { + if (text == null) return false; + return SetActive(text.gameObject, active); + } + + /// <summary> + /// 通过设置scale实现是否显示,优化性能,减少GC + /// </summary> + /// <param name="transform"></param> + /// <param name="active"></param> + public static bool SetActive(Transform transform, bool active) + { + if (transform == null) return false; + if (active) transform.localScale = Vector3.one; + else transform.localScale = Vector3.zero; + return true; + } + + public static void HideAllObject(GameObject obj, string match = null) + { + if (obj == null) return; + HideAllObject(obj.transform, match); + } + + public static void HideAllObject(Transform parent, string match = null) + { + if (parent == null) return; + ActiveAllObject(parent, false, match); + } + + public static void ActiveAllObject(Transform parent, bool active, string match = null) + { + if (parent == null) return; + for (int i = 0; i < parent.childCount; i++) + { + if (match == null) + SetActive(parent.GetChild(i), active); + else + { + var go = parent.GetChild(i); + if (go.name.StartsWith(match)) + { + SetActive(go, active); + } + } + } + } + + public static void DestroyAllChildren(Transform parent) + { + if (parent == null) return; + var childCount = parent.childCount; + for (int i = childCount - 1; i >= 0; i--) + { + var go = parent.GetChild(i); + if (go != null) + { + GameObject.DestroyImmediate(go.gameObject, true); + } + } + } + + public static void DestoryGameObject(Transform parent, string childName) + { + if (parent == null) return; + var go = parent.Find(childName); + if (go != null) + { + GameObject.DestroyImmediate(go.gameObject, true); + } + } + public static void DestoryGameObjectByMatch(Transform parent, string containString) + { + if (parent == null) return; + var childCount = parent.childCount; + for (int i = childCount - 1; i >= 0; i--) + { + var go = parent.GetChild(i); + if (go != null && go.name.Contains(containString)) + { + GameObject.DestroyImmediate(go.gameObject, true); + } + } + } + + public static void DestoryGameObjectByMatch(Transform parent, List<string> children) + { + if (parent == null) return; + if (children == null || children.Count == 0) return; + var childCount = parent.childCount; + for (int i = childCount - 1; i >= 0; i--) + { + var go = parent.GetChild(i); + if (go != null && children.Contains(go.name)) + { + GameObject.DestroyImmediate(go.gameObject, true); + } + } + } + + public static void DestoryGameObject(GameObject go) + { + if (go != null) GameObject.DestroyImmediate(go, true); + } + + public static string GetFullName(Transform transform) + { + string name = transform.name; + Transform obj = transform; + while (obj.transform.parent) + { + name = obj.transform.parent.name + "/" + name; + obj = obj.transform.parent; + } + return name; + } + + public static void RemoveComponent<T>(GameObject gameObject) + { + var component = gameObject.GetComponent<T>(); + if (component != null) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + GameObject.DestroyImmediate(component as UnityEngine.Object); + else + GameObject.Destroy(component as UnityEngine.Object); +#else + GameObject.Destroy(component as UnityEngine.Object); +#endif + } + } + + public static void RemoveTMPComponents(GameObject gameObject) + { + var coms = gameObject.GetComponents<Component>(); + foreach (var com in coms) + { + if (com.GetType().FullName.Contains("TMPro")) + { +#if UNITY_EDITOR + if (!Application.isPlaying) + GameObject.DestroyImmediate(com as UnityEngine.Object); + else + GameObject.Destroy(com as UnityEngine.Object); +#else + GameObject.Destroy(com as UnityEngine.Object); +#endif + } + } + } + + [System.Obsolete("Use EnsureComponent instead")] + public static T GetOrAddComponent<T>(Transform transform) where T : Component + { + return EnsureComponent<T>(transform.gameObject); + } + + [System.Obsolete("Use EnsureComponent instead")] + public static T GetOrAddComponent<T>(GameObject gameObject) where T : Component + { + return EnsureComponent<T>(gameObject); + } + + /// <summary> + /// Ensure that the transform has the specified component, add it if not. + /// ||确保对象有指定的组件,如果没有则添加。 + /// </summary> + /// <param name="transform"></param> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + public static T EnsureComponent<T>(Transform transform) where T : Component + { + return EnsureComponent<T>(transform.gameObject); + } + + /// <summary> + /// Ensure that the game object has the specified component, add it if not. + /// || 确保对象有指定的组件,如果没有则添加。 + /// </summary> + /// <param name="gameObject"></param> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + public static T EnsureComponent<T>(GameObject gameObject) where T : Component + { + if (gameObject.GetComponent<T>() == null) + { + var com = gameObject.AddComponent<T>(); + if (com == null) + { + RemoveTMPComponents(gameObject); + return gameObject.AddComponent<T>(); + } + return com; + } + else + { + return gameObject.GetComponent<T>(); + } + } + + public static GameObject AddObject(string name, Transform parent, Vector2 anchorMin, + Vector2 anchorMax, Vector2 pivot, Vector2 sizeDelta, int replaceIndex = -1, List<string> cacheNames = null) + { + GameObject obj; + if (parent.Find(name)) + { + obj = parent.Find(name).gameObject; + SetActive(obj, true); + obj.transform.localPosition = Vector3.zero; + obj.transform.localScale = Vector3.one; + obj.transform.localRotation = Quaternion.Euler(0, 0, 0); + } + else if (replaceIndex >= 0 && replaceIndex < parent.childCount) + { + obj = parent.GetChild(replaceIndex).gameObject; + if (!obj.name.Equals(name)) obj.name = name; + SetActive(obj, true); + } + else + { + obj = new GameObject(); + obj.name = name; + obj.transform.SetParent(parent); + obj.transform.localScale = Vector3.one; + obj.transform.localPosition = Vector3.zero; + obj.transform.localRotation = Quaternion.Euler(0, 0, 0); + obj.layer = parent.gameObject.layer; + } + RectTransform rect = EnsureComponent<RectTransform>(obj); + rect.localPosition = Vector3.zero; + rect.sizeDelta = sizeDelta; + rect.anchorMin = anchorMin; + rect.anchorMax = anchorMax; + rect.pivot = pivot; + rect.anchoredPosition3D = Vector3.zero; + + if (cacheNames != null && !cacheNames.Contains(name)) cacheNames.Add(name); + return obj; + } + + public static void UpdateRectTransform(GameObject obj, Vector2 anchorMin, + Vector2 anchorMax, Vector2 pivot, Vector2 sizeDelta) + { + if (obj == null) return; + RectTransform rect = EnsureComponent<RectTransform>(obj); + rect.sizeDelta = sizeDelta; + rect.anchorMin = anchorMin; + rect.anchorMax = anchorMax; + rect.pivot = pivot; + } + + public static ChartText AddTextObject(string objectName, Transform parent, Vector2 anchorMin, Vector2 anchorMax, + Vector2 pivot, Vector2 sizeDelta, TextStyle textStyle, ComponentTheme theme, Color autoColor, + TextAnchor autoAlignment, ChartText chartText = null) + { + GameObject txtObj = AddObject(objectName, parent, anchorMin, anchorMax, pivot, sizeDelta); + txtObj.transform.localEulerAngles = new Vector3(0, 0, textStyle.rotate); + txtObj.layer = parent.gameObject.layer; + if (chartText == null) + chartText = new ChartText(); +#if dUI_TextMeshPro + RemoveComponent<Text>(txtObj); + chartText.tmpText = EnsureComponent<TextMeshProUGUI>(txtObj); + chartText.tmpText.font = textStyle.tmpFont == null ? theme.tmpFont : textStyle.tmpFont; + chartText.tmpText.fontStyle = textStyle.tmpFontStyle; + chartText.tmpText.richText = true; + chartText.tmpText.raycastTarget = false; +#if UNITY_2023_2_OR_NEWER + chartText.tmpText.textWrappingMode = textStyle.autoWrap ? TextWrappingModes.Normal : TextWrappingModes.NoWrap; +#else + chartText.tmpText.enableWordWrapping = textStyle.autoWrap; +#endif +#else + chartText.text = EnsureComponent<Text>(txtObj); + chartText.text.font = textStyle.font == null ? theme.font : textStyle.font; + chartText.text.fontStyle = textStyle.fontStyle; + chartText.text.horizontalOverflow = textStyle.autoWrap ? HorizontalWrapMode.Wrap : HorizontalWrapMode.Overflow; + chartText.text.verticalOverflow = VerticalWrapMode.Overflow; + chartText.text.supportRichText = true; + chartText.text.raycastTarget = false; +#endif + if (textStyle.autoColor && autoColor != Color.clear) + chartText.SetColor(autoColor); + else + chartText.SetColor(textStyle.GetColor(theme.textColor)); + + chartText.SetAlignment(textStyle.autoAlign ? autoAlignment : textStyle.alignment); + chartText.SetFontSize(textStyle.GetFontSize(theme)); + chartText.SetText("Text"); + chartText.SetLineSpacing(textStyle.lineSpacing); + chartText.SetActive(textStyle.show); + + RectTransform rect = EnsureComponent<RectTransform>(txtObj); + rect.anchoredPosition3D = Vector3.zero; + rect.sizeDelta = sizeDelta; + rect.anchorMin = anchorMin; + rect.anchorMax = anchorMax; + rect.pivot = pivot; + return chartText; + } + + public static Painter AddPainterObject(string name, Transform parent, Vector2 anchorMin, Vector2 anchorMax, + Vector2 pivot, Vector2 sizeDelta, HideFlags hideFlags, int siblingIndex, List<string> childNodeNames) + { + var painterObj = ChartHelper.AddObject(name, parent, anchorMin, anchorMax, pivot, sizeDelta, -1, childNodeNames); + painterObj.hideFlags = hideFlags; + painterObj.transform.SetSiblingIndex(siblingIndex); + return ChartHelper.EnsureComponent<Painter>(painterObj); + } + + public static Image AddIcon(string name, Transform parent, IconStyle iconStyle) + { + return AddIcon(name, parent, iconStyle.width, iconStyle.height, iconStyle.sprite, iconStyle.type); + } + + public static Image AddIcon(string name, Transform parent, float width, float height, Sprite sprite = null, + Image.Type type = Image.Type.Simple) + { + var anchorMax = new Vector2(0.5f, 0.5f); + var anchorMin = new Vector2(0.5f, 0.5f); + var pivot = new Vector2(0.5f, 0.5f); + var sizeDelta = new Vector2(width, height); + GameObject iconObj = AddObject(name, parent, anchorMin, anchorMax, pivot, sizeDelta); + var img = EnsureComponent<Image>(iconObj); + if (img.raycastTarget != false) + img.raycastTarget = false; + if (img.type != type) + img.type = type; + if (sprite != null && img.sprite != sprite) + { + img.sprite = sprite; + if (width == 0 || height == 0) + { + img.SetNativeSize(); + } + } + return img; + } + + public static void SetBackground(Image background, ImageStyle imageStyle) + { + if (background == null) return; + if (imageStyle.show) + { + background.gameObject.SetActive(true); + background.sprite = imageStyle.sprite; + background.color = imageStyle.color; + background.type = imageStyle.type; + if (imageStyle.width > 0 && imageStyle.height > 0) + { + background.rectTransform.sizeDelta = new Vector2(imageStyle.width, imageStyle.height); + } + } + else + { + background.sprite = null; + background.color = Color.clear; + background.gameObject.SetActive(false); + } + } + + public static void SetBackground(Image background, Background imageStyle) + { + if (background == null) return; + if (imageStyle.show) + { + background.gameObject.SetActive(true); + background.sprite = imageStyle.image; + background.color = imageStyle.imageColor; + background.type = imageStyle.imageType; + if (imageStyle.imageWidth > 0 && imageStyle.imageHeight > 0) + { + background.rectTransform.sizeDelta = new Vector2(imageStyle.imageWidth, imageStyle.imageHeight); + } + } + else + { + background.sprite = null; + background.color = Color.clear; + background.gameObject.SetActive(false); + } + } + + public static ChartLabel AddAxisLabelObject(int total, int index, string name, Transform parent, + Vector2 sizeDelta, Axis axis, ComponentTheme theme, + string content, Color autoColor, TextAnchor autoAlignment = TextAnchor.MiddleCenter, Color32 iconDefaultColor = default(Color32)) + { + var textStyle = axis.axisLabel.textStyle; + var label = AddChartLabel(name, parent, axis.axisLabel, theme, content, autoColor, autoAlignment); + var labelShow = axis.IsNeedShowLabel(index, total); + label.UpdateIcon(axis.axisLabel.icon, axis.GetIcon(index), iconDefaultColor); + label.text.SetActive(labelShow); + return label; + } + + public static ChartLabel AddChartLabel(string name, Transform parent, LabelStyle labelStyle, + ComponentTheme theme, string content, Color autoColor, TextAnchor autoAlignment = TextAnchor.MiddleCenter, + bool isObjectAnchor = false) + { + Vector2 anchorMin, anchorMax, pivot; + var sizeDelta = new Vector2(labelStyle.width, labelStyle.height); + var textStyle = labelStyle.textStyle; + var alignment = isObjectAnchor ? autoAlignment : textStyle.GetAlignment(autoAlignment); + UpdateAnchorAndPivotByTextAlignment(alignment, out anchorMin, out anchorMax, out pivot); + var labelObj = AddObject(name, parent, anchorMin, anchorMax, pivot, sizeDelta); + //ChartHelper.RemoveComponent<Text>(labelObj); + var label = EnsureComponent<ChartLabel>(labelObj); + if(isObjectAnchor) + { + UpdateAnchorAndPivotByTextAlignment(textStyle.GetAlignment(autoAlignment), out anchorMin, out anchorMax, out pivot); + } + label.text = AddTextObject("Text", label.gameObject.transform, anchorMin, anchorMax, pivot, + sizeDelta, textStyle, theme, autoColor, autoAlignment, label.text); + label.icon = ChartHelper.AddIcon("Icon", label.gameObject.transform, labelStyle.icon); + label.SetSize(labelStyle.width, labelStyle.height); + label.SetTextPadding(labelStyle.textPadding); + label.SetText(content); + label.UpdateIcon(labelStyle.icon); + if (labelStyle.background.show) + { + label.color = (!labelStyle.background.autoColor || autoColor == Color.clear) ? + labelStyle.background.color : autoColor; + label.sprite = labelStyle.background.sprite; + label.type = labelStyle.background.type; + } + else + { + label.color = Color.clear; + label.sprite = null; + } + label.transform.localEulerAngles = new Vector3(0, 0, labelStyle.rotate); + label.transform.localPosition = labelStyle.offset; + return label; + } + + public static ChartLabel AddChartLabel2(string name, Transform parent, LabelStyle labelStyle, + ComponentTheme theme, string content, Color autoColor, TextAnchor autoAlignment = TextAnchor.MiddleCenter) + { + Vector2 anchorMin, anchorMax, pivot; + var sizeDelta = new Vector2(labelStyle.width, labelStyle.height); + var textStyle = labelStyle.textStyle; + var alignment = textStyle.GetAlignment(autoAlignment); + UpdateAnchorAndPivotByTextAlignment(alignment, out anchorMin, out anchorMax, out pivot); + var vector0_5 = new Vector2(0.5f, 0.5f); + var labelObj = AddObject(name, parent, vector0_5, vector0_5, vector0_5, sizeDelta); + var label = EnsureComponent<ChartLabel>(labelObj); + label.text = AddTextObject("Text", label.gameObject.transform, anchorMin, anchorMax, pivot, + sizeDelta, textStyle, theme, autoColor, autoAlignment, label.text); + label.icon = ChartHelper.AddIcon("Icon", label.gameObject.transform, labelStyle.icon); + label.SetSize(labelStyle.width, labelStyle.height); + label.SetTextPadding(labelStyle.textPadding); + label.SetText(content); + label.UpdateIcon(labelStyle.icon); + if (labelStyle.background.show) + { + label.color = (!labelStyle.background.autoColor || autoColor == Color.clear) ? + labelStyle.background.color : autoColor; + label.sprite = labelStyle.background.sprite; + if (label.type != labelStyle.background.type) + label.type = labelStyle.background.type; + } + else + { + label.color = Color.clear; + label.sprite = null; + } + label.transform.localEulerAngles = new Vector3(0, 0, labelStyle.rotate); + label.transform.localPosition = labelStyle.offset; + return label; + } + + public static void UpdateAnchorAndPivotByTextAlignment(TextAnchor alignment, out Vector2 anchorMin, out Vector2 anchorMax, + out Vector2 pivot) + { + switch (alignment) + { + case TextAnchor.LowerLeft: + anchorMin = new Vector2(0f, 0f); + anchorMax = new Vector2(0f, 0f); + pivot = new Vector2(0f, 0f); + break; + case TextAnchor.UpperLeft: + anchorMin = new Vector2(0f, 1f); + anchorMax = new Vector2(0f, 1f); + pivot = new Vector2(0f, 1f); + break; + case TextAnchor.MiddleLeft: + anchorMin = new Vector2(0f, 0.5f); + anchorMax = new Vector2(0f, 0.5f); + pivot = new Vector2(0f, 0.5f); + break; + case TextAnchor.LowerRight: + anchorMin = new Vector2(1f, 0f); + anchorMax = new Vector2(1f, 0f); + pivot = new Vector2(1f, 0f); + break; + case TextAnchor.UpperRight: + anchorMin = new Vector2(1f, 1f); + anchorMax = new Vector2(1f, 1f); + pivot = new Vector2(1f, 1f); + break; + case TextAnchor.MiddleRight: + anchorMin = new Vector2(1, 0.5f); + anchorMax = new Vector2(1, 0.5f); + pivot = new Vector2(1, 0.5f); + break; + case TextAnchor.LowerCenter: + anchorMin = new Vector2(0.5f, 0f); + anchorMax = new Vector2(0.5f, 0f); + pivot = new Vector2(0.5f, 0f); + break; + case TextAnchor.UpperCenter: + anchorMin = new Vector2(0.5f, 1f); + anchorMax = new Vector2(0.5f, 1f); + pivot = new Vector2(0.5f, 1f); + break; + case TextAnchor.MiddleCenter: + anchorMin = new Vector2(0.5f, 0.5f); + anchorMax = new Vector2(0.5f, 0.5f); + pivot = new Vector2(0.5f, 0.5f); + break; + default: + anchorMin = new Vector2(0.5f, 0.5f); + anchorMax = new Vector2(0.5f, 0.5f); + pivot = new Vector2(0.5f, 0.5f); + break; + } + } + + internal static ChartLabel AddTooltipIndicatorLabel(Tooltip tooltip, string name, Transform parent, + ThemeStyle theme, TextAnchor alignment, LabelStyle labelStyle) + { + var label = ChartHelper.AddChartLabel(name, parent, labelStyle, theme.tooltip, + "", Color.clear, alignment); + label.SetActive(tooltip.show && labelStyle.show, true); + return label; + } + + public static void GetPointList(ref List<Vector3> posList, Vector3 sp, Vector3 ep, float k = 30f) + { + Vector3 dir = (ep - sp).normalized; + float dist = Vector3.Distance(sp, ep); + int segment = (int)(dist / k); + posList.Clear(); + posList.Add(sp); + for (int i = 1; i < segment; i++) + { + posList.Add(sp + dir * dist * i / segment); + } + posList.Add(ep); + } + + public static bool IsValueEqualsColor(Color32 color1, Color32 color2) + { + return color1.a == color2.a && + color1.b == color2.b && + color1.g == color2.g && + color1.r == color2.r; + } + + public static bool IsValueEqualsColor(Color color1, Color color2) + { + return color1.a == color2.a && + color1.b == color2.b && + color1.g == color2.g && + color1.r == color2.r; + } + + public static bool IsValueEqualsString(string str1, string str2) + { + if (str1 == null && str2 == null) return true; + else if (str1 != null && str2 != null) return str1.Equals(str2); + else return false; + } + + public static bool IsValueEqualsVector2(Vector2 v1, Vector2 v2) + { + return v1.x == v2.x && v1.y == v2.y; + } + + public static bool IsValueEqualsVector3(Vector3 v1, Vector3 v2) + { + return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z; + } + + public static bool IsValueEqualsList<T>(List<T> list1, List<T> list2) + { + if (list1 == null || list2 == null) return false; + if (list1.Count != list2.Count) return false; + for (int i = 0; i < list1.Count; i++) + { + if (list1[i] == null && list2[i] == null) { } + else + { + if (list1[i] != null) + { + if (!list1[i].Equals(list2[i])) return false; + } + else + { + if (!list2[i].Equals(list1[i])) return false; + } + } + } + return true; + } + + public static bool IsEquals(double d1, double d2) + { + return Math.Abs(d1 - d2) < 0.000001d; + } + + public static bool IsEquals(float d1, float d2) + { + return Math.Abs(d1 - d2) < 0.000001f; + } + + public static bool IsClearColor(Color32 color) + { + return color.a == 0 && color.b == 0 && color.g == 0 && color.r == 0; + } + + public static bool IsClearColor(Color color) + { + return color.a == 0 && color.b == 0 && color.g == 0 && color.r == 0; + } + + public static bool IsZeroVector(Vector3 pos) + { + return pos.x == 0 && pos.y == 0 && pos.z == 0; + } + + public static bool CopyList<T>(List<T> toList, List<T> fromList) + { + if (toList == null || fromList == null) return false; + toList.Clear(); + foreach (var item in fromList) toList.Add(item); + return true; + } + public static bool CopyArray<T>(T[] toList, T[] fromList) + { + if (toList == null || fromList == null) return false; + if (toList.Length != fromList.Length) + { + toList = new T[fromList.Length]; + } + for (int i = 0; i < fromList.Length; i++) toList[i] = fromList[i]; + return true; + } + + public static List<float> ParseFloatFromString(string jsonData) + { + List<float> list = new List<float>(); + if (string.IsNullOrEmpty(jsonData)) return list; + int startIndex = jsonData.IndexOf("["); + int endIndex = jsonData.IndexOf("]"); + string temp = jsonData.Substring(startIndex + 1, endIndex - startIndex - 1); + if (temp.IndexOf("],") > -1 || temp.IndexOf("] ,") > -1) + { + string[] datas = temp.Split(new string[] { "],", "] ," }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < datas.Length; i++) + { + temp = datas[i]; + } + return list; + } + else + { + string[] datas = temp.Split(','); + for (int i = 0; i < datas.Length; i++) + { + list.Add(float.Parse(datas[i].Trim())); + } + return list; + } + } + + public static List<string> ParseStringFromString(string jsonData) + { + List<string> list = new List<string>(); + if (string.IsNullOrEmpty(jsonData)) return list; + string pattern = "[\"'](.*?)[\"']"; + if (Regex.IsMatch(jsonData, pattern)) + { + MatchCollection m = Regex.Matches(jsonData, pattern); + foreach (Match match in m) + { + list.Add(match.Groups[1].Value); + } + } + return list; + } + + public static Color32 GetColor(string hexColorStr) + { + Color color; + ColorUtility.TryParseHtmlString(hexColorStr, out color); + return (Color32)color; + } + + public static double GetMaxDivisibleValue(double max, double ceilRate) + { + if (max == 0) return 0; + double pow = 1; + if (max > -1 && max < 1) + { + pow = Mathf.Pow(10, MathUtil.GetPrecision(max)); + max *= pow; + } + if (ceilRate == 0) + { + var bigger = Math.Ceiling(Math.Abs(max)); + int n = 1; + while (bigger / (Mathf.Pow(10, n)) > 10) + { + n++; + } + double mm = bigger; + var pown = Mathf.Pow(10, n); + var powmax = Mathf.Pow(10, n + 1); + var aliquot = mm % pown == 0; + if (mm > 10 && n < 38) + { + mm = bigger - bigger % pown; + if (!aliquot) + mm += max > 0 ? pown : -pown; + } + var mmm = mm; + if (max > 100 && !aliquot && (max / mm < 0.8f)) + mmm -= Mathf.Pow(10, n) / 2; + if (mmm >= (powmax - pown) && mmm < powmax) + mmm = powmax; + if (max < 0) return -Math.Ceiling(mmm > -max ? mmm : mm); + else return Math.Ceiling(mmm > max ? mmm : mm) / pow; + } + else + { + return GetMaxCeilRate(max, ceilRate) / pow; + } + } + + public static double GetMaxCeilRate(double value, double ceilRate) + { + if (ceilRate == 0) return value; + var mod = value % ceilRate; + int rate = (int)(value / ceilRate); + return mod == 0 ? value : (value < 0 ? rate : rate + 1) * ceilRate; + } + + public static float GetMaxCeilRate(float value, float ceilRate) + { + if (ceilRate == 0) return value; + var mod = value % ceilRate; + int rate = (int)(value / ceilRate); + return mod == 0 ? value : (value < 0 ? rate : rate + 1) * ceilRate; + } + + public static double GetMinCeilRate(double value, double ceilRate) + { + if (ceilRate == 0) return value; + var mod = value % ceilRate; + int rate = (int)(value / ceilRate); + return mod == 0 ? value : (value < 0 ? rate - 1 : rate) * ceilRate; + } + + public static float GetMinCeilRate(float value, float ceilRate) + { + if (ceilRate == 0) return value; + var mod = value % ceilRate; + int rate = (int)(value / ceilRate); + return mod == 0 ? value : (value < 0 ? rate - 1 : rate) * ceilRate; + } + + public static double GetMinDivisibleValue(double min, double ceilRate) + { + if (min == 0) return 0; + double pow = 1; + if (min > -1 && min < 1) + { + pow = Mathf.Pow(10, MathUtil.GetPrecision(min)); + min *= pow; + } + if (ceilRate == 0) + { + var bigger = min < 0 ? Math.Ceiling(Math.Abs(min)) : Math.Floor(Math.Abs(min)); + int n = 1; + while (bigger / (Mathf.Pow(10, n)) > 10) + { + n++; + } + double mm = bigger; + if (mm > 10 && n < 38) + { + mm = bigger - bigger % (Mathf.Pow(10, n)); + mm += min < 0 ? Mathf.Pow(10, n) : -Mathf.Pow(10, n); + } + if (min < 0) return -Math.Floor(mm) / pow; + else return Math.Floor(mm) / pow; + } + else + { + return GetMinCeilRate(min, ceilRate) / pow; + } + } + + public static double GetMaxLogValue(double value, float logBase, bool isLogBaseE, out int splitNumber) + { + splitNumber = 1; + if (value <= 0) return 0; + double max = isLogBaseE ? Math.Exp(splitNumber) : Math.Pow(logBase, splitNumber); + while (max < value) + { + splitNumber++; + max = isLogBaseE ? Math.Exp(splitNumber) : Math.Pow(logBase, splitNumber); + } + return max; + } + + public static double GetMinLogValue(double value, float logBase, bool isLogBaseE, out int splitNumber) + { + splitNumber = 0; + if (value <= 0) return 0; + if (value > 1) return 1; + double min = isLogBaseE ? Math.Exp(-splitNumber) : Math.Pow(logBase, -splitNumber); + while (min > value) + { + splitNumber++; + min = isLogBaseE ? Math.Exp(-splitNumber) : Math.Pow(logBase, -splitNumber); + } + return min; + } + + public static void AddEventListener(GameObject obj, EventTriggerType type, + UnityEngine.Events.UnityAction<BaseEventData> call) + { + EventTrigger trigger = EnsureComponent<EventTrigger>(obj.gameObject); + EventTrigger.Entry entry = new EventTrigger.Entry(); + entry.eventID = type; + entry.callback = new EventTrigger.TriggerEvent(); + entry.callback.AddListener(call); + trigger.triggers.Add(entry); + } + + public static void ClearEventListener(GameObject obj) + { + EventTrigger trigger = obj.GetComponent<EventTrigger>(); + if (trigger != null) + { + trigger.triggers.Clear(); + } + } + + public static Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle) + { + Vector3 point = Quaternion.AngleAxis(angle, axis) * (position - center); + Vector3 resultVec3 = center + point; + return resultVec3; + } + + public static Vector3 GetPosition(Vector3 center, float angle, float radius) + { + var rad = angle * Mathf.Deg2Rad; + var px = Mathf.Sin(rad) * radius; + var py = Mathf.Cos(rad) * radius; + return center + new Vector3(px, py); + } + + /// <summary> + /// 获得0-360的角度(12点钟方向为0度) + /// </summary> + /// <param name="from"></param> + /// <param name="to"></param> + /// <returns></returns> + public static float GetAngle360(Vector2 from, Vector2 to) + { + float angle; + + Vector3 cross = Vector3.Cross(from, to); + angle = Vector2.Angle(from, to); + angle = cross.z > 0 ? -angle : angle; + angle = (angle + 360) % 360; + return angle; + } + + public static Vector3 GetPos(Vector3 center, float radius, float angle, bool isDegree = false) + { + angle = isDegree ? angle * Mathf.Deg2Rad : angle; + return new Vector3(center.x + radius * Mathf.Sin(angle), center.y + radius * Mathf.Cos(angle)); + } + + public static Vector3 GetDire(float angle, bool isDegree = false) + { + angle = isDegree ? angle * Mathf.Deg2Rad : angle; + return new Vector3(Mathf.Sin(angle), Mathf.Cos(angle)); + } + + public static Vector3 GetVertialDire(Vector3 dire) + { + if (dire.x == 0) + { + return new Vector3(-1, 0, 0); + } + if (dire.y == 0) + { + return new Vector3(0, -1, 0); + } + else + { + return new Vector3(-dire.y / dire.x, 1, 0).normalized; + } + } + + public static Vector3 GetLastValue(List<Vector3> list) + { + if (list.Count <= 0) return Vector3.zero; + else return list[list.Count - 1]; + } + + public static void SetColorOpacity(ref Color32 color, float opacity) + { + if (color.a != 0 && opacity != 1) + { + color.a = (byte)(color.a * opacity); + } + } + + public static Color32 GetHighlightColor(Color32 color, float rate = 0.8f) + { + var newColor = color; + newColor.r = (byte)(color.r * rate); + newColor.g = (byte)(color.g * rate); + newColor.b = (byte)(color.b * rate); + return newColor; + } + + public static Color32 GetBlurColor(Color32 color, float a = 0.3f) + { + var newColor = color; + newColor.a = (byte)(a * 255); + return newColor; + } + + public static Color32 GetSelectColor(Color32 color, float rate = 0.8f) + { + var newColor = color; + newColor.r = (byte)(color.r * rate); + newColor.g = (byte)(color.g * rate); + newColor.b = (byte)(color.b * rate); + return newColor; + } + + public static bool IsPointInQuadrilateral(Vector3 P, Vector3 A, Vector3 B, Vector3 C, Vector3 D) + { + Vector3 v0 = Vector3.Cross(A - D, P - D); + Vector3 v1 = Vector3.Cross(B - A, P - A); + Vector3 v2 = Vector3.Cross(C - B, P - B); + Vector3 v3 = Vector3.Cross(D - C, P - C); + if (Vector3.Dot(v0, v1) < 0 || Vector3.Dot(v0, v2) < 0 || Vector3.Dot(v0, v3) < 0) + { + return false; + } + else + { + return true; + } + } + + public static bool IsInRect(Vector3 pos, float xMin, float xMax, float yMin, float yMax) + { + return pos.x >= xMin && pos.x <= xMax && pos.y <= yMax && pos.y >= yMin; + } + + public static bool IsColorAlphaZero(Color color) + { + return !ChartHelper.IsClearColor(color) && color.a == 0; + } + + public static float GetActualValue(float valueOrRate, float total, float maxRate = 1.5f) + { + if (valueOrRate >= -maxRate && valueOrRate <= maxRate) return valueOrRate * total; + else return valueOrRate; + } + +#if UNITY_WEBGL + [DllImport("__Internal")] + private static extern void Download(string base64str, string fileName); +#endif + + public static Texture2D SaveAsImage(RectTransform rectTransform, Canvas canvas, string imageType = "png", string path = "") + { + var cam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; + var pos = RectTransformUtility.WorldToScreenPoint(cam, rectTransform.position); + var width = (int)(rectTransform.rect.width * canvas.scaleFactor); + var height = (int)(rectTransform.rect.height * canvas.scaleFactor); + var posX = pos.x + rectTransform.rect.xMin * canvas.scaleFactor; + var posY = pos.y + rectTransform.rect.yMin * canvas.scaleFactor; + var rect = new Rect(posX, posY, width, height); + var tex = new Texture2D(width, height, TextureFormat.ARGB32, false); + tex.ReadPixels(rect, 0, 0); + tex.Apply(); + byte[] bytes; + switch (imageType) + { + case "png": + bytes = tex.EncodeToPNG(); + break; + case "jpg": + bytes = tex.EncodeToJPG(); + break; + case "exr": + bytes = tex.EncodeToEXR(); + break; + default: + Debug.LogError("SaveAsImage ERROR: not support image type:" + imageType); + return null; + } + var fileName = rectTransform.name + "." + imageType; +#if UNITY_WEBGL + string base64str = Convert.ToBase64String(bytes); + Download(base64str, fileName); + Debug.Log("SaveAsImage: download by brower:" + fileName); + return tex; +#else + if (string.IsNullOrEmpty(path)) + { + var dir = Application.persistentDataPath + "/SavedImage"; +#if UNITY_EDITOR + dir = Application.dataPath + "/../SavedImage"; +#else + dir = Application.persistentDataPath + "/SavedImage"; +#endif + if (!System.IO.Directory.Exists(dir)) + { + System.IO.Directory.CreateDirectory(dir); + } + path = dir + "/" + fileName; + } + System.IO.File.WriteAllBytes(path, bytes); + Debug.Log("SaveAsImage:" + path); + return tex; +#endif + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs.meta new file mode 100644 index 0000000..2e6abc8 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ChartHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47cfa7bd879be4069bd187e46346d73d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs b/Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs new file mode 100644 index 0000000..f59a703 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; + +namespace XCharts.Runtime +{ + public static class ComponentHelper + { + public static AngleAxis GetAngleAxis(List<MainComponent> components, int polarIndex) + { + foreach (var component in components) + { + if (component is AngleAxis) + { + var axis = component as AngleAxis; + if (axis.polarIndex == polarIndex) return axis; + } + } + return null; + } + + public static RadiusAxis GetRadiusAxis(List<MainComponent> components, int polarIndex) + { + foreach (var component in components) + { + if (component is RadiusAxis) + { + var axis = component as RadiusAxis; + if (axis.polarIndex == polarIndex) return axis; + } + } + return null; + } + + public static float GetXAxisOnZeroOffset(List<MainComponent> components, XAxis axis) + { + if (!axis.axisLine.onZero) return 0; + foreach (var component in components) + { + if (component is YAxis) + { + var yAxis = component as YAxis; + if (yAxis.IsValue() && yAxis.gridIndex == axis.gridIndex) return yAxis.context.offset; + } + } + return 0; + } + + public static float GetYAxisOnZeroOffset(List<MainComponent> components, YAxis axis) + { + if (!axis.axisLine.onZero) return 0; + foreach (var component in components) + { + if (component is XAxis) + { + var xAxis = component as XAxis; + if (xAxis.IsValue() && xAxis.gridIndex == axis.gridIndex) return xAxis.context.offset; + } + } + return 0; + } + + public static bool IsAnyCategoryOfYAxis(List<MainComponent> components) + { + foreach (var component in components) + { + if (component is YAxis) + { + var yAxis = component as YAxis; + if (yAxis.type == Axis.AxisType.Category) + return true; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs.meta new file mode 100644 index 0000000..df48974 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/ComponentHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b7af706293fe4e63b4d079dbe5c0ea2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs b/Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs new file mode 100644 index 0000000..b039924 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class DataHelper + { + public static double DataAverage(ref List<SerieData> showData, SampleType sampleType, + int minCount, int maxCount, int rate) + { + double totalAverage = 0; + if (rate > 1 && sampleType == SampleType.Peak) + { + double total = 0; + for (int i = minCount; i < maxCount; i++) + { + total += showData[i].data[1]; + } + totalAverage = total / (maxCount - minCount); + } + return totalAverage; + } + + public static double SampleValue(ref List<SerieData> showData, SampleType sampleType, int rate, + int minCount, int maxCount, double totalAverage, int index, float dataAddDuration, float dataChangeDuration, + ref bool dataChanging, Axis axis, bool unscaledTime) + { + var inverse = axis.inverse; + var minValue = 0; + var maxValue = 0; + if (rate <= 1 || index == minCount) + { + if (showData[index].IsDataChanged()) + dataChanging = true; + + return showData[index].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime); + } + switch (sampleType) + { + case SampleType.Sum: + case SampleType.Average: + double total = 0; + var count = 0; + for (int i = index; i > index - rate; i--) + { + count++; + total += showData[i].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime); + if (showData[i].IsDataChanged()) + dataChanging = true; + } + if (sampleType == SampleType.Average) + return total / rate; + else + return total; + + case SampleType.Max: + double max = double.MinValue; + for (int i = index; i > index - rate; i--) + { + var value = showData[i].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime); + if (value > max) + max = value; + + if (showData[i].IsDataChanged()) + dataChanging = true; + } + return max; + + case SampleType.Min: + double min = double.MaxValue; + for (int i = index; i > index - rate; i--) + { + var value = showData[i].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime); + if (value < min) + min = value; + + if (showData[i].IsDataChanged()) + dataChanging = true; + } + return min; + + case SampleType.Peak: + max = double.MinValue; + min = double.MaxValue; + total = 0; + for (int i = index; i > index - rate; i--) + { + var value = showData[i].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime); + total += value; + if (value < min) + min = value; + if (value > max) + max = value; + + if (showData[i].IsDataChanged()) + dataChanging = true; + } + var average = total / rate; + if (average >= totalAverage) + return max; + else + return min; + } + if (showData[index].IsDataChanged()) + dataChanging = true; + + return showData[index].GetCurrData(1, dataAddDuration, dataChangeDuration, inverse, minValue, maxValue, unscaledTime); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs.meta new file mode 100644 index 0000000..da77e53 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/DataHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c982f1be15b204c9190197803101f2db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs b/Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs new file mode 100644 index 0000000..9487070 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs @@ -0,0 +1,111 @@ +#if INPUT_SYSTEM_ENABLED +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace XCharts.Runtime +{ + public class InputHelper + { + public static Vector2 mousePosition + { + get + { + var value = Vector2.zero; + if (null != Mouse.current) + { + value = Mouse.current.position.ReadValue(); + } + else if (null != Touchscreen.current && Touchscreen.current.touches.Count > 0) + { + value = Touchscreen.current.touches[0].position.ReadValue(); + } + return value; + } + } + public static int touchCount + { + get + { + var value = 0; + if (null != Touchscreen.current) + { + value = Touchscreen.current.touches.Count; + } + return value; + } + } + + public static Touch GetTouch(int v) + { + UnityEngine.TouchPhase PhaseConvert(TouchState state) + { + UnityEngine.TouchPhase temp = UnityEngine.TouchPhase.Began; + switch (state.phase) + { + case UnityEngine.InputSystem.TouchPhase.Began: + temp = UnityEngine.TouchPhase.Began; + break; + case UnityEngine.InputSystem.TouchPhase.Moved: + temp = UnityEngine.TouchPhase.Moved; + break; + case UnityEngine.InputSystem.TouchPhase.Canceled: + temp = UnityEngine.TouchPhase.Canceled; + break; + case UnityEngine.InputSystem.TouchPhase.Stationary: + temp = UnityEngine.TouchPhase.Stationary; + break; + default: + case UnityEngine.InputSystem.TouchPhase.Ended: + case UnityEngine.InputSystem.TouchPhase.None: + temp = UnityEngine.TouchPhase.Ended; + break; + } + return temp; + } + var touch = Touchscreen.current.touches[v]; + var value = touch.ReadValue(); + //copy touchcontrol's touchstate data into touch + return new Touch + { + deltaPosition = value.delta, + fingerId = value.touchId, + position = value.position, + phase = PhaseConvert(value), + pressure = value.pressure, + radius = value.radius.magnitude, + radiusVariance = value.radius.sqrMagnitude, + type = value.isPrimaryTouch ? TouchType.Direct : TouchType.Indirect, + tapCount = value.tapCount, + deltaTime = Time.realtimeSinceStartup - (float)value.startTime, + rawPosition = value.startPosition, + }; + } + + public static bool GetKeyDown(KeyCode keyCode) + { + var value = false; + if (null != Keyboard.current) + { + var key = Keyboard.current.spaceKey; + switch (keyCode) + { + case KeyCode.Space: + key = Keyboard.current.spaceKey; + break; + case KeyCode.L: + key = Keyboard.current.lKey; + break; + default: + Debug.LogError($"{nameof(InputHelper)}: not support {keyCode} yet , please add it yourself if needed"); + break; + } + + value = key.wasPressedThisFrame; + } + return value; + } + + } +} +#endif diff --git a/Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs.meta new file mode 100644 index 0000000..fddfc43 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/InputHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5069defa9fe8c7a43843e1189e2d606c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs b/Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs new file mode 100644 index 0000000..6b128f8 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs @@ -0,0 +1,226 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class LayerHelper + { + private static Vector2 s_Vector0And0 = new Vector2(0, 0); + private static Vector2 s_Vector0And0Dot5 = new Vector2(0, 0.5f); + private static Vector2 s_Vector0And1 = new Vector2(0, 1f); + private static Vector2 s_Vector0Dot5And1 = new Vector2(0.5f, 1f); + private static Vector2 s_Vector0Dot5And0Dot5 = new Vector2(0.5f, 0.5f); + private static Vector2 s_Vector0Dot5And0 = new Vector2(0.5f, 0f); + private static Vector2 s_Vector1And1 = new Vector2(1f, 1f); + private static Vector2 s_Vector1And0Dot5 = new Vector2(1f, 0.5f); + private static Vector2 s_Vector1And0 = new Vector2(1f, 0); + + internal static Vector2 ResetChartPositionAndPivot(Vector2 minAnchor, Vector2 maxAnchor, float width, + float height, ref float chartX, ref float chartY) + { + if (IsLeftTop(minAnchor, maxAnchor)) + { + chartX = 0; + chartY = -height; + return s_Vector0And1; + } + else if (IsLeftCenter(minAnchor, maxAnchor)) + { + chartX = 0; + chartY = -height / 2; + return s_Vector0And0Dot5; + } + else if (IsLeftBottom(minAnchor, maxAnchor)) + { + chartX = 0; + chartY = 0; + return s_Vector0And0; + } + else if (IsCenterTop(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = -height; + return s_Vector0Dot5And1; + } + else if (IsCenterCenter(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = -height / 2; + return s_Vector0Dot5And0Dot5; + } + else if (IsCenterBottom(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = 0; + return s_Vector0Dot5And0; + } + else if (IsRightTop(minAnchor, maxAnchor)) + { + chartX = -width; + chartY = -height; + return s_Vector1And1; + } + else if (IsRightCenter(minAnchor, maxAnchor)) + { + chartX = -width; + chartY = -height / 2; + return s_Vector1And0Dot5; + } + else if (IsRightBottom(minAnchor, maxAnchor)) + { + chartX = -width; + chartY = 0; + return s_Vector1And0; + } + else if (IsStretchTop(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = -height; + return s_Vector0Dot5And1; + } + else if (IsStretchMiddle(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = -height / 2; + return s_Vector0Dot5And0Dot5; + } + else if (IsStretchBottom(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = 0; + return s_Vector0Dot5And0; + } + else if (IsStretchLeft(minAnchor, maxAnchor)) + { + chartX = 0; + chartY = -height / 2; + return s_Vector0And0Dot5; + } + else if (IsStretchCenter(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = -height / 2; + return s_Vector0Dot5And0Dot5; + } + else if (IsStretchRight(minAnchor, maxAnchor)) + { + chartX = -width; + chartY = -height / 2; + return s_Vector1And0Dot5; + } + else if (IsStretchStrech(minAnchor, maxAnchor)) + { + chartX = -width / 2; + chartY = -height / 2; + return s_Vector0Dot5And0Dot5; + } + chartX = 0; + chartY = 0; + return Vector2.zero; + } + + private static bool IsLeftTop(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And1 && maxAnchor == s_Vector0And1; + } + + private static bool IsLeftCenter(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And0Dot5 && maxAnchor == s_Vector0And0Dot5; + } + + private static bool IsLeftBottom(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == Vector2.zero && maxAnchor == Vector2.zero; + } + + private static bool IsCenterTop(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0Dot5And1 && maxAnchor == s_Vector0Dot5And1; + } + + private static bool IsCenterCenter(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0Dot5And0Dot5 && maxAnchor == s_Vector0Dot5And0Dot5; + } + + private static bool IsCenterBottom(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0Dot5And0 && maxAnchor == s_Vector0Dot5And0; + } + + private static bool IsRightTop(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector1And1 && maxAnchor == s_Vector1And1; + } + + private static bool IsRightCenter(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector1And0Dot5 && maxAnchor == s_Vector1And0Dot5; + } + + private static bool IsRightBottom(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector1And0 && maxAnchor == s_Vector1And0; + } + + private static bool IsStretchTop(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And1 && maxAnchor == s_Vector1And1; + } + + private static bool IsStretchMiddle(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And0Dot5 && maxAnchor == s_Vector1And0Dot5; + } + + private static bool IsStretchBottom(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And0 && maxAnchor == s_Vector1And0; + } + + private static bool IsStretchLeft(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And0 && maxAnchor == s_Vector0And1; + } + + private static bool IsStretchCenter(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0Dot5And0 && maxAnchor == s_Vector0Dot5And1; + } + + private static bool IsStretchRight(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector1And0 && maxAnchor == s_Vector1And1; + } + + private static bool IsStretchStrech(Vector2 minAnchor, Vector2 maxAnchor) + { + return minAnchor == s_Vector0And0 && maxAnchor == s_Vector1And1; + } + + public static bool IsStretchPivot(RectTransform rt) + { + return IsStretchTop(rt.anchorMin, rt.anchorMax) || + IsStretchMiddle(rt.anchorMin, rt.anchorMax) || + IsStretchBottom(rt.anchorMin, rt.anchorMax) || + IsStretchLeft(rt.anchorMin, rt.anchorMax) || + IsStretchCenter(rt.anchorMin, rt.anchorMax) || + IsStretchRight(rt.anchorMin, rt.anchorMax) || + IsStretchStrech(rt.anchorMin, rt.anchorMax); + } + + public static bool IsFixedWidthHeight(RectTransform rt) + { + return IsLeftTop(rt.anchorMin, rt.anchorMax) || + IsLeftCenter(rt.anchorMin, rt.anchorMax) || + IsLeftBottom(rt.anchorMin, rt.anchorMax) || + IsCenterTop(rt.anchorMin, rt.anchorMax) || + IsCenterCenter(rt.anchorMin, rt.anchorMax) || + IsCenterBottom(rt.anchorMin, rt.anchorMax) || + IsRightTop(rt.anchorMin, rt.anchorMax) || + IsRightCenter(rt.anchorMin, rt.anchorMax) || + IsRightBottom(rt.anchorMin, rt.anchorMax); + } + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs.meta new file mode 100644 index 0000000..f9d31b8 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/LayoutHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d6eeea6fc2824cc891fec0674bf2d71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs b/Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs new file mode 100644 index 0000000..fed7cfb --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs @@ -0,0 +1,60 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class MathUtil + { + public static double Abs(double d) + { + return d > 0 ? d : -d; + } + + public static double Clamp(double d, double min, double max) + { + if (d >= min && d <= max) return d; + else if (d < min) return min; + else return max; + } + + public static bool Approximately(double a, double b) + { + return Math.Abs(b - a) < Math.Max(0.000001f * Math.Max(Math.Abs(a), Math.Abs(b)), Mathf.Epsilon * 8); + } + + public static double Clamp01(double value) + { + if (value < 0F) + return 0F; + else if (value > 1F) + return 1F; + else + return value; + } + + public static double Lerp(double a, double b, double t) + { + return a + (b - a) * Clamp01(t); + } + + public static bool IsInteger(double value) + { + if (value == 0) return true; + if (value >= -1 && value <= 1) return false; + return Math.Abs(value % 1) <= (Double.Epsilon * 100); + } + + public static int GetPrecision(double value) + { + if (IsInteger(value)) return 0; + int count = 1; + double intvalue = value * Mathf.Pow(10, count); + while (!IsInteger(intvalue) && count < 38) + { + count++; + intvalue = value * Mathf.Pow(10, count); + } + return count; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs.meta new file mode 100644 index 0000000..0c3d017 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/MathUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 094dc7b90e3a049b48f15f990c050db1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs b/Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs new file mode 100644 index 0000000..661d413 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// UI帮助类。 + /// </summary> + public static class UIHelper + { + public static void DrawBackground(VertexHelper vh, UIComponent component) + { + var background = component.background; + var rect = component.graphRect; + if (background.imageWidth > 0 || background.imageHeight > 0) + { + if (background.imageWidth > 0) + { + rect.width = background.imageWidth; + rect.x = component.graphX + (component.graphWidth - background.imageWidth) / 2; + } + if (background.imageHeight > 0) + { + rect.height = background.imageHeight; + rect.y = component.graphY + (component.graphHeight - background.imageHeight) / 2; + } + } + background.rect = rect; + if (!background.show) + return; + if (background.image != null) + return; + var backgroundColor = component.theme.GetBackgroundColor(background); + DrawBackground(vh, background, backgroundColor); + } + + public static void DrawBackground(VertexHelper vh, Background background, Color32 color, float smoothness = 2) + { + if (!background.show) + return; + if (background.image != null) + return; + var borderWidth = background.borderStyle.GetRuntimeBorderWidth(); + var borderColor = background.borderStyle.GetRuntimeBorderColor(); + var cornerRadius = background.borderStyle.GetRuntimeCornerRadius(); + UGL.DrawRoundRectangleWithBorder(vh, background.rect, color, color, cornerRadius, + borderWidth, borderColor, 0, smoothness); + } + + internal static void InitBackground(UIComponent component) + { + if (component.background.show == false || + (component.background.image == null && ChartHelper.IsClearColor(component.background.imageColor))) + { + ChartHelper.DestoryGameObject(component.transform, "Background"); + return; + } + var sizeDelta = component.background.imageWidth > 0 && component.background.imageHeight > 0 ? + new Vector2(component.background.imageWidth, component.background.imageHeight) : + component.graphSizeDelta; + var backgroundObj = ChartHelper.AddObject("Background", component.transform, component.graphMinAnchor, + component.graphMaxAnchor, component.graphPivot, sizeDelta); + backgroundObj.hideFlags = component.chartHideFlags; + + var backgroundImage = ChartHelper.EnsureComponent<Image>(backgroundObj); + ChartHelper.UpdateRectTransform(backgroundObj, component.graphMinAnchor, + component.graphMaxAnchor, component.graphPivot, sizeDelta); + ChartHelper.SetBackground(backgroundImage, component.background); + backgroundObj.transform.SetSiblingIndex(0); + backgroundObj.SetActive(component.background.show && component.background.image != null); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs.meta b/Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs.meta new file mode 100644 index 0000000..029495c --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/Utilities/UIHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3be0399ecf6194793aa056e45ebfe20a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs b/Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs new file mode 100644 index 0000000..63d6a91 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs @@ -0,0 +1,168 @@ +#if UNITY_EDITOR + +using System; +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + public class XCResourcesImporter + { + bool m_EssentialResourcesImported; + + public XCResourcesImporter() { } + + public void OnDestroy() { } + + public void OnGUI() + { + m_EssentialResourcesImported = Resources.Load<XCSettings>("XCSettings") != null; + + GUILayout.BeginVertical(); + { + GUILayout.BeginVertical(EditorStyles.helpBox); + { + GUILayout.Label("XCharts Essentials", EditorStyles.boldLabel); + GUILayout.Label("This appears to be the first time you access XCharts, as such we need to add resources to your project that are essential for using XCharts. These new resources will be placed at the root of your project in the \"XCharts\" folder.", new GUIStyle(EditorStyles.label) { wordWrap = true }); + GUILayout.Space(5f); + + GUI.enabled = !m_EssentialResourcesImported; + GUI.enabled = true; + if (GUILayout.Button("Import XCharts Essentials")) + { + string packageFullPath = XChartsMgr.GetPackageFullPath(); + if (packageFullPath != null) + { + var sourPath = Path.Combine(packageFullPath, "Resources"); + var destPath = Path.Combine(Application.dataPath, "XCharts/Resources"); + if (CopyFolder(sourPath, destPath)) + { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } + } + GUILayout.Space(5f); + GUI.enabled = true; + } + GUILayout.EndVertical(); + } + GUILayout.EndVertical(); + GUILayout.Space(5f); + } + + private static bool CopyFolder(string sourPath, string destPath) + { + try + { + if (!Directory.Exists(destPath)) + { + Directory.CreateDirectory(destPath); + } + var files = Directory.GetFiles(sourPath); + foreach (var file in files) + { + var name = Path.GetFileName(file); + var path = Path.Combine(destPath, name); + File.Copy(file, path); + } + var folders = Directory.GetDirectories(sourPath); + foreach (var folder in folders) + { + var name = Path.GetFileName(folder); + var path = Path.Combine(destPath, name); + CopyFolder(folder, path); + } + return true; + } + catch (Exception e) + { + Debug.LogError("CopyFolder:" + e.Message); + return false; + } + } + + internal void RegisterResourceImportCallback() + { + AssetDatabase.importPackageCompleted += ImportCallback; + } + + /// <summary> + /// + /// </summary> + /// <param name="packageName"></param> + void ImportCallback(string packageName) + { + if (packageName == "XCharts Essential Resources") + { + m_EssentialResourcesImported = true; +#if UNITY_2018_3_OR_NEWER + SettingsService.NotifySettingsProviderChanged(); +#endif + } + Debug.Log("[" + packageName + "] have been imported."); + + AssetDatabase.importPackageCompleted -= ImportCallback; + } + } + + public class XCResourceImporterWindow : UnityEditor.EditorWindow + { + [SerializeField] XCResourcesImporter m_ResourceImporter; + + static XCResourceImporterWindow m_ImporterWindow; + + public static void ShowPackageImporterWindow() + { + var packagePath = XChartsMgr.GetPackageFullPath(); + if (packagePath != null) + { + if (m_ImporterWindow == null) + { + m_ImporterWindow = GetWindow<XCResourceImporterWindow>(); + m_ImporterWindow.titleContent = new GUIContent("XCharts Importer"); + } + m_ImporterWindow.Focus(); + } + } + + void OnEnable() + { + SetEditorWindowSize(); + + if (m_ResourceImporter == null) + m_ResourceImporter = new XCResourcesImporter(); + } + + void OnDestroy() + { + m_ResourceImporter.OnDestroy(); + } + + void OnGUI() + { + m_ResourceImporter.OnGUI(); + } + + void OnInspectorUpdate() + { + Repaint(); + } + + /// <summary> + /// Limits the minimum size of the editor window. + /// ||</summary> + void SetEditorWindowSize() + { + EditorWindow editorWindow = this; + + Vector2 windowSize = new Vector2(640, 210); + editorWindow.minSize = windowSize; + editorWindow.maxSize = windowSize; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs.meta b/Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs.meta new file mode 100644 index 0000000..2fd06d7 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XCResourcesImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fee2f9747b8914ddba13895caa2aa236 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/XCSettings.cs b/Assets/XCharts/Runtime/Internal/XCSettings.cs new file mode 100644 index 0000000..6d5f7a9 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XCSettings.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace XCharts.Runtime +{ + [Serializable] +#if UNITY_2018_3 + + [ExcludeFromPresetAttribute] +#endif + public class XCSettings : ScriptableObject + { + public readonly static string THEME_ASSET_NAME_PREFIX = "XCTheme-"; + public readonly static string THEME_ASSET_FOLDER = "Assets/XCharts/Resources"; + + [SerializeField] private Lang m_Lang = null; + [SerializeField] private Font m_Font = null; +#if dUI_TextMeshPro + [SerializeField] private TMP_FontAsset m_TMPFont = null; +#endif + [SerializeField][Range(1, 200)] private int m_FontSizeLv1 = 28; + [SerializeField][Range(1, 200)] private int m_FontSizeLv2 = 24; + [SerializeField][Range(1, 200)] private int m_FontSizeLv3 = 20; + [SerializeField][Range(1, 200)] private int m_FontSizeLv4 = 18; + [SerializeField] private LineStyle.Type m_AxisLineType = LineStyle.Type.Solid; + [SerializeField][Range(0, 20)] private float m_AxisLineWidth = 0.8f; + [SerializeField] private LineStyle.Type m_AxisSplitLineType = LineStyle.Type.Solid; + [SerializeField][Range(0, 20)] private float m_AxisSplitLineWidth = 0.8f; + [SerializeField][Range(0, 20)] private float m_AxisTickWidth = 0.8f; + [SerializeField][Range(0, 20)] private float m_AxisTickLength = 5f; + [SerializeField][Range(0, 200)] private float m_GaugeAxisLineWidth = 15f; + [SerializeField][Range(0, 20)] private float m_GaugeAxisSplitLineWidth = 0.8f; + [SerializeField][Range(0, 20)] private float m_GaugeAxisSplitLineLength = 15f; + [SerializeField][Range(0, 20)] private float m_GaugeAxisTickWidth = 0.8f; + [SerializeField][Range(0, 20)] private float m_GaugeAxisTickLength = 5f; + [SerializeField][Range(0, 20)] private float m_TootipLineWidth = 0.8f; + [SerializeField][Range(0, 20)] private float m_DataZoomBorderWidth = 0.5f; + [SerializeField][Range(0, 20)] private float m_DataZoomDataLineWidth = 0.5f; + [SerializeField][Range(0, 20)] private float m_VisualMapBorderWidth = 0f; + + [SerializeField][Range(0, 20)] private float m_SerieLineWidth = 1.8f; + [SerializeField][Range(0, 200)] private float m_SerieLineSymbolSize = 5f; + [SerializeField][Range(0, 200)] private float m_SerieScatterSymbolSize = 20f; + [SerializeField][Range(0, 200)] private float m_SerieSelectedRate = 1.3f; + [SerializeField][Range(0, 10)] private float m_SerieCandlestickBorderWidth = 1f; + + [SerializeField] private bool m_EditorShowAllListData = false; + + [SerializeField][Range(1, 20)] protected int m_MaxPainter = 10; + [SerializeField][Range(1, 10)] protected float m_LineSmoothStyle = 3f; + [SerializeField][Range(1f, 20)] protected float m_LineSmoothness = 2f; + [SerializeField][Range(1f, 20)] protected float m_LineSegmentDistance = 3f; + [SerializeField][Range(1, 10)] protected float m_CicleSmoothness = 2f; + [SerializeField][Range(10, 50)] protected float m_VisualMapTriangeLen = 20f; + [SerializeField] protected List<Theme> m_CustomThemes = new List<Theme>(); + + public static Lang lang { get { return Instance.m_Lang; } } + public static Font font { get { return Instance.m_Font; } } +#if dUI_TextMeshPro + public static TMP_FontAsset tmpFont { get { return Instance.m_TMPFont; } } +#endif + /// <summary> + /// 一级字体大小。 + /// </summary> + public static int fontSizeLv1 { get { return Instance.m_FontSizeLv1; } } + public static int fontSizeLv2 { get { return Instance.m_FontSizeLv2; } } + public static int fontSizeLv3 { get { return Instance.m_FontSizeLv3; } } + public static int fontSizeLv4 { get { return Instance.m_FontSizeLv4; } } + public static LineStyle.Type axisLineType { get { return Instance.m_AxisLineType; } } + public static float axisLineWidth { get { return Instance.m_AxisLineWidth; } } + public static LineStyle.Type axisSplitLineType { get { return Instance.m_AxisSplitLineType; } } + public static float axisSplitLineWidth { get { return Instance.m_AxisSplitLineWidth; } } + public static float axisTickWidth { get { return Instance.m_AxisTickWidth; } } + public static float axisTickLength { get { return Instance.m_AxisTickLength; } } + public static float gaugeAxisLineWidth { get { return Instance.m_GaugeAxisLineWidth; } } + public static float gaugeAxisSplitLineWidth { get { return Instance.m_GaugeAxisSplitLineWidth; } } + public static float gaugeAxisSplitLineLength { get { return Instance.m_GaugeAxisSplitLineLength; } } + public static float gaugeAxisTickWidth { get { return Instance.m_GaugeAxisTickWidth; } } + public static float gaugeAxisTickLength { get { return Instance.m_GaugeAxisTickLength; } } + + public static float tootipLineWidth { get { return Instance.m_TootipLineWidth; } } + public static float dataZoomBorderWidth { get { return Instance.m_DataZoomBorderWidth; } } + public static float dataZoomDataLineWidth { get { return Instance.m_DataZoomDataLineWidth; } } + public static float visualMapBorderWidth { get { return Instance.m_VisualMapBorderWidth; } } + + #region serie + public static float serieLineWidth { get { return Instance.m_SerieLineWidth; } } + public static float serieLineSymbolSize { get { return Instance.m_SerieLineSymbolSize; } } + public static float serieScatterSymbolSize { get { return Instance.m_SerieScatterSymbolSize; } } + public static float serieSelectedRate { get { return Instance.m_SerieSelectedRate; } } + public static float serieCandlestickBorderWidth { get { return Instance.m_SerieCandlestickBorderWidth; } } + #endregion + + #region editor + public static bool editorShowAllListData { get { return Instance.m_EditorShowAllListData; } } + #endregion + + #region graphic + public static int maxPainter { get { return Instance.m_MaxPainter; } } + public static float lineSmoothStyle { get { return Instance.m_LineSmoothStyle; } } + public static float lineSmoothness { get { return Instance.m_LineSmoothness; } } + public static float lineSegmentDistance { get { return Instance.m_LineSegmentDistance; } } + public static float cicleSmoothness { get { return Instance.m_CicleSmoothness; } } + public static float visualMapTriangeLen { get { return Instance.m_VisualMapTriangeLen; } } + #endregion + + public static List<Theme> customThemes { get { return Instance.m_CustomThemes; } } + + private static XCSettings s_Instance; + public static XCSettings Instance + { + get + { + if (s_Instance == null) + { + s_Instance = Resources.Load<XCSettings>("XCSettings"); +#if UNITY_EDITOR + if (s_Instance == null) + { + var assetPath = GetSettingAssetPath(); + if (string.IsNullOrEmpty(assetPath)) + XCResourceImporterWindow.ShowPackageImporterWindow(); + else + s_Instance = AssetDatabase.LoadAssetAtPath<XCSettings>(assetPath); + } + else + { + if (s_Instance.m_Lang == null) + s_Instance.m_Lang = Resources.Load<Lang>("XCLang-EN"); + if (s_Instance.m_Lang == null) + s_Instance.m_Lang = ScriptableObject.CreateInstance<Lang>(); + if (s_Instance.m_Font == null) + s_Instance.m_Font = Resources.GetBuiltinResource<Font>("Arial.ttf"); +#if dUI_TextMeshPro + if (s_Instance.m_TMPFont == null) + s_Instance.m_TMPFont = Resources.Load<TMP_FontAsset>("LiberationSans SDF"); +#endif + } +#endif + } + return s_Instance; + } + } + +#if UNITY_EDITOR + public static bool ExistAssetFile() + { + return System.IO.File.Exists("Assets/XCharts/Resources/XCSettings.asset"); + } + + public static string GetSettingAssetPath() + { + var path = "Assets/XCharts/Resources/XCSettings.asset"; + if (File.Exists(path)) return path; + var dir = Application.dataPath; + string[] matchingPaths = Directory.GetDirectories(dir); + foreach (var match in matchingPaths) + { + if (match.Contains("XCharts")) + { + var jsonPath = string.Format("{0}/package.json", match); + if (File.Exists(jsonPath)) + { + var jsonText = File.ReadAllText(jsonPath); + if (jsonText.Contains("\"displayName\": \"XCharts\"")) + { + path = string.Format("{0}/Resources/XCSettings.asset", match.Replace('\\', '/')); + if (File.Exists(path)) + return path.Substring(path.IndexOf("/Assets/") + 1); + } + } + } + } + return null; + } +#endif + + public static bool AddCustomTheme(Theme theme) + { + if (theme == null) return false; + if (Instance == null || Instance.m_CustomThemes == null) return false; + if (!Instance.m_CustomThemes.Contains(theme)) + { + Instance.m_CustomThemes.Add(theme); +#if UNITY_EDITOR + EditorUtility.SetDirty(Instance); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); +#endif + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/XCSettings.cs.meta b/Assets/XCharts/Runtime/Internal/XCSettings.cs.meta new file mode 100644 index 0000000..4fc72f2 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XCSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3694d869548264b718bdfc6c8009dcf1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/XCThemeMgr.cs b/Assets/XCharts/Runtime/Internal/XCThemeMgr.cs new file mode 100644 index 0000000..a30148a --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XCThemeMgr.cs @@ -0,0 +1,160 @@ +using System.Collections.Generic; +using System.IO; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + public static class XCThemeMgr + { + /// <summary> + /// 重新加载主题列表 + /// </summary> + public static void ReloadThemeList() + { + XChartsMgr.themes.Clear(); + XChartsMgr.themeNames.Clear(); + AddTheme(LoadTheme(ThemeType.Default)); + AddTheme(LoadTheme(ThemeType.Dark)); + if (XCSettings.Instance != null) + { + foreach (var theme in XCSettings.customThemes) + { + AddTheme(theme); + } + } + } + + public static void CheckReloadTheme() + { + if (XChartsMgr.themeNames.Count < 0) + ReloadThemeList(); + } + + public static void AddTheme(Theme theme) + { + if (theme == null) return; + if (!XChartsMgr.themes.ContainsKey(theme.themeName)) + { + XChartsMgr.themes.Add(theme.themeName, theme); + XChartsMgr.themeNames.Add(theme.themeName); + XChartsMgr.themeNames.Sort(); + } + } + + public static Theme GetTheme(ThemeType type) + { + return GetTheme(type.ToString()); + } + + public static Theme GetTheme(string themeName) + { + if (!XChartsMgr.themes.ContainsKey(themeName)) + { + ReloadThemeList(); + if (XChartsMgr.themes.ContainsKey(themeName)) + return XChartsMgr.themes[themeName]; + else + return null; + } + else + { + return XChartsMgr.themes[themeName]; + } + } + + public static Theme LoadTheme(ThemeType type) + { + return LoadTheme(type.ToString()); + } + + public static Theme LoadTheme(string themeName) + { + var theme = Resources.Load<Theme>(XCSettings.THEME_ASSET_NAME_PREFIX + themeName); + if (theme == null) + theme = Resources.Load<Theme>(themeName); + return theme; + } + + public static List<string> GetAllThemeNames() + { + return XChartsMgr.themeNames; + } + + public static List<Theme> GetThemeList() + { + var list = new List<Theme>(); + foreach (var theme in XChartsMgr.themes.Values) + { + list.Add(theme); + } + return list; + } + + public static bool ContainsTheme(string themeName) + { + return XChartsMgr.themeNames.Contains(themeName); + } + + public static void SwitchTheme(BaseChart chart, string themeName) + { +#if UNITY_EDITOR + if (XChartsMgr.themes.Count == 0) + { + ReloadThemeList(); + } +#endif + if (!XChartsMgr.themes.ContainsKey(themeName)) + { + Debug.LogError("SwitchTheme ERROR: not exist theme:" + themeName); + return; + } + var target = XChartsMgr.themes[themeName]; + chart.UpdateTheme(target); + } + + public static bool ExportTheme(Theme theme, string themeNewName) + { +#if UNITY_EDITOR + var newtheme = Theme.EmptyTheme; + newtheme.CopyTheme(theme); + newtheme.themeType = ThemeType.Custom; + newtheme.themeName = themeNewName; + ExportTheme(newtheme); + return true; +#else + return false; +#endif + } + + public static bool ExportTheme(Theme theme) + { +#if UNITY_EDITOR + var themeAssetName = XCSettings.THEME_ASSET_NAME_PREFIX + theme.themeName; + var themeAssetPath = Application.dataPath + "/../" + XCSettings.THEME_ASSET_FOLDER; + if (!Directory.Exists(themeAssetPath)) + { + Directory.CreateDirectory(themeAssetPath); + } + var themeAssetFilePath = string.Format("{0}/{1}.asset", XCSettings.THEME_ASSET_FOLDER, themeAssetName); + AssetDatabase.CreateAsset(theme, themeAssetFilePath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + return true; +#else + return false; +#endif + } + + public static string GetThemeAssetPath(string themeName) + { + return string.Format("{0}/{1}{2}.asset", XCSettings.THEME_ASSET_FOLDER, + XCSettings.THEME_ASSET_NAME_PREFIX, themeName); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/XCThemeMgr.cs.meta b/Assets/XCharts/Runtime/Internal/XCThemeMgr.cs.meta new file mode 100644 index 0000000..7ac9815 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XCThemeMgr.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faf4bcb5b4fa24f0782ab4737a448696 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/XChartsMgr.cs b/Assets/XCharts/Runtime/Internal/XChartsMgr.cs new file mode 100644 index 0000000..dc7f09a --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XChartsMgr.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.SceneManagement; +using System.Linq; +#if UNITY_EDITOR +using ADB = UnityEditor.AssetDatabase; +#endif + +namespace XCharts.Runtime +{ + class XChartsVersion + { + public string version = ""; + public int date = 0; + public int checkdate = 0; + public string desc = ""; + public string homepage = ""; + } + + [ExecuteInEditMode] + public static class XChartsMgr + { + public static readonly string version = "3.14.0"; + public static readonly int versionDate = 20250315; + public static string fullVersion { get { return version + "-" + versionDate; } } + + internal static List<BaseChart> chartList = new List<BaseChart>(); + internal static Dictionary<string, Theme> themes = new Dictionary<string, Theme>(); + internal static List<string> themeNames = new List<string>(); + + static XChartsMgr() + { + SerieLabelPool.ClearAll(); + chartList.Clear(); + if (Resources.Load<XCSettings>("XCSettings")) + XCThemeMgr.ReloadThemeList(); + SceneManager.sceneUnloaded += OnSceneLoaded; + } + + static void OnSceneLoaded(Scene scene) + { + SerieLabelPool.ClearAll(); + } + + public static void AddChart(BaseChart chart) + { + var sameNameChart = GetChart(chart.chartName); + if (sameNameChart != null) + { + var path = ChartHelper.GetFullName(sameNameChart.transform); + Debug.LogError("A chart named `" + chart.chartName + "` already exists:" + path); + RemoveChart(chart.chartName); + } + if (!ContainsChart(chart)) + { + chartList.Add(chart); + } + } + + public static BaseChart GetChart(string chartName) + { + if (string.IsNullOrEmpty(chartName)) return null; + return chartList.Find(chart => chartName.Equals(chart.chartName)); + } + + public static List<BaseChart> GetCharts(string chartName) + { + if (string.IsNullOrEmpty(chartName)) return null; + return chartList.FindAll(chart => chartName.Equals(chart.chartName)); + } + + public static void RemoveChart(string chartName) + { + if (string.IsNullOrEmpty(chartName)) return; + chartList.RemoveAll(chart => chartName.Equals(chart.chartName)); + } + + public static bool ContainsChart(string chartName) + { + if (string.IsNullOrEmpty(chartName)) return false; + var list = GetCharts(chartName); + return list != null && list.Count > 0; + } + + public static bool ContainsChart(BaseChart chart) + { + return chartList.Contains(chart); + } + + public static bool IsRepeatChartName(BaseChart chart, string chartName = null) + { + if (chartName == null) + chartName = chart.chartName; + if (string.IsNullOrEmpty(chartName)) + return false; + foreach (var temp in chartList) + { + if (temp != chart && chartName.Equals(temp.chartName)) + return true; + } + return false; + } + + public static string GetRepeatChartNameInfo(BaseChart chart, string chartName) + { + if (string.IsNullOrEmpty(chartName)) + return string.Empty; + string result = ""; + foreach (var temp in chartList) + { + if (temp != chart && chartName.Equals(temp.chartName)) + result += ChartHelper.GetFullName(temp.transform) + "\n"; + } + return result; + } + + public static void RemoveAllChartObject() + { + if (chartList.Count == 0) + { + return; + } + foreach (var chart in chartList) + { + if (chart != null) + chart.RebuildChartObject(); + } + } + +#if UNITY_EDITOR + + public static string GetPackageFullPath() + { + string packagePath = Path.GetFullPath("Packages/com.monitor1394.xcharts"); + if (Directory.Exists(packagePath)) + { + return packagePath; + } + packagePath = ADB.FindAssets("t:Script") + .Where(v => Path.GetFileNameWithoutExtension(ADB.GUIDToAssetPath(v)) == "XChartsMgr") + .Select(id => ADB.GUIDToAssetPath(id)) + .FirstOrDefault(); + packagePath = Path.GetDirectoryName(packagePath); + packagePath = packagePath.Substring(0, packagePath.LastIndexOf("Runtime")); + return packagePath; + } + + [UnityEditor.Callbacks.DidReloadScripts] + static void OnEditorReload() + { + for (int i = chartList.Count - 1; i >= 0; i--) + { + var chart = chartList[i]; + if (chart == null) + { + chartList.RemoveAt(i); + } + else + { + chart.InitComponentHandlers(); + chart.InitSerieHandlers(); + } + } + } +#endif + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Internal/XChartsMgr.cs.meta b/Assets/XCharts/Runtime/Internal/XChartsMgr.cs.meta new file mode 100644 index 0000000..f86bdc6 --- /dev/null +++ b/Assets/XCharts/Runtime/Internal/XChartsMgr.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 953f0e846565c4086a4bcdc6bc14cf85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie.meta b/Assets/XCharts/Runtime/Serie.meta new file mode 100644 index 0000000..01ece4f --- /dev/null +++ b/Assets/XCharts/Runtime/Serie.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6db844a618e3c4634ac6c8afa60d4835 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Bar.meta b/Assets/XCharts/Runtime/Serie/Bar.meta new file mode 100644 index 0000000..924588f --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db4db63725f6848e785146f5cf4bb657 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Bar/Bar.cs b/Assets/XCharts/Runtime/Serie/Bar/Bar.cs new file mode 100644 index 0000000..aad83f3 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/Bar.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(BarHandler), true)] + [SerieConvert(typeof(Line), typeof(Pie))] + [CoordOptions(typeof(GridCoord), typeof(PolarCoord))] + [DefaultAnimation(AnimationType.BottomToTop)] + [DefaultTooltip(Tooltip.Type.Shadow, Tooltip.Trigger.Axis)] + [SerieComponent(typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField("m_Ignore")] + public class Bar : Serie, INeedSerieContainer + { + public override bool useSortData { get { return realtimeSort; } } + + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Bar>(serieName); + for (int i = 0; i < 5; i++) + { + chart.AddData(serie.index, UnityEngine.Random.Range(10, 90)); + } + return serie; + } + + public static Bar ConvertSerie(Serie serie) + { + var newSerie = SerieHelper.CloneSerie<Bar>(serie); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Bar/Bar.cs.meta b/Assets/XCharts/Runtime/Serie/Bar/Bar.cs.meta new file mode 100644 index 0000000..b13d9c4 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/Bar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cfb8051cfc49e4afabd94a11c5912c3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs new file mode 100644 index 0000000..868204d --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs @@ -0,0 +1,215 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// For polar coord + /// </summary> + internal sealed partial class BarHandler + { + private PolarCoord m_SeriePolar; + + private void UpdateSeriePolarContext() + { + if (m_SeriePolar == null) + return; + + var needCheck = (chart.isPointerInChart && m_SeriePolar.IsPointerEnter()) || m_LegendEnter; + var lineWidth = 0f; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + var needAnimation1 = false; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + serie.interact.SetValue(ref needAnimation1, lineWidth); + foreach (var serieData in serie.data) + { + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + var symbolSize = symbol.GetSize(serieData, chart.theme.serie.lineSymbolSize); + serieData.context.highlight = false; + serieData.interact.SetValue(ref needAnimation1, symbolSize); + } + if (needAnimation1) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + return; + } + m_LastCheckContextFlag = needCheck; + var themeSymbolSize = chart.theme.serie.lineSymbolSize; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + + var needInteract = false; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis); + serieData.context.highlight = true; + serieData.interact.SetValue(ref needInteract, size); + } + } + else + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + var dir = chart.pointerPos - new Vector2(m_SeriePolar.context.center.x, m_SeriePolar.context.center.y); + var pointerAngle = ChartHelper.GetAngle360(Vector2.up, dir); + var pointerRadius = Vector2.Distance(chart.pointerPos, m_SeriePolar.context.center); + Color32 color, toColor; + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + if (pointerAngle >= serieData.context.startAngle && + pointerAngle < serieData.context.toAngle && + pointerRadius >= serieData.context.insideRadius && + pointerRadius < serieData.context.outsideRadius) + { + serie.context.pointerItemDataIndex = i; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + } + else + { + serieData.context.highlight = false; + } + var state = SerieHelper.GetSerieState(serie, serieData, true); + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, state); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + } + if (needInteract) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + + private void DrawPolarBar(VertexHelper vh, Serie serie) + { + var datas = serie.data; + if (datas.Count <= 0) + return; + + m_SeriePolar = chart.GetChartComponent<PolarCoord>(serie.polarIndex); + if (m_SeriePolar == null) + return; + + var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, m_SeriePolar.index); + var m_RadiusAxis = ComponentHelper.GetRadiusAxis(chart.components, m_SeriePolar.index); + if (m_AngleAxis == null || m_RadiusAxis == null) + return; + + var startAngle = m_AngleAxis.context.startAngle; + var currDetailProgress = 0f; + var totalDetailProgress = datas.Count; + + serie.animation.InitProgress(currDetailProgress, totalDetailProgress); + + var isStack = SeriesHelper.IsStack<Bar>(chart.series, serie.stack); + if (isStack) + SeriesHelper.UpdateStackDataList(chart.series, serie, null, m_StackSerieData); + + var barCount = chart.GetSerieBarRealCount<Bar>(-1); + var categoryWidth = m_AngleAxis.IsCategory() ? + AxisHelper.GetDataWidth(m_AngleAxis, 360, datas.Count, null) : + AxisHelper.GetDataWidth(m_RadiusAxis, m_SeriePolar.context.radius, datas.Count, null); + var barGap = chart.GetSerieBarGap<Bar>(-1); + var totalBarWidth = chart.GetSerieTotalWidth<Bar>(categoryWidth, barGap, barCount, -1); + var barWidth = serie.GetBarWidth(categoryWidth, barCount); + var offset = (categoryWidth - totalBarWidth) * 0.5f; + var serieReadIndex = chart.GetSerieIndexIfStack<Bar>(serie, -1); + float gap = serie.barGap == -1 ? offset : + offset + chart.GetSerieTotalGap<Bar>(categoryWidth, barGap, serieReadIndex, -1); + + var areaColor = ColorUtil.clearColor32; + var areaToColor = ColorUtil.clearColor32; + var interacting = false; + var interactDuration = serie.animation.GetInteractionDuration(); + + float start, end; + float inside, outside; + double radiusValue, angleValue; + for (int i = 0; i < datas.Count; i++) + { + if (serie.animation.CheckDetailBreak(i)) + break; + var serieData = datas[i]; + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + var borderWidth = itemStyle.borderWidth; + var borderColor = itemStyle.borderColor; + + radiusValue = serieData.GetData(0); + angleValue = serieData.GetData(1); + if (m_AngleAxis.IsCategory()) + { + start = (float)(startAngle + categoryWidth * angleValue + gap); + end = start + barWidth; + inside = m_SeriePolar.context.insideRadius; + if (isStack) + { + for (int n = 0; n < m_StackSerieData.Count - 1; n++) + inside += m_StackSerieData[n][i].context.stackHeight; + } + outside = inside + m_RadiusAxis.GetValueLength(radiusValue, m_SeriePolar.context.radius); + serieData.context.stackHeight = outside - inside; + } + else + { + start = startAngle; + if (isStack) + { + for (int n = 0; n < m_StackSerieData.Count - 1; n++) + start += m_StackSerieData[n][i].context.stackHeight; + } + end = start + m_AngleAxis.GetValueLength(angleValue, 360); + serieData.context.stackHeight = end - start; + inside = m_SeriePolar.context.insideRadius + categoryWidth * (float)radiusValue + gap; + outside = inside + barWidth; + } + serieData.context.startAngle = start; + serieData.context.toAngle = end; + serieData.context.halfAngle = (start + end) / 2; + + if (!serieData.interact.TryGetColor(ref areaColor, ref areaToColor, ref interacting, interactDuration)) + { + SerieHelper.GetItemColor(out areaColor, out areaToColor, serie, serieData, chart.theme); + serieData.interact.SetColor(ref interacting, areaColor, areaToColor); + } + + var needRoundCap = serie.roundCap && inside > 0; + + serieData.context.insideRadius = inside; + serieData.context.outsideRadius = outside; + serieData.context.areaCenter = m_SeriePolar.context.center; + serieData.context.position = ChartHelper.GetPosition(m_SeriePolar.context.center, (start + end) / 2, (inside + outside) / 2); + + UGL.DrawDoughnut(vh, m_SeriePolar.context.center, inside, outside, areaColor, areaToColor, + ColorUtil.clearColor32, start, end, borderWidth, borderColor, serie.gap / 2, chart.settings.cicleSmoothness, + needRoundCap, true); + } + + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(totalDetailProgress); + serie.animation.CheckSymbol(serie.symbol.GetSize(null, chart.theme.serie.lineSymbolSize)); + chart.RefreshChart(); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs.meta b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs.meta new file mode 100644 index 0000000..30d890f --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.PolarCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 152848d4f7ed84b0491d277fd55b64ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs new file mode 100644 index 0000000..f2990c1 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs @@ -0,0 +1,463 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed partial class BarHandler : SerieHandler<Bar> + { + List<List<SerieData>> m_StackSerieData = new List<List<SerieData>>(); + private GridCoord m_SerieGrid; + private float[] m_CapusleDefaultCornerRadius = new float[] { 1, 1, 1, 1 }; + + public override void UpdateSerieContext() + { + if (serie.IsUseCoord<GridCoord>()) + UpdateSerieGridContext(); + else if (serie.IsUseCoord<PolarCoord>()) + UpdateSeriePolarContext(); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + UpdateCoordSerieParams(ref paramList, ref title, dataIndex, showCategory, category, + marker, itemFormatter, numericFormatter, ignoreDataDefaultContent); + } + + public override void DrawSerie(VertexHelper vh) + { + if (serie.IsUseCoord<PolarCoord>()) + { + DrawPolarBar(vh, serie); + } + else if (serie.IsUseCoord<GridCoord>()) + { + DrawBarSerie(vh, serie); + } + } + + public override Vector3 GetSerieDataLabelPosition(SerieData serieData, LabelStyle label) + { + if (serie.IsUseCoord<PolarCoord>()) + { + switch (label.position) + { + case LabelStyle.Position.Bottom: + var center = serieData.context.areaCenter; + var angle = serieData.context.halfAngle; + var radius = serieData.context.insideRadius; + return ChartHelper.GetPosition(center, angle, radius); + case LabelStyle.Position.Top: + center = serieData.context.areaCenter; + angle = serieData.context.halfAngle; + radius = serieData.context.outsideRadius; + return ChartHelper.GetPosition(center, angle, radius); + default: + return serieData.context.position; + } + } + else + { + switch (label.position) + { + case LabelStyle.Position.Bottom: + var center = serieData.context.rect.center; + if (serie.context.isHorizontal) + return new Vector3(center.x - serieData.context.rect.width / 2, center.y); + else + return new Vector3(center.x, center.y - serieData.context.rect.height / 2); + case LabelStyle.Position.Center: + case LabelStyle.Position.Inside: + return serieData.context.rect.center; + default: + return serieData.context.position; + } + } + } + + private void UpdateSerieGridContext() + { + if (m_SerieGrid == null) + return; + + var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter() && !serie.placeHolder) || m_LegendEnter; + var needInteract = false; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + Color32 color1, toColor1; + foreach (var serieData in serie.data) + { + serieData.context.highlight = false; + var state = SerieHelper.GetSerieState(serie, serieData, true); + SerieHelper.GetItemColor(out color1, out toColor1, serie, serieData, chart.theme, state); + serieData.interact.SetColor(ref needInteract, color1, toColor1); + } + chart.RefreshPainter(serie); + } + return; + } + m_LastCheckContextFlag = needCheck; + Color32 color, toColor; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + foreach (var serieData in serie.data) + { + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + } + else + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + if (serie.context.pointerAxisDataIndexs.Contains(serieData.index) || + serieData.context.rect.Contains(chart.pointerPos)) + { + serie.context.pointerItemDataIndex = serieData.index; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + } + else + { + serieData.context.highlight = false; + } + var state = SerieHelper.GetSerieState(serie, serieData, true); + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, state); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + private void DrawBarSerie(VertexHelper vh, Bar serie) + { + if (!serie.show || serie.animation.HasFadeOut()) + return; + + Axis axis; + Axis relativedAxis; + var isY = chart.GetSerieGridCoordAxis(serie, out axis, out relativedAxis); + if (axis == null) + return; + if (relativedAxis == null) + return; + + m_SerieGrid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (m_SerieGrid == null) + return; + if (serie.useSortData) + { + SerieHelper.UpdateSerieRuntimeFilterData(serie); + } + + var dataZoom = chart.GetDataZoomOfAxis(axis); + var showData = serie.GetDataList(dataZoom, true); + + if (showData.Count <= 0) + return; + + var axisLength = isY ? m_SerieGrid.context.height : m_SerieGrid.context.width; + var relativedAxisLength = isY ? m_SerieGrid.context.width : m_SerieGrid.context.height; + var axisXY = isY ? m_SerieGrid.context.y : m_SerieGrid.context.x; + + var isStack = SeriesHelper.IsStack<Bar>(chart.series, serie.stack); + if (isStack) + SeriesHelper.UpdateStackDataList(chart.series, serie, dataZoom, m_StackSerieData); + + var barCount = chart.GetSerieBarRealCount<Bar>(m_SerieGrid.index); + float categoryWidth = AxisHelper.GetDataWidth(axis, axisLength, showData.Count, dataZoom); + float relativedCategoryWidth = AxisHelper.GetDataWidth(relativedAxis, relativedAxisLength, showData.Count, dataZoom); + float barGap = chart.GetSerieBarGap<Bar>(m_SerieGrid.index); + float totalBarWidth = chart.GetSerieTotalWidth<Bar>(categoryWidth, barGap, barCount, m_SerieGrid.index); + float barWidth = serie.GetBarWidth(categoryWidth, barCount); + float offset = (categoryWidth - totalBarWidth) * 0.5f; + var serieReadIndex = chart.GetSerieIndexIfStack<Bar>(serie, m_SerieGrid.index); + float gap = serie.barGap == -1 ? offset : + offset + chart.GetSerieTotalGap<Bar>(categoryWidth, barGap, serieReadIndex, m_SerieGrid.index); + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > showData.Count ? showData.Count : serie.maxShow) : + showData.Count; + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series, serie.stack); + var dataChanging = false; + var dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var interactDuration = serie.animation.GetInteractionDuration(); + + var areaColor = ColorUtil.clearColor32; + var areaToColor = ColorUtil.clearColor32; + var interacting = false; + + serie.context.isHorizontal = isY; + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + serie.animation.InitProgress(axisXY, axisXY + axisLength); + for (int i = serie.minShow; i < maxCount; i++) + { + var serieData = showData[i]; + if (!serieData.show || serie.IsIgnoreValue(serieData)) + { + serie.context.dataPoints.Add(Vector3.zero); + serie.context.dataIndexs.Add(serieData.index); + continue; + } + + if (serieData.IsDataChanged()) + dataChanging = true; + + var state = SerieHelper.GetSerieState(serie, serieData); + var itemStyle = SerieHelper.GetItemStyle(serie, serieData, state); + var value = axis.IsCategory() ? i : serieData.GetData(0, axis.inverse); + var relativedValue = serieData.GetCurrData(1, dataAddDuration, dataChangeDuration, relativedAxis.inverse, 0, 0, serie.animation.unscaledTime); + var borderWidth = relativedValue == 0 ? 0 : itemStyle.borderWidth; + var borderGap = relativedValue == 0 ? 0 : itemStyle.borderGap; + var borderGapAndWidth = borderWidth + borderGap; + var backgroundColor = itemStyle.backgroundColor; + + if (!serieData.interact.TryGetColor(ref areaColor, ref areaToColor, ref interacting, interactDuration)) + { + SerieHelper.GetItemColor(out areaColor, out areaToColor, serie, serieData, chart.theme); + serieData.interact.SetColor(ref interacting, areaColor, areaToColor); + } + + var pX = 0f; + var pY = 0f; + UpdateXYPosition(m_SerieGrid, isY, axis, relativedAxis, i, categoryWidth, relativedCategoryWidth, barWidth, isStack, value, ref pX, ref pY); + var barHig = 0f; + if (isPercentStack) + { + var valueTotal = chart.GetSerieSameStackTotalValue<Bar>(serie.stack, i, m_SerieGrid.index); + barHig = valueTotal != 0 ? (float)(relativedValue / valueTotal * relativedAxisLength) : 0; + } + else + { + barHig = AxisHelper.GetAxisValueLength(m_SerieGrid, relativedAxis, relativedCategoryWidth, relativedValue); + } + float currHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, barHig); + Vector3 plb, plt, prt, prb, top; + UpdateRectPosition(m_SerieGrid, isY, relativedValue, pX, pY, gap, borderWidth, barWidth, currHig, + out plb, out plt, out prt, out prb, out top); + serieData.context.stackHeight = barHig; + serieData.context.position = top; + serieData.context.rect = Rect.MinMaxRect(plb.x + borderGapAndWidth, plb.y + borderGapAndWidth, + prt.x - borderGapAndWidth, prt.y - borderGapAndWidth); + serieData.context.backgroundRect = isY ? + Rect.MinMaxRect(m_SerieGrid.context.x, plb.y, m_SerieGrid.context.x + relativedAxisLength, prt.y) : + Rect.MinMaxRect(plb.x, m_SerieGrid.context.y, prb.x, m_SerieGrid.context.y + relativedAxisLength); + + if (!serie.clip || (serie.clip && m_SerieGrid.Contains(top))) + { + serie.context.dataPoints.Add(top); + serie.context.dataIndexs.Add(serieData.index); + } + else + { + continue; + } + + if (serie.show && !serie.placeHolder) + { + switch (serie.barType) + { + case BarType.Normal: + case BarType.Capsule: + DrawNormalBar(vh, serie, serieData, itemStyle, backgroundColor, gap, barWidth, + pX, pY, plb, plt, prt, prb, isY, m_SerieGrid, axis, areaColor, areaToColor, relativedValue); + break; + case BarType.Zebra: + DrawZebraBar(vh, serie, serieData, itemStyle, backgroundColor, gap, barWidth, + pX, pY, plb, plt, prt, prb, isY, m_SerieGrid, axis, areaColor, areaToColor); + break; + } + } + if (serie.animation.CheckDetailBreak(top, isY)) + { + break; + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + chart.RefreshPainter(serie); + } + if (dataChanging || interacting) + { + chart.RefreshPainter(serie); + } + } + + private void UpdateXYPosition(GridCoord grid, bool isY, Axis axis, Axis relativedAxis, int i, + float categoryWidth, float relativedCategoryWidth, float barWidth, bool isStack, + double value, ref float pX, ref float pY) + { + if (isY) + { + if (axis.IsCategory()) + { + pY = grid.context.y + i * categoryWidth + (axis.boundaryGap ? 0 : -categoryWidth * 0.5f); + } + else + { + if (axis.context.minMaxRange <= 0) pY = grid.context.y; + else + { + var valueLen = (float)((value - axis.context.minValue) / axis.context.minMaxRange) * grid.context.height; + pY = grid.context.y + valueLen - categoryWidth * 0.5f; + } + } + pX = AxisHelper.GetAxisValuePosition(grid, relativedAxis, relativedCategoryWidth, 0); + if (isStack) + { + for (int n = 0; n < m_StackSerieData.Count - 1; n++) + pX += m_StackSerieData[n][i].context.stackHeight; + } + } + else + { + if (axis.IsCategory()) + { + pX = grid.context.x + i * categoryWidth + (axis.boundaryGap ? 0 : -categoryWidth * 0.5f); + } + else + { + if (axis.context.minMaxRange <= 0) pX = grid.context.x; + else + { + var valueLen = (float)((value - axis.context.minValue) / axis.context.minMaxRange) * grid.context.width; + pX = grid.context.x + valueLen - categoryWidth * 0.5f; + } + } + pY = AxisHelper.GetAxisValuePosition(grid, relativedAxis, relativedCategoryWidth, 0); + if (isStack) + { + for (int n = 0; n < m_StackSerieData.Count - 1; n++) + pY += m_StackSerieData[n][i].context.stackHeight; + } + } + } + + private void UpdateRectPosition(GridCoord grid, bool isY, double yValue, float pX, float pY, float gap, float borderWidth, + float barWidth, float currHig, + out Vector3 plb, out Vector3 plt, out Vector3 prt, out Vector3 prb, out Vector3 top) + { + if (isY) + { + if (yValue < 0) + { + plt = new Vector3(pX + currHig, pY + gap + barWidth); + prt = new Vector3(pX, pY + gap + barWidth); + prb = new Vector3(pX, pY + gap); + plb = new Vector3(pX + currHig, pY + gap); + } + else + { + plt = new Vector3(pX, pY + gap + barWidth); + prt = new Vector3(pX + currHig, pY + gap + barWidth); + prb = new Vector3(pX + currHig, pY + gap); + plb = new Vector3(pX, pY + gap); + } + top = new Vector3(pX + currHig, pY + gap + barWidth / 2); + } + else + { + if (yValue < 0) + { + plb = new Vector3(pX + gap, pY + currHig); + plt = new Vector3(pX + gap, pY); + prt = new Vector3(pX + gap + barWidth, pY); + prb = new Vector3(pX + gap + barWidth, pY + currHig); + } + else + { + plb = new Vector3(pX + gap, pY); + plt = new Vector3(pX + gap, pY + currHig); + prt = new Vector3(pX + gap + barWidth, pY + currHig); + prb = new Vector3(pX + gap + barWidth, pY); + } + top = new Vector3(pX + gap + barWidth / 2, pY + currHig); + } + if (serie.clip) + { + plb = chart.ClampInGrid(grid, plb); + plt = chart.ClampInGrid(grid, plt); + prt = chart.ClampInGrid(grid, prt); + prb = chart.ClampInGrid(grid, prb); + top = chart.ClampInGrid(grid, top); + } + } + + private void DrawNormalBar(VertexHelper vh, Serie serie, SerieData serieData, ItemStyle itemStyle, Color32 backgroundColor, + float gap, float barWidth, float pX, float pY, Vector3 plb, Vector3 plt, Vector3 prt, + Vector3 prb, bool isYAxis, GridCoord grid, Axis axis, Color32 areaColor, Color32 areaToColor, double value) + { + var borderWidth = itemStyle.borderWidth; + var borderColor = itemStyle.borderColor; + if (ChartHelper.IsClearColor(borderColor)) + { + borderColor = areaColor; + borderColor.a = (byte)(areaColor.a * 1.2f); + } + var cornerRadius = serie.barType == BarType.Capsule && !itemStyle.IsNeedCorner() ? + m_CapusleDefaultCornerRadius : + itemStyle.cornerRadius; + var invert = value < 0; + if (!ChartHelper.IsClearColor(backgroundColor)) + { + UGL.DrawRoundRectangle(vh, serieData.context.backgroundRect, backgroundColor, backgroundColor, 0, + cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert); + } + UGL.DrawRoundRectangle(vh, serieData.context.rect, areaColor, areaToColor, 0, + cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert); + if (serie.barType == BarType.Capsule) + { + UGL.DrawBorder(vh, serieData.context.backgroundRect, borderWidth, borderColor, + 0, cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert, -borderWidth); + } + else + { + UGL.DrawBorder(vh, serieData.context.rect, borderWidth, borderColor, + 0, cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert, itemStyle.borderGap); + } + } + + private void DrawZebraBar(VertexHelper vh, Serie serie, SerieData serieData, ItemStyle itemStyle, Color32 backgroundColor, + float gap, float barWidth, float pX, float pY, Vector3 plb, Vector3 plt, Vector3 prt, + Vector3 prb, bool isYAxis, GridCoord grid, Axis axis, Color32 barColor, Color32 barToColor) + { + if (!ChartHelper.IsClearColor(backgroundColor)) + { + UGL.DrawRoundRectangle(vh, serieData.context.backgroundRect, backgroundColor, backgroundColor, 0, + null, isYAxis, chart.settings.cicleSmoothness, false); + } + if (isYAxis) + { + plt = (plb + plt) / 2; + prt = (prt + prb) / 2; + chart.DrawClipZebraLine(vh, plt, prt, barWidth / 2, serie.barZebraWidth, serie.barZebraGap, + barColor, barToColor, serie.clip, grid, grid.context.width); + } + else + { + plb = (prb + plb) / 2; + plt = (plt + prt) / 2; + chart.DrawClipZebraLine(vh, plb, plt, barWidth / 2, serie.barZebraWidth, serie.barZebraGap, + barColor, barToColor, serie.clip, grid, grid.context.height); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs.meta new file mode 100644 index 0000000..68b7310 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/BarHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5bd8425bf4c1b4bf2adf8940be58ddec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs new file mode 100644 index 0000000..a99218c --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs @@ -0,0 +1,42 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + [SerieHandler(typeof(SimplifiedBarHandler), true)] + [SerieConvert(typeof(SimplifiedLine), typeof(Bar))] + [CoordOptions(typeof(GridCoord))] + [DefaultAnimation(AnimationType.LeftToRight, false)] + [DefaultTooltip(Tooltip.Type.Shadow, Tooltip.Trigger.Axis)] + [SerieComponent()] + [SerieDataComponent()] + [SerieDataExtraField()] + public class SimplifiedBar : Serie, INeedSerieContainer, ISimplifiedSerie + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<SimplifiedBar>(serieName); + serie.symbol.show = false; + var lastValue = 0d; + for (int i = 0; i < 50; i++) + { + if (i < 20) + lastValue += UnityEngine.Random.Range(0, 5); + else + lastValue += UnityEngine.Random.Range(-3, 5); + chart.AddData(serie.index, lastValue); + } + return serie; + } + + public static SimplifiedBar ConvertSerie(Serie serie) + { + var newSerie = serie.Clone<SimplifiedBar>(); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs.meta b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs.meta new file mode 100644 index 0000000..d6b3a10 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7fc754e0afd4d4f138389c19611aaedb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs new file mode 100644 index 0000000..ab68767 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs @@ -0,0 +1,355 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class SimplifiedBarHandler : SerieHandler<SimplifiedBar> + { + private GridCoord m_SerieGrid; + + public override void Update() + { + base.Update(); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + UpdateCoordSerieParams(ref paramList, ref title, dataIndex, showCategory, category, + marker, itemFormatter, numericFormatter, ignoreDataDefaultContent); + } + + public override void DrawSerie(VertexHelper vh) + { + DrawBarSerie(vh, serie, serie.context.colorIndex); + } + + public override void UpdateSerieContext() + { + if (m_SerieGrid == null) + return; + + var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter()) || m_LegendEnter; + var needInteract = false; + Color32 color, toColor; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, SerieState.Normal); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + return; + } + m_LastCheckContextFlag = needCheck; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + foreach (var serieData in serie.data) + { + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, SerieState.Emphasis); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + } + else + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + if (serieData.context.rect.Contains(chart.pointerPos)) + { + serie.context.pointerItemDataIndex = serieData.index; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, SerieState.Emphasis); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + else + { + serieData.context.highlight = false; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, SerieState.Normal); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + } + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + private void DrawBarSerie(VertexHelper vh, SimplifiedBar serie, int colorIndex) + { + if (!serie.show || serie.animation.HasFadeOut()) + return; + + Axis axis; + Axis relativedAxis; + var isY = chart.GetSerieGridCoordAxis(serie, out axis, out relativedAxis); + m_SerieGrid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + + if (axis == null) + return; + if (relativedAxis == null) + return; + if (m_SerieGrid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(axis); + var showData = serie.GetDataList(dataZoom); + + if (showData.Count <= 0) + return; + + var axisLength = isY ? m_SerieGrid.context.height : m_SerieGrid.context.width; + var relativedAxisLength = isY ? m_SerieGrid.context.width : m_SerieGrid.context.height; + var axisXY = isY ? m_SerieGrid.context.y : m_SerieGrid.context.x; + + var barCount = chart.GetSerieBarRealCount<SimplifiedBar>(m_SerieGrid.index); + float categoryWidth = AxisHelper.GetDataWidth(axis, axisLength, showData.Count, dataZoom); + float relativedCategoryWidth = AxisHelper.GetDataWidth(relativedAxis, relativedAxisLength, showData.Count, dataZoom); + float barGap = chart.GetSerieBarGap<SimplifiedBar>(m_SerieGrid.index); + float totalBarWidth = chart.GetSerieTotalWidth<SimplifiedBar>(categoryWidth, barGap, barCount,m_SerieGrid.index); + float barWidth = serie.GetBarWidth(categoryWidth, barCount); + float offset = (categoryWidth - totalBarWidth) * 0.5f; + float barGapWidth = barWidth + barWidth * barGap; + float gap = serie.barGap == -1 ? offset : offset + serie.index * barGapWidth; + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > showData.Count ? showData.Count : serie.maxShow) : + showData.Count; + + var dataChanging = false; + var dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var interactDuration = serie.animation.GetInteractionDuration(); + + var areaColor = ColorUtil.clearColor32; + var areaToColor = ColorUtil.clearColor32; + var interacting = false; + + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + serie.animation.InitProgress(axisXY, axisXY + axisLength); + for (int i = serie.minShow; i < maxCount; i++) + { + var serieData = showData[i]; + if (!serieData.show || serie.IsIgnoreValue(serieData)) + { + serie.context.dataPoints.Add(Vector3.zero); + serie.context.dataIndexs.Add(serieData.index); + continue; + } + + if (serieData.IsDataChanged()) + dataChanging = true; + + var highlight = serieData.context.highlight || serie.highlight; + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + var value = axis.IsCategory() ? i : serieData.GetData(0, axis.inverse); + var relativedValue = serieData.GetCurrData(1, dataAddDuration, dataChangeDuration, relativedAxis.inverse, 0, 0, serie.animation.unscaledTime); + var borderWidth = relativedValue == 0 ? 0 : itemStyle.borderWidth; + + if (!serieData.interact.TryGetColor(ref areaColor, ref areaToColor, ref interacting, interactDuration)) + { + SerieHelper.GetItemColor(out areaColor, out areaToColor, serie, serieData, chart.theme); + serieData.interact.SetColor(ref interacting, areaColor, areaToColor); + } + + var pX = 0f; + var pY = 0f; + UpdateXYPosition(m_SerieGrid, isY, axis, relativedAxis, i, categoryWidth, relativedCategoryWidth, barWidth, value, ref pX, ref pY); + + var barHig = AxisHelper.GetAxisValueLength(m_SerieGrid, relativedAxis, relativedCategoryWidth, relativedValue); + var currHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, barHig); + + Vector3 plb, plt, prt, prb, top; + UpdateRectPosition(m_SerieGrid, isY, relativedValue, pX, pY, gap, borderWidth, barWidth, currHig, + out plb, out plt, out prt, out prb, out top); + serieData.context.stackHeight = barHig; + serieData.context.position = top; + serieData.context.rect = Rect.MinMaxRect(plb.x, plb.y, prb.x, prt.y); + serie.context.dataPoints.Add(top); + serie.context.dataIndexs.Add(serieData.index); + DrawNormalBar(vh, serie, serieData, itemStyle, colorIndex, highlight, gap, barWidth, + pX, pY, plb, plt, prt, prb, false, m_SerieGrid, areaColor, areaToColor); + + if (serie.animation.CheckDetailBreak(top, isY)) + { + break; + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + chart.RefreshPainter(serie); + } + if (dataChanging || interacting) + { + chart.RefreshPainter(serie); + } + } + + private void UpdateXYPosition(GridCoord grid, bool isY, Axis axis, Axis relativedAxis, int i, float categoryWidth, + float relativedCategoryWidth, float barWidth, double value, ref float pX, ref float pY) + { + if (isY) + { + if (axis.IsCategory()) + { + pY = grid.context.y + i * categoryWidth + (axis.boundaryGap ? 0 : -categoryWidth * 0.5f); + } + else + { + if (axis.context.minMaxRange <= 0) pY = grid.context.y; + else pY = grid.context.y + (float)((value - axis.context.minValue) / axis.context.minMaxRange) * (grid.context.height - barWidth); + } + pX = AxisHelper.GetAxisValuePosition(grid, relativedAxis, relativedCategoryWidth, 0); + } + else + { + if (axis.IsCategory()) + { + pX = grid.context.x + i * categoryWidth + (axis.boundaryGap ? 0 : -categoryWidth * 0.5f); + } + else + { + if (axis.context.minMaxRange <= 0) pX = grid.context.x; + else pX = grid.context.x + (float)((value - axis.context.minValue) / axis.context.minMaxRange) * (grid.context.width - barWidth); + } + pY = AxisHelper.GetAxisValuePosition(grid, relativedAxis, relativedCategoryWidth, 0); + } + } + + private void UpdateRectPosition(GridCoord grid, bool isY, double yValue, float pX, float pY, float gap, float borderWidth, + float barWidth, float currHig, + out Vector3 plb, out Vector3 plt, out Vector3 prt, out Vector3 prb, out Vector3 top) + { + if (isY) + { + if (yValue < 0) + { + plt = new Vector3(pX - borderWidth, pY + gap + barWidth - borderWidth); + prt = new Vector3(pX + currHig + borderWidth, pY + gap + barWidth - borderWidth); + prb = new Vector3(pX + currHig + borderWidth, pY + gap + borderWidth); + plb = new Vector3(pX - borderWidth, pY + gap + borderWidth); + } + else + { + plt = new Vector3(pX + borderWidth, pY + gap + barWidth - borderWidth); + prt = new Vector3(pX + currHig - borderWidth, pY + gap + barWidth - borderWidth); + prb = new Vector3(pX + currHig - borderWidth, pY + gap + borderWidth); + plb = new Vector3(pX + borderWidth, pY + gap + borderWidth); + } + top = new Vector3(pX + currHig - borderWidth, pY + gap + barWidth / 2); + } + else + { + if (yValue < 0) + { + plb = new Vector3(pX + gap + borderWidth, pY - borderWidth); + plt = new Vector3(pX + gap + borderWidth, pY + currHig + borderWidth); + prt = new Vector3(pX + gap + barWidth - borderWidth, pY + currHig + borderWidth); + prb = new Vector3(pX + gap + barWidth - borderWidth, pY - borderWidth); + } + else + { + plb = new Vector3(pX + gap + borderWidth, pY + borderWidth); + plt = new Vector3(pX + gap + borderWidth, pY + currHig - borderWidth); + prt = new Vector3(pX + gap + barWidth - borderWidth, pY + currHig - borderWidth); + prb = new Vector3(pX + gap + barWidth - borderWidth, pY + borderWidth); + } + top = new Vector3(pX + gap + barWidth / 2, pY + currHig - borderWidth); + } + if (serie.clip) + { + plb = chart.ClampInGrid(grid, plb); + plt = chart.ClampInGrid(grid, plt); + prt = chart.ClampInGrid(grid, prt); + prb = chart.ClampInGrid(grid, prb); + top = chart.ClampInGrid(grid, top); + } + } + + private void DrawNormalBar(VertexHelper vh, Serie serie, SerieData serieData, ItemStyle itemStyle, int colorIndex, + bool highlight, float gap, float barWidth, float pX, float pY, Vector3 plb, Vector3 plt, Vector3 prt, + Vector3 prb, bool isYAxis, GridCoord grid, Color32 areaColor, Color32 areaToColor) + { + + var borderWidth = itemStyle.borderWidth; + if (isYAxis) + { + if (serie.clip) + { + prb = chart.ClampInGrid(grid, prb); + plb = chart.ClampInGrid(grid, plb); + plt = chart.ClampInGrid(grid, plt); + prt = chart.ClampInGrid(grid, prt); + } + var itemWidth = Mathf.Abs(prb.x - plt.x); + var itemHeight = Mathf.Abs(prt.y - plb.y); + var center = new Vector3((plt.x + prb.x) / 2, (prt.y + plb.y) / 2); + if (itemWidth > 0 && itemHeight > 0) + { + var invert = center.x < plb.x; + if (itemStyle.IsNeedCorner()) + { + UGL.DrawRoundRectangle(vh, center, itemWidth, itemHeight, areaColor, areaToColor, 0, + itemStyle.cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert); + } + else + { + chart.DrawClipPolygon(vh, plb, plt, prt, prb, areaColor, areaToColor, serie.clip, grid); + } + UGL.DrawBorder(vh, center, itemWidth, itemHeight, borderWidth, itemStyle.borderColor, + itemStyle.borderToColor, 0, itemStyle.cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert); + } + } + else + { + if (serie.clip) + { + prb = chart.ClampInGrid(grid, prb); + plb = chart.ClampInGrid(grid, plb); + plt = chart.ClampInGrid(grid, plt); + prt = chart.ClampInGrid(grid, prt); + } + var itemWidth = Mathf.Abs(prt.x - plb.x); + var itemHeight = Mathf.Abs(plt.y - prb.y); + var center = new Vector3((plb.x + prt.x) / 2, (plt.y + prb.y) / 2); + if (itemWidth > 0 && itemHeight > 0) + { + var invert = center.y < plb.y; + if (itemStyle.IsNeedCorner()) + { + UGL.DrawRoundRectangle(vh, center, itemWidth, itemHeight, areaColor, areaToColor, 0, + itemStyle.cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert); + } + else + { + chart.DrawClipPolygon(vh, ref prb, ref plb, ref plt, ref prt, areaColor, areaToColor, + serie.clip, grid); + } + UGL.DrawBorder(vh, center, itemWidth, itemHeight, borderWidth, itemStyle.borderColor, + itemStyle.borderToColor, 0, itemStyle.cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs.meta new file mode 100644 index 0000000..86f252e --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Bar/SimplifiedBarHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: afd7226ecff7f4b9fad297101bc33b8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Candlestick.meta b/Assets/XCharts/Runtime/Serie/Candlestick.meta new file mode 100644 index 0000000..0cc9989 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 641a5dafd45e6455ca9ef9558efe1083 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs b/Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs new file mode 100644 index 0000000..64ab8f8 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs @@ -0,0 +1,34 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(CandlestickHandler), true)] + [DefaultAnimation(AnimationType.LeftToRight, false)] + [DefaultTooltip(Tooltip.Type.Shadow, Tooltip.Trigger.Axis)] + [SerieComponent()] + [SerieDataComponent(typeof(ItemStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField()] + public class Candlestick : Serie, INeedSerieContainer + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Candlestick>(serieName); + var lastValue = 50d; + for (int i = 0; i < 5; i++) + { + var open = lastValue; + var close = open + Random.Range(-20, 20); + var min = open < close ? open : close; + var max = open > close ? open : close; + var lowest = min + Random.Range(-10, -10); + var heighest = max + Random.Range(10, 10); + chart.AddData(serie.index, i, open, close, lowest, heighest); + lastValue = close; + } + return serie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs.meta b/Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs.meta new file mode 100644 index 0000000..4c84a79 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/Candlestick.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1fbb6247f54f4dd2a1f3e7f6bafb8c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs b/Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs new file mode 100644 index 0000000..b72be4d --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs @@ -0,0 +1,266 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class CandlestickHandler : SerieHandler<Candlestick> + { + public override void DrawSerie(VertexHelper vh) + { + DrawCandlestickSerie(vh, serie); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + if (dataIndex < 0) + dataIndex = serie.context.pointerItemDataIndex; + + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + + title = category; + + var color = chart.GetMarkColor(serie, serieData); + var newMarker = SerieHelper.GetItemMarker(serie, serieData, marker); + var newItemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + var newNumericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + var isEmptyItemFormatter = string.IsNullOrEmpty(newItemFormatter); + + if (isEmptyItemFormatter) + { + var param = serie.context.param; + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.dimension = 1; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = 0; + param.total = 0; + param.color = color; + param.marker = newMarker; + param.itemFormatter = newItemFormatter; + param.numericFormatter = newNumericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(serie.serieName); + param.columns.Add(string.Empty); + + paramList.Add(param); + for (int i = 1; i < 5; i++) + { + param = new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = i; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(i); + param.total = SerieHelper.GetMaxData(serie, i); + param.color = color; + param.marker = newMarker; + param.itemFormatter = newItemFormatter; + param.numericFormatter = newNumericFormatter; + param.isSecondaryMark = true; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(XCSettings.lang.GetCandlestickDimensionName(i - 1)); + param.columns.Add(ChartCached.NumberToStr(param.value, param.numericFormatter)); + + paramList.Add(param); + } + } + else + { + newItemFormatter = newItemFormatter.Replace("\\n", "\n"); + var temp = newItemFormatter.Split('\n'); + foreach (var str in temp) + { + var param = new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = 0; + param.total = 0; + param.color = color; + param.marker = newMarker; + param.itemFormatter = str; + param.numericFormatter = newNumericFormatter; + param.isSecondaryMark = false; + param.columns.Clear(); + paramList.Add(param); + } + } + } + + private void DrawCandlestickSerie(VertexHelper vh, Candlestick serie) + { + if (!serie.show) return; + if (serie.animation.HasFadeOut()) return; + XAxis xAxis; + YAxis yAxis; + GridCoord grid; + if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) return; + if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) return; + if (!chart.TryGetChartComponent<GridCoord>(out grid, xAxis.gridIndex)) return; + var theme = chart.theme; + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + var showData = serie.GetDataList(dataZoom); + float categoryWidth = AxisHelper.GetDataWidth(xAxis, grid.context.width, showData.Count, dataZoom); + float barWidth = serie.GetBarWidth(categoryWidth); + float gap = (categoryWidth - barWidth) / 2; + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > showData.Count ? showData.Count : serie.maxShow) : + showData.Count; + + bool dataChanging = false; + float dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + double yMinValue = yAxis.context.minValue; + double yMaxValue = yAxis.context.maxValue; + var isYAxis = false; + serie.containerIndex = grid.index; + serie.containterInstanceId = grid.instanceId; + var intensive = grid.context.width / (maxCount - serie.minShow) < 0.6f; + for (int i = serie.minShow; i < maxCount; i++) + { + var serieData = showData[i]; + if (!serieData.show || serie.IsIgnoreValue(serieData)) + { + serie.context.dataPoints.Add(Vector3.zero); + serie.context.dataIndexs.Add(serieData.index); + continue; + } + var state = SerieHelper.GetSerieState(serie, serieData); + var itemStyle = SerieHelper.GetItemStyle(serie, serieData, state); + var startDataIndex = serieData.data.Count > 4 ? 1 : 0; + var open = serieData.GetCurrData(startDataIndex, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var close = serieData.GetCurrData(startDataIndex + 1, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var lowest = serieData.GetCurrData(startDataIndex + 2, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var heighest = serieData.GetCurrData(startDataIndex + 3, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var isRise = yAxis.inverse ? close < open : close > open; + var borderWidth = open == 0 ? 0f : + (itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth : + itemStyle.borderWidth); + if (serieData.IsDataChanged()) dataChanging = true; + float pX = grid.context.x + i * categoryWidth; + float zeroY = grid.context.y + yAxis.context.offset; + if (!xAxis.boundaryGap) pX -= categoryWidth / 2; + float pY = zeroY; + var barHig = 0f; + double valueTotal = yMaxValue - yMinValue; + var minCut = (yMinValue > 0 ? yMinValue : 0); + if (valueTotal != 0) + { + barHig = (float)((close - open) / valueTotal * grid.context.height); + pY += (float)((open - minCut) / valueTotal * grid.context.height); + } + serieData.context.stackHeight = barHig; + float currHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, barHig); + Vector3 plb, plt, prt, prb, top; + + var offset = 2 * borderWidth; + if (isRise) + { + plb = new Vector3(pX + gap + offset, pY + offset); + plt = new Vector3(pX + gap + offset, pY + currHig - offset); + prt = new Vector3(pX + gap + barWidth - offset, pY + currHig - offset); + prb = new Vector3(pX + gap + barWidth - offset, pY + offset); + top = new Vector3(pX + gap + barWidth / 2, pY + currHig - offset); + } + else + { + plb = new Vector3(pX + gap + offset, pY - offset); + plt = new Vector3(pX + gap + offset, pY + currHig + offset); + prt = new Vector3(pX + gap + barWidth - offset, pY + currHig + offset); + prb = new Vector3(pX + gap + barWidth - offset, pY - offset); + top = new Vector3(pX + gap + barWidth / 2, pY + currHig + offset); + } + if (serie.clip) + { + plb = chart.ClampInGrid(grid, plb); + plt = chart.ClampInGrid(grid, plt); + prt = chart.ClampInGrid(grid, prt); + prb = chart.ClampInGrid(grid, prb); + top = chart.ClampInGrid(grid, top); + } + serie.context.dataPoints.Add(top); + serie.context.dataIndexs.Add(serieData.index); + var areaColor = isRise ? + itemStyle.GetColor(theme.serie.candlestickColor) : + itemStyle.GetColor0(theme.serie.candlestickColor0); + var borderColor = isRise ? + itemStyle.GetBorderColor(theme.serie.candlestickBorderColor) : + itemStyle.GetBorderColor0(theme.serie.candlestickBorderColor0); + var itemWidth = Mathf.Abs(prt.x - plb.x); + var itemHeight = Mathf.Abs(plt.y - prb.y); + var center = new Vector3((plb.x + prt.x) / 2, (plt.y + prb.y) / 2); + var lowPos = new Vector3(center.x, zeroY + (float)((lowest - minCut) / valueTotal * grid.context.height)); + var heighPos = new Vector3(center.x, zeroY + (float)((heighest - minCut) / valueTotal * grid.context.height)); + var openCenterPos = new Vector3(center.x, prb.y); + var closeCenterPos = new Vector3(center.x, prt.y); + if (intensive) + { + UGL.DrawLine(vh, lowPos, heighPos, borderWidth, borderColor); + } + else + { + if (barWidth > 2f * borderWidth) + { + if (itemWidth > 0 && itemHeight > 0) + { + if (itemStyle.IsNeedCorner()) + { + UGL.DrawRoundRectangle(vh, center, itemWidth, itemHeight, areaColor, areaColor, 0, + itemStyle.cornerRadius, isYAxis, 0.5f); + } + else + { + chart.DrawClipPolygon(vh, ref prb, ref plb, ref plt, ref prt, areaColor, areaColor, + serie.clip, grid); + } + UGL.DrawBorder(vh, center, itemWidth, itemHeight, 2 * borderWidth, borderColor, 0, + itemStyle.cornerRadius, isYAxis, 0.5f); + } + } + else + { + UGL.DrawLine(vh, openCenterPos, closeCenterPos, Mathf.Max(borderWidth, barWidth / 2), borderColor); + } + if (isRise) + { + UGL.DrawLine(vh, openCenterPos, lowPos, borderWidth, borderColor); + UGL.DrawLine(vh, closeCenterPos, heighPos, borderWidth, borderColor); + } + else + { + UGL.DrawLine(vh, closeCenterPos, lowPos, borderWidth, borderColor); + UGL.DrawLine(vh, openCenterPos, heighPos, borderWidth, borderColor); + } + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs.meta new file mode 100644 index 0000000..d9bbb9d --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/CandlestickHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d530c536c5784f2593e9a7c5a57df16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs new file mode 100644 index 0000000..918cd97 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs @@ -0,0 +1,41 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(SimplifiedCandlestickHandler), true)] + [DefaultAnimation(AnimationType.LeftToRight, false)] + [DefaultTooltip(Tooltip.Type.Shadow, Tooltip.Trigger.Axis)] + [SerieComponent()] + [SerieDataComponent()] + [SerieDataExtraField()] + public class SimplifiedCandlestick : Serie, INeedSerieContainer, ISimplifiedSerie + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<SimplifiedCandlestick>(serieName); + var lastValue = 50d; + for (int i = 0; i < 50; i++) + { + var open = lastValue; + var close = open + Random.Range(-20, 20); + var min = open < close ? open : close; + var max = open > close ? open : close; + var lowest = min + Random.Range(-10, -10); + var heighest = max + Random.Range(10, 10); + chart.AddData(serie.index, i, open, close, lowest, heighest); + lastValue = close; + } + return serie; + } + + public static SimplifiedCandlestick ConvertSerie(Serie serie) + { + var newSerie = serie.Clone<SimplifiedCandlestick>(); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs.meta b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs.meta new file mode 100644 index 0000000..682a2ba --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestick.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1202f0da64c484488bb69b8382af9918 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs new file mode 100644 index 0000000..dde4176 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs @@ -0,0 +1,265 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class SimplifiedCandlestickHandler : SerieHandler<SimplifiedCandlestick> + { + public override void DrawSerie(VertexHelper vh) + { + DrawCandlestickSerie(vh, serie); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + if (dataIndex < 0) + dataIndex = serie.context.pointerItemDataIndex; + + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + + title = category; + + var color = chart.GetMarkColor(serie, serieData); + var newMarker = SerieHelper.GetItemMarker(serie, serieData, marker); + var newItemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + var newNumericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + var isEmptyItemFormatter = string.IsNullOrEmpty(newItemFormatter); + + if (isEmptyItemFormatter) + { + var param = serie.context.param; + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.dimension = 1; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = 0; + param.total = 0; + param.color = color; + param.marker = newMarker; + param.itemFormatter = newItemFormatter; + param.numericFormatter = newNumericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(serie.serieName); + param.columns.Add(string.Empty); + + paramList.Add(param); + for (int i = 1; i < 5; i++) + { + param = new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = i; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(i); + param.total = SerieHelper.GetMaxData(serie, i); + param.color = color; + param.marker = newMarker; + param.itemFormatter = newItemFormatter; + param.numericFormatter = newNumericFormatter; + param.isSecondaryMark = true; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(XCSettings.lang.GetCandlestickDimensionName(i - 1)); + param.columns.Add(ChartCached.NumberToStr(param.value, param.numericFormatter)); + + paramList.Add(param); + } + } + else + { + newItemFormatter = newItemFormatter.Replace("\\n", "\n"); + var temp = newItemFormatter.Split('\n'); + foreach (var str in temp) + { + var param = new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = 0; + param.total = 0; + param.color = color; + param.marker = newMarker; + param.itemFormatter = str; + param.numericFormatter = newNumericFormatter; + param.isSecondaryMark = false; + param.columns.Clear(); + paramList.Add(param); + } + } + } + + private void DrawCandlestickSerie(VertexHelper vh, SimplifiedCandlestick serie) + { + if (!serie.show) return; + if (serie.animation.HasFadeOut()) return; + XAxis xAxis; + YAxis yAxis; + GridCoord grid; + if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) return; + if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) return; + if (!chart.TryGetChartComponent<GridCoord>(out grid, xAxis.gridIndex)) return; + var theme = chart.theme; + var dataZoom = chart.GetDataZoomOfAxis(xAxis); + var showData = serie.GetDataList(dataZoom); + float categoryWidth = AxisHelper.GetDataWidth(xAxis, grid.context.width, showData.Count, dataZoom); + float barWidth = serie.GetBarWidth(categoryWidth); + float gap = (categoryWidth - barWidth) / 2; + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > showData.Count ? showData.Count : serie.maxShow) : + showData.Count; + + bool dataChanging = false; + float dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + double yMinValue = yAxis.context.minValue; + double yMaxValue = yAxis.context.maxValue; + var isYAxis = false; + var itemStyle = serie.itemStyle; + serie.containerIndex = grid.index; + serie.containterInstanceId = grid.instanceId; + var intensive = grid.context.width / (maxCount - serie.minShow) < 0.6f; + for (int i = serie.minShow; i < maxCount; i++) + { + var serieData = showData[i]; + if (!serieData.show || serie.IsIgnoreValue(serieData)) + { + serie.context.dataPoints.Add(Vector3.zero); + serie.context.dataIndexs.Add(serieData.index); + continue; + } + var startDataIndex = serieData.data.Count > 4 ? 1 : 0; + var open = serieData.GetCurrData(startDataIndex, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var close = serieData.GetCurrData(startDataIndex + 1, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var lowest = serieData.GetCurrData(startDataIndex + 2, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var heighest = serieData.GetCurrData(startDataIndex + 3, dataAddDuration, dataChangeDuration, yAxis.inverse, yMinValue, yMaxValue, unscaledTime); + var isRise = yAxis.inverse ? close<open : close> open; + var borderWidth = open == 0 ? 0f : + (itemStyle.borderWidth == 0 ? theme.serie.candlestickBorderWidth : + itemStyle.borderWidth); + if (serieData.IsDataChanged()) dataChanging = true; + float pX = grid.context.x + i * categoryWidth; + float zeroY = grid.context.y + yAxis.context.offset; + if (!xAxis.boundaryGap) pX -= categoryWidth / 2; + float pY = zeroY; + var barHig = 0f; + double valueTotal = yMaxValue - yMinValue; + var minCut = (yMinValue > 0 ? yMinValue : 0); + if (valueTotal != 0) + { + barHig = (float) ((close - open) / valueTotal * grid.context.height); + pY += (float) ((open - minCut) / valueTotal * grid.context.height); + } + serieData.context.stackHeight = barHig; + float currHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, barHig); + Vector3 plb, plt, prt, prb, top; + + var offset = 2 * borderWidth; + if (isRise) + { + plb = new Vector3(pX + gap + offset, pY + offset); + plt = new Vector3(pX + gap + offset, pY + currHig - offset); + prt = new Vector3(pX + gap + barWidth - offset, pY + currHig - offset); + prb = new Vector3(pX + gap + barWidth - offset, pY + offset); + top = new Vector3(pX + gap + barWidth / 2, pY + currHig - offset); + } + else + { + plb = new Vector3(pX + gap + offset, pY - offset); + plt = new Vector3(pX + gap + offset, pY + currHig + offset); + prt = new Vector3(pX + gap + barWidth - offset, pY + currHig + offset); + prb = new Vector3(pX + gap + barWidth - offset, pY - offset); + top = new Vector3(pX + gap + barWidth / 2, pY + currHig + offset); + } + // if (serie.clip) + // { + // plb = chart.ClampInGrid(grid, plb); + // plt = chart.ClampInGrid(grid, plt); + // prt = chart.ClampInGrid(grid, prt); + // prb = chart.ClampInGrid(grid, prb); + // top = chart.ClampInGrid(grid, top); + // } + serie.context.dataPoints.Add(top); + serie.context.dataIndexs.Add(serieData.index); + var areaColor = isRise ? + itemStyle.GetColor(theme.serie.candlestickColor) : + itemStyle.GetColor0(theme.serie.candlestickColor0); + var borderColor = isRise ? + itemStyle.GetBorderColor(theme.serie.candlestickBorderColor) : + itemStyle.GetBorderColor0(theme.serie.candlestickBorderColor0); + var itemWidth = Mathf.Abs(prt.x - plb.x); + var itemHeight = Mathf.Abs(plt.y - prb.y); + var center = new Vector3((plb.x + prt.x) / 2, (plt.y + prb.y) / 2); + var lowPos = new Vector3(center.x, zeroY + (float) ((lowest - minCut) / valueTotal * grid.context.height)); + var heighPos = new Vector3(center.x, zeroY + (float) ((heighest - minCut) / valueTotal * grid.context.height)); + var openCenterPos = new Vector3(center.x, prb.y); + var closeCenterPos = new Vector3(center.x, prt.y); + if (intensive) + { + UGL.DrawLine(vh, lowPos, heighPos, borderWidth, borderColor); + } + else + { + if (barWidth > 2f * borderWidth) + { + if (itemWidth > 0 && itemHeight > 0) + { + if (itemStyle.IsNeedCorner()) + { + UGL.DrawRoundRectangle(vh, center, itemWidth, itemHeight, areaColor, areaColor, 0, + itemStyle.cornerRadius, isYAxis, 0.5f); + } + else + { + chart.DrawClipPolygon(vh, ref prb, ref plb, ref plt, ref prt, areaColor, areaColor, + serie.clip, grid); + } + UGL.DrawBorder(vh, center, itemWidth, itemHeight, 2 * borderWidth, borderColor, 0, + itemStyle.cornerRadius, isYAxis, 0.5f); + } + if (isRise) + { + UGL.DrawLine(vh, openCenterPos, lowPos, borderWidth, borderColor); + UGL.DrawLine(vh, closeCenterPos, heighPos, borderWidth, borderColor); + } + else + { + UGL.DrawLine(vh, closeCenterPos, lowPos, borderWidth, borderColor); + UGL.DrawLine(vh, openCenterPos, heighPos, borderWidth, borderColor); + } + } + else + { + UGL.DrawLine(vh, openCenterPos, closeCenterPos, Mathf.Max(borderWidth, barWidth / 2), borderColor); + } + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs.meta new file mode 100644 index 0000000..998419c --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Candlestick/SimplifiedCandlestickHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42727a035319b4eab92ddf0742630115 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Heatmap.meta b/Assets/XCharts/Runtime/Serie/Heatmap.meta new file mode 100644 index 0000000..ccda235 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 70535a50c140c47cc8cac1820dc03170 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs b/Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs new file mode 100644 index 0000000..28397d0 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs @@ -0,0 +1,57 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// The mapping type of heatmap. + /// ||热力图类型。通过颜色映射划分。 + /// </summary> + public enum HeatmapType + { + /// <summary> + /// Data mapping type.By default, the second dimension data is used as the color map. + /// ||数据映射型。默认用第2维数据作为颜色映射。要求数据至少有3个维度数据。 + /// </summary> + Data, + /// <summary> + /// Number mapping type.The number of occurrences of a statistic in a divided grid, as a color map. + /// ||个数映射型。统计数据在划分的格子中出现的次数,作为颜色映射。要求数据至少有2个维度数据。 + /// </summary> + Count + } + + [System.Serializable] + [SerieHandler(typeof(HeatmapHandler), true)] + [DefaultAnimation(AnimationType.LeftToRight, false)] + [DefaultTooltip(Tooltip.Type.None, Tooltip.Trigger.Axis)] + [RequireChartComponent(typeof(VisualMap))] + [CoordOptions(typeof(GridCoord), typeof(PolarCoord))] + [SerieComponent(typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField()] + public class Heatmap : Serie, INeedSerieContainer + { + [SerializeField][Since("v3.3.0")] private HeatmapType m_HeatmapType = HeatmapType.Data; + + /// <summary> + /// The mapping type of heatmap. + /// ||热力图类型。通过颜色映射划分。 + /// </summary> + public HeatmapType heatmapType + { + get { return m_HeatmapType; } + set { if (PropertyUtil.SetStruct(ref m_HeatmapType, value)) { SetVerticesDirty(); } } + } + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Heatmap>(serieName); + serie.itemStyle.show = true; + serie.itemStyle.borderWidth = 2; + serie.itemStyle.borderColor = Color.clear; + return serie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs.meta b/Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs.meta new file mode 100644 index 0000000..1c06a6d --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap/Heatmap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a9984972d3c74a01945c4064739a826 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs new file mode 100644 index 0000000..bb2b608 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs @@ -0,0 +1,202 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// For polar coord + /// </summary> + internal sealed partial class HeatmapHandler + { + private PolarCoord m_SeriePolar; + + private void UpdateSeriePolarContext() + { + if (m_SeriePolar == null) + return; + + var needCheck = (chart.isPointerInChart && m_SeriePolar.IsPointerEnter()) || m_LegendEnter; + var lineWidth = 0f; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + var needAnimation1 = false; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + serie.interact.SetValue(ref needAnimation1, lineWidth); + foreach (var serieData in serie.data) + { + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + var symbolSize = symbol.GetSize(serieData, chart.theme.serie.lineSymbolSize); + serieData.context.highlight = false; + serieData.interact.SetValue(ref needAnimation1, symbolSize); + } + if (needAnimation1) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + return; + } + m_LastCheckContextFlag = needCheck; + var themeSymbolSize = chart.theme.serie.lineSymbolSize; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + + var needInteract = false; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis); + serieData.context.highlight = true; + serieData.interact.SetValue(ref needInteract, size); + } + } + else + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + var dir = chart.pointerPos - new Vector2(m_SeriePolar.context.center.x, m_SeriePolar.context.center.y); + var pointerAngle = ChartHelper.GetAngle360(Vector2.up, dir); + var pointerRadius = Vector2.Distance(chart.pointerPos, m_SeriePolar.context.center); + Color32 color, toColor; + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + if (pointerAngle >= serieData.context.startAngle && + pointerAngle < serieData.context.toAngle && + pointerRadius >= serieData.context.insideRadius && + pointerRadius < serieData.context.outsideRadius) + { + serie.context.pointerItemDataIndex = i; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + } + else + { + serieData.context.highlight = false; + } + var state = SerieHelper.GetSerieState(serie, serieData, true); + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, state); + serieData.interact.SetColor(ref needInteract, color, toColor); + } + } + if (needInteract) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + + private void DrawPolarHeatmap(VertexHelper vh, Serie serie) + { + var datas = serie.data; + if (datas.Count <= 0) + return; + + m_SeriePolar = chart.GetChartComponent<PolarCoord>(serie.polarIndex); + if (m_SeriePolar == null) + return; + + var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, m_SeriePolar.index); + var m_RadiusAxis = ComponentHelper.GetRadiusAxis(chart.components, m_SeriePolar.index); + if (m_AngleAxis == null || m_RadiusAxis == null) + return; + var visualMap = chart.GetVisualMapOfSerie(serie); + + var startAngle = m_AngleAxis.context.startAngle; + var currDetailProgress = 0f; + var totalDetailProgress = datas.Count; + + var xCount = AxisHelper.GetTotalSplitGridNum(m_RadiusAxis); + var yCount = AxisHelper.GetTotalSplitGridNum(m_AngleAxis); + var xWidth = m_SeriePolar.context.radius / xCount; + var yWidth = 360 / yCount; + + serie.animation.InitProgress(currDetailProgress, totalDetailProgress); + + var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); + if (visualMap.autoMinMax) + { + double maxValue, minValue; + SerieHelper.GetMinMaxData(serie, dimension, out minValue, out maxValue); + VisualMapHelper.SetMinMax(visualMap, minValue, maxValue); + } + var rangeMin = visualMap.rangeMin; + var rangeMax = visualMap.rangeMax; + var color = chart.theme.GetColor(serie.index); + + float start, end; + float inside, outside; + double value, radiusValue, angleValue; + for (int i = 0; i < datas.Count; i++) + { + if (serie.animation.CheckDetailBreak(i)) + break; + var serieData = datas[i]; + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + var borderWidth = itemStyle.borderWidth; + var borderColor = itemStyle.borderColor; + + radiusValue = serieData.GetData(0); + angleValue = serieData.GetData(1); + value = serieData.GetData(2); + + var xIndex = AxisHelper.GetAxisValueSplitIndex(m_RadiusAxis, radiusValue, true, xCount); + var yIndex = AxisHelper.GetAxisValueSplitIndex(m_AngleAxis, angleValue, true, yCount); + + start = startAngle + yIndex * yWidth; + end = start + yWidth; + + inside = m_SeriePolar.context.insideRadius + xIndex * xWidth; + outside = inside + xWidth; + + serieData.context.startAngle = start; + serieData.context.toAngle = end; + serieData.context.halfAngle = (start + end) / 2; + serieData.context.insideRadius = inside; + serieData.context.outsideRadius = outside; + + if ((value < rangeMin && rangeMin != visualMap.min) || + (value > rangeMax && rangeMax != visualMap.max)) + { + continue; + } + if (!visualMap.IsInSelectedValue(value)) continue; + color = visualMap.GetColor(value); + if (serieData.context.highlight) + color = ChartHelper.GetHighlightColor(color); + + var needRoundCap = serie.roundCap && inside > 0; + + serieData.context.insideRadius = inside; + serieData.context.outsideRadius = outside; + serieData.context.areaCenter = m_SeriePolar.context.center; + serieData.context.position = ChartHelper.GetPosition(m_SeriePolar.context.center, (start + end) / 2, (inside + outside) / 2); + + UGL.DrawDoughnut(vh, m_SeriePolar.context.center, inside, outside, color, color, + ColorUtil.clearColor32, start, end, borderWidth, borderColor, serie.gap / 2, chart.settings.cicleSmoothness, + needRoundCap, true); + } + + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(totalDetailProgress); + serie.animation.CheckSymbol(serie.symbol.GetSize(null, chart.theme.serie.lineSymbolSize)); + chart.RefreshChart(); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs.meta b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs.meta new file mode 100644 index 0000000..e9f8a39 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.PolarCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: baaa1d070b88a4b9bb7d1eed341041e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs new file mode 100644 index 0000000..bd67aed --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs @@ -0,0 +1,515 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed partial class HeatmapHandler : SerieHandler<Heatmap> + { + private GridCoord m_SerieGrid; + private Dictionary<int, int> m_CountDict = new Dictionary<int, int>(); + + public override int defaultDimension { get { return 2; } } + + public static int GetGridKey(int x, int y) + { + return x * 100000 + y; + } + + public static void GetGridXYByKey(int key, out int x, out int y) + { + x = key / 100000; + y = key % 100000; + } + + public override void Update() + { + base.Update(); + } + + public override void DrawSerie(VertexHelper vh) + { + if (serie.heatmapType == HeatmapType.Count) + DrawCountHeatmapSerie(vh, serie); + else + { + if (serie.IsUseCoord<PolarCoord>()) + { + DrawPolarHeatmap(vh, serie); + } + else if (serie.IsUseCoord<GridCoord>()) + { + DrawDataHeatmapSerie(vh, serie); + } + } + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + dataIndex = serie.context.pointerItemDataIndex; + if (serie.heatmapType == HeatmapType.Count) + { + int value; + if (!m_CountDict.TryGetValue(dataIndex, out value)) return; + var visualMap = chart.GetVisualMapOfSerie(serie); + var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); + + title = serie.serieName; + itemFormatter = SerieHelper.GetItemFormatter(serie, null, itemFormatter); + numericFormatter = SerieHelper.GetNumericFormatter(serie, null, numericFormatter); + marker = SerieHelper.GetItemMarker(serie, null, marker); + var color = visualMap.GetColor(value); + + if (itemFormatter == null) itemFormatter = ""; + itemFormatter = itemFormatter.Replace("\\n", "\n"); + var temp = itemFormatter.Split('\n'); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = dimension; + param.dataCount = serie.dataCount; + param.serieData = null; + param.color = color; + param.marker = marker; + param.itemFormatter = formatter; + param.numericFormatter = numericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add("count"); + param.columns.Add(ChartCached.NumberToStr(value, param.numericFormatter)); + + paramList.Add(param); + } + } + else + { + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + var visualMap = chart.GetVisualMapOfSerie(serie); + var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); + + if (string.IsNullOrEmpty(category)) + { + var xAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex); + if (xAxis != null) + category = xAxis.GetData((int)serieData.GetData(0)); + } + title = serie.serieName; + itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + marker = SerieHelper.GetItemMarker(serie, serieData, marker); + + if (itemFormatter == null) itemFormatter = ""; + itemFormatter = itemFormatter.Replace("\\n", "\n"); + var temp = itemFormatter.Split('\n'); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = dimension; + param.dataCount = serie.dataCount; + param.serieData = serieData; + param.color = serieData.context.color; + param.marker = marker; + param.itemFormatter = formatter; + param.numericFormatter = numericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(category); + param.columns.Add(ChartCached.NumberToStr(serieData.GetData(dimension), param.numericFormatter)); + + paramList.Add(param); + } + } + } + + public override void UpdateSerieContext() + { + if (serie.IsUseCoord<GridCoord>()) + UpdateSerieGridContext(); + else if (serie.IsUseCoord<PolarCoord>()) + UpdateSeriePolarContext(); + } + + private void UpdateSerieGridContext() + { + if (m_SerieGrid == null) + return; + + var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter()) || m_LegendEnter; + var needInteract = false; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + serieData.context.highlight = false; + } + chart.RefreshPainter(serie); + } + return; + } + if (serie.heatmapType == HeatmapType.Count) + return; + m_LastCheckContextFlag = needCheck; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + foreach (var serieData in serie.data) + { + serieData.context.highlight = true; + } + } + else + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + if (!needInteract && serieData.context.rect.Contains(chart.pointerPos)) + { + serie.context.pointerItemDataIndex = serieData.index; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + needInteract = true; + } + else + { + serieData.context.highlight = false; + } + } + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + private void DrawDataHeatmapSerie(VertexHelper vh, Heatmap serie) + { + if (!serie.show || serie.animation.HasFadeOut()) return; + XAxis xAxis; + YAxis yAxis; + if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) return; + if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) return; + var visualMap = chart.GetVisualMapOfSerie(serie); + if (visualMap == null) return; + m_SerieGrid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + xAxis.boundaryGap = true; + yAxis.boundaryGap = true; + var emphasisStyle = serie.emphasisStyle; + var xCount = AxisHelper.GetTotalSplitGridNum(xAxis); + var yCount = AxisHelper.GetTotalSplitGridNum(yAxis); + var xWidth = m_SerieGrid.context.width / xCount; + var yWidth = m_SerieGrid.context.height / yCount; + + var zeroX = m_SerieGrid.context.x; + var zeroY = m_SerieGrid.context.y; + var borderWidth = serie.itemStyle.show ? serie.itemStyle.borderWidth : 0; + var splitWid = xWidth - 2 * borderWidth; + var splitHig = yWidth - 2 * borderWidth; + var defaultSymbolSize = Mathf.Min(splitWid, splitHig) * 0.25f; + + serie.animation.InitProgress(0, xCount); + var animationIndex = serie.animation.GetCurrIndex(); + var dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + var dataChanging = false; + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + + var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); + if (visualMap.autoMinMax) + { + double maxValue, minValue; + SerieHelper.GetMinMaxData(serie, dimension, out minValue, out maxValue); + VisualMapHelper.SetMinMax(visualMap, minValue, maxValue); + } + var rangeMin = visualMap.rangeMin; + var rangeMax = visualMap.rangeMax; + var color = chart.theme.GetColor(serie.index); + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 borderColor; + for (int n = 0; n < serie.dataCount; n++) + { + var serieData = serie.data[n]; + var xValue = serieData.GetData(0); + var yValue = serieData.GetData(1); + var i = AxisHelper.GetAxisValueSplitIndex(xAxis, xValue, true, xCount); + var j = AxisHelper.GetAxisValueSplitIndex(yAxis, yValue, true, yCount); + + if (serie.IsIgnoreValue(serieData, dimension)) + { + serie.context.dataPoints.Add(Vector3.zero); + serie.context.dataIndexs.Add(serieData.index); + continue; + } + var state = SerieHelper.GetSerieState(serie, serieData, true); + var symbol = SerieHelper.GetSerieSymbol(serie, serieData, state); + var isRectSymbol = symbol.type == SymbolType.Rect; + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, serieData, chart.theme, state); + var value = serieData.GetCurrData(dimension, dataAddDuration, dataChangeDuration, yAxis.inverse, + 0, 0, unscaledTime); + if (serieData.IsDataChanged()) dataChanging = true; + var pos = new Vector3(zeroX + (i + 0.5f) * xWidth, + zeroY + (j + 0.5f) * yWidth); + serie.context.dataPoints.Add(pos); + serie.context.dataIndexs.Add(serieData.index); + serieData.context.position = pos; + serieData.context.canShowLabel = false; + + if ((value < rangeMin && rangeMin != visualMap.min) || + (value > rangeMax && rangeMax != visualMap.max)) + { + continue; + } + if (!visualMap.IsInSelectedValue(value)) continue; + if (animationIndex >= 0 && i > animationIndex) continue; + color = visualMap.GetColor(value); + if (serieData.context.highlight) + color = ChartHelper.GetHighlightColor(color); + + serieData.context.canShowLabel = true; + serieData.context.color = color; + + var highlight = (serieData.context.highlight) || + visualMap.context.pointerIndex > 0; + var rectWid = 0f; + var rectHig = 0f; + if (isRectSymbol) + { + if (symbol.size == 0 && symbol.sizeType == SymbolSizeType.Custom) + { + rectWid = splitWid; + rectHig = splitHig; + } + else + { + var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, defaultSymbolSize, state); + rectWid = symbolSize; + rectHig = symbolSize; + } + serieData.context.rect = new Rect(pos.x - rectWid / 2, pos.y - rectHig / 2, rectWid, rectHig); + UGL.DrawRectangle(vh, serieData.context.rect, color); + + if (borderWidth > 0 && !ChartHelper.IsClearColor(borderColor)) + { + UGL.DrawBorder(vh, pos, rectWid, rectHig, borderWidth, borderColor, borderColor); + } + } + else + { + var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, defaultSymbolSize, state); + var emptyColor = SerieHelper.GetItemBackgroundColor(serie, serieData, chart.theme, serie.context.colorIndex, state); + serieData.context.rect = new Rect(pos.x - symbolSize / 2, pos.y - symbolSize / 2, symbolSize, symbolSize); + chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, pos, + color, color, emptyColor, borderColor, symbol.gap, cornerRadius, symbol.size2); + } + + if (visualMap.hoverLink && highlight && emphasisStyle != null && + emphasisStyle.itemStyle.borderWidth > 0) + { + var emphasisItemStyle = emphasisStyle.itemStyle; + var emphasisBorderWidth = emphasisItemStyle.borderWidth; + var emphasisBorderColor = emphasisItemStyle.opacity > 0 ? + emphasisItemStyle.borderColor : ChartConst.clearColor32; + var emphasisBorderToColor = emphasisItemStyle.opacity > 0 ? + emphasisItemStyle.borderToColor : ChartConst.clearColor32; + UGL.DrawBorder(vh, pos, rectWid, rectHig, emphasisBorderWidth, emphasisBorderColor, + emphasisBorderToColor); + } + + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(xCount); + chart.RefreshPainter(serie); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } + + private void DrawCountHeatmapSerie(VertexHelper vh, Heatmap serie) + { + if (!serie.show || serie.animation.HasFadeOut()) return; + XAxis xAxis; + YAxis yAxis; + if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) return; + if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) return; + m_SerieGrid = chart.GetChartComponent<GridCoord>(xAxis.gridIndex); + xAxis.boundaryGap = true; + yAxis.boundaryGap = true; + var visualMap = chart.GetVisualMapOfSerie(serie); + var emphasisStyle = serie.emphasisStyle; + var xCount = AxisHelper.GetTotalSplitGridNum(xAxis); + var yCount = AxisHelper.GetTotalSplitGridNum(yAxis); + var xWidth = m_SerieGrid.context.width / xCount; + var yWidth = m_SerieGrid.context.height / yCount; + + var zeroX = m_SerieGrid.context.x; + var zeroY = m_SerieGrid.context.y; + var borderWidth = serie.itemStyle.show ? serie.itemStyle.borderWidth : 0; + var splitWid = xWidth - 2 * borderWidth; + var splitHig = yWidth - 2 * borderWidth; + var defaultSymbolSize = Mathf.Min(splitWid, splitHig) * 0.25f; + + serie.animation.InitProgress(0, xCount); + var animationIndex = serie.animation.GetCurrIndex(); + var dataChanging = false; + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + + m_CountDict.Clear(); + double minCount = 0, maxCount = 0; + foreach (var serieData in serie.data) + { + var xValue = serieData.GetData(0); + var yValue = serieData.GetData(1); + var i = AxisHelper.GetAxisValueSplitIndex(xAxis, xValue, true, xCount); + var j = AxisHelper.GetAxisValueSplitIndex(yAxis, yValue, true, yCount); + var key = GetGridKey(i, j); + var count = 0; + + if (!m_CountDict.TryGetValue(key, out count)) + count = 1; + else + count++; + if (count > maxCount) + maxCount = count; + m_CountDict[key] = count; + } + + if (visualMap.autoMinMax) + { + VisualMapHelper.SetMinMax(visualMap, minCount, maxCount); + } + var rangeMin = visualMap.rangeMin; + var rangeMax = visualMap.rangeMax; + + int highlightX = -1; + int highlightY = -1; + if (serie.context.pointerItemDataIndex > 0) + { + if (m_CountDict.ContainsKey(serie.context.pointerItemDataIndex)) + { + GetGridXYByKey(serie.context.pointerItemDataIndex, out highlightX, out highlightY); + } + } + var state = SerieHelper.GetSerieState(serie, null, true); + var symbol = SerieHelper.GetSerieSymbol(serie, null, state); + var symbolSize = SerieHelper.GetSysmbolSize(serie, null, defaultSymbolSize, state); + var isRectSymbol = symbol.type == SymbolType.Rect; + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 color, toColor, emptyColor, borderColor; + SerieHelper.GetItemColor(out color, out toColor, out emptyColor, serie, null, chart.theme, serie.context.colorIndex, state); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, null, chart.theme, state); + foreach (var kv in m_CountDict) + { + int i, j; + GetGridXYByKey(kv.Key, out i, out j); + var value = kv.Value; + + if (serie.IsIgnoreValue(value)) + { + continue; + } + + if ((value < rangeMin && rangeMin != visualMap.min) || + (value > rangeMax && rangeMax != visualMap.max)) + { + continue; + } + if (!visualMap.IsInSelectedValue(value)) + continue; + if (animationIndex >= 0 && i > animationIndex) + continue; + + var highlight = i == highlightX && j == highlightY; + + color = visualMap.GetColor(value); + if (highlight) + color = ChartHelper.GetHighlightColor(color); + + var pos = new Vector3(zeroX + (i + 0.5f) * xWidth, + zeroY + (j + 0.5f) * yWidth); + + var rectWid = 0f; + var rectHig = 0f; + if (isRectSymbol) + { + if (symbol.size == 0 && symbol.sizeType == SymbolSizeType.Custom) + { + rectWid = splitWid; + rectHig = splitHig; + } + else + { + rectWid = symbolSize; + rectHig = symbolSize; + } + var rect = new Rect(pos.x - rectWid / 2, pos.y - rectHig / 2, rectWid, rectHig); + UGL.DrawRectangle(vh, rect, color); + + if (borderWidth > 0 && !ChartHelper.IsClearColor(borderColor)) + { + UGL.DrawBorder(vh, pos, rectWid, rectHig, borderWidth, borderColor, borderColor); + } + } + else + { + chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, pos, + color, color, emptyColor, borderColor, symbol.gap, cornerRadius, symbol.size2); + } + + if (visualMap.hoverLink && highlight && emphasisStyle != null && + emphasisStyle.itemStyle.borderWidth > 0) + { + var emphasisItemStyle = emphasisStyle.itemStyle; + var emphasisBorderWidth = emphasisItemStyle.borderWidth; + var emphasisBorderColor = emphasisItemStyle.opacity > 0 ? + emphasisItemStyle.borderColor : ChartConst.clearColor32; + var emphasisBorderToColor = emphasisItemStyle.opacity > 0 ? + emphasisItemStyle.borderToColor : ChartConst.clearColor32; + UGL.DrawBorder(vh, pos, rectWid, rectHig, emphasisBorderWidth, emphasisBorderColor, + emphasisBorderToColor); + } + + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(xCount); + chart.RefreshPainter(serie); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs.meta new file mode 100644 index 0000000..2a731f5 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Heatmap/HeatmapHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3a5cd70274da44d50b48fc04d8b52e21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line.meta b/Assets/XCharts/Runtime/Serie/Line.meta new file mode 100644 index 0000000..2f308b9 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 61f1a04d9920849e7861bebdfd070384 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/Line.cs b/Assets/XCharts/Runtime/Serie/Line/Line.cs new file mode 100644 index 0000000..bbc2cc7 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/Line.cs @@ -0,0 +1,36 @@ +using System; + +namespace XCharts.Runtime +{ + [Serializable] + [SerieHandler(typeof(LineHandler), true)] + [SerieConvert(typeof(Bar), typeof(Pie))] + [CoordOptions(typeof(GridCoord), typeof(PolarCoord))] + [DefaultAnimation(AnimationType.LeftToRight, false)] + [DefaultTooltip(Tooltip.Type.Line, Tooltip.Trigger.Axis)] + [SerieDataExtraField("m_State", "m_Ignore")] + [SerieComponent(typeof(LabelStyle), typeof(EndLabelStyle), typeof(LineArrow), typeof(AreaStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(SerieSymbol), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + public class Line : Serie, INeedSerieContainer + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Line>(serieName); + serie.symbol.show = true; + serie.animation.interaction.radius.value = 1.5f; + for (int i = 0; i < 5; i++) + { + chart.AddData(serie.index, UnityEngine.Random.Range(10, 90)); + } + return serie; + } + + public static Line ConvertSerie(Serie serie) + { + var newSerie = serie.Clone<Line>(); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/Line.cs.meta b/Assets/XCharts/Runtime/Serie/Line/Line.cs.meta new file mode 100644 index 0000000..b88d15c --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/Line.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 883eff3dc77e0439a80d257577790cbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs b/Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs new file mode 100644 index 0000000..c390ff7 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs @@ -0,0 +1,406 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// For grid coord + /// </summary> + internal sealed partial class LineHandler : SerieHandler<Line> + { + List<List<SerieData>> m_StackSerieData = new List<List<SerieData>>(); + private GridCoord m_SerieGrid; + + public override Vector3 GetSerieDataLabelOffset(SerieData serieData, LabelStyle label) + { + var invert = label.autoOffset && + SerieHelper.IsDownPoint(serie, serieData.index) && + (serie.areaStyle == null || !serie.areaStyle.show); + if (invert) + { + var offset = label.GetOffset(serie.context.insideRadius); + return new Vector3(offset.x, -offset.y, offset.z); + } + else + { + return label.GetOffset(serie.context.insideRadius); + } + } + + private void UpdateSerieGridContext() + { + if (m_SerieGrid == null) + return; + var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter()) || m_LegendEnter; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + serie.highlight = false; + serie.ResetInteract(); + foreach (var serieData in serie.data) + serieData.context.highlight = false; + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + return; + } + m_LastCheckContextFlag = needCheck; + var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + var themeSymbolSize = chart.theme.serie.lineSymbolSize; + var needInteract = false; + serie.ResetDataIndex(); + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis); + serieData.context.highlight = true; + serieData.interact.SetValue(ref needInteract, size); + } + } + else if (serie.context.isTriggerByAxis) + { + serie.context.pointerEnter = false; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var highlight = i == serie.context.pointerItemDataIndex; + serieData.context.highlight = highlight; + var state = SerieHelper.GetSerieState(serie, serieData, true); + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state); + serieData.interact.SetValue(ref needInteract, size); + if (highlight) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = i; + needInteract = true; + } + } + } + else + { + var lastIndex = serie.context.pointerItemDataIndex; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var dist = Vector3.Distance(chart.pointerPos, serieData.context.position); + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize); + var highlight = dist <= size; + serieData.context.highlight = highlight; + var state = SerieHelper.GetSerieState(serie, serieData, true); + size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state); + serieData.interact.SetValue(ref needInteract, size); + if (highlight) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = serieData.index; + } + } + if (lastIndex != serie.context.pointerItemDataIndex) + { + needInteract = true; + } + if (serie.context.pointerItemDataIndex >= 0) + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + else + serie.interact.SetValue(ref needInteract, lineWidth); + } + if (needInteract) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + + private void DrawLinePoint(VertexHelper vh, Serie serie) + { + if (!serie.show || serie.IsPerformanceMode()) + return; + + if (m_SerieGrid == null) + return; + + var count = serie.context.dataPoints.Count; + var clip = SeriesHelper.IsAnyClipSerie(chart.series); + var theme = chart.theme; + var interacting = false; + var lineArrow = serie.lineArrow; + var visualMap = chart.GetVisualMapOfSerie(serie); + var isVisualMapGradient = VisualMapHelper.IsNeedLineGradient(visualMap); + var interactDuration = serie.animation.GetInteractionDuration(); + + Axis axis; + Axis relativedAxis; + chart.GetSerieGridCoordAxis(serie, out axis, out relativedAxis); + + for (int i = 0; i < count; i++) + { + var index = serie.context.dataIndexs[i]; + var serieData = serie.GetSerieData(index); + if (serieData == null) + continue; + if (serieData.context.isClip) + continue; + var state = SerieHelper.GetSerieState(serie, serieData, true); + var symbol = SerieHelper.GetSerieSymbol(serie, serieData, state); + + if (!symbol.show || !symbol.ShowSymbol(index, count)) + continue; + + var pos = serie.context.dataPoints[i]; + if (lineArrow != null && lineArrow.show) + { + if (lineArrow.position == LineArrow.Position.Start && i == 0) + continue; + if (lineArrow.position == LineArrow.Position.End && i == count - 1) + continue; + } + + if (ChartHelper.IsIngore(pos)) + continue; + + var symbolSize = 0f; + if (!serieData.interact.TryGetValue(ref symbolSize, ref interacting, interactDuration)) + { + symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme.serie.lineSymbolSize, state); + serieData.interact.SetValue(ref interacting, symbolSize); + symbolSize = serie.animation.GetSysmbolSize(symbolSize); + } + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 symbolColor, symbolToColor, symbolEmptyColor, borderColor; + SerieHelper.GetItemColor(out symbolColor, out symbolToColor, out symbolEmptyColor, serie, serieData, theme, serie.context.colorIndex); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, null, chart.theme, state); + if (isVisualMapGradient) + { + symbolColor = VisualMapHelper.GetLineGradientColor(visualMap, pos, m_SerieGrid, axis, relativedAxis, symbolColor); + symbolToColor = symbolColor; + } + chart.DrawClipSymbol(vh, symbol.type, symbolSize, symbolBorder, pos, + symbolColor, symbolToColor, symbolEmptyColor, borderColor, symbol.gap, clip, cornerRadius, m_SerieGrid, + i > 0 ? serie.context.dataPoints[i - 1] : m_SerieGrid.context.position); + } + if (interacting) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + + private void DrawLineArrow(VertexHelper vh, Serie serie) + { + if (!serie.show || serie.lineArrow == null || !serie.lineArrow.show) + return; + + if (serie.context.dataPoints.Count < 2) + return; + + var lineColor = SerieHelper.GetLineColor(serie, null, chart.theme, serie.context.colorIndex); + var startPos = Vector3.zero; + var arrowPos = Vector3.zero; + var lineArrow = serie.lineArrow.arrow; + var dataPoints = serie.context.drawPoints; + switch (serie.lineArrow.position) + { + case LineArrow.Position.End: + if (dataPoints.Count < 3) + { + startPos = dataPoints[dataPoints.Count - 2].position; + arrowPos = dataPoints[dataPoints.Count - 1].position; + } + else + { + startPos = dataPoints[dataPoints.Count - 3].position; + arrowPos = dataPoints[dataPoints.Count - 2].position; + } + UGL.DrawArrow(vh, startPos, arrowPos, lineArrow.width, lineArrow.height, + lineArrow.offset, lineArrow.dent, lineArrow.GetColor(lineColor)); + + break; + + case LineArrow.Position.Start: + startPos = dataPoints[1].position; + arrowPos = dataPoints[0].position; + UGL.DrawArrow(vh, startPos, arrowPos, lineArrow.width, lineArrow.height, + lineArrow.offset, lineArrow.dent, lineArrow.GetColor(lineColor)); + + break; + } + } + + private void DrawLineSerie(VertexHelper vh, Line serie) + { + if (serie.animation.HasFadeOut()) + return; + + Axis axis; + Axis relativedAxis; + var isY = chart.GetSerieGridCoordAxis(serie, out axis, out relativedAxis); + + if (axis == null) + return; + if (relativedAxis == null) + return; + + m_SerieGrid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + if (m_SerieGrid == null) + return; + if (m_EndLabel != null && !m_SerieGrid.context.endLabelList.Contains(m_EndLabel)) + { + m_SerieGrid.context.endLabelList.Add(m_EndLabel); + } + + var visualMap = chart.GetVisualMapOfSerie(serie); + var dataZoom = chart.GetDataZoomOfAxis(axis); + var showData = serie.GetDataList(dataZoom); + + if (showData.Count <= 0) + return; + + var axisLength = isY ? m_SerieGrid.context.height : m_SerieGrid.context.width; + var axisRelativedLength = isY ? m_SerieGrid.context.width : m_SerieGrid.context.height; + + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > showData.Count ? showData.Count : serie.maxShow) : + showData.Count; + maxCount -= serie.context.dataZoomStartIndexOffset; + var scaleWid = AxisHelper.GetDataWidth(axis, axisLength, maxCount, dataZoom); + var scaleRelativedWid = AxisHelper.GetDataWidth(relativedAxis, axisRelativedLength, maxCount, dataZoom); + int rate = LineHelper.GetDataAverageRate(serie, axisLength, maxCount, false); + var totalAverage = serie.sampleAverage > 0 ? + serie.sampleAverage : + DataHelper.DataAverage(ref showData, serie.sampleType, serie.minShow, maxCount, rate); + var dataChanging = false; + var dataChangeDuration = serie.animation.GetChangeDuration(); + var unscaledTime = serie.animation.unscaledTime; + + var interacting = false; + var lineWidth = LineHelper.GetLineWidth(ref interacting, serie, chart.theme.serie.lineWidth); + + axis.context.scaleWidth = scaleWid; + serie.context.isHorizontal = isY; + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + + Serie lastSerie = null; + var isStack = SeriesHelper.IsStack<Line>(chart.series, serie.stack); + if (isStack) + { + lastSerie = SeriesHelper.GetLastStackSerie(chart.series, serie); + SeriesHelper.UpdateStackDataList(chart.series, serie, dataZoom, m_StackSerieData); + } + var lp = Vector3.zero; + for (int i = serie.minShow; i < showData.Count; i += rate) + { + var serieData = showData[i]; + var realIndex = i - serie.context.dataZoomStartIndexOffset; + var isIgnore = serie.IsIgnoreValue(serieData); + if (isIgnore) + { + serieData.context.stackHeight = 0; + serieData.context.position = Vector3.zero; + if (serie.ignoreLineBreak && serie.context.dataIgnores.Count > 0) + { + serie.context.dataIgnores[serie.context.dataIgnores.Count - 1] = true; + } + } + else + { + var np = Vector3.zero; + var xValue = axis.IsCategory() ? realIndex : serieData.GetData(0, axis.inverse); + var relativedValue = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, + maxCount, totalAverage, i, 0, dataChangeDuration, ref dataChanging, relativedAxis, unscaledTime); + + serieData.context.stackHeight = GetDataPoint(isY, axis, relativedAxis, m_SerieGrid, xValue, relativedValue, + i, scaleWid, scaleRelativedWid, isStack, ref np); + serieData.context.isClip = false; + if (serie.clip && !m_SerieGrid.Contains(np)) + { + //if (m_SerieGrid.BoundaryPoint(lp, np, ref np)) + { + serieData.context.isClip = true; + } + } + serie.context.dataIgnores.Add(false); + serieData.context.position = np; + serie.context.dataPoints.Add(np); + serie.context.dataIndexs.Add(serieData.index); + lp = np; + } + } + + if (dataChanging || interacting) + chart.RefreshPainter(serie); + + if (serie.context.dataPoints.Count <= 0) + return; + + serie.animation.InitProgress(serie.context.dataPoints, isY); + + VisualMapHelper.AutoSetLineMinMax(visualMap, serie, isY, axis, relativedAxis); + LineHelper.UpdateSerieDrawPoints(serie, chart.settings, chart.theme, visualMap, lineWidth, isY, m_SerieGrid); + LineHelper.DrawSerieLineArea(vh, serie, lastSerie, chart.theme, visualMap, isY, axis, relativedAxis, m_SerieGrid); + LineHelper.DrawSerieLine(vh, chart.theme, serie, visualMap, m_SerieGrid, axis, relativedAxis, lineWidth); + + serie.context.vertCount = vh.currentVertCount; + + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + serie.animation.CheckSymbol(serie.symbol.GetSize(null, chart.theme.serie.lineSymbolSize)); + chart.RefreshPainter(serie); + } + } + + private float GetDataPoint(bool isY, Axis axis, Axis relativedAxis, GridCoord grid, double xValue, + double yValue, int i, float scaleWid, float scaleRelativedWid, bool isStack, ref Vector3 np) + { + float xPos, yPos; + var gridXY = isY ? grid.context.x : grid.context.y; + var valueHig = 0f; + valueHig = AxisHelper.GetAxisValueDistance(grid, relativedAxis, scaleRelativedWid, yValue); + valueHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, valueHig); + if (isY) + { + xPos = gridXY + valueHig; + yPos = AxisHelper.GetAxisValuePosition(grid, axis, scaleWid, xValue); + if (isStack) + { + for (int n = 0; n < m_StackSerieData.Count - 1; n++) + xPos += m_StackSerieData[n][i].context.stackHeight; + } + } + else + { + yPos = gridXY + valueHig; + xPos = AxisHelper.GetAxisValuePosition(grid, axis, scaleWid, xValue); + if (isStack) + { + for (int n = 0; n < m_StackSerieData.Count - 1; n++) + yPos += m_StackSerieData[n][i].context.stackHeight; + } + } + np = new Vector3(xPos, yPos); + return AxisHelper.GetAxisValueLength(grid, relativedAxis, scaleRelativedWid, yValue); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs.meta b/Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs.meta new file mode 100644 index 0000000..b6f3a32 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHandler.GridCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34168c2605d4546c291adeb8e857fd62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs b/Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs new file mode 100644 index 0000000..74689e7 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs @@ -0,0 +1,283 @@ +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// For polar coord + /// </summary> + internal sealed partial class LineHandler + { + private PolarCoord m_SeriePolar; + + private void UpdateSeriePolarContext() + { + if (m_SeriePolar == null) + return; + + var needCheck = (chart.isPointerInChart && m_SeriePolar.IsPointerEnter()) || m_LegendEnter; + var lineWidth = 0f; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + var needAnimation1 = false; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + serie.interact.SetValue(ref needAnimation1, lineWidth); + foreach (var serieData in serie.data) + { + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + var symbolSize = symbol.GetSize(serieData, chart.theme.serie.lineSymbolSize); + serieData.context.highlight = false; + serieData.interact.SetValue(ref needAnimation1, symbolSize); + } + if (needAnimation1) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + return; + } + m_LastCheckContextFlag = needCheck; + var themeSymbolSize = chart.theme.serie.lineSymbolSize; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + + var needInteract = false; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis); + serieData.context.highlight = true; + serieData.interact.SetValue(ref needInteract, size); + } + } + else + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + var dir = chart.pointerPos - new Vector2(m_SeriePolar.context.center.x, m_SeriePolar.context.center.y); + var pointerAngle = ChartHelper.GetAngle360(Vector2.up, dir); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var angle0 = serieData.context.angle; + var angle1 = i >= serie.dataCount - 1 ? angle0 : serie.data[i + 1].context.angle; + + if (pointerAngle >= angle0 && pointerAngle < angle1) + { + serie.context.pointerItemDataIndex = i; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + } + else + { + serieData.context.highlight = false; + } + } + } + if (needInteract) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + + private void DrawPolarLine(VertexHelper vh, Serie serie) + { + var datas = serie.data; + if (datas.Count <= 0) + return; + + m_SeriePolar = chart.GetChartComponent<PolarCoord>(serie.polarIndex); + if (m_SeriePolar == null) + return; + + var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, m_SeriePolar.index); + var m_RadiusAxis = ComponentHelper.GetRadiusAxis(chart.components, m_SeriePolar.index); + if (m_AngleAxis == null || m_RadiusAxis == null) + return; + + var startAngle = m_AngleAxis.startAngle; + var firstSerieData = datas[0]; + var lp = PolarHelper.UpdatePolarAngleAndPos(m_SeriePolar, m_AngleAxis, m_RadiusAxis, firstSerieData); + var cp = Vector3.zero; + var lineColor = SerieHelper.GetLineColor(serie, null, chart.theme, serie.context.colorIndex); + var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + var currDetailProgress = 0f; + var totalDetailProgress = datas.Count; + + serie.animation.InitProgress(currDetailProgress, totalDetailProgress); + + var ltp = Vector3.zero; + var lbp = Vector3.zero; + var ntp = Vector3.zero; + var nbp = Vector3.zero; + var itp = Vector3.zero; + var ibp = Vector3.zero; + var clp = Vector3.zero; + var crp = Vector3.zero; + bool bitp = true, bibp = true; + if (datas.Count <= 2) + { + for (int i = 0; i < datas.Count; i++) + { + var serieData = datas[i]; + cp = PolarHelper.UpdatePolarAngleAndPos(m_SeriePolar, m_AngleAxis, m_RadiusAxis, datas[i]); + serieData.context.position = cp; + serie.context.dataPoints.Add(cp); + } + UGL.DrawLine(vh, serie.context.dataPoints, lineWidth, lineColor, false, false); + } + else + { + for (int i = 1; i < datas.Count; i++) + { + if (serie.animation.CheckDetailBreak(i)) + break; + + var serieData = datas[i]; + cp = PolarHelper.UpdatePolarAngleAndPos(m_SeriePolar, m_AngleAxis, m_RadiusAxis, datas[i]); + serieData.context.position = cp; + serie.context.dataPoints.Add(cp); + + var np = i == datas.Count - 1 ? cp : + PolarHelper.UpdatePolarAngleAndPos(m_SeriePolar, m_AngleAxis, m_RadiusAxis, datas[i + 1]); + + UGLHelper.GetLinePoints(lp, cp, np, lineWidth, + ref ltp, ref lbp, + ref ntp, ref nbp, + ref itp, ref ibp, + ref clp, ref crp, + ref bitp, ref bibp, i); + + if (i == 1) + { + UGL.AddVertToVertexHelper(vh, ltp, lbp, lineColor, false); + } + + if (bitp == bibp) + { + if (bitp) + UGL.AddVertToVertexHelper(vh, itp, ibp, lineColor, true); + else + { + UGL.AddVertToVertexHelper(vh, ltp, clp, lineColor, true); + UGL.AddVertToVertexHelper(vh, ltp, crp, lineColor, true); + } + } + else + { + if (bitp) + { + UGL.AddVertToVertexHelper(vh, itp, clp, lineColor, true); + UGL.AddVertToVertexHelper(vh, itp, crp, lineColor, true); + } + else if (bibp) + { + UGL.AddVertToVertexHelper(vh, clp, ibp, lineColor, true); + UGL.AddVertToVertexHelper(vh, crp, ibp, lineColor, true); + } + } + lp = cp; + } + } + + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(totalDetailProgress); + serie.animation.CheckSymbol(serie.symbol.GetSize(null, chart.theme.serie.lineSymbolSize)); + chart.RefreshChart(); + } + } + + private void DrawPolarLineArrow(VertexHelper vh, Serie serie) + { + if (!serie.show || serie.lineArrow == null || !serie.lineArrow.show) + return; + + if (serie.context.dataPoints.Count < 2) + return; + + var lineColor = SerieHelper.GetLineColor(serie, null, chart.theme, serie.context.colorIndex); + var startPos = Vector3.zero; + var arrowPos = Vector3.zero; + var lineArrow = serie.lineArrow.arrow; + var dataPoints = serie.context.dataPoints; + switch (serie.lineArrow.position) + { + case LineArrow.Position.End: + if (dataPoints.Count < 3) + { + startPos = dataPoints[dataPoints.Count - 2]; + arrowPos = dataPoints[dataPoints.Count - 1]; + } + else + { + startPos = dataPoints[dataPoints.Count - 3]; + arrowPos = dataPoints[dataPoints.Count - 2]; + } + UGL.DrawArrow(vh, startPos, arrowPos, lineArrow.width, lineArrow.height, + lineArrow.offset, lineArrow.dent, lineArrow.GetColor(lineColor)); + + break; + + case LineArrow.Position.Start: + startPos = dataPoints[1]; + arrowPos = dataPoints[0]; + UGL.DrawArrow(vh, startPos, arrowPos, lineArrow.width, lineArrow.height, + lineArrow.offset, lineArrow.dent, lineArrow.GetColor(lineColor)); + break; + } + } + + private void DrawPolarLineSymbol(VertexHelper vh) + { + for (int n = 0; n < chart.series.Count; n++) + { + var serie = chart.series[n]; + + if (!serie.show) + continue; + if (!(serie is Line)) + continue; + + var count = serie.dataCount; + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 symbolColor, symbolToColor, symbolEmptyColor, borderColor; + for (int i = 0; i < count; i++) + { + var serieData = serie.GetSerieData(i); + var state = SerieHelper.GetSerieState(serie, serieData, true); + var symbol = SerieHelper.GetSerieSymbol(serie, serieData, state); + if (ChartHelper.IsIngore(serieData.context.position)) + continue; + + if (!symbol.show || !symbol.ShowSymbol(i, count)) + continue; + + var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme.serie.lineSymbolSize, state); + SerieHelper.GetItemColor(out symbolColor, out symbolToColor, out symbolEmptyColor, serie, serieData, chart.theme, n); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, null, chart.theme, state); + + symbolSize = serie.animation.GetSysmbolSize(symbolSize); + chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, serieData.context.position, + symbolColor, symbolToColor, symbolEmptyColor, borderColor, symbol.gap, cornerRadius, symbol.size2); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs.meta b/Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs.meta new file mode 100644 index 0000000..fd0e63a --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHandler.PolarCoord.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8655c97b8c7e44e44852f8b81a7372b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHandler.cs b/Assets/XCharts/Runtime/Serie/Line/LineHandler.cs new file mode 100644 index 0000000..e06fa14 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHandler.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// For grid coord + /// </summary> + [UnityEngine.Scripting.Preserve] + internal sealed partial class LineHandler : SerieHandler<Line> + { + public override void Update() + { + base.Update(); + if (serie.IsUseCoord<GridCoord>()) + UpdateSerieGridContext(); + else if (serie.IsUseCoord<PolarCoord>()) + UpdateSeriePolarContext(); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + UpdateCoordSerieParams(ref paramList, ref title, dataIndex, showCategory, category, + marker, itemFormatter, numericFormatter, ignoreDataDefaultContent); + } + + public override void DrawSerie(VertexHelper vh) + { + if (serie.IsUseCoord<PolarCoord>()) + { + DrawPolarLine(vh, serie); + DrawPolarLineSymbol(vh); + DrawPolarLineArrow(vh, serie); + } + else if (serie.IsUseCoord<GridCoord>()) + { + DrawLineSerie(vh, serie); + + if (!SeriesHelper.IsStack(chart.series)) + { + DrawLinePoint(vh, serie); + DrawLineArrow(vh, serie); + } + } + } + + public override void DrawUpper(VertexHelper vh) + { + if (serie.IsUseCoord<GridCoord>()) + { + if (SeriesHelper.IsStack(chart.series)) + { + DrawLinePoint(vh, serie); + DrawLineArrow(vh, serie); + } + } + } + + public override void RefreshEndLabelInternal() + { + base.RefreshEndLabelInternal(); + if (m_SerieGrid == null) return; + if (!serie.animation.IsFinish()) return; + var endLabelList = m_SerieGrid.context.endLabelList; + if (endLabelList.Count <= 1) return; + + endLabelList.Sort(delegate (ChartLabel a, ChartLabel b) + { + if (a == null || b == null) return 1; + return b.transform.position.y.CompareTo(a.transform.position.y); + }); + var lastY = float.NaN; + for (int i = 0; i < endLabelList.Count; i++) + { + var label = endLabelList[i]; + if (label == null) continue; + if (!label.isAnimationEnd) continue; + var labelPosition = label.transform.localPosition; + if (float.IsNaN(lastY)) + { + lastY = labelPosition.y; + } + else + { + var labelHeight = label.GetTextHeight(); + if (labelPosition.y + labelHeight > lastY) + { + label.SetPosition(new Vector3(labelPosition.x, lastY - labelHeight, labelPosition.z)); + } + lastY = label.transform.localPosition.y; + } + } + } + + // public override int GetPointerItemDataIndex() + // { + // var symbolSize = SerieHelper.GetSysmbolSize(serie, null, chart.theme.serie.lineSymbolSize) * 1.5f; + // var count = serie.context.dataPoints.Count; + // for (int i = 0; i < count; i++) + // { + // var index = serie.context.dataIndexs[i]; + // var serieData = serie.GetSerieData(index); + // if (serieData == null) + // continue; + // if (serieData.context.isClip) + // continue; + + // var pos = serie.context.dataPoints[i]; + // if (Vector2.Distance(pos, chart.pointerPos) < symbolSize) + // { + // return i; + // } + // } + // return -1; + // } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Line/LineHandler.cs.meta new file mode 100644 index 0000000..51dec59 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e3a076ca3ee241c3b8b1088d4519dfa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHelper.cs b/Assets/XCharts/Runtime/Serie/Line/LineHelper.cs new file mode 100644 index 0000000..25b2839 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHelper.cs @@ -0,0 +1,622 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + public static class LineHelper + { + private static List<Vector3> s_CurvesPosList = new List<Vector3>(); + + public static int GetDataAverageRate(Serie serie, float axisLength, int maxCount, bool isYAxis) + { + var sampleDist = serie.sampleDist; + var rate = 0; + if (sampleDist > 0) + rate = (int)((maxCount - serie.minShow) / (axisLength / sampleDist)); + if (rate < 1) + rate = 1; + return rate; + } + + public static void DrawSerieLineArea(VertexHelper vh, Serie serie, Serie lastStackSerie, + ThemeStyle theme, VisualMap visualMap, bool isY, Axis axis, Axis relativedAxis, GridCoord grid) + { + Color32 areaColor, areaToColor; + bool innerFill, toTop; + if (!SerieHelper.GetAreaColor(out areaColor, out areaToColor, out innerFill, out toTop, serie, null, theme, serie.context.colorIndex)) + { + return; + } + if (innerFill) + { + UGL.DrawPolygon(vh, serie.context.dataPoints, areaColor); + return; + } + var gridXY = (isY ? grid.context.x : grid.context.y); + var min = gridXY; + var max = gridXY + (isY ? grid.context.width : grid.context.height); + var start = 0f; + switch(serie.areaStyle.origin) + { + case AreaStyle.AreaOrigin.Start: + start = min; + break; + case AreaStyle.AreaOrigin.End: + start = max; + break; + default: + start = gridXY + relativedAxis.context.offset; + break; + } + if (lastStackSerie == null) + { + DrawSerieLineNormalArea(vh, serie, isY, + start, + min, + max, + areaColor, + areaToColor, + visualMap, + axis, + relativedAxis, + grid, + toTop); + } + else + { + DrawSerieLineStackArea(vh, serie, lastStackSerie, isY, + start, + min, + max, + areaColor, + areaToColor, + visualMap, + toTop); + } + } + + private static void DrawSerieLineNormalArea(VertexHelper vh, Serie serie, bool isY, + float zero, float min, float max, Color32 areaColor, Color32 areaToColor, + VisualMap visualMap, Axis axis, Axis relativedAxis, GridCoord grid, bool toTop) + { + var points = serie.context.drawPoints; + var count = points.Count; + if (count < 2) + return; + + var isBreak = false; + var lp = Vector3.zero; + var isVisualMapGradient = VisualMapHelper.IsNeedAreaGradient(visualMap); + var areaLerp = !ChartHelper.IsValueEqualsColor(areaColor, areaToColor); + var zsp = isY ? + new Vector3(zero, points[0].position.y) : + new Vector3(points[0].position.x, zero); + var zep = isY ? + new Vector3(zero, points[count - 1].position.y) : + new Vector3(points[count - 1].position.x, zero); + + var lastDataIsIgnore = false; + for (int i = 0; i < points.Count; i++) + { + var pdata = points[i]; + var tp = pdata.position; + if (serie.clip) + { + grid.Clamp(ref tp); + } + var isIgnore = pdata.isIgnoreBreak; + var color = areaColor; + var toColor = areaToColor; + var lerp = areaLerp; + + if (serie.animation.CheckDetailBreak(tp, isY)) + { + isBreak = true; + + var progress = serie.animation.GetCurrDetail(); + var ip = Vector3.zero; + var axisStartPos = isY ? new Vector3(-10000, progress) : new Vector3(progress, -10000); + var axisEndPos = isY ? new Vector3(10000, progress) : new Vector3(progress, 10000); + + if (UGLHelper.GetIntersection(lp, tp, axisStartPos, axisEndPos, ref ip)) + tp = ip; + } + var zp = isY ? new Vector3(zero, tp.y) : new Vector3(tp.x, zero); + if (isVisualMapGradient) + { + color = VisualMapHelper.GetLineGradientColor(visualMap, zp, grid, axis, relativedAxis, areaColor); + toColor = VisualMapHelper.GetLineGradientColor(visualMap, tp, grid, axis, relativedAxis, areaToColor); + lerp = true; + } + if (i > 0) + { + if ((lp.y - zero > 0 && tp.y - zero < 0) || (lp.y - zero < 0 && tp.y - zero > 0)) + { + var ip = Vector3.zero; + if (UGLHelper.GetIntersection(lp, tp, zsp, zep, ref ip)) + { + if (lerp) + AddVertToVertexHelperWithLerpColor(vh, ip, ip, color, toColor, isY, min, max, i > 0, toTop); + else + { + if (lastDataIsIgnore) + UGL.AddVertToVertexHelper(vh, ip, ip, ColorUtil.clearColor32, true); + + UGL.AddVertToVertexHelper(vh, ip, ip, toColor, color, i > 0); + + if (isIgnore) + UGL.AddVertToVertexHelper(vh, ip, ip, ColorUtil.clearColor32, true); + } + } + } + } + + if (lerp) + AddVertToVertexHelperWithLerpColor(vh, tp, zp, color, toColor, isY, min, max, i > 0, toTop); + else + { + if (lastDataIsIgnore) + UGL.AddVertToVertexHelper(vh, tp, zp, ColorUtil.clearColor32, true); + + UGL.AddVertToVertexHelper(vh, tp, zp, toColor, color, i > 0); + + if (isIgnore) + UGL.AddVertToVertexHelper(vh, tp, zp, ColorUtil.clearColor32, true); + } + lp = tp; + lastDataIsIgnore = isIgnore; + if (isBreak) + break; + } + } + + private static void DrawSerieLineStackArea(VertexHelper vh, Serie serie, Serie lastStackSerie, bool isY, + float zero, float min, float max, Color32 color, Color32 toColor, VisualMap visualMap, bool toTop) + { + if (lastStackSerie == null) + return; + + var upPoints = serie.context.drawPoints; + var downPoints = lastStackSerie.context.drawPoints; + var upCount = upPoints.Count; + var downCount = downPoints.Count; + + if (upCount <= 0 || downCount <= 0) + return; + + var lerp = !ChartHelper.IsValueEqualsColor(color, toColor); + var ltp = upPoints[0].position; + var lbp = downPoints[0].position; + + if (lerp) + AddVertToVertexHelperWithLerpColor(vh, ltp, lbp, color, toColor, isY, min, max, false, toTop); + else + UGL.AddVertToVertexHelper(vh, ltp, lbp, color, false); + + int u = 1, d = 1; + var isBreakTop = false; + var isBreakBottom = false; + + while ((u < upCount || d < downCount)) + { + var tp = u < upCount ? upPoints[u].position : upPoints[upCount - 1].position; + var bp = d < downCount ? downPoints[d].position : downPoints[downCount - 1].position; + + var tnp = (u + 1) < upCount ? upPoints[u + 1].position : upPoints[upCount - 1].position; + var bnp = (d + 1) < downCount ? downPoints[d + 1].position : downPoints[downCount - 1].position; + + if (serie.animation.CheckDetailBreak(tp, isY)) + { + isBreakTop = true; + + var progress = serie.animation.GetCurrDetail(); + var ip = Vector3.zero; + + if (UGLHelper.GetIntersection(ltp, tp, + new Vector3(progress, -10000), + new Vector3(progress, 10000), ref ip)) + tp = ip; + else + tp = new Vector3(progress, tp.y); + } + if (serie.animation.CheckDetailBreak(bp, isY)) + { + isBreakBottom = true; + + var progress = serie.animation.GetCurrDetail(); + var ip = Vector3.zero; + + if (UGLHelper.GetIntersection(lbp, bp, + new Vector3(progress, -10000), + new Vector3(progress, 10000), ref ip)) + bp = ip; + else + bp = new Vector3(progress, bp.y); + } + + if (lerp) + AddVertToVertexHelperWithLerpColor(vh, tp, bp, color, toColor, isY, min, max, true, toTop); + else + UGL.AddVertToVertexHelper(vh, tp, bp, color, true); + u++; + d++; + if (bp.x < tp.x && bnp.x < tp.x) + u--; + if (tp.x < bp.x && tnp.x < bp.x) + d--; + + ltp = tp; + lbp = bp; + if (isBreakTop && isBreakBottom) + break; + } + } + + private static void AddVertToVertexHelperWithLerpColor(VertexHelper vh, Vector3 tp, Vector3 bp, + Color32 color, Color32 toColor, bool isY, float min, float max, bool needTriangle, bool toTop) + { + if (toTop) + { + var range = max - min; + var color1 = Color32.Lerp(color, toColor, ((isY ? tp.x : tp.y) - min) / range); + var color2 = Color32.Lerp(color, toColor, ((isY ? bp.x : bp.y) - min) / range); + + UGL.AddVertToVertexHelper(vh, tp, bp, color1, color2, needTriangle); + } + else + { + UGL.AddVertToVertexHelper(vh, tp, bp, toColor, color, needTriangle); + } + } + + internal static void DrawSerieLine(VertexHelper vh, ThemeStyle theme, Serie serie, VisualMap visualMap, + GridCoord grid, Axis axis, Axis relativedAxis, float lineWidth) + { + if (!serie.lineStyle.show || serie.lineStyle.type == LineStyle.Type.None) + return; + + var datas = serie.context.drawPoints; + + var dataCount = datas.Count; + if (dataCount < 2) + return; + + var ltp = Vector3.zero; + var lbp = Vector3.zero; + var ntp = Vector3.zero; + var nbp = Vector3.zero; + var itp = Vector3.zero; + var ibp = Vector3.zero; + var clp = Vector3.zero; + var crp = Vector3.zero; + + var isBreak = false; + var isY = axis is YAxis; + var isVisualMapGradient = VisualMapHelper.IsNeedLineGradient(visualMap); + var isLineStyleGradient = serie.lineStyle.IsNeedGradient(); + var lineColor = SerieHelper.GetLineColor(serie, null, theme, serie.context.colorIndex); + + var lastDataIsIgnore = datas[0].isIgnoreBreak; + var firstInGridPointIndex = serie.clip ? -1 : 1; + var segmentCount = 0; + var dashLength = serie.lineStyle.dashLength; + var gapLength = serie.lineStyle.gapLength; + var dotLength = serie.lineStyle.dotLength; + for (int i = 1; i < dataCount; i++) + { + var cdata = datas[i]; + var isIgnore = cdata.isIgnoreBreak; + var cp = cdata.position; + var lp = datas[i - 1].position; + + var np = i == dataCount - 1 ? cp : datas[i + 1].position; + if (serie.animation.CheckDetailBreak(cp, isY)) + { + isBreak = true; + var ip = Vector3.zero; + var progress = serie.animation.GetCurrDetail(); + var rate = 0f; + if (AnimationStyleHelper.GetAnimationPosition(serie.animation, isY, lp, cp, progress, ref ip, ref rate)) + cp = np = ip; + } + serie.context.lineEndPostion = cp; + serie.context.lineEndValueY = AxisHelper.GetAxisPositionValue(grid, relativedAxis, cp); + var handled = false; + var isClip = false; + if (serie.clip) + { + if (!grid.Contains(cp)) + isClip = true; + else if (firstInGridPointIndex <= 0) + firstInGridPointIndex = i; + if (isClip) isIgnore = true; + } + if (serie.lineStyle.type == LineStyle.Type.None) + { + handled = true; + break; + } + { + segmentCount++; + var index = 0f; + switch (serie.lineStyle.type) + { + case LineStyle.Type.Dashed: + index = segmentCount % (dashLength + gapLength); + if (index >= dashLength) + isIgnore = true; + break; + case LineStyle.Type.Dotted: + index = segmentCount % (dotLength + gapLength); + if (index >= dotLength) + isIgnore = true; + break; + case LineStyle.Type.DashDot: + index = segmentCount % (dashLength + dotLength + 2 * gapLength); + if (index >= dashLength && index < dashLength + gapLength) + isIgnore = true; + else if (index >= dashLength + gapLength + dotLength) + isIgnore = true; + break; + case LineStyle.Type.DashDotDot: + index = segmentCount % (dashLength + 2 * dotLength + 3 * gapLength); + if (index >= dashLength && index < dashLength + gapLength) + isIgnore = true; + else if (index >= dashLength + gapLength + dotLength && index < dashLength + dotLength + 2 * gapLength) + isIgnore = true; + else if (index >= dashLength + 2 * gapLength + 2 * dotLength) + isIgnore = true; + break; + } + } + + if (handled) + { + lastDataIsIgnore = isIgnore; + if (isBreak) + break; + else + continue; + } + bool bitp = true, bibp = true; + UGLHelper.GetLinePoints(lp, cp, np, lineWidth, + ref ltp, ref lbp, + ref ntp, ref nbp, + ref itp, ref ibp, + ref clp, ref crp, + ref bitp, ref bibp, i); + + if (i == 1) + { + if (isClip) lastDataIsIgnore = true; + AddLineVertToVertexHelper(vh, ltp, lbp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, false, lastDataIsIgnore, isIgnore); + if (dataCount == 2 || isBreak) + { + AddLineVertToVertexHelper(vh, clp, crp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + serie.context.lineEndPostion = cp; + serie.context.lineEndValueY = AxisHelper.GetAxisPositionValue(grid, relativedAxis, cp); + break; + } + } + + if (bitp == bibp) + { + if (bitp) + AddLineVertToVertexHelper(vh, itp, ibp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + else + { + AddLineVertToVertexHelper(vh, ltp, clp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + AddLineVertToVertexHelper(vh, ltp, crp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + } + } + else + { + if (bitp) + { + AddLineVertToVertexHelper(vh, itp, clp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + AddLineVertToVertexHelper(vh, itp, crp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + } + else if (bibp) + { + AddLineVertToVertexHelper(vh, clp, ibp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + AddLineVertToVertexHelper(vh, crp, ibp, lineColor, isVisualMapGradient, isLineStyleGradient, + visualMap, serie.lineStyle, grid, axis, relativedAxis, true, lastDataIsIgnore, isIgnore); + } + } + lastDataIsIgnore = isIgnore; + if (isBreak) + break; + } + } + + public static float GetLineWidth(ref bool interacting, Serie serie, float defaultWidth) + { + var lineWidth = 0f; + if (!serie.interact.TryGetValue(ref lineWidth, ref interacting, serie.animation.GetInteractionDuration())) + { + lineWidth = serie.lineStyle.GetWidth(defaultWidth); + serie.interact.SetValue(ref interacting, lineWidth); + } + return lineWidth; + } + + private static void AddLineVertToVertexHelper(VertexHelper vh, Vector3 tp, Vector3 bp, + Color32 lineColor, bool visualMapGradient, bool lineStyleGradient, VisualMap visualMap, + LineStyle lineStyle, GridCoord grid, Axis axis, Axis relativedAxis, bool needTriangle, + bool lastIgnore, bool ignore) + { + if (lastIgnore && needTriangle) + UGL.AddVertToVertexHelper(vh, tp, bp, ColorUtil.clearColor32, true); + + if (visualMapGradient) + { + var color1 = VisualMapHelper.GetLineGradientColor(visualMap, tp, grid, axis, relativedAxis, lineColor); + var color2 = VisualMapHelper.GetLineGradientColor(visualMap, bp, grid, axis, relativedAxis, lineColor); + UGL.AddVertToVertexHelper(vh, tp, bp, color1, color2, needTriangle); + } + else if (lineStyleGradient) + { + var color1 = VisualMapHelper.GetLineStyleGradientColor(lineStyle, tp, grid, axis, lineColor); + var color2 = VisualMapHelper.GetLineStyleGradientColor(lineStyle, bp, grid, axis, lineColor); + UGL.AddVertToVertexHelper(vh, tp, bp, color1, color2, needTriangle); + } + else + { + UGL.AddVertToVertexHelper(vh, tp, bp, lineColor, needTriangle); + } + if (lastIgnore && !needTriangle) + { + UGL.AddVertToVertexHelper(vh, tp, bp, ColorUtil.clearColor32, false); + } + if (ignore && needTriangle) + { + UGL.AddVertToVertexHelper(vh, tp, bp, ColorUtil.clearColor32, false); + } + } + + internal static void UpdateSerieDrawPoints(Serie serie, Settings setting, ThemeStyle theme, VisualMap visualMap, + float lineWidth, bool isY, GridCoord grid) + { + serie.context.drawPoints.Clear(); + var last = Vector3.zero; + switch (serie.lineType) + { + case LineType.Smooth: + UpdateSmoothLineDrawPoints(serie, setting, isY); + break; + case LineType.StepStart: + case LineType.StepMiddle: + case LineType.StepEnd: + UpdateStepLineDrawPoints(serie, setting, theme, isY, lineWidth); + break; + default: + UpdateNormalLineDrawPoints(serie, setting, visualMap, grid); + break; + } + } + + private static void UpdateNormalLineDrawPoints(Serie serie, Settings setting, VisualMap visualMap, GridCoord grid) + { + var isVisualMapGradient = VisualMapHelper.IsNeedGradient(visualMap); + if (isVisualMapGradient || serie.clip || (serie.lineStyle.IsNotSolidLine())) + { + var dataPoints = serie.context.dataPoints; + if (dataPoints.Count > 1) + { + var sp = dataPoints[0]; + var ip = Vector3.zero; + for (int i = 1; i < dataPoints.Count; i++) + { + var ep = dataPoints[i]; + var ignore = serie.context.dataIgnores[i]; + if (serie.clip && grid.NotAnyIntersect(sp, ep)) + { + sp = ep; + continue; + } + var dir = (ep - sp).normalized; + var dist = Vector3.Distance(sp, ep); + var segment = (int)(dist / setting.lineSegmentDistance); + serie.context.drawPoints.Add(new PointInfo(sp, ignore)); + for (int j = 1; j < segment; j++) + { + var np = sp + dir * dist * j / segment; + serie.context.drawPoints.Add(new PointInfo(np, ignore)); + } + sp = ep; + if (i == dataPoints.Count - 1) + { + serie.context.drawPoints.Add(new PointInfo(ep, ignore)); + } + } + } + else + { + serie.context.drawPoints.Add(new PointInfo(dataPoints[0], serie.context.dataIgnores[0])); + } + } + else + { + for (int i = 0; i < serie.context.dataPoints.Count; i++) + { + serie.context.drawPoints.Add(new PointInfo(serie.context.dataPoints[i], serie.context.dataIgnores[i])); + } + } + } + + private static void UpdateSmoothLineDrawPoints(Serie serie, Settings setting, bool isY) + { + var points = serie.context.dataPoints; + float smoothness = setting.lineSmoothness; + for (int i = 0; i < points.Count - 1; i++) + { + var sp = points[i]; + var ep = points[i + 1]; + var lsp = i > 0 ? points[i - 1] : sp; + var nep = i < points.Count - 2 ? points[i + 2] : ep; + var ignore = serie.context.dataIgnores[i]; + if (isY) + UGLHelper.GetBezierListVertical(ref s_CurvesPosList, sp, ep, smoothness, setting.lineSmoothStyle); + else + UGLHelper.GetBezierList(ref s_CurvesPosList, sp, ep, lsp, nep, smoothness, setting.lineSmoothStyle, serie.smoothLimit); + for (int j = 1; j < s_CurvesPosList.Count; j++) + { + serie.context.drawPoints.Add(new PointInfo(s_CurvesPosList[j], ignore)); + } + } + } + + private static void UpdateStepLineDrawPoints(Serie serie, Settings setting, ThemeStyle theme, bool isY, float lineWidth) + { + var points = serie.context.dataPoints; + var lp = points[0]; + serie.context.drawPoints.Clear(); + serie.context.drawPoints.Add(new PointInfo(lp, serie.context.dataIgnores[0])); + for (int i = 1; i < points.Count; i++) + { + var cp = points[i]; + var ignore = serie.context.dataIgnores[i]; + if ((isY && Mathf.Abs(lp.x - cp.x) <= lineWidth) || + (!isY && Mathf.Abs(lp.y - cp.y) <= lineWidth)) + { + serie.context.drawPoints.Add(new PointInfo(cp, ignore)); + lp = cp; + continue; + } + switch (serie.lineType) + { + case LineType.StepStart: + serie.context.drawPoints.Add(new PointInfo(isY ? + new Vector3(cp.x, lp.y) : + new Vector3(lp.x, cp.y), ignore)); + break; + case LineType.StepMiddle: + serie.context.drawPoints.Add(new PointInfo(isY ? + new Vector3(lp.x, (lp.y + cp.y) / 2) : + new Vector3((lp.x + cp.x) / 2, lp.y), ignore)); + serie.context.drawPoints.Add(new PointInfo(isY ? + new Vector3(cp.x, (lp.y + cp.y) / 2) : + new Vector3((lp.x + cp.x) / 2, cp.y), ignore)); + break; + case LineType.StepEnd: + serie.context.drawPoints.Add(new PointInfo(isY ? + new Vector3(lp.x, cp.y) : + new Vector3(cp.x, lp.y), ignore)); + break; + } + serie.context.drawPoints.Add(new PointInfo(cp, ignore)); + lp = cp; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/LineHelper.cs.meta b/Assets/XCharts/Runtime/Serie/Line/LineHelper.cs.meta new file mode 100644 index 0000000..ae015c8 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/LineHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7292748fb01ef44709a94d08f4907dd5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs new file mode 100644 index 0000000..0e27898 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs @@ -0,0 +1,42 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + [SerieHandler(typeof(SimplifiedLineHandler), true)] + [SerieConvert(typeof(SimplifiedBar), typeof(Line))] + [CoordOptions(typeof(GridCoord))] + [DefaultAnimation(AnimationType.LeftToRight, false)] + [DefaultTooltip(Tooltip.Type.Line, Tooltip.Trigger.Axis)] + [SerieComponent(typeof(AreaStyle))] + [SerieDataComponent()] + [SerieDataExtraField()] + public class SimplifiedLine : Serie, INeedSerieContainer, ISimplifiedSerie + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<SimplifiedLine>(serieName); + serie.symbol.show = false; + var lastValue = 0d; + for (int i = 0; i < 50; i++) + { + if (i < 20) + lastValue += UnityEngine.Random.Range(0, 5); + else + lastValue += UnityEngine.Random.Range(-3, 5); + chart.AddData(serie.index, lastValue); + } + return serie; + } + + public static SimplifiedLine ConvertSerie(Serie serie) + { + var newSerie = serie.Clone<SimplifiedLine>(); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs.meta b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs.meta new file mode 100644 index 0000000..486e64b --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5740626e12a84a0ca3a984935f61720 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs new file mode 100644 index 0000000..42f522b --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs @@ -0,0 +1,268 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + /// <summary> + /// For grid coord + /// </summary> + [UnityEngine.Scripting.Preserve] + internal sealed class SimplifiedLineHandler : SerieHandler<SimplifiedLine> + { + private GridCoord m_SerieGrid; + + public override void Update() + { + base.Update(); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + UpdateCoordSerieParams(ref paramList, ref title, dataIndex, showCategory, category, + marker, itemFormatter, numericFormatter, ignoreDataDefaultContent); + } + + public override void DrawSerie(VertexHelper vh) + { + DrawLineSerie(vh, serie); + } + + public override void UpdateSerieContext() + { + if (m_SerieGrid == null) + return; + + var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter()) || m_LegendEnter; + var lineWidth = 0f; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + var needAnimation1 = false; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + serie.interact.SetValue(ref needAnimation1, lineWidth); + foreach (var serieData in serie.data) + { + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + var symbolSize = symbol.GetSize(serieData, chart.theme.serie.lineSymbolSize); + serieData.context.highlight = false; + serieData.interact.SetValue(ref needAnimation1, symbolSize); + } + if (needAnimation1) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + return; + } + m_LastCheckContextFlag = needCheck; + var themeSymbolSize = chart.theme.serie.lineSymbolSize; + lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + + var needInteract = false; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, SerieState.Emphasis); + serieData.context.highlight = true; + serieData.interact.SetValue(ref needInteract, size); + } + } + else if (serie.context.isTriggerByAxis) + { + serie.context.pointerEnter = true; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var highlight = i == serie.context.pointerItemDataIndex; + serieData.context.highlight = highlight; + var state = SerieHelper.GetSerieState(serie, serieData, true); + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state); + serieData.interact.SetValue(ref needInteract, size); + if (highlight) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = i; + } + } + } + else + { + var lastIndex = serie.context.pointerItemDataIndex; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var dist = Vector3.Distance(chart.pointerPos, serieData.context.position); + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize); + var highlight = dist <= size; + serieData.context.highlight = highlight; + var state = SerieHelper.GetSerieState(serie, serieData, true); + size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state); + serieData.interact.SetValue(ref needInteract, size); + if (highlight) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = serieData.index; + serie.interact.SetValue(ref needInteract, serie.animation.interaction.GetWidth(lineWidth)); + } + } + if (lastIndex != serie.context.pointerItemDataIndex) + needInteract = true; + } + if (needInteract) + { + if (SeriesHelper.IsStack(chart.series)) + chart.RefreshTopPainter(); + else + chart.RefreshPainter(serie); + } + } + + private void DrawLineSerie(VertexHelper vh, SimplifiedLine serie) + { + if (!serie.show) + return; + if (serie.animation.HasFadeOut()) + return; + + Axis axis; + Axis relativedAxis; + var isY = chart.GetSerieGridCoordAxis(serie, out axis, out relativedAxis); + + m_SerieGrid = chart.GetChartComponent<GridCoord>(axis.gridIndex); + + if (axis == null) + return; + if (relativedAxis == null) + return; + if (m_SerieGrid == null) + return; + + var dataZoom = chart.GetDataZoomOfAxis(axis); + var showData = serie.GetDataList(dataZoom); + + if (showData.Count <= 0) + return; + + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > showData.Count ? showData.Count : serie.maxShow) : + showData.Count; + + var axisLength = isY ? m_SerieGrid.context.height : m_SerieGrid.context.width; + var axisRelativedLength = isY ? m_SerieGrid.context.width : m_SerieGrid.context.height; + var scaleWid = AxisHelper.GetDataWidth(axis, axisLength, maxCount, dataZoom); + var scaleRelativedWid = AxisHelper.GetDataWidth(relativedAxis, axisRelativedLength, maxCount, dataZoom); + + int rate = LineHelper.GetDataAverageRate(serie, axisLength, maxCount, false); + var totalAverage = serie.sampleAverage > 0 ? + serie.sampleAverage : + DataHelper.DataAverage(ref showData, serie.sampleType, serie.minShow, maxCount, rate); + var dataChanging = false; + var dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + + var interacting = false; + var lineWidth = LineHelper.GetLineWidth(ref interacting, serie, chart.theme.serie.lineWidth); + + axis.context.scaleWidth = scaleWid; + relativedAxis.context.scaleWidth = scaleRelativedWid; + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + + for (int i = serie.minShow; i < maxCount; i += rate) + { + var serieData = showData[i]; + var isIgnore = serie.IsIgnoreValue(serieData); + if (isIgnore) + { + serieData.context.stackHeight = 0; + serieData.context.position = Vector3.zero; + if (serie.ignoreLineBreak && serie.context.dataIgnores.Count > 0) + { + serie.context.dataIgnores[serie.context.dataIgnores.Count - 1] = true; + } + } + else + { + var np = Vector3.zero; + var xValue = axis.IsCategory() ? i : serieData.GetData(0, axis.inverse); + var relativedValue = DataHelper.SampleValue(ref showData, serie.sampleType, rate, serie.minShow, + maxCount, totalAverage, i, dataAddDuration, dataChangeDuration, ref dataChanging, relativedAxis, unscaledTime); + + serieData.context.stackHeight = GetDataPoint(isY, axis, relativedAxis, m_SerieGrid, xValue, relativedValue, + i, scaleWid, scaleRelativedWid, false, ref np); + + serieData.context.position = np; + + serie.context.dataPoints.Add(np); + serie.context.dataIndexs.Add(serieData.index); + serie.context.dataIgnores.Add(false); + } + } + + if (dataChanging || interacting) + chart.RefreshPainter(serie); + + if (serie.context.dataPoints.Count <= 0) + return; + + serie.animation.InitProgress(serie.context.dataPoints, isY); + + LineHelper.UpdateSerieDrawPoints(serie, chart.settings, chart.theme, null, lineWidth, isY, m_SerieGrid); + LineHelper.DrawSerieLineArea(vh, serie, null, chart.theme, null, isY, axis, relativedAxis, m_SerieGrid); + LineHelper.DrawSerieLine(vh, chart.theme, serie, null, m_SerieGrid, axis, relativedAxis, lineWidth); + + serie.context.vertCount = vh.currentVertCount; + + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + chart.RefreshPainter(serie); + } + } + + private float GetDataPoint(bool isY, Axis axis, Axis relativedAxis, GridCoord grid, double xValue, + double yValue, int i, float scaleWid, float scaleRelativedWid, bool isStack, ref Vector3 np) + { + float xPos, yPos; + var gridXY = isY ? grid.context.x : grid.context.y; + + if (isY) + { + var valueHig = AxisHelper.GetAxisValueDistance(grid, relativedAxis, scaleRelativedWid, yValue); + valueHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, valueHig); + + xPos = gridXY + valueHig; + yPos = AxisHelper.GetAxisValuePosition(grid, axis, scaleWid, xValue); + } + else + { + + var valueHig = AxisHelper.GetAxisValueDistance(grid, relativedAxis, scaleRelativedWid, yValue); + valueHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, valueHig); + + yPos = gridXY + valueHig; + xPos = AxisHelper.GetAxisValuePosition(grid, axis, scaleWid, xValue); + } + np = new Vector3(xPos, yPos); + return yPos; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs.meta new file mode 100644 index 0000000..6a41fe3 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Line/SimplifiedLineHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c4714bd08de34548ac7be3ec6523ee1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Parallel.meta b/Assets/XCharts/Runtime/Serie/Parallel.meta new file mode 100644 index 0000000..60bd75e --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Parallel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c2ba7099a74f54617b446aeaf3d95672 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs b/Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs new file mode 100644 index 0000000..0062b50 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(ParallelHandler), true)] + [RequireChartComponent(typeof(ParallelCoord))] + [SerieComponent(typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField()] + public class Parallel : Serie, INeedSerieContainer + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Parallel>(serieName); + serie.lineStyle.width = 0.8f; + serie.lineStyle.opacity = 0.6f; + + for (int i = 0; i < 100; i++) + { + var data = new List<double>() + { + Random.Range(0f, 50f), + Random.Range(0f, 100f), + Random.Range(0f, 1000f), + Random.Range(0, 5), + }; + serie.AddData(data, "data" + i); + } + chart.RefreshChart(); + return serie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs.meta b/Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs.meta new file mode 100644 index 0000000..3663842 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Parallel/Parallel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 283df79138f274a6ba975ff8f30c6d30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs b/Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs new file mode 100644 index 0000000..5c957c1 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class ParallelHandler : SerieHandler<Parallel> + { + public override void Update() + { + base.Update(); + } + + public override void DrawSerie(VertexHelper vh) + { + DrawParallelSerie(vh, serie); + } + + private void DrawParallelSerie(VertexHelper vh, Parallel serie) + { + if (!serie.show) return; + if (serie.animation.HasFadeOut()) return; + + var parallel = chart.GetChartComponent<ParallelCoord>(serie.parallelIndex); + if (parallel == null) + return; + + var axisCount = parallel.context.parallelAxes.Count; + if (axisCount <= 0) + return; + + var animationIndex = serie.animation.GetCurrIndex(); + var isHorizonal = parallel.orient == Orient.Horizonal; + + var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + + float currDetailProgress = !isHorizonal ? + parallel.context.x : + parallel.context.y; + + float totalDetailProgress = !isHorizonal ? + parallel.context.x + parallel.context.width : + parallel.context.y + parallel.context.height; + + serie.animation.InitProgress(currDetailProgress, totalDetailProgress); + + serie.containerIndex = parallel.index; + serie.containterInstanceId = parallel.instanceId; + + var currProgress = serie.animation.GetCurrDetail(); + var isSmooth = serie.lineType == LineType.Smooth; + foreach (var serieData in serie.data) + { + var count = Mathf.Min(axisCount, serieData.data.Count); + var lp = Vector3.zero; + var colorIndex = serie.colorByData?serieData.index : serie.context.colorIndex; + var lineColor = SerieHelper.GetLineColor(serie, serieData, chart.theme, colorIndex); + serieData.context.dataPoints.Clear(); + for (int i = 0; i < count; i++) + { + if (animationIndex >= 0 && i > animationIndex) continue; + var pos = GetPos(parallel, i, serieData.data[i], isHorizonal); + if (!isHorizonal) + { + if (isSmooth) + { + serieData.context.dataPoints.Add(pos); + } + else if (pos.x <= currProgress) + { + serieData.context.dataPoints.Add(pos); + } + else + { + var currProgressStart = new Vector3(currProgress, parallel.context.y - 50); + var currProgressEnd = new Vector3(currProgress, parallel.context.y + parallel.context.height + 50); + var intersectionPos = Vector3.zero; + + if (UGLHelper.GetIntersection(lp, pos, currProgressStart, currProgressEnd, ref intersectionPos)) + serieData.context.dataPoints.Add(intersectionPos); + else + serieData.context.dataPoints.Add(pos); + break; + } + } + else + { + if (isSmooth) + { + serieData.context.dataPoints.Add(pos); + } + else if (pos.y <= currProgress) + { + serieData.context.dataPoints.Add(pos); + } + else + { + var currProgressStart = new Vector3(parallel.context.x - 50, currProgress); + var currProgressEnd = new Vector3(parallel.context.x + parallel.context.width + 50, currProgress); + var intersectionPos = Vector3.zero; + + if (UGLHelper.GetIntersection(lp, pos, currProgressStart, currProgressEnd, ref intersectionPos)) + serieData.context.dataPoints.Add(intersectionPos); + else + serieData.context.dataPoints.Add(pos); + break; + } + } + lp = pos; + } + if (isSmooth) + UGL.DrawCurves(vh, serieData.context.dataPoints, lineWidth, lineColor, + chart.settings.lineSmoothStyle, + chart.settings.lineSmoothness, + UGL.Direction.XAxis, currProgress, isHorizonal); + else + UGL.DrawLine(vh, serieData.context.dataPoints, lineWidth, lineColor, isSmooth); + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(totalDetailProgress - currDetailProgress); + chart.RefreshPainter(serie); + } + } + + private static ParallelAxis GetAxis(ParallelCoord parallel, int index) + { + if (index >= 0 && index < parallel.context.parallelAxes.Count) + return parallel.context.parallelAxes[index]; + else + return null; + } + + private static Vector3 GetPos(ParallelCoord parallel, int axisIndex, double dataValue, bool isHorizonal) + { + var axis = GetAxis(parallel, axisIndex); + if (axis == null) + return Vector3.zero; + + var sValueDist = axis.GetDistance(dataValue, axis.context.width); + return new Vector3( + isHorizonal ? axis.context.x + sValueDist : axis.context.x, + isHorizonal ? axis.context.y : axis.context.y + sValueDist); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs.meta new file mode 100644 index 0000000..e190cca --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Parallel/ParallelHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 482b461d91f8c4013bf291153d5810e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Pie.meta b/Assets/XCharts/Runtime/Serie/Pie.meta new file mode 100644 index 0000000..cd666fa --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Pie.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a5dfd0cb375a24f659bf56e113aa6fc8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Pie/Pie.cs b/Assets/XCharts/Runtime/Serie/Pie/Pie.cs new file mode 100644 index 0000000..14086a1 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Pie/Pie.cs @@ -0,0 +1,44 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieConvert(typeof(Line), typeof(Bar))] + [SerieHandler(typeof(PieHandler), true)] + [DefaultAnimation(AnimationType.Clockwise)] + [SerieComponent(typeof(LabelStyle), typeof(LabelLine), typeof(TitleStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(LabelLine), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField("m_Ignore", "m_Selected", "m_Radius")] + public class Pie : Serie + { + [SerializeField][Since("v3.8.1")] private bool m_RadiusGradient = false; + + public override SerieColorBy defaultColorBy { get { return SerieColorBy.Data; } } + public override bool titleJustForSerie { get { return true; } } + + /// <summary> + /// Whether to use gradient color in pie chart. + /// || 是否开启半径方向的渐变效果。 + /// </summary> + public bool radiusGradient + { + get { return m_RadiusGradient; } + set { if (PropertyUtil.SetStruct(ref m_RadiusGradient, value)) { SetVerticesDirty(); } } + } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Pie>(serieName); + chart.AddData(serie.index, Random.Range(10, 100), "pie1"); + chart.AddData(serie.index, Random.Range(10, 100), "pie2"); + chart.AddData(serie.index, Random.Range(10, 100), "pie3"); + return serie; + } + + public static Pie ConvertSerie(Serie serie) + { + var newSerie = SerieHelper.CloneSerie<Pie>(serie); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Pie/Pie.cs.meta b/Assets/XCharts/Runtime/Serie/Pie/Pie.cs.meta new file mode 100644 index 0000000..41dba77 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Pie/Pie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53f0d225949a3450e9e90687759f1714 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs b/Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs new file mode 100644 index 0000000..66b3f6e --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs @@ -0,0 +1,736 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class PieHandler : SerieHandler<Pie> + { + public override void Update() + { + base.Update(); + } + + public override void DrawBase(VertexHelper vh) + { + UpdateRuntimeData(serie); + DrawPieLabelLine(vh, serie, false); + } + + public override void DrawSerie(VertexHelper vh) + { + UpdateRuntimeData(serie); + DrawPie(vh, serie); + chart.RefreshBasePainter(); + } + + public override void DrawUpper(VertexHelper vh) + { + DrawPieLabelLine(vh, serie, true); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + UpdateItemSerieParams(ref paramList, ref title, dataIndex, category, + marker, itemFormatter, numericFormatter, ignoreDataDefaultContent); + } + + public override Vector3 GetSerieDataLabelPosition(SerieData serieData, LabelStyle label) + { + var labelLine = SerieHelper.GetSerieLabelLine(serie, serieData); + if (labelLine != null && labelLine.show && serieData.labelObject != null) + { + var currAngle = serieData.context.halfAngle - serie.context.startAngle; + var isLeft = currAngle > 180 || (currAngle == 0 && serieData.context.startAngle > 0); + var textOffset = serieData.labelObject.text.GetPreferredWidth() / 2; + return serieData.context.labelPosition + (isLeft ? Vector3.left : Vector3.right) * textOffset; + } + else + { + return serieData.context.labelPosition; + } + } + + public override Vector3 GetSerieDataLabelOffset(SerieData serieData, LabelStyle label) + { + var offset = label.GetOffset(serie.context.insideRadius); + if (label.autoOffset) + { + var currAngle = serieData.context.halfAngle - serie.context.startAngle; + var isLeft = currAngle > 180 || (currAngle == 0 && serieData.context.startAngle > 0); + if (isLeft) + return new Vector3(-offset.x, offset.y, offset.z); + else + return offset; + } + else + { + return offset; + } + } + + public override Vector3 GetSerieDataTitlePosition(SerieData serieData, TitleStyle titleStyle) + { + return serie.context.center; + } + + public override void OnPointerDown(PointerEventData eventData) + { + if (chart.pointerPos == Vector2.zero) return; + var dataIndex = GetPiePosIndex(serie, chart.pointerPos); + var refresh = false; + if (dataIndex >= 0) + { + refresh = true; + for (int j = 0; j < serie.data.Count; j++) + { + if (j == dataIndex) serie.data[j].context.selected = !serie.data[j].context.selected; + else serie.data[j].context.selected = false; + } + } + if (refresh) chart.RefreshChart(); + base.OnPointerDown(eventData); + } + + public override int GetPointerItemDataIndex() + { + return GetPiePosIndex(serie, chart.pointerPos); + } + + public override void UpdateSerieContext() + { + var needCheck = m_LegendEnter || m_LegendExiting || m_ForceUpdateSerieContext || (chart.isPointerInChart && PointerIsInPieSerie(serie, chart.pointerPos)); + var needInteract = false; + var interactEnable = serie.animation.enable && serie.animation.interaction.enable; + Color32 color, toColor; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck || m_ForceUpdateSerieContext) + { + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + bool isAllZeroValue1 = SerieHelper.IsAllZeroValue(serie, 1); + var zeroReplaceValue1 = isAllZeroValue1 ? 360 / serie.dataCount : 0; + foreach (var serieData in serie.data) + { + serieData.context.highlight = false; + if (interactEnable) + { + var value = isAllZeroValue1 ? zeroReplaceValue1 : serieData.GetCurrData(1, serie.animation); + var colorIndex = chart.GetLegendRealShowNameIndex(serieData.legendName); + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, SerieState.Normal); + UpdateSerieDataRadius(serieData, value); + serieData.interact.SetValueAndColor(ref needInteract, serieData.context.outsideRadius, color, toColor); + serieData.interact.SetPosition(ref needInteract, serieData.context.offsetCenter); + } + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + else + { + m_LastCheckContextFlag = needCheck; + m_LegendExiting = false; + chart.RefreshPainter(serie); + } + } + return; + } + m_LastCheckContextFlag = needCheck; + var lastPointerItemDataIndex = serie.context.pointerItemDataIndex; + var dataIndex = GetPiePosIndex(serie, chart.pointerPos); + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = dataIndex >= 0; + + bool isAllZeroValue = SerieHelper.IsAllZeroValue(serie, 1); + var zeroReplaceValue = isAllZeroValue ? 360 / serie.dataCount : 0; + + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var value = isAllZeroValue ? zeroReplaceValue : serieData.GetCurrData(1, serie.animation); + var state = SerieState.Normal; + if (dataIndex == i || (m_LegendEnter && m_LegendEnterIndex == i)) + { + serie.context.pointerItemDataIndex = i; + serieData.context.highlight = true; + state = SerieState.Emphasis; + } + else + { + serieData.context.highlight = false; + } + if (interactEnable) + { + UpdateSerieDataRadius(serieData, value); + var colorIndex = chart.GetLegendRealShowNameIndex(serieData.legendName); + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, state); + serieData.interact.SetValueAndColor(ref needInteract, serieData.context.outsideRadius, color, toColor); + serieData.interact.SetPosition(ref needInteract, serieData.context.offsetCenter); + } + } + if (lastPointerItemDataIndex != serie.context.pointerItemDataIndex) + { + needInteract = true; + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + private void UpdateRuntimeData(Serie serie) + { + var data = serie.data; + serie.context.dataMax = serie.yMax; + serie.context.startAngle = GetStartAngle(serie); + var runtimePieDataTotal = serie.yTotal; + SerieHelper.UpdateCenter(serie, chart); + float startDegree = serie.context.startAngle; + float totalDegree = 0; + float zeroReplaceValue = 0; + int showdataCount = 0; + foreach (var sd in serie.data) + { + if (sd.show && serie.pieRoseType == RoseType.Area) showdataCount++; + sd.context.canShowLabel = false; + } + bool isAllZeroValue = SerieHelper.IsAllZeroValue(serie, 1); + var dataTotalFilterMinAngle = runtimePieDataTotal; + if (isAllZeroValue) + { + totalDegree = 360; + zeroReplaceValue = totalDegree / data.Count; + serie.context.dataMax = zeroReplaceValue; + runtimePieDataTotal = 360; + dataTotalFilterMinAngle = 360; + } + else + { + dataTotalFilterMinAngle = GetTotalAngle(serie, runtimePieDataTotal, ref totalDegree); + } + if (dataTotalFilterMinAngle == 0) + { + dataTotalFilterMinAngle = 360; + } + for (int n = 0; n < data.Count; n++) + { + var serieData = data[n]; + var value = isAllZeroValue ? zeroReplaceValue : serieData.GetCurrData(1, serie.animation); + serieData.context.startAngle = startDegree; + serieData.context.toAngle = startDegree; + serieData.context.halfAngle = startDegree; + serieData.context.currentAngle = startDegree; + if (!serieData.show) + { + continue; + } + float degree = serie.pieRoseType == RoseType.Area ? + (totalDegree / showdataCount) : + (float)(totalDegree * value / dataTotalFilterMinAngle); + if (serie.minAngle > 0 && degree < serie.minAngle) degree = serie.minAngle; + serieData.context.toAngle = startDegree + degree; + var halfDegree = (serieData.context.toAngle - startDegree) / 2; + serieData.context.halfAngle = startDegree + halfDegree; + serieData.context.angle = startDegree + halfDegree; + serieData.context.currentAngle = serie.animation.CheckDetailBreak(serieData.context.toAngle) + ? serie.animation.GetCurrDetail() : serieData.context.toAngle; + serieData.context.insideRadius = serie.context.insideRadius; + serieData.context.canShowLabel = serieData.context.currentAngle >= serieData.context.halfAngle && !serie.IsMinShowLabelValue(value); + UpdateSerieDataRadius(serieData, value); + UpdatePieLabelPosition(serie, serieData); + startDegree = serieData.context.toAngle; + } + AvoidLabelOverlap(serie, chart.theme.common); + } + + private void UpdateSerieDataRadius(SerieData serieData, double value) + { + var minChartWidth = Mathf.Min(chart.chartWidth, chart.chartHeight); + var minRadius = serie.minRadius > 0 ? ChartHelper.GetActualValue(serie.minRadius, minChartWidth) : 0; + if (serieData.radius > 0) + { + serieData.context.outsideRadius = ChartHelper.GetActualValue(serieData.radius, minChartWidth); + } + else + { + var minInsideRadius = minRadius > 0 ? minRadius : serie.context.insideRadius; + serieData.context.outsideRadius = serie.pieRoseType > 0 ? + minInsideRadius + (float)((serie.context.outsideRadius - minInsideRadius) * value / serie.context.dataMax) : + serie.context.outsideRadius; + } + if (minRadius > 0 && serieData.context.outsideRadius < minRadius) + { + serieData.context.outsideRadius = minRadius; + } + var offset = 0f; + var interactOffset = serie.animation.interaction.GetOffset(serie.context.outsideRadius); + if (serie.pieClickOffset && (serieData.selected || serieData.context.selected)) + { + offset += interactOffset; + } + if (offset > 0) + { + serieData.context.outsideRadius += interactOffset; + var currRad = serieData.context.halfAngle * Mathf.Deg2Rad; + var currSin = Mathf.Sin(currRad); + var currCos = Mathf.Cos(currRad); + serieData.context.offsetRadius = 0; + if (serie.pieClickOffset && (serieData.selected || serieData.context.selected)) + { + serieData.context.offsetRadius += interactOffset; + if (serieData.context.insideRadius > 0) + { + serieData.context.insideRadius += interactOffset; + } + } + serieData.context.offsetCenter = new Vector3( + serie.context.center.x + serieData.context.offsetRadius * currSin, + serie.context.center.y + serieData.context.offsetRadius * currCos); + } + else + { + serieData.context.offsetCenter = serie.context.center; + } + if (serieData.context.highlight) + { + serieData.context.outsideRadius = serie.animation.GetInteractionRadius(serieData.context.outsideRadius); + } + var halfRadius = serie.context.insideRadius + (serieData.context.outsideRadius - serie.context.insideRadius) / 2; + serieData.context.position = ChartHelper.GetPosition(serie.context.center, serieData.context.halfAngle, halfRadius); + } + + private double GetTotalAngle(Serie serie, double dataTotal, ref float totalAngle) + { + totalAngle = serie.context.startAngle + 360f; + if (serie.minAngle > 0) + { + var rate = serie.minAngle / 360; + var minAngleValue = dataTotal * rate; + foreach (var serieData in serie.data) + { + var value = serieData.GetData(1); + if (value < minAngleValue) + { + totalAngle -= serie.minAngle; + dataTotal -= value; + } + } + return dataTotal; + } + else + { + return dataTotal; + } + } + + private void DrawPieCenter(VertexHelper vh, Serie serie, ItemStyle itemStyle, float insideRadius) + { + if (!ChartHelper.IsClearColor(itemStyle.centerColor)) + { + var radius = insideRadius - itemStyle.centerGap; + UGL.DrawCricle(vh, serie.context.center, radius, itemStyle.centerColor, chart.settings.cicleSmoothness); + } + } + + private void DrawPie(VertexHelper vh, Pie serie) + { + if (!serie.show || serie.animation.HasFadeOut()) + { + return; + } + var dataChanging = false; + var interacting = false; + var color = ColorUtil.clearColor32; + var toColor = ColorUtil.clearColor32; + var interactDuration = serie.animation.GetInteractionDuration(); + var interactEnable = serie.animation.enable && serie.animation.interaction.enable + && !serie.animation.IsFadeIn() && !serie.animation.IsFadeOut(); + var data = serie.data; + serie.animation.InitProgress(0, 360); + if (data.Count == 0) + { + var itemStyle = SerieHelper.GetItemStyle(serie, null); + var fillColor = ChartHelper.IsClearColor(itemStyle.backgroundColor) ? + (Color32)chart.theme.legend.unableColor : itemStyle.backgroundColor; + UGL.DrawDoughnut(vh, serie.context.center, serie.context.insideRadius, + serie.context.outsideRadius, fillColor, fillColor, Color.clear, 0, + 360, itemStyle.borderWidth, itemStyle.borderColor, serie.gap / 2, chart.settings.cicleSmoothness, + false, true, serie.radiusGradient); + } + for (int n = 0; n < data.Count; n++) + { + var serieData = data[n]; + if (!serieData.show) + { + continue; + } + if (serieData.IsDataChanged()) + dataChanging = true; + + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + var colorIndex = chart.GetLegendRealShowNameIndex(serieData.legendName); + var outsideRadius = 0f; + + var needOffset = (serie.pieClickOffset && (serieData.selected || serieData.context.selected)); + var offsetCenter = needOffset ? serieData.context.offsetCenter : serie.context.center; + + var borderWidth = itemStyle.borderWidth; + var borderColor = itemStyle.borderColor; + + var progress = AnimationStyleHelper.CheckDataAnimation(chart, serie, n, 1); + var insideRadius = serieData.context.insideRadius * progress; + + if (!interactEnable || !serieData.interact.TryGetValueAndColor( + ref outsideRadius, ref offsetCenter, ref color, ref toColor, ref interacting, interactDuration)) + { + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex); + outsideRadius = serieData.context.outsideRadius * progress; + if (interactEnable) + { + serieData.interact.SetValueAndColor(ref interacting, outsideRadius, color, toColor); + serieData.interact.SetPosition(ref interacting, offsetCenter); + } + } + var drawEndDegree = serieData.context.currentAngle; + var needRoundCap = serie.roundCap && insideRadius > 0; + UGL.DrawDoughnut(vh, offsetCenter, insideRadius, + outsideRadius, color, toColor, Color.clear, serieData.context.startAngle, + drawEndDegree, borderWidth, borderColor, serie.gap / 2, chart.settings.cicleSmoothness, + needRoundCap, true, serie.radiusGradient); + DrawPieCenter(vh, serie, itemStyle, insideRadius); + + if (serie.animation.CheckDetailBreak(serieData.context.toAngle)) + break; + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(); + serie.animation.CheckSymbol(serie.symbol.GetSize(null, chart.theme.serie.lineSymbolSize)); + chart.RefreshPainter(serie); + } + if (dataChanging || interacting) + { + chart.RefreshPainter(serie); + } + } + + private static void UpdatePieLabelPosition(Serie serie, SerieData serieData) + { + if (serieData.labelObject == null) return; + var startAngle = serie.context.startAngle; + var currAngle = serieData.context.halfAngle; + var currRad = currAngle * Mathf.Deg2Rad; + var offsetRadius = serieData.context.offsetRadius; + var insideRadius = serieData.context.insideRadius; + var outsideRadius = serieData.context.outsideRadius; + var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + var labelLine = SerieHelper.GetSerieLabelLine(serie, serieData); + var center = serieData.context.offsetCenter; + var interact = false; + serieData.interact.TryGetValueAndColor(ref outsideRadius, ref center, ref interact, serie.animation.GetInteractionDuration()); + var diffAngle = (currAngle - startAngle) % 360; + var isLeft = diffAngle > 180 || (diffAngle == 0 && serieData.context.startAngle > 0); + switch (serieLabel.position) + { + case LabelStyle.Position.Center: + serieData.context.labelPosition = serie.context.center; + break; + case LabelStyle.Position.Inside: + case LabelStyle.Position.Middle: + var labelRadius = offsetRadius + insideRadius + (outsideRadius - insideRadius) / 2 + serieLabel.distance; + var labelCenter = new Vector2(center.x + labelRadius * Mathf.Sin(currRad), + center.y + labelRadius * Mathf.Cos(currRad)); + UpdateLabelPosition(serie, serieData, labelLine, labelCenter, isLeft); + break; + default: + //LabelStyle.Position.Outside + var startPos = new Vector2(center.x + outsideRadius * Mathf.Sin(currRad), + center.y + outsideRadius * Mathf.Cos(currRad)); + UpdateLabelPosition(serie, serieData, labelLine, startPos, isLeft); + break; + } + } + + private static void UpdateLabelPosition(Serie serie, SerieData serieData, LabelLine labelLine, Vector3 startPosition, bool isLeft) + { + serieData.context.labelLinePosition = startPosition; + if (labelLine == null || !labelLine.show) + { + serieData.context.labelPosition = startPosition; + return; + } + var dire = isLeft ? Vector3.left : Vector3.right; + var rad = Mathf.Deg2Rad * serieData.context.halfAngle; + var lineLength1 = ChartHelper.GetActualValue(labelLine.lineLength1, serie.context.outsideRadius); + var lineLength2 = ChartHelper.GetActualValue(labelLine.lineLength2, serie.context.outsideRadius); + var radius = lineLength1; + var pos1 = startPosition; + var pos2 = pos1 + new Vector3(Mathf.Sin(rad) * radius, Mathf.Cos(rad) * radius); + var pos5 = labelLine.lineType == LabelLine.LineType.HorizontalLine + ? pos1 + dire * (radius + lineLength2) + labelLine.GetEndSymbolOffset() + : pos2 + dire * lineLength2 + labelLine.GetEndSymbolOffset(); + if (labelLine.lineEndX != 0) + { + pos5.x = serie.context.center.x + (isLeft ? -Mathf.Abs(labelLine.lineEndX) : Mathf.Abs(labelLine.lineEndX)); + } + serieData.context.labelLinePosition2 = pos2; + serieData.context.labelPosition = pos5; + } + + private void DrawPieLabelLine(VertexHelper vh, Serie serie, bool isTop) + { + foreach (var serieData in serie.data) + { + var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + var labelLine = SerieHelper.GetSerieLabelLine(serie, serieData); + if (SerieLabelHelper.CanShowLabel(serie, serieData, serieLabel, 1)) + { + int colorIndex = chart.m_LegendRealShowName.IndexOf(serieData.name); + if (serieLabel != null && serieLabel.show && + labelLine != null && labelLine.show) + { + if (serieLabel.position == LabelStyle.Position.Inside || serieLabel.position == LabelStyle.Position.Middle) + { + if (!isTop) continue; + } + else + { + if (isTop && !labelLine.startSymbol.show) continue; + } + var color = ChartHelper.IsClearColor(labelLine.lineColor) ? + chart.theme.GetColor(colorIndex) : + labelLine.lineColor; + switch (labelLine.lineType) + { + case LabelLine.LineType.BrokenLine: + UGL.DrawLine(vh, serieData.context.labelLinePosition, serieData.context.labelLinePosition2, + serieData.context.labelPosition, labelLine.lineWidth, color); + break; + case LabelLine.LineType.Curves: + if (serieData.context.labelLinePosition2 == serieData.context.labelPosition) + { + UGL.DrawCurves(vh, serieData.context.labelLinePosition, serieData.context.labelPosition, + serieData.context.labelLinePosition, (serieData.context.labelLinePosition + serieData.context.labelPosition) * 0.6f, + labelLine.lineWidth, color, chart.settings.lineSmoothness); + } + else + { + UGL.DrawCurves(vh, serieData.context.labelLinePosition, serieData.context.labelPosition, + serieData.context.labelLinePosition, serieData.context.labelLinePosition2, + labelLine.lineWidth, color, chart.settings.lineSmoothness); + } + break; + case LabelLine.LineType.HorizontalLine: + UGL.DrawLine(vh, serieData.context.labelLinePosition, serieData.context.labelPosition, + labelLine.lineWidth, color); + break; + } + DrawLabelLineSymbol(vh, labelLine, serieData.context.labelLinePosition, serieData.context.labelPosition, color); + } + } + } + } + + private int GetPiePosIndex(Serie serie, Vector2 local) + { + if (!(serie is Pie)) + return -1; + + var dist = Vector2.Distance(local, serie.context.center); + var interactOffset = serie.animation.interaction.GetOffset(serie.context.outsideRadius); + var maxRadius = serie.context.outsideRadius + 2 * interactOffset; + if (dist < serie.context.insideRadius || dist > maxRadius) + return -1; + + var dir = local - new Vector2(serie.context.center.x, serie.context.center.y); + var angle = ChartHelper.GetAngle360(Vector2.up, dir); + for (int i = 0; i < serie.data.Count; i++) + { + var serieData = serie.data[i]; + if (angle >= serieData.context.startAngle && angle <= serieData.context.toAngle) + { + var ndist = (serieData.selected || serieData.context.selected) ? + Vector2.Distance(local, serieData.context.offsetCenter) : + dist; + if (ndist >= serieData.context.insideRadius && ndist <= serieData.context.outsideRadius) + { + return i; + } + } + } + return -1; + } + + private bool PointerIsInPieSerie(Serie serie, Vector2 local) + { + if (!(serie is Pie)) + return false; + + var dist = Vector2.Distance(local, serie.context.center); + if (dist >= serie.context.insideRadius && dist <= serie.context.outsideRadius) + return true; + + return false; + } + + private float GetStartAngle(Serie serie) + { + return serie.clockwise ? (serie.startAngle + 360) % 360 : 360 - serie.startAngle; + } + + private float GetToAngle(Serie serie, float angle) + { + var toAngle = angle + serie.startAngle; + if (!serie.clockwise) + { + toAngle = 360 - angle - serie.startAngle; + } + if (!serie.animation.IsFinish()) + { + var currAngle = serie.animation.GetCurrDetail(); + if (serie.clockwise) + { + toAngle = toAngle > currAngle ? currAngle : toAngle; + } + else + { + toAngle = toAngle < 360 - currAngle ? 360 - currAngle : toAngle; + } + } + return toAngle; + } + + private void AvoidLabelOverlap(Serie serie, ComponentTheme theme) + { + if (!serie.avoidLabelOverlap) return; + var lastCheckPos = Vector3.zero; + var lastX = 0f; + var data = serie.data; + var splitCount = 0; + for (int n = 0; n < data.Count; n++) + { + var serieData = data[n]; + if (serieData.context.labelPosition.x != 0 && serieData.context.labelPosition.x < serie.context.center.x) + { + splitCount = n; + break; + } + } + var limitX = float.MinValue; + for (int n = 0; n < splitCount; n++) + { + CheckSerieDataLabel(serie, data[n], splitCount, false, n == splitCount - 1, theme, ref lastCheckPos, ref lastX, ref limitX); + } + lastCheckPos = Vector3.zero; + limitX = float.MaxValue; + for (int n = data.Count - 1; n >= splitCount; n--) + { + CheckSerieDataLabel(serie, data[n], data.Count - splitCount, true, n == splitCount, theme, ref lastCheckPos, ref lastX, ref limitX); + } + } + + private void CheckSerieDataLabel(Serie serie, SerieData serieData, int total, bool isLeft, bool isLastOne, ComponentTheme theme, + ref Vector3 lastCheckPos, ref float lastX, ref float limitX) + { + if (!serieData.context.canShowLabel) + { + serieData.SetLabelActive(false); + return; + } + if (!serieData.show) return; + var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + if (serieLabel == null) return; + if (!serieLabel.show) return; + var labelLine = SerieHelper.GetSerieLabelLine(serie, serieData); + var fontSize = serieData.labelObject.GetHeight(); + var lineLength1 = 0f; + var lineLength2 = 0f; + if (labelLine != null && labelLine.show) + { + lineLength1 = ChartHelper.GetActualValue(labelLine.lineLength1, serie.context.outsideRadius); + lineLength2 = ChartHelper.GetActualValue(labelLine.lineLength2, serie.context.outsideRadius); + } + if (lastCheckPos == Vector3.zero) + { + lastCheckPos = serieData.context.labelPosition; + } + else if (serieData.context.labelPosition.x != 0) + { + if (lastCheckPos.y - serieData.context.labelPosition.y < fontSize) + { + var labelRadius = serie.context.outsideRadius + lineLength1; + var y1 = lastCheckPos.y - fontSize; + var cy = serie.context.center.y; + var diff = Mathf.Abs(y1 - cy); + var diffX = labelRadius * labelRadius - diff * diff; + diffX = diffX <= 0 ? 0 : diffX; + var x1 = serie.context.center.x + Mathf.Sqrt(diffX) * (isLeft ? -1 : 1); + var newPos = new Vector3(x1, y1); + serieData.context.labelLinePosition2 = newPos; + if (isLeft) + { + if (x1 < limitX) + { + limitX = x1; + serieData.context.labelPosition = new Vector3(newPos.x - lineLength2, newPos.y); + lastX = serieData.context.labelPosition.x; + } + else + { + serieData.context.labelPosition = new Vector3(lastX, y1); + lastX += 2; + } + } + else + { + if (x1 > limitX) + { + limitX = x1; + serieData.context.labelPosition = new Vector3(newPos.x + lineLength2, newPos.y); + lastX = serieData.context.labelPosition.x; + } + else + { + serieData.context.labelPosition = new Vector3(lastX, y1); + lastX -= 2; + } + + } + if (labelLine != null && labelLine.show && labelLine.lineEndX != 0) + { + serieData.context.labelPosition.x = isLeft ? -Mathf.Abs(labelLine.lineEndX) : Mathf.Abs(labelLine.lineEndX); + } + if (!isLastOne && serieData.context.labelPosition.y < serieData.context.labelLinePosition.y) + { + serieData.context.labelLinePosition2 = serieData.context.labelPosition; + } + else + { + if (isLeft && serieData.context.labelLinePosition2.x > serieData.context.labelLinePosition.x) + { + serieData.context.labelLinePosition2.x = serieData.context.labelLinePosition.x; + } + else if (!isLeft && serieData.context.labelLinePosition2.x < serieData.context.labelLinePosition.x) + { + serieData.context.labelLinePosition2.x = serieData.context.labelLinePosition.x; + } + } + + } + else + { + lastX = serieData.context.labelPosition.x; + } + lastCheckPos = serieData.context.labelPosition; + UpdateLabelPosition(serieData, serieLabel); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs.meta new file mode 100644 index 0000000..3c1fddb --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Pie/PieHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13b8c981e5910447fbe769d539b77f96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Radar.meta b/Assets/XCharts/Runtime/Serie/Radar.meta new file mode 100644 index 0000000..15c0c0c --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Radar.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 375cebcd4f8c54025ab608f35b1fa8c6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Radar/Radar.cs b/Assets/XCharts/Runtime/Serie/Radar/Radar.cs new file mode 100644 index 0000000..4ca21d0 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Radar/Radar.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(RadarHandler), true)] + [RequireChartComponent(typeof(RadarCoord))] + [SerieComponent(typeof(LabelStyle), typeof(AreaStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(AreaStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField()] + public class Radar : Serie, INeedSerieContainer + { + [SerializeField][Since("v3.2.0")] private bool m_Smooth = false; + + /// <summary> + /// Whether use smooth curve. + /// ||是否平滑曲线。平滑曲线时不支持区域填充颜色。 + /// </summary> + public bool smooth + { + get { return m_Smooth; } + set { if (PropertyUtil.SetStruct(ref m_Smooth, value)) { SetVerticesDirty(); } } + } + + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + public override SerieColorBy defaultColorBy { get { return radarType == RadarType.Multiple?SerieColorBy.Data : SerieColorBy.Serie; } } + public override bool multiDimensionLabel { get { return radarType == RadarType.Multiple; } } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + chart.EnsureChartComponent<RadarCoord>(); + var serie = chart.AddSerie<Radar>(serieName); + serie.symbol.show = true; + serie.symbol.type = SymbolType.Circle; + serie.showDataName = true; + List<double> data = new List<double>(); + for (int i = 0; i < 5; i++) + { + data.Add(Random.Range(20, 90)); + } + chart.AddData(serie.index, data, "legendName"); + return serie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Radar/Radar.cs.meta b/Assets/XCharts/Runtime/Serie/Radar/Radar.cs.meta new file mode 100644 index 0000000..1e2b8e5 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Radar/Radar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0bda9968e18724e389ff1cbde57baeee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs b/Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs new file mode 100644 index 0000000..3a7f232 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs @@ -0,0 +1,561 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class RadarHandler : SerieHandler<Radar> + { + private RadarCoord m_RadarCoord; + public override void Update() + { + base.Update(); + } + + public override void DrawSerie(VertexHelper vh) + { + if (!serie.show) return; + switch (serie.radarType) + { + case RadarType.Multiple: + DrawMutipleRadar(vh); + break; + case RadarType.Single: + DrawSingleRadar(vh); + break; + } + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + if (!serie.context.pointerEnter) + return; + dataIndex = serie.context.pointerItemDataIndex; + if (dataIndex < 0) + return; + + var radar = chart.GetChartComponent<RadarCoord>(serie.radarIndex); + if (radar == null) + return; + + if (serie.radarType == RadarType.Single) + { + var colorIndex1 = serie.colorByData ? dataIndex : serie.context.colorIndex; + category = radar.GetIndicatorName(dataIndex); + UpdateItemSerieParams(ref paramList, ref title, dataIndex, category, + marker, itemFormatter, numericFormatter, ignoreDataDefaultContent, 1, colorIndex1); + return; + } + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + + Color32 color, toColor; + var colorIndex = serie.colorByData ? chart.GetLegendRealShowNameIndex(serieData.legendName) : serie.context.colorIndex; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, SerieState.Normal); + title = serieData.name; + + itemFormatter = SerieHelper.GetItemFormatter(serie, null, itemFormatter); + numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + marker = SerieHelper.GetItemMarker(serie, serieData, marker); + if (string.IsNullOrEmpty(itemFormatter)) + { + for (int i = 0; i < serieData.data.Count; i++) + { + var indicator = radar.GetIndicator(i); + if (indicator == null) continue; + + var param = new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = i; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(i); + param.total = indicator.max; + param.color = color; + param.category = radar.GetIndicatorName(i); + param.marker = marker; + param.itemFormatter = itemFormatter; + param.numericFormatter = numericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(indicator.name); + param.columns.Add(ChartCached.NumberToStr(serieData.GetData(i), param.numericFormatter)); + + paramList.Add(param); + } + } + else + { + itemFormatter = itemFormatter.Replace("\\n", "\n"); + var temp = itemFormatter.Split('\n'); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = i; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(i); + param.total = serie.yTotal; + param.color = color; + param.category = radar.GetIndicatorName(i); + param.marker = marker; + param.itemFormatter = formatter; + param.numericFormatter = numericFormatter; + param.columns.Clear(); + + paramList.Add(param); + } + } + } + + public override void UpdateSerieContext() + { + var needCheck = m_LegendEnter || + (chart.isPointerInChart && (m_RadarCoord != null && m_RadarCoord.IsPointerEnter())); + var needInteract = false; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerItemDataDimension = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + serieData.context.highlight = false; + serieData.interact.Reset(); + } + chart.RefreshPainter(serie); + } + return; + } + m_LastCheckContextFlag = needCheck; + serie.highlight = false; + serie.context.pointerEnter = false; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerItemDataDimension = -1; + var areaStyle = serie.areaStyle; + var themeSymbolSize = chart.theme.serie.lineSymbolSize; + switch (serie.radarType) + { + case RadarType.Multiple: + for (int i = 0; i < serie.data.Count; i++) + { + var serieData = serie.data[i]; + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + var symbolSize = symbol.GetSize(serieData, chart.theme.serie.lineSymbolSize); + if (m_LegendEnter) + { + serieData.context.highlight = true; + serieData.interact.SetValue(ref needInteract, serie.animation.interaction.GetRadius(symbolSize)); + } + else + { + serieData.context.highlight = false; + for (int n = 0; n < serieData.context.dataPoints.Count; n++) + { + var pos = serieData.context.dataPoints[n]; + if (Vector3.Distance(chart.pointerPos, pos) < symbolSize * 2) + { + serie.highlight = true; + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = i; + serie.context.pointerItemDataDimension = n; + serieData.context.highlight = true; + break; + } + } + if (!serieData.context.highlight && areaStyle != null) + { + var center = m_RadarCoord.context.center; + var dataPoints = serieData.context.dataPoints; + for (int n = 0; n < dataPoints.Count; n++) + { + var p1 = dataPoints[n]; + var p2 = n >= dataPoints.Count - 1 ? dataPoints[0] : dataPoints[n + 1]; + if (UGLHelper.IsPointInTriangle(p1, center, p2, chart.pointerPos)) + { + serie.highlight = true; + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = i; + serie.context.pointerItemDataDimension = n; + serieData.context.highlight = true; + break; + } + } + } + if (serieData.context.highlight) + serieData.interact.SetValue(ref needInteract, serie.animation.interaction.GetRadius(symbolSize)); + else + serieData.interact.SetValue(ref needInteract, symbolSize); + } + } + break; + case RadarType.Single: + needInteract = false; + for (int i = 0; i < serie.data.Count; i++) + { + var serieData = serie.data[i]; + var size = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize); + if (Vector3.Distance(chart.pointerPos, serieData.context.position) < size * 2) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = i; + serie.context.pointerItemDataDimension = 1; + serieData.context.highlight = true; + needInteract = true; + } + else + { + serieData.context.highlight = false; + } + } + if (!serie.context.pointerEnter && areaStyle != null) + { + var center = m_RadarCoord.context.center; + var dataPoints = serie.data; + for (int n = 0; n < dataPoints.Count; n++) + { + var p1 = dataPoints[n]; + var p2 = n >= dataPoints.Count - 1 ? dataPoints[0] : dataPoints[n + 1]; + if (UGLHelper.IsPointInTriangle(p1.context.position, center, p2.context.position, chart.pointerPos)) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = n; + serie.context.pointerItemDataDimension = 1; + p1.context.highlight = true; + needInteract = true; + break; + } + } + } + break; + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + private void DrawMutipleRadar(VertexHelper vh) + { + if (!serie.show) return; + m_RadarCoord = chart.GetChartComponent<RadarCoord>(serie.radarIndex); + if (m_RadarCoord == null) return; + + serie.containerIndex = m_RadarCoord.index; + serie.containterInstanceId = m_RadarCoord.instanceId; + + var startPoint = Vector3.zero; + var toPoint = Vector3.zero; + var firstPoint = Vector3.zero; + var indicatorNum = m_RadarCoord.indicatorList.Count; + var angle = 2 * Mathf.PI / indicatorNum; + var centerPos = m_RadarCoord.context.center; + serie.animation.InitProgress(0, 1); + if (!serie.show || serie.animation.HasFadeOut()) + { + return; + } + var rate = serie.animation.GetCurrRate(); + var dataChanging = false; + var interacting = false; + SerieHelper.GetAllMinMaxData(serie, m_RadarCoord.ceilRate); + Color32 areaColor, areaToColor; + var startAngle = m_RadarCoord.startAngle * Mathf.PI / 180; + var interactDuration = serie.animation.GetInteractionDuration(); + for (int j = 0; j < serie.data.Count; j++) + { + var serieData = serie.data[j]; + string dataName = serieData.name; + if (!serieData.show) + { + continue; + } + var serieState = SerieHelper.GetSerieState(serie, serieData, true); + var lineStyle = SerieHelper.GetLineStyle(serie, serieData); + var symbol = SerieHelper.GetSerieSymbol(serie, serieData, serieState); + + var colorIndex = serie.colorByData ? chart.GetLegendRealShowNameIndex(serieData.legendName) : serie.context.colorIndex; + var showArea = SerieHelper.GetAreaColor(out areaColor, out areaToColor, serie, serieData, chart.theme, colorIndex); + var lineColor = SerieHelper.GetLineColor(serie, serieData, chart.theme, colorIndex); + var lineWidth = lineStyle.GetWidth(chart.theme.serie.lineWidth); + int dataCount = m_RadarCoord.indicatorList.Count; + serieData.context.dataPoints.Clear(); + for (int n = 0; n < dataCount; n++) + { + if (n >= serieData.data.Count) break; + var min = m_RadarCoord.GetIndicatorMin(n); + var max = m_RadarCoord.GetIndicatorMax(n); + var value = serieData.GetCurrData(n, serie.animation); + if (serieData.IsDataChanged()) dataChanging = true; + if (max == 0) + { + if (serie.data.Count > 1) + { + SerieHelper.GetMinMaxData(serie, n, out min, out max); + min = ChartHelper.GetMinDivisibleValue(min, 0); + max = ChartHelper.GetMaxDivisibleValue(max, 0); + if (min > 0) min = 0; + } + else + { + max = serie.context.dataMax; + } + } + if (max - min == 0) continue; + var radius = (float)(m_RadarCoord.context.dataRadius * (value - min) / (max - min)); + var currAngle = startAngle + (n + (m_RadarCoord.positionType == RadarCoord.PositionType.Between ? 0.5f : 0)) * angle; + radius *= rate; + if (n == 0) + { + startPoint = new Vector3(centerPos.x + radius * Mathf.Sin(currAngle), + centerPos.y + radius * Mathf.Cos(currAngle)); + firstPoint = startPoint; + } + else + { + toPoint = new Vector3(centerPos.x + radius * Mathf.Sin(currAngle), + centerPos.y + radius * Mathf.Cos(currAngle)); + if (showArea && !serie.smooth) + { + UGL.DrawTriangle(vh, startPoint, toPoint, centerPos, areaColor, areaColor, areaToColor); + } + if (lineStyle.show && !serie.smooth) + { + ChartDrawer.DrawLineStyle(vh, lineStyle.type, lineWidth, startPoint, toPoint, lineColor); + } + startPoint = toPoint; + } + serieData.context.dataPoints.Add(startPoint); + } + if (showArea && !serie.smooth) + { + UGL.DrawTriangle(vh, startPoint, firstPoint, centerPos, areaColor, areaColor, areaToColor); + } + if (lineStyle.show && !serie.smooth) + { + ChartDrawer.DrawLineStyle(vh, lineStyle.type, lineWidth, startPoint, firstPoint, lineColor); + } + + if (serie.smooth) + { + UGL.DrawCurves(vh, serieData.context.dataPoints, lineWidth, lineColor, + chart.settings.lineSmoothStyle, + chart.settings.lineSmoothness, + UGL.Direction.Random, + float.NaN, true); + } + + if (symbol.show && symbol.type != SymbolType.None) + { + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 symbolColor, symbolToColor, symbolEmptyColor, borderColor; + for (int m = 0; m < serieData.context.dataPoints.Count; m++) + { + var point = serieData.context.dataPoints[m]; + var symbolSize = 0f; + if (!serieData.interact.TryGetValue(ref symbolSize, ref interacting, interactDuration)) + { + symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme.serie.lineSymbolSize, serieState); + serieData.interact.SetValue(ref interacting, symbolSize); + symbolSize = serie.animation.GetSysmbolSize(symbolSize); + } + SerieHelper.GetItemColor(out symbolColor, out symbolToColor, out symbolEmptyColor, serie, serieData, chart.theme, colorIndex, serieState); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, serieData, chart.theme, serieState); + chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, point, symbolColor, + symbolToColor, symbolEmptyColor, borderColor, symbol.gap, cornerRadius, symbol.size2); + } + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(1); + chart.RefreshPainter(serie); + } + if (dataChanging || interacting) + { + chart.RefreshPainter(serie); + } + } + + private void DrawSingleRadar(VertexHelper vh) + { + m_RadarCoord = chart.GetChartComponent<RadarCoord>(serie.radarIndex); + if (m_RadarCoord == null) + return; + + var indicatorNum = m_RadarCoord.indicatorList.Count; + var angle = 2 * Mathf.PI / indicatorNum; + var centerPos = m_RadarCoord.context.center; + serie.animation.InitProgress(0, 1); + if (!serie.show || serie.animation.HasFadeOut()) + { + return; + } + var startPoint = Vector3.zero; + var toPoint = Vector3.zero; + var firstPoint = Vector3.zero; + var lastColor = ColorUtil.clearColor32; + var firstColor = ColorUtil.clearColor32; + + var rate = serie.animation.GetCurrRate(); + var dataChanging = false; + var startIndex = GetStartShowIndex(serie); + var endIndex = GetEndShowIndex(serie); + var startAngle = m_RadarCoord.startAngle * Mathf.PI / 180; + SerieHelper.UpdateMinMaxData(serie, 1, m_RadarCoord.ceilRate); + for (int j = 0; j < serie.data.Count; j++) + { + var serieData = serie.data[j]; + string dataName = serieData.name; + + if (!serieData.show) + { + serieData.context.labelPosition = Vector3.zero; + continue; + } + var lineStyle = SerieHelper.GetLineStyle(serie, serieData); + Color32 areaColor, areaToColor; + var colorIndex = serie.colorByData ? j : serie.context.colorIndex; + var showArea = SerieHelper.GetAreaColor(out areaColor, out areaToColor, serie, serieData, chart.theme, colorIndex - 1); + var lineColor = SerieHelper.GetLineColor(serie, serieData, chart.theme, colorIndex); + int dataCount = m_RadarCoord.indicatorList.Count; + var index = serieData.index; + var p = m_RadarCoord.context.center; + var max = m_RadarCoord.GetIndicatorMax(index); + var value = serieData.GetCurrData(1, serie.animation); + if (serieData.IsDataChanged()) dataChanging = true; + if (max == 0) + { + max = serie.context.dataMax; + } + if (!m_RadarCoord.IsInIndicatorRange(j, serieData.GetData(1))) + { + lineColor = m_RadarCoord.outRangeColor; + } + var radius = (float)(max < 0 ? m_RadarCoord.context.dataRadius - m_RadarCoord.context.dataRadius * value / max : + m_RadarCoord.context.dataRadius * value / max); + var currAngle = startAngle + (index + (m_RadarCoord.positionType == RadarCoord.PositionType.Between ? 0.5f : 0)) * angle; + radius *= rate; + if (index == startIndex) + { + startPoint = new Vector3(p.x + radius * Mathf.Sin(currAngle), + p.y + radius * Mathf.Cos(currAngle)); + firstPoint = startPoint; + lastColor = lineColor; + firstColor = lineColor; + } + else + { + toPoint = new Vector3(p.x + radius * Mathf.Sin(currAngle), + p.y + radius * Mathf.Cos(currAngle)); + if (showArea && !serie.smooth) + { + UGL.DrawTriangle(vh, startPoint, toPoint, p, areaColor, areaColor, areaToColor); + } + if (lineStyle.show && !serie.smooth) + { + if (m_RadarCoord.connectCenter) + ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, centerPos, + chart.theme.serie.lineWidth, LineStyle.Type.Solid, lastColor, lastColor); + ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, toPoint, chart.theme.serie.lineWidth, + LineStyle.Type.Solid, m_RadarCoord.lineGradient ? lastColor : lineColor, lineColor); + } + startPoint = toPoint; + lastColor = lineColor; + } + serie.context.dataPoints.Add(startPoint); + serie.context.dataIndexs.Add(serieData.index); + serieData.context.position = startPoint; + serieData.context.labelPosition = startPoint; + + if (showArea && j == endIndex && !serie.smooth) + { + SerieHelper.GetAreaColor(out areaColor, out areaToColor, serie, serieData, chart.theme, colorIndex); + UGL.DrawTriangle(vh, startPoint, firstPoint, centerPos, areaColor, areaColor, areaToColor); + } + if (lineStyle.show && j == endIndex && !serie.smooth) + { + if (m_RadarCoord.connectCenter) + ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, centerPos, + chart.theme.serie.lineWidth, LineStyle.Type.Solid, lastColor, lastColor); + ChartDrawer.DrawLineStyle(vh, lineStyle, startPoint, firstPoint, chart.theme.serie.lineWidth, + LineStyle.Type.Solid, lineColor, m_RadarCoord.lineGradient ? firstColor : lineColor); + } + } + if (serie.smooth) + { + var lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth); + var lineColor = SerieHelper.GetLineColor(serie, null, chart.theme, serie.context.colorIndex); + UGL.DrawCurves(vh, serie.context.dataPoints, lineWidth, lineColor, + chart.settings.lineSmoothStyle, + chart.settings.lineSmoothness, + UGL.Direction.Random, + float.NaN, true); + } + if (serie.symbol.show && serie.symbol.type != SymbolType.None) + { + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 symbolColor, symbolToColor, symbolEmptyColor, borderColor; + for (int j = 0; j < serie.data.Count; j++) + { + var serieData = serie.data[j]; + if (!serieData.show) continue; + var state = SerieHelper.GetSerieState(serie, serieData); + var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme.serie.lineSymbolSize, state); + var colorIndex = serie.colorByData ? serieData.index : serie.context.colorIndex; + SerieHelper.GetItemColor(out symbolColor, out symbolToColor, out symbolEmptyColor, serie, serieData, chart.theme, colorIndex, state); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, serieData, chart.theme, state); + if (!m_RadarCoord.IsInIndicatorRange(j, serieData.GetData(1))) + { + symbolColor = m_RadarCoord.outRangeColor; + symbolToColor = m_RadarCoord.outRangeColor; + } + chart.DrawSymbol(vh, serie.symbol.type, symbolSize, symbolBorder, serieData.context.labelPosition, symbolColor, + symbolToColor, symbolEmptyColor, borderColor, serie.symbol.gap, cornerRadius, serie.symbol.size2); + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(1); + chart.RefreshPainter(serie); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } + + private int GetStartShowIndex(Serie serie) + { + for (int i = 0; i < serie.dataCount; i++) + { + if (serie.data[i].show) return i; + } + return 0; + } + private int GetEndShowIndex(Serie serie) + { + for (int i = serie.dataCount - 1; i >= 0; i--) + { + if (serie.data[i].show) return i; + } + return 0; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs.meta new file mode 100644 index 0000000..5a455a2 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Radar/RadarHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8dd73709db7a4451b2fe6f03476cb7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Ring.meta b/Assets/XCharts/Runtime/Serie/Ring.meta new file mode 100644 index 0000000..98724f6 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Ring.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c15a1b60f1d3c4daca1fcd88d96ae7e7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Ring/Ring.cs b/Assets/XCharts/Runtime/Serie/Ring/Ring.cs new file mode 100644 index 0000000..368e187 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Ring/Ring.cs @@ -0,0 +1,57 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(RingHandler), true)] + [SerieComponent(typeof(LabelStyle), typeof(LabelLine), typeof(TitleStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(LabelLine), typeof(TitleStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField()] + public class Ring : Serie + { + [SerializeField][Since("v3.12.0")] private bool m_RadiusGradient = false; + + /// <summary> + /// Whether to use gradient color in pie chart. + /// || 是否开启半径方向的渐变效果。 + /// </summary> + public bool radiusGradient + { + get { return m_RadiusGradient; } + set { if (PropertyUtil.SetStruct(ref m_RadiusGradient, value)) { SetVerticesDirty(); } } + } + + public override SerieColorBy defaultColorBy { get { return SerieColorBy.Data; } } + + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Ring>(serieName); + serie.roundCap = true; + serie.gap = 10; + serie.radius = new float[] { 0.3f, 0.35f }; + + var label = serie.EnsureComponent<LabelStyle>(); + label.show = true; + label.position = LabelStyle.Position.Center; + label.formatter = "{d:f0}%"; + label.textStyle.autoColor = true; + label.textStyle.fontSize = 28; + + var titleStyle = serie.EnsureComponent<TitleStyle>(); + titleStyle.show = false; + titleStyle.offset = new Vector2(0, 30); + + var value = Random.Range(30, 90); + var max = 100; + chart.AddData(serie.index, value, max, "data1"); + return serie; + } + + public override double GetDataTotal(int dimension, SerieData serieData = null) + { + if (serieData == null || serieData.data.Count <= 1) + return base.GetDataTotal(dimension, serieData); + return serieData.GetData(1); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Ring/Ring.cs.meta b/Assets/XCharts/Runtime/Serie/Ring/Ring.cs.meta new file mode 100644 index 0000000..c069450 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Ring/Ring.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c7fe8316f26241d8a9f4b3ce94d61bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs b/Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs new file mode 100644 index 0000000..0ca6ffc --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs @@ -0,0 +1,485 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using XUGL; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class RingHandler : SerieHandler<Ring> + { + public override int defaultDimension { get { return 0; } } + + public override void Update() + { + base.Update(); + } + + public override void UpdateSerieContext() + { + var needCheck = chart.isPointerInChart || m_LegendEnter; + var needInteract = false; + if (!needCheck) + { + if (m_LastCheckContextFlag != needCheck) + { + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + foreach (var serieData in serie.data) + { + serieData.context.highlight = false; + } + chart.RefreshPainter(serie); + } + return; + } + m_LastCheckContextFlag = needCheck; + if (m_LegendEnter) + { + serie.context.pointerEnter = true; + foreach (var serieData in serie.data) + { + serieData.context.highlight = true; + } + } + else + { + serie.context.pointerEnter = false; + serie.context.pointerItemDataIndex = -1; + var ringIndex = GetRingIndex(chart.pointerPos); + foreach (var serieData in serie.data) + { + if (!needInteract && ringIndex == serieData.index) + { + serie.context.pointerEnter = true; + serie.context.pointerItemDataIndex = ringIndex; + serieData.context.highlight = true; + needInteract = true; + } + else + { + serieData.context.highlight = false; + } + } + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + if (dataIndex < 0) + dataIndex = serie.context.pointerItemDataIndex; + + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, dataIndex); + itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + marker = SerieHelper.GetItemMarker(serie, serieData, marker); + + if (itemFormatter == null) itemFormatter = ""; + itemFormatter = itemFormatter.Replace("\\n", "\n"); + var temp = itemFormatter.Split('\n'); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.dimension = defaultDimension; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(0); + param.total = serieData.GetData(1); + param.color = color; + param.marker = marker; + param.itemFormatter = formatter; + param.numericFormatter = numericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(serieData.name); + param.columns.Add(ChartCached.NumberToStr(param.value, param.numericFormatter)); + + paramList.Add(param); + } + } + + private Vector3 GetLabelLineEndPosition(Serie serie, SerieData serieData, LabelLine labelLine) + { + if (labelLine == null || !labelLine.show) + return serieData.context.labelLinePosition; + var isRight = !serie.clockwise; + var dire = isRight ? Vector3.right : Vector3.left; + var rad = Mathf.Deg2Rad * (isRight ? labelLine.lineAngle : 180 - labelLine.lineAngle); + var lineLength1 = ChartHelper.GetActualValue(labelLine.lineLength1, serie.context.outsideRadius); + var lineLength2 = ChartHelper.GetActualValue(labelLine.lineLength2, serie.context.outsideRadius); + var pos1 = serieData.context.labelLinePosition; + var pos2 = pos1 + new Vector3(Mathf.Cos(rad) * lineLength1, Mathf.Sin(rad) * lineLength1); + var pos5 = labelLine.lineType == LabelLine.LineType.HorizontalLine + ? pos1 + dire * (lineLength1 + lineLength2) + labelLine.GetEndSymbolOffset() + : pos2 + dire * lineLength2 + labelLine.GetEndSymbolOffset(); + if (labelLine.lineEndX != 0) + { + pos5.x = labelLine.lineEndX; + } + return pos5; + } + + public override void DrawSerie(VertexHelper vh) + { + if (!serie.show || serie.animation.HasFadeOut()) return; + UpdateRuntimeData(); + var data = serie.data; + serie.animation.InitProgress(serie.startAngle, serie.startAngle + 360); + var ringWidth = serie.context.outsideRadius - serie.context.insideRadius; + var dataChanging = false; + for (int j = 0; j < data.Count; j++) + { + var serieData = data[j]; + if (!serieData.show) continue; + if (serieData.IsDataChanged()) dataChanging = true; + var outsideRadius = serie.context.outsideRadius - j * (ringWidth + serie.gap); + if (outsideRadius < 0) continue; + var value = serieData.GetCurrData(0, serie.animation, false, false); + var max = serieData.GetLastData(); + var degree = (float)(360 * value / max); + var startDegree = GetStartAngle(serie); + var toDegree = GetToAngle(serie, degree); + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + var colorIndex = chart.GetLegendRealShowNameIndex(serieData.legendName); + Color32 itemColor, itemToColor; + SerieHelper.GetItemColor(out itemColor, out itemToColor, serie, serieData, chart.theme, colorIndex); + + var insideRadius = outsideRadius - ringWidth; + var borderWidth = itemStyle.borderWidth; + var borderColor = itemStyle.borderColor; + var roundCap = serie.roundCap && insideRadius > 0; + DrawBackground(vh, serie, serieData, j, insideRadius, outsideRadius); + UGL.DrawDoughnut(vh, serie.context.center, insideRadius, outsideRadius, itemColor, itemToColor, + Color.clear, startDegree, toDegree, borderWidth, borderColor, 0, chart.settings.cicleSmoothness, + roundCap, serie.clockwise, serie.radiusGradient); + DrawCenter(vh, serie, serieData, insideRadius, j == data.Count - 1); + } + + for (int j = 0; j < data.Count; j++) + { + var serieData = data[j]; + if (!serieData.show) continue; + var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + var colorIndex = chart.GetLegendRealShowNameIndex(serieData.legendName); + Color32 itemColor, itemToColor; + SerieHelper.GetItemColor(out itemColor, out itemToColor, serie, serieData, chart.theme, colorIndex); + if (SerieLabelHelper.CanShowLabel(serie, serieData, serieLabel, 0)) + { + DrawRingLabelLine(vh, serie, serieData, itemColor); + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(360); + chart.RefreshChart(); + } + if (dataChanging) + { + chart.RefreshChart(); + } + } + + private void UpdateRuntimeData() + { + var data = serie.data; + SerieHelper.UpdateCenter(serie, chart); + var ringWidth = serie.context.outsideRadius - serie.context.insideRadius; + for (int j = 0; j < data.Count; j++) + { + var serieData = data[j]; + if (!serieData.show) continue; + var outsideRadius = serie.context.outsideRadius - j * (ringWidth + serie.gap); + if (outsideRadius < 0) continue; + var value = serieData.GetCurrData(0, serie.animation, false, false); + var max = serieData.GetLastData(); + var degree = (float)(360 * value / max); + var startDegree = GetStartAngle(serie); + var toDegree = GetToAngle(serie, degree); + var insideRadius = outsideRadius - ringWidth; + var halfAngle = startDegree + (toDegree - startDegree) / 2; + var halfRadius = (outsideRadius + insideRadius) / 2; + serieData.context.startAngle = startDegree; + serieData.context.toAngle = toDegree; + serieData.context.insideRadius = insideRadius; + serieData.context.outsideRadius = serieData.radius > 0 ? serieData.radius : outsideRadius; + serieData.context.position = ChartHelper.GetPosition(serie.context.center, halfAngle, halfRadius); + UpdateLabelPosition(serieData); + } + AvoidLabelOverlap(); + } + + public override void OnLegendButtonClick(int index, string legendName, bool show) + { + if (!serie.IsLegendName(legendName)) + return; + LegendHelper.CheckDataShow(serie, legendName, show); + chart.UpdateLegendColor(legendName, show); + chart.RefreshPainter(serie); + } + + public override void OnLegendButtonEnter(int index, string legendName) + { + if (!serie.IsLegendName(legendName)) + return; + LegendHelper.CheckDataHighlighted(serie, legendName, true); + chart.RefreshPainter(serie); + } + + public override void OnLegendButtonExit(int index, string legendName) + { + if (!serie.IsLegendName(legendName)) + return; + LegendHelper.CheckDataHighlighted(serie, legendName, false); + chart.RefreshPainter(serie); + } + + public override void OnPointerDown(PointerEventData eventData) { } + + private float GetStartAngle(Serie serie) + { + return serie.clockwise ? serie.startAngle : 360 - serie.startAngle; + } + + private float GetToAngle(Serie serie, float angle) + { + var toAngle = angle + serie.startAngle; + if (!serie.clockwise) + { + toAngle = 360 - angle - serie.startAngle; + } + if (!serie.animation.IsFinish()) + { + var currAngle = serie.animation.GetCurrDetail(); + if (serie.clockwise) + { + toAngle = toAngle > currAngle ? currAngle : toAngle; + } + else + { + toAngle = toAngle < 360 - currAngle ? 360 - currAngle : toAngle; + } + } + return toAngle; + } + + private void DrawCenter(VertexHelper vh, Serie serie, SerieData serieData, float insideRadius, bool last) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (!ChartHelper.IsClearColor(itemStyle.centerColor) && last) + { + var radius = insideRadius - itemStyle.centerGap; + var smoothness = chart.settings.cicleSmoothness; + UGL.DrawCricle(vh, serie.context.center, radius, itemStyle.centerColor, smoothness); + } + } + + private void DrawBackground(VertexHelper vh, Serie serie, SerieData serieData, int index, float insideRadius, float outsideRadius) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + var backgroundColor = itemStyle.backgroundColor; + if (ChartHelper.IsClearColor(backgroundColor)) + { + backgroundColor = chart.theme.GetColor(index); + backgroundColor.a = 50; + } + if (itemStyle.backgroundWidth != 0) + { + var centerRadius = (outsideRadius + insideRadius) / 2; + var inradius = centerRadius - itemStyle.backgroundWidth / 2; + var outradius = centerRadius + itemStyle.backgroundWidth / 2; + UGL.DrawDoughnut(vh, serie.context.center, inradius, + outradius, backgroundColor, Color.clear, chart.settings.cicleSmoothness); + } + else + { + UGL.DrawDoughnut(vh, serie.context.center, insideRadius, + outsideRadius, backgroundColor, Color.clear, chart.settings.cicleSmoothness); + } + } + + private void DrawBorder(VertexHelper vh, Serie serie, SerieData serieData, float insideRadius, float outsideRadius) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (itemStyle.show && itemStyle.borderWidth > 0 && !ChartHelper.IsClearColor(itemStyle.borderColor)) + { + UGL.DrawDoughnut(vh, serie.context.center, outsideRadius, + outsideRadius + itemStyle.borderWidth, itemStyle.borderColor, + Color.clear, chart.settings.cicleSmoothness); + UGL.DrawDoughnut(vh, serie.context.center, insideRadius, + insideRadius + itemStyle.borderWidth, itemStyle.borderColor, + Color.clear, chart.settings.cicleSmoothness); + } + } + + private int GetRingIndex(Vector2 local) + { + var dist = Vector2.Distance(local, serie.context.center); + if (dist > serie.context.outsideRadius) return -1; + Vector2 dir = local - new Vector2(serie.context.center.x, serie.context.center.y); + float angle = VectorAngle(Vector2.up, dir); + for (int i = 0; i < serie.data.Count; i++) + { + var serieData = serie.data[i]; + if (dist >= serieData.context.insideRadius && + dist <= serieData.context.outsideRadius && + IsInAngle(serieData, angle, serie.clockwise)) + { + return i; + } + } + return -1; + } + + private bool IsInAngle(SerieData serieData, float angle, bool clockwise) + { + if (clockwise) + return angle >= serieData.context.startAngle && angle <= serieData.context.toAngle; + else + return angle >= serieData.context.toAngle && angle <= serieData.context.startAngle; + } + + private float VectorAngle(Vector2 from, Vector2 to) + { + float angle; + + Vector3 cross = Vector3.Cross(from, to); + angle = Vector2.Angle(from, to); + angle = cross.z > 0 ? -angle : angle; + angle = (angle + 360) % 360; + return angle; + } + + private void UpdateLabelPosition(SerieData serieData) + { + if (serieData.labelObject == null) return; + var label = SerieHelper.GetSerieLabel(serie, serieData); + var labelLine = SerieHelper.GetSerieLabelLine(serie, serieData); + var centerRadius = (serieData.context.outsideRadius + serieData.context.insideRadius) / 2; + var startAngle = serieData.context.startAngle; + var toAngle = serieData.context.toAngle; + switch (label.position) + { + case LabelStyle.Position.Bottom: + case LabelStyle.Position.Start: + var px1 = Mathf.Sin(startAngle * Mathf.Deg2Rad) * centerRadius; + var py1 = Mathf.Cos(startAngle * Mathf.Deg2Rad) * centerRadius; + var xDiff = serie.clockwise ? -label.distance : label.distance; + + if (labelLine != null && labelLine.show) + { + serieData.context.labelLinePosition = serie.context.center + new Vector3(px1, py1) + labelLine.GetStartSymbolOffset(); + serieData.context.labelPosition = GetLabelLineEndPosition(serie, serieData, labelLine) + new Vector3(xDiff, 0); + } + else + { + serieData.context.labelLinePosition = serie.context.center + new Vector3(px1 + xDiff, py1); + serieData.context.labelPosition = serieData.context.labelLinePosition; + } + break; + case LabelStyle.Position.Top: + case LabelStyle.Position.End: + case LabelStyle.Position.Outside: + startAngle += serie.clockwise ? -label.distance : label.distance; + toAngle += serie.clockwise ? label.distance : -label.distance; + var px2 = Mathf.Sin(toAngle * Mathf.Deg2Rad) * centerRadius; + var py2 = Mathf.Cos(toAngle * Mathf.Deg2Rad) * centerRadius; + + if (labelLine != null && labelLine.show) + { + serieData.context.labelLinePosition = serie.context.center + new Vector3(px2, py2) + labelLine.GetStartSymbolOffset(); + serieData.context.labelPosition = GetLabelLineEndPosition(serie, serieData, labelLine); + } + else + { + serieData.context.labelLinePosition = serie.context.center + new Vector3(px2, py2); + serieData.context.labelPosition = serieData.context.labelLinePosition; + } + break; + default: //LabelStyle.Position.Center + serieData.context.labelLinePosition = serie.context.center + label.offset; + serieData.context.labelPosition = serieData.context.labelLinePosition; + break; + } + } + + private void AvoidLabelOverlap() + { + if (!serie.avoidLabelOverlap) return; + serie.context.sortedData.Clear(); + foreach (var serieData in serie.data) + { + serie.context.sortedData.Add(serieData); + } + serie.context.sortedData.Sort(delegate (SerieData a, SerieData b) + { + if (a == null || b == null) return 0; + return a.context.labelPosition.y.CompareTo(b.context.labelPosition.y); + }); + var startY = serie.context.sortedData[0].context.labelPosition.y; + for (int i = 1; i < serie.context.sortedData.Count; i++) + { + var serieData = serie.context.sortedData[i]; + var fontSize = serieData.labelObject.GetHeight(); + if (serieData.context.labelPosition.y - startY < fontSize) + { + serieData.context.labelPosition.y = startY + fontSize; + } + startY = serieData.context.labelPosition.y; + } + } + + private void DrawRingLabelLine(VertexHelper vh, Serie serie, SerieData serieData, Color32 defaltColor) + { + var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + var labelLine = SerieHelper.GetSerieLabelLine(serie, serieData); + if (serieLabel != null && serieLabel.show && + labelLine != null && labelLine.show) + { + var color = ChartHelper.IsClearColor(labelLine.lineColor) ? + ChartHelper.GetHighlightColor(defaltColor, 0.9f) : + labelLine.lineColor; + var isRight = !serie.clockwise; + var rad = Mathf.Deg2Rad * (isRight ? labelLine.lineAngle : 180 - labelLine.lineAngle); + var lineLength1 = ChartHelper.GetActualValue(labelLine.lineLength1, serie.context.outsideRadius); + var pos1 = serieData.context.labelLinePosition; + var pos2 = pos1 + new Vector3(Mathf.Cos(rad) * lineLength1, Mathf.Sin(rad) * lineLength1); + var pos5 = serieData.context.labelPosition; + switch (labelLine.lineType) + { + case LabelLine.LineType.BrokenLine: + UGL.DrawLine(vh, pos1, pos2, pos5, labelLine.lineWidth, color); + break; + case LabelLine.LineType.Curves: + UGL.DrawCurves(vh, pos1, pos5, pos1, pos2, labelLine.lineWidth, color, + chart.settings.lineSmoothness, UGL.Direction.XAxis); + break; + case LabelLine.LineType.HorizontalLine: + UGL.DrawLine(vh, pos1, pos5, labelLine.lineWidth, color); + break; + } + DrawLabelLineSymbol(vh, labelLine, pos1, pos5, color); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs.meta new file mode 100644 index 0000000..293edfb --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Ring/RingHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c3c486efd6d8464a88d8f4b572b7bc4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter.meta b/Assets/XCharts/Runtime/Serie/Scatter.meta new file mode 100644 index 0000000..916354d --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 97fc5bddab1db4321aa7377ab8b8b8bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs new file mode 100644 index 0000000..bb0cbb5 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs @@ -0,0 +1,9 @@ +namespace XCharts.Runtime +{ + [System.Serializable] + public class BaseScatter : Serie, INeedSerieContainer + { + public int containerIndex { get; internal set; } + public int containterInstanceId { get; internal set; } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs.meta b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs.meta new file mode 100644 index 0000000..8b9933e --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dba0ca827ad4d4b9989def35aba66665 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs new file mode 100644 index 0000000..c8e44eb --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs @@ -0,0 +1,359 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal class BaseScatterHandler<T> : SerieHandler<T> where T : BaseScatter + { + private GridCoord m_Grid; + + public override void Update() + { + base.Update(); + } + + public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, + string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { + dataIndex = serie.context.pointerItemDataIndex; + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + + title = serie.serieName; + + itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + marker = SerieHelper.GetItemMarker(serie, serieData, marker); + var color = chart.GetMarkColor(serie, serieData); + + if (itemFormatter == null) itemFormatter = ""; + itemFormatter = itemFormatter.Replace("\\n", "\n"); + var temp = itemFormatter.Split('\n'); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.dimension = 1; + param.dataCount = serie.dataCount; + param.serieData = serieData; + param.color = color; + param.marker = marker; + param.itemFormatter = formatter; + param.numericFormatter = numericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + if (!string.IsNullOrEmpty(serieData.name)) + param.columns.Add(serieData.name); + param.columns.Add(ChartCached.NumberToStr(serieData.GetData(1), param.numericFormatter)); + + paramList.Add(param); + } + } + + public override void DrawSerie(VertexHelper vh) + { + if (serie.IsUseCoord<SingleAxisCoord>()) + { + DrawSingAxisScatterSerie(vh, serie); + } + else if (serie.IsUseCoord<GridCoord>()) + { + DrawScatterSerie(vh, serie); + } + } + + public override void UpdateSerieContext() + { + var needCheck = m_LegendEnter || (chart.isPointerInChart && (m_Grid == null || m_Grid.IsPointerEnter())); + + var needHideAll = false; + if (!needCheck) + { + if (m_LastCheckContextFlag == needCheck) + return; + needHideAll = true; + } + m_LastCheckContextFlag = needCheck; + serie.context.pointerItemDataIndex = -1; + serie.context.pointerEnter = false; + var themeSymbolSize = chart.theme.serie.scatterSymbolSize; + var needInteract = false; + for (int i = serie.dataCount - 1; i >= 0; i--) + { + var serieData = serie.data[i]; + var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize); + if (m_LegendEnter || + (!needHideAll && Vector3.Distance(serieData.context.position, chart.pointerPos) <= symbolSize)) + { + serie.context.pointerItemDataIndex = i; + serie.context.pointerEnter = true; + serieData.context.highlight = true; + } + else + { + serieData.context.highlight = false; + } + var state = SerieHelper.GetSerieState(serie, serieData, true); + symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, themeSymbolSize, state); + serieData.interact.SetValue(ref needInteract, symbolSize); + } + if (needInteract) + { + chart.RefreshPainter(serie); + } + } + + protected virtual void DrawScatterSerie(VertexHelper vh, BaseScatter serie) + { + if (serie.animation.HasFadeOut()) + return; + + if (!serie.show) + return; + + XAxis xAxis; + if (!chart.TryGetChartComponent<XAxis>(out xAxis, serie.xAxisIndex)) + return; + + YAxis yAxis; + if (!chart.TryGetChartComponent<YAxis>(out yAxis, serie.yAxisIndex)) + return; + + if (!chart.TryGetChartComponent<GridCoord>(out m_Grid, xAxis.gridIndex)) + return; + + DataZoom xDataZoom; + DataZoom yDataZoom; + chart.GetDataZoomOfSerie(serie, out xDataZoom, out yDataZoom); + + var theme = chart.theme; + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > serie.dataCount ? serie.dataCount : serie.maxShow) : + serie.dataCount; + serie.animation.InitProgress(0, 1); + var rate = serie.animation.GetCurrRate(); + var dataChangeDuration = serie.animation.GetChangeDuration(); + var interactDuration = serie.animation.GetInteractionDuration(); + var isFadeOut = serie.animation.IsFadeOut(); + var unscaledTime = serie.animation.unscaledTime; + var dataChanging = false; + var interacting = false; + var dataList = serie.GetDataList(xDataZoom); + var isEffectScatter = serie is EffectScatter; + var colorIndex = serie.context.colorIndex; + + serie.containerIndex = m_Grid.index; + serie.containterInstanceId = m_Grid.instanceId; + + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 color, toColor, emptyColor, borderColor; + foreach (var serieData in dataList) + { + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + if (!symbol.ShowSymbol(serieData.index, maxCount)) + continue; + + var state = SerieHelper.GetSerieState(serie, serieData, true); + + SerieHelper.GetItemColor(out color, out toColor, out emptyColor, serie, serieData, chart.theme, colorIndex, state); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, serieData, chart.theme, state); + double xValue = serieData.GetCurrData(0, 0, isFadeOut ? 0 : dataChangeDuration, unscaledTime, xAxis.inverse); + double yValue = serieData.GetCurrData(1, 0, isFadeOut ? 0 : dataChangeDuration, unscaledTime, yAxis.inverse); + + if (serieData.IsDataChanged()) + dataChanging = true; + + float xDataHig = GetDataHig(xAxis, xValue, m_Grid.context.width); + float yDataHig = GetDataHig(yAxis, yValue, m_Grid.context.height); + var pos = new Vector3(m_Grid.context.x + xDataHig, m_Grid.context.y + yDataHig); + + if (!m_Grid.Contains(pos)) + continue; + + serie.context.dataPoints.Add(pos); + serie.context.dataIndexs.Add(serieData.index); + serieData.context.position = pos; + var datas = serieData.data; + var symbolSize = 0f; + if (isFadeOut || !serieData.interact.TryGetValue(ref symbolSize, ref interacting, interactDuration)) + { + symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme.serie.scatterSymbolSize, state); + if (!isFadeOut) + { + serieData.interact.SetValue(ref interacting, symbolSize, true); + serieData.interact.TryGetValue(ref symbolSize, ref interacting, interactDuration); + } + } + symbolSize *= rate; + + if (isEffectScatter) + { + for (int count = 0; count < symbol.animationSize.Count; count++) + { + var nowSize = symbol.animationSize[count]; + color.a = (byte)(255 * (symbolSize - nowSize) / symbolSize); + chart.DrawSymbol(vh, symbol.type, nowSize, symbolBorder, pos, + color, toColor, emptyColor, borderColor, symbol.gap, cornerRadius); + } + chart.RefreshPainter(serie); + } + else + { + chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, pos, + color, toColor, emptyColor, borderColor, symbol.gap, cornerRadius); + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(1); + chart.RefreshPainter(serie); + } + if (dataChanging || interacting) + { + chart.RefreshPainter(serie); + } + } + + protected virtual void DrawSingAxisScatterSerie(VertexHelper vh, BaseScatter serie) + { + if (serie.animation.HasFadeOut()) + return; + + if (!serie.show) + return; + + var axis = chart.GetChartComponent<SingleAxis>(serie.singleAxisIndex); + if (axis == null) + return; + + DataZoom xDataZoom; + DataZoom yDataZoom; + chart.GetDataZoomOfSerie(serie, out xDataZoom, out yDataZoom); + + var theme = chart.theme; + int maxCount = serie.maxShow > 0 ? + (serie.maxShow > serie.dataCount ? serie.dataCount : serie.maxShow) : + serie.dataCount; + serie.animation.InitProgress(0, 1); + + var rate = serie.animation.GetCurrRate(); + var dataChangeDuration = serie.animation.GetChangeDuration(); + var unscaledTime = serie.animation.unscaledTime; + var dataChanging = false; + var dataList = serie.GetDataList(xDataZoom); + var isEffectScatter = serie is EffectScatter; + var colorIndex = serie.context.colorIndex; + + serie.containerIndex = axis.index; + serie.containterInstanceId = axis.instanceId; + + float symbolBorder = 0f; + float[] cornerRadius = null; + Color32 color, toColor, emptyColor, borderColor; + foreach (var serieData in dataList) + { + var symbol = SerieHelper.GetSerieSymbol(serie, serieData); + if (!symbol.ShowSymbol(serieData.index, maxCount)) + continue; + + var state = SerieHelper.GetSerieState(serie, serieData, true); + SerieHelper.GetItemColor(out color, out toColor, out emptyColor, serie, serieData, chart.theme, colorIndex, state); + SerieHelper.GetSymbolInfo(out borderColor, out symbolBorder, out cornerRadius, serie, serieData, chart.theme, state); + + if (serieData.IsDataChanged()) + dataChanging = true; + + var pos = Vector3.zero; + var xValue = serieData.GetCurrData(0, 0, dataChangeDuration, unscaledTime, axis.inverse); + + if (axis.orient == Orient.Horizonal) + { + var xDataHig = GetDataHig(axis, xValue, axis.context.width); + var yDataHig = axis.context.height / 2; + pos = new Vector3(axis.context.x + xDataHig, axis.context.y + yDataHig); + } + else + { + var yDataHig = GetDataHig(axis, xValue, axis.context.width); + var xDataHig = axis.context.height / 2; + pos = new Vector3(axis.context.x + xDataHig, axis.context.y + yDataHig); + } + serie.context.dataPoints.Add(pos); + serie.context.dataIndexs.Add(serieData.index); + serieData.context.position = pos; + + var datas = serieData.data; + var symbolSize = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme.serie.scatterSymbolSize, state); + symbolSize *= rate; + + if (isEffectScatter) + { + if (symbolSize > 100) symbolSize = 100; + for (int count = 0; count < symbol.animationSize.Count; count++) + { + var nowSize = symbol.animationSize[count]; + color.a = (byte)(255 * (symbolSize - nowSize) / symbolSize); + chart.DrawSymbol(vh, symbol.type, nowSize, symbolBorder, pos, + color, toColor, emptyColor, borderColor, symbol.gap, cornerRadius); + } + chart.RefreshPainter(serie); + } + else + { + if (symbolSize > 100) symbolSize = 100; + chart.DrawSymbol(vh, symbol.type, symbolSize, symbolBorder, pos, + color, toColor, emptyColor, borderColor, symbol.gap, cornerRadius); + } + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(1); + chart.RefreshPainter(serie); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } + + private static float GetDataHig(Axis axis, double value, float totalWidth) + { + if (axis.IsLog()) + { + var minIndex = axis.GetLogMinIndex(); + var nowIndex = axis.GetLogValue(value); + return (float)((nowIndex - minIndex) / axis.splitNumber * totalWidth); + } + else if (axis.IsCategory()) + { + if (axis.boundaryGap) + { + float tick = (float)(totalWidth / (axis.context.minMaxRange + 1)); + return tick / 2 + (float)(value - axis.context.minValue) * tick; + } + else + { + return (float)((value - axis.context.minValue) / axis.context.minMaxRange * totalWidth); + } + } + else + { + return (float)((value - axis.context.minValue) / axis.context.minMaxRange * totalWidth); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs.meta new file mode 100644 index 0000000..f665940 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/BaseScatterHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 31373c1595ff249188e33330f2eff1ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs new file mode 100644 index 0000000..0e3e89a --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(EffectScatterHandler), true)] + [CoordOptions(typeof(GridCoord), typeof(SingleAxisCoord))] + [DefaultTooltip(Tooltip.Type.None, Tooltip.Trigger.Item)] + [SerieComponent(typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField("m_Radius")] + public class EffectScatter : BaseScatter + { + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<EffectScatter>(serieName); + serie.symbol.show = true; + serie.symbol.type = SymbolType.Circle; + serie.itemStyle.opacity = 0.8f; + serie.clip = false; + for (int i = 0; i < 10; i++) + { + chart.AddData(serie.index, Random.Range(10, 100), Random.Range(10, 100)); + } + return serie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs.meta b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs.meta new file mode 100644 index 0000000..d452884 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c34d4976ef53c48a4b091d52694d8a7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs new file mode 100644 index 0000000..d7d86bf --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class EffectScatterHandler : BaseScatterHandler<EffectScatter> + { + private float m_EffectScatterSpeed = 15; + + public override void Update() + { + base.Update(); + var symbolSize = serie.symbol.GetSize(null, chart.theme.serie.scatterSymbolSize); + var deltaTime = serie.animation.unscaledTime? Time.unscaledDeltaTime : Time.deltaTime; + for (int i = 0; i < serie.symbol.animationSize.Count; ++i) + { + serie.symbol.animationSize[i] += m_EffectScatterSpeed * deltaTime; + if (serie.symbol.animationSize[i] > symbolSize) + { + serie.symbol.animationSize[i] = i * 5; + } + chart.RefreshPainter(serie); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs.meta new file mode 100644 index 0000000..5b7d2b7 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/EffectScatterHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb7c24770dff64d7b857f459de7b2333 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs b/Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs new file mode 100644 index 0000000..bf64074 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + [System.Serializable] + [SerieHandler(typeof(ScatterHandler), true)] + [CoordOptions(typeof(GridCoord), typeof(SingleAxisCoord))] + [DefaultTooltip(Tooltip.Type.None, Tooltip.Trigger.Item)] + [SerieComponent(typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] + [SerieDataExtraField("m_Radius")] + public class Scatter : BaseScatter + { + public static Serie AddDefaultSerie(BaseChart chart, string serieName) + { + var serie = chart.AddSerie<Scatter>(serieName); + serie.symbol.show = true; + serie.symbol.type = SymbolType.Circle; + serie.itemStyle.opacity = 0.8f; + serie.clip = false; + for (int i = 0; i < 10; i++) + { + chart.AddData(serie.index, Random.Range(10, 100), Random.Range(10, 100)); + } + return serie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs.meta b/Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs.meta new file mode 100644 index 0000000..e7e72d0 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/Scatter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 75a031f5547984317b5659a03d7f5e32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs b/Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs new file mode 100644 index 0000000..9174cd3 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs @@ -0,0 +1,6 @@ +namespace XCharts.Runtime +{ + [UnityEngine.Scripting.Preserve] + internal sealed class ScatterHandler : BaseScatterHandler<Scatter> + { } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs.meta b/Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs.meta new file mode 100644 index 0000000..51d8f55 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Scatter/ScatterHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ee7d7a8f04034cd38fd9d43f1a41825 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs b/Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs new file mode 100644 index 0000000..52a8a09 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; + +namespace XCharts.Runtime +{ + public partial class Serie + { + public static Dictionary<Type, string> extraComponentMap = new Dictionary<Type, string> + { + { typeof(LabelStyle), "m_Labels" }, + { typeof(LabelLine), "m_LabelLines" }, + { typeof(EndLabelStyle), "m_EndLabels" }, + { typeof(LineArrow), "m_LineArrows" }, + { typeof(AreaStyle), "m_AreaStyles" }, + { typeof(TitleStyle), "m_TitleStyles" }, + { typeof(EmphasisStyle), "m_EmphasisStyles" }, + { typeof(BlurStyle), "m_BlurStyles" }, + { typeof(SelectStyle), "m_SelectStyles" }, + }; + + [SerializeField][IgnoreDoc] private List<LabelStyle> m_Labels = new List<LabelStyle>(); + [SerializeField][IgnoreDoc] private List<LabelLine> m_LabelLines = new List<LabelLine>(); + [SerializeField][IgnoreDoc] private List<EndLabelStyle> m_EndLabels = new List<EndLabelStyle>(); + [SerializeField][IgnoreDoc] private List<LineArrow> m_LineArrows = new List<LineArrow>(); + [SerializeField][IgnoreDoc] private List<AreaStyle> m_AreaStyles = new List<AreaStyle>(); + [SerializeField][IgnoreDoc] private List<TitleStyle> m_TitleStyles = new List<TitleStyle>(); + [SerializeField][IgnoreDoc] private List<EmphasisStyle> m_EmphasisStyles = new List<EmphasisStyle>(); + [SerializeField][IgnoreDoc] private List<BlurStyle> m_BlurStyles = new List<BlurStyle>(); + [SerializeField][IgnoreDoc] private List<SelectStyle> m_SelectStyles = new List<SelectStyle>(); + + /// <summary> + /// The style of area. + /// ||区域填充样式。 + /// </summary> + public AreaStyle areaStyle { get { return m_AreaStyles.Count > 0 ? m_AreaStyles[0] : null; } } + /// <summary> + /// Text label of graphic element,to explain some data information about graphic item like value, name and so on. + /// ||图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。 + /// </summary> + public LabelStyle label { get { return m_Labels.Count > 0 ? m_Labels[0] : null; } } + public LabelStyle endLabel { get { return m_EndLabels.Count > 0 ? m_EndLabels[0] : null; } } + /// <summary> + /// The line of label. + /// ||标签上的视觉引导线。 + /// </summary> + public LabelLine labelLine { get { return m_LabelLines.Count > 0 ? m_LabelLines[0] : null; } } + /// <summary> + /// The arrow of line. + /// ||折线图的箭头。 + /// </summary> + public LineArrow lineArrow { get { return m_LineArrows.Count > 0 ? m_LineArrows[0] : null; } } + /// <summary> + /// the icon of data. + /// ||数据项标题样式。 + /// </summary> + public TitleStyle titleStyle { get { return m_TitleStyles.Count > 0 ? m_TitleStyles[0] : null; } } + /// <summary> + /// style of emphasis state. + /// ||高亮状态的样式。 + /// </summary> + public EmphasisStyle emphasisStyle { get { return m_EmphasisStyles.Count > 0 ? m_EmphasisStyles[0] : null; } } + /// <summary> + /// style of blur state. + /// ||淡出状态的样式。 + /// </summary> + public BlurStyle blurStyle { get { return m_BlurStyles.Count > 0 ? m_BlurStyles[0] : null; } } + /// <summary> + /// style of select state. + /// ||选中状态的样式。 + /// </summary> + public SelectStyle selectStyle { get { return m_SelectStyles.Count > 0 ? m_SelectStyles[0] : null; } } + + /// <summary> + /// Remove all extra components. + /// ||移除所有额外组件。 + /// </summary> + public void RemoveAllComponents() + { + var serieType = GetType(); + foreach (var kv in extraComponentMap) + { + ReflectionUtil.InvokeListClear(this, serieType.GetField(kv.Value)); + } + SetAllDirty(); + } + + [Obsolete("Use EnsureComponent<T>() instead.")] + public T AddExtraComponent<T>() where T : ChildComponent, ISerieComponent + { + return EnsureComponent<T>(); + } + + public T GetComponent<T>() where T : ChildComponent, ISerieComponent + { + return GetComponent(typeof(T)) as T; + } + + /// <summary> + /// Ensure the serie has the component. If not, add it. + /// ||确保系列有该组件。如果没有,则添加。 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>component or null</returns> + public T EnsureComponent<T>() where T : ChildComponent, ISerieComponent + { + return EnsureComponent(typeof(T)) as T; + } + + public bool CanAddComponent<T>() where T : ChildComponent, ISerieComponent + { + return CanAddComponent(typeof(T)); + } + + public bool CanAddComponent(Type type) + { + if (GetType().IsDefined(typeof(SerieComponentAttribute), false)) + { + var attr = GetType().GetAttribute<SerieComponentAttribute>(); + if (attr.Contains(type)) + { + return true; + } + } + return false; + } + + public ISerieComponent GetComponent(Type type) + { + if (GetType().IsDefined(typeof(SerieComponentAttribute), false)) + { + var attr = GetType().GetAttribute<SerieComponentAttribute>(); + if (attr.Contains(type)) + { + var fieldName = string.Empty; + if (extraComponentMap.TryGetValue(type, out fieldName)) + { + var field = typeof(Serie).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + if (ReflectionUtil.InvokeListCount(this, field) > 0) + { + return ReflectionUtil.InvokeListGet<ISerieComponent>(this, field, 0); + } + } + } + } + return null; + } + + public ISerieComponent EnsureComponent(Type type) + { + if (GetType().IsDefined(typeof(SerieComponentAttribute), false)) + { + var attr = GetType().GetAttribute<SerieComponentAttribute>(); + if (attr.Contains(type)) + { + var fieldName = string.Empty; + if (extraComponentMap.TryGetValue(type, out fieldName)) + { + var field = typeof(Serie).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + if (ReflectionUtil.InvokeListCount(this, field) <= 0) + { + var extraComponent = Activator.CreateInstance(type) as ISerieComponent; + ReflectionUtil.InvokeListAdd(this, field, extraComponent); + SetAllDirty(); + return extraComponent; + } + else + { + return ReflectionUtil.InvokeListGet<ISerieComponent>(this, field, 0); + } + } + } + } + throw new System.Exception(string.Format("Serie {0} not support component: {1}", + GetType().Name, type.Name)); + } + + public void RemoveComponent<T>() where T : ISerieComponent + { + RemoveComponent(typeof(T)); + } + + public void RemoveComponent(Type type) + { + if (GetType().IsDefined(typeof(SerieComponentAttribute), false)) + { + var attr = GetType().GetAttribute<SerieComponentAttribute>(); + if (attr.Contains(type)) + { + var fieldName = string.Empty; + if (extraComponentMap.TryGetValue(type, out fieldName)) + { + var field = typeof(Serie).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + ReflectionUtil.InvokeListClear(this, field); + SetAllDirty(); + return; + } + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs.meta b/Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs.meta new file mode 100644 index 0000000..97d8db3 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Serie.ExtraComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9c4f3a01039fd4e7fbf771a65ede0069 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/Serie.cs b/Assets/XCharts/Runtime/Serie/Serie.cs new file mode 100644 index 0000000..06b101e --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Serie.cs @@ -0,0 +1,2109 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Whether to show as Nightingale chart, which distinguishs data through radius. + /// ||是否展示成南丁格尔图,通过半径区分数据大小。 + /// </summary> + public enum RoseType + { + /// <summary> + /// Don't show as Nightingale chart. + /// ||不展示成南丁格尔玫瑰图。 + /// </summary> + None, + /// <summary> + /// Use central angle to show the percentage of data, radius to show data size. + /// ||扇区圆心角展现数据的百分比,半径展现数据的大小。 + /// </summary> + Radius, + /// <summary> + /// All the sectors will share the same central angle, the data size is shown only through radiuses. + /// ||所有扇区圆心角相同,仅通过半径展现数据大小。 + /// </summary> + Area + } + + /// <summary> + /// the type of line chart. + /// ||折线图样式类型 + /// </summary> + public enum LineType + { + /// <summary> + /// the normal line chart, + /// ||普通折线图。 + /// </summary> + Normal, + /// <summary> + /// the smooth line chart, + /// ||平滑曲线。 + /// </summary> + Smooth, + /// <summary> + /// step line. + /// ||阶梯线图:当前点。 + /// </summary> + StepStart, + /// <summary> + /// step line. + /// ||阶梯线图:当前点和下一个点的中间。 + /// </summary> + StepMiddle, + /// <summary> + /// step line. + /// ||阶梯线图:下一个拐点。 + /// </summary> + StepEnd + } + + /// <summary> + /// the type of bar. + /// ||柱状图类型。 + /// </summary> + public enum BarType + { + /// <summary> + /// normal bar. + /// ||普通柱形图。 + /// </summary> + Normal, + /// <summary> + /// zebra bar. + /// ||斑马柱形图。 + /// </summary> + Zebra, + /// <summary> + /// capsule bar. + /// ||胶囊柱形图。 + /// </summary> + Capsule + } + + /// <summary> + /// the type of radar. + /// ||雷达图类型。 + /// </summary> + public enum RadarType + { + /// <summary> + /// multiple radar. + /// ||多圈雷达图。此时可一个雷达里绘制多个圈,一个serieData就可组成一个圈(多维数据)。 + /// </summary> + Multiple, + /// <summary> + /// single radar. + /// ||单圈雷达图。此时一个雷达只能绘制一个圈,多个serieData组成一个圈,数据取自`data[1]`。 + /// </summary> + Single + } + + /// <summary> + /// sample type of line chart. + /// ||采样类型,一般用于折线图。 + /// </summary> + public enum SampleType + { + /// <summary> + /// Take a peak. When the average value of the filter point is greater than or equal to 'sampleAverage', + /// take the maximum value; If you do it the other way around, you get the minimum. + /// ||取峰值。 + /// </summary> + Peak, + /// <summary> + /// Take the average of the filter points. + /// ||取过滤点的平均值。 + /// </summary> + Average, + /// <summary> + /// Take the maximum value of the filter point. + /// ||取过滤点的最大值。 + /// </summary> + Max, + /// <summary> + /// Take the minimum value of the filter point. + /// ||取过滤点的最小值。 + /// </summary> + Min, + /// <summary> + /// Take the sum of the filter points. + /// ||取过滤点的和。 + /// </summary> + Sum + } + + /// <summary> + /// the sort type of serie data. + /// ||数据排序方式。 + /// </summary> + public enum SerieDataSortType + { + /// <summary> + /// In the order of data. + /// ||按数据的顺序。 + /// </summary> + None, + /// <summary> + /// Sort data in ascending order. + /// ||升序。 + /// </summary> + Ascending, + /// <summary> + /// Sort data in descending order. + /// ||降序。 + /// </summary> + Descending, + } + + /// <summary> + /// Alignment mode. + /// ||对齐方式。文本,图标,图形等的对齐方式。 + /// </summary> + public enum Align + { + Center, + Left, + Right + } + + /// <summary> + /// Serie state. Supports normal, emphasis, blur, and select states. + /// ||Serie状态。支持正常、高亮、淡出、选中四种状态。 + /// </summary> + public enum SerieState + { + /// <summary> + /// Normal state. + /// ||正常状态。 + /// </summary> + Normal, + /// <summary> + /// Emphasis state. + /// ||高亮状态。 + /// </summary> + Emphasis, + /// <summary> + /// Blur state. + /// ||淡出状态。 + /// </summary> + Blur, + /// <summary> + /// Select state. + /// ||选中状态。 + /// </summary> + Select, + /// <summary> + /// Auto state. + /// ||自动保持和父节点一致。一般用在SerieData。 + /// </summary> + Auto + } + + /// <summary> + /// The policy to take color from theme. + /// ||从主题中取色策略。 + /// </summary> + public enum SerieColorBy + { + /// <summary> + /// Select state. + /// ||默认策略。每种Serie都有自己的默认的取颜色策略。比如Line默认是Series策略,Pie默认是Data策略。 + /// </summary> + Default, + /// <summary> + /// assigns the colors in the palette by serie, so that all data in the same series are in the same color. + /// ||按照系列分配调色盘中的颜色,同一系列中的所有数据都是用相同的颜色。 + /// </summary> + Serie, + /// <summary> + /// assigns colors in the palette according to data items, with each data item using a different color. + /// ||按照数据项分配调色盘中的颜色,每个数据项都使用不同的颜色。 + /// </summary> + Data + } + + /// <summary> + /// 系列。系列一般由数据和配置组成,用来表示具体的图表图形,如折线图的一条折线,柱图的一组柱子等。一个图表中可以包含多个不同类型的系列。 + /// </summary> + [System.Serializable] + public partial class Serie : BaseSerie, IComparable + { + [SerializeField] private int m_Index; + [SerializeField] private bool m_Show = true; + [SerializeField] private string m_CoordSystem = "GridCoord"; + [SerializeField] private string m_SerieType = ""; + [SerializeField] private string m_SerieName; + [SerializeField][Since("v3.2.0")] private SerieState m_State = SerieState.Normal; + [SerializeField][Since("v3.2.0")] private SerieColorBy m_ColorBy = SerieColorBy.Default; + [SerializeField] private string m_Stack; + [SerializeField] private int m_XAxisIndex = 0; + [SerializeField] private int m_YAxisIndex = 0; + [SerializeField] private int m_RadarIndex = 0; + [SerializeField] private int m_VesselIndex = 0; + [SerializeField] private int m_PolarIndex = 0; + [SerializeField] private int m_SingleAxisIndex = 0; + [SerializeField] private int m_ParallelIndex = 0; + [SerializeField][Since("v3.8.0")] private int m_GridIndex = -1; + [SerializeField] protected int m_MinShow; + [SerializeField] protected int m_MaxShow; + [SerializeField] protected int m_MaxCache; + + [SerializeField] private float m_SampleDist = 0; + [SerializeField] private SampleType m_SampleType = SampleType.Average; + [SerializeField] private float m_SampleAverage = 0; + + [SerializeField] private LineType m_LineType = LineType.Normal; + [SerializeField][Since("v3.4.0")] private bool m_SmoothLimit = false; + [SerializeField] private BarType m_BarType = BarType.Normal; + [SerializeField] private bool m_BarPercentStack = false; + [SerializeField] private float m_BarWidth = 0; + [SerializeField][Since("v3.5.0")] private float m_BarMaxWidth = 0; + [SerializeField] private float m_BarGap = 0.1f; + [SerializeField] private float m_BarZebraWidth = 4f; + [SerializeField] private float m_BarZebraGap = 2f; + + [SerializeField] private float m_Min; + [SerializeField] private float m_Max; + [SerializeField] private float m_MinSize = 0f; + [SerializeField] private float m_MaxSize = 1f; + [SerializeField] private float m_StartAngle; + [SerializeField] private float m_EndAngle; + [SerializeField] private float m_MinAngle; + [SerializeField] private bool m_Clockwise = true; + [SerializeField] private bool m_RoundCap; + [SerializeField] private int m_SplitNumber; + [SerializeField] private bool m_ClickOffset = true; + [SerializeField] private RoseType m_RoseType = RoseType.None; + [SerializeField] private float m_Gap; + [SerializeField] private float[] m_Center = new float[2] { 0.5f, 0.46f }; + [SerializeField] private float[] m_Radius = new float[2] { 0, 0.28f }; + [SerializeField][Since("v3.8.0")] private float m_MinRadius = 0f; + [SerializeField][Since("v3.10.0")] private bool m_MinShowLabel = false; + [SerializeField][Since("v3.10.0")] private double m_MinShowLabelValue = 0; + + [SerializeField][Range(2, 10)] private int m_ShowDataDimension; + [SerializeField] private bool m_ShowDataName; + [SerializeField] private bool m_Clip = false; + [SerializeField] private bool m_Ignore = false; + [SerializeField] private double m_IgnoreValue = 0; + [SerializeField] private bool m_IgnoreLineBreak = false; + [SerializeField] private bool m_ShowAsPositiveNumber = false; + [SerializeField] private bool m_Large = true; + [SerializeField] private int m_LargeThreshold = 200; + [SerializeField] private bool m_AvoidLabelOverlap = false; + [SerializeField] private RadarType m_RadarType = RadarType.Multiple; + [SerializeField] private bool m_PlaceHolder = false; + + [SerializeField] private SerieDataSortType m_DataSortType = SerieDataSortType.Descending; + [SerializeField] private Orient m_Orient = Orient.Vertical; + [SerializeField] private Align m_Align = Align.Center; + [SerializeField] private float m_Left; + [SerializeField] private float m_Right; + [SerializeField] private float m_Top; + [SerializeField] private float m_Bottom; + [SerializeField] private bool m_InsertDataToHead; + [SerializeField][Since("v3.14.0")] private bool m_RealtimeSort = false; + + [SerializeField] private LineStyle m_LineStyle = new LineStyle(); + [SerializeField] private SerieSymbol m_Symbol = new SerieSymbol(); + [SerializeField] private AnimationStyle m_Animation = new AnimationStyle(); + [SerializeField] private ItemStyle m_ItemStyle = new ItemStyle(); + [SerializeField] private List<SerieData> m_Data = new List<SerieData>(); + [SerializeField] private List<SerieDataLink> m_Links = new List<SerieDataLink>(); + + [NonSerialized] internal int m_FilterStart; + [NonSerialized] internal int m_FilterEnd; + [NonSerialized] internal double m_FilterStartValue; + [NonSerialized] internal double m_FilterEndValue; + [NonSerialized] internal int m_FilterMinShow; + [NonSerialized] internal bool m_NeedUpdateFilterData; + [NonSerialized] public List<SerieData> m_FilterData = new List<SerieData>(); + [NonSerialized] private bool m_NameDirty; + + /// <summary> + /// event callback when click serie. + /// ||点击系列时的回调。 + /// </summary> + public Action<SerieEventData> onClick { get; set; } + /// <summary> + /// event callback when mouse down on serie. + /// ||鼠标按下时的回调。 + /// </summary> + public Action<SerieEventData> onDown { get; set; } + /// <summary> + /// event callback when mouse enter serie. + /// ||鼠标进入时的回调。 + /// </summary> + public Action<SerieEventData> onEnter { get; set; } + /// <summary> + /// event callback when mouse leave serie. + /// ||鼠标离开时的回调。 + /// </summary> + public Action<SerieEventData> onExit { get; set; } + + /// <summary> + /// The index of serie. + /// ||系列索引。 + /// </summary> + public int index { get { return m_Index; } internal set { m_Index = value; } } + /// <summary> + /// Whether to show serie in chart. + /// ||系列是否显示在图表上。 + /// </summary> + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) { SetVerticesDirty(); SetSerieNameDirty(); } } + } + /// <summary> + /// the chart coord system of serie. + /// ||使用的坐标系。 + /// </summary> + public string coordSystem + { + get { return m_CoordSystem; } + set { if (PropertyUtil.SetClass(ref m_CoordSystem, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// the type of serie. + /// ||系列类型。 + /// </summary> + public string serieType + { + get { return m_SerieType; } + set { if (PropertyUtil.SetClass(ref m_SerieType, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// Series name used for displaying in tooltip and filtering with legend. + /// ||系列名称,用于 tooltip 的显示,legend 的图例筛选。 + /// </summary> + public string serieName + { + get { return m_SerieName; } + set { if (PropertyUtil.SetClass(ref m_SerieName, value)) { SetVerticesDirty(); SetSerieNameDirty(); } } + } + /// <summary> + /// Legend name. When the serie name is not empty, the legend name is the series name; Otherwise, it is index. + /// ||图例名称。当系列名称不为空时,图例名称即为系列名称;反之则为索引index。 + /// </summary> + public string legendName { get { return string.IsNullOrEmpty(serieName) ? ChartCached.IntToStr(index) : serieName; } } + /// <summary> + /// The default state of a serie. + /// ||系列的默认状态。 + /// </summary> + public SerieState state + { + get { return m_State; } + set { if (PropertyUtil.SetStruct(ref m_State, value)) { SetAllDirty(); } } + } + /// <summary> + /// The policy to take color from theme. + /// ||从主题中取色的策略。 + /// </summary> + public SerieColorBy colorBy + { + //get { return m_ColorBy; } + get { return m_ColorBy == SerieColorBy.Default ? defaultColorBy : m_ColorBy; } + set { if (PropertyUtil.SetStruct(ref m_ColorBy, value)) { SetAllDirty(); } } + } + /// <summary> + /// If stack the value. On the same category axis, the series with the same stack name would be put on top of each other. + /// ||数据堆叠,同个类目轴上系列配置相同的stack值后,后一个系列的值会在前一个系列的值上相加。 + /// </summary> + public string stack + { + get { return m_Stack; } + set { if (PropertyUtil.SetClass(ref m_Stack, value)) SetVerticesDirty(); } + } + /// <summary> + /// the index of XAxis. + /// ||使用X轴的index。 + /// </summary> + public int xAxisIndex + { + get { return m_XAxisIndex; } + set { if (PropertyUtil.SetStruct(ref m_XAxisIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// the index of YAxis. + /// ||使用Y轴的index。 + /// </summary> + public int yAxisIndex + { + get { return m_YAxisIndex; } + set { if (PropertyUtil.SetStruct(ref m_YAxisIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// Index of radar component that radar chart uses. + /// ||雷达图所使用的 radar 组件的 index。 + /// </summary> + public int radarIndex + { + get { return m_RadarIndex; } + set { if (PropertyUtil.SetStruct(ref m_RadarIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// Index of vesel component that liquid chart uses. + /// ||水位图所使用的 vessel 组件的 index。 + /// </summary> + public int vesselIndex + { + get { return m_VesselIndex; } + set { if (PropertyUtil.SetStruct(ref m_VesselIndex, value)) SetVerticesDirty(); } + } + /// <summary> + /// Index of polar component that serie uses. + /// ||所使用的 polar 组件的 index。 + /// </summary> + public int polarIndex + { + get { return m_PolarIndex; } + set { if (PropertyUtil.SetStruct(ref m_PolarIndex, value)) SetVerticesDirty(); } + } + /// <summary>s + /// Index of single axis component that serie uses. + /// ||所使用的 singleAxis 组件的 index。 + /// </summary> + public int singleAxisIndex + { + get { return m_SingleAxisIndex; } + set { if (PropertyUtil.SetStruct(ref m_SingleAxisIndex, value)) SetAllDirty(); } + } + /// <summary>s + /// Index of parallel coord component that serie uses. + /// ||所使用的 parallel coord 组件的 index。 + /// </summary> + public int parallelIndex + { + get { return m_ParallelIndex; } + set { if (PropertyUtil.SetStruct(ref m_ParallelIndex, value)) SetAllDirty(); } + } + /// <summary> + /// Index of layout component that serie uses. Default is -1 means not use layout, otherwise use the first layout component. + /// ||所使用的 layout 组件的 index。 默认为-1不指定index, 当为大于或等于0时, 为第一个layout组件的第index个格子。 + /// </summary> + public int gridIndex + { + get { return m_GridIndex; } + set { if (PropertyUtil.SetStruct(ref m_GridIndex, value)) SetAllDirty(); } + } + /// <summary> + /// The min number of data to show in chart. + /// ||系列所显示数据的最小索引 + /// </summary> + public int minShow + { + get { return m_MinShow; } + set { if (PropertyUtil.SetStruct(ref m_MinShow, value < 0 ? 0 : value)) { SetVerticesDirty(); } } + } + /// <summary> + /// The max number of data to show in chart. + /// ||系列所显示数据的最大索引 + /// </summary> + public int maxShow + { + get { return m_MaxShow; } + set { if (PropertyUtil.SetStruct(ref m_MaxShow, value < 0 ? 0 : value)) { SetVerticesDirty(); } } + } + /// <summary> + /// The max number of serie data cache. + /// The first data will be remove when the size of serie data is larger then maxCache. + /// ||系列中可缓存的最大数据量。默认为0没有限制,大于0时超过指定值会移除旧数据再插入新数据。 + /// </summary> + public int maxCache + { + get { return m_MaxCache; } + set { if (PropertyUtil.SetStruct(ref m_MaxCache, value < 0 ? 0 : value)) { SetVerticesDirty(); } } + } + + /// <summary> + /// the symbol of serie data item. + /// ||标记的图形。 + /// </summary> + public SerieSymbol symbol + { + get { return m_Symbol; } + set { if (PropertyUtil.SetClass(ref m_Symbol, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// The type of line chart. + /// ||折线图样式类型。 + /// </summary> + public LineType lineType + { + get { return m_LineType; } + set { if (PropertyUtil.SetStruct(ref m_LineType, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to restrict the curve. When true, the curve between two continuous data of the same value + /// is restricted to not exceed the data point, and is flat to the data point. + /// ||是否限制曲线。当为true时,两个连续相同数值的数据间的曲线会限制为不超出数据点,和数据点是平直的。 + /// </summary> + public bool smoothLimit + { + get { return m_SmoothLimit; } + set { if (PropertyUtil.SetStruct(ref m_SmoothLimit, value)) { SetVerticesDirty(); } } + } + /// <summary> + /// the min pixel dist of sample. + /// ||采样的最小像素距离,默认为0时不采样。当两个数据点间的水平距离小于改值时,开启采样,保证两点间的水平距离不小于改值。 + /// </summary> + public float sampleDist + { + get { return m_SampleDist; } + set { if (PropertyUtil.SetStruct(ref m_SampleDist, value < 0 ? 0 : value)) SetVerticesDirty(); } + } + /// <summary> + /// the type of sample. + /// ||采样类型。当sampleDist大于0时有效。 + /// </summary> + public SampleType sampleType + { + get { return m_SampleType; } + set { if (PropertyUtil.SetStruct(ref m_SampleType, value)) SetVerticesDirty(); } + } + /// <summary> + /// 设定的采样平均值。当sampleType 为 Peak 时,用于和过滤数据的平均值做对比是取最大值还是最小值。默认为0时会实时计算所有数据的平均值。 + /// </summary> + public float sampleAverage + { + get { return m_SampleAverage; } + set { if (PropertyUtil.SetStruct(ref m_SampleAverage, value)) SetVerticesDirty(); } + } + /// <summary> + /// The style of line. + /// ||线条样式。 + /// </summary> + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (PropertyUtil.SetClass(ref m_LineStyle, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// 柱形图类型。 + /// </summary> + public BarType barType + { + get { return m_BarType; } + set { if (PropertyUtil.SetStruct(ref m_BarType, value)) SetVerticesDirty(); } + } + /// <summary> + /// 柱形图是否为百分比堆积。相同stack的serie只要有一个barPercentStack为true,则就显示成百分比堆叠柱状图。 + /// </summary> + public bool barPercentStack + { + get { return m_BarPercentStack; } + set { if (PropertyUtil.SetStruct(ref m_BarPercentStack, value)) SetVerticesDirty(); } + } + /// <summary> + /// The width of the bar. Adaptive when default 0. + /// ||柱条的宽度,不设时自适应。支持设置成相对于类目宽度的百分比。 + /// </summary> + public float barWidth + { + get { return m_BarWidth; } + set { if (PropertyUtil.SetStruct(ref m_BarWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// The max width of the bar. Adaptive when default 0. + /// ||柱条的最大宽度,默认为0为不限制最大宽度。支持设置成相对于类目宽度的百分比。 + /// </summary> + public float barMaxWidth + { + get { return m_BarMaxWidth; } + set { if (PropertyUtil.SetStruct(ref m_BarMaxWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// The gap between bars between different series, is a percent value like '0.3f' , which means 30% of the bar width, can be set as a fixed value. + /// Set barGap as '-1' can overlap bars that belong to different series, which is useful when making a series of bar be background. + /// In a single coodinate system, this attribute is shared by multiple 'bar' series. + /// This attribute should be set on the last 'bar' series in the coodinate system, + /// then it will be adopted by all 'bar' series in the coordinate system. + /// ||不同系列的柱间距离。为百分比(如 '0.3f',表示柱子宽度的 30%) + /// 如果想要两个系列的柱子重叠,可以设置 barGap 为 '-1f'。这在用柱子做背景的时候有用。 + /// 在同一坐标系上,此属性会被多个 'bar' 系列共享。此属性应设置于此坐标系中最后一个 'bar' 系列上才会生效,并且是对此坐标系中所有 'bar' 系列生效。 + /// </summary> + public float barGap + { + get { return m_BarGap; } + set { if (PropertyUtil.SetStruct(ref m_BarGap, value)) SetVerticesDirty(); } + } + /// <summary> + /// 斑马线的粗细。 + /// </summary> + public float barZebraWidth + { + get { return m_BarZebraWidth; } + set { if (PropertyUtil.SetStruct(ref m_BarZebraWidth, value < 0 ? 0 : value)) SetVerticesDirty(); } + } + /// <summary> + /// 斑马线的间距。 + /// </summary> + public float barZebraGap + { + get { return m_BarZebraGap; } + set { if (PropertyUtil.SetStruct(ref m_BarZebraGap, value < 0 ? 0 : value)) SetVerticesDirty(); } + } + + /// <summary> + /// Whether offset when mouse click pie chart item. + /// ||鼠标点击时是否开启偏移,一般用在PieChart图表中。 + /// </summary> + public bool pieClickOffset + { + get { return m_ClickOffset; } + set { if (PropertyUtil.SetStruct(ref m_ClickOffset, value)) SetVerticesDirty(); } + } + /// <summary> + /// Whether to show as Nightingale chart. + /// ||是否展示成南丁格尔图,通过半径区分数据大小。 + /// </summary> + public RoseType pieRoseType + { + get { return m_RoseType; } + set { if (PropertyUtil.SetStruct(ref m_RoseType, value)) SetVerticesDirty(); } + } + /// <summary> + /// gap of item. + /// ||间距。 + /// </summary> + public float gap + { + get { return m_Gap; } + set { if (PropertyUtil.SetStruct(ref m_Gap, value)) SetVerticesDirty(); } + } + /// <summary> + /// the center of chart. + /// ||中心点。 + /// </summary> + public float[] center + { + get { return m_Center; } + set { if (value != null && value.Length == 2) { m_Center = value; SetVerticesDirty(); } } + } + /// <summary> + /// the radius of chart. + /// ||半径。radius[0]表示内径,radius[1]表示外径。 + /// </summary> + public float[] radius + { + get { return m_Radius; } + set { if (value != null && value.Length == 2) { m_Radius = value; SetVerticesDirty(); } } + } + /// <summary> + /// the min radius of chart. It can be used to limit the minimum radius of the rose chart. + /// ||最小半径。可用于限制玫瑰图的最小半径。 + /// </summary> + public float minRadius + { + get { return m_MinRadius; } + set { if (PropertyUtil.SetStruct(ref m_MinRadius, value)) SetVerticesDirty(); } + } + /// <summary> + /// 最小值。 + /// </summary> + public float min + { + get { return m_Min; } + set { if (PropertyUtil.SetStruct(ref m_Min, value)) SetVerticesDirty(); } + } + /// <summary> + /// 最大值。 + /// </summary> + public float max + { + get { return m_Max; } + set { if (PropertyUtil.SetStruct(ref m_Max, value)) SetVerticesDirty(); } + } + /// <summary> + /// 数据最小值 min 映射的宽度。 + /// </summary> + public float minSize + { + get { return m_MinSize; } + set { if (PropertyUtil.SetStruct(ref m_MinSize, value)) SetVerticesDirty(); } + } + /// <summary> + /// 数据最大值 max 映射的宽度。 + /// </summary> + public float maxSize + { + get { return m_MaxSize; } + set { if (PropertyUtil.SetStruct(ref m_MaxSize, value)) SetVerticesDirty(); } + } + /// <summary> + /// 起始角度。和时钟一样,12点钟位置是0度,顺时针到360度。 + /// </summary> + public float startAngle + { + get { return m_StartAngle; } + set { if (PropertyUtil.SetStruct(ref m_StartAngle, value)) SetVerticesDirty(); } + } + /// <summary> + /// 结束角度。和时钟一样,12点钟位置是0度,顺时针到360度。 + /// </summary> + public float endAngle + { + get { return m_EndAngle; } + set { if (PropertyUtil.SetStruct(ref m_EndAngle, value)) SetVerticesDirty(); } + } + /// <summary> + /// The minimum angle of sector(0-360). It prevents some sector from being too small when value is small. + /// ||最小的扇区角度(0-360)。用于防止某个值过小导致扇区太小影响交互。 + /// </summary> + public float minAngle + { + get { return m_MinAngle; } + set { if (PropertyUtil.SetStruct(ref m_MinAngle, value)) SetVerticesDirty(); } + } + /// <summary> + /// 是否顺时针。 + /// </summary> + public bool clockwise + { + get { return m_Clockwise; } + set { if (PropertyUtil.SetStruct(ref m_Clockwise, value)) SetVerticesDirty(); } + } + /// <summary> + /// 刻度分割段数。最大可设置36。 + /// </summary> + public int splitNumber + { + get { return m_SplitNumber; } + set { if (PropertyUtil.SetStruct(ref m_SplitNumber, value > 36 ? 36 : value)) SetVerticesDirty(); } + } + /// <summary> + /// 是否开启圆弧效果。 + /// </summary> + public bool roundCap + { + get { return m_RoundCap; } + set { if (PropertyUtil.SetStruct(ref m_RoundCap, value)) SetVerticesDirty(); } + } + /// <summary> + /// 是否开启忽略数据。当为 true 时,数据值为 ignoreValue 时不进行绘制。 + /// </summary> + public bool ignore + { + get { return m_Ignore; } + set { if (PropertyUtil.SetStruct(ref m_Ignore, value)) SetVerticesDirty(); } + } + /// <summary> + /// 忽略数据的默认值。当ignore为true才有效。 + /// </summary> + public double ignoreValue + { + get { return m_IgnoreValue; } + set { if (PropertyUtil.SetStruct(ref m_IgnoreValue, value)) SetVerticesDirty(); } + } + /// <summary> + /// 忽略数据时折线是断开还是连接。默认false为连接。 + /// </summary> + public bool ignoreLineBreak + { + get { return m_IgnoreLineBreak; } + set { if (PropertyUtil.SetStruct(ref m_IgnoreLineBreak, value)) SetVerticesDirty(); } + } + /// <summary> + /// 雷达图类型。 + /// </summary> + public RadarType radarType + { + get { return m_RadarType; } + set { if (PropertyUtil.SetStruct(ref m_RadarType, value)) SetVerticesDirty(); } + } + /// <summary> + /// The start animation. + /// ||起始动画。 + /// </summary> + public AnimationStyle animation + { + get { return m_Animation; } + set { if (PropertyUtil.SetClass(ref m_Animation, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// The style of data item. + /// ||图形样式。 + /// </summary> + public ItemStyle itemStyle + { + get { return m_ItemStyle; } + set { if (PropertyUtil.SetClass(ref m_ItemStyle, value, true)) SetVerticesDirty(); } + } + /// <summary> + /// 数据项里的数据维数。 + /// </summary> + public int showDataDimension { get { return m_ShowDataDimension; } set { m_ShowDataDimension = Mathf.Clamp(value, 2, 10); } } + /// <summary> + /// 在Editor的inpsector上是否显示name参数 + /// </summary> + public bool showDataName { get { return m_ShowDataName; } set { m_ShowDataName = value; } } + /// <summary> + /// If clip the overflow on the coordinate system. + /// ||是否裁剪超出坐标系部分的图形。 + /// </summary> + public bool clip + { + get { return m_Clip; } + set { if (PropertyUtil.SetStruct(ref m_Clip, value)) SetVerticesDirty(); } + } + /// <summary> + /// Show negative number as positive number. + /// ||将负数数值显示为正数。一般和`AxisLabel`的`showAsPositiveNumber`配合使用。仅在折线图和柱状图中有效。 + /// </summary> + public bool showAsPositiveNumber + { + get { return m_ShowAsPositiveNumber; } + set { if (PropertyUtil.SetStruct(ref m_ShowAsPositiveNumber, value)) SetComponentDirty(); } + } + /// <summary> + /// 是否开启大数据量优化,在数据图形特别多而出现卡顿时候可以开启。 + /// 开启后配合 largeThreshold 在数据量大于指定阈值的时候对绘制进行优化。 + /// 缺点:优化后不能自定义设置单个数据项的样式,不能显示Label。 + /// </summary> + public bool large + { + get { return m_Large; } + set { if (PropertyUtil.SetStruct(ref m_Large, value)) SetAllDirty(); } + } + /// <summary> + /// Turn on the threshold for mass optimization. Enter performance mode only when large is enabled and the amount of data is greater than the threshold. + /// ||开启大数量优化的阈值。只有当开启了large并且数据量大于该阀值时才进入性能模式。 + /// </summary> + public int largeThreshold + { + get { return m_LargeThreshold; } + set { if (PropertyUtil.SetStruct(ref m_LargeThreshold, value)) SetAllDirty(); } + } + /// <summary> + /// If the pie chart and labels are displayed externally, whether to enable the label overlap prevention policy is disabled by default. If labels are crowded and overlapped, the positions of labels are moved to prevent label overlap. + /// ||在饼图且标签外部显示的情况下,是否启用防止标签重叠策略,默认关闭,在标签拥挤重叠的情况下会挪动各个标签的位置,防止标签间的重叠。 + /// </summary> + public bool avoidLabelOverlap + { + get { return m_AvoidLabelOverlap; } + set { if (PropertyUtil.SetStruct(ref m_AvoidLabelOverlap, value)) SetVerticesDirty(); } + } + + /// <summary> + /// Distance between component and the left side of the container. + /// ||组件离容器左侧的距离。 + /// </summary> + public float left + { + get { return m_Left; } + set { if (PropertyUtil.SetStruct(ref m_Left, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the right side of the container. + /// ||组件离容器右侧的距离。 + /// </summary> + public float right + { + get { return m_Right; } + set { if (PropertyUtil.SetStruct(ref m_Right, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the top side of the container. + /// ||组件离容器上侧的距离。 + /// </summary> + public float top + { + get { return m_Top; } + set { if (PropertyUtil.SetStruct(ref m_Top, value)) SetAllDirty(); } + } + /// <summary> + /// Distance between component and the bottom side of the container. + /// ||组件离容器下侧的距离。 + /// </summary> + public float bottom + { + get { return m_Bottom; } + set { if (PropertyUtil.SetStruct(ref m_Bottom, value)) SetAllDirty(); } + } + /// <summary> + /// Whether to add new data at the head or at the end of the list. + /// ||添加新数据时是在列表的头部还是尾部加入。 + /// </summary> + public bool insertDataToHead + { + get { return m_InsertDataToHead; } + set { if (PropertyUtil.SetStruct(ref m_InsertDataToHead, value)) SetAllDirty(); } + } + /// <summary> + /// 组件的数据排序。 + /// </summary> + public SerieDataSortType dataSortType + { + get { return m_DataSortType; } + set { if (PropertyUtil.SetStruct(ref m_DataSortType, value)) SetVerticesDirty(); } + } + /// <summary> + /// 组件的朝向。 + /// </summary> + public Orient orient + { + get { return m_Orient; } + set { if (PropertyUtil.SetStruct(ref m_Orient, value)) SetVerticesDirty(); } + } + /// <summary> + /// 组件水平方向对齐方式。 + /// </summary> + public Align align + { + get { return m_Align; } + set { if (PropertyUtil.SetStruct(ref m_Align, value)) SetVerticesDirty(); } + } + /// <summary> + /// 占位模式。占位模式时,数据有效但不参与渲染和显示。 + /// </summary> + public bool placeHolder + { + get { return m_PlaceHolder; } + set { if (PropertyUtil.SetStruct(ref m_PlaceHolder, value)) SetAllDirty(); } + } + /// <summary> + /// Whether the label is not displayed when the enabled value is less than the specified value. + /// ||是否开启值小于指定值`minShowLabelValue`时不显示标签。 + /// </summary> + public bool minShowLabel + { + get { return m_MinShowLabel; } + set { if (PropertyUtil.SetStruct(ref m_MinShowLabel, value)) SetVerticesDirty(); } + } + /// <summary> + /// When 'minShowLabel' is enabled, labels are not displayed if the value is less than this value. + /// ||当开启`minShowLabel`时,值小于该值时不显示标签。 + /// </summary> + public double minShowLabelValue + { + get { return m_MinShowLabelValue; } + set { if (PropertyUtil.SetStruct(ref m_MinShowLabelValue, value)) { SetVerticesDirty(); } } + } + /// <summary> + /// Whether to enable realtime sorting, which is used for bar-racing effect. Currently only available in Bar. + /// ||是否开启实时排序,用来实现动态排序图效果。目前仅在Bar中生效。 + /// </summary> + public bool realtimeSort + { + get { return m_RealtimeSort; } + set { if (PropertyUtil.SetStruct(ref m_RealtimeSort, value)) SetVerticesDirty(); } + } + /// <summary> + /// 系列中的数据内容数组。SerieData可以设置1到n维数据。 + /// </summary> + public List<SerieData> data { get { return m_Data; } } + /// <summary> + /// 数据节点的边。 + /// </summary> + public List<SerieDataLink> links { get { return m_Links; } } + /// <summary> + /// 取色策略是否为按数据项分配。 + /// </summary> + public bool colorByData { get { return colorBy == SerieColorBy.Data; } } + public override bool vertsDirty + { + get + { + return m_VertsDirty || + symbol.vertsDirty || + lineStyle.vertsDirty || + itemStyle.vertsDirty || + IsVertsDirty(lineArrow) || + IsVertsDirty(areaStyle) || + IsVertsDirty(label) || + IsVertsDirty(labelLine) || + IsVertsDirty(titleStyle) || + IsVertsDirty(emphasisStyle) || + IsVertsDirty(blurStyle) || + IsVertsDirty(selectStyle) || + AnySerieDataVerticesDirty(); + } + } + + public override bool componentDirty + { + get + { + return m_ComponentDirty || + symbol.componentDirty || + IsComponentDirty(titleStyle) || + IsComponentDirty(label) || + IsComponentDirty(labelLine) || + IsComponentDirty(emphasisStyle) || + IsComponentDirty(blurStyle) || + IsComponentDirty(selectStyle); + } + } + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + if (!IsPerformanceMode()) + { + foreach (var serieData in m_Data) + serieData.ClearVerticesDirty(); + } + symbol.ClearVerticesDirty(); + lineStyle.ClearVerticesDirty(); + itemStyle.ClearVerticesDirty(); + ClearVerticesDirty(areaStyle); + ClearVerticesDirty(label); + ClearVerticesDirty(emphasisStyle); + ClearVerticesDirty(blurStyle); + ClearVerticesDirty(selectStyle); + ClearVerticesDirty(lineArrow); + ClearVerticesDirty(titleStyle); + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + if (!IsPerformanceMode()) + { + foreach (var serieData in m_Data) + serieData.ClearComponentDirty(); + } + symbol.ClearComponentDirty(); + lineStyle.ClearComponentDirty(); + itemStyle.ClearComponentDirty(); + ClearComponentDirty(areaStyle); + ClearComponentDirty(label); + ClearComponentDirty(emphasisStyle); + ClearComponentDirty(blurStyle); + ClearComponentDirty(selectStyle); + ClearComponentDirty(lineArrow); + ClearComponentDirty(titleStyle); + } + + public override void SetAllDirty() + { + base.SetAllDirty(); + labelDirty = true; + titleDirty = true; + } + + public override void SetVerticesDirty() + { + base.SetVerticesDirty(); + interactDirty = true; + } + + private bool AnySerieDataVerticesDirty() + { + if (IsPerformanceMode()) + return false; + if (this is ISimplifiedSerie) + return false; + foreach (var serieData in m_Data) + if (serieData.vertsDirty) return true; + return false; + } + + private bool AnySerieDataComponentDirty() + { + if (IsPerformanceMode()) + return false; + if (this is ISimplifiedSerie) + return false; + foreach (var serieData in m_Data) + if (serieData.componentDirty) return true; + return false; + } + /// <summary> + /// Whether the serie is highlighted. + /// ||该系列是否高亮,一般由图例悬停触发。 + /// </summary> + public bool highlight { get; internal set; } + /// <summary> + /// the count of data list. + /// ||数据项个数。 + /// </summary> + public int dataCount { get { return m_Data.Count; } } + public bool nameDirty { get { return m_NameDirty; } } + public bool labelDirty { get; set; } + public bool titleDirty { get; set; } + public bool dataDirty { get; set; } + public bool interactDirty { get; set; } + + private void SetSerieNameDirty() + { + m_NameDirty = true; + } + + public void ClearSerieNameDirty() + { + m_NameDirty = false; + } + + public override void ClearDirty() + { + base.ClearDirty(); + } + + /// <summary> + /// 维度Y对应数据中最大值。 + /// </summary> + public double yMax + { + get + { + var max = double.MinValue; + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[1]) && sdata.data[1] > max) + { + max = sdata.data[1]; + } + } + return max; + } + } + + /// <summary> + /// 维度X对应数据中的最大值。 + /// </summary> + public double xMax + { + get + { + var max = double.MinValue; + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[0]) && sdata.data[0] > max) + { + max = sdata.data[0]; + } + } + return max; + } + } + + /// <summary> + /// 维度Y对应数据的最小值。 + /// </summary> + public double yMin + { + get + { + var min = double.MaxValue; + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[1]) && sdata.data[1] < min) + { + min = sdata.data[1]; + } + } + return min; + } + } + + /// <summary> + /// 维度X对应数据的最小值。 + /// </summary> + public double xMin + { + get + { + var min = double.MaxValue; + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[0]) && sdata.data[0] < min) + { + min = sdata.data[0]; + } + } + return min; + } + } + + /// <summary> + /// 维度Y数据的总和。 + /// </summary> + public double yTotal + { + get + { + double total = 0; + if (IsPerformanceMode()) + { + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[1])) + total += sdata.data[1]; + } + } + else + { + var duration = animation.GetChangeDuration(); + var dataAddDuration = animation.GetAdditionDuration(); + var unscaledTime = animation.unscaledTime; + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[1])) + total += sdata.GetCurrData(1, dataAddDuration, duration, unscaledTime); + } + } + return total; + } + } + + /// <summary> + /// 维度X数据的总和。 + /// </summary> + public double xTotal + { + get + { + double total = 0; + foreach (var sdata in data) + { + if (sdata.show && !IsIgnoreValue(sdata, sdata.data[1])) + total += sdata.data[0]; + } + return total; + } + } + + public void ResetInteract() + { + interact.Reset(); + foreach (var serieData in m_Data) + serieData.interact.Reset(); + } + + /// <summary> + /// 重置数据项索引。避免部分数据项的索引异常。 + /// </summary> + public bool ResetDataIndex() + { + var flag = false; + for (int i = 0; i < m_Data.Count; i++) + { + if (m_Data[i].index != i) + { + m_Data[i].index = i; + flag = true; + } + } + return flag; + } + + /// <summary> + /// 清空所有数据 + /// </summary> + public override void ClearData() + { + while (m_Data.Count > 0) + { + RemoveData(0); + } + m_Data.Clear(); + m_NeedUpdateFilterData = true; + dataDirty = true; + SetVerticesDirty(); + } + + /// <summary> + /// 清空所有Link数据 + /// </summary> + public void ClearLinks() + { + m_Links.Clear(); + SetVerticesDirty(); + } + + /// <summary> + /// 移除指定索引的数据 + /// </summary> + /// <param name="index"></param> + public void RemoveData(int index) + { + if (index >= 0 && index < m_Data.Count) + { + if (!string.IsNullOrEmpty(m_Data[index].name)) + { + SetSerieNameDirty(); + } + SetVerticesDirty(); + var serieData = m_Data[index]; + SerieDataPool.Release(serieData); + if (serieData.labelObject != null) + { + SerieLabelPool.Release(serieData.labelObject.gameObject); + } + m_Data.RemoveAt(index); + m_NeedUpdateFilterData = true; + labelDirty = true; + dataDirty = true; + } + } + + /// <summary> + /// 添加一个数据到维度Y(此时维度X对应的数据是索引) + /// </summary> + /// <param name="value"></param> + /// <param name="dataName"></param> + /// <param name="dataId">the unique id of data</param> + public SerieData AddYData(double value, string dataName = null, string dataId = null) + { + var flag = CheckMaxCache(); + int xValue = m_Data.Count; + var serieData = SerieDataPool.Get(); + serieData.data.Add(xValue); + serieData.data.Add(value); + serieData.name = dataName; + serieData.index = xValue; + serieData.id = dataId; + AddSerieData(serieData); + if (flag) ResetDataIndex(); + m_ShowDataDimension = 2; + SetVerticesDirty(); + CheckDataName(dataName); + labelDirty = true; + dataDirty = true; + return serieData; + } + + public virtual void AddSerieData(SerieData serieData) + { + if (m_InsertDataToHead) + m_Data.Insert(0, serieData); + else + m_Data.Add(serieData); + serieData.OnAdd(animation); + context.totalDataIndex++; + SetVerticesDirty(); + dataDirty = true; + m_NeedUpdateFilterData = true; + } + + private void CheckDataName(string dataName) + { + if (string.IsNullOrEmpty(dataName)) + SetSerieNameDirty(); + else + m_ShowDataName = true; + } + + /// <summary> + /// 添加(x,y)数据到维度X和维度Y + /// </summary> + /// <param name="xValue"></param> + /// <param name="yValue"></param> + /// <param name="dataName"></param> + /// <param name="dataId">the unique id of data</param> + public SerieData AddXYData(double xValue, double yValue, string dataName = null, string dataId = null) + { + var flag = CheckMaxCache(); + var serieData = SerieDataPool.Get(); + serieData.data.Clear(); + serieData.data.Add(xValue); + serieData.data.Add(yValue); + serieData.name = dataName; + serieData.index = m_Data.Count; + serieData.id = dataId; + AddSerieData(serieData); + if (flag) ResetDataIndex(); + m_ShowDataDimension = 2; + SetVerticesDirty(); + CheckDataName(dataName); + labelDirty = true; + return serieData; + } + + /// <summary> + /// 添加 (open, close, lowest, heighest) 数据 + /// </summary> + /// <param name="open"></param> + /// <param name="close"></param> + /// <param name="lowest"></param> + /// <param name="heighest"></param> + /// <param name="dataName"></param> + /// <param name="dataId">the unique id of data</param> + /// <returns></returns> + public SerieData AddData(double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null) + { + var flag = CheckMaxCache(); + var serieData = SerieDataPool.Get(); + serieData.data.Clear(); + serieData.data.Add(indexOrTimestamp); + serieData.data.Add(open); + serieData.data.Add(close); + serieData.data.Add(lowest); + serieData.data.Add(heighest); + serieData.name = dataName; + serieData.index = m_Data.Count; + serieData.id = dataId; + AddSerieData(serieData); + if (flag) ResetDataIndex(); + m_ShowDataDimension = 5; + SetVerticesDirty(); + CheckDataName(dataName); + labelDirty = true; + return serieData; + } + + /// <summary> + /// 将一组数据添加到系列中。 + /// 如果数据只有一个,默认添加到维度Y中。 + /// </summary> + /// <param name="valueList"></param> + /// <param name="dataName"></param> + /// <param name="dataId">the unique id of data</param> + public SerieData AddData(List<double> valueList, string dataName = null, string dataId = null) + { + if (valueList == null || valueList.Count == 0) return null; + if (valueList.Count == 1) + return AddYData(valueList[0], dataName, dataId); + else if (valueList.Count == 2) + return AddXYData(valueList[0], valueList[1], dataName, dataId); + else + { + var flag = CheckMaxCache(); + m_ShowDataDimension = valueList.Count; + var serieData = SerieDataPool.Get(); + serieData.name = dataName; + serieData.index = m_Data.Count; + serieData.id = dataId; + for (int i = 0; i < valueList.Count; i++) + { + serieData.data.Add(valueList[i]); + } + AddSerieData(serieData); + if (flag) ResetDataIndex(); + SetVerticesDirty(); + CheckDataName(dataName); + labelDirty = true; + return serieData; + } + } + + /// <summary> + /// 添加任意维数据到系列中。 + /// </summary> + /// <param name="values">任意维数据</param> + /// <returns></returns> + public SerieData AddData(params double[] values) + { + if (values == null || values.Length == 0) return null; + string dataName = null; + string dataId = null; + if (values.Length == 1) + return AddYData(values[0], dataName, dataId); + else if (values.Length == 2) + return AddXYData(values[0], values[1], dataName, dataId); + else + { + var flag = CheckMaxCache(); + m_ShowDataDimension = values.Length; + var serieData = SerieDataPool.Get(); + serieData.name = dataName; + serieData.index = m_Data.Count; + serieData.id = dataId; + for (int i = 0; i < values.Length; i++) + { + serieData.data.Add(values[i]); + } + AddSerieData(serieData); + if (flag) ResetDataIndex(); + SetVerticesDirty(); + CheckDataName(dataName); + labelDirty = true; + return serieData; + } + } + + public SerieData AddChildData(SerieData parent, double value, string name, string id) + { + var serieData = new SerieData(); + serieData.name = name; + serieData.index = m_Data.Count; + serieData.id = id; + serieData.data.Add(m_Data.Count); + serieData.data.Add(value); + AddChildData(parent, serieData); + return serieData; + } + + public SerieData AddChildData(SerieData parent, List<double> value, string name, string id) + { + var serieData = new SerieData(); + serieData.name = name; + serieData.index = m_Data.Count; + serieData.id = id; + serieData.data.AddRange(value); + AddChildData(parent, serieData); + return serieData; + } + + public void AddChildData(SerieData parent, SerieData serieData) + { + serieData.parentId = parent.id; + serieData.context.parent = parent; + + if (!m_Data.Contains(serieData)) + AddSerieData(serieData); + + if (!parent.context.children.Contains(serieData)) + { + parent.context.children.Add(serieData); + } + } + + /// <summary> + /// Add a link data. + /// ||添加一个关系图的关系数据。 + /// </summary> + /// <param name="sourceId"></param> + /// <param name="targetId"></param> + /// <param name="value"></param> + /// <returns></returns> + public virtual SerieDataLink AddLink(string sourceId, string targetId, double value = 0) + { + var link = new SerieDataLink(); + link.source = sourceId; + link.target = targetId; + link.value = value; + m_Links.Add(link); + SetVerticesDirty(); + labelDirty = true; + return link; + } + + private bool CheckMaxCache() + { + if (m_MaxCache <= 0) return false; + var flag = false; + while (m_Data.Count >= m_MaxCache) + { + m_NeedUpdateFilterData = true; + if (m_InsertDataToHead) RemoveData(m_Data.Count - 1); + else RemoveData(0); + flag = true; + } + return flag; + } + + /// <summary> + /// 获得指定index指定维数的数据 + /// </summary> + /// <param name="index"></param> + /// <param name="dimension"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public double GetData(int index, int dimension, DataZoom dataZoom = null) + { + if (index < 0 || dimension < 0) return 0; + var serieData = GetSerieData(index, dataZoom); + if (serieData != null && dimension < serieData.data.Count) + { + var value = serieData.GetData(dimension); + if (showAsPositiveNumber) + value = Math.Abs(value); + return value; + } + else + { + return 0; + } + } + + /// <summary> + /// 获得维度Y索引对应的数据 + /// </summary> + /// <param name="index"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public double GetYData(int index, DataZoom dataZoom = null) + { + if (index < 0) return 0; + var serieData = GetDataList(dataZoom); + if (index < serieData.Count) + { + var value = serieData[index].data[1]; + if (showAsPositiveNumber) + value = Math.Abs(value); + return value; + } + return 0; + } + + public double GetYCurrData(int index, DataZoom dataZoom = null) + { + if (index < 0) return 0; + var serieData = GetDataList(dataZoom); + if (index < serieData.Count) + { + var value = serieData[index].GetCurrData(1, 0, animation.GetChangeDuration(), animation.unscaledTime); + if (showAsPositiveNumber) + value = Math.Abs(value); + return value; + } + return 0; + } + + /// <summary> + /// 获得维度Y索引对应的数据和数据名 + /// </summary> + /// <param name="index">索引</param> + /// <param name="yData">对应的数据值</param> + /// <param name="dataName">对应的数据名</param> + /// <param name="dataZoom">区域缩放</param> + public void GetYData(int index, out double yData, out string dataName, DataZoom dataZoom = null) + { + yData = 0; + dataName = null; + if (index < 0) return; + var serieData = GetDataList(dataZoom); + if (index < serieData.Count) + { + yData = serieData[index].data[1]; + if (showAsPositiveNumber) + yData = Math.Abs(yData); + dataName = serieData[index].name; + } + } + + /// <summary> + /// 获得指定索引的数据项 + /// </summary> + /// <param name="index"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public SerieData GetSerieData(int index, DataZoom dataZoom = null) + { + var data = GetDataList(dataZoom); + if (index >= 0 && index <= data.Count - 1) + return data[index]; + return null; + } + + public SerieData GetSerieData(string id, DataZoom dataZoom = null) + { + var data = GetDataList(dataZoom); + foreach (var serieData in data) + { + var target = GetSerieData(serieData, id); + if (target != null) return target; + } + return null; + } + + public SerieData GetSerieData(SerieData parent, string id) + { + if (id.Equals(parent.id)) return parent; + foreach (var child in parent.context.children) + { + var data = GetSerieData(child, id); + if (data != null) + { + return data; + } + } + return null; + } + + /// <summary> + /// 获得指定索引的维度X和维度Y的数据 + /// </summary> + /// <param name="index"></param> + /// <param name="dataZoom"></param> + /// <param name="xValue"></param> + /// <param name="yVlaue"></param> + public void GetXYData(int index, DataZoom dataZoom, out double xValue, out double yVlaue) + { + xValue = 0; + yVlaue = 0; + if (index < 0) return; + var showData = GetDataList(dataZoom); + if (index < showData.Count) + { + var serieData = showData[index]; + xValue = serieData.data[0]; + yVlaue = serieData.data[1]; + if (showAsPositiveNumber) + { + xValue = Math.Abs(xValue); + yVlaue = Math.Abs(yVlaue); + } + } + } + + public virtual double GetDataTotal(int dimension, SerieData serieData = null) + { + if (m_Max > 0) return m_Max; + + double total = 0; + foreach (var sdata in data) + { + if (sdata.show) + total += sdata.GetData(dimension); + } + return total; + } + + /// <summary> + /// 获得系列的数据列表 + /// </summary> + /// <param name="dataZoom"></param> + /// <returns></returns> + public List<SerieData> GetDataList(DataZoom dataZoom = null, bool sorted = false) + { + if (dataZoom != null && dataZoom.enable && + (dataZoom.IsContainsXAxis(xAxisIndex) || dataZoom.IsContainsYAxis(yAxisIndex))) + { + SerieHelper.UpdateFilterData(this, dataZoom); + return m_FilterData; + } + else + { + return useSortData && sorted && context.sortedData.Count > 0 ? context.sortedData : m_Data; + } + } + + /// <summary> + /// 更新指定索引的维度Y数据 + /// </summary> + /// <param name="index"></param> + /// <param name="value"></param> + public bool UpdateYData(int index, double value) + { + return UpdateData(index, 1, value); + } + + /// <summary> + /// 更新指定索引的维度X和维度Y的数据 + /// </summary> + /// <param name="index"></param> + /// <param name="xValue"></param> + /// <param name="yValue"></param> + public bool UpdateXYData(int index, double xValue, double yValue) + { + var flag1 = UpdateData(index, 0, xValue); + var flag2 = UpdateData(index, 1, yValue); + return flag1 || flag2; + } + + /// <summary> + /// 更新指定索引指定维数的数据 + /// </summary> + /// <param name="index">要更新数据的索引</param> + /// <param name="dimension">要更新数据的维数</param> + /// <param name="value">新的数据值</param> + public bool UpdateData(int index, int dimension, double value) + { + if (index >= 0 && index < m_Data.Count) + { + var animationOpen = animation.enable; + var animationDuration = animation.GetChangeDuration(); + var unscaledTime = animation.unscaledTime; + var flag = m_Data[index].UpdateData(dimension, value, animationOpen, unscaledTime, animationDuration); + if (flag) + { + SetVerticesDirty(); + dataDirty = true; + } + return flag; + } + else + { + return false; + } + } + + /// <summary> + /// 更新指定索引的数据项数据列表 + /// </summary> + /// <param name="index"></param> + /// <param name="values"></param> + public bool UpdateData(int index, List<double> values) + { + if (index >= 0 && index < m_Data.Count && values != null) + { + var serieData = m_Data[index]; + var animationOpen = animation.enable; + var animationDuration = animation.GetChangeDuration(); + var unscaledTime = animation.unscaledTime; + for (int i = 0; i < values.Count; i++) + serieData.UpdateData(i, values[i], animationOpen, unscaledTime, animationDuration); + SetVerticesDirty(); + dataDirty = true; + return true; + } + return false; + } + + public bool UpdateDataName(int index, string name) + { + if (index >= 0 && index < m_Data.Count) + { + var serieData = m_Data[index]; + serieData.name = name; + SetSerieNameDirty(); + if (serieData.labelObject != null) + { + serieData.labelObject.SetText(name == null ? "" : name); + } + return true; + } + return false; + } + + /// <summary> + /// 清除所有数据的高亮标志 + /// </summary> + public void ClearHighlight() + { + highlight = false; + foreach (var serieData in m_Data) + serieData.context.highlight = false; + } + + /// <summary> + /// 设置指定索引的数据为高亮状态 + /// </summary> + public void SetHighlight(int index, bool flag) + { + var serieData = GetSerieData(index); + if (serieData != null) + serieData.context.highlight = flag; + } + + public float GetBarWidth(float categoryWidth, int barCount = 0, float defaultRate = 0.6f) + { + var realWidth = 0f; + if (categoryWidth < 2) + { + realWidth = categoryWidth; + } + else if (m_BarWidth == 0) + { + var width = ChartHelper.GetActualValue(defaultRate, categoryWidth); + if (barCount == 0) + realWidth = width < 1 ? categoryWidth : width; + else + realWidth = width / barCount; + } + else + { + realWidth = ChartHelper.GetActualValue(m_BarWidth, categoryWidth); + } + if (m_BarMaxWidth == 0) + { + return realWidth; + } + else + { + var maxWidth = ChartHelper.GetActualValue(m_BarMaxWidth, categoryWidth); + return realWidth > maxWidth ? maxWidth : realWidth; + } + } + + public bool IsIgnoreIndex(int index, int dimension = 1) + { + var serieData = GetSerieData(index); + if (serieData != null) + return IsIgnoreValue(serieData, dimension); + return false; + } + + public bool IsIgnoreValue(SerieData serieData, int dimension = 1) + { + return IsIgnoreValue(serieData, serieData.GetData(dimension)); + } + + public bool IsIgnoreValue(double value) + { + return m_Ignore && MathUtil.Approximately(value, m_IgnoreValue); + } + + public bool IsIgnoreValue(SerieData serieData, double value) + { + return serieData.ignore || IsIgnoreValue(value); + } + + public bool IsIgnorePoint(int index) + { + if (index >= 0 && index < dataCount) + { + return ChartHelper.IsIngore(data[index].context.position); + } + return false; + } + + public bool IsMinShowLabelValue(int index, int dimension = 1) + { + var serieData = GetSerieData(index); + if (serieData != null) + return IsMinShowLabelValue(serieData, dimension); + return false; + } + + public bool IsMinShowLabelValue(SerieData serieData, int dimension = 1) + { + return IsMinShowLabelValue(serieData.GetData(dimension)); + } + + public bool IsMinShowLabelValue(double value) + { + return m_MinShowLabel && value <= m_MinShowLabelValue; + } + + public bool IsSerie<T>() where T : Serie + { + return this is T; + } + + public bool IsUseCoord<T>() where T : CoordSystem + { + return ChartCached.GetTypeName<T>().Equals(m_CoordSystem); + } + + public bool SetCoord<T>() where T : CoordSystem + { + if (GetType().IsDefined(typeof(CoordOptionsAttribute), false)) + { + var attribute = GetType().GetAttribute<CoordOptionsAttribute>(); + if (attribute.Contains<T>()) + { + m_CoordSystem = typeof(T).Name; + return true; + } + } + Debug.LogError("not support coord system:" + typeof(T)); + return false; + } + + /// <summary> + /// 是否为性能模式。性能模式下不绘制Symbol,不刷新Label,不单独设置数据项配置。 + /// </summary> + public bool IsPerformanceMode() + { + return m_Large && m_Data.Count >= m_LargeThreshold; + } + + public bool IsLegendName(string legendName) + { + if (colorBy == SerieColorBy.Data) + { + return IsSerieDataLegendName(legendName) || IsSerieLegendName(legendName); + } + else + { + return IsSerieLegendName(legendName); + } + } + + public bool IsSerieLegendName(string legendName) + { + return legendName.Equals(this.legendName); + } + + public bool IsSerieDataLegendName(string legendName) + { + foreach (var serieData in m_Data) + { + if (legendName.Equals(serieData.legendName)) + return true; + } + return false; + } + + /// <summary> + /// 启用或取消初始动画 + /// </summary> + public void AnimationEnable(bool flag) + { + if (animation.enable != flag) + { + animation.enable = flag; + SetVerticesDirty(); + } + } + + /// <summary> + /// 渐入动画 + /// </summary> + public void AnimationFadeIn() + { + if (dataCount <= 0) return; + ResetInteract(); + if (animation.enable) animation.FadeIn(); + SetVerticesDirty(); + } + + /// <summary> + /// 渐出动画 + /// </summary> + public void AnimationFadeOut() + { + if (dataCount <= 0) return; + ResetInteract(); + if (animation.enable) animation.FadeOut(); + SetVerticesDirty(); + } + + /// <summary> + /// 暂停动画 + /// </summary> + public void AnimationPause() + { + if (dataCount <= 0) return; + if (animation.enable) animation.Pause(); + SetVerticesDirty(); + } + + /// <summary> + /// 继续动画 + /// </summary> + public void AnimationResume() + { + if (dataCount <= 0) return; + if (animation.enable) animation.Resume(); + SetVerticesDirty(); + } + + /// <summary> + /// 重置动画 + /// </summary> + public void AnimationReset() + { + if (dataCount <= 0) return; + if (animation.enable) animation.Reset(); + SetVerticesDirty(); + } + + /// <summary> + /// 重置动画 + /// </summary> + public void AnimationRestart() + { + if (dataCount <= 0) return; + if (animation.enable) animation.Restart(); + SetVerticesDirty(); + } + + public int CompareTo(object obj) + { + return index.CompareTo((obj as Serie).index); + } + + public T Clone<T>() where T : Serie + { + var newSerie = Activator.CreateInstance<T>(); + SerieHelper.CopySerie(this, newSerie); + return newSerie; + } + + public Serie Clone() + { + var newSerie = Activator.CreateInstance(GetType()) as Serie; + SerieHelper.CopySerie(this, newSerie); + newSerie.animation = new AnimationStyle(); + return newSerie; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/Serie.cs.meta b/Assets/XCharts/Runtime/Serie/Serie.cs.meta new file mode 100644 index 0000000..5a02397 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/Serie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa9c09045961a4ea9a34a098f099f2a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieContext.cs b/Assets/XCharts/Runtime/Serie/SerieContext.cs new file mode 100644 index 0000000..cf67a6c --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieContext.cs @@ -0,0 +1,142 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public struct PointInfo + { + public Vector3 position; + public bool isIgnoreBreak; + public double xValue; + public double yValue; + public double zValue; + + // public PointInfo(Vector3 pos, bool ignore) + // { + // this.position = pos; + // this.isIgnoreBreak = ignore; + // } + + public PointInfo(Vector3 pos, bool ignore, double x = 0, double y = 0, double z = 0) + { + this.position = pos; + this.isIgnoreBreak = ignore; + this.xValue = x; + this.yValue = y; + this.zValue = z; + } + } + + public class SerieContext + { + /// <summary> + /// 鼠标是否进入serie + /// </summary> + public bool pointerEnter; + /// <summary> + /// 鼠标当前指示的数据项索引(单个) + /// </summary> + public int pointerItemDataIndex = -1; + /// <summary> + /// 鼠标当前指示的数据项维度 + /// </summary> + public int pointerItemDataDimension = 1; + /// <summary> + /// 鼠标所在轴线上的数据项索引(可能有多个) + /// </summary> + public List<int> pointerAxisDataIndexs = new List<int>(); + public bool isTriggerByAxis = false; + public int dataZoomStartIndex = 0; + public int dataZoomStartIndexOffset = 0; + + /// <summary> + /// 中心点 + /// </summary> + public Vector3 center; + /// <summary> + /// 线段终点 + /// </summary> + public Vector3 lineEndPostion; + public double lineEndValueX; + public double lineEndValueY; + public double lineEndValueZ; + /// <summary> + /// 内半径 + /// </summary> + public float insideRadius; + /// <summary> + /// 外半径 + /// </summary> + public float outsideRadius; + public float startAngle; + /// <summary> + /// 最大值 + /// </summary> + public double dataMax; + /// <summary> + /// 最小值 + /// </summary> + public double dataMin; + public double checkValue; + /// <summary> + /// 左下角坐标X + /// </summary> + public float x; + /// <summary> + /// 左下角坐标Y + /// </summary> + public float y; + /// <summary> + /// 宽 + /// </summary> + public float width; + /// <summary> + /// 高 + /// </summary> + public float height; + /// <summary> + /// 矩形区域 + /// </summary> + public Rect rect; + /// <summary> + /// 绘制顶点数 + /// </summary> + public int vertCount; + /// <summary> + /// theme的颜色索引 + /// </summary> + public int colorIndex; + /// <summary> + /// 数据对应的位置坐标。 + /// </summary> + public List<Vector3> dataPoints = new List<Vector3>(); + /// <summary> + /// 数据对应的位置坐标是否忽略(忽略时连线是透明的),dataIgnore 和 dataPoints 一一对应。 + /// </summary> + public List<bool> dataIgnores = new List<bool>(); + /// <summary> + /// 数据对应的index索引。dataIndexs 和 dataPoints 一一对应。 + /// </summary> + public List<int> dataIndexs = new List<int>(); + /// <summary> + /// 排序后的数据 + /// </summary> + public List<SerieData> sortedData = new List<SerieData>(); + public List<SerieData> rootData = new List<SerieData>(); + /// <summary> + /// 绘制点 + /// </summary> + public List<PointInfo> drawPoints = new List<PointInfo>(); + public SerieParams param = new SerieParams(); + public ChartLabel titleObject { get; set; } + + public Tooltip.Type tooltipType; + public Tooltip.Trigger tooltipTrigger; + public int totalDataIndex; + public int clickTotalDataIndex; + /// <summary> + /// 水平方向的 + /// </summary> + public bool isHorizontal; + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieContext.cs.meta b/Assets/XCharts/Runtime/Serie/SerieContext.cs.meta new file mode 100644 index 0000000..0966877 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87f333572a32a4cb39aa0a05ed97983a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieData.cs b/Assets/XCharts/Runtime/Serie/SerieData.cs new file mode 100644 index 0000000..d610409 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieData.cs @@ -0,0 +1,802 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using XUGL; + +namespace XCharts.Runtime +{ + /// <summary> + /// A data item of serie. + /// ||系列中的一个数据项。可存储数据名和1-n维个数据。 + /// </summary> + [System.Serializable] + public class SerieData : ChildComponent + { + public static List<string> extraFieldList = new List<string>() + { + "m_Id", + "m_ParentId", + "m_State", + "m_Ignore", + "m_Selected", + "m_Radius", + }; + public static Dictionary<Type, string> extraComponentMap = new Dictionary<Type, string> + { { typeof(ItemStyle), "m_ItemStyles" }, + { typeof(LabelStyle), "m_Labels" }, + { typeof(LabelLine), "m_LabelLines" }, + { typeof(SerieSymbol), "m_Symbols" }, + { typeof(LineStyle), "m_LineStyles" }, + { typeof(AreaStyle), "m_AreaStyles" }, + { typeof(TitleStyle), "m_TitleStyles" }, + { typeof(EmphasisStyle), "m_EmphasisStyles" }, + { typeof(BlurStyle), "m_BlurStyles" }, + { typeof(SelectStyle), "m_SelectStyles" }, + }; + + [SerializeField] private int m_Index; + [SerializeField] private string m_Name; + [SerializeField] private string m_Id; + [SerializeField] private string m_ParentId; + [SerializeField] private bool m_Ignore; + [SerializeField] private bool m_Selected; + [SerializeField] private float m_Radius; + [SerializeField][Since("v3.2.0")] private SerieState m_State = SerieState.Auto; + [SerializeField][IgnoreDoc] private List<ItemStyle> m_ItemStyles = new List<ItemStyle>(); + [SerializeField][IgnoreDoc] private List<LabelStyle> m_Labels = new List<LabelStyle>(); + [SerializeField][IgnoreDoc] private List<LabelLine> m_LabelLines = new List<LabelLine>(); + [SerializeField][IgnoreDoc] private List<SerieSymbol> m_Symbols = new List<SerieSymbol>(); + [SerializeField][IgnoreDoc] private List<LineStyle> m_LineStyles = new List<LineStyle>(); + [SerializeField][IgnoreDoc] private List<AreaStyle> m_AreaStyles = new List<AreaStyle>(); + [SerializeField][IgnoreDoc] private List<TitleStyle> m_TitleStyles = new List<TitleStyle>(); + [SerializeField][IgnoreDoc] private List<EmphasisStyle> m_EmphasisStyles = new List<EmphasisStyle>(); + [SerializeField][IgnoreDoc] private List<BlurStyle> m_BlurStyles = new List<BlurStyle>(); + [SerializeField][IgnoreDoc] private List<SelectStyle> m_SelectStyles = new List<SelectStyle>(); + [SerializeField] private List<double> m_Data = new List<double>(); + + [NonSerialized] public SerieDataContext context = new SerieDataContext(); + [NonSerialized] public InteractData interact = new InteractData(); + + public ChartLabel labelObject { get; set; } + public ChartLabel titleObject { get; set; } + public int sortIndex { get; set; } + + private bool m_Show = true; + /// <summary> + /// the index of SerieData. + /// ||数据项索引。 + /// </summary> + public override int index { get { return m_Index; } set { m_Index = value; } } + /// <summary> + /// the name of data item. + /// ||数据项名称。 + /// </summary> + public string name { get { return m_Name; } set { m_Name = value; } } + /// <summary> + /// the id of data. + /// ||数据项的唯一id。唯一id不是必须设置的。 + /// </summary> + public string id { get { return m_Id; } set { m_Id = value; } } + /// <summary> + /// the id of parent SerieData. + /// ||父节点id。父节点id不是必须设置的。 + /// </summary> + public string parentId { get { return m_ParentId; } set { m_ParentId = value; } } + /// <summary> + /// 是否忽略数据。当为 true 时,数据不进行绘制。 + /// </summary> + public bool ignore + { + get { return m_Ignore; } + set { if (PropertyUtil.SetStruct(ref m_Ignore, value)) SetVerticesDirty(); } + } + /// <summary> + /// 自定义半径。可用在饼图中自定义某个数据项的半径。 + /// </summary> + public float radius { get { return m_Radius; } set { m_Radius = value; } } + /// <summary> + /// Whether the data item is selected. + /// ||该数据项是否被选中。 + /// </summary> + public bool selected { get { return m_Selected; } set { m_Selected = value; } } + /// <summary> + /// the state of serie data. + /// ||数据项的默认状态。 + /// </summary> + public SerieState state { get { return m_State; } set { m_State = value; } } + /// <summary> + /// 数据项图例名称。当数据项名称不为空时,图例名称即为系列名称;反之则为索引index。 + /// </summary> + public string legendName { get { return string.IsNullOrEmpty(name) ? ChartCached.IntToStr(index) : name; } } + + /// <summary> + /// 单个数据项的标签设置。 + /// </summary> + public LabelStyle labelStyle { get { return m_Labels.Count > 0 ? m_Labels[0] : null; } } + public LabelLine labelLine { get { return m_LabelLines.Count > 0 ? m_LabelLines[0] : null; } } + /// <summary> + /// 单个数据项的样式设置。 + /// </summary> + public ItemStyle itemStyle { get { return m_ItemStyles.Count > 0 ? m_ItemStyles[0] : null; } } + /// <summary> + /// 单个数据项的标记设置。 + /// </summary> + public SerieSymbol symbol { get { return m_Symbols.Count > 0 ? m_Symbols[0] : null; } } + public LineStyle lineStyle { get { return m_LineStyles.Count > 0 ? m_LineStyles[0] : null; } } + public AreaStyle areaStyle { get { return m_AreaStyles.Count > 0 ? m_AreaStyles[0] : null; } } + public TitleStyle titleStyle { get { return m_TitleStyles.Count > 0 ? m_TitleStyles[0] : null; } } + /// <summary> + /// 高亮状态的样式 + /// </summary> + public EmphasisStyle emphasisStyle { get { return m_EmphasisStyles.Count > 0 ? m_EmphasisStyles[0] : null; } } + /// <summary> + /// 淡出状态的样式。 + /// </summary> + public BlurStyle blurStyle { get { return m_BlurStyles.Count > 0 ? m_BlurStyles[0] : null; } } + /// <summary> + /// 选中状态的样式。 + /// </summary> + public SelectStyle selectStyle { get { return m_SelectStyles.Count > 0 ? m_SelectStyles[0] : null; } } + + /// <summary> + /// An arbitrary dimension data list of data item. + /// ||可指定任意维数的数值列表。 + /// </summary> + public List<double> data { get { return m_Data; } set { m_Data = value; } } + /// <summary> + /// [default:true] Whether the data item is showed. + /// ||该数据项是否要显示。 + /// </summary> + public bool show { get { return m_Show; } set { m_Show = value; } } + + private List<double> m_PreviousData = new List<double>(); + private List<float> m_DataUpdateTime = new List<float>(); + private List<bool> m_DataUpdateFlag = new List<bool>(); + private List<float> m_DataAddTime = new List<float>(); + private List<bool> m_DataAddFlag = new List<bool>(); + private List<Vector2> m_PolygonPoints = new List<Vector2>(); + + public override bool vertsDirty + { + get + { + return m_VertsDirty || + IsVertsDirty(labelLine) || + IsVertsDirty(itemStyle) || + IsVertsDirty(symbol) || + IsVertsDirty(lineStyle) || + IsVertsDirty(areaStyle) || + IsVertsDirty(emphasisStyle) || + IsVertsDirty(blurStyle) || + IsVertsDirty(selectStyle); + } + } + public override bool componentDirty + { + get + { + return m_ComponentDirty || + IsComponentDirty(labelStyle) || + IsComponentDirty(labelLine) || + IsComponentDirty(titleStyle) || + IsComponentDirty(emphasisStyle) || + IsComponentDirty(blurStyle) || + IsComponentDirty(selectStyle); + } + } + + public override void ClearVerticesDirty() + { + base.ClearVerticesDirty(); + ClearVerticesDirty(labelLine); + ClearVerticesDirty(itemStyle); + ClearVerticesDirty(lineStyle); + ClearVerticesDirty(areaStyle); + ClearVerticesDirty(emphasisStyle); + ClearVerticesDirty(blurStyle); + ClearVerticesDirty(selectStyle); + } + + public override void ClearComponentDirty() + { + base.ClearComponentDirty(); + ClearComponentDirty(labelLine); + ClearComponentDirty(itemStyle); + ClearComponentDirty(lineStyle); + ClearComponentDirty(areaStyle); + ClearComponentDirty(symbol); + ClearComponentDirty(emphasisStyle); + ClearComponentDirty(blurStyle); + ClearComponentDirty(selectStyle); + } + + public void Reset() + { + index = 0; + m_Id = null; + m_ParentId = null; + labelObject = null; + m_Name = string.Empty; + m_Show = true; + context.Reset(); + interact.Reset(); + m_Data.Clear(); + m_PreviousData.Clear(); + m_DataUpdateTime.Clear(); + m_DataUpdateFlag.Clear(); + m_DataAddTime.Clear(); + m_DataAddFlag.Clear(); + m_Labels.Clear(); + m_LabelLines.Clear(); + m_ItemStyles.Clear(); + m_Symbols.Clear(); + m_LineStyles.Clear(); + m_AreaStyles.Clear(); + m_TitleStyles.Clear(); + m_EmphasisStyles.Clear(); + m_BlurStyles.Clear(); + m_SelectStyles.Clear(); + } + + public void OnAdd(AnimationStyle animation, double startValue = 0) + { + if (!animation.enable) return; + if (!animation.context.enableSerieDataAddedAnimation) + { + animation.Addition(); + return; + } +#if UNITY_EDITOR + if (!Application.isPlaying) + return; +#endif + m_DataAddTime.Clear(); + m_DataAddFlag.Clear(); + if (animation.GetAdditionDuration() > 0) + { + for (int i = 0; i < m_Data.Count; i++) + { + m_DataAddTime.Add(animation.unscaledTime ? Time.unscaledTime : Time.time); + m_DataAddFlag.Add(true); + } + } + } + + [Obsolete("GetOrAddComponent is obsolete. Use EnsureComponent instead.")] + public T GetOrAddComponent<T>() where T : ChildComponent, ISerieDataComponent + { + return EnsureComponent<T>(); + } + + /// <summary> + /// Get the component of the serie data. return null if not exist. + /// ||获取数据项的指定类型的组件,如果不存在则返回null。 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + public T GetComponent<T>() where T : ChildComponent, ISerieDataComponent + { + return GetComponentInternal(typeof(T), false) as T; + } + + /// <summary> + /// Ensure the serie data has the component, if not, add it. + /// ||确保数据项有指定类型的组件,如果没有则添加。 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + [Since("v3.6.0")] + public T EnsureComponent<T>() where T : ChildComponent, ISerieDataComponent + { + return GetComponentInternal(typeof(T), true) as T; + } + + /// <summary> + /// Ensure the serie data has the component, if not, add it. + /// ||确保数据项有指定类型的组件,如果没有则添加。 + /// </summary> + /// <param name="type"></param> + /// <returns></returns> + [Since("v3.6.0")] + public ISerieDataComponent EnsureComponent(Type type) + { + return GetComponentInternal(type, true); + } + + private ISerieDataComponent GetComponentInternal(Type type, bool addIfNotExist) + { + if (type == typeof(ItemStyle)) + { + if (m_ItemStyles.Count == 0) + { + if (addIfNotExist) + m_ItemStyles.Add(new ItemStyle() { show = true }); + else + return null; + } + return m_ItemStyles[0]; + } + else if (type == typeof(LabelStyle)) + { + if (m_Labels.Count == 0) + { + if (addIfNotExist) + m_Labels.Add(new LabelStyle() { show = true }); + else + return null; + } + return m_Labels[0]; + } + else if (type == typeof(LabelLine)) + { + if (m_LabelLines.Count == 0) + { + if (addIfNotExist) + m_LabelLines.Add(new LabelLine() { show = true }); + else + return null; + } + return m_LabelLines[0]; + } + else if (type == typeof(EmphasisStyle)) + { + if (m_EmphasisStyles.Count == 0) + { + if (addIfNotExist) + m_EmphasisStyles.Add(new EmphasisStyle() { show = true }); + else + return null; + } + return m_EmphasisStyles[0]; + } + else if (type == typeof(BlurStyle)) + { + if (m_BlurStyles.Count == 0) + { + if (addIfNotExist) + m_BlurStyles.Add(new BlurStyle() { show = true }); + else + return null; + } + return m_BlurStyles[0]; + } + else if (type == typeof(SelectStyle)) + { + if (m_SelectStyles.Count == 0) + { + if (addIfNotExist) + m_SelectStyles.Add(new SelectStyle() { show = true }); + else + return null; + } + return m_SelectStyles[0]; + } + else if (type == typeof(SerieSymbol)) + { + if (m_Symbols.Count == 0) + { + if (addIfNotExist) + m_Symbols.Add(new SerieSymbol() { show = true }); + else + return null; + } + return m_Symbols[0]; + } + else if (type == typeof(LineStyle)) + { + if (m_LineStyles.Count == 0) + { + if (addIfNotExist) + m_LineStyles.Add(new LineStyle() { show = true }); + else + return null; + } + return m_LineStyles[0]; + } + else if (type == typeof(AreaStyle)) + { + if (m_AreaStyles.Count == 0) + { + if (addIfNotExist) + m_AreaStyles.Add(new AreaStyle() { show = true }); + else + return null; + } + return m_AreaStyles[0]; + } + else if (type == typeof(TitleStyle)) + { + if (m_TitleStyles.Count == 0) + { + if (addIfNotExist) + m_TitleStyles.Add(new TitleStyle() { show = true }); + else + return null; + } + return m_TitleStyles[0]; + } + else + { + throw new System.Exception("SerieData not support component:" + type); + } + } + + public void RemoveAllComponent() + { + m_ItemStyles.Clear(); + m_Labels.Clear(); + m_LabelLines.Clear(); + m_Symbols.Clear(); + m_EmphasisStyles.Clear(); + m_BlurStyles.Clear(); + m_SelectStyles.Clear(); + m_LineStyles.Clear(); + m_AreaStyles.Clear(); + m_TitleStyles.Clear(); + } + + public void RemoveComponent<T>() where T : ISerieDataComponent + { + RemoveComponent(typeof(T)); + } + + public void RemoveComponent(Type type) + { + if (type == typeof(ItemStyle)) + m_ItemStyles.Clear(); + else if (type == typeof(LabelStyle)) + m_Labels.Clear(); + else if (type == typeof(LabelLine)) + m_LabelLines.Clear(); + else if (type == typeof(EmphasisStyle)) + m_EmphasisStyles.Clear(); + else if (type == typeof(BlurStyle)) + m_BlurStyles.Clear(); + else if (type == typeof(SelectStyle)) + m_SelectStyles.Clear(); + else if (type == typeof(SerieSymbol)) + m_Symbols.Clear(); + else if (type == typeof(LineStyle)) + m_LineStyles.Clear(); + else if (type == typeof(AreaStyle)) + m_AreaStyles.Clear(); + else if (type == typeof(TitleStyle)) + m_TitleStyles.Clear(); + else + throw new System.Exception("SerieData not support component:" + type); + } + public double GetData(int index, bool inverse = false) + { + if (index >= 0 && index < m_Data.Count) + { + return inverse ? -m_Data[index] : m_Data[index]; + } + else return 0; + } + + public double GetData(int index, double min, double max) + { + if (index >= 0 && index < m_Data.Count) + { + var value = m_Data[index]; + if (value < min) return min; + else if (value > max) return max; + else return value; + } + else return 0; + } + + public double GetPreviousData(int index, bool inverse = false) + { + if (index >= 0 && index < m_PreviousData.Count) + { + return inverse ? -m_PreviousData[index] : m_PreviousData[index]; + } + else return 0; + } + + public double GetFirstData(bool unscaledTime, float animationDuration = 500f) + { + if (m_Data.Count > 0) return GetCurrData(0, 0, animationDuration, unscaledTime); + return 0; + } + + public double GetLastData() + { + if (m_Data.Count > 0) return m_Data[m_Data.Count - 1]; + return 0; + } + + public double GetCurrData(int index, AnimationStyle animation, bool inverse = false, bool loop = false) + { + if (animation == null || !animation.enable) + return GetData(index, inverse); + else + return GetCurrData(index, animation.GetAdditionDuration(), animation.GetChangeDuration(), + inverse, 0, 0, animation.unscaledTime, loop); + } + + public double GetCurrData(int index, AnimationStyle animation, bool inverse, double min, double max, bool loop = false) + { + if (animation == null || !animation.enable) + return GetData(index, inverse); + else + return GetCurrData(index, animation.GetAdditionDuration(), animation.GetChangeDuration(), + inverse, min, max, animation.unscaledTime, loop); + } + + public double GetCurrData(int index, float dataAddDuration = 500f, float animationDuration = 500f, bool unscaledTime = false, bool inverse = false) + { + return GetCurrData(index, dataAddDuration, animationDuration, inverse, 0, 0, unscaledTime); + } + + public double GetCurrData(int index, float dataAddDuration, float animationDuration, bool inverse, double min, double max, bool unscaledTime, bool loop = false) + { + if (dataAddDuration > 0) + { + if (index < m_DataAddFlag.Count && m_DataAddFlag[index]) + { + var time = (unscaledTime ? Time.unscaledTime : Time.time) - m_DataAddTime[index]; + var total = dataAddDuration / 1000; + + var rate = time / total; + if (rate > 1) rate = 1; + if (rate < 1) + { + var prev = min > 0 ? min : 0; + var next = GetData(index); + var curr = MathUtil.Lerp(prev, next, rate); + curr = inverse ? -curr : curr; + return curr; + } + else + { + for (int i = 0; i < m_DataAddFlag.Count; i++) + m_DataAddFlag[i] = false; + return GetData(index, inverse); + } + } + } + if (animationDuration > 0) + { + if (index < m_DataUpdateFlag.Count && m_DataUpdateFlag[index]) + { + var time = (unscaledTime ? Time.unscaledTime : Time.time) - m_DataUpdateTime[index]; + var total = animationDuration / 1000; + + var rate = time / total; + if (rate > 1) rate = 1; + if (rate < 1) + { + CheckLastData(unscaledTime); + var prev = GetPreviousData(index); + var next = GetData(index); + if (loop && next <= min && prev != 0) + { + next = max; + } + var curr = MathUtil.Lerp(prev, next, rate); + if (min != 0 || max != 0) + { + if (inverse) + { + var temp = min; + min = -max; + max = -temp; + } + var pre = m_PreviousData[index]; + if (pre < min) + { + m_PreviousData[index] = min; + curr = min; + } + else if (pre > max) + { + m_PreviousData[index] = max; + curr = max; + } + } + curr = inverse ? -curr : curr; + return curr; + } + else + { + for (int i = 0; i < m_DataUpdateFlag.Count; i++) + m_DataUpdateFlag[i] = false; + return GetData(index, inverse); + } + } + else + { + return GetData(index, inverse); + } + } + return GetData(index, inverse); + } + + public double GetAddAnimationData(double min, double max, float animationDuration = 500f, bool unscaledTime = false) + { + if (animationDuration > 0 && m_DataAddFlag.Count > 0 && m_DataAddFlag[0]) + { + var time = (unscaledTime ? Time.unscaledTime : Time.time) - m_DataAddTime[0]; + var total = animationDuration / 1000; + + var rate = time / total; + if (rate > 1) rate = 1; + if (rate < 1) + { + var curr = MathUtil.Lerp(min, max, rate); + return curr; + } + else + { + for (int i = 0; i < m_DataAddFlag.Count; i++) + m_DataAddFlag[i] = false; + return max; + } + } + else + { + return max; + } + } + + /// <summary> + /// the maxinum value. + /// ||最大值。 + /// </summary> + public double GetMaxData(bool inverse = false) + { + if (m_Data.Count == 0) return 0; + var temp = double.MinValue; + for (int i = 0; i < m_Data.Count; i++) + { + var value = GetData(i, inverse); + if (value > temp) temp = value; + } + return temp; + } + + /// <summary> + /// the mininum value. + /// ||最小值。 + /// </summary> + public double GetMinData(bool inverse = false) + { + if (m_Data.Count == 0) return 0; + var temp = double.MaxValue; + for (int i = 0; i < m_Data.Count; i++) + { + var value = GetData(i, inverse); + if (value < temp) temp = value; + } + return temp; + } + + public void GetMinMaxData(int startDimensionIndex, bool inverse, out double min, out double max) + { + if (m_Data.Count == 0) + { + min = 0; + max = 0; + } + min = double.MaxValue; + max = double.MinValue; + for (int i = startDimensionIndex; i < m_Data.Count; i++) + { + var value = GetData(i, inverse); + if (value < min) min = value; + if (value > max) max = value; + } + } + + public double GetTotalData() + { + var total = 0d; + foreach (var value in m_Data) + total += value; + return total; + } + + public bool UpdateData(int dimension, double value, bool updateAnimation, bool unscaledTime, float animationDuration = 500f) + { + if (dimension >= 0 && dimension < data.Count) + { + CheckLastData(unscaledTime); + m_PreviousData[dimension] = GetCurrData(dimension, 0, animationDuration, unscaledTime); + m_DataUpdateTime[dimension] = (unscaledTime ? Time.unscaledTime : Time.time); + m_DataUpdateFlag[dimension] = updateAnimation; + data[dimension] = value; + return true; + } + return false; + } + + public bool UpdateData(int dimension, double value) + { + if (dimension >= 0 && dimension < data.Count) + { + data[dimension] = value; + return true; + } + return false; + } + + private void CheckLastData(bool unscaledTime) + { + if (m_PreviousData.Count != m_Data.Count) + { + m_PreviousData.Clear(); + m_DataUpdateTime.Clear(); + m_DataUpdateFlag.Clear(); + for (int i = 0; i < m_Data.Count; i++) + { + m_PreviousData.Add(m_Data[i]); + m_DataUpdateTime.Add((unscaledTime ? Time.unscaledTime : Time.time)); + m_DataUpdateFlag.Add(false); + } + } + } + + public bool IsDataChanged() + { + for (int i = 0; i < m_DataUpdateFlag.Count; i++) + if (m_DataUpdateFlag[i]) return true; + for (int i = 0; i < m_DataAddFlag.Count; i++) + if (m_DataAddFlag[i]) return true; + return false; + } + + public float GetLabelWidth() + { + if (labelObject != null) return labelObject.GetTextWidth(); + else return 0; + } + + public float GetLabelHeight() + { + if (labelObject != null) return labelObject.GetTextHeight(); + return 0; + } + + public void SetLabelActive(bool flag, bool force = false) + { + if (labelObject != null) labelObject.SetActive(flag, force); + foreach (var labelObject in context.dataLabels) + { + labelObject.SetActive(flag, force); + } + } + + public void SetIconActive(bool flag) + { + if (labelObject != null) labelObject.SetIconActive(flag); + } + + public void SetPolygon(params Vector2[] points) + { + m_PolygonPoints.Clear(); + m_PolygonPoints.AddRange(points); + } + + public void SetPolygon(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) + { + m_PolygonPoints.Clear(); + m_PolygonPoints.Add(p1); + m_PolygonPoints.Add(p2); + m_PolygonPoints.Add(p3); + m_PolygonPoints.Add(p4); + } + + public void SetPolygon(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, Vector2 p5) + { + SetPolygon(p1, p2, p3, p4); + m_PolygonPoints.Add(p5); + } + + public bool IsInPolygon(Vector2 p) + { + return UGLHelper.IsPointInPolygon(p, m_PolygonPoints); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieData.cs.meta b/Assets/XCharts/Runtime/Serie/SerieData.cs.meta new file mode 100644 index 0000000..93cfdac --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dbf44007311214228976678a623479b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieDataContext.cs b/Assets/XCharts/Runtime/Serie/SerieDataContext.cs new file mode 100644 index 0000000..0e99efa --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieDataContext.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public class SerieDataContext + { + public Vector3 labelPosition; + public Vector3 labelLinePosition; + public Vector3 labelLinePosition2; + /// <summary> + /// 开始角度 + /// </summary> + public float startAngle; + /// <summary> + /// 结束角度 + /// </summary> + public float toAngle; + /// <summary> + /// 一半时的角度 + /// </summary> + public float halfAngle; + /// <summary> + /// 当前角度 + /// </summary> + public float currentAngle; + /// <summary> + /// 饼图数据项的内半径 + /// </summary> + public float insideRadius; + /// <summary> + /// 饼图数据项的偏移半径 + /// </summary> + public float offsetRadius; + public float outsideRadius; + public Vector3 position; + public List<Vector3> dataPoints = new List<Vector3>(); + public List<ChartLabel> dataLabels = new List<ChartLabel>(); + public List<SerieData> children = new List<SerieData>(); + /// <summary> + /// 绘制区域。 + /// </summary> + public Rect rect; + public Rect backgroundRect; + public Rect subRect; + public int level; + public SerieData parent; + public Color32 color; + public double area; + public float angle; + public Vector3 offsetCenter; + public Vector3 areaCenter; + public float stackHeight; + public bool isClip; + public bool canShowLabel = true; + public Image symbol; + /// <summary> + /// Whether the data item is highlighted. + /// ||该数据项是否被高亮,一般由鼠标悬停或图例悬停触发高亮。 + /// </summary> + public bool highlight; + public bool selected; + /// <summary> + /// the id of the node in the graph. + /// ||图中节点的id。 + /// </summary> + public string graphNodeId; + public double inTotalValue; + public double outTotalValue; + + public void Reset() + { + canShowLabel = true; + highlight = false; + parent = null; + symbol = null; + rect = Rect.zero; + subRect = Rect.zero; + children.Clear(); + dataPoints.Clear(); + dataLabels.Clear(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieDataContext.cs.meta b/Assets/XCharts/Runtime/Serie/SerieDataContext.cs.meta new file mode 100644 index 0000000..a202cbb --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieDataContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6fa67a86e80b4456cbe76ef4b330f3fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieDataLink.cs b/Assets/XCharts/Runtime/Serie/SerieDataLink.cs new file mode 100644 index 0000000..9d8e650 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieDataLink.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// the link of serie data. Used for sankey chart. Sankey chart only supports directed acyclic graph. make sure the data link is directed acyclic graph. + /// ||数据节点之间的连线。可用于桑基图等,桑基图只支持有向无环图,请保证数据的连线是有向无环图。 + /// </summary> + [System.Serializable] + [Since("v3.10.0")] + public class SerieDataLink : ChildComponent + { + [SerializeField] private string m_Source; + [SerializeField] private string m_Target; + [SerializeField] private double m_Value; + + /// <summary> + /// the source node name. + /// ||边的源节点名称。 + /// </summary> + public string source + { + get { return m_Source; } + set { m_Source = value; } + } + + /// <summary> + /// the target node name. + /// ||边的目标节点名称。 + /// </summary> + public string target + { + get { return m_Target; } + set { m_Target = value; } + } + + /// <summary> + /// the value of link. decide the width of link. + /// ||边的值。决定边的宽度。 + /// </summary> + public double value + { + get { return m_Value; } + set { m_Value = value; } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieDataLink.cs.meta b/Assets/XCharts/Runtime/Serie/SerieDataLink.cs.meta new file mode 100644 index 0000000..0940800 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieDataLink.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7402f12ebc4aa4421939efecae53624d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieHandler.cs b/Assets/XCharts/Runtime/Serie/SerieHandler.cs new file mode 100644 index 0000000..4d65f8f --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieHandler.cs @@ -0,0 +1,829 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts.Runtime +{ + public abstract class SerieHandler + { + public BaseChart chart { get; internal set; } + public SerieHandlerAttribute attribute { get; internal set; } + public bool inited { get; internal set; } + public virtual int defaultDimension { get; internal set; } + + public virtual void InitComponent() { } + public virtual void RemoveComponent() { } + public virtual void CheckComponent(StringBuilder sb) { } + public virtual void BeforeUpdate() { } + public virtual void Update() { } + public virtual void AfterUpdate() { } + public virtual void DrawBase(VertexHelper vh) { } + public virtual void DrawSerie(VertexHelper vh) { } + public virtual void DrawUpper(VertexHelper vh) { } + public virtual void DrawTop(VertexHelper vh) { } + public virtual void OnPointerClick(PointerEventData eventData) { } + public virtual void OnPointerDown(PointerEventData eventData) { } + public virtual void OnPointerUp(PointerEventData eventData) { } + public virtual void OnPointerEnter(PointerEventData eventData) { } + public virtual void OnPointerExit(PointerEventData eventData) { } + public virtual void OnDrag(PointerEventData eventData) { } + public virtual void OnBeginDrag(PointerEventData eventData) { } + public virtual void OnEndDrag(PointerEventData eventData) { } + public virtual void OnScroll(PointerEventData eventData) { } + public virtual void OnDataUpdate() { } + public virtual void RefreshLabelNextFrame() { } + public virtual void RefreshLabelInternal() { } + public virtual void ForceUpdateSerieContext() { } + public virtual void UpdateSerieContext() { } + public virtual void UpdateTooltipSerieParams(int dataIndex, bool showCategory, + string category, string marker, + string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + ref List<SerieParams> paramList, ref string title) + { } + public virtual void OnLegendButtonClick(int index, string legendName, bool show) { } + public virtual void OnLegendButtonEnter(int index, string legendName) { } + public virtual void OnLegendButtonExit(int index, string legendName) { } + internal abstract void SetSerie(Serie serie); + public virtual int GetPointerItemDataIndex() { return -1; } + public virtual int GetPointerItemDataDimension() { return 1; } + } + + public abstract class SerieHandler<T> : SerieHandler where T : Serie + { + private static readonly string s_SerieLabelObjectName = "label"; + private static readonly string s_SerieTitleObjectName = "title"; + private static readonly string s_SerieRootObjectName = "serie"; + private static readonly string s_SerieEndLabelObjectName = "end_label"; + protected GameObject m_SerieRoot; + protected GameObject m_SerieLabelRoot; + protected bool m_InitedLabel; + protected bool m_NeedInitComponent; + protected bool m_RefreshLabel; + protected bool m_LastCheckContextFlag = false; + protected bool m_LegendEnter = false; + protected bool m_LegendExiting = false; + protected bool m_ForceUpdateSerieContext = false; + protected int m_LegendEnterIndex; + protected ChartLabel m_EndLabel; + + private float[] m_LastRadius = new float[2] { 0, 0 }; + private float[] m_LastCenter = new float[2] { 0, 0 }; + private bool m_LastPointerEnter; + private int m_LastPointerDataIndex; + private int m_LastPointerDataDimension; + + public T serie { get; internal set; } + public GameObject labelObject { get { return m_SerieLabelRoot; } } + + internal override void SetSerie(Serie serie) + { + this.serie = (T)serie; + this.serie.context.param.serieType = typeof(T); + m_NeedInitComponent = true; + AnimationStyleHelper.UpdateSerieAnimation(serie); + } + + public override void BeforeUpdate() + { + m_LastPointerEnter = serie.context.pointerEnter; + m_LastPointerDataIndex = serie.context.pointerItemDataIndex; + m_LastPointerDataDimension = GetPointerItemDataDimension(); + serie.context.pointerEnter = false; + serie.context.pointerItemDataIndex = -1; + } + + public override void Update() + { + CheckConfigurationChanged(); + if (m_NeedInitComponent) + { + m_NeedInitComponent = false; + InitComponent(); + } + if (m_RefreshLabel) + { + m_RefreshLabel = false; + RefreshLabelInternal(); + RefreshEndLabelInternal(); + } + if (serie.dataDirty) + { + OnDataUpdate(); + SeriesHelper.UpdateSerieNameList(chart, ref chart.m_LegendRealShowName); + chart.OnSerieDataUpdate(serie.index); + serie.OnDataUpdate(); + serie.dataDirty = false; + } + if (serie.label != null && (serie.labelDirty || serie.label.componentDirty)) + { + serie.labelDirty = false; + serie.label.ClearComponentDirty(); + InitSerieLabel(); + InitSerieEndLabel(); + } + if (serie.endLabel != null && serie.endLabel.componentDirty) + { + serie.endLabel.ClearComponentDirty(); + InitSerieEndLabel(); + } + if (serie.titleStyle != null && (serie.titleDirty || serie.titleStyle.componentDirty)) + { + serie.titleDirty = false; + serie.titleStyle.ClearComponentDirty(); + InitSerieTitle(); + } + if (serie.nameDirty) + { + foreach (var component in chart.components) + { + if (component is Legend) + component.SetAllDirty(); + } + chart.RefreshChart(); + serie.ClearSerieNameDirty(); + } + if (serie.vertsDirty) + { + chart.RefreshPainter(serie); + serie.ClearVerticesDirty(); + } + if (serie.interactDirty) + { + if (serie.animation.enable && serie.animation.interaction.enable) + { + Color32 color1, toColor1; + bool needInteract = false; + serie.context.colorIndex = chart.GetLegendRealShowNameIndex(serie.legendName); + foreach (var serieData in serie.data) + { + var state = SerieHelper.GetSerieState(serie, serieData, true); + SerieHelper.GetItemColor(out color1, out toColor1, serie, serieData, chart.theme, state); + serieData.interact.SetColor(ref needInteract, color1, toColor1); + } + } + chart.RefreshChart(); + serie.interactDirty = false; + m_ForceUpdateSerieContext = true; + } + UpdateSerieContextInternal(); + } + + public override void AfterUpdate() + { + if (m_LastPointerEnter != serie.context.pointerEnter || m_LastPointerDataIndex != serie.context.pointerItemDataIndex) + { + if (chart.onSerieEnter != null || chart.onSerieExit != null || serie.onEnter != null || serie.onExit != null) + { + if (serie.context.pointerEnter) + { + if ((serie.onExit != null || chart.onSerieExit != null) && m_LastPointerDataIndex >= 0) + { + var dataValue = serie.GetData(m_LastPointerDataIndex, m_LastPointerDataDimension); + var exitEventData = SerieEventDataPool.Get(chart.pointerPos, serie.index, m_LastPointerDataIndex, m_LastPointerDataDimension, dataValue); + if (serie.onExit != null) serie.onExit(exitEventData); + if (chart.onSerieExit != null) chart.onSerieExit(exitEventData); + SerieEventDataPool.Release(exitEventData); + } + var dataIndex = GetPointerItemDataIndex(); + var dimension = GetPointerItemDataDimension(); + var value = serie.GetData(dataIndex, dimension); + var enterEventData = SerieEventDataPool.Get(chart.pointerPos, serie.index, dataIndex, dimension, value); + if (serie.onEnter != null) serie.onEnter(enterEventData); + if (chart.onSerieEnter != null) chart.onSerieEnter(enterEventData); + SerieEventDataPool.Release(enterEventData); + } + else if (m_LastPointerDataIndex >= 0) + { + var dataValue = serie.GetData(m_LastPointerDataIndex, m_LastPointerDataDimension); + var exitEventData = SerieEventDataPool.Get(chart.pointerPos, serie.index, m_LastPointerDataIndex, m_LastPointerDataDimension, dataValue); + if (serie.onExit != null) serie.onExit(exitEventData); + if (chart.onSerieExit != null) chart.onSerieExit(exitEventData); + SerieEventDataPool.Release(exitEventData); + } + } + } + } + + public override void ForceUpdateSerieContext() + { + m_ForceUpdateSerieContext = true; + } + + private void CheckConfigurationChanged() + { + if (m_LastRadius[0] != serie.radius[0] || m_LastRadius[1] != serie.radius[1]) + { + m_LastRadius[0] = serie.radius[0]; + m_LastRadius[1] = serie.radius[1]; + serie.SetVerticesDirty(); + } + if (m_LastCenter[0] != serie.center[0] || m_LastCenter[1] != serie.center[1]) + { + m_LastCenter[0] = serie.center[0]; + m_LastCenter[1] = serie.center[1]; + serie.SetVerticesDirty(); + } + } + + private void UpdateSerieContextInternal() + { + UpdateSerieContext(); + m_ForceUpdateSerieContext = false; + } + + public override void RefreshLabelNextFrame() + { + m_RefreshLabel = true; + } + + public override void InitComponent() + { + m_InitedLabel = false; + serie.context.totalDataIndex = serie.dataCount - 1; + InitRoot(); + InitSerieLabel(); + InitSerieTitle(); + InitSerieEndLabel(); + } + + public override void RemoveComponent() + { + ChartHelper.SetActive(m_SerieRoot, false); + } + + public override void OnLegendButtonClick(int index, string legendName, bool show) + { + if (serie.colorByData && serie.IsSerieDataLegendName(legendName)) + { + LegendHelper.CheckDataShow(serie, legendName, show); + chart.UpdateLegendColor(legendName, show); + chart.RefreshPainter(serie); + } + else if (serie.IsLegendName(legendName)) + { + chart.SetSerieActive(serie, show); + chart.RefreshPainter(serie); + } + } + + public override void OnLegendButtonEnter(int index, string legendName) + { + if (serie.colorByData && serie.IsSerieDataLegendName(legendName)) + { + m_LegendEnterIndex = LegendHelper.CheckDataHighlighted(serie, legendName, true); + m_LegendEnter = true; + chart.RefreshPainter(serie); + } + else if (serie.IsLegendName(legendName)) + { + m_LegendEnter = true; + chart.RefreshPainter(serie); + } + } + + public override void OnLegendButtonExit(int index, string legendName) + { + if (serie.colorByData && serie.IsSerieDataLegendName(legendName)) + { + LegendHelper.CheckDataHighlighted(serie, legendName, false); + m_LegendEnter = false; + m_LegendExiting = true; + chart.RefreshPainter(serie); + } + else if (serie.IsLegendName(legendName)) + { + m_LegendEnter = false; + m_LegendExiting = true; + chart.RefreshPainter(serie); + } + } + + private void InitRoot() + { + if (m_SerieRoot != null) + { + var rect = ChartHelper.EnsureComponent<RectTransform>(m_SerieRoot); + rect.localPosition = Vector3.zero; + rect.sizeDelta = chart.chartSizeDelta; + rect.anchorMin = chart.chartMinAnchor; + rect.anchorMax = chart.chartMaxAnchor; + rect.pivot = chart.chartPivot; + return; + } + var objName = s_SerieRootObjectName + "_" + serie.index; + m_SerieRoot = ChartHelper.AddObject(objName, chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta, -1, chart.childrenNodeNames); + m_SerieRoot.hideFlags = chart.chartHideFlags; + ChartHelper.SetActive(m_SerieRoot, true); + ChartHelper.HideAllObject(m_SerieRoot); + } + + private void InitSerieLabel() + { + InitRoot(); + m_SerieLabelRoot = ChartHelper.AddObject(s_SerieLabelObjectName, m_SerieRoot.transform, + chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); + m_SerieLabelRoot.hideFlags = chart.chartHideFlags; + SerieLabelPool.ReleaseAll(m_SerieLabelRoot.transform); + int count = 0; + SerieHelper.UpdateCenter(serie, chart); + for (int j = 0; j < serie.data.Count; j++) + { + var serieData = serie.data[j]; + serieData.index = j; + serieData.labelObject = null; + if (AddSerieLabel(m_SerieLabelRoot, serieData, ref count)) + { + m_InitedLabel = true; + count++; + } + } + RefreshLabelInternal(); + } + + protected bool AddSerieLabel(GameObject serieLabelRoot, SerieData serieData, ref int count) + { + if (serieData == null) + return false; + if (serieLabelRoot == null) + return false; + if (serie.IsPerformanceMode()) + return false; + + if (count == -1) count = serie.dataCount; + var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); + if (serieLabel == null) + { + return false; + } + + var dataAutoColor = GetSerieDataAutoColor(serieData); + serieData.context.dataLabels.Clear(); + if (serie.multiDimensionLabel) + { + for (int i = 0; i < serieData.data.Count; i++) + { + var textName = string.Format("{0}_{1}_{2}_{3}", s_SerieLabelObjectName, serie.index, serieData.index, i); + var label = ChartHelper.AddChartLabel(textName, serieLabelRoot.transform, serieLabel, chart.theme.common, + "", dataAutoColor, TextAnchor.MiddleCenter); + label.SetActive(false, true); + serieData.context.dataLabels.Add(label); + } + } + else + { + var textName = ChartCached.GetSerieLabelName(s_SerieLabelObjectName, serie.index, serieData.index); + var label = ChartHelper.AddChartLabel(textName, serieLabelRoot.transform, serieLabel, chart.theme.common, + "", dataAutoColor, TextAnchor.MiddleCenter); + label.SetActive(false, true); + serieData.labelObject = label; + } + + if (serieData.context.children.Count > 0) + { + foreach (var childSerieData in serieData.context.children) + { + AddSerieLabel(serieLabelRoot, childSerieData, ref count); + count++; + } + } + return true; + } + + private void InitSerieEndLabel() + { + if (serie.endLabel == null) + { + if (m_EndLabel != null) + { + m_EndLabel.SetActive(false); + m_EndLabel = null; + } + return; + } + InitRoot(); + var dataAutoColor = (Color)chart.GetLegendRealShowNameColor(serie.legendName); + m_EndLabel = ChartHelper.AddChartLabel(s_SerieEndLabelObjectName, m_SerieRoot.transform, serie.endLabel, + chart.theme.common, "", dataAutoColor, TextAnchor.MiddleLeft); + m_EndLabel.SetActive(serie.endLabel.show); + RefreshEndLabelInternal(); + } + + private void InitSerieTitle() + { + InitRoot(); + var serieTitleRoot = ChartHelper.AddObject(s_SerieTitleObjectName, m_SerieRoot.transform, + chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); + serieTitleRoot.hideFlags = chart.chartHideFlags; + SerieLabelPool.ReleaseAll(serieTitleRoot.transform); + ChartHelper.RemoveComponent<Text>(serieTitleRoot); + + SerieHelper.UpdateCenter(serie, chart); + + if (serie.titleJustForSerie) + { + var titleStyle = SerieHelper.GetTitleStyle(serie, null); + if (titleStyle != null) + { + var color = chart.GetItemColor(serie, null); + var content = string.Empty; + if (string.IsNullOrEmpty(titleStyle.formatter)) + { + content = serie.serieName; + } + else + { + content = titleStyle.formatter; + FormatterHelper.ReplaceContent(ref content, -1, titleStyle.numericFormatter, serie, chart); + } + var label = ChartHelper.AddChartLabel("title_" + 0, serieTitleRoot.transform, titleStyle, chart.theme.common, + content, color, TextAnchor.MiddleCenter); + serie.context.titleObject = label; + label.SetActive(titleStyle.show, true); + var labelPosition = GetSerieDataTitlePosition(null, titleStyle); + var offset = titleStyle.GetOffset(serie.context.insideRadius); + label.SetPosition(labelPosition + offset); + } + } + else + { + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + var titleStyle = SerieHelper.GetTitleStyle(serie, serieData); + if (titleStyle == null) continue; + var color = chart.GetItemColor(serie, serieData); + var content = string.Empty; + if (string.IsNullOrEmpty(titleStyle.formatter)) + { + content = serieData.name; + } + else + { + content = titleStyle.formatter; + FormatterHelper.ReplaceContent(ref content, i, titleStyle.numericFormatter, serie, chart); + } + var label = ChartHelper.AddChartLabel("title_" + i, serieTitleRoot.transform, titleStyle, chart.theme.common, + content, color, TextAnchor.MiddleCenter); + serieData.titleObject = label; + label.SetActive(titleStyle.show, true); + var labelPosition = GetSerieDataTitlePosition(serieData, titleStyle); + var offset = titleStyle.GetOffset(serie.context.insideRadius); + label.SetPosition(labelPosition + offset); + } + } + } + + public override void RefreshLabelInternal() + { + if (!m_InitedLabel) + return; + + var dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + var needCheck = serie.context.dataIndexs.Count > 0; + var allLabelZeroPosition = true; + var anyLabelActive = false; + foreach (var serieData in serie.data) + { + if (serieData.labelObject == null && serieData.context.dataLabels.Count <= 0) + { + continue; + } + if (needCheck && !serie.context.dataIndexs.Contains(serieData.index)) + { + serieData.SetLabelActive(false); + continue; + } + var currLabel = SerieHelper.GetSerieLabel(serie, serieData); + var isIgnore = serie.IsIgnoreIndex(serieData.index, defaultDimension); + if (serie.show && + currLabel != null && + currLabel.show && + serieData.context.canShowLabel && + !serieData.context.isClip && + !isIgnore) + { + if (serie.multiDimensionLabel) + { + var total = serieData.GetTotalData(); + var color = chart.GetItemColor(serie, serieData); + for (int i = 0; i < serieData.context.dataLabels.Count; i++) + { + if (i >= serieData.context.dataPoints.Count) continue; + var labelObject = serieData.context.dataLabels[i]; + var value = serieData.GetCurrData(i, dataAddDuration, dataChangeDuration, unscaledTime); + var content = string.IsNullOrEmpty(currLabel.formatter) ? + ChartCached.NumberToStr(value, currLabel.numericFormatter) : + SerieLabelHelper.GetFormatterContent(serie, serieData, value, total, + currLabel, color, chart); + var offset = GetSerieDataLabelOffset(serieData, currLabel); + var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value); + if (active) + { + anyLabelActive = true; + if (!ChartHelper.IsZeroVector(serieData.context.dataPoints[i])) + { + allLabelZeroPosition = false; + } + } + labelObject.SetActive(active); + labelObject.SetText(content); + labelObject.SetPosition(serieData.context.dataPoints[i] + offset); + labelObject.UpdateIcon(currLabel.icon); + if (currLabel.textStyle.autoColor) + { + var dataAutoColor = GetSerieDataAutoColor(serieData); + if (!ChartHelper.IsClearColor(dataAutoColor)) + labelObject.SetTextColor(dataAutoColor); + } + } + } + else + { + var value = serieData.GetCurrData(defaultDimension, dataAddDuration, dataChangeDuration, unscaledTime); + var total = serie.GetDataTotal(defaultDimension, serieData); + var color = chart.GetItemColor(serie, serieData); + var content = string.IsNullOrEmpty(currLabel.formatter) ? + ChartCached.NumberToStr(value, currLabel.numericFormatter) : + SerieLabelHelper.GetFormatterContent(serie, serieData, value, total, + currLabel, color, chart); + var labelPos = UpdateLabelPosition(serieData, currLabel); + var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value); + if (active) + { + anyLabelActive = true; + if (!ChartHelper.IsZeroVector(labelPos)) + { + allLabelZeroPosition = false; + } + } + serieData.SetLabelActive(active); + serieData.labelObject.UpdateIcon(currLabel.icon); + serieData.labelObject.SetText(content); + if (currLabel.textStyle.autoColor) + { + var dataAutoColor = GetSerieDataAutoColor(serieData); + if (!ChartHelper.IsClearColor(dataAutoColor)) + serieData.labelObject.SetTextColor(dataAutoColor); + } + } + } + else + { + serieData.SetLabelActive(false); + } + } + if (anyLabelActive && allLabelZeroPosition) + { + foreach (var serieData in serie.data) + { + serieData.SetLabelActive(false); + } + } + } + + public virtual void RefreshEndLabelInternal() + { + if (m_EndLabel == null) + return; + var endLabelStyle = serie.endLabel; + if (endLabelStyle == null) + return; + var dataCount = serie.context.dataPoints.Count; + var active = endLabelStyle.show && dataCount > 0; + m_EndLabel.SetActive(active); + if (active) + { + var value = serie.context.lineEndValueY; + var content = SerieLabelHelper.GetFormatterContent(serie, null, value, 0, + endLabelStyle, Color.clear); + m_EndLabel.SetText(content); + m_EndLabel.SetPosition(serie.context.lineEndPostion + endLabelStyle.offset); + } + m_EndLabel.isAnimationEnd = serie.animation.IsFinish(); + } + + protected Vector3 UpdateLabelPosition(SerieData serieData, LabelStyle currLabel) + { + var labelPosition = GetSerieDataLabelPosition(serieData, currLabel); + var offset = GetSerieDataLabelOffset(serieData, currLabel); + serieData.labelObject.SetPosition(labelPosition + offset); + if (currLabel.autoRotate && serieData.context.angle != 0) + { + if (serieData.context.angle > 90 && serieData.context.angle < 270) + serieData.labelObject.SetRotate(180 - serieData.context.angle + currLabel.rotate); + else + serieData.labelObject.SetRotate(-serieData.context.angle + currLabel.rotate); + } + return labelPosition; + } + + public virtual Vector3 GetSerieDataLabelPosition(SerieData serieData, LabelStyle label) + { + return ChartHelper.IsZeroVector(serieData.context.labelPosition) ? + serieData.context.position : + serieData.context.labelPosition; + } + + public virtual Vector3 GetSerieDataLabelOffset(SerieData serieData, LabelStyle label) + { + return label.GetOffset(serie.context.insideRadius); + } + + public virtual Vector3 GetSerieDataTitlePosition(SerieData serieData, TitleStyle titleStyle) + { + return serieData.context.position; + } + + public virtual Color GetSerieDataAutoColor(SerieData serieData) + { + var colorIndex = serie.colorByData ? serieData.index : serie.index; + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, SerieState.Normal, false); + return (Color)color; + } + + protected void UpdateCoordSerieParams(ref List<SerieParams> paramList, ref string title, + int dataIndex, bool showCategory, string category, string marker, + string itemFormatter, string numericFormatter, string ignoreDataDefaultContent) + { + var dimension = 1; + if (dataIndex < 0) + dataIndex = serie.context.pointerItemDataIndex; + + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + + var ignore = serie.IsIgnoreValue(serieData, dimension); + if (ignore && string.IsNullOrEmpty(ignoreDataDefaultContent)) + return; + + itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + if (serie.placeHolder || TooltipHelper.IsIgnoreFormatter(itemFormatter)) + return; + if (itemFormatter == null) itemFormatter = ""; + var newItemFormatter = itemFormatter.Replace("\\n", "\n"); + var newNumericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + var temp = newItemFormatter.Split('\n'); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.category = category; + param.dimension = dimension; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(dimension); + param.ignore = ignore; + param.total = serie.yTotal; + param.color = chart.GetMarkColor(serie, serieData); + param.marker = SerieHelper.GetItemMarker(serie, serieData, marker); + param.itemFormatter = formatter; + param.numericFormatter = newNumericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(showCategory ? category : serie.serieName); + param.columns.Add(ignore ? ignoreDataDefaultContent : ChartCached.NumberToStr(param.value, param.numericFormatter)); + + paramList.Add(param); + } + } + + protected void UpdateItemSerieParams(ref List<SerieParams> paramList, ref string title, + int dataIndex, string category, string marker, + string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, + int dimension = 1, int colorIndex = -1) + { + if (dataIndex < 0) + dataIndex = serie.context.pointerItemDataIndex; + + if (dataIndex < 0) + return; + + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + + var ignore = serie.IsIgnoreValue(serieData, dimension); + if (ignore && string.IsNullOrEmpty(ignoreDataDefaultContent)) + return; + + itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + if (serie.placeHolder || TooltipHelper.IsIgnoreFormatter(itemFormatter)) + return; + + if (colorIndex < 0) + colorIndex = serie.colorByData ? dataIndex : chart.GetLegendRealShowNameIndex(serieData.name); + + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, SerieState.Normal); + + if (itemFormatter == null) itemFormatter = ""; + var newItemFormatter = itemFormatter.Replace("\\n", "\n"); + var newNumericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + var temp = newItemFormatter.Split('\n'); + var mark = SerieHelper.GetItemMarker(serie, serieData, marker); + var total = serie.multiDimensionLabel ? serieData.GetTotalData() : serie.GetDataTotal(defaultDimension); + for (int i = 0; i < temp.Length; i++) + { + var formatter = temp[i]; + var param = i == 0 ? serie.context.param : new SerieParams(); + param.serieName = serie.serieName; + param.serieIndex = serie.index; + + param.category = category; + param.dimension = dimension; + param.serieData = serieData; + param.dataCount = serie.dataCount; + param.value = serieData.GetData(param.dimension); + param.ignore = ignore; + param.total = total; + param.color = color; + param.marker = mark; + param.itemFormatter = formatter; + param.numericFormatter = newNumericFormatter; + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(serieData.name); + + param.columns.Add(ignore ? ignoreDataDefaultContent : ChartCached.NumberToStr(param.value, param.numericFormatter)); + + paramList.Add(param); + } + } + + public void DrawLabelLineSymbol(VertexHelper vh, LabelLine labelLine, Vector3 startPos, Vector3 endPos, Color32 defaultColor) + { + if (labelLine.startSymbol != null && labelLine.startSymbol.show) + { + DrawSymbol(vh, labelLine.startSymbol, startPos, defaultColor); + } + if (labelLine.endSymbol != null && labelLine.endSymbol.show) + { + DrawSymbol(vh, labelLine.endSymbol, endPos, defaultColor); + } + } + + private void DrawSymbol(VertexHelper vh, SymbolStyle symbol, Vector3 pos, Color32 defaultColor) + { + var color = symbol.GetColor(defaultColor); + chart.DrawSymbol(vh, symbol.type, symbol.size, 1, pos, + color, color, ColorUtil.clearColor32, color, symbol.gap, null, symbol.size2); + } + + public override void OnPointerDown(PointerEventData eventData) + { + if (serie.onDown == null && chart.onSerieDown == null) return; + if (!serie.context.pointerEnter) return; + var dataIndex = GetPointerItemDataIndex(); + if (dataIndex < 0) return; + var dimension = GetPointerItemDataDimension(); + var value = serie.GetData(dataIndex, dimension); + var data = SerieEventDataPool.Get(chart.pointerPos, serie.index, dataIndex, dimension, value); + if (chart.onSerieDown != null) + chart.onSerieDown(data); + if (serie.onDown != null) + serie.onDown(data); + SerieEventDataPool.Release(data); + } + + public override void OnPointerClick(PointerEventData eventData) + { + serie.context.clickTotalDataIndex = serie.context.totalDataIndex; + if (serie.onClick == null && chart.onSerieClick == null) return; + if (!serie.context.pointerEnter) return; + var dataIndex = GetPointerItemDataIndex(); + if (dataIndex < 0) return; + var dimension = GetPointerItemDataDimension(); + var value = serie.GetData(dataIndex, dimension); + var data = SerieEventDataPool.Get(chart.pointerPos, serie.index, dataIndex, dimension, value); + if (chart.onSerieClick != null) + chart.onSerieClick(data); + if (serie.onClick != null) + serie.onClick(data); + SerieEventDataPool.Release(data); + } + + public override int GetPointerItemDataIndex() + { + return serie.context.pointerItemDataIndex; + } + + public override int GetPointerItemDataDimension() + { + return serie.context.pointerItemDataDimension; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieHandler.cs.meta b/Assets/XCharts/Runtime/Serie/SerieHandler.cs.meta new file mode 100644 index 0000000..87f9fd8 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7f2bc0a6a80a84eae9c87842c954bc32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieHelper.cs b/Assets/XCharts/Runtime/Serie/SerieHelper.cs new file mode 100644 index 0000000..eb56021 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieHelper.cs @@ -0,0 +1,1030 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static partial class SerieHelper + { + public static double GetMinData(Serie serie, int dimension = 1, DataZoom dataZoom = null) + { + double min = double.MaxValue; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (value < min && !serie.IsIgnoreValue(serieData, value)) min = value; + } + } + return min == double.MaxValue ? 0 : min; + } + public static SerieData GetMinSerieData(Serie serie, int dimension = 1, DataZoom dataZoom = null) + { + double min = double.MaxValue; + SerieData minData = null; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (value < min && !serie.IsIgnoreValue(serieData, value)) + { + min = value; + minData = serieData; + } + } + } + return minData; + } + public static double GetMaxData(Serie serie, int dimension = 1, DataZoom dataZoom = null) + { + double max = double.MinValue; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (value > max && !serie.IsIgnoreValue(serieData, value)) max = value; + } + } + return max == double.MinValue ? 0 : max; + } + public static SerieData GetMaxSerieData(Serie serie, int dimension = 1, DataZoom dataZoom = null) + { + double max = double.MinValue; + SerieData maxData = null; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (value > max && !serie.IsIgnoreValue(serieData, value)) + { + max = value; + maxData = serieData; + } + } + } + return maxData; + } + + public static double GetAverageData(Serie serie, int dimension = 1, DataZoom dataZoom = null) + { + double total = 0; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (!serie.IsIgnoreValue(serieData, value)) + total += value; + } + } + return total != 0 ? total / dataList.Count : 0; + } + + private static List<double> s_TempList = new List<double>(); + public static double GetMedianData(Serie serie, int dimension = 1, DataZoom dataZoom = null) + { + s_TempList.Clear(); + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (!serie.IsIgnoreValue(serieData, value)) + s_TempList.Add(value); + } + } + s_TempList.Sort(); + var n = s_TempList.Count; + if (n % 2 == 0) return (s_TempList[n / 2] + s_TempList[n / 2 - 1]) / 2; + else return s_TempList[n / 2]; + } + + /// <summary> + /// Gets the maximum and minimum values of the specified dimension of a serie. + /// ||获得系列指定维数的最大最小值。 + /// </summary> + /// <param name="serie">指定系列</param> + /// <param name="dimension">指定维数</param> + /// <param name="min">最小值</param> + /// <param name="max">最大值</param> + /// <param name="dataZoom">缩放组件,默认null</param> + public static void GetMinMaxData(Serie serie, int dimension, out double min, out double max, + DataZoom dataZoom = null) + { + max = double.MinValue; + min = double.MaxValue; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show && serieData.data.Count > dimension) + { + var value = serieData.data[dimension]; + if (!serie.IsIgnoreValue(serieData, value)) + { + if (value > max) max = value; + if (value < min) min = value; + } + } + } + if (min == double.MaxValue && max == double.MinValue) + { + min = 0; + max = 0; + } + } + + /// <summary> + /// Gets the maximum and minimum values of all data in the serie. + /// ||获得系列所有数据的最大最小值。 + /// </summary> + /// <param name="serie"></param> + /// <param name="min"></param> + /// <param name="max"></param> + /// <param name="dataZoom"></param> + public static void GetMinMaxData(Serie serie, out double min, out double max, DataZoom dataZoom = null, int dimension = 0) + { + max = double.MinValue; + min = double.MaxValue; + var dataList = serie.GetDataList(dataZoom); + for (int i = 0; i < dataList.Count; i++) + { + var serieData = dataList[i]; + if (serieData.show) + { + var count = 0; + if (dimension > 0) count = dimension; + else count = serie.showDataDimension > serieData.data.Count ? + serieData.data.Count : + serie.showDataDimension; + for (int j = 0; j < count; j++) + { + var value = serieData.data[j]; + if (!serie.IsIgnoreValue(serieData, value)) + { + if (value > max) max = value; + if (value < min) min = value; + } + } + } + } + if (min == double.MaxValue && max == double.MinValue) + { + min = 0; + max = 0; + } + } + + /// <summary> + /// Whether the data for the specified dimension of serie are all 0. + /// ||系列指定维数的数据是否全部为0。 + /// </summary> + /// <param name="serie">系列</param> + /// <param name="dimension">指定维数</param> + /// <returns></returns> + public static bool IsAllZeroValue(Serie serie, int dimension = 1) + { + if (serie.dataCount == 0) return false; + foreach (var serieData in serie.data) + { + if (serieData.GetData(dimension) != 0) return false; + } + return true; + } + + /// <summary> + /// 更新运行时中心点和半径 + /// </summary> + /// <param name="chartWidth"></param> + /// <param name="chartHeight"></param> + public static void UpdateCenter(Serie serie, BaseChart chart) + { + if (serie.center.Length < 2) return; + var chartPosition = chart.chartPosition; + var chartWidth = chart.chartWidth; + var chartHeight = chart.chartHeight; + if (serie.gridIndex >= 0) + { + var layout = chart.GetChartComponent<GridLayout>(0); + if (layout != null) + { + layout.UpdateGridContext(serie.gridIndex, ref chartPosition, ref chartWidth, ref chartHeight); + } + } + var centerX = serie.center[0] <= 1 ? chartWidth * serie.center[0] : serie.center[0]; + var centerY = serie.center[1] <= 1 ? chartHeight * serie.center[1] : serie.center[1]; + serie.context.center = chartPosition + new Vector3(centerX, centerY); + var minWidth = Mathf.Min(chartWidth, chartHeight); + serie.context.insideRadius = serie.radius[0] <= 1 ? minWidth * serie.radius[0] : serie.radius[0]; + serie.context.outsideRadius = serie.radius[1] <= 1 ? minWidth * serie.radius[1] : serie.radius[1]; + } + + public static void UpdateRect(Serie serie, Vector3 chartPosition, float chartWidth, float chartHeight) + { + if (serie.left != 0 || serie.right != 0 || serie.top != 0 || serie.bottom != 0) + { + var runtimeLeft = serie.left <= 1 ? serie.left * chartWidth : serie.left; + var runtimeBottom = serie.bottom <= 1 ? serie.bottom * chartHeight : serie.bottom; + var runtimeTop = serie.top <= 1 ? serie.top * chartHeight : serie.top; + var runtimeRight = serie.right <= 1 ? serie.right * chartWidth : serie.right; + + serie.context.x = chartPosition.x + runtimeLeft; + serie.context.y = chartPosition.y + runtimeBottom; + serie.context.width = chartWidth - runtimeLeft - runtimeRight; + serie.context.height = chartHeight - runtimeTop - runtimeBottom; + serie.context.center = new Vector3(serie.context.x + serie.context.width / 2, + serie.context.y + serie.context.height / 2); + serie.context.rect = new Rect(serie.context.x, serie.context.y, serie.context.width, serie.context.height); + } + else + { + serie.context.x = chartPosition.x; + serie.context.y = chartPosition.y; + serie.context.width = chartWidth; + serie.context.height = chartHeight; + serie.context.center = chartPosition + new Vector3(chartWidth / 2, chartHeight / 2); + serie.context.rect = new Rect(serie.context.x, serie.context.y, serie.context.width, serie.context.height); + } + } + + public static SerieState GetSerieState(Serie serie) + { + if (serie.highlight) return SerieState.Emphasis; + return serie.state; + } + + public static SerieState GetSerieState(SerieData serieData) + { + if (serieData.context.highlight) return SerieState.Emphasis; + return serieData.state; + } + + public static SerieState GetSerieState(Serie serie, SerieData serieData, bool defaultSerieState = false) + { + if (serieData == null) return GetSerieState(serie); + if (serieData.context.highlight) return SerieState.Emphasis; + if (serieData.state == SerieState.Auto) return defaultSerieState ? serie.state : GetSerieState(serie); + return serieData.state; + } + + public static Color32 GetItemBackgroundColor(Serie serie, SerieData serieData, ThemeStyle theme, int index, + SerieState state = SerieState.Auto, bool useDefault = false) + { + var color = ChartConst.clearColor32; + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null) + color = GetItemStyle(serie, serieData, SerieState.Normal).backgroundColor; + else + color = stateStyle.itemStyle.backgroundColor; + if (useDefault && ChartHelper.IsClearColor(color)) + { + color = theme.GetColor(index); + color.a = 50; + } + return color; + } + + public static void GetItemColor(out Color32 color, out Color32 toColor, + Serie serie, SerieData serieData, ThemeStyle theme, SerieState state = SerieState.Auto) + { + var colorIndex = serieData != null && serie.colorByData ? serieData.index : serie.context.colorIndex; + GetItemColor(out color, out toColor, serie, serieData, theme, colorIndex, state, true); + } + + public static void GetItemColor(out Color32 color, out Color32 toColor, + Serie serie, SerieData serieData, ThemeStyle theme, int index, SerieState state = SerieState.Auto, bool opacity = true) + { + color = ColorUtil.clearColor32; + toColor = ColorUtil.clearColor32; + if (serie == null) return; + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null) + { + var style = GetItemStyle(serie, serieData, SerieState.Normal); + GetColor(ref color, style.color, style.color, style.opacity, theme, index, opacity); + GetColor(ref toColor, style.toColor, color, style.opacity, theme, index, opacity, true); + switch (state) + { + case SerieState.Emphasis: + color = ChartHelper.GetHighlightColor(color); + toColor = ChartHelper.GetHighlightColor(toColor); + break; + case SerieState.Blur: + color = ChartHelper.GetBlurColor(color); + toColor = ChartHelper.GetBlurColor(toColor); + break; + case SerieState.Select: + color = ChartHelper.GetSelectColor(color); + toColor = ChartHelper.GetSelectColor(toColor); + break; + default: + break; + } + } + else + { + GetColor(ref color, stateStyle.itemStyle.color, stateStyle.itemStyle.color, stateStyle.itemStyle.opacity, theme, index, opacity); + GetColor(ref toColor, stateStyle.itemStyle.toColor, color, stateStyle.itemStyle.opacity, theme, index, opacity, true); + } + } + + public static void GetItemColor(out Color32 color, out Color32 toColor, out Color32 backgroundColor, + Serie serie, SerieData serieData, ThemeStyle theme, int index, SerieState state = SerieState.Auto, bool opacity = true) + { + color = ColorUtil.clearColor32; + toColor = ColorUtil.clearColor32; + backgroundColor = ColorUtil.clearColor32; + if (serie == null) return; + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null) + { + var style = GetItemStyle(serie, serieData, SerieState.Normal); + GetColor(ref color, style.color, style.color, style.opacity, theme, index, opacity); + GetColor(ref toColor, style.toColor, color, style.opacity, theme, index, opacity, true); + backgroundColor = style.backgroundColor; + switch (state) + { + case SerieState.Emphasis: + color = ChartHelper.GetHighlightColor(color); + toColor = ChartHelper.GetHighlightColor(toColor); + break; + case SerieState.Blur: + color = ChartHelper.GetBlurColor(color); + toColor = ChartHelper.GetBlurColor(toColor); + break; + case SerieState.Select: + color = ChartHelper.GetSelectColor(color); + toColor = ChartHelper.GetSelectColor(toColor); + break; + default: + break; + } + } + else + { + backgroundColor = stateStyle.itemStyle.backgroundColor; + GetColor(ref color, stateStyle.itemStyle.color, stateStyle.itemStyle.color, stateStyle.itemStyle.opacity, theme, index, opacity); + GetColor(ref toColor, stateStyle.itemStyle.toColor, color, stateStyle.itemStyle.opacity, theme, index, opacity, true); + } + } + + public static Color32 GetItemColor(Serie serie, SerieData serieData, ThemeStyle theme, int index, SerieState state = SerieState.Auto, bool opacity = true) + { + var color = ColorUtil.clearColor32; + if (serie == null) return color; + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null || !stateStyle.itemStyle.show) + { + var style = GetItemStyle(serie, serieData); + GetColor(ref color, style.color, style.color, style.opacity, theme, index, opacity); + switch (state) + { + case SerieState.Emphasis: + color = ChartHelper.GetHighlightColor(color); + break; + case SerieState.Blur: + color = ChartHelper.GetBlurColor(color); + break; + case SerieState.Select: + color = ChartHelper.GetSelectColor(color); + break; + default: + break; + } + } + else + { + GetColor(ref color, stateStyle.itemStyle.color, stateStyle.itemStyle.color, stateStyle.itemStyle.opacity, theme, index, opacity); + } + return color; + } + + public static bool IsDownPoint(Serie serie, int index) + { + var dataPoints = serie.context.dataPoints; + if (dataPoints.Count < 2) return false; + else if (index > 0 && index < dataPoints.Count - 1) + { + var lp = dataPoints[index - 1]; + var np = dataPoints[index + 1]; + var cp = dataPoints[index]; + var dot = Vector3.Cross(np - lp, cp - np); + return dot.z < 0; + } + else if (index == 0) + { + return dataPoints[0].y < dataPoints[1].y; + } + else if (index == dataPoints.Count - 1) + { + return dataPoints[index].y < dataPoints[index - 1].y; + } + else + { + return false; + } + } + + public static ItemStyle GetItemStyle(Serie serie, SerieData serieData, SerieState state = SerieState.Auto) + { + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null || !stateStyle.show) + { + return serieData != null && serieData.itemStyle != null ? serieData.itemStyle : serie.itemStyle; + } + else + { + return stateStyle.itemStyle; + } + } + + public static LabelStyle GetSerieLabel(Serie serie, SerieData serieData, SerieState state = SerieState.Auto) + { + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + if (state == SerieState.Normal) + { + return serieData != null && serieData.labelStyle != null ? serieData.labelStyle : serie.label; + } + else + { + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle != null && stateStyle.show) return stateStyle.label; + else if (serieData.labelStyle != null) return serieData.labelStyle; + else return serie.label; + } + } + + public static LabelLine GetSerieLabelLine(Serie serie, SerieData serieData, SerieState state = SerieState.Auto) + { + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + if (state == SerieState.Normal) + { + return serieData != null && serieData.labelLine != null ? serieData.labelLine : serie.labelLine; + } + else + { + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle != null && stateStyle.show) return stateStyle.labelLine; + else if (serieData.labelLine != null) return serieData.labelLine; + else return serie.labelLine; + } + } + + public static SerieSymbol GetSerieSymbol(Serie serie, SerieData serieData, SerieState state = SerieState.Auto) + { + if (state == SerieState.Auto) state = GetSerieState(serie, serieData); + if (state == SerieState.Normal) + { + return serieData != null && serieData.symbol != null ? serieData.symbol : serie.symbol; + } + else + { + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle != null && stateStyle.show) return stateStyle.symbol; + else if (serieData.symbol != null) return serieData.symbol; + else return serie.symbol; + } + } + + public static LineStyle GetLineStyle(Serie serie, SerieData serieData) + { + if (serieData != null && serieData.lineStyle != null) return serieData.lineStyle; + else return serie.lineStyle; + } + + public static AreaStyle GetAreaStyle(Serie serie, SerieData serieData) + { + if (serieData != null && serieData.areaStyle != null) return serieData.areaStyle; + else return serie.areaStyle; + } + + public static TitleStyle GetTitleStyle(Serie serie, SerieData serieData) + { + if (serieData != null && serieData.titleStyle != null) return serieData.titleStyle; + else return serie.titleStyle; + } + + public static EmphasisStyle GetEmphasisStyle(Serie serie, SerieData serieData) + { + if (serieData != null && serieData.emphasisStyle != null) return serieData.emphasisStyle; + else return serie.emphasisStyle; + } + + public static BlurStyle GetBlurStyle(Serie serie, SerieData serieData) + { + if (serieData != null && serieData.blurStyle != null) return serieData.blurStyle; + else return serie.blurStyle; + } + public static SelectStyle GetSelectStyle(Serie serie, SerieData serieData) + { + if (serieData != null && serieData.selectStyle != null) return serieData.selectStyle; + else return serie.selectStyle; + } + + public static StateStyle GetStateStyle(Serie serie, SerieData serieData, SerieState state) + { + switch (state) + { + case SerieState.Emphasis: + return GetEmphasisStyle(serie, serieData); + case SerieState.Blur: + return GetBlurStyle(serie, serieData); + case SerieState.Select: + return GetSelectStyle(serie, serieData); + default: + return null; + } + } + + public static bool GetAreaColor(out Color32 color, out Color32 toColor, + Serie serie, SerieData serieData, ThemeStyle theme, int index) + { + bool fill, toTop; + return GetAreaColor(out color, out toColor, out fill, out toTop, serie, serieData, theme, index); + } + + public static bool GetAreaColor(out Color32 color, out Color32 toColor, out bool innerFill, + out bool toTop, Serie serie, SerieData serieData, ThemeStyle theme, int index) + { + color = ChartConst.clearColor32; + toColor = ChartConst.clearColor32; + innerFill = false; + toTop = true; + var state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null) + { + var areaStyle = GetAreaStyle(serie, serieData); + if (areaStyle == null || !areaStyle.show) return false; + innerFill = areaStyle.innerFill; + toTop = areaStyle.toTop; + GetColor(ref color, areaStyle.color, serie.itemStyle.color, areaStyle.opacity, theme, index); + GetColor(ref toColor, areaStyle.toColor, color, areaStyle.opacity, theme, index, true); + switch (state) + { + case SerieState.Emphasis: + color = ChartHelper.GetHighlightColor(color); + toColor = ChartHelper.GetHighlightColor(toColor); + break; + case SerieState.Blur: + color = ChartHelper.GetBlurColor(color); + toColor = ChartHelper.GetBlurColor(toColor); + break; + case SerieState.Select: + color = ChartHelper.GetSelectColor(color); + toColor = ChartHelper.GetSelectColor(toColor); + break; + default: + break; + } + } + else + { + if (stateStyle.areaStyle.show) + { + innerFill = stateStyle.areaStyle.innerFill; + toTop = stateStyle.areaStyle.toTop; + GetColor(ref color, stateStyle.areaStyle.color, stateStyle.itemStyle.color, stateStyle.areaStyle.opacity, theme, index); + GetColor(ref toColor, stateStyle.areaStyle.toColor, color, stateStyle.areaStyle.opacity, theme, index, true, true); + } + else + { + return false; + } + } + return true; + } + + public static Color32 GetLineColor(Serie serie, SerieData serieData, ThemeStyle theme, int index, SerieState state = SerieState.Auto) + { + Color32 color = ChartConst.clearColor32; + if (state == SerieState.Auto) + state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null) + { + var lineStyle = GetLineStyle(serie, serieData); + GetColor(ref color, lineStyle.color, serie.itemStyle.color, lineStyle.opacity, theme, index); + switch (state) + { + case SerieState.Emphasis: + return ChartHelper.GetHighlightColor(color); + case SerieState.Blur: + return ChartHelper.GetBlurColor(color); + case SerieState.Select: + return ChartHelper.GetSelectColor(color); + default: + return color; + } + } + else + { + GetColor(ref color, stateStyle.lineStyle.color, stateStyle.itemStyle.color, stateStyle.lineStyle.opacity, theme, index); + return color; + } + } + + public static void GetColor(ref Color32 color, Color32 checkColor, Color32 itemColor, + float opacity, ThemeStyle theme, int colorIndex, bool setOpacity = true, bool resetOpacity = false) + { + if (!ChartHelper.IsClearColor(checkColor)) color = checkColor; + else if (!ChartHelper.IsClearColor(itemColor)) + { + color = itemColor; + if (resetOpacity) opacity = 1; + } + if (ChartHelper.IsClearColor(color) && colorIndex >= 0) color = theme.GetColor(colorIndex); + if (setOpacity) ChartHelper.SetColorOpacity(ref color, opacity); + } + + public static void GetSymbolInfo(out Color32 borderColor, out float border, out float[] cornerRadius, + Serie serie, SerieData serieData, ThemeStyle theme, SerieState state = SerieState.Auto) + { + borderColor = ChartConst.clearColor32; + if (state == SerieState.Auto) + state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + if (stateStyle == null) + { + var itemStyle = GetItemStyle(serie, serieData, SerieState.Normal); + border = itemStyle.borderWidth != 0 ? itemStyle.borderWidth : serie.lineStyle.GetWidth(theme.serie.lineWidth) * 1.8f; + cornerRadius = itemStyle.cornerRadius; + GetColor(ref borderColor, itemStyle.borderColor, itemStyle.borderColor, 1, theme, -1); + switch (state) + { + case SerieState.Emphasis: + borderColor = ChartHelper.GetHighlightColor(borderColor); + break; + case SerieState.Blur: + borderColor = ChartHelper.GetBlurColor(borderColor); + break; + case SerieState.Select: + borderColor = ChartHelper.GetSelectColor(borderColor); + break; + default: + break; + } + } + else + { + var itemStyle = stateStyle.itemStyle; + border = itemStyle.borderWidth != 0 ? itemStyle.borderWidth : stateStyle.lineStyle.GetWidth(theme.serie.lineWidth) * 1.8f; + cornerRadius = itemStyle.cornerRadius; + GetColor(ref borderColor, stateStyle.itemStyle.borderColor, ColorUtil.clearColor32, 1, theme, -1); + } + } + + public static float GetSysmbolSize(Serie serie, SerieData serieData, float defaultSize, SerieState state = SerieState.Auto, bool checkAnimation = false) + { + if (serie == null) return defaultSize; + if (state == SerieState.Auto) + state = GetSerieState(serie, serieData); + var stateStyle = GetStateStyle(serie, serieData, state); + var size = 0f; + if (stateStyle == null) + { + var symbol = GetSerieSymbol(serie, serieData, SerieState.Normal); + size = symbol.GetSize(serieData, defaultSize); + switch (state) + { + case SerieState.Emphasis: + case SerieState.Select: + size = serie.animation.interaction.GetRadius(size); + break; + default: + break; + } + } + else + { + var symbol = stateStyle.symbol; + size = symbol.GetSize(serieData, defaultSize); + } + if (serieData != null && checkAnimation) + { + size = (float)serieData.GetAddAnimationData(0, size, serie.animation.GetAdditionDuration()); + } + return size; + } + + public static string GetNumericFormatter(Serie serie, SerieData serieData, string defaultFormatter = null) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (!string.IsNullOrEmpty(itemStyle.numericFormatter)) return itemStyle.numericFormatter; + else return defaultFormatter; + } + + public static string GetItemFormatter(Serie serie, SerieData serieData, string defaultFormatter = null) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (!string.IsNullOrEmpty(itemStyle.itemFormatter)) return itemStyle.itemFormatter; + else return defaultFormatter; + } + + public static string GetItemMarker(Serie serie, SerieData serieData, string defaultMarker = null) + { + var itemStyle = SerieHelper.GetItemStyle(serie, serieData); + if (!string.IsNullOrEmpty(itemStyle.itemMarker)) return itemStyle.itemMarker; + else return defaultMarker; + } + + /// <summary> + /// 获得指定维数的最大最小值 + /// </summary> + /// <param name="dimension"></param> + /// <param name="dataZoom"></param> + /// <returns></returns> + public static void UpdateMinMaxData(Serie serie, int dimension, double ceilRate = 0, DataZoom dataZoom = null) + { + double min = 0, max = 0; + GetMinMaxData(serie, dimension, out min, out max, dataZoom); + if (ceilRate < 0) + { + serie.context.dataMin = min; + serie.context.dataMax = max; + } + else + { + serie.context.dataMin = ChartHelper.GetMinDivisibleValue(min, ceilRate); + serie.context.dataMax = ChartHelper.GetMaxDivisibleValue(max, ceilRate); + } + } + + public static void GetAllMinMaxData(Serie serie, double ceilRate = 0, DataZoom dataZoom = null) + { + double min = 0, max = 0; + GetMinMaxData(serie, out min, out max, dataZoom); + if (ceilRate < 0) + { + serie.context.dataMin = min; + serie.context.dataMax = max; + } + else + { + serie.context.dataMin = ChartHelper.GetMinDivisibleValue(min, ceilRate); + serie.context.dataMax = ChartHelper.GetMaxDivisibleValue(max, ceilRate); + } + } + + /// <summary> + /// 根据dataZoom更新数据列表缓存 + /// </summary> + /// <param name="dataZoom"></param> + public static void UpdateFilterData(Serie serie, DataZoom dataZoom) + { + if (dataZoom == null || !dataZoom.enable) + { + serie.m_NeedUpdateFilterData = true; + serie.context.dataZoomStartIndex = 0; + serie.context.dataZoomStartIndexOffset = 0; + return; + } + if (dataZoom.IsContainsXAxis(serie.xAxisIndex)) + { + if (dataZoom.IsXAxisIndexValue(serie.xAxisIndex)) + { + double min = 0, max = 0; + dataZoom.GetXAxisIndexValue(serie.xAxisIndex, out min, out max); + UpdateFilterData_XAxisValue(serie, dataZoom, 0, min, max); + } + else + { + UpdateFilterData_Category(serie, dataZoom); + } + } + else if (dataZoom.IsContainsYAxis(serie.yAxisIndex)) + { + if (dataZoom.IsYAxisIndexValue(serie.yAxisIndex)) + { + double min = 0, max = 0; + dataZoom.GetYAxisIndexValue(serie.yAxisIndex, out min, out max); + UpdateFilterData_XAxisValue(serie, dataZoom, 0, min, max); + } + else + { + UpdateFilterData_Category(serie, dataZoom); + } + } + } + + private static void UpdateFilterData_XAxisValue(Serie serie, DataZoom dataZoom, int dimension, double min, double max) + { + var data = serie.data; + var startValue = min; + var endValue = max; + if (endValue < startValue) endValue = startValue; + if (startValue != serie.m_FilterStartValue || endValue != serie.m_FilterEndValue || + dataZoom.minShowNum != serie.m_FilterMinShow || serie.m_NeedUpdateFilterData) + { + serie.m_FilterStartValue = startValue; + serie.m_FilterEndValue = endValue; + serie.m_FilterMinShow = dataZoom.minShowNum; + serie.m_NeedUpdateFilterData = false; + + if (ReferenceEquals(serie.m_FilterData, data)) + { + serie.m_FilterData = new List<SerieData>(); + } + serie.m_FilterData.Clear(); + foreach (var serieData in data) + { + var value = serieData.GetData(dimension); + if (value >= startValue && value <= endValue) + { + serie.m_FilterData.Add(serieData); + } + } + } + else if (endValue == 0) + { + if (serie.m_FilterData == null) + serie.m_FilterData = new List<SerieData>(); + else if (serie.m_FilterData.Count > 0) + serie.m_FilterData.Clear(); + } + } + + private static void UpdateFilterData_Category(Serie serie, DataZoom dataZoom) + { + var data = serie.data; + var range = Mathf.RoundToInt(data.Count * (dataZoom.end - dataZoom.start) / 100); + if (range <= 0) range = 1; + int start = 0, end = 0; + if (dataZoom.context.invert) + { + end = Mathf.RoundToInt(data.Count * dataZoom.end / 100); + start = end - range; + if (start < 0) start = 0; + } + else + { + start = Mathf.RoundToInt(data.Count * dataZoom.start / 100); + end = start + range; + if (end > data.Count) end = data.Count; + } + if (start != serie.m_FilterStart || end != serie.m_FilterEnd || + dataZoom.minShowNum != serie.m_FilterMinShow || serie.m_NeedUpdateFilterData) + { + serie.m_FilterStart = start; + serie.m_FilterEnd = end; + serie.m_FilterMinShow = dataZoom.minShowNum; + serie.m_NeedUpdateFilterData = false; + if (data.Count > 0) + { + if (range < dataZoom.minShowNum) + { + if (dataZoom.minShowNum > data.Count) range = data.Count; + else range = dataZoom.minShowNum; + } + if (range > data.Count - start) + start = data.Count - range; + if (start >= 0) + { + serie.context.dataZoomStartIndex = start; + serie.context.dataZoomStartIndexOffset = 0; + serie.m_FilterData = data.GetRange(start, range); + var nowCount = serie.m_FilterData.Count; + if (nowCount > 0) + { + if (serie.IsIgnoreValue(serie.m_FilterData[nowCount - 1])) + { + for (int i = start + range; i < data.Count; i++) + { + serie.m_FilterData.Add(data[i]); + if (!serie.IsIgnoreValue(data[i])) + break; + } + } + if (serie.IsIgnoreValue(serie.m_FilterData[0])) + { + for (int i = start - 1; i >= 0; i--) + { + serie.m_FilterData.Insert(0, data[i]); + serie.context.dataZoomStartIndexOffset++; + if (!serie.IsIgnoreValue(data[i])) + break; + } + } + } + } + else + { + serie.context.dataZoomStartIndex = 0; + serie.context.dataZoomStartIndexOffset = 0; + serie.m_FilterData = data; + } + } + else + { + serie.context.dataZoomStartIndex = 0; + serie.context.dataZoomStartIndexOffset = 0; + serie.m_FilterData = data; + } + } + else if (end == 0) + { + serie.context.dataZoomStartIndex = 0; + serie.context.dataZoomStartIndexOffset = 0; + if (serie.m_FilterData == null) + serie.m_FilterData = new List<SerieData>(); + else if (serie.m_FilterData.Count > 0) + serie.m_FilterData.Clear(); + } + } + + public static void UpdateSerieRuntimeFilterData(Serie serie, bool filterInvisible = true) + { + var realtimeData = true; + var dataChangeDuration = serie.animation.GetChangeDuration(); + var dataAddDuration = serie.animation.GetAdditionDuration(); + var unscaledTime = serie.animation.unscaledTime; + serie.context.sortedData.Clear(); + foreach (var serieData in serie.data) + { + if (!filterInvisible || (filterInvisible && serieData.show)) + serie.context.sortedData.Add(serieData); + } + switch (serie.dataSortType) + { + case SerieDataSortType.Ascending: + serie.context.sortedData.Sort(delegate (SerieData data1, SerieData data2) + { + var value1 = realtimeData ? + data1.GetCurrData(1, dataAddDuration, dataChangeDuration, false, 0, 0, unscaledTime) : + data1.GetData(1); + var value2 = realtimeData ? + data2.GetCurrData(1, dataAddDuration, dataChangeDuration, false, 0, 0, unscaledTime) : + data2.GetData(1); + if (value1 == value2) return 0; + else if (value1 > value2) return 1; + else return -1; + }); + break; + case SerieDataSortType.Descending: + serie.context.sortedData.Sort(delegate (SerieData data1, SerieData data2) + { + var value1 = realtimeData ? + data1.GetCurrData(1, dataAddDuration, dataChangeDuration, false, 0, 0, unscaledTime) : + data1.GetData(1); + var value2 = realtimeData ? + data2.GetCurrData(1, dataAddDuration, dataChangeDuration, false, 0, 0, unscaledTime) : + data2.GetData(1); + if (value1 == value2) return 0; + else if (value1 > value2) return -1; + else return 1; + }); + break; + case SerieDataSortType.None: + break; + } + for (int i = 0; i < serie.context.sortedData.Count; i++) + { + serie.context.sortedData[i].sortIndex = i; + } + } + + public static T CloneSerie<T>(Serie serie) where T : Serie + { + var newSerie = Activator.CreateInstance<T>(); + SerieHelper.CopySerie(serie, newSerie); + return newSerie; + } + + public static void CopySerie(Serie oldSerie, Serie newSerie) + { + var fields = typeof(Serie).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var field in fields) + { + if (field.IsDefined(typeof(SerializeField), false)) + { + var filedValue = field.GetValue(oldSerie); + if (filedValue == null) continue; + var filedType = filedValue.GetType(); + if (filedType.IsClass) + field.SetValue(newSerie, ReflectionUtil.DeepCloneSerializeField(filedValue)); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieHelper.cs.meta b/Assets/XCharts/Runtime/Serie/SerieHelper.cs.meta new file mode 100644 index 0000000..cfb4c05 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 73512c276f5c34fb4a28cf61b2a0c4f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SerieParams.cs b/Assets/XCharts/Runtime/Serie/SerieParams.cs new file mode 100644 index 0000000..a96e5e9 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieParams.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public class SerieParams + { + public Type serieType; + public int serieIndex; + public string serieName; + public string marker = "●"; + public bool isSecondaryMark; + public string category; + public int dimension; + public SerieData serieData; + public int dataCount; + public double value; + public double total; + public Color32 color; + public string itemFormatter; + public string numericFormatter; + public bool ignore; + public List<string> columns = new List<string>(); + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SerieParams.cs.meta b/Assets/XCharts/Runtime/Serie/SerieParams.cs.meta new file mode 100644 index 0000000..f8f138f --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SerieParams.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c46808eb5842743c5b02d03c4c503228 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Serie/SeriesHelper.cs b/Assets/XCharts/Runtime/Serie/SeriesHelper.cs new file mode 100644 index 0000000..ed6a0ab --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SeriesHelper.cs @@ -0,0 +1,508 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class SeriesHelper + { + + public static bool IsLegalLegendName(string name) + { + int numName = -1; + if (int.TryParse(name, out numName)) + { + if (numName >= 0 && numName < 100) return false; + } + return true; + } + + public static List<string> GetLegalSerieNameList(List<Serie> series) + { + var list = new List<string>(); + for (int n = 0; n < series.Count; n++) + { + var serie = series[n]; + if (serie.placeHolder) continue; + if (serie.colorByData) + { + for (int i = 0; i < serie.data.Count; i++) + { + var dataName = serie.data[i].name; + if (!string.IsNullOrEmpty(dataName) && IsLegalLegendName(dataName) && !list.Contains(dataName)) + list.Add(dataName); + } + } + else + { + if (!string.IsNullOrEmpty(serie.serieName) && !list.Contains(serie.serieName) && IsLegalLegendName(serie.serieName)) + list.Add(serie.serieName); + } + } + return list; + } + + /// <summary> + /// 获得所有系列名,不包含空名字。 + /// </summary> + /// <returns></returns> + public static void UpdateSerieNameList(BaseChart chart, ref List<string> serieNameList) + { + serieNameList.Clear(); + for (int n = 0; n < chart.series.Count; n++) + { + var serie = chart.series[n]; + if (serie.placeHolder) continue; + if (serie.colorByData) + { + for (int i = 0; i < serie.data.Count; i++) + { + var serieData = serie.data[i]; + if (serie is Pie && serie.IsIgnoreValue(serieData)) continue; + if (string.IsNullOrEmpty(serieData.name)) + serieNameList.Add(ChartCached.IntToStr(i)); + else if (!serieNameList.Contains(serieData.name)) + serieNameList.Add(serieData.name); + } + } + else + { + if (string.IsNullOrEmpty(serie.serieName)) + serieNameList.Add(ChartCached.IntToStr(n)); + else if (!serieNameList.Contains(serie.serieName)) + serieNameList.Add(serie.serieName); + } + } + } + + public static Color GetNameColor(BaseChart chart, int index, string name) + { + Serie destSerie = null; + SerieData destSerieData = null; + var series = chart.series; + for (int n = 0; n < series.Count; n++) + { + var serie = series[n]; + if (serie.placeHolder) continue; + if (serie.colorByData) + { + bool found = false; + for (int i = 0; i < serie.data.Count; i++) + { + if (name.Equals(serie.data[i].name)) + { + destSerie = serie; + destSerieData = serie.data[i]; + found = true; + break; + } + } + if (found) break; + } + if (name.Equals(serie.serieName)) + { + destSerie = serie; + destSerieData = null; + break; + } + } + var itemStyle = SerieHelper.GetItemStyle(destSerie, destSerieData, SerieState.Normal); + if (ChartHelper.IsClearColor(itemStyle.markColor)) + { + Color32 color, toColor; + SerieHelper.GetItemColor(out color, out toColor, destSerie, destSerieData, chart.theme, index, SerieState.Normal); + return color; + } + else + { + return itemStyle.markColor; + } + } + + /// <summary> + /// 是否有需裁剪的serie。 + /// </summary> + /// <returns></returns> + public static bool IsAnyClipSerie(List<Serie> series) + { + foreach (var serie in series) + { + if (serie.clip) return true; + } + return false; + } + + /// <summary> + /// check if series has any serie which is color by data. + /// || 是否有任何一个系列是按数据颜色的。 + /// </summary> + /// <param name="series"></param> + /// <returns></returns> + public static bool IsAnyColorByDataSerie(List<Serie> series) + { + foreach (var serie in series) + { + if (serie.defaultColorBy == SerieColorBy.Data) return true; + } + return false; + } + + /// <summary> + /// 获得上一个同堆叠且显示的serie。 + /// </summary> + /// <param name="serie"></param> + /// <returns></returns> + public static Serie GetLastStackSerie(List<Serie> series, Serie serie) + { + if (serie == null || string.IsNullOrEmpty(serie.stack)) return null; + for (int i = serie.index - 1; i >= 0; i--) + { + var temp = series[i]; + if (temp.show && serie.stack.Equals(temp.stack)) return temp; + } + return null; + } + + private static HashSet<string> _setForStack = new HashSet<string>(); + /// <summary> + /// 是否由数据堆叠 + /// </summary> + /// <returns></returns> + public static bool IsStack(List<Serie> series) + { + _setForStack.Clear(); + foreach (var serie in series) + { + if (string.IsNullOrEmpty(serie.stack)) continue; + if (_setForStack.Contains(serie.stack)) return true; + _setForStack.Add(serie.stack); + } + return false; + } + + /// <summary> + /// 是否堆叠 + /// </summary> + /// <param name="stackName"></param> + /// <param name="type"></param> + /// <returns></returns> + public static bool IsStack<T>(List<Serie> series, string stackName) where T : Serie + { + if (string.IsNullOrEmpty(stackName)) return false; + int count = 0; + foreach (var serie in series) + { + if (serie.show && serie is T) + { + if (stackName.Equals(serie.stack)) count++; + if (count >= 2) return true; + } + } + return false; + } + + /// <summary> + /// 是否时百分比堆叠 + /// </summary> + /// <param name="type"></param> + /// <returns></returns> + public static bool IsPercentStack<T>(List<Serie> series) where T : Serie + { + int count = 0; + bool isPercentStack = false; + foreach (var serie in series) + { + if (serie.show && serie is T) + { + if (!string.IsNullOrEmpty(serie.stack)) + { + count++; + if (serie.barPercentStack) isPercentStack = true; + } + if (count >= 2 && isPercentStack) return true; + } + } + return false; + } + + /// <summary> + /// 是否时百分比堆叠 + /// </summary> + /// <param name="stackName"></param> + /// <param name="type"></param> + /// <returns></returns> + public static bool IsPercentStack<T>(List<Serie> series, string stackName) where T : Serie + { + if (string.IsNullOrEmpty(stackName)) return false; + int count = 0; + bool isPercentStack = false; + foreach (var serie in series) + { + if (serie.show && serie is T) + { + if (stackName.Equals(serie.stack)) + { + count++; + if (serie.barPercentStack) isPercentStack = true; + } + if (count >= 2 && isPercentStack) return true; + } + } + return false; + } + + private static Dictionary<string, int> sets = new Dictionary<string, int>(); + /// <summary> + /// 获得堆叠系列列表 + /// </summary> + /// <param name="Dictionary<int"></param> + /// <param name="stackSeries"></param> + public static void GetStackSeries(List<Serie> series, ref Dictionary<int, List<Serie>> stackSeries) + { + int count = 0; + var serieCount = series.Count; + sets.Clear(); + if (stackSeries == null) + { + stackSeries = new Dictionary<int, List<Serie>>(serieCount); + } + else + { + foreach (var kv in stackSeries) + { + kv.Value.Clear(); + } + } + for (int i = 0; i < serieCount; i++) + { + var serie = series[i]; + serie.index = i; + if (string.IsNullOrEmpty(serie.stack)) + { + if (!stackSeries.ContainsKey(count)) + stackSeries[count] = new List<Serie>(serieCount); + stackSeries[count].Add(serie); + count++; + } + else + { + if (!sets.ContainsKey(serie.stack)) + { + sets.Add(serie.stack, count); + if (!stackSeries.ContainsKey(count)) + stackSeries[count] = new List<Serie>(serieCount); + stackSeries[count].Add(serie); + count++; + } + else + { + int stackIndex = sets[serie.stack]; + stackSeries[stackIndex].Add(serie); + } + } + } + } + + public static void UpdateStackDataList(List<Serie> series, Serie currSerie, DataZoom dataZoom, List<List<SerieData>> dataList) + { + dataList.Clear(); + for (int i = 0; i <= currSerie.index; i++) + { + var serie = series[i]; + if (serie.show && serie.GetType() == currSerie.GetType() && ChartHelper.IsValueEqualsString(serie.stack, currSerie.stack)) + { + dataList.Add(serie.GetDataList(dataZoom)); + } + } + } + + /// <summary> + /// 获得维度X的最大最小值 + /// </summary> + /// <param name="dataZoom"></param> + /// <param name="axisIndex"></param> + /// <param name="minValue"></param> + /// <param name="maxValue"></param> + public static void GetXMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue, + out double maxValue, bool isPolar = false, bool filterByDataZoom = true, bool needAnimation = false) + { + GetMinMaxValue(chart, axisIndex, inverse, 0, out minValue, out maxValue, isPolar, filterByDataZoom, needAnimation); + } + + /// <summary> + /// 获得维度Y的最大最小值 + /// </summary> + /// <param name="dataZoom"></param> + /// <param name="axisIndex"></param> + /// <param name="minValue"></param> + /// <param name="maxValue"></param> + public static void GetYMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue, + out double maxValue, bool isPolar = false, bool filterByDataZoom = true, bool needAnimation = false) + { + GetMinMaxValue(chart, axisIndex, inverse, 1, out minValue, out maxValue, isPolar, filterByDataZoom, needAnimation); + } + + /// <summary> + /// 获得维度Z的最大最小值 + /// </summary> + /// <param name="dataZoom"></param> + /// <param name="axisIndex"></param> + /// <param name="minValue"></param> + /// <param name="maxValue"></param> + public static void GetZMinMaxValue(BaseChart chart, int axisIndex, bool inverse, out double minValue, + out double maxValue, bool isPolar = false, bool filterByDataZoom = true, bool needAnimation = false) + { + GetMinMaxValue(chart, axisIndex, inverse, 2, out minValue, out maxValue, isPolar, filterByDataZoom, needAnimation); + } + + private static Dictionary<int, List<Serie>> _stackSeriesForMinMax = new Dictionary<int, List<Serie>>(); + private static Dictionary<int, double> _serieTotalValueForMinMax = new Dictionary<int, double>(); + public static void GetMinMaxValue(BaseChart chart, int axisIndex, + bool inverse, int dimension, out double minValue, out double maxValue, bool isPolar = false, + bool filterByDataZoom = true, bool needAnimation = false) + { + double min = double.MaxValue; + double max = double.MinValue; + var series = chart.series; + var isPercentStack = SeriesHelper.IsPercentStack<Bar>(series); + if (!SeriesHelper.IsStack(series)) + { + for (int i = 0; i < series.Count; i++) + { + var serie = series[i]; + if ((isPolar && serie.polarIndex != axisIndex) || + (!isPolar && serie.yAxisIndex != axisIndex) || + !serie.show) continue; + var updateDuration = needAnimation ? serie.animation.GetChangeDuration() : 0; + var dataAddDuration = needAnimation ? serie.animation.GetAdditionDuration() : 0; + var unscaledTime = serie.animation.unscaledTime; + if (isPercentStack && SeriesHelper.IsPercentStack<Bar>(series, serie.serieName)) + { + if (100 > max) max = 100; + if (0 < min) min = 0; + } + else + { + var showData = serie.GetDataList(filterByDataZoom ? chart.GetXDataZoomOfSerie(serie) : null); + if (serie is Candlestick || serie is SimplifiedCandlestick) + { + foreach (var data in showData) + { + double dataMin, dataMax; + data.GetMinMaxData(1, inverse, out dataMin, out dataMax); + if (dataMax > max) max = dataMax; + if (dataMin < min) min = dataMin; + } + } + else + { + var performanceMode = serie.IsPerformanceMode(); + foreach (var data in showData) + { + var currData = performanceMode ? data.GetData(dimension, inverse) : + data.GetCurrData(dimension, dataAddDuration, updateDuration, unscaledTime, inverse); + if (!serie.IsIgnoreValue(data, currData)) + { + if (currData > max) max = currData; + if (currData < min) min = currData; + } + } + } + } + } + } + else + { + SeriesHelper.GetStackSeries(series, ref _stackSeriesForMinMax); + foreach (var ss in _stackSeriesForMinMax) + { + _serieTotalValueForMinMax.Clear(); + for (int i = 0; i < ss.Value.Count; i++) + { + var serie = ss.Value[i]; + if ((isPolar && serie.polarIndex != axisIndex) || + (!isPolar && serie.yAxisIndex != axisIndex) || + !serie.show) continue; + var showData = serie.GetDataList(filterByDataZoom ? chart.GetXDataZoomOfSerie(serie) : null); + if (SeriesHelper.IsPercentStack<Bar>(series, serie.stack)) + { + for (int j = 0; j < showData.Count; j++) + { + _serieTotalValueForMinMax[j] = 100; + } + } + else + { + var updateDuration = needAnimation ? serie.animation.GetChangeDuration() : 0; + var dataAddDuration = needAnimation ? serie.animation.GetAdditionDuration() : 0; + var unscaledTime = serie.animation.unscaledTime; + for (int j = 0; j < showData.Count; j++) + { + if (!_serieTotalValueForMinMax.ContainsKey(j)) + _serieTotalValueForMinMax[j] = 0; + double currData = 0; + if (serie is Candlestick) + { + currData = showData[j].GetMaxData(false); + } + else + { + currData = showData[j].GetCurrData(dimension, dataAddDuration, updateDuration, unscaledTime, inverse); + } + if (!serie.IsIgnoreValue(showData[j], currData)) + _serieTotalValueForMinMax[j] = _serieTotalValueForMinMax[j] + currData; + } + } + } + double tmax = double.MinValue; + double tmin = double.MaxValue; + foreach (var tt in _serieTotalValueForMinMax) + { + if (tt.Value > tmax) tmax = tt.Value; + if (tt.Value < tmin) tmin = tt.Value; + } + if (tmax > max) max = tmax; + if (tmin < min) min = tmin; + } + } + if (max == double.MinValue && min == double.MaxValue) + { + minValue = 0; + maxValue = 0; + } + else if (min == 0 && max == 0) + { + minValue = 0; + maxValue = 1; + } + else + { + minValue = min; + maxValue = max; + } + } + + public static int GetMaxSerieDataCount(List<Serie> series) + { + int max = 0; + foreach (var serie in series) + { + if (serie.dataCount > max) max = serie.dataCount; + } + return max; + } + + public static float GetMinAnimationDuration(List<Serie> series) + { + float min = float.MaxValue; + foreach (var serie in series) + { + var changeAnimation = serie.animation.change.duration; + var additionAnimation = serie.animation.addition.duration; + if (changeAnimation != 0 && changeAnimation < min) min = changeAnimation; + if (additionAnimation != 0 && additionAnimation < min) min = additionAnimation; + } + return min; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Serie/SeriesHelper.cs.meta b/Assets/XCharts/Runtime/Serie/SeriesHelper.cs.meta new file mode 100644 index 0000000..4a9ecc1 --- /dev/null +++ b/Assets/XCharts/Runtime/Serie/SeriesHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a1c1086d9f88497d9e0ac89d719ff48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme.meta b/Assets/XCharts/Runtime/Theme.meta new file mode 100644 index 0000000..6987280 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b421f1dec4b2943d19640698c2504c6b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/AxisTheme.cs b/Assets/XCharts/Runtime/Theme/AxisTheme.cs new file mode 100644 index 0000000..807c6da --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/AxisTheme.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + [Serializable] + public class BaseAxisTheme : ComponentTheme + { + [SerializeField] protected LineStyle.Type m_LineType = LineStyle.Type.Solid; + [SerializeField] protected float m_LineWidth = 1f; + [SerializeField] protected float m_LineLength = 0f; + [SerializeField] protected Color32 m_LineColor; + [SerializeField] protected LineStyle.Type m_SplitLineType = LineStyle.Type.Dashed; + [SerializeField] protected float m_SplitLineWidth = 1f; + [SerializeField] protected float m_SplitLineLength = 0f; + [SerializeField] protected Color32 m_SplitLineColor; + [SerializeField] protected Color32 m_MinorSplitLineColor; + [SerializeField] protected float m_TickWidth = 1f; + [SerializeField] protected float m_TickLength = 5f; + [SerializeField] protected Color32 m_TickColor; + [SerializeField] protected List<Color32> m_SplitAreaColors = new List<Color32>(); + + /// <summary> + /// the type of line. + /// ||坐标轴线类型。 + /// </summary> + public LineStyle.Type lineType + { + get { return m_LineType; } + set { if (PropertyUtil.SetStruct(ref m_LineType, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of line. + /// ||坐标轴线宽。 + /// </summary> + public float lineWidth + { + get { return m_LineWidth; } + set { if (PropertyUtil.SetStruct(ref m_LineWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the length of line. + /// ||坐标轴线长。 + /// </summary> + public float lineLength + { + get { return m_LineLength; } + set { if (PropertyUtil.SetStruct(ref m_LineLength, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of line. + /// ||坐标轴线颜色。 + /// </summary> + public Color32 lineColor + { + get { return m_LineColor; } + set { if (PropertyUtil.SetColor(ref m_LineColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the type of split line. + /// ||分割线线类型。 + /// </summary> + public LineStyle.Type splitLineType + { + get { return m_SplitLineType; } + set { if (PropertyUtil.SetStruct(ref m_SplitLineType, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of split line. + /// ||分割线线宽。 + /// </summary> + public float splitLineWidth + { + get { return m_SplitLineWidth; } + set { if (PropertyUtil.SetStruct(ref m_SplitLineWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the length of split line. + /// ||分割线线长。 + /// </summary> + public float splitLineLength + { + get { return m_SplitLineLength; } + set { if (PropertyUtil.SetStruct(ref m_SplitLineLength, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of split line. + /// ||分割线线颜色。 + /// </summary> + public Color32 splitLineColor + { + get { return m_SplitLineColor; } + set { if (PropertyUtil.SetColor(ref m_SplitLineColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of minor split line. + /// ||次分割线线颜色。 + /// </summary> + public Color32 minorSplitLineColor + { + get { return ChartHelper.IsClearColor(m_MinorSplitLineColor) ? ColorUtil.GetColor("#F4F7FD") : m_MinorSplitLineColor; } + set { if (PropertyUtil.SetColor(ref m_MinorSplitLineColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the length of tick. + /// ||刻度线线长。 + /// </summary> + public float tickLength + { + get { return m_TickLength; } + set { if (PropertyUtil.SetStruct(ref m_TickLength, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of tick. + /// ||刻度线线宽。 + /// </summary> + public float tickWidth + { + get { return m_TickWidth; } + set { if (PropertyUtil.SetStruct(ref m_TickWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of tick. + /// ||坐标轴线颜色。 + /// </summary> + public Color32 tickColor + { + get { return m_TickColor; } + set { if (PropertyUtil.SetColor(ref m_TickColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the colors of split area. + /// ||坐标轴分隔区域的颜色。 + /// </summary> + public List<Color32> splitAreaColors + { + get { return m_SplitAreaColors; } + set { if (value != null) { m_SplitAreaColors = value; SetVerticesDirty(); } } + } + + public BaseAxisTheme(ThemeType theme) : base(theme) + { + m_FontSize = XCSettings.fontSizeLv4; + m_LineType = XCSettings.axisLineType; + m_LineWidth = XCSettings.axisLineWidth; + m_LineLength = 0; + m_SplitLineType = XCSettings.axisSplitLineType; + m_SplitLineWidth = XCSettings.axisSplitLineWidth; + m_SplitLineLength = 0; + m_TickWidth = XCSettings.axisTickWidth; + m_TickLength = XCSettings.axisTickLength; + switch (theme) + { + case ThemeType.Default: + m_LineColor = ColorUtil.GetColor("#6E7079"); + m_TickColor = ColorUtil.GetColor("#6E7079"); + m_SplitLineColor = ColorUtil.GetColor("#E0E6F1"); + m_MinorSplitLineColor = ColorUtil.GetColor("#F4F7FD"); + m_SplitAreaColors = new List<Color32> + { + new Color32(250, 250, 250, 51), + new Color32(210, 219, 238, 51) + }; + break; + case ThemeType.Light: + m_LineColor = ColorUtil.GetColor("#6E7079"); + m_TickColor = ColorUtil.GetColor("#6E7079"); + m_SplitLineColor = ColorUtil.GetColor("#E0E6F1"); + m_MinorSplitLineColor = ColorUtil.GetColor("#F4F7FD"); + m_SplitAreaColors = new List<Color32> + { + new Color32(250, 250, 250, 51), + new Color32(210, 219, 238, 51) + }; + break; + case ThemeType.Dark: + m_LineColor = ColorUtil.GetColor("#6E7079"); + m_TickColor = ColorUtil.GetColor("#6E7079"); + m_SplitLineColor = ColorUtil.GetColor("#E0E6F1"); + m_MinorSplitLineColor = ColorUtil.GetColor("#F4F7FD"); + m_SplitAreaColors = new List<Color32> + { + new Color32(255, 255, 255, (byte) (0.02f * 255)), + new Color32(210, 219, 238, (byte) (0.02f * 255)) + }; + break; + } + } + + public void Copy(BaseAxisTheme theme) + { + base.Copy(theme); + m_LineType = theme.lineType; + m_LineWidth = theme.lineWidth; + m_LineLength = theme.lineLength; + m_LineColor = theme.lineColor; + m_SplitLineType = theme.splitLineType; + m_SplitLineWidth = theme.splitLineWidth; + m_SplitLineLength = theme.splitLineLength; + m_SplitLineColor = theme.splitLineColor; + m_TickWidth = theme.tickWidth; + m_TickLength = theme.tickLength; + m_TickColor = theme.tickColor; + ChartHelper.CopyList(m_SplitAreaColors, theme.splitAreaColors); + } + } + + [Serializable] + public class AxisTheme : BaseAxisTheme + { + public AxisTheme(ThemeType theme) : base(theme) { } + } + + [Serializable] + public class RadiusAxisTheme : BaseAxisTheme + { + public RadiusAxisTheme(ThemeType theme) : base(theme) { } + } + + [Serializable] + public class AngleAxisTheme : BaseAxisTheme + { + public AngleAxisTheme(ThemeType theme) : base(theme) { } + } + + [Serializable] + public class PolarAxisTheme : BaseAxisTheme + { + public PolarAxisTheme(ThemeType theme) : base(theme) { } + } + + [Serializable] + public class RadarAxisTheme : BaseAxisTheme + { + public RadarAxisTheme(ThemeType theme) : base(theme) + { + m_SplitAreaColors.Clear(); + switch (theme) + { + case ThemeType.Dark: + m_SplitAreaColors.Add(ThemeStyle.GetColor("#6f6f6f")); + m_SplitAreaColors.Add(ThemeStyle.GetColor("#606060")); + break; + case ThemeType.Default: + m_SplitAreaColors.Add(ThemeStyle.GetColor("#f6f6f6")); + m_SplitAreaColors.Add(ThemeStyle.GetColor("#e7e7e7")); + break; + case ThemeType.Light: + m_SplitAreaColors.Add(ThemeStyle.GetColor("#f6f6f6")); + m_SplitAreaColors.Add(ThemeStyle.GetColor("#e7e7e7")); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/AxisTheme.cs.meta b/Assets/XCharts/Runtime/Theme/AxisTheme.cs.meta new file mode 100644 index 0000000..cc88ab9 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/AxisTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aefd22e76a6f642c9985b1a29e389858 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/ComponentTheme.cs b/Assets/XCharts/Runtime/Theme/ComponentTheme.cs new file mode 100644 index 0000000..5a03462 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/ComponentTheme.cs @@ -0,0 +1,102 @@ +using System; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + [Serializable] + public class ComponentTheme : ChildComponent + { + [SerializeField] protected Font m_Font; + [SerializeField] protected Color m_TextColor; + [SerializeField] protected Color m_TextBackgroundColor; + [SerializeField] protected int m_FontSize = 18; +#if dUI_TextMeshPro + [SerializeField] protected TMP_FontAsset m_TMPFont; +#endif + + /// <summary> + /// the font of text. + /// ||字体。 + /// </summary> + public Font font + { + get { return m_Font; } + set { m_Font = value; SetComponentDirty(); } + } + /// <summary> + /// the color of text. + /// ||文本颜色。 + /// </summary> + public Color textColor + { + get { return m_TextColor; } + set { if (PropertyUtil.SetColor(ref m_TextColor, value)) SetComponentDirty(); } + } + /// <summary> + /// the color of text. + /// ||文本颜色。 + /// </summary> + public Color textBackgroundColor + { + get { return m_TextBackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_TextBackgroundColor, value)) SetComponentDirty(); } + } + /// <summary> + /// the font size of text. + /// ||文本字体大小。 + /// </summary> + public int fontSize + { + get { return m_FontSize; } + set { if (PropertyUtil.SetStruct(ref m_FontSize, value)) SetComponentDirty(); } + } + +#if dUI_TextMeshPro + /// <summary> + /// the font of chart text。 + /// ||字体。 + /// </summary> + public TMP_FontAsset tmpFont + { + get { return m_TMPFont; } + set { m_TMPFont = value; SetComponentDirty(); } + } +#endif + + public ComponentTheme(ThemeType theme) + { + m_FontSize = XCSettings.fontSizeLv3; + switch (theme) + { + case ThemeType.Default: + m_TextColor = ColorUtil.GetColor("#514D4D"); + break; + case ThemeType.Light: + m_TextColor = ColorUtil.GetColor("#514D4D"); + break; + case ThemeType.Dark: + m_TextColor = ColorUtil.GetColor("#B9B8CE"); + break; + } + } + + public virtual void Copy(ComponentTheme theme) + { + m_Font = theme.font; + m_FontSize = theme.fontSize; + m_TextColor = theme.textColor; + m_TextBackgroundColor = theme.textBackgroundColor; +#if dUI_TextMeshPro + m_TMPFont = theme.tmpFont; +#endif + } + + public virtual void Reset(ComponentTheme defaultTheme) + { + Copy(defaultTheme); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/ComponentTheme.cs.meta b/Assets/XCharts/Runtime/Theme/ComponentTheme.cs.meta new file mode 100644 index 0000000..e401a88 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/ComponentTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e78d1c80572324fc0b5cc5c935a2e34c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/DataZoomTheme.cs b/Assets/XCharts/Runtime/Theme/DataZoomTheme.cs new file mode 100644 index 0000000..4512734 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/DataZoomTheme.cs @@ -0,0 +1,125 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + public class DataZoomTheme : ComponentTheme + { + [SerializeField] protected float m_BorderWidth; + [SerializeField] protected float m_DataLineWidth; + [SerializeField] protected Color32 m_FillerColor; + [SerializeField] protected Color32 m_BorderColor; + [SerializeField] protected Color32 m_DataLineColor; + [SerializeField] protected Color32 m_DataAreaColor; + [SerializeField] protected Color32 m_BackgroundColor; + + /// <summary> + /// the width of border line. + /// ||边框线宽。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of data line. + /// ||数据阴影线宽。 + /// </summary> + public float dataLineWidth + { + get { return m_DataLineWidth; } + set { if (PropertyUtil.SetStruct(ref m_DataLineWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of dataZoom data area. + /// ||数据区域颜色。 + /// </summary> + public Color32 fillerColor + { + get { return m_FillerColor; } + set { if (PropertyUtil.SetColor(ref m_FillerColor, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the color of dataZoom border. + /// ||边框颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetComponentDirty(); } + } + /// <summary> + /// the color of data area line. + /// ||数据阴影的线条颜色。 + /// </summary> + public Color32 dataLineColor + { + get { return m_DataLineColor; } + set { if (PropertyUtil.SetColor(ref m_DataLineColor, value)) SetComponentDirty(); } + } + /// <summary> + /// the color of data area line. + /// ||数据阴影的填充颜色。 + /// </summary> + public Color32 dataAreaColor + { + get { return m_DataAreaColor; } + set { if (PropertyUtil.SetColor(ref m_DataAreaColor, value)) SetComponentDirty(); } + } + /// <summary> + /// the background color of datazoom. + /// ||背景颜色。 + /// </summary> + public Color32 backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_BackgroundColor, value)) SetComponentDirty(); } + } + + public DataZoomTheme(ThemeType theme) : base(theme) + { + m_BorderWidth = XCSettings.dataZoomBorderWidth; + m_DataLineWidth = XCSettings.dataZoomDataLineWidth; + m_BackgroundColor = Color.clear; + switch (theme) + { + case ThemeType.Default: + m_TextColor = ColorUtil.GetColor("#333"); + m_FillerColor = new Color32(167, 183, 204, 110); + m_BorderColor = ColorUtil.GetColor("#ddd"); + m_DataLineColor = ColorUtil.GetColor("#2f4554"); + m_DataAreaColor = new Color32(47, 69, 84, 85); + break; + case ThemeType.Light: + m_TextColor = ColorUtil.GetColor("#333"); + m_FillerColor = new Color32(167, 183, 204, 110); + m_BorderColor = ColorUtil.GetColor("#ddd"); + m_DataLineColor = ColorUtil.GetColor("#2f4554"); + m_DataAreaColor = new Color32(47, 69, 84, 85); + break; + case ThemeType.Dark: + m_TextColor = ColorUtil.GetColor("#B9B8CE"); + m_FillerColor = new Color32(135, 163, 206, (byte) (0.2f * 255)); + m_BorderColor = ColorUtil.GetColor("#71708A"); + m_DataLineColor = ColorUtil.GetColor("#71708A"); + m_DataAreaColor = ColorUtil.GetColor("#71708A"); + break; + } + } + + public void Copy(DataZoomTheme theme) + { + base.Copy(theme); + m_BorderWidth = theme.borderWidth; + m_DataLineWidth = theme.dataLineWidth; + m_FillerColor = theme.fillerColor; + m_BorderColor = theme.borderColor; + m_DataLineColor = theme.dataLineColor; + m_DataAreaColor = theme.dataAreaColor; + m_BackgroundColor = theme.backgroundColor; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/DataZoomTheme.cs.meta b/Assets/XCharts/Runtime/Theme/DataZoomTheme.cs.meta new file mode 100644 index 0000000..9545b7f --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/DataZoomTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba535ef75742b4825b3cc2be4df6716f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/LegendTheme.cs b/Assets/XCharts/Runtime/Theme/LegendTheme.cs new file mode 100644 index 0000000..044c77d --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/LegendTheme.cs @@ -0,0 +1,36 @@ +using System; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + [Serializable] + public class LegendTheme : ComponentTheme + { + [SerializeField] protected Color m_UnableColor; + + /// <summary> + /// the color of text. + /// ||文本颜色。 + /// </summary> + public Color unableColor + { + get { return m_UnableColor; } + set { if (PropertyUtil.SetColor(ref m_UnableColor, value)) SetComponentDirty(); } + } + + public void Copy(LegendTheme theme) + { + base.Copy(theme); + m_UnableColor = theme.unableColor; + } + + public LegendTheme(ThemeType theme) : base(theme) + { + m_UnableColor = ColorUtil.GetColor("#cccccc"); + + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/LegendTheme.cs.meta b/Assets/XCharts/Runtime/Theme/LegendTheme.cs.meta new file mode 100644 index 0000000..d544c65 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/LegendTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a86fb06a6b71c4735b87769ee0708293 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/SerieTheme.cs b/Assets/XCharts/Runtime/Theme/SerieTheme.cs new file mode 100644 index 0000000..558e33d --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/SerieTheme.cs @@ -0,0 +1,128 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + public class SerieTheme : ChildComponent + { + [SerializeField] protected float m_LineWidth; + [SerializeField] protected float m_LineSymbolSize; + [SerializeField] protected float m_ScatterSymbolSize; + [SerializeField] protected Color32 m_CandlestickColor = new Color32(235, 84, 84, 255); + [SerializeField] protected Color32 m_CandlestickColor0 = new Color32(71, 178, 98, 255); + [SerializeField] protected float m_CandlestickBorderWidth = 1; + [SerializeField] protected Color32 m_CandlestickBorderColor = new Color32(235, 84, 84, 255); + [SerializeField] protected Color32 m_CandlestickBorderColor0 = new Color32(71, 178, 98, 255); + + /// <summary> + /// the color of text. + /// ||文本颜色。 + /// </summary> + public float lineWidth + { + get { return m_LineWidth; } + set { if (PropertyUtil.SetStruct(ref m_LineWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the symbol size of line serie. + /// ||折线图的Symbol大小。 + /// </summary> + public float lineSymbolSize + { + get { return m_LineSymbolSize; } + set { if (PropertyUtil.SetStruct(ref m_LineSymbolSize, value)) SetVerticesDirty(); } + } + /// <summary> + /// the symbol size of scatter serie. + /// ||散点图的Symbol大小。 + /// </summary> + public float scatterSymbolSize + { + get { return m_ScatterSymbolSize; } + set { if (PropertyUtil.SetStruct(ref m_ScatterSymbolSize, value)) SetVerticesDirty(); } + } + /// <summary> + /// K线图阳线(涨)填充色 + /// </summary> + public Color32 candlestickColor + { + get { return m_CandlestickColor; } + set { if (PropertyUtil.SetColor(ref m_CandlestickColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// K线图阴线(跌)填充色 + /// </summary> + public Color32 candlestickColor0 + { + get { return m_CandlestickColor0; } + set { if (PropertyUtil.SetColor(ref m_CandlestickColor0, value)) SetVerticesDirty(); } + } + /// <summary> + /// K线图阳线(跌)边框色 + /// </summary> + public Color32 candlestickBorderColor + { + get { return m_CandlestickBorderColor; } + set { if (PropertyUtil.SetColor(ref m_CandlestickBorderColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// K线图阴线(跌)边框色 + /// </summary> + public Color32 candlestickBorderColor0 + { + get { return m_CandlestickBorderColor0; } + set { if (PropertyUtil.SetColor(ref m_CandlestickBorderColor0, value)) SetVerticesDirty(); } + } + + /// <summary> + /// K线图边框宽度 + /// </summary> + public float candlestickBorderWidth + { + get { return m_CandlestickBorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_CandlestickBorderWidth, value < 0 ? 0f : value)) SetVerticesDirty(); } + } + + public void Copy(SerieTheme theme) + { + m_LineWidth = theme.lineWidth; + m_LineSymbolSize = theme.lineSymbolSize; + m_ScatterSymbolSize = theme.scatterSymbolSize; + m_CandlestickColor = theme.candlestickColor; + m_CandlestickColor0 = theme.candlestickColor0; + m_CandlestickBorderColor = theme.candlestickBorderColor; + m_CandlestickBorderColor0 = theme.candlestickBorderColor0; + m_CandlestickBorderWidth = theme.candlestickBorderWidth; + } + + public SerieTheme(ThemeType theme) + { + m_LineWidth = XCSettings.serieLineWidth; + m_LineSymbolSize = XCSettings.serieLineSymbolSize; + m_ScatterSymbolSize = XCSettings.serieScatterSymbolSize; + m_CandlestickBorderWidth = XCSettings.serieCandlestickBorderWidth; + switch (theme) + { + case ThemeType.Default: + m_CandlestickColor = ColorUtil.GetColor("#eb5454"); + m_CandlestickColor0 = ColorUtil.GetColor("#47b262"); + m_CandlestickBorderColor = ColorUtil.GetColor("#eb5454"); + m_CandlestickBorderColor0 = ColorUtil.GetColor("#47b262"); + break; + case ThemeType.Light: + m_CandlestickColor = ColorUtil.GetColor("#eb5454"); + m_CandlestickColor0 = ColorUtil.GetColor("#47b262"); + m_CandlestickBorderColor = ColorUtil.GetColor("#eb5454"); + m_CandlestickBorderColor0 = ColorUtil.GetColor("#47b262"); + break; + case ThemeType.Dark: + m_CandlestickColor = ColorUtil.GetColor("#f64e56"); + m_CandlestickColor0 = ColorUtil.GetColor("#54ea92"); + m_CandlestickBorderColor = ColorUtil.GetColor("#f64e56"); + m_CandlestickBorderColor0 = ColorUtil.GetColor("#54ea92"); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/SerieTheme.cs.meta b/Assets/XCharts/Runtime/Theme/SerieTheme.cs.meta new file mode 100644 index 0000000..6649238 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/SerieTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9030b0e4afb164967b4991247947b195 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/SubTitleTheme.cs b/Assets/XCharts/Runtime/Theme/SubTitleTheme.cs new file mode 100644 index 0000000..7e0ae93 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/SubTitleTheme.cs @@ -0,0 +1,25 @@ +using System; + +namespace XCharts.Runtime +{ + [Serializable] + public class SubTitleTheme : ComponentTheme + { + public SubTitleTheme(ThemeType theme) : base(theme) + { + m_FontSize = XCSettings.fontSizeLv2; + switch (theme) + { + case ThemeType.Default: + m_TextColor = ColorUtil.GetColor("#969696"); + break; + case ThemeType.Light: + m_TextColor = ColorUtil.GetColor("#969696"); + break; + case ThemeType.Dark: + m_TextColor = ColorUtil.GetColor("#B9B8CE"); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/SubTitleTheme.cs.meta b/Assets/XCharts/Runtime/Theme/SubTitleTheme.cs.meta new file mode 100644 index 0000000..c296a35 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/SubTitleTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c642293f2d6674cbb85d1f081b9d89e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/Theme.cs b/Assets/XCharts/Runtime/Theme/Theme.cs new file mode 100644 index 0000000..d2eb570 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/Theme.cs @@ -0,0 +1,405 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + /// <summary> + /// Theme. + /// ||主题相关配置。 + /// </summary> + [Serializable] + public class Theme : ScriptableObject + { + [SerializeField] private ThemeType m_ThemeType = ThemeType.Default; + [SerializeField] private string m_ThemeName = ThemeType.Default.ToString(); + [SerializeField] private Font m_Font; +#if dUI_TextMeshPro + [SerializeField] private TMP_FontAsset m_TMPFont; +#endif + + [SerializeField] private Color32 m_ContrastColor; + [SerializeField] private Color32 m_BackgroundColor; + +#if UNITY_2020_2 + [NonReorderable] +#endif + [SerializeField] private List<Color32> m_ColorPalette = new List<Color32>(13); + + [SerializeField] private ComponentTheme m_Common; + [SerializeField] private TitleTheme m_Title; + [SerializeField] private SubTitleTheme m_SubTitle; + [SerializeField] private LegendTheme m_Legend; + [SerializeField] private AxisTheme m_Axis; + [SerializeField] private TooltipTheme m_Tooltip; + [SerializeField] private DataZoomTheme m_DataZoom; + [SerializeField] private VisualMapTheme m_VisualMap; + [SerializeField] private SerieTheme m_Serie; + + /// <summary> + /// the theme of chart. + /// ||主题类型。 + /// </summary> + public ThemeType themeType + { + get { return m_ThemeType; } + set { PropertyUtil.SetStruct(ref m_ThemeType, value); } + } + /// <summary> + /// the name of theme. + /// ||主题名称。 + /// </summary> + public string themeName + { + get { return m_ThemeName; } + set { PropertyUtil.SetClass(ref m_ThemeName, value); } + } + + /// <summary> + /// the contrast color of chart. + /// ||对比色。 + /// </summary> + public Color32 contrastColor + { + get { return m_ContrastColor; } + set { PropertyUtil.SetColor(ref m_ContrastColor, value); } + } + /// <summary> + /// the background color of chart. + /// ||背景颜色。 + /// </summary> + public Color32 backgroundColor + { + get { return m_BackgroundColor; } + set { PropertyUtil.SetColor(ref m_BackgroundColor, value); } + } + + /// <summary> + /// The color list of palette. If no color is set in series, the colors would be adopted sequentially and circularly from this list as the colors of series. + /// ||调色盘颜色列表。如果系列没有设置颜色,则会依次循环从该列表中取颜色作为系列颜色。 + /// </summary> + public List<Color32> colorPalette { get { return m_ColorPalette; } set { m_ColorPalette = value; } } + public ComponentTheme common { get { return m_Common; } set { m_Common = value; } } + public TitleTheme title { get { return m_Title; } set { m_Title = value; } } + public SubTitleTheme subTitle { get { return m_SubTitle; } set { m_SubTitle = value; } } + public LegendTheme legend { get { return m_Legend; } set { m_Legend = value; } } + public AxisTheme axis { get { return m_Axis; } set { m_Axis = value; } } + public TooltipTheme tooltip { get { return m_Tooltip; } set { m_Tooltip = value; } } + public DataZoomTheme dataZoom { get { return m_DataZoom; } set { m_DataZoom = value; } } + public VisualMapTheme visualMap { get { return m_VisualMap; } set { m_VisualMap = value; } } + public SerieTheme serie { get { return m_Serie; } set { m_Serie = value; } } +#if dUI_TextMeshPro + /// <summary> + /// the font of chart text。 + /// ||主题字体。 + /// </summary> + public TMP_FontAsset tmpFont + { + get { return m_TMPFont; } + set + { + m_TMPFont = value; + SyncTMPFontToSubComponent(); + } + } +#endif + /// <summary> + /// the font of chart text。 + /// ||主题字体。 + /// </summary> + public Font font + { + get { return m_Font; } + set + { + m_Font = value; + SyncFontToSubComponent(); + } + } + + // void OnEnable() + // { + // } + + // void OnDisable() + // { + // } + + public void SetDefaultFont() + { +#if dUI_TextMeshPro + tmpFont = XCSettings.tmpFont; + SyncTMPFontToSubComponent(); +#else + font = XCSettings.font; + SyncFontToSubComponent(); +#endif + } + + /// <summary> + /// Gets the color of the specified index from the palette. + /// ||获得调色盘对应系列索引的颜色值。 + /// </summary> + /// <param name="index">编号索引</param> + /// <returns>the color,or Color.clear when failed.颜色值,失败时返回Color.clear</returns> + public Color32 GetColor(int index) + { + if (index < 0) index = 0; + var newIndex = index < m_ColorPalette.Count ? index : index % m_ColorPalette.Count; + if (newIndex < m_ColorPalette.Count) + return m_ColorPalette[newIndex]; + else return Color.clear; + } + + public void CheckWarning(StringBuilder sb) + { +#if dUI_TextMeshPro + if (m_TMPFont == null) + { + sb.AppendFormat("warning:theme->tmpFont is null\n"); + } +#else + if (m_Font == null) + { + sb.AppendFormat("warning:theme->font is null\n"); + } +#endif + if (m_ColorPalette.Count == 0) + { + sb.AppendFormat("warning:theme->colorPalette is empty\n"); + } + for (int i = 0; i < m_ColorPalette.Count; i++) + { + if (!ChartHelper.IsClearColor(m_ColorPalette[i]) && m_ColorPalette[i].a == 0) + sb.AppendFormat("warning:theme->colorPalette[{0}] alpha = 0\n", i); + } + } + + Dictionary<int, string> _colorDic = new Dictionary<int, string>(); + /// <summary> + /// Gets the hexadecimal color string of the specified index from the palette. + /// ||获得指定索引的十六进制颜色值字符串。 + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public string GetColorStr(int index) + { + if (index < 0) + { + index = 0; + } + index = index % m_ColorPalette.Count; + if (_colorDic.ContainsKey(index)) return _colorDic[index]; + else + { + _colorDic[index] = ColorUtility.ToHtmlStringRGBA(GetColor(index)); + return _colorDic[index]; + } + } + + public bool CopyTheme(ThemeType theme) + { + switch (theme) + { + case ThemeType.Dark: + ResetToDarkTheme(this); + return true; + case ThemeType.Default: + ResetToDefaultTheme(this); + return true; + } + return false; + } + + /// <summary> + /// copy all configurations from theme. + /// ||复制主题的所有配置。 + /// </summary> + /// <param name="theme"></param> + public void CopyTheme(Theme theme) + { + m_ThemeType = theme.themeType; + m_ThemeName = theme.themeName; +#if dUI_TextMeshPro + tmpFont = theme.tmpFont; +#endif + font = theme.font; + m_BackgroundColor = theme.backgroundColor; + m_Common.Copy(theme.common); + m_Legend.Copy(theme.legend); + m_Title.Copy(theme.title); + m_SubTitle.Copy(theme.subTitle); + m_Axis.Copy(theme.axis); + m_Tooltip.Copy(theme.tooltip); + m_DataZoom.Copy(theme.dataZoom); + m_VisualMap.Copy(theme.visualMap); + m_Serie.Copy(theme.serie); + ChartHelper.CopyList(m_ColorPalette, theme.colorPalette); + } + + /// <summary> + /// Clear all custom configurations. + /// ||重置,清除所有自定义配置。 + /// </summary> + public bool ResetTheme() + { + switch (m_ThemeType) + { + case ThemeType.Default: + ResetToDefaultTheme(this); + return true; + case ThemeType.Dark: + ResetToDarkTheme(this); + return true; + case ThemeType.Custom: + return false; + } + return false; + } + + /// <summary> + /// 克隆主题。 + /// </summary> + /// <returns></returns> + public Theme CloneTheme() + { + var theme = ScriptableObject.CreateInstance<Theme>(); + InitChartComponentTheme(theme); + theme.CopyTheme(this); + return theme; + } + + /// <summary> + /// default theme. + /// ||默认主题。 + /// </summary> + public static void ResetToDefaultTheme(Theme theme) + { + theme.themeType = ThemeType.Default; + theme.themeName = ThemeType.Default.ToString(); + theme.backgroundColor = new Color32(255, 255, 255, 255); + theme.colorPalette = new List<Color32> + { + ColorUtil.GetColor("#5470c6"), + ColorUtil.GetColor("#91cc75"), + ColorUtil.GetColor("#fac858"), + ColorUtil.GetColor("#ee6666"), + ColorUtil.GetColor("#73c0de"), + ColorUtil.GetColor("#3ba272"), + ColorUtil.GetColor("#fc8452"), + ColorUtil.GetColor("#9a60b4"), + ColorUtil.GetColor("#ea7ccc"), + + }; + InitChartComponentTheme(theme); + } + + /// <summary> + /// dark theme. + /// ||暗主题。 + /// </summary> + public static void ResetToDarkTheme(Theme theme) + { + theme.themeType = ThemeType.Dark; + theme.themeName = ThemeType.Dark.ToString(); + theme.backgroundColor = ColorUtil.GetColor("#100C2A"); + theme.colorPalette = new List<Color32> + { + ColorUtil.GetColor("#4992ff"), + ColorUtil.GetColor("#7cffb2"), + ColorUtil.GetColor("#fddd60"), + ColorUtil.GetColor("#ff6e76"), + ColorUtil.GetColor("#58d9f9"), + ColorUtil.GetColor("#05c091"), + ColorUtil.GetColor("#ff8a45"), + ColorUtil.GetColor("#8d48e3"), + ColorUtil.GetColor("#dd79ff"), + }; + InitChartComponentTheme(theme); + } + + public static Theme EmptyTheme + { + get + { + var theme = ScriptableObject.CreateInstance<Theme>(); + theme.themeType = ThemeType.Custom; + theme.themeName = ThemeType.Custom.ToString(); + theme.backgroundColor = Color.clear; + theme.colorPalette = new List<Color32>(); + InitChartComponentTheme(theme); + return theme; + } + } + + public void SyncFontToSubComponent() + { + common.font = font; + title.font = font; + subTitle.font = font; + legend.font = font; + axis.font = font; + tooltip.font = font; + dataZoom.font = font; + visualMap.font = font; + } + +#if dUI_TextMeshPro + public void SyncTMPFontToSubComponent() + { + common.tmpFont = tmpFont; + title.tmpFont = tmpFont; + subTitle.tmpFont = tmpFont; + legend.tmpFont = tmpFont; + axis.tmpFont = tmpFont; + tooltip.tmpFont = tmpFont; + dataZoom.tmpFont = tmpFont; + visualMap.tmpFont = tmpFont; + } +#endif + + private static void InitChartComponentTheme(Theme theme) + { + theme.common = new ComponentTheme(theme.themeType); + theme.title = new TitleTheme(theme.themeType); + theme.subTitle = new SubTitleTheme(theme.themeType); + theme.legend = new LegendTheme(theme.themeType); + theme.axis = new AxisTheme(theme.themeType); + theme.tooltip = new TooltipTheme(theme.themeType); + theme.dataZoom = new DataZoomTheme(theme.themeType); + theme.visualMap = new VisualMapTheme(theme.themeType); + theme.serie = new SerieTheme(theme.themeType); + theme.SetDefaultFont(); + } + + /// <summary> + /// Convert the html string to color. + /// ||将字符串颜色值转成Color。 + /// </summary> + /// <param name="hexColorStr"></param> + /// <returns></returns> + public static Color32 GetColor(string hexColorStr) + { + Color color; + ColorUtility.TryParseHtmlString(hexColorStr, out color); + return (Color32) color; + } + + public void SetColorPalette(List<string> hexColorStringList) + { + m_ColorPalette.Clear(); + foreach (var hexColor in hexColorStringList) + m_ColorPalette.Add(ColorUtil.GetColor(hexColor)); + + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/Theme.cs.meta b/Assets/XCharts/Runtime/Theme/Theme.cs.meta new file mode 100644 index 0000000..15f4828 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/Theme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c59330ca0f4443b69f06b890a44f32e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/ThemeStyle.cs b/Assets/XCharts/Runtime/Theme/ThemeStyle.cs new file mode 100644 index 0000000..7c459b4 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/ThemeStyle.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +#if dUI_TextMeshPro +using TMPro; +#endif + +namespace XCharts.Runtime +{ + /// <summary> + /// 主题 + /// </summary> + public enum ThemeType + { + /// <summary> + /// 默认主题。 + /// </summary> + Default, + /// <summary> + /// 亮主题。 + /// </summary> + Light, + /// <summary> + /// 暗主题。 + /// </summary> + Dark, + /// <summary> + /// 自定义主题。 + /// </summary> + Custom, + } + + [Serializable] + /// <summary> + /// Theme. + /// ||主题相关配置。 + /// </summary> + public class ThemeStyle : ChildComponent + { + [SerializeField] private bool m_Show = true; + [SerializeField] private Theme m_SharedTheme; + [SerializeField] private bool m_TransparentBackground = false; + [SerializeField] private bool m_EnableCustomTheme = false; + [SerializeField] private Font m_CustomFont; + [SerializeField] private Color32 m_CustomBackgroundColor; +#if UNITY_2020_2 + [NonReorderable] +#endif + [SerializeField] private List<Color32> m_CustomColorPalette = new List<Color32>(13); + + public bool show { get { return m_Show; } } + /// <summary> + /// the theme of chart. + /// ||主题类型。 + /// </summary> + public ThemeType themeType + { + get { return sharedTheme.themeType; } + } + /// <summary> + /// theme name. + /// ||主题名字。 + /// </summary> + public string themeName + { + get { return sharedTheme.themeName; } + } + /// <summary> + /// the asset of theme. + /// ||主题配置。 + /// </summary> + public Theme sharedTheme + { + get { return m_SharedTheme; } + set { m_SharedTheme = value; SetAllDirty(); } + } + /// <summary> + /// the contrast color of chart. + /// ||对比色。 + /// </summary> + public Color32 contrastColor + { + get { return sharedTheme.contrastColor; } + } + /// <summary> + /// the background color of chart. + /// ||背景颜色。 + /// </summary> + public Color32 backgroundColor + { + get + { + if (m_TransparentBackground) return ColorUtil.clearColor32; + else return m_EnableCustomTheme ? m_CustomBackgroundColor : sharedTheme.backgroundColor; + } + } + /// <summary> + /// Whether the background color is transparent. When true, the background color is not drawn. + /// ||是否透明背景颜色。当设置为true时,不绘制背景颜色。 + /// </summary> + public bool transparentBackground + { + get { return m_TransparentBackground; } + set { m_TransparentBackground = value; SetAllDirty(); } + } + /// <summary> + /// Whether to customize theme colors. When set to true, + /// you can use 'sync color to custom' to synchronize the theme color to the custom color. It can also be set manually. + /// ||是否自定义主题颜色。当设置为true时,可以用‘sync color to custom’同步主题的颜色到自定义颜色。也可以手动设置。 + /// </summary> + public bool enableCustomTheme + { + get { return m_EnableCustomTheme; } + set { m_EnableCustomTheme = value; _colorDic.Clear(); SetAllDirty(); } + } + /// <summary> + /// the custom background color of chart. + /// ||自定义的背景颜色。 + /// </summary> + public Color32 customBackgroundColor + { + get { return m_CustomBackgroundColor; } + set { m_CustomBackgroundColor = value; SetAllDirty(); } + } + + /// <summary> + /// The color list of palette. If no color is set in series, the colors would be adopted sequentially and circularly from this list as the colors of series. + /// ||调色盘颜色列表。如果系列没有设置颜色,则会依次循环从该列表中取颜色作为系列颜色。 + /// </summary> + public List<Color32> colorPalette + { + get { return m_EnableCustomTheme ? m_CustomColorPalette : sharedTheme.colorPalette; } + } + public List<Color32> customColorPalette { get { return m_CustomColorPalette; } set { m_CustomColorPalette = value; SetVerticesDirty(); } } + public ComponentTheme common { get { return sharedTheme.common; } } + public TitleTheme title { get { return sharedTheme.title; } } + public SubTitleTheme subTitle { get { return sharedTheme.subTitle; } } + public LegendTheme legend { get { return sharedTheme.legend; } } + public AxisTheme axis { get { return sharedTheme.axis; } } + public TooltipTheme tooltip { get { return sharedTheme.tooltip; } } + public DataZoomTheme dataZoom { get { return sharedTheme.dataZoom; } } + public VisualMapTheme visualMap { get { return sharedTheme.visualMap; } } + public SerieTheme serie { get { return sharedTheme.serie; } } + + /// <summary> + /// Gets the color of the specified index from the palette. + /// ||获得调色盘对应系列索引的颜色值。 + /// </summary> + /// <param name="index">编号索引</param> + /// <returns>the color,or Color.clear when failed.颜色值,失败时返回Color.clear</returns> + public Color32 GetColor(int index) + { + if (colorPalette.Count <= 0) return Color.clear; + if (index < 0) index = 0; + var newIndex = index < colorPalette.Count ? index : index % colorPalette.Count; + if (newIndex < colorPalette.Count) + return colorPalette[newIndex]; + else return Color.clear; + } + + public Color32 GetBackgroundColor(Background background) + { + if (background != null && background.show && !background.autoColor) + return background.imageColor; + else + return backgroundColor; + } + + public void SyncSharedThemeColorToCustom() + { + m_CustomBackgroundColor = sharedTheme.backgroundColor; + m_CustomColorPalette.Clear(); + foreach (var color in sharedTheme.colorPalette) + { + m_CustomColorPalette.Add(color); + } + SetAllDirty(); + } + + public void CheckWarning(StringBuilder sb) + { +#if dUI_TextMeshPro + if (sharedTheme.tmpFont == null) + { + sb.AppendFormat("warning:theme->tmpFont is null\n"); + } +#else + if (sharedTheme.font == null) + { + sb.AppendFormat("warning:theme->font is null\n"); + } +#endif + if (sharedTheme.colorPalette.Count == 0) + { + sb.AppendFormat("warning:theme->colorPalette is empty\n"); + } + for (int i = 0; i < sharedTheme.colorPalette.Count; i++) + { + if (!ChartHelper.IsClearColor(sharedTheme.colorPalette[i]) && sharedTheme.colorPalette[i].a == 0) + sb.AppendFormat("warning:theme->colorPalette[{0}] alpha = 0\n", i); + } + } + + Dictionary<int, string> _colorDic = new Dictionary<int, string>(); + /// <summary> + /// Gets the hexadecimal color string of the specified index from the palette. + /// ||获得指定索引的十六进制颜色值字符串。 + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + public string GetColorStr(int index) + { + if (index < 0) + { + index = 0; + } + index = index % colorPalette.Count; + if (_colorDic.ContainsKey(index)) return _colorDic[index]; + else + { + _colorDic[index] = ColorUtility.ToHtmlStringRGBA(GetColor(index)); + return _colorDic[index]; + } + } + + /// <summary> + /// Convert the html string to color. + /// ||将字符串颜色值转成Color。 + /// </summary> + /// <param name="hexColorStr"></param> + /// <returns></returns> + public static Color32 GetColor(string hexColorStr) + { + Color color; + ColorUtility.TryParseHtmlString(hexColorStr, out color); + return (Color32) color; + } + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/ThemeStyle.cs.meta b/Assets/XCharts/Runtime/Theme/ThemeStyle.cs.meta new file mode 100644 index 0000000..ba89424 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/ThemeStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd363d1f78f9d47dab079b1376cf0680 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/TitleTheme.cs b/Assets/XCharts/Runtime/Theme/TitleTheme.cs new file mode 100644 index 0000000..e675cf5 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/TitleTheme.cs @@ -0,0 +1,24 @@ +using System; + +namespace XCharts.Runtime +{ + [Serializable] + public class TitleTheme : ComponentTheme + { + public TitleTheme(ThemeType theme) : base(theme) + { + m_FontSize = XCSettings.fontSizeLv1; + switch (theme) + { + case ThemeType.Default: + m_TextColor = ColorUtil.GetColor("#514D4D"); + break; + case ThemeType.Light: + break; + case ThemeType.Dark: + m_TextColor = ColorUtil.GetColor("#EEF1FA"); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/TitleTheme.cs.meta b/Assets/XCharts/Runtime/Theme/TitleTheme.cs.meta new file mode 100644 index 0000000..a409e26 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/TitleTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6649bc33964624c14a13ce34dd7eae77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/TooltipTheme.cs b/Assets/XCharts/Runtime/Theme/TooltipTheme.cs new file mode 100644 index 0000000..ed90d03 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/TooltipTheme.cs @@ -0,0 +1,118 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + public class TooltipTheme : ComponentTheme + { + + [SerializeField] protected LineStyle.Type m_LineType = LineStyle.Type.Solid; + [SerializeField] protected float m_LineWidth = 1f; + [SerializeField] protected Color32 m_LineColor; + [SerializeField] protected Color32 m_AreaColor; + [SerializeField] protected Color32 m_LabelTextColor; + [SerializeField] protected Color32 m_LabelBackgroundColor; + + /// <summary> + /// the type of line. + /// ||坐标轴线类型。 + /// </summary> + public LineStyle.Type lineType + { + get { return m_LineType; } + set { if (PropertyUtil.SetStruct(ref m_LineType, value)) SetVerticesDirty(); } + } + /// <summary> + /// the width of line. + /// ||指示线线宽。 + /// </summary> + public float lineWidth + { + get { return m_LineWidth; } + set { if (PropertyUtil.SetStruct(ref m_LineWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of line. + /// ||指示线颜色。 + /// </summary> + public Color32 lineColor + { + get { return m_LineColor; } + set { if (PropertyUtil.SetColor(ref m_LineColor, value)) SetVerticesDirty(); } + } + + /// <summary> + /// the color of line. + /// ||区域指示的颜色。 + /// </summary> + public Color32 areaColor + { + get { return m_AreaColor; } + set { if (PropertyUtil.SetColor(ref m_AreaColor, value)) SetVerticesDirty(); } + } + /// <summary> + /// the text color of tooltip cross indicator's axis label. + /// ||十字指示器坐标轴标签的文本颜色。 + /// </summary> + public Color32 labelTextColor + { + get { return m_LabelTextColor; } + set { if (PropertyUtil.SetColor(ref m_LabelTextColor, value)) SetComponentDirty(); } + } + + /// <summary> + /// the background color of tooltip cross indicator's axis label. + /// ||十字指示器坐标轴标签的背景颜色。 + /// </summary> + public Color32 labelBackgroundColor + { + get { return m_LabelBackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_LabelBackgroundColor, value)) SetComponentDirty(); } + } + + public TooltipTheme(ThemeType theme) : base(theme) + { + m_LineType = LineStyle.Type.Solid; + m_LineWidth = XCSettings.tootipLineWidth; + switch (theme) + { + case ThemeType.Default: + m_TextBackgroundColor = ColorUtil.GetColor("#FFFFFFFF"); + m_TextColor = ColorUtil.GetColor("#000000FF"); + m_AreaColor = ColorUtil.GetColor("#51515120"); + m_LabelTextColor = ColorUtil.GetColor("#FFFFFFFF"); + m_LabelBackgroundColor = ColorUtil.GetColor("#292929FF"); + m_LineColor = ColorUtil.GetColor("#29292964"); + break; + case ThemeType.Light: + m_TextBackgroundColor = ColorUtil.GetColor("#FFFFFFFF"); + m_TextColor = ColorUtil.GetColor("#000000FF"); + m_AreaColor = ColorUtil.GetColor("#51515120"); + m_LabelTextColor = ColorUtil.GetColor("#FFFFFFFF"); + m_LabelBackgroundColor = ColorUtil.GetColor("#292929FF"); + m_LineColor = ColorUtil.GetColor("#29292964"); + break; + case ThemeType.Dark: + m_TextBackgroundColor = ColorUtil.GetColor("#FFFFFFFF"); + m_TextColor = ColorUtil.GetColor("#000000FF"); + m_AreaColor = ColorUtil.GetColor("#51515120"); + m_LabelTextColor = ColorUtil.GetColor("#FFFFFFFF"); + m_LabelBackgroundColor = ColorUtil.GetColor("#292929FF"); + m_LineColor = ColorUtil.GetColor("#29292964"); + break; + } + } + + public void Copy(TooltipTheme theme) + { + base.Copy(theme); + m_LineType = theme.lineType; + m_LineWidth = theme.lineWidth; + m_LineColor = theme.lineColor; + m_AreaColor = theme.areaColor; + m_LabelTextColor = theme.labelTextColor; + m_LabelBackgroundColor = theme.labelBackgroundColor; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/TooltipTheme.cs.meta b/Assets/XCharts/Runtime/Theme/TooltipTheme.cs.meta new file mode 100644 index 0000000..534835c --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/TooltipTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f38f041e827e042a88338628b2b2c0db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Theme/VisualMapTheme.cs b/Assets/XCharts/Runtime/Theme/VisualMapTheme.cs new file mode 100644 index 0000000..f07b952 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/VisualMapTheme.cs @@ -0,0 +1,85 @@ +using System; +using UnityEngine; + +namespace XCharts.Runtime +{ + [Serializable] + public class VisualMapTheme : ComponentTheme + { + [SerializeField] protected float m_BorderWidth; + [SerializeField] protected Color32 m_BorderColor; + [SerializeField] protected Color32 m_BackgroundColor; + [SerializeField][Range(10, 50)] protected float m_TriangeLen = 20f; + + /// <summary> + /// the width of border. + /// ||边框线宽。 + /// </summary> + public float borderWidth + { + get { return m_BorderWidth; } + set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); } + } + /// <summary> + /// the color of dataZoom border. + /// ||边框颜色。 + /// </summary> + public Color32 borderColor + { + get { return m_BorderColor; } + set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetComponentDirty(); } + } + + /// <summary> + /// the background color of visualmap. + /// ||背景颜色。 + /// </summary> + public Color32 backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtil.SetColor(ref m_BackgroundColor, value)) SetComponentDirty(); } + } + /// <summary> + /// 可视化组件的调节三角形边长。 + /// </summary> + public float triangeLen + { + get { return m_TriangeLen; } + set { if (PropertyUtil.SetStruct(ref m_TriangeLen, value < 0 ? 1f : value)) SetVerticesDirty(); } + } + + public VisualMapTheme(ThemeType theme) : base(theme) + { + m_BorderWidth = XCSettings.visualMapBorderWidth; + m_TriangeLen = XCSettings.visualMapTriangeLen; + m_FontSize = XCSettings.fontSizeLv4; + switch (theme) + { + case ThemeType.Default: + m_TextColor = ColorUtil.GetColor("#333"); + m_BorderColor = ColorUtil.GetColor("#ccc"); + m_BackgroundColor = ColorUtil.clearColor32; + break; + case ThemeType.Light: + m_TextColor = ColorUtil.GetColor("#333"); + m_BorderColor = ColorUtil.GetColor("#ccc"); + m_BackgroundColor = ColorUtil.clearColor32; + break; + case ThemeType.Dark: + m_TextColor = ColorUtil.GetColor("#B9B8CE"); + m_BorderColor = ColorUtil.GetColor("#ccc"); + m_BackgroundColor = ColorUtil.clearColor32; + break; + } + } + + public void Copy(VisualMapTheme theme) + { + base.Copy(theme); + m_TriangeLen = theme.triangeLen; + m_BorderWidth = theme.borderWidth; + m_BorderColor = theme.borderColor; + m_BackgroundColor = theme.backgroundColor; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Theme/VisualMapTheme.cs.meta b/Assets/XCharts/Runtime/Theme/VisualMapTheme.cs.meta new file mode 100644 index 0000000..e737722 --- /dev/null +++ b/Assets/XCharts/Runtime/Theme/VisualMapTheme.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35e5797039b994b23850aaa7ca827766 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities.meta b/Assets/XCharts/Runtime/Utilities.meta new file mode 100644 index 0000000..8665a90 --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c3e4cdd9c66b14907bd1934dd8037eee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/ColorUtil.cs b/Assets/XCharts/Runtime/Utilities/ColorUtil.cs new file mode 100644 index 0000000..99b9bdc --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/ColorUtil.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class ColorUtil + { + private static Dictionary<string, Color32> s_ColorCached = new Dictionary<string, Color32>(); + public static readonly Color32 clearColor32 = new Color32(0, 0, 0, 0); + public static readonly Color32 white = new Color32(255, 255, 255, 255); + public static readonly Vector2 zeroVector2 = Vector2.zero; + + /// <summary> + /// Convert the html string to color. + /// ||将字符串颜色值转成Color。 + /// </summary> + /// <param name="hexColorStr"></param> + /// <returns></returns> + public static Color32 GetColor(string hexColorStr) + { + if (s_ColorCached.ContainsKey(hexColorStr)) + { + return s_ColorCached[hexColorStr]; + } + Color color; + ColorUtility.TryParseHtmlString(hexColorStr, out color); + s_ColorCached[hexColorStr] = (Color32) color; + return s_ColorCached[hexColorStr]; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/ColorUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/ColorUtil.cs.meta new file mode 100644 index 0000000..a53ccbb --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/ColorUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4260c3b8fdaff435a8bc10375b812bd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs b/Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs new file mode 100644 index 0000000..052ddbc --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class DateTimeUtil + { +#if UNITY_2018_3_OR_NEWER + private static readonly DateTime k_LocalDateTime1970 = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local); +#else + private static readonly DateTime k_LocalDateTime1970 = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); +#endif + private static readonly DateTime k_DateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public static readonly int ONE_SECOND = 1; + public static readonly int ONE_MINUTE = ONE_SECOND * 60; + public static readonly int ONE_HOUR = ONE_MINUTE * 60; + public static readonly int ONE_DAY = ONE_HOUR * 24; + public static readonly int ONE_MONTH = ONE_DAY * 30; + public static readonly int ONE_YEAR = ONE_DAY * 365; + public static readonly int MIN_TIME_SPLIT_NUMBER = 4; + + private static string s_YearDateFormatter = "yyyy"; + //private static string s_MonthDateFormatter = "MM"; + //private static string s_DayDateFormatter = "dd"; + //private static string s_HourDateFormatter = "HH:mm"; + //private static string s_MinuteDateFormatter = "mm:ss"; + private static string s_SecondDateFormatter = "HH:mm:ss"; + //private static string s_FullDateFormatter = "yyyy-MM-dd HH:mm:ss"; + private static Regex s_DateOrTimeRegex = new Regex(@"^(date|time)\s*[:\s]+(.*)", RegexOptions.IgnoreCase); + + public static bool IsDateOrTimeRegex(string regex) + { + return regex.StartsWith("date") || regex.StartsWith("time"); + } + + public static bool IsDateOrTimeRegex(string regex, ref bool date, ref string formatter) + { + if(IsDateOrTimeRegex(regex)) + { + if(regex == "date" || regex == "time") + { + date = regex == "date"; + formatter = ""; + return true; + } + var mc = s_DateOrTimeRegex.Matches(regex); + date = mc[0].Groups[1].Value == "date"; + formatter = mc[0].Groups[2].Value; + return true; + } + return false; + } + + public static int GetTimestamp() + { + return (int)(DateTime.Now - k_LocalDateTime1970).TotalSeconds; + } + + public static int GetTimestamp(DateTime time, bool local = false) + { + if (local) + { + return (int)(time - k_LocalDateTime1970).TotalSeconds; + } + else + { + return (int)(time - k_DateTime1970).TotalSeconds; + } + } + + public static int GetTimestamp(string dateTime, bool local = false) + { + try + { + + return GetTimestamp(DateTime.Parse(dateTime), local); + } + catch (Exception e) + { + throw e; + } + } + + public static DateTime GetDateTime(double timestamp, bool local = true) + { + return local ? k_LocalDateTime1970.AddSeconds(timestamp) : k_DateTime1970.AddSeconds(timestamp); + } + + public static string GetDefaultDateTimeString(int timestamp, double range = 0) + { + var dateString = String.Empty; + var dateTime = GetDateTime(timestamp); + if (range <= 0 || range >= DateTimeUtil.ONE_DAY) + { + dateString = dateTime.ToString("yyyy-MM-dd"); + } + else + { + dateString = dateTime.ToString(s_SecondDateFormatter); + } + return dateString; + } + + internal static string GetDateTimeFormatString(DateTime dateTime, double range) + { + var dateString = String.Empty; + if (range >= DateTimeUtil.ONE_YEAR * DateTimeUtil.MIN_TIME_SPLIT_NUMBER) + { + dateString = dateTime.ToString(s_YearDateFormatter); + } + else if (range >= DateTimeUtil.ONE_MONTH * DateTimeUtil.MIN_TIME_SPLIT_NUMBER) + { + dateString = dateTime.Month == 1 ? + dateTime.ToString(s_YearDateFormatter) : + XCSettings.lang.GetMonthAbbr(dateTime.Month); + } + else if (range >= DateTimeUtil.ONE_DAY * DateTimeUtil.MIN_TIME_SPLIT_NUMBER) + { + dateString = dateTime.Day == 1 ? + XCSettings.lang.GetMonthAbbr(dateTime.Month) : + XCSettings.lang.GetDay(dateTime.Day); + } + else if (range >= DateTimeUtil.ONE_HOUR * DateTimeUtil.MIN_TIME_SPLIT_NUMBER) + { + dateString = dateTime.ToString(s_SecondDateFormatter); + } + else if (range >= DateTimeUtil.ONE_MINUTE * DateTimeUtil.MIN_TIME_SPLIT_NUMBER) + { + dateString = dateTime.ToString(s_SecondDateFormatter); + } + else + { + dateString = dateTime.ToString(s_SecondDateFormatter); + } + return dateString; + } + + /// <summary> + /// 根据给定的最大最小时间戳范围,计算合适的Tick值 + /// </summary> + /// <param name="list"></param> + /// <param name="minTimestamp"></param> + /// <param name="maxTimestamp"></param> + /// <param name="splitNumber"></param> + internal static float UpdateTimeAxisDateTimeList(List<double> list, int minTimestamp, int maxTimestamp, int splitNumber) + { + var firstValue = list.Count > 0 ? list[0] : 0; + var secondValue = list.Count > 1 ? list[1] : 0; + list.Clear(); + var range = maxTimestamp - minTimestamp; + if (range <= 0) return 0; + var dtMin = DateTimeUtil.GetDateTime(minTimestamp); + var dtMax = DateTimeUtil.GetDateTime(maxTimestamp); + int tick = 0; + if (range >= ONE_YEAR * MIN_TIME_SPLIT_NUMBER) + { + var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_YEAR) : Math.Max(range / (splitNumber * ONE_YEAR), 1); + var dtStart = (firstValue == 0 || secondValue == 0 || (minTimestamp > firstValue && minTimestamp > secondValue)) + ? (new DateTime(dtMin.Year, dtMin.Month, 1).AddMonths(1)) + : (minTimestamp > firstValue ? DateTimeUtil.GetDateTime(secondValue) : DateTimeUtil.GetDateTime(firstValue)); + tick = num * 365 * 24 * 3600; + dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); + while (dtStart.Ticks < dtMax.Ticks) + { + list.Add(DateTimeUtil.GetTimestamp(dtStart)); + dtStart = dtStart.AddYears(num); + } + } + else if (range >= ONE_MONTH * MIN_TIME_SPLIT_NUMBER) + { + var num = splitNumber <= 0 ? GetSplitNumber(range, ONE_MONTH) : Math.Max(range / (splitNumber * ONE_MONTH), 1); + var dtStart = (firstValue == 0 || secondValue == 0 || (minTimestamp > firstValue && minTimestamp > secondValue)) + ? (new DateTime(dtMin.Year, dtMin.Month, 1).AddMonths(1)) + : (minTimestamp > firstValue ? DateTimeUtil.GetDateTime(secondValue) : DateTimeUtil.GetDateTime(firstValue)); + dtStart = new DateTime(dtStart.Year, dtStart.Month, 1); + tick = num * 30 * 24 * 3600; + while (dtStart.Ticks < dtMax.Ticks) + { + list.Add(DateTimeUtil.GetTimestamp(dtStart)); + dtStart = dtStart.AddMonths(num); + } + } + else if (range >= ONE_DAY * MIN_TIME_SPLIT_NUMBER) + { + tick = GetTickSecond(range, splitNumber, ONE_DAY); + var let = minTimestamp % tick; + var startTimestamp = let == 0 ? minTimestamp : (minTimestamp - let) + tick; + AddTickTimestamp(list, startTimestamp, maxTimestamp, tick); + } + else if (range >= ONE_HOUR * MIN_TIME_SPLIT_NUMBER) + { + tick = GetTickSecond(range, splitNumber, ONE_HOUR); + var let = minTimestamp % tick; + var startTimestamp = let == 0 ? minTimestamp : (minTimestamp - let) + tick; + AddTickTimestamp(list, startTimestamp, maxTimestamp, tick); + } + else if (range >= ONE_MINUTE * MIN_TIME_SPLIT_NUMBER) + { + tick = GetTickSecond(range, splitNumber, ONE_MINUTE); + var let = minTimestamp % tick; + var startTimestamp = let == 0 ? minTimestamp : (minTimestamp - let) + tick; + AddTickTimestamp(list, startTimestamp, maxTimestamp, tick); + } + else + { + tick = GetTickSecond(range, splitNumber, ONE_SECOND); + var let = minTimestamp % tick; + var startTimestamp = let == 0 ? minTimestamp : (minTimestamp - let) + tick; + AddTickTimestamp(list, startTimestamp, maxTimestamp, tick); + } + return tick; + } + + private static int GetSplitNumber(int range, int tickSecond) + { + var num = 1; + while (range / (num * tickSecond) > 8) + { + num++; + } + return num; + } + + private static int GetTickSecond(int range, int splitNumber, int tickSecond) + { + var num = 0; + if (splitNumber > 0) + { + num = Math.Max(range / (splitNumber * tickSecond), 1); + } + else + { + num = 1; + var tick = tickSecond; + while (range / tick > 8) + { + num++; + tick = num * tickSecond; + } + } + return num * tickSecond; + } + + private static void AddTickTimestamp(List<double> list, int startTimestamp, int maxTimestamp, int tickSecond) + { + while (startTimestamp <= maxTimestamp) + { + list.Add(startTimestamp); + startTimestamp += tickSecond; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs.meta new file mode 100644 index 0000000..ac2244b --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/DateTimeUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f0ac80f189a04b5c826f40c8bc8af64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs b/Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs new file mode 100644 index 0000000..60a361c --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs @@ -0,0 +1,131 @@ +#if UNITY_EDITOR + +using System; +using System.Reflection; +using System.Text; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class DefineSymbolsUtil + { + private static readonly StringBuilder s_StringBuilder = new StringBuilder(); + + public static void AddGlobalDefine(string symbol) + { + var flag = false; + var num = 0; +#if UNITY_2022_1_OR_NEWER + foreach (var buildTargetGroup in (BuildTargetGroup[]) Enum.GetValues(typeof(BuildTargetGroup))) + { + if (IsValidBuildTargetGroup(buildTargetGroup)) + { + var buildTargetName = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup); + var symbols = PlayerSettings.GetScriptingDefineSymbols(buildTargetName); + symbols = symbols.Replace(" ", ""); + if (Array.IndexOf(symbols.Split(';'), symbol) != -1) continue; + flag = true; + num++; + var defines = symbols + (symbols.Length > 0 ? ";" + symbol : symbol); + PlayerSettings.SetScriptingDefineSymbols(buildTargetName, defines); + } + } +#else + foreach (var buildTargetGroup in (BuildTargetGroup[]) Enum.GetValues(typeof(BuildTargetGroup))) + { + if (IsValidBuildTargetGroup(buildTargetGroup)) + { + var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); + symbols = symbols.Replace(" ", ""); + if (Array.IndexOf(symbols.Split(';'), symbol) != -1) continue; + flag = true; + num++; + var defines = symbols + (symbols.Length > 0 ? ";" + symbol : symbol); + PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, defines); + } + } +#endif + if (flag) + { + Debug.LogFormat("Added global define symbol \"{0}\" to {1} BuildTargetGroups.", symbol, num); + } + } + + public static void RemoveGlobalDefine(string symbol) + { + var flag = false; + var num = 0; +#if UNITY_2022_1_OR_NEWER + foreach (var buildTargetGroup in (BuildTargetGroup[]) Enum.GetValues(typeof(BuildTargetGroup))) + { + if (IsValidBuildTargetGroup(buildTargetGroup)) + { + var buildTargetName = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup); + var symbols = PlayerSettings.GetScriptingDefineSymbols(buildTargetName).Split(';'); + if (Array.IndexOf(symbols, symbol) == -1) continue; + flag = true; + num++; + s_StringBuilder.Length = 0; + foreach (var str in symbols) + { + if (!str.Equals(symbol)) + { + if (s_StringBuilder.Length > 0) s_StringBuilder.Append(";"); + s_StringBuilder.Append(str); + } + } + PlayerSettings.SetScriptingDefineSymbols(buildTargetName, s_StringBuilder.ToString()); + } + } +#else + foreach (var buildTargetGroup in (BuildTargetGroup[]) Enum.GetValues(typeof(BuildTargetGroup))) + { + if (IsValidBuildTargetGroup(buildTargetGroup)) + { + var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(';'); + if (Array.IndexOf(symbols, symbol) == -1) continue; + flag = true; + num++; + s_StringBuilder.Length = 0; + foreach (var str in symbols) + { + if (!str.Equals(symbol)) + { + if (s_StringBuilder.Length > 0) s_StringBuilder.Append(";"); + s_StringBuilder.Append(str); + } + } + PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, s_StringBuilder.ToString()); + } + } +#endif + if (flag) + { + Debug.LogFormat("Removed global define symbol \"{0}\" to {1} BuildTargetGroups.", symbol, num); + } + } + + private static bool IsValidBuildTargetGroup(BuildTargetGroup group) + { + if (group == BuildTargetGroup.Unknown) return false; + var type = Type.GetType("UnityEditor.Modules.ModuleManager, UnityEditor.dll"); + if (type == null) return true; + var method1 = type.GetMethod("GetTargetStringFromBuildTargetGroup", BindingFlags.Static | BindingFlags.NonPublic); + var method2 = typeof(PlayerSettings).GetMethod("GetPlatformName", BindingFlags.Static | BindingFlags.NonPublic); + if (method1 == null || method2 == null) return true; + var str1 = (string) method1.Invoke(null, new object[] { group }); + var str2 = (string) method2.Invoke(null, new object[] { group }); + if (string.IsNullOrEmpty(str1)) + { + return !string.IsNullOrEmpty(str2); + } + else + { + return true; + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs.meta new file mode 100644 index 0000000..eae3d81 --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/DefineSymbolsUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91545951242fa441eb1a9bba3a6ad5a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/JsonUtil.cs b/Assets/XCharts/Runtime/Utilities/JsonUtil.cs new file mode 100644 index 0000000..b8b018a --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/JsonUtil.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.Networking; + +namespace XCharts.Runtime +{ + public static class JsonUtil + { + + public static IEnumerator GetWebJson<T>(string url, Action<T[]> callback) + { + var www = UnityWebRequest.Get(url); + yield return www; +#if UNITY_2020_1_OR_NEWER + if (www.result != UnityWebRequest.Result.Success) +#else + if (www.isNetworkError || www.isHttpError) +#endif + { + Debug.LogError("GetWebJson Error: " + www.error); + } + + else + { + var json = www.downloadHandler.text.Trim(); + callback(GetJsonArray<T>(json)); + www.Dispose(); + } + } + + public static IEnumerator GetWebJson<T>(string url, Action<T> callback) + { + var www = UnityWebRequest.Get(url); + yield return www; +#if UNITY_2020_1_OR_NEWER + if (www.result != UnityWebRequest.Result.Success) +#else + if (www.isNetworkError || www.isHttpError) +#endif + { + Debug.LogError("GetWebJson Error: " + www.error); + } + else + { + var json = www.downloadHandler.text.Trim(); + callback(GetJsonObject<T>(json)); + www.Dispose(); + } + } + + public static T GetJsonObject<T>(string json) + { + return JsonUtility.FromJson<T>(json); + } + + public static T[] GetJsonArray<T>(string json) + { + string newJson = "{ \"array\": " + json + "}"; + Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(newJson); + return wrapper.array; + } + + [Serializable] + private class Wrapper<T> + { +#pragma warning disable 0649 + public T[] array; +#pragma warning restore 0649 + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/JsonUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/JsonUtil.cs.meta new file mode 100644 index 0000000..db138b1 --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/JsonUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88e9115d32af34a3dae0d5c3e32de41c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/PropertyUtil.cs b/Assets/XCharts/Runtime/Utilities/PropertyUtil.cs new file mode 100644 index 0000000..313ac3d --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/PropertyUtil.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class PropertyUtil + { + public static bool SetColor(ref Color currentValue, Color newValue) + { + if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a) + return false; + + currentValue = newValue; + return true; + } + + public static bool SetColor(ref Color32 currentValue, Color32 newValue) + { + if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a) + return false; + + currentValue = newValue; + return true; + } + + public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct + { + if (EqualityComparer<T>.Default.Equals(currentValue, newValue)) + return false; + + currentValue = newValue; + return true; + } + + public static bool SetClass<T>(ref T currentValue, T newValue, bool notNull = false) where T : class + { + if (notNull) + { + if (newValue == null) + { + Debug.LogError("can not be null."); + return false; + } + } + if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue))) + return false; + + currentValue = newValue; + return true; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/PropertyUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/PropertyUtil.cs.meta new file mode 100644 index 0000000..9292ef0 --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/PropertyUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1f52eadd805d43aea47947fb81e761f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs b/Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs new file mode 100644 index 0000000..5af73af --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; + +namespace XCharts.Runtime +{ + public static class ReflectionUtil + { + private static Dictionary<object, MethodInfo> listClearMethodInfoCaches = new Dictionary<object, MethodInfo>(); + private static Dictionary<object, MethodInfo> listAddMethodInfoCaches = new Dictionary<object, MethodInfo>(); + + public static void InvokeListClear(object obj, FieldInfo field) + { + var list = field.GetValue(obj); + MethodInfo method; + if (!listClearMethodInfoCaches.TryGetValue(list, out method)) + { + method = list.GetType().GetMethod("Clear"); + listClearMethodInfoCaches[list] = method; + } + method.Invoke(list, new object[] { }); + } + public static int InvokeListCount(object obj, FieldInfo field) + { + var list = field.GetValue(obj); + return (int) list.GetType().GetProperty("Count").GetValue(list, null); + } + + public static void InvokeListAdd(object obj, FieldInfo field, object item) + { + var list = field.GetValue(obj); + MethodInfo method; + if (!listAddMethodInfoCaches.TryGetValue(list, out method)) + { + method = list.GetType().GetMethod("Add"); + listAddMethodInfoCaches[list] = method; + } + method.Invoke(list, new object[] { item }); + } + + public static T InvokeListGet<T>(object obj, FieldInfo field, int i) + { + var list = field.GetValue(obj); + var item = list.GetType().GetProperty("Item").GetValue(list, new object[] { i }); + return (T) item; + } + + public static void InvokeListAddTo<T>(object obj, FieldInfo field, Action<T> callback) + { + var list = field.GetValue(obj); + var listType = list.GetType(); + var count = Convert.ToInt32(listType.GetProperty("Count").GetValue(list, null)); + for (int i = 0; i < count; i++) + { + var item = listType.GetProperty("Item").GetValue(list, new object[] { i }); + callback((T) item); + } + } + + public static object DeepCloneSerializeField(object obj) + { + if (obj == null) + return null; + + var type = obj.GetType(); + if (type.IsValueType || type == typeof(string)) + { + return obj; + } + else if (type.IsArray) + { + var elementType = Type.GetType(type.FullName.Replace("[]", string.Empty)); + var array = obj as Array; + var copied = Array.CreateInstance(elementType, array.Length); + for (int i = 0; i < array.Length; i++) + copied.SetValue(DeepCloneSerializeField(array.GetValue(i)), i); + return Convert.ChangeType(copied, obj.GetType()); + } + else if (type.IsClass) + { + object returnObj; + var listObj = obj as IList; + if (listObj != null) + { + var properties = type.GetProperties(); + var customList = typeof(List<>).MakeGenericType((properties[properties.Length - 1]).PropertyType); + returnObj = (IList) Activator.CreateInstance(customList); + var list = (IList) returnObj; + foreach (var item in ((IList) obj)) + { + if (item == null) + continue; + list.Add(DeepCloneSerializeField(item)); + } + } + else + { + try + { + returnObj = Activator.CreateInstance(type); + } + catch + { + return null; + } + var fileds = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + for (int i = 0; i < fileds.Length; i++) + { + var field = fileds[i]; + if (!field.IsDefined(typeof(SerializeField), false)) + continue; + var filedValue = field.GetValue(obj); + if (filedValue == null) + { + field.SetValue(returnObj, filedValue); + } + else + { + field.SetValue(returnObj, DeepCloneSerializeField(filedValue)); + } + } + } + return returnObj; + } + else + { + throw new ArgumentException("DeepCloneSerializeField: Unknown type:" + type + "," + obj); + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs.meta new file mode 100644 index 0000000..77dc154 --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/ReflectionUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03acc4ee710ff4bad9a1740391c86cb9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs b/Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs new file mode 100644 index 0000000..8bd0fe0 --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEngine.Assertions; + +namespace XCharts.Runtime +{ + public static class RuntimeUtil + { + public static bool HasSubclass(Type type) + { + var typeMap = GetAllTypesDerivedFrom(type); + foreach (var t in typeMap) + { + return true; + } + return false; + } + + public static IEnumerable<Type> GetAllTypesDerivedFrom<T>() + { +#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER + return UnityEditor.TypeCache.GetTypesDerivedFrom<T>(); +#else + return GetAllAssemblyTypes().Where(t => t.IsSubclassOf(typeof(T))); +#endif + } + public static IEnumerable<Type> GetAllTypesDerivedFrom(Type type) + { +#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER + return UnityEditor.TypeCache.GetTypesDerivedFrom(type); +#else + return GetAllAssemblyTypes().Where(t => t.IsSubclassOf(type)); +#endif + } + + static IEnumerable<Type> m_AssemblyTypes; + + public static IEnumerable<Type> GetAllAssemblyTypes() + { + if (m_AssemblyTypes == null) + { + m_AssemblyTypes = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(t => + { + var innerTypes = new Type[0]; + try + { + innerTypes = t.GetTypes(); + } + catch { } + return innerTypes; + }); + } + return m_AssemblyTypes; + } + + public static T GetAttribute<T>(this Type type, bool check = true) where T : Attribute + { + if (type.IsDefined(typeof(T), false)) + return (T) type.GetCustomAttributes(typeof(T), false) [0]; + else + { + if (check) + Assert.IsTrue(false, "Attribute not found:" + type.Name); + return null; + } + } + public static T GetAttribute<T>(this MemberInfo type, bool check = true) where T : Attribute + { + if (type.IsDefined(typeof(T), false)) + return (T) type.GetCustomAttributes(typeof(T), false) [0]; + else + { + if (check) + Assert.IsTrue(false, "Attribute not found:" + type.Name); + return null; + } + } + + + + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs.meta b/Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs.meta new file mode 100644 index 0000000..448e24e --- /dev/null +++ b/Assets/XCharts/Runtime/Utilities/RuntimeUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44becf1664ae64397b44adcf65e6d8d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XCharts.Runtime.asmdef b/Assets/XCharts/Runtime/XCharts.Runtime.asmdef new file mode 100644 index 0000000..6460936 --- /dev/null +++ b/Assets/XCharts/Runtime/XCharts.Runtime.asmdef @@ -0,0 +1,13 @@ +{ + "name": "XCharts.Runtime", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XCharts.Runtime.asmdef.meta b/Assets/XCharts/Runtime/XCharts.Runtime.asmdef.meta new file mode 100644 index 0000000..2b08206 --- /dev/null +++ b/Assets/XCharts/Runtime/XCharts.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dd8043639e4014317a7246f064330196 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XLog.meta b/Assets/XCharts/Runtime/XLog.meta new file mode 100644 index 0000000..998fed3 --- /dev/null +++ b/Assets/XCharts/Runtime/XLog.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a4ed57531ebf43999c449f6aa58595c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XLog/XLog.cs b/Assets/XCharts/Runtime/XLog/XLog.cs new file mode 100644 index 0000000..f0d94d3 --- /dev/null +++ b/Assets/XCharts/Runtime/XLog/XLog.cs @@ -0,0 +1,340 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// <summary> + /// Log system. Used to output logs with date and log type, support output to file, support custom output log type. + /// ||日志系统。用于输出带日期和日志类型的日志,支持输出到文件,支持自定义输出的日志类型。 + /// </summary> + public class XLog : MonoBehaviour + { + public const int ALL = 0; + public const int WARNING = 1; + public const int DEBUG = 2; + public const int INFO = 3; + public const int PROTO = 4; + public const int VITAL = 5; + public const int ERROR = 6; + public const int EXCEPTION = 7; + + private const int MAX_ERROR_LOG = 20; + + public static bool isReportBug = false; + public static bool isOutputLog = false; + public static bool isUploadLog = false; + public static bool isCloseOutLog = false; + + public static int errorCount = 0; + public static int exceptCount = 0; + public static int uploadTick = 20; + public static int reportTick = 10; + + private static bool initFileSuccess = false; + private static bool[] levelList = new bool[] { true, true, true, true, true, true, true, true }; + private static List<string> writeList = new List<string>(); + private static float uploadTime = 0; + private static float reportTime = 0; + + private string outpath; + private StreamWriter writer; + private string[] temp; + + public int logCount = 0; + public static List<string> errorList = new List<string>(); + private static object m_Lock = new object(); + + private static XLog m_Instance; + public static XLog Instance + { + get + { + // if (m_Instance == null) + // { + // GameObject go = new GameObject("XLog"); + // m_Instance = go.AddComponent<XLog>(); + // DontDestroyOnLoad(go); + // } + return m_Instance; + } + } + + void Awake() + { + if (m_Instance != null) + { + Destroy(gameObject); + return; + } + m_Instance = this; + InitLogFile(); + // Application.logMessageReceived += HandleLog; + Application.logMessageReceivedThreaded += HandleLog; + } + + void OnDestroy() + { + if (writer != null) + { + writer.Close(); + writer.Dispose(); + } + // Application.logMessageReceived -= HandleLog; + Application.logMessageReceivedThreaded -= HandleLog; + } + + void Update() + { + uploadTime += Time.deltaTime; + reportTime += Time.deltaTime; + lock (m_Lock) + { + if (writeList.Count > 0) + { + logCount = writeList.Count; + if (!initFileSuccess) + { + writeList.Clear(); + return; + } + try + { + temp = writeList.ToArray(); + int count = 0; + foreach (var str in temp) + { + count++; + writer.WriteLine(str); + writeList.Remove(str); + if (count > 10) break; + } + writer.Flush(); + } + catch (Exception e) + { + initFileSuccess = false; + //Application.logMessageReceived -= HandleLog; + Application.logMessageReceivedThreaded -= HandleLog; + UnityEngine.Debug.LogError("write outlog.txt error:" + e.Message); + } + } + } + } + + private void InitLogFile() + { + ClearAllLog(); + XLog.EnableLog(ALL); + if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) + { + XLog.ClearAllLog(); + XLog.EnableLog(VITAL); + XLog.EnableLog(ERROR); + XLog.isReportBug = true; + XLog.isUploadLog = true; + } + else + { + XLog.isUploadLog = false; + XLog.isReportBug = false; + } + outpath = GetLogOutputPath(); + try + { + if (File.Exists(outpath)) + { + File.Delete(outpath); + } + writer = new StreamWriter(outpath, false, Encoding.UTF8); + writer.WriteLine(GetNowTime() + "init file success!!"); + UnityEngine.Debug.Log(GetNowTime() + "init file success:" + outpath); + writer.Flush(); + initFileSuccess = true; + } + catch (Exception e) + { + initFileSuccess = false; + Application.logMessageReceived -= HandleLog; + UnityEngine.Debug.LogError("write outlog.txt error:" + e.Message); + } + } + + private static string GetLogOutputPath() + { +#if UNITY_EDITOR + string path = Application.dataPath + "/../outlog.txt"; +#else + string path = Application.persistentDataPath + "/outlog.txt"; + if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) + { + path = Application.persistentDataPath + "/outlog.txt"; + } + else + { + path = Application.dataPath + "/../outlog.txt"; + } +#endif + return path; + } + + private void HandleLog(string logString, string stackTrace, LogType type) + { + lock (m_Lock) + { + if (!initFileSuccess) return; + int index = logString.IndexOf("stack traceback"); + if (index > 0) + { + string log = logString.Substring(0, index); + string trace = logString.Substring(index, logString.Length - index); + logString = log; + stackTrace = trace; + } + + if (type == LogType.Log) + { + } + else if (type == LogType.Error) + { + if (logString.IndexOf("LUA ERROR") > 0 || logString.IndexOf("stack traceback") > 0) exceptCount++; + else errorCount++; + + writeList.Add(logString); + //writeList.Add(stackTrace + "\n"); + + if (errorList.Count >= MAX_ERROR_LOG) + { + errorList.RemoveAt(1); + } + + if (errorList.Count < MAX_ERROR_LOG) + { + errorList.Add(logString); + // errorList.Add(stackTrace + "\n"); + } + } + else if (type == LogType.Exception) + { + exceptCount++; + + writeList.Add(logString); + writeList.Add(stackTrace + "\n"); + + if (errorList.Count >= MAX_ERROR_LOG) + { + errorList.RemoveAt(1); + } + + if (errorList.Count < MAX_ERROR_LOG) + { + errorList.Add(logString); + errorList.Add(stackTrace + "\n"); + } + } + } + } + + public static void FlushLog() + { + var instance = XLog.Instance; + if (instance != null && instance.writer != null) + { + for (int i = 0; i < writeList.Count; i++) + { + instance.writer.WriteLine(writeList[i]); + } + instance.writer.Flush(); + writeList.Clear(); + } + } + + public static void EnableLog(int logType) + { + if (logType < 0 || logType >= levelList.Length) return; + levelList[logType] = true; + } + + public static void ClearAllLog() + { + for (int i = 0; i < levelList.Length; i++) + { + levelList[i] = false; + } + } + + public static bool CanLog(int level) + { + if (level < 0 || level >= levelList.Length) return false; + return levelList[level] || levelList[0]; + } + + public static void Log(string log) + { + Debug(log); + } + + public static void LogError(string log) + { + Error(log); + } + + public static void LogWarning(string log) + { + Warning(log); + } + + public static void Debug(string log) + { + if (!CanLog(DEBUG)) return; + UnityEngine.Debug.Log(GetNowTime() + "[DEBUG]\t" + log); + } + + public static void Vital(string log) + { + if (!CanLog(INFO)) return; + UnityEngine.Debug.Log(GetNowTime() + "[VITAL]\t" + log); + } + + public static void Info(string log) + { + if (!CanLog(INFO)) return; + UnityEngine.Debug.Log(GetNowTime() + "[INFO]\t" + log); + } + + public static void Proto(string log) + { + if (!CanLog(PROTO)) return; + UnityEngine.Debug.Log(GetNowTime() + "[PROTO]\t" + log); + } + + public static void Warning(string log) + { + if (!CanLog(WARNING)) return; + UnityEngine.Debug.LogWarning(GetNowTime() + "[WARN]\t" + log); + } + + public static void Error(string log) + { + if (!CanLog(ERROR)) return; + UnityEngine.Debug.LogError(GetNowTime() + "[ERROR]\t" + log); + } + + public static string GetNowTime(string formatter = null) + { + DateTime now = DateTime.Now; + if (formatter == null) + return now.ToString("[HH:mm:ss fff]", DateTimeFormatInfo.InvariantInfo); + else + return now.ToString(formatter, DateTimeFormatInfo.InvariantInfo); + } + + public static ulong GetTimestamp() + { + return (ulong)(DateTime.Now - new DateTime(190, 1, 1, 0, 0, 0, 0)).TotalSeconds; + } + } +} diff --git a/Assets/XCharts/Runtime/XLog/XLog.cs.meta b/Assets/XCharts/Runtime/XLog/XLog.cs.meta new file mode 100644 index 0000000..79a5560 --- /dev/null +++ b/Assets/XCharts/Runtime/XLog/XLog.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: baf125f6000464daeb59d4c183eed941 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL.meta b/Assets/XCharts/Runtime/XUGL.meta new file mode 100644 index 0000000..53b2bee --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c4fe06f67e9674b808b44154ab0e5fc3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/SVG.meta b/Assets/XCharts/Runtime/XUGL/SVG.meta new file mode 100644 index 0000000..bd6001d --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6e78d1d27fb8f42948af1c6050eb6a46 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVG.cs b/Assets/XCharts/Runtime/XUGL/SVG/SVG.cs new file mode 100644 index 0000000..791620d --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVG.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XUGL +{ + public static class SVG + { + public static bool yMirror = false; + public static void Test(VertexHelper vh) + { + //UGL.DrawSvgPath(vh, "path://M600,800 C625,700 725,700 750,800 S875,900 900,800"); + //UGL.DrawSvgPath(vh, "path://M67.335,33.596L67.335,33.596c-0.002-1.39-1.153-3.183-3.328-4.218h-9.096v-2.07h5.371 c-4.939-2.07-11.199-4.141-14.89-4.141H19.72v12.421v5.176h38.373c4.033,0,8.457-1.035,9.142-5.176h-0.027 c0.076-0.367,0.129-0.751,0.129-1.165L67.335,33.596L67.335,33.596z M27.999,30.413h-3.105v-4.141h3.105V30.413z M35.245,30.413 h-3.104v-4.141h3.104V30.413z M42.491,30.413h-3.104v-4.141h3.104V30.413z M49.736,30.413h-3.104v-4.141h3.104V30.413z M14.544,40.764c1.143,0,2.07-0.927,2.07-2.07V35.59V25.237c0-1.145-0.928-2.07-2.07-2.07H-9.265c-1.143,0-2.068,0.926-2.068,2.07 v10.351v3.105c0,1.144,0.926,2.07,2.068,2.07H14.544L14.544,40.764z M8.333,26.272h3.105v4.141H8.333V26.272z M1.087,26.272h3.105 v4.141H1.087V26.272z M-6.159,26.272h3.105v4.141h-3.105V26.272z M-9.265,41.798h69.352v1.035H-9.265V41.798z"); + //UGL.DrawSvgPath(vh, "path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z"); + + //人体 + UGL.DrawSvgPath(vh, "path://M36.7,102.84c-1.17,2.54-2.99,4.98-3.39,7.63c-1.51,9.89-3.31,19.58-1.93,29.95 c0.95,7.15-2.91,14.82-3.57,22.35c-0.64,7.36-0.2,14.86,0.35,22.25c0.12,1.68,2.66,3.17,4.67,5.4c-0.6,0.82-1.5,2.22-2.58,3.48 c-0.96,1.12-1.96,2.35-3.21,3.04c-1.71,0.95-3.71,2.03-5.51,1.9c-1.18-0.08-3.04-2.13-3.16-3.43c-0.44-4.72,0-9.52-0.41-14.25 c-0.94-10.88-2.32-21.72-3.24-32.61c-0.49-5.84-1.63-12.01-0.35-17.54c3.39-14.56,2.8-28.84,0.36-43.4 c-2.71-16.16-1.06-32.4,0.54-48.59c0.91-9.22,4.62-17.36,8.53-25.57c1.32-2.77,1.88-6.84,0.87-9.62C21.89-3.77,18.09-11,14.7-18.38 c-0.56,0.1-1.13,0.21-1.69,0.31C10.17-11.52,6.29-5.2,4.71,1.65C2.05,13.21-4.42,22.3-11.43,31.28c-1.32,1.69-2.51,3.5-3.98,5.04 c-4.85,5.08-3.25,10.98-2.32,16.82c0.25,1.53,0.52,3.06,0.77,4.59c-0.53,0.22-1.07,0.43-1.6,0.65c-1.07-2.09-2.14-4.19-3.28-6.44 c-6.39,2.91-2.67,9.6-5.23,15.16c-1.61-3.31-2.77-5.68-3.93-8.06c0-0.33,0-0.67,0-1c6.96-16.08,14.63-31.9,20.68-48.31 C-5.24-4.07-2.03-18.55,2-32.73c0.36-1.27,0.75-2.53,0.98-3.82c1.36-7.75,4.19-10.23,11.88-10.38c1.76-0.04,3.52-0.21,5.76-0.35 c-0.55-3.95-1.21-7.3-1.45-10.68c-0.61-8.67,0.77-16.69,7.39-23.19c2.18-2.14,4.27-4.82,5.25-7.65c2.39-6.88,11.66-9,16.94-8.12 c5.92,0.99,12.15,7.93,12.16,14.12c0.01,9.89-5.19,17.26-12.24,23.68c-2.17,1.97-5.35,4.77-5.17,6.94c0.31,3.78,4.15,5.66,8.08,6.04 c1.82,0.18,3.7,0.37,5.49,0.1c5.62-0.85,8.8,2.17,10.85,6.73C73.38-27.19,78.46-14.9,84.2-2.91c1.52,3.17,4.52,5.91,7.41,8.09 c7.64,5.77,15.57,11.16,23.45,16.61c2.28,1.58,4.64,3.23,7.21,4.14c5.18,1.84,8.09,5.63,9.82,10.46c0.45,1.24,0.19,3.71-0.6,4.18 c-1.06,0.63-3.15,0.27-4.44-0.38c-7.05-3.54-12.84-8.88-19.14-13.5c-3.5-2.57-7.9-4-12.03-5.6c-9.44-3.66-17.73-8.42-22.5-18.09 c-2.43-4.94-6.09-9.27-9.69-14.61c-1.2,10.98-4.46,20.65,1.14,31.19c6.62,12.47,5.89,26.25,1.21,39.49 c-2.52,7.11-6.5,13.74-8.67,20.94c-1.91,6.33-2.2,13.15-3.23,19.75c-0.72,4.63-0.84,9.48-2.36,13.84 c-2.49,7.16-6.67,13.83-5.84,21.82c0.42,4.02,1.29,7.99,2.1,12.8c-3.74-0.49-7.47-0.4-10.67-1.66c-1.33-0.53-2.43-4.11-2.07-6.01 c1.86-9.94,3.89-19.69,0.07-29.74C34.55,108.63,36.19,105.52,36.7,102.84c1.25-8.45,2.51-16.89,3.71-24.9 c-0.83-0.58-0.85-0.59-0.87-0.61c-0.03,0.16-0.07,0.32-0.09,0.48C38.53,86.15,37.62,94.5,36.7,102.84z"); + + //UGL.DrawSvgPath(vh, "path://M29.902,23.275c1.86,0,3.368-1.506,3.368-3.365c0-1.859-1.508-3.365-3.368-3.365 c-1.857,0-3.365,1.506-3.365,3.365C26.537,21.769,28.045,23.275,29.902,23.275z M36.867,30.74c-1.666-0.467-3.799-1.6-4.732-4.199 c-0.932-2.6-3.131-2.998-4.797-2.998s-7.098,3.894-7.098,3.894c-1.133,1.001-2.1,6.502-0.967,6.769 c1.133,0.269,1.266-1.533,1.934-3.599c0.666-2.065,3.797-3.466,3.797-3.466s0.201,2.467-0.398,3.866 c-0.599,1.399-1.133,2.866-1.467,6.198s-1.6,3.665-3.799,6.266c-2.199,2.598-0.6,3.797,0.398,3.664 c1.002-0.133,5.865-5.598,6.398-6.998c0.533-1.397,0.668-3.732,0.668-3.732s0,0,2.199,1.867c2.199,1.865,2.332,4.6,2.998,7.73 s2.332,0.934,2.332-0.467c0-1.401,0.269-5.465-1-7.064c-1.265-1.6-3.73-3.465-3.73-5.265s1.199-3.732,1.199-3.732 c0.332,1.667,3.335,3.065,5.599,3.399C38.668,33.206,38.533,31.207,36.867,30.74z"); + + //钟表指针 + //UGL.DrawSvgPath(vh, "path://M2090.36389,615.30999 L2090.36389,615.30999 C2091.48372,615.30999 2092.40383,616.194028 2092.44859,617.312956 L2096.90698,728.755929 C2097.05155,732.369577 2094.2393,735.416212 2090.62566,735.56078 C2090.53845,735.564269 2090.45117,735.566014 2090.36389,735.566014 L2090.36389,735.566014 C2086.74736,735.566014 2083.81557,732.63423 2083.81557,729.017692 C2083.81557,728.930412 2083.81732,728.84314 2083.82081,728.755929 L2088.2792,617.312956 C2088.32396,616.194028 2089.24407,615.30999 2090.36389,615.30999 Z"); + + //钟表指针 + //UGL.DrawSvgPath(vh, "path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z"); + } + + public static void DrawPath(VertexHelper vh, string path) + { + var svgPath = SVGPath.Parse(path); + DrawPath(vh, svgPath); + } + + public static void DrawPath(VertexHelper vh, SVGPath path) + { + path.Draw(vh); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVG.cs.meta b/Assets/XCharts/Runtime/XUGL/SVG/SVG.cs.meta new file mode 100644 index 0000000..133f0d2 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVG.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbe2b3aa282ad4cd9b469792fde7e092 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs b/Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs new file mode 100644 index 0000000..9239ee1 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs @@ -0,0 +1,202 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.UI; + +namespace XUGL +{ + public class SVGPath + { + private static Regex s_PathRegex = new Regex(@"(([a-z]|[A-Z])(\d|\.|,|-)*)"); + private static Regex s_PathValueRegex = new Regex(@"(^[a-z]|[A-Z])\s*(-?\d+\.*\d*)*[\s|,|-]*(\d+\.*\d*)*"); + private static Regex s_PathValueRegex2 = new Regex(@"(-?\d+\.?\d*)"); + public bool mirrorY = true; + public List<SVGPathSeg> segs = new List<SVGPathSeg>(); + + public void AddSegment(SVGPathSeg seg) + { + segs.Add(seg); + } + + public static SVGPath Parse(string path) + { + if (string.IsNullOrEmpty(path)) + return new SVGPath(); + if (path.StartsWith("path://")) + { + path = path.Substring(7); + } + path = path.Replace(' ', ','); + var mc = s_PathRegex.Matches(path); + var svgPath = new SVGPath(); + + foreach (var m in mc) + { + var key = m.ToString(); + if (key.Equals("Z") || key.Equals("z")) + { + var seg = new SVGPathSeg(SVGPathSegType.Z); + seg.raw = key; + seg.relative = key.Equals("z"); + svgPath.AddSegment(seg); + } + else + { + var type = s_PathValueRegex.Match(key).Groups[1].ToString().ToCharArray() [0]; + var mc3 = s_PathValueRegex2.Matches(key); + SVGPathSeg seg = null; + switch (type) + { + case 'M': + case 'm': + seg = new SVGPathSeg(SVGPathSegType.M); + seg.relative = type == 'm'; + break; + case 'L': + case 'l': + seg = new SVGPathSeg(SVGPathSegType.L); + seg.relative = type == 'l'; + break; + case 'H': + case 'h': + seg = new SVGPathSeg(SVGPathSegType.H); + seg.relative = type == 'h'; + break; + case 'V': + case 'v': + seg = new SVGPathSeg(SVGPathSegType.V); + seg.relative = type == 'v'; + break; + case 'C': + case 'c': + seg = new SVGPathSeg(SVGPathSegType.C); + seg.relative = type == 'c'; + break; + case 'S': + case 's': + seg = new SVGPathSeg(SVGPathSegType.S); + seg.relative = type == 's'; + break; + case 'Q': + case 'q': + seg = new SVGPathSeg(SVGPathSegType.Q); + seg.relative = type == 'q'; + break; + case 'T': + case 't': + seg = new SVGPathSeg(SVGPathSegType.T); + seg.relative = type == 't'; + break; + case 'A': + case 'a': + seg = new SVGPathSeg(SVGPathSegType.A); + seg.relative = type == 'a'; + break; + } + if (seg != null) + { + seg.raw = key; + foreach (var m3 in mc3) + { + // if (type == 'c' || type == 'C') + //Debug.LogError("\tmc3:" + type + "," + m3.ToString()); + float p; + if (float.TryParse(m3.ToString(), out p)) + seg.parameters.Add(p); + } + svgPath.AddSegment(seg); + } + } + } + // Debug.LogError(path); + // foreach (var cmd in svgPath.commands) + // { + // Debug.LogError(cmd.raw); + // } + return svgPath; + } + + public void Draw(VertexHelper vh) + { + var sp = Vector2.zero; + var np = Vector2.zero; + var posList = new List<Vector3>(); + var bezierList = new List<Vector3>(); + var cp2 = Vector2.zero; + foreach (var seg in segs) + { + switch (seg.type) + { + case SVGPathSegType.M: + sp = np = seg.relative ? np + seg.p1 : seg.p1; + if (posList.Count > 0) + { + DrawPosList(vh, posList); + } + posList.Add(np); + break; + case SVGPathSegType.L: + np = seg.relative ? np + seg.p1 : seg.p1; + posList.Add(np); + break; + case SVGPathSegType.H: + np = seg.relative ? np + new Vector2(seg.value, 0) : new Vector2(seg.value, np.y); + posList.Add(np); + break; + case SVGPathSegType.V: + np = seg.relative ? np + new Vector2(0, seg.value) : new Vector2(np.x, seg.value); + posList.Add(np); + break; + case SVGPathSegType.C: + var cp1 = seg.relative ? np + seg.p1 : seg.p1; + cp2 = seg.relative ? np + seg.p2 : seg.p2; + var ep = seg.relative ? np + seg.p3 : seg.p3; + var dist = (int) Vector2.Distance(np, ep) * 2; + if (dist < 2) dist = 2; + UGLHelper.GetBezierList2(ref bezierList, np, ep, dist, cp1, cp2); + for (int n = 1; n < bezierList.Count; n++) + posList.Add(bezierList[n]); + np = ep; + break; + case SVGPathSegType.S: + cp1 = np + (np - cp2).normalized * Vector2.Distance(np, cp2); + var scp2 = seg.relative ? np + seg.p1 : seg.p1; + ep = seg.relative ? np + seg.p2 : seg.p2; + dist = (int) Vector2.Distance(np, ep) * 2; + if (dist < 2) dist = 2; + UGLHelper.GetBezierList2(ref bezierList, np, ep, dist, cp1, scp2); + for (int n = 1; n < bezierList.Count; n++) + posList.Add(bezierList[n]); + break; + case SVGPathSegType.Z: + posList.Add(sp); + DrawPosList(vh, posList); + break; + case SVGPathSegType.Q: + case SVGPathSegType.T: + case SVGPathSegType.A: + default: + Debug.LogError("unknow seg:" + seg.type); + break; + } + } + if (posList.Count > 0) + DrawPosList(vh, posList); + //UGL.DrawCricle(vh, sp, 1, Color.black); + } + + private void DrawPosList(VertexHelper vh, List<Vector3> posList) + { + if (mirrorY) + { + for (int i = posList.Count - 1; i >= 0; i--) + { + var pos = posList[i]; + posList[i] = new Vector3(pos.x, -pos.y); + } + } + UGL.DrawLine(vh, posList, 1f, Color.red, false); + posList.Clear(); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs.meta b/Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs.meta new file mode 100644 index 0000000..20f5c8d --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVGPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4119dc5490ec4f8bbcc67aa6eee024a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs new file mode 100644 index 0000000..acce361 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.UI; + +namespace XUGL +{ + public class SVGPathSeg + { + public SVGPathSegType type; + public bool relative; + public List<float> parameters = new List<float>(); + public string raw; + + public SVGPathSeg(SVGPathSegType type) + { + this.type = type; + } + + public float value + { + get + { + if (type == SVGPathSegType.H) + return SVG.yMirror ? -parameters[0] : parameters[0]; + else + return parameters[0]; + } + } + public float x { get { return parameters[0]; } } + public float y { get { return SVG.yMirror ? -parameters[1] : parameters[1]; } } + public Vector2 p1 { get { return new Vector2(parameters[0], (SVG.yMirror ? -parameters[1] : parameters[1])); } } + public Vector2 p2 { get { return new Vector2(parameters[2], (SVG.yMirror ? -parameters[3] : parameters[3])); } } + public Vector2 p3 { get { return new Vector2(parameters[4], (SVG.yMirror ? -parameters[5] : parameters[5])); } } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs.meta b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs.meta new file mode 100644 index 0000000..8127559 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSeg.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c97d44ceb28a471aa3d657f3984e6b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs new file mode 100644 index 0000000..174dc1c --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.UI; + +namespace XUGL +{ + public enum SVGPathSegType + { + /// <summary> + /// move to + /// </summary> + M, + /// <summary> + /// line to + /// </summary> + L, + /// <summary> + /// horizontal line to + /// </summary> + H, + /// <summary> + /// vertial line to + /// </summary> + V, + /// <summary> + /// curve to + /// </summary> + C, + /// <summary> + /// smooth curve to + /// </summary> + S, + /// <summary> + /// quadratic bezier curve + /// </summary> + Q, + /// <summary> + /// smooth quadratic bezier curve to + /// </summary> + T, + /// <summary> + /// elliptical Arc + /// </summary> + A, + /// <summary> + /// close path + /// </summary> + Z + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs.meta b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs.meta new file mode 100644 index 0000000..217d4b6 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/SVG/SVGPathSegType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ebd7fe1a38c81433697bbe21c2e962ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/UGL.cs b/Assets/XCharts/Runtime/XUGL/UGL.cs new file mode 100644 index 0000000..a6e0105 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/UGL.cs @@ -0,0 +1,2145 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XUGL +{ + /// <summary> + /// UGUI Graphics Library. + /// ||UGUI 图形库 + /// </summary> + public static class UGL + { + /// <summary> + /// 曲线方向 + /// </summary> + public enum Direction + { + /// <summary> + /// 沿X轴方向 + /// </summary> + XAxis, + /// <summary> + /// 沿Y轴方向 + /// </summary> + YAxis, + /// <summary> + /// 随机无序的。如一个闭合的环状曲线。 + /// </summary> + Random + } + private static readonly Color32 s_ClearColor32 = new Color32(0, 0, 0, 0); + private static readonly Vector2 s_ZeroVector2 = Vector2.zero; + private static UIVertex[] s_Vertex = new UIVertex[4]; + private static List<Vector3> s_CurvesPosList = new List<Vector3>(); + + /// <summary> + /// Draw a arrow. 画箭头 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始位置</param> + /// <param name="arrowPoint">箭头位置</param> + /// <param name="width">箭头宽</param> + /// <param name="height">箭头长</param> + /// <param name="offset">相对箭头位置的偏移</param> + /// <param name="dent">箭头凹度</param> + /// <param name="color">颜色</param> + public static void DrawArrow(VertexHelper vh, Vector3 startPoint, Vector3 arrowPoint, float width, + float height, float offset, float dent, Color32 color) + { + var dir = (arrowPoint - startPoint).normalized; + var sharpPos = arrowPoint + (offset + height / 4) * dir; + var middle = sharpPos + (dent - height) * dir; + var diff = Vector3.Cross(dir, Vector3.forward).normalized * width / 2; + var left = sharpPos - height * dir + diff; + var right = sharpPos - height * dir - diff; + DrawTriangle(vh, middle, sharpPos, left, color); + DrawTriangle(vh, middle, sharpPos, right, color); + } + + /// <summary> + /// Draw a line. 画直线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起点</param> + /// <param name="endPoint">终点</param> + /// <param name="width">线宽</param> + /// <param name="color">颜色</param> + public static void DrawLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, Color32 color) + { + DrawLine(vh, startPoint, endPoint, width, color, color); + } + + /// <summary> + /// Draw a line. 画直线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起点</param> + /// <param name="endPoint">终点</param> + /// <param name="width">线宽</param> + /// <param name="color">颜色</param> + /// <param name="toColor">渐变颜色</param> + public static void DrawLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, Color32 color, Color32 toColor) + { + if (startPoint == endPoint) return; + Vector3 v = Vector3.Cross(endPoint - startPoint, Vector3.forward).normalized * width; + s_Vertex[0].position = startPoint - v; + s_Vertex[1].position = endPoint - v; + s_Vertex[2].position = endPoint + v; + s_Vertex[3].position = startPoint + v; + + for (int j = 0; j < 4; j++) + { + s_Vertex[j].color = j == 0 || j == 3 ? color : toColor; + s_Vertex[j].uv0 = s_ZeroVector2; + } + vh.AddUIVertexQuad(s_Vertex); + } + + /// <summary> + /// Draw a line defined by three points. 画一条由3个点确定的折线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始点</param> + /// <param name="middlePoint">中间转折点</param> + /// <param name="endPoint">终点</param> + /// <param name="width">线宽</param> + /// <param name="color">颜色</param> + public static void DrawLine(VertexHelper vh, Vector3 startPoint, Vector3 middlePoint, Vector3 endPoint, + float width, Color32 color) + { + var dir1 = (middlePoint - startPoint).normalized; + var dir2 = (endPoint - middlePoint).normalized; + var dir1v = Vector3.Cross(dir1, Vector3.forward).normalized; + var dir2v = Vector3.Cross(dir2, Vector3.forward).normalized; + var dir3 = (dir1 + dir2).normalized; + var isDown = Vector3.Cross(dir1, dir2).z <= 0; + var angle = (180 - Vector3.Angle(dir1, dir2)) * Mathf.Deg2Rad / 2; + var diff = width / Mathf.Sin(angle); + var dirDp = Vector3.Cross(dir3, Vector3.forward).normalized; + var dnPos = middlePoint + (isDown ? dirDp : -dirDp) * diff; + var upPos1 = middlePoint + (isDown ? -dir1v : dir1v) * width; + var upPos2 = middlePoint + (isDown ? -dir2v : dir2v) * width; + var startUp = startPoint - dir1v * width; + var startDn = startPoint + dir1v * width; + var endUp = endPoint - dir2v * width; + var endDn = endPoint + dir2v * width; + if (isDown) + { + DrawQuadrilateral(vh, startDn, startUp, upPos1, dnPos, color); + DrawQuadrilateral(vh, dnPos, upPos2, endUp, endDn, color); + DrawTriangle(vh, dnPos, upPos1, upPos2, color); + } + else + { + DrawQuadrilateral(vh, startDn, startUp, dnPos, upPos1, color); + DrawQuadrilateral(vh, upPos2, dnPos, endUp, endDn, color); + DrawTriangle(vh, dnPos, upPos1, upPos2, color); + } + } + + public static void DrawLine(VertexHelper vh, List<Vector3> points, float width, Color32 color, bool smooth, bool closepath = false) + { + for (int i = points.Count - 1; i >= 1; i--) + { + if (UGLHelper.IsValueEqualsVector3(points[i], points[i - 1])) + points.RemoveAt(i); + } + if (points.Count < 2) return; + else if (points.Count <= 2) + { + DrawLine(vh, points[0], points[1], width, color); + } + else if (smooth) + { + DrawCurves(vh, points, width, color, 2, 2, Direction.XAxis, float.NaN, closepath); + } + else + { + var ltp = Vector3.zero; + var lbp = Vector3.zero; + var ntp = Vector3.zero; + var nbp = Vector3.zero; + var itp = Vector3.zero; + var ibp = Vector3.zero; + var ctp = Vector3.zero; + var cbp = Vector3.zero; + if (closepath && !UGLHelper.IsValueEqualsVector3(points[points.Count - 1], points[0])) + { + points.Add(points[0]); + } + for (int i = 1; i < points.Count - 1; i++) + { + bool bitp = true, bibp = true; + UGLHelper.GetLinePoints(points[i - 1], points[i], points[i + 1], width, + ref ltp, ref lbp, + ref ntp, ref nbp, + ref itp, ref ibp, + ref ctp, ref cbp, + ref bitp, ref bibp); + if (i == 1) + { + vh.AddVert(ltp, color, Vector2.zero); + vh.AddVert(lbp, color, Vector2.zero); + } + if (bitp == bibp) + { + AddVertToVertexHelper(vh, itp, ibp, color); + } + else + { + if (bitp) + { + AddVertToVertexHelper(vh, itp, ctp, color); + AddVertToVertexHelper(vh, itp, cbp, color); + } + else + { + AddVertToVertexHelper(vh, ctp, ibp, color); + AddVertToVertexHelper(vh, cbp, ibp, color); + } + } + } + AddVertToVertexHelper(vh, ntp, nbp, color); + } + } + + public static void AddVertToVertexHelper(VertexHelper vh, Vector3 top, + Vector3 bottom, Color32 color, bool needTriangle = true) + { + AddVertToVertexHelper(vh, top, bottom, color, color, needTriangle); + } + + public static void AddVertToVertexHelper(VertexHelper vh, Vector3 top, + Vector3 bottom, Color32 topColor, Color32 bottomColor, bool needTriangle = true) + { + var lastVertCount = vh.currentVertCount; + vh.AddVert(top, topColor, Vector2.zero); + vh.AddVert(bottom, bottomColor, Vector2.zero); + if (needTriangle) + { + var indexRt = lastVertCount; + var indexRb = indexRt + 1; + var indexLt = indexRt - 2; + var indexLb = indexLt + 1; + vh.AddTriangle(indexLt, indexRb, indexLb); + vh.AddTriangle(indexLt, indexRt, indexRb); + } + } + + /// <summary> + /// Draw a dash line. 画虚线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始点</param> + /// <param name="endPoint">结束点</param> + /// <param name="width">线宽</param> + /// <param name="color">起始颜色</param> + /// <param name="toColor">结束颜色</param> + /// <param name="lineLength">实线部分长度,默认为线宽的12倍</param> + /// <param name="gapLength">间隙部分长度,默认为线宽的3倍</param> + /// <param name="posList">可选,输出的关键点</param> + public static void DrawDashLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, + Color32 color, Color32 toColor, float lineLength = 0f, float gapLength = 0f, List<Vector3> posList = null) + { + float dist = Vector3.Distance(startPoint, endPoint); + if (dist < 0.1f) return; + if (lineLength == 0) lineLength = 12 * width; + if (gapLength == 0) gapLength = 3 * width; + int segment = Mathf.CeilToInt(dist / (lineLength + gapLength)); + Vector3 dir = (endPoint - startPoint).normalized; + Vector3 sp = startPoint, np; + var isGradient = !color.Equals(toColor); + if (posList != null) posList.Clear(); + for (int i = 1; i <= segment; i++) + { + if (posList != null) posList.Add(sp); + np = startPoint + dir * dist * i / segment; + var dashep = np - dir * gapLength; + DrawLine(vh, sp, dashep, width, isGradient ? Color32.Lerp(color, toColor, i * 1.0f / segment) : color); + sp = np; + } + if (posList != null) posList.Add(endPoint); + DrawLine(vh, sp, endPoint, width, toColor); + } + + /// <summary> + /// Draw a dot line. 画点线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始点</param> + /// <param name="endPoint">结束点</param> + /// <param name="width">线宽</param> + /// <param name="color">起始颜色</param> + /// <param name="toColor">结束颜色</param> + /// <param name="lineLength">实线部分长度,默认为线宽的3倍</param> + /// <param name="gapLength">间隙部分长度,默认为线宽的3倍</param> + /// <param name="posList">可选,输出的关键点</param> + public static void DrawDotLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, + Color32 color, Color32 toColor, float lineLength = 0f, float gapLength = 0f, List<Vector3> posList = null) + { + var dist = Vector3.Distance(startPoint, endPoint); + if (dist < 0.1f) return; + if (lineLength == 0) lineLength = 3 * width; + if (gapLength == 0) gapLength = 3 * width; + var segment = Mathf.CeilToInt(dist / (lineLength + gapLength)); + var dir = (endPoint - startPoint).normalized; + var sp = startPoint; + var np = Vector3.zero; + var isGradient = !color.Equals(toColor); + if (posList != null) posList.Clear(); + for (int i = 1; i <= segment; i++) + { + if (posList != null) posList.Add(sp); + np = startPoint + dir * dist * i / segment; + var dashep = np - dir * gapLength; + DrawLine(vh, sp, dashep, width, isGradient ? Color32.Lerp(color, toColor, i * 1.0f / segment) : color); + sp = np; + } + if (posList != null) posList.Add(endPoint); + DrawLine(vh, sp, endPoint, width, toColor); + } + + /// <summary> + /// Draw a dash-dot line. 画点划线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始点</param> + /// <param name="endPoint">结束点</param> + /// <param name="width">线宽</param> + /// <param name="color">颜色</param> + /// <param name="dashLength">划线长,默认15倍线宽</param> + /// <param name="dotLength">点线长,默认3倍线宽</param> + /// <param name="gapLength">间隙长,默认5倍线宽</param> + /// <param name="posList">可选,输出的关键点</param> + public static void DrawDashDotLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, + Color32 color, float dashLength = 0f, float dotLength = 0, float gapLength = 0f, + List<Vector3> posList = null) + { + float dist = Vector3.Distance(startPoint, endPoint); + if (dist < 0.1f) return; + if (dashLength == 0) dashLength = 15 * width; + if (dotLength == 0) dotLength = 3 * width; + if (gapLength == 0) gapLength = 5 * width; + int segment = Mathf.CeilToInt(dist / (dashLength + 2 * gapLength + dotLength)); + Vector3 dir = (endPoint - startPoint).normalized; + Vector3 sp = startPoint, np; + if (posList != null) posList.Clear(); + for (int i = 1; i <= segment; i++) + { + if (posList != null) posList.Add(sp); + np = startPoint + dir * dist * i / segment; + var dashep = np - dir * (2 * gapLength + dotLength); + DrawLine(vh, sp, dashep, width, color); + if (posList != null) posList.Add(dashep); + var dotsp = dashep + gapLength * dir; + var dotep = dotsp + dotLength * dir; + DrawLine(vh, dotsp, dotep, width, color); + if (posList != null) posList.Add(dotsp); + sp = np; + } + if (posList != null) posList.Add(endPoint); + DrawLine(vh, sp, endPoint, width, color); + } + + /// <summary> + /// Draw a dash-dot-dot line. 画双点划线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始点</param> + /// <param name="endPoint">结束点</param> + /// <param name="width">线宽</param> + /// <param name="color">颜色</param> + /// <param name="dashLength">折线长,默认15倍线宽</param> + /// <param name="dotLength">点线长,默认3倍线宽</param> + /// <param name="gapLength">间隙长,默认5倍线宽</param> + /// <param name="posList">可选,输出的关键点</param> + public static void DrawDashDotDotLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, + Color32 color, float dashLength = 0f, float dotLength = 0f, float gapLength = 0f, + List<Vector3> posList = null) + { + float dist = Vector3.Distance(startPoint, endPoint); + if (dist < 0.1f) return; + if (dashLength == 0) dashLength = 15 * width; + if (dotLength == 0) dotLength = 3 * width; + if (gapLength == 0) gapLength = 5 * width; + int segment = Mathf.CeilToInt(dist / (dashLength + 3 * gapLength + 2 * dotLength)); + Vector3 dir = (endPoint - startPoint).normalized; + Vector3 sp = startPoint, np; + if (posList != null) posList.Clear(); + for (int i = 1; i <= segment; i++) + { + if (posList != null) posList.Add(sp); + np = startPoint + dir * dist * i / segment; + var dashep = np - dir * (3 * gapLength + 2 * dotLength); + DrawLine(vh, sp, dashep, width, color); + if (posList != null) posList.Add(dashep); + var dotsp = dashep + gapLength * dir; + var dotep = dotsp + dotLength * dir; + DrawLine(vh, dotsp, dotep, width, color); + if (posList != null) posList.Add(dotep); + var dotsp2 = dotep + gapLength * dir; + var dotep2 = dotsp2 + dotLength * dir; + DrawLine(vh, dotsp2, dotep2, width, color); + if (posList != null) posList.Add(dotep2); + sp = np; + } + if (posList != null) posList.Add(endPoint); + DrawLine(vh, sp, endPoint, width, color); + } + + /// <summary> + /// Draw a zebar-line. 画斑马线 + /// </summary> + /// <param name="vh"></param> + /// <param name="startPoint">起始点</param> + /// <param name="endPoint">结束点</param> + /// <param name="width">线宽</param> + /// <param name="zebraWidth">斑马条纹宽</param> + /// <param name="zebraGap">间隙宽</param> + /// <param name="color">起始颜色</param> + /// <param name="toColor">结束颜色</param> + public static void DrawZebraLine(VertexHelper vh, Vector3 startPoint, Vector3 endPoint, float width, + float zebraWidth, float zebraGap, Color32 color, Color32 toColor, float maxDistance) + { + var dist = Vector3.Distance(startPoint, endPoint); + if (dist < 0.1f) return; + if (zebraWidth == 0) zebraWidth = 3 * width; + if (zebraGap == 0) zebraGap = 3 * width; + var segment = Mathf.CeilToInt(dist / (zebraWidth + zebraGap)) + 1; + var dir = (endPoint - startPoint).normalized; + var sp = startPoint; + var np = Vector3.zero; + var isGradient = !color.Equals(toColor); + var currDist = 0f; + for (int i = 0; i <= segment; i++) + { + if (currDist + zebraWidth + zebraGap <= dist) + { + currDist += (zebraWidth + zebraGap); + np = sp + dir * zebraWidth; + DrawLine(vh, sp, np, width, isGradient ? Color32.Lerp(color, toColor, currDist / maxDistance) : color); + sp = np + dir * zebraGap; + } + else + { + if (currDist + zebraWidth <= dist) + { + currDist += zebraWidth; + np = sp + dir * zebraWidth; + DrawLine(vh, sp, np, width, isGradient ? Color32.Lerp(color, toColor, currDist / maxDistance) : color); + if (dist - currDist > 6) + { + DrawLine(vh, endPoint - dir * 2f, endPoint, width, isGradient ? Color32.Lerp(color, toColor, dist / maxDistance) : color); + } + } + else + { + DrawLine(vh, sp, endPoint, width, isGradient ? Color32.Lerp(color, toColor, dist / maxDistance) : color); + } + break; + } + } + } + + /// <summary> + /// Draw a diamond. 画菱形(钻石形状) + /// </summary> + /// <param name="vh"></param> + /// <param name="center">中心点</param> + /// <param name="size">尺寸</param> + /// <param name="color">颜色</param> + public static void DrawDiamond(VertexHelper vh, Vector3 center, float size, Color32 color) + { + DrawDiamond(vh, center, size, color, color); + } + + /// <summary> + /// Draw a diamond. 画菱形(钻石形状) + /// </summary> + /// <param name="vh"></param> + /// <param name="center">中心点</param> + /// <param name="size">尺寸</param> + /// <param name="color">渐变色1</param> + /// <param name="toColor">渐变色2</param> + public static void DrawDiamond(VertexHelper vh, Vector3 center, float size, Color32 color, Color32 toColor) + { + DrawDiamond(vh, center, size, size, color, toColor); + } + + public static void DrawDiamond(VertexHelper vh, Vector3 center, float xRadius, float yRadius, Color32 color, Color32 toColor) + { + var p1 = new Vector2(center.x - xRadius, center.y); + var p2 = new Vector2(center.x, center.y + yRadius); + var p3 = new Vector2(center.x + xRadius, center.y); + var p4 = new Vector2(center.x, center.y - yRadius); + DrawTriangle(vh, p4, p1, p2, color, color, toColor); + DrawTriangle(vh, p3, p4, p2, color, color, toColor); + } + + public static void DrawEmptyDiamond(VertexHelper vh, Vector3 center, float xRadius, float yRadius, float tickness, Color32 color) + { + DrawEmptyDiamond(vh, center, xRadius, yRadius, tickness, color, s_ClearColor32); + } + + public static void DrawEmptyDiamond(VertexHelper vh, Vector3 center, float xRadius, float yRadius, float tickness, Color32 color, Color32 emptyColor) + { + var p1 = new Vector2(center.x - xRadius, center.y); + var p2 = new Vector2(center.x, center.y + yRadius); + var p3 = new Vector2(center.x + xRadius, center.y); + var p4 = new Vector2(center.x, center.y - yRadius); + + var xRadius1 = xRadius - tickness; + var yRadius1 = yRadius - tickness * 1.5f; + var ip1 = new Vector2(center.x - xRadius1, center.y); + var ip2 = new Vector2(center.x, center.y + yRadius1); + var ip3 = new Vector2(center.x + xRadius1, center.y); + var ip4 = new Vector2(center.x, center.y - yRadius1); + + if (!UGLHelper.IsClearColor(emptyColor)) + { + DrawQuadrilateral(vh, ip1, ip2, ip3, ip4, emptyColor); + } + + AddVertToVertexHelper(vh, p1, ip1, color, false); + AddVertToVertexHelper(vh, p2, ip2, color); + AddVertToVertexHelper(vh, p3, ip3, color); + AddVertToVertexHelper(vh, p4, ip4, color); + AddVertToVertexHelper(vh, p1, ip1, color); + } + + /// <summary> + /// Draw a square. 画正方形 + /// </summary> + /// <param name="center">中心点</param> + /// <param name="radius">半径</param> + /// <param name="color">颜色</param> + public static void DrawSquare(VertexHelper vh, Vector3 center, float radius, Color32 color) + { + DrawSquare(vh, center, radius, color, color, true); + } + + /// <summary> + /// Draw a square. 画带渐变的正方形 + /// </summary> + /// <param name="vh"></param> + /// <param name="center">中心点</param> + /// <param name="radius">半径</param> + /// <param name="color">渐变色1</param> + /// <param name="toColor">渐变色2</param> + /// <param name="vertical">渐变是否为垂直方向</param> + public static void DrawSquare(VertexHelper vh, Vector3 center, float radius, Color32 color, + Color32 toColor, bool vertical = true) + { + Vector3 p1, p2, p3, p4; + if (vertical) + { + p1 = new Vector3(center.x + radius, center.y - radius); + p2 = new Vector3(center.x - radius, center.y - radius); + p3 = new Vector3(center.x - radius, center.y + radius); + p4 = new Vector3(center.x + radius, center.y + radius); + } + else + { + p1 = new Vector3(center.x - radius, center.y - radius); + p2 = new Vector3(center.x - radius, center.y + radius); + p3 = new Vector3(center.x + radius, center.y + radius); + p4 = new Vector3(center.x + radius, center.y - radius); + } + DrawQuadrilateral(vh, p1, p2, p3, p4, color, toColor); + } + + /// <summary> + /// Draw a rectangle. 画带长方形 + /// </summary> + /// <param name="p1">起始点</param> + /// <param name="p2">结束点</param> + /// <param name="radius">半径</param> + /// <param name="color">颜色</param> + public static void DrawRectangle(VertexHelper vh, Vector3 p1, Vector3 p2, float radius, Color32 color) + { + DrawRectangle(vh, p1, p2, radius, color, color); + } + + /// <summary> + /// Draw a rectangle. 画带渐变的长方形 + /// </summary> + /// <param name="vh"></param> + /// <param name="p1">起始点</param> + /// <param name="p2">结束点</param> + /// <param name="radius">半径</param> + /// <param name="color">渐变色1</param> + /// <param name="toColor">渐变色2</param> + public static void DrawRectangle(VertexHelper vh, Vector3 p1, Vector3 p2, float radius, Color32 color, + Color32 toColor) + { + var dir = (p2 - p1).normalized; + var dirv = Vector3.Cross(dir, Vector3.forward).normalized; + + var p3 = p1 + dirv * radius; + var p4 = p1 - dirv * radius; + var p5 = p2 - dirv * radius; + var p6 = p2 + dirv * radius; + DrawQuadrilateral(vh, p3, p4, p5, p6, color, toColor); + } + + /// <summary> + /// Draw a rectangle. 画长方形 + /// </summary> + /// <param name="vh"></param> + /// <param name="p">中心点</param> + /// <param name="xRadius">x宽</param> + /// <param name="yRadius">y宽</param> + /// <param name="color">颜色</param> + /// <param name="vertical">是否垂直方向</param> + public static void DrawRectangle(VertexHelper vh, Vector3 p, float xRadius, float yRadius, + Color32 color, bool vertical = true) + { + DrawRectangle(vh, p, xRadius, yRadius, color, color, vertical); + } + + /// <summary> + /// Draw a rectangle. 画带渐变的长方形 + /// </summary> + /// <param name="vh"></param> + /// <param name="p">中心点</param> + /// <param name="xRadius">x宽</param> + /// <param name="yRadius">y宽</param> + /// <param name="color">渐变色1</param> + /// <param name="toColor">渐变色2</param> + /// <param name="vertical">是否垂直方向</param> + public static void DrawRectangle(VertexHelper vh, Vector3 p, float xRadius, float yRadius, + Color32 color, Color32 toColor, bool vertical = true) + { + Vector3 p1, p2, p3, p4; + if (vertical) + { + p1 = new Vector3(p.x + xRadius, p.y - yRadius); + p2 = new Vector3(p.x - xRadius, p.y - yRadius); + p3 = new Vector3(p.x - xRadius, p.y + yRadius); + p4 = new Vector3(p.x + xRadius, p.y + yRadius); + } + else + { + p1 = new Vector3(p.x - xRadius, p.y - yRadius); + p2 = new Vector3(p.x - xRadius, p.y + yRadius); + p3 = new Vector3(p.x + xRadius, p.y + yRadius); + p4 = new Vector3(p.x + xRadius, p.y - yRadius); + } + + DrawQuadrilateral(vh, p1, p2, p3, p4, color, toColor); + } + + public static void DrawRectangle(VertexHelper vh, Rect rect, Color32 color) + { + DrawRectangle(vh, rect.center, rect.width / 2, rect.height / 2, color, color, true); + } + + public static void DrawRectangle(VertexHelper vh, Rect rect, Color32 color, Color32 toColor) + { + DrawRectangle(vh, rect.center, rect.width / 2, rect.height / 2, color, toColor, true); + } + + public static void DrawRectangle(VertexHelper vh, Rect rect, float border, Color32 color) + { + DrawRectangle(vh, rect, border, color, color); + } + + public static void DrawRectangle(VertexHelper vh, Rect rect, float border, Color32 color, Color32 toColor) + { + DrawRectangle(vh, rect.center, rect.width / 2 - border, rect.height / 2 - border, color, toColor, true); + } + + /// <summary> + /// Draw a quadrilateral. 画任意的四边形 + /// </summary> + /// <param name="vh"></param> + /// <param name="p1"></param> + /// <param name="p2"></param> + /// <param name="p3"></param> + /// <param name="p4"></param> + /// <param name="color"></param> + public static void DrawQuadrilateral(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, + Color32 color) + { + DrawQuadrilateral(vh, p1, p2, p3, p4, color, color); + } + + /// <summary> + /// Draw a quadrilateral. 画任意带渐变的四边形 + /// </summary> + /// <param name="vh"></param> + /// <param name="p1"></param> + /// <param name="p2"></param> + /// <param name="p3"></param> + /// <param name="p4"></param> + /// <param name="startColor"></param> + /// <param name="toColor"></param> + public static void DrawQuadrilateral(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, + Color32 startColor, Color32 toColor) + { + DrawQuadrilateral(vh, p1, p2, p3, p4, startColor, startColor, toColor, toColor); + } + + public static void DrawQuadrilateral(VertexHelper vh, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, + Color32 color1, Color32 color2, Color32 color3, Color32 color4) + { + s_Vertex[0].position = p1; + s_Vertex[1].position = p2; + s_Vertex[2].position = p3; + s_Vertex[3].position = p4; + s_Vertex[0].color = color1; + s_Vertex[1].color = color2; + s_Vertex[2].color = color3; + s_Vertex[3].color = color4; + for (int j = 0; j < 4; j++) + { + s_Vertex[j].uv0 = s_ZeroVector2; + } + vh.AddUIVertexQuad(s_Vertex); + } + + public static void InitCornerRadius(float[] cornerRadius, float width, float height, bool horizontal, + bool invert, ref float brLt, ref float brRt, ref float brRb, ref float brLb, ref bool needRound) + { + if (cornerRadius == null || cornerRadius.Length == 0) return; + if (invert) + { + if (horizontal) + { + brLt = cornerRadius.Length > 0 ? cornerRadius[1] : 0; + brRt = cornerRadius.Length > 1 ? cornerRadius[0] : 0; + brRb = cornerRadius.Length > 2 ? cornerRadius[3] : 0; + brLb = cornerRadius.Length > 3 ? cornerRadius[2] : 0; + } + else + { + brLt = cornerRadius.Length > 0 ? cornerRadius[3] : 0; + brRt = cornerRadius.Length > 1 ? cornerRadius[2] : 0; + brRb = cornerRadius.Length > 2 ? cornerRadius[1] : 0; + brLb = cornerRadius.Length > 3 ? cornerRadius[0] : 0; + } + } + else + { + brLt = cornerRadius.Length > 0 ? cornerRadius[0] : 0; + brRt = cornerRadius.Length > 1 ? cornerRadius[1] : 0; + brRb = cornerRadius.Length > 2 ? cornerRadius[2] : 0; + brLb = cornerRadius.Length > 3 ? cornerRadius[3] : 0; + } + + needRound = brLb != 0 || brRt != 0 || brRb != 0 || brLb != 0; + if (needRound) + { + var min = Mathf.Min(width, height); + if (brLt == 1 && brRt == 1 && brRb == 1 && brLb == 1) + { + brLt = brRt = brRb = brLb = min / 2; + return; + } + if (brLt > 0 && brLt <= 1) brLt = brLt * min; + if (brRt > 0 && brRt <= 1) brRt = brRt * min; + if (brRb > 0 && brRb <= 1) brRb = brRb * min; + if (brLb > 0 && brLb <= 1) brLb = brLb * min; + if (horizontal) + { + if (brLb + brLt >= height) + { + var total = brLb + brLt; + brLb = height * (brLb / total); + brLt = height * (brLt / total); + } + if (brRt + brRb >= height) + { + var total = brRt + brRb; + brRt = height * (brRt / total); + brRb = height * (brRb / total); + } + if (brLt + brRt >= width) + { + var total = brLt + brRt; + brLt = width * (brLt / total); + brRt = width * (brRt / total); + } + if (brRb + brLb >= width) + { + var total = brRb + brLb; + brRb = width * (brRb / total); + brLb = width * (brLb / total); + } + } + else + { + if (brLt + brRt >= width) + { + var total = brLt + brRt; + brLt = width * (brLt / total); + brRt = width * (brRt / total); + } + if (brRb + brLb >= width) + { + var total = brRb + brLb; + brRb = width * (brRb / total); + brLb = width * (brLb / total); + } + if (brLb + brLt >= height) + { + var total = brLb + brLt; + brLb = height * (brLb / total); + brLt = height * (brLt / total); + } + if (brRt + brRb >= height) + { + var total = brRt + brRb; + brRt = height * (brRt / total); + brRb = height * (brRb / total); + } + } + } + } + + public static void DrawRoundRectangle(VertexHelper vh, Rect rect, + Color32 color, Color32 toColor, float rotate = 0, float[] cornerRadius = null, bool isYAxis = false, + float smoothness = 2, bool invert = false) + { + DrawRoundRectangle(vh, rect.center, rect.width, rect.height, color, toColor, rotate, cornerRadius, + isYAxis, smoothness, invert); + } + + /// <summary> + /// 绘制圆角矩形 + /// </summary> + /// <param name="vh"></param> + /// <param name="center"></param> + /// <param name="rectWidth"></param> + /// <param name="rectHeight"></param> + /// <param name="color"></param> + /// <param name="toColor"></param> + /// <param name="rotate"></param> + /// <param name="cornerRadius"></param> + /// <param name="horizontal"></param> + /// <param name="smoothness"></param> + /// <param name="invert"></param> + public static void DrawRoundRectangle(VertexHelper vh, Vector3 center, float rectWidth, float rectHeight, + Color32 color, Color32 toColor, float rotate = 0, float[] cornerRadius = null, bool horizontal = false, + float smoothness = 2, bool invert = false) + { + if (invert) + { + var temp = toColor; + toColor = color; + color = temp; + } + var isGradient = !UGLHelper.IsValueEqualsColor(color, toColor); + var halfWid = rectWidth / 2; + var halfHig = rectHeight / 2; + float brLt = 0, brRt = 0, brRb = 0, brLb = 0; + bool needRound = false; + InitCornerRadius(cornerRadius, rectWidth, rectHeight, horizontal, invert, ref brLt, ref brRt, ref brRb, + ref brLb, ref needRound); + var tempCenter = Vector3.zero; + var lbIn = new Vector3(center.x - halfWid, center.y - halfHig); + var ltIn = new Vector3(center.x - halfWid, center.y + halfHig); + var rtIn = new Vector3(center.x + halfWid, center.y + halfHig); + var rbIn = new Vector3(center.x + halfWid, center.y - halfHig); + if (needRound) + { + var lbIn2 = lbIn; + var ltIn2 = ltIn; + var rtIn2 = rtIn; + var rbIn2 = rbIn; + var roundLb = lbIn; + var roundLt = ltIn; + var roundRt = rtIn; + var roundRb = rbIn; + if (brLt > 0) + { + roundLt = new Vector3(center.x - halfWid + brLt, center.y + halfHig - brLt); + ltIn = roundLt + brLt * Vector3.left; + ltIn2 = roundLt + brLt * Vector3.up; + } + if (brRt > 0) + { + roundRt = new Vector3(center.x + halfWid - brRt, center.y + halfHig - brRt); + rtIn = roundRt + brRt * Vector3.up; + rtIn2 = roundRt + brRt * Vector3.right; + } + if (brRb > 0) + { + roundRb = new Vector3(center.x + halfWid - brRb, center.y - halfHig + brRb); + rbIn = roundRb + brRb * Vector3.right; + rbIn2 = roundRb + brRb * Vector3.down; + } + if (brLb > 0) + { + roundLb = new Vector3(center.x - halfWid + brLb, center.y - halfHig + brLb); + lbIn = roundLb + brLb * Vector3.left; + lbIn2 = roundLb + brLb * Vector3.down; + } + + if (horizontal) + { + var maxLeft = Mathf.Max(brLt, brLb); + var maxRight = Mathf.Max(brRt, brRb); + var ltInRight = ltIn + maxLeft * Vector3.right; + var lbInRight = lbIn + maxLeft * Vector3.right; + var rtIn2Left = rtIn2 + maxRight * Vector3.left; + var rbInLeft = rbIn + maxRight * Vector3.left; + + var roundLbRight = roundLb + (maxLeft - brLb) * Vector3.right; + var lbIn2Right = lbIn2 + (maxLeft - brLb) * Vector3.right; + if (roundLbRight.x > roundRb.x) roundLbRight.x = roundRb.x; + if (lbIn2Right.x > roundRb.x) lbIn2Right.x = roundRb.x; + + var ltIn2Right = ltIn2 + (maxLeft - brLt) * Vector3.right; + var roundLtRight = roundLt + (maxLeft - brLt) * Vector3.right; + if (ltIn2Right.x > roundRt.x) ltIn2Right.x = roundRt.x; + if (roundLtRight.x > roundRt.x) roundLtRight.x = roundRt.x; + + var roundRtLeft = roundRt + (maxRight - brRt) * Vector3.left; + var rtInLeft = rtIn + (maxRight - brRt) * Vector3.left; + if (roundRtLeft.x < roundLt.x) roundRtLeft.x = roundLt.x; + if (rtInLeft.x < roundLt.x) rtInLeft.x = roundLt.x; + + var rbIn2Left = rbIn2 + (maxRight - brRb) * Vector3.left; + var roundRbLeft = roundRb + (maxRight - brRb) * Vector3.left; + if (rbIn2Left.x < roundLb.x) rbIn2Left.x = roundLb.x; + if (roundRbLeft.x < roundLb.x) roundRbLeft.x = roundLb.x; + if (!isGradient) + { + DrawSector(vh, roundLt, brLt, color, color, 270, 360, 1, horizontal, smoothness); + DrawSector(vh, roundRt, brRt, toColor, toColor, 0, 90, 1, horizontal, smoothness); + DrawSector(vh, roundRb, brRb, toColor, toColor, 90, 180, 1, horizontal, smoothness); + DrawSector(vh, roundLb, brLb, color, color, 180, 270, 1, horizontal, smoothness); + + DrawQuadrilateral(vh, ltIn, ltInRight, lbInRight, lbIn, color, color); + DrawQuadrilateral(vh, lbIn2, roundLb, roundLbRight, lbIn2Right, color, color); + DrawQuadrilateral(vh, roundLt, ltIn2, ltIn2Right, roundLtRight, color, color); + + DrawQuadrilateral(vh, rbInLeft, rtIn2Left, rtIn2, rbIn, toColor, toColor); + DrawQuadrilateral(vh, roundRtLeft, rtInLeft, rtIn, roundRt, toColor, toColor); + DrawQuadrilateral(vh, rbIn2Left, roundRbLeft, roundRb, rbIn2, toColor, toColor); + + var clt = new Vector3(center.x - halfWid + maxLeft, center.y + halfHig); + var crt = new Vector3(center.x + halfWid - maxRight, center.y + halfHig); + var crb = new Vector3(center.x + halfWid - maxRight, center.y - halfHig); + var clb = new Vector3(center.x - halfWid + maxLeft, center.y - halfHig); + if (crt.x > clt.x) + { + DrawQuadrilateral(vh, clb, clt, crt, crb, color, toColor); + } + } + else + { + var tempLeftColor = Color32.Lerp(color, toColor, maxLeft / rectWidth); + var upLeftColor = Color32.Lerp(color, tempLeftColor, brLt / maxLeft); + var downLeftColor = Color32.Lerp(color, tempLeftColor, brLb / maxLeft); + + var tempRightColor = Color32.Lerp(color, toColor, (rectWidth - maxRight) / rectWidth); + var upRightColor = Color32.Lerp(tempRightColor, toColor, (maxRight - brRt) / maxRight); + var downRightColor = Color32.Lerp(tempRightColor, toColor, (maxRight - brRb) / maxRight); + + DrawSector(vh, roundLt, brLt, color, upLeftColor, 270, 360, 1, horizontal, smoothness); + DrawSector(vh, roundRt, brRt, upRightColor, toColor, 0, 90, 1, horizontal, smoothness); + DrawSector(vh, roundRb, brRb, downRightColor, toColor, 90, 180, 1, horizontal, smoothness); + DrawSector(vh, roundLb, brLb, color, downLeftColor, 180, 270, 1, horizontal, smoothness); + + DrawQuadrilateral(vh, lbIn, ltIn, ltInRight, lbInRight, color, tempLeftColor); + DrawQuadrilateral(vh, lbIn2, roundLb, roundLbRight, lbIn2Right, downLeftColor, + roundLbRight.x == roundRb.x ? downRightColor : tempLeftColor); + DrawQuadrilateral(vh, roundLt, ltIn2, ltIn2Right, roundLtRight, upLeftColor, + ltIn2Right.x == roundRt.x ? upRightColor : tempLeftColor); + + DrawQuadrilateral(vh, rbInLeft, rtIn2Left, rtIn2, rbIn, tempRightColor, toColor); + DrawQuadrilateral(vh, roundRtLeft, rtInLeft, rtIn, roundRt, + roundRtLeft.x == roundLt.x ? upLeftColor : tempRightColor, upRightColor); + DrawQuadrilateral(vh, rbIn2Left, roundRbLeft, roundRb, rbIn2, + rbIn2Left.x == roundLb.x ? downLeftColor : tempRightColor, downRightColor); + + var clt = new Vector3(center.x - halfWid + maxLeft, center.y + halfHig); + var crt = new Vector3(center.x + halfWid - maxRight, center.y + halfHig); + var crb = new Vector3(center.x + halfWid - maxRight, center.y - halfHig); + var clb = new Vector3(center.x - halfWid + maxLeft, center.y - halfHig); + if (crt.x > clt.x) + { + DrawQuadrilateral(vh, clb, clt, crt, crb, tempLeftColor, tempRightColor); + } + } + } + else + { + var maxup = Mathf.Max(brLt, brRt); + var maxdown = Mathf.Max(brLb, brRb); + var clt = new Vector3(center.x - halfWid, center.y + halfHig - maxup); + var crt = new Vector3(center.x + halfWid, center.y + halfHig - maxup); + var crb = new Vector3(center.x + halfWid, center.y - halfHig + maxdown); + var clb = new Vector3(center.x - halfWid, center.y - halfHig + maxdown); + var lbIn2Up = lbIn2 + maxdown * Vector3.up; + var rbIn2Up = rbIn2 + maxdown * Vector3.up; + var rtInDown = rtIn + maxup * Vector3.down; + var ltIn2Down = ltIn2 + maxup * Vector3.down; + + var roundLtDown = roundLt + (maxup - brLt) * Vector3.down; + var ltInDown = ltIn + (maxup - brLt) * Vector3.down; + if (roundLtDown.y < roundLb.y) roundLtDown.y = roundLb.y; + if (ltInDown.y < roundLb.y) ltInDown.y = roundLb.y; + + var rtIn2Down = rtIn2 + (maxup - brRt) * Vector3.down; + var roundRtDown = roundRt + (maxup - brRt) * Vector3.down; + if (rtIn2Down.y < roundRb.y) rtIn2Down.y = roundRb.y; + if (roundRtDown.y < roundRb.y) roundRtDown.y = roundRb.y; + + var lbInUp = lbIn + (maxdown - brLb) * Vector3.up; + var roundLbUp = roundLb + (maxdown - brLb) * Vector3.up; + if (lbInUp.y > roundLt.y) lbInUp.y = roundLt.y; + if (roundLbUp.y > roundLt.y) roundLbUp.y = roundLt.y; + + var roundRbUp = roundRb + (maxdown - brRb) * Vector3.up; + var rbInUp = rbIn + (maxdown - brRb) * Vector3.up; + if (roundRbUp.y > roundRt.y) roundRbUp.y = roundRt.y; + if (rbInUp.y > roundRt.y) rbInUp.y = roundRt.y; + + if (!isGradient) + { + DrawSector(vh, roundLt, brLt, toColor, toColor, 270, 360, 1, horizontal, smoothness); + DrawSector(vh, roundRt, brRt, toColor, toColor, 0, 90, 1, horizontal, smoothness); + DrawSector(vh, roundRb, brRb, color, color, 90, 180, 1, horizontal, smoothness); + DrawSector(vh, roundLb, brLb, color, color, 180, 270, 1, horizontal, smoothness); + + DrawQuadrilateral(vh, ltIn2, rtIn, rtInDown, ltIn2Down, toColor, toColor); + DrawQuadrilateral(vh, ltIn, roundLt, roundLtDown, ltInDown, toColor, toColor); + DrawQuadrilateral(vh, roundRt, rtIn2, rtIn2Down, roundRtDown, toColor, toColor); + + DrawQuadrilateral(vh, lbIn2, lbIn2Up, rbIn2Up, rbIn2, color, color); + DrawQuadrilateral(vh, lbIn, lbInUp, roundLbUp, roundLb, color, color); + DrawQuadrilateral(vh, roundRb, roundRbUp, rbInUp, rbIn, color, color); + if (clt.y > clb.y) + { + DrawQuadrilateral(vh, clt, crt, crb, clb, toColor, color); + } + } + else + { + var tempUpColor = Color32.Lerp(color, toColor, (rectHeight - maxup) / rectHeight); + var leftUpColor = Color32.Lerp(tempUpColor, toColor, (maxup - brLt) / maxup); + var rightUpColor = Color32.Lerp(tempUpColor, toColor, (maxup - brRt) / maxup); + var tempDownColor = Color32.Lerp(color, toColor, maxdown / rectHeight); + var leftDownColor = Color32.Lerp(color, tempDownColor, brLb / maxdown); + var rightDownColor = Color32.Lerp(color, tempDownColor, brRb / maxdown); + + DrawSector(vh, roundLt, brLt, leftUpColor, toColor, 270, 360, 1, horizontal, smoothness); + DrawSector(vh, roundRt, brRt, rightUpColor, toColor, 0, 90, 1, horizontal, smoothness); + DrawSector(vh, roundRb, brRb, rightDownColor, color, 90, 180, 1, horizontal, smoothness); + DrawSector(vh, roundLb, brLb, leftDownColor, color, 180, 270, 1, horizontal, smoothness); + + DrawQuadrilateral(vh, ltIn2, rtIn, rtInDown, ltIn2Down, toColor, tempUpColor); + DrawQuadrilateral(vh, ltIn, roundLt, roundLtDown, ltInDown, leftUpColor, + roundLtDown.y == roundLb.y ? leftDownColor : tempUpColor); + DrawQuadrilateral(vh, roundRt, rtIn2, rtIn2Down, roundRtDown, rightUpColor, + rtIn2Down.y == roundRb.y ? rightDownColor : tempUpColor); + + DrawQuadrilateral(vh, rbIn2, lbIn2, lbIn2Up, rbIn2Up, color, tempDownColor); + DrawQuadrilateral(vh, roundLb, lbIn, lbInUp, roundLbUp, leftDownColor, + lbInUp.y == roundLt.y ? leftUpColor : tempDownColor); + DrawQuadrilateral(vh, rbIn, roundRb, roundRbUp, rbInUp, rightDownColor, + roundRbUp.y == roundRt.y ? rightUpColor : tempDownColor); + if (clt.y > clb.y) + { + DrawQuadrilateral(vh, clt, crt, crb, clb, tempUpColor, tempDownColor); + } + } + } + } + else + { + if (horizontal) + DrawQuadrilateral(vh, lbIn, ltIn, rtIn, rbIn, color, toColor); + else + DrawQuadrilateral(vh, rbIn, lbIn, ltIn, rtIn, color, toColor); + } + } + + public static void DrawRoundRectangleWithBorder(VertexHelper vh, Rect rect, + Color32 color, Color32 toColor, float[] cornerRadius, float borderWidth, Color32 borderColor, + float rotate = 0, float smoothness = 2) + { + DrawRoundRectangle(vh, rect.center, rect.width, rect.height, color, toColor, rotate, cornerRadius, + false, smoothness, false); + if (borderWidth > 0) + { + UGL.DrawBorder(vh, rect, borderWidth, borderColor, rotate, cornerRadius, true, smoothness); + } + } + + /// <summary> + /// 绘制(圆角)边框 + /// </summary> + /// <param name="vh"></param> + /// <param name="center"></param> + /// <param name="rectWidth"></param> + /// <param name="rectHeight"></param> + /// <param name="borderWidth"></param> + /// <param name="color"></param> + /// <param name="rotate"></param> + /// <param name="cornerRadius"></param> + /// <param name="invertCorner"></param> + /// <param name="extWidth"></param> + public static void DrawBorder(VertexHelper vh, Vector3 center, float rectWidth, float rectHeight, + float borderWidth, Color32 color, float rotate = 0, float[] cornerRadius = null, + bool horizontal = false, float smoothness = 1f, bool invertCorner = false, float extWidth = 0) + { + DrawBorder(vh, center, rectWidth, rectHeight, borderWidth, color, s_ClearColor32, rotate, + cornerRadius, horizontal, smoothness, invertCorner, extWidth); + } + + /// <summary> + /// 绘制(圆角)边框 + /// </summary> + /// <param name="vh"></param> + /// <param name="rect"></param> + /// <param name="borderWidth"></param> + /// <param name="color"></param> + /// <param name="rotate"></param> + /// <param name="cornerRadius"></param> + /// <param name="horizontal"></param> + /// <param name="smoothness"></param> + /// <param name="invertCorner"></param> + /// <param name="extWidth"></param> + public static void DrawBorder(VertexHelper vh, Rect rect, + float borderWidth, Color32 color, float rotate = 0, float[] cornerRadius = null, + bool horizontal = false, float smoothness = 1f, bool invertCorner = false, float extWidth = 0) + { + DrawBorder(vh, rect.center, rect.width, rect.height, borderWidth, color, s_ClearColor32, rotate, + cornerRadius, horizontal, smoothness, invertCorner, extWidth); + } + + /// <summary> + /// 绘制(圆角)边框 + /// </summary> + /// <param name="vh"></param> + /// <param name="center"></param> + /// <param name="rectWidth"></param> + /// <param name="rectHeight"></param> + /// <param name="borderWidth"></param> + /// <param name="color"></param> + /// <param name="toColor"></param> + /// <param name="rotate"></param> + /// <param name="cornerRadius"></param> + /// <param name="horizontal"></param> + /// <param name="smoothness"></param> + /// <param name="invertCorner"></param> + /// <param name="extWidth"></param> + public static void DrawBorder(VertexHelper vh, Vector3 center, float rectWidth, float rectHeight, + float borderWidth, Color32 color, Color32 toColor, float rotate = 0, float[] cornerRadius = null, + bool horizontal = false, float smoothness = 1f, bool invertCorner = false, float extWidth = 0) + { + if (borderWidth == 0 || UGLHelper.IsClearColor(color)) return; + var halfWid = rectWidth / 2; + var halfHig = rectHeight / 2; + var lbIn = new Vector3(center.x - halfWid - extWidth, center.y - halfHig - extWidth); + var lbOt = new Vector3(center.x - halfWid - borderWidth - extWidth, center.y - halfHig - borderWidth - extWidth); + var ltIn = new Vector3(center.x - halfWid - extWidth, center.y + halfHig + extWidth); + var ltOt = new Vector3(center.x - halfWid - borderWidth - extWidth, center.y + halfHig + borderWidth + extWidth); + var rtIn = new Vector3(center.x + halfWid + extWidth, center.y + halfHig + extWidth); + var rtOt = new Vector3(center.x + halfWid + borderWidth + extWidth, center.y + halfHig + borderWidth + extWidth); + var rbIn = new Vector3(center.x + halfWid + extWidth, center.y - halfHig - extWidth); + var rbOt = new Vector3(center.x + halfWid + borderWidth + extWidth, center.y - halfHig - borderWidth - extWidth); + float brLt = 0, brRt = 0, brRb = 0, brLb = 0; + bool needRound = false; + InitCornerRadius(cornerRadius, rectWidth, rectHeight, horizontal, invertCorner, ref brLt, ref brRt, ref brRb, + ref brLb, ref needRound); + var tempCenter = Vector3.zero; + if (UGLHelper.IsClearColor(toColor)) + { + toColor = color; + } + if (needRound) + { + var lbIn2 = lbIn; + var lbOt2 = lbOt; + var ltIn2 = ltIn; + var ltOt2 = ltOt; + var rtIn2 = rtIn; + var rtOt2 = rtOt; + var rbIn2 = rbIn; + var rbOt2 = rbOt; + //if (brLt > 0) + { + tempCenter = new Vector3(center.x - halfWid + brLt, center.y + halfHig - brLt); + brLt += extWidth; + DrawDoughnut(vh, tempCenter, brLt, brLt + borderWidth, horizontal ? color : toColor, s_ClearColor32, + 270, 360, smoothness); + ltIn = tempCenter + brLt * Vector3.left; + ltOt = tempCenter + (brLt + borderWidth) * Vector3.left; + ltIn2 = tempCenter + brLt * Vector3.up; + ltOt2 = tempCenter + (brLt + borderWidth) * Vector3.up; + } + //if (brRt > 0) + { + tempCenter = new Vector3(center.x + halfWid - brRt, center.y + halfHig - brRt); + brRt += extWidth; + DrawDoughnut(vh, tempCenter, brRt, brRt + borderWidth, toColor, s_ClearColor32, 0, 90, smoothness); + rtIn = tempCenter + brRt * Vector3.up; + rtOt = tempCenter + (brRt + borderWidth) * Vector3.up; + rtIn2 = tempCenter + brRt * Vector3.right; + rtOt2 = tempCenter + (brRt + borderWidth) * Vector3.right; + } + //if (brRb > 0) + { + tempCenter = new Vector3(center.x + halfWid - brRb, center.y - halfHig + brRb); + brRb += extWidth; + DrawDoughnut(vh, tempCenter, brRb, brRb + borderWidth, horizontal ? toColor : color, s_ClearColor32, + 90, 180, smoothness); + rbIn = tempCenter + brRb * Vector3.right; + rbOt = tempCenter + (brRb + borderWidth) * Vector3.right; + rbIn2 = tempCenter + brRb * Vector3.down; + rbOt2 = tempCenter + (brRb + borderWidth) * Vector3.down; + } + //if (brLb > 0) + { + tempCenter = new Vector3(center.x - halfWid + brLb, center.y - halfHig + brLb); + brLb += extWidth; + DrawDoughnut(vh, tempCenter, brLb, brLb + borderWidth, color, s_ClearColor32, 180, 270, smoothness); + lbIn = tempCenter + brLb * Vector3.left; + lbOt = tempCenter + (brLb + borderWidth) * Vector3.left; + lbIn2 = tempCenter + brLb * Vector3.down; + lbOt2 = tempCenter + (brLb + borderWidth) * Vector3.down; + } + if (horizontal) + { + DrawQuadrilateral(vh, lbIn, lbOt, ltOt, ltIn, color, color); + DrawQuadrilateral(vh, ltIn2, ltOt2, rtOt, rtIn, color, toColor); + DrawQuadrilateral(vh, rtIn2, rtOt2, rbOt, rbIn, toColor, toColor); + DrawQuadrilateral(vh, rbIn2, rbOt2, lbOt2, lbIn2, toColor, color); + } + else + { + DrawQuadrilateral(vh, lbIn, lbOt, ltOt, ltIn, color, toColor); + DrawQuadrilateral(vh, ltIn2, ltOt2, rtOt, rtIn, toColor, toColor); + DrawQuadrilateral(vh, rtIn2, rtOt2, rbOt, rbIn, toColor, color); + DrawQuadrilateral(vh, rbIn2, rbOt2, lbOt2, lbIn2, color, color); + } + } + else + { + if (rotate > 0) + { + lbIn = UGLHelper.RotateRound(lbIn, center, Vector3.forward, rotate); + lbOt = UGLHelper.RotateRound(lbOt, center, Vector3.forward, rotate); + ltIn = UGLHelper.RotateRound(ltIn, center, Vector3.forward, rotate); + ltOt = UGLHelper.RotateRound(ltOt, center, Vector3.forward, rotate); + rtIn = UGLHelper.RotateRound(rtIn, center, Vector3.forward, rotate); + rtOt = UGLHelper.RotateRound(rtOt, center, Vector3.forward, rotate); + rbIn = UGLHelper.RotateRound(rbIn, center, Vector3.forward, rotate); + rbOt = UGLHelper.RotateRound(rbOt, center, Vector3.forward, rotate); + } + if (horizontal) + { + DrawQuadrilateral(vh, lbIn, lbOt, ltOt, ltIn, color, color); + DrawQuadrilateral(vh, ltIn, ltOt, rtOt, rtIn, color, toColor); + DrawQuadrilateral(vh, rtIn, rtOt, rbOt, rbIn, toColor, toColor); + DrawQuadrilateral(vh, rbIn, rbOt, lbOt, lbIn, toColor, color); + } + else + { + DrawQuadrilateral(vh, lbIn, lbOt, ltOt, ltIn, color, toColor); + DrawQuadrilateral(vh, ltIn, ltOt, rtOt, rtIn, toColor, toColor); + DrawQuadrilateral(vh, rtIn, rtOt, rbOt, rbIn, toColor, color); + DrawQuadrilateral(vh, rbIn, rbOt, lbOt, lbIn, color, color); + } + } + } + + public static void DrawTriangle(VertexHelper vh, Vector3 p1, + Vector3 p2, Vector3 p3, Color32 color) + { + DrawTriangle(vh, p1, p2, p3, color, color, color); + } + + public static void DrawTriangle(VertexHelper vh, Vector3 pos, float size, Color32 color) + { + DrawTriangle(vh, pos, size, color, color); + } + + public static void DrawTriangle(VertexHelper vh, Vector3 pos, float size, Color32 color, Color32 toColor) + { + var x = size * Mathf.Cos(30 * Mathf.PI / 180); + var y = size * Mathf.Sin(30 * Mathf.PI / 180); + var p1 = new Vector2(pos.x - x, pos.y - y); + var p2 = new Vector2(pos.x, pos.y + size); + var p3 = new Vector2(pos.x + x, pos.y - y); + DrawTriangle(vh, p1, p2, p3, color, toColor, color); + } + + public static void DrawTriangle(VertexHelper vh, Vector3 p1, + Vector3 p2, Vector3 p3, Color32 color, Color32 color2, Color32 color3) + { + UIVertex v1 = new UIVertex(); + v1.position = p1; + v1.color = color; + v1.uv0 = s_ZeroVector2; + UIVertex v2 = new UIVertex(); + v2.position = p2; + v2.color = color2; + v2.uv0 = s_ZeroVector2; + UIVertex v3 = new UIVertex(); + v3.position = p3; + v3.color = color3; + v3.uv0 = s_ZeroVector2; + int startIndex = vh.currentVertCount; + vh.AddVert(v1); + vh.AddVert(v2); + vh.AddVert(v3); + vh.AddTriangle(startIndex, startIndex + 1, startIndex + 2); + } + + public static void DrawEmptyTriangle(VertexHelper vh, Vector3 pos, float size, float tickness, Color32 color) + { + DrawEmptyTriangle(vh, pos, size, tickness, color, s_ClearColor32); + } + + public static void DrawEmptyTriangle(VertexHelper vh, Vector3 pos, float size, float tickness, Color32 color, Color32 backgroundColor) + { + var cos30 = Mathf.Cos(30 * Mathf.PI / 180); + var sin30 = Mathf.Sin(30 * Mathf.PI / 180); + var x = size * cos30; + var y = size * sin30; + var outsideLeft = new Vector2(pos.x - x, pos.y - y); + var outsideTop = new Vector2(pos.x, pos.y + size); + var outsideRight = new Vector2(pos.x + x, pos.y - y); + + var size2 = size - tickness; + var x1 = size2 * cos30; + var y1 = size2 * sin30; + var insideLeft = new Vector2(pos.x - x1, pos.y - y1); + var insideTop = new Vector2(pos.x, pos.y + size2); + var insideRight = new Vector2(pos.x + x1, pos.y - y1); + + if (!UGLHelper.IsClearColor(backgroundColor)) + { + DrawTriangle(vh, insideLeft, insideTop, insideRight, backgroundColor, backgroundColor, backgroundColor); + } + AddVertToVertexHelper(vh, outsideLeft, insideLeft, color, false); + AddVertToVertexHelper(vh, outsideTop, insideTop, color); + AddVertToVertexHelper(vh, outsideRight, insideRight, color); + AddVertToVertexHelper(vh, outsideLeft, insideLeft, color); + } + + public static void DrawCricle(VertexHelper vh, Vector3 center, float radius, Color32 color, + float smoothness = 2f) + { + DrawCricle(vh, center, radius, color, color, 0, s_ClearColor32, smoothness); + } + + public static void DrawCricle(VertexHelper vh, Vector3 center, float radius, Color32 color, + Color32 toColor, float smoothness = 2f) + { + DrawSector(vh, center, radius, color, toColor, 0, 360, 0, s_ClearColor32, smoothness); + } + + public static void DrawCricle(VertexHelper vh, Vector3 center, float radius, Color32 color, + Color32 toColor, float borderWidth, Color32 borderColor, float smoothness = 2f) + { + DrawSector(vh, center, radius, color, toColor, 0, 360, borderWidth, borderColor, smoothness); + } + + public static void DrawCricle(VertexHelper vh, Vector3 center, float radius, Color32 color, + float borderWidth, Color32 borderColor, float smoothness = 2f) + { + DrawCricle(vh, center, radius, color, color, borderWidth, borderColor, smoothness); + } + + public static void DrawEmptyCricle(VertexHelper vh, Vector3 center, float radius, float tickness, + Color32 color, Color32 emptyColor, float smoothness = 2f) + { + DrawDoughnut(vh, center, radius - tickness, radius, color, color, emptyColor, 0, 360, 0, s_ClearColor32, + 0, smoothness); + } + + public static void DrawEmptyCricle(VertexHelper vh, Vector3 center, float radius, float tickness, + Color32 color, Color32 emptyColor, float borderWidth, Color32 borderColor, float smoothness = 2f) + { + DrawDoughnut(vh, center, radius - tickness, radius, color, color, emptyColor, 0, 360, borderWidth, + borderColor, 0, smoothness); + } + + public static void DrawEmptyCricle(VertexHelper vh, Vector3 center, float radius, float tickness, + Color32 color, Color32 toColor, Color32 emptyColor, float smoothness = 2f) + { + DrawDoughnut(vh, center, radius - tickness, radius, color, toColor, emptyColor, 0, 360, 0, + s_ClearColor32, 0, smoothness); + } + + public static void DrawEmptyCricle(VertexHelper vh, Vector3 center, float radius, float tickness, + Color32 color, Color32 toColor, Color32 emptyColor, float borderWidth, Color32 borderColor, + float smoothness = 2f) + { + DrawDoughnut(vh, center, radius - tickness, radius, color, toColor, emptyColor, 0, 360, borderWidth, + borderColor, 0, smoothness); + } + + public static void DrawSector(VertexHelper vh, Vector3 center, float radius, Color32 color, + float startDegree, float toDegree, float smoothness = 2f) + { + DrawSector(vh, center, radius, color, color, startDegree, toDegree, 0, s_ClearColor32, smoothness); + } + + public static void DrawSector(VertexHelper vh, Vector3 center, float radius, Color32 color, Color32 toColor, + float startDegree, float toDegree, int gradientType = 0, bool isYAxis = false, float smoothness = 2f) + { + DrawSector(vh, center, radius, color, toColor, startDegree, toDegree, 0, s_ClearColor32, 0, smoothness, + gradientType, isYAxis); + } + + public static void DrawSector(VertexHelper vh, Vector3 center, float radius, Color32 color, + float startDegree, float toDegree, float borderWidth, Color32 borderColor, float smoothness = 2f) + { + DrawSector(vh, center, radius, color, color, startDegree, toDegree, borderWidth, borderColor, smoothness); + } + + public static void DrawSector(VertexHelper vh, Vector3 center, float radius, Color32 color, Color32 toColor, + float startDegree, float toDegree, float borderWidth, Color32 borderColor, float smoothness = 2f) + { + DrawSector(vh, center, radius, color, toColor, startDegree, toDegree, borderWidth, borderColor, 0, smoothness); + } + + /// <summary> + /// 绘制扇形(可带边框、有空白边距、3种类型渐变) + /// </summary> + /// <param name="vh"></param> + /// <param name="center">中心点</param> + /// <param name="radius">半径</param> + /// <param name="color">颜色</param> + /// <param name="toColor">渐变颜色</param> + /// <param name="startDegree">开始角度</param> + /// <param name="toDegree">结束角度</param> + /// <param name="borderWidth">边框宽度</param> + /// <param name="borderColor">边框颜色</param> + /// <param name="gap">边距</param> + /// <param name="smoothness">光滑度</param> + /// <param name="gradientType">渐变类型,0:向圆形渐变,1:水平或垂直渐变,2:开始角度向结束角度渐变</param> + /// <param name="isYAxis">水平渐变还是垂直渐变,gradientType为1时生效</param> + public static void DrawSector(VertexHelper vh, Vector3 center, float radius, Color32 color, Color32 toColor, + float startDegree, float toDegree, float borderWidth, Color32 borderColor, float gap, + float smoothness, int gradientType = 0, bool isYAxis = false) + { + if (radius == 0) return; + var isCircle = Mathf.Abs(toDegree - startDegree) >= 360; + if (gap > 0 && isCircle) gap = 0; + radius -= borderWidth; + smoothness = (smoothness < 0 ? 2f : smoothness); + int segments = (int)((2 * Mathf.PI * radius) * (Mathf.Abs(toDegree - startDegree) / 360) / smoothness); + if (segments < 1) segments = 1; + float startAngle = startDegree * Mathf.Deg2Rad; + float toAngle = toDegree * Mathf.Deg2Rad; + float realStartAngle = startAngle; + float realToAngle = toAngle; + float halfAngle = (toAngle - startAngle) / 2; + float borderAngle = 0; + float spaceAngle = 0; + + var p2 = center + radius * UGLHelper.GetDire(startAngle); + var p3 = Vector3.zero; + var p4 = Vector3.zero; + var spaceCenter = center; + var realCenter = center; + var lastP4 = center; + var lastColor = color; + var needBorder = borderWidth != 0; + var needSpace = gap != 0; + var borderLineWidth = needSpace ? borderWidth : borderWidth / 2; + var lastPos = Vector3.zero; + var middleDire = UGLHelper.GetDire(startAngle + halfAngle); + if (needBorder || needSpace) + { + float spaceDiff = 0f; + float borderDiff = 0f; + if (needSpace) + { + spaceDiff = gap / Mathf.Sin(halfAngle); + spaceCenter = center + spaceDiff * middleDire; + realCenter = spaceCenter; + spaceAngle = 2 * Mathf.Asin(gap / (2 * radius)); + realStartAngle = startAngle + spaceAngle; + realToAngle = toAngle - spaceAngle; + if (realToAngle < realStartAngle) realToAngle = realStartAngle; + p2 = UGLHelper.GetPos(center, radius, realStartAngle); + } + if (needBorder && !isCircle) + { + borderDiff = borderLineWidth / Mathf.Sin(halfAngle); + realCenter += borderDiff * middleDire; + borderAngle = 2 * Mathf.Asin(borderLineWidth / (2 * radius)); + realStartAngle = realStartAngle + borderAngle; + realToAngle = realToAngle - borderAngle; + if (realToAngle < realStartAngle) + { + realToAngle = realStartAngle; + p2 = UGLHelper.GetPos(center, radius, realStartAngle); + } + else + { + var borderX1 = UGLHelper.GetPos(center, radius, realStartAngle); + DrawQuadrilateral(vh, realCenter, spaceCenter, p2, borderX1, borderColor); + p2 = borderX1; + + var borderX2 = UGLHelper.GetPos(center, radius, realToAngle); + var pEnd = UGLHelper.GetPos(center, radius, toAngle - spaceAngle); + DrawQuadrilateral(vh, realCenter, borderX2, pEnd, spaceCenter, borderColor); + } + } + } + float segmentAngle = (realToAngle - realStartAngle) / segments; + bool isLeft = startDegree >= 180; + for (int i = 0; i <= segments; i++) + { + float currAngle = realStartAngle + i * segmentAngle; + p3 = center + radius * UGLHelper.GetDire(currAngle); + if (gradientType == 1) + { + if (isYAxis) + { + p4 = new Vector3(p3.x, realCenter.y); + var dist = p4.x - realCenter.x; + var tcolor = Color32.Lerp(color, toColor, dist >= 0 ? + dist / radius : + Mathf.Min(radius + dist, radius) / radius); + if (isLeft && (i == segments || i == 0)) tcolor = toColor; + DrawQuadrilateral(vh, lastP4, p2, p3, p4, lastColor, tcolor); + lastP4 = p4; + lastColor = tcolor; + } + else + { + p4 = new Vector3(realCenter.x, p3.y); + var tcolor = Color32.Lerp(color, toColor, Mathf.Abs(p4.y - realCenter.y) / radius); + DrawQuadrilateral(vh, lastP4, p2, p3, p4, lastColor, tcolor); + lastP4 = p4; + lastColor = tcolor; + } + } + else if (gradientType == 2) + { + var tcolor = Color32.Lerp(color, toColor, i / segments); + DrawQuadrilateral(vh, realCenter, p2, p3, realCenter, lastColor, tcolor); + lastColor = tcolor; + } + else + { + AddVertToVertexHelper(vh, p3, realCenter, color, toColor, i > 0); + } + p2 = p3; + + } + if (needBorder || needSpace) + { + if (realToAngle > realStartAngle) + { + var borderX2 = center + radius * UGLHelper.GetDire(realToAngle); + DrawTriangle(vh, realCenter, p2, borderX2, toColor, color, color); + if (needBorder) + { + var realStartDegree = (realStartAngle - borderAngle) * Mathf.Rad2Deg; + var realToDegree = (realToAngle + borderAngle) * Mathf.Rad2Deg; + DrawDoughnut(vh, center, radius, radius + borderWidth, borderColor, s_ClearColor32, + realStartDegree, realToDegree, smoothness); + } + } + } + } + + public static void DrawRoundCap(VertexHelper vh, Vector3 center, float width, float radius, float angle, + bool clockwise, Color32 color, bool end) + { + var px = Mathf.Sin(angle * Mathf.Deg2Rad) * radius; + var py = Mathf.Cos(angle * Mathf.Deg2Rad) * radius; + var pos = new Vector3(px, py) + center; + if (end) + { + if (clockwise) + DrawSector(vh, pos, width, color, angle, angle + 180, 0, s_ClearColor32); + else + DrawSector(vh, pos, width, color, angle, angle - 180, 0, s_ClearColor32); + } + else + { + if (clockwise) + DrawSector(vh, pos, width, color, angle + 180, angle + 360, 0, s_ClearColor32); + else + DrawSector(vh, pos, width, color, angle - 180, angle - 360, 0, s_ClearColor32); + } + } + + public static void DrawDoughnut(VertexHelper vh, Vector3 center, float insideRadius, float outsideRadius, + Color32 color, Color32 emptyColor, float smoothness = 2f) + { + DrawDoughnut(vh, center, insideRadius, outsideRadius, color, color, emptyColor, 0, 360, 0, + s_ClearColor32, 0, smoothness); + } + + public static void DrawDoughnut(VertexHelper vh, Vector3 center, float insideRadius, float outsideRadius, + Color32 color, Color32 emptyColor, float startDegree, + float toDegree, float smoothness = 1f) + { + DrawDoughnut(vh, center, insideRadius, outsideRadius, color, color, emptyColor, startDegree, toDegree, + 0, s_ClearColor32, 0, smoothness); + } + + public static void DrawDoughnut(VertexHelper vh, Vector3 center, float insideRadius, float outsideRadius, + Color32 color, Color32 emptyColor, float startDegree, + float toDegree, float borderWidth, Color32 borderColor, float smoothness = 2f) + { + DrawDoughnut(vh, center, insideRadius, outsideRadius, color, color, emptyColor, startDegree, toDegree, + borderWidth, borderColor, 0, smoothness); + } + + public static void DrawDoughnut(VertexHelper vh, Vector3 center, float insideRadius, float outsideRadius, + Color32 color, Color32 toColor, Color32 emptyColor, float smoothness = 2f) + { + DrawDoughnut(vh, center, insideRadius, outsideRadius, color, toColor, emptyColor, 0, 360, 0, + s_ClearColor32, 0, smoothness); + } + + public static void DrawDoughnut(VertexHelper vh, Vector3 center, float insideRadius, float outsideRadius, + Color32 color, Color32 toColor, Color32 emptyColor, float startDegree, float toDegree, float borderWidth, + Color32 borderColor, float gap, float smoothness, bool roundCap = false, bool clockwise = true, bool radiusGradient = true) + { + if (toDegree - startDegree == 0) return; + if (gap > 0 && Mathf.Abs(toDegree - startDegree) >= 360) gap = 0; + if (insideRadius <= 0) + { + DrawSector(vh, center, outsideRadius, color, toColor, startDegree, toDegree, borderWidth, borderColor, + gap, smoothness); + return; + } + outsideRadius -= borderWidth; + insideRadius += borderWidth; + smoothness = smoothness < 0 ? 2f : smoothness; + Vector3 p1, p2, p3, p4, e1, e2; + var isCircle = Mathf.Abs(toDegree - startDegree) >= 360; + var needBorder = borderWidth != 0; + var needSpace = gap != 0; + var diffAngle = Mathf.Abs(toDegree - startDegree) * Mathf.Deg2Rad; + + int segments = (int)((2 * Mathf.PI * outsideRadius) * (diffAngle * Mathf.Rad2Deg / 360) / smoothness); + if (segments < 1) segments = 1; + float startAngle = startDegree * Mathf.Deg2Rad; + float toAngle = toDegree * Mathf.Deg2Rad; + + float realStartOutAngle = startAngle; + float realToOutAngle = toAngle; + float realStartInAngle = startAngle; + float realToInAngle = toAngle; + float halfAngle = (toAngle - startAngle) / 2; + float borderAngle = 0, borderInAngle = 0, borderHalfAngle = 0; + float spaceAngle = 0, spaceInAngle = 0, spaceHalfAngle = 0; + + var spaceCenter = center; + var realCenter = center; + var startDire = new Vector3(Mathf.Sin(startAngle), Mathf.Cos(startAngle)).normalized; + var toDire = new Vector3(Mathf.Sin(toAngle), Mathf.Cos(toAngle)).normalized; + var middleDire = new Vector3(Mathf.Sin(startAngle + halfAngle), Mathf.Cos(startAngle + halfAngle)).normalized; + p1 = center + insideRadius * startDire; + p2 = center + outsideRadius * startDire; + e1 = center + insideRadius * toDire; + e2 = center + outsideRadius * toDire; + if (roundCap) + { + var roundRadius = (outsideRadius - insideRadius) / 2; + var roundAngleRadius = insideRadius + roundRadius; + var roundAngle = Mathf.Atan(roundRadius / roundAngleRadius); + if (diffAngle < 2 * roundAngle) + { + roundCap = false; + } + } + if (needBorder || needSpace) + { + if (needSpace) + { + var spaceDiff = gap / Mathf.Sin(halfAngle); + spaceCenter = center + Mathf.Abs(spaceDiff) * middleDire; + realCenter = spaceCenter; + spaceAngle = 2 * Mathf.Asin(gap / (2 * outsideRadius)); + spaceInAngle = 2 * Mathf.Asin(gap / (2 * insideRadius)); + spaceHalfAngle = 2 * Mathf.Asin(gap / (2 * (insideRadius + (outsideRadius - insideRadius) / 2))); + if (clockwise) + { + p1 = UGLHelper.GetPos(center, insideRadius, startAngle + spaceInAngle, false); + e1 = UGLHelper.GetPos(center, insideRadius, toAngle - spaceInAngle, false); + realStartOutAngle = startAngle + spaceAngle; + realToOutAngle = toAngle - spaceAngle; + realStartInAngle = startAngle + spaceInAngle; + realToInAngle = toAngle - spaceInAngle; + } + else + { + p1 = UGLHelper.GetPos(center, insideRadius, startAngle - spaceInAngle, false); + e1 = UGLHelper.GetPos(center, insideRadius, toAngle + spaceInAngle, false); + realStartOutAngle = startAngle - spaceAngle; + realToOutAngle = toAngle + spaceAngle; + realStartInAngle = startAngle - spaceInAngle; + realToOutAngle = toAngle + spaceInAngle; + } + p2 = UGLHelper.GetPos(center, outsideRadius, realStartOutAngle, false); + e2 = UGLHelper.GetPos(center, outsideRadius, realToOutAngle, false); + } + if (needBorder && !isCircle) + { + var borderDiff = borderWidth / Mathf.Sin(halfAngle); + realCenter += Mathf.Abs(borderDiff) * middleDire; + borderAngle = 2 * Mathf.Asin(borderWidth / (2 * outsideRadius)); + borderInAngle = 2 * Mathf.Asin(borderWidth / (2 * insideRadius)); + borderHalfAngle = 2 * Mathf.Asin(borderWidth / (2 * (insideRadius + (outsideRadius - insideRadius) / 2))); + if (clockwise) + { + realStartOutAngle = realStartOutAngle + borderAngle; + realToOutAngle = realToOutAngle - borderAngle; + realStartInAngle = startAngle + spaceInAngle + borderInAngle; + realToInAngle = toAngle - spaceInAngle - borderInAngle; + var newp1 = UGLHelper.GetPos(center, insideRadius, startAngle + spaceInAngle + borderInAngle, false); + var newp2 = UGLHelper.GetPos(center, outsideRadius, realStartOutAngle, false); + if (!roundCap) DrawQuadrilateral(vh, newp2, newp1, p1, p2, borderColor); + p1 = newp1; + p2 = newp2; + if (toAngle - spaceInAngle - 2 * borderInAngle > realStartOutAngle) + { + var newe1 = UGLHelper.GetPos(center, insideRadius, toAngle - spaceInAngle - borderInAngle, false); + var newe2 = UGLHelper.GetPos(center, outsideRadius, realToOutAngle, false); + if (!roundCap) DrawQuadrilateral(vh, newe2, e2, e1, newe1, borderColor); + e1 = newe1; + e2 = newe2; + } + } + else + { + realStartOutAngle = realStartOutAngle - borderAngle; + realToOutAngle = realToOutAngle + borderAngle; + realStartInAngle = startAngle - spaceInAngle - borderInAngle; + realToInAngle = toAngle + spaceInAngle + borderInAngle; + var newp1 = UGLHelper.GetPos(center, insideRadius, startAngle - spaceInAngle - borderInAngle, false); + var newp2 = UGLHelper.GetPos(center, outsideRadius, realStartOutAngle, false); + if (!roundCap) DrawQuadrilateral(vh, newp2, newp1, p1, p2, borderColor); + p1 = newp1; + p2 = newp2; + if (toAngle + spaceInAngle + 2 * borderInAngle < realStartOutAngle) + { + var newe1 = UGLHelper.GetPos(center, insideRadius, toAngle + spaceInAngle + borderInAngle, false); + var newe2 = UGLHelper.GetPos(center, outsideRadius, realToOutAngle, false); + if (!roundCap) DrawQuadrilateral(vh, newe2, e2, e1, newe1, borderColor); + e1 = newe1; + e2 = newe2; + } + } + } + } + if (roundCap) + { + var roundRadius = (outsideRadius - insideRadius) / 2; + var roundAngleRadius = insideRadius + roundRadius; + var roundAngle = Mathf.Atan(roundRadius / roundAngleRadius); + if (clockwise) + { + realStartOutAngle = startAngle + 2 * spaceHalfAngle + borderHalfAngle + roundAngle; + realStartInAngle = startAngle + 2 * spaceHalfAngle + borderHalfAngle + roundAngle; + } + else + { + realStartOutAngle = startAngle - 2 * spaceHalfAngle - borderHalfAngle - roundAngle; + realStartInAngle = startAngle - 2 * spaceHalfAngle - borderHalfAngle - roundAngle; + } + var roundTotalDegree = realStartOutAngle * Mathf.Rad2Deg; + var roundCenter = center + roundAngleRadius * UGLHelper.GetDire(realStartOutAngle); + var sectorStartDegree = clockwise ? roundTotalDegree + 180 : roundTotalDegree; + var sectorToDegree = clockwise ? roundTotalDegree + 360 : roundTotalDegree + 180; + DrawSector(vh, roundCenter, roundRadius, color, sectorStartDegree, sectorToDegree, smoothness / 2); + if (needBorder) + { + DrawDoughnut(vh, roundCenter, roundRadius, roundRadius + borderWidth, borderColor, + s_ClearColor32, sectorStartDegree, sectorToDegree, smoothness / 2); + } + p1 = UGLHelper.GetPos(center, insideRadius, realStartOutAngle); + p2 = UGLHelper.GetPos(center, outsideRadius, realStartOutAngle); + + if (clockwise) + { + realToOutAngle = toAngle - 2 * spaceHalfAngle - borderHalfAngle - roundAngle; + realToInAngle = toAngle - 2 * spaceHalfAngle - borderHalfAngle - roundAngle; + if (realToOutAngle < realStartOutAngle) realToOutAngle = realStartOutAngle; + } + else + { + realToOutAngle = toAngle + 2 * spaceHalfAngle + borderHalfAngle + roundAngle; + realToInAngle = toAngle + 2 * spaceHalfAngle + borderHalfAngle + roundAngle; + if (realToOutAngle > realStartOutAngle) realToOutAngle = realStartOutAngle; + } + roundTotalDegree = realToOutAngle * Mathf.Rad2Deg; + roundCenter = center + roundAngleRadius * UGLHelper.GetDire(realToOutAngle); + sectorStartDegree = clockwise ? roundTotalDegree : roundTotalDegree + 180; + sectorToDegree = clockwise ? roundTotalDegree + 180 : roundTotalDegree + 360; + DrawSector(vh, roundCenter, roundRadius, toColor, sectorStartDegree, sectorToDegree, smoothness / 2); + if (needBorder) + { + DrawDoughnut(vh, roundCenter, roundRadius, roundRadius + borderWidth, borderColor, + s_ClearColor32, sectorStartDegree, sectorToDegree, smoothness / 2); + } + e1 = UGLHelper.GetPos(center, insideRadius, realToOutAngle); + e2 = UGLHelper.GetPos(center, outsideRadius, realToOutAngle); + } + var segmentAngle = (realToInAngle - realStartInAngle) / segments; + var isGradient = !UGLHelper.IsValueEqualsColor(color, toColor); + for (int i = 0; i <= segments; i++) + { + float currAngle = realStartInAngle + i * segmentAngle; + p3 = new Vector3(center.x + outsideRadius * Mathf.Sin(currAngle), + center.y + outsideRadius * Mathf.Cos(currAngle)); + p4 = new Vector3(center.x + insideRadius * Mathf.Sin(currAngle), + center.y + insideRadius * Mathf.Cos(currAngle)); + if (isGradient) + { + if (radiusGradient) + { + if (i == 0 && (needSpace || needBorder)) + UGL.DrawTriangle(vh, p1, p2, p3, color, toColor, toColor); + AddVertToVertexHelper(vh, p3, p4, color, toColor, i > 0); + } + else + { + var tcolor = Color32.Lerp(color, toColor, i * 1.0f / segments); + if (i == 0 && (needSpace || needBorder)) + UGL.DrawTriangle(vh, p1, p2, p3, color, tcolor, tcolor); + AddVertToVertexHelper(vh, p3, p4, tcolor, tcolor, i > 0); + } + } + else + { + if (i == 0 && (needSpace || needBorder)) + UGL.DrawTriangle(vh, p1, p2, p3, color); + AddVertToVertexHelper(vh, p3, p4, color, color, i > 0); + } + p1 = p4; + p2 = p3; + } + if (!UGLHelper.IsClearColor(emptyColor)) + { + for (int i = 0; i <= segments; i++) + { + float currAngle = realStartInAngle + i * segmentAngle; + p4 = new Vector3(center.x + insideRadius * Mathf.Sin(currAngle), + center.y + insideRadius * Mathf.Cos(currAngle)); + AddVertToVertexHelper(vh, center, p4, emptyColor, emptyColor, i > 0); + } + } + if (needBorder || needSpace || roundCap) + { + if (clockwise) + { + var isInAngleFixed = toAngle - spaceInAngle - 2 * borderInAngle > realStartOutAngle; + if (isInAngleFixed) DrawQuadrilateral(vh, p2, e2, e1, p1, color, toColor); + else DrawTriangle(vh, p2, e2, p1, color, color, toColor); + if (needBorder) + { + var realStartDegree = (realStartOutAngle - (roundCap ? 0 : borderAngle)) * Mathf.Rad2Deg; + var realToDegree = (realToOutAngle + (roundCap ? 0 : borderAngle)) * Mathf.Rad2Deg; + if (realToDegree < realStartOutAngle) realToDegree = realStartOutAngle; + var inStartDegree = roundCap ? realStartDegree : (startAngle + spaceInAngle) * Mathf.Rad2Deg; + var inToDegree = roundCap ? realToDegree : (toAngle - spaceInAngle) * Mathf.Rad2Deg; + if (inToDegree < inStartDegree) inToDegree = inStartDegree; + if (isInAngleFixed) DrawDoughnut(vh, center, insideRadius - borderWidth, insideRadius, borderColor, + s_ClearColor32, inStartDegree, inToDegree, smoothness); + DrawDoughnut(vh, center, outsideRadius, outsideRadius + borderWidth, borderColor, s_ClearColor32, + realStartDegree, realToDegree, smoothness); + } + } + else + { + var isInAngleFixed = toAngle + spaceInAngle + 2 * borderInAngle < realStartOutAngle; + if (isInAngleFixed) DrawQuadrilateral(vh, p2, e2, e1, p1, color, toColor); + else DrawTriangle(vh, p2, e2, p1, color, color, toColor); + if (needBorder) + { + var realStartDegree = (realStartOutAngle + (roundCap ? 0 : borderAngle)) * Mathf.Rad2Deg; + var realToDegree = (realToOutAngle - (roundCap ? 0 : borderAngle)) * Mathf.Rad2Deg; + var inStartDegree = roundCap ? realStartDegree : (startAngle - spaceInAngle) * Mathf.Rad2Deg; + var inToDegree = roundCap ? realToDegree : (toAngle + spaceInAngle) * Mathf.Rad2Deg; + if (inToDegree > inStartDegree) inToDegree = inStartDegree; + if (isInAngleFixed) + { + DrawDoughnut(vh, center, insideRadius - borderWidth, insideRadius, borderColor, + s_ClearColor32, inStartDegree, inToDegree, smoothness); + } + DrawDoughnut(vh, center, outsideRadius, outsideRadius + borderWidth, borderColor, + s_ClearColor32, realStartDegree, realToDegree, smoothness); + } + } + } + } + + /// <summary> + /// 画贝塞尔曲线 + /// </summary> + /// <param name="vh"></param> + /// <param name="sp">起始点</param> + /// <param name="ep">结束点</param> + /// <param name="cp1">控制点1</param> + /// <param name="cp2">控制点2</param> + /// <param name="lineWidth">曲线宽</param> + /// <param name="lineColor">曲线颜色</param> + public static void DrawCurves(VertexHelper vh, Vector3 sp, Vector3 ep, Vector3 cp1, Vector3 cp2, + float lineWidth, Color32 lineColor, float smoothness, Direction dire = Direction.XAxis) + { + var dist = Vector3.Distance(sp, ep); + var segment = (int)(dist / (smoothness <= 0 ? 2f : smoothness)); + UGLHelper.GetBezierList2(ref s_CurvesPosList, sp, ep, segment, cp1, cp2); + DrawCurvesInternal(vh, s_CurvesPosList, lineWidth, lineColor, dire); + } + + /// <summary> + /// 画贝塞尔曲线 + /// </summary> + /// <param name="vh"></param> + /// <param name="points">坐标点列表</param> + /// <param name="width">曲线宽</param> + /// <param name="color">曲线颜色</param> + /// <param name="smoothStyle">曲线样式</param> + /// <param name="smoothness">平滑度</param> + /// <param name="dire">曲线方向</param> + /// <param name="currProgress">当前绘制进度</param> + /// <param name="closed">曲线是否闭合</param> + public static void DrawCurves(VertexHelper vh, List<Vector3> points, float width, Color32 color, + float smoothStyle, float smoothness, Direction dire, float currProgress = float.NaN, + bool closed = false) + { + var count = points.Count; + var size = (closed ? count : count - 1); + if (closed) + dire = Direction.Random; + for (int i = 0; i < size; i++) + { + var sp = points[i]; + var ep = closed ? (i == size - 1 ? points[0] : points[i + 1]) : points[i + 1]; + var lsp = i > 0 ? points[i - 1] : (closed ? points[count - 1] : sp); + var nep = i < points.Count - 2 ? points[i + 2] : (closed ? points[(i + 2) % count] : ep); + var smoothness2 = smoothness; + if (currProgress != float.NaN) + { + switch (dire) + { + case Direction.XAxis: + smoothness2 = ep.x <= currProgress ? smoothness : smoothness * 0.5f; + break; + case Direction.YAxis: + smoothness2 = ep.y <= currProgress ? smoothness : smoothness * 0.5f; + break; + case Direction.Random: + smoothness2 = smoothness * 0.5f; + break; + } + } + if (dire == Direction.YAxis) + UGLHelper.GetBezierListVertical(ref s_CurvesPosList, sp, ep, smoothness2, smoothStyle); + else + UGLHelper.GetBezierList(ref s_CurvesPosList, sp, ep, lsp, nep, smoothness2, smoothStyle, false, dire == Direction.Random); + + DrawCurvesInternal(vh, s_CurvesPosList, width, color, dire, currProgress); + } + } + + public static void DrawCurvesInternal(VertexHelper vh, List<Vector3> curvesPosList, float lineWidth, + Color32 lineColor, Direction dire, float currProgress = float.NaN) + { + if (curvesPosList.Count > 1) + { + var start = curvesPosList[0]; + var to = Vector3.zero; + var dir = curvesPosList[1] - start; + var diff = Vector3.Cross(dir, Vector3.forward).normalized * lineWidth; + var startUp = start - diff; + var startDn = start + diff; + var toUp = Vector3.zero; + var toDn = Vector3.zero; + + var lastVertCount = vh.currentVertCount; + AddVertToVertexHelper(vh, startUp, startDn, lineColor, false); + for (int i = 1; i < curvesPosList.Count; i++) + { + to = curvesPosList[i]; + if (currProgress != float.NaN) + { + if (dire == Direction.YAxis && to.y > currProgress) + break; + if (dire == Direction.XAxis && to.x > currProgress) + break; + } + + diff = Vector3.Cross(to - start, Vector3.forward).normalized * lineWidth; + toUp = to - diff; + toDn = to + diff; + + AddVertToVertexHelper(vh, toUp, toDn, lineColor); + + startUp = toUp; + startDn = toDn; + start = to; + } + AddVertToVertexHelper(vh, toUp, toDn, lineColor); + } + } + + public static void DrawEdge(VertexHelper vh, List<Vector3> topList, List<Vector3> bottomList, + Color32 lineColor, Color32 lineToColor, Direction dire, float currProgress = float.NaN) + { + if (topList.Count < 2 || bottomList.Count < 2) return; + var minCount = Mathf.Min(topList.Count, bottomList.Count); + var isGradient = !UGLHelper.IsValueEqualsColor(lineColor, lineToColor); + AddVertToVertexHelper(vh, topList[0], bottomList[0], lineColor, false); + for (int i = 1; i < minCount; i++) + { + var up = topList[i]; + var dn = bottomList[i]; + if (currProgress != float.NaN) + { + if (dire == Direction.YAxis && up.y > currProgress) + break; + if (dire == Direction.XAxis && up.x > currProgress) + break; + } + if (isGradient) + { + var tcolor = Color32.Lerp(lineColor, lineToColor, i * 1.0f / minCount); + AddVertToVertexHelper(vh, up, dn, tcolor); + } + else + { + AddVertToVertexHelper(vh, up, dn, lineColor); + } + } + } + + public static void DrawSvgPath(VertexHelper vh, string path) + { + SVG.DrawPath(vh, path); + } + + public static void DrawEllipse(VertexHelper vh, Vector3 center, float w, float h, Color32 color, float smoothness = 1) + { + DrawEllipse(vh, center, w, h, color, smoothness, 0, s_ClearColor32, 0, 360); + } + + public static void DrawEllipse(VertexHelper vh, Vector3 center, float w, float h, Color32 color, float smoothness, + float borderWidth, Color32 borderColor, + float startAngle, float endAngle) + { + startAngle = (startAngle + 360) % 360; + endAngle = (endAngle + 360) % 360; + if (endAngle < startAngle) + endAngle += 360; + if (endAngle <= startAngle) + return; + + var angle = startAngle; + var lp = Vector2.zero; + var fill = color.a != 0; + var border = borderWidth != 0 && borderColor.a != 0; + if (!fill && !border) + return; + + var startTriangleIndex = vh.currentVertCount; + if (fill) + { + vh.AddVert(center, color, Vector2.zero); + } + if (smoothness < 0.5f) + smoothness = 0.5f; + + var i = 0; + while (angle <= endAngle) + { + var rad = angle * Mathf.Deg2Rad; + var x = center.x + w * Mathf.Cos(rad); + var y = center.y + h * Mathf.Sin(rad); + var p1 = new Vector3(x, y); + vh.AddVert(p1, color, Vector2.zero); + if (border) + { + var dire = (p1 - center).normalized; + var diff = dire * borderWidth; + var p2 = p1 + diff; + vh.AddVert(p1, borderColor, Vector2.zero); + vh.AddVert(p2, borderColor, Vector2.zero); + + if (i > 0) + { + var index = startTriangleIndex + i * 3 + 2; + vh.AddTriangle(index - 3, index + 1, index - 2); + vh.AddTriangle(index - 3, index, index + 1); + if (fill) + vh.AddTriangle(startTriangleIndex, index - 1, index - 4); + } + } + else if (i > 0 && fill) + { + var index = startTriangleIndex + i; + vh.AddTriangle(startTriangleIndex, index + 1, index); + } + i++; + angle += smoothness; + } + } + + /// <summary> + /// 填充任意多边形(目前只支持凸多边形) + /// </summary> + /// <param name="vh"></param> + /// <param name="points"></param> + /// <param name="color"></param> + public static void DrawPolygon(VertexHelper vh, List<Vector3> points, Color32 color) + { + if (points.Count < 3 || UGLHelper.IsClearColor(color)) return; + var cv = vh.currentVertCount; + foreach (var pos in points) + { + vh.AddVert(pos, color, Vector2.zero); + } + for (int i = 2; i < points.Count; i++) + { + vh.AddTriangle(cv, cv + i - 1, cv + i); + } + } + + /// <summary> + /// Draw plus sign. + /// ||绘制加号 + /// </summary> + /// <param name="vh"></param> + /// <param name="center"></param> + /// <param name="radius"></param> + /// <param name="tickness"></param> + /// <param name="color"></param> + public static void DrawPlus(VertexHelper vh, Vector3 center, float radius, float tickness, Color32 color) + { + var xPos1 = new Vector3(center.x - radius, center.y); + var xPos2 = new Vector3(center.x + radius, center.y); + var yPos1 = new Vector3(center.x, center.y - radius); + var yPos2 = new Vector3(center.x, center.y + radius); + UGL.DrawLine(vh, xPos1, xPos2, tickness, color); + UGL.DrawLine(vh, yPos1, yPos2, tickness, color); + } + + /// <summary> + /// Draw minus sign. + /// ||绘制减号 + /// </summary> + /// <param name="vh"></param> + /// <param name="center"></param> + /// <param name="radius"></param> + /// <param name="tickness"></param> + /// <param name="color"></param> + public static void DrawMinus(VertexHelper vh, Vector3 center, float radius, float tickness, Color32 color) + { + var xPos1 = new Vector3(center.x - radius, center.y); + var xPos2 = new Vector3(center.x + radius, center.y); + UGL.DrawLine(vh, xPos1, xPos2, tickness, color); + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/UGL.cs.meta b/Assets/XCharts/Runtime/XUGL/UGL.cs.meta new file mode 100644 index 0000000..0ca349f --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/UGL.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 463dc57c2fc1849379941a7facf8dc84 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/UGLExample.cs b/Assets/XCharts/Runtime/XUGL/UGLExample.cs new file mode 100644 index 0000000..a245ec7 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/UGLExample.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace XUGL +{ + [ExecuteInEditMode] + public class UGLExample : MaskableGraphic + { + private float m_Width = 800; + private float m_Height = 800; + private Vector3 m_Center = Vector3.zero; + private Vector3 m_LeftTopPos = Vector3.zero; + private Color32 m_BackgroundColor = new Color32(224, 224, 224, 255); + private Color32 m_DrawColor = new Color32(255, 132, 142, 255); + private float[] m_BorderRadius = new float[] { 5, 5, 10, 10 }; + + protected override void Awake() + { + base.Awake(); + var rectTransform = GetComponent<RectTransform>(); + rectTransform.sizeDelta = new Vector2(500, 500); + rectTransform.anchorMin = new Vector2(0.5f, 0.5f); + rectTransform.anchorMax = new Vector2(0.5f, 0.5f); + rectTransform.pivot = new Vector2(0.5f, 0.5f); + m_Center = Vector3.zero; + m_LeftTopPos = new Vector3(-m_Width / 2, m_Height / 2); + } + + protected override void OnPopulateMesh(VertexHelper vh) + { + Vector3 sp, cp, ep; + vh.Clear(); + + //背景边框 + UGL.DrawSquare(vh, m_Center, m_Width / 2, m_BackgroundColor); + UGL.DrawBorder(vh, m_Center, m_Width, m_Height, 40, Color.green, Color.red, 0, m_BorderRadius, false, 1); + + //点 + UGL.DrawCricle(vh, m_LeftTopPos + new Vector3(20, -20), 10, m_DrawColor); + + //直线 + sp = new Vector3(m_LeftTopPos.x + 50, m_LeftTopPos.y - 20); + ep = new Vector3(m_LeftTopPos.x + 250, m_LeftTopPos.y - 20); + UGL.DrawLine(vh, sp, ep, 3, m_DrawColor); + + //3点确定的折线 + sp = new Vector3(m_LeftTopPos.x + 20, m_LeftTopPos.y - 100); + cp = new Vector3(m_LeftTopPos.x + 200, m_LeftTopPos.y - 40); + ep = new Vector3(m_LeftTopPos.x + 250, m_LeftTopPos.y - 80); + UGL.DrawLine(vh, sp, cp, ep, 5, m_DrawColor); + + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/UGLExample.cs.meta b/Assets/XCharts/Runtime/XUGL/UGLExample.cs.meta new file mode 100644 index 0000000..e216aea --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/UGLExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8a87ea5df031473da3eb5fb8f57e20a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/XUGL/UGLHelper.cs b/Assets/XCharts/Runtime/XUGL/UGLHelper.cs new file mode 100644 index 0000000..32db544 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/UGLHelper.cs @@ -0,0 +1,496 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XUGL +{ + public static class UGLHelper + { + public static bool IsValueEqualsColor(Color32 color1, Color32 color2) + { + return color1.a == color2.a && + color1.b == color2.b && + color1.g == color2.g && + color1.r == color2.r; + } + + public static bool IsValueEqualsColor(Color color1, Color color2) + { + return color1.a == color2.a && + color1.b == color2.b && + color1.g == color2.g && + color1.r == color2.r; + } + + public static bool IsValueEqualsString(string str1, string str2) + { + if (str1 == null && str2 == null) + return true; + else if (str1 != null && str2 != null) + return str1.Equals(str2); + else return false; + } + + public static bool IsValueEqualsVector2(Vector2 v1, Vector2 v2) + { + return v1.x == v2.x && + v1.y == v2.y; + } + + public static bool IsValueEqualsVector3(Vector3 v1, Vector3 v2) + { + return v1.x == v2.x && + v1.y == v2.y && + v1.z == v2.z; + } + + public static bool IsValueEqualsVector3(Vector3 v1, Vector2 v2) + { + return v1.x == v2.x && + v1.y == v2.y; + } + + public static bool IsValueEqualsList<T>(List<T> list1, List<T> list2) + { + if (list1 == null || list2 == null) + return false; + + if (list1.Count != list2.Count) + return false; + + for (int i = 0; i < list1.Count; i++) + { + if (list1[i] == null && list2[i] == null) { } + else + { + if (list1[i] != null) + { + if (!list1[i].Equals(list2[i])) + return false; + } + else + { + if (!list2[i].Equals(list1[i])) + return false; + } + } + } + return true; + } + + public static bool IsClearColor(Color32 color) + { + return color.a == 0 && + color.b == 0 && + color.g == 0 && + color.r == 0; + } + + public static bool IsClearColor(Color color) + { + return color.a == 0 && + color.b == 0 && + color.g == 0 && + color.r == 0; + } + + public static bool IsZeroVector(Vector3 pos) + { + return pos.x == 0 && + pos.y == 0 && + pos.z == 0; + } + + public static Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle) + { + Vector3 point = Quaternion.AngleAxis(angle, axis) * (position - center); + Vector3 resultVec3 = center + point; + return resultVec3; + } + + public static void GetBezierList(ref List<Vector3> posList, Vector3 sp, Vector3 ep, + Vector3 lsp, Vector3 nep, float smoothness = 2f, float k = 2.0f, bool limit = false, bool randomDire = false) + { + Vector3 cp1, cp2; + var dist = Vector3.Distance(sp, ep); + var dir = (ep - sp).normalized; + var diff = (randomDire ? dist : Mathf.Abs(sp.x - ep.x)) / k; + if (lsp == sp) + { + cp1 = sp + (nep - ep).normalized * diff; + if (limit) cp1.y = sp.y; + } + else + { + cp1 = sp + (ep - lsp).normalized * diff; + if (limit) cp1.y = sp.y; + } + if (nep == ep) + { + cp2 = ep; + } + else + { + cp2 = ep - (nep - sp).normalized * diff; + if (limit) cp2.y = ep.y; + } + int segment = (int)(dist / (smoothness <= 0 ? 2f : smoothness)); + if (segment < 1) segment = (int)(dist / 0.5f); + if (segment < 4) segment = 4; + GetBezierList2(ref posList, sp, ep, segment, cp1, cp2); + if (posList.Count < 2) + { + posList.Clear(); + posList.Add(sp); + posList.Add(ep); + } + } + + public static void GetBezierListVertical(ref List<Vector3> posList, Vector3 sp, Vector3 ep, + float smoothness = 2f, float k = 2.0f) + { + Vector3 dir = (ep - sp).normalized; + float dist = Vector3.Distance(sp, ep); + Vector3 cp1 = sp + dist / k * dir * 1; + Vector3 cp2 = sp + dist / k * dir * (k - 1); + cp1.x = sp.x; + cp2.x = ep.x; + int segment = (int)(dist / (smoothness <= 0 ? 2f : smoothness)); + GetBezierList2(ref posList, sp, ep, segment, cp1, cp2); + if (posList.Count < 2) + { + posList.Clear(); + posList.Add(sp); + posList.Add(ep); + } + } + + public static List<Vector3> GetBezierList(Vector3 sp, Vector3 ep, int segment, Vector3 cp) + { + List<Vector3> list = new List<Vector3>(); + for (int i = 0; i < segment; i++) + { + list.Add(GetBezier(i / (float)segment, sp, cp, ep)); + } + list.Add(ep); + return list; + } + + public static void GetBezierList2(ref List<Vector3> posList, Vector3 sp, Vector3 ep, + int segment, Vector3 cp, Vector3 cp2) + { + posList.Clear(); + if (posList.Capacity < segment + 1) + { + posList.Capacity = segment + 1; + } + for (int i = 0; i < segment; i++) + { + posList.Add((GetBezier2(i / (float)segment, sp, cp, cp2, ep))); + } + posList.Add(ep); + } + + public static Vector3 GetBezier(float t, Vector3 sp, Vector3 cp, Vector3 ep) + { + Vector3 aa = sp + (cp - sp) * t; + Vector3 bb = cp + (ep - cp) * t; + return aa + (bb - aa) * t; + } + + public static Vector3 GetBezier2(float t, Vector3 sp, Vector3 p1, Vector3 p2, Vector3 ep) + { + t = Mathf.Clamp01(t); + var oneMinusT = 1f - t; + return oneMinusT * oneMinusT * oneMinusT * sp + + 3f * oneMinusT * oneMinusT * t * p1 + + 3f * oneMinusT * t * t * p2 + + t * t * t * ep; + } + + public static Vector3 GetDire(float angle, bool isDegree = false) + { + angle = isDegree ? angle * Mathf.Deg2Rad : angle; + return new Vector3(Mathf.Sin(angle), Mathf.Cos(angle)); + } + + public static Vector3 GetVertialDire(Vector3 dire) + { + if (dire.x == 0) + return new Vector3(-1, 0, 0); + + if (dire.y == 0) + return new Vector3(0, -1, 0); + else + return new Vector3(-dire.y / dire.x, 1, 0).normalized; + } + + /// <summary> + /// 获得0-360的角度(12点钟方向为0度) + /// </summary> + /// <param name="from"></param> + /// <param name="to"></param> + /// <returns></returns> + public static float GetAngle360(Vector2 from, Vector2 to) + { + float angle; + + Vector3 cross = Vector3.Cross(from, to); + angle = Vector2.Angle(from, to); + angle = cross.z > 0 ? -angle : angle; + angle = (angle + 360) % 360; + return angle; + } + + public static Vector3 GetPos(Vector3 center, float radius, float angle, bool isDegree = false) + { + angle = isDegree ? angle * Mathf.Deg2Rad : angle; + return new Vector3(center.x + radius * Mathf.Sin(angle), + center.y + radius * Mathf.Cos(angle)); + } + + /// <summary> + /// 获得两直线的交点 + /// </summary> + /// <param name="p1">线段1起点</param> + /// <param name="p2">线段1终点</param> + /// <param name="p3">线段2起点</param> + /// <param name="p4">线段2终点</param> + /// <param name="intersection">相交点。当不相交时为初始值</param> + /// <returns>相交则返回 true, 否则返回 false</returns> + public static bool GetIntersection(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, ref Vector3 intersection) + { + float dx1 = p2.x - p1.x; + float dy1 = p2.y - p1.y; + float dx2 = p4.x - p3.x; + float dy2 = p4.y - p3.y; + + float d = dx1 * dy2 - dy1 * dx2; + if (Mathf.Abs(d) < 1e-6f) + return false; + + float dx3 = p3.x - p1.x; + float dy3 = p3.y - p1.y; + + float u = (dx3 * dy2 - dy3 * dx2) / d; + if (u < 0 || u > 1) return false; + + float v = (dx3 * dy1 - dy3 * dx1) / d; + if (v < 0 || v > 1) return false; + + intersection.x = p1.x + u * dx1; + intersection.y = p1.y + u * dy1; + intersection.z = p1.z; + return true; + } + + /// <summary> + /// 获得两直线的交点 + /// </summary> + /// <param name="p1">线段1起点</param> + /// <param name="p2">线段1终点</param> + /// <param name="p3">线段2起点</param> + /// <param name="p4">线段2终点</param> + /// <param name="intersection">相交点。当不相交时为初始值</param> + /// <returns>相交则返回 true, 否则返回 false</returns> + public static bool GetIntersection(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, ref List<Vector3> intersection) + { + var d = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x); + if (d == 0) + return false; + + var u = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / d; + var v = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / d; + if (u < 0 || u > 1 || v < 0 || v > 1) + return false; + + intersection.Add(new Vector3(p1.x + u * (p2.x - p1.x), p1.y + u * (p2.y - p1.y))); + return true; + } + + /// <summary> + /// 三个点画线段所需要的六个关键点 + /// </summary> + /// <param name="lp">上一个点</param> + /// <param name="cp">当前点</param> + /// <param name="np">下一个点</param> + /// <param name="width">线段宽度</param> + /// <param name="ltp">上一个点的上角点</param> + /// <param name="lbp">上一个点的下角点</param> + /// <param name="ntp">下一个点的上角点</param> + /// <param name="nbp">下一个点的下角点</param> + /// <param name="itp">交汇点的上角点</param> + /// <param name="ibp">交汇点的下角点</param> + public static void GetLinePoints(Vector3 lp, Vector3 cp, Vector3 np, float width, + ref Vector3 ltp, ref Vector3 lbp, + ref Vector3 ntp, ref Vector3 nbp, + ref Vector3 itp, ref Vector3 ibp, + ref Vector3 clp, ref Vector3 crp, + ref bool bitp, ref bool bibp, int debugIndex = 0) + { + var dir1 = (cp - lp).normalized; + var dir1v = Vector3.Cross(dir1, Vector3.forward).normalized * width; + ltp = lp - dir1v; + lbp = lp + dir1v; + if (debugIndex == 1 && cp == np) + { + ntp = np - dir1v; + nbp = np + dir1v; + clp = cp - dir1v; + crp = cp + dir1v; + return; + } + + var dir2 = (cp - np).normalized; + var dir2v = Vector3.Cross(dir2, Vector3.back).normalized * width; + ntp = np - dir2v; + nbp = np + dir2v; + clp = cp - dir2v; + crp = cp + dir2v; + + float crossMagnitude = Vector3.Cross(dir1, dir2).sqrMagnitude; + if (crossMagnitude < 1e-6f && np != cp) + { + itp = clp; + ibp = crp; + return; + } + + var ldist = (Vector3.Distance(cp, lp) + width) * dir1; + var rdist = (Vector3.Distance(cp, np) + width) * dir2; + + bitp = UGLHelper.GetIntersection(ltp, ltp + ldist, ntp, ntp + rdist, ref itp); + bibp = UGLHelper.GetIntersection(lbp, lbp + ldist, nbp, nbp + rdist, ref ibp); + if (bitp == bibp) + { + if (!bitp) + { + if (cp == np) + { + ltp = cp - dir1v; + clp = cp + dir1v; + crp = cp + dir1v; + } + else + { + Vector3 ibp2 = Vector3.zero; + if (UGLHelper.GetIntersection(lbp, lbp + ldist, ntp, nbp, ref ibp2)) + { + bibp = true; + ibp = ibp2; + clp = cp - dir1v; + crp = cp - dir2v; + } + else if (UGLHelper.GetIntersection(ltp, ltp + ldist, nbp, ntp, ref ibp2)) + { + bitp = true; + itp = ibp2; + clp = cp + dir1v; + crp = cp + dir2v; + } + else + { + if (IsUp(lp, cp, np)) + { + bibp = true; + + clp = cp - dir1v; + crp = cp - dir2v; + ibp = cp - Vector3.Cross((crp - clp).normalized, Vector3.back).normalized * width * 2f; + } + else + { + bitp = true; + clp = cp + dir1v; + crp = cp + dir2v; + itp = cp + Vector3.Cross((crp - clp).normalized, Vector3.back).normalized * width * 2f; + } + + } + } + } + } + else + { + if (!bitp) + { + itp = cp; + clp = cp - dir1v; + crp = cp - dir2v; + } + else + { + ibp = cp; + clp = cp + dir1v; + crp = cp + dir2v; + } + } + } + + public static bool IsUp(Vector3 p1, Vector3 p2, Vector3 p3) + { + var v1 = p1 - p2; + var v2 = p3 - p2; + var cross = v1.x * v2.y - v1.y * v2.x; + return cross > 0; + } + + public static bool IsPointInTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 check) + { + var dire1 = check - p1; + var dire2 = check - p2; + var dire3 = check - p3; + var c1 = dire1.x * dire2.y - dire1.y * dire2.x; + var c2 = dire2.x * dire3.y - dire2.y * dire3.x; + var c3 = dire3.x * dire1.y - dire3.y * dire1.x; + return c1 * c2 >= 0 && c1 * c3 >= 0; + } + + public static bool IsPointInPolygon(Vector3 p, List<Vector3> polyons) + { + if (polyons.Count == 0) return false; + var inside = false; + var j = polyons.Count - 1; + for (int i = 0; i < polyons.Count; j = i++) + { + var pi = polyons[i]; + var pj = polyons[j]; + if (((pi.y <= p.y && p.y < pj.y) || (pj.y <= p.y && p.y < pi.y)) && + (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x)) + inside = !inside; + } + return inside; + } + + public static bool IsPointInPolygon(Vector3 p, params Vector3[] polyons) + { + if (polyons.Length == 0) return false; + var inside = false; + var j = polyons.Length - 1; + for (int i = 0; i < polyons.Length; j = i++) + { + var pi = polyons[i]; + var pj = polyons[j]; + if (((pi.y <= p.y && p.y < pj.y) || (pj.y <= p.y && p.y < pi.y)) && + (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x)) + inside = !inside; + } + return inside; + } + + public static bool IsPointInPolygon(Vector3 p, List<Vector2> polyons) + { + if (polyons.Count == 0) return false; + var inside = false; + var j = polyons.Count - 1; + for (int i = 0; i < polyons.Count; j = i++) + { + var pi = polyons[i]; + var pj = polyons[j]; + if (((pi.y <= p.y && p.y < pj.y) || (pj.y <= p.y && p.y < pi.y)) && + (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x)) + inside = !inside; + } + return inside; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/XUGL/UGLHelper.cs.meta b/Assets/XCharts/Runtime/XUGL/UGLHelper.cs.meta new file mode 100644 index 0000000..2165db1 --- /dev/null +++ b/Assets/XCharts/Runtime/XUGL/UGLHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc77f59a050d547caa3de82f4a9abd99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/package.json b/Assets/XCharts/package.json new file mode 100644 index 0000000..d485bb1 --- /dev/null +++ b/Assets/XCharts/package.json @@ -0,0 +1,27 @@ +{ + "name": "com.monitor1394.xcharts", + "displayName": "XCharts", + "author": "monitor1394", + "license": "MIT", + "version": "3.14.0", + "date": "20250315", + "checkdate": "20250315", + "unity": "2018.3", + "description": "A charting and data visualization library for Unity. Support line chart, bar chart, pie chart, radar chart, scatter chart, heatmap chart, ring chart, candlestick chart, polar chart and parallel coordinates.", + "keywords": [ + "chart", + "charts", + "graph", + "unity-chart", + "data-visualization" + ], + "category": "chart", + "repository": { + "type": "git", + "url": "git+https://github.com/XCharts-Team/XCharts.git" + }, + "bugs": { + "url": "https://github.com/XCharts-Team/XCharts/issues" + }, + "homepage": "https://github.com/XCharts-Team/XCharts" +} \ No newline at end of file diff --git a/Assets/XCharts/package.json.meta b/Assets/XCharts/package.json.meta new file mode 100644 index 0000000..9bc810b --- /dev/null +++ b/Assets/XCharts/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c4d5abd20b2304597ae3d0d57fd8986e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: