# Tikz: How to rotate 3D objects on a single plane?

I managed to come up with the following picture of boxes with different orientations. But now I'd like to have them all on the same plane, which I want to indicate with some plane (black rectangle currently). The thing is, how can I rotate the objects, but not change the perspective?

The blue and red box and their contents should be on the same plane as the yellow one, which is not the case currently.

My idea would be to use tikZ-3dplot to perform a rotation, but therefore the coordinate frame of tikz-3dplot would need to be aligned with the one from the perspective. How can this be done? If it can be done at all. Also other approaches/solutions are welcome.

\documentclass[tikz,border=1cm]{standalone}
\usetikzlibrary{backgrounds,perspective} % cleaned up

\begin{document}
\begin{tikzpicture}[>=latex,line cap=round,line join=round,scale=2]
\newcommand{\simpleaxes}[6]{% 1-3 position, 4-6 size
\draw[->] (-0.5+#1,#2,#3) -- (#1+#4,#2,#3) node[pos=1.1]{x};
\draw[->] (#1,-0.5+#2,#3) -- (#1,#2+#5,#3) node[pos=1.1]{y};
\draw[->] (#1,#2,-0.5+#3) -- (#1,#2,#3+#6) node[pos=1.1]{z};}
\tikzset{pics/coordsys/.style={
code = {\tikzset{coordsys/.cd,#1}
\draw [->,pic actions] (tpp cs:x=0,y=0,z=0) -- (tpp cs:x=1,y=0,z=0)[red] node[pos=1.4]
{$\pgfkeysvalueof{/tikz/coordsys/x}$};
\begin{scope}[on background layer]
\draw [->,pic actions] (tpp cs:x=0,y=0,z=0) -- (tpp cs:x=0,y=1,z=0)[green!60!black] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsys/y}$};
\end{scope}
\draw [->,pic actions] (tpp cs:x=0,y=0,z=0) -- (tpp cs:x=0,y=0,z=1)[blue] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsys/z}$};
}
},coordsys/.cd,x/.initial=x,y/.initial=y,z/.initial=z}

%%%%%% fix euler rotation: Matlab eul2rotm([pi/4,pi/4,pi/4])*eye(3)
\tikzset{pics/coordsysshift/.style={
code = {\tikzset{coordsysshift/.cd,#1}
\draw [->,pic actions] (tpp cs:x=0,y=0,z=0) -- (tpp cs:x=0.5,y=0.5,z=-0.7071)[red] node[pos=1.3]
{$\pgfkeysvalueof{/tikz/coordsysshift/x}$};
\begin{scope}[on background layer]
\draw [->,pic actions] (tpp cs:x=0,y=0,z=0) -- (tpp cs:x=-0.1464,y=0.8536,z=0.5)[green!60!black] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsysshift/y}$};
\end{scope}
\draw [->,pic actions] (tpp cs:x=0,y=0,z=0) -- (tpp cs:x=0.8536,y=-0.1464,z=0.5)[blue] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsysshift/z}$};
}
},coordsysshift/.cd,x/.initial=x,y/.initial=y,z/.initial=z}

\tikzset{pics/complicated/.style={code={\tikzset{complicated/.cd,#1}
\begin{scope}
\draw[\boxframecolor] (tpp cs:x=0,y=0,z=\cubez) %xy plane/top face
-- ++(tpp cs:x=\cubex,y=0,z=0)
-- ++(tpp cs:x=0,y=\cubey,z=0)
-- ++(tpp cs:x=-\cubex,y=0,z=0) -- cycle;
\draw[\boxframecolor] (tpp cs:x=0,y=0,z=0) %yz plane/left face
-- ++(tpp cs:x=0,y=0,z=\cubez)
-- ++(tpp cs:x=0,y=\cubey,z=0)
-- ++(tpp cs:x=0,y=0,z=-\cubez) -- cycle;
\draw[\boxframecolor] (tpp cs:x=0,y=0,z=0) %xz plane/front face
-- ++(tpp cs:x=\cubex,y=0,z=0)
-- ++(tpp cs:x=0,y=0,z=\cubez)
-- ++(tpp cs:x=-\cubex,y=0,z=0) -- cycle;
\end{scope}
\begin{scope}[on background layer]
\fill[\boxfillcolor] (tpp cs:x=0,y=0,z=\cubez) %xy plane
-- ++(tpp cs:x=\cubex,y=0,z=0)
-- ++(tpp cs:x=0,y=\cubey,z=0)
-- ++(tpp cs:x=-\cubex,y=0,z=0) -- cycle;
\fill[\boxfillcolor] (tpp cs:x=0,y=0,z=0) %yz plane
-- ++(tpp cs:x=0,y=0,z=\cubez)
-- ++(tpp cs:x=0,y=\cubey,z=0)
-- ++(tpp cs:x=0,y=0,z=-\cubez) -- cycle;
\fill[\boxfillcolor] (tpp cs:x=0,y=0,z=0) %xz plane
-- ++(tpp cs:x=\cubex,y=0,z=0)
-- ++(tpp cs:x=0,y=0,z=\cubez)
-- ++(tpp cs:x=-\cubex,y=0,z=0) -- cycle;
\end{scope}

%%%% define positioning of M,B frames inside box
\pgfmathsetmacro{\dx}{0.5};
\pgfmathsetmacro{\dy}{0.5};
\pgfmathsetmacro{\dz}{0.4};
\coordinate (origin) at (5,3,0);
%\coordinate (M) at (\dx,\cubey-\dy,\dz);
%\coordinate (B) at (\cubex-\dx,\dy,\cubez-\dz);
\coordinate (M) at (\dx,\dy,\dz);
\coordinate (B) at (\cubex-\dx,\cubey-\dy,\cubez-\dz);

\draw (M) pic[thick] {coordsys={x=x_M,y=y_M,z=z_M}};
\node [below right] at (M.south) {\textit{M}};
\draw (B) pic[dotted] {coordsys={x=x_M',y=y_M',z=z_M'}};
\node [above left] at (B.west) {\textit{B}};
\draw (B) pic[thick] {coordsysshift={x=x_{B},y=y_{B},z=z_{B}}};

\draw [->, thick] (M) -- (B) node [midway,fill=\boxfillcolor] {$q_{MB}, r_{MB}$};
}},complicated/.cd,cube x/.store in=\cubex,cube x=8,
cube y/.store in=\cubey,cube y=8,
cube z/.store in=\cubez,cube z=2,
box fill color/.store in=\boxfillcolor,box fill color=yellow!20!,
box frame color/.store in=\boxframecolor,box frame color=gray!20!,}

\begin{scope}[3d view={-80}{15}]
\path (tpp cs:x=0,y=5,z=0) pic{complicated};
\simpleaxes{0}{0}{0}{8}{8}{2};
\draw (tpp cs:x=-10,y=0,z=0) -- (tpp cs:x=10,y=0,z=0) -- (tpp cs:x=10,y=10,z=0) -- (tpp cs:x=-10,y=10,z=0) --cycle;
\end{scope}
\begin{scope}[3d view={-30}{15}]
\path (tpp cs:x=2,y=4,z=0) pic{complicated={box fill color=blue!20}};
\end{scope}
\begin{scope}[3d view={-10}{15}]
\path (tpp cs:x=0,y=-5,z=0) pic{complicated={box fill color=red!20}};
\end{scope}

\end{tikzpicture}
\end{document}

• Welcome to TeX.SX! Do you use the perspective library only to use the 3d view key? You don't seem to use the actual perspective projection, so you could also skip using the tpp cs coordinate system. That could really make stuff simpler. – Max Sep 17 '19 at 13:12
• Hi @Max! Yes until now I use only the tpp cs coordinates. What do you mean by actual perspective projection? Setting the epipolar/vanishing points? I don't see how this can rotate my box. Do you have a simple example upon which I can build on? – avermaet Sep 17 '19 at 13:17
• Do you want to use vanishing points eventually? Because support for arbitrary rotations and translations in the perspective library is virtually non-existent, so you would have to calculate all coordinates manually. If you do not want to use vanishing points, then you should switch to normal 3D coordinates. I can give an example of the latter if you like. – Max Sep 17 '19 at 13:21
• I want basically to have 3 boxes with minor rotations to each other (their contents/frames should rotate with them), which are observed from a single view point (in 3d space). I don't really know what's necessary to get there because I don't have much experience using tikZ. If you have some approch you can show me that would be great. Maybe just 2 boxes (without contents) rotated to each other and observed from some point. – avermaet Sep 17 '19 at 13:37
• Regarding coordinates, I don't really know what I need/want or if vanishing points can do that. Also rotation of boxes can be just around z-axis(up). Arbitrary rotations are not necessary. I just want to depict that the object moved and changed it's orientation. However this can be accomplished. – avermaet Sep 17 '19 at 13:39

I think the problem was not necessarily that the rotations were wrong, but it looked wrong because of the drawing order. I tried to fix this using some transparency and drawing the common plane on the background. This is the result, not so different from what you already achieved:

To get this, I put the drawing of the boxes in a transparency group, and used fill opacity=0.8 (the group is necessary because you also use fill for the q_MB, r_MB node). I also changed all the tpp cs:x=..,y=..,z=.. to simply (..,..,..) which works fine if you do not use vanishing points. To get one viewing projection, I use 3d view only once. To rotate the boxes I used rotate around z=<angle>. Finally, for your coordsysshift pic, I used three consecutive rotations of 45 degrees, instead of providing the vector endpoints manually. Final code:

\documentclass[tikz,border=1cm]{standalone}
\usetikzlibrary{backgrounds,perspective} % cleaned up

\begin{document}
\begin{tikzpicture}[>=latex,line cap=round,line join=round,scale=2]
\newcommand{\simpleaxes}[6]{% 1-3 position, 4-6 size
\draw[->] (-0.5+#1,#2,#3) -- (#1+#4,#2,#3) node[pos=1.1]{x};
\draw[->] (#1,-0.5+#2,#3) -- (#1,#2+#5,#3) node[pos=1.1]{y};
\draw[->] (#1,#2,-0.5+#3) -- (#1,#2,#3+#6) node[pos=1.1]{z};}
\tikzset{pics/coordsys/.style={
code = {\tikzset{coordsys/.cd,#1}
\draw [->,pic actions] (0,0,0) -- (1,0,0)[red] node[pos=1.4]
{$\pgfkeysvalueof{/tikz/coordsys/x}$};
\draw [->,pic actions] (0,0,0) -- (0,1,0)[green!60!black] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsys/y}$};
\draw [->,pic actions] (0,0,0) -- (0,0,1)[blue] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsys/z}$};
}
},coordsys/.cd,x/.initial=x,y/.initial=y,z/.initial=z}

%%%%%% fix euler rotation: Matlab eul2rotm([pi/4,pi/4,pi/4])*eye(3)
\tikzset{pics/coordsysshift/.style={
code = {\tikzset{coordsysshift/.cd,#1}
\begin{scope}[rotate around z=45, rotate around y=45, rotate around x=45]
\draw [->,pic actions] (0,0,0) -- (1,0,0)[red] node[pos=1.3]
{$\pgfkeysvalueof{/tikz/coordsysshift/x}$};
\draw [->,pic actions] (0,0,0) -- (0,1,0)[green!60!black] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsysshift/y}$};
\draw [->,pic actions] (0,0,0) -- (0,0,1)[blue] node[pos=1.2]
{$\pgfkeysvalueof{/tikz/coordsysshift/z}$};
\end{scope}
}
},coordsysshift/.cd,x/.initial=x,y/.initial=y,z/.initial=z}

\tikzset{
pics/complicated/.style={
code={
\tikzset{complicated/.cd,#1}
\begin{scope}[transparency group, fill opacity=0.8]
\draw[\boxframecolor,fill=\boxfillcolor] (0,0,\cubez) %xy plane/top face
-- ++(\cubex,0,0)
-- ++(0,\cubey,0)
-- ++(-\cubex,0,0) -- cycle;
\draw[\boxframecolor,fill=\boxfillcolor] (0,0,0) %yz plane/left face
-- ++(0,0,\cubez)
-- ++(0,\cubey,0)
-- ++(0,0,-\cubez) -- cycle;
\draw[\boxframecolor,fill=\boxfillcolor] (0,0,0) %xz plane/front face
-- ++(\cubex,0,0)
-- ++(0,0,\cubez)
-- ++(-\cubex,0,0) -- cycle;

%%%% define positioning of M,B frames inside box
\pgfmathsetmacro{\dx}{0.5};
\pgfmathsetmacro{\dy}{0.5};
\pgfmathsetmacro{\dz}{0.4};
\coordinate (origin) at (5,3,0);
%\coordinate (M) at (\dx,\cubey-\dy,\dz);
%\coordinate (B) at (\cubex-\dx,\dy,\cubez-\dz);
\coordinate (M) at (\dx,\dy,\dz);
\coordinate (B) at (\cubex-\dx,\cubey-\dy,\cubez-\dz);

\draw (M) pic[thick] {coordsys={x=x_M,y=y_M,z=z_M}};
\node [below right] at (M.south) {\textit{M}};
\draw (B) pic[dotted] {coordsys={x=x_M',y=y_M',z=z_M'}};
\node [above left] at (B.west) {\textit{B}};
\draw (B) pic[thick] {coordsysshift={x=x_{B},y=y_{B},z=z_{B}}};

\draw [->, thick] (M) -- (B) node [midway,fill=\boxfillcolor] {$q_{MB}, r_{MB}$};
\end{scope}
}
},
complicated/.cd,
cube x/.store in=\cubex,
cube x=8,
cube y/.store in=\cubey,
cube y=8,
cube z/.store in=\cubez,
cube z=2,
box fill color/.store in=\boxfillcolor,
box fill color=yellow!25,
box frame color/.store in=\boxframecolor,
box frame color=gray!25
}

\begin{scope}[3d view={-80}{15}]
\draw[fill=gray!10] (-10,0,0) -- (10,0,0) -- (10,10,0) -- (-10,10,0) -- cycle;
\simpleaxes{0}{0}{0}{8}{8}{2};
\path (0,5,0) pic{complicated};
\path[rotate around z=-50] (2,4,0) pic{complicated={box fill color=blue!25}};
\path[rotate around z=-70] (0,-5,0) pic{complicated={box fill color=red!25}};
\end{scope}

\end{tikzpicture}
\end{document}


The only remaining problem is now that the z axis of the \simpleaxes macro is partially 'behind' the blue box, but this could be fixed by drawing the x and y axes first, then the boxes, and finally the z axis.

• Thanks! But somehow it still looks to me as if the blue and red boxes are not on the same plane (from the yellow box). Does a vanishing point improve this impression in any way? Also the \simpleaxisis not important can could be removed. – avermaet Sep 17 '19 at 14:23
• I don't think a vanishing point will make it any better, but you could try 3d view={-80}{10} to decrease the upward angle of the viewing position, this seems to make it look a little better. Alternatively you could extend the common plane to also fit the red block. You can do this easily with the 3d library and \draw[canvas is xy plane at z=0,fill=gray!10] (-10,-5) rectangle (10,10);. – Max Sep 17 '19 at 14:35
• Great, I think it was the ground plane which created this impression. Thanks again! – avermaet Sep 17 '19 at 14:39