blog/_site/posts/lsystems.html
2018-11-14 21:32:49 +01:00

162 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dimitri Lozeve - Generating and representing L-systems</title>
<link rel="stylesheet" href="../css/default.css" />
<link rel="stylesheet" href="../css/syntax.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML" async></script>
</head>
<body>
<header>
<div class="logo">
<a href="../">Dimitri Lozeve</a>
</div>
<nav>
<a href="../">Home</a>
<a href="../projects.html">Projects</a>
<a href="../archive.html">Archive</a>
<a href="../contact.html">Contact</a>
</nav>
</header>
<main role="main">
<h1>Generating and representing L-systems</h1>
<article>
<section class="header">
Posted on January 18, 2018
by Dimitri Lozeve
</section>
<section>
<p>L-systems are a formal way to make interesting visualisations. You can use them to model a wide variety of objects: space-filling curves, fractals, biological systems, tilings, etc.</p>
<p>See the Github repo: <a href="https://github.com/dlozeve/lsystems" class="uri">https://github.com/dlozeve/lsystems</a></p>
<h1 id="what-is-an-l-system">What is an L-system?</h1>
<h2 id="a-few-examples-to-get-started">A few examples to get started</h2>
<p><img src="../images/lsystems/dragon.png" /></p>
<p><img src="../images/lsystems/gosper.png" /></p>
<p><img src="../images/lsystems/plant.png" /></p>
<p><img src="../images/lsystems/penroseP3.png" /></p>
<h2 id="definition">Definition</h2>
<p>An <a href="https://en.wikipedia.org/wiki/L-system">L-system</a> is a set of rewriting rules generating sequences of symbols. Formally, an L-system is a triplet of:</p>
<ul>
<li>an <em>alphabet</em> <span class="math inline">\(V\)</span> (an arbitrary set of symbols)</li>
<li>an <em>axiom</em> <span class="math inline">\(\omega\)</span>, which is a non-empty word of the alphabet (<span class="math inline">\(\omega \in V^+\)</span>)</li>
<li>a set of <em>rewriting rules</em> (or <em>productions</em>) <span class="math inline">\(P\)</span>, each mapping a symbol to a word: <span class="math inline">\(P \subset V \times V^*\)</span>. Symbols not present in <span class="math inline">\(P\)</span> are assumed to be mapped to themselves.</li>
</ul>
<p>During an iteration, the algorithm takes each symbol in the current word and replaces it by the value in its rewriting rule. Not that the output of the rewriting rule can be absolutely <em>anything</em> in <span class="math inline">\(V^*\)</span>, including the empty word! (So yes, you can generate symbols just to delete them afterwards.)</p>
<p>At this point, an L-system is nothing more than a way to generate very long strings of characters. In order to get something useful out of this, we have to give them <em>meaning</em>.</p>
<h2 id="drawing-instructions-and-representation">Drawing instructions and representation</h2>
<p>Our objective is to draw the output of the L-system in order to visually inspect the output. The most common way is to interpret the output as a sequence of instruction for a LOGO-like drawing turtle. For instance, a simple alphabet consisting only in the symbols <span class="math inline">\(F\)</span>, <span class="math inline">\(+\)</span>, and <span class="math inline">\(-\)</span> could represent the instructions “move forward”, “turn right by 90°”, and “turn left by 90°” respectively.</p>
<p>Thus, we add new components to our definition of L-systems:</p>
<ul>
<li>a set of <em>instructions</em>, <span class="math inline">\(I\)</span>. These are limited by the capabilities of our imagined turtle, so we can assume that they are the same for every L-system we will consider:
<ul>
<li><code>Forward</code> makes the turtle draw a straight segment.</li>
<li><code>TurnLeft</code> and <code>TurnRight</code> makes the turtle turn on itself by a given angle.</li>
<li><code>Push</code> and <code>Pop</code> allow the turtle to store and retrieve its position on a stack. This will allow for branching in the turtles path.</li>
<li><code>Stay</code>, which orders the turtle to do nothing.</li>
</ul></li>
<li>a <em>distance</em> <span class="math inline">\(d \in \mathbb{R_+}\)</span>, i.e. how long should each forward segment should be.</li>
<li>an <em>angle</em> <span class="math inline">\(\theta\)</span> used for rotation.</li>
<li>a set of <em>representation rules</em> <span class="math inline">\(R \subset V \times I\)</span>. As before, they will match a symbol to an instruction. Symbols not matched by any rule will be associated to <code>Stay</code>.</li>
</ul>
<p>Finally, our complete L-system, representable by a turtle with capabilities <span class="math inline">\(I\)</span>, can be defined as <span class="math display">\[ L = (V, \omega, P, d, \theta,
R). \]</span></p>
<p>One could argue that the representation is not part of the L-system, and that the same L-system could be represented differently by changing the representation rules. However, in our setting, we wont observe the L-system other than by displaying it, so we might as well consider that two systems differing only by their representation rules are different systems altogether.</p>
<h1 id="implementation-details">Implementation details</h1>
<h2 id="the-lsystem-data-type">The <code>LSystem</code> data type</h2>
<p>The mathematical definition above translate almost immediately in a Haskell data type:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><a class="sourceLine" id="cb1-1" title="1"><span class="co">-- | L-system data type</span></a>
<a class="sourceLine" id="cb1-2" title="2"><span class="kw">data</span> <span class="dt">LSystem</span> a <span class="fu">=</span> <span class="dt">LSystem</span></a>
<a class="sourceLine" id="cb1-3" title="3"> {<span class="ot"> name ::</span> <span class="dt">String</span></a>
<a class="sourceLine" id="cb1-4" title="4"> ,<span class="ot"> alphabet ::</span> [a] <span class="co">-- ^ variables and constants used by the system</span></a>
<a class="sourceLine" id="cb1-5" title="5"> ,<span class="ot"> axiom ::</span> [a] <span class="co">-- ^ initial state of the system</span></a>
<a class="sourceLine" id="cb1-6" title="6"> ,<span class="ot"> rules ::</span> [(a, [a])] <span class="co">-- ^ production rules defining how each</span></a>
<a class="sourceLine" id="cb1-7" title="7"> <span class="co">-- variable can be replaced by a sequence of</span></a>
<a class="sourceLine" id="cb1-8" title="8"> <span class="co">-- variables and constants</span></a>
<a class="sourceLine" id="cb1-9" title="9"> ,<span class="ot"> angle ::</span> <span class="dt">Float</span> <span class="co">-- ^ angle used for the representation</span></a>
<a class="sourceLine" id="cb1-10" title="10"> ,<span class="ot"> distance ::</span> <span class="dt">Float</span> <span class="co">-- ^ distance of each segment in the representation</span></a>
<a class="sourceLine" id="cb1-11" title="11"> ,<span class="ot"> representation ::</span> [(a, <span class="dt">Instruction</span>)] <span class="co">-- ^ representation rules</span></a>
<a class="sourceLine" id="cb1-12" title="12"> <span class="co">-- defining how each variable</span></a>
<a class="sourceLine" id="cb1-13" title="13"> <span class="co">-- and constant should be</span></a>
<a class="sourceLine" id="cb1-14" title="14"> <span class="co">-- represented</span></a>
<a class="sourceLine" id="cb1-15" title="15"> } <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>, <span class="dt">Generic</span>)</a></code></pre></div>
<p>Here, <code>a</code> is the type of the literal in the alphabet. For all practical purposes, it will almost always be <code>Char</code>.</p>
<p><code>Instruction</code> is just a sum type over all possible instructions listed above.</p>
<h2 id="iterating-and-representing">Iterating and representing</h2>
<p>From here, generating L-systems and iterating is straightforward. We iterate recursively by looking up each symbol in <code>rules</code> and replacing it by its expansion. We then transform the result to a list of <code>Instruction</code>.</p>
<h2 id="drawing">Drawing</h2>
<p>The only remaining thing is to implement the virtual turtle which will actually execute the instructions. It goes through the list of instructions, building a sequence of points and maintaining an internal state (position, angle, stack). The stack is used when <code>Push</code> and <code>Pop</code> operations are met. In this case, the turtle builds a separate line starting from its current position.</p>
<p>The final output is a set of lines, each being a simple sequence of points. All relevant data types are provided by the <a href="https://hackage.haskell.org/package/gloss">Gloss</a> library, along with the function that can display the resulting <code>Picture</code>.</p>
<h1 id="common-file-format-for-l-systems">Common file format for L-systems</h1>
<p>In order to define new L-systems quickly and easily, it is necessary to encode them in some form. We chose to represent them as JSON values.</p>
<p>Here is an example for the <a href="https://en.wikipedia.org/wiki/Gosper_curve">Gosper curve</a>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode json"><code class="sourceCode json"><a class="sourceLine" id="cb2-1" title="1"><span class="fu">{</span></a>
<a class="sourceLine" id="cb2-2" title="2"> <span class="dt">&quot;name&quot;</span><span class="fu">:</span> <span class="st">&quot;gosper&quot;</span><span class="fu">,</span></a>
<a class="sourceLine" id="cb2-3" title="3"> <span class="dt">&quot;alphabet&quot;</span><span class="fu">:</span> <span class="st">&quot;AB+-&quot;</span><span class="fu">,</span></a>
<a class="sourceLine" id="cb2-4" title="4"> <span class="dt">&quot;axiom&quot;</span><span class="fu">:</span> <span class="st">&quot;A&quot;</span><span class="fu">,</span></a>
<a class="sourceLine" id="cb2-5" title="5"> <span class="dt">&quot;rules&quot;</span><span class="fu">:</span> <span class="ot">[</span></a>
<a class="sourceLine" id="cb2-6" title="6"> <span class="ot">[</span><span class="st">&quot;A&quot;</span><span class="ot">,</span> <span class="st">&quot;A-B--B+A++AA+B-&quot;</span><span class="ot">],</span></a>
<a class="sourceLine" id="cb2-7" title="7"> <span class="ot">[</span><span class="st">&quot;B&quot;</span><span class="ot">,</span> <span class="st">&quot;+A-BB--B-A++A+B&quot;</span><span class="ot">]</span></a>
<a class="sourceLine" id="cb2-8" title="8"> <span class="ot">]</span><span class="fu">,</span></a>
<a class="sourceLine" id="cb2-9" title="9"> <span class="dt">&quot;angle&quot;</span><span class="fu">:</span> <span class="fl">60.0</span><span class="fu">,</span></a>
<a class="sourceLine" id="cb2-10" title="10"> <span class="dt">&quot;distance&quot;</span><span class="fu">:</span> <span class="fl">10.0</span><span class="fu">,</span></a>
<a class="sourceLine" id="cb2-11" title="11"> <span class="dt">&quot;representation&quot;</span><span class="fu">:</span> <span class="ot">[</span></a>
<a class="sourceLine" id="cb2-12" title="12"> <span class="ot">[</span><span class="st">&quot;A&quot;</span><span class="ot">,</span> <span class="st">&quot;Forward&quot;</span><span class="ot">],</span></a>
<a class="sourceLine" id="cb2-13" title="13"> <span class="ot">[</span><span class="st">&quot;B&quot;</span><span class="ot">,</span> <span class="st">&quot;Forward&quot;</span><span class="ot">],</span></a>
<a class="sourceLine" id="cb2-14" title="14"> <span class="ot">[</span><span class="st">&quot;+&quot;</span><span class="ot">,</span> <span class="st">&quot;TurnRight&quot;</span><span class="ot">],</span></a>
<a class="sourceLine" id="cb2-15" title="15"> <span class="ot">[</span><span class="st">&quot;-&quot;</span><span class="ot">,</span> <span class="st">&quot;TurnLeft&quot;</span><span class="ot">]</span></a>
<a class="sourceLine" id="cb2-16" title="16"> <span class="ot">]</span></a>
<a class="sourceLine" id="cb2-17" title="17"><span class="fu">}</span></a></code></pre></div>
<p>Using this format, it is easy to define new L-systems (along with how they should be represented). This is translated nearly automatically to the <code>LSystem</code> data type using <a href="https://hackage.haskell.org/package/aeson">Aeson</a>.</p>
<h1 id="variations-on-l-systems">Variations on L-systems</h1>
<p>We can widen the possibilities of L-systems in various ways. L-systems are in effect deterministic context-free grammars.</p>
<p>By allowing multiple rewriting rules for each symbol with probabilities, we can extend the model to <a href="https://en.wikipedia.org/wiki/Probabilistic_context-free_grammar">probabilistic context-free grammars</a>.</p>
<p>We can also have replacement rules not for a single symbol, but for a subsequence of them, thus effectively taking into account their neighbours (context-sensitive grammars). This seems very close to 1D cellular automata.</p>
<p>Finally, L-systems could also have a 3D representation (for instance space-filling curves in 3 dimensions).</p>
<h1 id="usage-notes">Usage notes</h1>
<ol>
<li>Clone the repository: <code>git clone [[https://github.com/dlozeve/lsystems]]</code></li>
<li>Build: <code>stack build</code></li>
<li>Execute <code>stack exec lsystems-exe -- examples/penroseP3.json</code> to see the list of options</li>
<li>(Optional) Run tests and build documentation: <code>stack test --haddock</code></li>
</ol>
<p>Usage: <code>stack exec lsystems-exe -- --help</code></p>
<pre><code>lsystems -- Generate L-systems
Usage: lsystems-exe FILENAME [-n|--iterations N] [-c|--color R,G,B]
[-w|--white-background]
Generate and draw an L-system
Available options:
FILENAME JSON file specifying an L-system
-n,--iterations N Number of iterations (default: 5)
-c,--color R,G,B Foreground color RGBA
(0-255) (default: RGBA 1.0 1.0 1.0 1.0)
-w,--white-background Use a white background
-h,--help Show this help text
</code></pre>
<p>Apart from the selection of the input JSON file, you can adjust the number of iterations and the colors.</p>
<p><code>stack exec lsystems-exe -- examples/levyC.json -n 12 -c 0,255,255</code></p>
<p><img src="../images/lsystems/levyC.png" /></p>
<h1 id="references">References</h1>
<ol>
<li>Prusinkiewicz, Przemyslaw; Lindenmayer, Aristid (1990). <em>The Algorithmic Beauty of Plants.</em> Springer-Verlag. ISBN 978-0-387-97297-8. <a href="http://algorithmicbotany.org/papers/#abop" class="uri">http://algorithmicbotany.org/papers/#abop</a></li>
<li>Weisstein, Eric W. “Lindenmayer System.” From MathWorldA Wolfram Web Resource. <a href="http://mathworld.wolfram.com/LindenmayerSystem.html" class="uri">http://mathworld.wolfram.com/LindenmayerSystem.html</a></li>
<li>Corte, Leo. “L-systems and Penrose P3 in Inkscape.” <em>The Brick in the Sky.</em> <a href="https://thebrickinthesky.wordpress.com/2013/03/17/l-systems-and-penrose-p3-in-inkscape/" class="uri">https://thebrickinthesky.wordpress.com/2013/03/17/l-systems-and-penrose-p3-in-inkscape/</a></li>
</ol>
</section>
</article>
</main>
<footer>
Site proudly generated by
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
</footer>
</body>
</html>