25

In drawing sets of planes like the figure below

Intersecting Planes in 3D

one frequently see solutions that involve drawing each visual piece of each plane in the order from the back to the front, like the code included here, and for another example see here (intersecting planes).

Question: Is it possible to draw the planes using 3D-coordinates and choose a viewpoint within TikZ, without having to calculate the view before hand?

 \documentclass{standalone}
 \usepackage{tikz}
 \usetikzlibrary{positioning,calc}
 \usetikzlibrary{intersections}

 \begin{document}
 \pagecolor{blue!30}
 \pagestyle{empty}

 \begin{tikzpicture}[scale=1.6]

 \definecolor{bg}{RGB}{246,202,203}

 \coordinate (A) at (0.95,3.41);
 \coordinate (B) at (1.95,0.23);
 \coordinate (C) at (3.95,1.23);
 \coordinate (D) at (2.95,4.41);

 \coordinate (E) at (1.90,3.30);
 \coordinate (F) at (0.25,0.45);
 \coordinate (G) at (2.25,1.45);
 \coordinate (H) at (3.90,4.30);

 \coordinate (I) at (-0.2,1.80);
 \coordinate (J) at (2.78,1.00);
 \coordinate (K) at (4.78,2.00);
 \coordinate (L) at (1.80,2.80);

  \path[name path=AB] (A) -- (B);
  \path[name path=CD] (C) -- (D);
  \path[name path=EF] (E) -- (F);
  \path[name path=IJ] (I) -- (J);
  \path[name path=KL] (K) -- (L);
  \path[name path=HG] (H) -- (G);
  \path[name path=IL] (I) -- (L);
    \path [name intersections={of=AB and EF,by=M}];
    \path [name intersections={of=EF and IJ,by=N}];
    \path [name intersections={of=AB and IJ,by=O}];
    \path [name intersections={of=AB and IL,by=P}];
    \path [name intersections={of=CD and KL,by=Q}];
    \path [name intersections={of=CD and HG,by=R}];
    \path [name intersections={of=KL and HG,by=S}];
  \path[name path=NS] (N) -- (S);
  \path[name path=FG] (F) -- (G);
    \path [name intersections={of=NS and AB,by=T}];
    \path [name intersections={of=FG and AB,by=U}];

 \draw[thick, color=white, fill=bg] (A) -- (B) -- (C) -- (D) -- cycle;
 %\draw[thick, color=white, fill=bg] (E) -- (F) -- (G) -- (H) -- cycle;
 %\draw[thick, color=white, fill=bg] (I) -- (J) -- (K) -- (L) -- cycle;

 \draw[thick, color=white, fill=gray!80] (P) -- (O) -- (I) -- cycle;
 \draw[thick, color=white, fill=gray!80] (O) -- (J) -- (K) -- (Q) -- cycle;
 \draw[thick, color=white, fill=gray!40] (H) -- (E) -- (M) -- (R) -- cycle;
 \draw[thick, color=white, fill=gray!40] (M) -- (N) -- (T) -- cycle;
 \draw[thick, color=white, fill=gray!40] (N) -- (F) -- (U) -- (O) -- cycle;

 \end{tikzpicture}
 \end{document}
6
  • 1
    To my knowledge, no. There is tikz-3dplot that highly simplifies drawings in 3D, but tikz essentially always draws the projection to the 2D paper. In your case, you can easily calculate the planes' lines of intersection and draw parts of the planes after each other.
    – Dux
    Commented Feb 18, 2016 at 17:40
  • Check out z buffer = sort. It is an option for tikz that may help put front faces on top. I use it with pfgplots a lot.
    – GregH
    Commented Feb 23, 2016 at 21:14
  • 1
    @GregH It is a pgfplots option - it is not part of standard PGF/TikZ. At least, unless there's an extension package or library which you're aware provides it? @ OP My understanding is Dux's understanding. You are asking about really handling 3D as 3D and PGF/TikZ doesn't do that. The best you can do is fake it - and, yes, that means that if you change perspective etc., you need to recalculate everything.
    – cfr
    Commented Feb 23, 2016 at 23:25
  • @GregH Can you provide an example? I fail to see how it would work ...
    – Paulo Ney
    Commented Feb 23, 2016 at 23:31
  • 1
    My comment was a bad one. I didn't have time to investigate fully but hoped a Google search would help the readers. But, ... yes, @cfr is right in that z buffer=sort is only part of pgfplots, not part of tikz. Also, even within pgfplots, that option only helps graph individual functions, not sets of functions. So it helps draw an ellipsoid, keeping the "back" faces in the back, but it won't help draw the intersection of an ellipsoid and a plane, nor the intersections of three planes. (I tried, and fail.) Asymptote is a viable option - very powerful and worth the learning curve.
    – GregH
    Commented Feb 24, 2016 at 12:42

2 Answers 2

11

This is more a fun answer with the message that it can be done but requires some patience. Moreover, I allow the latitude angle theta only to be in the range above 90 degrees. In this case, there are only two cases that have to be distinguished, i.e. this is not the general case. There are four cases, which are distinguished by two binary numbers

  • the sign of the projection of the 3D x-axis on the x-direction of the screen, \xproj.
  • the sign of cos(theta), \zproj (in the conventions of tikz-3dplot theta is between 0 and 180, and the equator is at theta=90). This sign tells one if one is in the southern or northern hemisphere.

That is, we will distinguish the cases \xproj and \zproj positive or negative. Depending on these signs, the order in which the planes are drawn changes. In order to retain a bit of clarity this answer comes with a macro \DrawSinglePlane{<plane number>, such that a change of the drawing order just corresponds to a permutation of the list of plane numbers.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\usetikzlibrary{calc}
\newcommand{\DrawPlane}[3][]{\draw[#1] 
(-1*\PlaneScale,{\PlaneScale*cos(#2)},{\PlaneScale*sin(#2)})
 -- ++  (2*\PlaneScale,0,0)
 -- ++ (0,{sqrt(3)*\PlaneScale*cos(#3)},{sqrt(3)*\PlaneScale*sin(#3)})
 -- ++  (-2*\PlaneScale,0,0) -- cycle;}
\newcommand{\DrawSinglePlane}[2][]{\ifcase#2
\or
\DrawPlane[fill=blue,#1]{210}{240} %left bottom 
\or
\DrawPlane[fill=red,#1]{-30}{-60} % right bottom
\or
\DrawPlane[fill=purple,#1]{210}{180} % bottom left
\or
\DrawPlane[fill=purple,#1]{210}{0} % bottom middle
\or
\DrawPlane[fill=purple,#1]{-30}{0} % bottom right
\or
\DrawPlane[fill=blue,#1]{90}{240} % left top
\or
\DrawPlane[fill=red,#1]{90}{-60} % right middle
\or
\DrawPlane[fill=red,#1]{90}{120} % right top
\or
\DrawPlane[fill=blue,#1]{90}{60} % left top
\fi
} 
\begin{document}
\foreach \X in {0,5,...,355}
{\tdplotsetmaincoords{90+40*sin(\X)}{\X} % the first argument cannot be larger than 90
\pgfmathsetmacro{\PlaneScale}{1}
\begin{tikzpicture}
\path[use as bounding box] (-4*\PlaneScale,-4*\PlaneScale) rectangle (4*\PlaneScale,4*\PlaneScale);
\begin{scope}[tdplot_main_coords]
% \draw[thick,->] (0,0,0) -- (2,0,0) node[anchor=north east]{$x$};
% \draw[thick,->] (0,0,0) -- (0,2,0) node[anchor=north west]{$y$};
% \draw[thick,->] (0,0,0) -- (0,0,1.5) node[anchor=south]{$z$};
\path let \p1=(1,0,0)  in 
\pgfextra{\pgfmathtruncatemacro{\xproj}{sign(\x1)}\xdef\xproj{\xproj}};
\pgfmathtruncatemacro{\zproj}{sign(cos(\tdplotmaintheta))}
\xdef\zproj{\zproj}
% \node[anchor=north west] at (current bounding box.north west)
% {\tdplotmaintheta,\tdplotmainphi,\xproj,\zproj};
\ifnum\zproj=1
  \ifnum\xproj=1
   \foreach \X in {2,1,5,4,3,7,6,9,8}
    {\DrawSinglePlane{\X}}
  \else
   \foreach \X in {1,...,9}
    {\DrawSinglePlane{\X}}
  \fi  
\else
  \ifnum\xproj=1
   \foreach \X in {9,8,7,6,3,4,5,2,1}
    {\DrawSinglePlane{\X}}
  \else
   \foreach \X in {8,9,6,7,3,5,4,1,2}
    {\DrawSinglePlane{\X}}
  \fi  
\fi  
\end{scope}
\end{tikzpicture}}
\end{document}

enter image description here

And for \tdplotsetmaincoords{90+40*cos(\X)}{\X}

enter image description here

A potentially important comment concerns pgfplots. In principle one can use patchplots to do the same. pfplots comes, as discussed in the comments, with some means to do the ordering.

UDPATE: Covers now the full range.

IMPORTANT NOTE: No duck or marmot were harmed in these animations. ;-)

2
  • 2
    Your gifs always look amazing.
    – manooooh
    Commented Aug 14, 2018 at 5:28
  • 1
    greattttt! I have done many of these creatures by Maple but you did by Latex!!!!! I don' tbelieve. +1000
    – Mikasa
    Commented Dec 23, 2018 at 12:57
10

Is Tikz a strong requirement? I've found Asymptote (included with TeXLive) to be an excellent tool for such tasks. Below is a very lightly edited example from the Asymptote Gallery.

You can change the viewpoint simply by changing the line that begins with currentprojection.

size(6cm,0);
import bsp;
real u=2.5;
real v=1;
currentprojection = oblique;
path3 y=plane((2u,0,0),(0,2v,0),(-u,-v,0));
path3 a=rotate(45,X)*y;
path3 l=rotate(-45,Z)*rotate(45,Y)*rotate(45,Z)*y;
path3 g=rotate(45,X)*rotate(45,Y)*rotate(45,Z)*y;
face[] faces;
filldraw(faces.push(a),project(a),gray);
filldraw(faces.push(l),project(l),blue);
filldraw(faces.push(g),project(g),pink);
add(faces);

This produces the following figure:

Intersecting planes generated by Asymptote.

While the one line change to currentprojection = perspective(5,2,3); produces this figure:

enter image description here

An excellent Asymptote Tutorial has been written by Charles Staats, a PhD student at the University of Chicago.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .