private double EllipticArc_NearestPointTo(Double2 v) { // First, center and un-rotate the point var p = (v - Center).RotScale(ComplexRot.NegateY); var k = p.X * p.X / (Radii.X * Radii.X) + p.Y * p.Y / (Radii.Y * Radii.Y) - 1; // If it is a circle, just calculate its angle double t; if (RoughlyEquals(Radii.X, Radii.Y)) { t = p.Angle; } // If the point is over the ellipse, calculate its angle the "dumb" form else if (RoughlyZero(k)) { t = Math.Atan2(Radii.X * p.Y, Radii.Y * p.X); } else { // Make the point on the first quadrant bool nx = p.X < 0, ny = p.Y < 0; if (nx) { p.X = -p.X; } if (ny) { p.Y = -p.Y; } // Radii difference var rx = Radii.X; var ry = Radii.Y; var rr = ry * ry - rx * rx; // Function to test for roots double f(double tx) => 0.5 * rr * Math.Sin(2 * tx) + rx * p.X * Math.Sin(tx) - ry * p.Y * Math.Cos(tx); // There will always be a single root on this equation t = Equations.Bisection(f, 0, Pi_2); // Now, update the guess for the correct quadrants if (nx && ny) { t += Pi; } else if (nx) { t = Pi - t; } else { t = -t; } } // If the guess is inside the interval var tu = AngleToParameter(t); if (!double.IsInfinity(tu)) { return(tu); } // Else, pick the nearest of 0 or 1 return(EllipticArc_At(0).DistanceSquaredTo(v) < EllipticArc_At(1).DistanceSquaredTo(v) ? 0 : 1); }