# Tikz:: shift and rotate in 3d?

I would like to translate (Tx,Ty,Tz) and rotate (roll,pitch,yaw) in 3D space an object defined in scope similar to the same way we can do in 2D space as below:

\begin{scope}[shift={(7,9)},rotate=30]
% draw some object
\end{scope}


Is there any simple way for 3D rotation and translation?

Here's a solution, but only for translation (so far). It uses the fact that internaly TikZ handles 3D points as 2D ones. So your 3D shift vector is saved in the coordinate mytranslation which then is a 2D point and thus can be passed to shift. The thick lines are just to illustrate that the translation is done correctly:

\documentclass{standalone}
\usepackage{tikz}

\newcommand{\translatepoint}[1]%
{   \coordinate (mytranslation) at (#1);
}

\newcommand{\somedrawing}%
{   \coordinate (a) at (0,0,0);
\coordinate (b) at (5,1,0);
\coordinate (c) at (2,4,0);
\coordinate (d) at (1,-1,-4);
\draw (a) -- (b) (a) -- (c) (a) -- (d) (b) -- (c) (b) -- (d) (c) -- (d);
}

\begin{document}

\begin{tikzpicture}[x={(0.866cm,0.5cm)},y={(-0.866cm,0.5cm)},z={(0cm,1cm)}]
\somedrawing

\translatepoint{5,0,0}
\draw[very thick,red] (0,0,0) -- (5,0,0);
\begin{scope}[shift=(mytranslation),draw=red]
\somedrawing
\end{scope}

\translatepoint{0,6,0}
\draw[very thick,blue] (0,0,0) -- (0,6,0);
\begin{scope}[shift=(mytranslation),draw=blue]
\somedrawing
\end{scope}

\translatepoint{0,0,8}
\draw[very thick,green!50!gray] (0,0,0) -- (0,0,8);
\begin{scope}[shift=(mytranslation),draw=green!50!gray]
\somedrawing
\end{scope}

\translatepoint{-5,0,-2}
\draw[very thick,orange] (0,0,0) -- (-5,0,-2);
\begin{scope}[shift=(mytranslation),draw=orange]
\somedrawing
\end{scope}
\end{tikzpicture}

\end{document}


Edit 1: I finally managed the rotation, but, that was a bitch. I put the mathematical foundation for RPY rotations first, a really nice matrix. Then I borrowed an idea from Andrew Stacey from this question: finding out where the (cartesian) unit vectors point via \pgfgetlastxy. Then it was only writing the matrix in Tikz notation, using \pgfgetlastxy once again and there you go!

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{arrows}
\usepackage{amsmath}

\newcommand{\rotateRPY}[3]% roll, pitch, yaw
{   \pgfmathsetmacro{\rollangle}{#1}
\pgfmathsetmacro{\pitchangle}{#2}
\pgfmathsetmacro{\yawangle}{#3}

% to what vector is the x unit vector transformed, and which 2D vector is this?
\pgfmathsetmacro{\newxx}{cos(\yawangle)*cos(\pitchangle)}
\pgfmathsetmacro{\newxy}{sin(\yawangle)*cos(\pitchangle)}
\pgfmathsetmacro{\newxz}{-sin(\pitchangle)}
\path (\newxx,\newxy,\newxz);
\pgfgetlastxy{\nxx}{\nxy};

% to what vector is the y unit vector transformed, and which 2D vector is this?
\pgfmathsetmacro{\newyx}{cos(\yawangle)*sin(\pitchangle)*sin(\rollangle)-sin(\yawangle)*cos(\rollangle)}
\pgfmathsetmacro{\newyy}{sin(\yawangle)*sin(\pitchangle)*sin(\rollangle)+ cos(\yawangle)*cos(\rollangle)}
\pgfmathsetmacro{\newyz}{cos(\pitchangle)*sin(\rollangle)}
\path (\newyx,\newyy,\newyz);
\pgfgetlastxy{\nyx}{\nyy};

% to what vector is the z unit vector transformed, and which 2D vector is this?
\pgfmathsetmacro{\newzx}{cos(\yawangle)*sin(\pitchangle)*cos(\rollangle)+ sin(\yawangle)*sin(\rollangle)}
\pgfmathsetmacro{\newzy}{sin(\yawangle)*sin(\pitchangle)*cos(\rollangle)-cos(\yawangle)*sin(\rollangle)}
\pgfmathsetmacro{\newzz}{cos(\pitchangle)*cos(\rollangle)}
\path (\newzx,\newzy,\newzz);
\pgfgetlastxy{\nzx}{\nzy};
}

\tikzset{RPY/.style={x={(\nxx,\nxy)},y={(\nyx,\nyy)},z={(\nzx,\nzy)}}}

\begin{document}

$R=\begin{pmatrix} \cos \alpha \cos \beta & \textcolor{red}{\cos \alpha \sin \beta \sin \gamma - \sin \alpha \cos \gamma} & \cos \alpha \sin \beta \cos \gamma + \sin \alpha \sin \gamma \\ \textcolor{red}{\sin \alpha \cos \beta} & \sin \alpha \sin \beta \sin \gamma + \cos \alpha \cos \gamma & \textcolor{red}{\sin \alpha \sin \beta \cos \gamma - \cos \alpha \sin \gamma} \\ - \sin \beta & \textcolor{red}{\cos \beta \sin \gamma} & \cos \beta \cos \gamma \end{pmatrix}\\p'=R\cdot p$

\begin{tikzpicture}
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);

\rotateRPY{0}{0}{45}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);
\end{scope}
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 0,0,45};
\end{tikzpicture}
\begin{tikzpicture}
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);

\rotateRPY{0}{30}{0}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);
\end{scope}
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 0,30,0};
\end{tikzpicture}

\begin{tikzpicture}
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);

\rotateRPY{80}{0}{0}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);
\end{scope}
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 80,0,0};
\end{tikzpicture}
\begin{tikzpicture}[x={(0.866cm,-0.5cm)},y={(-0.866cm,-0.5cm)},z={(0cm,1cm)}]
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);

\rotateRPY{13}{171}{55}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\draw[-latex] node at (3.5,0,0) {x} (0,0,0) -- (3,0,0);
\draw[-latex] node at (0,3.5,0) {y} (0,0,0) -- (0,3,0);
\draw[-latex] node at (0,0,3.5) {z} (0,0,0) -- (0,0,3);
\end{scope}
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 13,171,55};
\end{tikzpicture}

\end{document}


Edit 2: Now you can pass a point to the command and determine to what new xyz coordinates it is mapped. Also, inspired by Herbert's solution, there now is a better example featuring cubes and arrows:

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{arrows,3D}

\newcommand{\savedx}{0}
\newcommand{\savedy}{0}
\newcommand{\savedz}{0}

\newcommand{\somedrawing}%
{   \coordinate (a) at (-2,-2,-2);
\coordinate (b) at (-2,-2,2);
\coordinate (c) at (-2,2,-2);
\coordinate (d) at (-2,2,2);
\coordinate (e) at (2,-2,-2);
\coordinate (f) at (2,-2,2);
\coordinate (g) at (2,2,-2);
\coordinate (h) at (2,2,2);
\draw (a)--(b) (a)--(c) (a)--(e) (b)--(d) (b)--(f) (c)--(d) (c)--(g) (d)--(h) (e)--(f) (e)--(g) (f)--(h) (g)--(h);
\fill (a) circle (0.1cm);
\fill (d) ++(0.1cm,0.1cm) rectangle ++(-0.2cm,-0.2cm);
}

\newcommand{\rotateRPY}[4][0/0/0]% point to be saved to \savedxyz, roll, pitch, yaw
{   \pgfmathsetmacro{\rollangle}{#2}
\pgfmathsetmacro{\pitchangle}{#3}
\pgfmathsetmacro{\yawangle}{#4}

% to what vector is the x unit vector transformed, and which 2D vector is this?
\pgfmathsetmacro{\newxx}{cos(\yawangle)*cos(\pitchangle)}% a
\pgfmathsetmacro{\newxy}{sin(\yawangle)*cos(\pitchangle)}% d
\pgfmathsetmacro{\newxz}{-sin(\pitchangle)}% g
\path (\newxx,\newxy,\newxz);
\pgfgetlastxy{\nxx}{\nxy};

% to what vector is the y unit vector transformed, and which 2D vector is this?
\pgfmathsetmacro{\newyx}{cos(\yawangle)*sin(\pitchangle)*sin(\rollangle)-sin(\yawangle)*cos(\rollangle)}% b
\pgfmathsetmacro{\newyy}{sin(\yawangle)*sin(\pitchangle)*sin(\rollangle)+ cos(\yawangle)*cos(\rollangle)}% e
\pgfmathsetmacro{\newyz}{cos(\pitchangle)*sin(\rollangle)}% h
\path (\newyx,\newyy,\newyz);
\pgfgetlastxy{\nyx}{\nyy};

% to what vector is the z unit vector transformed, and which 2D vector is this?
\pgfmathsetmacro{\newzx}{cos(\yawangle)*sin(\pitchangle)*cos(\rollangle)+ sin(\yawangle)*sin(\rollangle)}
\pgfmathsetmacro{\newzy}{sin(\yawangle)*sin(\pitchangle)*cos(\rollangle)-cos(\yawangle)*sin(\rollangle)}
\pgfmathsetmacro{\newzz}{cos(\pitchangle)*cos(\rollangle)}
\path (\newzx,\newzy,\newzz);
\pgfgetlastxy{\nzx}{\nzy};

% transform the point given by #1
\foreach \x/\y/\z in {#1}
{   \pgfmathsetmacro{\transformedx}{\x*\newxx+\y*\newyx+\z*\newzx}
\pgfmathsetmacro{\transformedy}{\x*\newxy+\y*\newyy+\z*\newzy}
\pgfmathsetmacro{\transformedz}{\x*\newxz+\y*\newyz+\z*\newzz}
\xdef\savedx{\transformedx}
\xdef\savedy{\transformedy}
\xdef\savedz{\transformedz}
}
}

\tikzset{RPY/.style={x={(\nxx,\nxy)},y={(\nyx,\nyy)},z={(\nzx,\nzy)}}}

\begin{document}

\begin{tikzpicture}
\somedrawing
\rotateRPY{0}{0}{101}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\somedrawing
\end{scope}
\draw[canvas is xy plane at z=2,-latex,thick, blue] (-2,2) arc (135:236:2.828);
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 0,0,101};
\end{tikzpicture}
\begin{tikzpicture}
\somedrawing
\rotateRPY{0}{55}{0}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\somedrawing
\end{scope}
\draw[canvas is xz plane at y=2,-latex,thick, blue] (-2,2) arc (135:80:2.828);
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 0,55,0};
\end{tikzpicture}

\begin{tikzpicture}
\somedrawing
\rotateRPY{61}{0}{0}
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\somedrawing
\end{scope}
\draw[canvas is yz plane at x=-2,-latex,thick, blue] (2,2) arc (45:106:2.828);
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 71,0,0};
\end{tikzpicture}
\begin{tikzpicture}%[x={(1cm,1cm)},y={(-1cm,1cm)},z={(-2cm,-1cm)}]
\somedrawing
\rotateRPY[-2/2/2]{13}{171}{55}
\node[fill=white,fill opacity=0.7,text opacity=1,align=left,text=blue] at (5,2)  {-2 $\longrightarrow$ \savedx \\ 2 $\longrightarrow$ \savedy \\ 2 $\longrightarrow$ \savedz};
\begin{scope}[draw=red, text=red,fill=red,densely dashed,RPY]
\somedrawing
\end{scope}
\draw[blue,ultra thick] (-2,2,2) -- (\savedx,2,2) -- (\savedx,\savedy,2) -- (\savedx,\savedy,\savedz) circle (0.25);
\rotateRPY[-2/-2/-2]{13}{171}{55}
\node[fill=white,fill opacity=0.7,text opacity=1,align=left,text=green!50!gray] at (5,0)  {-2 $\longrightarrow$ \savedx \\ -2 $\longrightarrow$ \savedy \\ -2 $\longrightarrow$ \savedz};
\draw[green!50!gray,ultra thick] (-2,-2,-2) -- (\savedx,-2,-2) -- (\savedx,\savedy,-2) -- (\savedx,\savedy,\savedz) circle (0.25);
\node[fill=white,fill opacity=0.7,text opacity=1] {RPY: 13,171,55};
\end{tikzpicture}

\end{document}


• Perfect! Thank you very much for explaining the solution in details. Aug 18, 2012 at 16:47
• Can your test coordinate systems be changed to polar coordinates smoothly? - - I am thinking your case application here tex.stackexchange.com/q/345303/13173 in polar coordinates. Dec 22, 2016 at 21:30
• @Tom Bombadil - I presume this can work with any curve (defined as a smooth combination of arbitrary points) and is not limited to straight line segments? Feb 3, 2017 at 23:24

an example with PStricks. Run it with xelatex

\documentclass{article}
\pagestyle{empty}
\setlength\parindent{0pt}
\usepackage{pst-solides3d}
\begin{document}
\psset{viewpoint=20 60 20 rtp2xyz,lightsrc=10 15 7,Decran=20}
\begin{pspicture}(-5,-5)(6,5)
\psSolid[object=grille,base=-3 3 -3 3,fillcolor=red!50]
\axesIIID(4,4,3)
\psSolid[object=cube,a=2,ngrid=3,RotX=45,RotY=30,fillcolor=yellow!50](-2,0,0)
\psSolid[object=cube,a=2,ngrid=3,transform={.75 3 .5 scaleOpoint3d}](2,0,1)
\psSolid[object=cube,a=2,ngrid=3,RotZ=30,transform={ 3 -2 0 addv3d },action=draw](-2,0,0)
\end{pspicture}
\end{document}

• 1st cube: rotated at the x and y axis (center of the box at (-2,0,0) )
• 2nd: scaled by x=0.75, y=3, z=0.5 (center at (2,0,1) )
• 3rd: rotated at the z axis and moved by (3,-2,0) (center at (-2,0,0) )