276 lines
9.9 KiB
C#
276 lines
9.9 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
[RequireComponent(typeof(CanvasRenderer))]
|
|
public class UIRadarGraphic : MaskableGraphic
|
|
{
|
|
private const float MainStrokeWidth = 2.5f;
|
|
private const float ReferenceStrokeWidth = 2f;
|
|
private const float MainPointSize = 8f;
|
|
private const float ReferencePointSize = 7f;
|
|
|
|
private readonly List<URadarAxisEntry> axisEntries = new List<URadarAxisEntry>();
|
|
|
|
private float minValue;
|
|
private float maxValue = 100f;
|
|
private int splitCount = 5;
|
|
private bool showReferenceSeries;
|
|
private Color gridLineColor = new Color32(255, 255, 255, 180);
|
|
private Color outerGridLineColor = Color.white;
|
|
private float gridLineWidth = 2f;
|
|
private Color mainStrokeColor = new Color32(82, 126, 255, 255);
|
|
private Color mainFillColor = new Color(82f / 255f, 126f / 255f, 255f / 255f, 0.28f);
|
|
private Color mainPointColor = Color.white;
|
|
private Color referenceStrokeColor = new Color32(72, 229, 229, 255);
|
|
private Color referenceFillColor = new Color(72f / 255f, 229f / 255f, 229f / 255f, 0.22f);
|
|
private Color referencePointColor = Color.white;
|
|
private float radarPlotPadding = 8f;
|
|
private float radarInnerRadius;
|
|
private float radarLabelRadialOffset;
|
|
|
|
public void SetData(
|
|
List<URadarAxisEntry> sourceAxes,
|
|
float sourceMinValue,
|
|
float sourceMaxValue,
|
|
int sourceSplitCount,
|
|
bool sourceShowReferenceSeries,
|
|
Color sourceGridLineColor,
|
|
Color sourceOuterGridLineColor,
|
|
float sourceGridLineWidth,
|
|
Color sourceMainStrokeColor,
|
|
Color sourceMainFillColor,
|
|
Color sourceMainPointColor,
|
|
Color sourceReferenceStrokeColor,
|
|
Color sourceReferenceFillColor,
|
|
Color sourceReferencePointColor,
|
|
float sourceRadarPlotPadding,
|
|
float sourceRadarInnerRadius,
|
|
float sourceRadarLabelRadialOffset)
|
|
{
|
|
axisEntries.Clear();
|
|
if (sourceAxes != null)
|
|
{
|
|
for (int i = 0; i < sourceAxes.Count; i++)
|
|
{
|
|
URadarAxisEntry source = sourceAxes[i];
|
|
if (source == null)
|
|
{
|
|
axisEntries.Add(new URadarAxisEntry());
|
|
continue;
|
|
}
|
|
|
|
axisEntries.Add(new URadarAxisEntry
|
|
{
|
|
label = source.label,
|
|
value = source.value,
|
|
referenceValue = source.referenceValue
|
|
});
|
|
}
|
|
}
|
|
|
|
minValue = sourceMinValue;
|
|
maxValue = Mathf.Max(sourceMinValue + 0.0001f, sourceMaxValue);
|
|
splitCount = Mathf.Max(2, sourceSplitCount);
|
|
showReferenceSeries = sourceShowReferenceSeries;
|
|
gridLineColor = sourceGridLineColor;
|
|
outerGridLineColor = sourceOuterGridLineColor;
|
|
gridLineWidth = Mathf.Max(0.1f, sourceGridLineWidth);
|
|
mainStrokeColor = sourceMainStrokeColor;
|
|
mainFillColor = sourceMainFillColor;
|
|
mainPointColor = sourceMainPointColor;
|
|
referenceStrokeColor = sourceReferenceStrokeColor;
|
|
referenceFillColor = sourceReferenceFillColor;
|
|
referencePointColor = sourceReferencePointColor;
|
|
radarPlotPadding = Mathf.Max(0f, sourceRadarPlotPadding);
|
|
radarInnerRadius = Mathf.Max(0f, sourceRadarInnerRadius);
|
|
radarLabelRadialOffset = sourceRadarLabelRadialOffset;
|
|
|
|
SetVerticesDirty();
|
|
}
|
|
|
|
protected override void OnPopulateMesh(VertexHelper vh)
|
|
{
|
|
vh.Clear();
|
|
|
|
if (axisEntries.Count < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Rect rect = GetPixelAdjustedRect();
|
|
Vector2 center = rect.center;
|
|
float maxRadius = Mathf.Max(0f, Mathf.Min(rect.width, rect.height) * 0.5f - radarPlotPadding - Mathf.Max(MainPointSize, ReferencePointSize));
|
|
float innerRadius = Mathf.Clamp(radarInnerRadius, 0f, maxRadius * 0.8f);
|
|
|
|
DrawGrid(vh, center, innerRadius, maxRadius);
|
|
|
|
List<Vector2> mainPoints = BuildSeriesPoints(center, innerRadius, maxRadius, false);
|
|
if (showReferenceSeries)
|
|
{
|
|
List<Vector2> referencePoints = BuildSeriesPoints(center, innerRadius, maxRadius, true);
|
|
DrawFilledPolygon(vh, center, referencePoints, referenceFillColor);
|
|
DrawPolyline(vh, referencePoints, true, ReferenceStrokeWidth, referenceStrokeColor);
|
|
DrawPointMarkers(vh, referencePoints, ReferencePointSize, referencePointColor);
|
|
}
|
|
|
|
DrawFilledPolygon(vh, center, mainPoints, mainFillColor);
|
|
DrawPolyline(vh, mainPoints, true, MainStrokeWidth, mainStrokeColor);
|
|
DrawPointMarkers(vh, mainPoints, MainPointSize, mainPointColor);
|
|
}
|
|
|
|
private void DrawGrid(VertexHelper vh, Vector2 center, float innerRadius, float maxRadius)
|
|
{
|
|
int axisCount = axisEntries.Count;
|
|
for (int ring = 1; ring <= splitCount; ring++)
|
|
{
|
|
float t = ring / (float)splitCount;
|
|
float radius = Mathf.Lerp(innerRadius, maxRadius, t);
|
|
List<Vector2> ringPoints = new List<Vector2>(axisCount);
|
|
for (int i = 0; i < axisCount; i++)
|
|
{
|
|
ringPoints.Add(GetAxisPoint(center, radius, i, axisCount));
|
|
}
|
|
|
|
DrawPolyline(vh, ringPoints, true, gridLineWidth, ring == splitCount ? outerGridLineColor : gridLineColor);
|
|
}
|
|
|
|
for (int i = 0; i < axisCount; i++)
|
|
{
|
|
Vector2 start = GetAxisPoint(center, innerRadius, i, axisCount);
|
|
Vector2 end = GetAxisPoint(center, maxRadius, i, axisCount);
|
|
DrawLine(vh, start, end, gridLineWidth, gridLineColor);
|
|
}
|
|
}
|
|
|
|
private List<Vector2> BuildSeriesPoints(Vector2 center, float innerRadius, float maxRadius, bool useReferenceValue)
|
|
{
|
|
int axisCount = axisEntries.Count;
|
|
List<Vector2> points = new List<Vector2>(axisCount);
|
|
|
|
for (int i = 0; i < axisCount; i++)
|
|
{
|
|
URadarAxisEntry entry = axisEntries[i];
|
|
float value = useReferenceValue ? entry.referenceValue : entry.value;
|
|
float normalized = Mathf.InverseLerp(minValue, maxValue, Mathf.Clamp(value, minValue, maxValue));
|
|
float radius = Mathf.Lerp(innerRadius, maxRadius, normalized);
|
|
points.Add(GetAxisPoint(center, radius, i, axisCount));
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
private static Vector2 GetAxisPoint(Vector2 center, float radius, int axisIndex, int axisCount)
|
|
{
|
|
float angle = Mathf.PI * 0.5f - (Mathf.PI * 2f / axisCount) * axisIndex;
|
|
return center + new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius;
|
|
}
|
|
|
|
private static void DrawFilledPolygon(VertexHelper vh, Vector2 center, List<Vector2> points, Color color)
|
|
{
|
|
if (points == null || points.Count < 3 || color.a <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int startIndex = vh.currentVertCount;
|
|
UIVertex centerVertex = UIVertex.simpleVert;
|
|
centerVertex.color = color;
|
|
centerVertex.position = center;
|
|
vh.AddVert(centerVertex);
|
|
|
|
for (int i = 0; i < points.Count; i++)
|
|
{
|
|
UIVertex vertex = UIVertex.simpleVert;
|
|
vertex.color = color;
|
|
vertex.position = points[i];
|
|
vh.AddVert(vertex);
|
|
}
|
|
|
|
for (int i = 0; i < points.Count; i++)
|
|
{
|
|
int current = startIndex + 1 + i;
|
|
int next = startIndex + 1 + ((i + 1) % points.Count);
|
|
vh.AddTriangle(startIndex, current, next);
|
|
}
|
|
}
|
|
|
|
private static void DrawPolyline(VertexHelper vh, List<Vector2> points, bool closed, float thickness, Color color)
|
|
{
|
|
if (points == null || points.Count < 2 || color.a <= 0f || thickness <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < points.Count - 1; i++)
|
|
{
|
|
DrawLine(vh, points[i], points[i + 1], thickness, color);
|
|
}
|
|
|
|
if (closed)
|
|
{
|
|
DrawLine(vh, points[points.Count - 1], points[0], thickness, color);
|
|
}
|
|
}
|
|
|
|
private static void DrawLine(VertexHelper vh, Vector2 start, Vector2 end, float thickness, Color color)
|
|
{
|
|
Vector2 delta = end - start;
|
|
if (delta.sqrMagnitude <= 0.0001f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector2 normal = new Vector2(-delta.y, delta.x).normalized * (thickness * 0.5f);
|
|
int index = vh.currentVertCount;
|
|
|
|
UIVertex vertex = UIVertex.simpleVert;
|
|
vertex.color = color;
|
|
|
|
vertex.position = start - normal;
|
|
vh.AddVert(vertex);
|
|
vertex.position = start + normal;
|
|
vh.AddVert(vertex);
|
|
vertex.position = end + normal;
|
|
vh.AddVert(vertex);
|
|
vertex.position = end - normal;
|
|
vh.AddVert(vertex);
|
|
|
|
vh.AddTriangle(index, index + 1, index + 2);
|
|
vh.AddTriangle(index, index + 2, index + 3);
|
|
}
|
|
|
|
private static void DrawPointMarkers(VertexHelper vh, List<Vector2> points, float size, Color color)
|
|
{
|
|
if (points == null || color.a <= 0f || size <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float half = size * 0.5f;
|
|
for (int i = 0; i < points.Count; i++)
|
|
{
|
|
DrawQuad(vh, points[i], new Vector2(half, half), color);
|
|
}
|
|
}
|
|
|
|
private static void DrawQuad(VertexHelper vh, Vector2 center, Vector2 halfSize, Color color)
|
|
{
|
|
int index = vh.currentVertCount;
|
|
UIVertex vertex = UIVertex.simpleVert;
|
|
vertex.color = color;
|
|
|
|
vertex.position = new Vector2(center.x - halfSize.x, center.y - halfSize.y);
|
|
vh.AddVert(vertex);
|
|
vertex.position = new Vector2(center.x - halfSize.x, center.y + halfSize.y);
|
|
vh.AddVert(vertex);
|
|
vertex.position = new Vector2(center.x + halfSize.x, center.y + halfSize.y);
|
|
vh.AddVert(vertex);
|
|
vertex.position = new Vector2(center.x + halfSize.x, center.y - halfSize.y);
|
|
vh.AddVert(vertex);
|
|
|
|
vh.AddTriangle(index, index + 1, index + 2);
|
|
vh.AddTriangle(index, index + 2, index + 3);
|
|
}
|
|
}
|