Vegetation3D
home features docs
screenshots download

Developer's Guide


This guide will help you to learn how to use Vegetation3D Library in your application.
This guide assumes, that you already know L-Systems basics. If not, it is recommended to read book "The algorithmic Beauty of Plants" by Przemysław Prusinkiewicz and Aristid Lindenmayer. This book may be found at http://algorithmicbotany.org/ : High quality (17MB) or Low Quality (4MB). For good understanding of what L-Systems are and how to use them it is enough to read Chapter One and Chapter Two.

Vegetation3D Library consists of three modules :

  • L-System engine
  • Turtle interpreter
  • Geometry generator
Every module is independent from others - each of them needs some data on input and produces some output. You may use them separately and produce necessery input data by yourself or use output of one module as input for another to connect them.



L-System engine

L-System engine takes definition of L-System and produces a word (sequence of symbols) in specified number of itarations of derivation process. Definition of L-System is a data structure defined by classes from vegetation3d.lsystems.core package. LSystem class represents definition of L-System containing axiom, productions etc. Instance of LSystem class may be constructed in code by defining parameters, axiom and productions using classes from package vegetation3d.lsystems.core, but to simplify process of creating L-System definitions there is LSystemIO class which translates definition of L-System described in XML file into LSystem class instance. Format of XML file containing definition of L-System is described here. To load definition from file, call loadFromFile method of LSystemIO :

File file = new File("path/to/xml/file");
LSystem lsystem = LSystemIO.loadFromFile(file);


Constructing LSystem instance in code is more complicated. As example, let's create L-System for following definition :

param a = 10.0
param b = 2.0

axiom : A(x=a)B(y=b)

A(x) -> A(x=x+1)B(y=x)
B(y) -> B(y=y+1)

Corresponding Java code :

// First, create list of constants (params)
Const a = new Const("Constant a","a",10.0); // create param a
Const b = new Const("Constant b","b",10.0); // create param b
List consts = ArrayList();
consts.add(a);
consts.add(b);

// Create axiom as two symbols : A and B
Symbol symbolA = new Symbol("A"); // create symbol A
symbolA.addValue( new SymbolValue("x","a") ); // add param x=a to symbol A

Symbol symbolB = new Symbol("B"); // create symbol B
symbolB.addValue( new SymbolValue("y","b") ); // add param y=b to symbol B

SymbolsSequence axiom = new SymbolsSequence(); // create empty axiom
axiom.addSymbol(symbolA); // add symbol A to axiom
axiom.addSymbol(symbolB); // add symbol B to axiom

// Create two productions
// First production (p1) : A(x) -> A(x=x+1)B(y=x)
Symbol p1_left = new Symbol("A"); // create symbol A as left side of p1
p1_left.addValue( new SymbolValue("x") ); // add param x to symbol A

SymbolsSequence p1_right = new SymbolsSequence(); // create empty right side of p1

Symbol p1_right_A = new Symbol("A"); // create symbol A
p1_right_A.addValue( new SymbolValue("x","x+1") ); // add param x=x+1 to symbol A

Symbol p1_right_B = new Symbol("B"); // create symbol B
p1_right_B.addValue( new SymbolValue("y","x") ); // add param y=x to symbol B

// add symbols A and B to right side of p1
p1_right.addSymbol(p1_right_A); p1_right.addSymbol(p1_right_B);
Production p1 = new Production( p1_left, p1_right ); // create p1

// Second production (p2) : B(y) -> B(y=y+1)
Symbol p2_left = new Symbol("B"); // create symbol B as left side of p2
p2_left.addValue( new SymbolValue("y") ); // add param y to symbol B

SymbolsSequence p2_right = new SymbolsSequence(); // create empty right side of p2

Symbol p2_right_B = new Symbol("B"); // create symbol B
p2_right_B.addValue( new SymbolValue("y","y+1") ); // add param y=y+1 to symbol B

p2_right.addSymbol(p1_right_B); // add symbol B to right side of p2
Production p2 = new Production( p2_left, p2_right ); // create p2

// create list of productions
List productions = new ArrayList();
productions.add(p1);
productions.add(p2);

// Finally, create LSystem instance
LSystem lsystem = new LSystem("example L-System", consts, axiom, productions);

Notice that the same result may be achieved by only few lines of code when using array arguments in constructors rather than list arguments (no need to create list objects) :

Const a = new Const("Constant a","a",10.0);
Const b = new Const("Constant b","b",10.0);

SymbolsSequence axiom = new SymbolsSequence(new Symbol[]{
new Symbol("A",new SymbolValue[]{new SymbolValue("x","a")}),
new Symbol("B",new SymbolValue[]{new SymbolValue("y","b")})
});

Production p1 = new Production(
new Symbol("A",new SymbolValue[]{new SymbolValue("x")}),
new SymbolsSequence(new Symbol[] {
new Symbol("A",new SymbolValue[]{new SymbolValue("x","x+1")}),
new Symbol("B",new SymbolValue[]{new SymbolValue("y","x")})
})
);
Production p2 = new Production(
new Symbol("B",new SymbolValue[]{new SymbolValue("y")}),
new SymbolsSequence(new Symbol[] {
new Symbol("B",new SymbolValue[]{new SymbolValue("y","y+1")})
})
);
LSystem lsystem = new LSystem("example L-System", new Const[]{a,b}, axiom, new Production[]{p1,p2});


As you can see constructing definition of L-System in code is not convenient, you may however write your own parser and describe definition of L-System in any format.

When definition of L-System is finally constructed, Derivation class is used to perform derivation process to get final sequence of symbols. There are three static methods in Derviation class :

Derivation.derivate(LSystem ls, int count, long seed) - performs count iterations of derivation starting with axiom. Since random numbers generator is used, seed parameter has to be defined to initialize it. You may use System.currentTimeMillis() to get random result. Derivation process guarantees that result is the same every time for the same seed value.

Derivation.derivate(LSystem ls, SymbolsSequence word, int count, long seed) - performs count iterations of derivation starting with given word.

Derivation.derivate(LSystem ls, SymbolsSequence word, long seed) - performs one iteration of derivation starting with given word.

Example :

LSystem ls = LSystemIO.loadFromFile(new File("path/to/lsystem.xml")); SymbolsSequence word = Derivation.derivate(ls,3,System.currentTimeMillis());




Turtle interpreter

Sequence of symbols obtained in derivation process is still only a sequence of symbols. What changes it into three dimensional shape of plant is turtle interpreter. Turtle interpreter implemented in Vegetation3D library supports following symbols :

F - Forward, creates new segment (line)
Parameters :
L - length
W - width

U - Rotation around up axis
Parameters :
a - angle in degrees

L - Rotation around left axis
Parameters :
a - angle in degrees

H - Rotation around head axis
Parameters :
a - angle in degrees

UR - Rotation around up axis according to right hand rule
Parameters :
a - angle in degrees (a>0)

UL - Rotation around up axis in opposite direction then according to right hand rule
Parameters :
a - angle in degrees (a>0)

LR - Rotation around left axis according to right hand rule
Parameters :
a - angle in degrees (a>0)

LL - Rotation around left axis in opposite direction then according to right hand rule
Parameters :
a - angle in degrees (a>0)

HR - Rotation around head axis according to right hand rule
Parameters :
a - angle in degrees (a>0)

HL - Rotation around head axis in opposite direction then according to right hand rule
Parameters :
a - angle in degrees (a>0)

[ - Puts current state of turtle into stack

] - Takes state of turtle from stack and sets it as current


Interpretation is done by TopologyGenerator class :


SymbolsSequence word = ...;
TopologyGenerator generator = new TopologyGenerator();
PlantTopology topology = generator.generateTopology(word);


PlantTopology class is wrapper for tree of Segment objects describing structure of plant. Every Segment object corresponds to single F symbol from L-System. Using [ and ] symbols causes 'line' drawn by turtle to create branches, so final structure is tree. Every segment (except root) has one parent and zero or more children. Root segment has no parent and one or more children. Every segment has point of begining, unit vector of direction and length. Some additional parameters are stored in MeshSettings and MaterialSettings connected with segment.



Geometry generator

Geometry generator is the most innovative module in library. There are many L-System implementations, but only few can generate geometry better than few cylinders connected by ends. Vegetation3D library uses new algorithm that not only generates smooth transitions between segments, but also produces imitation of tree bark at geometry level (it means it is not a texture but actual geometry).

Using geometry generator is very simple :


PlantTopology topology = ...;
MeshGenerator generator = new MeshGenerator();
List<Mesh> meshes = generator.generateMesh(topology,System.currentTimeMillis());


Second parameter of generateMesh method is seed for random numbers generator. The same values should be used for Derivation.derivate and MeshGenerator.generateMesh methods.

W3C XHTML 1.0 W3C CSS sourceforge.net GNU LGPL 3.0 JAVA
SourceForge.net Logo