Coordenadas 3D simbólicas em TikZ

Coordenadas 3D simbólicas em TikZ

Atenção: responder a esta pergunta pode exigir alguns esforços. O objetivo da pergunta é "ensinar TikCoordenadas Z 3d". O que isso significa? Se definirmos uma coordenada em TikZ,

 \path (<x>,<y>) coordinate(A);

esta coordenada Aé associada a 2 comprimentos que especificam a localização. Em qualquer sistema de coordenadas transformado (deslocado, girado, inclinado), ainda podemos nos referir a esta coordenada e, digamos, desenhar uma seta para ela. O que é mais importante para esta questão é que podemos sempre trabalhar de trás para frente e descobrir qual é a localização relativa de outra coordenada, por exemplo, com a biblioteca calc

\path let \p1=($(A)-(B)$),\n1={veclen(\x1,\y1)},\n2={atan2(\y1,\x1)} in <do something with this information>;

Isso é impossível em 3D, pois TikZ trunca as coordenadas.

Uma maneira possível de lidar com isso foi proposta emesta bela resposta. Isso é ótimo, mas não funciona tão bem quanto a sintaxe de cálculo mencionada acima. Talvez o mais importante seja que é necessário fazer esforços extras para armazenar as coordenadas 3D. Idealmente, alguém teria algo como

 \path (x,y,z) coordinate(A);

e TikzZ também se lembraria da coordenada.

Observe que esse pedido pode parecer mais inocente à primeira vista do que realmente é. Em 2d, temos um referencial predefinido, as coordenadas da tela. Além disso, as rotações formam um grupo abeliano, por isso é menos complicado acompanhá-las e invertê-las. A resposta mencionada acima salva as coordenadas em frames locais, portanto é impossível comparar coordenadas em frames diferentes. No entanto, isso seria fundamental para muitas aplicações, nas quais se muda para, digamos, canvas is xy plane at z=0. Idealmente, uma resposta a esta questão deveria associar cada ponto simbólico a cerca de três comprimentos que são as coordenadas num referencial habilmente escolhido, e deveria haver meios para determinar a localização relativa de dois pontos de uma forma independente de coordenadas, de forma semelhante a veclenem 2d.

No melhor de todos os mundos, uma resposta também viria com um analisador apropriado que nos permitisse fazer produtos escalares, produtos vetoriais, calcular a norma de um vetor e fazer multiplicações de matrizes, ou seja, transformações ortogonais. (Acho que ir além das transformações ortogonais é uma bagunça porque então a inversão de matrizes será realmente complicada.) Algum progresso em relação à análise foi feito norespostas desta perguntamas, novamente, é provavelmente justo dizer que isso ainda não é tão conveniente quanto as contrapartes 2D.

As respostas podem ou não ser baseadas em tikz-3dplot. ( tikz-3dplotvem com boas projeções ortonormais.) Claro, a melhor de todas as opções seria algo que também funcionasse com oPerspectiva de três pontosbiblioteca.

Observe que algumas operações matriciais foram implementadas no calculatorpacote. É um pacote impressionante que contém muitas coisas e suas rotinas podem ser úteis para a tarefa aqui. Se existem ou não outros pacotes deste tipo, não sei.

Responder1

É possível inventar algo nesse sentido. Esses são alguns resultados nessa direção.

Ponto principal

Pode-se hackear TikZ para gravar o vielbein. Supondo que o usuário tenha uma visão ortográfica, dois vetores de base são suficientes. Esses dois vetores básicos têm os componentes e_1=(\pgf@xx,\pgf@yx,\pgf@zx)e e_2=(\pgf@xy,\pgf@yy,\pgf@zy), a normal à tela é simplesmente e_3=e_1 x e_2. A distância (virtual) de uma coordenada da tela é chamada de "profundidade da tela" a partir de agora. Simplesmente é p.e_3, onde pestá um ponto.

Para gravar o vielbein automaticamente, é necessário "hackear" o TikZ (ou defina um estilo para isso). Então, se você não se sentir confortável fazendo nada disso, pare de ler.

Limitações

A partir de agora, isso funciona apenas para coordenadas/nós criados em coordenadas cartesianas, e os fatores de escala (ainda?) não são levados em consideração. Além disso, pode ser desejável ter uma sintaxe

\path let \p1=(A) in <do something with \z1>;

onde \z1 é a profundidade da tela. Isto (ainda?) não está implementado.

Exemplo explícito

Este código define uma função screendepthque retorna a profundidade da tela mencionada acima. Claramente, é independente do sistema de coordenadas. Em particular, se quisermos obter uma ordenação 3D, os objetos com profundidades de tela maiores deverão ser desenhados por último. Funciona independentemente de como você instala a visualização 3D. Por exemplo, poderíamos ter usado tikz-3dplotem vez da perspectivebiblioteca.

\documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{calc,perspective}
\makeatletter
\pgfmathdeclarefunction{tdnormal}{6}{\begingroup
\pgfmathsetmacro\pgfutil@tmpa{(#2/1cm)*(#6)-(#3/1cm)*(#5)}%
\pgfmathsetmacro\pgfutil@tmpb{(#3/1cm)*(#4)-(#1/1cm)*(#6)}%
\pgfmathsetmacro\pgfutil@tmpc{(#1/1cm)*(#5)-(#2/1cm)*(#4)}%
\edef\pgfmathresult{\pgfutil@tmpa,\pgfutil@tmpb,\pgfutil@tmpc}%
\pgfmathsmuggle\pgfmathresult%
\endgroup}%
\pgfmathdeclarefunction{screendepth}{1}{\begingroup
\def\tikz@td@pp(##1){\edef\pgfutil@tmp{\csname tikz@dcl@coord@##1\endcsname}}%
\edef\pgfutil@tmp{\csname tikz@dcl@coord@#1\endcsname}% 
\loop
\pgfutil@tempcnta=0%
\pgfutil@for\pgf@tmp:={\pgfutil@tmp}\do{\advance\pgfutil@tempcnta by1}%
\ifnum\pgfutil@tempcnta=1\relax
\expandafter\tikz@td@pp\pgfutil@tmp%
\repeat
\edef\pgfmathresult{0}%
\ifcase\pgfutil@tempcnta
\message{Something is wrong here.^^J}
\or
\message{Something is wrong here.^^J}
\or
\or
\edef\tikz@td@vielbein{\csname tikz@vielbein@#1\endcsname}%
\pgfmathsetmacro{\tikz@td@normal}{tdnormal(\tikz@td@vielbein)}%
\def\tikz@td@strip@brackets(##1,##2,##3)##4,##5,##6;{%
\edef\pgf@tmp{(##1)*(##4)+(##2)*(##5)+(##3)*(##6)}}%
\edef\temp{\noexpand\tikz@td@strip@brackets\pgfutil@tmp\tikz@td@normal;}%
\temp
\pgfmathparse{\pgf@tmp}%
\fi
\pgfmathsmuggle\pgfmathresult%
\endgroup}
\def\tikz@@fig@main{%
    \pgfutil@ifundefined{pgf@sh@s@\tikz@shape}%
    {\tikzerror{Unknown shape ``\tikz@shape.'' Using ``rectangle'' instead}%
      \def\tikz@shape{rectangle}}%
    {}%
    \expandafter\xdef\csname tikz@dcl@coord@\tikz@fig@name\endcsname{%
      \csname tikz@scan@point@coordinate\endcsname}%
    \expandafter\xdef\csname tikz@vielbein@\tikz@fig@name\endcsname{%
       \the\pgf@xx,\the\pgf@xy,\the\pgf@yx,\the\pgf@yy,\the\pgf@zx,\the\pgf@zy}%
    \expandafter\xdef\csname tikz@trafo@\tikz@fig@name\endcsname{%
       {{\pgf@pt@aa,\pgf@pt@ab},{\pgf@pt@ba,\pgf@pt@bb},%
       {\the\pgf@pt@x,\the\pgf@pt@y}}}%
    \tikzset{every \tikz@shape\space node/.try}%
    \tikz@node@textfont%
    \tikz@node@begin@hook%
    \iftikz@is@matrix%
      \let\tikz@next=\tikz@do@matrix%
    \else%
      \let\tikz@next=\tikz@do@fig%
    \fi%
    \tikz@next%
}%
\makeatother

\begin{document}
\begin{tikzpicture}[dot/.style={circle,fill,inner sep=1.2pt}]
 \begin{scope}[3d view]
  \draw[-stealth] (0,0,0) -- (2,0,0) node[pos=1.2]{$\vec x$};
  \draw[-stealth] (0,0,0) -- (0,2,0) node[pos=1.2]{$\vec y$};
  \draw[-stealth] (0,0,0) -- (0,0,2) node[pos=1.2]{$\vec z$};
  \path[nodes=dot] (1,2,3) node (A){} (4,5) node (B){} (A) node (C){};
  \path let \p1=(A),\p2=(B),\p3=(C) in 
   (A) node[above] {$A=({}$\x1,\y1,\pgfmathparse{screendepth("A")}\pgfmathresult pt)}
   (B) node[above] {$B=({}$\x2,\y2,\pgfmathparse{screendepth("B")}\pgfmathresult pt)}
   (C) node[below] {$C=({}$\x3,\y3,\pgfmathparse{screendepth("C")}\pgfmathresult pt)};
 \end{scope}  
 \begin{scope}[xshift=6cm,3d view={110}{20}]
  \draw[-stealth] (0,0,0) -- (2,0,0) node[pos=1.2]{$\vec x'$};
  \draw[-stealth] (0,0,0) -- (0,2,0) node[pos=1.2]{$\vec y'$};
  \draw[-stealth] (0,0,0) -- (0,0,2) node[pos=1.2]{$\vec z'$};
  \path[nodes=dot] (1,2,3) node (A'){} (4,5) node (B'){} (A') node (C'){};
  \path let \p1=(A'),\p2=(B'),\p3=(C') in 
   (A') node[above] {$A'=({}$\x1,\y1,\pgfmathparse{screendepth("A'")}\pgfmathresult pt)}
   (B') node[above] {$B'=({}$\x2,\y2,\pgfmathparse{screendepth("B'")}\pgfmathresult pt)}
   (C') node[below] {$C'=({}$\x3,\y3,\pgfmathparse{screendepth("C'")}\pgfmathresult pt)};
 \end{scope}  
\end{tikzpicture}
\end{document}

insira a descrição da imagem aqui

O resultado não é cativante nem nada, mas uma tentativa de fazer ordenação 3D em TikZ um pouco menos complicado.

Alternativamente, pode-se "hackear" calcem vez de TikZ. Este hack não é completamente simétrico, é preciso referir-se à coordenada pelo seu nome original e, claro, não se pode usar algo como ($(A)+(B)$). Fazer isso exigiria uma cirurgia mais substancial. No entanto, você pode obter os componentes "físicos" com a calcsintaxe.

\documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{calc,perspective}
\makeatletter
\pgfmathdeclarefunction{tdnormal}{6}{\begingroup
\pgfmathsetmacro\pgfutil@tmpa{(#2/1cm)*(#6)-(#3/1cm)*(#5)}%
\pgfmathsetmacro\pgfutil@tmpb{(#3/1cm)*(#4)-(#1/1cm)*(#6)}%
\pgfmathsetmacro\pgfutil@tmpc{(#1/1cm)*(#5)-(#2/1cm)*(#4)}%
\edef\pgfmathresult{\pgfutil@tmpa,\pgfutil@tmpb,\pgfutil@tmpc}%
\pgfmathsmuggle\pgfmathresult%
\endgroup}%
\pgfmathdeclarefunction{z3d}{1}{\begingroup
\def\tikz@td@pp(##1){\edef\pgfutil@tmp{\csname tikz@dcl@coord@##1\endcsname}}%
\edef\pgfutil@tmp{\csname tikz@dcl@coord@#1\endcsname}% 
\loop
\pgfutil@tempcnta=0%
\pgfutil@for\pgf@tmp:={\pgfutil@tmp}\do{\advance\pgfutil@tempcnta by1}%
\ifnum\pgfutil@tempcnta=1\relax
\expandafter\tikz@td@pp\pgfutil@tmp%
\repeat
\edef\pgfmathresult{0}%
\ifcase\pgfutil@tempcnta
\message{Something is wrong here.^^J}%
\or
\message{Something is wrong here.^^J}%
\or
\or
\pgfmathsetmacro{\tikz@td@normal}{tdnormal(\the\pgf@xx,\the\pgf@xy,\the\pgf@yx,\the\pgf@yy,\the\pgf@zx,\the\pgf@zy)}%
\def\tikz@td@strip@brackets(##1,##2,##3)##4,##5,##6;{%
\edef\pgf@tmp{(##1)*(##4)+(##2)*(##5)+(##3)*(##6)}}%
\edef\temp{\noexpand\tikz@td@strip@brackets\pgfutil@tmp\tikz@td@normal;}%
\temp
\pgfmathparse{\pgf@tmp}%
\fi
\pgfmathsmuggle\pgfmathresult%
\endgroup}
\def\tikz@let@command et{%
  \let\p=\tikz@cc@dop%
  \let\x=\tikz@cc@dox%
  \let\y=\tikz@cc@doy%
  \let\z=\tikz@cc@doz%
  \let\n=\tikz@cc@don%
  \pgfutil@ifnextchar i{\tikz@cc@stop@let}{\tikz@cc@handle@line}%
}%
\def\tikz@cc@doz#1{\csname tikz@cc@z@#1\endcsname}%
\def\tikz@cc@dolet#1{%
  \pgf@process{#1}%
  \expandafter\edef\csname tikz@cc@p@\tikz@cc@coord@name\endcsname{\the\pgf@x,\the\pgf@y}%
  \expandafter\edef\csname tikz@cc@x@\tikz@cc@coord@name\endcsname{\the\pgf@x}%
  \expandafter\edef\csname tikz@cc@y@\tikz@cc@coord@name\endcsname{\the\pgf@y}%
  \pgfutil@ifnextchar,{\tikz@cc@handle@nextline}{\tikz@cc@stop@let}%
}%
\tikzset{record z/.style={execute at end node={%
\pgfmathparse{z3d("\tikz@fig@name")}%
\expandafter\xdef\csname tikz@cc@z@\tikz@fig@name\endcsname{\pgfmathresult pt}}}}
\makeatother

\begin{document}
\begin{tikzpicture}[dot/.style={circle,fill,inner sep=1.2pt,record z}]
 \begin{scope}[3d view]
  \draw[-stealth] (0,0,0) -- (2,0,0) node[pos=1.2]{$\vec x$};
  \draw[-stealth] (0,0,0) -- (0,2,0) node[pos=1.2]{$\vec y$};
  \draw[-stealth] (0,0,0) -- (0,0,2) node[pos=1.2]{$\vec z$};
  \path[nodes=dot] (1,2,3) node (A){} (4,5) node (B){} (A) node (C){};
  \path let \p1=(A),\p2=(B),\p3=(C) in 
   (A) node[above] {$A=({}$\x1,\y1,\z{A})}
   (B) node[above] {$B=({}$\x2,\y2,\z{B}\pgfmathresult pt)}
   (C) node[below] {$C=({}$\x3,\y3,\z{C})};
 \end{scope}  
 \begin{scope}[xshift=6cm,3d view={110}{20}]
  \draw[-stealth] (0,0,0) -- (2,0,0) node[pos=1.2]{$\vec x'$};
  \draw[-stealth] (0,0,0) -- (0,2,0) node[pos=1.2]{$\vec y'$};
  \draw[-stealth] (0,0,0) -- (0,0,2) node[pos=1.2]{$\vec z'$};
  \path[nodes=dot] (1,2,3) node (A'){} (4,5) node (B'){} (A') node (C'){};
  \path let \p1=(A'),\p2=(B'),\p3=(C'),\p4=(A),\p5=(B),\p6=(C) in 
   (A') node[above] {$A'=({}$\x1,\y1,\z{A'})}
   (B') node[above] {$B'=({}$\x2,\y2,\z{B'})}
   (C') node[below] {$C'=({}$\x3,\y3,\z{C'})}
   (A) edge[edge label={\pgfmathparse{sqrt(pow(\x1/1cm-\x4/1cm,2)+pow(\y1/1cm-\y4/1cm,2)+pow(\z{A}/1cm-\z{A'}/1cm,2))}%
   $d=\pgfmathprintnumber\pgfmathresult$cm}] (A');
 \end{scope}  
\end{tikzpicture}
\end{document}

insira a descrição da imagem aqui

Observe que a z3dfunção pode ser usada independentemente de possíveis hacks, porém, ela calcula o zcomponente assumindo que o usuário não mudou seu sistema de coordenadas.

informação relacionada