/// <summary> /// Creates a interpolated curve through a set of points.<br/> /// <em>Refer to Algorithm A9.1 on The NURBS Book, pp.369-370 for details.</em> /// </summary> /// <param name="pts">The set of points to interpolate.</param> /// <param name="degree">The Curve degree.</param> /// <param name="startTangent">The tangent vector for the first point.</param> /// <param name="endTangent">The tangent vector for the last point.</param> /// <param name="centripetal">True use the chord as per knot spacing, false use the squared chord.</param> /// <returns>A the interpolated curve.</returns> public static NurbsBase Interpolated(List <Point3> pts, int degree, Vector3?startTangent = null, Vector3?endTangent = null, bool centripetal = false) { if (pts.Count < degree + 1) { throw new Exception($"You must supply at least degree + 1 points. You supplied {pts.Count} pts."); } // Gets uk parameters. List <double> uk = CurveHelpers.Parametrization(pts, centripetal); // Compute knot vectors. bool hasTangents = startTangent != null && endTangent != null; KnotVector knots = ComputeKnotsForInterpolation(uk, degree, hasTangents); // Global interpolation. // Build matrix of basis function coefficients. Matrix coeffMatrix = BuildCoefficientsMatrix(pts, degree, hasTangents, uk, knots); // Solve for each points. List <Point4> ctrlPts = (hasTangents) ? SolveCtrlPtsWithTangents(knots, pts, coeffMatrix, degree, new Vector3(startTangent.Value), new Vector3(endTangent.Value)) : SolveCtrlPts(pts, coeffMatrix); return(new NurbsCurve(degree, knots, ctrlPts)); }
public void It_Refines_The_Curve_Knot(double val, int insertion) { // Arrange int degree = 3; List <double> newKnots = new List <double>(); for (int i = 0; i < insertion; i++) { newKnots.Add(val); } List <Point3> pts = new List <Point3>(); for (int i = 0; i <= 12 - degree - 2; i++) { pts.Add(new Point3(i, 0.0, 0.0)); } NurbsCurve curve = new NurbsCurve(pts, degree); // Act NurbsBase curveAfterRefine = KnotVector.Refine(curve, newKnots); Point3 p0 = curve.PointAt(2.5); Point3 p1 = curveAfterRefine.PointAt(2.5); // Assert (curve.Knots.Count + insertion).Should().Be(curveAfterRefine.Knots.Count); (pts.Count + insertion).Should().Be(curveAfterRefine.ControlPointLocations.Count); (p0 == p1).Should().BeTrue(); }
public void It_Returns_A_Derive_Basic_Function_Given_NI() { // Arrange // Values and formulas from The Nurbs Book p.69 & p.72 int degree = 2; int span = 4; int order = 2; double parameter = 2.5; KnotVector knots = new KnotVector { 0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5 }; double[,] expectedResult = new double[, ] { { 0.125, 0.75, 0.125 }, { -0.5, 0.0, 0.5 }, { 1.0, -2.0, 1.0 } }; // Act List <Vector> resultToCheck = GShark.Evaluate.Curve.DerivativeBasisFunctionsGivenNI(span, parameter, degree, order, knots); // Assert resultToCheck[0][0].Should().BeApproximately(expectedResult[0, 0], GSharkMath.MaxTolerance); resultToCheck[0][1].Should().BeApproximately(expectedResult[0, 1], GSharkMath.MaxTolerance); resultToCheck[0][2].Should().BeApproximately(expectedResult[0, 2], GSharkMath.MaxTolerance); resultToCheck[1][0].Should().BeApproximately(expectedResult[1, 0], GSharkMath.MaxTolerance); resultToCheck[1][1].Should().BeApproximately(expectedResult[1, 1], GSharkMath.MaxTolerance); resultToCheck[1][2].Should().BeApproximately(expectedResult[1, 2], GSharkMath.MaxTolerance); resultToCheck[2][0].Should().BeApproximately(expectedResult[2, 0], GSharkMath.MaxTolerance); resultToCheck[2][1].Should().BeApproximately(expectedResult[2, 1], GSharkMath.MaxTolerance); resultToCheck[2][2].Should().BeApproximately(expectedResult[2, 2], GSharkMath.MaxTolerance); resultToCheck.Count.Should().Be(order + 1); resultToCheck[0].Count.Should().Be(degree + 1); }
/// <summary> /// Computes the non-vanishing basis functions.<br/> /// <em>Implementation of Algorithm A2.2 from The NURBS Book by Piegl and Tiller.<br/> /// Uses recurrence to compute the basis functions, also known as Cox - deBoor recursion formula.</em> /// </summary> /// <param name="degree">Degree of a curve.</param> /// <param name="knots">Set of knots.</param> /// <param name="span">Index span of knots.</param> /// <param name="knot">knot value.</param> /// <returns>List of non-vanishing basis functions.</returns> internal static List <double> BasisFunction(int degree, KnotVector knots, int span, double knot) { Vector left = Vector.Zero1d(degree + 1); Vector right = Vector.Zero1d(degree + 1); // N[0] = 1.0 by definition; Vector N = Vector.Zero1d(degree + 1); N[0] = 1.0; for (int j = 1; j < degree + 1; j++) { left[j] = knot - knots[span + 1 - j]; right[j] = knots[span + j] - knot; double saved = 0.0; for (int r = 0; r < j; r++) { double temp = N[r] / (right[r + 1] + left[j - r]); N[r] = saved + right[r + 1] * temp; saved = left[j - r] * temp; } N[j] = saved; } return(N); }
protected NurbsBase(int degree, KnotVector knots, List <Point4> controlPoints) { if (controlPoints is null) { throw new ArgumentNullException(nameof(controlPoints)); } if (knots is null) { throw new ArgumentNullException(nameof(knots)); } if (degree < 1) { throw new ArgumentException("Degree must be greater than 1!"); } if (knots.Count != controlPoints.Count + degree + 1) { throw new ArgumentException("Number of controlPoints + degree + 1 must equal knots length!"); } if (!knots.IsValid(degree, controlPoints.Count)) { throw new ArgumentException("Invalid knot format! Should begin with degree + 1 repeats and end with degree + 1 repeats!"); } Weights = Point4.GetWeights(controlPoints); Degree = degree; Knots = knots; ControlPointLocations = Point4.PointDehomogenizer1d(controlPoints); ControlPoints = controlPoints; }
/// <summary> /// Compute R - Eqn 9.67. /// </summary> private static List <Point3> ComputeValuesR(KnotVector knots, List <double> curveParameters, List <Point3> Rk, int degree, int numberOfCtrPts) { List <Vector> vectorR = new List <Vector>(); for (int i = 1; i < numberOfCtrPts - 1; i++) { List <Vector> ruTemp = new List <Vector>(); for (int j = 0; j < Rk.Count; j++) { double tempBasisVal = Evaluate.Curve.OneBasisFunction(degree, knots, i, curveParameters[j + 1]); ruTemp.Add(Rk[j] * tempBasisVal); } Vector tempVec = Vector.Zero1d(ruTemp[0].Count); for (int g = 0; g < ruTemp[0].Count; g++) { foreach (Vector vec in ruTemp) { tempVec[g] += vec[g]; } } vectorR.Add(tempVec); } return(vectorR.Select(v => new Point3(v[0], v[1], v[2])).ToList()); }
/// <summary> /// Computes Rk - Eqn 9.63. /// </summary> private static List <Point3> ComputesValuesRk(KnotVector knots, List <double> curveParameters, int degree, List <Point3> pts, int numberOfCtrPts) { Point3 pt0 = pts[0]; // Q0 Point3 ptm = pts[pts.Count - 1]; // Qm List <Point3> Rk = new List <Point3>(); for (int i = 1; i < pts.Count - 1; i++) { Point3 pti = pts[i]; double n0p = Evaluate.Curve.OneBasisFunction(degree, knots, 0, curveParameters[i]); double nnp = Evaluate.Curve.OneBasisFunction(degree, knots, numberOfCtrPts - 1, curveParameters[i]); Point3 elem2 = pt0 * n0p; Point3 elem3 = ptm * nnp; Point3 tempVec = new Point3(); for (int j = 0; j < 3; j++) { tempVec[j] = (pti[j] - elem2[j] - elem3[j]); } tempVec.X = pti.X - elem2.X - elem3.X; tempVec.Y = pti.Y - elem2.Y - elem3.Y; tempVec.Z = pti.Z - elem2.Z - elem3.Z; Rk.Add(tempVec); } return(Rk); }
/// <summary> /// This method evaluate a B-spline span using the deBoor algorithm. /// https://github.com/mcneel/opennurbs/blob/2b96cf31429dab25bf8a1dbd171227c506b06f88/opennurbs_evaluate_nurbs.cpp#L1249 /// This method is not implemented for clamped knots. /// </summary> /// <param name="controlPts">The control points of the curve.</param> /// <param name="knots">The knot vector of the curve.</param> /// <param name="degree">The value degree of the curve.</param> /// <param name="parameter">The parameter value where the curve is evaluated.</param> internal static void DeBoor(ref List <Point4> controlPts, KnotVector knots, int degree, double parameter) { if (Math.Abs(knots[degree] - knots[degree - 1]) < GSharkMath.Epsilon) { throw new Exception($"DeBoor evaluation failed: {knots[degree]} == {knots[degree + 1]}"); } // deltaT = {knot[order-1] - t, knot[order] - t, .. knot[2*order-3] - t} List <double> deltaT = new List <double>(); for (int k = 0; k < degree; k++) { deltaT.Add(knots[degree + 1 + k] - parameter); } for (int i = degree; i > 0; --i) { for (int j = 0; j < i; j++) { double k0 = knots[degree + 1 - i + j]; double k1 = knots[degree + 1 + j]; double alpha0 = deltaT[j] / (k1 - k0); double alpha1 = 1.0 - alpha0; Point4 cv1 = controlPts[j + 1]; Point4 cv0 = controlPts[j]; controlPts[j] = (cv0 * alpha0) + (cv1 * alpha1); } } }
/// <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> /// Defines the control points defining the tangent values for the first and last points. /// </summary> private static List <Point4> SolveCtrlPtsWithTangents(KnotVector knots, List <Point3> pts, Matrix coeffMatrix, int degree, Vector3 startTangent, Vector3 endTangent) { Matrix matrixLu = Matrix.Decompose(coeffMatrix, out int[] permutation); Matrix ptsSolved = new Matrix(); // Equations 9.11 double mult0 = knots[degree + 1] / degree; // Equations 9.12 double mult1 = (1 - knots[knots.Count - degree - 2]) / degree; // Solve for each dimension. for (int i = 0; i < 3; i++) { Vector b = new Vector { pts[0][i], startTangent[i] * mult0 }; // Insert the tangents at the second and second to last index. // Equations 9.11 b.AddRange(pts.Skip(1).Take(pts.Count - 2).Select(pt => pt[i])); // Equations 9.12 b.Add(endTangent[i] * mult1); b.Add(pts[pts.Count - 1][i]); Vector solution = Matrix.Solve(matrixLu, permutation, b); ptsSolved.Add(solution); } return(ptsSolved.Transpose().Select(pt => new Point4(pt[0], pt[1], pt[2], 1)).ToList()); }
/// <summary> /// Constructs a NURBS surface from a 2D grid of points.<br/> /// The grid of points should be organized as, the V direction from left to right and the U direction increases from bottom to top. /// </summary> /// <param name="degreeU">Degree of surface in U direction.</param> /// <param name="degreeV">Degree of surface in V direction.</param> /// <param name="points">Points locations.</param> /// <param name="weight">A 2D collection of weights.</param> /// <returns>A NURBS surface.</returns> public static NurbsSurface FromPoints(int degreeU, int degreeV, List <List <Point3> > points, List <List <double> > weight = null) { KnotVector knotU = new KnotVector(degreeU, points.Count); KnotVector knotV = new KnotVector(degreeV, points[0].Count); var controlPts = points.Select((pts, i) => pts.Select((pt, j) => weight != null ? new Point4(pt, weight[i][j]) : new Point4(pt)).ToList()).ToList(); return(new NurbsSurface(degreeU, degreeV, knotU, knotV, controlPts)); }
[InlineData(new double[] { -0.666, -0.333, 0, 0.333, 0.666, 1, 1.333, 1.666 }, 2, 5, true)] // Periodic public void It_Checks_If_The_Knots_Are_Valid(double[] knots, int degree, int ctrlPts, bool expectedResult) { // Act KnotVector knot = new KnotVector(knots); // Assert knot.IsValid(degree, ctrlPts).Should().Be(expectedResult); }
public void It_Returns_True_If_KnotVector_Is_Clamped(double[] knots, int degree, bool expectedResult) { // Act KnotVector knotVector = new KnotVector(knots); // Assert knotVector.IsClamped(degree).Should().Be(expectedResult); }
public void It_Checks_If_Knots_Is_Periodic(double[] knots, int degree, bool expectedResult) { // Act KnotVector knot = new KnotVector(knots); // Assert knot.IsPeriodic(degree).Should().Be(expectedResult); }
protected NurbsBase() { Weights = new List <double>(); Degree = 0; Knots = new KnotVector(); ControlPointLocations = new List <Point3>(); ControlPoints = new List <Point4>(); }
/// <summary> /// Computes the value of a basis function for a single value.<br/> /// <em>Implementation of Algorithm A2.4 from The NURBS Book by Piegl and Tiller.</em><br/> /// </summary> /// <param name="degree">Degree of a curve.</param> /// <param name="knots">Set of knots.</param> /// <param name="span">Index span of knots.</param> /// <param name="knot">knot value.</param> /// <returns>The single parameter value of the basis function.</returns> public static double OneBasisFunction(int degree, KnotVector knots, int span, double knot) { // Special case at boundaries. if ((span == 0 && Math.Abs(knot - knots[0]) < GSharkMath.MaxTolerance) || (span == knots.Count - degree - 2) && Math.Abs(knot - knots[knots.Count - 1]) < GSharkMath.MaxTolerance) { return(1.0); } // Local property, parameter is outside of span range. if (knot < knots[span] || knot >= knots[span + degree + 1]) { return(0.0); } List <double> N = CollectionHelpers.RepeatData(0.0, degree + span + 1); // Initialize the zeroth degree basic functions. for (int j = 0; j < degree + 1; j++) { if (knot >= knots[span + j] && knot < knots[span + j + 1]) { N[j] = 1.0; } } // Compute triangular table of basic functions. for (int k = 1; k < degree + 1; k++) { double saved = 0.0; if (N[0] != 0.0) { saved = ((knot - knots[span]) * N[0]) / (knots[span + k] - knots[span]); } for (int j = 0; j < degree - k + 1; j++) { double uLeft = knots[span + j + 1]; double uRight = knots[span + j + k + 1]; if (N[j + 1] == 0.0) { N[j] = saved; saved = 0.0; } else { double temp = N[j + 1] / (uRight - uLeft); N[j] = saved + (uRight - knot) * temp; saved = (knot - uLeft) * temp; } } } return(N[0]); }
public virtual NurbsBase Reverse() { List <Point4> controlPts = new List <Point4>(ControlPoints); controlPts.Reverse(); KnotVector knots = KnotVector.Reverse(Knots); return(new NurbsCurve(Degree, knots, controlPts)); }
/// <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 CreateUniformPeriodicKnotVector_Throws_An_Exception_If_Are_Not_Valid_Inputs(int degree, int numberOfControlPts) { // Are identifies as not valid inputs when: // Degree and control points count is less than 2. // Degree is bigger than the control points count. // Act Func <KnotVector> funcResult = () => KnotVector.UniformPeriodic(degree, numberOfControlPts); // Assert funcResult.Should().Throw <Exception>(); }
public void It_Throws_An_Exception_If_Input_Knot_Vector_Is_Empty() { // Assert KnotVector knots = new KnotVector(); // Act Func <KnotVector> func = () => knots.Normalize(); // Arrange func.Should().Throw <Exception>().WithMessage("Input knot vector cannot be empty"); }
/// <summary> /// Samples a curve in an adaptive way. <br/> /// <em>Corresponds to this algorithm http://ariel.chronotext.org/dd/defigueiredo93adaptive.pdf </em> /// </summary> /// <param name="curve">The curve to sampling.</param> /// <param name="tolerance">The tolerance for the adaptive division.</param> /// <returns>A tuple collecting the parameter where it was sampled and the points.</returns> public static (List <double> tValues, List <Point3> pts) AdaptiveSample(NurbsBase curve, double tolerance = GSharkMath.MinTolerance) { if (curve.Degree != 1) { return(AdaptiveSampleRange(curve, curve.Knots[0], curve.Knots[curve.Knots.Count - 1], tolerance)); } KnotVector copyKnot = new KnotVector(curve.Knots); copyKnot.RemoveAt(0); copyKnot.RemoveAt(copyKnot.Count - 1); return(copyKnot, curve.ControlPointLocations); }
public void KnotMultiplicity_Returns_Knot_Multiplicity_At_The_Given_Index(double knot, int expectedMultiplicity) { // Arrange KnotVector knots = new KnotVector { 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3.3, 4, 4, 4 }; // Act int multiplicity = knots.Multiplicity(knot); // Assert multiplicity.Should().Be(expectedMultiplicity); }
public void It_Creates_A_Copy_Of_The_Knot_Vector() { // Assert KnotVector knotVector = new KnotVector { 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3.3, 4, 4, 4 }; // Act KnotVector knotVectorCopy = knotVector.Copy(); // Arrange knotVectorCopy.Should().NotBeSameAs(knotVector); knotVectorCopy.Should().BeEquivalentTo(knotVector); }
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); }
public virtual NurbsBase Close() { // Wrapping control points List <Point4> pts = new List <Point4>(ControlPoints); for (int i = 0; i < Degree; i++) { pts.Add(pts[i]); } KnotVector knots = KnotVector.UniformPeriodic(Degree, pts.Count); return(new NurbsCurve(Degree, knots, pts)); }
/// <summary> /// Internal constructor used to validate the NURBS surface. /// </summary> /// <param name="degreeU">The degree in the U direction.</param> /// <param name="degreeV">The degree in the V direction.</param> /// <param name="knotsU">The knotVector in the U direction.</param> /// <param name="knotsV">The knotVector in the V direction.</param> /// <param name="controlPts">Two dimensional array of points.</param> internal NurbsSurface(int degreeU, int degreeV, KnotVector knotsU, KnotVector knotsV, List <List <Point4> > controlPts) { if (controlPts == null) { throw new ArgumentNullException("Control points array connot be null!"); } if (degreeU < 1) { throw new ArgumentException("DegreeU must be greater than 1!"); } if (degreeV < 1) { throw new ArgumentException("DegreeV must be greater than 1!"); } if (knotsU == null) { throw new ArgumentNullException("KnotU cannot be null!"); } if (knotsV == null) { throw new ArgumentNullException("KnotV cannot be null!"); } if (knotsU.Count != controlPts.Count() + degreeU + 1) { throw new ArgumentException("Points count + degreeU + 1 must equal knotsU count!"); } if (knotsV.Count != controlPts.First().Count() + degreeV + 1) { throw new ArgumentException("Points count + degreeV + 1 must equal knotsV count!"); } if (!knotsU.IsValid(degreeU, controlPts.Count())) { throw new ArgumentException("Invalid knotsU!"); } if (!knotsV.IsValid(degreeV, controlPts.First().Count())) { throw new ArgumentException("Invalid knotsV!"); } DegreeU = degreeU; DegreeV = degreeV; KnotsU = (Math.Abs(knotsU.GetDomain(degreeU).Length - 1.0) > GSharkMath.Epsilon) ? knotsU.Normalize() : knotsU; KnotsV = (Math.Abs(knotsV.GetDomain(degreeV).Length - 1.0) > GSharkMath.Epsilon) ? knotsV.Normalize() : knotsV; Weights = Point4.GetWeights2d(controlPts); ControlPointLocations = Point4.PointDehomogenizer2d(controlPts); ControlPoints = controlPts; DomainU = new Interval(KnotsU.First(), KnotsU.Last()); DomainV = new Interval(KnotsV.First(), KnotsV.Last()); }
public void It_Creates_An_Unclamped_Uniform_KnotVector() { // Arrange int degree = 3; int ctrlPts = 5; KnotVector expectedKnotVector = new KnotVector { 0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0 }; // Act KnotVector knots = new KnotVector(degree, ctrlPts, false); // Assert knots.Should().BeEquivalentTo(expectedKnotVector); }
public void It_Reverses_A_Knot_Vectors() { // Assert KnotVector knotVector = new KnotVector { 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3.3, 4, 4, 4 }; KnotVector expectedKnotVector = new KnotVector { 0, 0, 0, 0.7000000000000002, 1, 2, 2, 2, 3, 3, 4, 4, 4, 4 }; // Act KnotVector reversedKnots = KnotVector.Reverse(knotVector); // Arrange reversedKnots.Should().BeEquivalentTo(expectedKnotVector); }
public void It_Returns_A_Normalized_Knot_Vector() { // Arrange KnotVector knots = new KnotVector { -5, -5, -3, -2, 2, 3, 5, 5 }; KnotVector knotsExpected = new KnotVector { 0.0, 0.0, 0.2, 0.3, 0.7, 0.8, 1.0, 1.0 }; // Act KnotVector normalizedKnots = knots.Normalize(); // Assert normalizedKnots.Should().BeEquivalentTo(knotsExpected); }