[feature][polar] support heatmap in polar

This commit is contained in:
monitor1394
2022-09-20 13:16:22 +08:00
parent 4e05759c3c
commit ae35a4d7e6
9 changed files with 256 additions and 9 deletions

View File

@@ -58,6 +58,7 @@
## master ## master
* (2022.09.20) 增加`PolarChart``Heatmap`热力图的支持
* (2022.09.19) 增加`PolarChart`对多柱图和堆叠柱图的支持 * (2022.09.19) 增加`PolarChart`对多柱图和堆叠柱图的支持
* (2022.09.16) 增加`PolarChart``Bar`柱图的支持 * (2022.09.16) 增加`PolarChart``Bar`柱图的支持
* (2022.09.14) 增加`PolarCoord`可通过`radius`设置环形极坐标的支持 * (2022.09.14) 增加`PolarCoord`可通过`radius`设置环形极坐标的支持

View File

@@ -19,14 +19,21 @@ namespace XCharts.Editor
PropertyField("m_YAxisIndex"); PropertyField("m_YAxisIndex");
} }
PropertyField("m_BarType"); PropertyField("m_BarType");
PropertyField("m_BarPercentStack");
PropertyField("m_BarWidth"); PropertyField("m_BarWidth");
PropertyField("m_BarGap"); PropertyField("m_BarGap");
if (serie.IsUseCoord<PolarCoord>())
{
PropertyField("m_RoundCap");
}
else
{
PropertyField("m_BarPercentStack");
if (serie.barType == BarType.Zebra) if (serie.barType == BarType.Zebra)
{ {
PropertyField("m_BarZebraWidth"); PropertyField("m_BarZebraWidth");
PropertyField("m_BarZebraGap"); PropertyField("m_BarZebraGap");
} }
}
PropertyField("m_Clip"); PropertyField("m_Clip");
PropertyFiledMore(() => PropertyFiledMore(() =>
{ {

View File

@@ -7,6 +7,15 @@ namespace XCharts.Editor
{ {
public override void OnCustomInspectorGUI() public override void OnCustomInspectorGUI()
{ {
if (serie.IsUseCoord<PolarCoord>())
{
PropertyField("m_PolarIndex");
}
else
{
PropertyField("m_XAxisIndex");
PropertyField("m_YAxisIndex");
}
PropertyField("m_HeatmapType"); PropertyField("m_HeatmapType");
PropertyField("m_Ignore"); PropertyField("m_Ignore");
PropertyField("m_IgnoreValue"); PropertyField("m_IgnoreValue");

View File

@@ -225,6 +225,10 @@ namespace XCharts.Runtime
break; break;
} }
} }
else
{
iconType = Legend.Type.Rect;
}
} }
else else
{ {

View File

@@ -175,7 +175,7 @@ namespace XCharts.Runtime
for (int n = 0; n < m_StackSerieData.Count - 1; n++) for (int n = 0; n < m_StackSerieData.Count - 1; n++)
start += m_StackSerieData[n][i].context.stackHeight; start += m_StackSerieData[n][i].context.stackHeight;
} }
end = startAngle + m_AngleAxis.GetValueLength(angleValue, 360); end = start + m_AngleAxis.GetValueLength(angleValue, 360);
serieData.context.stackHeight = end - start; serieData.context.stackHeight = end - start;
inside = m_SeriePolar.context.insideRadius + categoryWidth * (float) radiusValue + gap; inside = m_SeriePolar.context.insideRadius + categoryWidth * (float) radiusValue + gap;
outside = inside + barWidth; outside = inside + barWidth;

View File

@@ -24,6 +24,7 @@ namespace XCharts.Runtime
[SerieHandler(typeof(HeatmapHandler), true)] [SerieHandler(typeof(HeatmapHandler), true)]
[DefaultAnimation(AnimationType.LeftToRight)] [DefaultAnimation(AnimationType.LeftToRight)]
[RequireChartComponent(typeof(VisualMap))] [RequireChartComponent(typeof(VisualMap))]
[CoordOptions(typeof(GridCoord), typeof(PolarCoord))]
[SerieExtraComponent(typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] [SerieExtraComponent(typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))]
[SerieDataExtraComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))] [SerieDataExtraComponent(typeof(ItemStyle), typeof(LabelStyle), typeof(EmphasisStyle), typeof(BlurStyle), typeof(SelectStyle))]
[SerieDataExtraField()] [SerieDataExtraField()]

View File

@@ -0,0 +1,202 @@
using UnityEngine;
using UnityEngine.UI;
using XUGL;
namespace XCharts.Runtime
{
/// <summary>
/// For polar coord
/// </summary>
internal sealed partial class HeatmapHandler
{
private PolarCoord m_SeriePolar;
private void UpdateSeriePolarContext()
{
if (m_SeriePolar == null)
return;
var needCheck = (chart.isPointerInChart && m_SeriePolar.IsPointerEnter()) || m_LegendEnter;
var lineWidth = 0f;
if (!needCheck)
{
if (m_LastCheckContextFlag != needCheck)
{
var needAnimation1 = false;
lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
m_LastCheckContextFlag = needCheck;
serie.context.pointerItemDataIndex = -1;
serie.context.pointerEnter = false;
serie.interact.SetValue(ref needAnimation1, lineWidth, false);
foreach (var serieData in serie.data)
{
var symbol = SerieHelper.GetSerieSymbol(serie, serieData);
var symbolSize = symbol.GetSize(serieData.data, chart.theme.serie.lineSymbolSize);
serieData.context.highlight = false;
serieData.interact.SetValue(ref needAnimation1, symbolSize);
}
if (needAnimation1)
{
if (SeriesHelper.IsStack(chart.series))
chart.RefreshTopPainter();
else
chart.RefreshPainter(serie);
}
}
return;
}
m_LastCheckContextFlag = needCheck;
var themeSymbolSize = chart.theme.serie.lineSymbolSize;
lineWidth = serie.lineStyle.GetWidth(chart.theme.serie.lineWidth);
var needInteract = false;
if (m_LegendEnter)
{
serie.context.pointerEnter = true;
serie.interact.SetValue(ref needInteract, lineWidth, true, chart.theme.serie.selectedRate);
for (int i = 0; i < serie.dataCount; i++)
{
var serieData = serie.data[i];
var size = SerieHelper.GetSysmbolSize(serie, serieData, chart.theme, themeSymbolSize, SerieState.Emphasis);
serieData.context.highlight = true;
serieData.interact.SetValue(ref needInteract, size);
}
}
else
{
serie.context.pointerItemDataIndex = -1;
serie.context.pointerEnter = false;
var dir = chart.pointerPos - new Vector2(m_SeriePolar.context.center.x, m_SeriePolar.context.center.y);
var pointerAngle = ChartHelper.GetAngle360(Vector2.up, dir);
var pointerRadius = Vector2.Distance(chart.pointerPos, m_SeriePolar.context.center);
Color32 color, toColor;
for (int i = 0; i < serie.dataCount; i++)
{
var serieData = serie.data[i];
if (pointerAngle >= serieData.context.startAngle &&
pointerAngle < serieData.context.toAngle &&
pointerRadius >= serieData.context.insideRadius &&
pointerRadius < serieData.context.outsideRadius)
{
serie.context.pointerItemDataIndex = i;
serie.context.pointerEnter = true;
serieData.context.highlight = true;
}
else
{
serieData.context.highlight = false;
}
var state = SerieHelper.GetSerieState(serie, serieData, true);
SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, state);
serieData.interact.SetColor(ref needInteract, color, toColor);
}
}
if (needInteract)
{
if (SeriesHelper.IsStack(chart.series))
chart.RefreshTopPainter();
else
chart.RefreshPainter(serie);
}
}
private void DrawPolarHeatmap(VertexHelper vh, Serie serie)
{
var datas = serie.data;
if (datas.Count <= 0)
return;
m_SeriePolar = chart.GetChartComponent<PolarCoord>(serie.polarIndex);
if (m_SeriePolar == null)
return;
var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, m_SeriePolar.index);
var m_RadiusAxis = ComponentHelper.GetRadiusAxis(chart.components, m_SeriePolar.index);
if (m_AngleAxis == null || m_RadiusAxis == null)
return;
var visualMap = chart.GetVisualMapOfSerie(serie);
var startAngle = m_AngleAxis.context.startAngle;
var currDetailProgress = 0f;
var totalDetailProgress = datas.Count;
var xCount = AxisHelper.GetTotalSplitGridNum(m_RadiusAxis);
var yCount = AxisHelper.GetTotalSplitGridNum(m_AngleAxis);
var xWidth = m_SeriePolar.context.radius / xCount;
var yWidth = 360 / yCount;
serie.animation.InitProgress(currDetailProgress, totalDetailProgress);
var dimension = VisualMapHelper.GetDimension(visualMap, defaultDimension);
if (visualMap.autoMinMax)
{
double maxValue, minValue;
SerieHelper.GetMinMaxData(serie, dimension, out minValue, out maxValue);
VisualMapHelper.SetMinMax(visualMap, minValue, maxValue);
}
var rangeMin = visualMap.rangeMin;
var rangeMax = visualMap.rangeMax;
var color = chart.theme.GetColor(serie.index);
float start, end;
float inside, outside;
double value, radiusValue, angleValue;
for (int i = 0; i < datas.Count; i++)
{
if (serie.animation.CheckDetailBreak(i))
break;
var serieData = datas[i];
var itemStyle = SerieHelper.GetItemStyle(serie, serieData);
var borderWidth = itemStyle.borderWidth;
var borderColor = itemStyle.borderColor;
radiusValue = serieData.GetData(0);
angleValue = serieData.GetData(1);
value = serieData.GetData(2);
var xIndex = AxisHelper.GetAxisValueSplitIndex(m_RadiusAxis, radiusValue, xCount);
var yIndex = AxisHelper.GetAxisValueSplitIndex(m_AngleAxis, angleValue, yCount);
start = startAngle + yIndex * yWidth;
end = start + yWidth;
inside = m_SeriePolar.context.insideRadius + xIndex * xWidth;
outside = inside + xWidth;
serieData.context.startAngle = start;
serieData.context.toAngle = end;
serieData.context.halfAngle = (start + end) / 2;
serieData.context.insideRadius = inside;
serieData.context.outsideRadius = outside;
if ((value < rangeMin && rangeMin != visualMap.min) ||
(value > rangeMax && rangeMax != visualMap.max))
{
continue;
}
if (!visualMap.IsInSelectedValue(value)) continue;
color = visualMap.GetColor(value);
if (serieData.context.highlight)
color = ChartHelper.GetHighlightColor(color);
var needRoundCap = serie.roundCap && inside > 0;
serieData.context.insideRadius = inside;
serieData.context.outsideRadius = outside;
serieData.context.areaCenter = m_SeriePolar.context.center;
serieData.context.position = ChartHelper.GetPosition(m_SeriePolar.context.center, (start + end) / 2, (inside + outside) / 2);
UGL.DrawDoughnut(vh, m_SeriePolar.context.center, inside, outside, color, color,
ColorUtil.clearColor32, start, end, borderWidth, borderColor, serie.gap / 2, chart.settings.cicleSmoothness,
needRoundCap, true);
}
if (!serie.animation.IsFinish())
{
serie.animation.CheckProgress(totalDetailProgress);
serie.animation.CheckSymbol(serie.symbol.GetSize(null, chart.theme.serie.lineSymbolSize));
chart.RefreshChart();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: baaa1d070b88a4b9bb7d1eed341041e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -6,7 +6,7 @@ using XUGL;
namespace XCharts.Runtime namespace XCharts.Runtime
{ {
[UnityEngine.Scripting.Preserve] [UnityEngine.Scripting.Preserve]
internal sealed class HeatmapHandler : SerieHandler<Heatmap> internal sealed partial class HeatmapHandler : SerieHandler<Heatmap>
{ {
private GridCoord m_SerieGrid; private GridCoord m_SerieGrid;
private Dictionary<int, int> m_CountDict = new Dictionary<int, int>(); private Dictionary<int, int> m_CountDict = new Dictionary<int, int>();
@@ -27,7 +27,10 @@ namespace XCharts.Runtime
public override void Update() public override void Update()
{ {
base.Update(); base.Update();
if (serie.IsUseCoord<GridCoord>())
UpdateSerieContext(); UpdateSerieContext();
else if (serie.IsUseCoord<PolarCoord>())
UpdateSeriePolarContext();
} }
public override void DrawSerie(VertexHelper vh) public override void DrawSerie(VertexHelper vh)
@@ -35,8 +38,17 @@ namespace XCharts.Runtime
if (serie.heatmapType == HeatmapType.Count) if (serie.heatmapType == HeatmapType.Count)
DrawCountHeatmapSerie(vh, serie); DrawCountHeatmapSerie(vh, serie);
else else
{
if (serie.IsUseCoord<PolarCoord>())
{
DrawPolarHeatmap(vh, serie);
}
else if (serie.IsUseCoord<GridCoord>())
{
DrawDataHeatmapSerie(vh, serie); DrawDataHeatmapSerie(vh, serie);
} }
}
}
public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category,
string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent,