From 8d6d4ce0c542d63578ebec82178dc9f0975aaea0 Mon Sep 17 00:00:00 2001 From: monitor1394 Date: Thu, 15 Jul 2021 21:18:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0`MarkLine`=E6=A0=87=E7=BA=BF?= =?UTF-8?q?=20(#142)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/XCharts/CHANGELOG.md | 1 + .../Documentation/XCharts配置项手册.md | 22 + .../Documentation/xcharts-configuration-EN.md | 22 + .../Editor/PropertyDrawers/MarkLineDrawer.cs | 69 ++ .../PropertyDrawers/MarkLineDrawer.cs.meta | 11 + .../Editor/PropertyDrawers/SerieDrawer.cs | 2 + Assets/XCharts/Runtime/API/BaseChart_API.cs | 17 +- .../XCharts/Runtime/Component/Main/Serie.cs | 83 ++- .../XCharts/Runtime/Component/Main/Series.cs | 79 +-- .../XCharts/Runtime/Component/Main/XGrid.cs | 2 + .../XCharts/Runtime/Component/Sub/MarkLine.cs | 664 ++++++++++++++++++ .../Runtime/Component/Sub/MarkLine.cs.meta | 11 + .../Runtime/Component/Sub/SerieLabel.cs | 15 + .../Runtime/Component/Sub/SerieSymbol.cs | 4 + .../XCharts/Runtime/Helper/FormatterHelper.cs | 4 +- Assets/XCharts/Runtime/Helper/SerieHelper.cs | 105 +++ Assets/XCharts/Runtime/Internal/BaseChart.cs | 15 +- .../Runtime/Internal/CoordinateChart.cs | 8 +- .../Internal/CoordinateChart_DrawLine.cs | 2 +- .../Runtime/Internal/Utility/ChartDrawer.cs | 11 +- 20 files changed, 1050 insertions(+), 97 deletions(-) create mode 100644 Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs create mode 100644 Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs.meta create mode 100644 Assets/XCharts/Runtime/Component/Sub/MarkLine.cs create mode 100644 Assets/XCharts/Runtime/Component/Sub/MarkLine.cs.meta diff --git a/Assets/XCharts/CHANGELOG.md b/Assets/XCharts/CHANGELOG.md index e8168198..174bbf33 100644 --- a/Assets/XCharts/CHANGELOG.md +++ b/Assets/XCharts/CHANGELOG.md @@ -38,6 +38,7 @@ ## master +* (2021.07.09) 增加`MarkLine`标线 (#142) * (2021.07.09) 优化`BarChart`可通过`serieData.show`设置是否显示柱条 * (2021.07.08) 优化数据存储类型由`float`全部转为`double` * (2021.07.05) 修复`PieChart`的`avoidLabelOverlap`参数不生效的问题 diff --git a/Assets/XCharts/Documentation/XCharts配置项手册.md b/Assets/XCharts/Documentation/XCharts配置项手册.md index 37508bf9..c9038ecc 100644 --- a/Assets/XCharts/Documentation/XCharts配置项手册.md +++ b/Assets/XCharts/Documentation/XCharts配置项手册.md @@ -45,6 +45,7 @@ * [LineArrow 折线图箭头](#LineArrow) * [LineStyle 折线图样式](#LineStyle) * [Location 位置](#Location) +* [MarkLine 标线](#MarkLine) * [SerieAnimation 动画](#SerieAnimation) * [SerieData 数据项](#SerieData) * [SerieLabel 图形上的文本标签](#SerieLabel) @@ -951,6 +952,27 @@ K线图系列。 * `top`:离容器上侧的距离。 * `bottom`:离容器下侧的距离。 +## `MarkLine` + +* `show`:是否显示标线。 +* `animation`:标线的动画样式。 +* `data`:标线的数据项[MarkLineData](#MarkLineData)列表。当数据项的group为0时,每个数据项表示一条标线;当group不为0时,相同group的两个数据项分别表示标线的起始点和终止点来组成一条标线,此时标线的相关样式参数取起始点的参数。 + +## `MarkLineData` + +* `name`:标注名称,将会作为文字显示。label的formatter可通过{b}显示名称,通过{c}显示数值。 +* `type`:特殊的标注类型,用于标注最大值最小值等。。有以下标注类型: + * `None`:无类型。此时通过 + * `Min`:最小值。`dimension`维度上数据的最小值。 + * `Max`:最大值。`dimension`维度上数据的最大值。 + * `Average`:平均值。`dimension`维度上数据的平均值。 + * `Median`:中位数。`dimension`维度上数据的中位数。 +* `dimension`:当type为特殊类型时,指示从哪个维度的数据上计算特殊值。 +* `xPosition`:相对原点的 x 坐标,单位像素。当type为None时有效。 +* `yPosition`:相对原点的 y 坐标,单位像素。当type为None时有效。 +* `xValue`:X轴上的指定值。当X轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 +* `yValue`:Y轴上的指定值。当Y轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 + ## `SerieData` * `name`:数据项名称。 diff --git a/Assets/XCharts/Documentation/xcharts-configuration-EN.md b/Assets/XCharts/Documentation/xcharts-configuration-EN.md index 1d6db659..7f634ded 100644 --- a/Assets/XCharts/Documentation/xcharts-configuration-EN.md +++ b/Assets/XCharts/Documentation/xcharts-configuration-EN.md @@ -48,6 +48,7 @@ __Sub component:__ * [LineArrow](#LineArrow) * [LineStyle](#LineStyle) * [Location](#Location) +* [MarkLine](#MarkLine) * [SerieAnimation](#SerieAnimation) * [SerieData](#SerieData) * [SerieLabel](#SerieLabel) @@ -845,6 +846,27 @@ K线图系列。 * `top`: 离容器上侧的距离。 * `bottom`: 离容器下侧的距离。 +## `MarkLine` + +* `show`:是否显示标线。 +* `animation`:标线的动画样式。 +* `data`:标线的数据项[MarkLineData](#MarkLineData)列表。当数据项的group为0时,每个数据项表示一条标线;当group不为0时,相同group的两个数据项分别表示标线的起始点和终止点来组成一条标线,此时标线的相关样式参数取起始点的参数。 + +## `MarkLineData` + +* `name`:标注名称,将会作为文字显示。label的formatter可通过{b}显示名称,通过{c}显示数值。 +* `type`:特殊的标注类型,用于标注最大值最小值等。。有以下标注类型: + * `None`:无类型。此时通过 + * `Min`:最小值。`dimension`维度上数据的最小值。 + * `Max`:最大值。`dimension`维度上数据的最大值。 + * `Average`:平均值。`dimension`维度上数据的平均值。 + * `Median`:中位数。`dimension`维度上数据的中位数。 +* `dimension`:当type为特殊类型时,指示从哪个维度的数据上计算特殊值。 +* `xPosition`:相对原点的 x 坐标,单位像素。当type为None时有效。 +* `yPosition`:相对原点的 y 坐标,单位像素。当type为None时有效。 +* `xValue`:X轴上的指定值。当X轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 +* `yValue`:Y轴上的指定值。当Y轴为类目轴时指定值表示类目轴数据的索引,否则为具体的值。当type为None时有效。 + ## `SerieData` * `name`: 数据项名称。 diff --git a/Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs b/Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs new file mode 100644 index 00000000..38ff32a5 --- /dev/null +++ b/Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs @@ -0,0 +1,69 @@ +/************************************************/ +/* */ +/* Copyright (c) 2018 - 2021 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/************************************************/ + +using UnityEditor; +using UnityEngine; + +namespace XCharts +{ + [CustomPropertyDrawer(typeof(MarkLine), true)] + public class MarkLineDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "MarkLine"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeFoldout(prop, "m_Show")) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Animation"); + PropertyListField(prop, "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 (MakeFoldout(prop, "")) + { + ++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/PropertyDrawers/MarkLineDrawer.cs.meta b/Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs.meta new file mode 100644 index 00000000..e633f39a --- /dev/null +++ b/Assets/XCharts/Editor/PropertyDrawers/MarkLineDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9c530aeba79d40a8918424df421d081 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Editor/PropertyDrawers/SerieDrawer.cs b/Assets/XCharts/Editor/PropertyDrawers/SerieDrawer.cs index 9fdf62e4..14d7aa85 100644 --- a/Assets/XCharts/Editor/PropertyDrawers/SerieDrawer.cs +++ b/Assets/XCharts/Editor/PropertyDrawers/SerieDrawer.cs @@ -75,6 +75,7 @@ namespace XCharts PropertyField(prop, "m_LineStyle"); PropertyField(prop, "m_LineArrow"); PropertyField(prop, "m_AreaStyle"); + PropertyField(prop, "m_MarkLine"); break; case SerieType.Bar: PropertyField(prop, "m_Stack"); @@ -102,6 +103,7 @@ namespace XCharts PropertyField(prop, "m_ShowAsPositiveNumber"); PropertyField(prop, "m_Large"); PropertyField(prop, "m_LargeThreshold"); + PropertyField(prop, "m_MarkLine"); break; case SerieType.Pie: PropertyField(prop, "m_RoseType"); diff --git a/Assets/XCharts/Runtime/API/BaseChart_API.cs b/Assets/XCharts/Runtime/API/BaseChart_API.cs index f5f7007d..8303b505 100644 --- a/Assets/XCharts/Runtime/API/BaseChart_API.cs +++ b/Assets/XCharts/Runtime/API/BaseChart_API.cs @@ -652,7 +652,7 @@ namespace XCharts /// public void AnimationEnable(bool flag) { - m_Series.AnimationEnable(flag); + foreach (var serie in m_Series.list) serie.AnimationEnable(flag); } /// @@ -661,8 +661,7 @@ namespace XCharts /// public void AnimationFadeIn() { - m_Series.AnimationFadeIn(); - RefreshChart(); + foreach (var serie in m_Series.list) serie.AnimationFadeIn(); } /// @@ -671,8 +670,7 @@ namespace XCharts /// public void AnimationFadeOut() { - m_Series.AnimationFadeOut(); - RefreshChart(); + foreach (var serie in m_Series.list) serie.AnimationFadeOut(); } /// @@ -681,8 +679,7 @@ namespace XCharts /// public void AnimationPause() { - m_Series.AnimationPause(); - RefreshChart(); + foreach (var serie in m_Series.list) serie.AnimationPause(); } /// @@ -691,8 +688,7 @@ namespace XCharts /// public void AnimationResume() { - m_Series.AnimationResume(); - RefreshChart(); + foreach (var serie in m_Series.list) serie.AnimationResume(); } /// @@ -701,8 +697,7 @@ namespace XCharts /// public void AnimationReset() { - m_Series.AnimationReset(); - RefreshChart(); + foreach (var serie in m_Series.list) serie.AnimationReset(); } /// diff --git a/Assets/XCharts/Runtime/Component/Main/Serie.cs b/Assets/XCharts/Runtime/Component/Main/Serie.cs index 78a93af3..0835f782 100644 --- a/Assets/XCharts/Runtime/Component/Main/Serie.cs +++ b/Assets/XCharts/Runtime/Component/Main/Serie.cs @@ -319,6 +319,7 @@ namespace XCharts [SerializeField] private ItemStyle m_ItemStyle = new ItemStyle(); [SerializeField] private Emphasis m_Emphasis = new Emphasis(); [SerializeField] private TitleStyle m_TitleStyle = new TitleStyle(); + [SerializeField] private MarkLine m_MarkLine = MarkLine.defaultMarkLine; [SerializeField] [Range(1, 10)] private int m_ShowDataDimension; [SerializeField] private bool m_ShowDataName; [SerializeField] private bool m_ShowDataIcon; @@ -853,6 +854,14 @@ namespace XCharts set { if (PropertyUtil.SetClass(ref m_TitleStyle, value, true)) SetAllDirty(); } } /// + /// 标线。 + /// + public MarkLine markLine + { + get { return m_MarkLine; } + set { if (PropertyUtil.SetClass(ref m_MarkLine, value, true)) SetAllDirty(); } + } + /// /// 数据项里的数据维数。 /// public int showDataDimension { get { return m_ShowDataDimension; } set { m_ShowDataDimension = value; } } @@ -1068,7 +1077,8 @@ namespace XCharts label.vertsDirty || emphasis.vertsDirty || gaugeAxis.vertsDirty || - gaugePointer.vertsDirty; + gaugePointer.vertsDirty || + markLine.vertsDirty; } } @@ -1086,6 +1096,7 @@ namespace XCharts gaugeAxis.ClearVerticesDirty(); gaugePointer.ClearVerticesDirty(); titleStyle.ClearVerticesDirty(); + markLine.ClearVerticesDirty(); } public override void ClearComponentDirty() @@ -1101,6 +1112,7 @@ namespace XCharts gaugeAxis.ClearComponentDirty(); gaugePointer.ClearComponentDirty(); titleStyle.ClearComponentDirty(); + markLine.ClearComponentDirty(); } /// @@ -1911,6 +1923,75 @@ namespace XCharts } } + /// + /// 启用或取消初始动画 + /// + public void AnimationEnable(bool flag) + { + if (animation.enable) animation.enable = flag; + if (markLine.show && markLine.animation.enable) markLine.animation.enable = flag; + SetVerticesDirty(); + } + + /// + /// 渐入动画 + /// + public void AnimationFadeIn() + { + if (animation.enable) animation.FadeIn(); + if (markLine.show && markLine.animation.enable) markLine.animation.FadeIn(); + SetVerticesDirty(); + } + + /// + /// 渐出动画 + /// + public void AnimationFadeOut() + { + if (animation.enable) animation.FadeOut(); + if (markLine.show && markLine.animation.enable) markLine.animation.FadeOut(); + SetVerticesDirty(); + } + + /// + /// 暂停动画 + /// + public void AnimationPause() + { + if (animation.enable) animation.Pause(); + if (markLine.show && markLine.animation.enable) markLine.animation.Pause(); + SetVerticesDirty(); + } + + /// + /// 继续动画 + /// + public void AnimationResume() + { + if (animation.enable) animation.Resume(); + if (markLine.show && markLine.animation.enable) markLine.animation.Resume(); + SetVerticesDirty(); + } + + /// + /// 重置动画 + /// + public void AnimationReset() + { + if (animation.enable) animation.Reset(); + if (markLine.show && markLine.animation.enable) markLine.animation.Reset(); + SetVerticesDirty(); + } + /// + /// 重置动画 + /// + public void AnimationRestart() + { + if (animation.enable) animation.Restart(); + if (markLine.show && markLine.animation.enable) markLine.animation.Restart(); + SetVerticesDirty(); + } + /// /// 从json中导入数据 /// diff --git a/Assets/XCharts/Runtime/Component/Main/Series.cs b/Assets/XCharts/Runtime/Component/Main/Series.cs index 2a90febb..d7bba41e 100644 --- a/Assets/XCharts/Runtime/Component/Main/Series.cs +++ b/Assets/XCharts/Runtime/Component/Main/Series.cs @@ -103,9 +103,9 @@ namespace XCharts /// public void ClearData() { - AnimationFadeIn(); foreach (var serie in m_Series) { + serie.AnimationFadeIn(); serie.ClearData(); } } @@ -259,7 +259,7 @@ namespace XCharts /// public void RemoveAll() { - AnimationFadeIn(); + foreach(var serie in m_Series) serie.AnimationFadeIn(); m_Series.Clear(); } @@ -302,7 +302,7 @@ namespace XCharts { serie.symbol.show = false; } - serie.animation.Restart(); + serie.AnimationRestart(); if (addToHead) m_Series.Insert(0, serie); else if (index >= 0) m_Series.Insert(index, serie); else m_Series.Add(serie); @@ -662,8 +662,8 @@ namespace XCharts if (serie != null) { serie.show = active; - serie.animation.Reset(); - if (active) serie.animation.FadeIn(); + serie.AnimationReset(); + if (active) serie.AnimationFadeIn(); } } @@ -692,74 +692,5 @@ namespace XCharts serie.symbol.selectedSizeCallback = selectedSize; } } - - /// - /// 启用或取消初始动画 - /// - public void AnimationEnable(bool flag) - { - foreach (var serie in m_Series) - { - serie.animation.enable = flag; - } - } - - /// - /// 渐入动画 - /// - public void AnimationFadeIn() - { - foreach (var serie in m_Series) - { - if (serie.animation.enable) - { - serie.animation.FadeIn(); - } - } - } - - /// - /// 渐出动画 - /// - public void AnimationFadeOut() - { - foreach (var serie in m_Series) - { - if (serie.animation.enable) serie.animation.FadeOut(); - } - } - - /// - /// 暂停动画 - /// - public void AnimationPause() - { - foreach (var serie in m_Series) - { - if (serie.animation.enable) serie.animation.Pause(); - } - } - - /// - /// 继续动画 - /// - public void AnimationResume() - { - foreach (var serie in m_Series) - { - if (serie.animation.enable) serie.animation.Resume(); - } - } - - /// - /// 重置动画 - /// - public void AnimationReset() - { - foreach (var serie in m_Series) - { - if (serie.animation.enable) serie.animation.Reset(); - } - } } } diff --git a/Assets/XCharts/Runtime/Component/Main/XGrid.cs b/Assets/XCharts/Runtime/Component/Main/XGrid.cs index 46e57df9..fb96ef3e 100644 --- a/Assets/XCharts/Runtime/Component/Main/XGrid.cs +++ b/Assets/XCharts/Runtime/Component/Main/XGrid.cs @@ -90,6 +90,7 @@ namespace XCharts public float runtimeY { get; private set; } public float runtimeWidth { get; private set; } public float runtimeHeight { get; private set; } + public Vector3 runtimePosition { get; private set; } internal void UpdateRuntimeData(float chartX, float chartY, float chartWidth, float chartHeight) { @@ -101,6 +102,7 @@ namespace XCharts runtimeY = chartY + runtimeBottom; runtimeWidth = chartWidth - runtimeLeft - runtimeRight; runtimeHeight = chartHeight - runtimeTop - runtimeBottom; + runtimePosition = new Vector3(runtimeX, runtimeY); } public static Grid defaultGrid diff --git a/Assets/XCharts/Runtime/Component/Sub/MarkLine.cs b/Assets/XCharts/Runtime/Component/Sub/MarkLine.cs new file mode 100644 index 00000000..b272075e --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Sub/MarkLine.cs @@ -0,0 +1,664 @@ +/************************************************/ +/* */ +/* Copyright (c) 2018 - 2021 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/************************************************/ + +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XCharts +{ + public enum MarkLineType + { + None, + /// + /// 最小值。 + /// + Min, + /// + /// 最大值。 + /// + Max, + /// + /// 平均值。 + /// + Average, + /// + /// 中位数。 + /// + Median + } + /// + /// Data of marking line. + /// 图表标线的数据。 + /// + [System.Serializable] + public class MarkLineData : SubComponent + { + [SerializeField] private string m_Name; + [SerializeField] private MarkLineType m_Type = MarkLineType.None; + [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 SerieSymbol m_StartSymbol = new SerieSymbol(); + [SerializeField] private SerieSymbol m_EndSymbol = new SerieSymbol(); + [SerializeField] private LineStyle m_LineStyle = new LineStyle(); + [SerializeField] private SerieLabel m_Label = new SerieLabel(); + //[SerializeField] private Emphasis m_Emphasis = new Emphasis(); + + public int index { get; set; } + 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; } + + /// + /// Name of the marker, which will display as a label. + /// 标注名称,将会作为文字显示。label的formatter可通过{b}显示名称,通过{c}显示数值。 + /// + public string name + { + get { return m_Name; } + set { if (PropertyUtil.SetClass(ref m_Name, value)) SetVerticesDirty(); } + } + /// + /// Special label types, are used to label maximum value, minimum value and so on. + /// 特殊的标注类型,用于标注最大值最小值等。 + /// + public MarkLineType type + { + get { return m_Type; } + set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetVerticesDirty(); } + } + /// + /// From which dimension of data to calculate the maximum and minimum value and so on. + /// 从哪个维度的数据计算最大最小值等。 + /// + public int dimension + { + get { return m_Dimension; } + set { if (PropertyUtil.SetStruct(ref m_Dimension, value)) SetVerticesDirty(); } + } + /// + /// The x coordinate relative to the origin, in pixels. + /// 相对原点的 x 坐标,单位像素。当type为None时有效。 + /// + public float xPosition + { + get { return m_XPosition; } + set { if (PropertyUtil.SetStruct(ref m_XPosition, value)) SetVerticesDirty(); } + } + /// + /// The y coordinate relative to the origin, in pixels. + /// 相对原点的 y 坐标,单位像素。当type为None时有效。 + /// + public float yPosition + { + get { return m_YPosition; } + set { if (PropertyUtil.SetStruct(ref m_YPosition, value)) SetVerticesDirty(); } + } + /// + /// 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时有效。 + /// + public double xValue + { + get { return m_XValue; } + set { if (PropertyUtil.SetStruct(ref m_XValue, value)) SetVerticesDirty(); } + } + /// + /// 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时有效。 + /// + public double yValue + { + get { return m_YValue; } + set { if (PropertyUtil.SetStruct(ref m_YValue, value)) SetVerticesDirty(); } + } + /// + /// 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组成一条标线。 + /// + public int group + { + get { return m_Group; } + set { if (PropertyUtil.SetStruct(ref m_Group, value)) SetVerticesDirty(); } + } + /// + /// Is the origin of the coordinate system. + /// 是否为坐标系原点。 + /// + public bool zeroPosition + { + get { return m_ZeroPosition; } + set { if (PropertyUtil.SetStruct(ref m_ZeroPosition, value)) SetVerticesDirty(); } + } + /// + /// The symbol of the start point of markline. + /// 起始点的图形标记。 + /// + public SerieSymbol startSymbol + { + get { return m_StartSymbol; } + set { if (PropertyUtil.SetClass(ref m_StartSymbol, value)) SetVerticesDirty(); } + } + /// + /// The symbol of the end point of markline. + /// 结束点的图形标记。 + /// + public SerieSymbol endSymbol + { + get { return m_EndSymbol; } + set { if (PropertyUtil.SetClass(ref m_EndSymbol, value)) SetVerticesDirty(); } + } + /// + /// The line style of markline. + /// 标线样式。 + /// + public LineStyle lineStyle + { + get { return m_LineStyle; } + set { if (PropertyUtil.SetClass(ref m_LineStyle, value)) SetVerticesDirty(); } + } + /// + /// Text styles of label. You can set position to Start, Middle, and End to display text in different locations. + /// 文本样式。可设置position为Start、Middle和End在不同的位置显示文本。 + /// + public SerieLabel 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(); } + // } + } + + /// + /// Use a line in the chart to illustrate. + /// 图表标线。 + /// + [System.Serializable] + public class MarkLine : SubComponent + { + [SerializeField] private bool m_Show; + [SerializeField] private SerieAnimation m_Animation = new SerieAnimation(); + [SerializeField] private List m_Data = new List(); + + /// + /// Whether to display the marking line. + /// 是否显示标线。 + /// + public bool show + { + get { return m_Show; } + set { if (PropertyUtil.SetStruct(ref m_Show, value)) SetVerticesDirty(); } + } + /// + /// The animation of markline. + /// 标线的动画样式。 + /// + public SerieAnimation animation + { + get { return m_Animation; } + set { if (PropertyUtil.SetClass(ref m_Animation, value)) SetVerticesDirty(); } + } + /// + /// 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的两个数据项分别表 + /// 示标线的起始点和终止点来组成一条标线,此时标线的相关样式参数取起始点的参数。 + /// + public List data + { + get { return m_Data; } + set { if (PropertyUtil.SetClass(ref m_Data, value)) SetVerticesDirty(); } + } + public static MarkLine defaultMarkLine + { + get + { + var markLine = new MarkLine + { + m_Show = false, + m_Data = new List() + }; + var data = new MarkLineData(); + data.type = MarkLineType.Min; + data.startSymbol.show = true; + data.startSymbol.type = SerieSymbolType.Circle; + data.endSymbol.show = true; + data.endSymbol.type = SerieSymbolType.Triangle; + markLine.data.Add(data); + return markLine; + } + } + } + + internal class MarkLineHandler : IComponentHandler + { + public CoordinateChart chart; + private GameObject m_MarkLineLabelRoot; + private bool m_RefreshLabel = false; + + public MarkLineHandler(CoordinateChart chart) + { + this.chart = chart; + } + + public void DrawBase(VertexHelper vh) + { + } + + public void DrawTop(VertexHelper vh) + { + DrawMarkLine(vh); + } + + public void Init() + { + foreach (var serie in chart.series.list) InitMarkLine(serie); + } + + public void OnBeginDrag(PointerEventData eventData) + { + } + + public void OnDrag(PointerEventData eventData) + { + } + + public void OnEndDrag(PointerEventData eventData) + { + } + + public void OnPointerDown(PointerEventData eventData) + { + } + + public void OnScroll(PointerEventData eventData) + { + } + + public void Update() + { + if (m_RefreshLabel) + { + m_RefreshLabel = false; + foreach (var serie in chart.series.list) + { + if (!serie.show || !serie.markLine.show) continue; + foreach (var data in serie.markLine.data) + { + if (data.runtimeLabel != null) + { + data.runtimeLabel.SetPosition(MarkLineHelper.GetLabelPosition(data)); + data.runtimeLabel.SetText(MarkLineHelper.GetFormatterContent(serie, data)); + } + } + } + } + } + + private void InitMarkLine(Serie serie) + { + if (!serie.show || !serie.markLine.show) return; + ResetTempMarkLineGroupData(serie.markLine); + m_MarkLineLabelRoot = ChartHelper.AddObject("markline", chart.transform, chart.chartMinAnchor, + chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); + m_MarkLineLabelRoot.hideFlags = chart.chartHideFlags; + ChartHelper.HideAllObject(m_MarkLineLabelRoot); + var serieColor = (Color)chart.theme.GetColor(chart.GetLegendRealShowNameIndex(serie.name)); + 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 serie.markLine.data) + { + if (data.group != 0) continue; + InitMarkLineLabel(serie, data, serieColor); + } + } + + private void InitMarkLineLabel(Serie serie, MarkLineData data, Color serieColor) + { + data.painter = chart.m_PainterTop; + data.refreshComponent = delegate () + { + var label = data.label; + var textName = string.Format("markLine_{0}_{1}", serie.index, data.index); + var color = !ChartHelper.IsClearColor(label.textStyle.color) ? label.textStyle.color : chart.theme.axis.textColor; + var element = ChartHelper.AddSerieLabel(textName, m_MarkLineLabelRoot.transform, label.backgroundWidth, + label.backgroundHeight, color, label.textStyle, chart.theme); + var isAutoSize = label.backgroundWidth == 0 || label.backgroundHeight == 0; + var item = new ChartLabel(); + item.SetLabel(element, isAutoSize, label.paddingLeftRight, label.paddingTopBottom); + item.SetIconActive(false); + item.SetActive(true); + item.SetPosition(MarkLineHelper.GetLabelPosition(data)); + item.SetText(MarkLineHelper.GetFormatterContent(serie, data)); + data.runtimeLabel = item; + }; + data.refreshComponent(); + } + + private void DrawMarkLine(VertexHelper vh) + { + foreach (var serie in chart.series.list) + { + DrawMarkLine(vh, serie); + } + } + + private Dictionary> m_TempGroupData = new Dictionary>(); + private void DrawMarkLine(VertexHelper vh, Serie serie) + { + if (!serie.show || !serie.markLine.show) return; + if (serie.markLine.data.Count == 0) return; + var yAxis = chart.GetSerieYAxisOrDefault(serie); + var xAxis = chart.GetSerieXAxisOrDefault(serie); + var grid = chart.GetSerieGridOrDefault(serie); + var dataZoom = DataZoomHelper.GetAxisRelatedDataZoom(xAxis, chart.dataZooms); + var animation = serie.markLine.animation; + var showData = serie.GetDataList(dataZoom); + var sp = Vector3.zero; + var ep = Vector3.zero; + var colorIndex = chart.GetLegendRealShowNameIndex(serie.name); + var serieColor = SerieHelper.GetLineColor(serie, chart.theme, colorIndex, false); + animation.InitProgress(1, 0, 1f); + ResetTempMarkLineGroupData(serie.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 serie.markLine.data) + { + if (data.group != 0) continue; + switch (data.type) + { + case MarkLineType.Min: + data.runtimeValue = SerieHelper.GetMinData(serie, data.dimension, dataZoom); + GetStartEndPos(xAxis, yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.Max: + data.runtimeValue = SerieHelper.GetMaxData(serie, data.dimension, dataZoom); + GetStartEndPos(xAxis, yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.Average: + data.runtimeValue = SerieHelper.GetAverageData(serie, data.dimension, dataZoom); + GetStartEndPos(xAxis, yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.Median: + data.runtimeValue = SerieHelper.GetMedianData(serie, data.dimension, dataZoom); + GetStartEndPos(xAxis, yAxis, grid, data.runtimeValue, ref sp, ref ep); + break; + case MarkLineType.None: + if (data.xPosition != 0) + { + data.runtimeValue = data.xPosition; + var pX = grid.runtimeX + data.xPosition; + sp = new Vector3(pX, grid.runtimeY); + ep = new Vector3(pX, grid.runtimeY + grid.runtimeHeight); + } + else if (data.yPosition != 0) + { + data.runtimeValue = data.yPosition; + var pY = grid.runtimeY + data.yPosition; + sp = new Vector3(grid.runtimeX, pY); + ep = new Vector3(grid.runtimeX + grid.runtimeWidth, pY); + } + else if (data.yValue != 0) + { + data.runtimeValue = data.yValue; + if (yAxis.IsCategory()) + { + var categoryIndex = (int)data.yValue; + var scaleWid = AxisHelper.GetDataWidth(yAxis, grid.runtimeHeight, showData.Count, dataZoom); + float startY = grid.runtimeY + (yAxis.boundaryGap ? scaleWid / 2 : 0); + var pY = startY + scaleWid * categoryIndex; + sp = new Vector3(grid.runtimeX, pY); + ep = new Vector3(grid.runtimeX + grid.runtimeWidth, pY); + } + else + { + GetStartEndPos(xAxis, yAxis, grid, data.yValue, ref sp, ref ep); + } + } + else + { + data.runtimeValue = data.xValue; + if (xAxis.IsCategory()) + { + var categoryIndex = (int)data.xValue; + var scaleWid = AxisHelper.GetDataWidth(xAxis, grid.runtimeWidth, showData.Count, dataZoom); + float startX = grid.runtimeX + (xAxis.boundaryGap ? scaleWid / 2 : 0); + var pX = startX + scaleWid * categoryIndex; + sp = new Vector3(pX, grid.runtimeY); + ep = new Vector3(pX, grid.runtimeY + grid.runtimeHeight); + } + else + { + GetStartEndPos(xAxis, yAxis, 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(); + } + m_TempGroupData[data.group].Add(data); + } + } + + private void DrawMakLineData(VertexHelper vh, MarkLineData data, SerieAnimation animation, Serie serie, + Grid grid, Color32 serieColor, Vector3 sp, Vector3 ep) + { + if (!animation.IsFinish()) + ep = Vector3.Lerp(sp, ep, animation.GetCurrDetail()); + data.runtimeCurrentEndPosition = ep; + if (sp != Vector3.zero || ep != Vector3.zero) + { + 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, lineColor, lineWidth, LineStyle.Type.Dashed); + 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, SerieSymbol symbol, Serie serie, Grid grid, ChartTheme theme, + Vector3 pos, Vector3 startPos, Color32 lineColor) + { + var symbolSize = symbol.GetSize(null, theme.serie.lineSymbolSize); + var tickness = SerieHelper.GetSymbolBorder(serie, null, theme, false); + var cornerRadius = SerieHelper.GetSymbolCornerRadius(serie, null, false); + chart.Internal_CheckClipAndDrawSymbol(vh, symbol.type, symbolSize, tickness, pos, lineColor, lineColor, + symbol.gap, true, cornerRadius, grid, startPos); + } + + private void GetStartEndPos(Axis xAxis, Axis yAxis, Grid grid, double value, ref Vector3 sp, ref Vector3 ep) + { + if (xAxis.IsCategory()) + { + var yDataHig = (value - yAxis.runtimeMinValue) / yAxis.runtimeMinMaxRange * grid.runtimeHeight; + var pY = grid.runtimeY + (float)yDataHig; + sp = new Vector3(grid.runtimeX, pY); + ep = new Vector3(grid.runtimeX + grid.runtimeWidth, pY); + } + else + { + var xDataHig = (value - xAxis.runtimeMinValue) / xAxis.runtimeMinMaxRange * grid.runtimeWidth; + var pX = grid.runtimeX + (float)xDataHig; + sp = new Vector3(pX, grid.runtimeY); + ep = new Vector3(pX, grid.runtimeY + grid.runtimeHeight); + } + } + + private float GetAxisPosition(Grid grid, Axis axis, DataZoom dataZoom, int dataCount, double value) + { + if (axis is YAxis) + { + if (axis.IsCategory()) + { + var categoryIndex = (int)value; + var scaleWid = AxisHelper.GetDataWidth(axis, grid.runtimeHeight, dataCount, dataZoom); + float startY = grid.runtimeY + (axis.boundaryGap ? scaleWid / 2 : 0); + return startY + scaleWid * categoryIndex; + } + var yDataHig = (value - axis.runtimeMinValue) / axis.runtimeMinMaxRange * grid.runtimeHeight; + return grid.runtimeY + (float)yDataHig; + } + else + { + if (axis.IsCategory()) + { + var categoryIndex = (int)value; + var scaleWid = AxisHelper.GetDataWidth(axis, grid.runtimeWidth, dataCount, dataZoom); + float startX = grid.runtimeX + (axis.boundaryGap ? scaleWid / 2 : 0); + return startX + scaleWid * categoryIndex; + } + else + { + var xDataHig = (value - axis.runtimeMinValue) / axis.runtimeMinMaxRange * grid.runtimeWidth; + return grid.runtimeX + (float)xDataHig; + } + } + } + + private Vector3 GetSinglePos(Axis xAxis, Axis yAxis, Grid 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.runtimePosition; + } + else + { + pX = data.xPosition != 0 ? grid.runtimeX + data.xPosition : + GetAxisPosition(grid, xAxis, dataZoom, serieDataCount, data.xValue); + pY = data.yPosition != 0 ? grid.runtimeY + data.yPosition : + GetAxisPosition(grid, yAxis, dataZoom, serieDataCount, data.yValue); + data.runtimeValue = data.yValue; + return new Vector3(pX, pY); + } + default: + return grid.runtimePosition; + } + } + } + + internal static class MarkLineHelper + { + public static string GetFormatterContent(Serie serie, MarkLineData data) + { + var serieLabel = data.label; + var numericFormatter = serieLabel.numericFormatter; + if (serieLabel.formatterFunction != null) + { + return serieLabel.formatterFunction(data.index, data.runtimeValue); + } + if (string.IsNullOrEmpty(serieLabel.formatter)) + return ChartCached.NumberToStr(data.runtimeValue, numericFormatter); + else + { + var content = serieLabel.formatter; + FormatterHelper.ReplaceSerieLabelContent(ref content, numericFormatter, data.runtimeValue, + 0, serie.name, data.name, Color.clear); + return content; + } + } + + public static Vector3 GetLabelPosition(MarkLineData data) + { + if (!data.label.show) return Vector3.zero; + switch (data.label.position) + { + case SerieLabel.Position.Start: + return data.runtimeStartPosition + data.label.offset; + case SerieLabel.Position.Middle: + return (data.runtimeStartPosition + data.runtimeCurrentEndPosition) / 2 + data.label.offset; + default: + return data.runtimeCurrentEndPosition + data.label.offset; + } + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Sub/MarkLine.cs.meta b/Assets/XCharts/Runtime/Component/Sub/MarkLine.cs.meta new file mode 100644 index 00000000..dba1cc5c --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Sub/MarkLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd9631fa54e73444884c717bd04765a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Sub/SerieLabel.cs b/Assets/XCharts/Runtime/Component/Sub/SerieLabel.cs index 9bed43ca..bba559d7 100644 --- a/Assets/XCharts/Runtime/Component/Sub/SerieLabel.cs +++ b/Assets/XCharts/Runtime/Component/Sub/SerieLabel.cs @@ -58,6 +58,21 @@ namespace XCharts /// 图形标志的右边。 /// Right, + /// + /// the start of line. + /// 线的起始点。 + /// + Start, + /// + /// the middle of line. + /// 线的中点。 + /// + Middle, + /// + /// the end of line. + /// 线的结束点。 + /// + End } /// diff --git a/Assets/XCharts/Runtime/Component/Sub/SerieSymbol.cs b/Assets/XCharts/Runtime/Component/Sub/SerieSymbol.cs index c9e9ce65..a4658fde 100644 --- a/Assets/XCharts/Runtime/Component/Sub/SerieSymbol.cs +++ b/Assets/XCharts/Runtime/Component/Sub/SerieSymbol.cs @@ -40,6 +40,10 @@ namespace XCharts /// 不显示标记。 /// None, + /// + /// 箭头。 + /// + Arrow } /// diff --git a/Assets/XCharts/Runtime/Helper/FormatterHelper.cs b/Assets/XCharts/Runtime/Helper/FormatterHelper.cs index 2b43c294..30a59ccf 100644 --- a/Assets/XCharts/Runtime/Helper/FormatterHelper.cs +++ b/Assets/XCharts/Runtime/Helper/FormatterHelper.cs @@ -245,8 +245,8 @@ namespace XCharts var isPercent = p == 'd' || p == 'D'; if (isPercent) { - var percent = total == 0 ? 0 : value / total * 100; - content = content.Replace(old, ChartCached.FloatToStr(percent, numericFormatter)); + if (total != 0) + content = content.Replace(old, ChartCached.FloatToStr(value / total * 100, numericFormatter)); } else { diff --git a/Assets/XCharts/Runtime/Helper/SerieHelper.cs b/Assets/XCharts/Runtime/Helper/SerieHelper.cs index a77d7758..1856a03d 100644 --- a/Assets/XCharts/Runtime/Helper/SerieHelper.cs +++ b/Assets/XCharts/Runtime/Helper/SerieHelper.cs @@ -13,6 +13,111 @@ namespace XCharts { 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) 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) + { + 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) 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) + { + 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) + { + total += serieData.data[dimension]; + } + } + return total != 0 ? total / dataList.Count : 0; + } + + private static List s_TempList = new List(); + 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) + { + s_TempList.Add(serieData.data[dimension]); + } + } + 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]; + } + /// /// Gets the maximum and minimum values of the specified dimension of a serie. /// 获得系列指定维数的最大最小值。 diff --git a/Assets/XCharts/Runtime/Internal/BaseChart.cs b/Assets/XCharts/Runtime/Internal/BaseChart.cs index 4f42cca2..746b1119 100644 --- a/Assets/XCharts/Runtime/Internal/BaseChart.cs +++ b/Assets/XCharts/Runtime/Internal/BaseChart.cs @@ -82,7 +82,7 @@ namespace XCharts internal bool m_IsPlayingAnimation = false; internal protected List m_LegendRealShowName = new List(); protected List m_PainterList = new List(); - protected Painter m_PainterTop; + internal Painter m_PainterTop; protected GameObject m_SerieLabelRoot; private Theme m_CheckTheme = 0; @@ -120,8 +120,8 @@ namespace XCharts if (m_Tooltips.Count == 0) m_Tooltips = new List() { Tooltip.defaultTooltip }; CheckTheme(); base.Awake(); - m_Series.AnimationReset(); - m_Series.AnimationFadeIn(); + AnimationReset(); + AnimationFadeIn(); XChartsMgr.Instance.AddChart(this); } @@ -732,7 +732,7 @@ namespace XCharts if (!m_CheckAnimation) { m_CheckAnimation = true; - m_Series.AnimationFadeIn(); + AnimationFadeIn(); } } @@ -1011,11 +1011,16 @@ namespace XCharts public void DrawSymbol(VertexHelper vh, SerieSymbolType type, float symbolSize, float tickness, Vector3 pos, Color32 color, Color32 toColor, float gap, float[] cornerRadius) + { + DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, gap, cornerRadius, Vector3.zero); + } + public void DrawSymbol(VertexHelper vh, SerieSymbolType type, float symbolSize, + float tickness, Vector3 pos, Color32 color, Color32 toColor, float gap, float[] cornerRadius, Vector3 startPos) { var backgroundColor = ThemeHelper.GetBackgroundColor(m_Theme, m_Background); var smoothness = settings.cicleSmoothness; ChartDrawer.DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, gap, - cornerRadius, backgroundColor, smoothness); + cornerRadius, backgroundColor, smoothness, startPos); } public void DrawLabelBackground(VertexHelper vh, Serie serie, SerieData serieData) diff --git a/Assets/XCharts/Runtime/Internal/CoordinateChart.cs b/Assets/XCharts/Runtime/Internal/CoordinateChart.cs index 41cb6f2e..9f543288 100644 --- a/Assets/XCharts/Runtime/Internal/CoordinateChart.cs +++ b/Assets/XCharts/Runtime/Internal/CoordinateChart.cs @@ -32,6 +32,10 @@ namespace XCharts InitAxisX(); InitAxisY(); tooltip.UpdateToTop(); + + var handler = new MarkLineHandler(this); + m_ComponentHandlers.Add(handler); + handler.Init(); } protected override void Update() @@ -1675,11 +1679,11 @@ namespace XCharts } public void Internal_CheckClipAndDrawSymbol(VertexHelper vh, SerieSymbolType type, float symbolSize, float tickness, - Vector3 pos, Color32 color, Color32 toColor, float gap, bool clip, float[] cornerRadius, Grid grid) + Vector3 pos, Color32 color, Color32 toColor, float gap, bool clip, float[] cornerRadius, Grid grid, Vector3 startPos) { if (!IsInChart(pos)) return; if (!clip || (clip && (IsInGrid(grid, pos)))) - DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, gap, cornerRadius); + DrawSymbol(vh, type, symbolSize, tickness, pos, color, toColor, gap, cornerRadius, startPos); } public void Internal_CheckClipAndDrawZebraLine(VertexHelper vh, Vector3 p1, Vector3 p2, float size, float zebraWidth, diff --git a/Assets/XCharts/Runtime/Internal/CoordinateChart_DrawLine.cs b/Assets/XCharts/Runtime/Internal/CoordinateChart_DrawLine.cs index 727edc9c..112a6771 100644 --- a/Assets/XCharts/Runtime/Internal/CoordinateChart_DrawLine.cs +++ b/Assets/XCharts/Runtime/Internal/CoordinateChart_DrawLine.cs @@ -46,7 +46,7 @@ namespace XCharts var cornerRadius = SerieHelper.GetSymbolCornerRadius(serie, serieData, highlight); symbolSize = serie.animation.GetSysmbolSize(symbolSize); Internal_CheckClipAndDrawSymbol(vh, symbol.type, symbolSize, symbolBorder, serie.dataPoints[i], symbolColor, - symbolToColor, symbol.gap, clip, cornerRadius, grid); + symbolToColor, symbol.gap, clip, cornerRadius, grid, i > 0 ? serie.dataPoints[i - 1] : grid.runtimePosition); } } diff --git a/Assets/XCharts/Runtime/Internal/Utility/ChartDrawer.cs b/Assets/XCharts/Runtime/Internal/Utility/ChartDrawer.cs index 8e40f5c8..4432e65c 100644 --- a/Assets/XCharts/Runtime/Internal/Utility/ChartDrawer.cs +++ b/Assets/XCharts/Runtime/Internal/Utility/ChartDrawer.cs @@ -15,9 +15,10 @@ namespace XCharts { public static class ChartDrawer { + public static void DrawSymbol(VertexHelper vh, SerieSymbolType type, float symbolSize, float tickness, Vector3 pos, Color32 color, Color32 toColor, float gap, float[] cornerRadius, - Color32 backgroundColor, float smoothness) + Color32 backgroundColor, float smoothness, Vector3 startPos) { switch (type) { @@ -77,6 +78,14 @@ namespace XCharts UGL.DrawDiamond(vh, pos, symbolSize, color, toColor); } break; + case SerieSymbolType.Arrow: + var arrowWidth = symbolSize * 2; + var arrowHeight = arrowWidth * 1.5f; + var arrowOffset = 0; + var arrowDent = arrowWidth / 3.3f; + UGL.DrawArrow(vh, startPos, pos, arrowWidth, arrowHeight, + arrowOffset, arrowDent, color); + break; } }