Select Git revision
UILine.cs 5.93 KiB
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
public class UILine : Graphic
{
#region InspectorVariables
[Header("General")]
public List<Vector2> points = new();
public float width = 5f;
[Header("Rounding")]
public bool roundCorners = true;
public bool roundStart = false;
public bool roundEnd = false;
[Header("Dashed")]
public bool dashed = false;
public int dashLength = 10;
public int dashSpacing = 5;
#endregion InspectorVariables
#region UnityMethods
protected override void OnPopulateMesh(VertexHelper vh)
{
// clear old vertecies and triangles
vh.Clear();
// a line with less than 2 points does not make any sense => return
if (points.Count < 2)
return;
if (dashed)
GenerateLinesDashed(vh);
else
GenerateLinesStandard(vh);
if (roundStart)
CreateCircle(vh, points[0], width);
if (roundEnd)
CreateCircle(vh, points[^1], width);
}
#endregion UnityMethods
#region Implementation
#region GenerateLines
private void GenerateLinesStandard(VertexHelper vh)
{
points.Zip(points.Skip(1), (a, b) => Tuple.Create(a, b))
.ToList()
.ForEach(tup => CreateSegment(vh, tup.Item1, tup.Item2));
if (roundCorners)
{
// take all points except for start and end and apply rounding
points.Take(points.Count - 1).Skip(1).ToList()
.ForEach(p => CreateCircle(vh, p, width));
}
}
private void GenerateLinesDashed(VertexHelper vh)
{
float restLen = dashLength;
bool isDash = true;
var lines = points.Zip(points.Skip(1), (a, b) => Tuple.Create(a, b)).ToList();
for (int i = 0; i < lines.Count; i++)
{
var tup = lines[i];
Vector2 start = tup.Item1;
Vector2 end = tup.Item2;
Vector2 dir = (end - start).normalized;
// main spacing algorithm
Vector2 current = start;
while (Vector2.Distance(current, end) >= restLen)
{
Vector2 segmentEnd = current + restLen * dir;
if (isDash)
CreateSegment(vh, current, segmentEnd);
current = segmentEnd;
isDash = !isDash;
restLen = isDash ? dashLength : dashSpacing;
}
// is there a dash wrapping around a corner?
bool dashOverCorner = false;
float distLeft = Vector2.Distance(current, end);
if (!isDash)
restLen -= distLeft;
// dont fill remaining distance with dash, if it would be short (shorter than width/2)
else if (isDash && distLeft > width / 2)
{
CreateSegment(vh, current, end);
restLen -= distLeft;
dashOverCorner = true;
}
// discard rest of dash if it is too short (shorter than width/2)
if (isDash && restLen < width / 2)
{
isDash = false;
restLen = dashSpacing;
dashOverCorner = false; // dash does not wrap around corner as dashSpacing will follow
}
// only create round corners if roundCorners are enabled and there is a dash wrapping around the corner and not last corner (aka end)
if (roundCorners && dashOverCorner && i != lines.Count - 1)
CreateCircle(vh, end, width);
}
}
#endregion GenerateLines
#region CreateLine
private void CreateSegment(VertexHelper vh, Vector2 start, Vector2 end)
{
//start += (Vector2)transform.position;
//end += (Vector2)transform.position;
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
Vector2 dir = end - start;
Vector2 perp = Vector2.Perpendicular(dir).normalized;
Vector2 off = perp * width / 2;
vertex.position = new Vector3(start.x + off.x, start.y + off.y);
vh.AddVert(vertex);
vertex.position = new Vector3(end.x + off.x, end.y + off.y);
vh.AddVert(vertex);
vertex.position = new Vector3(end.x - off.x, end.y - off.y);
vh.AddVert(vertex);
vertex.position = new Vector3(start.x - off.x, start.y - off.y);
vh.AddVert(vertex);
int offset = vh.currentVertCount - 4;
vh.AddTriangle(0 + offset, 1 + offset, 2 + offset);
vh.AddTriangle(2 + offset, 3 + offset, 0 + offset);
}
#endregion CreateLine
#region Rounding
private void CreateCircle(VertexHelper vh, Vector2 center, float diameter, int sideCount = 20)
{
//center += (Vector2)transform.position;
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
Vector3[] vertices = GetCirclePoints(diameter / 2, sideCount, center).Union(new Vector3[] { center }).ToArray();
vertices.ToList().ForEach(vert =>
{
vertex.position = vert;
vh.AddVert(vertex);
}
);
int startVert = vh.currentVertCount - vertices.Length;
for (int i = 0; i < vertices.Length - 1; i++)
{
vh.AddTriangle(
vh.currentVertCount - 1, // center
startVert + i,
startVert + ((i + 1) % (vertices.Length - 1))
);
}
return;
}
protected static Vector3[] GetCirclePoints(float circleRadius, int pointCount, Vector3 offset)
{
Vector3[] circle = new Vector3[pointCount];
float slice = (2f * Mathf.PI) / pointCount;
for (int i = 0; i < pointCount; i++)
{
float angle = i * slice;
circle[i] = new Vector3(circleRadius * Mathf.Sin(angle), circleRadius * Mathf.Cos(angle)) + offset;
}
return circle;
}
#endregion Rounding
#endregion Implementation
}