Tanoda
UILineRendererList.cs
Go to the documentation of this file.
1
4
5using System.Collections.Generic;
6
8{
9 [AddComponentMenu("UI/Extensions/Primitives/UILineRendererList")]
10 [RequireComponent(typeof(RectTransform))]
12 {
13 private enum SegmentType
14 {
15 Start,
16 Middle,
17 End,
18 Full,
19 }
20
21 public enum JoinType
22 {
23 Bevel,
24 Miter
25 }
26
27 public enum BezierType
28 {
29 None,
30 Quick,
31 Basic,
32 Improved,
33 Catenary,
34 }
35
36 private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad;
37
38 // A bevel 'nice' join displaces the vertices of the line segment instead of simply rendering a
39 // quad to connect the endpoints. This improves the look of textured and transparent lines, since
40 // there is no overlapping.
41 private const float MIN_BEVEL_NICE_JOIN = 30 * Mathf.Deg2Rad;
42
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;
45
46 [SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")]
47 internal List<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;
50
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;
61
62 public float LineThickness
63 {
64 get { return lineThickness; }
65 set { lineThickness = value; SetAllDirty(); }
66 }
67
68 public bool RelativeSize
69 {
70 get { return relativeSize; }
71 set { relativeSize = value; SetAllDirty(); }
72 }
73
74 public bool LineList
75 {
76 get { return lineList; }
77 set { lineList = value; SetAllDirty(); }
78 }
79
80 public bool LineCaps
81 {
82 get { return lineCaps; }
83 set { lineCaps = value; SetAllDirty(); }
84 }
85
86 [Tooltip("The type of Join used between lines, Square/Mitre or Curved/Bevel")]
87 public JoinType LineJoins = JoinType.Bevel;
88
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")]
91
93 {
94 get { return bezierSegmentsPerCurve; }
95 set { bezierSegmentsPerCurve = value; }
96 }
97
98 [HideInInspector]
99 public bool drivenExternally = false;
100
105 public List<Vector2> Points
106 {
107 get
108 {
109 return m_points;
110 }
111
112 set
113 {
114 if (m_points == value)
115 return;
116 m_points = value;
117 SetAllDirty();
118 }
119 }
120
121 // /// <summary>
122 // /// List of Segments to be drawn.
123 // /// </summary>
124 // public List<Vector2[]> Segments
125 //{
126 // get
127 // {
128 // return m_segments;
129 // }
130
131 // set
132 // {
133 // m_segments = value;
134 // SetAllDirty();
135 // }
136 //}
137
138
139 public void AddPoint(Vector2 pointToAdd)
140 {
141 m_points.Add(pointToAdd);
142 SetAllDirty();
143 }
144
145 public void RemovePoint(Vector2 pointToRemove)
146 {
147 m_points.Remove(pointToRemove);
148 SetAllDirty();
149 }
150
151 public void ClearPoints()
152 {
153 m_points.Clear();
154 SetAllDirty();
155 }
156
157 private void PopulateMesh(VertexHelper vh, List<Vector2> pointsToDraw)
158 {
159 //If Bezier is desired, pick the implementation
160 if (BezierMode != BezierType.None && BezierMode != BezierType.Catenary && pointsToDraw.Count > 3) {
161 BezierPath bezierPath = new BezierPath ();
162
163 bezierPath.SetControlPoints (pointsToDraw);
164 bezierPath.SegmentsPerCurve = bezierSegmentsPerCurve;
165 List<Vector2> drawingPoints;
166 switch (BezierMode) {
167 case BezierType.Basic:
168 drawingPoints = bezierPath.GetDrawingPoints0 ();
169 break;
170 case BezierType.Improved:
171 drawingPoints = bezierPath.GetDrawingPoints1 ();
172 break;
173 default:
174 drawingPoints = bezierPath.GetDrawingPoints2 ();
175 break;
176 }
177
178 pointsToDraw = drawingPoints;
179 }
180 if (BezierMode == BezierType.Catenary && pointsToDraw.Count == 2) {
181 CableCurve cable = new CableCurve (pointsToDraw);
182 cable.slack = Resolution;
183 cable.steps = BezierSegmentsPerCurve;
184 pointsToDraw.Clear();
185 pointsToDraw.AddRange(cable.Points());
186 }
187
188 if (ImproveResolution != ResolutionMode.None) {
189 pointsToDraw = IncreaseResolution (pointsToDraw);
190 }
191
192 // scale based on the size of the rect or use absolute, this is switchable
193 var sizeX = !relativeSize ? 1 : rectTransform.rect.width;
194 var sizeY = !relativeSize ? 1 : rectTransform.rect.height;
195 var offsetX = -rectTransform.pivot.x * sizeX;
196 var offsetY = -rectTransform.pivot.y * sizeY;
197
198 // Generate the quads that make up the wide line
199 var segments = new List<UIVertex[]> ();
200 if (lineList) {
201 for (var i = 1; i < pointsToDraw.Count; i += 2) {
202 var start = pointsToDraw [i - 1];
203 var end = pointsToDraw [i];
204 start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
205 end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
206
207 if (lineCaps) {
208 segments.Add (CreateLineCap (start, end, SegmentType.Start));
209 }
210
211 //segments.Add(CreateLineSegment(start, end, SegmentType.Full));
212 segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
213
214 if (lineCaps) {
215 segments.Add (CreateLineCap (start, end, SegmentType.End));
216 }
217 }
218 } else {
219 for (var i = 1; i < pointsToDraw.Count; i++) {
220 var start = pointsToDraw [i - 1];
221 var end = pointsToDraw [i];
222 start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
223 end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
224
225 if (lineCaps && i == 1) {
226 segments.Add (CreateLineCap (start, end, SegmentType.Start));
227 }
228
229 segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
230 //segments.Add(CreateLineSegment(start, end, SegmentType.Full));
231
232 if (lineCaps && i == pointsToDraw.Count - 1) {
233 segments.Add (CreateLineCap (start, end, SegmentType.End));
234 }
235 }
236 }
237
238 // Add the line segments to the vertex helper, creating any joins as needed
239 for (var i = 0; i < segments.Count; i++) {
240 if (!lineList && i < segments.Count - 1) {
241 var vec1 = segments [i] [1].position - segments [i] [2].position;
242 var vec2 = segments [i + 1] [2].position - segments [i + 1] [1].position;
243 var angle = Vector2.Angle (vec1, vec2) * Mathf.Deg2Rad;
244
245 // Positive sign means the line is turning in a 'clockwise' direction
246 var sign = Mathf.Sign (Vector3.Cross (vec1.normalized, vec2.normalized).z);
247
248 // Calculate the miter point
249 var miterDistance = lineThickness / (2 * Mathf.Tan (angle / 2));
250 var miterPointA = segments [i] [2].position - vec1.normalized * miterDistance * sign;
251 var miterPointB = segments [i] [3].position + vec1.normalized * miterDistance * sign;
252
253 var joinType = LineJoins;
254 if (joinType == JoinType.Miter) {
255 // Make sure we can make a miter join without too many artifacts.
256 if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_MITER_JOIN) {
257 segments [i] [2].position = miterPointA;
258 segments [i] [3].position = miterPointB;
259 segments [i + 1] [0].position = miterPointB;
260 segments [i + 1] [1].position = miterPointA;
261 } else {
262 joinType = JoinType.Bevel;
263 }
264 }
265
266 if (joinType == JoinType.Bevel) {
267 if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_BEVEL_NICE_JOIN) {
268 if (sign < 0) {
269 segments [i] [2].position = miterPointA;
270 segments [i + 1] [1].position = miterPointA;
271 } else {
272 segments [i] [3].position = miterPointB;
273 segments [i + 1] [0].position = miterPointB;
274 }
275 }
276
277 var join = new UIVertex[] { segments [i] [2], segments [i] [3], segments [i + 1] [0], segments [i + 1] [1] };
278 vh.AddUIVertexQuad (join);
279 }
280 }
281
282 vh.AddUIVertexQuad (segments [i]);
283 }
284 if (vh.currentVertCount > 64000) {
285 Debug.LogError ("Max Verticies size is 64000, current mesh verticies count is [" + vh.currentVertCount + "] - Cannot Draw");
286 vh.Clear ();
287 return;
288 }
289
290 }
291
292 protected override void OnPopulateMesh(VertexHelper vh)
293 {
294 if (m_points != null && m_points.Count > 0) {
295 GeneratedUVs ();
296 vh.Clear ();
297
298 PopulateMesh (vh, m_points);
299
300 }
301 //else if (m_segments != null && m_segments.Count > 0) {
302 // GeneratedUVs ();
303 // vh.Clear ();
304
305 // for (int s = 0; s < m_segments.Count; s++) {
306 // Vector2[] pointsToDraw = m_segments [s];
307 // PopulateMesh (vh, pointsToDraw);
308 // }
309 //}
310 }
311
312 private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
313 {
314 if (type == SegmentType.Start)
315 {
316 var capStart = start - ((end - start).normalized * lineThickness / 2);
317 return CreateLineSegment(capStart, start, SegmentType.Start);
318 }
319 else if (type == SegmentType.End)
320 {
321 var capEnd = end + ((end - start).normalized * lineThickness / 2);
322 return CreateLineSegment(end, capEnd, SegmentType.End);
323 }
324
325 Debug.LogError("Bad SegmentType passed in to CreateLineCap. Must be SegmentType.Start or SegmentType.End");
326 return null;
327 }
328
329 private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, SegmentType type)
330 {
331 Vector2 offset = new Vector2((start.y - end.y), end.x - start.x).normalized * lineThickness / 2;
332
333 var v1 = start - offset;
334 var v2 = start + offset;
335 var v3 = end + offset;
336 var v4 = end - offset;
337 //Return the VDO with the correct uvs
338 switch (type)
339 {
340 case SegmentType.Start:
341 return SetVbo(new[] { v1, v2, v3, v4 }, startUvs);
342 case SegmentType.End:
343 return SetVbo(new[] { v1, v2, v3, v4 }, endUvs);
344 case SegmentType.Full:
345 return SetVbo(new[] { v1, v2, v3, v4 }, fullUvs);
346 default:
347 return SetVbo(new[] { v1, v2, v3, v4 }, middleUvs);
348 }
349 }
350
351 protected override void GeneratedUVs()
352 {
353 if (activeSprite != null)
354 {
355 var outer = Sprites.DataUtility.GetOuterUV(activeSprite);
356 var inner = Sprites.DataUtility.GetInnerUV(activeSprite);
357 UV_TOP_LEFT = new Vector2(outer.x, outer.y);
358 UV_BOTTOM_LEFT = new Vector2(outer.x, outer.w);
359 UV_TOP_CENTER_LEFT = new Vector2(inner.x, inner.y);
360 UV_TOP_CENTER_RIGHT = new Vector2(inner.z, inner.y);
361 UV_BOTTOM_CENTER_LEFT = new Vector2(inner.x, inner.w);
362 UV_BOTTOM_CENTER_RIGHT = new Vector2(inner.z, inner.w);
363 UV_TOP_RIGHT = new Vector2(outer.z, outer.y);
364 UV_BOTTOM_RIGHT = new Vector2(outer.z, outer.w);
365 }
366 else
367 {
368 UV_TOP_LEFT = Vector2.zero;
369 UV_BOTTOM_LEFT = new Vector2(0, 1);
370 UV_TOP_CENTER_LEFT = new Vector2(0.5f, 0);
371 UV_TOP_CENTER_RIGHT = new Vector2(0.5f, 0);
372 UV_BOTTOM_CENTER_LEFT = new Vector2(0.5f, 1);
373 UV_BOTTOM_CENTER_RIGHT = new Vector2(0.5f, 1);
374 UV_TOP_RIGHT = new Vector2(1, 0);
375 UV_BOTTOM_RIGHT = Vector2.one;
376 }
377
378
379 startUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_CENTER_LEFT, UV_TOP_CENTER_LEFT };
380 middleUvs = new[] { UV_TOP_CENTER_LEFT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_CENTER_RIGHT };
381 endUvs = new[] { UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_RIGHT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
382 fullUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
383 }
384
385 protected override void ResolutionToNativeSize(float distance)
386 {
387 if (UseNativeSize)
388 {
389 m_Resolution = distance / (activeSprite.rect.width / pixelsPerUnit);
390 lineThickness = activeSprite.rect.height / pixelsPerUnit;
391 }
392 }
393 }
394}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
void SetControlPoints(List< Vector2 > newControlPoints)
Definition: BezierPath.cs:51
override void OnPopulateMesh(VertexHelper vh)
override void ResolutionToNativeSize(float distance)
List< Vector2 > Points
Points to be drawn in the line.
Vector2[] IncreaseResolution(Vector2[] input)
UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
Credit Erdener Gonenc - @PixelEnvision.