Update screen during draw() without thread?

It’s a long running question here but I just wanted to make sure:

Is it possible
Update screen during draw() without using thread??

Maybe we can make this a feature request?

I got a very deep recursion running and I like to update the screen throughout

Is there a better way than a thread solution?
Thank you all!

Hi @Chrisir – sorry for the late reply. I’m not certain that I understood this question.

What is your goal in avoiding thread? Note that redraw() and draw() will not work – you can try to create a metadraw() and then invoke draw manually, but the screen won’t actually update.

This sketch doesn’t demonstrate recursion – it just shows the failure to update the screen when you try to wrap something around a series of manual draw calls:

// THIS DOES NOT UPDATE THE SCREEN UNTIL THE END
int metaFrameCount = 0;

void setup() {
  noLoop();
  metadraw();
}

void metadraw() {
  background(192);
  for(int i=0; i<100; i++){
    delay(50);
    draw();
    // redraw();
  }
  metaFrameCount++;
}

void draw() {
  rect(random(-10, width), random(-10, height), 20, 20);
  println("mfc:", metaFrameCount, "fc:", frameCount, millis());
}

If you have a basic recursive structure – like this example:

void setup() {
  size(640, 640);
  noStroke();
  rectMode(RADIUS);
  background(255);
  drawSquare(width/2, height/2, width/2.1, 0.88, 2);
}

void drawSquare(float x, float y, float w, float scale, float min) {
  fill(random(128,255), random(128,255), random(128,255), 255);
  rect(x, y, w, w);
  float off = w/2.0;
  float w2 = off * scale;
  if (w2 > min) {
    drawSquare(x-off, y-off, w2, scale, min);
    drawSquare(x+off, y-off, w2, scale, min);
    drawSquare(x-off, y+off, w2, scale, min);
    drawSquare(x+off, y+off, w2, scale, min);
  }
}

…and you want to animate it in the draw thread, then you need to queue a set of actions and draw off the queue. So, rather than drawSquare, queueSquare would add new float[]{x, y, w2, scale, min} to an ArrayList<float[]> squareQueue. This gives you one big queuing pass, but once it is done you can now loop over the queue data and animate it – for example, only drawing n rows per second.

However, what if it takes you an hour to render that queue data, and you want a preview during that time – but you only want to do it on one thread? In that case, you need to model your recursion tree as a linked data structure in a way such that you can be expanding nodes on the tree, stop (when the tree gets to large or too much time has passed, respecting your update limits) to draw from it, then resume on the next frame and keep expanding nodes on the tree. There are many ways to do this – depending on how drawing relates to intermediate or terminal nodes, which should be drawn first, and if you can safely throw away items away after you have drawn then.

3 Likes

Dear Jeremy,

thanks for a great reply!

No worries about the small delay.

I like the recursive rects!

I guess I would like to propose a new function in processing that allows you to update the screen anywhere you want, even inside a recursion

(or a hack to achieve a screen update)

Warmest Regards, Chrisir :wink:

I am not an expert on the Processing graphics pipeline, but I don’t believe that this is even possible with the current architecture due to how the core of Processing works. But I defer to people like @neilcsmith and @sampottinger who have a much better understanding of the internals.

1 Like

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