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.
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:
Gradiente de línea:
Condición para líneas perpendiculares – producto de gradientes = -1:
Cuando se reorganiza y se sustituye en la ecuación de tu elipse…
…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]))
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.