/// <summary> /// Builds the coefficient matrix used to calculate a curve global interpolation. /// </summary> private static Matrix BuildCoefficientsMatrix(List <Point3> pts, int degree, bool hasTangents, List <double> curveParameters, KnotVector knots) { int dim = (hasTangents) ? pts.Count + 1 : pts.Count - 1; Matrix coeffMatrix = new Matrix(); int dimEnd = (hasTangents) ? pts.Count - (degree - 1) : pts.Count - (degree + 1); foreach (double u in curveParameters) { int span = knots.Span(dim, degree, u); List <double> basicFunction = Evaluate.Curve.BasisFunction(degree, knots, span, u); List <double> startRow = CollectionHelpers.RepeatData(0.0, span - degree); List <double> endRow = CollectionHelpers.RepeatData(0.0, dimEnd - (span - degree)); coeffMatrix.Add(startRow.Concat(basicFunction).Concat(endRow).ToList()); } if (!hasTangents) { return(coeffMatrix); } List <double> zeros = CollectionHelpers.RepeatData(0.0, coeffMatrix[0].Count - 2); List <double> tangent = new List <double> { -1.0, 1.0 }; coeffMatrix.Insert(1, tangent.Concat(zeros).ToList()); coeffMatrix.Insert(coeffMatrix.Count - 1, zeros.Concat(tangent).ToList()); return(coeffMatrix); }
/// <summary> /// Bezier degree reduction. /// <em>Refer to The NURBS Book by Piegl and Tiller at page 220.</em> /// </summary> private static double BezierDegreeReduce(Point4[] bpts, int degree, out Point4[] rbpts) { // Eq. 5.40 int r = (int)Math.Floor(((double)degree - 1) / 2); Point4[] P = new Point4[degree]; P[0] = bpts[0]; P[P.Length - 1] = bpts.Last(); bool isDegreeOdd = degree % 2 != 0; int r1 = (isDegreeOdd) ? r - 1 : r; // Eq. 5.41 for (int i = 1; i <= r1; i++) { double alphaI = (double)i / degree; P[i] = (bpts[i] - (P[i - 1] * alphaI)) / (1 - alphaI); } for (int i = degree - 2; i >= r + 1; i--) { double alphaI = (double)(i + 1) / degree; P[i] = (bpts[i + 1] - (P[i + 1] * (1 - alphaI))) / alphaI; } /* Equations (5.43) and (5.44) express the parametric error {distance between points at corresponding parameter values); * the maximums of geometric and parametric errors are not necessarily at the same u value. * For p even the maximum error occurs at u = 1/2; for p odd the error is zero at u = 1/2, and it has two peaks a bit to the left and * right of u = 1/2. */ // Eq. 5.43 p even. KnotVector knots = new KnotVector(degree - 1, P.Length); int span = knots.Span(degree - 1, 0.5); List <double> Br = Evaluate.Curve.BasisFunction(degree - 1, knots, span, 0.5); double parametricError = bpts[r + 1].DistanceTo((P[r] + P[r + 1]) * 0.5) * Br[r + 1]; // Eq. 5.42 if (isDegreeOdd) { double alphaR = (double)r / degree; Point4 PLeft = (bpts[r] - (P[r - 1] * alphaR)) / (1 - alphaR); double alphaR1 = (double)(r + 1) / degree; Point4 PRight = (bpts[r + 1] - (P[r + 1] * (1 - alphaR1))) / alphaR1; P[r] = (PLeft + PRight) * 0.5; // Eq. 5.44 p odd. parametricError = ((1 - alphaR) * 0.5) * ((Br[r] - Br[r + 1]) * PLeft.DistanceTo(PRight)); } rbpts = P; return(parametricError); }
public void It_Returns_The_KnotSpan_Given_A_Parameter(int expectedValue, double parameter) { // Arrange KnotVector knotVector = new KnotVector { 0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5 }; int degree = 2; // Act int result = knotVector.Span(knotVector.Count - degree - 2, 2, parameter); // Assert result.Should().Be(expectedValue); }
/// <summary> /// Computes a point on a non-uniform, non-rational b-spline curve.<br/> /// <em>Corresponds to algorithm 3.1 from The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <param name="curve">The curve object.</param> /// <param name="parameter">Parameter on the curve at which the point is to be evaluated</param> /// <returns>The evaluated point on the curve.</returns> internal static Point3 PointAt(NurbsBase curve, double parameter) { List <Point4> controlPts = curve.ControlPoints; KnotVector knots = curve.Knots; int n = knots.Count - curve.Degree - 2; int knotSpan = knots.Span(n, curve.Degree, parameter); List <double> basisValue = BasisFunction(curve.Degree, knots, knotSpan, parameter); Point4 pointOnCurve = Point4.Zero; for (int i = 0; i <= curve.Degree; i++) { double valToMultiply = basisValue[i]; Point4 pt = controlPts[knotSpan - curve.Degree + i]; pointOnCurve.X += valToMultiply * pt.X; pointOnCurve.Y += valToMultiply * pt.Y; pointOnCurve.Z += valToMultiply * pt.Z; pointOnCurve.W += valToMultiply * pt.W; } return(new Point3(pointOnCurve)); }
/// <summary> /// Splits (divides) the surface into two parts at the specified parameter /// </summary> /// <param name="surface">The NURBS surface to split.</param> /// <param name="parameter">The parameter at which to split the surface, parameter should be between 0 and 1.</param> /// <param name="direction">Where to split in the U or V direction of the surface.</param> /// <returns>If the surface is split vertically (U direction) the left side is returned as the first surface and the right side is returned as the second surface.<br/> /// If the surface is split horizontally (V direction) the bottom side is returned as the first surface and the top side is returned as the second surface.<br/> /// If the spit direction selected is both, the split computes first a U direction split and on the result a V direction split.</returns> internal static NurbsSurface[] Split(NurbsSurface surface, double parameter, SplitDirection direction) { KnotVector knots = surface.KnotsV; int degree = surface.DegreeV; List <List <Point4> > srfCtrlPts = surface.ControlPoints; if (direction != SplitDirection.V) { srfCtrlPts = CollectionHelpers.Transpose2DArray(surface.ControlPoints); knots = surface.KnotsU; degree = surface.DegreeU; } List <double> knotsToInsert = CollectionHelpers.RepeatData(parameter, degree + 1); int span = knots.Span(degree, parameter); List <List <Point4> > surfPtsLeft = new List <List <Point4> >(); List <List <Point4> > surfPtsRight = new List <List <Point4> >(); NurbsBase result = null; foreach (List <Point4> pts in srfCtrlPts) { NurbsCurve tempCurve = new NurbsCurve(degree, knots, pts); result = KnotVector.Refine(tempCurve, knotsToInsert); surfPtsLeft.Add(result.ControlPoints.GetRange(0, span + 1)); surfPtsRight.Add(result.ControlPoints.GetRange(span + 1, span + 1)); } if (result == null) { throw new Exception($"Could not split {nameof(surface)}."); } KnotVector knotLeft = result.Knots.GetRange(0, span + degree + 2).ToKnot(); KnotVector knotRight = result.Knots.GetRange(span + 1, span + degree + 2).ToKnot(); NurbsSurface[] surfaceResult = Array.Empty <NurbsSurface>(); switch (direction) { case SplitDirection.U: { surfaceResult = new NurbsSurface[] { new NurbsSurface(degree, surface.DegreeV, knotLeft, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsLeft)), new NurbsSurface(degree, surface.DegreeV, knotRight, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsRight)) }; break; } case SplitDirection.V: { surfaceResult = new NurbsSurface[] { new NurbsSurface(surface.DegreeU, degree, surface.KnotsU.Copy(), knotLeft, surfPtsLeft), new NurbsSurface(surface.DegreeU, degree, surface.KnotsU.Copy(), knotRight, surfPtsRight) }; break; } case SplitDirection.Both: { NurbsSurface srf1 = new NurbsSurface(degree, surface.DegreeV, knotLeft, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsLeft)); NurbsSurface srf2 = new NurbsSurface(degree, surface.DegreeV, knotRight, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsRight)); NurbsSurface[] split1 = Split(srf1, parameter, SplitDirection.V); NurbsSurface[] split2 = Split(srf2, parameter, SplitDirection.V); surfaceResult = split2.Concat(split1).ToArray(); break; } } return(surfaceResult); }
/// <summary> /// Inserts a collection of knots on a curve.<br/> /// <em>Implementation of Algorithm A5.4 of The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <param name="curve">The curve object.</param> /// <param name="knotsToInsert">The set of knots.</param> /// <returns>A curve with refined knots.</returns> internal static NurbsBase KnotRefine(NurbsBase curve, IList <double> knotsToInsert) { if (knotsToInsert.Count == 0) { return(curve); } int degree = curve.Degree; List <Point4> controlPoints = curve.ControlPoints; KnotVector knots = curve.Knots; // Initialize common variables. int n = controlPoints.Count - 1; int m = n + degree + 1; int r = knotsToInsert.Count - 1; int a = knots.Span(degree, knotsToInsert[0]); int b = knots.Span(degree, knotsToInsert[r]); Point4[] controlPointsPost = new Point4[n + r + 2]; double[] knotsPost = new double[m + r + 2]; // New control points. for (int i = 0; i < a - degree + 1; i++) { controlPointsPost[i] = controlPoints[i]; } for (int i = b - 1; i < n + 1; i++) { controlPointsPost[i + r + 1] = controlPoints[i]; } // New knot vector. for (int i = 0; i < a + 1; i++) { knotsPost[i] = knots[i]; } for (int i = b + degree; i < m + 1; i++) { knotsPost[i + r + 1] = knots[i]; } // Initialize variables for knot refinement. int g = b + degree - 1; int k = b + degree + r; int j = r; // Apply knot refinement. while (j >= 0) { while (knotsToInsert[j] <= knots[g] && g > a) { controlPointsPost[k - degree - 1] = controlPoints[g - degree - 1]; knotsPost[k] = knots[g]; --k; --g; } controlPointsPost[k - degree - 1] = controlPointsPost[k - degree]; for (int l = 1; l < degree + 1; l++) { int ind = k - degree + l; double alfa = knotsPost[k + l] - knotsToInsert[j]; if (Math.Abs(alfa) < GSharkMath.Epsilon) { controlPointsPost[ind - 1] = controlPointsPost[ind]; } else { alfa /= (knotsPost[k + l] - knots[g - degree + l]); controlPointsPost[ind - 1] = (controlPointsPost[ind - 1] * alfa) + (controlPointsPost[ind] * (1.0 - alfa)); } } knotsPost[k] = knotsToInsert[j]; --k; --j; } return(new NurbsCurve(degree, knotsPost.ToKnot(), controlPointsPost.ToList())); }