Objective: |
In this project, you will learn how to generate a forest of trees using
an Iterated Function System (IFS) called Lindenmayer Systems (a.k.a. L-Systems), and place those trees
on a terrain. This assignment consists of two parts: a "Preparation" part and a "New Stuff" part.
NOTE: The "prep" portion is due on November 04, and the "new" stuff is due on November 11.
|
|
Preparation: |
The aim of this preparation part is for you to create the IFS for generating the
strings that will define each tree for the forest. In addition, you will create a
PolyCylinder routine to better understand transformations (e.g., translations, rotations) in WebGL.
A drawing pattern is defined by a turtle string made up of command characters that
control how the turtle moves, as well as its state. The commands include:
Character |
Meaning |
F |
Move forward a step of length len, drawing a line (or cylinder) to the new point. |
f |
Move forward a step of length len without drawing. |
+ |
Apply a positive rotatation about the X-axis of xrot degrees. |
- |
Apply a negative rotatation about the X-axis of xrot degrees. |
& |
Apply a positive rotatation about the Y-axis of yrot degrees. |
^ |
Apply a negative rotatation about the Y-axis of yrot degrees. |
\ |
Apply a positive rotatation about the Z-axis of zrot degrees. |
/ |
Apply a negative rotatation about the Z-axis of zrot degrees. |
| |
Turn around 180 degrees. |
[ |
Push the current state of the turtle onto a pushdown stack. |
] |
Pop the state from the top of the turtle stack, and make it the current turtle stack. |
L-Systems are used to generate a turtle string by iteratively expanding a start token
by applying production (or re-writing) rules. Each L-System consists of a grammar that
defines re-writing rules. Each rule in the grammar consists of a left-hand side (LHS) and a
right-hand side (RHS), separated by a colon.
A sample grammar looks like this:
start: F-F-F-F
F: F-F+F+FF-F-F+F
In addition to specifying a grammar, we also need to specify the values for
len, xrot, yrot, and zrot, as well as a value denoting
how many times we want to iterate (i.e., apply the production rule(s)).
Similar to the way the Koch curve is created by replacing each segment with a predefined
pattern, the turtle string is rewritten by substituting every instance of a LHS by its
corresponding RHS. For any token in the string for which there is no matching LHS, the token
is simply copied into the new string. For example, given the grammar:
start: F+F
F: F-F+F-F
after one iteration, the resulting turtle string would be:
F-F+F-F+F-F+F-F
and after two iterations, the resulting turtle string would be:
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
Your system should be able to handle multiple production rules, each having a unique LHS, for example:
start: X
X: F-[[X]+X]+F[+FX]-X
F: FF
Here is an example (in 2D) of some grammars and their corresponding trees. Can you extend these to 3D for
use in this project? (The value of n is the number of iteration, and the angle specified is the
rotation for the "+" and "-" characters.)
|
|
Setup: |
Create basic .html and .js files initialized to do nothing special but provide placeholders.
|
|
Prep Coding: |
-
L-Systems: Write a program that implements the L-System re-writing scheme described above. Your program should
be able to read several grammars from grammar files, store them in appropriate data structures, and
then generate the correct turtle strings by applying the rewriting rules as specified.
Here is a sample L-System file. A description of the file format is contained
in the file itself, and you may assume that all files have the same format.
Compile and run your program!
-
The PolyCylinder: We define a PolyCylinder as a polyline in three dimensions. Given a sequence of
points in 3-space { (x1, y1, z1), (x2, y2, z2), (x3, y3, z3) }, a polycylinder draws a cylinder from
(x1, y1, z1) to (x2, y2, z2), and another one from (x2, y2, z2) to (x3, y3, z3). To avoid "gaps" in the
joints of the polycylinder, a sphere of the same diameter as the cylinders is drawn at each point.
You need to write some code that:
- Moves (translates) to the first point,
- Draws a sphere,
- Points (rotates) towards the next point,
- Draws a cylinder to the next point,
- Moves (translates) to the next point,
- Draws a sphere,
- Points (rotates) towards the next point,
- Draws a cylinder to the next point,
- Moves (translates) to the next point,
- And so on, for all the points in the list.
HINT 1: This part is an exercise in translating and rotating your coordinate system before drawing
your geometry. After you accomplish this, you will understand how transformations are accumulated in WebGL.
HINT 2: You can simplify your life by always drawing your cylinder along the Z axis. To do this,
apply your rotations such that you are always "moving" along the Z axis by first rotating yourself to point
the current Z direction towards the next point.
Set up a view of your scene so that you can see all the content, draw a ground mesh (see below), and draw some
PolyCylinders in the scene. You can just come up with some (x, y, z) points on your own for this. Be creative!
Write your name, or something.
Run your program!
(If you REALLY want to test your program, try this input file. This is
from p. 20 of the reference book listed at the bottom of this page and is PURELY OPTIONAL!)
|
|
New Stuff: |
Now we need to put it all together.
- Create a simple mesh (as described in class) to use as a ground plane. You may want to look up how to use
triangle strips in WebGL (here is one info page
that covers tri-strips inWebGL, but you may find other, better ones). You must displace the vertices in the up direction
using any method you like. You might want to explore the use of height maps, or apply a mathematical function for
this. You are free to use any method (including by hand), but the goal here is efficiency, so your time may be better
spent writing code for this.
-
Using the code you wrote in the preparation section, your program should read in five L-System files,
"lsys1.txt,"
"lsys2.txt,"
"lsys3.txt,"
"lsys4.txt," and ONE OF YOUR OWN, and store them in instances of your grammar
class. You should then apply the re-writing rules for each grammar according to the values specified for this
in each grammar file.
-
Implement a variation on L-Systems, called Stochastic L-Systems, whereby
a production rule can have a probability associated with it, and there can be
multiple production rules that have the same LHS. For example, the grammar:
start: F
F(0.33): F+F
F(0.33): F-F
F(0.34): F+F-F
would choose how to replace each "F" it encounters according to the probabilities
associated with each rule. (The probabilities for each unique LHS should sum to 1.)
This adds better variation to the trees that are created. A sample stochastic L-System
file can be found in "lsysStochastic.txt." After getting
this to work, you must create 2 additional stochastic grammars that show some interesting trees.
-
Add leaves to the end of your branches by drawing the appropriate geometry.
You don't have to get too fancy, but choose a different color for them.
-
Reduce the diameter and length of each tree segment, depending on how far "up the tree" or
how far "away from the trunk" the segment is. This will give a tapering effect to the tree.
-
Choose a random location on the terrain to start drawing one of the randomly selected tress (maybe using a
random color) and draw it.
HINT: You should apply a translation and a rotation to move to the correct start location, including
the height at that point.
-
Repeat Step 6 (at least) 10 times in order to draw your forest.
|
|
Attacking the Problem: |
For the L-System part, start out by creating several classes that will help you manage the different things
you will need to keep straight. For example, you might want to have a turtle class consisting of a
position, orientation, length when drawing, and a string representing the turtle
string.
You might want to have a rule class that has strings for the lhs and rhs.
A grammar class would consist of a list of rules, along with a method (addRule) to add
a new rule to the grammar. The main method for the grammar class might be something like a rewrite method
that takes in a turtle and a number of iterations, and returns a new string after applying the
rules to the turtle string for the desired number of iterations.
The functionality for implementing '[' and ']' can be greatly simplified by using
functions PushMatrix and PopMatrix routines. All you need to do is implement a stack, then use it when
moving "up" or "down" a hierarchy. This will save and return you to the proper
state.
JavaScript has many string manipulation functions as well, which will help you parse the input files.
|
|
Documentation: |
You must create adequate documentation, both internal and external, along with your assignment.
The best way to produce internal documentation is by including inline comments. The preferred way to do this
is to write the comments as you code. Get in the habit of writing comments as you type in
your code. A good rule of thumb is that all code that does something non-trivial should have comments
describing what you are doing. This is as much for others who might have to maintain your code, as for
you (imagine you have to go back and maintain code you have not looked at for six months -- this WILL
happen to you in the future!).
I use these file and function (method) headers, in
my code.
Create external documentation for your program and submit it along with the project. The documentation
does not have to be unnecessarily long, but should explain briefly what each part of your program does, and
how your filenames tie in.
|
|
What to Turn in: |
Submit everything you need to run your program (source files, data files, etc.)
BEFORE YOU SUBMIT YOUR ASSIGNMENT, put everything in one directory and make sure it runs. Then zip everything up into a single archive file.
Capture some screen shots of your output, and turn those in as well.
The command to ZIP everything, assuming your code is in a directory "proj2", is:
zip FirstName_LastName_proj2-prep.zip proj2
and
zip FirstName_LastName_proj2.zip proj2
To submit your work, email the ZIP file to me.
|
|
Academic Honesty: |
Remember the policy on Academic Honesty: You may discuss the
assignment with others, but you are to do your own work. The official WPI statement
for Academic Honesty can be accessed
HERE.
|
|
References: |
|
Most of the motivation for my interest in this work comes from the book
"The Algorithmic Beauty of Plants," by Prezmyslaw Prusinkiewicz and Arstid Lindenmayer,
Springer Verlag, New York, 1990, ISBN 0-387-97297-8, ISBN 3-540-97297-8.
The whole book has been put online by
the author. In addition, there are links to other interesting works on this page.
You can also find it on Amazon.
|
Back to course page.
|