distance from given point to given ellipse

后端 未结 7 1953
抹茶落季
抹茶落季 2021-02-05 08:07

I have an ellipse, defined by Center Point, radiusX and radiusY, and I have a Point. I want to find the point on the ellipse that is closest to the given point. In the illustrat

7条回答
  •  青春惊慌失措
    2021-02-05 08:29

    Given an ellipse E in parametric form and a point P

    eqn

    the square of the distance between P and E(t) is

    eqn

    The minimum must satisfy

    eqn

    Using the trigonometric identities

    eqn

    and substituting

    yields the following quartic equation:

    Here's an example C function that solves the quartic directly and computes sin(t) and cos(t) for the nearest point on the ellipse:

    void nearest(double a, double b, double x, double y, double *ecos_ret, double *esin_ret) {
        double ax = fabs(a*x);
        double by = fabs(b*y);
        double r  = b*b - a*a;
        double c, d;
        int switched = 0;
    
        if (ax <= by) {
            if (by == 0) {
                if (r >= 0) { *ecos_ret = 1; *esin_ret = 0; }
                else        { *ecos_ret = 0; *esin_ret = 1; }
                return;
            }
            c = (ax - r) / by;
            d = (ax + r) / by;
        } else {
            c = (by + r) / ax;
            d = (by - r) / ax;
            switched = 1;
        }
    
        double cc = c*c;
        double D0 = 12*(c*d + 1);      // *-4
        double D1 = 54*(d*d - cc);     // *4
        double D  = D1*D1 + D0*D0*D0;  // *16
    
        double St;
        if (D < 0) {
            double t = sqrt(-D0);             // *2
            double phi = acos(D1 / (t*t*t));
            St = 2*t*cos((1.0/3)*phi);        // *2
        } else {
            double Q = cbrt(D1 + sqrt(D));    // *2
            St = Q - D0 / Q;                  // *2
        }
    
        double p    = 3*cc;                          // *-2
        double SS   = (1.0/3)*(p + St);              // *4
        double S    = sqrt(SS);                      // *2
        double q    = 2*cc*c + 4*d;                  // *2
        double l    = sqrt(p - SS + q / S) - S - c;  // *2
        double ll   = l*l;                           // *4
        double ll4  = ll + 4;                        // *4
        double esin = (4*l)    / ll4;
        double ecos = (4 - ll) / ll4;
    
        if (switched) {
            double t = esin;
            esin = ecos;
            ecos = t;
        }
    
        *ecos_ret = copysign(ecos, a*x);
        *esin_ret = copysign(esin, b*y);
    }
    

    Try it online!

提交回复
热议问题