Lindenmayer L-System Library

repository·main·Indexed 41 days ago

https://github.com/nylki/lindenmayer

A lightweight ES6 JavaScript library for generating L-Systems (Lindenmayer systems) version 1.5.4. It supports a concise syntax for string-based, array-based (parametric), object-based (context-sensitive, conditional, and stochastic), and function-based productions. Features include support for classic context-sensitive syntax, custom parameters for organic growth simulation, and a finals system for visualization tasks like turtle graphics.

Tokens
2.2K
Snippets
7
Records
10
Agent score
53%

What's inside Lindenmayer

  1. Define different types of productions

    main

    Lindenmayer.js supports several production formats to handle different L-System complexities:

    1. String-Based

    Simple replacement using a string. myLsystem.setProduction('F', 'FF');

    2. Array-Based (Parametric)

    Used for parametric L-Systems where symbols carry custom parameters. Each object in the array must have a symbol property.

    myLsystem.setProduction('F', [
    	{symbol: 'F', params: [5,6,1]},
    	{symbol: 'F', params: [1,0,0]}
    ]);

    3. Object-Based (Context-Sensitive, Conditional, Stochastic)

    Wrap a successor in an object to add advanced logic:

    • Context-Sensitive: Use leftCtx and rightCtx to define required neighbors.
    • Conditional: Use a condition function that returns a boolean.
    • Stochastic: Use the successors array with weight properties to define probabilities.
    // Context-sensitive
    myLsystem.setProduction('F', { successor: 'FF', leftCtx: 'FB' });
    
    // Conditional
    myLsystem.setProduction('F', { successor: 'FF', condition: () => true });
    
    // Stochastic
    myLsystem.setProduction('B', {
      successors: [
        {weight: 50, successor: 'X'},
        {weight: 50, successor: 'Y'}
      ]
    });

    4. Function-Based

    Provides maximum flexibility. The function receives an info object and returns a successor (String or Array).

    The info object contains:

    • index: Current index in the axiom.
    • part: The current symbol part (the full object if using Array-based axioms).
    • params: Shorthand for part.params.
    • currentAxiom: Reference to the current axiom.
    myLsystem.setProduction('F', ({index, part}) => {
        // Access custom parameters from 'part'
        return [{symbol: 'F', size: part.size / 2}];
    });
  2. Use finals to visualize or post-process L-Systems

    main

    Finals are functions defined for specific symbols that execute during the post-processing phase. Unlike productions, which replace symbols in the axiom/word, finals are used to perform actions (like drawing or sound generation) based on the symbols in the final string.

    To execute all defined final functions, call lsystem.final(). This is commonly used to implement turtle graphics on an HTML5 Canvas.

    var koch = new LSystem({
    	axiom: 'F++F++F',
    	productions: {'F': 'F-F++F-F'},
    	finals: {
    		'+': () => { ctx.rotate((Math.PI/180) * 60) },
    		'-': () => { ctx.rotate((Math.PI/180) * -60) },
    		'F': () => {
    			ctx.beginPath()
    			ctx.moveTo(0,0)
    			ctx.lineTo(0, 40/(koch.iterations + 1))
    			ctx.stroke()
    			ctx.translate(0, 40/(koch.iterations + 1))
    		}
    	}
    })
    
    koch.iterate(3)
    koch.final()
  3. Use Parametric L-Systems with custom parameters

    main

    You can create highly flexible L-Systems by using an Array of Objects for the axiom instead of simple strings. This allows you to inject custom parameters (e.g., food, size, angle) into your symbols.

    When defining productions, the function receives an object representing the current part. This object contains the symbol and any custom parameters you defined in the axiom. You can then use these parameters to drive conditional logic within your production rules, such as simulating organic growth or resource consumption.

    Note: When using objects in the axiom, you must explicitly define the symbol key so the engine can match it to the correct production rule.

    let parametricLsystem = new lsys.LSystem({
      axiom: [
        {symbol: 'A', food:0.5},
        {symbol: 'B'},
        {symbol: 'A', food:0.1},
        {symbol: 'C'}
      ],
      productions: {
        'A': ({part, index}) => {
          // split A into one A and a new B if it ate enough:
          if(part.food >= 1.0) {
            return [{symbol: 'A', food:0}, {symbol: 'B', food:0}]
          } else {
            // otherwise eat a random amount of food
            part.food += Math.random() * 0.1;
            return part;
          }
        }
      }
    });
    
    // parametricLsystem.iterate(60);
  4. Initialize an LSystem object

    main

    Create a new LSystem instance by passing an options object to the constructor. This allows you to define the initial state and the rules for growth immediately.

    let myLsystem = new LSystem({
    	axiom: 'F',
    	productions: {
    		'F': 'F-A',
    		'A': 'FF++FA'
    	}
    })
  5. Configure LSystem options

    main

    When initializing new LSystem(options), you can provide the following configuration keys:

    • axiom: The initial word/string (the initiator). Can be a String (e.g., 'ABC') or an Array of Objects (e.g., [{symbol: 'A'}, {symbol: 'B'}]).
    • productions: A key-value Object mapping symbols to their replacement rules. Rules can be Strings, Arrays, Objects, or Functions.
    • finals: An optional key-value Object of functions executed for each symbol in order. Useful for visualization. Each function follows the signature (info, arg) => {} where info is {index, part}.
    • allowClassicSyntax: Boolean (default: true). Enables classic context-sensitive syntax (e.g., A<B>C).
    • branchSymbols: A String tuple of symbols treated as branches (default: '[]').
    • ignoredSymbols: A String of symbols to ignore during context-sensitive checks (default: '+-&^/|\').
    • forceObjects: Boolean (default: false). If true, automatically converts Strings like 'AB' into arrays of objects [{symbol: 'A'}, {symbol: 'B'}].
  6. Use classic syntax for context sensitive productions

    main

    If allowClassicSyntax is set to true, you can use a shorthand string syntax for context-sensitive productions. Instead of using the explicit setProduction method with context objects, you can define the context directly in the symbol string using < and > delimiters.

    Example: myLsystem.setProduction('A<B>C', 'X') is equivalent to myLsystem.setProduction('B', {leftCtx: 'A', rightCtx: 'C', successor: 'X'}).

    myLsystem.setProduction('A<B>C', 'X')
  7. Set productions using setter methods

    main

    You can define or update production rules after initialization using setProduction or setProductions.

    • setProduction(symbol, production): Sets a single production rule for a specific symbol.
    • setProductions(productionsObject): Sets multiple production rules at once using an object mapping symbols to productions.
    // Set single production
    myLsystem.setProduction('F', 'FF');
    
    // Set multiple productions
    myLsystem.setProductions({
    	'F': 'FF',
    	'A': 'A+B'
    });
  8. Iterate and retrieve L-System results

    main

    To evolve the L-System, call iterate([n]). This applies the defined productions to the current axiom for n steps (default is 1).

    Retrieving the Axiom

    • iterate(n): Returns the resulting axiom after n iterations.
    • getString(): Returns the axiom as a concatenated String.
    • getRaw(): Returns the raw axiom (useful for Array-based/Parametric systems).
    • axiom: Direct access to the current axiom property.
    // Run 5 iterations
    myLsystem.iterate(5);
    
    // Get results
    let str = myLsystem.getString();
    let raw = myLsystem.getRaw();
  9. Build the minified UMD bundle via environment variable

    main

    The project can be built with a minified version of the UMD bundle by setting the minify environment variable to true. When minify is enabled, the output file is saved to dist/lindenmayer.min.js instead of dist/lindenmayer.js. The minification process uses terser and preserves the LSystem name to ensure compatibility with the UMD global variable.

    # Example command to trigger a minified build
    minify=true rollup -c rollup.umd.config.js
  10. Install Lindenmayer

    main

    You can install Lindenmayer via npm or by downloading the browser script directly.

    Using npm

    npm install --save lindenmayer

    Then import it in your Node.js script:

    var LSystem = require('lindenmayer')
    // or
    import LSystem from 'lindenmayer'

    Direct Download (Browser)

    Download the latest lindenmayer.browser.js from the releases page and include it in your HTML:

    <script src=