public Spline(Spline src) { nodes = ArrayTools.Copy(src.nodes); count = src.count; }
/*UI ui = new UI(); * SplineObject so; * * public void OnSceneGUI () * { * if (so == null) so = (SplineObject)target; * ui.DrawInspector(() => DrawSpline(so.splineSys)); * }*/ public static void DrawSpline(SplineObject obj) { using (Cell.LineStd) using (new Draw.FoldoutGroup(ref guiDisplay, "Display", isLeft: true)) if (guiDisplay) { using (Cell.LineStd) Draw.ToggleLeft(ref obj.drawNodes, "Draw Nodes"); using (Cell.LineStd) Draw.ToggleLeft(ref obj.drawLines, "Draw Segments"); using (Cell.LineStd) Draw.ToggleLeft(ref obj.drawBeizer, "Draw Beizer"); if (Cell.current.valChanged) { SceneView.lastActiveSceneView?.Repaint(); } } Cell.EmptyLinePx(4); using (Cell.LineStd) using (new Draw.FoldoutGroup(ref guiLines, "Splines", isLeft: true)) if (guiLines) { using (Cell.LineStd) Draw.DualLabel("Lines Count", obj.splines.Length.ToString()); if (SplineEditor.selectedNodes.Count == 1) { (int l, int n) = SplineEditor.selectedNodes.Any(); Spline spline = obj.splines[l]; //using (Cell.LineStd) Draw.DualLabel("Length", spline.length.ToString()); using (Cell.LineStd) Draw.DualLabel("Node Counts", spline.nodes.Length.ToString()); //using (Cell.LineStd) Draw.Toggle(ref spline.looped, "Looped"); using (Cell.LineStd) if (Draw.Button("Remove")) { ArrayTools.RemoveAt(ref obj.splines, l); SplineEditor.selectedNodes.Clear(); SplineEditor.dispSelectedNodes.Clear(); } } using (Cell.LineStd) if (Draw.Button("Add")) { Spline newSpline = new Spline( SceneView.lastActiveSceneView.pivot - new Vector3(SceneView.lastActiveSceneView.cameraDistance / 10, 0, 0), SceneView.lastActiveSceneView.pivot + new Vector3(SceneView.lastActiveSceneView.cameraDistance / 10, 0, 0)); newSpline.nodes[0].y = 0; newSpline.nodes[1].y = 0; ArrayTools.Add(ref obj.splines, newSpline); } } Cell.EmptyLinePx(4); using (Cell.LineStd) using (new Draw.FoldoutGroup(ref guiKnobs, "Nodes", isLeft: true)) if (guiKnobs) { using (Cell.LineStd) Draw.DualLabel("Selected", SplineEditor.selectedNodes.Count.ToString()); if (SplineEditor.selectedNodes.Count == 1) { Cell.EmptyLinePx(4); (int l, int n) = SplineEditor.selectedNodes.Any(); using (Cell.LinePx(0)) DrawNode(obj.splines, l, n); } } /*Cell.EmptyLinePx(4); * using (Cell.LineStd) * if (Draw.Button("Update")) * { * sys.Update(); * SceneView.lastActiveSceneView?.Repaint(); * }*/ }
public static Vector3[] GetAllPoints(Spline spline, float resPerUnit = 0.1f, int minRes = 3, int maxRes = 20) /// Converts line into array of points to draw polyline /// Requires length updated /// Maybe rename to version of Subdivide? { //calculating lengths float[] lengths = new float[spline.nodes.Length - 1]; for (int n = 0; n < lengths.Length; n++) { lengths[n] = ApproxLengthBeizer(spline, n); } //calculating number of points int numPoints = 0; for (int n = 0; n < lengths.Length; n++) { int modRes = (int)(lengths[n] * resPerUnit); if (modRes < minRes) { modRes = minRes; } if (modRes > maxRes) { modRes = maxRes; } numPoints += modRes; } Vector3[] points = new Vector3[numPoints + 1]; int i = 0; for (int n = 0; n < lengths.Length; n++) { int modRes = (int)(lengths[n] * resPerUnit); if (modRes < minRes) { modRes = minRes; } if (modRes > maxRes) { modRes = maxRes; } Vector3 pos0 = spline.nodes[n]; Vector3 dir0 = spline.GetTangent(n); Vector3 pos1 = spline.nodes[n + 1]; Vector3 dir1 = spline.GetTangent(n + 1, true); for (int p = 0; p < modRes; p++) { float percent = 1f * p / modRes; points[i] = BeizerPosition(pos0, dir0, pos1, dir1, percent); i++; } } //the last one points[points.Length - 1] = spline.nodes[spline.nodes.Length - 1]; return(points); }
/* * public float IntegralLength (float t=1, int iterations=8) * /// A variation of Legendre-Gauss solution from Primer (https://pomax.github.io/bezierinfo/#arclength) * { * float z = t / 2; * float sum = 0; * * double[] abscissaeLutArr = abscissaeLut[iterations]; * double[] weightsLutArr = weightsLut[iterations]; * * float correctedT; * for (int i=0; i<iterations; i++) * { * correctedT = z * (float)abscissaeLutArr[i] + z; * * Vector3 derivative = GetDerivative(correctedT); * float b = Mathf.Sqrt(derivative.x*derivative.x + derivative.z*derivative.z); * sum += (float)weightsLutArr[i] * b; * } * * return z * sum; * } * * * public void FillIntegralLengthLut (float fullLength=-1, int iterations=32) * /// Fills the distance for each length (i.e. if lengthPercents is 4 then finds dist for 0.25,0.5,075,1) * { * if (fullLength < 0) fullLength = IntegralLength(); * * float pHalf = LengthToPercent(fullLength*0.5f, iterations:iterations); * float p025 = LengthToPercent(fullLength*0.25f, pFrom:0, pTo:pHalf, iterations:iterations/2); * float p075 = LengthToPercent(fullLength*0.75f, pFrom:pHalf, pTo:1, iterations:iterations/2); * float p0125 = LengthToPercent(fullLength*0.125f, pFrom:0, pTo:p025, iterations:iterations/4); * float p0375 = LengthToPercent(fullLength*0.375f, pFrom:p025, pTo:pHalf, iterations:iterations/4); * float p0625 = LengthToPercent(fullLength*0.625f, pFrom:pHalf, pTo:p075, iterations:iterations/4); * float p0875 = LengthToPercent(fullLength*0.875f, pFrom:p075, pTo:1, iterations:iterations/4); * * lengthToPercentLut.b0 = (byte)(p0125*255 + 0.5f); * lengthToPercentLut.b1 = (byte)(p025*255 + 0.5f); * lengthToPercentLut.b2 = (byte)(p0375*255 + 0.5f); * lengthToPercentLut.b3 = (byte)(pHalf*255 + 0.5f); * lengthToPercentLut.b4 = (byte)(p0625*255 + 0.5f); * lengthToPercentLut.b5 = (byte)(p075*255 + 0.5f); * lengthToPercentLut.b6 = (byte)(p0875*255 + 0.5f); * } * * * public float LengthToPercent (float length, float pFrom=0, float pTo=1, int iterations=8) * /// Converts world length to segments percent without using segment length LUT. Used to fill that LUT. * { * float pMid = (pFrom+pTo)/2; * float lengthMid = IntegralLength(pMid); * * if (length < lengthMid) * { * if (iterations <= 1) return (pFrom+pMid) /2; * else return LengthToPercent(length, pFrom:pFrom, pTo:pMid, iterations:iterations-1); * } * * else * { * if (iterations <= 1) return (pMid+pTo) /2; * else return LengthToPercent(length, pFrom:pMid, pTo:pTo, iterations:iterations-1); * } * } */ #endregion #region Uniform Split // Operations related with the real spline distances (not distorted by beizer) // Requires length to percent lut - it's either approximate or linear LengthToPercentLut public static float NormLengthToPercent(Spline spline, int n, float normLength, float[] lengthToPercentLut) => NormLengthToPercent(spline.nodes[n], spline.GetTangent(n), spline.nodes[n + 1], spline.GetTangent(n + 1, true), normLength, lengthToPercentLut);
public static void LengthToPercentLut(this Spline spline, int n, float[] lengths, float[] lut) => LengthToPercentLut(spline.nodes[n], spline.GetTangent(n), spline.nodes[n + 1], spline.GetTangent(n + 1, true), lengths, lut);
public static float ApproxLengthBeizer(this Spline spline, int n, float pStart = 0, float pEnd = 1, int iterations = 32) => ApproxLengthBeizer(spline.nodes[n], spline.GetTangent(n), spline.nodes[n + 1], spline.GetTangent(n + 1, true), pStart, pEnd, iterations);
/*private struct Segment * { * public Vector3 pos0; * public Vector3 dir0; * public Vector3 pos1; * public Vector3 dir1; * * * public Vector3 BeizerPosition (float p) * /// Gets position at n segment and p percent * /// Large overhead of getting tangents * { * float ip = 1f-p; * return * ip*ip*ip*pos0 + * 3*p*ip*ip*(pos0+dir0) + * 3*p*p*ip*(pos1+dir1) + * p*p*p*pos1; * } * }*/ public static Vector3 GetTangent(this Spline spline, int n, bool prev = false) /// Gets the auto-layouted direction of n node, looking towards next node /// Or prev node if prev enabled { //only two nodes if (spline.nodes.Length == 2) { if (n == 0) { Vector3 dir = (spline.nodes[n + 1] - spline.nodes[n]) * 0.333f; return(!prev ? dir : -dir); } else { Vector3 dir = (spline.nodes[n] - spline.nodes[n - 1]) * 0.333f; return(!prev ? dir : -dir); } } //first else if (n == 0) { Vector3 nextTan = GetTangent(spline, 1, true); Vector3 dir = ((spline.nodes[n + 1] + nextTan) - spline.nodes[n]) * 0.333f; //Vector3 dir = (spline.nodes[n+1] - spline.nodes[n]) * 0.333f; return(!prev ? dir : -dir); } //last else if (n == spline.nodes.Length - 1) { Vector3 prevTan = GetTangent(spline, spline.nodes.Length - 2, false); Vector3 dir = (spline.nodes[n] - (spline.nodes[n - 1] + prevTan)) * 0.333f; //Vector3 dir = (spline.nodes[n-1] - spline.nodes[n]) * 0.333f; return(!prev ? dir : -dir); } //common case else { Vector3 outDir = spline.nodes[n + 1] - spline.nodes[n]; float outDirLength = outDir.magnitude; if (outDirLength > 0.00001f) //prevPos match with pos, usually on first segment { outDir /= outDirLength; } else { outDir = new Vector3(); } Vector3 inDir = spline.nodes[n - 1] - spline.nodes[n]; float inDirLength = inDir.magnitude; if (inDirLength > 0.00001f) { inDir /= inDirLength; } else { inDir = new Vector3(); } Vector3 newInDir = (inDir - outDir).normalized; Vector3 newOutDir = -newInDir; //(outDir - inDir).normalized; inDir = newInDir.normalized * inDirLength * 0.35f; outDir = newOutDir.normalized * outDirLength * 0.35f; return(!prev ? outDir : inDir); } }