LString

Description

A gnomon LString is a data strucure which represents the temporal evolution of a branching structure, as generated by a Lindenmayer System The default data class implementing LString in gnomon is lStringDataLPy that relies on the L-System implementation provided by the L-Py Python package.

Default reader plugin

The default reader of lstring form is lStringReaderText which reads a lstring from a plain text conforming with the nested brackets representation and saved as a .txt file.

Gnomon LString Example

Gnomon LString illustration

Generating LString forms

LStrings are very versatile forms that are typically used to represent plant architecture, or other evolving branching structures, in the context of model simulations. The common may to generate lstrings is to define a model through the L-System Model workspace.

The lStringEvolutionModelLPy plugin allows to read a L-Py code in order to define a L-System that can be run through the Gnomon interface. The resulting trajectory of L-System states generates a time series of LString forms that is displayed in 3D using the interpretation rules of the same L-System.

A Use Case, Visualizing LString module sequence in Matplotlib

import numpy as np

import matplotlib.pyplot as plt

import gnomon.visualization
from gnomon.utils import visualizationPlugin
from gnomon.utils.decorators import lStringInput

import openalea.lpy as lpy


@visualizationPlugin(version="0.4.0", coreversion="0.81.1")
@lStringInput('lstring', "lStringDataLPy", name="LString Module Sequence")
class gnomonLStringMplVisualization(gnomon.visualization.gnomonAbstractLStringMplVisualization):
    
    def __init__(self):
        super().__init__()

        self._parameters = {}
        self.lstring = None
        self.ls = None

        self.figure = None

    def __del__(self):
        self.figure.clf()

    def setLString(self, lString):
        self.lstring = lString
        self.ls = self.lstring.data()._lstring

    def clear(self):
        if self.figure is not None:
            self.figure.clf()
            self.figure.canvas.draw()

    def setVisible(self, visible):
        pass

    def update(self):
        if self.lstring is None:
            return

        if self.figure is None:
            self.figure = plt.figure(self.figureNumber())
        self.figure.clf()

        if len(self.ls) > 0:
            modules = np.unique([n.name for n in self.ls])
            color_sequence = plt.rcParams['axes.prop_cycle'].by_key()['color']
            module_colors = {m: color_sequence[i % len(color_sequence)] for i, m in enumerate(modules)}

            square_size = int(np.ceil(np.sqrt(len(self.ls))))

            for i,n in enumerate(self.ls):
                module_x = i%square_size - square_size/2
                module_y = square_size/2 - i//square_size

                size = (500/square_size)/np.sqrt(len(n.name))

                self.figure.gca().text(module_x,module_y,n.name,ha='center',va='center',size=size,color=module_colors[n.name])

            self.figure.gca().set_xlim(-square_size/2,square_size/2)
            self.figure.gca().set_ylim(-square_size/2,square_size/2)
        else:
            print("The L-String has no modules...")

        self.figure.gca().axis('off')
        self.render()

    def render(self):
        if self.figure is not None:
            self.figure.canvas.draw()