diff --git a/src/GShark.Test.XUnit/Data/NurbsSurfaceCollection.cs b/src/GShark.Test.XUnit/Data/NurbsSurfaceCollection.cs index b16ac282..44f04767 100644 --- a/src/GShark.Test.XUnit/Data/NurbsSurfaceCollection.cs +++ b/src/GShark.Test.XUnit/Data/NurbsSurfaceCollection.cs @@ -87,7 +87,7 @@ public static NurbsSurface Loft() NurbsCurve crv2 = new NurbsCurve(pts3, 3); List crvs = new List { ln, crv0, poly, crv1, crv2 }; - return NurbsSurface.Lofted(crvs); + return NurbsSurface.FromLoft(crvs); } } } diff --git a/src/GShark.Test.XUnit/DebugFiles/GHDebug_Surface.gh b/src/GShark.Test.XUnit/DebugFiles/GHDebug_Surface.gh index 92462609..fc579eb1 100644 Binary files a/src/GShark.Test.XUnit/DebugFiles/GHDebug_Surface.gh and b/src/GShark.Test.XUnit/DebugFiles/GHDebug_Surface.gh differ diff --git a/src/GShark.Test.XUnit/Geometry/NurbsSurfaceTests.cs b/src/GShark.Test.XUnit/Geometry/NurbsSurfaceTests.cs index bdd610f5..c44d071f 100644 --- a/src/GShark.Test.XUnit/Geometry/NurbsSurfaceTests.cs +++ b/src/GShark.Test.XUnit/Geometry/NurbsSurfaceTests.cs @@ -88,7 +88,7 @@ public void It_Returns_A_Normal_Lofted_Surface_By_Opened_Curves(double u, double Point3 expectedPt = new Point3(pt[0], pt[1], pt[2]); // Act - NurbsSurface surface = NurbsSurface.Lofted(NurbsCurveCollection.OpenCurves()); + NurbsSurface surface = NurbsSurface.FromLoft(NurbsCurveCollection.OpenCurves()); Point3 evalPt = surface.PointAt(u, v); // Assert @@ -106,7 +106,7 @@ public void It_Returns_A_Loose_Lofted_Surface_By_Opened_Curves(double u, double Point3 expectedPt = new Point3(pt[0], pt[1], pt[2]); // Act - NurbsSurface surface = NurbsSurface.Lofted(NurbsCurveCollection.OpenCurves(), LoftType.Loose); + NurbsSurface surface = NurbsSurface.FromLoft(NurbsCurveCollection.OpenCurves(), LoftType.Loose); Point3 evalPt = surface.PointAt(u, v); // Assert @@ -124,7 +124,7 @@ public void It_Returns_A_Loose_Lofted_Surface_By_Closed_Curves(double u, double Point3 expectedPt = new Point3(pt[0], pt[1], pt[2]); // Act - NurbsSurface surface = NurbsSurface.Lofted(NurbsCurveCollection.ClosedCurves(), LoftType.Loose); + NurbsSurface surface = NurbsSurface.FromLoft(NurbsCurveCollection.ClosedCurves(), LoftType.Loose); Point3 evalPt = surface.PointAt(u, v); // Assert @@ -136,7 +136,7 @@ public void It_Returns_A_Loose_Lofted_Surface_By_Closed_Curves(double u, double public void Lofted_Surface_Throws_An_Exception_If_The_Curves_Are_Null() { // Act - Func func = () => NurbsSurface.Lofted(null); + Func func = () => NurbsSurface.FromLoft(null); // Assert func.Should().Throw() @@ -151,7 +151,7 @@ public void Lofted_Surface_Throws_An_Exception_If_There_Are_Null_Curves() crvs.Add(null); // Act - Func func = () => NurbsSurface.Lofted(crvs); + Func func = () => NurbsSurface.FromLoft(crvs); // Assert func.Should().Throw() @@ -165,7 +165,7 @@ public void Lofted_Surface_Throws_An_Exception_If_Curves_Count_Are_Less_Than_Two NurbsBase[] crvs = { NurbsCurveCollection.OpenCurves()[0] }; // Act - Func func = () => NurbsSurface.Lofted(crvs); + Func func = () => NurbsSurface.FromLoft(crvs); // Assert func.Should().Throw() @@ -180,7 +180,7 @@ public void Lofted_Surface_Throws_An_Exception_If_The_All_Curves_Are_Not_Closed_ crvs[1] = crvs[1].Close(); // Act - Func func = () => NurbsSurface.Lofted(crvs); + Func func = () => NurbsSurface.FromLoft(crvs); // Assert func.Should().Throw() @@ -221,7 +221,7 @@ public void Returns_True_If_Two_Surfaces_Are_Equals() public void Returns_True_If_Surface_Is_Close() { // Act - NurbsSurface surface = NurbsSurface.Lofted(NurbsCurveCollection.ClosedCurves(), LoftType.Loose); + NurbsSurface surface = NurbsSurface.FromLoft(NurbsCurveCollection.ClosedCurves(), LoftType.Loose); // Assert surface.IsClosed(SurfaceDirection.V).Should().BeTrue(); @@ -381,12 +381,12 @@ public void It_Returns_A_Revolved_Surface_From_A_Line() // Assert revolvedSurface0.ControlPointLocations - .Zip(expectedPts0, (pt0, pt1) => pt0.SequenceEqual(pt1)) + .Zip(expectedPts0, (ptsA, ptsB) => ptsA.SequenceEqual(ptsB)) .All(res => res) .Should().BeTrue(); revolvedSurface1.ControlPointLocations - .Zip(expectedPts1, (pt0, pt1) => pt0.SequenceEqual(pt1)) + .Zip(expectedPts1, (ptsA, ptsB) => ptsA.SequenceEqual(ptsB)) .All(res => res) .Should().BeTrue(); } @@ -425,9 +425,73 @@ public void It_Returns_A_Revolved_Surface_From_An_Arc() // Assert revolvedSurface.ControlPointLocations - .Zip(expectedPts, (pt0, pt1) => pt0.SequenceEqual(pt1)) + .Zip(expectedPts, (ptsA, ptsB) => ptsA.SequenceEqual(ptsB)) .All(res => res) .Should().BeTrue(); } + + [Fact] + public void It_Creates_A_NurbsSurface_From_Extrusion() + { + // Arrange + List ptsA = new List + { + new Point3(0, 0, 0), + new Point3(0, 0, 5), + new Point3(5, 0, 5), + new Point3(5, 0, 0), + new Point3(10, 0, 0) + }; + List ptsB = new List + { + new Point3(0, 10, 0), + new Point3(0, 10, 5), + new Point3(5, 10, 5), + new Point3(5, 10, 0), + new Point3(10, 10, 0) + }; + NurbsCurve curve = new NurbsCurve(ptsA, 3); + Vector3 direction = Vector3.YAxis * 10; + List> expectedPts = new List> {ptsA, ptsB}; + + // Act + NurbsSurface extrudedSurface = NurbsSurface.FromExtrusion(direction, curve); + + // Assert + extrudedSurface.ControlPointLocations + .Zip(expectedPts, (ptsA, ptsB) => ptsA.SequenceEqual(ptsB)) + .All(res => res) + .Should().BeTrue(); + extrudedSurface.DegreeU.Should().Be(1); + extrudedSurface.DegreeV.Should().Be(curve.Degree); + } + + [Fact] + public void It_Creates_A_NurbsSurface_From_Swep() + { + // Arrange + List ptsA = new List + { + new Point3(5, 0, 0), + new Point3(5, 5, 0), + new Point3(0, 5, 0), + new Point3(0, 5, 5), + new Point3(5, 5, 5) + }; + List ptsB = new List + { + new Point3(4, 0, 1), + new Point3(5, 0, 0), + new Point3(6, 0, 1) + }; + NurbsCurve rail = new NurbsCurve(ptsA, 3); + NurbsCurve profile = new NurbsCurve(ptsB, 2); + + // Act + NurbsSurface sweepSurface = NurbsSurface.FromSweep(rail, profile); + + // Assert + (sweepSurface.ControlPointLocations.Last()[1] == ptsA.Last()).Should().BeTrue(); + } } } \ No newline at end of file diff --git a/src/GShark/Geometry/NurbsSurface.cs b/src/GShark/Geometry/NurbsSurface.cs index 7b178420..d99b7b04 100644 --- a/src/GShark/Geometry/NurbsSurface.cs +++ b/src/GShark/Geometry/NurbsSurface.cs @@ -1,11 +1,11 @@ -using GShark.Core; -using GShark.Enumerations; -using GShark.ExtendedMethods; -using GShark.Interfaces; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; +using GShark.Core; +using GShark.Enumerations; +using GShark.ExtendedMethods; +using GShark.Interfaces; namespace GShark.Geometry { @@ -154,7 +154,7 @@ public static NurbsSurface FromPoints(int degreeU, int degreeV, ListSet of a minimum of two curves to create the surface. /// Enum to choose the type of loft generation. /// A NURBS surface. - public static NurbsSurface Lofted(IList curves, LoftType loftType = LoftType.Normal) + public static NurbsSurface FromLoft(IList curves, LoftType loftType = LoftType.Normal) { if (curves == null) throw new ArgumentException("An invalid number of curves to perform the loft."); @@ -218,6 +218,44 @@ public static NurbsSurface Lofted(IList curves, LoftType loftType = L return new NurbsSurface(degreeU, degreeV, knotVectorU, knotVectorV, surfaceControlPoints); } + /// + /// Constructs a surface extruding a curve profile long a direction. + /// + /// The extrusion direction. + /// The profile curve to extrude. + /// The extruded surface. + public static NurbsSurface FromExtrusion(Vector3 direction, NurbsBase profile) + { + Transform xForm = Core.Transform.Translation(direction); + List translatedControlPts = + profile.ControlPoints.Select(controlPoint => controlPoint.Transform(xForm)).ToList(); + + return new NurbsSurface(1, profile.Degree, new KnotVector { 0, 0, 1, 1 }, profile.Knots, + new List> { profile.ControlPoints, translatedControlPts }); + } + + /// + /// Constructs a sweep surface with one rail curve. + /// Follows the algorithm A10.2 at page 477 of The NURBS Book by Piegl and Tiller. + /// + /// The rail curve. + /// The section curve. + /// The sweep surface. + public static NurbsSurface FromSweep(NurbsBase rail, NurbsBase profile) + { + var (tValues, _) = Sampling.Curve.AdaptiveSample(rail, GSharkMath.MaxTolerance); + List frames = rail.PerpendicularFrames(tValues); + List curves = new List {profile}; + + for (int i = 1; i <= frames.Count; i++) + { + Transform xForm = Core.Transform.PlaneToPlane(frames[0], frames[i]); + curves.Add(((NurbsCurve)curves[0]).Transform(xForm)); + } + + return FromLoft(curves); + } + /// /// Constructs a ruled surface between two curves. /// Follows the algorithm at page 337 of The NURBS Book by Piegl and Tiller. @@ -356,7 +394,7 @@ public static NurbsSurface Revolved(NurbsBase curveProfile, Ray axis, double rot } } - + return new NurbsSurface(2, curveProfile.Degree, knotsU, curveProfile.Knots, controlPts.Select(pts => pts.ToList()).ToList()); } diff --git a/src/GShark/Sampling/Curve.cs b/src/GShark/Sampling/Curve.cs index 20a6b2d3..e03a36a4 100644 --- a/src/GShark/Sampling/Curve.cs +++ b/src/GShark/Sampling/Curve.cs @@ -3,7 +3,6 @@ using System.Linq; using GShark.Core; using GShark.Geometry; -using GShark.Operation; namespace GShark.Sampling {