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