Precisão de cálculos em TikZ com aritmética de ponto fixo

Precisão de cálculos em TikZ com aritmética de ponto fixo

Quero construir uma árvore que satisfaça o seguinte:

  1. Cresce para a direita.
  2. O primeiro filho cresce a 0 graus (os filhos subsequentes crescem no sentido horário).
  3. Todos os nós filhos estão na mesma vertical e a distância entre nós consecutivos é sempre a mesma.

Resumindo, o que eu gostaria de alcançar é o seguinte:

insira a descrição da imagem aqui

Na verdade, uma maneira fácil de codificar isso é através

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

No entanto, tentei uma abordagem alternativa, que esperava produzir os mesmos 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}

Observe que no código acima presumi que 1) a distância de nível do primeiro filho (horizontal) é de 3 cm e 2) o segundo filho cresce a -5 graus. Com essas duas condições e alguma trigonometria básica, pode-se calcular primeiro a distância irmã entre nós consecutivos e depois o ângulo ge a distância de nível lpara qualquer nó filho.

Acredito que minha parametrização esteja correta; no entanto, alguns ângulos/distâncias de nível irmãos estão ligeiramente errados,

insira a descrição da imagem aqui

embora (seguindo outras respostas) eu tenha usado o mecanismo aritmético de ponto fixo para tentar melhorar a precisão. Mesmo substituindo as funções trigonométricas pelos primeiros termos da sua série 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}

os mesmos problemas estão presentes (observe que o número de nós aqui foi escolhido de modo a garantir que a série atanconvirja):

insira a descrição da imagem aqui

Há algo que eu possa fazer para superar esses problemas de imprecisão ou é um problema intrínseco do TikZ que não pode ser evitado? (assumindo que não há nenhuma falha na minha abordagem, como penso ser o caso).

Responder1

Não sei bem por que a matemática parece falhar neste caso (embora eu suponha que deveria), mas parece que não é um problema com a forma como o TikZ/PGF faz a matemática, tem algo a ver com a forma como as árvores são construídas .

Infelizmente, não consegui descobrir qual é exatamente o problema, mas o fato de que o problema énãoa precisão dos cálculos matemáticos em si pode ser demonstrada usando os mesmos cálculos fora de uma árvore:

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

insira a descrição da imagem aqui

Então deve haver algo mais acontecendo.

De qualquer forma, penso que você está trabalhando muito com algo que poderia ser feitomuitomais simplesmente com uma função de crescimento personalizada.

O seguinte mostra um exemplo da grow via three pointsfunção de crescimento da 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}

insira a descrição da imagem aqui

Responder2

Uma solução 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}

saída

Tudo o que você precisa fazer é alterar os valores dos parâmetros ( \NoDots, \Hori, e \Vert) e o desenho será ajustado de acordo.

Responder3

Seu problema me deixou curioso. O MetaPost também usa aritmética de ponto fixo por padrão: um “numérico” não pode ser menor que 2^(-16)nem maior que 2^12, embora internamente o MetaPost possa lidar com valores até 2^15. (Desde muito recentemente é possível alternar para vários modos aritméticos de ponto flutuante, mas não os usarei aqui.)

Resolvi então fazer uma versão MetaPost do seu segundo gráfico, com as mesmas funções, e observar o alinhamento. (Para ser processado com 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}

Bem, pelo que posso ver, eles parecem alinhados corretamente:

insira a descrição da imagem aqui

Portanto, neste caso parece haver um problema, não com a aritmética de ponto fixo em si, mas com a biblioteca de ponto fixo de tikz(dois bits usados ​​para representar os números?). Ou com a forma como as funções trigonométricas são definidas.

De qualquer forma, se for possível mudar para aritmética de ponto flutuante tikz(talvez com LuaTeX?), isso melhoraria drasticamente a precisão.

informação relacionada