
В TikZ я хочу нарисовать ортогональную проекцию из точки на (повернутый и смещенный) эллипс. В качестве конкретного примера я хотел бы нарисовать кратчайшую линию от точки на картинке до эллипса и желательно также отметить точку на эллипсе:
Мне удалось сделать это с окружностью (поскольку точка просто задана пересечением с окружностью и линией, проходящей через саму точку и центр окружности). Но с эллипсом я, похоже, не могу заставить это работать в TikZ.
Пример кода для приведенного выше изображения выглядит следующим образом:
\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}
решение1
Я предлагаю 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}
решение2
Математическая задача и алгоритмический подход
Как предполагает @Thruston, для решения проблемы нужна математика. В любом случае это приводит к нетривиальному (четвертому) уравнению, которое трудно решить аналитическим способом (давайте рассмотриманалогичный вопросилиАнализ уравнения расстояния от точки до эллипса и от точки до эллипсоида). Идея состоит в том, чтобы решить это уравнение численно. Наhttps://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/Я нашел геометрический и стабильный алгоритм, который находит точку (ортогональную проекцию) на эллипсе, минимизируя расстояние от исходной точки.
Алгоритм
Следующие шаги и изображение подскажут вам эту идею.
- СоединятьОипдля того, чтобы получитьНачало(это позволяет запустить алгоритм «с правой стороны» эллипса).
- Нарисуйте круг (синий) и найдите середину двух пересечений с синим кругом и эллипсом.
- Используйте среднюю точку, чтобы нарисовать новый меньший круг (фиолетовый) и повторите процесс (т. е. красный, оранжевый, розовый,...)
Код
Коду нужны пакеты tikz
и tkz-euclide
, в частности, \usetikzlibrary{intersections}
для точек пересечения. Я использую, tkz-euclide
потому что чувствую себя хорошо с командами. В любом случае вы можете получить тот же результат в чистом tikz.
\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}
решение3
Для сравнения, вы можете сделать это очень просто вМетапостиспользуя solve
макрос и подходящую вспомогательную функцию.
\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}
Он упакован в , luamplib
поэтому вы можете скомпилировать его с помощью lualatex
.
Примечания
solve
объясняется на страницах 176-177книга Метафонт.Идея состоит в том, что вы определяете макрос
foo
таким образом, чтоfoo(x)
либоtrue
илиfalse
. Затем вы вызываете ,solve foo(a, b)
гдеa
иb
являются значениями такими, чтоfoo(a)
является истинным иfoo(b)
является ложным.solve
использует двоичный поиск для нахождения граничного значения междуa
иb
.В этом случае я определил макрос
acute
, который используетdotprod
оператор, чтобы сообщить нам, образует ли касательная в точкеt
эллипса острый угол с линией, проходящей черезp
точкуt
эллипса.solve
находит точку, в которой угол больше не является острым, то есть точку, в которой прямая кp
ортогональна касательной, и, следовательно, является ближайшей кp
.Для выбора правильных начальных значений для различных положений требуются определенные навыки и рассудительность
p
.
Как видите, мое объяснение гораздо длиннее кода...