/// <summary> /// Approximate a cubic Bezier curve with a spline of n quadratics. /// </summary> /// <param name="curve">The four vectors representing the control points of the cubic Bezier curve</param> /// <param name="maxError">Permitted deviation from the original curve</param> /// <param name="spline">A list of 2D tuples, representing control points of the quadratic /// spline if it fits within the given tolerance, or null if no /// suitable spline could be calculated</param> /// <returns>True if the curve could be converted, else false</returns> internal static bool CurveToQuadratic(CubicCurve curve, float maxError, out Complex[] spline) { spline = null; for (var n = 1; n < MaxN + 1; n++) { if (CubicApproximateSpline(curve, n, maxError, out spline)) { return(true); } } return(false); }
/// <summary> /// Approximate a cubic Bezier with a single quadratic within a given tolerance. /// </summary> /// <param name="controlPoints">Four complex numbers representing control points of the cubic Bezier curve.</param> /// <param name="maxError">Permitted deviation from the original curve.</param> /// <param name="quadraticCurve">Three complex numbers representing control points of the quadratic /// curve if it fits within the given tolerance, or null if no suitable /// curve could be calculated.</param> /// <returns>True if the curve could be converted, else false</returns> private static bool CubicApproximateQuadratic(CubicCurve controlPoints, float maxError, out QuadraticCurve quadraticCurve) { if (!TryCalculateIntersection(controlPoints.Point0, controlPoints.Point1, controlPoints.Point2, controlPoints.Point3, out var q1)) { return(false); } var c0 = controlPoints.Point0; var c3 = controlPoints.Point3; var c1 = c0 + (q1 - c0) * TwoOverThree; var c2 = c3 + (q1 - c3) * TwoOverThree; if (CubicFarthestFitsInside(0, c1 - controlPoints.Point1, c2 - controlPoints.Point2, 0, maxError)) { return(false); } quadraticCurve = new QuadraticCurve(c0, q1, c3); return(true); }
// Cubic bezier curve public void Curve4(float x1, float y1, float x2, float y2, float x3, float y3) { var curve = new CubicCurve( new Complex(lastX, lastY), new Complex(x1, y1), new Complex(x2, y2), new Complex(x3, y3) ); if (!CubicToQuadraticConverter.CurveToQuadratic(curve, 1, out var spline)) { throw new Exception("Couldn't create a spline from the bezier curve"); } for (var i = 0; i + 2 < spline.Length; i += 2) { var p1 = spline[i + 1]; var p2 = spline[i + 2]; Curve3((float)p1.Real, (float)p1.Imaginary, (float)p2.Real, (float)p2.Imaginary); } }
/// <summary> /// Approximate a cubic Bezier curve with a spline of n quadratics. /// </summary> /// <param name="curve">The four complex numbers representing control points of the cubic Bezier curve.</param> /// <param name="n">Number of quadratic Bezier curves in the spline.</param> /// <param name="maxError">Permitted deviation from the original curve.</param> /// <param name="spline">A list of `n+2` complex numbers, representing control points of the /// quadratic spline if it fits within the given tolerance, or null if /// no suitable spline could be calculated.</param> /// <returns>True if the curve could be converted, else false</returns> private static bool CubicApproximateSpline(CubicCurve curve, int n, float maxError, out Complex[] spline) { spline = null; if (n == 1) { if (CubicApproximateQuadratic(curve, maxError, out var quadraticCurve)) { spline = new Complex[] { quadraticCurve.Point0, quadraticCurve.Point1, quadraticCurve.Point2 }; return(true); } } var cubics = SplitCubicIntoNIterations(curve.Point0, curve.Point1, curve.Point2, curve.Point3, n).ToArray(); // Calculate the spline of quadratics and check errors at the same time var nextCurve = cubics[0]; var nextQ1 = CubicApproximateControl(0, nextCurve.Point0, nextCurve.Point1, nextCurve.Point2, nextCurve.Point3); var q2 = curve.Point0; var d1 = Complex.Zero; var splineTemp = new List <Complex> { curve.Point0, nextQ1 }; for (var i = 1; i < n + 1; i++) { // Current cubic to convert var(_, c1, c2, c3) = nextCurve; // Current quadratic approximation of current cubic var q0 = q2; var q1 = nextQ1; if (i < n) { nextCurve = cubics[i]; nextQ1 = CubicApproximateControl(i / (n - 1), nextCurve.Point0, nextCurve.Point1, nextCurve.Point2, nextCurve.Point3); splineTemp.Add(nextQ1); q2 = (q1 + nextQ1) * 0.5f; } else { q2 = c3; } // End-point deltas var d0 = d1; d1 = q2 - c3; var f1 = q0 + (q1 - q0) * TwoOverThree - c1; var f2 = q2 + (q1 - q2) * TwoOverThree - c2; if (Complex.Abs(d1) > maxError || !CubicFarthestFitsInside(d0, f1, f2, d1, maxError)) { return(false); } } splineTemp.Add(curve.Point3); spline = splineTemp.ToArray(); return(true); }