# Draw a prism in TikZ or PSTricks

Is there a way to define an polygon in plane and output an (orthogonal) prism with that polygon as its base? The prism should be drawn in 3D in parallel oblique perspective with controllable height h, scaling factor k and angle α. Would be great if something like this is possible in TikZ or PSTricks.

So I want a command \prism which takes the list of points (which define the polygon in a plane), α,k and h as an argument and give me the prism as output.

Perhaps I should make clear what I mean by k and α: For example if you draw a cube in 3D you draw one line, then another one in an angle α = 45° but with k = 1/2 the length of the first one etc.

I think this is called parallel oblique projection (α = 45° and k = 1 would be called cavalier projection, α = 63,4° and k=1/2 cabinet projection). Even though it would be interesting for further purposes, I don't want a one-point perspective projection.

In the picture above the lines in the background are not dashed. However I want dashed background lines. If you have a better picture of those projections, feel free to replace it.

Here are some references about projection types:

• I opened the question to pstricks, since it is really important for me to get this problem solved. Would be great if it is possible in tikz because I love this, but pstricks or some other solution would be fine too. Dec 10, 2011 at 9:46

base contains the list of the x/y polygon coordinates and axe defines the direction vector "x y z" of the prism, which is by default axe=0 0 1

\documentclass{article}
\usepackage{pst-solides3d}
\begin{document}

\psset{unit=0.5,lightsrc=10 5 50,viewpoint=50 20 30 rtp2xyz,Decran=50}
\begin{pspicture*}(-6,-4)(6,9)
\psframe(-6,-4)(6,9)
\psSolid[object=grille,base=-4 4 -4 4,fillcolor=red!30]
\psSolid[object=prisme,h=6,fillcolor=blue!10,
base=0 1 -1 0 0 -2 1 -1 0 0]
\axesIIID(4,4,6)(4.5,4.5,8)
\end{pspicture*}
%
\begin{pspicture*}(-6,-4)(6,9)
\psframe(-6,-4)(6,9)
\psSolid[object=grille,base=-4 4 -4 4,fillcolor=red!30]
\psSolid[object=prisme,fillcolor=blue!10,
axe=0 1 2,h=8,base=0 -2 1 -1 0 0 0 1 -1 0]
\psPoint(0,4.2,8.4){V}
\psline[linecolor=blue,arrowscale=2]{->}(0,0)(V)
\axesIIID(4,4,4)(4.5,4.5,8)
\end{pspicture*}

\end{document}


Simple Boxes with pst-3dplot

\documentclass{article}
\usepackage{pst-3dplot}
\begin{document}
\psset{coorType=1,Alpha=135}
\begin{pspicture}(-1,-2)(5,2.25)
%\pstThreeDCoor[xMin=-1,xMax=4,yMin=-1,yMax=4,zMin=-1,zMax=4]
\pstThreeDBox[hiddenLine=false](0,0,0)(0,0,3)(3,0,0)(0,3,0)
\end{pspicture}
%
\psset{coorType=2}
\begin{pspicture}(-3,-2)(2,2.25)
%\pstThreeDCoor[xMin=-1,xMax=4,yMin=-1,yMax=4,zMin=-1,zMax=4]
\pstThreeDBox[hiddenLine](0,0,0)(0,0,3)(3,0,0)(0,3,0)
\end{pspicture}

\end{document}


\documentclass{article}
\usepackage{pst-3dplot}
\begin{document}

\psset{coorType=2}
\begin{pspicture}(-2,-2.25)(2,5)
\pstThreeDCoor[xMin=-2,xMax=2,yMin=-2,yMax=5,zMin=-2,zMax=6]
\pstThreeDLine(0,0,0)(0,3,0)(-2,0,0)(0,-3,0)(1,-3,0)(0,0,0)
\pstThreeDLine(1,2,5)(1,5,5)(-1,2,5)(1,-1,5)(2,-1,5)(1,2,5)
\pstThreeDLine(0,0,0)(1,2,5)
\pstThreeDLine(0,3,0)(1,5,5)
\pstThreeDLine[linestyle=dashed](-2,0,0)(-1,2,5)
\pstThreeDLine[linestyle=dashed](0,-3,0)(1,-1,5)
\pstThreeDLine(1,-3,0)(2,-1,5)
\end{pspicture}

\end{document}


and an automatic solution which needs the latest pst-3dplot.tex from http://texnik.dante.de/tex/generic/pst-3dplot/. The Macro \psThreeDPrism will move later to CTAN and also very later I'll realize hidden lines. move=x y is the translation vector for the upper polygon

\documentclass{article}
\usepackage{pst-3dplot}
\begin{document}

\psset{coorType=2}
\begin{pspicture}(-3,-2)(2,5)
\pstThreeDCoor[xMin=-2,xMax=2,yMin=-2,yMax=5,zMin=-2,zMax=7]
\pstThreeDPrism[height=6,move=1 2](0,0,0)(0.5,3,0)(-2,0,0)(0,-3,0)(1,-3,0)(0,0,0)
\end{pspicture}

\end{document}


• Thanks, looks great. Is it possible to get the prism's drawn like in Altermundus's answer (the orientation is ok, just white sides and dashed background lines? Dec 10, 2011 at 10:11
• If I try this, I get an error: ERROR: Undefined control sequence. --- TeX said --- <recently read> \c@lor@to@ps l.9 \psframe(-6,-4)(6,9) Dec 10, 2011 at 10:14
• use action=draw
– user2478
Dec 10, 2011 at 10:16
• run it with xelatex or use the sequence latex->dvips->ps2pdf or use package auto-pst-pdf and run it with pdflatex -shell-escape <file>. See tug.org/PSTricks/main.cgi?file=pdf/pdfoutput
– user2478
Dec 10, 2011 at 10:18
• Thanks, auto-pst-pdf works for me, action-draw works too. Is it possible to control the dash pattern in some way? Dec 10, 2011 at 10:28

Version 1

You define xand y to get correct a and k. It's not the unique way and it's also possible to reduce the code with a macro.

\documentclass[]{scrartcl}
\usepackage{tikz}
\usetikzlibrary{3d}
\begin{document}

\begin{tikzpicture}[x  = {(-0.65cm,-0.45cm)},
y  = {(0.65cm,-0.45cm)},
z  = {(0cm,0.8cm)},
scale = 2]

\begin{scope}[canvas is zy plane at x=5]
\draw (0,0) coordinate (a1)
-- (3,2) coordinate (a2)
-- (3,4) coordinate (a3)
-- (2,5) coordinate (a4)
-- (0,4) coordinate (a5)--cycle ;
\end{scope}

\begin{scope}[canvas is zy plane at x=0]
\path (0,0) coordinate (b1)
(3,2) coordinate (b2)
(3,4) coordinate (b3)
(2,5) coordinate (b4)
(0,4) coordinate (b5);
\end{scope}

\draw (b2)--(b3)--(b4)--(b5);

\foreach \i in {2,...,5}
\draw (a\i)--(b\i);

\draw[dashed] (b5)--(b1)--(b2) (a1)--(b1);
\end{tikzpicture}
\end{document}


Version 2 I changed the name of the nodes. Bi for vertices of the Background face and Fi for vertices of the Front face. Now I created a macro to define the points. You need to give the coordinates, the coefficient and alpha (l'angle de fuite).

The code for the first picture is

\begin{tikzpicture}[scale=1.6]
\definePrism{(0,0),
(1,0),
(1,1),
(0,1)}{0}{1}{.5}{30}
\begin{scope}[x  = {(0cm,1cm)},
y  = {(1cm,0)},
z  = {(-\ordz cm,-\absz cm)}]
\begin{scope}[canvas is yz plane at x=0]
\draw[dotted] (0,0) circle (1cm);
\draw[<->] (1,0) arc (0:-90:1cm);
\draw[dotted,blue] (0,0)--(0,-1);
\node[text width=2cm] at (0.5,-2) {fuite\\ $\alpha=30^{\circ}$};
\node[text width=2cm] at (-0.6,0.2) {$-k\cos(\alpha)$\\
$-k\sin(\alpha)$};
\end{scope}
\end{scope}
\end{tikzpicture}


Now a complete example

\documentclass[]{scrartcl}
\usepackage{tikz}
\usetikzlibrary{3d}

\newcommand {\definePrism}[5]
{\pgfmathsetmacro{\absz}{#4*sin(#5)} \pgfmathsetmacro{\ordz}{#4*cos(#5)}
\begin{scope}[x  = {(0cm,1cm)},
y  = {(1cm,0)},
z  = {(-\ordz cm,-\absz cm)}]
\begin{scope}[canvas is xy plane at z=#2]
\path \foreach \coord [count=\ni] in {#1} {\coord coordinate (B\ni)};
\end{scope}
\begin{scope}[canvas is xy plane at z=#3]
\path  \foreach \coord [count=\ni] in {#1} {\coord coordinate (F\ni)};
\end{scope}
\end{scope}
}
\begin{document}

\begin{tikzpicture}[scale=1]

\definePrism{(0,0),
(3,2),
(3,4),
(2,5),
(0,2)}{0}{8}{.7}{45}

\draw (F1) \foreach \i in {2,...,5} {--(F\i)} -- cycle;
\draw (B2)--(B3)--(B4);
\draw[dashed] (B4)--(B5)--(B1)--(B2);

\draw          (F2)--(B2)
(F3)--(B3)
(F4)--(B4);
\draw[dashed]  (F1)--(B1)
(F5)--(B5);
\end{tikzpicture}
\end{document}


version 2 with macro \definePrism

 \definePrism[options]{list 1}{list 2}
options angle (default=45) coeff (default=.5) zB (default=0) zF (default=2)
list 1 (x1,y1),(x2,y2),...,(xn,yn)
list 2  s1,s2,...,sn with sn = 0 or 1---> 0 if  Bn is hidden
coordinates defined : B1,B2,...,Bn and F1,F2,...,Fn


Only problem : how to determine s1,s2,...,sn automatically . I know some algorithms but too complicated with TeX

\documentclass[]{scrartcl}
\usepackage{tikz}
\usetikzlibrary{3d}

\pgfkeys{
/definePrism/.cd,
angle/.code                = {\def\dpangle{#1}},
coeff/.code                = {\def\dpcoeff{#1}},
zB/.code                    = {\def\zB{#1}},
zF/.code                    = {\def\zF{#1}},}
\makeatletter
\def\definePrism{\pgfutil@ifnextchar[{\define@Prism}{\define@Prism[]}}
\def\define@Prism[#1]#2#3{%
\begingroup
\pgfkeys{/definePrism/.cd, angle=45,coeff=.5,zB=0,zF=2}
\pgfqkeys{/definePrism}{#1}
\pgfmathsetmacro{\absz}{\dpcoeff*sin(\dpangle)}
\pgfmathsetmacro{\ordz}{\dpcoeff*cos(\dpangle)}
\begin{scope}[x  = {(0cm,1cm)},
y  = {(1cm,0)},
z  = {(-\ordz cm,-\absz cm)}]
\begin{scope}[canvas is xy plane at z=\zB]
\path \foreach \coord [count=\ni] in {#2} {%
\coord   coordinate  (B\ni)
};
\end{scope}
\begin{scope}[canvas is xy plane at z=\zF]
\path  \foreach \coord [count=\ni] in {#2} {%
\coord coordinate (F\ni)
};
\end{scope}
\end{scope}

\foreach \k [count=\ni] in {#3} {%
\global\let\nb\ni
\global\let\lasti\k}
\draw (F1) \foreach \i in {2,...,\nb} {--(F\i)} -- cycle;

\foreach \i  [count=\ni,count=\si from \nb] in {#3}{
\ifnum \ni > \nb \pgfmathtruncatemacro{\ni}{1} \fi
\ifnum \si > \nb \pgfmathtruncatemacro{\si}{1} \fi
\ifnum \i  = 0
\draw[dashed] (B\si)--(B\ni)--(F\ni);
\else
\draw (F\ni)--(B\ni);
\ifnum \lasti=1
\draw (B\si)--(B\ni);
\else
\draw[dashed] (B\si)--(B\ni);
\fi
\fi
\global\let\lasti\i
}%
\endgroup}
\begin{document}

\begin{tikzpicture}[scale=1]
\definePrism[angle=30,zF=8]{(0,0),(4,1),(3,4),(2,3),(0,2)}{0,1,1,1,1}
\end{tikzpicture}
\begin{tikzpicture}[scale=1]
\definePrism[angle=30]{(0,0),(0,2),(2,2),(2,0)}{0,1,1,1}
\end{tikzpicture}
\end{document}


• Thanks, that's exaclty that type of image that I want. However, how to make a macro which takes a list of points (which define the polygon in the plane), \alpha, k and h as arguments and returns the image? Perhaps the problem is that the number of points is variable? Dec 7, 2011 at 15:13
• No the list of points is not the main problem but there is a big problem with dashed edges. Dec 7, 2011 at 15:36
• Yes I see. Don't know if there is an algorithm to determine the background edges automatically. However this is what I need. Dec 10, 2011 at 9:35
• I think your answer is great, but only the first step in the right direction. I need that everything is drawn automatically, define only the polygon and so on. So even though I voted your answer up, I am not going to accept it, because it's really important for me to get this fully automated... Dec 10, 2011 at 9:39
• @user4011 A possibility would be to give for with each point of the polygon the state ( a boolean) in the background face (hidden or not). I's not obvious to know automatically the state of a point. I'm very interesting if someone knows how to do this ! Dec 10, 2011 at 10:07

I've modified @Alain Matthes' version 2 code such that one can pass a piece of code that is then expanded in the right scope, allowing one to draw additional lines with access to the vertices. I'm not trying to take credit for anything here, Alain clearly did 99% of the work, and even over 5 years later his code is still very helpful. It just took me a bit to put this example together so I wanted to share it (I undid the (unintentional?) x/y coordinate swapping from his code, too).

\documentclass[preview]{standalone}
\usepackage{tikz}
\usetikzlibrary{3d}
\usetikzlibrary{calc}

\pgfkeys{
/definePrism/.cd,
angle/.code                = {\def\dpangle{#1}},
coeff/.code                = {\def\dpcoeff{#1}},
zB/.code                   = {\def\zB{#1}},
zF/.code                   = {\def\zF{#1}}
}
\makeatletter
\def\definePrism{\pgfutil@ifnextchar[{\define@Prism}{\define@Prism[]}}
\def\define@Prism[#1]#2#3#4{%
\begingroup
\pgfkeys{/definePrism/.cd, angle=45,coeff=.5,zB=0,zF=2}
\pgfqkeys{/definePrism}{#1}
\pgfmathsetmacro{\absz}{\dpcoeff*sin(\dpangle)}
\pgfmathsetmacro{\ordz}{\dpcoeff*cos(\dpangle)}
\begin{scope}[x  = {(1cm,0cm)},
y  = {(0cm,1cm)},
z  = {(-\absz cm,-\ordz cm)}]
\begin{scope}[canvas is xy plane at z=\zB]
\path \foreach \coord [count=\ni] in {#2} {%
\coord   coordinate  (B\ni)};
\end{scope}
\begin{scope}[canvas is xy plane at z=\zF]
\path  \foreach \coord [count=\ni] in {#2} {%
\coord coordinate (F\ni)};
#4;
\end{scope}
\end{scope}

\foreach \k [count=\ni] in {#3} {%
\global\let\nb\ni
\global\let\lasti\k}
\draw (F1) \foreach \i in {2,...,\nb} {--(F\i)} -- cycle;

\foreach \i  [count=\ni,count=\si from \nb] in {#3}{
\ifnum \ni > \nb \pgfmathtruncatemacro{\ni}{1} \fi
\ifnum \si > \nb \pgfmathtruncatemacro{\si}{1} \fi
\ifnum \i  = 0
\draw[draw=none] (B\si)--(B\ni)--(F\ni);
\else
\draw (F\ni)--(B\ni);
\ifnum \lasti=1
\draw (B\si)--(B\ni);
\else
\draw[draw=none] (B\si)--(B\ni);
\fi
\fi
\global\let\lasti\i
}%
\endgroup}
\begin{document}

\begin{figure}
\centering
\begin{tikzpicture}[scale=1]
\def\smallHeight{1.0}
\def\largeHeight{3.0}
\def\width{5}
\def\angle{30}
\def\depth{5}
\def\coeff{0.25}
\pgfmathsetmacro{\ol}{0.8} % overlap
\pgfmathsetmacro{\nol}{1.0-\ol}
\definePrism[angle=\angle,zF=\depth,coeff=\coeff]%
{
(\width,0),
(0,0),
(0,\largeHeight),
(\ol*\width,\ol*\smallHeight + \nol*\largeHeight),
(\width,\smallHeight)
}{1,0,0,1,1}{
\draw[-{latex},semithick] ($(F3)!0.25!(F5)+(0,-0.2)$) -- ($(F3)!0.75!(F5)+(0,-0.2)$);
}
\definePrism[angle=\angle,zF=\depth,coeff=\coeff]%
{
(\ol*\width,\ol*\smallHeight+\nol*\largeHeight),
(-\nol*\width,-\nol*\smallHeight+\largeHeight+\nol*\largeHeight),
(-\nol*\width,\ol*\smallHeight+\largeHeight+\nol*\largeHeight),
(\ol*\width,\ol*\smallHeight+\largeHeight+\nol*\largeHeight)
}{1,0,1,1}{
\draw[-{latex},semithick] ($(F1)!0.25!(F2)+(0,0.2)$) -- ($(F1)!0.75!(F2)+(0,0.2)$);
}
\end{tikzpicture}
\caption{A thrust fault}
\end{figure}

\begin{figure}
\centering
\begin{tikzpicture}[scale=1]
\def\height{0.2}
\def\width{1.5}
\def\angle{30}
\def\depth{5}
\def\coeff{0.25}
\pgfmathsetmacro{\ol}{0.5} % overlap
\pgfmathsetmacro{\nol}{1.0-\ol}
\definePrism[angle=\angle,zB=-\nol*\depth,zF=\ol*\depth,coeff=\coeff]%
{
(0,0),
(0,\height),
(\width,\height),
(\width,0)
}{0,1,1,1}{
\draw[-{latex},semithick]
($(F3)!0.25!(B3)+(-0.2,0)$) -- ($(F3)!0.75!(B3)+(-0.2,0)$);
}
\definePrism[angle=\angle,zB=0,zF=\depth,coeff=\coeff]%
{
(\width,0),
(\width,\height),
(2*\width,\height),
(2*\width,0)
}{0,1,1,1}{
%% NB: Instead of figuring out what parts of the first parallelepiped,
%% should not be drawn, we simply draw over it here. If the background is
%% not white, the result will not be the same, naturally.
\path[fill=white] (B1)--(B2)--(F2)--(F1)--cycle;
\path[fill=white] (F1)--(F2)--(F3)--(F4)--cycle;
\draw[fill=white] (F2)--(B2)--(B3)--(F3)--cycle;
\draw[-{latex},semithick]
($(B2)!0.25!(F2)+(0.2,0)$) -- ($(B2)!0.75!(F2)+(0.2,0)$);
}
\end{tikzpicture}
\caption{A strike-slip fault}
\end{figure}
\end{document}


• @toandhsp This should address your question. Mar 21, 2017 at 15:46