Wednesday, October 10, 2007

Vis Part Two

L-System



And now for the Processing Code:

Code taken from class examples and modified slightly, a thank you to Liubo Borissov for the code.

/*
L-Systems are a great simulation to explore, as there is a near infinite
variety of images you can generate. In this example we calculate the L-System
when we start our applet, and then alter the angle used to draw the system
in the draw() method. We could just as well work with other parameters in
the draw method.
*/
import processing.serial.*;

LSystem ls;
Serial myPort;
int inByte = -1;
int dataByteLSB = -1; // Variable to hold keystoke values
int dataByteMSB = -1; // Variable to hold keystoke values
int dataByte = -1; // Variable to hold keystoke values
int headerByte = 224;

void setup() {

// setup the environment
size(500, 500);
background(0);
smooth();

// setup the L-System
// ls = new LSystem("F", "FF-[-F+F+F]+[+F-F-F]");
ls = new LSystem("F", "CF[+F][--F]F[-F][++F]F");


// Age the L-System four generations
ls.simulate(4);
myPort = new Serial(this, Serial.list()[0], 9600);
}




void serialEvent() {
// setup the drawing area


// set the angle for our L-System and draw it
}

void draw() {
// If there are bytes available in the input buffer,
// Read them and print them:
while (myPort.available() > 0) {
inByte = myPort.read();
// println(getByte);

if (inByte == headerByte) {
dataByteLSB = myPort.read(); //get LSB
dataByteMSB = myPort.read(); //get MSB
//println(dataByteLSB);
//println(dataByteMSB);
}

// These two lines 'reconstruct' the data
// into a 0-1023 number
dataByte = dataByteMSB << 7;
dataByte = dataByte + dataByteLSB;

}

if (dataByte >= 0) {
background(0);
stroke(255);

float theta = ((dataByte/2) / (float)width) * 180.0f;
ls.useTheta(theta);
ls.render();
}

}

The Class is as follows:

/*

The LSystem class contains all of the algorithms necessary to
create, iterate, and draw Fractal L-Systems. The theory behind
an L-System is based upon a simple grammar that tells the system
how to draw itself. The grammar is made up of 5 characters:

F : draw a line forward by a define length
+ : rotate in the positive direction by a defined angle
- : rotate in the negative direction by a defined angle
[ : store our current position
] : retrieve the previous position

Each L-System will start with an Axiom and a Rule, the Axiom is
the starting condition (typically just "F"), and the Rule is a
substitution applied at each step. A simple rule might be:
"F[+F--F]+F"

For each iteration of the fractal, every occurance of the letter
"F" is replaced with the Rule. So when we begin, the Fractal is
defined by it's axiom:

1: F

At the second iteration we apply the rule:

2: F[+F--F]+F

At the third iteration we apply the rule again, replacing every occurance of
F with the rule:

3: F[+F--F]+F[+F[+F--F]+F--F[+F--F]+F]+F[+F--F]+F

We can repeat this pattern as often as desired. At each iteration we shorten the
length of the line segment drawn by any occurance of F.
*/

public class LSystem {


private String axiom;
private String rule;
private String production;

private float startLength;
private float drawLength;
private float theta;

private int generations;

public LSystem() {

// create default values
axiom = "F";
rule = "F+F--F+F";


startLength = 40.0f;
theta = radians(60.0);

reset();
}

public LSystem(String axiom_, String rule_) {

// save the values
axiom = axiom_;
rule = rule_;

startLength = 80.0f;
theta = radians(60.0);

reset();
}

public void useRule(String r_) {

rule = r_;
}

public void useAxiom(String a_) {

axiom = a_;
}

public void useLength(float l_) {

startLength = l_;
}

public void useTheta(float t_) {
theta = radians(t_);
}

public void reset() {

production = axiom;
drawLength = startLength;
generations = 0;
}

public int getAge() {

return generations;

}

/*
We need to create a simple drawing system that walks through our
production (the current state of our LSystem) and follows the rules
provided to draw the system.
*/
public void render() {

// start at the bottom center of the screen
translate(width / 2, height);

for (int i = 0; i < production.length(); i++) {

char step = production.charAt(i);

if (step == 'F') {

// draw a line
line(0, 0, 0, -drawLength);

// move to the end of the line
translate(0, -drawLength);

} else if (step == '+') {

// rotate
rotate(theta);

} else if (step == '-') {

// rotate
rotate(-theta);

} else if (step == '[') {

// save our position
pushMatrix();

} else if (step == ']') {

// reset our position
popMatrix();

}
else if (step == 'C'){
strokeWeight(constrain(100/(i+1)+1,1,10));
stroke(color(100,255,0));
}
else if (step == 'L'){
pushMatrix();
translate(drawLength/3,0);
fill(200,40,40,10);
ellipse(0,0,constrain(i,1,5),i/60);
popMatrix();
}

}
}

/*
Our basic simulate method is a little different,
it will age our LSystem by one generation, leaving
the rendering part for us to call later
*/
public void simulate() {

production = iterate(production, rule);

}

/*

For conveinience we have a second simulate method that
lets us specify what age we want to simulate our LSystem
up too.
*/
public void simulate(int gen) {

while (getAge() < gen) {
production = iterate(production, rule);
}
}

/*

It is up to our iterate method to update the production (text) of
the LSystem, as well as the drawLength and age
*/
private String iterate(String prod_, String rule_) {

// update our drawing size
drawLength = drawLength * 0.6;

// update our generations
generations++;

// run the iteration
String newProduction = prod_;
newProduction = newProduction.replaceAll("F", rule_);

return newProduction;
}
}

0 Comments:

Post a Comment

<< Home