public Tuple <PrimitiveBezier, PrimitiveBezier> Split(double t) { // Splitting the curve using de Casteljau's algorithm: https://pomax.github.io/bezierinfo/#decasteljau if (t < 0.0 || t > 1.0) { throw new ArgumentOutOfRangeException(nameof(t), "Curves can only be split on the interval [0.0, 1.0]."); } var midPoint = ComputeParameterizedPoint(t); var midControlPoint = Point.ScaledAlongPath(P2, P3, t); // compute the first curve part var newP2 = Point.ScaledAlongPath(P1, P2, t); var newP3 = Point.ScaledAlongPath(newP2, midControlPoint, t); var newP4 = midPoint; var curve1 = new PrimitiveBezier(P1, newP2, newP3, newP4); // compute the second curve part var newP1 = midPoint; newP3 = Point.ScaledAlongPath(P3, P4, t); newP2 = Point.ScaledAlongPath(midControlPoint, newP3, t); var curve2 = new PrimitiveBezier(newP1, newP2, newP3, P4); return(Tuple.Create(curve1, curve2)); }
/// <summary> /// Approximate the given Bezier curve as lines and arcs. /// </summary> internal static IPrimitive[] AsSimplePrimitives(PrimitiveBezier bezier) { var simplePrimitives = new List <IPrimitive>(); var intervals = new Queue <Tuple <double, double, int> >(); intervals.Enqueue(Tuple.Create(0.0, 1.0, 0)); while (intervals.Count > 0) { var interval = intervals.Dequeue(); var intervalStart = interval.Item1; var intervalEnd = interval.Item2; var currentSplitCount = interval.Item3; var intervalSpread = intervalEnd - intervalStart; var intervalMid = intervalStart + (intervalSpread * 0.5); var pStart = bezier.ComputeParameterizedPoint(intervalStart); var pMid = bezier.ComputeParameterizedPoint(intervalMid); var pEnd = bezier.ComputeParameterizedPoint(intervalEnd); var intervalLine = new PrimitiveLine(pStart, pEnd); var candidatePrimitive = intervalLine.IsPointOnPrimitive(pMid, MathHelper.BezierEpsilon) ? (IPrimitive)intervalLine : PrimitiveEllipse.ThreePointArc(pStart, pMid, pEnd, idealNormal: Vector.ZAxis); candidatePrimitive = candidatePrimitive ?? intervalLine; // ensure it has a value var intervalFirstQuarter = intervalStart + (intervalSpread * 0.25); var intervalThirdQuarter = intervalStart + (intervalSpread * 0.75); var pFirstQuarter = bezier.ComputeParameterizedPoint(intervalFirstQuarter); var pThirdQuarter = bezier.ComputeParameterizedPoint(intervalThirdQuarter); if (currentSplitCount >= MaximumPrimitiveSplitCount || (candidatePrimitive.IsPointOnPrimitive(pFirstQuarter, MathHelper.BezierEpsilon) && candidatePrimitive.IsPointOnPrimitive(pThirdQuarter, MathHelper.BezierEpsilon))) { simplePrimitives.Add(candidatePrimitive); } else { intervals.Enqueue(Tuple.Create(intervalStart, intervalMid, currentSplitCount + 1)); intervals.Enqueue(Tuple.Create(intervalMid, intervalEnd, currentSplitCount + 1)); } } return(simplePrimitives.ToArray()); }