
In TikZ möchte ich die orthogonale Projektion von einem Punkt auf eine (gedrehte und verschobene) Ellipse zeichnen. Als konkretes Beispiel möchte ich die kürzeste Linie vom Punkt im Bild zur Ellipse zeichnen und vorzugsweise auch den Punkt auf der Ellipse markieren:
Bei einem Kreis ist mir das gelungen (da der Punkt einfach durch den Schnittpunkt mit dem Kreis und der Linie durch den Punkt selbst und den Mittelpunkt des Kreises gegeben ist). Bei der Ellipse scheint es in TikZ jedoch nicht zu funktionieren.
Der Beispielcode für das obige Bild lautet wie folgt:
\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}
Antwort1
Ich schlage TikZ + Gradientenabstieg vor
\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}
Antwort2
Das mathematische Problem und der algorithmische Ansatz
Wie @Thruston andeutet, ist Mathematik erforderlich, um das Problem zu lösen. Wie dem auch sei, dies führt zu einer nicht trivialen (quartischen) Gleichung, die auf analytische Weise schwer zu lösen ist (sehen wir uns entwederähnliche FrageoderAnalyse der Punkt-zu-Ellipse- und Punkt-zu-Ellipsoid-Distanzgleichungen). Die Idee ist also, eine Gleichung numerisch zu lösen.https://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/Ich habe einen geometrischen und stabilen Algorithmus gefunden, der den Punkt (orthogonale Projektion) auf der Ellipse findet, indem er die Entfernung vom ursprünglichen Punkt minimiert.
Der Algorithmus
Die folgenden Schritte und das Bild vermitteln einen Eindruck davon.
- VerbindenÖUndPum zu bekommenEin Anfang(dadurch kann der Algorithmus „auf der rechten Seite“ der Ellipse ausgeführt werden).
- Zeichnen Sie einen Kreis (blau) und ermitteln Sie den Mittelpunkt der beiden Schnittpunkte mit dem blauen Kreis und der Ellipse.
- Verwenden Sie den Mittelpunkt, um einen neuen, kleineren Kreis (lila) zu zeichnen, und wiederholen Sie den Vorgang (z. B. rot, orange, rosa, …).
Der Code
Der Code benötigt die Pakete tikz
und tkz-euclide
insbesondere \usetikzlibrary{intersections}
für die Schnittpunkte. Ich verwende, tkz-euclide
weil ich mit den Befehlen gut zurechtkomme. Trotzdem können Sie mit reinem Tikz dasselbe Ergebnis erzielen.
\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}
Antwort3
Nur zum Vergleich: Sie können dies ganz einfach inMetapostmithilfe des solve
Makros und einer passenden Hilfsfunktion.
\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}
Dies ist in zusammengefasst, luamplib
sodass Sie es mit kompilieren können lualatex
.
Anmerkungen
solve
wird auf den Seiten 176-177 vondas Metafont-Buch.Die Idee besteht darin, dass Sie ein Makro so definieren,
foo
dassfoo(x)
entwedertrue
oder istfalse
. Dann rufen Sie auf,solve foo(a, b)
wobeia
undb
Werte sind, sodassfoo(a)
wahr undfoo(b)
falsch ist.solve
verwendet eine binäre Suche, um den Randwert zwischena
und zu findenb
.In diesem Fall habe ich ein Makro namens definiert,
acute
das dendotprod
Operator verwendet, um uns mitzuteilen, ob die Tangente am Punktt
der Ellipse einen spitzen Winkel mit der Linie vomp
Punkt zum Punktt
der Ellipse bildet.solve
findet den Punkt, an dem der Winkel nicht mehr spitz ist, also den Punkt, an dem die Linie zup
orthogonal zur Tangente ist und daher am nächsten zu liegtp
.Es sind einiges Geschick und Urteilsvermögen erforderlich, um die richtigen Anfangswerte für verschiedene Positionen von auszuwählen
p
.
Wie Sie sehen, ist meine Erklärung etwas länger als der Code ...