/// <summary> /// Retrieve an elipse in its center-radius form using parameters from the SVG specification /// </summary> /// <param name="cur">Current point</param> /// <param name="radii">The ellipse radius</param> /// <param name="xRotation">The rotation of the ellipse in relation to the x-axis</param> /// <param name="largeArc">Wheter the large arc should be selected or not</param> /// <param name="sweep">Sweep direction (false for clockwise, true for counterclockwise)</param> /// <param name="target">The target point</param> /// <returns></returns> public static Curve EllipticArc(Double2 cur, Double2 radii, double xRotation, bool largeArc, bool sweep, Double2 target) { // The algorithm used here is presented on this link: https://svgwg.org/svg2-draft/implnote.html var xpun = (cur - target) / 2; var xpr = xpun.Rotate(-xRotation); // Guarantee that the radii are positive radii.X = Math.Abs(radii.X); radii.Y = Math.Abs(radii.Y); // Guarantee that the radius are large enough var rr = radii.X * radii.X * xpr.Y * xpr.Y + radii.Y * radii.Y * xpr.X * xpr.X; var r2 = radii.X * radii.X * radii.Y * radii.Y; double skr; if (rr > r2) { radii *= Math.Sqrt(rr / r2); skr = 0; } else { skr = Math.Sqrt((r2 - rr) / rr); } var cpr = new Double2(skr * radii.X * xpr.Y / radii.Y, -skr * radii.Y * xpr.X / radii.X); if (largeArc == sweep) { cpr = -cpr; } // Calculate the center var cpun = cpr.Rotate(xRotation) + (target + cur) / 2; // Calculate the angle var t1 = Math.Atan2(radii.X * (xpr.Y - cpr.Y), radii.Y * (xpr.X - cpr.X)); var t2 = Math.Atan2(radii.X * (-xpr.Y - cpr.Y), radii.Y * (-xpr.X - cpr.X)); var dt = t2 - t1; if (!sweep && dt > 0) { dt -= TwoPi; } else if (sweep && dt < 0) { dt += TwoPi; } return(EllipticArcInternal(cpun, radii, Double2.FromAngle(xRotation), t1, dt)); }