Verbesserung von TikZ-Kartoffeln

Verbesserung von TikZ-Kartoffeln

Mein Ziel ist es, Werkzeuge bereitzustellen, mit denen sich einige unregelmäßige Formen leichter zeichnen lassen. Natürlich kenne ich die glatten Plots von TikZ, aber mein Ziel ist es, einen alternativen Ansatz zu ermöglichen. Sehr oft werden die Formen mehr oder weniger durch einige Extrempunkte definiert, an denen der Abstand zum Schwerpunkt der Form extrem ist (Beispiele finden Sie weiter unten). Ich konnte einige Formen erhalten, aber nur auf Kosten der Übernahme von Routinen von anderen Stellen, die ich zitiere, und diese Routinen scheinen komplexer zu sein als unbedingt nötig. Außerdem behandelt mein Code nur 4 Koordinaten. Im Prinzip weiß ich jetzt, wie ich ihn erweitern kann, aber ich denke, ich sollte die Dinge vorher vereinfachen. Dies ist mein Code bisher:

\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}

Bildbeschreibung hier eingeben

Es ist nicht schwer zu erkennen, dass der Code ein echtes Durcheinander ist, aber er tut ungefähr das, was ich will, nämlich Kartoffeln zu zeichnen, die durch die Extrempunkte (mit schwarzen Punkten markiert) definiert sind. Zum Vergleich (Vergleiche sind nie fair;-) zeichne ich auch die Ergebnisse der oben erwähnten glatten Diagramme. Meine eigentliche Frage ist, wie man die Dinge einfacher machen kann, vor allem

  • die Winkelanordnung der Koordinaten,

  • meine Art die Formen "füllbar" zu machen ist sicher nicht optimal,

  • die Analyse der geordneten Koordinaten in ein Makro, das eine beliebige Anzahl von Punkten akzeptiert (also 4 oder mehr Punkte). Kommentare sind herzlich willkommen.

BEARBEITEN: Die Ausgabe des Hobby-Algorithmus wurde zum Vergleich in die Erdnussfigur eingefügt.

AKTUALISIEREN: „potatoe“ korrigiert. Mir ist klar, dass man diese Formen mit einigen zusätzlichen Anpassungen mit glatten Plots oder Hobby-Plots nachbilden kann. Ziel ist es hier jedoch, diese zusätzlichen Anpassungen zu vermeiden. Wenn mir jemand ein Wörterbuch zeigen könnte, mit dem ich den Potato-Code in die Hobby-Sprache übersetzen kann, wäre das natürlich großartig.

ZWEITES UPDATE: Ich habe eine einfache Animation hinzugefügt, die veranschaulicht, was der Code bisher macht.

\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}

Bildbeschreibung hier eingeben

Antwort1

Mit den Bibliotheken mathund calcist es möglich, es etwas einfacher zu machen. Sie können zwei beliebige Koordinaten umdrehen, um zu sehen, ob es auch für Koordinaten funktioniert, die noch nicht geordnet sind (ich habe nur das Umdrehen Aund getestet, Baber es sollte für alle funktionieren).

Ich habe zwischendurch nichts geändert \begin{document}und \end{document}„sodass“ \Distanceist immer noch da, aber ich verwende es nicht in meiner Definition von „ \DrawPotatosodass Sie es nicht wirklich brauchen“.

Bildbeschreibung hier eingeben

\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}

verwandte Informationen