/// <summary> /// Find Curve Segment At a given Length. /// When there is no curve segments in the PolyCurve, return null; /// If the provided length is greater than the PolyCurve Length, then it return the last segment in PolyCurve; /// </summary> /// <param name="length">Segment Length</param> public NurbsBase SegmentAtLength(double length) { NurbsBase segment = null; if (length > Length) { return(_segments.Last()); } if (_segments.Count == 1) { return(_segments.First()); } double temp = 0; foreach (NurbsBase curve in _segments) { double cumulativeLength = curve.Length + temp; if (length >= temp && length < cumulativeLength) { segment = curve; break; } temp = cumulativeLength; } return(segment); }
/// <summary> /// Constructs a ruled surface between two curves. /// <em>Follows the algorithm at page 337 of The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <param name="curveA">The first curve.</param> /// <param name="curveB">The second curve.</param> /// <returns>A ruled surface.</returns> public static NurbsSurface Ruled(NurbsBase curveA, NurbsBase curveB) { IList <NurbsBase> 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 <List <Point4> > { curves[0].ControlPoints, curves[1].ControlPoints })); }
/// <summary> /// Constructs a surface extruding a curve profile long a direction. /// </summary> /// <param name="direction">The extrusion direction.</param> /// <param name="profile">The profile curve to extrude.</param> /// <returns>The extruded surface.</returns> public static NurbsSurface FromExtrusion(Vector3 direction, NurbsBase profile) { Transform xForm = Core.Transform.Translation(direction); List <Point4> 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 <List <Point4> > { profile.ControlPoints, translatedControlPts })); }
/// <summary> /// Constructs a sweep surface with one rail curve. /// <em>Follows the algorithm A10.2 at page 477 of The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <param name="rail">The rail curve.</param> /// <param name="profile">The section curve.</param> /// <returns>The sweep surface.</returns> public static NurbsSurface FromSweep(NurbsBase rail, NurbsBase profile) { var(tValues, _) = Sampling.Curve.AdaptiveSample(rail, GSharkMath.MaxTolerance); List <Plane> frames = rail.PerpendicularFrames(tValues); List <NurbsBase> curves = new List <NurbsBase> { 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)); }
/// <summary> /// Checks to define if the curve can be appended. /// </summary> private void HealthChecks(NurbsBase curve, NurbsBase curveToAppend) { if (_segments.Count <= 0) { return; } if (curve.IsClosed) { throw new InvalidOperationException($"The polyCurve is closed can not be possible to connect the {curveToAppend.GetType()}."); } if (curve.EndPoint.DistanceTo(curveToAppend.StartPoint) > GSharkMath.Epsilon) { throw new InvalidOperationException("The two curves can not be connected."); } }
/// <summary> /// Creates a surface of revolution through an arbitrary angle, and axis. /// <em>Corresponds the algorithm A8.1 of The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <param name="curveProfile">Profile curve.</param> /// <param name="axis">Revolution axis.</param> /// <param name="rotationAngle">Angle in radiance.</param> /// <returns>The revolution surface.</returns> public static NurbsSurface Revolved(NurbsBase curveProfile, Ray axis, double rotationAngle) { // if angle is less than 90. int arcCount = 1; KnotVector knotsU = Vector.Zero1d(6).ToKnot(); if (rotationAngle <= Math.PI && rotationAngle > (Math.PI / 2)) { arcCount = 2; knotsU[3] = knotsU[4] = 0.5; } if (rotationAngle <= (3 * Math.PI / 2) && rotationAngle > Math.PI) { arcCount = 3; knotsU = Vector.Zero1d(6 + 2 * (arcCount - 1)).ToKnot(); knotsU[3] = knotsU[4] = (double)1 / 3; knotsU[5] = knotsU[6] = (double)2 / 3; } if (rotationAngle <= (4 * Math.PI) && rotationAngle > (3 * Math.PI / 2)) { arcCount = 4; knotsU = Vector.Zero1d(6 + 2 * (arcCount - 1)).ToKnot(); knotsU[3] = knotsU[4] = (double)1 / 4; knotsU[5] = knotsU[6] = (double)1 / 2; knotsU[7] = knotsU[8] = (double)3 / 4; } // load start and end knots. int t = 3 + 2 * (arcCount - 1); for (int i = 0; i < 3; i++, t++) { knotsU[i] = 0.0; knotsU[t] = 1.0; } // some initialization. double divideAngle = rotationAngle / arcCount; int n = 2 * arcCount; double wm = divideAngle / 2; // is the base angle. // initialize the sines and cosines only once. double angle = 0.0; double[] sines = new double[arcCount + 1]; double[] cosines = new double[arcCount + 1]; for (int i = 1; i <= arcCount; i++) { angle += divideAngle; sines[i] = Math.Sin(angle); cosines[i] = Math.Cos(angle); } // loop and compute each u row of control points and weights. List <List <Point4> > controlPts = new List <List <Point4> >(); for (int r = 0; r < 2 * arcCount + 1; r++) { List <Point4> temp = CollectionHelpers.RepeatData(Point4.Zero, curveProfile.ControlPoints.Count); controlPts.Add(temp); } for (int j = 0; j < curveProfile.ControlPointLocations.Count; j++) { Point3 ptO = axis.ClosestPoint(curveProfile.ControlPointLocations[j]); Vector3 vectorX = curveProfile.ControlPointLocations[j] - ptO; double radius = vectorX.Length; // the radius at that length. Vector3 vectorY = Vector3.CrossProduct(axis.Direction, vectorX); if (radius > GSharkMath.Epsilon) { vectorX *= (1 / radius); vectorY *= (1 / radius); } // initialize the first control points and weights. Point3 pt0 = curveProfile.ControlPointLocations[j]; controlPts[0][j] = new Point4(pt0, curveProfile.Weights[j]); Vector3 tangent0 = vectorY; int index = 0; for (int i = 1; i <= arcCount; i++) { // rotated generatrix point. Point3 pt2 = (Math.Abs(radius) < GSharkMath.Epsilon) ? ptO : ptO + (vectorX * (cosines[i] * radius) + vectorY * (sines[i] * radius)); controlPts[index + 2][j] = new Point4(pt2, curveProfile.Weights[j]); // construct the vector tangent to the rotation. Vector3 rotationTangent = vectorX * (-1 * sines[i]) + vectorY * cosines[i]; // construct the next control point. if (Math.Abs(radius) < GSharkMath.Epsilon) { controlPts[index + 1][j] = ptO; } else { Line ln0 = new Line(pt0, tangent0, tangent0.Length); Line ln1 = new Line(pt2, rotationTangent, rotationTangent.Length); Intersection.Intersect.LineLine(ln0, ln1, out Point3 intersectionPt, out _, out _, out _); controlPts[index + 1][j] = new Point4(intersectionPt, wm * curveProfile.Weights[j]); } index += 2; if (i >= arcCount) { continue; } pt0 = pt2; tangent0 = rotationTangent; } } return(new NurbsSurface(2, curveProfile.Degree, knotsU, curveProfile.Knots, controlPts.Select(pts => pts.ToList()).ToList())); }
/// <summary> /// Constructs a NURBS surface from a set of NURBS curves.<br/> /// </summary> /// <param name="curves">Set of a minimum of two curves to create the surface.</param> /// <param name="loftType">Enum to choose the type of loft generation.</param> /// <returns>A NURBS surface.</returns> public static NurbsSurface FromLoft(IList <NurbsBase> curves, LoftType loftType = LoftType.Normal) { if (curves == null) { throw new ArgumentException("An invalid number of curves to perform the loft."); } if (curves.Count < 2) { throw new ArgumentException("An invalid number of curves to perform the loft."); } if (curves.Any(x => x == null)) { throw new ArgumentException("The input set contains null curves."); } bool isClosed = curves[0].IsClosed; foreach (NurbsBase c in curves.Skip(1)) { if (isClosed != c.IsClosed) { throw new ArgumentException("Loft only works if all curves are open, or all curves are closed."); } } // Copy curves for possible operation of homogenization. IList <NurbsBase> copyCurves = new List <NurbsBase>(curves); // Clamp curves if periodic. if (copyCurves[0].IsPeriodic) { for (int i = 0; i < copyCurves.Count; i++) { copyCurves[i] = copyCurves[i].ClampEnds(); } } // If necessary, the curves can be brought to a common degree and knots, as we do for the ruled surface. // In fact, the ruled surface is a special case of a skinned surface. if (copyCurves.Any(c => c.Degree != copyCurves[0].Degree)) { copyCurves = CurveHelpers.NormalizedDegree(copyCurves); copyCurves = CurveHelpers.NormalizedKnots(copyCurves); } int degreeV = copyCurves[0].Degree; int degreeU = 3; KnotVector knotVectorU = new KnotVector(); KnotVector knotVectorV = copyCurves[0].Knots; List <List <Point4> > surfaceControlPoints = new List <List <Point4> >(); switch (loftType) { case LoftType.Normal: List <List <Point4> > tempPts = new List <List <Point4> >(); for (int n = 0; n < copyCurves[0].ControlPointLocations.Count; n++) { List <Point3> pts = copyCurves.Select(c => c.ControlPointLocations[n]).ToList(); NurbsBase crv = Fitting.Curve.Interpolated(pts, degreeU); tempPts.Add(crv.ControlPoints); knotVectorU = crv.Knots; } surfaceControlPoints = CollectionHelpers.Transpose2DArray(tempPts); break; case LoftType.Loose: surfaceControlPoints = copyCurves.Select(c => c.ControlPoints).ToList(); knotVectorU = new KnotVector(degreeU, copyCurves.Count); break; } return(new NurbsSurface(degreeU, degreeV, knotVectorU, knotVectorV, surfaceControlPoints)); }