From 11ee80411f7d11fefe6b104ee3f421ff3fbdbd2c Mon Sep 17 00:00:00 2001 From: Simon Jackson Date: Fri, 4 Aug 2017 20:01:02 +0100 Subject: [PATCH] Added a new Line Renderer mode (Catenary) --- .../MenuExample/Prefabs/Menus/MainMenu.prefab | 2 +- Scripts/Primitives/UILineRenderer.cs | 28 ++- Scripts/Utilities/CableCurve.cs | 222 ++++++++++++++++++ Scripts/Utilities/CableCurve.cs.meta | 12 + 4 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 Scripts/Utilities/CableCurve.cs create mode 100644 Scripts/Utilities/CableCurve.cs.meta diff --git a/Examples/MenuExample/Prefabs/Menus/MainMenu.prefab b/Examples/MenuExample/Prefabs/Menus/MainMenu.prefab index 250c76f..3123103 100644 --- a/Examples/MenuExample/Prefabs/Menus/MainMenu.prefab +++ b/Examples/MenuExample/Prefabs/Menus/MainMenu.prefab @@ -133,7 +133,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!114 &114012036402784198 MonoBehaviour: m_ObjectHideFlags: 1 diff --git a/Scripts/Primitives/UILineRenderer.cs b/Scripts/Primitives/UILineRenderer.cs index d933c53..b49353c 100644 --- a/Scripts/Primitives/UILineRenderer.cs +++ b/Scripts/Primitives/UILineRenderer.cs @@ -30,7 +30,8 @@ namespace UnityEngine.UI.Extensions Quick, Basic, Improved, - } + Catenary, + } private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad; @@ -42,18 +43,18 @@ namespace UnityEngine.UI.Extensions 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; private static Vector2[] startUvs, middleUvs, endUvs, fullUvs; - [SerializeField] + [SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")] internal Vector2[] m_points; - [SerializeField] + [SerializeField, Tooltip("Thickness of the line")] internal float lineThickness = 2; - [SerializeField] + [SerializeField, Tooltip("Use the relative bounds of the Rect Transform (0,0 -> 0,1) or screen space coordinates")] internal bool relativeSize; - [SerializeField] + [SerializeField, Tooltip("Do the points identify a single line or split pairs of lines")] internal bool lineList; - [SerializeField] + [SerializeField, Tooltip("Add end caps to each line\nMultiple caps when used with Line List")] internal bool lineCaps; - [SerializeField] + [SerializeField, Tooltip("Resolution of the Bezier curve, different to line Resolution")] internal int bezierSegmentsPerCurve = 10; public float LineThickness @@ -80,9 +81,11 @@ namespace UnityEngine.UI.Extensions set { lineCaps = value; SetAllDirty(); } } + [Tooltip("The type of Join used between lines, Square/Mitre or Curved/Bevel")] public JoinType LineJoins = JoinType.Bevel; - public BezierType BezierMode = BezierType.None; + [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")] + public BezierType BezierMode = BezierType.None; public int BezierSegmentsPerCurve { @@ -120,7 +123,7 @@ namespace UnityEngine.UI.Extensions GeneratedUVs(); Vector2[] pointsToDraw = m_points; //If Bezier is desired, pick the implementation - if (BezierMode != BezierType.None && m_points.Length > 3) + if (BezierMode != BezierType.None && BezierMode != BezierType.Catenary && m_points.Length > 3) { BezierPath bezierPath = new BezierPath(); @@ -142,6 +145,13 @@ namespace UnityEngine.UI.Extensions pointsToDraw = drawingPoints.ToArray(); } + if (BezierMode == BezierType.Catenary && m_points.Length == 2) + { + CableCurve cable = new CableCurve(pointsToDraw); + cable.slack = Resoloution; + cable.steps = BezierSegmentsPerCurve; + pointsToDraw = cable.Points(); + } if (ImproveResolution != ResolutionMode.None) { diff --git a/Scripts/Utilities/CableCurve.cs b/Scripts/Utilities/CableCurve.cs new file mode 100644 index 0000000..a6440f4 --- /dev/null +++ b/Scripts/Utilities/CableCurve.cs @@ -0,0 +1,222 @@ +/// Credit Farfarer +/// Sourced from - https://gist.github.com/Farfarer/a765cd07920d48a8713a0c1924db6d70 +/// Updated for UI / 2D - SimonDarksideJ + +using System; + +namespace UnityEngine.UI.Extensions +{ + [System.Serializable] + public class CableCurve + { + [SerializeField] + Vector2 m_start; + [SerializeField] + Vector2 m_end; + [SerializeField] + float m_slack; + [SerializeField] + int m_steps; + [SerializeField] + bool m_regen; + + static Vector2[] emptyCurve = new Vector2[] { new Vector2(0.0f, 0.0f), new Vector2(0.0f, 0.0f) }; + [SerializeField] + Vector2[] points; + + public bool regenPoints + { + get { return m_regen; } + set + { + m_regen = value; + } + } + + public Vector2 start + { + get { return m_start; } + set + { + if (value != m_start) + m_regen = true; + m_start = value; + } + } + + public Vector2 end + { + get { return m_end; } + set + { + if (value != m_end) + m_regen = true; + m_end = value; + } + } + public float slack + { + get { return m_slack; } + set + { + if (value != m_slack) + m_regen = true; + m_slack = Mathf.Max(0.0f, value); + } + } + public int steps + { + get { return m_steps; } + set + { + if (value != m_steps) + m_regen = true; + m_steps = Mathf.Max(2, value); + } + } + + public Vector2 midPoint + { + get + { + Vector2 mid = Vector2.zero; + if (m_steps == 2) + { + return (points[0] + points[1]) * 0.5f; + } + else if (m_steps > 2) + { + int m = m_steps / 2; + if ((m_steps % 2) == 0) + { + mid = (points[m] + points[m + 1]) * 0.5f; + } + else + { + mid = points[m]; + } + } + return mid; + } + } + + public CableCurve() + { + points = emptyCurve; + m_start = Vector2.up; + m_end = Vector2.up + Vector2.right; + m_slack = 0.5f; + m_steps = 20; + m_regen = true; + } + + public CableCurve(Vector2[] inputPoints) + { + points = inputPoints; + m_start = inputPoints[0]; + m_end = inputPoints[1]; + m_slack = 0.5f; + m_steps = 20; + m_regen = true; + } + + public CableCurve(CableCurve v) + { + points = v.Points(); + m_start = v.start; + m_end = v.end; + m_slack = v.slack; + m_steps = v.steps; + m_regen = v.regenPoints; + } + + public Vector2[] Points() + { + if (!m_regen) + return points; + + if (m_steps < 2) + return emptyCurve; + + float lineDist = Vector2.Distance(m_end, m_start); + float lineDistH = Vector2.Distance(new Vector2(m_end.x, m_start.y), m_start); + float l = lineDist + Mathf.Max(0.0001f, m_slack); + float r = 0.0f; + float s = m_start.y; + float u = lineDistH; + float v = end.y; + + if ((u - r) == 0.0f) + return emptyCurve; + + float ztarget = Mathf.Sqrt(Mathf.Pow(l, 2.0f) - Mathf.Pow(v - s, 2.0f)) / (u - r); + + int loops = 30; + int iterationCount = 0; + int maxIterations = loops * 10; // For safety. + bool found = false; + + float z = 0.0f; + float ztest = 0.0f; + float zstep = 100.0f; + float ztesttarget = 0.0f; + for (int i = 0; i < loops; i++) + { + for (int j = 0; j < 10; j++) + { + iterationCount++; + ztest = z + zstep; + ztesttarget = (float)Math.Sinh(ztest) / ztest; + + if (float.IsInfinity(ztesttarget)) + continue; + + if (ztesttarget == ztarget) + { + found = true; + z = ztest; + break; + } + else if (ztesttarget > ztarget) + { + break; + } + else + { + z = ztest; + } + + if (iterationCount > maxIterations) + { + found = true; + break; + } + } + + if (found) + break; + + zstep *= 0.1f; + } + + float a = (u - r) / 2.0f / z; + float p = (r + u - a * Mathf.Log((l + v - s) / (l - v + s))) / 2.0f; + float q = (v + s - l * (float)Math.Cosh(z) / (float)Math.Sinh(z)) / 2.0f; + + points = new Vector2[m_steps]; + float stepsf = m_steps - 1; + float stepf; + for (int i = 0; i < m_steps; i++) + { + stepf = i / stepsf; + Vector2 pos = Vector2.zero; + pos.x = Mathf.Lerp(start.x, end.x, stepf); + pos.y = a * (float)Math.Cosh(((stepf * lineDistH) - p) / a) + q; + points[i] = pos; + } + + m_regen = false; + return points; + } + } +} \ No newline at end of file diff --git a/Scripts/Utilities/CableCurve.cs.meta b/Scripts/Utilities/CableCurve.cs.meta new file mode 100644 index 0000000..32679b1 --- /dev/null +++ b/Scripts/Utilities/CableCurve.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2444f085ab49ff24b888b175f46b55c7 +timeCreated: 1501871616 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: