7

How can I simplify some positioning with some foreach loops or some own styles? Here is the original picture:

Screenshot

And here is my MWE:

\documentclass[border=5pt,tikz]{standalone}
\usetikzlibrary{calc}
\begin{document}
    \begin{tikzpicture}[every node/.style={font=\tiny},scale=1.5,xscale=1.2]
        \coordinate (a) at (0:1.5);
        \coordinate (b) at (60:1);
        \coordinate (c) at (120:1);
        \coordinate (d) at (180:1.5);
        \coordinate (e) at (240:1);
        \coordinate (f) at (300:1);
            \draw[dashed] (a) -- (b) -- (c) -- (d);
            \draw (d) -- (e) -- (f) -- (a);
                \draw (a) -- (0,3);
                \draw (0,3) -- ($(b)!.5!(0,3)$);
                \draw[dashed] (c) -- ($(c)!.5!(0,3)$);
                    \draw ($(c)!.5!(0,3)$) -- (0,3);
                \draw[dashed] (b) -- ($(b)!.5!(0,3)$);
                \draw (0,3) -- (d);
                \draw (0,3) -- (e);
                \draw (0,3) -- (f);
            \draw ($(a)!.5!(0,3)$) -- ($(b)!.5!(0,3)$) -- ($(c)!.5!(0,3)$) -- ($(d)!.5!(0,3)$) -- ($(e)!.5!(0,3)$) -- ($(f)!.5!(0,3)$) -- cycle;
                    \fill[black,opacity=.3] ($(a)!.5!(0,3)$) -- ($(b)!.5!(0,3)$) -- ($(c)!.5!(0,3)$) -- ($(d)!.5!(0,3)$) -- ($(e)!.5!(0,3)$) -- ($(f)!.5!(0,3)$) -- cycle;
                \fill[gray,opacity=.3] ($(a)!.5!(0,3)$) -- ($(b)!.5!(0,3)$) -- (b) -- (a) -- cycle;
                \fill[gray,opacity=.3] ($(b)!.5!(0,3)$) -- ($(c)!.5!(0,3)$) -- (c) -- (b) -- cycle;
                \fill[gray,opacity=.3] ($(d)!.5!(0,3)$) -- ($(e)!.5!(0,3)$) -- (e) -- (d) -- cycle;
                \fill[gray,opacity=.3] ($(c)!.5!(0,3)$) -- ($(d)!.5!(0,3)$) -- (d) -- (c) -- cycle;
                \fill[gray,opacity=.3] ($(e)!.5!(0,3)$) -- ($(f)!.5!(0,3)$) -- (f) -- (e) -- cycle;
                \fill[black,opacity=.3] ($(f)!.5!(0,3)$) -- ($(a)!.5!(0,3)$) -- (a) -- (f) -- cycle;
            \draw[dashed] ([xshift=-.75cm]$(a)!.5!(0,3)$) -- (0,0);
                \draw[fill=white,radius=.1em] (0,0) circle node[right] {$O_1$};
                    \draw ([xshift=-.75cm]$(a)!.5!(0,3)$) -- (0,3);
            \draw[fill=white,radius=.1em] ([xshift=-.75cm]$(a)!.5!(0,3)$) circle node[above right=-3.5] {$O_1'$};
                \node[above right=-2] at (b) {$A_i$};
                    \node[above left=-2] at (b) {$A_2'$};
                    \node[above right=-2] at (c) {$A_1'$};
                        \draw[fill=white,radius=.1em] (b) circle;
                        \draw[fill=white,radius=.1em] (c) circle;
                \node[left] at (d) {$A_n$};
                \node[below] at (e) {$A_1$};
                \node[below] at (f) {$A_2$};
            \draw[fill=white,radius=.1em] (0,3) circle node[above] {$O$};
                \node[above left=-2] at ($(d)!.5!(0,3)$) {$A_n'$};
                \node[above right=-4] at ($(b)!.5!(0,3)$) {$A_i'$};
            \draw[fill=white,radius=.1em] ($(a)!.5!(0,3)$) circle;
            \draw[fill=white,radius=.1em] ($(b)!.5!(0,3)$) circle;
            \draw[fill=white,radius=.1em] ($(c)!.5!(0,3)$) circle;
            \draw[fill=white,radius=.1em] ($(d)!.5!(0,3)$) circle;
            \draw[fill=white,radius=.1em] ($(e)!.5!(0,3)$) circle;
            \draw[fill=white,radius=.1em] ($(f)!.5!(0,3)$) circle;
    \end{tikzpicture}
\end{document}

Here is the ouput, too:

Screenshot

BONUS: How can in generally be the code cleaned up?

3
  • 2
    +1. TikZ is very literate, even understands the alphabet. So you could simplify the last lines of your code to \foreach \X in {a,...,f} {\draw[fill=white,radius=.1em] ($(\X)!.5!(0,3)$) circle;} and so on...
    – user121799
    Aug 4, 2018 at 22:38
  • @marmot: Oh, I didn't know about that, thank you! Are there any more possibilities to clean the “mess“ a little bit up? Aug 4, 2018 at 22:39
  • 1
    +1: Great example of a professional looking LaTeX drawing! – Aug 6, 2018 at 20:19

2 Answers 2

9

Since this is IMHO a very nice question, I'd like to collect some tricks I learned from others or the pgfmanual.

  1. TikZ is very literate, it understands the alphabet, so you can do loops over {a,...,f}, say.

  2. And you can also do double loops.

  3. One can convert number to letters. I don't know if my way is the most elegant one, but one can define a counter, set it, and use \alph to convert it to a letter. (I do not claim any priority on this trick and would not be surprised if that has been done more elegantly somewhere else. I just did not search long enough.)

  4. A trick that I learned (if I remember correctly) from @percusse is to do something like

    \draw plot[samples at={a,...,f},variable=\x] (\x);

    to connect a to b ... to f.

  5. An arguably even more powerful trick, which I learned when writing this answer, is the foreach style. An example is in the code below. There is also Loop Space's answer, which I however did not dare to try out. Note that this does not at all imply that there is something wrong, I just hesitate to use it. EDIT: As pointed out by @Max Snippe, an even more elegant way is to simply say

    \fill[black,opacity=0.3] (a') foreach \p in {b,...,f}{ -- (\p')} -- cycle;

I also added three front faces with low opacity to make the 3D feel even more pronounced.

\documentclass[border=5pt,tikz]{standalone}
\usetikzlibrary{calc}
\newcounter{cheat}
\begin{document}
    \begin{tikzpicture}[every node/.style={font=\tiny},scale=1.5,xscale=1.2]
        \foreach \X [count=\Y,evaluate=\Y as \Z using {int(60*(\Y-1))}] 
        in {a,...,f}
        {\pgfmathtruncatemacro{\Cheat}{int(mod(\Y-1,3))}
        \ifnum\Cheat=0
            \coordinate (\X) at (\Z:1.5);
        \else
            \coordinate (\X) at (\Z:1);
        \fi
        }
        \draw[dashed] (a) -- (b) -- (c) -- (d);
        \draw (d) -- (e) -- (f) -- (a);
        \draw (a) -- (0,3);
        % define primed coordinates for later use
        \foreach \X in {a,...,f}
        {\coordinate (\X') at ($(\X)!.5!(0,3)$);}
        \draw (0,3) -- (b');
        \draw[dashed] (c) -- (c');
        \draw (c') -- (0,3);
        \draw[dashed] (b) -- (b');
        \foreach \X in {d,e,f}
            {\draw (0,3) -- (\X);}
        % foreach style from https://tex.stackexchange.com/a/124105/121799  
        \fill[black,opacity=.3,foreach/.style={insert path=--(#1')}] (a') [foreach/.list={b,...,f}]
        -- cycle;       
        \foreach \X [count=\Y,evaluate=\Y as \Z using {ifthenelse(\Y==5,6,int(mod(\Y+1,6)))}] 
            in {a,...,f} {\setcounter{cheat}{\Z}
            \def\NextX{\alph{cheat}}
            \draw (\X') -- (\NextX');
        \fill[gray,opacity=.3] (\NextX') -- (\X') --
            (\X) -- (\NextX) -- cycle;
        }
        \draw[dashed] ([xshift=-.75cm]$(a)!.5!(0,3)$) -- (0,0);
        \draw[fill=white,radius=.1em] (0,0) circle node[right] {$O_1$};
        \draw ([xshift=-.75cm]$(a)!.5!(0,3)$) -- (0,3);
        \draw[fill=white,radius=.1em] ([xshift=-.75cm]$(a)!.5!(0,3)$) circle node[above right=-3.5] {$O_1'$};
        \node[above right=-2] at (b) {$A_i$};
        \node[above left=-2] at (b) {$A_2'$};
        \node[above right=-2] at (c) {$A_1'$};
        \draw[fill=white,radius=.1em] (b) circle;
        \draw[fill=white,radius=.1em] (c) circle;
        \node[left] at (d) {$A_n$};
        \node[below] at (e) {$A_1$};
        \node[below] at (f) {$A_2$};
        \draw[fill=white,radius=.1em] (0,3) circle node[above] {$O$};
        \node[above left=-2] at ($(d)!.5!(0,3)$) {$A_n'$};
        \node[above right=-4] at ($(b)!.5!(0,3)$) {$A_i'$};
        \foreach \X in {a,...,f}
            {\draw[fill=white,radius=.1em] ($(\X)!.5!(0,3)$) circle;}
        \foreach \X/\Y in {d/e,e/f,f/a} 
        {\fill[opacity=0.15,gray] (\X) -- (\X') -- (\Y') -- (\Y) -- cycle;}
    \end{tikzpicture}
\end{document}

enter image description here

12
  • 1
    @current_user I think this is the first time we disagree: you are not a fool! ;-)
    – user121799
    Aug 4, 2018 at 23:04
  • 1
    @current_user You made me discover discover a very useful trick, thanks! (Marmots are beings, please see the tikzmarmots package documentation for a proof ;-)
    – user121799
    Aug 4, 2018 at 23:25
  • 1
    Just trowing this out there: it's not so much the foreach style, it's more calling it with the /.list handler. You can define a key with arbitrary code (it should do something with a single argument, otherwise it wouldn't make sense) and when calling it with the /.list handler it will execute that code sequentially for all values in the list argument.
    – Max
    Aug 5, 2018 at 7:58
  • 1
    Also I just noticed, in this case you don't even need the list handler. You can replace the corresponding line with \fill[black,opacity=0.3] (a') foreach \p in {b,...,f}{ -- (\p')} -- cycle;.
    – Max
    Aug 5, 2018 at 10:30
  • 1
    @Max_Snippe: Could you post an answer with all the corrections/simplifications that you've found? Aug 5, 2018 at 10:53
3

Improving on marmot's nice answer adding a few more loops to append the nodes and simplifying a bit more (added Max Snippe's hint, for example):

\documentclass[border=5pt,tikz]{standalone}
\usetikzlibrary{calc}
\newcounter{cheat}
\begin{document}
    \begin{tikzpicture}[every node/.style={font=\tiny},scale=1.5,xscale=1.2]
        \foreach \X [count=\Y,evaluate=\Y as \Z using {int(60*(\Y-1))}] 
          in {a,...,f}
          {\pgfmathtruncatemacro{\Cheat}{int(mod(\Y-1,3))}
          \ifnum\Cheat=0
            \coordinate (\X) at (\Z:1.5);
          \else
            \coordinate (\X) at (\Z:1);
          \fi}
        \coordinate (h) at (0,3);
        \coordinate (g) at (0,0);
        \draw[dashed] (a) -- (b) -- (c) -- (d);
        \draw (d) -- (e) -- (f) -- (a);
        \draw (a) -- (h);
        \foreach \X in {a,...,g}
          {\coordinate (\X') at ($(\X)!.5!(0,3)$);}
        \foreach \X in {b,c,g}
          {\draw[dashed] (\X) -- (\X');
          \draw (h) -- (\X');}
        \foreach \X in {d,e,f}
          {\draw (h) -- (\X);}
        \fill[black,opacity=.3] (a') foreach \X in {b,...,f} { -- (\X') } -- cycle;
        \fill[gray,opacity=.3] (a) foreach \X in {f,f',a'} { -- (\X) } -- cycle;
        \foreach \X [count=\Y,evaluate=\Y as \Z using {ifthenelse(\Y==5,6,int(mod(\Y+1,6)))}] 
          in {a,...,f} 
          {\setcounter{cheat}{\Z}
            \def\NextX{\alph{cheat}}
            \draw (\X') -- (\NextX');
          \fill[gray,opacity=.3] (\NextX') -- (\X') -- (\X) -- (\NextX) -- cycle;}
        \foreach \X in {b,c,g,h}
          {\draw[fill=white,radius=.1em] (\X) circle;}
        \foreach \X in {a,...,g}
          {\draw[fill=white,radius=.1em] (\X') circle;}
        \foreach \X/\Y/\Z 
          in {e/below/A_1,c/below/A_1',
              f/below/A_2,b/below/A_2',
              d/left/A_n, d'/left/A_n',
              b/right/A_i,b'/right/A_i',
              g/right/O_1,h/right/O}
          {\node[\Y] at (\X) {$\Z$};}
        \node[below right=-2.5] at (g') {$O_1'$};
    \end{tikzpicture}
\end{document}
1
  • 2
    One could easily simplify further. There is no reason why in your last \foreach below and so on are doubled for the first 5 entries, i.e. you could treat primed and unprimed quantities the same way.
    – user121799
    Aug 5, 2018 at 23:54

You must log in to answer this question.

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