L-System Tutorial

Starting with version 14.04.14, Lazy Nezumi Pro allows you to draw complex shapes and fractals using the Distortion feature's L-System functions.

This tutorial will teach you how to use these functions step by step.

Reference tables of all functions and instruction symbols can be found at the bottom of this page.

Definition

An L-System is defined by:

  • A set of symbols called the alphabet.
  • A string of these symbols called axiom that defines the initial state of the system.
  • A set of rules that describe how these symbols should be transformed each time the system is iterated.

The system will be iterated a number of times, with symbols being changed according to the rules. This will produce a final string of symbols, which will be read as instructions to move the pen in order to draw a shape.

Example 1 - Koch Curve

Let's start by looking at a famous L-System: The Koch Curve.

  • Alphabet: F, +, -
  • Axiom: F
  • Rules: F = F-F+F+F-F

If we start with the Axiom and use the given rule to replace F, the system evolves like so:

  • After 1 iteration:
    F-F+F+F-F
  • After 2 iterations:
    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 3 iterations:
    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+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-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

How is this used to draw something? We read the final string from left to right. Each time we encounter the F symbol, we draw a straight line. If we encounter a - or + symbol, we turn left or right 90 degrees. The following image shows the results.

Koch Curve L-System

L-System Distortion Programs

So how do we use this in Lazy Nezumi Pro? Start off by making a new preset. In its details, enable Distortion. Select the custom mode, and paste this code into the text box:

lsysAxiom("F");
lsysRules("F=F-F+F+F-F");
lsysDrawSymbols("F");
lsysAngle(90);
lsysLength(length);
lsysIterations(iterations);
lsysAdvance();

Then go ahead and click Compile. Since this program has two user variables, two parameter sliders should appear: length and iterations. Click on their "..." buttons to set their range to something more useful than [0-1].

Length (set with the lsysLength function) is the distance the pen travels each time the system encounters a Draw Symbol (in this case F, as set with the lsysDrawSymbols function). A value around 10 should be a good start.

Iterations (set with the lsysIterations function) is the number of iterations that should be performed on the system after compiling the program. Be careful with this one: if you have highly recursive rules, setting a high number of iterations will take a long time to compute the final system string (and the application might become unresponsive for a little while). Usually you won't need to go above 10 iterations, so set a range between 0 and 10.

All of the L-System functions except lsysAdvance are only used when the program is being compiled. When you are drawing, lsysAdvance steps through the final L-System string until it finds a Draw Symbol, at which point it moves the current pen position and returns. This position is stored in the ox and oy variables.

If you open the Distortion Graph window, you can see a preview of the resulting L-System as you change the parameters. Drawing an L-System in your art app may take some time, so it's a good idea to look at the preview first to get a general idea of what you will get.

Koch Curve L-System

Example 2 - Trees

L-Systems are really good at modeling trees and other plants. Let's start with this example:

lsysAxiom("[B]");
lsysRules("B=A[-B][+B]");
lsysDrawSymbols("AB");
lsysAngle(20);
lsysLength(length);
lsysDir(0, -1);
lsysIterations(iterations);
lsysAdvance();

Here we introduce two new symbols: [ and ]. When an open bracket is encountered, the state of the system is saved (or pushed) onto a stack. When a closing bracket is encountered, the state is restored by popping the top element from the stack. The system state is composed of the following:

  • Pen Position
  • Direction
  • Line Length
  • Line Thickness
  • Rotation Angle

This allows us to easily create branches. In this example, we start off with a branch, which sprouts two new branches after each iteration. Here's what this system looks like after 4 iterations:

Tree L-System

But real tree branches usually get smaller and smaller. How can we simulate this with our L-System? Here's one way:

lsysRules("B=A[-B][+B],A=AA");

Adding the A=AA rule will cause each branch to double in size for each iteration of the system. Here's what this looks like after 4 iterations:

Tree L-System

But what if we want the branches to grow at a slower rate than doubling at each iteration? You have two more options: the % symbol which will divide the length by the Length Scale Factor (which is set to 1.3 by default), and the & symbol, which will decrease the length by the Length Increase Factor (which is set to 0.1 by default). You can change these values by calling the lsysLengthScale and lsysLengthInc functions.

Here's an example rule that applies the scale factor to the left branches, and the increase factor to the right branches, so you can see the difference it makes:

lsysRules("B=A[%-B][&+B]");
Tree L-System

Now let's make this tree look a bit more natural, by adding a central branch and decreasing the thickness as we branch out. For this we use the ! symbol:

lsysRules("B=A[!%-B][!%+B]!%AB");

Here's what it looks like after 6 iterations. This is starting to look a lot better! For this to work correctly, don't forget to bind the pen's pressure to line thickness in your art app's brush settings.

Tree L-System

If you draw this, you'll notice it takes longer, since there are now 3 branches splitting off of every branch. We can make the tree look more natural and reduce some of this drawing time by randomly skipping some branches. We do this with the ? symbol. When this symbol is encountered, the instruction (or branch) after it is skipped with a certain probability. A random number between 0 and 10 is drawn, and if it is higher than the current Skip Number, then we skip the instruction. We set the Skip Number by simply writing a number somewhere in our rule string, before the ? symbol. Here's an example:

lsysRules("B=A7?[!%-B]8?[!%+B]!%AB");

What we are doing here is giving the left branch 7 chances in 10 to be drawn, and the right branch 8 chances in 10. Now that we've introduced some randomness in our L-System, every tree we draw will be slightly different! Here's an example of what we can get with this rule after 6 iterations:

Tree L-System

Removing perfect symmetry makes things look a lot more natural. Let's add even more randomness! We can have the system add some random values to the angle and length when rotating and drawing by using the following functions:

lsysAngleRandom(angleRandom);
lsysLengthRandom(lengthRandom);

This will make two new sliders appear. Set the range for angleRandom to [0-45] (since it is an angle), and you can leave the range of lengthRandom to [0-1] (since it's a percentage of the current length). Small values will go a long way to make realistic looking trees. If you set these too high though, you will get some pretty crazy results which might not look like trees at all anymore.

Here are some examples of what you can get after 6 iterations, with angleRandom set to around 5, and lengthRandom to 0.15. I've also set the Length Scale Factor to 1.6, to simulate leaves at the end of branches.

Tree L-System

Further Exploration

I hope this tutorial has sparked your interest and that you will start exploring L-Systems on your own! If you look under the L-System category in the Distortion Mode, you will find some examples to get you started. Feel free to copy/paste the code into your own Custom mode programs to tweak them and create new patterns!

You will also find a wealth of information and examples on the internet by simply doing a Google search. There is no standard way of defining L-System instruction symbols, so you may have to do some minor translations to get them working correctly in Lazy Nezumi Pro.

Distortion Functions

Here is a table of all the available L-System related functions that you can use in a Distortion program.

Function Description
lsysAxiom(string)
Set the Axiom (starting state) of the system.
lsysRules(string)
Set the Rules of the system, in the following format:
[symbol]=[symbols],[symbol]=[symbols],...
lsysIterations(number)
Set the number of times the system should be iterated before producing the final instruction string.
lsysDrawSymbols(string)
Set the symbols (one after the other, without spaces or commas) that will cause the pen to move when encountered in the final instruction string. Other symbols will only be used to control the evolution of the system as it is iterated.
lsysDir(x, y)
Set the pen's starting Direction vector. Default is (1, 0).
lsysAngle(angle)
Set the starting Angle (in degrees) that will be used when processing a rotate (+ or -) instruction. Default is 90.
lsysAngleRandom(angle)
Set the maximum angle (in degrees) that can be randomly added to the current Angle when processing a rotate instruction. Default is 0.
lsysAngleScale(factor)
Set the Angle Scale Factor. Default is 1.2.
lsysAngleInc(inc)
Set the Angle Increase Factor. Default is 5.
lsysThickness(thick)
Set the starting line Thickness, between 0.0 and 1.0. Default is 1.0.
lsysThicknessRandom(factor)
Set the percentage of current Thickness that can be randomly added when moving the pen position. Default is 0.
lsysThicknessScale(factor)
Set the Thickness Scale Factor. Default is 2.0.
lsysThicknessInc(inc)
Set the Thickness Increase Factor. Default is 0.1.
lsysLength(len)
Set the starting line Length. Default is 1.0.
lsysLengthRandom(factor)
Set the percentage of current Length that can be randomly added when moving the pen position. Default is 0.
lsysLengthScale(factor)
Set the Length Scale Factor. Default is 1.3.
lsysLengthInc(inc)
Set the Length Increase Factor. Default is 0.1.
lsysLoop(loop)
If loop is 1, then lsysAdvance will wrap around to the start of the system's instruction string upon reaching the end. Otherwise, the pen position will no longer be updated. Default is 0.
lsysAdvance()
Step through the system's instruction string until finding a Draw Symbol, at which point the pen's position is moved by the current Length in the current Direction. The new position is stored in the ox, oy variables, and the current thickness is stored in the op variable.

Symbol Instructions

The following table describes the function of each Symbol you can use when defining your L-System Axiom and Rules.

Symbol Instruction
Any Draw Symbol Move the pen position by Length amount in the current Direction.
0-9
Sets the current Skip Number (used with the ? symbol).
?
Random Skip: Draw a random number between 0 and 10. If it is higher than the current Skip Number, then skip the next instruction.
:
Skip the next instruction. You can use this to write statements like 5?A:B, where either A or B will be executed.
( )
Open and close an Instruction Group. This lets you skip an entire group of instructions by writing something like 5?(AB):(CD).
+
Rotate the current Direction left by the set Angle amount, plus a random amount of which the maximum is set by lsysAngleRandom.
-
Rotate the current Direction right by the set Angle amount, plus a random amount of which the maximum is set by lsysAngleRandom.
|
Reverse direction (rotate 180 degrees).
.
Exchange meaning of + and -.
!
Divide Thickness by Thickness Scale Factor.
@
Multiply Thickness by Thickness Scale Factor.
#
Decrease Thickness by Thickness Increase Factor.
$
Increase Thickness by Thickness Increase Factor.
%
Divide Length by Length Scale Factor.
^
Multiply Length by Length Scale Factor.
&
Decrease Length by Length Increase Factor times Start Length.
*
Increase Length by Length Increase Factor times Start Length.
{
Divide Angle by Angle Scale Factor.
}
Multiply Angle by Angle Scale Factor.
<
Decrease Angle by Angle Increase Factor.
>
Increase Angle by Angle Increase Factor.
[
Save the state and start a new Branch.
]
End the current Branch and restore the state.