TikZ 中的符號 3d 座標

TikZ 中的符號 3d 座標

警告:回答這個問題可能需要付出一些努力。問題的目的是「教TikZ 3d 座標」。這是什麼意思?如果我們在 Ti 中定義一個座標kZ,

 \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 中是不可能的,因為 TikZ 截斷座標

已提出解決此問題的可能方法這個很好的答案。這很棒,但不如上面提到的 calc 語法那麼順利。也許更重要的是,人們必須付出額外的努力來儲存 3D 座標。理想情況下,人們會有類似的東西

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

和鈦kZ 也會記得z座標。

請注意,這個請求乍看之下可能比實際情況更無辜。在 2d 中,我們有一個預先定義的參考系,即螢幕座標。此外,旋轉形成阿貝爾群,因此跟踪它們並反轉它們並不那麼麻煩。上述答案將座標保存在本地幀中,因此無法比較不同幀中的座標。然而,這對於許多應用程式很有幫助,例如切換到canvas is xy plane at z=0.理想情況下,這個問題的答案應該將每個符號點與三個長度相關聯,這三個長度是巧妙選擇的參考系中的座標,並且應該有方法以與座標無關的方式確定兩個點的相對位置,veclen類似2d.

在最好的情況下,答案還包括一個適當的解析器,它允許我們進行標量積、向量積、計算向量的範數以及進行矩陣乘法,即正交變換。 (我認為超越正交變換是一團糟,因為矩陣求逆會非常麻煩。)在解析方面已經取得了一些進展這個問題的答案但可以公平地說,這還不如 2d 對應物那麼方便。

答案可能基於也可能不是基於tikz-3dplot。 (tikz-3dplot帶有很好的正交投影。)當然,所有選項中最好的選項是也適用於三點透視圖書館.

請注意,包中已實現了一些矩陣運算calculator。這是一個令人印象深刻的包,其中有很多東西,它的例程可能對這裡的任務有用。我不知道是否有其他類似的包。

答案1

沿著這些思路烹飪一些東西是可能的。這些是這個方向的一些結果。

重點

一個人可以破解TikZ 錄製 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,需要「破解」TikZ(或為其定義樣式)。因此,如果您對其中任何一個操作感到不舒服,請停止閱讀。

限制

到目前為止,這僅適用於在笛卡爾座標中建立的座標/節點,並且尚未考慮比例因子。另外,可能需要有一個語法

\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 排序kZ稍微不那麼麻煩。

或可以「破解」calc而不是 TikZ。($(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會假設使用者沒有切換其座標系來計算元件。

相關內容