/// <summary> /// Constructs a nurbs curve representation of this polyline. /// </summary> /// <returns>A Nurbs curve shaped like this polyline.</returns> private void ToNurbs() { double lengthSum = 0; List <double> weights = new List <double>(); KnotVector knots = new KnotVector { 0, 0 }; List <Point4> ctrlPts = new List <Point4>(); for (int i = 0; i < ControlPointLocations.Count - 1; i++) { lengthSum += Segments[i].Length; knots.Add(lengthSum); weights.Add(1.0); ctrlPts.Add(new Point4(ControlPointLocations[i], 1.0)); } knots.Add(lengthSum); weights.Add(1.0); ctrlPts.Add(new Point4(ControlPointLocations.Last(), 1.0)); Weights = weights; Knots = knots.Normalize(); Degree = 1; ControlPoints = ctrlPts; }
/// <summary> /// Computes the knot vectors used to calculate a curve global interpolation. /// </summary> private static KnotVector ComputeKnotsForInterpolation(List <double> curveParameters, int degree, bool hasTangents) { // Start knot vectors. KnotVector knots = CollectionHelpers.RepeatData(0.0, degree + 1).ToKnot(); // If we have tangent values we need two more control points and knots. int start = (hasTangents) ? 0 : 1; int end = (hasTangents) ? curveParameters.Count - degree + 1 : curveParameters.Count - degree; // Use averaging method (Eqn 9.8) to compute internal knots in the knot vector. for (int i = start; i < end; i++) { double weightSum = 0.0; for (int j = 0; j < degree; j++) { weightSum += curveParameters[i + j]; } knots.Add((1.0 / degree) * weightSum); } // Add end knot vectors. knots.AddRange(CollectionHelpers.RepeatData(1.0, degree + 1)); return(knots); }
/// <summary> /// Computes the knots vectors used to calculate an approximate curve. /// </summary> private static KnotVector ComputeKnotsForCurveApproximation(List <double> curveParameters, int degree, int numberOfCtrPts, int numberOfPts) { // Start knot vectors. KnotVector knots = CollectionHelpers.RepeatData(0.0, degree + 1).ToKnot(); // Compute 'd' value - Eqn 9.68. double d = (double)numberOfPts / (numberOfCtrPts - degree); // Find the internal knots - Eqn 9.69. for (int j = 1; j < numberOfCtrPts - degree; j++) { int i = (int)(j * d); double alpha = (j * d) - i; double knot = ((1.0 - alpha) * curveParameters[i - 1]) + (alpha * curveParameters[i]); knots.Add(knot); } // Add end knot vectors. knots.AddRange(CollectionHelpers.RepeatData(1.0, degree + 1)); return(knots); }
/// <summary> /// Reduce the degree of a NURBS curve. /// </summary> /// <param name="curve">The object curve to elevate.</param> /// <param name="tolerance">Tolerance value declaring if the curve is degree reducible.</param> /// <returns>The curve after degree reduction, the curve will be degree - 1 from the input.</returns> internal static NurbsBase ReduceDegree(NurbsBase curve, double tolerance) { int n = curve.Knots.Count - curve.Degree - 2; int p = curve.Degree; KnotVector U = curve.Knots; List <Point4> Qw = curve.ControlPoints; // local arrays. Point4[] bpts = new Point4[p + 1]; Point4[] nextbpts = new Point4[p - 1]; Point4[] rbpts = new Point4[p]; double[] alphas = new double[p - 1]; // Output values; List <Point4> Pw = new List <Point4>(); KnotVector Uh = new KnotVector(); // Initialize some variables. int ph = p - 1; int mh = ph; int kind = ph + 1; int r = -1; int a = p; int b = p + 1; int m = n + p + 1; Pw.Add(Qw[0]); // Compute left end of knot vector. for (int i = 0; i <= ph; i++) { Uh.Add(U[0]); } // Initialize first Bezier segment. for (int i = 0; i <= p; i++) { bpts[i] = Qw[i]; } // Initialize error vector. Vector e = Vector.Zero1d(m); // Loop through the knot vector. while (b < m) { // First compute knot multiplicity. int i = b; while (b < m && Math.Abs(U[b] - U[b + 1]) < GSharkMath.Epsilon) { b += 1; } int mult = b - i + 1; mh = mh + mult - 1; int oldr = r; r = p - mult; // Insert knot U[b] r times. // Checks for integer arithmetic. int lbz = (oldr > 0) ? (int)Math.Floor((double)((oldr + 2) / 2)) : 1; if (r > 0) { // Inserts knot to get Bezier segment. double numer = U[b] - U[a]; for (int k = p; k > mult; k--) { alphas[k - mult - 1] = numer / (U[a + k] - U[a]); } for (int j = 1; j <= r; j++) { int save = r - j; int s = mult + j; for (int k = p; k >= s; k--) { bpts[k] = (bpts[k] * alphas[k - s]) + (bpts[k - 1] * (1.0 - alphas[k - s])); } nextbpts[save] = bpts[p]; } } // Degree reduce Bezier segment. double maxError = BezierDegreeReduce(bpts, p, out rbpts); e[a] += maxError; if (e[a] > tolerance) { throw new Exception("Curve not degree reducible"); } // Remove knot U[a] oldr times. if (oldr > 0) { // Must remove knot u=U[a] oldr times. int first = kind; int last = kind; for (int k = 0; k < oldr; k++) { // Knot removal loop. int l = first; int j = last; int kj = j - kind; while (j - l > k) { double alfa = (U[a] - Uh[l - 1]) / (U[b] - Uh[l - 1]); double beta = (U[a] - Uh[j - k - 1]) / (U[b] - Uh[j - k - 1]); Pw[l - 1] = (Pw[l - 1] - Pw[l - 2] * (1.0 - alfa)) / alfa; rbpts[kj] = (rbpts[kj] - rbpts[kj + 1] * beta) / (1.0 - beta); l += 1; j -= 1; kj -= 1; } // Compute knot removal error bounds (Br). double Br; if (j - l < k) { Br = Pw[l - 2].DistanceTo(rbpts[kj + 1]); } else { double delta = (U[a] - Uh[l - 1]) / (U[b] - Uh[l - 1]); Point4 A = (rbpts[kj + 1] * delta) + (Pw[l - 2] * (1.0 - delta)); Br = Pw[l - 1].DistanceTo(A); } // Update the error vector. int K = a + oldr - k; int q = (int)Math.Floor((double)(2 * p - k + 1) / 2); int L = K - q; for (int ii = L; ii <= a; ii++) { // These knot vectors were effected. e[ii] += Br; if (e[ii] > tolerance) { throw new Exception("Curve not degree reducible"); } } first -= 1; last += 1; } } // Load knot vector and control points. if (a != p) { // Load the knot ua. for (int j = 0; j < ph - oldr; j++) { Uh[kind] = U[a]; kind += 1; } } for (int j = lbz; j <= ph; j++) { // Load control points into Qw. Pw.Add(rbpts[j]); } if (b < m) { // Set up for the next pass thru loop. for (int j = 0; j < r; j++) { bpts[j] = nextbpts[j]; } for (int j = r; j <= p; j++) { bpts[j] = Qw[b - p + j]; } a = b; b += 1; } else { // End knot. for (int j = 0; j <= ph; j++) { Uh.Add(U[b]); } } } return(new NurbsCurve(p - 1, Uh, Pw)); }
/// <summary> /// Elevates the degree of a curve. /// <em>Implementation of Algorithm A5.9 of The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <param name="curve">The object curve to elevate.</param> /// <param name="finalDegree">The expected final degree. If the supplied degree is less or equal the curve is returned unmodified.</param> /// <returns>The curve after degree elevation.</returns> internal static NurbsBase ElevateDegree(NurbsBase curve, int finalDegree) { if (finalDegree <= curve.Degree) { return(curve); } int n = curve.Knots.Count - curve.Degree - 2; int p = curve.Degree; KnotVector U = curve.Knots; List <Point4> Pw = curve.ControlPoints; int t = finalDegree - curve.Degree; // Degree elevate a curve t times. // local arrays. double[,] bezalfs = new double[p + t + 1, p + 1]; Point4[] bpts = new Point4[p + 1]; Point4[] ebpts = new Point4[p + t + 1]; Point4[] nextbpts = new Point4[p - 1]; double[] alphas = new double[p - 1]; int m = n + p + 1; int ph = finalDegree; int ph2 = (int)Math.Floor((double)(ph / 2)); // Output values; List <Point4> Qw = new List <Point4>(); KnotVector Uh = new KnotVector(); // Compute Bezier degree elevation coefficients. bezalfs[0, 0] = bezalfs[ph, p] = 1.0; for (int i = 1; i <= ph2; i++) { double inv = 1.0 / GSharkMath.GetBinomial(ph, i); int mpi = Math.Min(p, i); for (int j = Math.Max(0, i - t); j <= mpi; j++) { bezalfs[i, j] = inv * GSharkMath.GetBinomial(p, j) * GSharkMath.GetBinomial(t, i - j); } } for (int i = ph2 + 1; i <= ph - 1; i++) { int mpi = Math.Min(p, i); for (int j = Math.Max(0, i - t); j <= mpi; j++) { bezalfs[i, j] = bezalfs[ph - i, p - j]; } } int mh = ph; int kind = ph + 1; int r = -1; int a = p; int b = p + 1; int cind = 1; double ua = U[0]; Qw.Add(Pw[0]); for (int i = 0; i <= ph; i++) { Uh.Add(ua); } // Initialize first Bezier segment. for (int i = 0; i <= p; i++) { bpts[i] = Pw[i]; } // Big loop thru knot vector. while (b < m) { int i = b; while (b < m && Math.Abs(U[b] - U[b + 1]) < GSharkMath.Epsilon) { b += 1; } int mul = b - i + 1; mh = mh + mul + t; double ub = U[b]; int oldr = r; r = p - mul; // Insert knot U[b] r times. // Checks for integer arithmetic. int lbz = (oldr > 0) ? (int)Math.Floor((double)((2 + oldr) / 2)) : 1; int rbz = (r > 0) ? (int)Math.Floor((double)(ph - (r + 1) / 2)) : ph; if (r > 0) { // Inserts knot to get Bezier segment. double numer = ub - ua; for (int k = p; k > mul; k--) { alphas[k - mul - 1] = (numer / (U[a + k] - ua)); } for (int j = 1; j <= r; j++) { int save = r - j; int s = mul + j; for (int k = p; k >= s; k--) { bpts[k] = Point4.Interpolate(bpts[k], bpts[k - 1], alphas[k - s]); } nextbpts[save] = bpts[p]; } } // End of insert knot. // Degree elevate Bezier. for (int j = lbz; j <= ph; j++) { ebpts[j] = Point4.Zero; int mpi = Math.Min(p, j); for (int k = Math.Max(0, j - t); k <= mpi; k++) { ebpts[j] += bpts[k] * bezalfs[j, k]; } } if (oldr > 1) { // Must remove knot u=U[a] oldr times. int first = kind - 2; int last = kind; double den = ub - ua; double bet = (ub - Uh[kind - 1]) / den; for (int tr = 1; tr < oldr; tr++) { // Knot removal loop. int ii = first; int jj = last; int kj = jj - kind + 1; while (jj - ii > tr) { // Loop and compute the new control points for one removal step. if (ii < cind) { double alf = (ub - Uh[ii]) / (ua - Uh[ii]); Qw.Add(Point4.Interpolate(Qw[ii], Qw[ii - 1], alf)); } if (jj >= lbz) { if (jj - tr <= kind - ph + oldr) { double gam = (ub - Uh[jj - tr]) / den; ebpts[kj] = Point4.Interpolate(ebpts[kj], ebpts[kj + 1], gam); } else { ebpts[kj] = Point4.Interpolate(ebpts[kj], ebpts[kj + 1], bet); } } ii += 1; jj -= 1; kj -= 1; } first -= 1; last += 1; } } // End of removing knot, n = U[a]. if (a != p) { // Load the knot ua. for (int j = 0; j < ph - oldr; j++) { Uh.Add(ua); } } for (int j = lbz; j <= rbz; j++) { // Load control points into Qw. Qw.Add(ebpts[j]); } if (b < m) { // Set up for the next pass thru loop. for (int j = 0; j < r; j++) { bpts[j] = nextbpts[j]; } for (int j = r; j <= p; j++) { bpts[j] = Pw[b - p + j]; } a = b; b += 1; ua = ub; } else { // End knot. for (int j = 0; j <= ph; j++) { Uh.Add(ub); } } } return(new NurbsCurve(finalDegree, Uh, Qw)); }