/*
Copyright 2006 by Daniel Kuebrich
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package sim.app.lsystem;
import sim.util.*;
import sim.engine.*;
// This is the steppable that interprets the fully expanded l-system
public class LSystemDrawer implements Steppable
{
private static final long serialVersionUID = 1;
// copy the code over so that you can draw while calculating
ByteList code;
// draw settings remain local so that no changes are made mid-draw
int draw_time;
double x,y,theta, angle;
double segsize;
// used to keep track of popping and pushing positions
// Bags have pop() and push() functions in addition to normal list functions
// Meaning that they can be used either way, or as both... freaky.
public Bag stack;
// draw segment name
Segment s;
// we can stop when done drawing
public Stoppable stopper;
LSystemDrawer( LSystemData l )
{
// copy copy copy
this.code = new ByteList(l.code);
this.x = l.x;
this.y = l.y;
this.angle = l.angle;
this.theta = l.theta;
this.segsize = l.segsize;
// -1 is an odd number to initialize to, but seems the best choice for the step loop
draw_time = -1;
stack = new Bag();
}
public void step( final SimState state )
{
// draw stuff
LSystem ls = (LSystem)state;
while(true)
{
draw_time++;
// If done, then stop the sim
if(draw_time >= code.length)
{
if(stopper!=null) stopper.stop();
return;
}
// stack functionality
if(code.b[draw_time] == ((byte)'[')) // push
{
// using a double3d.. except the z is actually the angle
stack.push(new Double3D(x,y,theta));
}
else if(code.b[draw_time] == ((byte)']')) // pop
{
Double3D d = (Double3D)stack.pop();
x = d.x;
y = d.y;
theta = d.z;
}
// else normal stuff
// rotate
else if(code.b[draw_time] == ((byte)'-'))
theta += angle;
// rotate
else if(code.b[draw_time] == ((byte)'+'))
theta -= angle;
// if it's a capital letter, draw forward
else if(code.b[draw_time] >= ((byte)'A') && code.b[draw_time] <= ((byte)'Z'))
{
// draw a segment there
s = new Segment(x,y,segsize,theta);
ls.drawEnvironment.setObjectLocation(s, new Double2D(s.x, s.y));
x += (segsize * /*Strict*/Math.cos(theta));
y += (segsize * /*Strict*/Math.sin(theta));
break;
}
// if it's a lowercase letter, skip forward but don't draw
else if(code.b[draw_time] >= ((byte)'a') && code.b[draw_time] <= ((byte)'z'))
{
// don't draw, just skip the space
x += (segsize * /*Strict*/Math.cos(theta));
y += (segsize * /*Strict*/Math.sin(theta));
}
else
{
// this should never happen except on bad user input
System.err.println("Error--bad code: " + (char)code.b[draw_time] );
break;
}
}
// end while
}
// end step
}
// end class