Update screen during draw() without thread?

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});
  }
}

FractalTree

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:

export-8643

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.

export-714

4 Likes