
En TikZ, quiero dibujar la proyección ortogonal desde un punto hasta una elipse (girada y desplazada). Como ejemplo particular, me gustaría dibujar la línea más corta desde el punto de la imagen hasta la elipse y preferiblemente marcar también el punto en la elipse:
He logrado hacer esto con un círculo (ya que el punto viene dado por la intersección con el círculo y la línea que pasa por el punto mismo y el centro del círculo). Pero con la elipse, parece que no consigo que funcione en TikZ.
El código de ejemplo para la imagen de arriba es el siguiente:
\documentclass{standalone}
\usepackage{tikz,tkz-euclide}
\begin{document}
\newcommand{\boundellipse}[3]% center, xdim, ydim
{(#1) ellipse (#2 and #3)
}
\begin{tikzpicture}
\draw[shift={(-0.875,0)},rotate=25] \boundellipse{0,0}{1}{3};%left
\node at (0,4)[circle,fill,inner sep=1.5pt]{};
\end{tikzpicture}
\end{document}
Respuesta1
Sugiero descenso de gradiente TikZ +
\documentclass[tikz]{standalone}
\usepackage{tikz,tkz-euclide}
\begin{document}
\newcommand{\boundellipse}[3]% center, xdim, ydim
{(#1) ellipse (#2 and #3)}
\makeatletter
\xdef\sx{-0.875} % shift x
\xdef\sy{0} % shift y
\xdef\ra{1} % radius a
\xdef\rb{3} % radius b
\xdef\ro{25} % rotation
\pgfpointxy{0}{4}
\xdef\Px{\the\pgf@x}\xdef\Py{\the\pgf@y}
% let \ang ("angle") be a free variable and run gradient descent
\def\ang{234} % choose your favorite initial value
\foreach\iterationcounter in{1,...,20}{
\begin{tikzpicture}
\draw(-5,-3)rectangle(1,5);
\draw[shift={(-0.875,0)},rotate=25] \boundellipse{0,0}{1}{3};
\node at (0,4)[circle,fill,inner sep=1.5pt]{};
% evaluate Ellipse(\ang)
\pgfpointxy{\sx + \rb*cos(\ang)*sin(\ro) + \ra*sin(\ang)*cos(\ro)}
{\sy - \rb*cos(\ang)*cos(\ro) + \ra*sin(\ang)*sin(\ro)}
\xdef\Qx{\the\pgf@x}\xdef\Qy{\the\pgf@y}
\draw(\Qx,\Qy)circle(.1);
% evaluate diff vector to target point
\xdef\Dx{\the\dimexpr\Px-\Qx}
\xdef\Dy{\the\dimexpr\Py-\Qy}
\draw[red,->](\Qx,\Qy)--+(\Dx,\Dy);
% evaluate tangent line = d Ellipse(\ang) / d\ang
\pgfpointxy{- \rb*sin(\ang)*sin(\ro) + \ra*cos(\ang)*cos(\ro)}
{+ \rb*sin(\ang)*cos(\ro) + \ra*cos(\ang)*sin(\ro)}
\xdef\Tx{\the\pgf@x}
\xdef\Ty{\the\pgf@y}
\draw[blue,->](\Qx,\Qy)--+(\Tx,\Ty);
% inner product
\pgfmathsetmacro\Inn{\Dx*\Tx + \Dy*\Ty}
% rescale inner product
\pgfmathsetmacro\inn{\Inn / sqrt(\Tx*\Tx+\Ty*\Ty)}
\message{^^J thinbold: \inn ^^J}
% update angle
\pgfmathsetmacro\ang{\ang + \inn/10} % /10 is the step length
\xdef\ang{\ang}
\end{tikzpicture}
}
\end{document}
Respuesta2
El problema matemático y el enfoque algorítmico
Como sugiere @Thruston, se necesitan matemáticas para resolver el problema. De todos modos, esto conduce a una ecuación no trivial (cuártica) que es difícil de resolver de forma analítica (echemos un vistazo apregunta similaroAnálisis de ecuaciones de distancia punto a elipse y punto a elipsoide). Entonces la idea es resolver esa ecuación numéricamente. Enhttps://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/Encontré un algoritmo geométrico y estable que encuentra el punto (proyección ortogonal) en la elipse minimizando la distancia desde el punto original.
el algoritmo
Los siguientes pasos y la imagen te sugerirán la idea.
- ConectarohyPAGpara poder conseguirUn inicio(esto permite ejecutar el algoritmo "en el lado derecho" de la elipse).
- Dibuja un círculo (azul) y obtén el punto medio de las dos intersecciones con el círculo azul y la elipse.
- Utilice el punto medio para dibujar un nuevo círculo más pequeño (morado) e iterar el proceso (es decir, rojo, naranja, rosa,...)
El código
El código necesita los paquetes tikz
y tkz-euclide
, en particular, \usetikzlibrary{intersections}
los puntos de intersección. Lo uso tkz-euclide
porque me siento bien con los comandos. De todos modos puedes obtener el mismo resultado en tikz puro.
\begin{tikzpicture}
% INITIAL DATA %
% the arbitrary point P
\tkzDefPoint(3,2){P}
% the center of the ellipse
\tkzDefPoint(0,0){O}
% use rotate=angle to set the desired orientation
\path[draw,name path=theellipse,rotate=20] (O) ellipse (2cm and 1cm);
\tkzLabelPoints[above right](P)
\tkzLabelPoints[below left](O)
% STARTING POINT OF ALGORITHM %
\path[name path=OP] (O)--(P);
\path[name intersections={of=OP and theellipse,by={Aone}}];
% comment/erase if need next three code lines
\tkzLabelPoint[above left](Aone){$A_{\textrm{start}}$}
\tkzDrawCircle[help lines](P,Aone)
\tkzDrawPoints(Aone)
% ALGORITHM TO FIND THE ORTHOGONAL PROJECTION %
% set up a different number of steps if needed
% (algorithm converges relatively fast)
\foreach \i in {1,...,3}
{
% define a circle with center P through Aone
% (Astart for the first step)
\tkzDefCircle[radius](P,Aone)
\tkzGetLength{dPAone}
\path[name path=circle] (P) circle (\dPAone pt);
% find intersections of circle with ellipse (Aone, Atwo)
\path[name intersections={of=circle and theellipse,by={Atwo,Aone}}];
% find a "proper" midpoint of Aone -- Atwo on the ellipse
\tkzDefMidPoint(Aone,Atwo)\tkzGetPoint{Aone}
\path[name path=PAone] (P)--(Aone);
\path[name intersections={of=PAone and theellipse,by={Aone}}];
}
% GET AND PRINT OUT THE DISTANCE
\tkzDrawPoints(O,P,Aone)
\tkzDrawSegment[red](P,Aone)
\end{tikzpicture}
Respuesta3
Sólo a modo de comparación, puedes hacer esto de forma muy sencilla enMetapostutilizando la solve
macro y una función auxiliar adecuada.
\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
beginfig(1);
path e; pair p; numeric k;
e = fullcircle xscaled 233 yscaled 144 rotated 10;
p = 160 dir 142;
vardef acute(expr t) =
direction t of e dotprod (p - point t of e) > 0
enddef;
k = solve acute(0, 4);
drawarrow p -- point k of e withcolor red;
draw e;
dotlabel.top(btex $p$ etex, p);
endfig;
\end{mplibcode}
\end{document}
Esto está incluido luamplib
para que puedas compilarlo con lualatex
.
Notas
solve
se explica en las páginas 176-177 deel libro metafont.La idea es que definas una macro
foo
de modo quefoo(x)
seatrue
ofalse
. Luego llamassolve foo(a, b)
a dondea
yb
son valores tales quefoo(a)
son verdaderos yfoo(b)
falsos.solve
utiliza una búsqueda binaria para encontrar el valor del borde entrea
yb
.En este caso he definido una macro llamada
acute
que usa eldotprod
operador para decirnos si la tangente en el puntot
de la elipse forma un ángulo agudo con la línea que va desdep
el puntot
de la elipse.solve
encuentra el punto en el que el ángulo ya no es agudo, que por tanto es el punto en el que la recta ap
es ortogonal a la tangente y, por tanto, es la más cercana ap
.Se requiere cierta habilidad y criterio para elegir los valores iniciales correctos para diferentes posiciones de
p
.
Como puedes ver mi explicación es bastante más larga que el código...