Precisión de cálculos en TikZ con aritmética de punto fijo

Precisión de cálculos en TikZ con aritmética de punto fijo

Quiero construir un árbol que satisfaga lo siguiente:

  1. Crece hacia la derecha.
  2. El primer niño crece a 0 grados (los niños siguientes crecen en el sentido de las agujas del reloj).
  3. Todos los nodos secundarios están en la misma vertical y la distancia entre nodos consecutivos es siempre la misma.

Considerándolo todo, lo que me gustaría lograr es esto:

ingrese la descripción de la imagen aquí

De hecho, una manera fácil de codificar es a través de

\documentclass{article}
\usepackage{tikz}
\tikzset{bcir/.style={circle,fill=black,
 minimum size=4pt,inner sep=0},every node/.style={bcir}}
\begin{document}
\begin{tikzpicture}[x=3cm,y=2mm]
\node (A) {};
\foreach \i in {0,...,8} \node at (1,-\i) {} edge (A);
\end{tikzpicture}
\end{document}

Sin embargo, probé un enfoque alternativo, que esperaba que produjera los mismos resultados:

\documentclass{article}
\usepackage{tikz}
\usepackage{fp}
\usetikzlibrary{fixedpointarithmetic}
\tikzset{bcir/.style={circle,fill=black,
minimum size=4pt,inner sep=0},every node/.style={bcir}}

\begin{document}
\begin{tikzpicture}[fixed point arithmetic]  
\node {}
  child[grow=\g,level distance=\l cm] 
  foreach \i [evaluate={
     \k=tan(5)*\i;
     \g=-atan(\k);
     \l=3/cos(\g);
  }] in {0,...,13} {node {}};
\end{tikzpicture}  
\end{document}

Tenga en cuenta que en el código anterior he asumido que 1) la distancia de nivel del primer niño (horizontal) es de 3 cm y 2) el segundo niño crece a -5 grados. Con estas dos condiciones y algo de trigonometría básica, se puede calcular primero la distancia entre hermanos entre nodos consecutivos y luego el ángulo gy la distancia de nivel lpara cualquier nodo secundario.

Creo que mi parametrización es correcta; sin embargo, algunos ángulos/distancias de nivel entre hermanos están ligeramente desviados,

ingrese la descripción de la imagen aquí

aunque (después de otras respuestas) utilicé el motor aritmético de punto fijo para intentar mejorar la precisión. Incluso reemplazando las funciones trigonométricas por los primeros términos de su serie de Taylor,

\begin{tikzpicture}[fixed point arithmetic]  
\node {}
  child[grow=\g,level distance=\l cm] 
  foreach \i [evaluate={
     \k=tan(5)*\i;
     \grad=\k-\k^3/3+\k^5/5-\k^7/7+\k^9/9;
     \g=-deg(\grad);
     \l=3/(1-\grad^2/2!+\grad^4/4!-\grad^6/6!);
  }] in {0,...,8} {node {}};
\end{tikzpicture}

Están presentes los mismos problemas (tenga en cuenta que el número de nodos aquí se ha elegido para garantizar que la serie atanconverja):

ingrese la descripción de la imagen aquí

¿Hay algo que pueda hacer para superar estos problemas de inexactitud o es un problema intrínseco de TikZ que no se puede evitar? (suponiendo que no haya ningún defecto en mi enfoque, como creo que es el caso).

Respuesta1

No sé muy bien por qué las matemáticas parecen fallar en este caso (aunque supongo que debería hacerlo), pero parece que no es un problema con la forma en que TikZ/PGF hace las matemáticas, tiene algo que ver con la forma en que se construyen los árboles. .

Desafortunadamente, no he podido determinar cuál es exactamente el problema, pero el hecho de que el problema seanoLa precisión de los cálculos matemáticos per se se puede demostrar utilizando los mismos cálculos fuera de un árbol:

\documentclass[tikz, border=5]{standalone}
\tikzset{bcir/.style={circle,fill=black,
  minimum size=4pt,inner sep=0}, every node/.style={bcir}}

\begin{document}
\begin{tikzpicture}[every node/.style={bcir, anchor=center}]
\node {} child [grow=\g, level distance=\l cm] foreach \i [evaluate={%
  \k=tan(5)*\i; \g=-atan(\k); \l=3/cos(\g);}]
  in {0,...,13} { node {} };

\foreach \i [evaluate={%
  \k=tan(5)*\i; \g=-atan(\k); \l=3/cos(\g);}]
  in {0,...,13} { \draw [red] (0,0) -- (\g:\l) node [bcir,fill=red]{}; }
\end{tikzpicture}  
\end{document}

ingrese la descripción de la imagen aquí

Entonces debe haber algo más sucediendo.

En cualquier caso, creo que estás trabajando mucho en algo que se podría hacer.muchomás simplemente con una función de crecimiento personalizada.

A continuación se muestra un ejemplo de la grow via three pointsfunción de crecimiento de la treesbiblioteca.

\documentclass[tikz, border=5]{standalone}
\usetikzlibrary{trees}
\tikzset{bcir/.style={circle,fill=black,
  minimum size=4pt,inner sep=0}, every node/.style={bcir}}

\begin{document}
\begin{tikzpicture}[grow via three points={%
  one child at (3,0) and two children at (3,0) and (3,-1/4)
}]
\node {} child foreach \i in {0,...,13} { node {} };
\end{tikzpicture}  
\end{document}

ingrese la descripción de la imagen aquí

Respuesta2

Una solución de PSTricks:

\documentclass{article}

\usepackage{multido}
\usepackage{pstricks}
\usepackage{xfp}

% parameters
\def\NoDots{9}
\def\Hori{4}
\def\Vert{3}

\begin{document}

\begin{pspicture}(\Hori,\Vert)
  \psdot(0,\Vert)
  \multido{\r = \Vert+-\fpeval{\Vert/(\NoDots-1)}}{\NoDots}{%
    \psline(0,\Vert)(\Hori,\r)
    \psdot(\Hori,\r)}
\end{pspicture}

\end{document}

producción

Todo lo que tienes que hacer es cambiar los valores de los parámetros ( \NoDots, \Horiy \Vert) y el dibujo se ajustará en consecuencia.

Respuesta3

Tu problema me dio curiosidad. MetaPost también utiliza aritmética de punto fijo por defecto: un “numérico” no puede ser menor 2^(-16)ni mayor que 2^12, aunque internamente MetaPost puede manejar valores hasta 2^15. (Desde hace poco es posible cambiar a varios modos aritméticos de punto flotante, pero no los usaré aquí).

Así que decidí hacer una versión MetaPost de tu segundo gráfico, con las mismas funciones, y observar la alineación. (Para ser procesado con LuaLaTeX).

\documentclass[border=2mm]{standalone}
\usepackage{luamplib}
  \mplibsetformat{metafun}  
\begin{document}
  \begin{mplibcode}
    u := cm;
    def dot(expr c) = drawdot c withpen pencircle scaled 3bp enddef;
    beginfig(1);
      dot(origin); 
      for i = 0 upto 13:
        k := i*tand5; % or i*sind5/cosd5 with Plain MetaPost
        g := -atan k ; % or -angle(1, k) with Plain MetaPost; 
        l := 3u/cosd g;
        draw origin -- l*dir g;
        dot(l*dir g);
      endfor
    endfig;
  \end{mplibcode}
\end{document}

Bueno, por lo que puedo ver, parecen alineados correctamente:

ingrese la descripción de la imagen aquí

Entonces, en este caso parece haber un problema, no con la aritmética de punto fijo en sí misma, sino con la biblioteca de punto fijo de tikz(¿dos pocos bits utilizados para representar los números?). O con la forma en que se definen las funciones trigonométricas.

De todos modos, si es posible cambiar a la aritmética de punto flotante tikz(¿quizás con LuaTeX?), mejoraría drásticamente la precisión.

información relacionada