public static IEnumerable <Vector3> UniformPositions(Spline spline, int n, int count) /// Iterates in a given number of positions /// Includes original point, but not includes final (n+1) point { if (n >= spline.nodes.Length - 1) { throw new Exception("Beizer positions should be calculated for n-1"); } Vector3 pos0 = spline.nodes[n]; Vector3 dir0 = spline.GetTangent(n); Vector3 pos1 = spline.nodes[n + 1]; Vector3 dir1 = spline.GetTangent(n + 1, true); float[] lengths = new float[64]; float[] lengthToPercentLut = new float[8]; LengthToPercentLut(pos0, dir0, pos1, dir1, lengths, lengthToPercentLut); for (int i = 0; i < count; i++) { float l = (float)i / count; float p = NormLengthToPercent(pos0, dir0, pos1, dir1, l, lengthToPercentLut); yield return(BeizerPosition(pos0, dir0, pos1, dir1, p)); } }
public static void Subdivide(Spline spline, int num) /// Splits each segment in several shorter segments { Vector3[] newNodes = new Vector3[(spline.nodes.Length - 1) * num + 1]; for (int n = 0; n < spline.nodes.Length - 1; n++) { 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 i = 0; i < num; i++) { float percent = (float)i / num; Vector3 split = BeizerPosition(pos0, dir0, pos1, dir1, percent); newNodes[n * num + i] = split; //float percent = 1f / (num-i); //(Segment,Segment) split = currSegment.GetSplitted(percent); //currSegment = split.Item2; } } newNodes[newNodes.Length - 1] = spline.nodes[spline.nodes.Length - 1]; spline.nodes = newNodes; }
public static IEnumerable <Vector3> BeizerPositions(this Spline spline, int n, int count) /// Iterates in a given number of positions /// Includes original point, but not includes final (n+1) point { if (n >= spline.nodes.Length - 1) { throw new Exception("Beizer positions should be calculated for n-1"); } Vector3 n0dir = spline.GetTangent(n); Vector3 n1dir = spline.GetTangent(n + 1, true); yield return(spline.nodes[n]); for (int i = 1; i < count; i++) { float p = (float)i / count; float ip = 1f - p; yield return (ip * ip * ip * spline.nodes[n] + 3 * p * ip * ip * (spline.nodes[n] + n0dir) + 3 * p * p * ip * (spline.nodes[n + 1] + n1dir) + p * p * p * spline.nodes[n + 1]); } }
public static void Optimize(Spline spline, float deviation) /// Removes those nodes that should not change the shape a lot /// Similar to Spline.Optimize, but takes tangents into account { int iterations = spline.nodes.Length - 2; //in worst case should remove all but start/end if (iterations <= 0) { return; } for (int i = 0; i < iterations; i++) //using recorded iterations since nodes count will change //for (int i=0; i<deviation; i++) { float minDeviation = float.MaxValue; int minN = -1; //Vector3 posM1 = spline.nodes[0]; //Vector3 dirM1 = spline.GetTangent(0); for (int n = 1; n < spline.nodes.Length - 1; n++) { Vector3 posM1 = spline.nodes[n - 1]; Vector3 dirM1 = spline.GetTangent(n - 1); Vector3 pos0 = spline.nodes[n]; Vector3 dir0 = spline.GetTangent(n); Vector3 pos1 = spline.nodes[n + 1]; Vector3 dir1 = spline.GetTangent(n + 1, true); //checking how far point placed from tangent-tangent line float currDistToLine = DistanceToLine( posM1 + dirM1, pos1 + dir1, pos0); //float currLine = ((nodes[n-1].pos+nodes[n-1].outDir) - (nodes[n+1].pos+nodes[n+1].inDir)).magnitude; //float currDeviation = (currDistToLine*currDistToLine) / currLine; float currDeviation = currDistToLine; if (currDeviation < minDeviation) { minN = n; minDeviation = currDeviation; } //posM1 = pos0; dirM1 = dir0; } if (minDeviation > deviation) { break; } ArrayTools.RemoveAt(ref spline.nodes, minN); } }
public static Vector3 BeizerPosition(this Spline spline, int n, float p) /// Gets position at n segment and p percent /// Large overhead of getting tangents { Vector3 n0dir = spline.GetTangent(n); Vector3 n1dir = spline.GetTangent(n + 1, true); float ip = 1f - p; return (ip * ip * ip * spline.nodes[n] + 3 * p * ip * ip * (spline.nodes[n] + n0dir) + 3 * p * p * ip * (spline.nodes[n + 1] + n1dir) + p * p * p * spline.nodes[n + 1]); }
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);