436 lines
20 KiB
C#
436 lines
20 KiB
C#
|
using System.Collections.Generic;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.EventSystems;
|
||
|
using UnityEngine.UI;
|
||
|
using XUGL;
|
||
|
|
||
|
namespace XCharts.Runtime
|
||
|
{
|
||
|
[UnityEngine.Scripting.Preserve]
|
||
|
internal sealed class BarHandler : SerieHandler<Bar>
|
||
|
{
|
||
|
List<List<SerieData>> m_StackSerieData = new List<List<SerieData>>();
|
||
|
private GridCoord m_SerieGrid;
|
||
|
private float[] m_CapusleDefaultCornerRadius = new float[] { 1, 1, 1, 1 };
|
||
|
|
||
|
public override void Update()
|
||
|
{
|
||
|
base.Update();
|
||
|
UpdateSerieContext();
|
||
|
}
|
||
|
|
||
|
public override void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category,
|
||
|
string marker, string itemFormatter, string numericFormatter,
|
||
|
ref List<SerieParams> paramList, ref string title)
|
||
|
{
|
||
|
UpdateCoordSerieParams(ref paramList, ref title, dataIndex, showCategory, category,
|
||
|
marker, itemFormatter, numericFormatter);
|
||
|
}
|
||
|
|
||
|
public override void DrawSerie(VertexHelper vh)
|
||
|
{
|
||
|
DrawBarSerie(vh, serie, serie.context.colorIndex);
|
||
|
}
|
||
|
|
||
|
public override Vector3 GetSerieDataLabelPosition(SerieData serieData, LabelStyle label)
|
||
|
{
|
||
|
switch (label.position)
|
||
|
{
|
||
|
case LabelStyle.Position.Bottom:
|
||
|
var center = serieData.context.rect.center;
|
||
|
return new Vector3(center.x, center.y - serieData.context.rect.height / 2);
|
||
|
case LabelStyle.Position.Center:
|
||
|
case LabelStyle.Position.Inside:
|
||
|
return serieData.context.rect.center;
|
||
|
default:
|
||
|
return serieData.context.position;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnPointerDown(PointerEventData eventData)
|
||
|
{
|
||
|
if (!serie.context.pointerEnter) return;
|
||
|
if (serie.context.pointerItemDataIndex < 0) return;
|
||
|
if (chart.onPointerClickBar != null)
|
||
|
{
|
||
|
chart.onPointerClickBar(eventData, serie.context.pointerItemDataIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void UpdateSerieContext()
|
||
|
{
|
||
|
if (m_SerieGrid == null)
|
||
|
return;
|
||
|
|
||
|
var needCheck = (chart.isPointerInChart && m_SerieGrid.IsPointerEnter()) || 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;
|
||
|
serieData.interact.Reset();
|
||
|
}
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
m_LastCheckContextFlag = needCheck;
|
||
|
if (m_LegendEnter)
|
||
|
{
|
||
|
serie.context.pointerEnter = true;
|
||
|
foreach (var serieData in serie.data)
|
||
|
{
|
||
|
var barColor = SerieHelper.GetItemColor(serie, serieData, chart.theme, serie.context.colorIndex, true);
|
||
|
var barToColor = SerieHelper.GetItemToColor(serie, serieData, chart.theme, serie.context.colorIndex, true);
|
||
|
serieData.interact.SetColor(ref needInteract, barColor, barToColor);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
serie.context.pointerItemDataIndex = -1;
|
||
|
serie.context.pointerEnter = false;
|
||
|
foreach (var serieData in serie.data)
|
||
|
{
|
||
|
if (serie.context.pointerAxisDataIndexs.Contains(serieData.index) ||
|
||
|
serieData.context.rect.Contains(chart.pointerPos))
|
||
|
{
|
||
|
serie.context.pointerItemDataIndex = serieData.index;
|
||
|
serie.context.pointerEnter = true;
|
||
|
serieData.context.highlight = true;
|
||
|
|
||
|
var barColor = SerieHelper.GetItemColor(serie, serieData, chart.theme, serie.context.colorIndex, true);
|
||
|
var barToColor = SerieHelper.GetItemToColor(serie, serieData, chart.theme, serie.context.colorIndex, true);
|
||
|
serieData.interact.SetColor(ref needInteract, barColor, barToColor);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
serieData.context.highlight = false;
|
||
|
var barColor = SerieHelper.GetItemColor(serie, serieData, chart.theme, serie.context.colorIndex, false);
|
||
|
var barToColor = SerieHelper.GetItemToColor(serie, serieData, chart.theme, serie.context.colorIndex, false);
|
||
|
serieData.interact.SetColor(ref needInteract, barColor, barToColor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (needInteract)
|
||
|
{
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawBarSerie(VertexHelper vh, Bar serie, int colorIndex)
|
||
|
{
|
||
|
if (!serie.show || serie.animation.HasFadeOut())
|
||
|
return;
|
||
|
|
||
|
var isY = ComponentHelper.IsAnyCategoryOfYAxis(chart.components);
|
||
|
|
||
|
Axis axis;
|
||
|
Axis relativedAxis;
|
||
|
|
||
|
if (isY)
|
||
|
{
|
||
|
axis = chart.GetChartComponent<YAxis>(serie.yAxisIndex);
|
||
|
relativedAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
axis = chart.GetChartComponent<XAxis>(serie.xAxisIndex);
|
||
|
relativedAxis = chart.GetChartComponent<YAxis>(serie.yAxisIndex);
|
||
|
}
|
||
|
m_SerieGrid = chart.GetChartComponent<GridCoord>(axis.gridIndex);
|
||
|
|
||
|
if (axis == null)
|
||
|
return;
|
||
|
if (relativedAxis == null)
|
||
|
return;
|
||
|
if (m_SerieGrid == null)
|
||
|
return;
|
||
|
var dataZoom = chart.GetDataZoomOfAxis(axis);
|
||
|
var showData = serie.GetDataList(dataZoom);
|
||
|
|
||
|
if (showData.Count <= 0)
|
||
|
return;
|
||
|
|
||
|
var axisLength = isY ? m_SerieGrid.context.height : m_SerieGrid.context.width;
|
||
|
var relativedAxisLength = isY ? m_SerieGrid.context.width : m_SerieGrid.context.height;
|
||
|
var axisXY = isY ? m_SerieGrid.context.y : m_SerieGrid.context.x;
|
||
|
|
||
|
var isStack = SeriesHelper.IsStack<Bar>(chart.series, serie.stack);
|
||
|
if (isStack)
|
||
|
SeriesHelper.UpdateStackDataList(chart.series, serie, dataZoom, m_StackSerieData);
|
||
|
|
||
|
var barCount = chart.GetSerieBarRealCount<Bar>();
|
||
|
float categoryWidth = AxisHelper.GetDataWidth(axis, axisLength, showData.Count, dataZoom);
|
||
|
float barGap = chart.GetSerieBarGap<Bar>();
|
||
|
float totalBarWidth = chart.GetSerieTotalWidth<Bar>(categoryWidth, barGap, barCount);
|
||
|
float barWidth = serie.GetBarWidth(categoryWidth, barCount);
|
||
|
float offset = (categoryWidth - totalBarWidth) * 0.5f;
|
||
|
var serieReadIndex = chart.GetSerieIndexIfStack<Bar>(serie);
|
||
|
float gap = serie.barGap == -1 ? offset : offset + chart.GetSerieTotalGap<Bar>(categoryWidth, barGap, serieReadIndex);
|
||
|
int maxCount = serie.maxShow > 0 ?
|
||
|
(serie.maxShow > showData.Count ? showData.Count : serie.maxShow) :
|
||
|
showData.Count;
|
||
|
var isPercentStack = SeriesHelper.IsPercentStack<Bar>(chart.series, serie.stack);
|
||
|
bool dataChanging = false;
|
||
|
float dataChangeDuration = serie.animation.GetUpdateAnimationDuration();
|
||
|
double yMinValue = relativedAxis.context.minValue;
|
||
|
double yMaxValue = relativedAxis.context.maxValue;
|
||
|
|
||
|
var areaColor = ColorUtil.clearColor32;
|
||
|
var areaToColor = ColorUtil.clearColor32;
|
||
|
var interacting = false;
|
||
|
|
||
|
serie.containerIndex = m_SerieGrid.index;
|
||
|
serie.containterInstanceId = m_SerieGrid.instanceId;
|
||
|
serie.animation.InitProgress(axisXY, axisXY + axisLength);
|
||
|
for (int i = serie.minShow; i < maxCount; i++)
|
||
|
{
|
||
|
var serieData = showData[i];
|
||
|
serieData.index = i;
|
||
|
if (!serieData.show || serie.IsIgnoreValue(serieData))
|
||
|
{
|
||
|
serie.context.dataPoints.Add(Vector3.zero);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (serieData.IsDataChanged())
|
||
|
dataChanging = true;
|
||
|
|
||
|
var highlight = serieData.context.highlight || serie.highlight;
|
||
|
var itemStyle = SerieHelper.GetItemStyle(serie, serieData, highlight);
|
||
|
var value = axis.IsCategory() ? i : serieData.GetData(0, axis.inverse);
|
||
|
var relativedValue = serieData.GetCurrData(1, dataChangeDuration, relativedAxis.inverse, yMinValue, yMaxValue);
|
||
|
var borderWidth = relativedValue == 0 ? 0 : itemStyle.runtimeBorderWidth;
|
||
|
var borderGap = relativedValue == 0 ? 0 : itemStyle.borderGap;
|
||
|
var borderGapAndWidth = borderWidth + borderGap;
|
||
|
|
||
|
if (!serieData.interact.TryGetColor(ref areaColor, ref areaToColor, ref interacting))
|
||
|
{
|
||
|
areaColor = SerieHelper.GetItemColor(serie, serieData, chart.theme, colorIndex, highlight);
|
||
|
areaToColor = SerieHelper.GetItemToColor(serie, serieData, chart.theme, colorIndex, highlight);
|
||
|
serieData.interact.SetColor(ref interacting, areaColor, areaToColor);
|
||
|
}
|
||
|
|
||
|
var pX = 0f;
|
||
|
var pY = 0f;
|
||
|
UpdateXYPosition(m_SerieGrid, isY, axis, relativedAxis, i, categoryWidth, barWidth, isStack, value, ref pX, ref pY);
|
||
|
var barHig = 0f;
|
||
|
if (isPercentStack)
|
||
|
{
|
||
|
var valueTotal = chart.GetSerieSameStackTotalValue<Bar>(serie.stack, i);
|
||
|
barHig = valueTotal != 0 ? (float) (relativedValue / valueTotal * relativedAxisLength) : 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
barHig = AxisHelper.GetAxisValueLength(m_SerieGrid, relativedAxis, categoryWidth, relativedValue);
|
||
|
}
|
||
|
|
||
|
float currHig = AnimationStyleHelper.CheckDataAnimation(chart, serie, i, barHig);
|
||
|
Vector3 plb, plt, prt, prb, top;
|
||
|
UpdateRectPosition(m_SerieGrid, isY, relativedValue, pX, pY, gap, borderWidth, barWidth, currHig,
|
||
|
out plb, out plt, out prt, out prb, out top);
|
||
|
serieData.context.stackHeight = barHig;
|
||
|
serieData.context.position = top;
|
||
|
serieData.context.rect = Rect.MinMaxRect(plb.x + borderGapAndWidth, plb.y + borderGapAndWidth,
|
||
|
prt.x - borderGapAndWidth, prt.y - borderGapAndWidth);
|
||
|
serieData.context.backgroundRect = isY ?
|
||
|
Rect.MinMaxRect(m_SerieGrid.context.x, plb.y, m_SerieGrid.context.x + relativedAxisLength, prt.y) :
|
||
|
Rect.MinMaxRect(plb.x, m_SerieGrid.context.y, prb.x, m_SerieGrid.context.y + relativedAxisLength);
|
||
|
|
||
|
if (!serie.clip || (serie.clip && m_SerieGrid.Contains(top)))
|
||
|
serie.context.dataPoints.Add(top);
|
||
|
else
|
||
|
continue;
|
||
|
|
||
|
if (serie.show && currHig != 0 && !serie.placeHolder)
|
||
|
{
|
||
|
switch (serie.barType)
|
||
|
{
|
||
|
case BarType.Normal:
|
||
|
case BarType.Capsule:
|
||
|
DrawNormalBar(vh, serie, serieData, itemStyle, colorIndex, highlight, gap, barWidth,
|
||
|
pX, pY, plb, plt, prt, prb, isY, m_SerieGrid, axis, areaColor, areaToColor, relativedValue);
|
||
|
break;
|
||
|
case BarType.Zebra:
|
||
|
DrawZebraBar(vh, serie, serieData, itemStyle, colorIndex, highlight, gap, barWidth,
|
||
|
pX, pY, plb, plt, prt, prb, isY, m_SerieGrid, axis, areaColor, areaToColor);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (serie.animation.CheckDetailBreak(top, isY))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!serie.animation.IsFinish())
|
||
|
{
|
||
|
serie.animation.CheckProgress();
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
if (dataChanging || interacting)
|
||
|
{
|
||
|
chart.RefreshPainter(serie);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void UpdateXYPosition(GridCoord grid, bool isY, Axis axis, Axis relativedAxis, int i, float categoryWidth, float barWidth, bool isStack,
|
||
|
double value, ref float pX, ref float pY)
|
||
|
{
|
||
|
if (isY)
|
||
|
{
|
||
|
if (axis.IsCategory())
|
||
|
{
|
||
|
pY = grid.context.y + i * categoryWidth + (axis.boundaryGap ? 0 : -categoryWidth * 0.5f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (axis.context.minMaxRange <= 0) pY = grid.context.y;
|
||
|
else
|
||
|
{
|
||
|
var valueLen = (float) ((value - axis.context.minValue) / axis.context.minMaxRange) * grid.context.height;
|
||
|
pY = grid.context.y + valueLen - categoryWidth * 0.5f;
|
||
|
}
|
||
|
}
|
||
|
pX = AxisHelper.GetAxisValuePosition(grid, relativedAxis, categoryWidth, 0);
|
||
|
if (isStack)
|
||
|
{
|
||
|
for (int n = 0; n < m_StackSerieData.Count - 1; n++)
|
||
|
pX += m_StackSerieData[n][i].context.stackHeight;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (axis.IsCategory())
|
||
|
{
|
||
|
pX = grid.context.x + i * categoryWidth + (axis.boundaryGap ? 0 : -categoryWidth * 0.5f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (axis.context.minMaxRange <= 0) pX = grid.context.x;
|
||
|
else
|
||
|
{
|
||
|
var valueLen = (float) ((value - axis.context.minValue) / axis.context.minMaxRange) * grid.context.width;
|
||
|
pX = grid.context.x + valueLen - categoryWidth * 0.5f;
|
||
|
}
|
||
|
}
|
||
|
pY = AxisHelper.GetAxisValuePosition(grid, relativedAxis, categoryWidth, 0);
|
||
|
if (isStack)
|
||
|
{
|
||
|
for (int n = 0; n < m_StackSerieData.Count - 1; n++)
|
||
|
pY += m_StackSerieData[n][i].context.stackHeight;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void UpdateRectPosition(GridCoord grid, bool isY, double yValue, float pX, float pY, float gap, float borderWidth,
|
||
|
float barWidth, float currHig,
|
||
|
out Vector3 plb, out Vector3 plt, out Vector3 prt, out Vector3 prb, out Vector3 top)
|
||
|
{
|
||
|
if (isY)
|
||
|
{
|
||
|
if (yValue < 0)
|
||
|
{
|
||
|
plt = new Vector3(pX + currHig, pY + gap + barWidth);
|
||
|
prt = new Vector3(pX, pY + gap + barWidth);
|
||
|
prb = new Vector3(pX, pY + gap);
|
||
|
plb = new Vector3(pX + currHig, pY + gap);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plt = new Vector3(pX, pY + gap + barWidth);
|
||
|
prt = new Vector3(pX + currHig, pY + gap + barWidth);
|
||
|
prb = new Vector3(pX + currHig, pY + gap);
|
||
|
plb = new Vector3(pX, pY + gap);
|
||
|
}
|
||
|
top = new Vector3(pX + currHig, pY + gap + barWidth / 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (yValue < 0)
|
||
|
{
|
||
|
plb = new Vector3(pX + gap, pY + currHig);
|
||
|
plt = new Vector3(pX + gap, pY);
|
||
|
prt = new Vector3(pX + gap + barWidth, pY);
|
||
|
prb = new Vector3(pX + gap + barWidth, pY + currHig);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plb = new Vector3(pX + gap, pY);
|
||
|
plt = new Vector3(pX + gap, pY + currHig);
|
||
|
prt = new Vector3(pX + gap + barWidth, pY + currHig);
|
||
|
prb = new Vector3(pX + gap + barWidth, pY);
|
||
|
}
|
||
|
top = new Vector3(pX + gap + barWidth / 2, pY + currHig);
|
||
|
}
|
||
|
if (serie.clip)
|
||
|
{
|
||
|
plb = chart.ClampInGrid(grid, plb);
|
||
|
plt = chart.ClampInGrid(grid, plt);
|
||
|
prt = chart.ClampInGrid(grid, prt);
|
||
|
prb = chart.ClampInGrid(grid, prb);
|
||
|
top = chart.ClampInGrid(grid, top);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawNormalBar(VertexHelper vh, Serie serie, SerieData serieData, ItemStyle itemStyle, int colorIndex,
|
||
|
bool highlight, float gap, float barWidth, float pX, float pY, Vector3 plb, Vector3 plt, Vector3 prt,
|
||
|
Vector3 prb, bool isYAxis, GridCoord grid, Axis axis, Color32 areaColor, Color32 areaToColor, double value)
|
||
|
{
|
||
|
var borderWidth = itemStyle.runtimeBorderWidth;
|
||
|
var backgroundColor = SerieHelper.GetItemBackgroundColor(serie, serieData, chart.theme, colorIndex, highlight, false);
|
||
|
var cornerRadius = serie.barType == BarType.Capsule && !itemStyle.IsNeedCorner() ?
|
||
|
m_CapusleDefaultCornerRadius :
|
||
|
itemStyle.cornerRadius;
|
||
|
var invert = value < 0;
|
||
|
if (!ChartHelper.IsClearColor(backgroundColor))
|
||
|
{
|
||
|
UGL.DrawRoundRectangle(vh, serieData.context.backgroundRect, backgroundColor, backgroundColor, 0,
|
||
|
cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert);
|
||
|
}
|
||
|
UGL.DrawRoundRectangle(vh, serieData.context.rect, areaColor, areaToColor, 0,
|
||
|
cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert);
|
||
|
if (serie.barType == BarType.Capsule)
|
||
|
{
|
||
|
UGL.DrawBorder(vh, serieData.context.backgroundRect, borderWidth, itemStyle.borderColor,
|
||
|
0, cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert, -borderWidth);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UGL.DrawBorder(vh, serieData.context.rect, borderWidth, itemStyle.borderColor,
|
||
|
0, cornerRadius, isYAxis, chart.settings.cicleSmoothness, invert, itemStyle.borderGap);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawZebraBar(VertexHelper vh, Serie serie, SerieData serieData, ItemStyle itemStyle, int colorIndex,
|
||
|
bool highlight, float gap, float barWidth, float pX, float pY, Vector3 plb, Vector3 plt, Vector3 prt,
|
||
|
Vector3 prb, bool isYAxis, GridCoord grid, Axis axis, Color32 barColor, Color32 barToColor)
|
||
|
{
|
||
|
var backgroundColor = SerieHelper.GetItemBackgroundColor(serie, serieData, chart.theme, colorIndex, highlight, false);
|
||
|
if (!ChartHelper.IsClearColor(backgroundColor))
|
||
|
{
|
||
|
UGL.DrawRoundRectangle(vh, serieData.context.backgroundRect, backgroundColor, backgroundColor, 0,
|
||
|
null, isYAxis, chart.settings.cicleSmoothness, false);
|
||
|
}
|
||
|
if (isYAxis)
|
||
|
{
|
||
|
plt = (plb + plt) / 2;
|
||
|
prt = (prt + prb) / 2;
|
||
|
chart.DrawClipZebraLine(vh, plt, prt, barWidth / 2, serie.barZebraWidth, serie.barZebraGap,
|
||
|
barColor, barToColor, serie.clip, grid, grid.context.width);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plb = (prb + plb) / 2;
|
||
|
plt = (plt + prt) / 2;
|
||
|
chart.DrawClipZebraLine(vh, plb, plt, barWidth / 2, serie.barZebraWidth, serie.barZebraGap,
|
||
|
barColor, barToColor, serie.clip, grid, grid.context.height);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|