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 A
se 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 tikz
Z 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 veclen
en 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-3dplot
viene 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 calculator
paquete. 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 p
hay 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 screendepth
que 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-3dplot
en lugar de la perspective
biblioteca.
\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}
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" calc
en 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 calc
sintaxis.
\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}
Tenga en cuenta que la z3d
función se puede utilizar independientemente de posibles hacks; sin embargo, calcula el z
componente suponiendo que el usuario no cambió su sistema de coordenadas.