share
TeX - LaTeXDraw a prism in TikZ or PSTricks
[+27] [3] student
[2011-12-07 13:52:15]
[ tikz-pgf pstricks programming 3d ]
[ https://tex.stackexchange.com/questions/37442/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.

Cabinet and Cavalier

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. - student
[+29] [2011-12-07 14:51:22] Alain Matthes

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).

enter image description here

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}

enter image description here

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} 

enter image description here


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? - student
No the list of points is not the main problem but there is a big problem with dashed edges. - Alain Matthes
Yes I see. Don't know if there is an algorithm to determine the background edges automatically. However this is what I need. - student
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... - student
@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 ! - Alain Matthes
It seems that the pstricks solution can detect the background lines. Perhaps one can analyze the pst-solides3d source code and convert the solution to tikz, too... - student
@user4011 Too complicated for me. There a lot of things that you can do with Postcript and not with only TeX. pst-solides3d is a fine solution but I don't understand how you get the dashed lines in Herbert's solution. - Alain Matthes
Great update. The algorithms too complicated for tex: Perhaps it would be an idea to calculate the background lines with an external program? - student
yes perhaps with lua - Alain Matthes
@AlainMatthes Can I label the vertices of a Prism in this lines? \begin{tikzpicture}[scale=1] \definePrism[angle=30]{(0,0),(0,2),(2,2),(2,0)}{0,1,1,1} \end{tikzpicture} - minhthien_2016
1
[+15] [2011-12-10 10:04:23] user2478 [ACCEPTED]

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}

enter image description here

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}

enter image description here

\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}

enter image description here

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}

enter image description here


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? - student
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) - student
use action=draw - user2478
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
Thanks, auto-pst-pdf works for me, action-draw works too. Is it possible to control the dash pattern in some way? - student
\pstVerb{SolidesDict begin /pointilles {[6.25 3.75] 1.25 setdash } def end } that is the original definition. You can insert this command before the pspicture environment and change the values 6.25 3.75 which represents line--space. This sequence may also contain more than two values - user2478
the base points are x y values because z is always 0 - user2478
Is it possible to get a clearer syntax for specifiying the base points? Like enclosing each point in brackets e.g. (1,1)? - student
sure, everything is possible with some code changes. But I cannot see the advantage over the list where each pair can be in an own line if it maybe easier to read. - user2478
Ok, thanks, I will put them simply in a new line... - student
With all the enthusiasm about your solution I have overseen, that your example uses a one-point projection. However what I want is a parallel, oblique projection. I will give some references to projection types in my original post. - student
parallel view is the same as viewing an object from a very large distance: \psset{unit=0.5,viewpoint=1000 20 30 rtp2xyz,Decran=1000,action=draw}%,dash=0.2 0.4} - user2478
ok, how must I choose the parameters to get for example the cabinet projection? - student
see edited answer for simple boxes in parallel view - user2478
... but how does your simple box example genaralize to genaral prisms in parallel view? - student
see edit. However, everything is shown in the documentation. - user2478
no, that's not what I want, using pst-3dplot you have to specify everything manually. Your first solution with pst-3dsolids is almost perfect the only missing point is how to get the parallel oblique perspective... - student
do you need the dashed lines? - user2478
Yes I need the dashed lines. However an automatic solution without dashed lines would be better than nothing... - student
see my edited answer - user2478
I get the following error:ERROR: Package xkeyval Error: linejoin' undefined in families pstricks'. --- TeX said --- See the xkeyval package documentation for explanation. Type H <return> for immediate help. ... l.244 \setIIIDplotDefaults - student
your pstricks.tex is out of date and I suppose also some other files. - user2478
Ok, thanks, I updated my pstricks base, now it works with latex, but not with pdflatex and auto-pst-pdf - student
2
[+2] [2017-03-21 15:42:36] anonymous

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}

Rendering


@toandhsp This should address your question. - anonymous
3