From ccc5110e8e983082d0715b5fa4d896e0514dd6b2 Mon Sep 17 00:00:00 2001 From: monitor1394 Date: Tue, 6 Sep 2022 07:22:04 +0800 Subject: [PATCH] [feature][heatmap] support `heatmapType` --- CHANGELOG.md | 1 + Documentation/XChartsAPI-EN.md | 10 +- Documentation/XChartsAPI-ZH.md | 10 +- Documentation/XChartsConfiguration-EN.md | 7 +- Documentation/XChartsConfiguration-ZH.md | 7 +- Editor/Series/HeatmapEditor.cs | 1 + Runtime/Component/Axis/AxisHelper.cs | 4 +- Runtime/Component/Tooltip/TooltipHandler.cs | 14 +- Runtime/Serie/Heatmap/Heatmap.cs | 30 +++ Runtime/Serie/Heatmap/HeatmapHandler.cs | 248 +++++++++++++++++--- 10 files changed, 282 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 762e3cdb..c8d2b236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ ## master +* (2022.09.05) 增加`Heatmap`的`heatmapType`支持设置`Data`和`Count`两种不同映射方式的热力图 * (2022.09.05) 优化`Tooltip`在热力图为数值轴时的指示 * (2022.09.02) 增加`onPointerEnterPie`回调支持 * (2022.09.02) 优化`HeatmapChart` diff --git a/Documentation/XChartsAPI-EN.md b/Documentation/XChartsAPI-EN.md index f09576c8..931cd18f 100644 --- a/Documentation/XChartsAPI-EN.md +++ b/Documentation/XChartsAPI-EN.md @@ -130,12 +130,14 @@ Inherits or Implemented: [MainComponentHandler](#MainComponentHandler) | `GetAxisValueDistance()` |public static float GetAxisValueDistance(GridCoord grid, Axis axis, float scaleWidth, double value)
获得数值value在坐标轴上相对起点的距离 | | `GetAxisValueLength()` |public static float GetAxisValueLength(GridCoord grid, Axis axis, float scaleWidth, double value)
获得数值value在坐标轴上对应的长度 | | `GetAxisValuePosition()` |public static float GetAxisValuePosition(GridCoord grid, Axis axis, float scaleWidth, double value)
获得数值value在坐标轴上的坐标位置 | +| `GetAxisValueSplitIndex()` |public static int GetAxisValueSplitIndex(Axis axis, double value, int totalSplitNumber = -1)
获得数值value在坐标轴上对应的split索引 | | `GetAxisXOrY()` |public static float GetAxisXOrY(GridCoord grid, Axis axis, Axis relativedAxis)
| | `GetDataWidth()` |public static float GetDataWidth(Axis axis, float coordinateWidth, int dataCount, DataZoom dataZoom)
获得一个类目数据在坐标系中代表的宽度 | | `GetEachWidth()` |public static float GetEachWidth(Axis axis, float coordinateWidth, DataZoom dataZoom = null)
| | `GetScaleNumber()` |public static int GetScaleNumber(Axis axis, float coordinateWidth, DataZoom dataZoom = null)
获得分割线条数 | | `GetScaleWidth()` |public static float GetScaleWidth(Axis axis, float coordinateWidth, int index, DataZoom dataZoom = null)
获得分割段宽度 | | `GetSplitNumber()` |public static int GetSplitNumber(Axis axis, float coordinateWid, DataZoom dataZoom)
获得分割段数 | +| `GetTotalSplitGridNum()` |public static int GetTotalSplitGridNum(Axis axis)
获得分割网格个数,包含次刻度 | | `GetXAxisXOrY()` |public static float GetXAxisXOrY(GridCoord grid, Axis xAxis, Axis relativedAxis)
| | `GetYAxisXOrY()` |public static float GetYAxisXOrY(GridCoord grid, Axis yAxis, Axis relativedAxis)
| | `NeedShowSplit()` |public static bool NeedShowSplit(Axis axis)
| @@ -153,12 +155,12 @@ Inherits or Implemented: [BaseGraph](#BaseGraph),[ISerializationCallbackReceiver | `AddChartComponent()` |public MainComponent AddChartComponent(Type type)
| | `AddData()` |public SerieData AddData(int serieIndex, DateTime time, double yValue, string dataName = null, string dataId = null)
Add a (time,y) data to serie. | | `AddData()` |public SerieData AddData(int serieIndex, double data, string dataName = null, string dataId = null)
Add a data to serie. | -| `AddData()` |public SerieData AddData(int serieIndex, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| +| `AddData()` |public SerieData AddData(int serieIndex, double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| | `AddData()` |public SerieData AddData(int serieIndex, double xValue, double yValue, string dataName = null, string dataId = null)
Add a (x,y) data to serie. | | `AddData()` |public SerieData AddData(int serieIndex, List multidimensionalData, string dataName = null, string dataId = null)
Add an arbitray dimension data to serie,such as (x,y,z,...). | | `AddData()` |public SerieData AddData(string serieName, DateTime time, double yValue, string dataName = null, string dataId = null)
Add a (time,y) data to serie. | | `AddData()` |public SerieData AddData(string serieName, double data, string dataName = null, string dataId = null)
Add a data to serie. | -| `AddData()` |public SerieData AddData(string serieName, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| +| `AddData()` |public SerieData AddData(string serieName, double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| | `AddData()` |public SerieData AddData(string serieName, double xValue, double yValue, string dataName = null, string dataId = null)
Add a (x,y) data to serie. | | `AddData()` |public SerieData AddData(string serieName, List multidimensionalData, string dataName = null, string dataId = null)
Add an arbitray dimension data to serie,such as (x,y,z,...). | | `AddXAxisData()` |public void AddXAxisData(string category, int xAxisIndex = 0)
Add a category data to xAxis. | @@ -201,13 +203,13 @@ Inherits or Implemented: [BaseGraph](#BaseGraph),[ISerializationCallbackReceiver | `GetSeriesMinMaxValue()` |public virtual void GetSeriesMinMaxValue(Axis axis, int axisIndex, out double tempMinValue, out double tempMaxValue)
| | `GetTitlePosition()` |public Vector3 GetTitlePosition(Title title)
| | `GetVisualMapOfSerie()` |public VisualMap GetVisualMapOfSerie(Serie serie)
| +| `GetXDataZoomOfSerie()` |public DataZoom GetXDataZoomOfSerie(Serie serie)
| | `GetXLerpColor()` |public Color32 GetXLerpColor(Color32 areaColor, Color32 areaToColor, Vector3 pos, GridCoord grid)
| | `GetYLerpColor()` |public Color32 GetYLerpColor(Color32 areaColor, Color32 areaToColor, Vector3 pos, GridCoord grid)
| | `HasChartComponent()` |public bool HasChartComponent(Type type)
| | `HasChartComponent()` |public bool HasChartComponent()
| | `HasSerie()` |public bool HasSerie(Type type)
| | `Init()` |public void Init(bool defaultChart = true)
| -| `InitAxisRuntimeData()` |public virtual void InitAxisRuntimeData(Axis axis)
| | `InsertSerie()` |public void InsertSerie(Serie serie, int index = -1, bool addToHead = false)
| | `Internal_CheckAnimation()` |public void Internal_CheckAnimation()
| | `IsActiveByLegend()` |public virtual bool IsActiveByLegend(string legendName)
Whether serie is activated. | @@ -1038,7 +1040,7 @@ Inherits or Implemented: [MainComponentContext](#MainComponentContext) |public method|description| |--|--| | `AutoSetLineMinMax()` |public static void AutoSetLineMinMax(VisualMap visualMap, Serie serie, bool isY, Axis axis, Axis relativedAxis)
| -| `GetDimension()` |public static int GetDimension(VisualMap visualMap, int serieDataCount)
| +| `GetDimension()` |public static int GetDimension(VisualMap visualMap, int defaultDimension)
| | `IsNeedAreaGradient()` |public static bool IsNeedAreaGradient(VisualMap visualMap)
| | `IsNeedGradient()` |public static bool IsNeedGradient(VisualMap visualMap)
| | `IsNeedLineGradient()` |public static bool IsNeedLineGradient(VisualMap visualMap)
| diff --git a/Documentation/XChartsAPI-ZH.md b/Documentation/XChartsAPI-ZH.md index e2994473..878e8c08 100644 --- a/Documentation/XChartsAPI-ZH.md +++ b/Documentation/XChartsAPI-ZH.md @@ -130,12 +130,14 @@ Inherits or Implemented: [MainComponentHandler](#MainComponentHandler) | `GetAxisValueDistance()` |public static float GetAxisValueDistance(GridCoord grid, Axis axis, float scaleWidth, double value)
获得数值value在坐标轴上相对起点的距离 | | `GetAxisValueLength()` |public static float GetAxisValueLength(GridCoord grid, Axis axis, float scaleWidth, double value)
获得数值value在坐标轴上对应的长度 | | `GetAxisValuePosition()` |public static float GetAxisValuePosition(GridCoord grid, Axis axis, float scaleWidth, double value)
获得数值value在坐标轴上的坐标位置 | +| `GetAxisValueSplitIndex()` |public static int GetAxisValueSplitIndex(Axis axis, double value, int totalSplitNumber = -1)
获得数值value在坐标轴上对应的split索引 | | `GetAxisXOrY()` |public static float GetAxisXOrY(GridCoord grid, Axis axis, Axis relativedAxis)
| | `GetDataWidth()` |public static float GetDataWidth(Axis axis, float coordinateWidth, int dataCount, DataZoom dataZoom)
获得一个类目数据在坐标系中代表的宽度 | | `GetEachWidth()` |public static float GetEachWidth(Axis axis, float coordinateWidth, DataZoom dataZoom = null)
| | `GetScaleNumber()` |public static int GetScaleNumber(Axis axis, float coordinateWidth, DataZoom dataZoom = null)
获得分割线条数 | | `GetScaleWidth()` |public static float GetScaleWidth(Axis axis, float coordinateWidth, int index, DataZoom dataZoom = null)
获得分割段宽度 | | `GetSplitNumber()` |public static int GetSplitNumber(Axis axis, float coordinateWid, DataZoom dataZoom)
获得分割段数 | +| `GetTotalSplitGridNum()` |public static int GetTotalSplitGridNum(Axis axis)
获得分割网格个数,包含次刻度 | | `GetXAxisXOrY()` |public static float GetXAxisXOrY(GridCoord grid, Axis xAxis, Axis relativedAxis)
| | `GetYAxisXOrY()` |public static float GetYAxisXOrY(GridCoord grid, Axis yAxis, Axis relativedAxis)
| | `NeedShowSplit()` |public static bool NeedShowSplit(Axis axis)
| @@ -153,12 +155,12 @@ Inherits or Implemented: [BaseGraph](#BaseGraph),[ISerializationCallbackReceiver | `AddChartComponent()` |public MainComponent AddChartComponent(Type type)
| | `AddData()` |public SerieData AddData(int serieIndex, DateTime time, double yValue, string dataName = null, string dataId = null)
添加(time,y)数据到指定的系列中。 | | `AddData()` |public SerieData AddData(int serieIndex, double data, string dataName = null, string dataId = null)
添加一个数据到指定的系列中。 | -| `AddData()` |public SerieData AddData(int serieIndex, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| +| `AddData()` |public SerieData AddData(int serieIndex, double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| | `AddData()` |public SerieData AddData(int serieIndex, double xValue, double yValue, string dataName = null, string dataId = null)
添加(x,y)数据到指定系列中。 | | `AddData()` |public SerieData AddData(int serieIndex, List multidimensionalData, string dataName = null, string dataId = null)
添加多维数据(x,y,z...)到指定的系列中。 | | `AddData()` |public SerieData AddData(string serieName, DateTime time, double yValue, string dataName = null, string dataId = null)
添加(time,y)数据到指定的系列中。 | | `AddData()` |public SerieData AddData(string serieName, double data, string dataName = null, string dataId = null)
If serieName doesn't exist in legend,will be add to legend. | -| `AddData()` |public SerieData AddData(string serieName, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| +| `AddData()` |public SerieData AddData(string serieName, double indexOrTimestamp, double open, double close, double lowest, double heighest, string dataName = null, string dataId = null)
| | `AddData()` |public SerieData AddData(string serieName, double xValue, double yValue, string dataName = null, string dataId = null)
添加(x,y)数据到指定系列中。 | | `AddData()` |public SerieData AddData(string serieName, List multidimensionalData, string dataName = null, string dataId = null)
添加多维数据(x,y,z...)到指定的系列中。 | | `AddXAxisData()` |public void AddXAxisData(string category, int xAxisIndex = 0)
添加一个类目数据到指定的x轴。 | @@ -201,13 +203,13 @@ Inherits or Implemented: [BaseGraph](#BaseGraph),[ISerializationCallbackReceiver | `GetSeriesMinMaxValue()` |public virtual void GetSeriesMinMaxValue(Axis axis, int axisIndex, out double tempMinValue, out double tempMaxValue)
| | `GetTitlePosition()` |public Vector3 GetTitlePosition(Title title)
| | `GetVisualMapOfSerie()` |public VisualMap GetVisualMapOfSerie(Serie serie)
| +| `GetXDataZoomOfSerie()` |public DataZoom GetXDataZoomOfSerie(Serie serie)
| | `GetXLerpColor()` |public Color32 GetXLerpColor(Color32 areaColor, Color32 areaToColor, Vector3 pos, GridCoord grid)
| | `GetYLerpColor()` |public Color32 GetYLerpColor(Color32 areaColor, Color32 areaToColor, Vector3 pos, GridCoord grid)
| | `HasChartComponent()` |public bool HasChartComponent(Type type)
| | `HasChartComponent()` |public bool HasChartComponent()
| | `HasSerie()` |public bool HasSerie(Type type)
| | `Init()` |public void Init(bool defaultChart = true)
| -| `InitAxisRuntimeData()` |public virtual void InitAxisRuntimeData(Axis axis)
| | `InsertSerie()` |public void InsertSerie(Serie serie, int index = -1, bool addToHead = false)
| | `Internal_CheckAnimation()` |public void Internal_CheckAnimation()
| | `IsActiveByLegend()` |public virtual bool IsActiveByLegend(string legendName)
获得指定图例名字的系列是否显示。 | @@ -1038,7 +1040,7 @@ Inherits or Implemented: [MainComponentContext](#MainComponentContext) |public method|description| |--|--| | `AutoSetLineMinMax()` |public static void AutoSetLineMinMax(VisualMap visualMap, Serie serie, bool isY, Axis axis, Axis relativedAxis)
| -| `GetDimension()` |public static int GetDimension(VisualMap visualMap, int serieDataCount)
| +| `GetDimension()` |public static int GetDimension(VisualMap visualMap, int defaultDimension)
| | `IsNeedAreaGradient()` |public static bool IsNeedAreaGradient(VisualMap visualMap)
| | `IsNeedGradient()` |public static bool IsNeedGradient(VisualMap visualMap)
| | `IsNeedLineGradient()` |public static bool IsNeedLineGradient(VisualMap visualMap)
| diff --git a/Documentation/XChartsConfiguration-EN.md b/Documentation/XChartsConfiguration-EN.md index 7a696b8a..abfef096 100644 --- a/Documentation/XChartsConfiguration-EN.md +++ b/Documentation/XChartsConfiguration-EN.md @@ -628,6 +628,9 @@ Grid component. Inherits or Implemented: [Serie](#Serie),[INeedSerieContainer](#INeedSerieContainer) +|field|default|since|comment| +|--|--|--|--| +|`heatmapType`||3.3.0|The mapping type of heatmap.
`HeatmapType`:
- `Data`: Data mapping type.By default, the second dimension data is used as the color map.
- `Count`: Number mapping type.The number of occurrences of a statistic in a divided grid, as a color map.
| ## `IconStyle` @@ -781,7 +784,7 @@ Legend component.The legend component shows different sets of tags, colors, and |field|default|since|comment| |--|--|--|--| |`show`|true||Whether to show legend component. -|`iconType`|||Type of legend.
`Legend.Type`:
- `Auto`: 自动匹配。
- `Custom`: 自定义图标。
- `EmptyCircle`: 空心圆。
- `Circle`: 圆形。
- `Rect`: 正方形。可通过Setting的legendIconCornerRadius参数调整圆角。
- `Triangle`: 三角形。
- `Diamond`: 菱形。
| +|`iconType`|||Type of legend.
`Legend.Type`:
- `Auto`: 自动匹配。
- `Custom`: 自定义图标。
- `EmptyCircle`: 空心圆。
- `Circle`: 圆形。
- `Rect`: 正方形。可通过Setting的legendIconCornerRadius参数调整圆角。
- `Triangle`: 三角形。
- `Diamond`: 菱形。
- `Candlestick`: 烛台(可用于K线图)。
| |`selectedMode`|||Selected mode of legend, which controls whether series can be toggled displaying by clicking legends.
`Legend.SelectedMode`:
- `Multiple`: 多选。
- `Single`: 单选。
- `None`: 无法选择。
| |`orient`|||Specify whether the layout of legend component is horizontal or vertical.
`Orient`:
- `Horizonal`: 水平
- `Vertical`: 垂直
| |`location`|||The location of legend. [Location](#Location)| @@ -1479,7 +1482,7 @@ VisualMap component. Mapping data to visual elements such as colors. |`selectedMode`|||the selected mode for Piecewise visualMap.
`VisualMap.SelectedMode`:
- `Multiple`: 多选。
- `Single`: 单选。
| |`serieIndex`|0||the serie index of visualMap. |`min`|0||范围最小值 -|`max`|100||范围最大值 +|`max`|0||范围最大值 |`range`|||Specifies the position of the numeric value corresponding to the handle. Range should be within the range of [min,max]. |`text`|||Text on both ends. |`textGap`|||The distance between the two text bodies. diff --git a/Documentation/XChartsConfiguration-ZH.md b/Documentation/XChartsConfiguration-ZH.md index effc6b6b..f0c24a82 100644 --- a/Documentation/XChartsConfiguration-ZH.md +++ b/Documentation/XChartsConfiguration-ZH.md @@ -628,6 +628,9 @@ Drawing grid in rectangular coordinate. Line chart, bar chart, and scatter chart Inherits or Implemented: [Serie](#Serie),[INeedSerieContainer](#INeedSerieContainer) +|field|default|since|comment| +|--|--|--|--| +|`heatmapType`||3.3.0|热力图类型。通过颜色映射划分。
`HeatmapType`:
- `Data`: 数据映射型。默认用第2维数据作为颜色映射。要求数据至少有3个维度数据。
- `Count`: 个数映射型。统计数据在划分的格子中出现的次数,作为颜色映射。要求数据至少有2个维度数据。
| ## `IconStyle` @@ -781,7 +784,7 @@ Inherits or Implemented: [MainComponent](#MainComponent),[IPropertyChanged](#IPr |field|default|since|comment| |--|--|--|--| |`show`|true||是否显示图例组件。 -|`iconType`|||图例类型。
`Legend.Type`:
- `Auto`: 自动匹配。
- `Custom`: 自定义图标。
- `EmptyCircle`: 空心圆。
- `Circle`: 圆形。
- `Rect`: 正方形。可通过Setting的legendIconCornerRadius参数调整圆角。
- `Triangle`: 三角形。
- `Diamond`: 菱形。
| +|`iconType`|||图例类型。
`Legend.Type`:
- `Auto`: 自动匹配。
- `Custom`: 自定义图标。
- `EmptyCircle`: 空心圆。
- `Circle`: 圆形。
- `Rect`: 正方形。可通过Setting的legendIconCornerRadius参数调整圆角。
- `Triangle`: 三角形。
- `Diamond`: 菱形。
- `Candlestick`: 烛台(可用于K线图)。
| |`selectedMode`|||选择模式。控制是否可以通过点击图例改变系列的显示状态。默认开启图例选择,可以设成 None 关闭。
`Legend.SelectedMode`:
- `Multiple`: 多选。
- `Single`: 单选。
- `None`: 无法选择。
| |`orient`|||布局方式是横还是竖。
`Orient`:
- `Horizonal`: 水平
- `Vertical`: 垂直
| |`location`|||图例显示的位置。 [Location](#Location)| @@ -1479,7 +1482,7 @@ Inherits or Implemented: [MainComponent](#MainComponent) |`selectedMode`|||选择模式。
`VisualMap.SelectedMode`:
- `Multiple`: 多选。
- `Single`: 单选。
| |`serieIndex`|0||影响的serie索引。 |`min`|0||范围最小值 -|`max`|100||范围最大值 +|`max`|0||范围最大值 |`range`|||指定手柄对应数值的位置。range 应在[min,max]范围内。 |`text`|||两端的文本,如 ['High', 'Low']。 |`textGap`|||两端文字主体之间的距离,单位为px。 diff --git a/Editor/Series/HeatmapEditor.cs b/Editor/Series/HeatmapEditor.cs index 739952ba..60e3caed 100644 --- a/Editor/Series/HeatmapEditor.cs +++ b/Editor/Series/HeatmapEditor.cs @@ -7,6 +7,7 @@ namespace XCharts.Editor { public override void OnCustomInspectorGUI() { + PropertyField("m_HeatmapType"); PropertyField("m_Ignore"); PropertyField("m_IgnoreValue"); diff --git a/Runtime/Component/Axis/AxisHelper.cs b/Runtime/Component/Axis/AxisHelper.cs index 73c96f5d..249f5e92 100644 --- a/Runtime/Component/Axis/AxisHelper.cs +++ b/Runtime/Component/Axis/AxisHelper.cs @@ -31,9 +31,7 @@ namespace XCharts.Runtime else { var splitNum = axis.splitNumber <= 0 ? GetSplitNumber(axis, 0, null) : axis.splitNumber; - return axis.minorTick.show ? - splitNum * axis.minorTick.splitNumber : - splitNum; + return splitNum * axis.minorTick.splitNumber; } } diff --git a/Runtime/Component/Tooltip/TooltipHandler.cs b/Runtime/Component/Tooltip/TooltipHandler.cs index 2520cd79..05a356de 100644 --- a/Runtime/Component/Tooltip/TooltipHandler.cs +++ b/Runtime/Component/Tooltip/TooltipHandler.cs @@ -286,11 +286,19 @@ namespace XCharts.Runtime var xAxisIndex = AxisHelper.GetAxisValueSplitIndex(xAxis, xAxis.context.pointerValue); var yAxisIndex = AxisHelper.GetAxisValueSplitIndex(yAxis, yAxis.context.pointerValue); serie.context.pointerItemDataIndex = -1; - + if (serie is Heatmap) + { + var heatmap = serie as Heatmap; + if (heatmap.heatmapType == HeatmapType.Count) + { + serie.context.pointerItemDataIndex = HeatmapHandler.GetGridKey(xAxisIndex, yAxisIndex); + return; + } + } foreach (var serieData in serie.data) { - var x = AxisHelper.GetAxisValueSplitIndex(xAxis,serieData.GetData(0)); - var y = AxisHelper.GetAxisValueSplitIndex(yAxis,serieData.GetData(1)); + var x = AxisHelper.GetAxisValueSplitIndex(xAxis, serieData.GetData(0)); + var y = AxisHelper.GetAxisValueSplitIndex(yAxis, serieData.GetData(1)); if (xAxisIndex == x && y == yAxisIndex) { serie.context.pointerItemDataIndex = serieData.index; diff --git a/Runtime/Serie/Heatmap/Heatmap.cs b/Runtime/Serie/Heatmap/Heatmap.cs index ec4fc180..a40b745d 100644 --- a/Runtime/Serie/Heatmap/Heatmap.cs +++ b/Runtime/Serie/Heatmap/Heatmap.cs @@ -2,6 +2,24 @@ using UnityEngine; namespace XCharts.Runtime { + /// + /// The mapping type of heatmap. + /// |热力图类型。通过颜色映射划分。 + /// + public enum HeatmapType + { + /// + /// Data mapping type.By default, the second dimension data is used as the color map. + /// |数据映射型。默认用第2维数据作为颜色映射。要求数据至少有3个维度数据。 + /// + Data, + /// + /// Number mapping type.The number of occurrences of a statistic in a divided grid, as a color map. + /// |个数映射型。统计数据在划分的格子中出现的次数,作为颜色映射。要求数据至少有2个维度数据。 + /// + Count + } + [System.Serializable] [SerieHandler(typeof(HeatmapHandler), true)] [DefaultAnimation(AnimationType.LeftToRight)] @@ -11,8 +29,20 @@ namespace XCharts.Runtime [SerieDataExtraField()] public class Heatmap : Serie, INeedSerieContainer { + [SerializeField][Since("3.3.0")] private HeatmapType m_HeatmapType = HeatmapType.Data; + + /// + /// The mapping type of heatmap. + /// |热力图类型。通过颜色映射划分。 + /// + public HeatmapType heatmapType + { + get { return m_HeatmapType; } + set { if (PropertyUtil.SetStruct(ref m_HeatmapType, value)) { SetVerticesDirty(); } } + } public int containerIndex { get; internal set; } public int containterInstanceId { get; internal set; } + public static Serie AddDefaultSerie(BaseChart chart, string serieName) { var serie = chart.AddSerie(serieName); diff --git a/Runtime/Serie/Heatmap/HeatmapHandler.cs b/Runtime/Serie/Heatmap/HeatmapHandler.cs index 0edd024d..c5c3295e 100644 --- a/Runtime/Serie/Heatmap/HeatmapHandler.cs +++ b/Runtime/Serie/Heatmap/HeatmapHandler.cs @@ -9,9 +9,21 @@ namespace XCharts.Runtime internal sealed class HeatmapHandler : SerieHandler { private GridCoord m_SerieGrid; + private Dictionary m_CountDict = new Dictionary(); public override int defaultDimension { get { return 2; } } + public static int GetGridKey(int x, int y) + { + return x * 100000 + y; + } + + public static void GetGridXYByKey(int key, out int x, out int y) + { + x = key / 100000; + y = key % 100000; + } + public override void Update() { base.Update(); @@ -20,7 +32,10 @@ namespace XCharts.Runtime public override void DrawSerie(VertexHelper vh) { - DrawHeatmapSerie(vh, serie); + if (serie.heatmapType == HeatmapType.Count) + DrawCountHeatmapSerie(vh, serie); + else + DrawDataHeatmapSerie(vh, serie); } public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, @@ -28,40 +43,70 @@ namespace XCharts.Runtime ref List paramList, ref string title) { dataIndex = serie.context.pointerItemDataIndex; - if (dataIndex < 0) - return; - - var serieData = serie.GetSerieData(dataIndex); - if (serieData == null) - return; - var visualMap = chart.GetVisualMapOfSerie(serie); - var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); - - if (string.IsNullOrEmpty(category)) + if (serie.heatmapType == HeatmapType.Count) { - var xAxis = chart.GetChartComponent(serie.xAxisIndex); - if (xAxis != null) - category = xAxis.GetData((int) serieData.GetData(0)); + int value; + if (!m_CountDict.TryGetValue(dataIndex, out value)) return; + var visualMap = chart.GetVisualMapOfSerie(serie); + var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); + + title = serie.serieName; + + var param = serie.context.param; + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = dimension; + param.dataCount = serie.dataCount; + param.serieData = null; + param.color = visualMap.GetColor(value); + param.marker = SerieHelper.GetItemMarker(serie, null, marker); + param.itemFormatter = SerieHelper.GetItemFormatter(serie, null, itemFormatter); + param.numericFormatter = SerieHelper.GetNumericFormatter(serie, null, numericFormatter); + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add("count"); + param.columns.Add(ChartCached.NumberToStr(value, param.numericFormatter)); + + paramList.Add(param); } - title = serie.serieName; + else + { + if (dataIndex < 0) + return; - var param = serie.context.param; - param.serieName = serie.serieName; - param.serieIndex = serie.index; - param.dimension = dimension; - param.dataCount = serie.dataCount; - param.serieData = serieData; - param.color = serieData.context.color; - param.marker = SerieHelper.GetItemMarker(serie, serieData, marker); - param.itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); - param.numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); - param.columns.Clear(); + var serieData = serie.GetSerieData(dataIndex); + if (serieData == null) + return; + var visualMap = chart.GetVisualMapOfSerie(serie); + var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension); - param.columns.Add(param.marker); - param.columns.Add(category); - param.columns.Add(ChartCached.NumberToStr(serieData.GetData(dimension), param.numericFormatter)); + if (string.IsNullOrEmpty(category)) + { + var xAxis = chart.GetChartComponent(serie.xAxisIndex); + if (xAxis != null) + category = xAxis.GetData((int) serieData.GetData(0)); + } + title = serie.serieName; - paramList.Add(param); + var param = serie.context.param; + param.serieName = serie.serieName; + param.serieIndex = serie.index; + param.dimension = dimension; + param.dataCount = serie.dataCount; + param.serieData = serieData; + param.color = serieData.context.color; + param.marker = SerieHelper.GetItemMarker(serie, serieData, marker); + param.itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); + param.numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); + param.columns.Clear(); + + param.columns.Add(param.marker); + param.columns.Add(category); + param.columns.Add(ChartCached.NumberToStr(serieData.GetData(dimension), param.numericFormatter)); + + paramList.Add(param); + } } private void UpdateSerieContext() @@ -86,6 +131,8 @@ namespace XCharts.Runtime } return; } + if (serie.heatmapType == HeatmapType.Count) + return; m_LastCheckContextFlag = needCheck; if (m_LegendEnter) { @@ -120,7 +167,7 @@ namespace XCharts.Runtime } } - private void DrawHeatmapSerie(VertexHelper vh, Heatmap serie) + private void DrawDataHeatmapSerie(VertexHelper vh, Heatmap serie) { if (!serie.show || serie.animation.HasFadeOut()) return; XAxis xAxis; @@ -212,7 +259,6 @@ namespace XCharts.Runtime var highlight = (serieData.context.highlight) || visualMap.context.pointerIndex > 0; - //UGL.DrawRectangle(vh, pos, rectWid / 2, rectHig / 2, color); UGL.DrawRectangle(vh, serieData.context.rect, color); if (borderWidth > 0 && !ChartHelper.IsClearColor(borderColor)) @@ -243,5 +289,143 @@ namespace XCharts.Runtime chart.RefreshPainter(serie); } } + + private void DrawCountHeatmapSerie(VertexHelper vh, Heatmap serie) + { + if (!serie.show || serie.animation.HasFadeOut()) return; + XAxis xAxis; + YAxis yAxis; + if (!chart.TryGetChartComponent(out xAxis, serie.xAxisIndex)) return; + if (!chart.TryGetChartComponent(out yAxis, serie.yAxisIndex)) return; + m_SerieGrid = chart.GetChartComponent(xAxis.gridIndex); + xAxis.boundaryGap = true; + yAxis.boundaryGap = true; + var visualMap = chart.GetVisualMapOfSerie(serie); + var emphasisStyle = serie.emphasisStyle; + var xCount = AxisHelper.GetTotalSplitGridNum(xAxis); + var yCount = AxisHelper.GetTotalSplitGridNum(yAxis); + var xWidth = m_SerieGrid.context.width / xCount; + var yWidth = m_SerieGrid.context.height / yCount; + + var zeroX = m_SerieGrid.context.x; + var zeroY = m_SerieGrid.context.y; + var color = chart.theme.GetColor(serie.index); + var borderWidth = serie.itemStyle.show ? serie.itemStyle.borderWidth : 0; + var rectWid = xWidth - 2 * borderWidth; + var rectHig = yWidth - 2 * borderWidth; + + var borderColor = serie.itemStyle.opacity > 0 ? + serie.itemStyle.borderColor : + ChartConst.clearColor32; + borderColor.a = (byte) (borderColor.a * serie.itemStyle.opacity); + + var borderToColor = serie.itemStyle.opacity > 0 ? + serie.itemStyle.borderToColor : + ChartConst.clearColor32; + borderToColor.a = (byte) (borderToColor.a * serie.itemStyle.opacity); + + serie.animation.InitProgress(0, xCount); + var animationIndex = serie.animation.GetCurrIndex(); + var dataChanging = false; + serie.containerIndex = m_SerieGrid.index; + serie.containterInstanceId = m_SerieGrid.instanceId; + + m_CountDict.Clear(); + double minCount = 0, maxCount = 0; + foreach (var serieData in serie.data) + { + var xValue = serieData.GetData(0); + var yValue = serieData.GetData(1); + var i = AxisHelper.GetAxisValueSplitIndex(xAxis, xValue, xCount); + var j = AxisHelper.GetAxisValueSplitIndex(yAxis, yValue, yCount); + var key = GetGridKey(i, j); + var count = 0; + + if (!m_CountDict.TryGetValue(key, out count)) + count = 1; + else + count++; + if (count > maxCount) + maxCount = count; + m_CountDict[key] = count; + } + + if (visualMap.autoMinMax) + { + VisualMapHelper.SetMinMax(visualMap, minCount, maxCount); + } + var rangeMin = visualMap.rangeMin; + var rangeMax = visualMap.rangeMax; + + int highlightX = -1; + int highlightY = -1; + if (serie.context.pointerItemDataIndex > 0) + { + if (m_CountDict.ContainsKey(serie.context.pointerItemDataIndex)) + { + GetGridXYByKey(serie.context.pointerItemDataIndex, out highlightX, out highlightY); + } + } + + foreach (var kv in m_CountDict) + { + int i, j; + GetGridXYByKey(kv.Key, out i, out j); + var value = kv.Value; + + if (serie.IsIgnoreValue(value)) + { + continue; + } + + if ((value < rangeMin && rangeMin != visualMap.min) || + (value > rangeMax && rangeMax != visualMap.max)) + { + continue; + } + if (!visualMap.IsInSelectedValue(value)) + continue; + if (animationIndex >= 0 && i > animationIndex) + continue; + + var highlight = i == highlightX && j == highlightY; + + color = visualMap.GetColor(value); + if (highlight) + color = ChartHelper.GetHighlightColor(color); + + var pos = new Vector3(zeroX + (i + 0.5f) * xWidth, + zeroY + (j + 0.5f) * yWidth); + var rect = new Rect(pos.x - rectWid / 2, pos.y - rectHig / 2, rectWid, rectHig); + UGL.DrawRectangle(vh, rect, color); + + if (borderWidth > 0 && !ChartHelper.IsClearColor(borderColor)) + { + UGL.DrawBorder(vh, pos, rectWid, rectHig, borderWidth, borderColor, borderToColor); + } + if (visualMap.hoverLink && highlight && emphasisStyle != null && + emphasisStyle.itemStyle.borderWidth > 0) + { + var emphasisItemStyle = emphasisStyle.itemStyle; + var emphasisBorderWidth = emphasisItemStyle.borderWidth; + var emphasisBorderColor = emphasisItemStyle.opacity > 0 ? + emphasisItemStyle.borderColor : ChartConst.clearColor32; + var emphasisBorderToColor = emphasisItemStyle.opacity > 0 ? + emphasisItemStyle.borderToColor : ChartConst.clearColor32; + UGL.DrawBorder(vh, pos, rectWid, rectHig, emphasisBorderWidth, emphasisBorderColor, + emphasisBorderToColor); + } + + } + if (!serie.animation.IsFinish()) + { + serie.animation.CheckProgress(xCount); + chart.RefreshPainter(serie); + } + if (dataChanging) + { + chart.RefreshPainter(serie); + } + } } } \ No newline at end of file