323 lines
14 KiB
C#
323 lines
14 KiB
C#
|
using System.Collections.Generic;
|
||
|
using System.Text;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.EventSystems;
|
||
|
using UnityEngine.UI;
|
||
|
using XUGL;
|
||
|
|
||
|
namespace XCharts.Runtime
|
||
|
{
|
||
|
[UnityEngine.Scripting.Preserve]
|
||
|
internal sealed class RingHandler : SerieHandler<Ring>
|
||
|
{
|
||
|
|
||
|
public override int defaultDimension { get { return 0; } }
|
||
|
|
||
|
public override void Update()
|
||
|
{
|
||
|
base.Update();
|
||
|
UpdateSerieContext();
|
||
|
}
|
||
|
|
||
|
private void UpdateSerieContext()
|
||
|
{
|
||
|
var needCheck = chart.isPointerInChart || m_LegendEnter;
|
||
|
var needInteract = false;
|
||
|
if (!needCheck)
|
||
|
{
|
||
|
if (m_LastCheckContextFlag != needCheck)
|
||
|
{
|
||
|
m_LastCheckContextFlag = needCheck;
|
||
|
serie.context.pointerItemDataIndex = -1;
|
||
|
serie.context.pointerEnter = false;
|
||
|
foreach (var serieData in serie.data)
|
||
|
{
|
||
|
serieData.context.highlight = false;
|
||
|
}
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
m_LastCheckContextFlag = needCheck;
|
||
|
if (m_LegendEnter)
|
||
|
{
|
||
|
serie.context.pointerEnter = true;
|
||
|
foreach (var serieData in serie.data)
|
||
|
{
|
||
|
serieData.context.highlight = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
serie.context.pointerEnter = false;
|
||
|
serie.context.pointerItemDataIndex = -1;
|
||
|
var ringIndex = GetRingIndex(chart.pointerPos);
|
||
|
foreach (var serieData in serie.data)
|
||
|
{
|
||
|
if (!needInteract && ringIndex == serieData.index)
|
||
|
{
|
||
|
serie.context.pointerEnter = true;
|
||
|
serie.context.pointerItemDataIndex = ringIndex;
|
||
|
serieData.context.highlight = true;
|
||
|
needInteract = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
serieData.context.highlight = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (needInteract)
|
||
|
{
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category,
|
||
|
string marker, string itemFormatter, string numericFormatter,
|
||
|
ref List<SerieParams> paramList, ref string title)
|
||
|
{
|
||
|
if (dataIndex < 0)
|
||
|
dataIndex = serie.context.pointerItemDataIndex;
|
||
|
|
||
|
if (dataIndex < 0)
|
||
|
return;
|
||
|
|
||
|
var serieData = serie.GetSerieData(dataIndex);
|
||
|
if (serieData == null)
|
||
|
return;
|
||
|
|
||
|
var param = serie.context.param;
|
||
|
param.serieName = serie.serieName;
|
||
|
param.serieIndex = serie.index;
|
||
|
param.category = category;
|
||
|
param.dimension = defaultDimension;
|
||
|
param.serieData = serieData;
|
||
|
param.dataCount = serie.dataCount;
|
||
|
param.value = serieData.GetData(0);
|
||
|
param.total = serieData.GetData(1);
|
||
|
param.color = SerieHelper.GetItemColor(serie, serieData, chart.theme, dataIndex, false);
|
||
|
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(serieData.name);
|
||
|
param.columns.Add(ChartCached.NumberToStr(param.value, param.numericFormatter));
|
||
|
|
||
|
paramList.Add(param);
|
||
|
}
|
||
|
|
||
|
public override Vector3 GetSerieDataLabelPosition(SerieData serieData, LabelStyle label)
|
||
|
{
|
||
|
var centerRadius = (serieData.context.outsideRadius + serieData.context.insideRadius) / 2;
|
||
|
var startAngle = serieData.context.startAngle;
|
||
|
var toAngle = serieData.context.toAngle;
|
||
|
switch (label.position)
|
||
|
{
|
||
|
case LabelStyle.Position.Bottom:
|
||
|
case LabelStyle.Position.Start:
|
||
|
var px1 = Mathf.Sin(startAngle * Mathf.Deg2Rad) * centerRadius;
|
||
|
var py1 = Mathf.Cos(startAngle * Mathf.Deg2Rad) * centerRadius;
|
||
|
var xDiff = serie.clockwise ? -label.distance : label.distance;
|
||
|
serieData.context.labelPosition = serie.context.center + new Vector3(px1 + xDiff, py1);
|
||
|
break;
|
||
|
case LabelStyle.Position.Top:
|
||
|
case LabelStyle.Position.End:
|
||
|
startAngle += serie.clockwise ? -label.distance : label.distance;
|
||
|
toAngle += serie.clockwise ? label.distance : -label.distance;
|
||
|
var px2 = Mathf.Sin(toAngle * Mathf.Deg2Rad) * centerRadius;
|
||
|
var py2 = Mathf.Cos(toAngle * Mathf.Deg2Rad) * centerRadius;
|
||
|
serieData.context.labelPosition = serie.context.center + new Vector3(px2, py2);
|
||
|
break;
|
||
|
default: //LabelStyle.Position.Center
|
||
|
serieData.context.labelPosition = serie.context.center + label.offset;
|
||
|
break;
|
||
|
}
|
||
|
return serieData.context.labelPosition;
|
||
|
}
|
||
|
|
||
|
public override void DrawSerie(VertexHelper vh)
|
||
|
{
|
||
|
if (!serie.show || serie.animation.HasFadeOut()) return;
|
||
|
var data = serie.data;
|
||
|
serie.animation.InitProgress(serie.startAngle, serie.startAngle + 360);
|
||
|
SerieHelper.UpdateCenter(serie, chart.chartPosition, chart.chartWidth, chart.chartHeight);
|
||
|
var dataChangeDuration = serie.animation.GetUpdateAnimationDuration();
|
||
|
var ringWidth = serie.context.outsideRadius - serie.context.insideRadius;
|
||
|
var dataChanging = false;
|
||
|
for (int j = 0; j < data.Count; j++)
|
||
|
{
|
||
|
var serieData = data[j];
|
||
|
serieData.index = j;
|
||
|
if (!serieData.show) continue;
|
||
|
if (serieData.IsDataChanged()) dataChanging = true;
|
||
|
var value = serieData.GetFirstData(dataChangeDuration);
|
||
|
var max = serieData.GetLastData();
|
||
|
var degree = (float) (360 * value / max);
|
||
|
var startDegree = GetStartAngle(serie);
|
||
|
var toDegree = GetToAngle(serie, degree);
|
||
|
var itemStyle = SerieHelper.GetItemStyle(serie, serieData, serieData.context.highlight);
|
||
|
var colorIndex = chart.GetLegendRealShowNameIndex(serieData.legendName);
|
||
|
var itemColor = SerieHelper.GetItemColor(serie, serieData, chart.theme, colorIndex, serieData.context.highlight);
|
||
|
var itemToColor = SerieHelper.GetItemToColor(serie, serieData, chart.theme, colorIndex, serieData.context.highlight);
|
||
|
var outsideRadius = serie.context.outsideRadius - j * (ringWidth + serie.gap);
|
||
|
var insideRadius = outsideRadius - ringWidth;
|
||
|
var borderWidth = itemStyle.borderWidth;
|
||
|
var borderColor = itemStyle.borderColor;
|
||
|
var roundCap = serie.roundCap && insideRadius > 0;
|
||
|
|
||
|
serieData.context.startAngle = serie.clockwise ? startDegree : toDegree;
|
||
|
serieData.context.toAngle = serie.clockwise ? toDegree : startDegree;
|
||
|
serieData.context.insideRadius = insideRadius;
|
||
|
serieData.context.outsideRadius = serieData.radius > 0 ? serieData.radius : outsideRadius;
|
||
|
DrawBackground(vh, serie, serieData, j, insideRadius, outsideRadius);
|
||
|
UGL.DrawDoughnut(vh, serie.context.center, insideRadius, outsideRadius, itemColor, itemToColor,
|
||
|
Color.clear, startDegree, toDegree, borderWidth, borderColor, 0, chart.settings.cicleSmoothness,
|
||
|
roundCap, serie.clockwise);
|
||
|
DrawCenter(vh, serie, serieData, insideRadius, j == data.Count - 1);
|
||
|
}
|
||
|
if (!serie.animation.IsFinish())
|
||
|
{
|
||
|
serie.animation.CheckProgress(360);
|
||
|
chart.RefreshChart();
|
||
|
}
|
||
|
if (dataChanging)
|
||
|
{
|
||
|
chart.RefreshChart();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnLegendButtonClick(int index, string legendName, bool show)
|
||
|
{
|
||
|
if (!serie.IsLegendName(legendName))
|
||
|
return;
|
||
|
LegendHelper.CheckDataShow(serie, legendName, show);
|
||
|
chart.UpdateLegendColor(legendName, show);
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
|
||
|
public override void OnLegendButtonEnter(int index, string legendName)
|
||
|
{
|
||
|
if (!serie.IsLegendName(legendName))
|
||
|
return;
|
||
|
LegendHelper.CheckDataHighlighted(serie, legendName, true);
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
|
||
|
public override void OnLegendButtonExit(int index, string legendName)
|
||
|
{
|
||
|
if (!serie.IsLegendName(legendName))
|
||
|
return;
|
||
|
LegendHelper.CheckDataHighlighted(serie, legendName, false);
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
|
||
|
public override void OnPointerDown(PointerEventData eventData)
|
||
|
{ }
|
||
|
|
||
|
private float GetStartAngle(Serie serie)
|
||
|
{
|
||
|
return serie.clockwise ? serie.startAngle : 360 - serie.startAngle;
|
||
|
}
|
||
|
|
||
|
private float GetToAngle(Serie serie, float angle)
|
||
|
{
|
||
|
var toAngle = angle + serie.startAngle;
|
||
|
if (!serie.clockwise)
|
||
|
{
|
||
|
toAngle = 360 - angle - serie.startAngle;
|
||
|
}
|
||
|
if (!serie.animation.IsFinish())
|
||
|
{
|
||
|
var currAngle = serie.animation.GetCurrDetail();
|
||
|
if (serie.clockwise)
|
||
|
{
|
||
|
toAngle = toAngle > currAngle ? currAngle : toAngle;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
toAngle = toAngle < 360 - currAngle ? 360 - currAngle : toAngle;
|
||
|
}
|
||
|
}
|
||
|
return toAngle;
|
||
|
}
|
||
|
|
||
|
private void DrawCenter(VertexHelper vh, Serie serie, SerieData serieData, float insideRadius, bool last)
|
||
|
{
|
||
|
var itemStyle = SerieHelper.GetItemStyle(serie, serieData);
|
||
|
if (!ChartHelper.IsClearColor(itemStyle.centerColor) && last)
|
||
|
{
|
||
|
var radius = insideRadius - itemStyle.centerGap;
|
||
|
var smoothness = chart.settings.cicleSmoothness;
|
||
|
UGL.DrawCricle(vh, serie.context.center, radius, itemStyle.centerColor, smoothness);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawBackground(VertexHelper vh, Serie serie, SerieData serieData, int index, float insideRadius, float outsideRadius)
|
||
|
{
|
||
|
var itemStyle = SerieHelper.GetItemStyle(serie, serieData);
|
||
|
var backgroundColor = SerieHelper.GetItemBackgroundColor(serie, serieData, chart.theme, index, false);
|
||
|
if (itemStyle.backgroundWidth != 0)
|
||
|
{
|
||
|
var centerRadius = (outsideRadius + insideRadius) / 2;
|
||
|
var inradius = centerRadius - itemStyle.backgroundWidth / 2;
|
||
|
var outradius = centerRadius + itemStyle.backgroundWidth / 2;
|
||
|
UGL.DrawDoughnut(vh, serie.context.center, inradius,
|
||
|
outradius, backgroundColor, Color.clear, chart.settings.cicleSmoothness);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UGL.DrawDoughnut(vh, serie.context.center, insideRadius,
|
||
|
outsideRadius, backgroundColor, Color.clear, chart.settings.cicleSmoothness);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawBorder(VertexHelper vh, Serie serie, SerieData serieData, float insideRadius, float outsideRadius)
|
||
|
{
|
||
|
var itemStyle = SerieHelper.GetItemStyle(serie, serieData);
|
||
|
if (itemStyle.show && itemStyle.borderWidth > 0 && !ChartHelper.IsClearColor(itemStyle.borderColor))
|
||
|
{
|
||
|
UGL.DrawDoughnut(vh, serie.context.center, outsideRadius,
|
||
|
outsideRadius + itemStyle.borderWidth, itemStyle.borderColor,
|
||
|
Color.clear, chart.settings.cicleSmoothness);
|
||
|
UGL.DrawDoughnut(vh, serie.context.center, insideRadius,
|
||
|
insideRadius + itemStyle.borderWidth, itemStyle.borderColor,
|
||
|
Color.clear, chart.settings.cicleSmoothness);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private int GetRingIndex(Vector2 local)
|
||
|
{
|
||
|
var dist = Vector2.Distance(local, serie.context.center);
|
||
|
if (dist > serie.context.outsideRadius) return -1;
|
||
|
Vector2 dir = local - new Vector2(serie.context.center.x, serie.context.center.y);
|
||
|
float angle = VectorAngle(Vector2.up, dir);
|
||
|
for (int i = 0; i < serie.data.Count; i++)
|
||
|
{
|
||
|
var serieData = serie.data[i];
|
||
|
serieData.index = i;
|
||
|
if (dist >= serieData.context.insideRadius &&
|
||
|
dist <= serieData.context.outsideRadius &&
|
||
|
angle >= serieData.context.startAngle &&
|
||
|
angle <= serieData.context.toAngle)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
private float VectorAngle(Vector2 from, Vector2 to)
|
||
|
{
|
||
|
float angle;
|
||
|
|
||
|
Vector3 cross = Vector3.Cross(from, to);
|
||
|
angle = Vector2.Angle(from, to);
|
||
|
angle = cross.z > 0 ? -angle : angle;
|
||
|
angle = (angle + 360) % 360;
|
||
|
return angle;
|
||
|
}
|
||
|
}
|
||
|
}
|