public void It_Returns_The_Closest_Point() { // Arrange Ray ray = new Ray(new Point3(0, 0, 0), new Vector3(30, 45, 0)); var pt = new Point3(10, 20, 0); var expectedPt = new Point3(12.30769230769231, 18.461538461538463, 0); // Act var closestPt = ray.ClosestPoint(pt); // Assert closestPt.Should().BeEquivalentTo(expectedPt); }
/// <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())); }