This is a JavaScript-based implementation of Lindenmayer systems.
It consists of two key pieces:
- A parse function that takes an axiom and a set of production rules, and returns a generated command string.
- A number of different renderers that take a command string and output graphics. The renderers use a class-based system that allows them to be extended with custom graphics rules. Renderers are provided for canvas and SVG (though the base renderer could be extended for other uses.)
npm i l-system-tools
The core concept of L systems is that you can take a starting string (an axiom) and iteratively replace characters in that string following a set of production rules. This outputs a longer, generated string composed of repeating patterns that mimic natural, organic structures.
Here's an example of using the parser:
import { parse } from "l-system-tools";
const commandString = parse({
// Starting string
axiom: "F",
// Object containing production rules
production_rules: {
// The character to replace
F: {
// What to replace the character with
replacement: "F[-F][+F]",
// Optionally include a decimal chance of the rule applying
chance: "1",
},
},
// How many times to iterate over the string
iterations: 2,
});
This would output the following command string:
F[-F][+F][-F[-F][+F]][+F[-F][+F]]
The renderer takes a command string and outputs graphics. There are currently three main renderers:
Renderer
— A base renderer. Does not actually output graphics. (Contains core functionality that can be extended for various graphic outputs.)SvgRenderer
— Renders a command string's output to an existing SVG elementCanvasRenderer
— Renders a command string's output to an existing canvas element.
Here's an example of using the SVG renderer:
import { parse, SvgRenderer } from "l-system-tools";
// The renderer expects to be passed an SVG element when initialized
const renderer = new SvgRenderer(document.querySelector("svg"));
renderer.render({
commandString: parse({
axiom: "F",
productions: {
F: {
replacement: "F[-F][+F]",
},
},
iterations: 2,
}),
// Optionally define a starting rotation (determines starting direction)
// Defaults to 90 (degrees)
startRotation: 90,
// Optionally define a rotation for how far to turn when a turn command is encountered
// Defaults to 30 (degrees)
turnRotation: 30,
// Optionally define a distance to move when a forward command is encountered
// Defaults to 10
distance: 10,
// Optionally define a starting poition
// Defaults to { x: 50, y: 50 }
startPosition: { x: 50, y: 50 },
});
The canvas renderer has the same syntax, but takes a canvas element instead of an SVG.
import { parse, CanvasRenderer } from "l-system-tools";
// The renderer expects to be passed an SVG element when initialized
const renderer = new CanvasRenderer(document.querySelector("canvas"));
renderer.render({
// The Canvas Renderer takes the same options as the SVG Renderer above
});
Any of the renderers can be extended to add additional functionality or styles. Here's an example of extending the SVG renderer to add a special rendering rule for the letter 'C':
import { SvgRenderer } from "../renderer.js";
export class ExperimentalSvgRenderer extends SvgRenderer {
customCommand = (command) => {
if (command === "C") {
// The SVG renderer builds a `markup` string containing the SVG markup
this.markup += `
<circle
cx="${this.position.x}"
cy="${this.position.y}"
r="${this.distance / 5}"
/>`;
}
};
}
Here's a similar example for the canvas Renderer:
import { canvasRenderer } from "../renderer.js";
export class ExperimentalCanvasRenderer extends canvasRenderer {
customCommand = (command) => {
if (command === "C") {
// Canvas renderers expose a canvas 2d context property.
this.context.arc(
this.position.x,
this.position.y,
this.distance / 5,
0,
2 * Math.PI,
false
);
}
};
}
For further information about extending renderers I recommend you review the source code and see how the SVG Renderer and Canvas Renderer are coded.
You can see an example of using this library with rollup here: https://github.com/Paul-Hebert/l-system-tools-test
You can view an interactive demo here: https://l-system-tools.netlify.app/src/demo/