If you want to be able to render progressively from an unbounded recursive process in a single thread – and don’t want to use thread()
– then there are ways.
I mentioned a linked data structure that acts as a recursion queue. I actually have a draft library I wrote that does that – “Recursor”.
Supposing you start out with a basic recursive function that cannot be animated. It looks like this:
void tree(float[] seg) {
line(seg[0], seg[1], seg[0]+seg[2], seg[1]+seg[3]);
PVector s = new PVector(seg[2], seg[3]);
if (s.mag()>4) {
s.mult(0.7);
s.rotate(-PI/4);
tree(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y});
s.rotate(2*PI/4);
tree(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y});
}
}
Full sketch
void setup() {
size(200, 200);
fill(0);
noLoop();
}
void draw() {
background(255);
translate(width/2, height);
float[] seed = new float[]{0, 0, 0, -width/4.0};
tree(seed);
}
void tree(float[] seg) {
line(seg[0], seg[1], seg[0]+seg[2], seg[1]+seg[3]);
PVector s = new PVector(seg[2], seg[3]);
if (s.mag()>4) {
s.mult(0.7);
s.rotate(-PI/4);
tree(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y});
s.rotate(2*PI/4);
tree(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y});
}
}
In order to animate it with the Recursor library you can create a function or a class with a single method. Here is a class:
class Call {
float[] seg;
Call(float [] seg) {
this.seg = seg;
}
ArrayList<Call> recurse() {
line(seg[0], seg[1], seg[0]+seg[2], seg[1]+seg[3]);
PVector s = new PVector(seg[2], seg[3]);
ArrayList<Call> result = new ArrayList<Call>();
if (s.mag()>4) {
s.mult(0.7);
s.rotate(-PI/4);
result.add(new Call(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y}));
s.rotate(2*PI/4);
result.add(new Call(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y}));
}
return result;
}
}
The main difference is that, rather than calling directly, the method returns an ArrayList<> of itself (new calls).
You then create a special controller object that uses your special class.
RecursorQueue rq = new RecursorQueue<Call>() {
public ArrayList<Call> recurse(Call call) {
return call.recurse();
}
};
Then you just call rq.step()
or rq.stepUntil()
to step through the animation. Or you can check the status and step for a certain number of millis, or until the queue is a certain size. Or you can step using built-in modes like FIRST, LAST, EITHER or methods like shuffle()
. Here is the class above stepped in three different ways:
Full Sketch
import com.jeremydouglass.recursor.*;
RecursorQueue rq = new RecursorQueue<Call>() {
public ArrayList<Call> recurse(Call call) {
return call.recurse();
}
};
RecursorQueue rq2;
RecursorQueue rq3;
void setup() {
size(600, 200);
fill(0);
background(255);
rq2 = (RecursorQueue) rq.clone();
rq3 = (RecursorQueue) rq.clone();
rq.add( new Call(new float[]{1*width/6, height, 0, -height/4.0}));
rq2.add(new Call(new float[]{3*width/6, height, 0, -height/4.0}));
rq3.add(new Call(new float[]{5*width/6, height, 0, -height/4.0}));
rq.popMode = Mode.FIRST;
rq2.popMode = Mode.LAST;
rq3.popMode = Mode.EITHER;
}
void draw() {
rq.stepUntil(6, 0);
rq2.stepUntil(6, 0);
rq3.shuffle();
rq3.stepUntil(6, 0);
}
void keyReleased() {
rq.clear();
frameCount = -1; // restart
}
class Call {
float[] seg;
Call(float [] seg) {
this.seg = seg;
}
ArrayList<Call> recurse() {
line(seg[0], seg[1], seg[0]+seg[2], seg[1]+seg[3]);
PVector s = new PVector(seg[2], seg[3]);
ArrayList<Call> result = new ArrayList<Call>();
if (s.mag()>4) {
s.mult(0.7);
s.rotate(-PI/4);
result.add(new Call(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y}));
s.rotate(2*PI/4);
result.add(new Call(new float[]{seg[0]+seg[2], seg[1]+seg[3], s.x, s.y}));
}
return result;
}
}
The upshot – you can take any normal recursive method, alter its return to work with the library, and then have fine-grained progressive rendering control.