TikZ のシンボリック 3D 座標

TikZ のシンボリック 3D 座標

警告: この質問に答えるには、ある程度の努力が必要になるかもしれません。質問の目的は「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では不可能です。Zは座標を切り捨てます

これに対処する一つの方法は、この素敵な答えこれは素晴らしいですが、前述のcalc構文ほどスムーズには動作しません。おそらくもっと重要なのは、3D座標を保存するために余分な努力をしなければならないことです。理想的には、次のようなものになります。

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

とティZzも座標を記憶します。

この要求は、一見すると実際よりも無害に聞こえるかもしれないことに注意してください。2d では、画面座標という定義済みの参照フレームがあります。さらに、回転はアーベル群を形成するため、それらを追跡したり反転したりすることが面倒ではありません。上記の回答では、座標をローカル フレームに保存するため、異なるフレームの座標を比較することはできません。ただし、これは、たとえばに切り替える多くのアプリケーションに役立ちます。canvas is xy plane at z=0理想的には、この質問に対する回答では、各シンボリック ポイントを、巧みに選択された参照フレームの座標である 3 つの長さに関連付け、2d の場合と同様に、座標に依存しない方法で 2 つのポイントの相対的な位置を決定する手段が必要ですveclen

最良の答えは、スカラー積、ベクトル積、ベクトルのノルムの計算、行列乗算、つまり直交変換を実行できる適切なパーサーも付属していることです。(直交変換を超えると行列の逆変換が非常に面倒になるため、混乱を招くと思います。)解析に関しては、いくつかの進歩が見られました。この質問の答えしかし、これもまた、2D のものほど便利ではないと言っても過言ではないでしょう。

答えは に基づいている場合とそうでない場合がありますtikz-3dplot。(tikz-3dplotは優れた正規直交投影を備えています。)もちろん、すべてのオプションの中で最良のものは、三点透視図法図書館。

パッケージにはいくつかの行列演算が実装されていることに注意してくださいcalculator。これは多くの点で印象的なパッケージであり、そのルーチンはここでのタスクに役立つ可能性があります。この種のパッケージが他にも存在するかどうかはわかりません。

答え1

そういった方向で何かを作り出すことは可能です。これらはその方向でのいくつかの結果です。

主なポイント

TiをハッキングできるZ を使用して、画面を記録します。ユーザーが正投影ビューを持っていると仮定すると、2 つの基底ベクトルで十分です。これらの 2 つの基底ベクトルには、コンポーネントとがあり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 はちょっと扱いにくくなりました。

あるいは、calcTiの代わりに「ハック」することもできるZ。このハックは完全に対称的ではなく、座標を元の名前で参照する必要があり、もちろん のようなものは使用できません($(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ユーザーが座標系を切り替えていないと想定してコンポーネントを計算することに注意してください。

関連情報