コード例 #1
0
        /// <summary>
        /// Determines the derivatives of a curve at a given parameter.<br/>
        /// <em>Corresponds to algorithm 3.2 from The NURBS Book by Piegl and Tiller.</em>
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="parameter">Parameter on the curve at which the point is to be evaluated.</param>
        /// <param name="numberDerivs">Integer number of basis functions - 1 = knots.length - degree - 2.</param>
        /// <returns>The derivatives.</returns>
        internal static List <Point4> CurveDerivatives(NurbsBase curve, double parameter, int numberDerivs)
        {
            List <Point4> curveHomogenizedPoints = curve.ControlPoints;

            int n             = curve.Knots.Count - curve.Degree - 2;
            int derivateOrder = numberDerivs < curve.Degree ? numberDerivs : curve.Degree;

            Point4[]      ck        = new Point4[numberDerivs + 1];
            int           knotSpan  = curve.Knots.Span(n, curve.Degree, parameter);
            List <Vector> derived2d = DerivativeBasisFunctionsGivenNI(knotSpan, parameter, curve.Degree, derivateOrder, curve.Knots);

            for (int k = 0; k < derivateOrder + 1; k++)
            {
                for (int j = 0; j < curve.Degree + 1; j++)
                {
                    double valToMultiply = derived2d[k][j];
                    Point4 pt            = curveHomogenizedPoints[knotSpan - curve.Degree + j];
                    for (int i = 0; i < pt.Size; i++)
                    {
                        ck[k][i] = ck[k][i] + (valToMultiply * pt[i]);
                    }
                }
            }
            return(ck.ToList());
        }
コード例 #2
0
        public void It_Returns_A_Circle3D_With_Its_Nurbs_Representation()
        {
            // Arrange
            var ptsExpected = new List <Point3>
            {
                new Point3(74.264416, 36.39316, -1.884313),
                new Point3(62.298962, 25.460683, 1.083287),
                new Point3(73.626287, 13.863582, 4.032316),
                new Point3(84.953611, 2.266482, 6.981346),
                new Point3(96.919065, 13.198959, 4.013746),
                new Point3(108.884519, 24.131437, 1.046146),
                new Point3(97.557194, 35.728537, -1.902883),
                new Point3(86.22987, 47.325637, -4.851913),
                new Point3(74.264416, 36.39316, -1.88431)
            };

            // Act
            NurbsBase circleNurbs = _circle3D;

            // Assert
            circleNurbs.Knots.GetDomain(circleNurbs.Degree).Length.Should().Be(1.0);
            for (int ptIndex = 0; ptIndex < ptsExpected.Count; ptIndex++)
            {
                circleNurbs.ControlPointLocations[ptIndex].EpsilonEquals(ptsExpected[ptIndex], GSharkMath.MaxTolerance);
            }
        }
コード例 #3
0
        /// <summary>
        /// Approximates the parameter at a given length on a curve.
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="segmentLength">The arc length for which to do the procedure.</param>
        /// <param name="tolerance">If set less or equal 0.0, the tolerance used is 1e-10.</param>
        /// <returns>The parameter on the curve.</returns>
        internal static double ParameterAtLength(NurbsBase curve, double segmentLength, double tolerance = -1)
        {
            if (segmentLength < GSharkMath.Epsilon)
            {
                return(curve.Knots[0]);
            }
            if (Math.Abs(curve.Length - segmentLength) < GSharkMath.Epsilon)
            {
                return(curve.Knots[curve.Knots.Count - 1]);
            }

            List <NurbsBase> curves = Modify.Curve.DecomposeIntoBeziers(curve);
            int    i                 = 0;
            double curveLength       = -GSharkMath.Epsilon;
            double segmentLengthLeft = segmentLength;

            // Iterate through the curves consuming the bezier's, summing their length along the way.
            while (curveLength < segmentLength && i < curves.Count)
            {
                double bezierLength = BezierLength(curves[i]);
                curveLength += bezierLength;

                if (segmentLength < curveLength + GSharkMath.Epsilon)
                {
                    return(BezierParameterAtLength(curves[i], segmentLengthLeft, tolerance));
                }
                i++;
                segmentLengthLeft -= bezierLength;
            }

            return(-1);
        }
コード例 #4
0
        public void It_Refines_The_Curve_Knot(double val, int insertion)
        {
            // Arrange
            int degree = 3;

            List <double> newKnots = new List <double>();

            for (int i = 0; i < insertion; i++)
            {
                newKnots.Add(val);
            }

            List <Point3> pts = new List <Point3>();

            for (int i = 0; i <= 12 - degree - 2; i++)
            {
                pts.Add(new Point3(i, 0.0, 0.0));
            }

            NurbsCurve curve = new NurbsCurve(pts, degree);

            // Act
            NurbsBase curveAfterRefine = KnotVector.Refine(curve, newKnots);
            Point3    p0 = curve.PointAt(2.5);
            Point3    p1 = curveAfterRefine.PointAt(2.5);

            // Assert
            (curve.Knots.Count + insertion).Should().Be(curveAfterRefine.Knots.Count);
            (pts.Count + insertion).Should().Be(curveAfterRefine.ControlPointLocations.Count);
            (p0 == p1).Should().BeTrue();
        }
コード例 #5
0
        public void It_Returns_A_Curve_Where_Degree_Is_Reduced_From_5_To_4()
        {
            // Arrange
            // Followed example Under C1 constrain condition https://www.hindawi.com/journals/mpe/2016/8140427/tab1/
            List <Point3> pts = new List <Point3>
            {
                new Point3(-5.0, 0.0, 0.0),
                new Point3(-7.0, 2.0, 0.0),
                new Point3(-3.0, 5.0, 0.0),
                new Point3(2.0, 6.0, 0.0),
                new Point3(5.0, 3.0, 0.0),
                new Point3(3.0, 0.0, 0.0)
            };
            int        degree     = 5;
            double     tolerance  = 10e-2;
            NurbsCurve curve      = new NurbsCurve(pts, degree);
            Point3     ptOnCurve0 = curve.PointAt(0.5);
            Point3     ptOnCurve1 = curve.PointAt(0.25);

            // Act
            NurbsBase reducedCurve            = curve.ReduceDegree(tolerance);
            Point3    ptOnReducedDegreeCurve0 = reducedCurve.PointAt(0.5);
            Point3    ptOnReducedDegreeCurve1 = reducedCurve.PointAt(0.25);

            // Assert
            reducedCurve.Degree.Should().Be(degree - 1);

            ptOnCurve0.DistanceTo(ptOnReducedDegreeCurve0).Should().BeLessThan(GSharkMath.MinTolerance);
            ptOnCurve1.DistanceTo(ptOnReducedDegreeCurve1).Should().BeLessThan(tolerance);
        }
コード例 #6
0
        /// <summary>
        /// Samples a curve at equally spaced parametric intervals.
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="numSamples">Number of samples.</param>
        /// <returns>A tuple with the set of points and the t parameter where the point was evaluated.</returns>
        internal static (List <double> tvalues, List <Point3> pts) RegularSample(NurbsBase curve, int numSamples)
        {
            if (numSamples < 1)
            {
                throw new Exception("Number of sample must be at least 1 and not negative.");
            }

            double start = curve.Knots[0];
            double end   = curve.Knots[curve.Knots.Count - 1];

            double        span    = (end - start) / (numSamples - 1);
            List <Point3> pts     = new List <Point3>();
            List <double> tValues = new List <double>();

            for (int i = 0; i < numSamples; i++)
            {
                double t = start + span * i;

                Point3 ptEval = curve.PointAt(t);
                pts.Add(ptEval);
                tValues.Add(t);
            }

            return(tValues, pts);
        }
コード例 #7
0
        /// <summary>
        /// Computes the intersection between a curve and a plane.<br/>
        /// https://www.parametriczoo.com/index.php/2020/03/31/plane-and-curve-intersection/
        /// </summary>
        /// <param name="crv">The curve to intersect.</param>
        /// <param name="pl">The plane to intersect with the curve.</param>
        /// <param name="tolerance">Tolerance set per default at 1e-6.</param>
        /// <returns>If intersection found a collection of <see cref="CurvePlaneIntersectionResult"/> otherwise the result will be empty.</returns>
        public static List <CurvePlaneIntersectionResult> CurvePlane(NurbsBase crv, Plane pl, double tolerance = 1e-6)
        {
            List <NurbsBase> bBoxRoot = BoundingBoxOperations.BoundingBoxPlaneIntersection(new LazyCurveBBT(crv), pl);
            List <CurvePlaneIntersectionResult> intersectionResults = bBoxRoot.Select(
                x => IntersectionRefiner.CurvePlaneWithEstimation(crv, pl, x.Knots[0], x.Knots[0], tolerance)).ToList();

            return(intersectionResults);
        }
コード例 #8
0
        /// <summary>
        /// Divides a curve for a given number of time, including the end points.<br/>
        /// The result is not split curves but a collection of t values and lengths that can be used for splitting.<br/>
        /// As with all arc length methods, the result is an approximation.
        /// </summary>
        /// <param name="curve">The curve object to divide.</param>
        /// <param name="divisions">The number of parts to split the curve into.</param>
        /// <returns>A tuple define the t values where the curve is divided and the lengths between each division.</returns>
        internal static List <double> ByCount(NurbsBase curve, int divisions)
        {
            double approximatedLength  = Analyze.Curve.Length(curve);
            double arcLengthSeparation = approximatedLength / divisions;
            var    divisionByLength    = ByLength(curve, arcLengthSeparation);
            var    tValues             = divisionByLength.tValues;

            return(tValues);
        }
コード例 #9
0
        /// <summary>
        /// Computes the self intersections of a curve.
        /// </summary>
        /// <param name="crv">The curve for self-intersections.</param>
        /// <param name="tolerance">Tolerance set per default at 1e-6.</param>
        /// <returns>If intersection found a collection of <see cref="CurvesIntersectionResult"/> otherwise the result will be empty.</returns>
        public static List <CurvesIntersectionResult> CurveSelf(NurbsBase crv, double tolerance = 1e-6)
        {
            List <Tuple <NurbsBase, NurbsBase> > bBoxTreeIntersections = BoundingBoxOperations.BoundingBoxTreeIntersection(new LazyCurveBBT(crv), 0);
            List <CurvesIntersectionResult>      intersectionResults   = bBoxTreeIntersections
                                                                         .Select(x => IntersectionRefiner.CurvesWithEstimation(x.Item1, x.Item2, x.Item1.Knots[0], x.Item2.Knots[0], tolerance))
                                                                         .Where(crInRe => Math.Abs(crInRe.ParameterA - crInRe.ParameterB) > tolerance)
                                                                         .Unique((a, b) => Math.Abs(a.ParameterA - b.ParameterA) < tolerance * 5);

            return(intersectionResults);
        }
コード例 #10
0
        public void It_Returns_Parameter_At_The_Given_Length(double segmentLength, double tValueExpected)
        {
            // Arrange
            NurbsBase curve = NurbsCurveCollection.PlanarCurveDegreeThree();

            // Act
            double parameter = curve.ParameterAtLength(segmentLength);

            // Assert
            parameter.Should().BeApproximately(tValueExpected, GSharkMath.MinTolerance);
        }
コード例 #11
0
        public void It_Returns_True_If_The_NurbsBase_Form_Of_A_Line_Is_Correct()
        {
            //Act
            NurbsBase nurbsLine = _exampleLine;

            // Assert
            nurbsLine.ControlPointLocations.Count.Should().Be(2);
            nurbsLine.ControlPointLocations[0].Equals(_exampleLine.StartPoint).Should().BeTrue();
            nurbsLine.ControlPointLocations[1].Equals(_exampleLine.EndPoint).Should().BeTrue();
            nurbsLine.Degree.Should().Be(1);
        }
コード例 #12
0
        public void It_Returns_The_Segment_At_Length(double length, int segmentIndex)
        {
            //Arrange
            NurbsBase expectedSegment = _polycurve.Segments[segmentIndex];

            //Act
            NurbsBase segmentResult = _polycurve.SegmentAtLength(length);

            //Assert
            segmentResult.Should().BeSameAs(expectedSegment);
        }
コード例 #13
0
        /// <summary>
        /// Samples a curve in an adaptive way. <br/>
        /// <em>Corresponds to this algorithm http://ariel.chronotext.org/dd/defigueiredo93adaptive.pdf </em>
        /// </summary>
        /// <param name="curve">The curve to sampling.</param>
        /// <param name="tolerance">The tolerance for the adaptive division.</param>
        /// <returns>A tuple collecting the parameter where it was sampled and the points.</returns>
        public static (List <double> tValues, List <Point3> pts) AdaptiveSample(NurbsBase curve, double tolerance = GSharkMath.MinTolerance)
        {
            if (curve.Degree != 1)
            {
                return(AdaptiveSampleRange(curve, curve.Knots[0], curve.Knots[curve.Knots.Count - 1], tolerance));
            }
            KnotVector copyKnot = new KnotVector(curve.Knots);

            copyKnot.RemoveAt(0);
            copyKnot.RemoveAt(copyKnot.Count - 1);
            return(copyKnot, curve.ControlPointLocations);
        }
コード例 #14
0
        public void It_Returns_The_Length_Of_The_Curve()
        {
            // Arrange
            NurbsBase curve          = NurbsCurveCollection.PlanarCurveDegreeThree();
            double    expectedLength = 50.334675;

            // Act
            double crvLength = curve.Length;

            // Assert
            crvLength.Should().BeApproximately(expectedLength, GSharkMath.MinTolerance);
        }
コード例 #15
0
        public void Returns_The_Surface_Isocurve_At_U_Direction()
        {
            // Arrange
            NurbsSurface surface    = NurbsSurfaceCollection.SurfaceFromPoints();
            Point3       expectedPt = new Point3(3.591549, 10, 4.464789);

            // Act
            NurbsBase Isocurve = surface.IsoCurve(0.3, SurfaceDirection.U);

            // Assert
            Isocurve.ControlPointLocations[1].DistanceTo(expectedPt).Should().BeLessThan(GSharkMath.MinTolerance);
        }
コード例 #16
0
        /// <summary>
        /// Refines an intersection pair for two curves given an initial guess.<br/>
        /// This is an unconstrained minimization, so the caller is responsible for providing a very good initial guess.
        /// </summary>
        /// <param name="crv0">The first curve.</param>
        /// <param name="crv1">The second curve.</param>
        /// <param name="firstGuess">The first guess parameter.</param>
        /// <param name="secondGuess">The second guess parameter.</param>
        /// <param name="tolerance">The value tolerance for the intersection.</param>
        /// <returns>The results collected into the object <see cref="CurvesIntersectionResult"/>.</returns>
        internal static CurvesIntersectionResult CurvesWithEstimation(NurbsBase crv0, NurbsBase crv1,
                                                                      double firstGuess, double secondGuess, double tolerance)
        {
            IObjectiveFunction objectiveFunctions = new CurvesIntersectionObjectives(crv0, crv1);
            Minimizer          min      = new Minimizer(objectiveFunctions);
            MinimizationResult solution = min.UnconstrainedMinimizer(new Vector {
                firstGuess, secondGuess
            }, tolerance * tolerance);

            // These are not the same points, also are used to filter where the intersection is not happening.
            Point3 pt1 = crv0.PointAt(solution.SolutionPoint[0]);
            Point3 pt2 = crv1.PointAt(solution.SolutionPoint[1]);

            return(new CurvesIntersectionResult(pt1, pt2, solution.SolutionPoint[0], solution.SolutionPoint[1]));
        }
コード例 #17
0
        public void It_Returns_The_Closest_Point_And_Parameter(double[] ptToCheck, double[] ptExpected, double tValExpected)
        {
            // Arrange
            NurbsBase curve      = NurbsCurveCollection.PlanarCurveDegreeThree();
            Point3    testPt     = new Point3(ptToCheck[0], ptToCheck[1], ptToCheck[2]);
            Point3    expectedPt = new Point3(ptExpected[0], ptExpected[1], ptExpected[2]);

            // Act
            Point3 pt        = curve.ClosestPoint(testPt);
            double parameter = curve.ClosestParameter(testPt);

            // Assert
            parameter.Should().BeApproximately(tValExpected, GSharkMath.MaxTolerance);
            pt.EpsilonEquals(expectedPt, GSharkMath.MaxTolerance).Should().BeTrue();
        }
コード例 #18
0
        public void Returns_The_Surface_Isocurve_At_V_Direction()
        {
            // Arrange
            NurbsSurface surface      = NurbsSurfaceCollection.SurfaceFromPoints();
            Point3       expectedPt   = new Point3(5, 4.615385, 2.307692);
            Point3       expectedPtAt = new Point3(5, 3.913043, 1.695652);

            // Act
            NurbsBase Isocurve = surface.IsoCurve(0.3, SurfaceDirection.V);
            Point3    ptAt     = Isocurve.PointAt(0.5);

            // Assert
            Isocurve.ControlPointLocations[1].DistanceTo(expectedPt).Should().BeLessThan(GSharkMath.MinTolerance);
            ptAt.DistanceTo(expectedPtAt).Should().BeLessThan(GSharkMath.MinTolerance);
        }
コード例 #19
0
        /// <summary>
        /// Computes the approximate length of a rational bezier curve by gaussian quadrature - assumes a smooth curve.
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="u">The parameter at which to approximate the length.</param>
        /// <param name="gaussDegIncrease">
        /// the degree of gaussian quadrature to perform.
        /// A higher number yields a more exact result, default set to 17.
        /// </param>
        /// <returns>The approximate length of a bezier.</returns>
        internal static double BezierLength(NurbsBase curve, double u = -1.0, int gaussDegIncrease = 17)
        {
            double uSet        = u < 0.0 ? curve.Knots.Last() : u;
            double z           = (uSet - curve.Knots[0]) / 2;
            double sum         = 0.0;
            int    gaussDegree = curve.Degree + gaussDegIncrease;

            for (int i = 0; i < gaussDegree; i++)
            {
                double         cu  = z * LegendreGaussData.tValues[gaussDegree][i] + z + curve.Knots[0];
                List <Vector3> tan = Evaluate.Curve.RationalDerivatives(curve, cu);

                sum += LegendreGaussData.cValues[gaussDegree][i] * tan[1].Length;
            }

            return(z * sum);
        }
コード例 #20
0
        /// <summary>
        /// Refines an intersection between a curve and a plane given an initial guess.<br/>
        /// This is an unconstrained minimization, so the caller is responsible for providing a very good initial guess.
        /// </summary>
        /// <param name="crv">The curve to intersect.</param>
        /// <param name="plane">The plane to intersect with the curve.</param>
        /// <param name="firstGuess">The first guess parameter.</param>
        /// <param name="secondGuess">The second guess parameter.</param>
        /// <param name="tolerance">The value tolerance for the intersection.</param>
        /// <returns>The results collected into the object <see cref="CurvePlaneIntersectionResult"/>.</returns>
        internal static CurvePlaneIntersectionResult CurvePlaneWithEstimation(NurbsBase crv, Plane plane,
                                                                              double firstGuess, double secondGuess, double tolerance)
        {
            IObjectiveFunction objectiveFunctions = new CurvePlaneIntersectionObjectives(crv, plane);
            Minimizer          min      = new Minimizer(objectiveFunctions);
            MinimizationResult solution = min.UnconstrainedMinimizer(new Vector {
                firstGuess, secondGuess
            }, tolerance * tolerance);

            Point3 pt = crv.PointAt(solution.SolutionPoint[0]);

            (double u, double v)parameters = plane.ClosestParameters(pt);
            Vector uv = new Vector {
                parameters.u, parameters.v, 0.0
            };

            return(new CurvePlaneIntersectionResult(pt, solution.SolutionPoint[0], uv));
        }
コード例 #21
0
        public void It_Returns_True_If_The_NurbsBase_Form_Of_A_Polygon_Is_Correct()
        {
            // Arrange
            PolyLine polygon   = new Polygon(Planar2D);
            double   lengthSum = 0.0;

            // Act
            NurbsBase polygonCurve = polygon;

            // Assert
            polygonCurve.Degree.Should().Be(1);
            polygonCurve.ControlPointLocations[0]
            .EpsilonEquals(polygonCurve.ControlPointLocations.Last(), GSharkMath.MinTolerance).Should().BeTrue();
            for (int i = 0; i < polygon.SegmentsCount; i++)
            {
                lengthSum += polygon.Segments[i].Length;
                polygon.ControlPointLocations[i + 1].EpsilonEquals(polygonCurve.PointAtLength(lengthSum), GSharkMath.MaxTolerance).Should().BeTrue();
            }
        }
コード例 #22
0
        /// <summary>
        /// Decompose a curve into a collection of bezier curves.<br/>
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="normalize">Set as per default false, true normalize the knots between 0 to 1.</param>
        /// <returns>Collection of curve objects, defined by degree, knots, and control points.</returns>
        internal static List <NurbsBase> DecomposeIntoBeziers(NurbsBase curve, bool normalize = false)
        {
            int           degree        = curve.Degree;
            List <Point4> controlPoints = curve.ControlPoints;
            KnotVector    knots         = curve.Knots;

            // Find all of the unique knot values and their multiplicity.
            // For each, increase their multiplicity to degree + 1.
            Dictionary <double, int> knotMultiplicities = knots.Multiplicities();
            int reqMultiplicity = degree + 1;

            // Insert the knots.
            foreach (KeyValuePair <double, int> kvp in knotMultiplicities)
            {
                if (kvp.Value >= reqMultiplicity)
                {
                    continue;
                }
                List <double> knotsToInsert = CollectionHelpers.RepeatData(kvp.Key, reqMultiplicity - kvp.Value);
                NurbsBase     curveTemp     = new NurbsCurve(degree, knots, controlPoints);
                NurbsBase     curveResult   = KnotRefine(curveTemp, knotsToInsert);
                knots         = curveResult.Knots;
                controlPoints = curveResult.ControlPoints;
            }

            int crvKnotLength       = reqMultiplicity * 2;
            List <NurbsBase> curves = new List <NurbsBase>();
            int i = 0;

            while (i < controlPoints.Count)
            {
                KnotVector knotsRange = (normalize)
                    ? knots.GetRange(i, crvKnotLength).ToKnot().Normalize()
                    : knots.GetRange(i, crvKnotLength).ToKnot();
                List <Point4> ptsRange = controlPoints.GetRange(i, reqMultiplicity);

                NurbsBase tempCrv = new NurbsCurve(degree, knotsRange, ptsRange);
                curves.Add(tempCrv);
                i += reqMultiplicity;
            }

            return(curves);
        }
コード例 #23
0
        /// <summary>
        /// Computes the approximate length of a rational curve by gaussian quadrature - assumes a smooth curve.
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="u">The parameter at which to approximate the length.</param>
        /// <param name="gaussDegIncrease">
        /// The degree of gaussian quadrature to perform.
        /// A higher number yields a more exact result, default set to 17.
        /// </param>
        /// <returns>The approximate length.</returns>
        internal static double Length(NurbsBase curve, double u = -1.0, int gaussDegIncrease = 17)
        {
            double uSet = u < 0.0 ? curve.Knots.Last() : u;

            List <NurbsBase> crvs = Modify.Curve.DecomposeIntoBeziers(curve);
            double           sum  = 0.0;

            foreach (NurbsBase bezier in crvs)
            {
                if (!(bezier.Knots[0] + GSharkMath.Epsilon < uSet))
                {
                    break;
                }
                double param = Math.Min(bezier.Knots.Last(), uSet);
                sum += BezierLength(bezier, param, gaussDegIncrease);
            }

            return(sum);
        }
コード例 #24
0
        /// <summary>
        /// Computes the curve parameter at a given length.
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="segmentLength">The length to find the parameter.</param>
        /// <param name="tolerance">If set less or equal 0.0, the tolerance used is 1e-10.</param>
        /// <returns>The parameter at the given length.</returns>
        internal static double BezierParameterAtLength(NurbsBase curve, double segmentLength, double tolerance)
        {
            if (segmentLength < 0)
            {
                return(curve.Knots[0]);
            }

            // We compute the whole length.
            double curveLength = BezierLength(curve);

            if (segmentLength > curveLength)
            {
                return(curve.Knots[curve.Knots.Count - 1]);
            }

            // Divide and conquer.
            double setTolerance = tolerance <= 0.0 ? GSharkMath.Epsilon : tolerance;

            double startT      = curve.Knots[0];
            double startLength = 0.0;

            double endT      = curve.Knots[curve.Knots.Count - 1];
            double endLength = curveLength;

            while (endLength - startLength > setTolerance)
            {
                double midT      = (startT + endT) / 2;
                double midLength = BezierLength(curve, midT);

                if (midLength > segmentLength)
                {
                    endT      = midT;
                    endLength = midLength;
                }
                else
                {
                    startT      = midT;
                    startLength = midLength;
                }
            }

            return((startT + endT) / 2);
        }
コード例 #25
0
        /// <summary>
        /// Divides a curve for a given length, including the end points.<br/>
        /// The result is not split curves but a collection of t values and lengths that can be used for splitting.<br/>
        /// As with all arc length methods, the result is an approximation.
        /// </summary>
        /// <param name="curve">The curve object to divide.</param>
        /// <param name="length">The length separating the resultant samples.</param>
        /// <returns>A tuple define the t values where the curve is divided and the lengths between each division.</returns>
        internal static (List <double> tValues, List <double> lengths) ByLength(NurbsBase curve, double length)
        {
            List <NurbsBase> curves       = Modify.Curve.DecomposeIntoBeziers(curve);
            List <double>    curveLengths = curves.Select(NurbsBase => Analyze.Curve.BezierLength(NurbsBase)).ToList();
            double           totalLength  = curveLengths.Sum();

            List <double> tValues = new List <double> {
                curve.Knots[0]
            };
            List <double> divisionLengths = new List <double> {
                0.0
            };

            if (length > totalLength)
            {
                return(tValues, divisionLengths);
            }

            int    i             = 0;
            double sum           = 0.0;
            double sum2          = 0.0;
            double segmentLength = length;

            while (i < curves.Count)
            {
                sum += curveLengths[i];

                while (segmentLength < sum + GSharkMath.Epsilon)
                {
                    double t = Analyze.Curve.BezierParameterAtLength(curves[i], segmentLength - sum2, GSharkMath.MinTolerance);

                    tValues.Add(t);
                    divisionLengths.Add(segmentLength);

                    segmentLength += length;
                }

                sum2 += curveLengths[i];
                i++;
            }

            return(tValues, divisionLengths);
        }
コード例 #26
0
        public void It_Is_A_Curve_Representation_Of_ExampleArc3D()
        {
            // Arrange
            double[] weightChecks = { 1.0, 0.7507927793532885, 1.0, 0.7507927793532885, 1.0, 0.7507927793532885, 1.0 };
            Point3[] ptChecks     =
            {
                new Point3(74.264416,                    36.39316, -1.8843129999999997),
                new Point3(63.73736394529969,  26.774907230101093,  0.7265431054950776),
                new Point3(72.2808868605866,   15.429871621311115,  3.6324963299804987),
                new Point3(80.8244097758736,    4.084836012521206,   6.538449554465901),
                new Point3(93.52800280921122,  10.812836698886068,     4.6679117389561),
                new Point3(106.23159584254901,  17.54083738525103,   2.797373923446271),
                new Point3(100.92443,                   30.599893, -0.5851159999999997)
            };

            // Act
            NurbsBase arc = _exampleArc3D;

            // Assert
            arc.ControlPointLocations.Count.Should().Be(7);
            arc.Degree.Should().Be(2);

            for (int i = 0; i < ptChecks.Length; i++)
            {
                arc.ControlPointLocations[i].EpsilonEquals(ptChecks[i], GSharkMath.MaxTolerance).Should().BeTrue();
                arc.ControlPoints[i].W.Should().Be(weightChecks[i]);

                if (i < 3)
                {
                    arc.Knots[i].Should().Be(0);
                    arc.Knots[i + 7].Should().Be(1);
                }
                else if (i < 5)
                {
                    arc.Knots[i].Should().Be(0.3333333333333333);
                }
                else
                {
                    arc.Knots[i].Should().Be(0.6666666666666666);
                }
            }
        }
コード例 #27
0
        public void It_Returns_A_Curve_Where_Degree_Is_Elevated_From_1_To_Elevated_Degree_Value(int finalDegree)
        {
            // Arrange
            List <Point3> pts = new List <Point3>
            {
                new Point3(0.0, 0.0, 1.0),
                new Point3(7.0, 3.0, -10),
                new Point3(5.2, 5.2, -5),
            };
            int        degree    = 1;
            NurbsCurve curve     = new NurbsCurve(pts, degree);
            Point3     ptOnCurve = curve.PointAt(0.5);

            // Act
            NurbsBase elevatedDegreeCurve     = curve.ElevateDegree(finalDegree);
            Point3    ptOnElevatedDegreeCurve = elevatedDegreeCurve.PointAt(0.5);

            // Assert
            elevatedDegreeCurve.Degree.Should().Be(finalDegree);
            ptOnElevatedDegreeCurve.DistanceTo(ptOnCurve).Should().BeLessThan(GSharkMath.MinTolerance);
        }
コード例 #28
0
        /// <summary>
        /// Calculates all the extrema on a curve.<br/>
        /// Extrema are calculated for each dimension, rather than for the full curve, <br/>
        /// so that the result is not the number of convex/concave transitions, but the number of those transitions for each separate dimension.
        /// </summary>
        /// <param name="curve">Curve where the extrema are calculated.</param>
        /// <returns>The extrema.</returns>
        internal static Extrema ComputeExtrema(NurbsBase curve)
        {
            var     derivPts = DerivativeCoordinates(curve.ControlPointLocations);
            Extrema extrema  = new Extrema();

            int dim = derivPts[0][0].Size;

            for (int j = 0; j < dim; j++)
            {
                List <double> p0 = new List <double>();

                for (int i = 0; i < derivPts[0].Count; i++)
                {
                    p0.Add(derivPts[0][i][j]);
                }

                List <double> result = new List <double>(DerivativesRoots(p0));

                if (curve.Degree == 3)
                {
                    IList <double> p1 = new List <double>();

                    for (int i = 0; i < derivPts[1].Count; i++)
                    {
                        p1.Add(derivPts[1][i][j]);
                    }

                    result = result.Concat(DerivativesRoots(p1).ToList()).ToList();
                }

                result = result.Where((t) => t >= 0 && t <= 1).ToList();
                result.Sort();
                extrema[j] = result;
            }

            extrema.Values = extrema[0].Union(extrema[1]).Union(extrema[2]).OrderBy(x => x).ToList();
            return(extrema);
        }
コード例 #29
0
        /// <summary>
        /// Performs a knot refinement on a surface by inserting knots at various parameters.<br/>
        /// <em>Implementation of Algorithm A5.5 of The NURBS Book by Piegl and Tiller.</em>
        /// </summary>
        internal static NurbsSurface SurfaceKnotRefine(NurbsSurface surface, IList <double> knotsToInsert, SurfaceDirection direction)
        {
            List <List <Point4> > modifiedControlPts = new List <List <Point4> >();
            List <List <Point4> > controlPts         = surface.ControlPoints;
            KnotVector            knots = surface.KnotsV;
            int degree = surface.DegreeV;

            if (direction != SurfaceDirection.V)
            {
                controlPts = CollectionHelpers.Transpose2DArray(surface.ControlPoints);
                knots      = surface.KnotsU;
                degree     = surface.DegreeU;
            }

            NurbsBase curve = null;

            foreach (List <Point4> pts in controlPts)
            {
                curve = Curve.KnotRefine(new NurbsCurve(degree, knots, pts), knotsToInsert);
                modifiedControlPts.Add(curve.ControlPoints);
            }

            if (curve == null)
            {
                throw new Exception(
                          "The refinement was not be able to be completed. A problem occur refining the internal curves.");
            }

            if (direction != SurfaceDirection.V)
            {
                var reversedControlPts = CollectionHelpers.Transpose2DArray(modifiedControlPts);
                return(new NurbsSurface(surface.DegreeU, surface.DegreeV, curve.Knots, surface.KnotsV.Copy(),
                                        reversedControlPts));
            }

            return(new NurbsSurface(surface.DegreeU, surface.DegreeV, surface.KnotsU.Copy(), curve.Knots,
                                    modifiedControlPts));
        }
コード例 #30
0
        /// <summary>
        /// Samples a curve in an adaptive way. <br/>
        /// <em>Corresponds to this algorithm http://ariel.chronotext.org/dd/defigueiredo93adaptive.pdf <br/>
        /// https://www.modelical.com/en/grasshopper-scripting-107/ </em>
        /// </summary>
        /// <param name="curve">The curve to sampling.</param>
        /// <param name="start">The start parameter for sampling.</param>
        /// <param name="end">The end parameter for sampling.</param>
        /// <param name="tolerance">Tolerance for the adaptive scheme. The default tolerance is set as (1e-6).</param>
        /// <returns>A tuple with the set of points and the t parameter where the point was evaluated.</returns>
        public static (List <double> tValues, List <Point3> pts) AdaptiveSampleRange(NurbsBase curve, double start, double end, double tolerance = GSharkMath.MinTolerance)
        {
            // Sample curve at three pts.
            Random random = new Random();
            double t      = 0.5 + 0.2 * random.NextDouble();
            double mid    = start + (end - start) * t;

            Point3 pt1 = Point4.PointDehomogenizer(Evaluate.Curve.PointAt(curve, start));
            Point3 pt2 = Point4.PointDehomogenizer(Evaluate.Curve.PointAt(curve, mid));
            Point3 pt3 = Point4.PointDehomogenizer(Evaluate.Curve.PointAt(curve, end));

            Vector3 diff  = pt1 - pt3;
            Vector3 diff2 = pt1 - pt2;

            if ((Vector3.DotProduct(diff, diff) < tolerance && Vector3.DotProduct(diff2, diff2) > tolerance) ||
                !Trigonometry.ArePointsCollinear(pt1, pt2, pt3, tolerance))
            {
                // Get the exact middle value or a random value start + (end - start) * (0.45 + 0.1 * random.NextDouble());
                double tMiddle = start + (end - start) * 0.5;

                // Recurse the two halves.
                (List <double> tValues, List <Point3> pts)leftHalves  = AdaptiveSampleRange(curve, start, tMiddle, tolerance);
                (List <double> tValues, List <Point3> pts)rightHalves = AdaptiveSampleRange(curve, tMiddle, end, tolerance);

                leftHalves.tValues.RemoveAt(leftHalves.tValues.Count - 1);
                List <double> tMerged = leftHalves.tValues.Concat(rightHalves.tValues).ToList();
                leftHalves.pts.RemoveAt(leftHalves.pts.Count - 1);
                List <Point3> ptsMerged = leftHalves.pts.Concat(rightHalves.pts).ToList();

                return(tMerged, ptsMerged);
            }

            return(new List <double> {
                start, end
            }, new List <Point3> {
                pt1, pt3
            });
        }