24. September 2013
Download l_system_tree.zip Download l_system_tree.zip
import controlP5.*;
float xrot = 0;
float yrot = 0;
float zoom = 0;
ControlP5 cp5;
Slider sliderDepth;
Slider sliderAngle;
Slider sliderScale;
Textlabel labelRule;
LTree tree;
int currentDepth = 1;
float currentScale = 1;
float currentAngle = 0;
float targetAngle = currentAngle;
void setup () {
size(1280,720, P3D);
colorMode(HSB);
cp5 = new ControlP5(this);
sliderDepth = cp5.addSlider("L-Depth")
.setPosition(30,50)
.setSize(100,20)
.setValue(3)
.setNumberOfTickMarks(7)
.setRange(1,7);
sliderAngle = cp5.addSlider("Angle")
.setPosition(0,height-20)
.setSize(width,20)
.setValue(60)
.setRange(-360,360);
sliderScale = cp5.addSlider("Scale")
.setPosition(width-130,50)
.setSize(100,20)
.setNumberOfTickMarks(50)
.setRange(0.1, 5.0)
.setValue(1.0);
currentAngle = sliderAngle.getValue();
targetAngle = currentAngle;
currentDepth = (int)sliderDepth.getValue();
tree = new LTree();
labelRule = cp5.addTextlabel("Rule")
.setPosition(30,30)
.setText(tree.getRule(0));
tree.setScale(sliderScale.getValue());
tree.generate(currentDepth);
}
void draw() {
if (sliderDepth.getValue() != currentDepth) {
currentDepth = (int)sliderDepth.getValue();
tree.generate(currentDepth);
}
if (sliderAngle.getValue() != currentAngle) {
currentAngle = sliderAngle.getValue();
targetAngle = currentAngle;
tree.setAngle(currentAngle);
}
if (sliderScale.getValue() != currentScale) {
currentScale = sliderScale.getValue();
tree.setScale(currentScale);
}
labelRule.setText(tree.getRule(0));
pushMatrix();
translate(0,0,zoom);
background(0);
translate(width/2,height/2);
rotateY(radians(xrot));
rotateX(radians(yrot));
tree.drawTree();
popMatrix();
}
void mouseDragged () {
if (mouseButton == RIGHT) {
zoom += (mouseY-pmouseY);
println(zoom);
} else if (mouseButton == LEFT) {
xrot = xrot + ((float)(mouseX-pmouseX)) / width * 360;
yrot = yrot + -1 * ((float)(mouseY-pmouseY)) / height * 360;
}
}
void keyPressed () {
if (key == CODED) {
switch (keyCode) {
case RIGHT:
sliderAngle.setValue(constrain(sliderAngle.getValue()+1, sliderAngle.getMin(), sliderAngle.getMax()));
break;
case LEFT:
sliderAngle.setValue(constrain(sliderAngle.getValue()-1,sliderAngle.getMin(),sliderAngle.getMax()));
break;
}
}
}
void keyReleased () {
// for demo purposes
if (key == CODED) {
switch (keyCode) {
case UP:
sliderDepth.setValue(constrain(sliderDepth.getValue()+1,sliderDepth.getMin(),sliderDepth.getMax()));
break;
case DOWN:
sliderDepth.setValue(constrain(sliderDepth.getValue()-1,sliderDepth.getMin(),sliderDepth.getMax()));
break;
}
} else {
switch (key) {
case '0':
tree.setRule(0, "F[+F]F[-F]F");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '1':
tree.setRule(0, "F[+F][-F][aF][zF]");
tree.setAxiom("F+FaF+FzF+F");
tree.generate(currentDepth);
break;
case '2':
tree.setRule(0, "+z[+Fzaaz]FF[zaazF+]z+");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '3':
tree.setRule(0, "F[-zF[zF]]");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '4':
tree.setRule(0, "F[FF]z+a-a+F[+Fzaaz]");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '5':
tree.setRule(0, "a+FzFFzF+");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '6':
tree.setRule(0, "F+z+F");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '7':
tree.setRule(0, "-F++[aFz]");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '8':
tree.setRule(0, "-FzF-");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case '9':
tree.setRule(0, "+FaFzFaF+");
tree.setAxiom("F");
tree.generate(currentDepth);
break;
case 'y':
sliderScale.setValue(constrain(sliderScale.getValue()-0.1,sliderScale.getMin(),sliderScale.getMax()));
break;
case 'x':
sliderScale.setValue(constrain(sliderScale.getValue()+0.1,sliderScale.getMin(),sliderScale.getMax()));
break;
}
/*
+FaFzFaF+
// Some sample rules for the "F"
// taken from http://web.mit.edu/~eric_r/Public/lsystems/lsys3D12.pde
"Fz-F+F";
"zF-[F]";
"F[a+F[z--F]]";
"FaF-F";
"+F+aFa-";
"[F+]FzFz-";
"[-F+a[F]]";
"[z]Fz+aFa";
"aFaaFF+zF";
"aFF-z";
"FF[F+F]z+";
"-F[aF]";
"a+a+FzF"; //the broken dome
"FzzFaF+"; //the worm
"a+F+F+-za";
"Fz++zF"; //the wheel
"F-zaF[a+F]"; //Qsplosion
"-F++[aFz]"; //shaman
"zFz-F"; //flowerish
"Fz+FzFF"; //turkey...
"F[-zF[zF]]"; //a nice tree
"aFaF[z[+F]]"; //freaky tree
"F+z+F"; // mandala
"+FFz[-z-F]"; //thorn bush
"-FzF-"; //indescribably awesome geometric
"aF+zF"; // ball of string
"F[[Fa]+aF]"; //treekazoid
"F[+F][-F][aF][zF]"; //symmetree
"[+F]F[zF]"; // yet another tree thing
"F[+F][--F][aF][zzF]";//asymmetree (awesome)
"[-aF]FFFa"; //freakstarfishcake
"FaF-"; //geode ashtray
"[F-F]FFa"; //star
"F+FaF-FzF";
"a+FzFFzF+"; //a 90 d 3 s .5
"F[FF]z+a-a+F[+Fzaaz]"; //thornwheel
*/
}
}
class LSystem {
protected char[] l_alphabet = {'F', '+', '-', '[', ']'};
protected String[] l_rules = {"F[+F]F[-F]F", "+", "-", "[", "]"};
protected String l_axiom = "F";
protected String l_tree = "";
LSystem() {
}
public void generate (int depth) {
l_tree = l_axiom;
// cache the lengths of each rule
int[] ruleLengths = new int[l_alphabet.length];
for (int i = 0; i < l_alphabet.length; i++) {
ruleLengths[i] = l_rules[i].length();
}
// iterate over the tree - calculate the new tree length and then generate tree from it
for (int i = 0; i < depth; i++) {
int newLength = 0;
int oldLength = l_tree.length();
for (int j = 0; j < oldLength; j++) {
char c = l_tree.charAt(j);
for (int k = 0; k < l_alphabet.length; k++) {
if (c == l_alphabet[k]) {
newLength += ruleLengths[k];
}
}
}
// generate new tree
StringBuffer newTree = new StringBuffer(newLength);
for (int j = 0; j < oldLength; j++) {
char c = l_tree.charAt(j);
for (int k = 0; k < l_alphabet.length; k++) {
if (c == l_alphabet[k]) {
newTree.append(l_rules[k]);
break;
}
}
}
// save the new tree
l_tree = newTree.toString();
}
println("new tree:" + l_tree);
}
public String getTree() {
return l_tree;
}
public String getRule (int index) {
if (0 <= index && index < l_rules.length) {
return l_rules[index];
}
return "";
}
public void reset() {
l_alphabet = new char [] {'F', '+', '-', '[', ']'};
l_rules = new String [] {"F[+F]F[-F]F", "+", "-", "[", "]"};
l_axiom = "F";
}
public void setAxiom (String newAxiom) {
l_axiom = newAxiom; // TODO: validate brackets and chars
}
public void setAlphabet(char [] newAlphabet) {
l_alphabet = newAlphabet;
}
public void setRules(String [] newRules) {
l_rules = newRules; // TODO: check length?
}
public void setRule(int index, String rule) {
if (0 <= index && index < l_rules.length) {
l_rules[index] = rule;
}
}
}
class LTree extends LSystem {
private float t_distance = 50;
private float t_angle = 60;
private float t_scaleFactor = .7;
LTree () {
setInit();
}
public void setInit () {
l_axiom = "F";
l_alphabet = new char [] {'F', '+', '-', '[', ']', 'z', 'a', 'B', 'C'};
l_rules = new String [] {"F[+F][-F][aF][zF]", "+", "-", "[", "]", "z", "a", "B", "Ca+F"};
}
public void drawTree() {
for (int i = 0; i<l_tree.length(); i++) {
switch (l_tree.charAt(i)) {
case 'F':
case 'B':
float newhue = float(i)/l_tree.length()*255;
fill(color(newhue, 255, 255));
translate(0,-t_distance/2,0);
box(t_distance/4,t_distance,t_distance/4);
translate(0,-t_distance/2,0);
break;
case '-':
rotateX(radians(-t_angle));
break;
case '+':
rotateX(radians(t_angle));
break;
case 'z':
rotateZ(radians(t_angle));
break;
case 'a':
rotateZ(radians(-t_angle));
break;
case '[':
pushMatrix();
t_distance = t_distance * t_scaleFactor;
break;
case ']':
popMatrix();
t_distance = t_distance / t_scaleFactor;
break;
}
}
}
public void setAngle (float newAngle) {
t_angle = constrain(newAngle, -360, 360);
}
public void setScale (float newScale) {
t_scaleFactor = newScale;
}
}