Files
XCharts/Scripts/BaseAxesChart.cs

601 lines
20 KiB
C#
Raw Normal View History

2018-09-28 06:34:19 +08:00
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
2019-03-12 08:10:25 +08:00
using System.Text;
2018-09-28 06:34:19 +08:00
namespace XCharts
2018-09-28 06:34:19 +08:00
{
[System.Serializable]
public class Coordinate
{
public bool show = true;
public float left = 40f;
2018-10-01 17:00:15 +08:00
public float right = 30f;
public float top = 40;
public float bottom = 25f;
public float tickness = 0.6f;
2018-10-13 07:03:54 +08:00
public float splitWidth = 5.0f;
2018-09-28 06:34:19 +08:00
}
[System.Serializable]
public enum AxisType
{
value,
category,
time,
log
}
2018-10-01 17:00:15 +08:00
public enum SplitLineType
{
solid,
dashed,
dotted
}
2018-09-28 06:34:19 +08:00
[System.Serializable]
public class Axis
{
public AxisType type;
public int splitNumber = 5;
public int maxSplitNumber = 5;
2018-10-01 17:00:15 +08:00
public bool showSplitLine = true;
public SplitLineType splitLineType = SplitLineType.dashed;
2018-09-28 06:34:19 +08:00
public bool boundaryGap = true;
2019-04-02 00:24:57 +08:00
[SerializeField]
private List<string> data;
private List<string> Data { get { return data; } }
2018-09-28 06:34:19 +08:00
2019-03-31 09:11:35 +08:00
public void AddData(string category)
2018-09-28 06:34:19 +08:00
{
2018-10-13 07:03:54 +08:00
if (data.Count >= maxSplitNumber && maxSplitNumber != 0)
2018-09-28 06:34:19 +08:00
{
data.RemoveAt(0);
}
data.Add(category);
}
2019-03-31 09:11:35 +08:00
2019-04-02 00:24:57 +08:00
public string GetData(int index)
{
return Data[index];
2019-03-31 09:11:35 +08:00
}
public int GetSplitNumber()
{
2019-04-02 00:24:57 +08:00
if (Data.Count > 2 * splitNumber || Data.Count <= 0)
2019-03-31 09:11:35 +08:00
return splitNumber;
else
2019-04-02 00:24:57 +08:00
return Data.Count;
2019-03-31 09:11:35 +08:00
}
public float GetSplitWidth(float coordinateWidth)
{
return coordinateWidth / (boundaryGap ? GetSplitNumber(): GetSplitNumber() - 1);
}
public int GetDataNumber()
{
2019-04-02 00:24:57 +08:00
return Data.Count;
2019-03-31 09:11:35 +08:00
}
public float GetDataWidth(float coordinateWidth)
{
2019-04-02 00:24:57 +08:00
return coordinateWidth / (boundaryGap ? Data.Count : Data.Count - 1);
}
public string GetScaleName(int index, float maxData = 0)
{
if (type == AxisType.value)
{
return ((int)(maxData * index / GetSplitNumber())).ToString();
}
int dataCount = Data.Count;
if (dataCount <= 0) return "";
float rate = dataCount / GetScaleNumber();
if (rate < 1) rate = 1;
int newIndex = (int)(index * rate >= dataCount - 1 ? dataCount - 1 : index * rate);
return Data[newIndex];
2019-03-31 09:11:35 +08:00
}
public int GetScaleNumber()
{
2019-04-02 00:24:57 +08:00
if (Data.Count > 2 * splitNumber || Data.Count <= 0)
2019-03-31 09:11:35 +08:00
return boundaryGap ? splitNumber + 1 : splitNumber;
else
2019-04-02 00:24:57 +08:00
return boundaryGap ? Data.Count + 1 : Data.Count;
2019-03-31 09:11:35 +08:00
}
public float GetScaleWidth(float coordinateWidth)
{
int num = GetScaleNumber() - 1;
if (num <= 0) num = 1;
return coordinateWidth / num;
}
2018-09-28 06:34:19 +08:00
}
[System.Serializable]
public class XAxis : Axis
{
}
[System.Serializable]
public class YAxis : Axis
{
}
public class BaseAxesChart : BaseChart
{
private const int DEFAULT_YSACLE_NUM = 5;
private const string YSCALE_TEXT_PREFIX = "yScale";
private const string XSCALE_TEXT_PREFIX = "xScale";
[SerializeField]
2018-10-01 17:00:15 +08:00
protected Coordinate coordinate = new Coordinate();
2018-09-28 06:34:19 +08:00
[SerializeField]
2018-10-01 17:00:15 +08:00
protected XAxis xAxis = new XAxis();
2018-09-28 06:34:19 +08:00
[SerializeField]
2018-10-01 17:00:15 +08:00
protected YAxis yAxis = new YAxis();
2018-09-28 06:34:19 +08:00
private float lastXMaxValue;
private float lastYMaxValue;
private float lastCoordinateWid;
private float lastCoordinateHig;
private float lastCoordinateScaleLen;
private XAxis checkXAxis = new XAxis();
private YAxis checkYAxis = new YAxis();
private Coordinate checkCoordinate = new Coordinate();
protected List<Text> yScaleTextList = new List<Text>();
protected List<Text> xScaleTextList = new List<Text>();
protected float zeroX { get { return coordinate.left; } }
protected float zeroY { get { return coordinate.bottom; } }
protected float coordinateWid { get { return chartWid - coordinate.left - coordinate.right; } }
protected float coordinateHig { get { return chartHig - coordinate.top - coordinate.bottom; } }
2019-04-02 00:24:57 +08:00
public Axis XAxis { get { return xAxis; } }
public Axis YAxis { get { return yAxis; } }
2018-09-28 06:34:19 +08:00
protected override void Awake()
{
base.Awake();
lastCoordinateHig = chartHig;
lastCoordinateWid = chartWid;
2018-10-13 07:03:54 +08:00
lastCoordinateScaleLen = coordinate.splitWidth;
2019-04-01 23:05:35 +08:00
checkXAxis = xAxis;
checkYAxis = yAxis;
2018-09-28 06:34:19 +08:00
InitXScale();
InitYScale();
}
protected override void Update()
{
base.Update();
CheckYAxisType();
CheckXAxisType();
CheckMaxValue();
CheckCoordinate();
}
2019-03-12 08:10:25 +08:00
protected override void DrawChart(VertexHelper vh)
2018-09-28 06:34:19 +08:00
{
2019-03-12 08:10:25 +08:00
base.DrawChart(vh);
2018-09-28 06:34:19 +08:00
DrawCoordinate(vh);
}
2019-03-12 08:10:25 +08:00
protected override void CheckTootipArea(Vector2 local)
{
if (local.x < zeroX || local.x > zeroX + coordinateWid ||
local.y < zeroY || local.y > zeroY + coordinateHig)
{
tooltip.DataIndex = 0;
RefreshTooltip();
}
else
{
if (xAxis.type == AxisType.value)
{
2019-03-31 09:11:35 +08:00
float splitWid =yAxis.GetDataWidth(coordinateHig);
for (int i = 0; i < yAxis.GetDataNumber(); i++)
2019-03-12 08:10:25 +08:00
{
float pY = zeroY + i * splitWid;
if (yAxis.boundaryGap)
{
if (local.y > pY && local.y <= pY + splitWid)
{
tooltip.DataIndex = i + 1;
break;
}
}
else
{
if (local.y > pY - splitWid / 2 && local.y <= pY + splitWid / 2)
{
tooltip.DataIndex = i + 1;
break;
}
}
}
}
else
{
2019-03-31 09:11:35 +08:00
float splitWid =xAxis.GetDataWidth(coordinateWid);
for (int i = 0; i < xAxis.GetDataNumber(); i++)
2019-03-12 08:10:25 +08:00
{
float pX = zeroX + i * splitWid;
if (xAxis.boundaryGap)
{
if (local.x > pX && local.x <= pX + splitWid)
{
tooltip.DataIndex = i + 1;
break;
}
}
else
{
if (local.x > pX - splitWid / 2 && local.x <= pX + splitWid / 2)
{
tooltip.DataIndex = i + 1;
break;
}
}
}
}
}
if (tooltip.DataIndex > 0)
{
tooltip.UpdatePos(new Vector2(local.x + 18, local.y - 25));
RefreshTooltip();
if (tooltip.LastDataIndex != tooltip.DataIndex)
{
RefreshChart();
}
tooltip.LastDataIndex = tooltip.DataIndex;
}
}
protected override void RefreshTooltip()
{
base.RefreshTooltip();
int index = tooltip.DataIndex - 1;
Axis tempAxis = xAxis.type == AxisType.value ? (Axis)yAxis : (Axis)xAxis;
2019-03-31 09:11:35 +08:00
if (index < 0)
{
tooltip.SetActive(false);
return;
}
2019-03-12 08:10:25 +08:00
tooltip.SetActive(true);
if (seriesList.Count == 1)
{
2019-04-02 00:24:57 +08:00
string txt = tempAxis.GetData(index) + ": " + seriesList[0].DataList[index];
2019-03-12 08:10:25 +08:00
tooltip.UpdateTooltipText(txt);
}
else
{
2019-04-02 00:24:57 +08:00
StringBuilder sb = new StringBuilder(tempAxis.GetData(index));
2019-03-16 07:49:36 +08:00
for (int i = 0; i < seriesList.Count; i++)
2019-03-12 08:10:25 +08:00
{
string strColor = ColorUtility.ToHtmlStringRGBA(themeInfo.GetColor(i));
string key = seriesList[i].name;
2019-04-02 00:24:57 +08:00
float value = seriesList[i].DataList[index];
2019-03-12 08:10:25 +08:00
sb.Append("\n");
sb.AppendFormat("<color=#{0}>● </color>", strColor);
sb.AppendFormat("{0}: {1}", key, value);
}
tooltip.UpdateTooltipText(sb.ToString());
}
var pos = tooltip.GetPos();
if (pos.x + tooltip.Width > chartWid)
{
pos.x = chartWid - tooltip.Width;
}
if (pos.y - tooltip.Height < 0)
{
pos.y = tooltip.Height;
}
tooltip.UpdatePos(pos);
}
TextGenerationSettings GetTextSetting()
{
var setting = new TextGenerationSettings();
var fontdata = FontData.defaultFontData;
//setting.generationExtents = rectTransform.rect.size;
setting.generationExtents = new Vector2(200.0F, 50.0F);
setting.fontSize = 14;
setting.textAnchor = TextAnchor.MiddleCenter;
setting.scaleFactor = 1f;
setting.color = Color.red;
setting.font = themeInfo.font;
setting.pivot = new Vector2(0.5f, 0.5f);
setting.richText = false;
setting.lineSpacing = 0;
setting.fontStyle = FontStyle.Normal;
setting.resizeTextForBestFit = false;
setting.horizontalOverflow = HorizontalWrapMode.Overflow;
setting.verticalOverflow = VerticalWrapMode.Overflow;
return setting;
}
2018-10-01 17:00:15 +08:00
protected override void OnThemeChanged()
{
base.OnThemeChanged();
InitXScale();
InitYScale();
}
2018-09-28 06:34:19 +08:00
public void AddXAxisCategory(string category)
{
2019-03-31 09:11:35 +08:00
xAxis.AddData(category);
2018-09-28 06:34:19 +08:00
OnXAxisChanged();
}
public void AddYAxisCategory(string category)
{
2019-03-31 09:11:35 +08:00
yAxis.AddData(category);
2018-09-28 06:34:19 +08:00
OnYAxisChanged();
}
private void InitYScale()
{
yScaleTextList.Clear();
2019-03-31 09:11:35 +08:00
float max = GetMaxValue();
float scaleWid = yAxis.GetScaleWidth(coordinateHig);
for (int i = 0; i < yAxis.GetSplitNumber(); i++)
2018-09-28 06:34:19 +08:00
{
2019-03-31 09:11:35 +08:00
Text txt = ChartUtils.AddTextObject(YSCALE_TEXT_PREFIX + i, transform, themeInfo.font,
2018-10-01 17:00:15 +08:00
themeInfo.textColor, TextAnchor.MiddleRight, Vector2.zero, Vector2.zero,
new Vector2(1, 0.5f),
2018-09-28 06:34:19 +08:00
new Vector2(coordinate.left, 20));
2019-03-31 09:11:35 +08:00
txt.transform.localPosition = GetYScalePosition(scaleWid, i);
2019-04-02 00:24:57 +08:00
txt.text = yAxis.GetScaleName(i, max);
2019-03-31 09:11:35 +08:00
txt.gameObject.SetActive(coordinate.show);
yScaleTextList.Add(txt);
2018-09-28 06:34:19 +08:00
}
}
2019-03-31 09:11:35 +08:00
public void InitXScale()
2018-09-28 06:34:19 +08:00
{
xScaleTextList.Clear();
2019-03-31 09:11:35 +08:00
float max = GetMaxValue();
float scaleWid = xAxis.GetScaleWidth(coordinateWid);
for (int i = 0; i < xAxis.GetSplitNumber(); i++)
2018-09-28 06:34:19 +08:00
{
2019-03-31 09:11:35 +08:00
Text txt = ChartUtils.AddTextObject(XSCALE_TEXT_PREFIX + i, transform, themeInfo.font,
themeInfo.textColor, TextAnchor.MiddleCenter, Vector2.zero, Vector2.zero,
new Vector2(1, 0.5f),
new Vector2(scaleWid, 20));
txt.transform.localPosition = GetXScalePosition(scaleWid, i);
2019-04-01 23:05:35 +08:00
2019-04-02 00:24:57 +08:00
txt.text = xAxis.GetScaleName(i, max);
2019-03-31 09:11:35 +08:00
txt.gameObject.SetActive(coordinate.show);
xScaleTextList.Add(txt);
2018-09-28 06:34:19 +08:00
}
}
2019-03-31 09:11:35 +08:00
private Vector3 GetYScalePosition(float scaleWid,int i)
2018-09-28 06:34:19 +08:00
{
2019-03-31 09:11:35 +08:00
if (yAxis.boundaryGap)
2018-09-28 06:34:19 +08:00
{
2018-10-13 07:03:54 +08:00
return new Vector3(zeroX - coordinate.splitWidth - 2f,
2019-03-31 09:11:35 +08:00
zeroY + (i + 0.5f) * scaleWid, 0);
2018-09-28 06:34:19 +08:00
}
else
{
2019-03-31 09:11:35 +08:00
return new Vector3(zeroX - coordinate.splitWidth - 2f,
zeroY + i * scaleWid, 0);
2018-09-28 06:34:19 +08:00
}
}
2019-03-31 09:11:35 +08:00
private Vector3 GetXScalePosition(float scaleWid,int i)
2018-09-28 06:34:19 +08:00
{
2019-03-31 09:11:35 +08:00
if (xAxis.boundaryGap)
2018-09-28 06:34:19 +08:00
{
2019-03-31 09:11:35 +08:00
return new Vector3(zeroX + (i + 1) * scaleWid, zeroY - coordinate.splitWidth - 5, 0);
2018-09-28 06:34:19 +08:00
}
else
{
2019-03-31 09:11:35 +08:00
return new Vector3(zeroX + (i + 1 - 0.5f) * scaleWid,
zeroY - coordinate.splitWidth - 10, 0);
2018-09-28 06:34:19 +08:00
}
}
private void CheckCoordinate()
{
if (lastCoordinateHig != coordinateHig
|| lastCoordinateWid != coordinateWid
2018-10-13 07:03:54 +08:00
|| lastCoordinateScaleLen != coordinate.splitWidth)
2018-09-28 06:34:19 +08:00
{
lastCoordinateWid = coordinateWid;
lastCoordinateHig = coordinateHig;
2018-10-13 07:03:54 +08:00
lastCoordinateScaleLen = coordinate.splitWidth;
2018-09-28 06:34:19 +08:00
OnCoordinateSize();
}
if (checkCoordinate.show != coordinate.show)
{
checkCoordinate.show = coordinate.show;
OnXAxisChanged();
OnYAxisChanged();
}
}
private void CheckYAxisType()
{
if (checkYAxis.type != yAxis.type ||
checkYAxis.boundaryGap != yAxis.boundaryGap ||
checkYAxis.showSplitLine != yAxis.showSplitLine ||
checkYAxis.splitNumber != yAxis.splitNumber)
{
checkYAxis.type = yAxis.type;
checkYAxis.boundaryGap = yAxis.boundaryGap;
checkYAxis.showSplitLine = yAxis.showSplitLine;
checkYAxis.splitNumber = yAxis.splitNumber;
OnYAxisChanged();
}
}
private void CheckXAxisType()
{
if (checkXAxis.type != xAxis.type ||
checkXAxis.boundaryGap != xAxis.boundaryGap ||
checkXAxis.showSplitLine != xAxis.showSplitLine ||
checkXAxis.splitNumber != xAxis.splitNumber)
{
checkXAxis.type = xAxis.type;
checkXAxis.boundaryGap = xAxis.boundaryGap;
checkXAxis.showSplitLine = xAxis.showSplitLine;
checkXAxis.splitNumber = xAxis.splitNumber;
OnXAxisChanged();
}
}
private void CheckMaxValue()
{
if (xAxis.type == AxisType.value)
{
float max = GetMaxValue();
if (lastXMaxValue != max)
{
lastXMaxValue = max;
OnXMaxValueChanged();
}
}
else if (yAxis.type == AxisType.value)
{
float max = GetMaxValue();
if (lastYMaxValue != max)
{
lastYMaxValue = max;
OnYMaxValueChanged();
}
}
}
protected virtual void OnCoordinateSize()
{
2019-04-01 23:05:35 +08:00
InitXScale();
InitYScale();
2018-09-28 06:34:19 +08:00
}
protected virtual void OnYAxisChanged()
{
HideChild(YSCALE_TEXT_PREFIX);
InitYScale();
}
protected virtual void OnXAxisChanged()
{
HideChild(XSCALE_TEXT_PREFIX);
InitXScale();
}
protected virtual void OnXMaxValueChanged()
{
float max = GetMaxValue();
for (int i = 0; i < xScaleTextList.Count; i++)
{
xScaleTextList[i].text = ((int)(max * i / xScaleTextList.Count)).ToString();
}
}
2019-04-01 23:05:35 +08:00
protected override void OnSizeChanged()
{
base.OnSizeChanged();
InitXScale();
InitYScale();
}
2018-09-28 06:34:19 +08:00
protected override void OnYMaxValueChanged()
{
float max = GetMaxValue();
for (int i = 0; i < yScaleTextList.Count; i++)
{
yScaleTextList[i].text = ((int)(max * i / (yScaleTextList.Count - 1))).ToString();
}
}
private void DrawCoordinate(VertexHelper vh)
{
if (!coordinate.show) return;
2018-10-01 17:00:15 +08:00
// draw splitline
2019-03-31 09:11:35 +08:00
for (int i = 1; i < yAxis.GetScaleNumber(); i++)
2018-09-28 06:34:19 +08:00
{
2018-10-13 07:03:54 +08:00
float pX = zeroX - coordinate.splitWidth;
2019-03-31 09:11:35 +08:00
float pY = zeroY + i * coordinateHig / (yAxis.GetScaleNumber() - 1);
2018-09-28 06:34:19 +08:00
ChartUtils.DrawLine(vh, new Vector3(pX, pY), new Vector3(zeroX, pY), coordinate.tickness,
2018-10-03 22:30:25 +08:00
themeInfo.axisLineColor);
2018-09-28 06:34:19 +08:00
if (yAxis.showSplitLine)
{
2019-03-16 07:49:36 +08:00
DrawSplitLine(vh, true, yAxis.splitLineType, new Vector3(zeroX, pY),
2018-10-01 17:00:15 +08:00
new Vector3(zeroX + coordinateWid, pY));
2018-09-28 06:34:19 +08:00
}
}
2019-03-31 09:11:35 +08:00
for (int i = 1; i < xAxis.GetScaleNumber(); i++)
2018-09-28 06:34:19 +08:00
{
2019-03-31 09:11:35 +08:00
float pX = zeroX + i * coordinateWid / (xAxis.GetScaleNumber() - 1);
2018-10-13 07:03:54 +08:00
float pY = zeroY - coordinate.splitWidth - 2;
2018-09-28 06:34:19 +08:00
ChartUtils.DrawLine(vh, new Vector3(pX, zeroY), new Vector3(pX, pY), coordinate.tickness,
2018-10-03 22:30:25 +08:00
themeInfo.axisLineColor);
2018-09-28 06:34:19 +08:00
if (xAxis.showSplitLine)
{
2019-03-16 07:49:36 +08:00
DrawSplitLine(vh, false, xAxis.splitLineType, new Vector3(pX, zeroY),
2018-10-01 17:00:15 +08:00
new Vector3(pX, zeroY + coordinateHig));
2018-09-28 06:34:19 +08:00
}
}
//draw x,y axis
2018-10-13 07:03:54 +08:00
ChartUtils.DrawLine(vh, new Vector3(zeroX, zeroY - coordinate.splitWidth),
2019-03-16 07:49:36 +08:00
new Vector3(zeroX, zeroY + coordinateHig + 2), coordinate.tickness,
2018-10-03 22:30:25 +08:00
themeInfo.axisLineColor);
2018-10-13 07:03:54 +08:00
ChartUtils.DrawLine(vh, new Vector3(zeroX - coordinate.splitWidth, zeroY),
2019-03-16 07:49:36 +08:00
new Vector3(zeroX + coordinateWid + 2, zeroY), coordinate.tickness,
2018-10-03 22:30:25 +08:00
themeInfo.axisLineColor);
2018-10-01 17:00:15 +08:00
}
2019-03-16 07:49:36 +08:00
private void DrawSplitLine(VertexHelper vh, bool isYAxis, SplitLineType type, Vector3 startPos,
2018-10-03 22:30:25 +08:00
Vector3 endPos)
2018-10-01 17:00:15 +08:00
{
switch (type)
{
case SplitLineType.dashed:
case SplitLineType.dotted:
var startX = startPos.x;
var startY = startPos.y;
var dashLen = type == SplitLineType.dashed ? 6 : 2.5f;
2019-03-16 07:49:36 +08:00
var count = isYAxis ? (endPos.x - startPos.x) / (dashLen * 2) :
2018-10-01 17:00:15 +08:00
(endPos.y - startPos.y) / (dashLen * 2);
for (int i = 0; i < count; i++)
{
if (isYAxis)
{
var toX = startX + dashLen;
ChartUtils.DrawLine(vh, new Vector3(startX, startY), new Vector3(toX, startY),
coordinate.tickness, themeInfo.axisSplitLineColor);
startX += dashLen * 2;
}
else
{
var toY = startY + dashLen;
ChartUtils.DrawLine(vh, new Vector3(startX, startY), new Vector3(startX, toY),
coordinate.tickness, themeInfo.axisSplitLineColor);
startY += dashLen * 2;
}
2019-03-16 07:49:36 +08:00
2018-10-01 17:00:15 +08:00
}
break;
case SplitLineType.solid:
ChartUtils.DrawLine(vh, startPos, endPos, coordinate.tickness,
themeInfo.axisSplitLineColor);
break;
}
2018-09-28 06:34:19 +08:00
}
}
}