警告:回答这个问题可能需要一些努力。问题的目的是“教 Ti钾Z 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 中是不可能的,因为 Ti钾Z 截断坐标。
解决这个问题的一个可能的方法已在这个很好的答案。这很棒,但不如上述 calc 语法那么流畅。也许更重要的是,人们必须付出额外的努力来存储 3d 坐标。理想情况下,人们应该有类似的东西
\path (x,y,z) coordinate(A);
和 Ti钾Z 也会记住z
坐标。
请注意,这个请求乍一看可能比实际情况更无辜。在 2d 中,我们有一个预定义的参考系,即屏幕坐标。此外,旋转形成一个阿贝尔群,因此跟踪和反转它们不那么麻烦。上述答案将坐标保存在局部框架中,因此无法比较不同框架中的坐标。但是,这对于许多应用程序都很有用,在这些应用程序中,人们会切换到,比如说canvas is xy plane at z=0
。理想情况下,这个问题的答案应该将每个符号点与一些三个长度相关联,这些长度是巧妙选择的参考系中的坐标,并且应该有方法以与坐标无关的方式确定两个点的相对位置,类似于veclen
在 2d 中。
在最好的情况下,答案还会附带一个适当的解析器,允许我们进行标量积、向量积、计算向量范数和进行矩阵乘法,即正交变换。(我认为超越正交变换会很麻烦,因为矩阵求逆会非常麻烦。)在解析方面取得了一些进展这个问题的答案但公平地说,这还不如 2d 版本那么方便。
答案可能基于,也可能不基于tikz-3dplot
。(tikz-3dplot
带有良好的正交投影。)当然,最好的选择是也可以与三点透视图书馆。
请注意,该包中已经实现了一些矩阵运算calculator
。这是一个令人印象深刻的包,包含许多东西,其例程可能对此处的任务有用。我不知道是否存在其他此类包。
答案1
有可能按照这个思路做出一些事情。这些是这个方向的一些成果。
要点
有人可以破解Ti钾Z 记录 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,需要“破解”Ti钾Z(或为其定义一种样式)。因此,如果您对这两种做法都感到不舒服,请停止阅读。
限制
到目前为止,这仅适用于在笛卡尔坐标系中创建的坐标/节点,并且比例因子尚未考虑在内。此外,可能希望有一个语法
\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
而不是 Ti钾Z。此 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
会假设用户未切换其坐标系来计算组件。