Draw a triangle. Now take each line of the triangle and replace it by a line with a triangular bump on it. Repeat. Like on the animated image (public domain, based on Wikipedia), you get a Koch snowflake.

This can be modeled by a super-simple turtle-graphics program. Imagine that F means "draw forward", and "+" and "-" turn by 60 degrees, counterclockwise and clockwise, respectively. Then the initial triangle can be drawn by:

F++F++F

I.e., go forward, turn right by 120 degrees, then go forward, turn right by 120 degrees, go forward.

The triangular bump line can be drawn by:

F-F++F-F

So here's how we can generate a snowflake. We take the initial turtle program F++F++F to draw a triangle. Each F in it represents a line. So replace each F by F-F++F-F. If we keep on going, we generate the Koch snowflake.

This is a simple example of an L-system. The idea behind an L-system is that we start with a string of characters (in this case F++F++F), and then a bunch of rules how to change the characters (in this case, the one rule to replace F with F-F++F-F) in each iteration. We apply this a bunch of times and we get a rather complicated string. For instance, after two iterations in the snowflake case, we get:

F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F

After four, we get the image above. (To really get a fractal, the lines should shrink with each iteration, but that won't work in Minecraft.)

There is a lot of really good information in this free PDF book.

I wrote a very simple lsystem.py module. To implement the snowflake, start with boilerplate:

import lsystem from turtle import * t = Turtle() t.pendelay(0) t.penblock(block.GOLD_BLOCK)

Now we need to define the rules. I define the rules by a python dictionary:

rules = { 'F': 'F-F++F-F' }

Next we define the axiom, or starting point:

axiom = 'F++F++F'

Finally we need to tell the system what each of the symbols in the strings mean. For different L-systems, we will assign different meanings to them (rotations by different angles, for instance). So we need a second python dictionary specifying what is done for each symbol. If you don't specify an action for a symbol, the symbol is ignored when it is time for the turtle to draw the output (but it might be important for the generation). The meanings are given by a dictionary that specifies a function to call for each symbol. One-line functions can be specified with the lambda operator. In this case, all the functions are one-liners:

dictionary = { 'F': lambda: t.go(2), '+': lambda: t.yaw(60), '-': lambda: t.yaw(-60) }

Finally we invoke the L-system, specifying how many iterations (in this case 4):

lsystem.lsystem(axiom, rules, dictionary, 4)

There is one special trick for some L-systems. These L-systems are grid-aligned, with all the rotations being 90 degrees. The square curve (squarecurve.py) and dragon curve (dragoncurve.py) are nice examples. The trick is to call, somewhere near the beginning of your code:

t.gridalign()

This moves your turtle to an integer grid location, and aligns its heading to a grid direction. After this, the turtle will remain exactly grid aligned provided that you move it only by integer amounts (e.g., 7, not 7.1, and not even 7. or 7.0 as these are floating point in Python), and you rotate only by integer amounts that are multiples of 90 degrees (e.g., -180, or 90, but not 90.0 or 45). The dragon curve code also gives an example of a forward function that is a bit more complicated--instead of just a line, it draws a wall with an opening in it.

Actually, calling gridalign() can sometimes be a good idea even if not all of your angles are right angles. You will probably get some round-off problems in a large image, but it can still look better. See the spacefilling curve example (rendered in purple stained glass!).

L-systems don't have to be two-dimensional. You can include symbols that do yaw, pitch and roll rotations. For designing trees, a useful trick is to have stack commands: '[' to save the current drawing state to a stack and ']' to restore it. This will use the turtle module's push() and pop() methods. For instance, here's a snippet of code for drawing a simple tree (ltree.py):

t.pitch(90) rules = { 'L':'[^FL]>[^FL]>[^FL]' } axiom = 'FL' dictionary = { 'F': lambda: t.go(10), '^': lambda: t.pitch(20), '>': lambda: t.roll(120), '[': lambda: t.push(), ']': lambda: t.pop() } lsystem.lsystem(axiom,rules,dictionary,5)

Think of the L as a leaf (though this simple code doesn't actually draw the leaf--that would need to be added to the dictionary). We start with FL, which is a trunk plus a leaf. Then we replace each leaf by [^FL]>[^FL]>[^FL]. This is a set of three branches, each tilted by 20 degrees from the trunk, 120 degrees apart. The brackets ensure that after each new ^FL is drawn, we're back where we were before it. This repeats, so that the leaves on the branches are replaced by triples of branches, and so on.

A more realistic tree could have more complex code for '['. It could make the succeeding branches shorter and thinner, and change their material as we get closer to the leaves (and then restore on ']'). I include such a tree as the demo code in lsystem.py, based on rules (with some tweaks) from the Geeky Blogger.

You can also do grid-aligned 3D things. For instance, hilbert.py has a 3D Hilbert curve.

Finally, you might want to introduce some randomization into the L-system rules. So far our rules were deterministic: a single string was given that replaces a symbol, e.g., 'F': 'F-F++F-F'. But instead of a simple replacement string, one can give a Python list of pairs (p,string), where p is a probability (from 0 to 1) and string is the string to use with that probability. The probabilities for a given source symbol had better not add up to more than 1, but they can add up to less than one--in that case, there is a chance that there will be no replacement. For instance, here's a slightly randomized version of geeky.blogger's tree:

rules = {'A': [(0.55,'^f[^^f>>>>>>A]>>>[^^f>>>>>>A]>>>>>[^^f>>>>>>A]'), (0.25,'^f>>[^^f>>>>>>A]>>>[^^f>>>>>>A]')]}

This rule has a 55% chance of replacing an A with a three-branch structure, and a 25% chance of replacing it with a two-branch structure. This isn't very random--more randomness would make things even more lifelike. I attach a screenshot of a fairly sparse forest randomly generated using this rule (and random placement of trees, subject to a minimum distance).