using System.Collections.Generic; using UnityEngine; namespace ARLocation { public enum SplineType { CatmullromSpline, LinearSpline, } public abstract class Spline { /// /// The points interpolated of the spline. /// public Vector3[] Points { get; protected set; } /// /// The CatmullRom curve-segments of the spline. /// protected Curve[] segments; /// /// The number of segments that make up the spline. /// protected int segmentCount = 0; /// /// The full (estimated) length of the spline. /// public float Length { get; protected set; } protected float[] lengths; /// /// Calculate the catmull-rom segments. Also estimates the curve's length. /// /// The number sample points used to estimate each segment's length. public abstract void CalculateSegments(int n); /// /// Returns the point of the spline at a given arc-length. /// /// The arc-length. /// public Vector3 GetPointAtArcLength(float s) { s = Mathf.Clamp(s, 0, Length); for (var i = 0; i < segmentCount; i++) { if (s <= lengths[i]) { var offset = i == 0 ? 0 : lengths[i - 1]; return segments[i].GetPointAtLength(s - offset); } } return segments[segmentCount - 1].GetPoint(1); } /// /// Returns a CurvePointData whith the point and tangent of the spline /// at a given arc-length. /// /// The arc-length. /// public CurvePointData GetPointAndTangentAtArcLength(float s) { s = Mathf.Clamp(s, 0, Length); for (var i = 0; i < segmentCount; i++) { if (s <= lengths[i]) { var offset = i == 0 ? 0 : lengths[i - 1]; return segments[i].GetPointAndTangentAtLength(s - offset); } } return segments[segmentCount - 1].GetPointAndTangentAtLength(1); } /// /// Draws the curve using a given LineRenderer, with points being processed by a given /// function beforehand. /// /// /// /// public void DrawCurveWithLineRenderer(LineRenderer renderer, System.Func func, int n = 100) { var points = new List(); float s = 0.0f; while (s <= Length) { var pointData = GetPointAndTangentAtArcLength(s); points.Add(func(pointData.point)); s += Length / (n + 1.0f); } var arr = points.ToArray(); renderer.positionCount = arr.Length; renderer.SetPositions(arr); } /// /// Calculates a sample of (N+2) equidistant points along the spline. /// /// The number of points in the sample will be (N+2). /// A function that can be used to transform the sampled poins. /// public Vector3[] SamplePoints(int n, System.Func func) { var sample = new Vector3[n + 2]; var delta = Length / (n + 1.0f); var s = 0.0f; for (var i = 0; i < (n + 2); i++) { sample[i] = func(GetPointAtArcLength(s)); s += delta; } return sample; } /// /// Calculates a sample of (N+2) equidistant points along the spline. /// /// The number of points in the sample will be (N+2). /// public Vector3[] SamplePoints(int n) { return SamplePoints(n, (p) => p); } /// /// Draw the curve and sample point using Gizmos. /// public void DrawGizmos() { DrawPointsGizmos(); DrawCurveLengthGizmos(); } private void DrawPointsGizmos() { foreach (var p in Points) { Gizmos.color = Color.blue; Gizmos.DrawSphere(p, 0.1f); } } private void DrawCurveLengthGizmos() { var p = GetPointAtArcLength(0f); float s = 0.0f; while (s <= Length) { Gizmos.color = Color.green; var pointData = GetPointAndTangentAtArcLength(s); Vector3 n = pointData.point; Gizmos.DrawLine(p, n); p = n; s += 0.1f; Gizmos.color = Color.magenta; var tan = pointData.tangent; Gizmos.color = Color.blue; Gizmos.DrawLine(n, n + tan); } } } }