TikZ 中的符号 3d 坐标

TikZ 中的符号 3d 坐标

警告:回答这个问题可能需要一些努力。问题的目的是“教 TiZ 3d 坐标”。这是什么意思?如果我们在 Ti 中定义一个坐标Z,

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

此坐标A与指定位置的 2 个长度相关联。在任何变换(移位、旋转、倾斜)的坐标系中,我们仍然可以引用此坐标,例如,向其绘制一个箭头。对于这个问题更重要的是,我们总是可以反向工作并找出相对于另一个坐标的相对位置,例如使用 calc 库

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

这在 3d 中是不可能的,因为 TiZ 截断坐标

解决这个问题的一个可能的方法已在这个很好的答案。这很棒,但不如上述 calc 语法那么流畅。也许更重要的是,人们必须付出额外的努力来存储 3d 坐标。理想情况下,人们应该有类似的东西

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

和 TiZ 也会记住z坐标。

请注意,这个请求乍一看可能比实际情况更无辜。在 2d 中,我们有一个预定义的参考系,即屏幕坐标。此外,旋转形成一个阿贝尔群,因此跟踪和反转它们不那么麻烦。上述答案将坐标保存在局部框架中,因此无法比较不同框架中的坐标。但是,这对于许多应用程序都很有用,在这些应用程序中,人们会切换到,比如说canvas is xy plane at z=0。理想情况下,这个问题的答案应该将每个符号点与一些三个长度相关联,这些长度是巧妙选择的参考系中的坐标,并且应该有方法以与坐标无关的方式确定两个点的相对位置,类似于veclen在 2d 中。

在最好的情况下,答案还会附带一个适当的解析器,允许我们进行标量积、向量积、计算向量范数和进行矩阵乘法,即正交变换。(我认为超越正交变换会很麻烦,因为矩阵求逆会非常麻烦。)在解析方面取得了一些进展这个问题的答案但公平地说,这还不如 2d 版本那么方便。

答案可能基于,也可能不基于tikz-3dplot。(tikz-3dplot带有良好的正交投影。)当然,最好的选择是也可以与三点透视图书馆。

请注意,该包中已经实现了一些矩阵运算calculator。这是一个令人印象深刻的包,包含许多东西,其例程可能对此处的任务有用。我不知道是否存在其他此类包。

答案1

有可能按照这个思路做出一些事情。这些是这个方向的一些成果。

要点

有人可以破解TiZ 记录 vielbein。假设用户具有正交视图,则两个基向量就足够了。这两个基向量具有分量e_1=(\pgf@xx,\pgf@yx,\pgf@zx)e_2=(\pgf@xy,\pgf@yy,\pgf@zy),到屏幕的法线就是e_3=e_1 x e_2。从现在开始,坐标与屏幕的(虚拟)距离称为“屏幕深度”。它就是p.e_3,其中p是点。

为了自动记录vielbein,需要“破解”TiZ(或为其定义一种样式)。因此,如果您对这两种做法都感到不舒服,请停止阅读。

限制

到目前为止,这仅适用于在笛卡尔坐标系中创建的坐标/节点,并且比例因子尚未考虑在内。此外,可能希望有一个语法

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

其中 \z1 是屏幕深度。此功能尚未实现。

明确示例

screendepth此代码定义了一个返回上述屏幕深度的函数。显然,它与坐标系无关。特别是,如果想要实现 3D 排序,则必须最后绘制具有较大屏幕深度的对象。无论您如何安装 3D 视图,它都可以工作。例如,我们可以使用tikz-3dplot而不是perspective库。

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

在此处输入图片描述

结果并不引人注目,只是尝试在 Ti 中进行 3d 排序Z 稍微不那么麻烦。

或者可以“破解”calc而不是 TiZ。此 hack 并非完全对称,必须使用其原始名称来引用坐标,当然不能使用类似 之类的名称($(A)+(B)$)。这样做需要进行更大规模的修改。但是,您可以使用语法获取“物理”组件calc

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

在此处输入图片描述

z3d请注意,无论可能存在何种黑客攻击,都可以使用该函数,但是,它z会假设用户未切换其坐标系来计算组件。

相关内容