Achtung: Die Beantwortung dieser Frage kann einige Mühe erfordern. Der Zweck der Frage ist es, „Ti zu lehrenkZ 3d Koordinaten". Was bedeutet das? Wenn wir eine Koordinate in Ti definierenkZ,
\path (<x>,<y>) coordinate(A);
Diese Koordinate A
wird mit 2 Längen verknüpft, die den Standort angeben. In jedem transformierten (verschobenen, gedrehten, geneigten) Koordinatensystem können wir uns immer noch auf diese Koordinate beziehen und beispielsweise einen Pfeil dorthin zeichnen. Was für diese Frage wichtiger ist, wir können immer rückwärts arbeiten und herausfinden, was der relative Standort zu einer anderen Koordinate ist, z. B. mit der Calc-Bibliothek
\path let \p1=($(A)-(B)$),\n1={veclen(\x1,\y1)},\n2={atan2(\y1,\x1)} in <do something with this information>;
Dies ist in 3D unmöglich, da TikZ kürzt die Koordinaten.
Ein möglicher Weg, damit umzugehen, wurde vorgeschlagen indiese nette Antwort. Das ist großartig, funktioniert aber nicht ganz so reibungslos wie die oben erwähnte Calc-Syntax. Vielleicht noch wichtiger ist, dass man zusätzliche Anstrengungen unternehmen muss, um die 3D-Koordinaten zu speichern. Idealerweise hätte man so etwas wie
\path (x,y,z) coordinate(A);
und TikZ würde sich z
auch an die Koordinate erinnern.
Beachten Sie, dass diese Anfrage auf den ersten Blick harmloser klingt, als sie tatsächlich ist. In 2D haben wir ein vordefiniertes Referenzsystem, die Bildschirmkoordinaten. Darüber hinaus bilden die Rotationen eine abelsche Gruppe, sodass es weniger umständlich ist, sie zu verfolgen und umzukehren. Die oben erwähnte Antwort speichert die Koordinaten in lokalen Systemen, sodass es unmöglich ist, Koordinaten in verschiedenen Systemen zu vergleichen. Dies wäre jedoch für viele Anwendungen von entscheidender Bedeutung, bei denen man beispielsweise in wechselt. canvas is xy plane at z=0
Idealerweise sollte eine Antwort auf diese Frage jedem symbolischen Punkt drei Längen zuordnen, die die Koordinaten in einem geschickt gewählten Referenzsystem sind, und es sollte Mittel geben, um die relative Position zweier Punkte auf koordinatenunabhängige Weise zu bestimmen, ähnlich wie veclen
in 2D.
Im besten Fall würde eine Antwort auch mit einem geeigneten Parser einhergehen, der es uns ermöglicht, Skalarprodukte und Vektorprodukte zu berechnen, die Norm eines Vektors zu berechnen und Matrixmultiplikationen, also orthogonale Transformationen, durchzuführen. (Ich denke, dass es ein Chaos ist, über orthogonale Transformationen hinauszugehen, weil dann die Matrixinversion wirklich umständlich wird.) Einige Fortschritte beim Parsen wurden in derAntworten auf diese FrageAber man kann wohl auch hier mit Fug und Recht behaupten, dass es noch nicht so praktisch ist wie die 2D-Gegenstücke.
Die Antworten können auf basieren oder auch nicht tikz-3dplot
. ( tikz-3dplot
kommt mit schönen orthonormalen Projektionen.) Natürlich wäre die beste aller Optionen etwas, das auch mit dem funktioniertDreipunktperspektiveBibliothek.
Beachten Sie, dass einige Matrixoperationen im calculator
Paket implementiert wurden. Es ist ein beeindruckendes Paket, das viele Dinge und seine Routinen für die hier beschriebene Aufgabe nützlich sein könnten. Ob es andere Pakete dieser Art gibt, weiß ich nicht.
Antwort1
Es ist möglich, etwas in dieser Richtung auszuhecken. Dies sind einige Ergebnisse in dieser Richtung.
Hauptpunkt
Man kann Ti hackenkZ, um das Vielbein aufzuzeichnen. Vorausgesetzt, der Benutzer hat eine orthographische Ansicht, genügen zwei Basisvektoren. Diese beiden Basisvektoren haben die Komponenten e_1=(\pgf@xx,\pgf@yx,\pgf@zx)
und e_2=(\pgf@xy,\pgf@yy,\pgf@zy)
, die Normale zum Bildschirm ist einfach e_3=e_1 x e_2
. Der (virtuelle) Abstand einer Koordinate vom Bildschirm wird von nun an „Bildschirmtiefe“ genannt. Er ist einfach p.e_3
, wobei p
ein Punkt ist.
Um das Vielbein automatisch aufzuzeichnen, muss man Ti "hacken"kZ (oder definieren Sie einen Stil dafür). Wenn Sie sich also bei beidem nicht wohl fühlen, hören Sie auf zu lesen.
Einschränkungen
Derzeit funktioniert dies nur für Koordinaten/Knoten, die in kartesischen Koordinaten erstellt wurden, und Skalierungsfaktoren werden (noch?) nicht berücksichtigt. Außerdem wäre es möglicherweise wünschenswert, eine Syntax zu haben
\path let \p1=(A) in <do something with \z1>;
wobei \z1 die Bildschirmtiefe ist. Dies ist (noch?) nicht implementiert.
Explizites Beispiel
Dieser Code definiert eine Funktion screendepth
, die die oben genannte Bildschirmtiefe zurückgibt. Dies ist offensichtlich unabhängig vom Koordinatensystem. Insbesondere wenn eine 3D-Anordnung erreicht werden soll, müssen Objekte mit größerer Bildschirmtiefe zuletzt gezeichnet werden. Dies funktioniert unabhängig davon, wie Sie die 3D-Ansicht installieren. Wir hätten beispielsweise tikz-3dplot
anstelle der perspective
Bibliothek verwenden können.
\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}
Das Ergebnis ist nicht eingängig oder so, sondern ein Versuch, 3D-Ordnung in Ti zu machenkZ etwas weniger umständlich.
calc
Alternativ kann man statt Ti auch "hacken"kZ. Dieser Hack ist nicht vollständig symmetrisch, man muss die Koordinate mit ihrem ursprünglichen Namen bezeichnen und kann natürlich nichts wie verwenden ($(A)+(B)$)
. Dies würde einen größeren Eingriff erfordern. Sie können jedoch die „physischen“ Komponenten mit der calc
Syntax erhalten.
\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}
Beachten Sie, dass die z3d
Funktion unabhängig von möglichen Hacks verwendet werden kann, die z
Komponente jedoch unter der Annahme berechnet, dass der Benutzer sein Koordinatensystem nicht gewechselt hat.