toosoon-lsystem
TypeScript icon, indicating that this package has built-in type declarations

3.0.0 • Public • Published

TOOSOON L-SYSTEM

Library providing functionalities for creating and manipulating Lindenmayer systems (L-Systems) using various parameters.

Credits: lindenmayer

Installation

Yarn:

$ yarn add toosoon-lsystem

NPM:

$ npm install toosoon-lsystem

Usage

Basic

import LSystem from 'toosoon-lsystem';

const system = new LSystem({
  axiom: 'F++F++F',
  productions: { F: 'F-F++F-F' },
  iterations: 2
});

system.iterate();

console.log(system.getAxiomString());
// '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'

Custom Alphabet

Using custom alphabets allows you to use custom strings as symbols.

const ALPHABET = ['Block', 'Line'] as const;
type A = Alphabet<(typeof ALPHABET)[number]>;

new LSystem<A>({
  alphabet: [...ALPHABET],
  axiom: 'Block',
  productions: {
    Block: 'Block-Line',
    Line: 'Line++Block--Line'
  }
});

Defines

Defines are especially usefull when coupled with parametric syntax.

new LSystem({
  defines: {
    [key]: [value]
  }
});

Productions

You can set productions in two ways.

Multiple productions via constructor:

new LSystem({
  productions: {
    [symbol]: [production],
    [symbol]: [production]
  }
});

Or via the setter-methods:

// Set single production
system.setProduction([symbol], [production]);
// Set multiple productions
system.setProductions({
  [symbol]: [production],
  [symbol]: [production]
});

String-Based Productions

The most basic production consists of a single string, representing the result of a production.

// Each 'F' will be replacd with 'FF'
system.setProduction('F', 'FF');

Object-based Productions

To allow even more flexibility than String-based productions, you can choose to use a wrapper Object in the following way to allow for stochastic, context-sensitive and conditional L-Systems. This object basically wraps around a regular Array, String or Function Production, which are now defined in the successor field.

String-based successor

Equivalent to String-based productions for Object-based ones.

LSystem.setProduction('F', { successor: 'FF' });
Array-based successor

If you are reading about L-System in the classic ABOP, you may have stumbled upon parametric L-Systems. Those have optional parameters inside each symbol. To make this possible you can use Arrays of successors besides basic Strings as production results (and axioms).

LSystem.setProduction('F', {
  successor: [
    { symbol: 'F', params: [1, 2] },
    { symbol: 'F', params: [3, 4] }
  ]
});
Context-sensitive production

To add a context-sensitive check you can add before and after contexts properties.

// Replace 'F' with 'FF' only if precceded by 'FA' and followed by 'A'
system.setProduction('F', {
  successor: 'FF',
  context: { before: 'FA', after: 'A' }
});

See also the chapter on classic syntax to learn how to write more concise context sensitive productions.

Parametrical production

See the chapter on classic syntax to learn how to write concise parametrical productions.

Conditional production

You may also define a condition which has to return a boolean.

// Replace 'F' with 'FF' only if condition is `true`
myLsystem.setProduction('F', {
  successor: 'FF',
  condition: () => {
    return condition === true;
  }
});
Stochastic production

Instead of a single successor, a stochastic L-System defines an Array which includes multiple Objects with their own successor. The weight property defines the probability of each successor to be choosen. If all successors have the same weight they have an equal chance to get choosen. If one successor has a higher weight than another, it is more likely to get choosen.

LSystem.setProduction('F', {
  stochastic: [
    { successor: 'A', weight: 50 }, // 50% probability
    { successor: 'AB', weight: 25 }, // 25% probability
    { successor: 'A+B', weight: 25 } // 25% probability
  ]
});

In order to create pseudo-randomness, toosoon-utils/prng functions are used to determine stochastic outputs.

Function-based successor

Besides Strings and Arrays, you can also define functions as successors for complete flexibilty. Each successor function has also access to an info object:

  • axiom: Reference to the current axiom. Useful in combination with index.
  • index: The current index of the symbol inside the whole axiom.
  • part: The current symbol part. Not very useful for String based L-Systems. But for Array based ones, this lets you access the whole symbol object, including any custom parameters you added.
  • params: Shorthand for part.params.

A successor function returns a valid production result. If nothing or false is returned, the symbol will not replaced.

Usages examples:

// Replace 'F' with 'A' if it is at least at index 3 (4th position) inside the current axiom, otherwise return 'B'
system.setAxiom('FFFFFFF');
system.setProduction('F', {
  successor: ({ index }) => (index >= 3 ? 'A' : 'B')
});
system.iterate(); // 'FFFFFF' results in -> 'BBBAAAA'

// Replace any occurrence of 'F' with a random amount (but max. 5) of 'F'
system.setProduction('F', {
  successor: () => {
    let result = '';
    let n = Math.ceil(Math.random() * 5);
    for (let i = 0; i < n; i++) result += 'F';
    return result;
  }
});

// Replace 'F' with 'FM' on mondays and with 'FT' on tuesdays. Otherwise nothing is returned, therefore 'F' stays 'F'
system.setProduction('F', {
  successor: () => {
    const day = new Date().getDay();
    if (day === 1) return 'FM';
    if (day === 2) return 'FT';
  }
});

Apply Productions

To apply your productions onto the axiom you call iterate() on your L-System instance.

In each iteration step, all symbols of the axiom are replaced with new symbols based on your defined productions.

When you call iterate(), the resulted axiom of your L-System is returned. You can also get the resulted axiom via getAxiomString().

Commands

To visualize or post-process your L-System you can define commands functions for each symbol. These functions are similar to productions, but instead of replacing the existing axiom, commands are used to draw for example different lines for different symbols. All commands are executed by calling run().

A very common application for commands would be the creation of turtle graphics.

Classic Syntax

Context-sensitive syntax

// Instead of:
system.setProduction('F', {
  successor: 'G',
  context: { before: 'AB', after: 'C' }
});

// You can write:
system.setProduction('AB<F>C', 'G');

Parametric syntax

// Instead of:
system.setProduction('F', {
  successor: [
    { symbol: 'A', params: [1] },
    { symbol: 'B', params: [2, 1] }
  ]
});

// You can write:
system.setProduction('F', 'A(1) B(2, 1)');

// Then use parametrical productions:
system.setProduction('A(t)', 'B(t, t + 1)');
system.setProduction('B(t, d)', 'A(t * d)');

API

See full documentation here.

License

MIT License, see LICENSE for details.

Package Sidebar

Install

npm i toosoon-lsystem

Weekly Downloads

6

Version

3.0.0

License

MIT

Unpacked Size

60.6 kB

Total Files

18

Last publish

Collaborators

  • arnaudrocca