noc-book-2/content/08_fractals.html
2023-05-11 20:13:15 +00:00

966 lines
No EOL
62 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

<section data-type="chapter">
<h1 id="chapter-8-fractals">Chapter 8. Fractals</h1>
<blockquote data-type="epigraph">
<p>“Pathological monsters! cried the terrified mathematician</p>
<p>Every one of them a splinter in my eye</p>
<p>I hate the Peano Space and the Koch Curve</p>
<p>I fear the Cantor Ternary Set</p>
<p>The Sierpinski Gasket makes me wanna cry</p>
<p>And a million miles away a butterfly flapped its wings</p>
<p>
On a cold November day a man named Benoit Mandelbrot was born”
<em>— Jonathan Coulton, lyrics from “Mandelbrot Set”</em>
</p>
</blockquote>
<p>Once upon a time, I took a course in high school called “Geometry.” Perhaps you did too. You learned about shapes in one dimension, two dimensions, and maybe even three. What is the circumference of a circle? The area of a rectangle? The distance between a point and a line? Come to think of it, Ive been covering geometry all along in this book, using vectors to describe the motion of bodies in Cartesian space. This sort of geometry is generally referred to as Euclidean geometry, after the Greek mathematician Euclid.</p>
<figure>
<img src="images/08_fractals/08_fractals_1.png" alt="Figure 8.1">
<figcaption>Figure 8.1</figcaption>
</figure>
<p>For us nature coders, I would suggest the question: Can our world be described with Euclidean geometry? The laptop screen Im staring at right now sure looks like a rectangle. And the plum I ate this morning is circular. But what if I were to look further, and consider the trees that line the street, the leaves that hang off those trees, the lightning from last nights thunderstorm, the cauliflower I ate for dinner, the blood vessels in my body, and the mountains and coastlines that cover land beyond Brooklyn, NY? Most of the stuff you find in nature cannot be described by the idealized geometrical forms of Euclidean geometry. So if you want to start building computational designs with patterns beyond the shapes <code>circle()</code>, <code>square()</code>, and <code>line()</code>, its time to learn about the concepts behind and techniques for simulating the geometry of nature: fractals.</p>
<h2 id="81-what-is-a-fractal">8.1 What Is a Fractal?</h2>
<p>The term <strong><em>fractal</em></strong> (from the Latin <em>fractus</em>, meaning “broken”) was coined by the mathematician Benoit Mandelbrot in 1975. In his seminal work “The Fractal Geometry of Nature,” he defines a fractal as “a rough or fragmented geometric shape that can be split into parts, each of which is (at least approximately) a reduced-size copy of the whole.”</p>
<figure>
<img src="images/08_fractals/08_fractals_2.png" alt="Figure 8.2: One of the most well-known and recognizable fractal patterns is named for Benoit Mandelbrot himself. Generating the Mandelbrot set involves testing the properties of complex numbers after they are passed through an iterative function. Do they tend to infinity? Do they stay bounded? While a fascinating mathematical discussion, this “escape-time” algorithm is a less practical method for generating fractals than the recursive techniques well examine in this chapter. However, an example for generating the Mandelbrot set is included in the code examples. ">
<figcaption>Figure 8.2: One of the most well-known and recognizable fractal patterns is named for Benoit Mandelbrot himself. Generating the Mandelbrot set involves testing the properties of complex numbers after they are passed through an iterative function. Do they tend to infinity? Do they stay bounded? While a fascinating mathematical discussion, this “escape-time” algorithm is a less practical method for generating fractals than the recursive techniques well examine in this chapter. However, an example for generating the Mandelbrot set is included in the code examples. </figcaption>
</figure>
<p>Lets illustrate this definition with two simple examples. First, lets think about a tree branching structure (for which Ill write the code later):</p>
<figure>
<img src="images/08_fractals/08_fractals_3.png" alt="Figure 8.3: A branching fractal tree">
<figcaption>Figure 8.3: A branching fractal tree</figcaption>
</figure>
<p>Notice how the tree in Figure 8.3 has a single root with branches connected at its end. Each one of those branches has branches at its end and those branches have branches and so on and so forth. What if you were to pluck one branch from the tree and examine it on its own?</p>
<figure>
<img src="images/08_fractals/08_fractals_4.png" alt="Figure 8.4: Zooming in on one branch of the fractal tree.">
<figcaption>Figure 8.4: Zooming in on one branch of the fractal tree.</figcaption>
</figure>
<p>In the illustrated tree from Figure 8.4, youll see that the zoomed in branch is an exact replica of the whole. However, fractals do not have to be perfectly self-similar. Lets take a look at an illustration of the coastline of Greenland.</p>
<figure>
<img src="images/08_fractals/08_fractals_5.png" alt="Figure 8.5: Two coastlines of Greenland">
<figcaption>Figure 8.5: Two coastlines of Greenland</figcaption>
</figure>
<p>And one more.</p>
<p>Its not an accident that I omitted the labels, however. Graphs of stock market data are examples of fractals because they look the same at any scale. Are these graphs of the stock over one year? One day? One hour? Theres no way for you to know without a label. (Incidentally, graph A shows six months worth of data and graph B zooms into a tiny part of graph A, showing six hours.)</p>
<p>The absence of a scale in the illustrations is not an accident. Coastlines, like the one of Greenland, are examples of fractals look the same at any scale. Is the illustration showing the entire coastline or just a small portion? Theres no way for you to know without a scale reference. (Incidentally, coastline A shows <strong><em>[TBD] </em></strong>and B zooms into a tiny part of coastline A, showing <strong><em>[TBD]</em></strong>.)</p>
<p></p>
<figure>
<img src="images/08_fractals/08_fractals_6.png" alt="Figure 8.6">
<figcaption>Figure 8.6</figcaption>
</figure>
<p>This is an example of a <strong><em>stochastic</em></strong> fractal, meaning that it is built out of probabilities and randomness. Unlike the deterministic (or predictable) tree-branching structure, it is statistically self-similar. As I go through the examples in this chapter, I will explore both deterministic and stochastic techniques for generating fractal patterns.</p>
<p>While self-similarity is a key trait of fractals, its important to realize that self-similarity alone does not make a fractal. After all, a straight line is self-similar. A straight line looks the same at any scale, and can be thought of as comprising lots of little lines. But its not a fractal. Fractals are characterized by having a fine structure at small scales (keep zooming into the coastline and youll continue to find fluctuations) and cannot be described with Euclidean geometry. If you can say “Its a line!” then its not a fractal.</p>
<p>The concept of fractals has a long history pre-dating Mandrelbrots 1975 book, appearing in various forms across different cultures. Indigenous and ancient societies often incorporated fractal patterns into their art, architecture, and textiles, long before the formal study of fractals in Western mathematics.</p>
<figure>
<img src="images/08_fractals/08_fractals_7.png" alt="(TBD) image of fulani wedding blanket (could be chapter opening instead?)">
<figcaption>(TBD) image of fulani wedding blanket (could be chapter opening instead?)</figcaption>
</figure>
<p>For example, the African fractal design found in the traditional Ba-ila village layouts of Zambia and the intricate geometric patterns in Islamic architecture both exhibit fractal properties. These patterns highlight the significance of fractals in diverse cultural contexts and underscore their timeless appeal.</p>
<p>Another fundamental component of fractal geometry is recursion. Fractals all have a recursive definition. Lets start by defining recursion before developing techniques and code examples for building fractal patterns in p5.js.</p>
<h2 id="82-recursion">8.2 Recursion</h2>
<p>As a starting point, let's examine recursion in the context of the first appearance of fractals in modern mathematics. Here is where youll first encounter an “exact” (or “predictable”) fractal, where each part is a precise replica of the whole. In 1883, German mathematician George Cantor developed simple rules to generate an infinite set:</p>
<figure>
<img src="images/08_fractals/08_fractals_8.png" alt="Figure 8.7: The instructions for the Cantor Set fractal">
<figcaption>Figure 8.7: The instructions for the Cantor Set fractal</figcaption>
</figure>
<p>There is a feedback loop at work here. Take a single line and break it into two. Then return to those two lines and apply the same rule, breaking each line into two, and now youre left with four. Then return to those four lines and apply the rule. Now youve got eight. This process is known as <strong><em>recursion</em></strong>: the repeated application of a rule to successive results. Cantor was interested in what happens when you apply these rules an infinite number of times. For this book, however, a p5.js sketch is limited to a finite pixel space and can so I will mostly ignore the questions and paradoxes that arise from infinite recursion. Instead, the code will be constructed in such a way that the rules are not applied “forever” (resulting in an infinite loop and a frozen computer).</p>
<p>Before I implement the Cantor set, lets take a look at what it means to have recursion in code. Heres something you are used to doing all the time—calling a function inside another function.</p>
<pre class="codesplit" data-code-language="javascript">function someFunction() {
//{!1} Calling the function background() in the definition of someFunction()
background(0);
}</pre>
<p>What would happen if you called the function you defined within the function itself? Can <code>someFunction()</code> call <code>someFunction()</code>?</p>
<pre class="codesplit" data-code-language="javascript">function someFunction() {
//{!1} Is this a paradox?
someFunction();
}</pre>
<p>In fact, this is not only allowed, but its quite encouraged! And essential to how I will implement the Cantor Set. Functions that call themselves are <em>recursive</em> and well-suited for solving certain problems. For example, some mathematical calculations are implemented recursively; the most well-known example is <em>factorial</em>.</p>
<p>The factorial of any number n, usually written as n!, is defined as:</p>
<div data-type="equation">n! = n × (n - 1) ×× 3 × 2 × 1</div>
<div data-type="equation">0! = 1</div>
<p>Here I'll write a function in p5.js that uses a <code>for</code> loop to calculate factorial:</p>
<pre class="codesplit" data-code-language="javascript">function factorial(n) {
let f = 1;
//{!3} Using a regular loop to compute factorial
for (let i = 0; i &#x3C; n; i++) {
f = f * (i + 1);
}
return f;
}</pre>
<p>Upon close examination, youll notice something interesting about how factorial works. Lets look at 4! and 3!</p>
<div data-type="equation">4! = 4 * 3 * 2 * 1</div>
<div data-type="equation">3! = 3 * 2 * 1</div>
<p><em>therefore. . .</em></p>
<div data-type="equation">4! = 4 * 3!</div>
<p>In more general terms, for any positive integer n:</p>
<div data-type="equation">n! = n * (n-1)!</div>
<div data-type="equation">1! = 1</div>
<p>Written out:</p>
<p>The <em>factorial</em> of <span data-type="equation">n</span> is defined as <span data-type="equation">n</span> times the <em>factorial</em> of <span data-type="equation">n-1</span>.</p>
<p>The definition of <strong><em>factorial</em></strong> includes <strong><em>factorial</em></strong>?! Its kind of like defining a “pizza" as “a delicious meal that includes slices of pizza.” While this definition of pizza is admittedly nonsensical, it highlights the concept of self-reference in a definition, the essence of recursion. When applied to a function definition in code, it can lead to remarkably elegant solutions. Lets look at the definition of a function <code>factorial()</code> that calls itself.</p>
<pre class="codesplit" data-code-language="javascript">function factorial(n) {
if (n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}</pre>
<p>It may look a bit odd at first, but it works. Here are the steps that happen when <code>factorial(4)</code> is called.</p>
<figure>
<img src="images/08_fractals/08_fractals_9.png" alt="Figure 8.8: Visualizing the process of recursive factorial.">
<figcaption>Figure 8.8: Visualizing the process of recursive factorial.</figcaption>
</figure>
<p>You can apply the same principle to graphics in a canvas, and this is precisely what you will see in the examples throughout this chapter. Lets start with a recursive function to draw one circle.</p>
<div data-type="example">
<h3 id="example-81-recursive-circles-i">Example 8.1: Recursive Circles I</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/yqMKM_1NS" data-example-path="examples/08_fractals/8_1_recursion"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function drawCircle(x, y, r) {
circle(x, y, r * 2);
//{!1} Exit condition, stop when radius is too small
if (r > 4) {
r *= 0.75;
//{!1} Call the function inside the function! (recursion!)
drawCircle(x, y, r);
}
}</pre>
<p><code>drawCircle()</code> draws a circle based on a set of parameters that it receives as arguments. It then calls itself with those same parameters, adjusting them slightly. The result is a series of circles, each of which is drawn inside the previous circle.</p>
<p>Notice that the above function only recursively calls itself if the radius is greater than 4. This is a crucial point. As with iteration, <em>all recursive functions must have an exit condition!</em> You are likely already aware that all <code>for</code> and <code>while</code> loops must include a boolean expression that eventually evaluates to false, thus exiting the loop. Without one, the sketch would get caught inside an infinite loop. The same can be said about recursion. If a recursive function calls itself forever and ever with no exit, youll be be treated to a chilly frozen screen.</p>
<p>This circles example is rather trivial; it could easily be achieved through simple iteration. However, for scenarios in which a function calls itself more than once, recursion becomes wonderfully elegant.</p>
<p>Lets make <code>drawCircle()</code> a bit more complex. For every circle displayed, draw a circle half its size to the left and right of that circle.</p>
<div data-type="example">
<h3 id="example-82-recursion-twice">Example 8.2: Recursion twice</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/0ENwBr7Idw" data-example-path="examples/08_fractals/8_2_recursion"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function setup() {
createCanvas(640, 240);
}
function draw() {
background(255);
drawCircle(width / 2, height / 2, 320);
}
function drawCircle(x, y, radius) {
stroke(0);
strokeWeight(2);
noFill();
circle(x, y, radius * 2);
if (radius > 4) {
//{!2} drawCircle() calls itself twice. For every circle, a smaller circle is drawn to the left and the right.
drawCircle(x + radius / 2, y, radius / 2);
drawCircle(x - radius / 2, y, radius / 2);
}
}</pre>
<p>Two more lines of code, and there are circles above and below each other circle.</p>
<div data-type="example">
<h3 id="example-83-recursion-four-times">Example 8.3: Recursion four times</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/JkT90swnM" data-example-path="examples/08_fractals/8_3_recursion_circles"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function drawCircle(x, y, radius) {
stroke(0);
strokeWeight(2);
noFill();
circle(x, y, radius * 2);
if (radius > 16) {
//{!4} drawCircle() calls itself four times.
drawCircle(x + radius / 2, y, radius / 2);
drawCircle(x - radius / 2, y, radius / 2);
drawCircle(x, y + radius / 2, radius / 2);
drawCircle(x, y - radius / 2, radius / 2);
}
}</pre>
<p>Try reproducing this sketch with iteration instead of recursion—I dare you!</p>
<h2 id="83-the-cantor-set-with-a-recursive-function">8.3 The Cantor Set with a Recursive Function</h2>
<p>Now I'm ready to visualize the Cantor set in p5.js using a recursive function. Where do I begin? Well, I know that the Cantor set begins with a line. So I will start there and write a function that draws a line.</p>
<pre class="codesplit" data-code-language="javascript">function cantor(x, y, length) {
line(x, y, x + length, y);
}</pre>
<p>The above <code>cantor()</code> function draws a line that starts at pixel coordinate <span data-type="equation">x,y</span> with a <code>length</code>. (The line is drawn horizontally, but this is an arbitrary decision.) Lets say the function is called like so:</p>
<pre class="codesplit" data-code-language="javascript">cantor(10, 20, width - 20);</pre>
<p>youd see the following:</p>
<figure class="cantor()">
<img src="images/08_fractals/08_fractals_10.png" alt="Figure 8.9: The visual result of a single call to drawing one line.">
<figcaption>Figure 8.9: The visual result of a single call to drawing one line.</figcaption>
</figure>
<figure class="half-width-right">
<img src="images/08_fractals/08_fractals_11.png" alt="Figure 8.10: Demonstrating the rules of how the next iteration of lines in the cantor set are 1/3rd the length of the previous line.">
<figcaption>Figure 8.10: Demonstrating the rules of how the next iteration of lines in the cantor set are 1/3rd the length of the previous line.</figcaption>
</figure>
<p>Now, the Cantor rule operates by duplicating the original line, erasing its middle third section, leaving two remaining lines, one from the beginning to the one-third mark, and one from the two-thirds mark to the end of the line.</p>
<p>I can now add two more lines of code to draw a pair of lines, moving the y-position down 20 pixels so that the next generation of lines appears below the first..</p>
<pre class="codesplit" data-code-language="javascript">function cantor(x, y, length) {
line(x, y, x + length, y);
y += 20;
//{.bold} From start to 1/3rd
line(x, y, x + length / 3, y);
//{!1 .bold} From 2/3rd to end
line(x + (2 * length) / 3, y, x + length, y);
}</pre>
<figure>
<img src="images/08_fractals/08_fractals_12.png" alt="Figure 8.11: Two generations of lines drawn with the Cantor set rules.">
<figcaption>Figure 8.11: Two generations of lines drawn with the Cantor set rules.</figcaption>
</figure>
<p>While this is a fine start, this manual approach of calling <code>line()</code> three times is not the path to follow. It will get unwieldy quite quickly, as I'd need four, then eight, then sixteen calls to <code>line()</code>. Yes, a <code>for</code> loop is the usual way around such a problem, but give that a try and youll see that working out the math for each iteration quickly proves inordinately complicated. Here is where recursion comes to the rescue!</p>
<p>Take a look at where I draw that first line from the start to the one-third mark.</p>
<pre class="codesplit" data-code-language="javascript"> line(x, y, x + length / 3, y);</pre>
<p>Instead of calling the <code>line()</code> function directly, why not call the <code>cantor()</code> function itself. After all, what does the <code>cantor()</code> function do? It draws a line at an <span data-type="equation">x,y</span> position with a given <code>length</code>! So this is precisely the equivalent!</p>
<pre class="codesplit" data-code-language="javascript"> cantor(x, y, length / 3);</pre>
<p>And for the second line:</p>
<pre class="codesplit" data-code-language="javascript"> cantor(x + (2 * length / 3), y, length / 3);</pre>
<p>Leaving us with:</p>
<pre class="codesplit" data-code-language="javascript">function cantor(x, y, length) {
line(x, y, x + len, y);
//{$1} Two recursive calls, note how 20 pixels are added to y
cantor(x, y + 20, length / 3);
cantor(x + (2 * length / 3), y + 20, length / 3);
}</pre>
<p>And since the <code>cantor()</code> function is called recursively, the same rule will be applied to the next lines and to the next and to the next as <code>cantor()</code> calls itself again and again! Now, dont go and run this code yet. The sketch missing that crucial element: an exit condition. You'll want to make sure to stop at some point—for example, Ill choose to stop if the length of the line ever is less than 1 pixel.</p>
<div data-type="example">
<h3 id="example-84-cantor-set">Example 8.4: Cantor set</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/4OW3OCzz6" data-example-path="examples/08_fractals/8_4_cantor_set"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function cantor(x, y, length) {
//{!1} Stop at 1 pixel!
if (length > 1) {
line(x, y, x + length, y);
cantor(x, y + 20, length / 3);
cantor(x + (2 * length) / 3, y + 20, length / 3);
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-81">Exercise 8.1</h3>
<p>Using Examples 8.3 and 8.4 as a model, design your own recursive pattern. Here is an example of one using lines.</p>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/tqFR5Oeki" data-example-path="examples/08_fractals/exercise_8_1_fractal_lines"></div>
<figcaption></figcaption>
</figure>
</div>
<h2 id="84-the-koch-curve">8.4 The Koch Curve</h2>
<p>Writing a function that recursively calls itself is one technique for generating a fractal pattern on screen. However, what if you wanted the lines in the above Cantor set to exist as individual objects that could be moved independently? The recursive function is simple and elegant, but it does not allow you to do much beyond generating the pattern itself. However, there is another way you can apply recursion in combination with an <code>Array</code> that will allow you to not only generate a fractal pattern, but keep track of all its individual parts as objects.</p>
<p>To demonstrate this technique, I'll look at another famous fractal pattern, discovered in 1904 by Swedish mathematician Helge von Koch. Here are the rules. (Note that it starts the same way as the Cantor set, with a single line that is then divided into three parts.)</p>
<figure>
<img src="images/08_fractals/08_fractals_13.png" alt="Figure 8.12">
<figcaption>Figure 8.12</figcaption>
</figure>
<p>The result looks like:</p>
<figure>
<img src="images/08_fractals/08_fractals_14.png" alt="Figure 8.13">
<figcaption>Figure 8.13</figcaption>
</figure>
<div data-type="note">
<h3 id="the-monster-curve">The “Monster” Curve</h3>
<p>The Koch curve and other fractal patterns are often called “mathematical monsters.” This is due to an odd paradox that emerges when you apply the recursive definition an infinite number of times. If the length of the original starting line is one, the first iteration of the Koch curve will yield a line of length four-thirds (each segment is one-third the length of the starting line). Do it again and you get a length of sixteen-ninths. As you iterate towards infinity, the length of the Koch curve approaches infinity. Yet it fits in the tiny finite space provided right here on this paper (or screen)!</p>
<p>Since you are working in the p5.js land of finite pixels, this theoretical paradox wont be a factor. You'll have to limit the number of times you recursively apply the Koch rules so that your program wont run out of memory or crash.</p>
</div>
<p>I could proceed in the same manner as I did with the Cantor set, and write a recursive function that iteratively applies the Koch rules over and over. Nevertheless, I am going to tackle this problem in a different manner by treating each segment of the Koch curve as an individual object. This will open up some design possibilities. For example, if each segment is an object, it could move independently from its original position and participate in a physics simulation. In addition, the visual appearance of each segment could vary as the object include properties for color, line thickness, and so on.</p>
<p>In order to accomplish this goal of treating each segment as an individual object, I must first decide what this object should be in the first place. What data should it store? What functions should it have?</p>
<p>The Koch curve is a series of connected lines, and so I will think of each segment as a “KochLine.” Each <code>KochLine</code> object has a start point (“a”) and an end point (“b”). These points are represented as <code>p5.Vector</code> objects, and the line is drawn using the <code>line()</code> function.</p>
<pre class="codesplit" data-code-language="javascript">class KochLine {
//{!2} A line between two points: a and b
constructor(a, b) {
// a and b are p5.Vector objects
this.start = a.copy();
this.end = b.copy();
}
show() {
stroke(0);
//{!1} Draw the line from a to b.
line(this.start.x, this.start.y, this.end.x, this.end.y);
}
}</pre>
<p>Now that I have the <code>KochLine</code> class, I can get started on <code>setup()</code> and <code>draw()</code>. I'll need a data structure to keep track of what will eventually become many <code>KochLine</code> objects, and a JavaScript <code>Array</code> (see Chapter 4 for a review of arrays) will do just fine.</p>
<pre class="codesplit" data-code-language="javascript">let segments = [];</pre>
<p>In <code>setup()</code>, I'll want to add the first line segment to the array, a line that stretches from 0 to the width of the sketch.</p>
<pre class="codesplit" data-code-language="javascript">function setup() {
createCanvas(640, 240);
// Left side of canvas
let start = createVector(0, 200);
// Right side of canvas
let end = createVector(width, 200);
//{!1} The first KochLine object
segments.push(new KochLine(start, end));
}</pre>
<p>Then in <code>draw()</code>, all <code>KochLine</code> objects (just one right now) can be rendered with a for of loop.</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(255);
for (let segment of segments) {
segment.show();
}
}</pre>
<p>This is my foundation.</p>
<ul>
<li><strong><em>KochLine class:</em></strong> A class to keep track of a line from point <code>start</code> to <code>end</code>.</li>
<li><strong><em>Array:</em></strong> A list of all <code>KochLine</code> objects.</li>
</ul>
<p>With the above elements, how and where should I apply Koch rules and principles of recursion?</p>
<p>Remember the Game of Life cellular automata? In that simulation, I always kept track of two generations: “current” and “next.” When I was finished calculating the next generation, “next” became “current” and I moved on to computing the new next generation. I am going to apply a similar technique here. I have an array <code>segments</code> that keeps track of the current set of line segments (at the start of the program, there is only one). I will need a second array (lets call it <code>next</code>) where I will place all the new <code>KochLine</code> objects generated from applying the Koch rules. For every single<code>KochLine</code> in the current array, four new line segments are added to <code>next</code>. When I'm done, the next <code>array</code> becomes the current <code>segments</code>.</p>
<figure>
<img src="images/08_fractals/08_fractals_15.png" alt="Figure 8.14: “next” is calculated from “current” in the transition from Generation 0 to 1 and so on.">
<figcaption>Figure 8.14: “next” is calculated from “current” in the transition from Generation 0 to 1 and so on.</figcaption>
</figure>
<p>Heres how the code looks:</p>
<pre class="codesplit" data-code-language="javascript">function generate() {
// Create the next array
let next = [];
// For every segment
for (let segment of segments) {
//{!4} Add four new lines. How do I calculate the start and end points of each?!?
next.push(new KochLine(???, ???));
next.push(new KochLine(???, ???));
next.push(new KochLine(???, ???));
next.push(new KochLine(???, ???));
}
// The next segments!
segments = next;
}</pre>
<p>By calling <code>generate()</code> over and over, Koch curve rules are recursively applied to the existing set of <code>KochLine</code> segments. Of course, the above omits the real “work” here, which is figuring out those rules. How do I break one line segment into four as described by the rules? Because the the <code>KochLine</code> object uses <code>p5.Vector</code>, this is a wonderful opportunity to practice all of the vector math from Chapter 1 along with some trigonometry from Chapter 3! Lets start by establishing how many points need to be computed for each <code>KochLine</code> object.</p>
<figure>
<img src="images/08_fractals/08_fractals_16.png" alt="Figure 8.15: One line segment between start and end (2 points) becomes 5 points (a, b, c, d, e).">
<figcaption>Figure 8.15: One line segment between start and end (2 points) becomes 5 points (a, b, c, d, e).</figcaption>
</figure>
<p>As you can see from the above figure, I need to turn the two points (<span data-type="equation">start</span>, <span data-type="equation">end</span>) points into five (<span data-type="equation">a</span>, <span data-type="equation">b</span>, <span data-type="equation">c</span>, <span data-type="equation">d</span>, <span data-type="equation">e</span>) to generate the new line segments (<span data-type="equation">a→b</span>, <span data-type="equation">b→c</span>, <span data-type="equation">c→d</span>, <span data-type="equation">d→e</span>).</p>
<pre class="codesplit" data-code-language="javascript"> next.add(new KochLine(a, b));
next.add(new KochLine(b, c));
next.add(new KochLine(c, d));
next.add(new KochLine(d, e));</pre>
<p>Where do I get these points? Why not ask the<code>KochLine</code> object to calculate them for me?</p>
<pre class="codesplit" data-code-language="javascript">function generate() {
let next = [];
for (let segment in lines) {
//{!5} A KochLine needs has five functions, each of which return a point computed according to the Koch rules.
let a = segment.kochA();
let b = segment.kochB();
let c = segment.kochC();
let d = segment.kochD();
let e = segment.kochE();
next.push(new KochLine(a, b));
next.push(new KochLine(b, c));
next.push(new KochLine(c, d));
next.push(new KochLine(d, e));
}
segments = next;
}</pre>
<p>Now I just need to write five new functions in the <code>KochLine</code> class, each one returning a <code>p5.Vector</code> according to Figure 8.15 above. Lets knock off <code>kochA()</code> and <code>kochE()</code> first, which are the easiest: just a copy of <span data-type="equation">a</span> and <span data-type="equation">b</span> (the start and end points) of the original line.</p>
<pre class="codesplit" data-code-language="javascript"> kochA() {
//{!1} Note the use of copy(). As discussed in chapter 5 it's best to avoid making copies whenever
// possible, but here a new object is needed in case the segments to move
// independently of each other.
return this.start.copy();
}
kochE() {
return this.end.copy();
}
</pre>
<p>Now I will move on to points <span data-type="equation">b</span> and <span data-type="equation">d</span>. The point<em> </em><span data-type="equation">b</span> is one-third of the way along the line segment and <span data-type="equation">d</span> is two-thirds. If I create a vector <span data-type="equation">\vec{v}</span> that points from the original <span data-type="equation">a</span> to <span data-type="equation">b</span>, I can find the new points by scaling its magnitude it to one-third for the new <span data-type="equation">b</span> and two-thirds for the new <span data-type="equation">d</span>.</p>
<figure>
<img src="images/08_fractals/08_fractals_17.png" alt="Figure 8.16">
<figcaption>Figure 8.16</figcaption>
</figure>
<pre class="codesplit" data-code-language="javascript"> kochB() {
// Vector from start to end
let b = p5.Vector.sub(this.end, this.start);
// One-third the length
b.div(3);
//{!1} Add that vector to the beginning of the line to find the new point.
b.add(this.start);
return b;
}
kochD() {
let d = p5.Vector.sub(this.end, this.start);
//{!1} Same thing here, only move two-thirds
// along the line instead of one-third.
d.mult(2 / 3);
d.add(this.start);
return d;
}</pre>
<figure class="half-width-right">
<img src="images/08_fractals/08_fractals_18.png" alt="Figure 8.17">
<figcaption>Figure 8.17</figcaption>
</figure>
<p>The last point <span data-type="equation">c</span> is the most difficult one to computer. However, if you recall that the angles of an equilateral triangle are all sixty degrees, this makes things suddenly easier. If you know how to find the new <span data-type="equation">b</span> with a vector one-third the length of the line, what if you rotate that same vector 60 degrees (or <span data-type="equation">\pi/3</span> radians) and add it to <span data-type="equation">b</span>? Youd then have arrived at <span data-type="equation">c</span>!</p>
<pre class="codesplit" data-code-language="javascript">kochC() {
//{!1} Start at the beginning.
let c = this.start.copy();
let v = p5.Vector.sub(this.end, this.start);
//{!1} Move 1/3rd of the way to point B.
v.div(3);
c.add(v);
//{!1} Rotate by -PI/3 radians (negative angle so it rotates "up").
v.rotate(-PI / 3);
//{!1} Move along that vector to point C.
c.add(v);
return c;
}</pre>
<p>Putting it all together, if you call <code>generate()</code> five times in <code>setup()</code>, youll see the following:</p>
<div data-type="example">
<h3 id="example-85-koch-curve">Example 8.5: Koch curve</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/Tbb2MVsA9" data-example-path="examples/08_fractals/8_5_koch_curve"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let segments = [];
function setup() {
createCanvas(640, 240);
let start = createVector(0, 200);
let end = createVector(width, 200);
segments.push(new KochLine(start, end));
//{!3} Apply the Koch rules five times.
for (let i = 0; i &#x3C; 5; i++) {
generate();
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-82">Exercise 8.2</h3>
<p>Draw the Koch snowflake (or some other variation of the Koch curve).</p>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/GLil3E3qK" data-example-path="examples/08_fractals/exercise_8_2_koch_snowflake"></div>
<figcaption></figcaption>
</figure>
<figure>
<img src="images/08_fractals/08_fractals_19.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<div data-type="exercise">
<h3 id="exercise-83">Exercise 8.3</h3>
<p>Try animating the Koch curve. For example, can you draw it from left to right? Can you vary the visual design of the line segments? Can you move the line segments using techniques from earlier chapters? What if each line segment were made into a spring (toxiclibs) or constraint (matter.js)?</p>
</div>
<div data-type="exercise">
<h3 id="exercise-84">Exercise 8.4</h3>
<p>Rewrite the Cantor set example using objects and an <code>array</code>.</p>
</div>
<div data-type="exercise">
<h3 id="exercise-85">Exercise 8.5</h3>
<p>Draw the Sierpiński triangle (as seen in Wolfram elementary CA) using recursion.</p>
<figure>
<img src="images/08_fractals/08_fractals_20.png" alt=" ">
<figcaption> </figcaption>
</figure>
</div>
<h2 id="85-trees">8.5 Trees</h2>
<p>The fractals presented in this chapter so far are deterministic, meaning they have no randomness and will always produce the same outcome each time they are run. While they are excellent demonstrations of classic fractals and the programming techniques behind drawing them, they appear too precise to be from nature. In this section, I will examine techniques for generating a stochastic (or non-deterministic) fractal, with a case study of a branching tree. Starting with the a deterministic version, lets look at the production rules:</p>
<figure>
<img src="images/08_fractals/08_fractals_21.png" alt="Figure 8.18: Each generation of a fractal tree, following the production rules (final tree is several generations later). ">
<figcaption>Figure 8.18: Each generation of a fractal tree, following the production rules (final tree is several generations later).</figcaption>
</figure>
<p>Again, I have a nice fractal with a recursive definition: A branch is a line with two branches connected to it.</p>
<p>The part that is a bit more difficult than the previous fractals lies in the use of the word <em>rotate</em> in the fractals rules. Each new branch must rotate relative to the previous branch, which is rotated relative to all its previous branches. Luckily for me, p5.js has a mechanism to keep track of rotations—<strong><em>transformations</em></strong>.</p>
<p>Transformations refer to a set of functions, such as <code>translate()</code>, <code>rotate()</code>, <code>scale()</code>, <code>push()</code>, and <code>pop()</code>, that allow you to change the position, orientation, and scale of shapes in your sketch. The <code>translate()</code> function moves the coordinate system, <code>rotate()</code> rotates it, and <code>push()</code> and <code>pop()</code> help save and restore the current “transformation state.”</p>
<p>If you arent familiar with these functions, you can watch <a href="https://thecodingtrain.com/tracks/transformations-in-p5">my transformations in p5.js video tutorials available at thecodingtrain.com</a>, which cover the concepts in more detail.</p>
<p>Lets begin by drawing a single branch, the trunk of the tree. Since I am going to involve the <code>rotate()</code> function, I'll need to make sure I am continuously translating along the branches while we draw the tree. And since the root starts at the bottom of the window (see above), the first step requires translating to that spot…</p>
<pre class="codesplit" data-code-language="javascript">translate(width / 2, height);</pre>
<p>…followed by drawing a line upwards (Figure 8.19):</p>
<pre class="codesplit" data-code-language="javascript">line(0, 0, 0, -100);</pre>
<p>Once I've finished the root, I need to translate to the end and rotate in order to draw the next branch (see Figure 8.19). (Eventually, I'm going to need to package up what I'm doing right now into a recursive function, but I'll sort out the steps first.)</p>
<figure>
<img src="images/08_fractals/08_fractals_22.png" alt="Figure 8.19: The process of drawing a line, translating to the end of the line, and rotating by an angle.">
<figcaption>Figure 8.19: The process of drawing a line, translating to the end of the line, and rotating by an angle.</figcaption>
</figure>
<p>Remember, when you rotate in p5.js, you are always rotating around the point of origin, so here the point of origin must always be translated to the end of the current branch as demonstrated in Figure 8.19.</p>
<pre class="codesplit" data-code-language="javascript">translate(0, -100);
//{$1} PI divided by 6 is equivalent to 30°
rotate(PI / 6);
line(0, 0, 0, -100);</pre>
<p>Now that I have a branch going to the right, I need one going to the left (see Figure 8.20). I can use <code>push()</code> to save the transformation state before rotating, letting me call <code>pop()</code> to restore that state and draw the branch to the left. Lets look at all the code together.</p>
<figure class="half-width-right">
<img src="images/08_fractals/08_fractals_23.png" alt="Figure 8.20: After “popping” back, a new branch is rotated to the left.">
<figcaption>Figure 8.20: After “popping” back, a new branch is rotated to the left.</figcaption>
</figure>
<pre class="codesplit" data-code-language="javascript">translate(width / 2, height);
//{!1} The root
line(0, 0, 0, -100);
translate(0, -100);
// Branch to the right
push();
rotate(PI / 6);
line(0, 0, 0, -100);
pop();
// Branch to the left
rotate(-PI / 6);
line(0, 0, 0, -100);</pre>
<p>If you think of each call to the function <code>line()</code> as a “branch,” you can see from the code above that I have implemented a definition of branching as a line that has two lines connected to its end. I could keep adding more and more calls to <code>line()</code> for more and more branches, but just as with the Cantor set and Koch curve, my code would become incredibly complicated and unwieldy. Instead, I can use the above logic as a foundation for writing a recursive function, replacing the direct calls to <code>line()</code> with my own function called <code>branch()</code>. Lets take a look.</p>
<pre class="codesplit" data-code-language="javascript">function branch() {
// Draw the branch itself.
line(0, 0, 0, -100);
// Translate to the end.
translate(0, -100);
push();
//{!2} Rotate to the right and branch again.
rotate(PI / 6);
branch();
pop();
push();
//{!2} Rotate to the left and branch again.
rotate(-PI / 6);
branch();
pop();
}</pre>
<p>Notice how in the above code I use <code>push()</code> and <code>pop()</code> around each subsequent call to <code>branch()</code>. This is one of those elegant code solutions that feels like magic. Each call to <code>branch()</code> takes a moment to remember the position of that particular branch. If you turn yourself into p5.js for a moment and try to follow the recursive function with pencil and paper, youll notice that you end up drawing all of the branches to the right first. At the very of the right side, <code>pop()</code> will send you back along all of the branches drawn and populate the branches to the right.</p>
<div data-type="exercise">
<h3 id="exercise-86">Exercise 8.6</h3>
<figure class="half-width-right">
<img src="images/08_fractals/08_fractals_24.png" alt="">
<figcaption></figcaption>
</figure>
<p>Follow the recursive algorithm of drawing branches and number them in the diagram in the order that p5.js would actually draw each one.</p>
</div>
<p>You may have noticed that the recursive function as written has a major issue. After all, it has no exit condition and would get stuck in infinite recursive calls to itself. Youll might also notice that in the diagram, the branches of the tree get shorter at each level. Lets look at how to shrink the length of the lines as the tree is drawn, and stop branching once the lines have become too short.</p>
<pre class="codesplit" data-code-language="javascript">//{!1} Each branch now receives its length as an argument.
function branch(len) {
line(0, 0, 0, -len);
translate(0, -len);
//{!1} Each branchs length shrinks by two-thirds.
len *= 0.67;
if (len > 2) {
push();
rotate(angle);
//{!1} Subsequent calls to branch() include the length argument.
branch(len);
pop();
push();
rotate(-angle);
branch(len);
pop();
}
}</pre>
<p>I've also included a variable for <code>angle</code>. In the finished example the angle is controlled by the <code>mouseX</code> position.</p>
<div data-type="example">
<h3 id="example-86-recursive-tree">Example 8.6: Recursive tree</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/DaINq2A85" data-example-path="examples/08_fractals/8_6_tree"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let angle;
function setup() {
createCanvas(640, 240);
}
function draw() {
background(255);
// Mapping the angle between 0 to 90° (HALF_PI) according to mouseX
angle = map(mouseX, 0, width, 0, HALF_PI);
// Start the tree from the bottom of the canvas
translate(width / 2, height);
stroke(0);
strokeWeight(2);
branch(80);
}</pre>
<div data-type="exercise">
<h3 id="exercise-87">Exercise 8.7</h3>
<p>Vary the <code>strokeWeight()</code> for each branch. Make the root thick and each subsequent branch thinner.</p>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/x_ZEtNJKb" data-example-path="examples/08_fractals/exercise_8_7_branch_thickness"></div>
<figcaption></figcaption>
</figure>
</div>
<div data-type="exercise">
<h3 id="exercise-88">Exercise 8.8</h3>
<p>The tree structure can also be generated using the <code>array</code> technique demonstrated with the Koch curve. Recreate the tree using a <code>Branch</code> object and an <code>array</code> to keep track of the branches. Hint: youll want to keep track of the branch directions and lengths using vector math instead of p5.js transformations. Once you have the tree built with an <code>array</code> of <code>Branch</code> objects, can you animate the trees growth. What about drawing leaves at the end of the branches?</p>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/mR4_GpQS-" data-example-path="examples/08_fractals/exercise_8_9_branch_objects_animation"></div>
<figcaption></figcaption>
</figure>
</div>
<p>The recursive tree fractal is a nice example of a scenario in which adding a little bit of randomness can make the tree look more natural. Take a look outside and youll notice that branch lengths and angles vary from branch to branch, not to mention the fact that branches dont all have exactly the same number of smaller branches. First, lets see what happens when the angle is random. This is a pretty easy one to do just by adding <code>random()</code>.</p>
<pre class="codesplit" data-code-language="javascript"> //{!1} Pick a random angle between 0 and PI/3 for each branch.
let angle = random(0, PI / 3);</pre>
<p>In the original example, <code>branch()</code> is always called twice. But it is also possible pick a random number of branches (each with a random angle) for each branch.</p>
<div data-type="example">
<h3 id="example-87-stochastic-tree">Example 8.7: Stochastic tree</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/xh0kdMTP4" data-example-path="examples/08_fractals/8_7_stochastic_tree"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function branch(length) {
line(0, 0, 0, -length);
translate(0, -length);
length *= 0.67;
if (length > 2) {
// A random number of branches
let n = Math.floor(random(1, 4));
for (let i = 0; i &#x3C; n; i++) {
// Picking a random angle
let angle = random(-PI / 2, PI / 2);
push();
rotate(angle);
branch(length);
pop();
}
}
}</pre>
<p>Example 8.7 demonstrates the use of randomness in angles and numbers of branches, but the resulting tree is not particularly "organic" looking. For a more natural-looking tree, you might try limiting the range of random angles, or incorporating perlin noise for more gradual angle changes.</p>
<div data-type="exercise">
<h3 id="exercise-89">Exercise 8.9</h3>
<p>Set the angles of the branches of the tree according to Perlin noise values. Adjust the noise values over time to animate the tree. See if you can get it to appear as if it is blowing in the wind.</p>
</div>
<div data-type="exercise">
<h3 id="exercise-810">Exercise 8.10</h3>
<p>Use toxiclibs.js to simulate tree physics. Each branch of the tree could be two particles connected with a spring. How can you get the tree to stand up and not fall down?</p>
</div>
<h2 id="86-l-systems">8.6 L-systems</h2>
<p>In 1968, Hungarian botanist Aristid Lindenmayer developed a grammar-based system to model the growth patterns of plants. L-systems (short for Lindenmayer systems) can be used to generate the recursive fractal patterns demonstrated so far in this chapter. L-systems are additionally useful because they provide a mechanism for keeping track of fractal structures that require complex and multi-faceted production rules.</p>
<p>In order to implement an L-system in p5.js, Ill need to know how to work with (a) recursion, (b) transformations, and (c) strings of text. This chapter already covers recursion and transformations, but strings are new. Here is a quick snippet of code demonstrating the three aspects of working with text needed, for more you can refer to the book website for additional resources and tutorials.</p>
<pre class="codesplit" data-code-language="javascript">// A string is created as text between quotes (single or double)
let message1 = "Hello!";
// Strings can be joined ("concatenated") with the plus operator, the string is now "Hello Goodbye!"
let message2 = message1 + " Goodbye!";
// The length of a string is stored in the property "length"
for (let i = 0; i &#x3C; message.length; i++) {
//{!1} Individual characters can be accessed by an index just like an array! But with charAt(i) instead of [i]
let character = message.charAt(i);
}</pre>
<p>An L-system involves three main components:</p>
<ul>
<li><strong><em>Alphabet.</em></strong> An L-systems alphabet is comprised of the valid characters that can be included. For example, I could say the alphabet is “ABC,” meaning that any valid “sentence” (a string of characters) in an L-system can only include these three characters.</li>
<li><strong><em>Axiom.</em></strong> The axiom is a sentence (created with characters from the alphabet) that describes the initial state of the system. For example, with the alphabet “ABC,” some example axioms are “AAA” or “B” or “ACBAB.”</li>
<li><strong><em>Rules.</em></strong> The “production” rules of an L-system are applied to the axiom and then recursively, generating new sentences over and over again. An L-system rule includes two sentences, a “predecessor” and a “successor.” For example, with the Rule “A —> AB” means that when an “A” occurs in a sentence, it is replaced with “AB” in the next generation.</li>
</ul>
<p>Lets begin with a simple L-system. (This is, in fact, Lindenmayers original L-system for modeling the growth of algae.)</p>
<figure class="half-width-right">
<img src="images/08_fractals/08_fractals_25.png" alt="Figure 8.21: And so on and so forth... ">
<figcaption>Figure 8.21: And so on and so forth... </figcaption>
</figure>
<div data-type="equation">Alphabet: A B</div>
<div data-type="equation">Axiom: A</div>
<div data-type="equation">Rules: (A → AB) (B → A)</div>
<p>As with the recursive fractal shapes, I can consider each successive application of the L-system rules to be a generation. Generation 0 is, by definition, the axiom.</p>
<p>Lets look at how to create the generations with code. Ill start with a string to store the axiom and in a variable. Ill name the variable <code>current</code> as it will always store the “current” generation (starting with the axiom.)</p>
<pre class="codesplit" data-code-language="javascript">let current = "A";</pre>
<p>And once again, just as with the Game of Life and the Koch curve examples, I will need an entirely separate string for the “next” generation.</p>
<pre class="codesplit" data-code-language="javascript">let next = "";</pre>
<p>Now its time to apply the production rules to <code>current</code> and write the results to <code>next</code>.</p>
<pre class="codesplit" data-code-language="javascript">for (let i = 0; i &#x3C; current.length; i++) {
let c = current.charAt(i);
//{!1} Production rule A --> AB
if (c === 'A') {
next += "AB";
//{!1} Production rule B --> A
} else if (c === 'B') {
next += "A";
}
}</pre>
<p>And when it's done, <code>current</code> is set to <code>next</code>.</p>
<pre class="codesplit" data-code-language="javascript">current = next;</pre>
<p>To be sure this is working, I will package it into a function and use a loop to call <code>generate()</code> multiple times, drawing the current string to the canvas.</p>
<div data-type="example">
<h3 id="example-88-simple-l-system-sentence-generation">Example 8.8: Simple L-system sentence generation</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/H_4SeFV3H" data-example-path="examples/08_fractals/8_8_l_system_string_only"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">//{!1} Start with an axiom.
let current = "A";
function setup() {
createCanvas(640, 160);
background(255);
noLoop();
// 9 generations
for (let i = 0; i &#x3C; 9; i++) {
generate();
// Render text to canvas
textSize(16);
textFont("courier");
text(i + ": " + current, 4, 20 + i * 16);
}
}
function generate() {
let next = "";
for (let i = 0; i &#x3C; current.length; i++) {
// For every character of the current sentence
let c = current.charAt(i);
//{!5} Apply the production rules A->AB, B->A
if (c == "A") {
next += "AB";
} else if (c == "B") {
next += "A";
}
}
// Save the next generation
current = next;
}</pre>
<p>You may find yourself wondering right about now: what exactly is the point of all this? After all, isnt this a chapter about <em>drawing</em> fractal patterns? Yes, the recursive nature of the L-system sentence structure seems relevant to the discussion, but how exactly does this model plant growth in a visual way?</p>
<p>What I've left unsaid until now is that embedded into these L-system sentences are instructions for drawing. Lets see how this works with another example.</p>
<table>
<tbody>
<tr>
<td>Alphabet</td>
<td><span data-type="equation">AB</span></td>
</tr>
<tr>
<td>Axiom</td>
<td><span data-type="equation">A</span></td>
</tr>
<tr>
<td>Rules</td>
<td><span data-type="equation">A→ABA\\B→BBB</span></td>
</tr>
</tbody>
</table>
<p>To read a sentence, I'll translate it in the following way:</p>
<table>
<tbody>
<tr>
<td><span data-type="equation">A</span></td>
<td>Draw a line forward.</td>
</tr>
<tr>
<td><span data-type="equation">B</span></td>
<td>Move forward (without drawing a line)</td>
</tr>
</tbody>
</table>
<p>Lets look at the sentence of each generation and its visual output.</p>
<table>
<tbody>
<tr>
<td>Generation 0</td>
<td><span data-type="equation">A</span></td>
</tr>
<tr>
<td>Generation 1</td>
<td><span data-type="equation">ABA</span></td>
</tr>
<tr>
<td>Generation 2</td>
<td><span data-type="equation">ABABBBABA</span></td>
</tr>
<tr>
<td>Generation 3</td>
<td><span data-type="equation">ABABBBABABBBBBBBBBABABBBABA</span></td>
</tr>
</tbody>
</table>
<p>Look familiar? This is the Cantor set generated with an L-system.</p>
<figure>
<img src="images/08_fractals/08_fractals_26.png" alt="Figure 8.22: The Cantor Set as expressed with the alphabet of an L-System">
<figcaption>Figure 8.22: The Cantor Set as expressed with the alphabet of an L-System</figcaption>
</figure>
<p>The following alphabet is often used with L-systems: “FG+-[]”, meaning:</p>
<table>
<tbody>
<tr>
<td><span data-type="equation">F</span></td>
<td>Draw a line and move forward.</td>
</tr>
<tr>
<td><span data-type="equation">G</span></td>
<td>Move forward (without drawing a line)</td>
</tr>
<tr>
<td><span data-type="equation">+</span></td>
<td>Turn right.</td>
</tr>
<tr>
<td><span data-type="equation">-</span></td>
<td>Turn left.</td>
</tr>
<tr>
<td><span data-type="equation">[</span></td>
<td>Save current state.</td>
</tr>
<tr>
<td><span data-type="equation">]</span></td>
<td>Restore current state.</td>
</tr>
</tbody>
</table>
<p>This type of drawing framework is often referred to as "Turtle graphics" (from the old days of LOGO programming). Imagine a turtle sitting on your p5.js canvas to which you could issue a small set of commands: turn left, turn right, draw a line, and so on. While p5.js isn't set up to operate this way by default, I can emulate a Turtle graphics engine fairly easily with <code>translate()</code>, <code>rotate()</code>, and <code>line()</code>.</p>
<p>Heres how I would translate the above L-system alphabet into p5.js code.</p>
<table>
<tbody>
<tr>
<td><span data-type="equation">F</span></td>
<td>
<pre class="codesplit" data-code-language="javascript">line(0, 0, 0, length);
translate(0, length);</pre>
</td>
</tr>
<tr>
<td><span data-type="equation">G</span></td>
<td>
<pre class="codesplit" data-code-language="javascript">translate(0, length);</pre>
</td>
</tr>
<tr>
<td><span data-type="equation">+</span></td>
<td>
<pre class="codesplit" data-code-language="javascript">rotate(angle);</pre>
</td>
</tr>
<tr>
<td><span data-type="equation">-</span></td>
<td>
<pre class="codesplit" data-code-language="javascript">rotate(-angle);</pre>
</td>
</tr>
<tr>
<td><span data-type="equation">[</span></td>
<td>
<pre class="codesplit" data-code-language="javascript">push();</pre>
</td>
</tr>
<tr>
<td><span data-type="equation">]</span></td>
<td>
<pre class="codesplit" data-code-language="javascript">pop();</pre>
</td>
</tr>
</tbody>
</table>
<p>Assuming I have a sentence generated from the L-system, I can iterate through the sentence character by character and call the appropriate function as outlined above.</p>
<pre class="codesplit" data-code-language="javascript">for (let i = 0; i &#x3C; sentence.length; i++) {
//{!1} Looking at each character one at a time
let c = sentence.charAt(i);
//{!14} Performing the correct task for each character.
// This could also be written with a “case” statement,
// which might be nicer to look at, but leaving it as an
// if/else if structure helps readers not familiar with case statements.
if (c == 'F') {
line(0, 0, length, 0);
translate(length, 0);
} else if (c == 'G') {
translate(length, 0);
} else if (c == '+') {
rotate(angle);
} else if (c == '-') {
rotate(-anglee);
} else if (c == '[') {
push();
} else if (c == ']') {
pop();
}
}</pre>
<p>The next example will draw a more elaborate structure with the following L-system.</p>
<table>
<tbody>
<tr>
<td>Alphabet</td>
<td><span data-type="equation">FG+-[]</span></td>
</tr>
<tr>
<td>Axiom</td>
<td><span data-type="equation">F</span></td>
</tr>
<tr>
<td>Rules</td>
<td><span data-type="equation">F → FF+[+F-F-F]-[-F+F+F]</span></td>
</tr>
</tbody>
</table>
<p>The example available for download on the books website takes all of the L-system code provided in this section and organizes it into three classes:</p>
<ul>
<li>Rule: A class that stores the predecessor and successor strings for an L-system rule.</li>
<li>LSystem: A class to iterate a new L-system generation (as demonstrated with the <code>StringBuffer</code> technique).</li>
<li>Turtle: A class to manage reading the L-system sentence and following its instructions to draw on the screen.</li>
</ul>
<p>We wont write out these classes here since they simply duplicate the code weve already worked out in this chapter. However, lets see how they are put together in the main tab.</p>
<div data-type="example">
<h3 id="example-89-l-system">Example 8.9: L-System</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/og2ycZ0Yd" data-example-path="examples/08_fractals/figure_8_24"><img src="examples/08_fractals/figure_8_24/screenshot.png"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let lsystem;
let turtle;
function setup() {
createCanvas(640, 240);
// Rules can be defined as a JavaScript Object.
let rules = {
"F": "FF+[+F-F-F]-[-F+F+F]",
};
// The L-system is created with an axiom and a ruleset.
lsystem = new LSystem("F", rules);
// Run the L-system through 4 generations
for (let i = 0; i &#x3C; 4; i++) {
lsystem.generate();
}
//{!2 .offset} The Turtle object is a length and angle.
turtle = new Turtle(4, radians(25));
}
function draw() {
background(255);
//{!1} Start at the bottom of the canvas
translate(width / 2, height);
// Ask the turtle engine to render the sentence
turtle.render(lsystem.sentence);
}</pre>
<div data-type="exercise">
<h3 id="exercise-811">Exercise 8.11</h3>
<p>Use an L-system as a set of instructions for creating objects stored in an <code>array</code>. Use trigonometry and vector math to perform the rotations instead of matrix transformations (much like I did with the Koch curve example).</p>
</div>
<div data-type="exercise">
<h3 id="exercise-812">Exercise 8.12</h3>
<p>The seminal work in L-systems and plant structures, <a href="http://algorithmicbotany.org/papers/abop/abop.pdf"><em>The Algorithmic Beauty of Plants</em></a> by Przemysław Prusinkiewicz and Aristid Lindenmayer, was published in 1990. Chapter 1 describes many sophisticated L-systems with additional drawing rules and available alphabet characters. In addition, it describes several methods for generating stochastic L-systems. Expand the L-system example to include one or more additional features described by Prusinkiewicz and Lindenmayer.</p>
</div>
<div data-type="exercise">
<h3 id="exercise-813">Exercise 8.13</h3>
<p>In this chapter, I emphasized using fractal algorithms for generating visual patterns. However, fractals can be found in other creative mediums. For example, fractal patterns are evident in Johann Sebastian Bachs Cello Suite no. 3. The structure of David Foster Wallaces novel <em>Infinite Jest</em> was inspired by fractals. Consider using the examples in this chapter to generate audio or text.</p>
</div>
<div data-type="project">
<h3 id="the-ecosystem-project-7">The Ecosystem Project</h3>
<p>Step 8 Exercise:</p>
<p>Incorporate fractals into your ecosystem. Some possibilities:</p>
<ul>
<li>Add plant-like creatures to the ecosystem environment.</li>
<li>Lets say one of your plants is similar to a tree. Can you add leaves or flowers to the end of the branches? What if the leaves can fall off the tree (depending on a wind force)? What if you add fruit that can be picked and eaten by the creatures?</li>
<li>Design a creature with a fractal pattern.</li>
<li>Use an L-system to generate instructions for how a creature should move or behave.</li>
</ul>
</div>
</section>