{"id":585,"date":"2016-11-14T18:00:37","date_gmt":"2016-11-14T17:00:37","guid":{"rendered":"http:\/\/piflik.de\/?p=585"},"modified":"2016-11-09T21:27:49","modified_gmt":"2016-11-09T20:27:49","slug":"utilities-spline","status":"publish","type":"post","link":"http:\/\/piflik.de\/?p=585","title":{"rendered":"Utilities: Spline"},"content":{"rendered":"<p>To make use of the <a title=\"Utilities: Bezier\" href=\"http:\/\/piflik.de\/?p=572\">Bezier class from the last post<\/a>, here is a Spline class. It is not the most grabage friendly class, so I might revisit it at some time, but it does what I wanted it to do when I wrote it. As I said it uses the Bezier class for smooth curves, but there is much automatisation, so no user-input is needed\/possible to define the typical Bezier handles. This has the drawback that there is a risk of overshooting, if the distance between spline-vertices varies too much. Splines can be smooth or segmented, closed or open, and are visualized via a LineRenderer.<\/p>\n<p>As usual, the most current code is found on my <a title=\"Utilities\" href=\"https:\/\/bitbucket.org\/Piflik\/utilities\/src\" target=\"_blank\">Bitbucket repository<\/a>.<\/p>\n<p><!--more--><\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&lt;pre&gt;using System.Collections.Generic;\r\nusing System.Linq;\r\nusing UnityEngine;\r\n\r\nnamespace Utilities {\r\n\tpublic class Spline {\r\n\r\n\t\tpublic Vector3[] vertices { get; private set; }\r\n\r\n\t\tprivate bool _closed;\r\n\t\tprivate bool _smooth;\r\n\t\tprivate float _thickness;\r\n\r\n\t\tprivate bool _isInitialized;\r\n\r\n\t\tprivate GameObject _visualizationObject;\r\n\t\tprivate LineRenderer _visualizationRenderer;\r\n\r\n\t\tpublic struct Segment {\r\n\t\t\tpublic Vector3 start;\r\n\t\t\tpublic Vector3 end;\r\n\r\n\t\t\tpublic Vector3 A, B;\r\n\r\n\t\t\tpublic float length;\r\n\t\t\tpublic float startLength;\r\n\t\t};\r\n\r\n\t\tprivate Segment[] _segments;\r\n\r\n\t\tpublic float length { get; private set; }\r\n\r\n\t\tpublic bool visible {\r\n\t\t\tget { return _visualizationRenderer.enabled; }\r\n\t\t\tset { _visualizationRenderer.enabled = value; }\r\n\t\t}\r\n\r\n\t\tprivate Spline() {}\r\n\r\n\t\tpublic static Spline Create(Vector3[] points, Material material, float thickness = 0.1f, bool smooth = true, bool closed = true) {\r\n\r\n\t\t\tif (points.Length &lt; 2) return null;\r\n\r\n\t\t\tSpline spline = new Spline {\r\n\t\t\t\tvertices = points,\r\n\t\t\t\t_smooth = smooth,\r\n\t\t\t\t_closed = closed,\r\n\t\t\t\t_thickness = thickness,\r\n\t\t\t\t_visualizationObject = new GameObject(&quot;Spline Vertex&quot;) {layer = GeneralUtilities.GIZMOS_LAYER}\r\n\t\t\t};\r\n\r\n\t\t\tspline._visualizationRenderer = spline._visualizationObject.AddComponent&lt;LineRenderer&gt;();\r\n\t\t\tspline._visualizationRenderer.material = material;\r\n\r\n\t\t\tspline.Setup();\r\n\r\n\t\t\treturn spline;\r\n\t\t}\r\n\r\n\t\tpublic void Update(Vector3[] points) {\r\n\t\t\tvertices = points;\r\n\t\t\tSetup();\r\n\t\t}\r\n\r\n\t\tvoid Setup() {\r\n\t\t\tif (vertices.Length &lt; 2) return;\r\n\r\n\t\t\t_segments = new Segment[_closed ? vertices.Length : vertices.Length - 1];\r\n\t\t\tlength = 0;\r\n\r\n\t\t\tif (!_smooth || vertices.Length == 2) {\r\n\t\t\t\tfor (int i = 0; i &lt; vertices.Length - 1; ++i) {\r\n\r\n\t\t\t\t\tfloat l = (vertices[i + 1] - vertices[i]).magnitude;\r\n\r\n\t\t\t\t\t_segments[i] = new Segment {\r\n\t\t\t\t\t\tstart = vertices[i],\r\n\t\t\t\t\t\tend = vertices[i + 1],\r\n\t\t\t\t\t\tlength = l,\r\n\t\t\t\t\t\tstartLength = length\r\n\t\t\t\t\t};\r\n\r\n\t\t\t\t\tlength += l;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_closed) {\r\n\t\t\t\t\tfloat l = (vertices[0] - vertices[vertices.Length - 1]).magnitude;\r\n\r\n\t\t\t\t\t_segments[_segments.Length - 1] = new Segment {\r\n\t\t\t\t\t\tstart = vertices[vertices.Length - 1],\r\n\t\t\t\t\t\tend = vertices[0],\r\n\t\t\t\t\t\tlength = l,\r\n\t\t\t\t\t\tstartLength = length\r\n\t\t\t\t\t};\r\n\r\n\t\t\t\t\tlength += l;\r\n\t\t\t\t}\r\n\r\n\t\t\t} else if (_closed) {\r\n\t\t\t\tfor (int i = 0; i &lt; vertices.Length; ++i) {\r\n\t\t\t\t\tVector3 start = vertices[i];\r\n\t\t\t\t\tVector3 end = vertices[(i + 1) % vertices.Length];\r\n\t\t\t\t\tVector3 controlPointA = start + (end - vertices[(i - 1 + vertices.Length) % vertices.Length]) \/ 4;\r\n\t\t\t\t\tVector3 controlPointB = end + (start - vertices[(i + 2) % vertices.Length]) \/ 4;\r\n\r\n\t\t\t\t\tfloat l = Bezier.Length(start, end, controlPointA, controlPointB, 10);\r\n\r\n\t\t\t\t\t_segments[i] = new Segment {\r\n\t\t\t\t\t\tstart = start,\r\n\t\t\t\t\t\tend = end,\r\n\t\t\t\t\t\tlength = l,\r\n\t\t\t\t\t\tstartLength = length,\r\n\t\t\t\t\t\tA = controlPointA,\r\n\t\t\t\t\t\tB = controlPointB\r\n\t\t\t\t\t};\r\n\r\n\t\t\t\t\tlength += l;\r\n\t\t\t\t}\r\n\r\n\t\t\t} else {\r\n\t\t\t\tVector3 start = vertices[0];\r\n\t\t\t\tVector3 end = vertices[1];\r\n\t\t\t\tVector3 controlPointB = end + (start - vertices[2]) \/ 4;\r\n\t\t\t\tVector3 controlPointA = (controlPointB + 2 * start) \/ 3;\r\n\r\n\t\t\t\tfloat l = Bezier.Length(start, end, controlPointA, controlPointB, 10);\r\n\r\n\t\t\t\t_segments[0] = new Segment {\r\n\t\t\t\t\tstart = start,\r\n\t\t\t\t\tend = end,\r\n\t\t\t\t\tlength = l,\r\n\t\t\t\t\tstartLength = 0,\r\n\t\t\t\t\tA = controlPointA,\r\n\t\t\t\t\tB = controlPointB\r\n\t\t\t\t};\r\n\r\n\t\t\t\tlength += l;\r\n\r\n\t\t\t\tfor (int i = 1; i &lt; vertices.Length - 2; ++i) {\r\n\t\t\t\t\tstart = vertices[i];\r\n\t\t\t\t\tend = vertices[i + 1];\r\n\t\t\t\t\tcontrolPointA = start + (end - vertices[i - 1]) \/ 4;\r\n\t\t\t\t\tcontrolPointB = end + (start - vertices[i + 2]) \/ 4;\r\n\r\n\t\t\t\t\tl = Bezier.Length(start, end, controlPointA, controlPointB, 10);\r\n\r\n\t\t\t\t\t_segments[i] = new Segment {\r\n\t\t\t\t\t\tstart = start,\r\n\t\t\t\t\t\tend = end,\r\n\t\t\t\t\t\tlength = l,\r\n\t\t\t\t\t\tstartLength = length,\r\n\t\t\t\t\t\tA = controlPointA,\r\n\t\t\t\t\t\tB = controlPointB\r\n\t\t\t\t\t};\r\n\r\n\t\t\t\t\tlength += l;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tstart = vertices[vertices.Length - 2];\r\n\t\t\t\tend = vertices[vertices.Length - 1];\r\n\t\t\t\tcontrolPointA = start + (end - vertices[vertices.Length - 3]) \/ 4;\r\n\t\t\t\tcontrolPointB = (controlPointA + 2 * end) \/ 3;\r\n\r\n\t\t\t\tl = Bezier.Length(start, end, controlPointA, controlPointB, 10);\r\n\r\n\t\t\t\t_segments[vertices.Length - 2] = new Segment {\r\n\t\t\t\t\tstart = start,\r\n\t\t\t\t\tend = end,\r\n\t\t\t\t\tlength = l,\r\n\t\t\t\t\tstartLength = length,\r\n\t\t\t\t\tA = controlPointA,\r\n\t\t\t\t\tB = controlPointB\r\n\t\t\t\t};\r\n\t\t\t\tlength += l;\r\n\t\t\t}\r\n\r\n\t\t\t_isInitialized = true;\r\n\t\t}\r\n\r\n\t\tpublic void SetMaterial(Material material) {\r\n\t\t\t_visualizationRenderer.material = material;\r\n\t\t}\r\n\r\n\t\tpublic void SetColor(Color color) {\r\n\t\t\t_visualizationRenderer.material.color = color;\r\n\t\t}\r\n\r\n\t\tpublic void Draw() {\r\n\t\t\tif (vertices.Length &lt; 2) {\r\n\t\t\t\t_visualizationRenderer.SetVertexCount(0);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isInitialized) Setup();\r\n\r\n\t\t\tList&lt;Vector3&gt; segmentPoints;\r\n\r\n\t\t\tif (!_smooth || vertices.Length == 2) {\r\n\t\t\t\tsegmentPoints = _segments.Select(s =&gt; s.start).ToList();\r\n\t\t\t} else {\r\n\t\t\t\tsegmentPoints = _segments.SelectMany(s =&gt; GetCurvePoints(s)).ToList();\r\n\t\t\t}\r\n\r\n\t\t\tsegmentPoints.Add(_segments[_segments.Length - 1].end);\r\n\r\n\t\t\t_visualizationRenderer.SetVertexCount(segmentPoints.Count);\r\n\t\t\t_visualizationRenderer.SetPositions(segmentPoints.ToArray());\r\n\r\n\t\t\t_visualizationRenderer.SetWidth(_thickness, _thickness);\r\n\t\t\tvisible = true;\r\n\t\t}\r\n\r\n\t\tpublic void Hide() {\r\n\t\t\tvisible = false;\r\n\t\t}\r\n\r\n\t\tpublic void Clear() {\r\n\t\t\tvertices = null;\r\n\t\t\tHide();\r\n\t\t}\r\n\r\n\t\tprivate static Vector3[] GetCurvePoints(Segment segment, int segments = 10) {\r\n\t\t\tVector3[] positions = new Vector3[segments];\r\n\r\n\t\t\tfor (int i = 0; i &lt; segments; ++i) {\r\n\t\t\t\tpositions[i] = Bezier.Interpolate(segment.start, segment.end, segment.A, segment.B, (float) i \/ segments);\r\n\t\t\t}\r\n\r\n\t\t\treturn positions;\r\n\t\t}\r\n\r\n\t\tpublic Vector3 GetPoint(float alongPath) {\r\n\t\t\tif (!_isInitialized) {\r\n\t\t\t\tSetup();\r\n\t\t\t}\r\n\r\n\t\t\tif (alongPath &gt;= 1) {\r\n\t\t\t\treturn vertices[vertices.Length - 1];\r\n\t\t\t}\r\n\r\n\t\t\tif (alongPath &lt;= 0) {\r\n\t\t\t\treturn vertices[0];\r\n\t\t\t}\r\n\r\n\t\t\tif (vertices.Length == 2) {\r\n\t\t\t\treturn Vector3.Lerp(vertices[0], vertices[1], alongPath);\r\n\t\t\t}\r\n\r\n\t\t\tfloat alongLength = alongPath * length;\r\n\r\n\t\t\tint segIndex = 1;\r\n\r\n\t\t\twhile (segIndex &lt; _segments.Length &amp;&amp; _segments[segIndex].startLength &lt; alongLength) {\r\n\t\t\t\tsegIndex++;\r\n\t\t\t}\r\n\r\n\t\t\tSegment seg = _segments[segIndex - 1];\r\n\t\t\tfloat localPercentage = (alongLength - seg.startLength) \/ (seg.length);\r\n\r\n\t\t\treturn !_smooth ? Vector3.Lerp(seg.start, seg.end, localPercentage) : Bezier.Interpolate(seg.start, seg.end, seg.A, seg.B, localPercentage);\r\n\t\t}\r\n\t}\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>To make use of the Bezier class from the last post, here is a Spline class. It is not the most grabage friendly class, so I might revisit it at some time, but it does what I wanted it to do when I wrote it. As I said it uses the Bezier class for smooth [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[13,12],"tags":[],"_links":{"self":[{"href":"http:\/\/piflik.de\/index.php?rest_route=\/wp\/v2\/posts\/585"}],"collection":[{"href":"http:\/\/piflik.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/piflik.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/piflik.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/piflik.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=585"}],"version-history":[{"count":2,"href":"http:\/\/piflik.de\/index.php?rest_route=\/wp\/v2\/posts\/585\/revisions"}],"predecessor-version":[{"id":587,"href":"http:\/\/piflik.de\/index.php?rest_route=\/wp\/v2\/posts\/585\/revisions\/587"}],"wp:attachment":[{"href":"http:\/\/piflik.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=585"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/piflik.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=585"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/piflik.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=585"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}