From e595034833ae3ea15ce0cf87e162e41b9cfbbb7e Mon Sep 17 00:00:00 2001 From: monitor1394 Date: Sun, 8 Mar 2020 10:47:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0`RingChart`=E7=8E=AF=E5=BD=A2?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + Documentation/XChartsAPI.md | 7 + Documentation/XCharts配置项手册.md | 23 ++ Editor/PropertyDrawers/ItemStyleDrawer.cs | 14 +- Editor/PropertyDrawers/SerieDrawer.cs | 31 +- Editor/PropertyDrawers/TitleStyleDrawer.cs | 8 +- Editor/XChartEditor.cs | 6 + README.md | 4 +- Runtime/API/BaseChart_API.cs | 2 +- Runtime/API/RingChart_API.cs | 95 ++++++ Runtime/API/RingChart_API.cs.meta | 11 + Runtime/Component/Main/Serie.cs | 43 ++- Runtime/Component/Main/Series.cs | 1 + Runtime/Component/Sub/ItemStyle.cs | 36 ++ Runtime/Component/Sub/SerieAnimation.cs | 2 +- Runtime/Component/Sub/SerieData.cs | 30 +- Runtime/Component/Sub/SerieLabel.cs | 10 +- Runtime/Component/Sub/TextStyle.cs | 62 +--- Runtime/Component/Sub/TitleStyle.cs | 69 ++-- Runtime/GaugeChart.cs | 99 ++---- Runtime/Helper/SerieHelper.cs | 50 +++ Runtime/Helper/SerieHelper.cs.meta | 11 + Runtime/Helper/SerieLabelHelper.cs | 128 ++++++++ Runtime/Helper/SerieLabelHelper.cs.meta | 11 + Runtime/Helper/TitleStyleHelper.cs | 43 +++ Runtime/Helper/TitleStyleHelper.cs.meta | 11 + Runtime/Internal/APIException.cs | 20 ++ Runtime/Internal/APIException.cs.meta | 11 + Runtime/Internal/BaseChart.cs | 10 +- Runtime/PieChart.cs | 18 +- Runtime/RingChart.cs | 364 +++++++++++++++++++++ Runtime/RingChart.cs.meta | 11 + Runtime/Utility/ChartDrawer.cs | 22 ++ Runtime/Utility/XChartsMgr.cs | 2 +- 34 files changed, 1030 insertions(+), 236 deletions(-) create mode 100644 Runtime/API/RingChart_API.cs create mode 100644 Runtime/API/RingChart_API.cs.meta create mode 100644 Runtime/Helper/SerieHelper.cs create mode 100644 Runtime/Helper/SerieHelper.cs.meta create mode 100644 Runtime/Helper/SerieLabelHelper.cs create mode 100644 Runtime/Helper/SerieLabelHelper.cs.meta create mode 100644 Runtime/Helper/TitleStyleHelper.cs create mode 100644 Runtime/Helper/TitleStyleHelper.cs.meta create mode 100644 Runtime/Internal/APIException.cs create mode 100644 Runtime/Internal/APIException.cs.meta create mode 100644 Runtime/RingChart.cs create mode 100644 Runtime/RingChart.cs.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index c2e45c13..e039fbb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 更新日志 +* (2020.03.08) 增加`RingChart`环形图 * (2020.03.05) 调整`Serie`的`arcShaped`参数重命名为`roundCap` * (2020.03.05) 增加运行时和非运行时参数变更自动刷新图表 * (2020.02.26) 重构`Legend`图例,改变样式,增加自定义图标等设置 diff --git a/Documentation/XChartsAPI.md b/Documentation/XChartsAPI.md index 09070165..b54a6fea 100644 --- a/Documentation/XChartsAPI.md +++ b/Documentation/XChartsAPI.md @@ -107,6 +107,13 @@ * 继承自 `BaseChart`。 * 继承自 `CoordinateChart`。 +## `RingChart` + +* 继承自 `BaseChart`。 +* `RingChart.UpdateMax(int serieIndex, int dataIndex, float value)`:更新指定系列执行数据项的最大值。 +* `RingChart.UpdateMax(int serieIndex, float value)`:更新指定系列的所有数据项的最大值。 +* `RingChart.UpdateMax(float value)`:更新第一个系列第一个数据项的最大值。 + [返回首页](https://github.com/monitor1394/unity-ugui-XCharts) [XCharts配置项手册](XCharts配置项手册.md) [XCharts问答](XCharts问答.md) diff --git a/Documentation/XCharts配置项手册.md b/Documentation/XCharts配置项手册.md index e0f8fedd..6f32171d 100644 --- a/Documentation/XCharts配置项手册.md +++ b/Documentation/XCharts配置项手册.md @@ -19,6 +19,7 @@ * [Serie-Scatter 散点图](#Serie-Scatter) * [Serie-Heatmap 热力图](#Serie-Heatmap) * [Serie-Gauge 仪表盘](#Serie-Gauge) +* [Serie-Ring 环形图](#Serie-Ring) * [Settings 设置](#Settings) * [Theme 主题](#Theme) * [Tooltip 提示框](#Tooltip) @@ -601,6 +602,26 @@ * `animation`:起始动画 [SerieAnimation](#SerieAnimation)。 * `data`:系列中的数据项 [SerieData](#SerieData) 数组,可以设置`1`到`n`维数据。仪表盘的数据一般只有一个,值通过`label`样式显示,`name`通过`titleStyle`样式显示。 +## `Serie-Ring` + +环形图系列。 + +* `show`:系列是否显示在图表上。 +* `type`:`Ring`。 +* `name`:系列名称。用于 `tooltip` 的显示,`legend` 的图例筛选。 +* `center`:中心点坐标。当值为0-1的浮点数时表示百分比。 +* `radius`:仪表盘半径。 +* `startAngle`:仪表盘起始角度。和时钟一样,12点钟位置是0度,顺时针到360度。 +* `ringGap`:环形图的环间隙。 +* `roundCap`:是否启用圆弧效果。 +* `clockwise`:是否顺时针,默认为`true`。 +* `titleStyle`:环形图中心标题 [TitleStyle](#TitleStyle)。 +* `itemStyle`:环形图的圆环样式,包括设置背景颜色和边框等 [ItemStyle](#ItemStyle)。 +* `label`:图形上的文本标签 [SerieLabel](#SerieLabel),可用于说明图形的一些数据信息,比如值,名称等。 +* `emphasis`:高亮样式 [Emphasis](#Emphasis)。 +* `animation`:起始动画 [SerieAnimation](#SerieAnimation)。 +* `data`:系列中的数据项 [SerieData](#SerieData) 数组,可以设置`1`到`n`维数据。环形图的数据只有二维,`data[0]`表示当前值,`data[1]`表示最大值。 + ## `Settings` 全局参数设置组件。一般情况下可使用默认值,当有需要时可进行调整。 @@ -701,6 +722,8 @@ * `show`:是否启用。 * `color`:颜色。 +* `backgroundColor`:背景颜色。 +* `backgroundWidth`:背景的宽。 * `borderType`:边框的类型。 * `borderColor`:边框的颜色。 * `borderWidth`:边框宽。 diff --git a/Editor/PropertyDrawers/ItemStyleDrawer.cs b/Editor/PropertyDrawers/ItemStyleDrawer.cs index 89e58616..647c959c 100644 --- a/Editor/PropertyDrawers/ItemStyleDrawer.cs +++ b/Editor/PropertyDrawers/ItemStyleDrawer.cs @@ -22,6 +22,10 @@ namespace XCharts drawRect.height = EditorGUIUtility.singleLineHeight; SerializedProperty show = prop.FindPropertyRelative("m_Show"); SerializedProperty m_Color = prop.FindPropertyRelative("m_Color"); + SerializedProperty m_BackgroundColor = prop.FindPropertyRelative("m_BackgroundColor"); + SerializedProperty m_BackgroundWidth = prop.FindPropertyRelative("m_BackgroundWidth"); + SerializedProperty m_CenterColor = prop.FindPropertyRelative("m_CenterColor"); + SerializedProperty m_CenterGap = prop.FindPropertyRelative("m_CenterGap"); SerializedProperty m_BorderType = prop.FindPropertyRelative("m_BorderType"); SerializedProperty m_BorderWidth = prop.FindPropertyRelative("m_BorderWidth"); SerializedProperty m_BorderColor = prop.FindPropertyRelative("m_BorderColor"); @@ -33,6 +37,14 @@ namespace XCharts ++EditorGUI.indentLevel; EditorGUI.PropertyField(drawRect, m_Color); drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_BackgroundColor); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_BackgroundWidth); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_CenterColor); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_CenterGap); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; EditorGUI.PropertyField(drawRect, m_BorderType); drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; EditorGUI.PropertyField(drawRect, m_BorderWidth); @@ -50,7 +62,7 @@ namespace XCharts float height = 0; if (ChartEditorHelper.IsToggle(m_ItemStyleToggle, prop)) { - height += 6 * EditorGUIUtility.singleLineHeight + 5 * EditorGUIUtility.standardVerticalSpacing; + height += 10 * EditorGUIUtility.singleLineHeight + 9 * EditorGUIUtility.standardVerticalSpacing; } else { diff --git a/Editor/PropertyDrawers/SerieDrawer.cs b/Editor/PropertyDrawers/SerieDrawer.cs index a835a831..5c0345eb 100644 --- a/Editor/PropertyDrawers/SerieDrawer.cs +++ b/Editor/PropertyDrawers/SerieDrawer.cs @@ -62,8 +62,9 @@ namespace XCharts SerializedProperty m_Max = prop.FindPropertyRelative("m_Max"); SerializedProperty m_StartAngle = prop.FindPropertyRelative("m_StartAngle"); SerializedProperty m_EndAngle = prop.FindPropertyRelative("m_EndAngle"); + SerializedProperty m_RingGap = prop.FindPropertyRelative("m_RingGap"); SerializedProperty m_SplitNumber = prop.FindPropertyRelative("m_SplitNumber"); - //SerializedProperty m_Clockwise = prop.FindPropertyRelative("m_Clockwise"); + SerializedProperty m_Clockwise = prop.FindPropertyRelative("m_Clockwise"); SerializedProperty m_RoundCap = prop.FindPropertyRelative("m_RoundCap"); SerializedProperty m_GaugeType = prop.FindPropertyRelative("m_GaugeType"); SerializedProperty m_GaugeAxis = prop.FindPropertyRelative("m_GaugeAxis"); @@ -189,6 +190,26 @@ namespace XCharts EditorGUI.PropertyField(drawRect, m_Emphasis); drawRect.y += EditorGUI.GetPropertyHeight(m_Emphasis); break; + case SerieType.Ring: + ChartEditorHelper.MakeTwoField(ref drawRect, pos.width, m_Center, "Center"); + ChartEditorHelper.MakeTwoField(ref drawRect, pos.width, m_Radius, "Radius"); + EditorGUI.PropertyField(drawRect, m_StartAngle); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_RingGap); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_RoundCap); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_Clockwise); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_TitleStyle); + drawRect.y += EditorGUI.GetPropertyHeight(m_TitleStyle); + EditorGUI.PropertyField(drawRect, m_ItemStyle); + drawRect.y += EditorGUI.GetPropertyHeight(m_ItemStyle); + EditorGUI.PropertyField(drawRect, m_Label); + drawRect.y += EditorGUI.GetPropertyHeight(m_Label); + EditorGUI.PropertyField(drawRect, m_Emphasis); + drawRect.y += EditorGUI.GetPropertyHeight(m_Emphasis); + break; case SerieType.Radar: EditorGUI.PropertyField(drawRect, m_RadarIndex); drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; @@ -452,6 +473,14 @@ namespace XCharts height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_Emphasis")); height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_Animation")); break; + case SerieType.Ring: + height += 10 * EditorGUIUtility.singleLineHeight + 9 * EditorGUIUtility.standardVerticalSpacing; + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_TitleStyle")); + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_ItemStyle")); + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_Label")); + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_Emphasis")); + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_Animation")); + break; case SerieType.Radar: height += 4 * EditorGUIUtility.singleLineHeight + 3 * EditorGUIUtility.standardVerticalSpacing; height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_Symbol")); diff --git a/Editor/PropertyDrawers/TitleStyleDrawer.cs b/Editor/PropertyDrawers/TitleStyleDrawer.cs index 253ecc3b..28cedfa9 100644 --- a/Editor/PropertyDrawers/TitleStyleDrawer.cs +++ b/Editor/PropertyDrawers/TitleStyleDrawer.cs @@ -21,14 +21,14 @@ namespace XCharts Rect drawRect = pos; drawRect.height = EditorGUIUtility.singleLineHeight; SerializedProperty show = prop.FindPropertyRelative("m_Show"); - SerializedProperty m_textStyle = prop.FindPropertyRelative("m_textStyle"); + SerializedProperty m_TextStyle = prop.FindPropertyRelative("m_TextStyle"); ChartEditorHelper.MakeFoldout(ref drawRect, ref m_TitleStyleToggle, prop, "Title Style", show, false); drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; if (ChartEditorHelper.IsToggle(m_TitleStyleToggle, prop)) { ++EditorGUI.indentLevel; - EditorGUI.PropertyField(drawRect, m_textStyle); - drawRect.y += EditorGUI.GetPropertyHeight(m_textStyle); + EditorGUI.PropertyField(drawRect, m_TextStyle); + drawRect.y += EditorGUI.GetPropertyHeight(m_TextStyle); --EditorGUI.indentLevel; } } @@ -39,7 +39,7 @@ namespace XCharts if (ChartEditorHelper.IsToggle(m_TitleStyleToggle, prop)) { height += 1 * EditorGUIUtility.singleLineHeight + 1 * EditorGUIUtility.standardVerticalSpacing; - height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_textStyle")); + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_TextStyle")); } else { diff --git a/Editor/XChartEditor.cs b/Editor/XChartEditor.cs index fbfac0a8..f1b7cb52 100644 --- a/Editor/XChartEditor.cs +++ b/Editor/XChartEditor.cs @@ -109,5 +109,11 @@ namespace XCharts { AddChart("GaugeChart"); } + + [MenuItem("GameObject/XCharts/RingChart", priority = 51)] + public static void AddRingChart() + { + AddChart("RingChart"); + } } } \ No newline at end of file diff --git a/README.md b/README.md index 9f970bde..298b05f5 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ A powerful, easy-to-use, configurable charting and data visualization library for Unity. -一款基于`UGUI`的功能强大、易用、参数可配置的数据可视化图表插件。支持折线图、柱状图、饼图、雷达图、散点图、热力图、仪表盘等常见图表。 +一款基于`UGUI`的功能强大、易用、参数可配置的数据可视化图表插件。支持折线图、柱状图、饼图、雷达图、散点图、热力图、仪表盘、环形图等常见图表。 ## 特性 * 内置丰富示例和模板,参数可视化配置,效果实时预览,纯代码绘制。 -* 支持折线图、柱状图、饼图、雷达图、散点图、热力图、仪表盘等常见图表。 +* 支持折线图、柱状图、饼图、雷达图、散点图、热力图、仪表盘、环形图等常见图表。 * 支持直线图、曲线图、面积图、阶梯线图等折线图。 * 支持并列柱图、堆叠柱图、堆积百分比柱图、斑马柱图等柱状图。 * 支持环形图、玫瑰图等饼图。 diff --git a/Runtime/API/BaseChart_API.cs b/Runtime/API/BaseChart_API.cs index 9d364671..6633af7c 100644 --- a/Runtime/API/BaseChart_API.cs +++ b/Runtime/API/BaseChart_API.cs @@ -220,7 +220,7 @@ namespace XCharts /// y data /// the name of data /// Returns True on success - public virtual SerieData AddData(string serieName, float xValue, float yValue, string dataName) + public virtual SerieData AddData(string serieName, float xValue, float yValue, string dataName = null) { var serieData = m_Series.AddXYData(serieName, xValue, yValue, dataName); if (serieData != null) diff --git a/Runtime/API/RingChart_API.cs b/Runtime/API/RingChart_API.cs new file mode 100644 index 00000000..1bc54637 --- /dev/null +++ b/Runtime/API/RingChart_API.cs @@ -0,0 +1,95 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ + +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts +{ + public partial class RingChart + { + /// + /// 更新指定系列执行数据项的最大值 + /// + /// + /// + /// + /// + public bool UpdateMax(int serieIndex, int dataIndex, float value) + { + var serie = m_Series.GetSerie(serieIndex); + if (serie != null) + { + var serieData = serie.GetSerieData(dataIndex); + if (serieData != null) + { + return serieData.UpdateData(1, value); + } + } + return false; + } + + /// + /// 更新指定系列的所有数据项的最大值 + /// + /// + /// + /// + public bool UpdateMax(int serieIndex, float value) + { + var serie = m_Series.GetSerie(serieIndex); + if (serie != null) + { + var flag = true; + foreach (var serieData in serie.data) + { + if (!serieData.UpdateData(1, value)) flag = false; + } + return flag; + } + return false; + } + + /// + /// 更新第一个系列第一个数据项的最大值 + /// + /// + /// + public bool UpdateMax(float value) + { + return UpdateMax(0, 0, value); + } + + /// + /// Adds the data with the specified maximum value to the specified serie. + /// 添加指定最大值的数据到指定系列中。 + /// + /// the name of serie + /// the data + /// the max data + /// the name of data + /// Returns True on success + public override SerieData AddData(string serieName, float value, float max, string dataName = null) + { + return base.AddData(serieName, value, max, dataName); + } + + /// + /// Adds the data with the specified maximum value to the specified serie. + /// 添加指定最大值的数据到指定系列中。 + /// + /// the index of serie + /// the data + /// the max data + /// the name of data + /// Returns True on success + public override SerieData AddData(int serieIndex, float value, float max, string dataName = null) + { + return base.AddData(serieIndex, value, max, dataName); + } + } +} \ No newline at end of file diff --git a/Runtime/API/RingChart_API.cs.meta b/Runtime/API/RingChart_API.cs.meta new file mode 100644 index 00000000..78e0d0dc --- /dev/null +++ b/Runtime/API/RingChart_API.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a1cd7e0dade8470cb91d6a7971ff022 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Component/Main/Serie.cs b/Runtime/Component/Main/Serie.cs index 6248b409..cdf39a38 100644 --- a/Runtime/Component/Main/Serie.cs +++ b/Runtime/Component/Main/Serie.cs @@ -52,6 +52,10 @@ namespace XCharts /// 仪表盘。 /// Gauge, + /// + /// 环形图。只支持一个数据的环形图。 + /// + Ring, } /// @@ -225,9 +229,11 @@ namespace XCharts [SerializeField] private float m_Max; [SerializeField] private float m_StartAngle; [SerializeField] private float m_EndAngle; - [SerializeField] private bool m_Clockwise; + [SerializeField] private bool m_Clockwise = true; [FormerlySerializedAs("m_ArcShaped")] [SerializeField] private bool m_RoundCap; + [SerializeField] private float m_RingGap = 10f; + [SerializeField] private int m_SplitNumber; [SerializeField] private GaugeType m_GaugeType = GaugeType.Pointer; [SerializeField] private GaugeAxis m_GaugeAxis = new GaugeAxis(); @@ -577,6 +583,15 @@ namespace XCharts get { return m_Clockwise; } set { if (PropertyUtility.SetStruct(ref m_Clockwise, value)) SetVerticesDirty(); } } + + /// + /// 环形图的环间隙。 + /// + public float ringGap + { + get { return m_RingGap; } + set { if (PropertyUtility.SetStruct(ref m_RingGap, value)) SetVerticesDirty(); } + } /// /// 刻度分割段数。最大可设置36。 /// @@ -704,10 +719,11 @@ namespace XCharts label.vertsDirty || emphasis.vertsDirty || gaugeAxis.vertsDirty || - gaugePointer.vertsDirty || - titleStyle.vertsDirty; + gaugePointer.vertsDirty; } } + + public override bool componentDirty { get { return m_ComponentDirty || titleStyle.componentDirty; } } internal override void ClearVerticesDirty() { base.ClearVerticesDirty(); @@ -995,11 +1011,20 @@ namespace XCharts m_Data.Add(serieData); m_ShowDataDimension = 1; SetVerticesDirty(); + CheckDataName(dataName); + return serieData; + } + + private void CheckDataName(string dataName) + { if (string.IsNullOrEmpty(dataName)) { SetNameDirty(); } - return serieData; + else + { + m_ShowDataName = true; + } } /// @@ -1028,10 +1053,7 @@ namespace XCharts m_Data.Add(serieData); m_ShowDataDimension = 2; SetVerticesDirty(); - if (string.IsNullOrEmpty(dataName)) - { - SetNameDirty(); - } + CheckDataName(dataName); return serieData; } @@ -1073,10 +1095,7 @@ namespace XCharts } m_Data.Add(serieData); SetVerticesDirty(); - if (string.IsNullOrEmpty(dataName)) - { - SetNameDirty(); - } + CheckDataName(dataName); return serieData; } } diff --git a/Runtime/Component/Main/Series.cs b/Runtime/Component/Main/Series.cs index 373e4451..1cb96dbb 100644 --- a/Runtime/Component/Main/Series.cs +++ b/Runtime/Component/Main/Series.cs @@ -1033,6 +1033,7 @@ namespace XCharts { case SerieType.Pie: case SerieType.Radar: + case SerieType.Ring: for (int i = 0; i < serie.data.Count; i++) { if (string.IsNullOrEmpty(serie.data[i].name)) diff --git a/Runtime/Component/Sub/ItemStyle.cs b/Runtime/Component/Sub/ItemStyle.cs index 6bd957ee..df234d3b 100644 --- a/Runtime/Component/Sub/ItemStyle.cs +++ b/Runtime/Component/Sub/ItemStyle.cs @@ -36,6 +36,10 @@ namespace XCharts } [SerializeField] private bool m_Show = false; [SerializeField] private Color m_Color; + [SerializeField] private Color m_BackgroundColor; + [SerializeField] private float m_BackgroundWidth; + [SerializeField] private Color m_CenterColor; + [SerializeField] private float m_CenterGap; [SerializeField] private Type m_BorderType = Type.Solid; [SerializeField] private float m_BorderWidth = 0; [SerializeField] private Color m_BorderColor; @@ -58,6 +62,38 @@ namespace XCharts set { if (PropertyUtility.SetColor(ref m_Color, value)) SetVerticesDirty(); } } /// + /// 数据项背景颜色。 + /// + public Color backgroundColor + { + get { return m_BackgroundColor; } + set { if (PropertyUtility.SetColor(ref m_BackgroundColor, value)) SetVerticesDirty(); } + } + /// + /// 中心区域颜色。 + /// + public Color centerColor + { + get { return m_CenterColor; } + set { if (PropertyUtility.SetColor(ref m_CenterColor, value)) SetVerticesDirty(); } + } + /// + /// 中心区域间隙。 + /// + public float centerGap + { + get { return m_CenterGap; } + set { if (PropertyUtility.SetStruct(ref m_CenterGap, value)) SetVerticesDirty(); } + } + /// + /// 数据项背景颜色。 + /// + public float backgroundWidth + { + get { return m_BackgroundWidth; } + set { if (PropertyUtility.SetStruct(ref m_BackgroundWidth, value)) SetVerticesDirty(); } + } + /// /// 边框的类型。 /// public Type borderType diff --git a/Runtime/Component/Sub/SerieAnimation.cs b/Runtime/Component/Sub/SerieAnimation.cs index c548e1f9..4c5d9930 100644 --- a/Runtime/Component/Sub/SerieAnimation.cs +++ b/Runtime/Component/Sub/SerieAnimation.cs @@ -237,7 +237,7 @@ namespace XCharts return (delay > 0 && Time.time - startTime < delay / 1000); } - public bool CheckDetailBreak(int dataIndex, float detail) + public bool CheckDetailBreak(float detail) { return !IsFinish() && detail > m_CurrDetailProgress; } diff --git a/Runtime/Component/Sub/SerieData.cs b/Runtime/Component/Sub/SerieData.cs index cbfb711b..01c6c5f9 100644 --- a/Runtime/Component/Sub/SerieData.cs +++ b/Runtime/Component/Sub/SerieData.cs @@ -153,7 +153,7 @@ namespace XCharts public float runtimePieOffsetRadius { get; internal set; } public Vector3 runtiemPieOffsetCenter { get; internal set; } - private List m_LastData = new List(); + private List m_PreviousData = new List(); private List m_DataUpdateTime = new List(); private List m_DataUpdateFlag = new List(); @@ -166,15 +166,27 @@ namespace XCharts else return 0; } - public float GetLastData(int index) + public float GetPreviousData(int index) { - if (index >= 0 && index < m_LastData.Count) + if (index >= 0 && index < m_PreviousData.Count) { - return m_LastData[index]; + return m_PreviousData[index]; } else return 0; } + public float GetFirstData(float animationDuration = 500f) + { + if (m_Data.Count > 0) return GetCurrData(0, animationDuration); + return 0; + } + + public float GetLastData() + { + if (m_Data.Count > 0) return m_Data[m_Data.Count - 1]; + return 0; + } + public float GetCurrData(int index, float animationDuration = 500f) { if (index < m_DataUpdateFlag.Count && m_DataUpdateFlag[index] && animationDuration > 0) @@ -184,7 +196,7 @@ namespace XCharts if (time <= total) { CheckLastData(); - var curr = Mathf.Lerp(GetLastData(index), GetData(index), time / total); + var curr = Mathf.Lerp(GetPreviousData(index), GetData(index), time / total); return curr; } else @@ -204,7 +216,7 @@ namespace XCharts if (dimension >= 0 && dimension < data.Count) { CheckLastData(); - m_LastData[dimension] = data[dimension]; + m_PreviousData[dimension] = data[dimension]; m_DataUpdateTime[dimension] = Time.time; m_DataUpdateFlag[dimension] = true; data[dimension] = value; @@ -215,14 +227,14 @@ namespace XCharts private void CheckLastData() { - if (m_LastData.Count != m_Data.Count) + if (m_PreviousData.Count != m_Data.Count) { - m_LastData.Clear(); + m_PreviousData.Clear(); m_DataUpdateTime.Clear(); m_DataUpdateFlag.Clear(); for (int i = 0; i < m_Data.Count; i++) { - m_LastData.Add(m_Data[i]); + m_PreviousData.Add(m_Data[i]); m_DataUpdateTime.Add(Time.time); m_DataUpdateFlag.Add(false); } diff --git a/Runtime/Component/Sub/SerieLabel.cs b/Runtime/Component/Sub/SerieLabel.cs index dfe91c50..89e8a9a0 100644 --- a/Runtime/Component/Sub/SerieLabel.cs +++ b/Runtime/Component/Sub/SerieLabel.cs @@ -193,7 +193,7 @@ namespace XCharts public float backgroundHeight { get { return m_BackgroundHeight; } - set { if (PropertyUtility.SetStruct(ref m_BackgroundWidth, value)) SetComponentDirty(); } + set { if (PropertyUtility.SetStruct(ref m_BackgroundHeight, value)) SetComponentDirty(); } } /// /// Rotate label. @@ -202,7 +202,7 @@ namespace XCharts public float rotate { get { return m_Rotate; } - set { if (PropertyUtility.SetStruct(ref m_BackgroundWidth, value)) SetComponentDirty(); } + set { if (PropertyUtility.SetStruct(ref m_Rotate, value)) SetComponentDirty(); } } /// /// the text padding of left and right. defaut:2. @@ -211,7 +211,7 @@ namespace XCharts public float paddingLeftRight { get { return m_PaddingLeftRight; } - set { if (PropertyUtility.SetStruct(ref m_BackgroundWidth, value)) SetComponentDirty(); } + set { if (PropertyUtility.SetStruct(ref m_PaddingLeftRight, value)) SetComponentDirty(); } } /// /// the text padding of top and bottom. defaut:2. @@ -220,7 +220,7 @@ namespace XCharts public float paddingTopBottom { get { return m_PaddingTopBottom; } - set { if (PropertyUtility.SetStruct(ref m_BackgroundWidth, value)) SetComponentDirty(); } + set { if (PropertyUtility.SetStruct(ref m_PaddingTopBottom, value)) SetComponentDirty(); } } /// /// font size. @@ -229,7 +229,7 @@ namespace XCharts public int fontSize { get { return m_FontSize; } - set { if (PropertyUtility.SetStruct(ref m_BackgroundWidth, value)) SetAllDirty(); } + set { if (PropertyUtility.SetStruct(ref m_FontSize, value)) SetAllDirty(); } } /// /// font style. diff --git a/Runtime/Component/Sub/TextStyle.cs b/Runtime/Component/Sub/TextStyle.cs index 579afccc..f347feee 100644 --- a/Runtime/Component/Sub/TextStyle.cs +++ b/Runtime/Component/Sub/TextStyle.cs @@ -15,7 +15,7 @@ namespace XCharts /// 文本的相关设置。 /// [Serializable] - public class TextStyle : SubComponent, IEquatable + public class TextStyle : SubComponent { [SerializeField] private Font m_Font; [SerializeField] private float m_Rotate = 0; @@ -135,65 +135,5 @@ namespace XCharts this.color = color; this.rotate = rotate; } - - public void Copy(TextStyle style) - { - this.fontSize = style.fontSize; - this.fontStyle = style.fontStyle; - this.color = style.color; - this.backgroundColor = style.backgroundColor; - this.rotate = style.rotate; - this.offset = style.offset; - this.lineSpacing = style.lineSpacing; - } - - public TextStyle Clone() - { - var textStyle = new TextStyle(); - textStyle.rotate = rotate; - textStyle.color = color; - textStyle.backgroundColor = backgroundColor; - textStyle.fontSize = fontSize; - textStyle.fontStyle = fontStyle; - textStyle.offset = offset; - textStyle.lineSpacing = lineSpacing; - return textStyle; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - else if (obj is TextStyle) - { - return Equals((TextStyle)obj); - } - else - { - return false; - } - } - - public bool Equals(TextStyle other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - return rotate == other.rotate && - fontSize == other.fontSize && - fontStyle == other.fontStyle && - offset == other.offset && - lineSpacing == other.lineSpacing && - ChartHelper.IsValueEqualsColor(m_BackgroundColor, other.backgroundColor) && - ChartHelper.IsValueEqualsColor(m_Color, other.color); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } } } \ No newline at end of file diff --git a/Runtime/Component/Sub/TitleStyle.cs b/Runtime/Component/Sub/TitleStyle.cs index f49ff0c5..1ddca59b 100644 --- a/Runtime/Component/Sub/TitleStyle.cs +++ b/Runtime/Component/Sub/TitleStyle.cs @@ -9,6 +9,7 @@ using System; using UnityEngine; using UnityEngine.UI; +using UnityEngine.Serialization; namespace XCharts { @@ -17,62 +18,42 @@ namespace XCharts /// 标题相关设置。 /// [Serializable] - public class TitleStyle : SubComponent, IEquatable + public class TitleStyle : SubComponent { [SerializeField] private bool m_Show; - [SerializeField] private TextStyle m_textStyle = new TextStyle(18); + [FormerlySerializedAs("m_textStyle")] + [SerializeField] private TextStyle m_TextStyle = new TextStyle(18); /// /// Whether to show title. /// 是否显示标题。 /// - public bool show { get { return m_Show; } set { m_Show = value; } } + public bool show + { + get { return m_Show; } + set { if (PropertyUtility.SetStruct(ref m_Show, value)) SetComponentDirty(); } + } /// /// the color of text. /// 文本的颜色。 /// - public TextStyle textStyle { get { return m_textStyle; } set { m_textStyle = value; } } + public TextStyle textStyle + { + get { return m_TextStyle; } + set { if (PropertyUtility.SetClass(ref m_TextStyle, value, true)) SetComponentDirty(); } + } + + public override bool componentDirty { get { return m_ComponentDirty || textStyle.componentDirty; } } + + internal override void ClearComponentDirty() + { + base.ClearComponentDirty(); + textStyle.ClearComponentDirty(); + } + public Text runtimeText { get; set; } - public TitleStyle Clone() - { - var title = new TitleStyle(); - title.show = show; - title.textStyle = textStyle.Clone(); - return title; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - else if (obj is TitleStyle) - { - return Equals((TitleStyle)obj); - } - else - { - return false; - } - } - - public bool Equals(TitleStyle other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - return textStyle.Equals(other.textStyle); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - public bool IsInited() { return runtimeText != null; @@ -90,13 +71,13 @@ namespace XCharts { if (runtimeText) { - runtimeText.transform.localPosition = pos + new Vector3(m_textStyle.offset.x, m_textStyle.offset.y); + runtimeText.transform.localPosition = pos + new Vector3(m_TextStyle.offset.x, m_TextStyle.offset.y); } } public void SetText(string text) { - if (runtimeText) + if (runtimeText && !runtimeText.text.Equals(text)) { if (textStyle.color != Color.clear) runtimeText.color = textStyle.color; runtimeText.text = text; diff --git a/Runtime/GaugeChart.cs b/Runtime/GaugeChart.cs index 44756e25..2cbe73d7 100644 --- a/Runtime/GaugeChart.cs +++ b/Runtime/GaugeChart.cs @@ -32,20 +32,25 @@ namespace XCharts base.Start(); foreach (var serie in m_Series.list) { - UpdateTitle(serie); - UpdateLabel(serie); + TitleStyleHelper.CheckTitle(serie, ref m_ReinitTitle, ref m_UpdateTitleText); + SerieLabelHelper.CheckLabel(serie, ref m_ReinitLabel, ref m_UpdateLabelText); } - UpdateTitleAndLabelText(); + UpdateAxisLabel(); } protected override void Update() { base.Update(); - if (m_UpdateLabelText || m_UpdateTitleText) + if (m_UpdateTitleText) { m_UpdateTitleText = false; + TitleStyleHelper.UpdateTitleText(m_Series); + } + if (m_UpdateLabelText) + { m_UpdateLabelText = false; - UpdateTitleAndLabelText(); + SerieLabelHelper.UpdateLabelText(m_Series,m_ThemeInfo); + UpdateAxisLabel(); } } @@ -134,16 +139,14 @@ namespace XCharts serie.UpdateCenter(chartWidth, chartHeight); var destAngle = GetCurrAngle(serie, true); serie.animation.InitProgress(0, serie.startAngle, destAngle); - //var currAngle = serie.animation.GetCurrDetail(); var currAngle = serie.animation.IsFinish() ? GetCurrAngle(serie, false) : serie.animation.GetCurrDetail(); DrawProgressBar(vh, serie, currAngle); DrawStageColor(vh, serie); DrawLineStyle(vh, serie); DrawAxisTick(vh, serie); DrawPointer(vh, serie, currAngle); - UpdateTitle(serie); - // UpdateAxisLabel(serie); - UpdateLabel(serie); + TitleStyleHelper.CheckTitle(serie, ref m_ReinitTitle, ref m_UpdateTitleText); + SerieLabelHelper.CheckLabel(serie, ref m_ReinitLabel, ref m_UpdateLabelText); CheckAnimation(serie); if (!serie.animation.IsFinish()) @@ -168,15 +171,15 @@ namespace XCharts backgroundColor, m_ThemeInfo.backgroundColor, m_Settings.cicleSmoothness, serie.startAngle, serie.endAngle); if (serie.roundCap) { - DrawArcShape(vh, serie, serie.startAngle, backgroundColor, true); - DrawArcShape(vh, serie, serie.endAngle, backgroundColor); + DrawRoundCap(vh, serie, serie.startAngle, backgroundColor, true); + DrawRoundCap(vh, serie, serie.endAngle, backgroundColor); } ChartDrawer.DrawDoughnut(vh, serie.runtimeCenterPos, serie.runtimeInsideRadius, outsideRadius, color, m_ThemeInfo.backgroundColor, m_Settings.cicleSmoothness, serie.startAngle, currAngle); if (serie.roundCap && currAngle != serie.startAngle) { - DrawArcShape(vh, serie, currAngle, color); - DrawArcShape(vh, serie, serie.startAngle, color, true); + DrawRoundCap(vh, serie, currAngle, color); + DrawRoundCap(vh, serie, serie.startAngle, color, true); } } @@ -275,7 +278,7 @@ namespace XCharts return false; } - private void DrawArcShape(VertexHelper vh, Serie serie, float angle, Color color, bool invert = false) + private void DrawRoundCap(VertexHelper vh, Serie serie, float angle, Color color, bool invert = false) { var radius = serie.gaugeAxis.axisLine.width / 2; var len = serie.runtimeInsideRadius + radius; @@ -296,27 +299,6 @@ namespace XCharts } } - private void UpdateTitle(Serie serie) - { - if (serie.titleStyle.show) - { - if (serie.titleStyle.IsInited()) - { - serie.titleStyle.SetActive(true); - serie.titleStyle.UpdatePosition(serie.runtimeCenterPos); - m_UpdateTitleText = true; - } - else - { - m_ReinitTitle = true; - } - } - else - { - serie.titleStyle.SetActive(false); - } - } - private void UpdateAxisLabel(Serie serie) { var show = serie.gaugeAxis.show && serie.gaugeAxis.axisLabel.show; @@ -347,56 +329,12 @@ namespace XCharts } } - private void UpdateLabel(Serie serie) - { - var serieData = serie.GetSerieData(0); - if (serieData != null) - { - if (serie.label.show) - { - if (serieData.IsInitLabel()) - { - serieData.SetLabelActive(true); - serieData.SetLabelPosition(serie.runtimeCenterPos + serie.label.offset); - m_UpdateLabelText = true; - } - else - { - m_ReinitLabel = true; - } - } - else - { - serieData.SetLabelActive(false); - } - } - } - - private void UpdateTitleAndLabelText() + private void UpdateAxisLabel() { foreach (var serie in m_Series.list) { if (serie.type == SerieType.Gauge) { - var serieData = serie.GetSerieData(0); - if (serieData != null) - { - if (serie.label.show && serieData.IsInitLabel()) - { - var value = serieData.GetData(1); - var total = serie.max; - var content = serie.label.GetFormatterContent(serie.name, serieData.name, value, total); - serieData.SetLabelText(content); - if (serie.label.color != Color.clear) - { - serieData.SetLabelColor(serie.label.color); - } - } - if (serie.titleStyle.show && serie.titleStyle.IsInited()) - { - serie.titleStyle.SetText(serieData.name); - } - } UpdateAxisLabel(serie); } } @@ -415,6 +353,7 @@ namespace XCharts if (serie.dataCount > 0) { var serieData = serie.data[0]; + serieData.labelPosition = serie.runtimeCenterPos + serie.label.offset; value = dest ? serieData.GetData(1) : serieData.GetCurrData(1, serie.animation.GetUpdateAnimationDuration()); value = Mathf.Clamp(value, serie.min, serie.max); } diff --git a/Runtime/Helper/SerieHelper.cs b/Runtime/Helper/SerieHelper.cs new file mode 100644 index 00000000..74ef550e --- /dev/null +++ b/Runtime/Helper/SerieHelper.cs @@ -0,0 +1,50 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts +{ + internal static class SerieHelper + { + internal static Color GetItemBackgroundColor(Serie serie, ThemeInfo theme, int index, bool highlight) + { + if (serie.itemStyle.backgroundColor != Color.clear) + { + var color = serie.itemStyle.backgroundColor; + if (highlight) color *= color; + color.a *= serie.itemStyle.opacity; + return color; + } + else + { + var color = (Color)theme.GetColor(index); + if (highlight) color *= color; + color.a = 0.2f; + return color; + } + } + + internal static Color GetItemColor(Serie serie, ThemeInfo theme, int index, bool highlight) + { + if (serie.itemStyle.color != Color.clear) + { + var color = serie.itemStyle.color; + if (highlight) color *= color; + color.a *= serie.itemStyle.opacity; + return color; + } + else + { + var color = (Color)theme.GetColor(index); + if (highlight) color *= color; + color.a *= serie.itemStyle.opacity; + return color; + } + } + } +} \ No newline at end of file diff --git a/Runtime/Helper/SerieHelper.cs.meta b/Runtime/Helper/SerieHelper.cs.meta new file mode 100644 index 00000000..39d6749c --- /dev/null +++ b/Runtime/Helper/SerieHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f69933c37061c417d9cadd9e486e6785 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Helper/SerieLabelHelper.cs b/Runtime/Helper/SerieLabelHelper.cs new file mode 100644 index 00000000..1c33bbe9 --- /dev/null +++ b/Runtime/Helper/SerieLabelHelper.cs @@ -0,0 +1,128 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts +{ + internal static class SerieLabelHelper + { + public static void CheckLabel(Serie serie, ref bool m_ReinitLabel, ref bool m_UpdateLabelText) + { + switch (serie.type) + { + case SerieType.Gauge: + case SerieType.Ring: + var serieData = serie.GetSerieData(0); + if (serieData != null) + { + if (serie.label.show && serie.show) + { + if (serieData.IsInitLabel()) + { + serieData.SetLabelActive(true); + m_UpdateLabelText = true; + } + else + { + m_ReinitLabel = true; + } + } + else + { + serieData.SetLabelActive(false); + } + } + break; + } + } + + public static void UpdateLabelText(Series series, ThemeInfo themeInfo) + { + foreach (var serie in series.list) + { + if (!serie.label.show) continue; + switch (serie.type) + { + case SerieType.Gauge: + SetGaugeLabelText(serie); + break; + case SerieType.Ring: + SetRingLabelText(serie, themeInfo); + break; + } + } + } + + public static Color GetLabelColor(Serie serie, ThemeInfo themeInfo, int index) + { + if (serie.label.color != Color.clear) + { + return serie.label.color; + } + else + { + return themeInfo.GetColor(index); + } + } + + private static void SetGaugeLabelText(Serie serie) + { + var serieData = serie.GetSerieData(0); + if (serieData != null) + { + if (serieData.IsInitLabel()) + { + var value = serieData.GetData(1); + var total = serie.max; + var content = serie.label.GetFormatterContent(serie.name, serieData.name, value, total); + serieData.SetLabelText(content); + serieData.SetLabelPosition(serie.runtimeCenterPos + serie.label.offset); + if (serie.label.color != Color.clear) + { + serieData.SetLabelColor(serie.label.color); + } + } + } + } + + private static void SetRingLabelText(Serie serie, ThemeInfo themeInfo) + { + for (int i = 0; i < serie.dataCount; i++) + { + var serieData = serie.data[i]; + if (serieData.IsInitLabel()) + { + if (!serie.show || !serieData.show) + { + serieData.SetLabelActive(false); + continue; + } + var value = serieData.GetData(0); + var total = serieData.GetData(1); + var content = serie.label.GetFormatterContent(serie.name, serieData.name, value, total); + serieData.SetLabelActive(true); + serieData.SetLabelText(content); + serieData.SetLabelColor(GetLabelColor(serie, themeInfo, i)); + + if (serie.label.position == SerieLabel.Position.Bottom) + { + var labelWidth = serieData.GetLabelWidth(); + if (serie.clockwise) + serieData.SetLabelPosition(serieData.labelPosition - new Vector3(labelWidth / 2, 0)); + else + serieData.SetLabelPosition(serieData.labelPosition + new Vector3(labelWidth / 2, 0)); + } + else + { + serieData.SetLabelPosition(serieData.labelPosition); + } + } + } + } + } +} \ No newline at end of file diff --git a/Runtime/Helper/SerieLabelHelper.cs.meta b/Runtime/Helper/SerieLabelHelper.cs.meta new file mode 100644 index 00000000..0cc0269d --- /dev/null +++ b/Runtime/Helper/SerieLabelHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d74ec0ba2845e409ca2e1c85a3814939 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Helper/TitleStyleHelper.cs b/Runtime/Helper/TitleStyleHelper.cs new file mode 100644 index 00000000..fb3b43d2 --- /dev/null +++ b/Runtime/Helper/TitleStyleHelper.cs @@ -0,0 +1,43 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts +{ + internal static class TitleStyleHelper + { + public static void CheckTitle(Serie serie, ref bool m_ReinitTitle, ref bool m_UpdateTitleText) + { + if (serie.titleStyle.show) + { + if (serie.titleStyle.IsInited()) + { + serie.titleStyle.UpdatePosition(serie.runtimeCenterPos); + m_UpdateTitleText = true; + } + else + { + m_ReinitTitle = true; + } + } + } + + public static void UpdateTitleText(Series series) + { + foreach (var serie in series.list) UpdateTitleText(serie); + } + + public static void UpdateTitleText(Serie serie) + { + if (serie.titleStyle.show) + { + serie.titleStyle.SetText(serie.name); + } + } + } +} \ No newline at end of file diff --git a/Runtime/Helper/TitleStyleHelper.cs.meta b/Runtime/Helper/TitleStyleHelper.cs.meta new file mode 100644 index 00000000..afee7009 --- /dev/null +++ b/Runtime/Helper/TitleStyleHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76af3a33fe0b643e4ac6e875864233c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Internal/APIException.cs b/Runtime/Internal/APIException.cs new file mode 100644 index 00000000..30ccb415 --- /dev/null +++ b/Runtime/Internal/APIException.cs @@ -0,0 +1,20 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ + + +namespace XCharts +{ + public class APIException : System.Exception + { + public APIException() { } + public APIException(string message) : base(message) { } + public APIException(string message, System.Exception inner) : base(message, inner) { } + protected APIException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/Runtime/Internal/APIException.cs.meta b/Runtime/Internal/APIException.cs.meta new file mode 100644 index 00000000..da90be97 --- /dev/null +++ b/Runtime/Internal/APIException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62abeafe2ed4141d69bdb9ad76c02e41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Internal/BaseChart.cs b/Runtime/Internal/BaseChart.cs index f2747727..94a46e62 100644 --- a/Runtime/Internal/BaseChart.cs +++ b/Runtime/Internal/BaseChart.cs @@ -163,6 +163,10 @@ namespace XCharts if (m_Series.vertsDirty) RefreshChart(); if (m_Series.labelDirty) m_ReinitLabel = true; if (m_Series.labelUpdate && !m_RefreshChart) m_RefreshLabel = true; + foreach (var serie in m_Series.list) + { + if (serie.titleStyle.componentDirty) m_ReinitTitle = true; + } m_Series.ClearDirty(); } } @@ -203,6 +207,7 @@ namespace XCharts m_Legend.SetAllDirty(); m_Tooltip.SetAllDirty(); m_ReinitLabel = true; + m_ReinitTitle = true; } #endif @@ -388,10 +393,10 @@ namespace XCharts var isAutoSize = serieLabel.backgroundWidth == 0 || serieLabel.backgroundHeight == 0; serieData.InitLabel(labelObj, isAutoSize, serieLabel.paddingLeftRight, serieLabel.paddingTopBottom); serieData.SetLabelActive(false); - //serieData.SetLabelText(serieData.name); count++; } } + SerieLabelHelper.UpdateLabelText(m_Series,m_ThemeInfo); } private void InitSerieTitle() @@ -415,8 +420,9 @@ namespace XCharts txt.text = ""; txt.transform.localPosition = new Vector2(0, 0); txt.transform.localEulerAngles = Vector2.zero; - ChartHelper.SetActive(txt, false); + ChartHelper.SetActive(txt, serie.titleStyle.show); serie.titleStyle.runtimeText = txt; + serie.titleStyle.UpdatePosition(serie.runtimeCenterPos); var serieData = serie.GetSerieData(0); if (serieData != null) { diff --git a/Runtime/PieChart.cs b/Runtime/PieChart.cs index 086969b9..feb285d4 100644 --- a/Runtime/PieChart.cs +++ b/Runtime/PieChart.cs @@ -123,7 +123,7 @@ namespace XCharts serieData.runtimePieCurrAngle = serieData.runtimePieToAngle; serieData.runtiemPieOffsetCenter = center; serieData.runtimePieInsideRadius = serie.runtimeInsideRadius; - if (serie.animation.CheckDetailBreak(n, serieData.runtimePieToAngle)) + if (serie.animation.CheckDetailBreak(serieData.runtimePieToAngle)) { isFinish = false; serieData.runtimePieCurrAngle = serie.animation.GetCurrDetail(); @@ -144,7 +144,7 @@ namespace XCharts center.y + serieData.runtimePieOffsetRadius * currCos); var drawStartDegree = startDegree + serie.pieSpace; var drawEndDegree = serieData.runtimePieCurrAngle - serie.pieSpace; - DrawArcShape(vh, serie, serieData, serieData.runtiemPieOffsetCenter, color, ref drawStartDegree, ref drawEndDegree); + DrawRoundCap(vh, serie, serieData, serieData.runtiemPieOffsetCenter, color, ref drawStartDegree, ref drawEndDegree); ChartDrawer.DrawDoughnut(vh, serieData.runtiemPieOffsetCenter, serieData.runtimePieInsideRadius, serieData.runtimePieOutsideRadius, color, m_ThemeInfo.backgroundColor, m_Settings.cicleSmoothness, drawStartDegree, drawEndDegree); } @@ -152,7 +152,7 @@ namespace XCharts { var drawStartDegree = startDegree + serie.pieSpace; var drawEndDegree = serieData.runtimePieCurrAngle - serie.pieSpace; - DrawArcShape(vh, serie, serieData, center, color, ref drawStartDegree, ref drawEndDegree); + DrawRoundCap(vh, serie, serieData, center, color, ref drawStartDegree, ref drawEndDegree); ChartDrawer.DrawDoughnut(vh, center, serieData.runtimePieInsideRadius, serieData.runtimePieOutsideRadius, color, m_ThemeInfo.backgroundColor, m_Settings.cicleSmoothness, drawStartDegree, drawEndDegree); } @@ -178,7 +178,7 @@ namespace XCharts raycastTarget = isClickOffset && isDataHighlight; } - private void DrawArcShape(VertexHelper vh, Serie serie, SerieData serieData, Vector3 centerPos, + private void DrawRoundCap(VertexHelper vh, Serie serie, SerieData serieData, Vector3 centerPos, Color color, ref float drawStartDegree, ref float drawEndDegree) { if (serie.roundCap && serieData.runtimePieInsideRadius > 0) @@ -189,14 +189,8 @@ namespace XCharts drawStartDegree += diffDegree; drawEndDegree -= diffDegree; - var px = Mathf.Sin(drawStartDegree * Mathf.Deg2Rad) * radius; - var py = Mathf.Cos(drawStartDegree * Mathf.Deg2Rad) * radius; - var pos = new Vector3(px, py) + centerPos; - ChartDrawer.DrawSector(vh, pos, width, color, drawStartDegree + 180, drawStartDegree + 360); - px = Mathf.Sin(drawEndDegree * Mathf.Deg2Rad) * radius; - py = Mathf.Cos(drawEndDegree * Mathf.Deg2Rad) * radius; - pos = new Vector3(px, py) + centerPos; - ChartDrawer.DrawSector(vh, pos, width, color, drawEndDegree, drawEndDegree + 180); + ChartDrawer.DrawRoundCap(vh, centerPos, width, radius, drawStartDegree, serie.clockwise, color, false); + ChartDrawer.DrawRoundCap(vh, centerPos, width, radius, drawEndDegree, serie.clockwise, color, true); } } diff --git a/Runtime/RingChart.cs b/Runtime/RingChart.cs new file mode 100644 index 00000000..fdab9daa --- /dev/null +++ b/Runtime/RingChart.cs @@ -0,0 +1,364 @@ + +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ + +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts +{ + [AddComponentMenu("XCharts/RingChart", 20)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + public partial class RingChart : BaseChart + { + private bool m_UpdateTitleText = false; + private bool m_UpdateLabelText = false; + private bool m_IsEnterLegendButtom; + + protected override void Update() + { + base.Update(); + if (m_UpdateTitleText) + { + m_UpdateTitleText = false; + TitleStyleHelper.UpdateTitleText(m_Series); + } + if (m_UpdateLabelText) + { + m_UpdateLabelText = false; + SerieLabelHelper.UpdateLabelText(m_Series, m_ThemeInfo); + } + } + +#if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + m_Title.text = "RingChart"; + m_Tooltip.type = Tooltip.Type.Line; + RemoveData(); + var serie = AddSerie(SerieType.Ring, "serie1"); + serie.roundCap = true; + serie.radius = new float[] { 0.3f, 0.35f }; + serie.titleStyle.show = false; + serie.titleStyle.textStyle.offset = new Vector2(0, 30); + serie.label.show = true; + serie.label.position = SerieLabel.Position.Center; + serie.label.formatter = "{d:f0}%"; + serie.label.fontSize = 28; + var value = Random.Range(30, 90); + var max = 100; + AddData(0, value, max, "data1"); + } +#endif + + protected override void DrawChart(VertexHelper vh) + { + base.DrawChart(vh); + for (int i = 0; i < m_Series.list.Count; i++) + { + var serie = m_Series.list[i]; + var data = serie.data; + serie.index = i; + if (!serie.show || serie.type != SerieType.Ring || serie.animation.HasFadeOut()) + { + continue; + } + serie.animation.InitProgress(data.Count, serie.startAngle, serie.startAngle + 360); + serie.UpdateCenter(chartWidth, chartHeight); + TitleStyleHelper.CheckTitle(serie, ref m_ReinitTitle, ref m_UpdateTitleText); + SerieLabelHelper.CheckLabel(serie, ref m_ReinitLabel, ref m_UpdateLabelText); + var dataChangeDuration = serie.animation.GetUpdateAnimationDuration(); + var ringWidth = serie.runtimeOutsideRadius - serie.runtimeInsideRadius; + 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 value = serieData.GetFirstData(dataChangeDuration); + var max = serieData.GetLastData(); + var degree = 360 * value / max; + var startDegree = GetStartAngle(serie); + var toDegree = GetToAngle(serie, degree); + var itemColor = SerieHelper.GetItemColor(serie, m_ThemeInfo, j, serieData.highlighted); + var outsideRadius = serie.runtimeOutsideRadius - j * (ringWidth + serie.ringGap); + var insideRadius = outsideRadius - ringWidth; + var centerRadius = (outsideRadius + insideRadius) / 2; + + serieData.runtimePieStartAngle = serie.clockwise ? startDegree : toDegree; + serieData.runtimePieToAngle = serie.clockwise ? toDegree : startDegree; + serieData.runtimePieInsideRadius = insideRadius; + serieData.runtimePieOutsideRadius = outsideRadius; + + DrawBackground(vh, serie, j, insideRadius, outsideRadius); + DrawRoundCap(vh, serie, serie.runtimeCenterPos, itemColor, insideRadius, outsideRadius, + ref startDegree, ref toDegree); + ChartDrawer.DrawDoughnut(vh, serie.runtimeCenterPos, insideRadius, + outsideRadius, itemColor, Color.clear, m_Settings.cicleSmoothness, + startDegree, toDegree); + DrawBorder(vh, serie, insideRadius, outsideRadius); + DrawCenter(vh, serie, insideRadius, j == data.Count - 1); + UpateLabelPosition(serie, serieData, j, startDegree, toDegree, centerRadius); + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(360); + serie.animation.CheckSymbol(serie.symbol.size); + RefreshChart(); + } + if (dataChanging) + { + RefreshChart(); + } + } + } + + 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 - toAngle - 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, float insideRadius, bool last) + { + if (serie.itemStyle.centerColor != Color.clear && last) + { + var radius = insideRadius - serie.itemStyle.centerGap; + ChartDrawer.DrawCricle(vh, serie.runtimeCenterPos, radius, serie.itemStyle.centerColor); + } + } + + private void UpateLabelPosition(Serie serie, SerieData serieData, int index, float startAngle, + float toAngle, float centerRadius) + { + if (!serie.label.show) return; + switch (serie.label.position) + { + case SerieLabel.Position.Center: + serieData.labelPosition = serie.runtimeCenterPos + serie.label.offset; + break; + case SerieLabel.Position.Bottom: + var px1 = Mathf.Sin(startAngle * Mathf.Deg2Rad) * centerRadius; + var py1 = Mathf.Cos(startAngle * Mathf.Deg2Rad) * centerRadius; + var xDiff = serie.clockwise ? -serie.label.margin : serie.label.margin; + serieData.labelPosition = serie.runtimeCenterPos + new Vector3(px1 + xDiff, py1); + break; + case SerieLabel.Position.Top: + startAngle += serie.clockwise ? -serie.label.margin : serie.label.margin; + toAngle += serie.clockwise ? serie.label.margin : -serie.label.margin; + var px2 = Mathf.Sin(toAngle * Mathf.Deg2Rad) * centerRadius; + var py2 = Mathf.Cos(toAngle * Mathf.Deg2Rad) * centerRadius; + serieData.labelPosition = serie.runtimeCenterPos + new Vector3(px2, py2); + break; + } + } + + private void DrawBackground(VertexHelper vh, Serie serie, int index, float insideRadius, float outsideRadius) + { + var backgroundColor = SerieHelper.GetItemBackgroundColor(serie, m_ThemeInfo, index, false); + if (serie.itemStyle.backgroundWidth != 0) + { + var centerRadius = (outsideRadius + insideRadius) / 2; + var inradius = centerRadius - serie.itemStyle.backgroundWidth / 2; + var outradius = centerRadius + serie.itemStyle.backgroundWidth / 2; + ChartDrawer.DrawDoughnut(vh, serie.runtimeCenterPos, inradius, + outradius, backgroundColor, Color.clear, m_Settings.cicleSmoothness); + } + else + { + ChartDrawer.DrawDoughnut(vh, serie.runtimeCenterPos, insideRadius, + outsideRadius, backgroundColor, Color.clear, m_Settings.cicleSmoothness); + } + } + + private void DrawBorder(VertexHelper vh, Serie serie, float insideRadius, float outsideRadius) + { + if (serie.itemStyle.show && serie.itemStyle.borderWidth > 0 && serie.itemStyle.borderColor != Color.clear) + { + ChartDrawer.DrawDoughnut(vh, serie.runtimeCenterPos, outsideRadius, + outsideRadius + serie.itemStyle.borderWidth, serie.itemStyle.borderColor, + Color.clear, m_Settings.cicleSmoothness); + ChartDrawer.DrawDoughnut(vh, serie.runtimeCenterPos, insideRadius, + insideRadius + serie.itemStyle.borderWidth, serie.itemStyle.borderColor, + Color.clear, m_Settings.cicleSmoothness); + } + } + + + private void DrawRoundCap(VertexHelper vh, Serie serie, Vector3 centerPos, Color color, + float insideRadius, float outsideRadius, ref float drawStartDegree, ref float drawEndDegree) + { + if (serie.roundCap && insideRadius > 0 && drawStartDegree != drawEndDegree) + { + var width = (outsideRadius - insideRadius) / 2; + var radius = insideRadius + width; + + var diffDegree = Mathf.Asin(width / radius) * Mathf.Rad2Deg; + drawStartDegree += serie.clockwise ? diffDegree : -diffDegree; + drawEndDegree -= serie.clockwise ? diffDegree : -diffDegree; + ChartDrawer.DrawRoundCap(vh, centerPos, width, radius, drawStartDegree, serie.clockwise, color, false); + ChartDrawer.DrawRoundCap(vh, centerPos, width, radius, drawEndDegree, serie.clockwise, color, true); + } + } + + protected override void OnLegendButtonClick(int index, string legendName, bool show) + { + CheckDataShow(legendName, show); + UpdateLegendColor(legendName, show); + RefreshChart(); + } + + protected override void OnLegendButtonEnter(int index, string legendName) + { + m_IsEnterLegendButtom = true; + CheckDataHighlighted(legendName, true); + RefreshChart(); + } + + protected override void OnLegendButtonExit(int index, string legendName) + { + m_IsEnterLegendButtom = false; + CheckDataHighlighted(legendName, false); + RefreshChart(); + } + + protected override void CheckTootipArea(Vector2 local) + { + if (m_IsEnterLegendButtom) return; + m_Tooltip.runtimeDataIndex.Clear(); + bool selected = false; + foreach (var serie in m_Series.list) + { + int index = GetRingIndex(serie, local); + m_Tooltip.runtimeDataIndex.Add(index); + if (serie.type != SerieType.Ring) continue; + bool refresh = false; + for (int j = 0; j < serie.data.Count; j++) + { + var serieData = serie.data[j]; + if (serieData.highlighted != (j == index)) refresh = true; + serieData.highlighted = j == index; + } + if (index >= 0) selected = true; + if (refresh) RefreshChart(); + } + if (selected) + { + m_Tooltip.UpdateContentPos(new Vector2(local.x + 18, local.y - 25)); + UpdateTooltip(); + } + else if (m_Tooltip.IsActive()) + { + m_Tooltip.SetActive(false); + RefreshChart(); + } + } + + private int GetRingIndex(Serie serie, Vector2 local) + { + if (serie.type != SerieType.Ring) return -1; + var dist = Vector2.Distance(local, serie.runtimeCenterPos); + if (dist > serie.runtimeOutsideRadius) return -1; + Vector2 dir = local - new Vector2(serie.runtimeCenterPos.x, serie.runtimeCenterPos.y); + float angle = VectorAngle(Vector2.up, dir); + for (int i = 0; i < serie.data.Count; i++) + { + var serieData = serie.data[i]; + if (dist >= serieData.runtimePieInsideRadius && + dist <= serieData.runtimePieOutsideRadius && + angle >= serieData.runtimePieStartAngle && + angle <= serieData.runtimePieToAngle) + { + return i; + } + } + return -1; + } + + 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; + } + + StringBuilder sb = new StringBuilder(); + protected override void UpdateTooltip() + { + base.UpdateTooltip(); + bool showTooltip = false; + foreach (var serie in m_Series.list) + { + int index = m_Tooltip.runtimeDataIndex[serie.index]; + if (index < 0) continue; + showTooltip = true; + if (tooltip.IsNoFormatter()) + { + var serieData = serie.GetSerieData(index); + float value = serieData.GetFirstData(); + sb.Length = 0; + if (!string.IsNullOrEmpty(serieData.name)) + { + sb.Append("● ") + .Append(serieData.name).Append(": ").Append(ChartCached.FloatToStr(value, 0, m_Tooltip.forceENotation)); + } + else + { + sb.Append(ChartCached.FloatToStr(value, 0, m_Tooltip.forceENotation)); + } + m_Tooltip.UpdateContentText(sb.ToString()); + } + else + { + m_Tooltip.UpdateContentText(m_Tooltip.GetFormatterContent(index, m_Series, null, m_ThemeInfo)); + } + + var pos = m_Tooltip.GetContentPos(); + if (pos.x + m_Tooltip.runtimeWidth > chartWidth) + { + pos.x = chartWidth - m_Tooltip.runtimeWidth; + } + if (pos.y - m_Tooltip.runtimeHeight < 0) + { + pos.y = m_Tooltip.runtimeHeight; + } + m_Tooltip.UpdateContentPos(pos); + } + m_Tooltip.SetActive(showTooltip); + } + } +} diff --git a/Runtime/RingChart.cs.meta b/Runtime/RingChart.cs.meta new file mode 100644 index 00000000..a46c903b --- /dev/null +++ b/Runtime/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/Runtime/Utility/ChartDrawer.cs b/Runtime/Utility/ChartDrawer.cs index 2d823bb5..3c7f1e3f 100644 --- a/Runtime/Utility/ChartDrawer.cs +++ b/Runtime/Utility/ChartDrawer.cs @@ -335,6 +335,28 @@ namespace XCharts } } + public static void DrawRoundCap(VertexHelper vh, Vector3 center, float width, float radius, float angle, + bool clockwise, Color 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) + ChartDrawer.DrawSector(vh, pos, width, color, angle, angle + 180); + else + ChartDrawer.DrawSector(vh, pos, width, color, angle, angle - 180); + } + else + { + if (clockwise) + ChartDrawer.DrawSector(vh, pos, width, color, angle + 180, angle + 360); + else + ChartDrawer.DrawSector(vh, pos, width, color, angle - 180, angle - 360); + } + } + public static void DrawDoughnut(VertexHelper vh, Vector3 p, float insideRadius, float outsideRadius, Color32 color, Color emptyColor, float smoothness = 2f, float startDegree = 0, float toDegree = 360) { diff --git a/Runtime/Utility/XChartsMgr.cs b/Runtime/Utility/XChartsMgr.cs index e7044a59..ec7c2674 100644 --- a/Runtime/Utility/XChartsMgr.cs +++ b/Runtime/Utility/XChartsMgr.cs @@ -25,7 +25,7 @@ namespace XCharts public class XChartsMgr : MonoBehaviour { public const string version = "1.2.0"; - public const int date = 20200226; + public const int date = 20200308; [SerializeField] private string m_NowVersion; [SerializeField] private string m_NewVersion;