![Улучшение картофеля TikZ](https://rvso.com/image/370329/%D0%A3%D0%BB%D1%83%D1%87%D1%88%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%BA%D0%B0%D1%80%D1%82%D0%BE%D1%84%D0%B5%D0%BB%D1%8F%20TikZ.png)
Моя цель — предоставить инструменты для более легкого рисования некоторых нерегулярных фигур. Конечно, я знаю о гладких графиках, предоставляемых TiкZ, но моя цель — сделать так, чтобы обеспечить альтернативный подход. Очень часто формы более или менее определяются некоторыми экстремальными точками, в которых расстояние до барицентра формы экстремально (примеры приведены ниже). Мне удалось получить некоторые формы, но только за счет заимствования процедур из некоторых других мест, которые я цитирую, и эти процедуры кажутся более сложными, чем необходимо. Кроме того, мой код обрабатывает только 4 координаты. В принципе, я знаю, как его расширить, но я чувствую, что должен сначала упростить вещи. Вот мой код на данный момент:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{hobby}
\makeatletter % from https://tex.stackexchange.com/a/283273/121799
% Here we define the comparison macro for pairs (a,b)
% We assume decimal numbers acceptable to \ifdim tests
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
\long\def\@thirdoffour #1#2#3#4{#3}%
\long\def\@fourthoffour #1#2#3#4{#4}%
\def\IfFirstPairIsGreaterTF #1#2{\@IfFirstPairIsGreaterTF #1,#2,}%
\def\@IfFirstPairIsGreaterTF #1,#2,#3,#4,{%
\ifdim #1\p@=#3\p@
\xintdothis{%
\ifdim #2\p@>#4\p@\expandafter\@firstoftwo
\else\expandafter\@secondoftwo\fi}\fi
\ifdim #1\p@>#3\p@\expandafter\@thirdoffour
\else\expandafter\@fourthoffour\fi
\xintorthat{}%
}%
% not needed for numerical inputs
% \catcode`! 3
% \catcode`? 3
% Here there is a very strange \romannumeral0\romannumeral0, this is
% due to some convoluted scheme to avoid double spaces or no spaces
% in between coordinate pairs. Trust me.
\def\QSpairs {\romannumeral0\romannumeral0\qspairs }%
% first we check if empty list
\def\qspairs #1{\expandafter\qspairs@a\romannumeral-`0#1(!)(?)}%
\def\qspairs@a #1(#2{\ifx!#2\expandafter\qspairs@abort\else
\expandafter\qspairs@b\fi (#2}%
\edef\qspairs@abort #1(?){\space\space}%
%
% we check if empty of single and if not pick up the first as Pivot:
\def\qspairs@b #1(#2)#3(#4){\ifx?#4\xintdothis\qspairs@empty\fi
\ifx!#4\xintdothis\qspairs@single\fi
\xintorthat \qspairs@separate {}{}{#2}(#4)}%
\def\qspairs@empty #1(?){ }%
\edef\qspairs@single #1#2#3#4(?){\space\space(#3)}%
\def\qspairs@separate #1#2#3#4(#5)%
{%
\ifx!#5\expandafter\qspairs@separate@done\fi
\IfFirstPairIsGreaterTF {#5}{#3}%
\qspairs@separate@appendtogreater
\qspairs@separate@appendtosmaller {#5}{#1}{#2}{#3}%
}%
%
\def\qspairs@separate@appendtogreater #1#2{\qspairs@separate {#2 (#1)}}%
\def\qspairs@separate@appendtosmaller #1#2#3{\qspairs@separate {#2}{#3 (#1)}}%
%
\def\qspairs@separate@done\IfFirstPairIsGreaterTF #1#2%
\qspairs@separate@appendtogreater
\qspairs@separate@appendtosmaller #3#4#5#6(?)%
{%
\expandafter\qspairs@f\expandafter
{\romannumeral0\qspairs@b #4(!)(?)}{\qspairs@b #5(!)(?)}{ (#2)}%
}%
%
\def\qspairs@f #1#2#3{#2#3#1}%
%
% \catcode`! 12
% \catcode`? 12
\makeatother
\makeatletter % from https://tex.stackexchange.com/a/412901/121799
\newcommand{\Distance}[3]{% % from https://tex.stackexchange.com/q/56353/121799
\tikz@scan@one@point\pgfutil@firstofone($#1-#2$)\relax
\pgfmathsetmacro{#3}{veclen(\the\pgf@x,\the\pgf@y)/28.45274}
}
\makeatother
\newcount\nbofwords
\makeatletter% from https://tex.stackexchange.com/a/12819/121799
\def\myutil@empty{}
\def\multiwords#1 #2\@nil{%
\def\NextArg{#2}%
\advance\nbofwords by 1 %
\expandafter\edef\csname word\@alph\nbofwords\endcsname{#1}%
\ifx\myutil@empty\NextArg
\let\next\@gobble
\fi
\next#2\@nil
}%
\def\GetWords#1{%
\let\next\multiwords
\nbofwords=0 %
\expandafter\next#1 \@nil %
}%
\makeatother
\long\def\First(#1,#2){#1}
\long\def\Second(#1,#2){#2}
\tikzset{declare
function={interpolator(\x,\xmin,\xmax,\rmin,\rmax)=(\rmin+\rmax)/2+((\rmin-\rmax)/2)*cos((\x-\xmin)*(180/(\xmax-\xmin)));}}
%\tikzset{declare function={PotatoeRadius(\x,\angleA,\angleB,\angleC,\angleD,\distanceA,\distanceB,\distanceC,\distanceD)=\distanceA+(\x-\angleA)*((\distanceB-\distanceA)/(\angleB-\angleA)+(\x-\angleB)*(((-1)*((\distanceB-\distanceA)/(\angleB-\angleA))+(\distanceC-\distanceB)/(\angleC-\angleB))/(\angleC-\angleA)+(((-1)*(((-1)*((\distanceB-\distanceA)/(\angleB-\angleA))+(\distanceC-\distanceB)/(\angleC-\angleB))/(\angleC-\angleA))+((-1)*((\distanceC-\distanceB)/(\angleC-\angleB))+(\distanceD-\distanceC)/(\angleD-\angleC))/(\angleD-\angleB))*(\x-\angleC))/(\angleD-\angleA)));}}
%(\angleC*(\angleC-\angleD)*\angleD*((\distanceA-\distanceB)*(\angleC-\x)*(\angleD-\x)*\x+pow(\angleA,3)*(-(\angleC*pow(\angleD,2)*\distanceB)+\angleD*(\distanceB-\distanceC)*(\angleD-\x)*\x+\angleC*(\distanceB-\distanceD)*pow(\x,2)+pow(\angleC,2)*(\angleD*\distanceB+(-\distanceB+\distanceD)*\x))+pow(\angleB,3)*(-(\angleA*pow(\angleD,2)*\distanceC)-\angleD*(\distanceA-\distanceC)*(\angleD-\x)*\x+\angleA*(\distanceC-\distanceD)*pow(\x,2)+pow(\angleC,2)*(-(\angleD*\distanceA)+\angleA*\distanceD+\distanceA*\x-\distanceD*\x)+pow(\angleA,2)*(\angleD*\distanceC-\distanceC*\x+\distanceD*\x)+\angleC*(pow(\angleD,2)*\distanceA-pow(\angleA,2)*\distanceD+(-\distanceA+\distanceD)*pow(\x,2)))+pow(\angleA,2)*(-(\angleD*(\distanceB-\distanceC)*(\angleD-\x)*\x*(\angleD+\x))-pow(\angleC,3)*(\angleD*\distanceB+(-\distanceB+\distanceD)*\x)+\angleC*(pow(\angleD,3)*\distanceB+(-\distanceB+\distanceD)*pow(\x,3)))+\angleA*(pow(\angleD,2)*(\distanceB-\distanceC)*(\angleD-\x)*pow(\x,2)+pow(\angleC,3)*(pow(\angleD,2)*\distanceB+(-\distanceB+\distanceD)*pow(\x,2))-pow(\angleC,2)*(pow(\angleD,3)*\distanceB+(-\distanceB+\distanceD)*pow(\x,3)))+pow(\angleB,2)*(\angleD*(\distanceA-\distanceC)*(\angleD-\x)*\x*(\angleD+\x)+pow(\angleC,3)*(\angleD*\distanceA-\angleA*\distanceD-\distanceA*\x+\distanceD*\x)-pow(\angleA,3)*(\angleD*\distanceC+(-\distanceC+\distanceD)*\x)+\angleC*(-(pow(\angleD,3)*\distanceA)+pow(\angleA,3)*\distanceD+(\distanceA-\distanceD)*pow(\x,3))+\angleA*(pow(\angleD,3)*\distanceC+(-\distanceC+\distanceD)*pow(\x,3)))+\angleB*(-(pow(\angleD,2)*(\distanceA-\distanceC)*(\angleD-\x)*pow(\x,2))+pow(\angleC,3)*(-(pow(\angleD,2)*\distanceA)+pow(\angleA,2)*\distanceD+(\distanceA-\distanceD)*pow(\x,2))+pow(\angleA,3)*(pow(\angleD,2)*\distanceC+(-\distanceC+\distanceD)*pow(\x,2))+pow(\angleC,2)*(pow(\angleD,3)*\distanceA-pow(\angleA,3)*\distanceD+(-\distanceA+\distanceD)*pow(\x,3))-pow(\angleA,2)*(pow(\angleD,3)*\distanceC+(-\distanceC+\distanceD)*pow(\x,3))))/((\angleA-\angleB)*(\angleA-\angleC)*(\angleB-\angleC)*(\angleA-\angleD)*(\angleB-\angleD)*(\angleC-\angleD)));}}
\newcommand{\DrawArcAngle}[6][]{% just for emergencies
\pgfmathanglebetweenpoints{\pgfpointanchor{#3}{center}}{\pgfpointanchor{#2}{center}}
\xdef\angleA{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{#3}{center}}{\pgfpointanchor{#4}{center}}
\xdef\angleB{\pgfmathresult}
\draw[#1] ($(#3)+(\angleA:#5)$) arc [start angle=\angleA,end angle=\angleB,radius=#5]
#6;
}
\newcommand{\DrawPotato}[5][]{
\coordinate (PotatoCenter) at (barycentric cs:#2=1,#3=1,#4=1,#5=1);
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#2}{center}}
\xdef\angleA{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#3}{center}}
\xdef\angleB{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#4}{center}}
\xdef\angleC{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#5}{center}}
\xdef\angleD{\pgfmathresult}
\Distance{(PotatoCenter)}{(#2)}{\distanceA}
\Distance{(PotatoCenter)}{(#3)}{\distanceB}
\Distance{(PotatoCenter)}{(#4)}{\distanceC}
\Distance{(PotatoCenter)}{(#5)}{\distanceD}
\xdef\coordList{(\angleA,\distanceA) (\angleB,\distanceB) (\angleC,\distanceC) (\angleD,\distanceD)}%
\typeout{\coordList}
\xdef\sortedList{\QSpairs{\coordList}}%
\GetWords{\sortedList}
\xdef\NewList{\worda,\wordb,\wordc,\wordd}%
\xdef\NewList{\expandafter\First\worda/\expandafter\Second\worda,
\expandafter\First\wordb/\expandafter\Second\wordb,
\expandafter\First\wordc/\expandafter\Second\wordc,
\expandafter\First\wordd/\expandafter\Second\wordd}% this list is not used
\xdef\angleA{\expandafter\First\worda}%
\xdef\distanceA{\expandafter\Second\worda}%
\xdef\angleB{\expandafter\First\wordb}%
\xdef\distanceB{\expandafter\Second\wordb}%
\xdef\angleC{\expandafter\First\wordc}%
\xdef\distanceC{\expandafter\Second\wordc}%
\xdef\angleD{\expandafter\First\wordd}%
\xdef\distanceD{\expandafter\Second\wordd}%
\begin{scope}[shift=(PotatoCenter)]
\draw[#1,smooth,samples=50] plot[variable=\x,domain=\angleA:\angleB] %
(\x:{interpolator(\x,\angleA,\angleB,\distanceA,\distanceB)})
--
plot[variable=\x,domain=\angleB:\angleC] %
(\x:{interpolator(\x,\angleB,\angleC,\distanceB,\distanceC)})
--
plot[variable=\x,domain=\angleC:\angleD] %
(\x:{interpolator(\x,\angleC,\angleD,\distanceC,\distanceD)})
--
plot[variable=\x,domain=\angleD:{\angleA+360}] %
(\x:{interpolator(\x,\angleD,{\angleA+360},\distanceD,\distanceA)});
\end{scope}
}
\begin{document}
\begin{tabular}{cc}
potato & potato (ordering) \\
\begin{tikzpicture}
\coordinate (A) at (-1,1);
\coordinate (B) at (3,0.5);
\coordinate (C) at (4,-1);
\coordinate (D) at (1,-1);
\foreach \p in {A,B,C,D}
{\draw[fill=black] (\p) circle (1pt);}
\DrawPotato[blue]{A}{B}{C}{D}
\draw[red] plot[smooth cycle,tension=1] coordinates{(A) (B) (C) (D)};
\end{tikzpicture}&
\begin{tikzpicture}
\coordinate (A) at (-1,1);
\coordinate (B) at (3,0.5);
\coordinate (C) at (4,-1);
\coordinate (D) at (1,-1);
\foreach \p in {A,B,C,D}
{\draw[fill=black] (\p) circle (1pt);}
\DrawPotato[blue]{A}{C}{B}{D}
\draw[purple] plot[smooth cycle,tension=1] coordinates{(A) (C) (B) (D)};
\end{tikzpicture}
\end{tabular}
\begin{tabular}{ll}
filled potato&filled peanut\\
\begin{tikzpicture}
\coordinate (A) at (-1,1);
\coordinate (B) at (3,0.5);
\coordinate (C) at (4,-1);
\coordinate (D) at (1,-1);
\foreach \p in {A,B,C,D}
{\draw[fill=black] (\p) circle (1pt);}
\DrawPotato[fill=blue]{A}{C}{B}{D}
\end{tikzpicture}
&
\begin{tikzpicture}
\coordinate (A) at (-1,1);
\coordinate (B) at (2,0.5);
\coordinate (C) at (4,-1);
\coordinate (D) at (1,-1);
\foreach \p in {A,B,C,D}
{\draw[fill=black] (\p) circle (1pt);}
\DrawPotato[blue,fill=red]{A}{B}{C}{D}
\begin{scope}[closed hobby]
\draw[thick] plot coordinates{(A) (B) (C) (D)};
\end{scope}
\end{tikzpicture}
\end{tabular}
\end{document}
Нетрудно заметить, что код — настоящий бардак, но он делает примерно то, что мне нужно, а именно рисует картофелины, определяемые крайними точками (отмеченными черными маркерами). Для сравнения (сравнения никогда не бывают честными ;-) я также рисую результаты вышеупомянутых гладких графиков. Мой настоящий вопрос — как можно сделать все проще, самое главное
угловой порядок координат,
Мой способ сделать формы «заполняемыми» определенно не является оптимальным,
разбор упорядоченных координат в макрос, который принимает произвольное количество точек (т.е. 4 или более точек). Любые комментарии приветствуются.
РЕДАКТИРОВАТЬ: Для сравнения добавлен вывод алгоритма Хобби на рисунке с арахисом.
ОБНОВЛЯТЬ: Исправлено "potatoe". Мне ясно, что с некоторым количеством дополнительных корректировок можно воссоздать эти формы с помощью гладких участков или участков Hobby. Однако цель здесь — избежать этих дополнительных корректировок. Конечно, если бы кто-то мог указать мне словарь, который позволяет мне перевести код potato на язык Hobby, это было бы здорово.
ВТОРОЕ ОБНОВЛЕНИЕ: Я добавил простую анимацию, иллюстрирующую, что код делает на данный момент.
\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{calc}
%\usetikzlibrary{hobby}
\makeatletter % from https://tex.stackexchange.com/a/283273/121799
% Here we define the comparison macro for pairs (a,b)
% We assume decimal numbers acceptable to \ifdim tests
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
\long\def\@thirdoffour #1#2#3#4{#3}%
\long\def\@fourthoffour #1#2#3#4{#4}%
\def\IfFirstPairIsGreaterTF #1#2{\@IfFirstPairIsGreaterTF #1,#2,}%
\def\@IfFirstPairIsGreaterTF #1,#2,#3,#4,{%
\ifdim #1\p@=#3\p@
\xintdothis{%
\ifdim #2\p@>#4\p@\expandafter\@firstoftwo
\else\expandafter\@secondoftwo\fi}\fi
\ifdim #1\p@>#3\p@\expandafter\@thirdoffour
\else\expandafter\@fourthoffour\fi
\xintorthat{}%
}%
% not needed for numerical inputs
% \catcode`! 3
% \catcode`? 3
% Here there is a very strange \romannumeral0\romannumeral0, this is
% due to some convoluted scheme to avoid double spaces or no spaces
% in between coordinate pairs. Trust me.
\def\QSpairs {\romannumeral0\romannumeral0\qspairs }%
% first we check if empty list
\def\qspairs #1{\expandafter\qspairs@a\romannumeral-`0#1(!)(?)}%
\def\qspairs@a #1(#2{\ifx!#2\expandafter\qspairs@abort\else
\expandafter\qspairs@b\fi (#2}%
\edef\qspairs@abort #1(?){\space\space}%
%
% we check if empty of single and if not pick up the first as Pivot:
\def\qspairs@b #1(#2)#3(#4){\ifx?#4\xintdothis\qspairs@empty\fi
\ifx!#4\xintdothis\qspairs@single\fi
\xintorthat \qspairs@separate {}{}{#2}(#4)}%
\def\qspairs@empty #1(?){ }%
\edef\qspairs@single #1#2#3#4(?){\space\space(#3)}%
\def\qspairs@separate #1#2#3#4(#5)%
{%
\ifx!#5\expandafter\qspairs@separate@done\fi
\IfFirstPairIsGreaterTF {#5}{#3}%
\qspairs@separate@appendtogreater
\qspairs@separate@appendtosmaller {#5}{#1}{#2}{#3}%
}%
%
\def\qspairs@separate@appendtogreater #1#2{\qspairs@separate {#2 (#1)}}%
\def\qspairs@separate@appendtosmaller #1#2#3{\qspairs@separate {#2}{#3 (#1)}}%
%
\def\qspairs@separate@done\IfFirstPairIsGreaterTF #1#2%
\qspairs@separate@appendtogreater
\qspairs@separate@appendtosmaller #3#4#5#6(?)%
{%
\expandafter\qspairs@f\expandafter
{\romannumeral0\qspairs@b #4(!)(?)}{\qspairs@b #5(!)(?)}{ (#2)}%
}%
%
\def\qspairs@f #1#2#3{#2#3#1}%
%
% \catcode`! 12
% \catcode`? 12
\makeatother
\makeatletter % from https://tex.stackexchange.com/a/412901/121799
\newcommand{\Distance}[3]{% % from https://tex.stackexchange.com/q/56353/121799
\tikz@scan@one@point\pgfutil@firstofone($#1-#2$)\relax
\pgfmathsetmacro{#3}{veclen(\the\pgf@x,\the\pgf@y)/28.45274}
}
\makeatother
\newcount\nbofwords
\makeatletter% from https://tex.stackexchange.com/a/12819/121799
\def\myutil@empty{}
\def\multiwords#1 #2\@nil{%
\def\NextArg{#2}%
\advance\nbofwords by 1 %
\expandafter\edef\csname word\@alph\nbofwords\endcsname{#1}%
\ifx\myutil@empty\NextArg
\let\next\@gobble
\fi
\next#2\@nil
}%
\def\GetWords#1{%
\let\next\multiwords
\nbofwords=0 %
\expandafter\next#1 \@nil %
}%
\makeatother
\long\def\First(#1,#2){#1}
\long\def\Second(#1,#2){#2}
\tikzset{declare
function={interpolator(\x,\xmin,\xmax,\rmin,\rmax)=(\rmin+\rmax)/2+((\rmin-\rmax)/2)*cos((\x-\xmin)*(180/(\xmax-\xmin)));}}
%\tikzset{declare function={PotatoeRadius(\x,\angleA,\angleB,\angleC,\angleD,\distanceA,\distanceB,\distanceC,\distanceD)=\distanceA+(\x-\angleA)*((\distanceB-\distanceA)/(\angleB-\angleA)+(\x-\angleB)*(((-1)*((\distanceB-\distanceA)/(\angleB-\angleA))+(\distanceC-\distanceB)/(\angleC-\angleB))/(\angleC-\angleA)+(((-1)*(((-1)*((\distanceB-\distanceA)/(\angleB-\angleA))+(\distanceC-\distanceB)/(\angleC-\angleB))/(\angleC-\angleA))+((-1)*((\distanceC-\distanceB)/(\angleC-\angleB))+(\distanceD-\distanceC)/(\angleD-\angleC))/(\angleD-\angleB))*(\x-\angleC))/(\angleD-\angleA)));}}
%(\angleC*(\angleC-\angleD)*\angleD*((\distanceA-\distanceB)*(\angleC-\x)*(\angleD-\x)*\x+pow(\angleA,3)*(-(\angleC*pow(\angleD,2)*\distanceB)+\angleD*(\distanceB-\distanceC)*(\angleD-\x)*\x+\angleC*(\distanceB-\distanceD)*pow(\x,2)+pow(\angleC,2)*(\angleD*\distanceB+(-\distanceB+\distanceD)*\x))+pow(\angleB,3)*(-(\angleA*pow(\angleD,2)*\distanceC)-\angleD*(\distanceA-\distanceC)*(\angleD-\x)*\x+\angleA*(\distanceC-\distanceD)*pow(\x,2)+pow(\angleC,2)*(-(\angleD*\distanceA)+\angleA*\distanceD+\distanceA*\x-\distanceD*\x)+pow(\angleA,2)*(\angleD*\distanceC-\distanceC*\x+\distanceD*\x)+\angleC*(pow(\angleD,2)*\distanceA-pow(\angleA,2)*\distanceD+(-\distanceA+\distanceD)*pow(\x,2)))+pow(\angleA,2)*(-(\angleD*(\distanceB-\distanceC)*(\angleD-\x)*\x*(\angleD+\x))-pow(\angleC,3)*(\angleD*\distanceB+(-\distanceB+\distanceD)*\x)+\angleC*(pow(\angleD,3)*\distanceB+(-\distanceB+\distanceD)*pow(\x,3)))+\angleA*(pow(\angleD,2)*(\distanceB-\distanceC)*(\angleD-\x)*pow(\x,2)+pow(\angleC,3)*(pow(\angleD,2)*\distanceB+(-\distanceB+\distanceD)*pow(\x,2))-pow(\angleC,2)*(pow(\angleD,3)*\distanceB+(-\distanceB+\distanceD)*pow(\x,3)))+pow(\angleB,2)*(\angleD*(\distanceA-\distanceC)*(\angleD-\x)*\x*(\angleD+\x)+pow(\angleC,3)*(\angleD*\distanceA-\angleA*\distanceD-\distanceA*\x+\distanceD*\x)-pow(\angleA,3)*(\angleD*\distanceC+(-\distanceC+\distanceD)*\x)+\angleC*(-(pow(\angleD,3)*\distanceA)+pow(\angleA,3)*\distanceD+(\distanceA-\distanceD)*pow(\x,3))+\angleA*(pow(\angleD,3)*\distanceC+(-\distanceC+\distanceD)*pow(\x,3)))+\angleB*(-(pow(\angleD,2)*(\distanceA-\distanceC)*(\angleD-\x)*pow(\x,2))+pow(\angleC,3)*(-(pow(\angleD,2)*\distanceA)+pow(\angleA,2)*\distanceD+(\distanceA-\distanceD)*pow(\x,2))+pow(\angleA,3)*(pow(\angleD,2)*\distanceC+(-\distanceC+\distanceD)*pow(\x,2))+pow(\angleC,2)*(pow(\angleD,3)*\distanceA-pow(\angleA,3)*\distanceD+(-\distanceA+\distanceD)*pow(\x,3))-pow(\angleA,2)*(pow(\angleD,3)*\distanceC+(-\distanceC+\distanceD)*pow(\x,3))))/((\angleA-\angleB)*(\angleA-\angleC)*(\angleB-\angleC)*(\angleA-\angleD)*(\angleB-\angleD)*(\angleC-\angleD)));}}
\newcommand{\DrawArcAngle}[6][]{% just for emergencies
\pgfmathanglebetweenpoints{\pgfpointanchor{#3}{center}}{\pgfpointanchor{#2}{center}}
\xdef\angleA{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{#3}{center}}{\pgfpointanchor{#4}{center}}
\xdef\angleB{\pgfmathresult}
\draw[#1] ($(#3)+(\angleA:#5)$) arc [start angle=\angleA,end angle=\angleB,radius=#5]
#6;
}
\newcommand{\DrawPotato}[5][]{
\coordinate (PotatoCenter) at (barycentric cs:#2=1,#3=1,#4=1,#5=1);
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#2}{center}}
\xdef\angleA{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#3}{center}}
\xdef\angleB{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#4}{center}}
\xdef\angleC{\pgfmathresult}
\pgfmathanglebetweenpoints{\pgfpointanchor{PotatoCenter}{center}}{\pgfpointanchor{#5}{center}}
\xdef\angleD{\pgfmathresult}
\Distance{(PotatoCenter)}{(#2)}{\distanceA}
\Distance{(PotatoCenter)}{(#3)}{\distanceB}
\Distance{(PotatoCenter)}{(#4)}{\distanceC}
\Distance{(PotatoCenter)}{(#5)}{\distanceD}
\xdef\coordList{(\angleA,\distanceA) (\angleB,\distanceB) (\angleC,\distanceC) (\angleD,\distanceD)}%
\typeout{\coordList}
\xdef\sortedList{\QSpairs{\coordList}}%
\GetWords{\sortedList}
\xdef\NewList{\worda,\wordb,\wordc,\wordd}%
\xdef\NewList{\expandafter\First\worda/\expandafter\Second\worda,
\expandafter\First\wordb/\expandafter\Second\wordb,
\expandafter\First\wordc/\expandafter\Second\wordc,
\expandafter\First\wordd/\expandafter\Second\wordd}% this list is not used
\xdef\angleA{\expandafter\First\worda}%
\xdef\distanceA{\expandafter\Second\worda}%
\xdef\angleB{\expandafter\First\wordb}%
\xdef\distanceB{\expandafter\Second\wordb}%
\xdef\angleC{\expandafter\First\wordc}%
\xdef\distanceC{\expandafter\Second\wordc}%
\xdef\angleD{\expandafter\First\wordd}%
\xdef\distanceD{\expandafter\Second\wordd}%
\begin{scope}[shift=(PotatoCenter)]
\draw[#1,smooth,samples=50] plot[variable=\x,domain=\angleA:\angleB] %
(\x:{interpolator(\x,\angleA,\angleB,\distanceA,\distanceB)})
--
plot[variable=\x,domain=\angleB:\angleC] %
(\x:{interpolator(\x,\angleB,\angleC,\distanceB,\distanceC)})
--
plot[variable=\x,domain=\angleC:\angleD] %
(\x:{interpolator(\x,\angleC,\angleD,\distanceC,\distanceD)})
--
plot[variable=\x,domain=\angleD:{\angleA+360}] %
(\x:{interpolator(\x,\angleD,{\angleA+360},\distanceD,\distanceA)});
\end{scope}
}
\begin{document}
\begin{frame}
\frametitle{What does the current code do?}
\begin{overlayarea}{\textwidth}{\textheight}
\begin{tikzpicture}
\path[use as bounding box] (-5,-3) rectangle (7,5);
\node[text width=8cm] (text) at (0,3){%
\only<1>{input: four points, here A, B, C \& D}
\only<2>{step one: compute the barycenter of those points}
\only<3>{imagine now a circle around the barycenter with radius equal to the
average distance}
\only<4>{the code simply adds some sine functions to the radius such that the
contour runs through the points and the distance is extremal at those points}
\only<5>{however, the code is extremely clumsy and slow, and I have the feeling
that I'm re--inventing the wheel when dealing with lists etc.}
};
\coordinate (A) at (-1,1);
\coordinate (B) at (2,0.5);
\coordinate (C) at (4,-1);
\coordinate (D) at (1,-1);
\coordinate (Center) at (barycentric cs:A=1,B=1,C=1,D=1);
\pgfmathsetmacro{\TotalDistance}{0}
\xdef\DistanceList{}
\foreach \p in {A,B,C,D}
{\draw[fill=black] (\p) circle (1pt) node[below]{\p};
}
\Distance{(Center)}{(A)}{\DistanceA}
\Distance{(Center)}{(B)}{\DistanceB}
\Distance{(Center)}{(C)}{\DistanceC}
\Distance{(Center)}{(D)}{\DistanceD}
\pgfmathsetmacro{\AverageDistance}{(\DistanceA+\DistanceB+\DistanceC+\DistanceD)/4}
\pause
\draw[fill=black] (Center) circle (1pt) node[below]{Center};
\pause
\draw[-,dashed] (Center) circle (\AverageDistance);
\pause
\only<4->{
\DrawPotato[blue]{A}{B}{C}{D}
}
\end{tikzpicture}
\end{overlayarea}
\end{frame}
\end{document}
решение1
С библиотеками math
и calc
это можно сделать немного проще. Вы можете перевернуть любые две координаты, чтобы увидеть, что это также работает для координат, которые еще не упорядочены (я тестировал только переворачивание A
и , B
но это должно работать для всех из них).
Я ничего не менял между \begin{document}
и \end{document}
так\Distance
он все еще там, но я не использую его в своем определении, \DrawPotato
так что он вам на самом деле не нужен.
\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{calc,math}
% I don't use this in my \DrawPotato
\makeatletter % from https://tex.stackexchange.com/a/412901/121799
\newcommand{\Distance}[3]{% % from https://tex.stackexchange.com/q/56353/121799
\tikz@scan@one@point\pgfutil@firstofone($#1-#2$)\relax
\pgfmathsetmacro{#3}{veclen(\the\pgf@x,\the\pgf@y)/28.45274}
}
\makeatother
\tikzset{
declare function={
interpolator(\x,\xmin,\xmax,\rmin,\rmax)=
1/28.4527*((\rmin+\rmax)/2+((\rmin-\rmax)/2)*cos((\x-\xmin)*(180/(\xmax-\xmin))));
}
}
\newcommand\DrawPotato[5][]{%
\tikzmath{
coordinate \p,\cent;
\p0 = {(#2)};
\p1 = {(#3)};
\p2 = {(#4)};
\p3 = {(#5)};
\cent = ({(\px0+\px1+\px2+\px3)/4},{(\py0+\py1+\py2+\py3)/4}); % Average point
real \angl,\dist;
int \i,\j;
for \i in {0,1,2,3}{
\angl{\i} = atan2(\py{\i}-\centy,\px{\i}-\centx);
\dist{\i} = veclen(\py{\i}-\centy,\px{\i}-\centx);
};
for \i in {0,1,2,3}{
for \j in {0,1,2,3}{
if min(\angl{0},\angl{1},\angl{2},\angl{3}) == \angl{\j} then {
\sortang{\i} = \angl{\j};
\sortind{\i} = \j;
};
};
\angl{\sortind{\i}} = 500; % Arbitrary large value
\sortdis{\i} = \dist{\sortind{\i}};
};
}
\draw[#1,smooth,samples=50,shift={(\cent)}]
plot[variable=\x,domain=\sortang{0}:\sortang{1}] %
(\x:{interpolator(\x,{\sortang{0}},{\sortang{1}},{\sortdis{0}},{\sortdis{1}})}) --
plot[variable=\x,domain=\sortang{1}:\sortang{2}] %
(\x:{interpolator(\x,{\sortang{1}},{\sortang{2}},{\sortdis{1}},{\sortdis{2}})}) --
plot[variable=\x,domain=\sortang{2}:\sortang{3}] %
(\x:{interpolator(\x,{\sortang{2}},{\sortang{3}},{\sortdis{2}},{\sortdis{3}})}) --
plot[variable=\x,domain=\sortang{3}:\sortang{0}+360] %
(\x:{interpolator(\x,{\sortang{3}},{\sortang{0}+360},{\sortdis{3}},{\sortdis{0}})}) --
cycle ;
}
\begin{document}
\begin{frame}
\frametitle{What does the current code do?}
\begin{overlayarea}{\textwidth}{\textheight}
\begin{tikzpicture}
\path[use as bounding box] (-5,-3) rectangle (7,5);
\node[text width=8cm] (text) at (0,3){%
\only<1>{input: four points, here A, B, C \& D}
\only<2>{step one: compute the barycenter of those points}
\only<3>{imagine now a circle around the barycenter with radius equal to the
average distance}
\only<4>{the code simply adds some sine functions to the radius such that the
contour runs through the points and the distance is extremal at those points}
\only<5>{however, the code is extremely clumsy and slow, and I have the feeling
that I'm re--inventing the wheel when dealing with lists etc.}
};
\coordinate (A) at (-1,1);
\coordinate (B) at (2,0.5);
\coordinate (C) at (4,-1);
\coordinate (D) at (1,-1);
\coordinate (Center) at (barycentric cs:A=1,B=1,C=1,D=1);
\pgfmathsetmacro{\TotalDistance}{0}
\xdef\DistanceList{}
\foreach \p in {A,B,C,D}
{\draw[fill=black] (\p) circle (1pt) node[below]{\p};
}
\Distance{(Center)}{(A)}{\DistanceA}
\Distance{(Center)}{(B)}{\DistanceB}
\Distance{(Center)}{(C)}{\DistanceC}
\Distance{(Center)}{(D)}{\DistanceD}
\pgfmathsetmacro{\AverageDistance}{(\DistanceA+\DistanceB+\DistanceC+\DistanceD)/4}
\pause
\draw[fill=black] (Center) circle (1pt) node[below]{Center};
\pause
\draw[-,dashed] (Center) circle (\AverageDistance);
\pause
\only<4->{
\DrawPotato[blue]{A}{B}{C}{D}
}
\end{tikzpicture}
\end{overlayarea}
\end{frame}
\end{document}