Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Join us on Dec 8, 2022, between 7 am & 7 pm EST, in the DOTS Dev Blitz Day 2022 - Q&A forum, Discord, and Unity3D Subreddit to learn more about DOTS directly from the Unity Developers.
    Dismiss Notice
  3. Have a look at our Games Focus blog post series which will show what Unity is doing for all game developers – now, next year, and in the future.
    Dismiss Notice

New UI and line drawing

Discussion in 'UGUI & TextMesh Pro' started by jctz, Jun 25, 2014.

  1. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,337
    You could use an asset like this one.
     
  2. ahmadw

    ahmadw

    Joined:
    Jan 17, 2019
    Posts:
    3
     
  3. ahmadw

    ahmadw

    Joined:
    Jan 17, 2019
    Posts:
    3
    how can i draw aline between Ui element and mouse with this Line
     
  4. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,677
    If you check the UI Line COnnector script, it demonstrates how to procedurally wire up multiple UI elements using their positon.
    https://bitbucket.org/UnityUIExtens...c/master/Scripts/Utilities/UILineConnector.cs

    The trick with lining up with the mouse however, is that you need to transform the mouse coordinate to a screenspace coordinate for UI to use. This keeps changing with each unity version, check this reddit for the current conversations on the matter.
    https://www.reddit.com/r/Unity3D/comments/34bqxh/getting_ui_position_from_mouse_position/

    P.S. couldn't reply to your PM as it was locked?
     
  5. ahmadw

    ahmadw

    Joined:
    Jan 17, 2019
    Posts:
    3
    thank you for that i solved the mouse problem but I have one more problem
    i cant add a new rect transform to the line connector in runtime how can i solve this??
     
  6. radiatoryang

    radiatoryang

    Joined:
    Jul 7, 2014
    Posts:
    16
    like others, I didn't want to import all of the UI Extensions just to use UI Line Renderer, so I modified this into a standalone version

    - Bezier class is inside UILineRenderer in case you already have a Bezier class in your project
    - removed the Catenary BezierType because I didn't really see the point
    - pasted-in a lot of stuff from Unity UI Image class to get it working without any dependencies

    just drop UILineRenderer.cs somewhere in your Assets folder... it works for me as of Unity 2020.1.14f1 / Windows

    but of course, it's messy, it might be broken, use at your own risk, etc.

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. namespace UnityEngine.UI
    6. {
    7.     // based off of code from https://forum.unity.com/threads/new-ui-and-line-drawing.253772/page-2
    8.     // and the Unity UI Extensions project https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/About
    9.     [AddComponentMenu("UI/Line Renderer (UI)")]
    10.     [RequireComponent(typeof(RectTransform), typeof(CanvasRenderer))]
    11.     public class UILineRenderer : MaskableGraphic
    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.         }
    34.  
    35.         private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad;
    36.  
    37.         // A bevel 'nice' join displaces the vertices of the line segment instead of simply rendering a
    38.         // quad to connect the endpoints. This improves the look of textured and transparent lines, since
    39.         // there is no overlapping.
    40.         private const float MIN_BEVEL_NICE_JOIN = 30 * Mathf.Deg2Rad;
    41.  
    42.         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;
    43.         private static Vector2[] startUvs, middleUvs, endUvs, fullUvs;
    44.  
    45.         /// <summary>
    46.         /// The specified Material used by this Image. The default Material is used instead if one wasn't specified.
    47.         /// </summary>
    48.         public override Material material
    49.         {
    50.             get
    51.             {
    52.                 if (m_Material != null)
    53.                     return m_Material;
    54. #if UNITY_EDITOR
    55.                 if (Application.isPlaying && activeSprite && activeSprite.associatedAlphaSplitTexture != null)
    56.                     return defaultETC1GraphicMaterial;
    57. #else
    58.  
    59.                 if (activeSprite && activeSprite.associatedAlphaSplitTexture != null)
    60.                     return defaultETC1GraphicMaterial;
    61. #endif
    62.  
    63.                 return defaultMaterial;
    64.             }
    65.  
    66.             set
    67.             {
    68.                 base.material = value;
    69.             }
    70.         }
    71.  
    72.         static protected Material s_ETC1DefaultUI = null;
    73.         static public Material defaultETC1GraphicMaterial
    74.         {
    75.             get
    76.             {
    77.                 if (s_ETC1DefaultUI == null)
    78.                     s_ETC1DefaultUI = Canvas.GetETC1SupportedCanvasMaterial();
    79.                 return s_ETC1DefaultUI;
    80.             }
    81.         }
    82.  
    83.         /// <summary>
    84.         /// Image's texture comes from the UnityEngine.Image.
    85.         /// </summary>
    86.         public override Texture mainTexture
    87.         {
    88.             get
    89.             {
    90.                 if (activeSprite == null)
    91.                 {
    92.                     if (material != null && material.mainTexture != null)
    93.                     {
    94.                         return material.mainTexture;
    95.                     }
    96.                     return s_WhiteTexture;
    97.                 }
    98.  
    99.                 return activeSprite.texture;
    100.             }
    101.         }
    102.  
    103.         [SerializeField] private Sprite m_Sprite;
    104.         public Sprite sprite { get { return m_Sprite; } set { if (SetClass(ref m_Sprite, value)) GeneratedUVs(); SetAllDirty(); } }
    105.  
    106.         [NonSerialized]
    107.         private Sprite m_OverrideSprite;
    108.  
    109.         public Sprite overrideSprite
    110.         {
    111.             get { return activeSprite; }
    112.             set
    113.             {
    114.                 if (SetClass(ref m_OverrideSprite, value))
    115.                 {
    116.                     GeneratedUVs();
    117.                     SetAllDirty();
    118.                 }
    119.             }
    120.         }
    121.  
    122.         private Sprite activeSprite { get { return m_OverrideSprite != null ? m_OverrideSprite : sprite; } }
    123.  
    124.         [SerializeField, Tooltip("Initial control points to draw lines between; by default in UI mesh space but tweak it with useRectAsNormalizedSpace")]
    125.         internal Vector2[] m_points;
    126.         [SerializeField, Tooltip("Segments to be drawn after Bezier pass, an array of points")]
    127.         internal List<Vector2[]> m_segments;
    128.         [SerializeField, Tooltip("Use relative normalized bounds of the Rect Transform (0.0 - 1.0) or screen space coordinates?")]
    129.         internal bool useRectAsNormalizedSpace;
    130.  
    131.         [SerializeField, Tooltip("Thickness of the line")]
    132.         internal float lineThickness = 2;
    133.  
    134.         [SerializeField, Tooltip("Do the points identify a single long continuous line, or split pairs of lines?")]
    135.         internal bool drawPointsAsPairs;
    136.         [SerializeField, Tooltip("Add end caps to each line\nMultiple caps when used with Line List")]
    137.         internal bool lineCaps;
    138.  
    139.         public float LineThickness
    140.         {
    141.             get { return lineThickness; }
    142.             set { lineThickness = value; SetAllDirty(); }
    143.         }
    144.  
    145.         public bool RelativeSize
    146.         {
    147.             get { return useRectAsNormalizedSpace; }
    148.             set { useRectAsNormalizedSpace = value; SetAllDirty(); }
    149.         }
    150.  
    151.         public bool LineList
    152.         {
    153.             get { return drawPointsAsPairs; }
    154.             set { drawPointsAsPairs = value; SetAllDirty(); }
    155.         }
    156.  
    157.         public bool LineCaps
    158.         {
    159.             get { return lineCaps; }
    160.             set { lineCaps = value; SetAllDirty(); }
    161.         }
    162.  
    163.         [Tooltip("The type of Join used between lines, Square/Mitre or Curved/Bevel")]
    164.         public JoinType LineJoins = JoinType.Bevel;
    165.  
    166.         [Tooltip("Bezier method to apply to line for smoothing, requires at least 4 points")]
    167.         public BezierType BezierMode = BezierType.None;
    168.  
    169.         [SerializeField, Tooltip("Resolution of the Bezier curve, more segments = more smoothness = more vertices")]
    170.         internal int bezierSegmentsPerCurve = 10;
    171.  
    172.         public int BezierSegmentsPerCurve
    173.         {
    174.             get { return bezierSegmentsPerCurve; }
    175.             set { bezierSegmentsPerCurve = value; }
    176.         }
    177.  
    178.         [HideInInspector]
    179.         public bool drivenExternally = false;
    180.  
    181.  
    182.         /// <summary>
    183.         /// Points to be drawn in the line.
    184.         /// </summary>
    185.         public Vector2[] Points
    186.         {
    187.             get
    188.             {
    189.                 return m_points;
    190.             }
    191.  
    192.             set
    193.             {
    194.                 if (m_points == value)
    195.                     return;
    196.                 m_points = value;
    197.                 SetAllDirty();
    198.             }
    199.         }
    200.  
    201.         /// <summary>
    202.         /// List of Segments to be drawn.
    203.         /// </summary>
    204.         public List<Vector2[]> Segments
    205.         {
    206.             get
    207.             {
    208.                 return m_segments;
    209.             }
    210.  
    211.             set
    212.             {
    213.                 m_segments = value;
    214.                 SetAllDirty();
    215.             }
    216.         }
    217.  
    218.         protected UILineRenderer()
    219.         {
    220.             useLegacyMeshGeneration = false;
    221.         }
    222.  
    223.         /// <summary>
    224.         /// Update the renderer's material.
    225.         /// </summary>
    226.         protected override void UpdateMaterial()
    227.         {
    228.             base.UpdateMaterial();
    229.  
    230.             // check if this sprite has an associated alpha texture (generated when splitting RGBA = RGB + A as two textures without alpha)
    231.  
    232.             if (activeSprite == null)
    233.             {
    234.                 canvasRenderer.SetAlphaTexture(null);
    235.                 return;
    236.             }
    237.  
    238.             Texture2D alphaTex = activeSprite.associatedAlphaSplitTexture;
    239.  
    240.             if (alphaTex != null)
    241.             {
    242.                 canvasRenderer.SetAlphaTexture(alphaTex);
    243.             }
    244.         }
    245.  
    246.         private void PopulateMesh(VertexHelper vh, Vector2[] pointsToDraw)
    247.         {
    248.             //If Bezier is desired, pick the implementation
    249.             if (BezierMode != BezierType.None && pointsToDraw.Length > 3)
    250.             {
    251.                 BezierPath bezierPath = new BezierPath();
    252.  
    253.                 bezierPath.SetControlPoints(pointsToDraw);
    254.                 bezierPath.SegmentsPerCurve = bezierSegmentsPerCurve;
    255.                 List<Vector2> drawingPoints;
    256.                 switch (BezierMode)
    257.                 {
    258.                     case BezierType.Basic:
    259.                         drawingPoints = bezierPath.GetDrawingPoints0();
    260.                         break;
    261.                     case BezierType.Improved:
    262.                         drawingPoints = bezierPath.GetDrawingPoints1();
    263.                         break;
    264.                     default:
    265.                         drawingPoints = bezierPath.GetDrawingPoints2();
    266.                         break;
    267.                 }
    268.  
    269.                 pointsToDraw = drawingPoints.ToArray();
    270.             }
    271.  
    272.             // scale based on the size of the rect or use absolute, this is switchable
    273.             var sizeX = !useRectAsNormalizedSpace ? 1 : rectTransform.rect.width;
    274.             var sizeY = !useRectAsNormalizedSpace ? 1 : rectTransform.rect.height;
    275.             var offsetX = -rectTransform.pivot.x * sizeX;
    276.             var offsetY = -rectTransform.pivot.y * sizeY;
    277.  
    278.             // Generate the quads that make up the wide line
    279.             var segments = new List<UIVertex[]>();
    280.             if (drawPointsAsPairs)
    281.             {
    282.                 for (var i = 1; i < pointsToDraw.Length; i += 2)
    283.                 {
    284.                     var start = pointsToDraw[i - 1];
    285.                     var end = pointsToDraw[i];
    286.                     start = new Vector2(start.x * sizeX + offsetX, start.y * sizeY + offsetY);
    287.                     end = new Vector2(end.x * sizeX + offsetX, end.y * sizeY + offsetY);
    288.  
    289.                     if (lineCaps)
    290.                     {
    291.                         segments.Add(CreateLineCap(start, end, SegmentType.Start));
    292.                     }
    293.  
    294.                     segments.Add(CreateLineSegment(start, end, SegmentType.Middle, segments.Count > 1 ? segments[segments.Count - 2] : null));
    295.  
    296.                     if (lineCaps)
    297.                     {
    298.                         segments.Add(CreateLineCap(start, end, SegmentType.End));
    299.                     }
    300.                 }
    301.             }
    302.             else
    303.             {
    304.                 for (var i = 1; i < pointsToDraw.Length; i++)
    305.                 {
    306.                     var start = pointsToDraw[i - 1];
    307.                     var end = pointsToDraw[i];
    308.                     start = new Vector2(start.x * sizeX + offsetX, start.y * sizeY + offsetY);
    309.                     end = new Vector2(end.x * sizeX + offsetX, end.y * sizeY + offsetY);
    310.  
    311.                     if (lineCaps && i == 1)
    312.                     {
    313.                         segments.Add(CreateLineCap(start, end, SegmentType.Start));
    314.                     }
    315.  
    316.                     segments.Add(CreateLineSegment(start, end, SegmentType.Middle));
    317.  
    318.                     if (lineCaps && i == pointsToDraw.Length - 1)
    319.                     {
    320.                         segments.Add(CreateLineCap(start, end, SegmentType.End));
    321.                     }
    322.                 }
    323.             }
    324.  
    325.             // Add the line segments to the vertex helper, creating any joins as needed
    326.             for (var i = 0; i < segments.Count; i++)
    327.             {
    328.                 if (!drawPointsAsPairs && i < segments.Count - 1)
    329.                 {
    330.                     var vec1 = segments[i][1].position - segments[i][2].position;
    331.                     var vec2 = segments[i + 1][2].position - segments[i + 1][1].position;
    332.                     var angle = Vector2.Angle(vec1, vec2) * Mathf.Deg2Rad;
    333.  
    334.                     // Positive sign means the line is turning in a 'clockwise' direction
    335.                     var sign = Mathf.Sign(Vector3.Cross(vec1.normalized, vec2.normalized).z);
    336.  
    337.                     // Calculate the miter point
    338.                     var miterDistance = lineThickness / (2 * Mathf.Tan(angle / 2));
    339.                     var miterPointA = segments[i][2].position - vec1.normalized * miterDistance * sign;
    340.                     var miterPointB = segments[i][3].position + vec1.normalized * miterDistance * sign;
    341.  
    342.                     var joinType = LineJoins;
    343.                     if (joinType == JoinType.Miter)
    344.                     {
    345.                         // Make sure we can make a miter join without too many artifacts.
    346.                         if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_MITER_JOIN)
    347.                         {
    348.                             segments[i][2].position = miterPointA;
    349.                             segments[i][3].position = miterPointB;
    350.                             segments[i + 1][0].position = miterPointB;
    351.                             segments[i + 1][1].position = miterPointA;
    352.                         }
    353.                         else
    354.                         {
    355.                             joinType = JoinType.Bevel;
    356.                         }
    357.                     }
    358.  
    359.                     if (joinType == JoinType.Bevel)
    360.                     {
    361.                         if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_BEVEL_NICE_JOIN)
    362.                         {
    363.                             if (sign < 0)
    364.                             {
    365.                                 segments[i][2].position = miterPointA;
    366.                                 segments[i + 1][1].position = miterPointA;
    367.                             }
    368.                             else
    369.                             {
    370.                                 segments[i][3].position = miterPointB;
    371.                                 segments[i + 1][0].position = miterPointB;
    372.                             }
    373.                         }
    374.  
    375.                         var join = new UIVertex[] { segments[i][2], segments[i][3], segments[i + 1][0], segments[i + 1][1] };
    376.                         vh.AddUIVertexQuad(join);
    377.                     }
    378.                 }
    379.  
    380.                 vh.AddUIVertexQuad(segments[i]);
    381.             }
    382.             if (vh.currentVertCount > 64000)
    383.             {
    384.                 Debug.LogError("Max Verticies size is 64000, current mesh vertcies count is [" + vh.currentVertCount + "] - Cannot Draw");
    385.                 vh.Clear();
    386.                 return;
    387.             }
    388.  
    389.         }
    390.  
    391.         protected override void OnPopulateMesh(VertexHelper vh)
    392.         {
    393.             if (m_points != null && m_points.Length > 0)
    394.             {
    395.                 GeneratedUVs();
    396.                 vh.Clear();
    397.  
    398.                 PopulateMesh(vh, m_points);
    399.  
    400.             }
    401.             else if (m_segments != null && m_segments.Count > 0)
    402.             {
    403.                 GeneratedUVs();
    404.                 vh.Clear();
    405.  
    406.                 for (int s = 0; s < m_segments.Count; s++)
    407.                 {
    408.                     Vector2[] pointsToDraw = m_segments[s];
    409.                     PopulateMesh(vh, pointsToDraw);
    410.                 }
    411.             }
    412.  
    413.  
    414.         }
    415.  
    416.         private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
    417.         {
    418.             if (type == SegmentType.Start)
    419.             {
    420.                 var capStart = start - ((end - start).normalized * lineThickness / 2);
    421.                 return CreateLineSegment(capStart, start, SegmentType.Start);
    422.             }
    423.             else if (type == SegmentType.End)
    424.             {
    425.                 var capEnd = end + ((end - start).normalized * lineThickness / 2);
    426.                 return CreateLineSegment(end, capEnd, SegmentType.End);
    427.             }
    428.  
    429.             Debug.LogError("Bad SegmentType passed in to CreateLineCap. Must be SegmentType.Start or SegmentType.End");
    430.             return null;
    431.         }
    432.  
    433.         private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, SegmentType type, UIVertex[] previousVert = null)
    434.         {
    435.             Vector2 offset = new Vector2((start.y - end.y), end.x - start.x).normalized * lineThickness / 2;
    436.  
    437.             Vector2 v1 = Vector2.zero;
    438.             Vector2 v2 = Vector2.zero;
    439.             if (previousVert != null)
    440.             {
    441.                 v1 = new Vector2(previousVert[3].position.x, previousVert[3].position.y);
    442.                 v2 = new Vector2(previousVert[2].position.x, previousVert[2].position.y);
    443.             }
    444.             else
    445.             {
    446.                 v1 = start - offset;
    447.                 v2 = start + offset;
    448.             }
    449.  
    450.             var v3 = end + offset;
    451.             var v4 = end - offset;
    452.             //Return the VDO with the correct uvs
    453.             switch (type)
    454.             {
    455.                 case SegmentType.Start:
    456.                     return SetVbo(new[] { v1, v2, v3, v4 }, startUvs);
    457.                 case SegmentType.End:
    458.                     return SetVbo(new[] { v1, v2, v3, v4 }, endUvs);
    459.                 case SegmentType.Full:
    460.                     return SetVbo(new[] { v1, v2, v3, v4 }, fullUvs);
    461.                 default:
    462.                     return SetVbo(new[] { v1, v2, v3, v4 }, middleUvs);
    463.             }
    464.         }
    465.  
    466.         protected UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
    467.         {
    468.             UIVertex[] vbo = new UIVertex[4];
    469.             for (int i = 0; i < vertices.Length; i++)
    470.             {
    471.                 var vert = UIVertex.simpleVert;
    472.                 vert.color = color;
    473.                 vert.position = vertices[i];
    474.                 vert.uv0 = uvs[i];
    475.                 vbo[i] = vert;
    476.             }
    477.             return vbo;
    478.         }
    479.  
    480.         protected void GeneratedUVs()
    481.         {
    482.             if (activeSprite != null)
    483.             {
    484.                 var outer = Sprites.DataUtility.GetOuterUV(activeSprite);
    485.                 var inner = Sprites.DataUtility.GetInnerUV(activeSprite);
    486.                 UV_TOP_LEFT = new Vector2(outer.x, outer.y);
    487.                 UV_BOTTOM_LEFT = new Vector2(outer.x, outer.w);
    488.                 UV_TOP_CENTER_LEFT = new Vector2(inner.x, inner.y);
    489.                 UV_TOP_CENTER_RIGHT = new Vector2(inner.z, inner.y);
    490.                 UV_BOTTOM_CENTER_LEFT = new Vector2(inner.x, inner.w);
    491.                 UV_BOTTOM_CENTER_RIGHT = new Vector2(inner.z, inner.w);
    492.                 UV_TOP_RIGHT = new Vector2(outer.z, outer.y);
    493.                 UV_BOTTOM_RIGHT = new Vector2(outer.z, outer.w);
    494.             }
    495.             else
    496.             {
    497.                 UV_TOP_LEFT = Vector2.zero;
    498.                 UV_BOTTOM_LEFT = new Vector2(0, 1);
    499.                 UV_TOP_CENTER_LEFT = new Vector2(0.5f, 0);
    500.                 UV_TOP_CENTER_RIGHT = new Vector2(0.5f, 0);
    501.                 UV_BOTTOM_CENTER_LEFT = new Vector2(0.5f, 1);
    502.                 UV_BOTTOM_CENTER_RIGHT = new Vector2(0.5f, 1);
    503.                 UV_TOP_RIGHT = new Vector2(1, 0);
    504.                 UV_BOTTOM_RIGHT = Vector2.one;
    505.             }
    506.  
    507.  
    508.             startUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_CENTER_LEFT, UV_TOP_CENTER_LEFT };
    509.             middleUvs = new[] { UV_TOP_CENTER_LEFT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_CENTER_RIGHT };
    510.             endUvs = new[] { UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_RIGHT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
    511.             fullUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
    512.         }
    513.  
    514.         private int GetSegmentPointCount()
    515.         {
    516.             if (Segments?.Count > 0)
    517.             {
    518.                 int pointCount = 0;
    519.                 foreach (var segment in Segments)
    520.                 {
    521.                     pointCount += segment.Length;
    522.                 }
    523.                 return pointCount;
    524.             }
    525.             return Points.Length;
    526.         }
    527.  
    528.         /// <summary>
    529.         /// Get the Vector2 position of a line index
    530.         /// </summary>
    531.         /// <remarks>
    532.         /// Positive numbers should be used to specify Index and Segment
    533.         /// </remarks>
    534.         /// <param name="index">Requied Index of the point, starting from point 1</param>
    535.         /// <param name="segmentIndex">(optional) Required Segment the point is held in, Starting from Segment 1</param>
    536.         /// <returns>Vector2 position of the point within UI Space</returns>
    537.         public Vector2 GetPosition(int index, int segmentIndex = 0)
    538.         {
    539.             if (segmentIndex > 0)
    540.             {
    541.                 return Segments[segmentIndex - 1][index - 1];
    542.             }
    543.             else if (Segments.Count > 0)
    544.             {
    545.                 var segmentIndexCount = 0;
    546.                 var indexCount = index;
    547.                 foreach (var segment in Segments)
    548.                 {
    549.                     if (indexCount - segment.Length > 0)
    550.                     {
    551.                         indexCount -= segment.Length;
    552.                         segmentIndexCount += 1;
    553.                     }
    554.                     else
    555.                     {
    556.                         break;
    557.                     }
    558.                 }
    559.                 return Segments[segmentIndexCount][indexCount - 1];
    560.             }
    561.             else
    562.             {
    563.                 return Points[index - 1];
    564.             }
    565.         }
    566.  
    567.         /// <summary>
    568.         /// Get the Vector2 position of a line within a specific segment
    569.         /// </summary>
    570.         /// <param name="index">Requied Index of the point, starting from point 1</param>
    571.         /// <param name="segmentIndex"> Required Segment the point is held in, Starting from Segment 1</param>
    572.         /// <returns>Vector2 position of the point within UI Space</returns>
    573.         public Vector2 GetPositionBySegment(int index, int segment)
    574.         {
    575.             return Segments[segment][index - 1];
    576.         }
    577.  
    578.         /// <summary>
    579.         /// Get the closest point between two given Vector2s from a given Vector2 point
    580.         /// </summary>
    581.         /// <param name="p1">Starting postion</param>
    582.         /// <param name="p2">End position</param>
    583.         /// <param name="p3">Desired / Selected point</param>
    584.         /// <returns>Closest Vector2 position of the target within UI Space</returns>
    585.         public Vector2 GetClosestPoint(Vector2 p1, Vector2 p2, Vector2 p3)
    586.         {
    587.             Vector2 from_p1_to_p3 = p3 - p1;
    588.             Vector2 from_p1_to_p2 = p2 - p1;
    589.             float dot = Vector2.Dot(from_p1_to_p3, from_p1_to_p2.normalized);
    590.             dot /= from_p1_to_p2.magnitude;
    591.             float t = Mathf.Clamp01(dot);
    592.             return p1 + from_p1_to_p2 * t;
    593.         }
    594.  
    595.         bool SetClass<T>(ref T currentValue, T newValue) where T : class
    596.         {
    597.             if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
    598.                 return false;
    599.  
    600.             currentValue = newValue;
    601.             return true;
    602.         }
    603.  
    604.  
    605.         /**
    606.         This class demonstrates the code discussed in these two articles:
    607.         http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/
    608.         http://devmag.org.za/2011/06/23/bzier-path-algorithms/
    609.         Use this code as you wish, at your own risk. If it blows up
    610.         your computer, makes a plane crash, or otherwise cause damage,
    611.         injury, or death, it is not my fault.
    612.         @author Herman Tulleken, dev.mag.org.za
    613.         */
    614.  
    615.         /**
    616.             Class for representing a Bezier path, and methods for getting suitable points to
    617.             draw the curve with line segments.
    618.         */
    619.         class BezierPath
    620.         {
    621.             public int SegmentsPerCurve = 10;
    622.             public float MINIMUM_SQR_DISTANCE = 0.01f;
    623.  
    624.             // This corresponds to about 172 degrees, 8 degrees from a straight line
    625.             public float DIVISION_THRESHOLD = -0.99f;
    626.  
    627.             private List<Vector2> controlPoints;
    628.  
    629.             private int curveCount; //how many bezier curves in this path?
    630.  
    631.             /**
    632.                 Constructs a new empty Bezier curve. Use one of these methods
    633.                 to add points: SetControlPoints, Interpolate, SamplePoints.
    634.             */
    635.             public BezierPath()
    636.             {
    637.                 controlPoints = new List<Vector2>();
    638.             }
    639.  
    640.             /**
    641.                 Sets the control points of this Bezier path.
    642.                 Points 0-3 forms the first Bezier curve, points
    643.                 3-6 forms the second curve, etc.
    644.             */
    645.             public void SetControlPoints(List<Vector2> newControlPoints)
    646.             {
    647.                 controlPoints.Clear();
    648.                 controlPoints.AddRange(newControlPoints);
    649.                 curveCount = (controlPoints.Count - 1) / 3;
    650.             }
    651.  
    652.             public void SetControlPoints(Vector2[] newControlPoints)
    653.             {
    654.                 controlPoints.Clear();
    655.                 controlPoints.AddRange(newControlPoints);
    656.                 curveCount = (controlPoints.Count - 1) / 3;
    657.             }
    658.  
    659.             /**
    660.                 Returns the control points for this Bezier curve.
    661.             */
    662.             public List<Vector2> GetControlPoints()
    663.             {
    664.                 return controlPoints;
    665.             }
    666.  
    667.  
    668.             /**
    669.                 Calculates a Bezier interpolated path for the given points.
    670.             */
    671.             public void Interpolate(List<Vector2> segmentPoints, float scale)
    672.             {
    673.                 controlPoints.Clear();
    674.  
    675.                 if (segmentPoints.Count < 2)
    676.                 {
    677.                     return;
    678.                 }
    679.  
    680.                 for (int i = 0; i < segmentPoints.Count; i++)
    681.                 {
    682.                     if (i == 0) // is first
    683.                     {
    684.                         Vector2 p1 = segmentPoints[i];
    685.                         Vector2 p2 = segmentPoints[i + 1];
    686.  
    687.                         Vector2 tangent = (p2 - p1);
    688.                         Vector2 q1 = p1 + scale * tangent;
    689.  
    690.                         controlPoints.Add(p1);
    691.                         controlPoints.Add(q1);
    692.                     }
    693.                     else if (i == segmentPoints.Count - 1) //last
    694.                     {
    695.                         Vector2 p0 = segmentPoints[i - 1];
    696.                         Vector2 p1 = segmentPoints[i];
    697.                         Vector2 tangent = (p1 - p0);
    698.                         Vector2 q0 = p1 - scale * tangent;
    699.  
    700.                         controlPoints.Add(q0);
    701.                         controlPoints.Add(p1);
    702.                     }
    703.                     else
    704.                     {
    705.                         Vector2 p0 = segmentPoints[i - 1];
    706.                         Vector2 p1 = segmentPoints[i];
    707.                         Vector2 p2 = segmentPoints[i + 1];
    708.                         Vector2 tangent = (p2 - p0).normalized;
    709.                         Vector2 q0 = p1 - scale * tangent * (p1 - p0).magnitude;
    710.                         Vector2 q1 = p1 + scale * tangent * (p2 - p1).magnitude;
    711.  
    712.                         controlPoints.Add(q0);
    713.                         controlPoints.Add(p1);
    714.                         controlPoints.Add(q1);
    715.                     }
    716.                 }
    717.  
    718.                 curveCount = (controlPoints.Count - 1) / 3;
    719.             }
    720.  
    721.             /**
    722.                 Sample the given points as a Bezier path.
    723.             */
    724.             public void SamplePoints(List<Vector2> sourcePoints, float minSqrDistance, float maxSqrDistance, float scale)
    725.             {
    726.                 if (sourcePoints.Count < 2)
    727.                 {
    728.                     return;
    729.                 }
    730.  
    731.                 Stack<Vector2> samplePoints = new Stack<Vector2>();
    732.  
    733.                 samplePoints.Push(sourcePoints[0]);
    734.  
    735.                 Vector2 potentialSamplePoint = sourcePoints[1];
    736.  
    737.                 int i = 2;
    738.  
    739.                 for (i = 2; i < sourcePoints.Count; i++)
    740.                 {
    741.                     if (
    742.                         ((potentialSamplePoint - sourcePoints[i]).sqrMagnitude > minSqrDistance) &&
    743.                         ((samplePoints.Peek() - sourcePoints[i]).sqrMagnitude > maxSqrDistance))
    744.                     {
    745.                         samplePoints.Push(potentialSamplePoint);
    746.                     }
    747.  
    748.                     potentialSamplePoint = sourcePoints[i];
    749.                 }
    750.  
    751.                 //now handle last bit of curve
    752.                 Vector2 p1 = samplePoints.Pop(); //last sample point
    753.                 Vector2 p0 = samplePoints.Peek(); //second last sample point
    754.                 Vector2 tangent = (p0 - potentialSamplePoint).normalized;
    755.                 float d2 = (potentialSamplePoint - p1).magnitude;
    756.                 float d1 = (p1 - p0).magnitude;
    757.                 p1 = p1 + tangent * ((d1 - d2) / 2);
    758.  
    759.                 samplePoints.Push(p1);
    760.                 samplePoints.Push(potentialSamplePoint);
    761.  
    762.  
    763.                 Interpolate(new List<Vector2>(samplePoints), scale);
    764.             }
    765.  
    766.             /**
    767.                 Caluclates a point on the path.
    768.                
    769.                 @param curveIndex The index of the curve that the point is on. For example,
    770.                 the second curve (index 1) is the curve with controlpoints 3, 4, 5, and 6.
    771.                
    772.                 @param t The paramater indicating where on the curve the point is. 0 corresponds
    773.                 to the "left" point, 1 corresponds to the "right" end point.
    774.             */
    775.             public Vector2 CalculateBezierPoint(int curveIndex, float t)
    776.             {
    777.                 int nodeIndex = curveIndex * 3;
    778.  
    779.                 Vector2 p0 = controlPoints[nodeIndex];
    780.                 Vector2 p1 = controlPoints[nodeIndex + 1];
    781.                 Vector2 p2 = controlPoints[nodeIndex + 2];
    782.                 Vector2 p3 = controlPoints[nodeIndex + 3];
    783.  
    784.                 return CalculateBezierPoint(t, p0, p1, p2, p3);
    785.             }
    786.  
    787.             /**
    788.                 Gets the drawing points. This implementation simply calculates a certain number
    789.                 of points per curve.
    790.             */
    791.             public List<Vector2> GetDrawingPoints0()
    792.             {
    793.                 List<Vector2> drawingPoints = new List<Vector2>();
    794.  
    795.                 for (int curveIndex = 0; curveIndex < curveCount; curveIndex++)
    796.                 {
    797.                     if (curveIndex == 0) //Only do this for the first end point.
    798.                                          //When i != 0, this coincides with the
    799.                                          //end point of the previous segment,
    800.                     {
    801.                         drawingPoints.Add(CalculateBezierPoint(curveIndex, 0));
    802.                     }
    803.  
    804.                     for (int j = 1; j <= SegmentsPerCurve; j++)
    805.                     {
    806.                         float t = j / (float)SegmentsPerCurve;
    807.                         drawingPoints.Add(CalculateBezierPoint(curveIndex, t));
    808.                     }
    809.                 }
    810.  
    811.                 return drawingPoints;
    812.             }
    813.  
    814.             /**
    815.                 Gets the drawing points. This implementation simply calculates a certain number
    816.                 of points per curve.
    817.                 This is a lsightly different inplementation from the one above.
    818.             */
    819.             public List<Vector2> GetDrawingPoints1()
    820.             {
    821.                 List<Vector2> drawingPoints = new List<Vector2>();
    822.  
    823.                 for (int i = 0; i < controlPoints.Count - 3; i += 3)
    824.                 {
    825.                     Vector2 p0 = controlPoints[i];
    826.                     Vector2 p1 = controlPoints[i + 1];
    827.                     Vector2 p2 = controlPoints[i + 2];
    828.                     Vector2 p3 = controlPoints[i + 3];
    829.  
    830.                     if (i == 0) //only do this for the first end point. When i != 0, this coincides with the end point of the previous segment,
    831.                     {
    832.                         drawingPoints.Add(CalculateBezierPoint(0, p0, p1, p2, p3));
    833.                     }
    834.  
    835.                     for (int j = 1; j <= SegmentsPerCurve; j++)
    836.                     {
    837.                         float t = j / (float)SegmentsPerCurve;
    838.                         drawingPoints.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
    839.                     }
    840.                 }
    841.  
    842.                 return drawingPoints;
    843.             }
    844.  
    845.             /**
    846.                 This gets the drawing points of a bezier curve, using recursive division,
    847.                 which results in less points for the same accuracy as the above implementation.
    848.             */
    849.             public List<Vector2> GetDrawingPoints2()
    850.             {
    851.                 List<Vector2> drawingPoints = new List<Vector2>();
    852.  
    853.                 for (int curveIndex = 0; curveIndex < curveCount; curveIndex++)
    854.                 {
    855.                     List<Vector2> bezierCurveDrawingPoints = FindDrawingPoints(curveIndex);
    856.  
    857.                     if (curveIndex != 0)
    858.                     {
    859.                         //remove the fist point, as it coincides with the last point of the previous Bezier curve.
    860.                         bezierCurveDrawingPoints.RemoveAt(0);
    861.                     }
    862.  
    863.                     drawingPoints.AddRange(bezierCurveDrawingPoints);
    864.                 }
    865.  
    866.                 return drawingPoints;
    867.             }
    868.  
    869.             List<Vector2> FindDrawingPoints(int curveIndex)
    870.             {
    871.                 List<Vector2> pointList = new List<Vector2>();
    872.  
    873.                 Vector2 left = CalculateBezierPoint(curveIndex, 0);
    874.                 Vector2 right = CalculateBezierPoint(curveIndex, 1);
    875.  
    876.                 pointList.Add(left);
    877.                 pointList.Add(right);
    878.  
    879.                 FindDrawingPoints(curveIndex, 0, 1, pointList, 1);
    880.  
    881.                 return pointList;
    882.             }
    883.  
    884.  
    885.             /**
    886.                 @returns the number of points added.
    887.             */
    888.             int FindDrawingPoints(int curveIndex, float t0, float t1,
    889.                 List<Vector2> pointList, int insertionIndex)
    890.             {
    891.                 Vector2 left = CalculateBezierPoint(curveIndex, t0);
    892.                 Vector2 right = CalculateBezierPoint(curveIndex, t1);
    893.  
    894.                 if ((left - right).sqrMagnitude < MINIMUM_SQR_DISTANCE)
    895.                 {
    896.                     return 0;
    897.                 }
    898.  
    899.                 float tMid = (t0 + t1) / 2;
    900.                 Vector2 mid = CalculateBezierPoint(curveIndex, tMid);
    901.  
    902.                 Vector2 leftDirection = (left - mid).normalized;
    903.                 Vector2 rightDirection = (right - mid).normalized;
    904.  
    905.                 if (Vector2.Dot(leftDirection, rightDirection) > DIVISION_THRESHOLD || Mathf.Abs(tMid - 0.5f) < 0.0001f)
    906.                 {
    907.                     int pointsAddedCount = 0;
    908.  
    909.                     pointsAddedCount += FindDrawingPoints(curveIndex, t0, tMid, pointList, insertionIndex);
    910.                     pointList.Insert(insertionIndex + pointsAddedCount, mid);
    911.                     pointsAddedCount++;
    912.                     pointsAddedCount += FindDrawingPoints(curveIndex, tMid, t1, pointList, insertionIndex + pointsAddedCount);
    913.  
    914.                     return pointsAddedCount;
    915.                 }
    916.  
    917.                 return 0;
    918.             }
    919.  
    920.  
    921.  
    922.             /**
    923.                 Caluclates a point on the Bezier curve represented with the four controlpoints given.
    924.             */
    925.             private Vector2 CalculateBezierPoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3)
    926.             {
    927.                 float u = 1 - t;
    928.                 float tt = t * t;
    929.                 float uu = u * u;
    930.                 float uuu = uu * u;
    931.                 float ttt = tt * t;
    932.  
    933.                 Vector2 p = uuu * p0; //first term
    934.  
    935.                 p += 3 * uu * t * p1; //second term
    936.                 p += 3 * u * tt * p2; //third term
    937.                 p += ttt * p3; //fourth term
    938.  
    939.                 return p;
    940.  
    941.             }
    942.         }
    943.  
    944.  
    945.     }
    946. }