Files
XCharts/Scripts/RadarChart.cs

391 lines
15 KiB
C#
Raw Normal View History

2018-09-25 07:33:04 +08:00
using System.Collections.Generic;
2019-03-21 07:51:20 +08:00
using System.Text;
2018-09-25 07:33:04 +08:00
using UnityEngine;
using UnityEngine.UI;
namespace xcharts
{
[System.Serializable]
public class RadarIndicator
{
public string name;
public float max;
}
[System.Serializable]
public class RadarInfo
{
2018-09-27 07:00:27 +08:00
public bool cricle;
public bool area;
2019-03-16 07:49:36 +08:00
2018-09-25 07:33:04 +08:00
public float radius = 100;
public int splitNumber = 5;
public float left;
public float right;
public float top;
public float bottom;
public float lineTickness = 1f;
public float linePointSize = 5f;
public Color lineColor = Color.grey;
2018-10-01 17:00:15 +08:00
public List<Color> backgroundColorList = new List<Color>();
2018-09-26 09:00:11 +08:00
public bool showIndicator = true;
2018-10-01 17:00:15 +08:00
public List<RadarIndicator> indicatorList = new List<RadarIndicator>();
2018-09-26 09:00:11 +08:00
public int checkIndicatorCount { get; set; }
2018-09-25 07:33:04 +08:00
}
public class RadarChart : BaseChart
{
2018-09-26 09:00:11 +08:00
private const string INDICATOR_TEXT = "indicator";
2018-09-25 07:33:04 +08:00
[SerializeField]
2018-10-01 17:00:15 +08:00
private RadarInfo radarInfo = new RadarInfo();
2018-09-25 07:33:04 +08:00
2018-09-26 09:00:11 +08:00
private RadarInfo checkRadarInfo = new RadarInfo();
2018-09-25 07:33:04 +08:00
private float radarCenterX = 0f;
private float radarCenterY = 0f;
private float radarRadius = 0;
2018-09-26 09:00:11 +08:00
private List<Text> indicatorTextList = new List<Text>();
2019-03-21 07:51:20 +08:00
private List<List<Vector3>> dataPosList = new List<List<Vector3>>();
2018-09-25 07:33:04 +08:00
protected override void Awake()
{
base.Awake();
2018-09-26 09:00:11 +08:00
UpdateRadarCenter();
2018-09-25 07:33:04 +08:00
}
protected override void Update()
{
base.Update();
2018-09-26 09:00:11 +08:00
CheckRadarInfoChanged();
}
private void InitIndicator()
{
indicatorTextList.Clear();
HideChild(INDICATOR_TEXT);
int indicatorNum = radarInfo.indicatorList.Count;
float txtWid = 100;
float txtHig = 20;
for (int i = 0; i < indicatorNum; i++)
{
var pos = GetIndicatorPosition(i);
TextAnchor anchor = TextAnchor.MiddleCenter;
var diff = pos.x - radarCenterX;
if (diff < -1f)
{
pos = new Vector3(pos.x - 5, pos.y);
anchor = TextAnchor.MiddleRight;
}
else if (diff > 1f)
{
anchor = TextAnchor.MiddleLeft;
2019-03-16 07:49:36 +08:00
pos = new Vector3(pos.x + txtWid + 5, pos.y);
2018-09-26 09:00:11 +08:00
}
else
{
anchor = TextAnchor.MiddleCenter;
float y = pos.y > radarCenterY ? pos.y + txtHig / 2 : pos.y - txtHig / 2;
pos = new Vector3(pos.x + txtWid / 2, y);
}
2018-10-01 17:00:15 +08:00
Text txt = ChartUtils.AddTextObject(INDICATOR_TEXT + i, transform, themeInfo.font,
themeInfo.textColor, anchor, Vector2.zero, Vector2.zero, new Vector2(1, 0.5f),
2018-09-26 09:00:11 +08:00
new Vector2(txtWid, txtHig));
txt.transform.localPosition = pos;
txt.text = radarInfo.indicatorList[i].name;
txt.gameObject.SetActive(radarInfo.showIndicator);
indicatorTextList.Add(txt);
}
}
private void CheckRadarInfoChanged()
{
2019-03-16 07:49:36 +08:00
if (checkRadarInfo.radius != radarInfo.radius ||
2018-09-26 09:00:11 +08:00
checkRadarInfo.left != radarInfo.left ||
checkRadarInfo.right != radarInfo.right ||
checkRadarInfo.top != radarInfo.top ||
checkRadarInfo.bottom != radarInfo.bottom ||
checkRadarInfo.checkIndicatorCount != radarInfo.indicatorList.Count ||
checkRadarInfo.showIndicator != radarInfo.showIndicator)
{
checkRadarInfo.radius = radarInfo.radius;
checkRadarInfo.left = radarInfo.left;
checkRadarInfo.right = radarInfo.right;
checkRadarInfo.top = radarInfo.top;
checkRadarInfo.bottom = radarInfo.bottom;
checkRadarInfo.showIndicator = radarInfo.showIndicator;
checkRadarInfo.checkIndicatorCount = radarInfo.indicatorList.Count;
OnRadarChanged();
}
}
private void OnRadarChanged()
{
UpdateRadarCenter();
InitIndicator();
}
private Vector3 GetIndicatorPosition(int i)
{
int indicatorNum = radarInfo.indicatorList.Count;
var angle = 2 * Mathf.PI / indicatorNum * i;
var x = radarCenterX + radarInfo.radius * Mathf.Sin(angle);
var y = radarCenterY + radarInfo.radius * Mathf.Cos(angle);
2019-03-16 07:49:36 +08:00
return new Vector3(x, y);
2018-09-25 07:33:04 +08:00
}
2019-03-12 08:10:25 +08:00
protected override void DrawChart(VertexHelper vh)
2018-09-25 07:33:04 +08:00
{
2019-03-12 08:10:25 +08:00
base.DrawChart(vh);
2018-09-25 07:33:04 +08:00
UpdateRadarCenter();
2018-09-27 07:00:27 +08:00
if (radarInfo.cricle)
DrawCricleRadar(vh);
else
DrawRadar(vh);
2018-09-25 07:33:04 +08:00
DrawData(vh);
}
protected override void OnLegendButtonClicked()
{
base.OnLegendButtonClicked();
}
2018-10-01 17:00:15 +08:00
protected override void OnThemeChanged()
{
base.OnThemeChanged();
radarInfo.backgroundColorList.Clear();
switch (theme)
{
case Theme.Dark:
radarInfo.backgroundColorList.Add(ThemeInfo.GetColor("#6f6f6f"));
radarInfo.backgroundColorList.Add(ThemeInfo.GetColor("#606060"));
break;
case Theme.Default:
radarInfo.backgroundColorList.Add(ThemeInfo.GetColor("#f6f6f6"));
radarInfo.backgroundColorList.Add(ThemeInfo.GetColor("#e7e7e7"));
break;
case Theme.Light:
radarInfo.backgroundColorList.Add(ThemeInfo.GetColor("#f6f6f6"));
radarInfo.backgroundColorList.Add(ThemeInfo.GetColor("#e7e7e7"));
break;
}
2018-10-02 08:57:47 +08:00
InitIndicator();
2018-10-01 17:00:15 +08:00
}
2018-09-25 07:33:04 +08:00
private void DrawData(VertexHelper vh)
{
int indicatorNum = radarInfo.indicatorList.Count;
var angle = 2 * Mathf.PI / indicatorNum;
var p = new Vector3(radarCenterX, radarCenterY);
Vector3 startPoint = Vector3.zero;
Vector3 toPoint = Vector3.zero;
Vector3 firstPoint = Vector3.zero;
2019-03-21 07:51:20 +08:00
dataPosList.Clear();
2019-03-22 08:22:02 +08:00
dataPosList.Capacity = seriesList.Count;
2018-09-25 07:33:04 +08:00
for (int i = 0; i < seriesList.Count; i++)
{
2019-03-21 07:51:20 +08:00
if (!legend.IsShowSeries(i))
{
dataPosList.Add(new List<Vector3>());
continue;
}
2018-09-25 07:33:04 +08:00
var dataList = seriesList[i].dataList;
2018-10-01 17:00:15 +08:00
var color = themeInfo.GetColor(i);
2019-03-16 07:49:36 +08:00
var areaColor = new Color(color.r, color.g, color.b, color.a * 0.7f);
2018-09-25 07:33:04 +08:00
var max = radarInfo.indicatorList[i].max > 0 ?
radarInfo.indicatorList[i].max :
GetMaxValue();
2019-03-22 08:22:02 +08:00
List<Vector3> pointList = new List<Vector3>(dataList.Count);
2019-03-21 07:51:20 +08:00
dataPosList.Add(pointList);
2018-09-25 07:33:04 +08:00
for (int j = 0; j < dataList.Count; j++)
{
var radius = radarInfo.radius * dataList[j] / max;
2018-09-25 07:33:04 +08:00
var currAngle = j * angle;
if (j == 0)
{
startPoint = new Vector3(p.x + radius * Mathf.Sin(currAngle),
p.y + radius * Mathf.Cos(currAngle));
firstPoint = startPoint;
}
else
{
toPoint = new Vector3(p.x + radius * Mathf.Sin(currAngle),
p.y + radius * Mathf.Cos(currAngle));
2018-10-03 22:30:25 +08:00
if (radarInfo.area)
{
ChartUtils.DrawTriangle(vh, p, startPoint, toPoint, areaColor);
}
2018-09-25 07:33:04 +08:00
ChartUtils.DrawLine(vh, startPoint, toPoint, radarInfo.lineTickness, color);
startPoint = toPoint;
}
pointList.Add(startPoint);
}
if (radarInfo.area) ChartUtils.DrawTriangle(vh, p, startPoint, firstPoint, areaColor);
2018-09-25 07:33:04 +08:00
ChartUtils.DrawLine(vh, startPoint, firstPoint, radarInfo.lineTickness, color);
foreach (var point in pointList)
{
float radius = radarInfo.linePointSize - radarInfo.lineTickness * 2;
ChartUtils.DrawCricle(vh, point, radius, Color.white);
ChartUtils.DrawDoughnut(vh, point, radius, radarInfo.linePointSize, 0, 360, color);
}
}
}
private void DrawRadar(VertexHelper vh)
{
float insideRadius = 0, outsideRadius = 0;
float block = radarInfo.radius / radarInfo.splitNumber;
int indicatorNum = radarInfo.indicatorList.Count;
Vector3 p1, p2, p3, p4;
2019-03-16 07:49:36 +08:00
Vector3 p = new Vector3(radarCenterX, radarCenterY);
2018-09-25 07:33:04 +08:00
float angle = 2 * Mathf.PI / indicatorNum;
for (int i = 0; i < radarInfo.splitNumber; i++)
{
2019-03-16 07:49:36 +08:00
Color color = radarInfo.backgroundColorList[i % radarInfo.backgroundColorList.Count];
2018-09-25 07:33:04 +08:00
outsideRadius = insideRadius + block;
p1 = new Vector3(p.x + insideRadius * Mathf.Sin(0), p.y + insideRadius * Mathf.Cos(0));
p2 = new Vector3(p.x + outsideRadius * Mathf.Sin(0), p.y + outsideRadius * Mathf.Cos(0));
for (int j = 0; j <= indicatorNum; j++)
{
float currAngle = j * angle;
2018-10-03 22:30:25 +08:00
p3 = new Vector3(p.x + outsideRadius * Mathf.Sin(currAngle),
p.y + outsideRadius * Mathf.Cos(currAngle));
p4 = new Vector3(p.x + insideRadius * Mathf.Sin(currAngle),
p.y + insideRadius * Mathf.Cos(currAngle));
2018-09-25 07:33:04 +08:00
ChartUtils.DrawPolygon(vh, p1, p2, p3, p4, color);
ChartUtils.DrawLine(vh, p2, p3, radarInfo.lineTickness, radarInfo.lineColor);
p1 = p4;
p2 = p3;
}
insideRadius = outsideRadius;
}
for (int j = 0; j <= indicatorNum; j++)
{
float currAngle = j * angle;
2018-10-03 22:30:25 +08:00
p3 = new Vector3(p.x + outsideRadius * Mathf.Sin(currAngle),
p.y + outsideRadius * Mathf.Cos(currAngle));
2019-03-16 07:49:36 +08:00
ChartUtils.DrawLine(vh, p, p3, radarInfo.lineTickness / 2, radarInfo.lineColor);
2018-09-25 07:33:04 +08:00
}
}
2018-09-27 07:00:27 +08:00
private void DrawCricleRadar(VertexHelper vh)
{
float insideRadius = 0, outsideRadius = 0;
float block = radarInfo.radius / radarInfo.splitNumber;
int indicatorNum = radarInfo.indicatorList.Count;
Vector3 p = new Vector3(radarCenterX, radarCenterY);
Vector3 p1;
float angle = 2 * Mathf.PI / indicatorNum;
for (int i = 0; i < radarInfo.splitNumber; i++)
{
Color color = radarInfo.backgroundColorList[i % radarInfo.backgroundColorList.Count];
outsideRadius = insideRadius + block;
ChartUtils.DrawDoughnut(vh, p, insideRadius, outsideRadius, 0, 360, color);
2018-10-03 22:30:25 +08:00
ChartUtils.DrawCicleNotFill(vh, p, outsideRadius, radarInfo.lineTickness,
radarInfo.lineColor);
2018-09-27 07:00:27 +08:00
insideRadius = outsideRadius;
}
for (int j = 0; j <= indicatorNum; j++)
{
float currAngle = j * angle;
2019-03-16 07:49:36 +08:00
p1 = new Vector3(p.x + outsideRadius * Mathf.Sin(currAngle),
2018-10-03 22:30:25 +08:00
p.y + outsideRadius * Mathf.Cos(currAngle));
2018-09-27 07:00:27 +08:00
ChartUtils.DrawLine(vh, p, p1, radarInfo.lineTickness / 2, radarInfo.lineColor);
}
}
2018-09-25 07:33:04 +08:00
private void UpdateRadarCenter()
{
float diffX = chartWid - radarInfo.left - radarInfo.right;
float diffY = chartHig - radarInfo.top - radarInfo.bottom;
float diff = Mathf.Min(diffX, diffY);
if (radarInfo.radius <= 0)
{
radarRadius = diff / 3 * 2;
radarCenterX = radarInfo.left + radarRadius;
radarCenterY = radarInfo.bottom + radarRadius;
}
else
{
radarRadius = radarInfo.radius;
radarCenterX = chartWid / 2;
radarCenterY = chartHig / 2;
if (radarInfo.left > 0) radarCenterX = radarInfo.left + radarRadius;
if (radarInfo.right > 0) radarCenterX = chartWid - radarInfo.right - radarRadius;
if (radarInfo.top > 0) radarCenterY = chartHig - radarInfo.top - radarRadius;
if (radarInfo.bottom > 0) radarCenterY = radarInfo.bottom + radarRadius;
}
}
2019-03-21 07:51:20 +08:00
protected override void CheckTootipArea(Vector2 local)
{
if (dataPosList.Count <= 0) return;
tooltip.DataIndex = 0;
for (int i = 0; i < seriesList.Count; i++)
{
if (!legend.IsShowSeries(i)) continue;
for (int j = 0; j < dataPosList[i].Count; j++)
{
if (Vector3.Distance(local, dataPosList[i][j]) <= radarInfo.linePointSize * 1.2f)
{
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;
}
else
{
tooltip.SetActive(false);
}
}
protected override void RefreshTooltip()
{
base.RefreshTooltip();
int index = tooltip.DataIndex - 1;
if (index < 0)
{
tooltip.SetActive(false);
return;
}
tooltip.SetActive(true);
StringBuilder sb = new StringBuilder(legend.dataList[index]);
for (int i = 0; i < radarInfo.indicatorList.Count; i++)
{
string key = radarInfo.indicatorList[i].name;
float value = seriesList[index].dataList[i];
sb.Append("\n");
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);
}
2018-09-25 07:33:04 +08:00
}
}