public virtual Point3 ClosestPoint(Point3 point) { double t = Analyze.Curve.ClosestParameter(this, point); Point3 pointAt = Evaluate.Curve.PointAt(this, t); return(Point4.PointDehomogenizer(pointAt)); }
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> /// Initializes a new point by copying coordinates from a four-dimensional point. /// The first three coordinates are divided by the last one. /// If the W (fourth) dimension of the input point is zero, then it will be discarded. /// </summary> /// <param name="point">A point.</param> public Point3(Point4 point) { double w = (Math.Abs(point.W - 1.0) > GSharkMath.Epsilon && point.W != 0.0) ? 1.0 / point.W : 1.0; X = point.X * w; Y = point.Y * w; Z = point.Z * w; }
/// <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()); }
/// <summary> /// Defines the NURBS form of the polyline. /// </summary> private void ToNurbsForm() { if (_segments.Count == 1) { Weights = Point4.GetWeights(_segments[0].ControlPoints); Degree = _segments[0].Degree; Knots = _segments[0].Knots; ControlPointLocations = Point4.PointDehomogenizer1d(_segments[0].ControlPoints); ControlPoints = _segments[0].ControlPoints; return; } // Extract the biggest degree between the curves. int finalDegree = _segments.Max(c => c.Degree); // Homogenized degree curves. IEnumerable <NurbsBase> homogenizedCurves = _segments.Select(curve => curve.Degree != finalDegree ? Modify.Curve.ElevateDegree(curve, finalDegree) : curve); // Join curves. List <double> joinedKnots = new List <double>(); List <Point4> joinedControlPts = new List <Point4>(); joinedKnots.AddRange(homogenizedCurves.First().Knots.Take(homogenizedCurves.First().Knots.Count - 1)); joinedControlPts.AddRange(homogenizedCurves.First().ControlPoints); foreach (NurbsBase curve in homogenizedCurves.Skip(1)) { joinedKnots.AddRange(curve.Knots.Take(curve.Knots.Count - 1).Skip(finalDegree + 1).Select(k => k + joinedKnots.Last()).ToList()); joinedControlPts.AddRange(curve.ControlPoints.Skip(1)); } // Appending the last knot to the end. joinedKnots.Add(joinedKnots.Last()); Weights = Point4.GetWeights(joinedControlPts); Degree = finalDegree; Knots = joinedKnots.ToKnot().Normalize(); ControlPointLocations = Point4.PointDehomogenizer1d(joinedControlPts); ControlPoints = joinedControlPts; }
/// <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 curve representation of this arc.<br/> /// <em>Implementation of Algorithm A7.1 from The NURBS Book by Piegl and Tiller.</em> /// </summary> /// <returns>A nurbs curve shaped like this arc.</returns> internal void ToNurbs() { Vector3 axisX = Plane.XAxis; Vector3 axisY = Plane.YAxis; double curveAngle = _domain.Length; int numberOfArc; Point3[] pts; Point4[] ctrPts; double[] weights; // Number of arcs. double piNum = 0.5 * Math.PI; if ((curveAngle - piNum) <= GSharkMath.Epsilon) { numberOfArc = 1; pts = new Point3[3]; ctrPts = new Point4[3]; weights = new double[3]; } else if ((curveAngle - piNum * 2) <= GSharkMath.Epsilon) { numberOfArc = 2; pts = new Point3[5]; ctrPts = new Point4[5]; weights = new double[5]; } else if ((curveAngle - piNum * 3) <= GSharkMath.Epsilon) { numberOfArc = 3; pts = new Point3[7]; ctrPts = new Point4[7]; weights = new double[7]; } else { numberOfArc = 4; pts = new Point3[9]; ctrPts = new Point4[9]; weights = new double[9]; } double detTheta = curveAngle / numberOfArc; double weight = Math.Cos(detTheta / 2); Point3 p0 = Center + (axisX * (Radius * Math.Cos(_domain.T0)) + axisY * (Radius * Math.Sin(_domain.T0))); Vector3 t0 = axisY * Math.Cos(_domain.T0) - axisX * Math.Sin(_domain.T0); KnotVector knots = new KnotVector(CollectionHelpers.RepeatData(0.0, ctrPts.Length + 3)); int index = 0; double angle = _domain.T0; pts[0] = p0; ctrPts[0] = new Point4(p0); weights[0] = weight; for (int i = 1; i < numberOfArc + 1; i++) { angle += detTheta; Point3 p2 = Center + (axisX * (Radius * Math.Cos(angle)) + axisY * (Radius * Math.Sin(angle))); ctrPts[index + 2] = new Point4(p2); pts[index + 2] = p2; weights[index + 2] = 1.0; Vector3 t2 = (axisY * Math.Cos(angle)) - (axisX * Math.Sin(angle)); Line ln0 = new Line(p0, t0.Unitize() + p0); Line ln1 = new Line(p2, t2.Unitize() + p2); Intersect.LineLine(ln0, ln1, out _, out _, out double u0, out _); Point3 p1 = p0 + (t0 * u0); ctrPts[index + 1] = new Point4(p1, weight); pts[index + 1] = p1; weights[index + 1] = weight; index += 2; if (i >= numberOfArc) { continue; } p0 = p2; t0 = t2; } int j = 2 * numberOfArc + 1; for (int i = 0; i < 3; i++) { knots[i] = 0.0; knots[i + j] = 1.0; } switch (numberOfArc) { case 2: knots[3] = knots[4] = 0.5; break; case 3: knots[3] = knots[4] = (double)1 / 3; knots[5] = knots[6] = (double)2 / 3; break; case 4: knots[3] = knots[4] = 0.25; knots[5] = knots[6] = 0.5; knots[7] = knots[8] = 0.75; break; } Weights = weights.ToList(); Degree = 2; Knots = knots; ControlPoints = ctrPts.ToList(); ControlPointLocations = pts.ToList(); }