5using System.Collections.Generic;
9 [AddComponentMenu(
"UI/Extensions/Primitives/UILineRenderer")]
10 [RequireComponent(typeof(RectTransform))]
13 private enum SegmentType
36 private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad;
41 private const float MIN_BEVEL_NICE_JOIN = 30 * Mathf.Deg2Rad;
43 private static Vector2 UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_TOP_CENTER_LEFT, UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_RIGHT, UV_BOTTOM_RIGHT;
44 private static Vector2[] startUvs, middleUvs, endUvs, fullUvs;
46 [SerializeField, Tooltip(
"Points to draw lines between\n Can be improved using the Resolution Option")]
47 internal Vector2[] m_points;
48 [SerializeField, Tooltip(
"Segments to be drawn\n This is a list of arrays of points")]
49 internal List<Vector2[]> m_segments;
51 [SerializeField, Tooltip(
"Thickness of the line")]
52 internal float lineThickness = 2;
53 [SerializeField, Tooltip(
"Use the relative bounds of the Rect Transform (0,0 -> 0,1) or screen space coordinates")]
54 internal bool relativeSize;
55 [SerializeField, Tooltip(
"Do the points identify a single line or split pairs of lines")]
56 internal bool lineList;
57 [SerializeField, Tooltip(
"Add end caps to each line\nMultiple caps when used with Line List")]
58 internal bool lineCaps;
59 [SerializeField, Tooltip(
"Resolution of the Bezier curve, different to line Resolution")]
60 internal int bezierSegmentsPerCurve = 10;
64 get {
return lineThickness; }
65 set { lineThickness = value; SetAllDirty(); }
70 get {
return relativeSize; }
71 set { relativeSize = value; SetAllDirty(); }
76 get {
return lineList; }
77 set { lineList = value; SetAllDirty(); }
82 get {
return lineCaps; }
83 set { lineCaps = value; SetAllDirty(); }
86 [Tooltip(
"The type of Join used between lines, Square/Mitre or Curved/Bevel")]
89 [Tooltip(
"Bezier method to apply to line, see docs for options\nCan't be used in conjunction with Resolution as Bezier already changes the resolution")]
94 get {
return bezierSegmentsPerCurve; }
95 set { bezierSegmentsPerCurve = value; }
114 if (m_points == value)
138 private void PopulateMesh(VertexHelper vh, Vector2[] pointsToDraw)
146 List<Vector2> drawingPoints;
159 pointsToDraw = drawingPoints.ToArray ();
162 CableCurve cable =
new CableCurve (pointsToDraw);
165 pointsToDraw = cable.Points ();
173 var sizeX = !relativeSize ? 1 : rectTransform.rect.width;
174 var sizeY = !relativeSize ? 1 : rectTransform.rect.height;
175 var offsetX = -rectTransform.pivot.x * sizeX;
176 var offsetY = -rectTransform.pivot.y * sizeY;
179 var segments =
new List<UIVertex[]> ();
181 for (var i = 1; i < pointsToDraw.Length; i += 2) {
182 var start = pointsToDraw [i - 1];
183 var end = pointsToDraw [i];
184 start =
new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
185 end =
new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
188 segments.Add (CreateLineCap (start, end, SegmentType.Start));
191 segments.Add(CreateLineSegment(start, end, SegmentType.Middle, segments.Count > 1 ? segments[segments.Count - 2] :
null));
194 segments.Add (CreateLineCap (start, end, SegmentType.End));
198 for (var i = 1; i < pointsToDraw.Length; i++) {
199 var start = pointsToDraw [i - 1];
200 var end = pointsToDraw [i];
201 start =
new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
202 end =
new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
204 if (lineCaps && i == 1) {
205 segments.Add (CreateLineCap (start, end, SegmentType.Start));
208 segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
210 if (lineCaps && i == pointsToDraw.Length - 1) {
211 segments.Add (CreateLineCap (start, end, SegmentType.End));
217 for (var i = 0; i < segments.Count; i++) {
218 if (!lineList && i < segments.Count - 1) {
219 var vec1 = segments [i] [1].position - segments [i] [2].position;
220 var vec2 = segments [i + 1] [2].position - segments [i + 1] [1].position;
221 var angle =
Vector2.Angle (vec1, vec2) * Mathf.Deg2Rad;
224 var sign = Mathf.Sign (
Vector3.Cross (vec1.normalized, vec2.normalized).z);
227 var miterDistance = lineThickness / (2 * Mathf.Tan (angle / 2));
228 var miterPointA = segments [i] [2].position - vec1.normalized * miterDistance * sign;
229 var miterPointB = segments [i] [3].position + vec1.normalized * miterDistance * sign;
234 if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_MITER_JOIN) {
235 segments [i] [2].position = miterPointA;
236 segments [i] [3].position = miterPointB;
237 segments [i + 1] [0].position = miterPointB;
238 segments [i + 1] [1].position = miterPointA;
245 if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_BEVEL_NICE_JOIN) {
247 segments [i] [2].position = miterPointA;
248 segments [i + 1] [1].position = miterPointA;
250 segments [i] [3].position = miterPointB;
251 segments [i + 1] [0].position = miterPointB;
255 var join =
new UIVertex[] { segments [i] [2], segments [i] [3], segments [i + 1] [0], segments [i + 1] [1] };
256 vh.AddUIVertexQuad (join);
260 vh.AddUIVertexQuad (segments [i]);
262 if (vh.currentVertCount > 64000) {
263 Debug.LogError (
"Max Verticies size is 64000, current mesh verticies count is [" + vh.currentVertCount +
"] - Cannot Draw");
272 if (m_points !=
null && m_points.Length > 0) {
276 PopulateMesh (vh, m_points);
279 else if (m_segments !=
null && m_segments.Count > 0) {
283 for (
int s = 0; s < m_segments.Count; s++) {
284 Vector2[] pointsToDraw = m_segments [s];
285 PopulateMesh (vh, pointsToDraw);
292 private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
294 if (type == SegmentType.Start)
296 var capStart = start - ((end - start).normalized * lineThickness / 2);
297 return CreateLineSegment(capStart, start, SegmentType.Start);
299 else if (type == SegmentType.End)
301 var capEnd = end + ((end - start).normalized * lineThickness / 2);
302 return CreateLineSegment(end, capEnd, SegmentType.End);
305 Debug.LogError(
"Bad SegmentType passed in to CreateLineCap. Must be SegmentType.Start or SegmentType.End");
309 private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, SegmentType type, UIVertex[] previousVert =
null)
311 Vector2 offset =
new Vector2((start.y - end.y), end.x - start.x).normalized * lineThickness / 2;
313 Vector2 v1 = Vector2.zero;
314 Vector2 v2 = Vector2.zero;
315 if (previousVert !=
null) {
316 v1 =
new Vector2(previousVert[3].position.x, previousVert[3].position.y);
317 v2 =
new Vector2(previousVert[2].position.x, previousVert[2].position.y);
323 var v3 = end + offset;
324 var v4 = end - offset;
328 case SegmentType.Start:
329 return SetVbo(
new[] { v1, v2, v3, v4 }, startUvs);
330 case SegmentType.End:
331 return SetVbo(
new[] { v1, v2, v3, v4 }, endUvs);
332 case SegmentType.Full:
333 return SetVbo(
new[] { v1, v2, v3, v4 }, fullUvs);
335 return SetVbo(
new[] { v1, v2, v3, v4 }, middleUvs);
343 var outer = Sprites.DataUtility.GetOuterUV(
activeSprite);
344 var inner = Sprites.DataUtility.GetInnerUV(
activeSprite);
345 UV_TOP_LEFT =
new Vector2(outer.x, outer.y);
346 UV_BOTTOM_LEFT =
new Vector2(outer.x, outer.w);
347 UV_TOP_CENTER_LEFT =
new Vector2(inner.x, inner.y);
348 UV_TOP_CENTER_RIGHT =
new Vector2(inner.z, inner.y);
349 UV_BOTTOM_CENTER_LEFT =
new Vector2(inner.x, inner.w);
350 UV_BOTTOM_CENTER_RIGHT =
new Vector2(inner.z, inner.w);
351 UV_TOP_RIGHT =
new Vector2(outer.z, outer.y);
352 UV_BOTTOM_RIGHT =
new Vector2(outer.z, outer.w);
356 UV_TOP_LEFT = Vector2.zero;
357 UV_BOTTOM_LEFT =
new Vector2(0, 1);
358 UV_TOP_CENTER_LEFT =
new Vector2(0.5f, 0);
359 UV_TOP_CENTER_RIGHT =
new Vector2(0.5f, 0);
360 UV_BOTTOM_CENTER_LEFT =
new Vector2(0.5f, 1);
361 UV_BOTTOM_CENTER_RIGHT =
new Vector2(0.5f, 1);
362 UV_TOP_RIGHT =
new Vector2(1, 0);
363 UV_BOTTOM_RIGHT = Vector2.one;
367 startUvs =
new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_CENTER_LEFT, UV_TOP_CENTER_LEFT };
368 middleUvs =
new[] { UV_TOP_CENTER_LEFT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_CENTER_RIGHT };
369 endUvs =
new[] { UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_RIGHT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
370 fullUvs =
new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
382 private int GetSegmentPointCount()
389 pointCount += segment.Length;
407 if (segmentIndex > 0)
409 return Segments[segmentIndex - 1][index - 1];
413 var segmentIndexCount = 0;
414 var indexCount = index;
417 if (indexCount - segment.Length > 0)
419 indexCount -= segment.Length;
420 segmentIndexCount += 1;
427 return Segments[segmentIndexCount][indexCount - 1];
443 return Segments[segment][index - 1];
455 Vector2 from_p1_to_p3 = p3 - p1;
456 Vector2 from_p1_to_p2 = p2 - p1;
457 float dot = Vector2.Dot(from_p1_to_p3, from_p1_to_p2.normalized);
458 dot /= from_p1_to_p2.magnitude;
459 float t = Mathf.Clamp01(dot);
460 return p1 + from_p1_to_p2 * t;
List< Vector2 > GetDrawingPoints0()
void SetControlPoints(List< Vector2 > newControlPoints)
List< Vector2 > GetDrawingPoints2()
List< Vector2 > GetDrawingPoints1()
override void GeneratedUVs()
Vector2[] Points
Points to be drawn in the line.
override void ResolutionToNativeSize(float distance)
override void OnPopulateMesh(VertexHelper vh)
Vector2 GetPosition(int index, int segmentIndex=0)
Get the Vector2 position of a line index
List< Vector2[]> Segments
List of Segments to be drawn.
Vector2 GetClosestPoint(Vector2 p1, Vector2 p2, Vector2 p3)
Get the closest point between two given Vector2s from a given Vector2 point
Vector2 GetPositionBySegment(int index, int segment)
Get the Vector2 position of a line within a specific segment
int BezierSegmentsPerCurve
Vector2[] IncreaseResolution(Vector2[] input)
ResolutionMode ImproveResolution
UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
Credit Erdener Gonenc - @PixelEnvision.