5

I am trying to use the lindenmayersystems package to draw the following pattern which resembles the cantor set: x, xox, xoxoooxox, ...

I have defined the following system:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{lindenmayersystems}
\begin{document}

\begin{figure}
    \centering
    \pgfdeclarelindenmayersystem{cantor}{
        \symbol{X}{\pgflsystemdrawforward}
        \symbol{Y}{\pgflsystemmoveforward}
        \rule{X -> XYX}
        \rule{Y -> YYY}
    }%
\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system={cantor, axiom=X, order=3}];
\end{tikzpicture}
\end{figure}
\end{document}

I would like the x's to render as a filled circle, and the o's the render as an unfilled circle. Unfortunately, I was not able to find any examples quite similar to this. Any help would be appreciated.

Perhaps using a linden mayer system is not the best idea here, and a recursive solution would be best.

5
  • 4
    Please add the few missing lines, which will turn your code snippet into sth. that we can copy&compile. Thank you
    – MS-SPO
    Apr 2 at 16:58
  • 1
    Right, sorry! I added enough code to make it a working example.
    – misogrumpy
    Apr 2 at 20:58
  • 1
    I just get 8 dashes from your MWE. For those of us who don't know what it is supposed to look like, could you tell us a bit more? Apr 2 at 21:33
  • Sure. At order 1, the system produces "X". At order 2, the system produces "XYX", and at order 3 the system produces "XYXYYYXYX". I am hoping that the X's can be rendered as filled circles, and the Y's rendered as open circles. So it should be a sequence of circles, spaced equally, where some are filled and some are not. I had it draw lines to give you the idea of where the filled circles should be. So after each move forward, I want it to render the appropriate object. I'm not entirely sure that this is possible within the lindenmayersystems package.
    – misogrumpy
    Apr 2 at 21:41
  • I should point out that I am not tied to the idea of rendering an object. What I really want is a way to differentiate the two types of positions. So perhaps moving forward, then drawing either a filled or unfilled square would suffice.
    – misogrumpy
    Apr 2 at 22:22

3 Answers 3

4

My original solution (below) was convoluted by the fact that I felt compelled to recreate the OP's early comment that, At order 1, the system produces "X". At order 2, the system produces "XYX", and at order 3 the system produces "XYXYYYXYX".

However, when it was later discussed that these results could be considered as orders 0, 1, and 2 (rather than 1, 2, and 3), everything fell much more simply into place. It simply amounts to making the \symbol macros place the desired icon via a \node command, followed by a \pgflsystemmoveforward to advance to the next location.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{lindenmayersystems}
\begin{document}
\begin{figure}
  \centering
  \pgfdeclarelindenmayersystem{cantor}{
    \symbol{X}{\node{$\bullet$};\pgflsystemmoveforward}
    \symbol{Y}{\node{$\circ$};\pgflsystemmoveforward}
    \rule{X -> XYX}
    \rule{Y -> YYY}
  }%
\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system=
      {cantor, axiom=X, order=0}];
\end{tikzpicture}

\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system=
      {cantor, axiom=X, order=1}];
\end{tikzpicture}

\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system=
      {cantor, axiom=X, order=2}];
\end{tikzpicture}

\end{figure}
\end{document}

enter image description here

ORIGINAL ANSWER (ORDERS 1, 2, and 3)

I can't say I know what I am doing here. But I noticed if I just used simple rules without counters, symbols were being duplicated more than expected. So I played around.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{lindenmayersystems}
\newcounter{myindex}
\begin{document}
\begin{figure}
  \centering
  \pgfdeclarelindenmayersystem{cantor}{
    \symbol{X}{\ifnum\themyindex<2\stepcounter{myindex}\else
      \pgflsystemmoveforward\node{$\bullet$};\setcounter{myindex}{0}\fi}
    \symbol{Y}{\ifnum\themyindex<2\stepcounter{myindex}\else
      \pgflsystemmoveforward\node{$\circ$};\setcounter{myindex}{0}\fi}
    \rule{X -> XYX}
    \rule{Y -> YYY}
  }%
\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system={cantor, axiom=X, order=1}];
\end{tikzpicture}

\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system={cantor, axiom=X, order=2}];
\end{tikzpicture}

\begin{tikzpicture}
    \draw lindenmayer system [lindenmayer system={cantor, axiom=X, order=3}];
\end{tikzpicture}
\end{figure}
\end{document}
3
  • 2
    This is wonderful! Thank you very much. I guess the key takeaway here is that you can render an image whenever a symbol is encountered by adding it to the symbol definition. I couldn't quite follow the counter logic, but I removed it, and it renders perfectly. So for instance, \symbol{X}{\node{$\bullet$};\pgflsystemmoveforward;}. This just drops a bullet whenever it encounters X and then moves forward. Similarly for Y.
    – misogrumpy
    Apr 2 at 22:39
  • 1
    @misogrumpy I can see in my early attempts, I was failing to \pgflsystemmoveforward on X, which accounted for added difficulties. However, when I do as you suggest, I get the proper result, but for the next higher order (order=1 produces an order=2 diagram). Apr 2 at 22:48
  • 2
    The single bullet is the base case for the construction anyhow, so having it be order=0 is good. Thank you again!
    – misogrumpy
    Apr 2 at 22:53
4

In OpTeX, we can use \replstring:

\def\tmpb{\X}
\def\nextlevel{\replstring\tmpb{\Y}{\Y\Y\Y}\replstring\tmpb{\X}{\X\Y\X}}
\let\X=\bullet \let\Y=\circ
\def\printstring{\centerline{$\tmpb$}}

\printstring
\nextlevel\printstring
\nextlevel\printstring
\nextlevel\printstring

\bye

Note that we needn't TikZ, only very simple macros are used.

2

Surely using the lindenmayersystems library is attracting. However, the rules are quite easy to be implemented directly.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\cantor}{m}
 {% #1 = number of iterations
  \misogrumpy_cantor:n { #1 }
 }

% choose the symbols for X and Y
\tl_const:Nn \c_misogrumpy_cantor_x_tl { \,\textbullet\, }
\tl_const:Nn \c_misogrumpy_cantor_y_tl { \,\ensuremath{\circ}\, }

% variables
\seq_new:N \l__misogrumpy_cantor_levels_seq % for the final printing
\tl_new:N \l__misogrumpy_cantor_level_tl % current level
\tl_new:N \l__misogrumpy_cantor_temp_tl % for substituting

\cs_new_protected:Nn \misogrumpy_cantor:n
 {
  \tl_set:Nn \l__misogrumpy_cantor_level_tl { X } % initialize
  \seq_clear:N \l__misogrumpy_cantor_levels_seq
  \prg_replicate:nn { #1+1 } { \__misogrumpy_cantor_next: }
  \__misogrumpy_cantor_print:
 }

\cs_new_protected:Nn \__misogrumpy_cantor_next:
 {
  % store the previous level
  \tl_set_eq:NN \l__misogrumpy_cantor_temp_tl \l__misogrumpy_cantor_level_tl
  \tl_replace_all:Nnn \l__misogrumpy_cantor_temp_tl { X } { \c_misogrumpy_cantor_x_tl }
  \tl_replace_all:Nnn \l__misogrumpy_cantor_temp_tl { Y } { \c_misogrumpy_cantor_y_tl }
  \tl_put_left:Nn \l__misogrumpy_cantor_temp_tl { \! }
  \tl_put_right:Nn \l__misogrumpy_cantor_temp_tl { \! }
  \seq_put_right:NV \l__misogrumpy_cantor_levels_seq \l__misogrumpy_cantor_temp_tl
  % compute the next level
  \tl_replace_all:Nnn \l__misogrumpy_cantor_level_tl { Y } { YYY }
  \tl_replace_all:Nnn \l__misogrumpy_cantor_level_tl { X } { XYX }
 }

\cs_new_protected:Nn \__misogrumpy_cantor_print:
 {
  \begin{tabular}{@{}c@{}}
  \seq_use:Nn \l__misogrumpy_cantor_levels_seq { \\[-1ex] }
  \end{tabular}
 }

\ExplSyntaxOff

\begin{document}

\section{Zero}
\cantor{0}

\section{One}
\cantor{1}

\section{Two}
\cantor{2}

\section{Three}
\cantor{3}

\end{document}

enter image description here

You must log in to answer this question.

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