Beispiel #1
0
        /// <summary>
        /// Builds the coefficient matrix used to calculate a curve global interpolation.
        /// </summary>
        private static Matrix BuildCoefficientsMatrix(List <Point3> pts, int degree, bool hasTangents, List <double> curveParameters, KnotVector knots)
        {
            int    dim         = (hasTangents) ? pts.Count + 1 : pts.Count - 1;
            Matrix coeffMatrix = new Matrix();

            int dimEnd = (hasTangents) ? pts.Count - (degree - 1) : pts.Count - (degree + 1);

            foreach (double u in curveParameters)
            {
                int           span          = knots.Span(dim, degree, u);
                List <double> basicFunction = Evaluate.Curve.BasisFunction(degree, knots, span, u);

                List <double> startRow = CollectionHelpers.RepeatData(0.0, span - degree);
                List <double> endRow   = CollectionHelpers.RepeatData(0.0, dimEnd - (span - degree));

                coeffMatrix.Add(startRow.Concat(basicFunction).Concat(endRow).ToList());
            }

            if (!hasTangents)
            {
                return(coeffMatrix);
            }

            List <double> zeros   = CollectionHelpers.RepeatData(0.0, coeffMatrix[0].Count - 2);
            List <double> tangent = new List <double> {
                -1.0, 1.0
            };

            coeffMatrix.Insert(1, tangent.Concat(zeros).ToList());
            coeffMatrix.Insert(coeffMatrix.Count - 1, zeros.Concat(tangent).ToList());

            return(coeffMatrix);
        }
Beispiel #2
0
        /// <summary>
        /// Bezier degree reduction.
        /// <em>Refer to The NURBS Book by Piegl and Tiller at page 220.</em>
        /// </summary>
        private static double BezierDegreeReduce(Point4[] bpts, int degree, out Point4[] rbpts)
        {
            // Eq. 5.40
            int r = (int)Math.Floor(((double)degree - 1) / 2);

            Point4[] P = new Point4[degree];
            P[0]            = bpts[0];
            P[P.Length - 1] = bpts.Last();

            bool isDegreeOdd = degree % 2 != 0;

            int r1 = (isDegreeOdd) ? r - 1 : r;

            // Eq. 5.41
            for (int i = 1; i <= r1; i++)
            {
                double alphaI = (double)i / degree;
                P[i] = (bpts[i] - (P[i - 1] * alphaI)) / (1 - alphaI);
            }

            for (int i = degree - 2; i >= r + 1; i--)
            {
                double alphaI = (double)(i + 1) / degree;
                P[i] = (bpts[i + 1] - (P[i + 1] * (1 - alphaI))) / alphaI;
            }

            /* Equations (5.43) and (5.44) express the parametric error {distance between points at corresponding parameter values);
             * the maximums of geometric and parametric errors are not necessarily at the same u value.
             * For p even the maximum error occurs at u = 1/2; for p odd the error is zero at u = 1/2, and it has two peaks a bit to the left and
             * right of u = 1/2. */

            // Eq. 5.43 p even.
            KnotVector    knots           = new KnotVector(degree - 1, P.Length);
            int           span            = knots.Span(degree - 1, 0.5);
            List <double> Br              = Evaluate.Curve.BasisFunction(degree - 1, knots, span, 0.5);
            double        parametricError = bpts[r + 1].DistanceTo((P[r] + P[r + 1]) * 0.5) * Br[r + 1];

            // Eq. 5.42
            if (isDegreeOdd)
            {
                double alphaR = (double)r / degree;
                Point4 PLeft  = (bpts[r] - (P[r - 1] * alphaR)) / (1 - alphaR);

                double alphaR1 = (double)(r + 1) / degree;
                Point4 PRight  = (bpts[r + 1] - (P[r + 1] * (1 - alphaR1))) / alphaR1;

                P[r] = (PLeft + PRight) * 0.5;
                // Eq. 5.44 p odd.
                parametricError = ((1 - alphaR) * 0.5) * ((Br[r] - Br[r + 1]) * PLeft.DistanceTo(PRight));
            }

            rbpts = P;
            return(parametricError);
        }
Beispiel #3
0
        public void It_Returns_The_KnotSpan_Given_A_Parameter(int expectedValue, double parameter)
        {
            // Arrange
            KnotVector knotVector = new KnotVector {
                0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5
            };
            int degree = 2;

            // Act
            int result = knotVector.Span(knotVector.Count - degree - 2, 2, parameter);

            // Assert
            result.Should().Be(expectedValue);
        }
Beispiel #4
0
        /// <summary>
        /// Computes a point on a non-uniform, non-rational b-spline curve.<br/>
        /// <em>Corresponds to algorithm 3.1 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>
        /// <returns>The evaluated point on the curve.</returns>
        internal static Point3 PointAt(NurbsBase curve, double parameter)
        {
            List <Point4> controlPts = curve.ControlPoints;
            KnotVector    knots      = curve.Knots;

            int n = knots.Count - curve.Degree - 2;

            int           knotSpan     = knots.Span(n, curve.Degree, parameter);
            List <double> basisValue   = BasisFunction(curve.Degree, knots, knotSpan, parameter);
            Point4        pointOnCurve = Point4.Zero;

            for (int i = 0; i <= curve.Degree; i++)
            {
                double valToMultiply = basisValue[i];
                Point4 pt            = controlPts[knotSpan - curve.Degree + i];

                pointOnCurve.X += valToMultiply * pt.X;
                pointOnCurve.Y += valToMultiply * pt.Y;
                pointOnCurve.Z += valToMultiply * pt.Z;
                pointOnCurve.W += valToMultiply * pt.W;
            }

            return(new Point3(pointOnCurve));
        }
Beispiel #5
0
        /// <summary>
        /// Splits (divides) the surface into two parts at the specified parameter
        /// </summary>
        /// <param name="surface">The NURBS surface to split.</param>
        /// <param name="parameter">The parameter at which to split the surface, parameter should be between 0 and 1.</param>
        /// <param name="direction">Where to split in the U or V direction of the surface.</param>
        /// <returns>If the surface is split vertically (U direction) the left side is returned as the first surface and the right side is returned as the second surface.<br/>
        /// If the surface is split horizontally (V direction) the bottom side is returned as the first surface and the top side is returned as the second surface.<br/>
        /// If the spit direction selected is both, the split computes first a U direction split and on the result a V direction split.</returns>
        internal static NurbsSurface[] Split(NurbsSurface surface, double parameter, SplitDirection direction)
        {
            KnotVector            knots      = surface.KnotsV;
            int                   degree     = surface.DegreeV;
            List <List <Point4> > srfCtrlPts = surface.ControlPoints;

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

            List <double> knotsToInsert = CollectionHelpers.RepeatData(parameter, degree + 1);
            int           span          = knots.Span(degree, parameter);

            List <List <Point4> > surfPtsLeft  = new List <List <Point4> >();
            List <List <Point4> > surfPtsRight = new List <List <Point4> >();
            NurbsBase             result       = null;

            foreach (List <Point4> pts in srfCtrlPts)
            {
                NurbsCurve tempCurve = new NurbsCurve(degree, knots, pts);
                result = KnotVector.Refine(tempCurve, knotsToInsert);

                surfPtsLeft.Add(result.ControlPoints.GetRange(0, span + 1));
                surfPtsRight.Add(result.ControlPoints.GetRange(span + 1, span + 1));
            }

            if (result == null)
            {
                throw new Exception($"Could not split {nameof(surface)}.");
            }

            KnotVector knotLeft  = result.Knots.GetRange(0, span + degree + 2).ToKnot();
            KnotVector knotRight = result.Knots.GetRange(span + 1, span + degree + 2).ToKnot();

            NurbsSurface[] surfaceResult = Array.Empty <NurbsSurface>();

            switch (direction)
            {
            case SplitDirection.U:
            {
                surfaceResult = new NurbsSurface[]
                {
                    new NurbsSurface(degree, surface.DegreeV, knotLeft, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsLeft)),
                    new NurbsSurface(degree, surface.DegreeV, knotRight, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsRight))
                };
                break;
            }

            case SplitDirection.V:
            {
                surfaceResult = new NurbsSurface[]
                {
                    new NurbsSurface(surface.DegreeU, degree, surface.KnotsU.Copy(), knotLeft, surfPtsLeft),
                    new NurbsSurface(surface.DegreeU, degree, surface.KnotsU.Copy(), knotRight, surfPtsRight)
                };
                break;
            }

            case SplitDirection.Both:
            {
                NurbsSurface srf1 = new NurbsSurface(degree, surface.DegreeV, knotLeft, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsLeft));
                NurbsSurface srf2 = new NurbsSurface(degree, surface.DegreeV, knotRight, surface.KnotsV.Copy(), CollectionHelpers.Transpose2DArray(surfPtsRight));

                NurbsSurface[] split1 = Split(srf1, parameter, SplitDirection.V);
                NurbsSurface[] split2 = Split(srf2, parameter, SplitDirection.V);

                surfaceResult = split2.Concat(split1).ToArray();
                break;
            }
            }

            return(surfaceResult);
        }
Beispiel #6
0
        /// <summary>
        /// Inserts a collection of knots on a curve.<br/>
        /// <em>Implementation of Algorithm A5.4 of The NURBS Book by Piegl and Tiller.</em>
        /// </summary>
        /// <param name="curve">The curve object.</param>
        /// <param name="knotsToInsert">The set of knots.</param>
        /// <returns>A curve with refined knots.</returns>
        internal static NurbsBase KnotRefine(NurbsBase curve, IList <double> knotsToInsert)
        {
            if (knotsToInsert.Count == 0)
            {
                return(curve);
            }

            int           degree        = curve.Degree;
            List <Point4> controlPoints = curve.ControlPoints;
            KnotVector    knots         = curve.Knots;

            // Initialize common variables.
            int n = controlPoints.Count - 1;
            int m = n + degree + 1;
            int r = knotsToInsert.Count - 1;
            int a = knots.Span(degree, knotsToInsert[0]);
            int b = knots.Span(degree, knotsToInsert[r]);

            Point4[] controlPointsPost = new Point4[n + r + 2];
            double[] knotsPost         = new double[m + r + 2];

            // New control points.
            for (int i = 0; i < a - degree + 1; i++)
            {
                controlPointsPost[i] = controlPoints[i];
            }
            for (int i = b - 1; i < n + 1; i++)
            {
                controlPointsPost[i + r + 1] = controlPoints[i];
            }

            // New knot vector.
            for (int i = 0; i < a + 1; i++)
            {
                knotsPost[i] = knots[i];
            }
            for (int i = b + degree; i < m + 1; i++)
            {
                knotsPost[i + r + 1] = knots[i];
            }

            // Initialize variables for knot refinement.
            int g = b + degree - 1;
            int k = b + degree + r;
            int j = r;

            // Apply knot refinement.
            while (j >= 0)
            {
                while (knotsToInsert[j] <= knots[g] && g > a)
                {
                    controlPointsPost[k - degree - 1] = controlPoints[g - degree - 1];
                    knotsPost[k] = knots[g];
                    --k;
                    --g;
                }

                controlPointsPost[k - degree - 1] = controlPointsPost[k - degree];

                for (int l = 1; l < degree + 1; l++)
                {
                    int    ind  = k - degree + l;
                    double alfa = knotsPost[k + l] - knotsToInsert[j];

                    if (Math.Abs(alfa) < GSharkMath.Epsilon)
                    {
                        controlPointsPost[ind - 1] = controlPointsPost[ind];
                    }
                    else
                    {
                        alfa /= (knotsPost[k + l] - knots[g - degree + l]);
                        controlPointsPost[ind - 1] = (controlPointsPost[ind - 1] * alfa) +
                                                     (controlPointsPost[ind] * (1.0 - alfa));
                    }
                }

                knotsPost[k] = knotsToInsert[j];
                --k;
                --j;
            }

            return(new NurbsCurve(degree, knotsPost.ToKnot(), controlPointsPost.ToList()));
        }