L-System Reference

The nodeforge.lsystem package exposes L-system constructors through the package namespace:

from packages import lsystem as ls

This page is the API and grammar reference. For a worked introduction with complete examples, use the L-System Guide.

Compatibility

NodeForge L-System 4.0 requires NodeForge 0.65.0 or newer.

ls.system(part, ...)

Builds an L-system and returns Geometry.

Part Required Multiplicity Purpose
ls.axiom(...) yes one Initial module stream.
ls.iterations(...) yes one Static or runtime rewrite count.
ls.angle(...) yes one Default turtle rotation angle.
ls.step(...) yes one Default forward distance.
ls.rule(...) no many One-symbol rewrite rules.
ls.param(...) no many Named numeric values used by parameterized modules.
ls.marker(...) no many Modules that emit marker points.

All parts are positional. Their order inside ls.system(...) does not affect declaration resolution, so a rule or axiom may reference parameters and markers declared later in the call.

Duplicate singleton parts, rule predecessors, parameter names, or marker names raise CompileError.

plant = ls.system(
    ls.axiom("X"),
    ls.rule("X", "F[+X][-X]FX"),
    ls.rule("F", "FF"),
    ls.iterations(4),
    ls.angle(25),
    ls.step(0.08),
)

Constructors

ls.axiom(value)

Defines the module stream before the first rewrite iteration.

Parameter Type
value compile-time String

The string may contain turtle commands, one-character grammar symbols, declared markers, and parameterized modules.

plant = ls.system(
    ls.axiom("F[+F]F"),
    ls.iterations(0),
    ls.angle(25),
    ls.step(1),
)

ls.rule(symbol, replacement)

Defines one parallel rewrite rule.

Parameter Type Constraint
symbol compile-time String Exactly one turtle command or one ASCII letter, digit, or _.
replacement compile-time String Module stream; may be empty.

Each iteration rewrites every module from the same input generation. Symbols without a rule pass through unchanged; an empty replacement deletes its predecessor.

Rule strings may be assembled with f-strings when every interpolated value is a compile-time string.

plant = ls.system(
    ls.axiom("F"),
    ls.rule("F", "F[+F]F[-F]F"),
    ls.iterations(2),
    ls.angle(25),
    ls.step(0.2),
)

ls.iterations(value)

Sets the number of rewrite generations.

Accepted value Behavior
compile-time Int Must be non-negative; expansion happens during compilation.
runtime Int Expansion happens inside Geometry Nodes and may change topology without recompilation.

Runtime values below 0 are treated as 0.

Runtime iteration mode requires the axiom and every rule replacement to balance [ and ] independently. Rules may not rewrite [ or ] in this mode.

iterations = input_int("Iterations", default=4)
plant = ls.system(
    ls.axiom("F"),
    ls.iterations(iterations),
    ls.angle(25),
    ls.step(0.2),
)

ls.angle(...) and ls.step(...) both accept either a compile-time number or a runtime Float or Int.

ls.angle(value)

Sets the default rotation angle, in degrees, for unparameterized +, -, ^, &, /, and \ commands. An explicit argument such as +(30) or /(roll) takes precedence for that module.

angle = input_float("Angle", default=25.0)
plant = ls.system(
    ls.axiom("F+F"),
    ls.iterations(0),
    ls.angle(angle),
    ls.step(1),
)

ls.step(value)

Sets the default forward distance for unparameterized F and f. F(0.5) or F(length) supplies a per-module distance instead.

step = input_float("Step", default=0.2)
plant = ls.system(
    ls.axiom("FF"),
    ls.iterations(0),
    ls.angle(25),
    ls.step(step),
)

ls.param(name, value)

Binds a numeric value to a name used inside parameterized commands or markers.

Parameter Type Constraint
name compile-time String Identifier matching [A-Za-z_][A-Za-z0-9_]*.
value compile-time number or runtime Float or Int Numeric value exposed to the grammar.

A module argument is either a numeric literal or one declared parameter name. NodeForge expressions are not parsed inside the L-system string; compute an expression in the surrounding script and bind its result with ls.param(...).

length = input_float("Length", default=0.5)
plant = ls.system(
    ls.axiom("F(length)F"),
    ls.iterations(0),
    ls.angle(25),
    ls.step(0.2),
    ls.param("length", length),
)

ls.marker(name, *parameter_names)

Declares a marker module. Reaching that module emits a point at the current turtle position without moving or rotating the turtle.

Parameter Type Constraint
name compile-time String Identifier that is not a built-in turtle command.
parameter_names compile-time String values Unique attribute names for marker arguments.

Marker parameter names cannot use the reserved nf_lsys_ prefix.

size = input_float("Leaf Size", default=0.25)
plant = ls.system(
    ls.axiom("FLeaf(size)"),
    ls.iterations(0),
    ls.angle(25),
    ls.step(0.5),
    ls.param("size", size),
    ls.marker("Leaf", "size"),
)

Every marker point stores:

  • nf_lsys_marker_tangent — turtle Heading;
  • nf_lsys_marker_up — turtle Up;
  • one point attribute for each declared marker parameter.

ls.points(geometry, marker="Name")

Extracts points belonging to one declared marker.

Parameter Type Constraint
geometry Geometry Geometry carrying L-system marker attributes.
marker compile-time String Required keyword selecting the marker name.

Returns Geometry containing only the selected marker points. Marker identity is stored in numeric point attributes, so filtering continues to work after operations such as assignment, transform(...), and join(...) when Blender preserves those attributes.

leaf_points = ls.points(plant, marker="Leaf")

Rewrite and grammar rules

An L-system starts from the axiom, applies all matching rewrite rules in parallel for each iteration, and then sends the final module stream to the turtle interpreter.

Ordinary grammar symbols are one ASCII letter, digit, or _. They may participate in rewriting but are ignored during turtle drawing if they remain in the final stream. Declared markers may use multi-character identifiers.

Whitespace, unsupported punctuation, Unicode grammar symbols, undeclared parameter names, malformed argument lists, unmatched ], and unclosed [ raise CompileError when the invalid structure is known during compilation.

Turtle coordinate frame

The turtle starts at the world origin with this local frame:

Axis Initial direction Meaning
Heading +X Forward direction used by F and f.
Left +Y Local pitch axis.
Up +Z Local yaw axis.

Yaw, pitch, and roll update the local frame, so rotation order matters. [ stores position and all three orientation axes; ] restores the saved state.

Turtle commands

Rotation arguments are measured in degrees.

Module Meaning
F Move by ls.step(...) and draw a segment.
F(length) Move by length and draw a segment.
f Move by ls.step(...) without drawing.
f(length) Move by length without drawing.
+ Yaw left around local Up by ls.angle(...).
+(angle) Yaw left by angle.
- Yaw right around local Up by ls.angle(...).
-(angle) Yaw right by angle.
^ Pitch up around local Left by ls.angle(...).
^(angle) Pitch up by angle.
& Pitch down around local Left by ls.angle(...).
&(angle) Pitch down by angle.
/ Roll around local Heading by the positive default angle.
/(angle) Roll around local Heading by positive angle.
\ Roll around local Heading by the negative default angle.
\(angle) Roll around local Heading by negative angle.
[ Save the complete turtle state and begin a branch.
] Restore the most recently saved state.

A parameterized command accepts exactly one numeric literal or one ls.param(...) name.

Runtime values

These constructor arguments may remain runtime values:

Constructor Runtime type Effect
ls.iterations(...) Int Changes rewrite count and topology.
ls.angle(...) Float or Int Changes default rotations.
ls.step(...) Float or Int Changes default forward distance.
ls.param(...) Float or Int Changes commands or markers that reference the parameter.

The axiom, rule strings, parameter names, marker declarations, and rule set are compile-time declarations.

Limits

Limit Applies to Result when exceeded
200000 expanded modules compile-time ls.iterations(...) CompileError
branch depth 32 compile-time-expanded planar branched systems using runtime angle or step values CompileError

Runtime Int iterations are evaluated inside Geometry Nodes and are not covered by the compile-time 200000-module guard. Constrain user-facing runtime iteration inputs to practical ranges.

A compiled system must have a possible drawable segment or marker for the selected backend. A compile-time-expanded path that resolves to no drawable content raises CompileError.