<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>L-System &#8211; INCD021</title>
	<atom:link href="https://incd021.com/tag/l-system/feed/" rel="self" type="application/rss+xml" />
	<link>https://incd021.com</link>
	<description>Programming, thoughts, life  and art.</description>
	<lastBuildDate>Tue, 01 Apr 2025 18:40:30 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
	<item>
		<title>Implementing simple L-System in Kotlin</title>
		<link>https://incd021.com/2024/10/27/implementing-simple-l-system-in-kotlin/</link>
					<comments>https://incd021.com/2024/10/27/implementing-simple-l-system-in-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[INC $D021]]></dc:creator>
		<pubDate>Sun, 27 Oct 2024 17:58:44 +0000</pubDate>
				<category><![CDATA[Proof of concept]]></category>
		<category><![CDATA[kotlin]]></category>
		<category><![CDATA[L-System]]></category>
		<guid isPermaLink="false">https://incd021.com/?p=773</guid>

					<description><![CDATA[Intro L-systems were introduced and developed by Aristid Lindenmayer back in 1968 and are often used to produce fractals or images of flowers, as the repeating nature of some flowers....]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">Intro</h3>



<p>L-systems were introduced and developed by Aristid Lindenmayer back in 1968 and are often used to produce fractals or images of flowers, as the repeating nature of some flowers is very easy to reproduce using L-systems. L-systems consist of four parts: <strong>variables</strong>, <strong>constants</strong>, <strong>Axiom</strong>, and <strong>rules</strong>. The rules describe how the axiom, or start expression, evolves from iteration to iteration.</p>



<h3 class="wp-block-heading">Let’s look at a classical example:</h3>



<ul class="wp-block-list">
<li>Variables: <strong>L</strong></li>



<li>Constants: <strong>+</strong>, <strong>−</strong></li>



<li>Axiom: <strong>L</strong></li>



<li>Rules: <strong>L =&gt; L-L++L-L</strong></li>
</ul>



<p>How the different symbols are interpreted is entirely up to us, but in this example, we define:</p>



<ul class="wp-block-list">
<li><strong>L</strong> to mean &#8220;draw line forward&#8221;</li>



<li><strong>+</strong> to mean &#8220;turn left&#8221;</li>



<li><strong>−</strong> to mean &#8220;turn right&#8221;</li>
</ul>



<p>So the algorithm is a simple search and replace. We go through the axiom or start string L and replace it according to the rule, resulting in the following strings:</p>



<ul class="wp-block-list">
<li><em>Iteration 1</em>: L =&gt; <strong>L-L++L-L </strong></li>



<li><em>Iteration 2</em>: L-L++L-L =&gt; <strong>L-L++L-L-L-L++L-L++L-L++L-L-L-L++L-L </strong></li>



<li><em>Iteration 3</em>: L-L++L-L-L-L++L-L++L-L++L-L-L-L++L-L =&gt; <strong>L-L++L-L-L-L++L-L++L-L++L-L-L-L …</strong></li>
</ul>



<p>and we can keep going for as many iterations as we like, but as we can already see, the resulting string grows very raptly because of the “long” rule for <strong>L</strong>. If the rule for <strong>L</strong> was simpler, the resulting string of cause wouldn’t grow as fast.<br></p>



<p>Repeatedly applying the rules increases structure and complexity.</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://incd021.com/wp-content/uploads/2024/09/L-System-Iterations-1.png" alt="L-System Iterations" class="wp-image-164"/><figcaption class="wp-element-caption">Basic L-System iterations</figcaption></figure>



<p>But enough theory; If you want more information, you can read an in-depth explanation of <a href="https://en.wikipedia.org/wiki/L-system">L-systems on Wikipedia</a>.</p>



<h2 class="wp-block-heading">Implementing the L-system.</h2>



<p>We’ll be use strings for variables, constants, and axiom, a <strong>StringBuilder</strong> for building up the result, and a simple <strong>Map&lt;string, string&gt;</strong> for the rule(s), defining the string to search for and what to replace it with.</p>



<p>First we define the <strong>RuleSet</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="@Serializable
data class RuleSet(val rules: Map&lt;String, String&gt;, val axiom: String)" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">@Serializable</span></span>
<span class="line"><span style="color: #F286C4">data</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">RuleSet</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> rules: </span><span style="color: #97E1F1; font-style: italic">Map</span><span style="color: #F6F6F4">&lt;</span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">&gt;, </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> axiom: </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">)</span></span></code></pre></div>



<p>As simple as can be, simple data class containing the axiom and the Map containing the rules.<br>We need the annotation @Serializable as we will be reading the rules from a JSON file.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JSON</span><span role="button" tabindex="0" data-code="{
  &quot;rules&quot;: {
    &quot;L&quot;: &quot;L-L++L-L&quot;
  },
  &quot;axiom&quot;: &quot;L&quot;
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E2F2">&quot;</span><span style="color: #97E1F1">rules</span><span style="color: #97E2F2">&quot;</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E2F2">&quot;</span><span style="color: #97E1F1">L</span><span style="color: #97E2F2">&quot;</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">L-L++L-L</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">  },</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E2F2">&quot;</span><span style="color: #97E1F1">axiom</span><span style="color: #97E2F2">&quot;</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">L</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>This is the simple rule for creating the image displayed above.</p>



<p>The simple constructor for the <strong>LSystem</strong> class is only taking a <strong>RuleSet</strong> as parameter:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="class LSystem(private val ruleSet: RuleSet)" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">LSystem</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> ruleSet: </span><span style="color: #97E1F1; font-style: italic">RuleSet</span><span style="color: #F6F6F4">)</span></span></code></pre></div>



<p><br>The <strong>generate</strong> function takes in the number of iterations to run and calls the <strong>generateExpression</strong> that number of time. It also times how long each generation takes in nanoseconds.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="fun generate(iterations: Int) {
    expression.append(ruleSet.axiom)
    
    var startTime: Long
    var endTime: Long
    
    for (i in 0 until iterations) {
        startTime = System.nanoTime()
        expression = generateExpression(expression)
    
        endTime = System.nanoTime()
        println(&quot;Time to generate iteration ${i + 1}${endTime-startTime}ns - With a size of ${expression.length}&quot;)
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">fun</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">generate</span><span style="color: #F6F6F4">(iterations: </span><span style="color: #97E1F1; font-style: italic">Int</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    expression.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(ruleSet.axiom)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> startTime: </span><span style="color: #97E1F1; font-style: italic">Long</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> endTime: </span><span style="color: #97E1F1; font-style: italic">Long</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> (i </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4"> until iterations) {</span></span>
<span class="line"><span style="color: #F6F6F4">        startTime </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> System.</span><span style="color: #62E884">nanoTime</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        expression </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">generateExpression</span><span style="color: #F6F6F4">(expression)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">        endTime </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> System.</span><span style="color: #62E884">nanoTime</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #62E884">println</span><span style="color: #F6F6F4">(</span><span style="color: #E7EE98">&quot;Time to generate iteration </span><span style="color: #F286C4">${</span><span style="color: #E7EE98">i </span><span style="color: #F286C4">+</span><span style="color: #E7EE98"> </span><span style="color: #BF9EEE">1</span><span style="color: #F286C4">}${</span><span style="color: #E7EE98">endTime</span><span style="color: #F286C4">-</span><span style="color: #E7EE98">startTime</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">ns - With a size of </span><span style="color: #F286C4">${</span><span style="color: #E7EE98">expression.length</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p><br>The <strong>generateExpression</strong> function is the heart of the L-System. It goes through the current expression character by character and tries to match up the rules with the upcoming characters using the <strong>matchesPattern</strong> helper function. When a match is found, the resulting string from the rule is appended to the new expression. If no match is found, the current character is appended to the new expression. This approach avoids the need for rules that don&#8217;t replace anything, such as <strong>&#8220;-&#8221; =&gt; &#8220;-&#8220;</strong>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="private fun generateExpression(expression: StringBuilder) : StringBuilder {
    val newExpression = StringBuilder()
    
    var position = 0
    while (position &lt; expression.length) {
        var matched = false
    
        // Check each pattern in order
        for (pattern in ruleSet.rules) {
            if (position + pattern.key.length &lt;= expression.length &amp;&amp; // check we are not matching outside the expression
                matchesPattern(this.expression, position, pattern.key)
            ) {
                matched = true
                position += pattern.key.length // Move the position forward after matching
                newExpression.append(ruleSet.rules[pattern.key])
                break
            }
        }
    
        if (!matched) {
            newExpression.append(expression[position])
            position++ // Move to the next position if no pattern matched
        }
    }
    
    return newExpression
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">fun</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">generateExpression</span><span style="color: #F6F6F4">(expression: </span><span style="color: #97E1F1; font-style: italic">StringBuilder</span><span style="color: #F6F6F4">) : </span><span style="color: #97E1F1; font-style: italic">StringBuilder</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> newExpression </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">StringBuilder</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> position </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">while</span><span style="color: #F6F6F4"> (position </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4"> expression.length) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> matched </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #7B7F8B">// Check each pattern in order</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> (pattern </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> ruleSet.rules) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (position </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> pattern.key.length </span><span style="color: #F286C4">&lt;=</span><span style="color: #F6F6F4"> expression.length </span><span style="color: #F286C4">&amp;&amp;</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// check we are not matching outside the expression</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #62E884">matchesPattern</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE; font-style: italic">this</span><span style="color: #F6F6F4">.expression, position, pattern.key)</span></span>
<span class="line"><span style="color: #F6F6F4">            ) {</span></span>
<span class="line"><span style="color: #F6F6F4">                matched </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span></span>
<span class="line"><span style="color: #F6F6F4">                position </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> pattern.key.length </span><span style="color: #7B7F8B">// Move the position forward after matching</span></span>
<span class="line"><span style="color: #F6F6F4">                newExpression.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(ruleSet.rules[pattern.key])</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #F286C4">break</span></span>
<span class="line"><span style="color: #F6F6F4">            }</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (</span><span style="color: #F286C4">!</span><span style="color: #F6F6F4">matched) {</span></span>
<span class="line"><span style="color: #F6F6F4">            newExpression.</span><span style="color: #62E884">append</span><span style="color: #F6F6F4">(expression[position])</span></span>
<span class="line"><span style="color: #F6F6F4">            position</span><span style="color: #F286C4">++</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// Move to the next position if no pattern matched</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> newExpression</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p><br>The helper function <strong>matchesPattern</strong> goes through the characters in the pattern and tries to match them with the next characters in the input <strong>StringBuilder</strong>. This is a greedy approach, so the order of the rules matters. For instance, if we have the two rules <strong>&#8220;-&#8221; =&gt; &#8220;L&#8221;</strong> and <strong>&#8220;&#8211;&#8221; =&gt; &#8220;L&#8221;</strong> in that order, the second rule will never be applied because the first rule always matches first. Therefore, place the longer rules first.</p>



<p>We could pre-sort the rules, but for this introduction, we just have to keep the order in mind.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="  private fun matchesPattern(input: StringBuilder, position: Int, pattern: String): Boolean {
    for (index in pattern.indices) {
        if (input[position + index] != pattern[index]) {
            return false // Return false if any character doesn't match
        }
    }
    
    return true // Return true if all characters match
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">fun</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">matchesPattern</span><span style="color: #F6F6F4">(input: </span><span style="color: #97E1F1; font-style: italic">StringBuilder</span><span style="color: #F6F6F4">, position: </span><span style="color: #97E1F1; font-style: italic">Int</span><span style="color: #F6F6F4">, pattern: </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">): </span><span style="color: #97E1F1; font-style: italic">Boolean</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">for</span><span style="color: #F6F6F4"> (index </span><span style="color: #F286C4">in</span><span style="color: #F6F6F4"> pattern.indices) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (input[position </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> index] </span><span style="color: #F286C4">!=</span><span style="color: #F6F6F4"> pattern[index]) {</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">false</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// Return false if any character doesn&#39;t match</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// Return true if all characters match</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<h2 class="wp-block-heading"><br>Implementing the Visualizer</h2>



<p>So now we have a long string of characters, but we need to interpret in some way. This is where the visualizer comes in. <br>We started by defining the variables and constants <strong>&#8211;</strong>, <strong>+</strong>, and <strong>L</strong>, so now we just have to draw from the expression.</p>



<p> The <strong>Visualizer</strong> constructor takes a few parameters:</p>



<ul class="wp-block-list">
<li><strong>expression</strong>, the expression to be interpreted</li>



<li><strong>imageSize</strong>, the size for the output image</li>



<li><strong>padding</strong>, the padding around the figure to the edge of the image, in pixels</li>



<li><strong>filename</strong>, the filename for the output file.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="class Visualizer(
    private val expression: StringBuilder,
    private val imageSize: Vector2,
    private var padding: Float,
    private var filename: String
) " style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Visualizer</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> expression: </span><span style="color: #97E1F1; font-style: italic">StringBuilder</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> imageSize: </span><span style="color: #97E1F1; font-style: italic">Vector2</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> padding: </span><span style="color: #97E1F1; font-style: italic">Float</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> filename: </span><span style="color: #97E1F1; font-style: italic">String</span></span>
<span class="line"><span style="color: #F6F6F4">) </span></span></code></pre></div>



<p>Let&#8217;s start with the rules and the drawing function <strong>doTurtleAction</strong>. But for that to make sense, we&#8217;ll also have to look at the <strong>SystemState</strong> data class.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="data class SystemState(
    var position: Vector2,
    var angle: Float,
    var length: Float,
)

fun doTurtleAction(c: Char, state: SystemState, canvas: Canvas? = null, draw : Boolean = true) {
    when (c) {
        'L' -&gt; drawLine(state, canvas, aPen, draw)
        '+' -&gt; state.angle += 45
        '-' -&gt; state.angle -= 45
    }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">data</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">SystemState</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> position: </span><span style="color: #97E1F1; font-style: italic">Vector2</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> angle: </span><span style="color: #97E1F1; font-style: italic">Float</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">var</span><span style="color: #F6F6F4"> length: </span><span style="color: #97E1F1; font-style: italic">Float</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">fun</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">doTurtleAction</span><span style="color: #F6F6F4">(c: </span><span style="color: #97E1F1; font-style: italic">Char</span><span style="color: #F6F6F4">, state: </span><span style="color: #97E1F1; font-style: italic">SystemState</span><span style="color: #F6F6F4">, canvas: </span><span style="color: #97E1F1; font-style: italic">Canvas</span><span style="color: #F6F6F4">? </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, draw : </span><span style="color: #97E1F1; font-style: italic">Boolean</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">when</span><span style="color: #F6F6F4"> (c) {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #E7EE98">&#39;L&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">drawLine</span><span style="color: #F6F6F4">(state, canvas, aPen, draw)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #E7EE98">&#39;+&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> state.angle </span><span style="color: #F286C4">+=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">45</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #E7EE98">&#39;-&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> state.angle </span><span style="color: #F286C4">-=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">45</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>The <strong>SystemState</strong> class keeps track of the current position of the pen drawing the figure, the current angle in degrees, and the length. This can be extended to keep a stack of positions that can be pushed and popped so that when drawing, it can store a position, draw something, and return to that position. However, this isn&#8217;t implemented in this simple version.</p>



<p>In <strong>doTurtleAction</strong>, using Kotlin&#8217;s <em>when</em> function makes interpreting the character very straightforward. Here, we define what we want the different variables and constants to do. We use 45 degrees for <strong>+</strong> and <strong>&#8211;</strong> because this will create a nice Koch fractal, but you can set it to whatever you like. <strong>L</strong> draws a line based on the length and angle in <strong>state</strong>.</p>



<p>I added some extra steps in the <strong>Visualizer</strong> to help always output a nice image of the whole figure. That is, I go through the entire algorithm without drawing anything; I only log the minimum and maximum positions. This allows me to scale the lines to draw and set the starting position so the figure is centered and fills the given image size, excluding the padding.</p>



<p>Calculating the area of the figure with the line at size 1 is done using the <strong>calcArea</strong> function.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="private fun calcArea(): Rectangle {
    val state = SystemState(
        angle = 0f,
        length = 1f,
        position = Vector2(0f, 0f)
    ) hosted 
    
    expression.toString().forEach { c -&gt;
        doTurtleAction(c, state, null,false)
        updateAreaData(state)
    }
    
    val x = floor(minXPosition)
    val y = floor(minYPosition)
    val w = ceil(maxXPosition) - x
    val h = ceil(maxYPosition) - y
    return Rectangle(x.toInt(), y.toInt(), w.toInt(), h.toInt())
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">fun</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">calcArea</span><span style="color: #F6F6F4">(): </span><span style="color: #97E1F1; font-style: italic">Rectangle</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> state </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">SystemState</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">        angle </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0f</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        length </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1f</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        position </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Vector2</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">0f</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">0f</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    ) hosted </span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    expression.</span><span style="color: #62E884">toString</span><span style="color: #F6F6F4">().</span><span style="color: #62E884">forEach</span><span style="color: #F6F6F4"> { c </span><span style="color: #F286C4">-&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #62E884">doTurtleAction</span><span style="color: #F6F6F4">(c, state, </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">,</span><span style="color: #BF9EEE">false</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #62E884">updateAreaData</span><span style="color: #F6F6F4">(state)</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> x </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">floor</span><span style="color: #F6F6F4">(minXPosition)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> y </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">floor</span><span style="color: #F6F6F4">(minYPosition)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> w </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">ceil</span><span style="color: #F6F6F4">(maxXPosition) </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> x</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> h </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">ceil</span><span style="color: #F6F6F4">(maxYPosition) </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> y</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Rectangle</span><span style="color: #F6F6F4">(x.</span><span style="color: #62E884">toInt</span><span style="color: #F6F6F4">(), y.</span><span style="color: #62E884">toInt</span><span style="color: #F6F6F4">(), w.</span><span style="color: #62E884">toInt</span><span style="color: #F6F6F4">(), h.</span><span style="color: #62E884">toInt</span><span style="color: #F6F6F4">())</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>When outputting the image, we start by getting the area and performing some calculations to get the scale and the starting point to use.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="private fun outputAsImage(maxImageSize: Vector2, filename: String) {
    val area = calcArea()
     
    val xScale = (maxImageSize.x - padding) / area.width
    val yScale = (maxImageSize.y - padding) / area.height
    val scale = if (xScale &lt; yScale) xScale else yScale
    
    val state = SystemState(
        angle = 0f,
        length = scale,
        posi hosted tion = Vector2(minXPosition * (-scale) + (padding / 2), minYPosition * (-scale) + (padding / 2))
    )
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">fun</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">outputAsImage</span><span style="color: #F6F6F4">(maxImageSize: </span><span style="color: #97E1F1; font-style: italic">Vector2</span><span style="color: #F6F6F4">, filename: </span><span style="color: #97E1F1; font-style: italic">String</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> area </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">calcArea</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">     </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> xScale </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (maxImageSize.x </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> padding) </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> area.width</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> yScale </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> (maxImageSize.y </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4"> padding) </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> area.height</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> scale </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (xScale </span><span style="color: #F286C4">&lt;</span><span style="color: #F6F6F4"> yScale) xScale </span><span style="color: #F286C4">else</span><span style="color: #F6F6F4"> yScale</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> state </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">SystemState</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">        angle </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0f</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        length </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> scale,</span></span>
<span class="line"><span style="color: #F6F6F4">        posi hosted tion </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">Vector2</span><span style="color: #F6F6F4">(minXPosition </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> (</span><span style="color: #F286C4">-</span><span style="color: #F6F6F4">scale) </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> (padding </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">), minYPosition </span><span style="color: #F286C4">*</span><span style="color: #F6F6F4"> (</span><span style="color: #F286C4">-</span><span style="color: #F6F6F4">scale) </span><span style="color: #F286C4">+</span><span style="color: #F6F6F4"> (padding </span><span style="color: #F286C4">/</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">))</span></span>
<span class="line"><span style="color: #F6F6F4">    )</span></span>
<span class="line"></span></code></pre></div>



<p>Now we setup the canvas to draw on. Here we use the Kotlin wrapper for the <a href="https://skia.org/">Skia</a> 2d graphics library called <a href="https://github.com/JetBrains/skiko">skiko</a>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="    val surface = Surface.makeRaster(ImageInfo.makeN32(maxImageSize.x.toInt(), maxImageSize.y.toInt(), ColorAlphaType.OPAQUE))
    
    // Get the canvas from the surface
    val canvas = surface.canvas
    // Clear the canvas with a white background
    canvas.clear(Color.TRANSPARENT)" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> surface </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> Surface.</span><span style="color: #62E884">makeRaster</span><span style="color: #F6F6F4">(ImageInfo.</span><span style="color: #62E884">makeN32</span><span style="color: #F6F6F4">(maxImageSize.x.</span><span style="color: #62E884">toInt</span><span style="color: #F6F6F4">(), maxImageSize.y.</span><span style="color: #62E884">toInt</span><span style="color: #F6F6F4">(), ColorAlphaType.OPAQUE))</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Get the canvas from the surface</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> canvas </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> surface.canvas</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Clear the canvas with a white background</span></span>
<span class="line"><span style="color: #F6F6F4">    canvas.</span><span style="color: #62E884">clear</span><span style="color: #F6F6F4">(Color.TRANSPARENT)</span></span></code></pre></div>



<p><em>Note</em>: The Kotlin wrapper for Skia doesn&#8217;t use the same naming for classes and functions, which makes it challenging to use the documentation for Skia. After a lot of searching, I got it working, but without a deeper understanding of why, so I&#8217;m not sure if it&#8217;s the right way of using it. But it works.</p>



<p>Now we draw the figure and output the image to a file.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Kotlin</span><span role="button" tabindex="0" data-code="    expression.toString().forEach { c -&gt; doTurtleAction(c, state, canvas) }
    
    // Get the snapshot of the surface
    val image = surface.makeImageSnapshot()
    
    // Encode the image as a PNG
    val data = image.encodeToData(EncodedImageFormat.PNG, 100)
    
    // Save the PNG to a file
    File(&quot;$filename.png&quot;).writeBytes(data!!.bytes)
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">    expression.</span><span style="color: #62E884">toString</span><span style="color: #F6F6F4">().</span><span style="color: #62E884">forEach</span><span style="color: #F6F6F4"> { c </span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">doTurtleAction</span><span style="color: #F6F6F4">(c, state, canvas) }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Get the snapshot of the surface</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> image </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> surface.</span><span style="color: #62E884">makeImageSnapshot</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Encode the image as a PNG</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">val</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">data</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> image.</span><span style="color: #62E884">encodeToData</span><span style="color: #F6F6F4">(EncodedImageFormat.PNG, </span><span style="color: #BF9EEE">100</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Save the PNG to a file</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">File</span><span style="color: #F6F6F4">(</span><span style="color: #E7EE98">&quot;</span><span style="color: #F6F6F4">$filename</span><span style="color: #E7EE98">.png&quot;</span><span style="color: #F6F6F4">).</span><span style="color: #62E884">writeBytes</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">data!!</span><span style="color: #F6F6F4">.bytes)</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p><strong>That&#8217;s it!</strong><br>I&#8217;ve added the full project code here, for <a href="http://192.168.2.128/wp-content/uploads/2024/09/L-System-Kotlin.zip">free download</a>.</p>



<h3 class="wp-block-heading">Thoughts:</h3>



<p>However, nothing says we must use repetitive rules; we can do whatever we want. We could alternate between differenthosted rules, shift rules when a specific pattern is seen, reverse parts based on other triggers, or use any approach that could yield an interesting result. I like to add randomness to the constants, so instead of turning exactly 90 degrees, we turn 90 degrees plus or minus some small random value to introduce variation. Create different weighted rules, allowing multiple rules for the same pattern with different probabilities! &#8220;Barney Codes&#8221; has already implemented this, as seen in <a href="https://www.youtube.com/watch?v=TOPxa1xIG5Q">YouTube video</a>.</p>



<figure class="wp-block-embed is-type-wp-embed is-provider-incd-021 wp-block-embed-incd-021"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="KUvuAtgB9J"><a href="https://incd021.com/disclaimer/">Disclaimer!</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;Disclaimer!&#8221; &#8212; INCD021" src="https://incd021.com/disclaimer/embed/#?secret=PIIRGl3WlH#?secret=KUvuAtgB9J" data-secret="KUvuAtgB9J" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://incd021.com/2024/10/27/implementing-simple-l-system-in-kotlin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Page Caching using Disk: Enhanced 

Served from: incd021.com @ 2025-05-17 06:03:10 by W3 Total Cache
-->