Coordenadas simbólicas 3d en TikZ

Coordenadas simbólicas 3d en TikZ

Advertencia: responder a esta pregunta puede requerir algunos esfuerzos. El propósito de la pregunta es "enseñarle a TikCoordenadas Z 3d". ¿Qué significa eso? Si definimos una coordenada en Tikz,

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

esta coordenada Ase asocia con 2 longitudes que especifican la ubicación. En cualquier sistema de coordenadas transformado (desplazado, rotado, inclinado), aún podemos referirnos a esta coordenada y, digamos, dibujarle una flecha. Lo que es más importante para esta pregunta es que siempre podemos trabajar hacia atrás y determinar cuál es la ubicación relativa a otra coordenada, por ejemplo, con la biblioteca calc.

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

Esto es imposible en 3d desde Ti.kZ trunca las coordenadas.

Una posible forma de abordar esto ha sido propuesta enesta buena respuesta. Esto es genial, pero no funciona tan bien como la sintaxis calc mencionada anteriormente. Quizás lo más importante es que hay que hacer esfuerzos adicionales para almacenar las coordenadas 3D. Lo ideal sería tener algo como

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

y tikzZ también recordaría la coordenada.

Tenga en cuenta que esta petición puede parecer más inocente a primera vista de lo que realmente es. En 2d, tenemos un marco de referencia predefinido, las coordenadas de la pantalla. Además, las rotaciones forman un grupo abeliano, por lo que es menos engorroso seguirlas e invertirlas. La respuesta mencionada anteriormente guarda las coordenadas en marcos locales, por lo que es imposible comparar coordenadas en diferentes marcos. Sin embargo, esto sería fundamental para muchas aplicaciones, en las que se cambia a, por ejemplo, canvas is xy plane at z=0. Idealmente, una respuesta a esta pregunta debería asociar cada punto simbólico con unas tres longitudes que son las coordenadas en un sistema de referencia inteligentemente elegido, y debería haber medios para determinar la ubicación relativa de dos puntos de manera independiente de las coordenadas, de manera similar a como ocurre veclenen 2do.

En el mejor de los mundos, una respuesta también vendría con un analizador apropiado que nos permita hacer productos escalares, productos vectoriales, calcular la norma de un vector y hacer multiplicaciones de matrices, es decir, transformaciones ortogonales. (Creo que ir más allá de las transformaciones ortogonales es un desastre porque entonces la inversión de matrices será realmente engorrosa). Se han logrado algunos avances con respecto al análisis en elrespuestas de esta preguntapero nuevamente, probablemente sea justo decir que todavía no es tan conveniente como sus contrapartes 2d.

Las respuestas pueden basarse o no en tikz-3dplot. ( tikz-3dplotviene con bonitas proyecciones ortonormales). Por supuesto, la mejor de todas las opciones sería algo que también funcione con elPerspectiva de tres puntosbiblioteca.

Tenga en cuenta que algunas operaciones matriciales se han implementado en el calculatorpaquete. Es un paquete impresionante con muchas cosas y sus rutinas pueden ser útiles para la tarea aquí. No sé si existen o no otros paquetes de este tipo.

Respuesta1

Es posible cocinar algo parecido. Estos son algunos resultados en esa dirección.

Punto principal

Uno puede hackear TikZ para grabar el vielbein. Suponiendo que el usuario tenga una vista ortográfica, dos vectores base son suficientes. Estos dos vectores base tienen los componentes e_1=(\pgf@xx,\pgf@yx,\pgf@zx)y e_2=(\pgf@xy,\pgf@yy,\pgf@zy), la normal a la pantalla es simplemente e_3=e_1 x e_2. La distancia (virtual) de una coordenada desde la pantalla se denomina a partir de ahora "profundidad de pantalla". Simplemente es p.e_3, donde phay un punto.

Para grabar el vielbein automáticamente, es necesario "piratear" TikZ (o definir un estilo para eso). Entonces, si no te sientes cómodo haciendo ninguna de estas cosas, deja de leer.

Limitaciones

A partir de ahora, esto funciona sólo para coordenadas/nodos creados en coordenadas cartesianas, y los factores de escala no se tienen (¿todavía?) en cuenta. Además, podría ser deseable tener una sintaxis

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

donde \z1 es la profundidad de la pantalla. Esto no está (¿todavía?) implementado.

Ejemplo explícito

Este código define una función screendepthque devuelve la profundidad de pantalla mencionada anteriormente. Claramente, es independiente del sistema de coordenadas. En particular, si uno quiere lograr un orden 3D, los objetos con mayores profundidades de pantalla deben dibujarse al final. Funciona independientemente de cómo instales la vista 3D. Por ejemplo, podríamos haber usado tikz-3dploten lugar de la 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}

ingrese la descripción de la imagen aquí

El resultado no es pegadizo ni nada más que un intento de realizar pedidos en 3D en Ti.kZ un poco menos engorroso.

Alternativamente se puede "hackear" calcen lugar de TikZ. Este truco no es completamente simétrico, hay que referirse a la coordenada por su nombre original y, por supuesto, no se puede usar algo como ($(A)+(B)$). Hacer esto requeriría una cirugía más sustancial. Sin embargo, puedes obtener los componentes "físicos" con la calcsintaxis.

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

ingrese la descripción de la imagen aquí

Tenga en cuenta que la z3dfunción se puede utilizar independientemente de posibles hacks; sin embargo, calcula el zcomponente suponiendo que el usuario no cambió su sistema de coordenadas.

información relacionada