1 3D Tools: 3dtools 3dtools 3dtools
1 3D Tools: 3dtools 3dtools 3dtools
1
/tikz/3d coordinate (no value)
Allow one to define a 3d coordinate from other coordinates.
Notice that, as of now, only the syntax \path (1,2,3) coordinate (A); works, yet the syntax
\coordinate (A) at (1,2,3); does not work, but leads to error messages.
\usetikzlibrary {3dtools}
B \begin{tikzpicture}
\path (1,2,3) coordinate (A)
(2,3,-1) coordinate (B)
(-1,-2,1) coordinate (C)
D [3d coordinate={(D)=0.25*(1,2,3)x(B)},
3d coordinate={(E)=0.25*(C)x(B)},
3d coordinate={(F)=(A)-(B)},];
A \path foreach \X in {A,...,E}
{(\X) node[fill,inner sep=1pt,
E label=above:$\X$]{}};
\end{tikzpicture}
The actual parsing are done by the function \pgfmathtdparse that allows one to parse 3d expressions.
The supported vector operations are + (addition +), - (subtraction −), * (multiplication of the vector by
a scalar), x (vector product ×) and o (scalar product). There is limited support for ordinary pgf parsings,
which are to be wrapped into [...]. Admittedly, this function is currently very poor. Enhancing it will
be a top priority if this is ever to become a CTAN library/package.
\pgfmathtdparse{hx i}
Parses 3d expressions.
TDx("vector ")
Yields the x-component of a 3d expression.
TDy("vector ")
Yields the y-component of a 3d expression.
TDz("vector ")
Yields the z-component of a 3d expression.
2
TDunit("vector ")
Yields the result of the expression normalized to unity. If the length of the expression is too close
to zero, a warning is issued and the unnormalized vector gets returned.
screendepth("vector ")
Yields distance a coordinate is above (positive) or below (negative) the screen. The values are only
really meaningful if the user has installed some reasonable view. The larger the screen depth of a
point is, the closer is the point to the observer. The function reconstructs the 3-bein1 from lengths
like \pgf@xy and so on, so the function is independent of the tool that is employed to install a view
(cf. section 1.2). The screen depth is crucial to decide whether a given object is in front of other
objects, or behind.
nscreenx
Yields the x-component of the normal on the screen.
nscreeny
Yields the y-component of the normal on the screen.
nscreenz
Yields the z-component of the normal on the screen.
x2d
Yields the x-component of a symbolic coordinate on the screen.
y2d
Yields the y-component of a symbolic coordinate on the screen.
In order to pretty-print the result one may want to use \pgfmathprintvector, and use the math
function TD for parsing.
\pgfmathprintvector{hx i}
Pretty-prints vectors.
~ − 0.3 B
0.2 A ~ + 0.6 C
~ = (−1, −1.7, 1.5) \usetikzlibrary {3dtools}
\pgfmathparse{TD("0.2*(A)
-0.3*(B)+0.6*(C)")}%
$0.2\,\vec A-0.3\,\vec B+0.6\,\vec C
=(\pgfmathprintvector\pgfmathresult)$
The alert reader may wonder why this works, i.e. how would Tik Z “know” what the coordinates A, B
and C are. It works because the coordinates in Tik Z are global, so they get remembered from the above
example.
Warning. The expressions that are used in the coordinates will only be evaluated when they are
retrieved. So, if you use, say, random numbers, you will get each time a different result. This is because
this library is working with \tikz@dcl@coord@hcoord i, where hcoord i is the name of the coordinate, say
A. This is the string with which the coordinate was generated, e.g. (1,2,3). However, the coordinate
will always be at the same location. So you may want to avoid using functions that change their values
when declaring coordinates that get used later in coordinate calculations.
1 A 3-bein or dreibein is a German word that stands for a local frame, its literal translation is something like three-
leg. Like the term eigenvector this is a foreign word that, to the best of my knowledge, has no commonly used English
counterpart.
3
\usetikzlibrary {3dtools}
~ = (0.4, 0.68, 0.64)
R \begin{tikzpicture}
\path[overlay] (0.1+rnd,0.1+rnd,0.1+rnd)
coordinate (R);
~ = (0.38, 0.1, 0.97)
R \def\ppv{\pgfmathprintvector\pgfmathresult}
\draw[red] (R) circle[radius=0.1];
\node at (0,1)
{\pgfmathparse{TD("(R)")}%
$\vec R=(\ppv)$};
\draw[blue] (R) circle[radius=0.2];
\node at (0,0)
{\pgfmathparse{TD("(R)")}%
$\vec R=(\ppv)$};
\end{tikzpicture}
The main usage of \pgfmathprintvector is to strip off unnecessary zeros, which emerge since the
internal computations are largely done with the fpu library.
~·B
A ~ =5 \usetikzlibrary {3dtools}
\pgfmathparse{TD("(A)o(B)")}%
$\vec A\cdot \vec B=
\pgfmathprintnumber\pgfmathresult$
Notice that, as of now, the only purpose of brackets (...) is to delimit vectors. Further, the addition
+ and subtraction - have a higher precedence than vector products x and scalar products o. That is,
(A)+(B)o(C) gets interpreted as (A ~ + B) ~ and (A)+(B)x(C) as (A
~ · C, ~ + B)
~ × C.~
~ + B)
(A ~ ·C
~ = −11 \usetikzlibrary {3dtools}
\pgfmathparse{TD("(A)+(B)o(C)")}%
$(\vec A+\vec B)\cdot\vec C=
\pgfmathprintnumber\pgfmathresult$
~ + B)
(A ~ ×C
~ = (9, −5, −1) \usetikzlibrary {3dtools}
\pgfmathparse{TD("(A)+(B)x(C)")}%
$(\vec A+\vec B)\times\vec C=
(\pgfmathprintvector\pgfmathresult)$
You can use square brackets [...] to separately parse subexpressions with the standard pgf parser.
~ + cos(30) B
sin(45) A ~ = (2.44, 4.01, 1.26)T
\usetikzlibrary {3dtools}
\pgfmathparse{TD("{[sin(45)]*(A)+[cos(30)]*(B)}")}%
$\sin(45)\,\vec A+\cos(30)\,\vec B=
(\pgfmathprintvector\pgfmathresult)^T$
Moreover, any expression can only have either one o or one x, or none of these. Expressions with
more of these can be accidentally right.
axisangles("vector ")
Yields the the rotation angles that transforms the vector in the z-axis. Since an axis has a residual
rotation symmetry, namely the rotation around this axis, only two angles are required, and thus
returned. In the conventions of section 1.2, these are the angles φ and ψ. It corresponds to the
macro \tdplotgetpolarcoords{hx i}{hyi}{hz i} from the tikz-3dplot package.
~ = −116.57, −36.7
^(A) \usetikzlibrary {3dtools}
\pgfmathparse{axisangles("(A)")}%
$\sphericalangle(\vec A)=
\pgfmathprintvector\pgfmathresult$
4
the correct projection in 3d. In order to make these projections more user friendly, 3dtools offers the
possibility to define extended objects such as lines or planes. This is the same concept as what is offered
by plain Tik Z, where the user can name coordinates and nodes, and, if the intersections library is
loaded, also paths.
/tikz/3d/plane through=point 1 and point 2 and point 3 named name (no default)
Defines a plane of name name by the requirement that it goes through the three specified points.
Internally the plane is stored in terms of its normal and a point it goes through.
/tikz/3d/sphere with center=center and radius radius named name (no default)
Defines a sphere with center center and radius radius.
The following code example illustrates the usage. It also makes use of the install view key, which
we describe in section 1.2.
5
\usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view={phi=110,psi=0,theta=60},
dot/.style={circle,inner sep=0pt,
minimum size=2pt,fill}]
\draw[every coordinate node/.append style={dot}]
(2,1,2) coordinate[label=above:{$A$}] (A) --
(1,2,1) coordinate[label=below:{$B$}] (B) --
(2,0,0) coordinate[label=below:{$C$}] (C) -- cycle
(0,0,0) coordinate[label=above:{$O$}] (O)
(3,-2,1) coordinate[label=below:{$D$}] (D);
\path[3d/plane through={(A) and (B) and (C) named pABC},
3d/plane with normal={(1,1,1) through (C) named ptwo},
3d/line through={(A) and (B) named lAB},
3d/line through={(O) and (D) named lOD}];
% project point on plane
\path[3d/project={(O) on pABC}] coordinate (O’);
% project point on line
\path[3d/project={(C) on lAB}] coordinate (C’);
% intersection of plane and line
\path[3d/intersection of={lOD with pABC}] coordinate (I);
\draw[dashed] (C) -- (C’)
coordinate[dot,label=above right:{$C’=\pgfmathparse{TD("(C’)")}%
(\pgfmathprintvector\pgfmathresult)^T$}];
\path (O’) coordinate[dot,label=right:{$O’=\pgfmathparse{TD("(O’)")}%
(\pgfmathprintvector\pgfmathresult)^T$}];
\path (I) coordinate[dot,label=above:{$I=\pgfmathparse{TD("(I)")}%
(\pgfmathprintvector\pgfmathresult)^T
$}];
\end{tikzpicture}
\usetikzlibrary {3dtools}
p9 \begin{tikzpicture}
\path foreach \X in {1,...,10}
{ (rnd*360:rnd*3) coordinate (p\X)
node[circle,inner sep=1pt,fill,label={below:$p_{\X}$}]{}};
\draw[convex hull of={p1,p2,p3,p4,p5,p6,p7,p8,p9,p10}];
p5 \end{tikzpicture}
p8
p10 p
pp47 3 p6
p1
p2
The initial projection is such that x is right an y is up, as if we had no third direction.
6
y \usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view]
\draw[-stealth] (0,0,0) -- (1,0,0)
node[pos=1.2] {$x$};
z x \draw[-stealth] (0,0,0) -- (0,1,0)
node[pos=1.2] {$y$};
\draw[-stealth] (0,0,0) -- (0,0,1)
node[pos=1.2] {$z$};
\end{tikzpicture}
The 3d-like pictures emerge by rotating the view. The conventions for the parametrization of the
orthogonal rotations in terms of three rotation angles φ, ψ and θ are
cφ cψ sφ cψ −sψ
O(φ, ψ, θ) = cφ sψ sθ − sφ cθ sφ sψ sθ + cφ cθ cψ sθ . (1)
cφ sψ cθ + sφ sθ sφ sψ cθ − cφ sθ cψ cθ
/tikz/3d/psi (initially 0)
3d rotation angle.
/tikz/3d/theta (initially 0)
3d rotation angle.
The rotation angles can be used to define the view. The conventions are chosen in such a way that they
resemble those of the tikz-3dplot package, which gets widely used. This matrix can be written as
where
1 0 0 cos(ψ) 0 − sin(ψ)
Rx (θ) = 0 cos(θ) sin(θ) , Ry (ψ) = 0 1 0 ,
0 − sin(θ) cos(θ) sin(ψ) 0 cos(ψ)
1 0 0
Rz (φ) = 0 cos(φ) sin(φ)
0 − sin(φ) cos(φ)
are rotations about the x, y and z axis, respectively. For ψ = 0, O(φ = φd , ψ = 0, θ = θd ) = Rd (θd , φd )
from the tikz-3dplot package. Note, however, that there seems to be an inconsistency in equation (2.1)
of that package.2
z \usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view={phi=110,psi=0,theta=70}]
\draw[-stealth] (0,0,0) -- (1,0,0)
node[pos=1.2] {$x$};
y \draw[-stealth] (0,0,0) -- (0,1,0)
x node[pos=1.2] {$y$};
\draw[-stealth] (0,0,0) -- (0,0,1)
node[pos=1.2] {$z$};
\end{tikzpicture}
7
/tikz/3d/screen coords (no value)
Defines locally screen coordinate system, i.e. this style brings you back to the original co-
ordinate system in tikzpictures. It is taken from tikz-3dplot, where this style is called
tdplot_screen_coords.
\usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view={phi=50,theta=70},
declare function={a=3;alpha=20;},
line join=round]
\path
(-a/2,0,0) coordinate (P_1)
(a/2,0,0) coordinate (P_2)
(-a/2,a,0) coordinate (A_1)
(a/2,a,0) coordinate (A_2)
(-a/2,{a*cos(alpha/2)},{a*sin(alpha/2)}) coordinate (B_1)
(a/2,{a*cos(alpha/2)},{a*sin(alpha/2)}) coordinate (B_2)
(-a/2,{a*cos(alpha)},{a*sin(alpha)}) coordinate (C_1)
(a/2,{a*cos(alpha)},{a*sin(alpha)}) coordinate (C_2);
\foreach \X in {A,B,C}
{\path[save named path=p\X] (P_1) -- (P_2) --
(\X_2) -- (\X_1) -- cycle;}
\pgfmathsetmacro{\nA}{TDunit("(A_1)-(P_1)x(P_2)-(P_1)")}
\pgfmathsetmacro{\nC}{TDunit("(C_1)-(P_1)x(P_1)-(P_2)")}
\pgfmathtruncatemacro{\itest}{screendepth(\nA)>screendepth(\nC)?1:0}
\tikzset{3d/ordered paths/pA/.style={fill=red}}
\ifnum\itest=1
\tikzset{3d/draw ordered paths={pC,pB,pA}}
\else
\tikzset{3d/draw ordered paths={pA,pB,pC}}
\fi
\end{tikzpicture}
8
Since −1 ≤ sin(β) ≤ 1, this equation only has solutions if
h
cot(θ) ≤ 1 . (2)
r
The solutions are then given by
(1) h (2) (1)
βcrit = arcsin cot(θ) and βcrit = 180 − βcrit ,
r
(1) (2)
and βcrit = βcrit if the inequality (2) is saturated. The following code example illustrates this. Notice
that it could be made more concise with the 3d library.
\usetikzlibrary {3dtools}
\begin{tikzpicture}[declare function={%
R=2.5;lambda=20;theta=60;
r=R*cos(lambda);h=R*sin(lambda);
betacrit=asin(h/r*cot(theta));}]
\draw (0,0) circle[radius=R];
\begin{scope}[smooth,
3d/install view={phi=0,psi=0,theta=theta}]
\draw[dashed]
plot[variable=\t,
domain=betacrit:180-betacrit]
({r*cos(\t)},{r*sin(\t)},h);
\draw
plot[variable=\t,
domain=betacrit:-180-betacrit]
({r*cos(\t)},{r*sin(\t)},h);
\end{scope}
\end{tikzpicture}
p test: |P − C| < R. The circle only makes sense if |P − C| < R. The radius of the circle is
First
r = R2 − |P − C|2 . Call the normal on the screen nscreen .
Case n k nscreen . If n and nscreen are linearly dependent, the circle is completely hidden if the screen
depth of P is smaller than the screen depth of C, sd P < sd, and visible otherwise. We can choose a
coordinate system in which
n
ez = , ex arbitrary but ⊥ ez and ey = ez × ex
|n|
to draw the circle.
Case n 6 k nscreen . From now on we assume that n and nscreen are not linearly dependent. This means
that they span a plane, and we can choose nscreen to point in the x-direction of this plane (see figure 1).
The normal can be decomposed in a part parallel to nscreen , nk , and a perpendicular part, n⊥ .
The projection of the circle on this plane is a line, and has slope α = arctan(−nk /n⊥ ). If r cos α >
|sd P − sd C|, the circle has both hidden and visible stretches, from between −r cos α and sd P − sd C it
is hidden and between sd P − sd C and r cos α it is solid.
We are going to draw the circle in a coordinate system defined by
n
ez = , ey = ez × nscreen and ex = ey × ez .
|n|
The circle is then in the x-y plane, and the positive x-direction is the direction of increasing screen depth
(or visibility). If r cos α > |sd P |, then the stretch between β := arccos sdrC−sd P
and is visible,
cos α −β
between β and 360◦ − β it is hidden.
9
n
n⊥
P nk
α
r
sd C sd
Figure 1: n 6 k nscreen .
with some function R(u). What is the contour of an orthonormal projection of the surface on the screen?
It is the boundary between the hidden and visible parts. What distinguishes the visible from the hidden
parts? It is the projection of the normal of the surface on the screen, F (u, v) := nS (u, v) · nscreen . The
hidden and visible stretches are separated by the zeros of this projection. In the case of our surface (3),
−R0 (u)
~ u f (u, v) × ∇
~nS (u, v) = ∇ ~ v f (u, v) = R(u) cos(v) , (4)
sin(v)
and for ψ = 0
sin(θ) sin(φ)
~nscreen = − sin(θ) cos(φ) . (5)
cos(θ)
10
unilluminating and given by
√
a a2 + b2 − c2 ± b c
[sin(v)]1,2 = − , (7a)
a2 + b2
√
b a2 + b2 − c2 ∓ a c
[cos(v)]1,2 = − , (7b)
a2 + b2
where a, b and c are implicitly defined in (6). Hence, for “reasonable” configurations, it is straightforward
to plot the contour of the surface with LATEX.
a
x
11
\usetikzlibrary {3dtools}
\begin{tikzpicture}[declare function={phi=30;theta=70;},
3d/install view={phi=phi,psi=0,theta=theta},
line cap=butt,line join=round,
declare function={%
R(\u)=1.5+sin(\u*45)/(2+\u/8);%<- input
Rprime(\u)=(R(\u+0.01)-R(\u-0.01))/0.02;%<- numerical derivative
a=cos(phi)*sin(theta);% see equation {eq:vcrit}
b=-1*cos(theta);%
c(\u)=sin(theta)*sin(phi)*Rprime(\u);%
sv1(\u)=-((b*c(\u)+sqrt(a*a+b*b-c(\u)*c(\u))*abs(a))/(a*a+b*b));%
sv2(\u)=(-(b*c(\u))+sqrt(a*a+b*b-c(\u)*c(\u))*abs(a))/(a*a+b*b);%
cv1(\u)=-((a*c(\u)+sqrt(a*a+b*b-c(\u)*c(\u))*abs(b))/(a*a+b*b));%
cv2(\u)=(-(a*c(\u))+sqrt(a*a+b*b-c(\u)*c(\u))*abs(b))/(a*a+b*b);%
},
icircle/.style={thick},
iplane/.style={fill=white,fill opacity=0.8},
iline/.style={semithick},
extra/.code={},annot/.code={\tikzset{extra/.code={#1}}}]
\newcommand\DrawIntersectingPlane[2][]{%
\begin{scope}[canvas is yz plane at x=#2,transform shape,#1]
\draw[iplane] (3,3) -- (-3,3) -- (-3,-3)-- (3,-3) -- cycle;
\draw[icircle] (0,0) circle[radius={R(#2)}];
\tikzset{extra}
\end{scope}}
%
\DrawIntersectingPlane[annot={%
\path (2.9,2.9) node[below left,transform shape]{$P$};
\draw (2,3) arc[start angle=180,end angle=270,radius=1];
\draw[dashed] (0,-2.25) node[below,transform shape=false]{$a$} -- (0,0);
}]{0}
\draw[iline] (0,0,-2.25) -- (4,0,-2.25);
\draw[thick,smooth,variable=\u,domain=0:4]
plot (\u,{R(\u)*cv1(\u)},{R(\u)*sv1(\u)})
plot (\u,{R(\u)*cv2(\u)},{R(\u)*sv2(\u)});
\DrawIntersectingPlane[icircle/.append style={fill=gray,fill opacity=0.5},
annot={%
\draw[dashed] (0,-2.25) node[below,transform shape=false]{$x$} -- (0,-1.5);
}]{4}
\draw[iline] (4,0,-2.25) -- (8,0,-2.25);
\draw[thick,smooth,variable=\u,domain=4:8]
plot (\u,{R(\u)*cv1(\u)},{R(\u)*sv1(\u)})
plot (\u,{R(\u)*cv2(\u)},{R(\u)*sv2(\u)});
%
\DrawIntersectingPlane[annot={%
\path (2.9,2.9) node[below left,transform shape]{$Q$};
\draw (2,3) arc[start angle=180,end angle=270,radius=1];
\draw[dashed] (0,-2.25) node[below,transform shape=false]{$b$} -- (0,0);
}]{8}
\draw[iline] (8,0,-2.25) -- (9,0,-2.25) ;
\end{tikzpicture}
There are some special cases that may be of particular interest: R0 (u) = 0 for a cylinder and
R (u) = −r/h for a cone of height h and base radius r.
0
12
TDphysx("vector ")
Yields the physical x-component of a 3d named coordinate.
TDphysy("vector ")
Yields the physical y-component of a 3d named coordinate.
TDphysz("vector ")
Yields the physical z-component of a 3d named coordinate.
TDphys("vector ")
Yields an array containing physical x, y and z-components of a 3d named coordinate.
Sometimes one may want to retrieve the components of a physical coordinate in a local frame.
TDlocalx("vector ")
Yields the local x-component of a 3d named coordinate the physical coordinates of which have been
recorded.
TDlocaly("vector ")
Yields the local y-component of a 3d named coordinate the physical coordinates of which have been
recorded.
TDlocalz("vector ")
Yields the local z-component of a 3d named coordinate the physical coordinates of which have been
recorded.
TDlocal("vector ")
Yields an array containing local x, y and z-components of a 3d named coordinate the physical
coordinates of which have been recorded.
13
\usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/record physical components,
dot/.style={circle,inner sep=1pt,fill}]
\begin{scope}[3d/install view={phi=0,psi=0,theta=70}]
\path (0,0,3) coordinate (A);
\path[3d/install view={phi=30}] (3,0,0) coordinate (B);
\path[3d/install view={phi=150}] (3,0,0) coordinate (C);
\path[3d/install view={phi=270}] (3,0,0) coordinate (D);
\end{scope}
\draw[dashed] foreach \X [remember=\X as \Y (initially D)]
in {B,C,D}
{(A) -- (\X) (\Y) -- (\X)
coordinate[dot,label=below:{$\begin{aligned}
\X_\mathrm{phys}&=\pgfmathparse{TDphys("\X")}%
(\pgfmathprintvector\pgfmathresult)^T\\
\X_\mathrm{loc}&=\pgfmathparse{TDlocal("\X")}%
(\pgfmathprintvector\pgfmathresult)^T\\
\end{aligned}$}]}
(A) coordinate[dot,label=above:{$A=\pgfmathparse{TDphys("A")}%
(\pgfmathprintvector\pgfmathresult)^T
$}];
\end{tikzpicture}
Once one has defined physical coordinates, one can use them to compute e.g. distances between
coordinates defined in different frames.
~ phys = (0, 2.82, 1.03),
A \usetikzlibrary {3dtools}
$\pgfmathsetmacro{\vecA}{TDphys("A")}%
~ phys = (2.6, −0.51, 1.41),
B \pgfmathsetmacro{\vecB}{TDphys("B")}%
~ B)
dphys (A, ~ = 4.24 \pgfmathsetmacro{\physdist}{%
sqrt(TD("(\vecA)-(\vecB)o(\vecA)-(\vecB)"))}%
\begin{array}{l}
\vec A_\mathrm{phys}=(\pgfmathprintvector\vecA),\\
\vec B_\mathrm{phys}=(\pgfmathprintvector\vecB),\\
d_\mathrm{phys}(\vec A,\vec B)=%
\pgfmathprintnumber\physdist
\end{array}$
These are the things that work. However, there are, unfortunately, more than enough things that do
not work. They include shifted frames, other coordinate systems such as spherical ones, all coordinates
that are defined in canvas is xy plane at z=0 or relatives, etc.
Y
X
14
\usetikzlibrary {3dtools}
\begin{tikzpicture}[dot/.style={circle,inner sep=1pt,fill}]
\begin{scope}[3d/install view={phi=100,psi=0,theta=70}]
\path (1,0,0) coordinate (X)
(0,1,0) coordinate (Y)
(0,1,1) coordinate (Z)
(1,-1,1) coordinate (P);
\path[3d/circumsphere center={A={(X)},B={(Y)},C={(Z)},D={(P)}}]
coordinate (I);
\end{scope}
\pgfmathsetmacro{\csr}{sqrt(TD("(X)-(I)o(X)-(I)"))}
\draw (I) circle[radius=\csr];
\path foreach \X in {X,Y,Z,P}
{(\X) coordinate[dot,label=below:{$\X$}]}
(I) (I) coordinate[dot,label=above:{$I=\pgfmathparse{TD("(I)")}%
(\pgfmathprintvector\pgfmathresult)^T$}];
\end{tikzpicture}
\usetikzlibrary {3dtools}
C \begin{tikzpicture}[declare function={a=3;},
dot/.style={circle,inner sep=1pt,fill},
3d/install view={phi=100,psi=0,theta=70}]
\path (a,0,0) coordinate (A)
(0,a,0) coordinate (B)
(0,0,a) coordinate (C);
I = (1, 1, 1)T \path[3d/circumcircle center]
coordinate (I);
\pgfmathsetmacro{\myr}{%
B
sqrt(TD("(A)-(I)o(A)-(I)"))}
\tikzset{3d/define orthonormal dreibein}
A \begin{scope}[x={(ex)},y={(ey)}]
\draw (I) circle[radius=\myr];
\end{scope}
\path foreach \X in {A,B,C}
{(\X) coordinate[dot,label=above:{$\X$}]}
(I) (I) coordinate[dot,label=above:{$I=%
\pgfmathparse{TD("(I)")}%
(\pgfmathprintvector\pgfmathresult)^T$}];
\end{tikzpicture}
\usetikzlibrary {3dtools}
C \begin{tikzpicture}[declare function={a=1.5;},
dot/.style={circle,inner sep=1pt,fill},
3d/install view={phi=100,psi=0,theta=70}]
\path (a,0,0) coordinate (A)
(2.39, 1.64,B0.72)T (0,a,0) coordinate (B)
A (0,0,a) coordinate (C);
\path[3d/intersection of three spheres=
{rA=2,rB=2.5,rC=3}];
(0.22, −0.53, −1.44)T \path (i1)
coordinate[dot,label=above:{$%
\pgfmathparse{TD("(i1)")}%
(\pgfmathprintvector\pgfmathresult)^T$}];
\path (i2)
coordinate[dot,label=above:{$%
\pgfmathparse{TD("(i2)")}%
(\pgfmathprintvector\pgfmathresult)^T$}];
\path foreach \X in {A,B,C}
{(\X) coordinate[dot,label=above:{$\X$}]};
\end{tikzpicture}
15
/tikz/3d/insphere center=hoptionsi (no default, initially empty)
Computes the center of an insphere center of a tetrahedron defined by four corners, A, B, C, and D.
The initial values are A=(A), B=(B), C=(C), and D=(D).
\usetikzlibrary {3dtools}
\begin{tikzpicture}[line join=round,line cap=round,
3d/install view={phi=100,psi=0,theta=70}]
\path (0,0,0) coordinate (D)
(2,0,0) coordinate (A)
(0,3,0) coordinate (B)
(0,0,4) coordinate (C);
\path[3d/insphere center] coordinate (I);
\tikzset{3d/plane through=(A) and (B) and (C) named pABC}
\path[3d/project={(I) on pABC}] coordinate (I’);
\pgfmathsetmacro{\myr}{tddistance("(I)","(I’)")}
\draw (A) -- (B) -- (C) -- cycle --(D) --(B) (C) -- (D);
\shade[3d/screen coords,ball color=green, opacity=0.8]
(I) circle [radius=\myr];
\end{tikzpicture}
\usetikzlibrary {3dtools}
C \begin{tikzpicture}[3d/install view={%
B phi=30,psi=0,theta=70}]
M MA \foreach \X in {A,B,C}
{\pgfmathsetmacro{\myx}{3*(rnd-1/2)}
\pgfmathsetmacro{\myy}{3*(rnd-1/2)}
\pgfmathsetmacro{\myz}{3*(rnd-1/2)}
\path (\myx,\myy,\myz) coordinate (\X);}
\path pic{3d circle through 3 points={%
A={(A)},B={(B)},C={(C)},center name=MM}};
\foreach \X in {A,B,C,MM}
{\fill (\X) circle[radius=1.5pt]
node[above]{$\X$};}
\end{tikzpicture}
16
coordinate (nscreen) which contains the normal to the screen. The foreground path will use the
style /tikz/3d/visible, and the hidden path will be drawn with the style /tikz/3d/hidden. For
an alternative approach with more functionality see the tikz-3dplot-circleofsphere package.
\usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view={phi=110,theta=70},
declare function={R=2;}]
\shade[ball color=white,3d/screen coords] circle[radius=R];
\path pic{3d/circle on sphere={R=R,P={(0.5,-1.2,1)}}};
\end{tikzpicture}
17
A
B
C
\usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view={phi=110,psi=0,theta=70}]
\draw
(8,5,5) coordinate[label=above:{$A$}] (A) --
(1,2,0) coordinate[label=below:{$B$}] (B) --
(5,-5,0) coordinate[label=below:{$C$}] (C) -- cycle;
\path pic[red,dashed]{3d incircle={%
A={(A)},B={(B)},C={(C)},center name=I}};
\draw (I) -- (tmppa) (I) -- (tmppb) (I) -- (tmppc);
\end{tikzpicture}
/tikz/3d/polyhedron/draw open face with corners=hlist of cornersi (no default, initially empty)
Same as the previous key except it does not close the surface boundary path.
18
\usetikzlibrary {3dtools}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\begin{tikzpicture}
\begin{scope}[3d/install view={phi=110,psi=10,%
theta=70},scale=2]
\path (0,0,1) coordinate (v1)
({sqrt(8/9)},0,-1/3) coordinate (v2)
({-sqrt(2/9)},{sqrt(2/3)},-1/3) coordinate (v3)
({-sqrt(2/9)},{-sqrt(2/3)},-1/3) coordinate (v4);
\tikzset{3d/polyhedron/.cd,
fore layer=foreground,back layer=background,
fore/.append style={opacity=0.4,thick},
back/.append style={3d/polyhedron/complete dashes,
opacity=0.4},
draw face with corners={{(v1)},{(v2)},{(v3)}},
draw face with corners={{(v1)},{(v2)},{(v4)}},
draw face with corners={{(v1)},{(v3)},{(v4)}},
draw face with corners={{(v2)},{(v3)},{(v4)}}}
\end{scope}
\end{tikzpicture}
19
\usetikzlibrary {3dtools}
\pgfdeclarelayer{background}\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\begin{tikzpicture}
\edef\lstV{{-1.376,0.,0.2628},{1.376,0.,-0.2628},{-0.4253,-1.309,0.2628},
{-0.4253,1.309,0.2628},{1.113,-0.8090,0.2628},{1.113,0.8090,0.2628},
{-0.2628,-0.8090,1.113},{-0.2628,0.8090,1.113},{-0.6881,-0.5,-1.113},
{-0.6881,0.5,-1.113},{0.6881,-0.5,1.113},{0.6881,0.5,1.113},
{0.8506,0.,-1.113},{-1.113,-0.8090,-0.2628},{-1.113,0.8090,-0.2628},
{-0.8506,0.,1.113},{0.2628,-0.8090,-1.113},{0.2628,0.8090,-1.113},
{0.4253,-1.309,-0.2628},{0.4253,1.309,-0.2628}}
\edef\lstFaces{{15,10,9,14,1},{2,6,12,11,5},{5,11,7,3,19},{11,12,8,16,7},
{12,6,20,4,8},{6,2,13,18,20},{2,5,19,17,13},{4,20,18,10,15},{18,13,17,9,10},
{17,19,3,14,9},{3,7,16,1,14},{16,8,4,15,1}}
\tikzset{3d/polyhedron/.cd,fore layer=foreground,back layer=background,
fore/.append style={fill opacity=0.7},
back/.append style={3d/polyhedron/complete dashes,fill opacity=0.7}}
\begin{scope}[3d/install view={phi=120,psi=20,%
theta=70}]
\tikzset{name prefix=Va,%<- used for all vertices
3d/define vertices/.expanded={\lstV},
3d/polyhedron/create faces from vertex list/.expanded={\lstFaces}}
\end{scope}
\begin{scope}[yshift=-3.5cm,
3d/install view={phi=210,psi=30,theta=60}]
\tikzset{name prefix=Vb,%<- used for all vertices
3d/define vertices/.expanded={\lstV},
3d/polyhedron/create faces from vertex list/.expanded={\lstFaces}}
\end{scope}
\begin{scope}[yshift=-7cm,
3d/install view={phi=30,psi=-10,theta=75}]
\tikzset{name prefix=Vc,%<- used for all vertices
3d/define vertices/.expanded={\lstV},
3d/polyhedron/create faces from vertex list/.expanded={\lstFaces}}
\end{scope}
\end{tikzpicture}
The library has also some experimental pics for cones, truncated cones and cylinders. They share a
couple of common keys.
/tikz/3d/r (initially 1)
Key for radii, e.g. of cylinders or cones.
/tikz/3d/h (initially 1)
Key for heights, e.g. of cylinders or cones.
/tikz/3d/R (initially 2)
Key for larger radii, e.g. of a frustum.
20
\usetikzlibrary {calc,3dtools}
\begin{tikzpicture}[3d/install view=%
{phi=110,psi=0,theta=60}]
\pic{3d/shaded cone={r=2,h=3}};
\end{tikzpicture}
\usetikzlibrary {calc,3dtools}
\begin{tikzpicture}[3d/install view=%
{phi=110,psi=0,theta=60}]
\pic{3d/frustum};
\end{tikzpicture}
\usetikzlibrary {3dtools}
\begin{tikzpicture}[3d/install view={phi=30,psi=0,theta=80}]
\pic{ycylinder={r=0.4,h=3,
top/.style={fill=blue}}};
\end{tikzpicture}
To do:
21
/tikz/stored path/coordinate prefix=hprefix i (no default, initially stored-)
Prefix for the stored coordinates. If you store several paths and want to possibly combine them, you
may want to give each path a unique prefix.
present there is no sanity check. If n is larger than the number of stored coordinates, there will be
an error.
tikztdindexoflastcoordinate
Yields index of the last coordinate of a stored path. This can be used to test if a path is empty.
22
/list management/list separator (initially ,)
Code used by the .print list key handler to separate items.
23
Often one is given a list of entries where \usetikzlibrary {3dtools}
one wants to sort on the basis of some \begin{minipage}{6.4cm}
\pgfkeys{/my lists/.cd,
function that can be fed with an entry. my initial array/.is array={%
One may also want to have an index map- 12,19,1,3,17,4,23,5,9,7},
pingwhich indicates at which position the my values/.initial=\pgfkeysvalueof{%
ith entry of the initial list ended up get- /my lists/my initial array/content},%
my values/.sort numeric list=%
ting. In this example, we start out with {\temp}{\templ},%
my sorted array/.is array/.expanded={\temp},
{λi }ni=1 = {12, 19, 1, 3, 17, 4, 23, 5, 9, 7} . my index machinery/.is array/.expanded={\templ}}%
Often one is given a list of entries where\
The fact that we used the /.is array key one wants to sort on the basis of some\
function that can be fed with an entry.\
handler means that we know its dimen- One may also want to have an index mapping
sion, n = 10. After sorting we get which indicates at which position the\
$i^\mathrm{th}$ entry of the initial list\
{λ0i }ni=1 = {1, 3, 4, 5, 7, 9, 12, 17, 19, 23} ended up getting. In this example, we\
start out with
\[\{\lambda_i\}_{i=1}^n=
and an index or reshuffling or permutation \{\pgfkeys{/my lists/my initial array/%
list content/.print list}\}
\;.\]
P = (3, 4, 6, 8, 10, 9, 1, 5, 2, 7) . The fact that we used the \texttt{/.is array}\
key handler means that we know its dimension,\
For instance, the 5th entry of the permu- $n=\pgfkeysvalueof{/my lists/my initial array/dim}$.\
After sorting we get
tation list being P5 = 10 tells us that the \[\{\lambda_i’\}_{i=1}^n=\{
10th entry of our original list, λ10 = 7, \pgfkeys{/my lists/my sorted array/%
ended up at position 5 in the sorted list, content/.print list}\}\]
i.e. λ05 = λ10 . Similarly, P3 = 6 means and an index or reshuffling or permutation list
\[P=(\pgfkeys{/my lists/%
that for the 6th entry of our original list, my index machinery/content/.print list})\;.\]
λ6 = 4 = λ03 . For instance, the $5^\mathrm{th}$ entry of the\
permutation list being\
$P_5=\pgfkeysvalueof{/my lists/my index machinery/5}$\
tells us that the\
$\pgfkeysvalueof{/my lists/my index machinery/5}
^\mathrm{th}$\ entry of our original list,\
$\lambda_{\pgfkeysvalueof{/my lists/%
my index machinery/5}}
=\pgfkeysvalueof{/my lists/my initial array/%
\pgfkeysvalueof{/my lists/my index machinery/5}}$,\
ended up at position $5$ in the sorted list,\
i.e.\ $\lambda_5’=\lambda_{\pgfkeysvalueof{/my lists/%
my index machinery/5}}$. Similarly,\
$P_3=\pgfkeysvalueof{/my lists/my index machinery/3}$\
means that for the\
$\pgfkeysvalueof{/my lists/my index machinery/3}
^\mathrm{th}$ entry of our original list,\
$\lambda_{\pgfkeysvalueof{/my lists/%
my index machinery/3}}
=\pgfkeysvalueof{/my lists/my initial array/%
\pgfkeysvalueof{/my lists/my index machinery/3}}
=\lambda_3’$.
\end{minipage}
24
Saves a path very much like the save path key but uses strings, i.e. you can say save named
path=A, and prefixes the global macro by tikz@td@named@path@ to minimize the chance that this
macro interferes with other macros.
\usetikzlibrary {3dtools}
\begin{tikzpicture}
\draw[decoration={3d coil color=red,aspect=0.35, segment length=3.1mm,
amplitude=3mm,3d complete coil},
decorate] (0,1) -- (0,6);
\draw[decoration={3d coil color=blue,3d coil opacity=0.9,aspect=0.5,
segment length={2*pi*3cm/50}, amplitude=5mm,3d complete coil,
3d coil closed},
decorate] (5,3.5) circle[radius=3cm];
\end{tikzpicture}
25
Acknowledgments
I’d like to thank minhthien2016 for continuous support and encouragement. I thank the creators, main-
tainers, moderators and users of topanswers.xyz for their invaluable support. These acknowledgements
extend to all users on this site, including those with whom I have passionate disagreements. Disagree-
ments are good, they indicate critical thinking, or in the words of Nobel Laureate Steven Weinberg,
physics thrives on contradictions.
26