diff --git a/src/GShark/Core/CurveHelpers.cs b/src/GShark/Core/CurveHelpers.cs
new file mode 100644
index 00000000..84caa2bb
--- /dev/null
+++ b/src/GShark/Core/CurveHelpers.cs
@@ -0,0 +1,50 @@
+using GShark.ExtendedMethods;
+using GShark.Geometry;
+using GShark.Operation;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace GShark.Core
+{
+ internal static class CurveHelpers
+ {
+ ///
+ /// Normalizes knot vectors of all curves to the same domain.
+ ///
+ internal static IList NormalizedKnots(IList curves)
+ {
+ // Unify knots, normalized them.
+ curves = curves
+ .Select(curve => curve.Knots.GetDomain(curve.Degree).Length > 1
+ ? new NurbsCurve(curve.Degree, curve.Knots.Normalize(), curve.ControlPoints)
+ : curve).ToList();
+
+ // Unify curves by knots.
+ KnotVector combinedKnots = curves.First().Knots.Copy();
+ foreach (NurbsCurve curve in curves.Skip(1))
+ {
+ combinedKnots.AddRange(curve.Knots.Where(k => !combinedKnots.Contains(k)).ToList());
+ }
+
+ curves = (from curve in curves
+ let knotToInsert = combinedKnots.OrderBy(k => k).Where(k => !curve.Knots.Contains(k)).ToKnot()
+ select Modify.CurveKnotRefine(curve, knotToInsert)).ToList();
+ return curves;
+ }
+
+ ///
+ /// Elevates degree of all curves to highest degree among all curves.
+ ///
+ internal static IList NormalizedDegree(IList curves)
+ {
+ // Unify curves by degree.
+ int targetDegree = curves.Max(c => c.Degree);
+ curves = curves
+ .Select(curve => curve.Degree != targetDegree
+ ? Modify.ElevateDegree(curve, targetDegree)
+ : curve).ToList();
+
+ return curves;
+ }
+ }
+}
diff --git a/src/GShark/ExtendedMethods/Curve.cs b/src/GShark/ExtendedMethods/Curve.cs
deleted file mode 100644
index f9385c28..00000000
--- a/src/GShark/ExtendedMethods/Curve.cs
+++ /dev/null
@@ -1,239 +0,0 @@
-using GShark.Core;
-using GShark.Geometry;
-using GShark.Operation;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace GShark.ExtendedMethods
-{
- public static class Curve
- {
- ///
- /// Divides a curve for a given number of time, including the end points.
- /// The result is not split curves but a collection of t values and lengths that can be used for splitting.
- /// As with all arc length methods, the result is an approximation.
- ///
- /// The curve object to divide.
- /// The number of parts to split the curve into.
- /// A tuple define the t values where the curve is divided and the lengths between each division.
- public static (List Points, List Parameters) Divide(this NurbsCurve curve, int numberOfSegments)
- {
- if (numberOfSegments < 2)
- {
- throw new ArgumentException("Number of segments must be greater than 1.", nameof(numberOfSegments));
- }
-
- if (curve == null)
- {
- throw new ArgumentNullException(nameof(curve));
- }
-
- var divideResult = Operation.Divide.CurveByCount(curve, numberOfSegments);
- var points = divideResult.Select(curve.PointAt).ToList();
- return (points, divideResult);
- }
-
- ///
- /// Divides a curve for a given max segment length, including the end points.
- /// The result is not split curves but a collection of t values and lengths that can be used for splitting.
- /// As with all arc length methods, the result is an approximation.
- ///
- /// The curve object to divide.
- /// The maximum length the segments have to be split in.
- /// Force to have all the segments of the same lengths.
- /// A tuple define the t values where the curve is divided and the lengths between each division.
- public static (List Points, List Parameters) Divide(this NurbsCurve curve, double maxSegmentLength, bool equalSegmentLengths = false)
- {
- if (maxSegmentLength <= 0)
- {
- throw new ArgumentException("Segment length must be greater than 0.", nameof(maxSegmentLength));
- }
-
- if (curve == null)
- {
- throw new ArgumentNullException(nameof(curve));
- }
-
- var len = maxSegmentLength;
- if (equalSegmentLengths)
- {
- List curves = Modify.DecomposeCurveIntoBeziers(curve);
- List curveLengths = curves.Select(nurbsCurve => Analyze.BezierCurveLength(nurbsCurve)).ToList();
- double totalLength = curveLengths.Sum();
-
- len = totalLength / Math.Ceiling(totalLength / maxSegmentLength);
- }
-
- var (tValues, lengths) = Operation.Divide.CurveByLength(curve, len);
- var points = tValues.Select(curve.PointAt).ToList();
-
- return (points, tValues);
- }
-
- ///
- /// Creates rotation minimized perpendicular frames (RMF) at given t parameters along the curve.
- /// //Double reflection method taken from Wang, W., J¨uttler, B., Zheng, D., and Liu, Y. 2008. "Computation of rotation minimizing frame." https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/Computation-of-rotation-minimizing-frames.pdf
- ///
- ///The input curve.
- /// ///The curve parameter values to locate perpendicular curve frames
- public static List PerpendicularFrames(this NurbsCurve curve, List uValues)
- {
- var pointsOnCurve = uValues.Select(curve.PointAt).ToList(); //get points at t values
- var pointsOnCurveTan = uValues.Select(t => Evaluation.RationalCurveTangent(curve, t)).ToList(); //get tangents at t values
- var firstParameter = uValues[0]; //get first t value
-
- //Create initial frame at first parameter
- var origin = curve.PointAt(firstParameter);
- var crvTan = Evaluation.RationalCurveTangent(curve, firstParameter);
- var crvNormal = Vector3.PerpendicularTo(crvTan);
- var yAxis = Vector3.CrossProduct(crvTan, crvNormal);
- var xAxis = Vector3.CrossProduct(yAxis, crvTan);
-
- //Set initial frame
- Plane[] perpFrames = new Plane[pointsOnCurve.Count];
- perpFrames[0] = new Plane(origin, xAxis, yAxis);
-
- //Given boundary data(x0, t0; x1, t1) and an initial right-handed
- //orthonormal frame U0 = (r0, s0, t0) at x0, the next frame U1 = (r1, s1, t1)
- //at x1 for RMF approximation is computed by the double reflection method in
- //the following two steps.
- //
- //Step 1.Let R1 denote the reflection in the bisecting plane of the points x0
- //and x1(see Figure 4).Use R1 to map U0 to a left - handed orthonormal frame
- //UL0 = (rL0, sL0, tL0).
- //
- //Step 2.Let R2 denote the reflection in the bisecting plane of the points x1 + tL
- //0 and x1 +t1. Use R2 to map UL0 to a right - handed orthonormal frame U1 = (r1, s1, t1).
- //Output U1.
-
- for (int i = 0; i < pointsOnCurve.Count - 1; i++)
- {
- Vector3 v1 = pointsOnCurve[i + 1] - pointsOnCurve[i]; //compute reflection vector of R1
- double c1 = v1 * v1;
- Vector3 rLi = perpFrames[i].XAxis - (2 / c1) * (v1 * perpFrames[i].XAxis) * v1; //compute reflected rL vector by R1
- Vector3 tLi = pointsOnCurveTan[i] - (2 / c1) * (v1 * pointsOnCurveTan[i]) * v1; //compute reflected tL vector by R1
- Vector3 v2 = pointsOnCurveTan[i + 1] - tLi; //compute reflection vector of R2
- double c2 = v2 * v2;
- Vector3 rNext = rLi - (2 / c2) * (v2 * rLi) * v2; //compute reflected r vector by R2
- var sNext = Vector3.CrossProduct(pointsOnCurveTan[i + 1], rNext); //compute vector s[i+1] of next frame
-
- //create output frame
- var frameNext = new Plane { Origin = pointsOnCurve[i + 1], XAxis = rNext, YAxis = sNext };
- perpFrames[i + 1] = frameNext; //output frame
- }
-
- return perpFrames.ToList();
- }
-
- ///
- /// Splits a curve into two parts at a given parameter.
- ///
- /// The curve object.
- /// The parameter at which to split the curve.
- /// Two NurbsCurve objects.
- public static List SplitAt(this NurbsCurve curve, double t)
- {
- int degree = curve.Degree;
-
- List knotsToInsert = CollectionHelpers.RepeatData(t, degree + 1);
-
- NurbsCurve refinedCurve = Modify.CurveKnotRefine(curve, knotsToInsert);
-
- int s = curve.Knots.Span(degree, t);
-
- KnotVector knots0 = refinedCurve.Knots.ToList().GetRange(0, s + degree + 2).ToKnot();
- KnotVector knots1 = refinedCurve.Knots.GetRange(s + 1, refinedCurve.Knots.Count - (s + 1)).ToKnot();
-
- List controlPoints0 = refinedCurve.ControlPoints.GetRange(0, s + 1);
- List controlPoints1 = refinedCurve.ControlPoints.GetRange(s + 1, refinedCurve.ControlPointLocations.Count - (s + 1));
-
- return new List { new NurbsCurve(degree, knots0, controlPoints0), new NurbsCurve(degree, knots1, controlPoints1) };
- }
-
- ///
- /// Splits a curve at given parameters and returns the segments as curves.
- ///
- /// The curve to split.
- /// The parameters at which to split the curve. Values should be between 0.0 and 1.0.
- /// Collection of curve segments.
- public static List SplitAt(this NurbsCurve curve, double[] parameters)
- {
- var curves = new List();
- if (parameters.Length == 0)
- {
- curves.Add(curve);
- return curves;
- }
-
- var sortedParameters = parameters.OrderBy(x => x).ToArray();
- Interval curveDomain = curve.Knots.GetDomain(curve.Degree);
-
- if (Math.Abs(sortedParameters[0] - curveDomain.T0) > GSharkMath.MaxTolerance)
- {
- var tempParams = new double[sortedParameters.Length + 1];
- tempParams[0] = curveDomain.T0;
- for (var i = 0; i < sortedParameters.Length; i++)
- {
- tempParams[i + 1] = sortedParameters[i];
- }
- sortedParameters = tempParams;
- }
-
- if (Math.Abs(sortedParameters[sortedParameters.Length - 1] - curveDomain.T1) > GSharkMath.MaxTolerance)
- {
- Array.Resize(ref sortedParameters, sortedParameters.Length + 1);
- sortedParameters[sortedParameters.Length - 1] = curveDomain.T1;
- }
-
- for (int i = 0; i < sortedParameters.Length - 1; i++)
- {
- curves.Add(SubCurve(curve, new Interval(sortedParameters[i], sortedParameters[i + 1])));
- }
-
- return curves;
- }
-
- ///
- /// Extract sub-curve defined by domain.
- ///
- /// The curve from which to extract the sub-curve.
- /// Domain of sub-curve
- /// NurbsCurve.
- public static NurbsCurve SubCurve(this NurbsCurve curve, Interval domain)
- {
- int degree = curve.Degree;
- int order = degree + 1;
- Interval subCurveDomain = domain;
-
- //NOTE: Handling decreasing domain by flipping it to maintain direction of original curve in sub-curve. Is this what we want?
- if (domain.IsDecreasing)
- {
- subCurveDomain = new Interval(domain.T1, domain.T0);
- }
-
- var isT0AtStart = Math.Abs(subCurveDomain.T0 - curve.Knots[0]) < GSharkMath.MaxTolerance;
- var isT1AtEnd = Math.Abs(subCurveDomain.T1 - curve.Knots[curve.Knots.Count - 1]) < GSharkMath.MaxTolerance;
-
- if (isT0AtStart && isT1AtEnd)
- {
- return curve;
- }
-
- if (isT0AtStart || isT1AtEnd)
- {
- return isT0AtStart ? curve.SplitAt(subCurveDomain.T1)[0] : curve.SplitAt(subCurveDomain.T0)[1];
- }
-
- List knotsToInsert = CollectionHelpers.RepeatData(domain.T0, order).Concat(CollectionHelpers.RepeatData(domain.T1, degree + 1)).ToList();
- NurbsCurve refinedCurve = Modify.CurveKnotRefine(curve, knotsToInsert);
-
- var subCurveControlPoints = refinedCurve.ControlPoints.GetRange(order, order);
- var subCrvCrtlPtsLocations = subCurveControlPoints.Select(Point4.PointDehomogenizer).ToList();
-
- var subCurve = new NurbsCurve(subCrvCrtlPtsLocations, curve.Degree);
-
- return subCurve;
- }
- }
-}
diff --git a/src/GShark/ExtendedMethods/Surface.cs b/src/GShark/ExtendedMethods/Surface.cs
deleted file mode 100644
index a4766046..00000000
--- a/src/GShark/ExtendedMethods/Surface.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using GShark.Geometry;
-using GShark.Operation;
-using System;
-using GShark.Enumerations;
-
-namespace GShark.ExtendedMethods
-{
- public static class Surface
- {
- ///
- /// Splits (divides) the surface into two parts at the specified parameter
- ///
- /// The NURBS surface to split.
- /// The parameter at which to split the surface, parameter should be between 0 and 1.
- /// Where to split in the U or V direction of the surface.
- /// 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.
- /// 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.
- public static NurbsSurface[] Split(this NurbsSurface surface, double parameter, SplitDirection direction)
- {
- if (parameter < 0.0 || parameter > 1.0)
- {
- throw new ArgumentOutOfRangeException(nameof(parameter), "The parameter is not into the domain 0.0 to 1.0.");
- }
-
- if (surface == null)
- {
- throw new ArgumentNullException(nameof(surface));
- }
-
- return Divide.SplitSurface(surface, parameter, direction);
- }
- }
-}
diff --git a/src/GShark/Geometry/NurbsCurve.cs b/src/GShark/Geometry/NurbsCurve.cs
index 945158b3..91a03cae 100644
--- a/src/GShark/Geometry/NurbsCurve.cs
+++ b/src/GShark/Geometry/NurbsCurve.cs
@@ -1,5 +1,6 @@
#nullable enable
using GShark.Core;
+using GShark.Interfaces;
using GShark.Operation;
using GShark.Operation.Utilities;
using System;
@@ -7,7 +8,7 @@
using System.Globalization;
using System.Linq;
using System.Text;
-using GShark.Interfaces;
+using GShark.ExtendedMethods;
namespace GShark.Geometry
{
@@ -142,7 +143,7 @@ public BoundingBox GetBoundingBox()
/// True if the curve is closed.
public bool IsClosed()
{
- Point3 pt0 = Evaluation.CurvePointAt(this,0.0);
+ Point3 pt0 = Evaluation.CurvePointAt(this, 0.0);
Point3 pt1 = Evaluation.CurvePointAt(this, 1.0);
return pt0.EpsilonEquals(pt1, GSharkMath.Epsilon);
}
@@ -310,7 +311,7 @@ public Plane FrameAt(double t)
List derivatives = Evaluation.RationalCurveDerivatives(this, t, 2);
- Vector3 normal = (derivatives[2].Length == 0.0)
+ Vector3 normal = (derivatives[2].Length == 0.0)
? Vector3.PerpendicularTo(derivatives[1])
: Analyze.Curvature(derivatives[1], derivatives[2]);
@@ -347,7 +348,6 @@ public double ClosestParameter(Point3 pt)
/// Computes the parameter along the curve which coincides with a given length.
///
/// Length of segment to measure. Must be less than or equal to the length of the curve.
- /// If set less or equal 0.0, the tolerance used is 1e-10.
/// The parameter on the curve at the given length.
public double ParameterAtLength(double segmentLength)
{
@@ -371,7 +371,7 @@ public double LengthAt(double t)
{
if (t <= 0.0)
{
- return 0.0;
+ return 0.0;
}
if (t >= 1.0)
@@ -432,6 +432,218 @@ public NurbsCurve Offset(double distance, Plane pln)
return Fitting.InterpolatedCurve(offsetPts, Degree);
}
+ ///
+ /// Divides a curve for a given number of time, including the end points.
+ /// The result is not split curves but a collection of t values and lengths that can be used for splitting.
+ /// As with all arc length methods, the result is an approximation.
+ ///
+ /// The number of parts to split the curve into.
+ /// A tuple define the t values where the curve is divided and the lengths between each division.
+ public (List Points, List Parameters) Divide(int numberOfSegments)
+ {
+ if (numberOfSegments < 2)
+ {
+ throw new ArgumentException("Number of segments must be greater than 1.", nameof(numberOfSegments));
+ }
+
+ var divideResult = Operation.Divide.CurveByCount(this, numberOfSegments);
+ var points = divideResult.Select(PointAt).ToList();
+ return (points, divideResult);
+ }
+
+ ///
+ /// Divides a curve for a given max segment length, including the end points.
+ /// The result is not split curves but a collection of t values and lengths that can be used for splitting.
+ /// As with all arc length methods, the result is an approximation.
+ ///
+ /// The maximum length the segments have to be split in.
+ /// Force to have all the segments of the same lengths.
+ /// A tuple define the t values where the curve is divided and the lengths between each division.
+ public (List Points, List Parameters) Divide(double maxSegmentLength, bool equalSegmentLengths = false)
+ {
+ if (maxSegmentLength <= 0)
+ {
+ throw new ArgumentException("Segment length must be greater than 0.", nameof(maxSegmentLength));
+ }
+
+ var len = maxSegmentLength;
+ if (equalSegmentLengths)
+ {
+ List curves = Modify.DecomposeCurveIntoBeziers(this);
+ List curveLengths = curves.Select(nurbsCurve => Analyze.BezierCurveLength(nurbsCurve)).ToList();
+ double totalLength = curveLengths.Sum();
+
+ len = totalLength / Math.Ceiling(totalLength / maxSegmentLength);
+ }
+
+ var (tValues, lengths) = Operation.Divide.CurveByLength(this, len);
+ var points = tValues.Select(PointAt).ToList();
+
+ return (points, tValues);
+ }
+
+ ///
+ /// Creates rotation minimized perpendicular frames (RMF) at given t parameters along the curve.
+ /// Double reflection method taken from Wang, W., J¨uttler, B., Zheng, D., and Liu, Y. 2008. "Computation of rotation minimizing frame."
+ /// https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/Computation-of-rotation-minimizing-frames.pdf
+ ///
+ /// ///The curve parameter values to locate perpendicular curve frames
+ public List PerpendicularFrames(List uValues)
+ {
+ var pointsOnCurve = uValues.Select(PointAt).ToList(); //get points at t values
+ var pointsOnCurveTan = uValues.Select(t => Evaluation.RationalCurveTangent(this, t)).ToList(); //get tangents at t values
+ var firstParameter = uValues[0]; //get first t value
+
+ //Create initial frame at first parameter
+ var origin = PointAt(firstParameter);
+ var crvTan = Evaluation.RationalCurveTangent(this, firstParameter);
+ var crvNormal = Vector3.PerpendicularTo(crvTan);
+ var yAxis = Vector3.CrossProduct(crvTan, crvNormal);
+ var xAxis = Vector3.CrossProduct(yAxis, crvTan);
+
+ //Set initial frame
+ Plane[] perpFrames = new Plane[pointsOnCurve.Count];
+ perpFrames[0] = new Plane(origin, xAxis, yAxis);
+
+ //Given boundary data(x0, t0; x1, t1) and an initial right-handed
+ //orthonormal frame U0 = (r0, s0, t0) at x0, the next frame U1 = (r1, s1, t1)
+ //at x1 for RMF approximation is computed by the double reflection method in
+ //the following two steps.
+ //
+ //Step 1.Let R1 denote the reflection in the bisecting plane of the points x0
+ //and x1(see Figure 4).Use R1 to map U0 to a left - handed orthonormal frame
+ //UL0 = (rL0, sL0, tL0).
+ //
+ //Step 2.Let R2 denote the reflection in the bisecting plane of the points x1 + tL
+ //0 and x1 +t1. Use R2 to map UL0 to a right - handed orthonormal frame U1 = (r1, s1, t1).
+ //Output U1.
+
+ for (int i = 0; i < pointsOnCurve.Count - 1; i++)
+ {
+ Vector3 v1 = pointsOnCurve[i + 1] - pointsOnCurve[i]; //compute reflection vector of R1
+ double c1 = v1 * v1;
+ Vector3 rLi = perpFrames[i].XAxis - (2 / c1) * (v1 * perpFrames[i].XAxis) * v1; //compute reflected rL vector by R1
+ Vector3 tLi = pointsOnCurveTan[i] - (2 / c1) * (v1 * pointsOnCurveTan[i]) * v1; //compute reflected tL vector by R1
+ Vector3 v2 = pointsOnCurveTan[i + 1] - tLi; //compute reflection vector of R2
+ double c2 = v2 * v2;
+ Vector3 rNext = rLi - (2 / c2) * (v2 * rLi) * v2; //compute reflected r vector by R2
+ var sNext = Vector3.CrossProduct(pointsOnCurveTan[i + 1], rNext); //compute vector s[i+1] of next frame
+
+ //create output frame
+ var frameNext = new Plane { Origin = pointsOnCurve[i + 1], XAxis = rNext, YAxis = sNext };
+ perpFrames[i + 1] = frameNext; //output frame
+ }
+
+ return perpFrames.ToList();
+ }
+
+ ///
+ /// Splits a curve into two parts at a given parameter.
+ ///
+ /// The parameter at which to split the curve.
+ /// Two NurbsCurve objects.
+ public List SplitAt(double t)
+ {
+ int degree = Degree;
+
+ List knotsToInsert = CollectionHelpers.RepeatData(t, degree + 1);
+
+ NurbsCurve refinedCurve = Modify.CurveKnotRefine(this, knotsToInsert);
+
+ int s = Knots.Span(degree, t);
+
+ KnotVector knots0 = refinedCurve.Knots.ToList().GetRange(0, s + degree + 2).ToKnot();
+ KnotVector knots1 = refinedCurve.Knots.GetRange(s + 1, refinedCurve.Knots.Count - (s + 1)).ToKnot();
+
+ List controlPoints0 = refinedCurve.ControlPoints.GetRange(0, s + 1);
+ List controlPoints1 = refinedCurve.ControlPoints.GetRange(s + 1, refinedCurve.ControlPointLocations.Count - (s + 1));
+
+ return new List { new NurbsCurve(degree, knots0, controlPoints0), new NurbsCurve(degree, knots1, controlPoints1) };
+ }
+
+ ///
+ /// Splits a curve at given parameters and returns the segments as curves.
+ ///
+ /// The parameters at which to split the curve. Values should be between 0.0 and 1.0.
+ /// Collection of curve segments.
+ public List SplitAt(double[] parameters)
+ {
+ var curves = new List();
+ if (parameters.Length == 0)
+ {
+ curves.Add(this);
+ return curves;
+ }
+
+ var sortedParameters = parameters.OrderBy(x => x).ToArray();
+ Interval curveDomain = Knots.GetDomain(this.Degree);
+
+ if (Math.Abs(sortedParameters[0] - curveDomain.T0) > GSharkMath.MaxTolerance)
+ {
+ var tempParams = new double[sortedParameters.Length + 1];
+ tempParams[0] = curveDomain.T0;
+ for (var i = 0; i < sortedParameters.Length; i++)
+ {
+ tempParams[i + 1] = sortedParameters[i];
+ }
+ sortedParameters = tempParams;
+ }
+
+ if (Math.Abs(sortedParameters[sortedParameters.Length - 1] - curveDomain.T1) > GSharkMath.MaxTolerance)
+ {
+ Array.Resize(ref sortedParameters, sortedParameters.Length + 1);
+ sortedParameters[sortedParameters.Length - 1] = curveDomain.T1;
+ }
+
+ for (int i = 0; i < sortedParameters.Length - 1; i++)
+ {
+ curves.Add(SubCurve(new Interval(sortedParameters[i], sortedParameters[i + 1])));
+ }
+
+ return curves;
+ }
+
+ ///
+ /// Extract sub-curve defined by domain.
+ ///
+ /// Domain of sub-curve
+ /// NurbsCurve.
+ public NurbsCurve SubCurve(Interval domain)
+ {
+ int degree = Degree;
+ int order = degree + 1;
+ Interval subCurveDomain = domain;
+
+ //NOTE: Handling decreasing domain by flipping it to maintain direction of original curve in sub-curve. Is this what we want?
+ if (domain.IsDecreasing)
+ {
+ subCurveDomain = new Interval(domain.T1, domain.T0);
+ }
+
+ var isT0AtStart = Math.Abs(subCurveDomain.T0 - Knots[0]) < GSharkMath.MaxTolerance;
+ var isT1AtEnd = Math.Abs(subCurveDomain.T1 - Knots[Knots.Count - 1]) < GSharkMath.MaxTolerance;
+
+ if (isT0AtStart && isT1AtEnd)
+ {
+ return this;
+ }
+
+ if (isT0AtStart || isT1AtEnd)
+ {
+ return isT0AtStart ? SplitAt(subCurveDomain.T1)[0] : SplitAt(subCurveDomain.T0)[1];
+ }
+
+ List knotsToInsert = CollectionHelpers.RepeatData(domain.T0, order).Concat(CollectionHelpers.RepeatData(domain.T1, degree + 1)).ToList();
+ NurbsCurve refinedCurve = Modify.CurveKnotRefine(this, knotsToInsert);
+
+ var subCurveControlPoints = refinedCurve.ControlPoints.GetRange(order, order);
+ var subCrvCrtlPtsLocations = subCurveControlPoints.Select(Point4.PointDehomogenizer).ToList();
+
+ var subCurve = new NurbsCurve(subCrvCrtlPtsLocations, Degree);
+
+ return subCurve;
+ }
+
///
/// Compares two curves for equality.
/// Two NURBS curves are equal when the have same control points, weights, knots and degree.
diff --git a/src/GShark/Geometry/NurbsSurface.cs b/src/GShark/Geometry/NurbsSurface.cs
index 7381df39..1304983b 100644
--- a/src/GShark/Geometry/NurbsSurface.cs
+++ b/src/GShark/Geometry/NurbsSurface.cs
@@ -1,13 +1,13 @@
using GShark.Core;
+using GShark.Enumerations;
using GShark.ExtendedMethods;
+using GShark.Interfaces;
using GShark.Operation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
-using GShark.Enumerations;
-using GShark.Interfaces;
namespace GShark.Geometry
{
@@ -188,7 +188,8 @@ public static NurbsSurface CreateLoftedSurface(IList curves, LoftTyp
// In fact, the ruled surface is a special case of a skinned surface.
if (copyCurves.Any(c => c.Degree != copyCurves[0].Degree))
{
- copyCurves = HomogenizedCurves(copyCurves);
+ copyCurves = CurveHelpers.NormalizedDegree(copyCurves);
+ copyCurves = CurveHelpers.NormalizedKnots(copyCurves);
}
int degreeV = copyCurves[0].Degree;
@@ -228,8 +229,9 @@ public static NurbsSurface CreateLoftedSurface(IList curves, LoftTyp
/// A ruled surface.
public static NurbsSurface CreateRuledSurface(NurbsCurve curveA, NurbsCurve curveB)
{
- IList curves = new []{curveA, curveB};
- curves = HomogenizedCurves(curves);
+ IList curves = new[] { curveA, curveB };
+ curves = CurveHelpers.NormalizedDegree(curves);
+ curves = CurveHelpers.NormalizedKnots(curves);
return new NurbsSurface(1, curves[0].Degree, new KnotVector(1, 2), curves[0].Knots,
new List> { curves[0].ControlPoints, curves[1].ControlPoints });
@@ -280,6 +282,23 @@ public Vector3 EvaluateAt(double u, double v, EvaluateSurfaceDirection direction
return normal.Unitize();
}
+ ///
+ /// Splits (divides) the surface into two parts at the specified parameter
+ ///
+ /// The parameter at which to split the surface, parameter should be between 0 and 1.
+ /// Where to split in the U or V direction of the surface.
+ /// 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.
+ /// 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.
+ public NurbsSurface[] Split(double parameter, SplitDirection direction)
+ {
+ if (parameter < 0.0 || parameter > 1.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(parameter), "The parameter is not into the domain 0.0 to 1.0.");
+ }
+
+ return Divide.SplitSurface(this, parameter, direction);
+ }
+
///
/// Transforms a NURBS surface with the given transformation matrix.
///
@@ -346,36 +365,5 @@ public override string ToString()
return stringBuilder.ToString();
}
-
- ///
- /// The curves are brought to a common degree and knots.
- ///
- private static IList HomogenizedCurves(IList copyCurves)
- {
- // Unify knots, normalized them.
- copyCurves = copyCurves
- .Select(curve => curve.Knots.GetDomain(curve.Degree).Length > 1
- ? new NurbsCurve(curve.Degree, curve.Knots.Normalize(), curve.ControlPoints)
- : curve).ToList();
-
- // Unify curves by degree.
- int targetDegree = copyCurves.Max(c => c.Degree);
- copyCurves = copyCurves
- .Select(curve => curve.Degree != targetDegree
- ? Modify.ElevateDegree(curve, targetDegree)
- : curve).ToList();
-
- // Unify curves by knots.
- KnotVector combinedKnots = copyCurves.First().Knots.Copy();
- foreach (NurbsCurve curve in copyCurves.Skip(1))
- {
- combinedKnots.AddRange(curve.Knots.Where(k => !combinedKnots.Contains(k)).ToList());
- }
-
- copyCurves = (from curve in copyCurves
- let knotToInsert = combinedKnots.OrderBy(k => k).Where(k => !curve.Knots.Contains(k)).ToKnot()
- select Modify.CurveKnotRefine(curve, knotToInsert)).ToList();
- return copyCurves;
- }
}
}
\ No newline at end of file