12

Ok so I am continuing my construction of fractals in Tikz - another one is here: Tikz Fractal - Cantor Dust. I was hoping someone would be able to help me create another, somewhat more complicated, fractal. It's called the Menger Sponge and is essentially a '3D' (although defining fractals as such is a little contradictory) Sierpinski Carpet. It looks like this:

enter image description here

Where you start with the unit cube, and recursively remove the centre 9th of each face all the way through the cube. A better, more precise description is here: https://en.wikipedia.org/wiki/Menger_sponge.

I'm thinking it should be possible with a 'lindenmayer system' but I have only a small amount of experience with those and I've never used it for 3D shapes.

After using Nancy-N's solution, I get this:

enter image description here

(notice the little lines around each smaller cube).

5
  • This is 3D with shadows so you better not bet on TikZ too much. I would try PSTricks or Asymptote for this.
    – percusse
    Jul 26, 2017 at 19:39
  • @percusse Ok I have never used either of those, any suggestions?
    – JSharpee
    Jul 26, 2017 at 19:47
  • 2
    Lines are probably just viewer artefacts.
    – cfr
    Jul 26, 2017 at 23:01
  • @cfr Lines are artifacts somehow indeed, due to the fact that we are making the drawing by superimposing successively different small cubes (which are drawn as sets of color zones); and sometimes two zones which should be mathematically adjacent are drawn with a very small gap between them, at least in the redering of the viewer. A way to circumvent that is to change the code of level 0 so as to ask for a very slightly larger cube, so that small cubes will (slightly) overlap. Jul 27, 2017 at 14:50
  • 1
    @JSharpee Now, Nancy's answer is complete. You should choose it so that I can remove mine... Thanks. Jul 28, 2017 at 6:18

1 Answer 1

16

I do not know if this will make you happy, but yes, a kind of Lindenmayer system can work for your problem, using the fact that the cube is made of 20 copies of a 3-times-smaller itself, who themselves are made of a 3-times-smaller cube, and so on. Here is a way you can do it; I let you make the needed adaptations to get a better picture.

First generate a .tex file level0.tex with no \documentclass, \begin{document} or so, whose content just would be:

\fill[yellow] (1, 1, 1) -- (1, -1, 1) -- (-1, -1, 1) -- (-1, 1, 1) -- cycle;
\fill[cyan] (1, 1, 1) -- (1, 1, -1) -- (1, -1, -1) -- (1, -1, 1) -- cycle;
\fill[magenta] (1, 1, 1) -- (1, 1, -1) -- (-1, 1, -1) -- (-1, 1, 1) -- cycle;

Now you create a file level1.tex (again, no \documentclass or so) in which you use the content of level0 twenty times as if you were putting several small cubes near each other. Beware that you have to draw the cubes in a correct order, so that a cube hiding another one will only be drawn after it. Your file level1.tex would be the following:

\begin{scope}[scale=.3333333333]
\begin{scope}[shift={(-2, -2, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(0, -2, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, -2, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, 0, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, 0, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, 2, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(0, 2, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, 2, -2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, -2, 0)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, -2, 0)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, 2, 0)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, 2, 0)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, -2, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(0, -2, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, -2, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, 0, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, 0, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(-2, 2, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(0, 2, 2)}]
\input{level0}
\end{scope}
\begin{scope}[shift={(2, 2, 2)}]
\input{level0}
\end{scope}
\end{scope}

Next you create a file level2.tex which is the same as level1.tex, except that \input{level0} is replaced by \input{level1}. And finally your final drawing (inside a real .tex source file with \documentclass and so on) will be

\begin{tikzpicture}
\input{level2}
\end{tikzpicture}

Below is the rendering I get. Of course you can go up to level 3, 4 or so, at least theoretically, but do not forget that the amount of computation and memory used will be multiplied by a factor 20 at each further level!

Menger sponge, level 2

Kind regards, /Nancy-N


EDIT: Paul Gaborit has quite nicely improved my solution by coding a command \mengersponge taking one argument, so that you do not have to create one file per level. Here is his code (where he also chose more relevant colours ;-)):

\documentclass[tikz]{standalone}
\usepackage{luatex85}
\newcommand\mengersponge[1]{
  \pgfmathsetmacro\level{int(#1 - 1)}
  \ifnum \level = 0 \relax
  \filldraw[gray!50!black,line width=0] (1, 1, 1) -- (1, 1, -1) -- (1, -1, -1) -- (1, -1, 1) -- cycle;
  \filldraw[gray,line width=0] (1, 1, 1) -- (1, 1, -1) -- (-1, 1, -1) -- (-1, 1, 1) -- cycle;
  \filldraw[gray!50,line width=0] (1, 1, 1) -- (1, -1, 1) -- (-1, -1, 1) -- (-1, 1, 1) -- cycle;
  \else
  \begin{scope}[scale=.3333333333]
    \begin{scope}[shift={(-2, -2, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(0, -2, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, -2, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, 0, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, 0, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, 2, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(0, 2, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, 2, -2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, -2, 0)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, -2, 0)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, 2, 0)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, 2, 0)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, -2, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(0, -2, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, -2, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, 0, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, 0, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(-2, 2, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(0, 2, 2)}]
      \mengersponge{\level}
    \end{scope}
    \begin{scope}[shift={(2, 2, 2)}]
      \mengersponge{\level}
    \end{scope}
  \end{scope}
  \fi
}
\begin{document}
\begin{tikzpicture}
  \mengersponge{4}
\end{tikzpicture}
\end{document}

With pdflatex, Paul Gaborit could only perform the computations up to level 3 (corresponding to \mengersponge{4} in his code), which took 40 seconds to compilate; but with lualatex he could compile level 4 (corresponding to \mengersponge{5}). Below are his pictures :-)

For level 3:

Menger sponge, level 3

For level 4:

Menger sponge, level 4

Regards, /Nancy-N

5
  • I might go for milder colors... but I'll give it a go, thank you.
    – JSharpee
    Jul 26, 2017 at 20:57
  • It is messy as hell but it does work. Thank you. The only issue is that there are little lines around all of the cubes (see update).
    – JSharpee
    Jul 26, 2017 at 21:43
  • Mathematically speaking, I would not say that it is messy actually; but it is a huge waste of cumputing power undoubtedly ;-) As regards the issue of small lines, see my answer to cfr's comment under your main post. Jul 27, 2017 at 14:53
  • @JSharpee Notice that you are not forced to keep this orientation of the cube, of course: you can ask TikZ to choose the representation of the basic vectors (1, 0, 0), (0, 1, 0) and (0, 0, 1) the way you want ;-) Jul 27, 2017 at 14:56
  • 1
    @Nancy-N The compilation of \mengersponge{4} takes 40 seconds. The compilation of \mengersponge{5} takes a much longer time! ;-) Jul 28, 2017 at 6:22

You must log in to answer this question.

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