/// <summary> /// Creates a list of equally spaced points that lie on the path described by straight line segments between /// adjacent points in the source list. /// </summary> /// <param name="src">Source list of points.</param> /// <param name="md">Distance between points on the new path.</param> /// <returns>List of equally-spaced points on the path.</returns> public static List <VECTOR> Linearize(List <VECTOR> src, FLOAT md) { if (src == null) { throw new ArgumentNullException("src"); } if (md <= VectorHelper.EPSILON) { throw new InvalidOperationException("md " + md + " is be less than epislon " + EPSILON); } List <VECTOR> dst = new List <VECTOR>(); if (src.Count > 0) { VECTOR pp = src[0]; dst.Add(pp); FLOAT cd = 0; for (int ip = 1; ip < src.Count; ip++) { VECTOR p0 = src[ip - 1]; VECTOR p1 = src[ip]; FLOAT td = VectorHelper.Distance(p0, p1); if (cd + td > md) { FLOAT pd = md - cd; dst.Add(VectorHelper.Lerp(p0, p1, pd / td)); FLOAT rd = td - pd; while (rd > md) { rd -= md; VECTOR np = VectorHelper.Lerp(p0, p1, (td - rd) / td); if (!VectorHelper.EqualsOrClose(np, pp)) { dst.Add(np); pp = np; } } cd = rd; } else { cd += td; } } // last point VECTOR lp = src[src.Count - 1]; if (!VectorHelper.EqualsOrClose(pp, lp)) { dst.Add(lp); } } return(dst); }
/// <summary> /// Adds a curve to the end of the spline. /// </summary> public void Add(CubicBezier curve) { if (_curves.Count > 0 && !VectorHelper.EqualsOrClose(_curves[_curves.Count - 1].p3, curve.p0)) { throw new InvalidOperationException("The new curve does at index " + _curves.Count + " does not connect with the previous curve at index " + (_curves.Count - 1)); } _curves.Add(curve); for (int i = 0; i < _samplesPerCurve; i++) // expand the array since updateArcLengths expects these values to be there { _arclen.Add(0); } UpdateArcLengths(_curves.Count - 1); }
/// <summary> /// Removes any repeated points (that is, one point extremely close to the previous one). The same point can /// appear multiple times just not right after one another. This does not modify the input list. If no repeats /// were found, it returns the input list; otherwise it creates a new list with the repeats removed. /// </summary> /// <param name="pts">Initial list of points.</param> /// <returns>Either pts (if no duplicates were found), or a new list containing pts with duplicates removed.</returns> public static List <VECTOR> RemoveDuplicates(List <VECTOR> pts) { if (pts.Count < 2) { return(pts); } // Common case -- no duplicates, so just return the source list VECTOR prev = pts[0]; int len = pts.Count; int nDup = 0; for (int i = 1; i < len; i++) { VECTOR cur = pts[i]; if (VectorHelper.EqualsOrClose(prev, cur)) { nDup++; } else { prev = cur; } } if (nDup == 0) { return(pts); } else { // Create a copy without them List <VECTOR> dst = new List <VECTOR>(len - nDup); prev = pts[0]; dst.Add(prev); for (int i = 1; i < len; i++) { VECTOR cur = pts[i]; if (!VectorHelper.EqualsOrClose(prev, cur)) { dst.Add(cur); prev = cur; } } return(dst); } }
/// <summary> /// Modifies a curve in the spline. It must connect with the previous and next curves (if applicable). This requires that the /// arc length table be recalculated for that curve and all curves after it -- for example, if you update the first curve in the /// spline, each curve after that would need to be recalculated (could avoid this by caching the lengths on a per-curve basis if you're /// doing this often, but since the typical case is only updating the last curve, and the entire array needs to be visited anyway, it /// wouldn't save much). /// </summary> /// <param name="index">Index of the curve to update in <see cref="Curves"/>.</param> /// <param name="curve">The new curve with which to replace it.</param> public void Update(int index, CubicBezier curve) { if (index < 0) { throw new IndexOutOfRangeException("Negative index"); } if (index >= _curves.Count) { throw new IndexOutOfRangeException("Curve index " + index + " is out of range (there are " + _curves.Count + " curves in the spline)"); } if (index > 0 && !VectorHelper.EqualsOrClose(_curves[index - 1].p3, curve.p0)) { throw new InvalidOperationException("The updated curve at index " + index + " does not connect with the previous curve at index " + (index - 1)); } if (index < _curves.Count - 1 && !VectorHelper.EqualsOrClose(_curves[index + 1].p0, curve.p3)) { throw new InvalidOperationException("The updated curve at index " + index + " does not connect with the next curve at index " + (index + 1)); } _curves[index] = curve; for (int i = index; i < _curves.Count; i++) { UpdateArcLengths(i); } }