Saltar al contenido

distancia de un punto dado a una elipse dada

Después de tanto luchar pudimos dar con el resultado de este asunto que muchos lectores de nuestro espacio han presentado. Si tienes alguna información que aportar puedes compartir tu conocimiento.

Solución:

Considere un círculo límite alrededor del punto dado (c, d), que pasa por el punto más cercano de la elipse. A partir del diagrama, está claro que el punto más cercano es tal que una línea trazada desde él hasta el punto dado debe ser perpendicular a la tangente compartida de la elipse y el círculo. Cualquier otro punto estaría fuera del círculo y, por lo tanto, debe estar más lejos del punto dado.

ingrese la descripción de la imagen aquí

Así que el punto que estás buscando es no la intersección entre la línea y la elipse, sino el punto (x, y) en el diagrama.

Gradiente de tangente:

ingrese la descripción de la imagen aquí

Gradiente de línea:

ingrese la descripción de la imagen aquí

Condición para líneas perpendiculares – producto de gradientes = -1:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Cuando se reorganiza y se sustituye en la ecuación de tu elipse…

ingrese la descripción de la imagen aquí

…esto dará dos desagradables ecuaciones cuarticas (polinomio de cuarto grado) en términos de x o y. AFAIK no hay general métodos analíticos (algebraicos exactos) para resolverlos. Puede probar un método iterativo: busque el algoritmo iterativo de búsqueda de raíces de Newton-Raphson.

Eche un vistazo a este muy buen documento sobre el tema: http://www.spaceroots.org/documents/distance/distance-to-ellipse.pdf

Perdón por la respuesta incompleta: culpo totalmente a las leyes de las matemáticas y la naturaleza …

EDITAR: Vaya, parece que tengo a y b al revés en el diagrama xD

Existe un método numérico relativamente simple con mejor convergencia que el método de Newton. Tengo una publicación de blog sobre por qué funciona http://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/

Esta implementación funciona sin funciones trigonométricas:

def solve(semi_major, semi_minor, p):  
    px = abs(p[0])
    py = abs(p[1])

    tx = 0.707
    ty = 0.707

    a = semi_major
    b = semi_minor

    for x in range(0, 3):
        x = a * tx
        y = b * ty

        ex = (a*a - b*b) * tx**3 / a
        ey = (b*b - a*a) * ty**3 / b

        rx = x - ex
        ry = y - ey

        qx = px - ex
        qy = py - ey

        r = math.hypot(ry, rx)
        q = math.hypot(qy, qx)

        tx = min(1, max(0, (qx * r / q + ex) / a))
        ty = min(1, max(0, (qy * r / q + ey) / b))
        t = math.hypot(ty, tx)
        tx /= t 
        ty /= t 

    return (math.copysign(a * tx, p[0]), math.copysign(b * ty, p[1]))

Convergencia

Crédito a Adrian Stephens por la optimización Trig-Free.

Aquí está el código traducido a C# implementado a partir de este documento para resolver la elipse: http://www.geometrictools.com/Documentation/DistancePointEllipseEllipsoid.pdf

Tenga en cuenta que este código no está probado; si encuentra algún error, hágamelo saber.

    //Pseudocode for robustly computing the closest ellipse point and distance to a query point. It
    //is required that e0 >= e1 > 0, y0 >= 0, and y1 >= 0.
    //e0,e1 = ellipse dimension 0 and 1, where 0 is greater and both are positive.
    //y0,y1 = initial point on ellipse axis (center of ellipse is 0,0)
    //x0,x1 = intersection point

    double GetRoot ( double r0 , double z0 , double z1 , double g )
    
        double n0 = r0*z0;
        double s0 = z1 - 1; 
        double s1 = ( g < 0 ? 0 : Math.Sqrt(n0*n0+z1*z1) - 1 ) ;
        double s = 0;
        for ( int i = 0; i < maxIter; ++i )
            s = ( s0 + s1 ) / 2 ;
            if ( s == s0 
        return s;
    
    double DistancePointEllipse( double e0 , double e1 , double y0 , double y1 , out double x0 , out double x1)
    
        double distance;
        if ( y1 > 0)
            if ( y0 > 0)
                double z0 = y0 / e0; 
                double z1 = y1 / e1; 
                double g = z0*z0+z1*z1 - 1;
                if ( g != 0)
                    double r0 = (e0/e1)*(e0/e1);
                    double sbar = GetRoot(r0 , z0 , z1 , g);
                    x0 = r0 * y0 /( sbar + r0 );
                    x1 = y1 /( sbar + 1 );
                    distance = Math.Sqrt( (x0-y0)*(x0-y0) + (x1-y1)*(x1-y1) );
                    else
                        x0 = y0; 
                        x1 = y1;
                        distance = 0;
                    
                
                else // y0 == 0
                    x0 = 0 ; x1 = e1 ; distance = Math.Abs( y1 - e1 );
        else // y1 == 0
            double numer0 = e0*y0 , denom0 = e0*e0 - e1*e1;
            if ( numer0 < denom0 )
                    double xde0 = numer0/denom0;
                    x0 = e0*xde0 ; x1 = e1*Math.Sqrt(1 - xde0*xde0 );
                    distance = Math.Sqrt( (x0-y0)*(x0-y0) + x1*x1 );
                else
                    x0 = e0; 
                    x1 = 0; 
                    distance = Math.Abs( y0 - e0 );
            
        
        return distance;
    

Reseñas y valoraciones

Si piensas que te ha sido de utilidad nuestro post, sería de mucha ayuda si lo compartieras con otros entusiastas de la programación de esta forma contrubuyes a extender este contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *