27

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:

student
  • 29,003
  • 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 Dec 10 '11 at 09:46

3 Answers3

29

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

Moriambar
  • 11,466
Alain Matthes
  • 95,075
  • 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 Dec 07 '11 at 15:13
  • No the list of points is not the main problem but there is a big problem with dashed edges. – Alain Matthes Dec 07 '11 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. – student Dec 10 '11 at 09: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... – student Dec 10 '11 at 09: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 ! – Alain Matthes Dec 10 '11 at 10:07
  • 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 Dec 10 '11 at 12:39
  • @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 Dec 10 '11 at 13:26
  • Great update. The algorithms too complicated for tex: Perhaps it would be an idea to calculate the background lines with an external program? – student Dec 10 '11 at 22:19
  • yes perhaps with lua – Alain Matthes Dec 10 '11 at 22:30
  • @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 Nov 03 '16 at 02:08
15

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 Dec 10 '11 at 10:11
  • If I try this, I get an error: `ERROR: Undefined control sequence.

    --- TeX said --- \c@lor@to@ps

    l.9 \psframe(-6,-4)(6,9)`

    – student Dec 10 '11 at 10:14
  • use action=draw –  Dec 10 '11 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 http://tug.org/PSTricks/main.cgi?file=pdf/pdfoutput –  Dec 10 '11 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? – student Dec 10 '11 at 10:28
  • \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 –  Dec 10 '11 at 10:36
  • the base points are x y values because z is always 0 –  Dec 10 '11 at 10:37
  • Is it possible to get a clearer syntax for specifiying the base points? Like enclosing each point in brackets e.g. (1,1)? – student Dec 10 '11 at 10:44
  • 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. –  Dec 10 '11 at 10:47
  • Ok, thanks, I will put them simply in a new line... – student Dec 10 '11 at 10:53
  • 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 Dec 10 '11 at 11:34
  • 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} –  Dec 10 '11 at 11:54
  • ok, how must I choose the parameters to get for example the cabinet projection? – student Dec 10 '11 at 12:31
  • see edited answer for simple boxes in parallel view –  Dec 10 '11 at 14:01
  • ... but how does your simple box example genaralize to genaral prisms in parallel view? – student Dec 10 '11 at 14:07
  • see edit. However, everything is shown in the documentation. –  Dec 10 '11 at 14:47
  • 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 Dec 10 '11 at 14:56
  • do you need the dashed lines? –  Dec 10 '11 at 18:45
  • Yes I need the dashed lines. However an automatic solution without dashed lines would be better than nothing... – student Dec 10 '11 at 19:15
  • see my edited answer –  Dec 10 '11 at 19:34
  • I get the following error:ERROR: Package xkeyval Error: linejoin' undefined in familiespstricks'.

    --- TeX said ---

    See the xkeyval package documentation for explanation. Type H for immediate help. ...

    l.244 \setIIIDplotDefaults

    – student Dec 10 '11 at 21:57
  • your pstricks.tex is out of date and I suppose also some other files. –  Dec 10 '11 at 22:01
  • Ok, thanks, I updated my pstricks base, now it works with latex, but not with pdflatex and auto-pst-pdf – student Dec 10 '11 at 22:09
2

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

anonymous
  • 1,159