Equations of Form

Some people like zoos or art exhibits, I like to explore abstract species of thought. Yesterday I spent the afternoon roaming around L-systems.

L-systems are little self-contained universes of alphabets, axioms, and rules. The alphabets are building blocks and may be any symbols or letters imaginable. The axiom is an initial combination of those letters and the rules change the axiom, morphing it into something else generation by generation. This simple trifecta creates an infinity of forms.

L-systems are deterministic and linear. This means given a certain starting point and a certain set of rules, they will always produce the same results. They don’t have the ability of chaotic systems to veer off in unexpected directions, although there may be ways of coaxing them to do so depending on how their results are interpreted. Part of their algorithmic DNA is recursion. A fractal reflexiveness that applies rules to the results of rules in an infinite spiral.

We can map L-systems graphically and they can produce intricate patterns with an affinity to the natural world: leaves, trees, clouds, terrain. But they don’t have to be interpreted graphically. They could be mapped to poetry, music, rhythm, dance.

Here’s the relevant Processing code that interpreted the L-system for the graphic above:

// Symbols: F+-[]
// Axiom: F
// Rules F->FF+[+F-F-F]-[-F+F+F]

var axiom = "F";
var sentence = axiom;
var rules = [];

rules.push({l:"F",r:"FF+[+F-F-F]-[-F+F+F]"});

// This is the entire l-system processor
function generate() {
	var newSentence = "";
	for (var i = 0; i < sentence.length; i++) {
		var ch = sentence.charAt(i);
		var nl = newSentence.length;
		for (var j = 0; j < rules.length; j++) {
			// look for one rune that matches this symbol, apply it and bail
  			if (ch == rules[j].l) { 
  				newSentence += rules[j].r;
  				break;
  			}
		}
		// if no rules matched, return symbol unchanged
		if (nl === newSentence.length) newSentence += ch; 
	}
	sentence = newSentence;
	console.log(sentence);
	interpret();
}


// Interpret l-system generated string using old logo method
// ...as if moving a turtle around to draw lines
var len = 60
var angle = 20

function interpret() {
	len *= 0.5;
	resetMatrix();
	translate(width/2, height);
	background(0);
	stroke(255,0,0);
	for (var i = 0; i < sentence.length; i++) {
		var ch = sentence.charAt(i);
		if (ch == "F") {
			line(0,0,0,-len);
			translate(0, -len);
		} else if (ch == "+") {
			rotate(radians(angle));
		} else if (ch == "-") {
			rotate(-radians(angle));
		} else if (ch == "[") {
			push();
		} else if (ch == "]") {
			pop();
		}
	}	
}

Inspiration for this came from Dan’s. always entertaining and educational, Coding Challenges; an even deeper dive can be found here.