Order of layers not working as expected

I want the circles on top and all the lines below them. Since I’m drawing the circles last, I expected them to all be on top of the lines.

How can I solve this problem?

p.s.: This is only a small simplified piece of what I’m actually working on.

canvas

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
  background(50);
}

function draw() {
  for (let i = 0; i < 3; i++) {
    let x = 100 * cos(120 * i) + width / 2;
    let y = 100 * sin(120 * i) + height / 2;

    stroke(200);
    strokeWeight(2);
    line(
      x,
      y,
      100 * cos(120 * (i + 1)) + width / 2,
      100 * sin(120 * (i + 1)) + height / 2
    );

    fill(50);
    circle(x, y, 150);
  }
}

I figured out the order that things are drawn.

  1. first line is drawn
  2. first circle is drawn
  3. second line is drawn
  4. second circle is drawn
  5. third line is drawn
  6. third circle is drawn

That was obvious, but helps to visualize.

I thought that circle 2 would be drawn where circle 4 is. Although then line 3 would be on top of that circle.

The problem is that both the lines and the circles are in the same loop. If I put them in separate loops then the problem would be fixed. But is there a way to fix the problem while still keeping them in the same loop?

order

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
  background(50);
}

function draw() {
  for (let i = 0; i < 3; i++) {
    let x = 100 * cos(120 * i) + width / 2;
    let y = 100 * sin(120 * i) + height / 2;

    stroke(100 * i);
    strokeWeight(5 * i + 1);
    line(
      x,
      y,
      100 * cos(120 * (i + 1)) + width / 2,
      100 * sin(120 * (i + 1)) + height / 2
    );

    fill(50);
    circle(x, y, 100);
  }
}

createGraphics()

'use strict';

var pg, cx, cy;

function setup() {
  createCanvas(400, 400);
  noLoop();

  fill(50).stroke(200).strokeWeight(2).angleMode(DEGREES);

  pg = createGraphics(width, height).fill(50).stroke(200).strokeWeight(2);

  cx = width  >> 1;
  cy = height >> 1;
}

function draw() {
  background(50);
  pg.clear();

  var i = 0, px = 100 + cx, py = cy;

  while (i++ < 3) {
    pg.circle(px, py, 150);
    line(px, py, px = 100 * cos(120 * i) + cx, py = 100 * sin(120 * i) + cy);
  }

  image(pg, 0, 0);
}

That works, but I can’t even understand the code :confused:
I’d rather just use two loops in that case.

And what about performance? What would be more performant? Using two loops or using createGraphics?

pg.circle(px, py, 150); should have same performance as circle(px, py, 150);.

The slowest part is the additional statement image(pg, 0, 0);.

But I still think image(pg, 0, 0); should be much faster than having an extra loop.

Can you pinpoint exactly the parts where you couldn’t understand?

1 Like

BtW, here’s my version w/ 2 loops in place of createGraphics() + image():

'use strict';

var cx, cy;

function setup() {
  createCanvas(400, 400);
  noLoop();

  fill(50).stroke(200).strokeWeight(2).angleMode(DEGREES);

  cx = width  >> 1;
  cy = height >> 1;
}

function draw() {
  background(50);

  var i = 0, px = 100 + cx, py = cy;

  while (i++ < 3)
    line(px, py, px = 100 * cos(120 * i) + cx, py = 100 * sin(120 * i) + cy);

  i = 0, px = 100 + cx, py = cy;

  while (i++ < 3) {
    circle(px, py, 150);
    px = 100 * cos(120 * i) + cx;
    py = 100 * sin(120 * i) + cy;
  }
}
1 Like

Most of it :stuck_out_tongue:
I guess by looking at it more I kind of understand, but just don’t see why write it like that. It’s more confusing for me.

I managed to just add the createGraphics() stuff to my previous code though:

let pg;

function setup() {
  createCanvas(400, 400);
  pg = createGraphics(400, 400);
  angleMode(DEGREES);
  background(50);
}

function draw() {
  image(pg, 0, 0);

  for (let i = 0; i < 3; i++) {
    let x = 100 * cos(120 * i) + width / 2;
    let y = 100 * sin(120 * i) + height / 2;

    pg.stroke(100 * i);
    pg.strokeWeight(5 * i + 1);
    pg.line(
      x,
      y,
      100 * cos(120 * (i + 1)) + width / 2,
      100 * sin(120 * (i + 1)) + height / 2
    );

    stroke(100 * i);
    strokeWeight(5 * i + 1);
    fill(50);
    circle(x, y, 100);
  }
}

It works here, but am having a bit of issues when introducing it to the rest of the sketch I’m working on. Things not working as expected when running as animation. And also things were looking a bit pixelated. Maybe pixel density issue.

I’ll keep playing with it and see what I manage. Thanks! :slight_smile:

1 Like

It’s just my attempt to halve the calls for both cos() & sin() and other calcs. :innocent:

Ohhh, I see. Will that make a considerable difference in performance?

Also, why you do the chaining with the dots? Like fill(50).stroke(200).strokeWeight(2)? Is there a benefit to doing it this way instead of just one below the other without chaining with dots?

And what about the >>? I know those are bitwise operators, but how do they actually work?
Would width >> 1 be the same as width / 2?

Given those 3 methods are chainable, I’m taking that opportunity to shorten the code.

Pretty much. W/ the added benefit of truncating the fractional part as well.

A little for sure. Lowering unnecessary trigonometry calls always helps I guess.

Alternatively we could pre-calculate & cache those cos() & sin() calls into 2 arrays:

// Discourse.Processing.org/t/order-of-layers-not-working-as-expected/40452/10
// GoToLoop (2023/Jan/05)

'use strict';

const TURBO = true;
var pg, xCoords, yCoords;

function setup() {
  createCanvas(400, 400);
  noLoop();

  fill(50).stroke(200).strokeWeight(2).angleMode(DEGREES);

  pg = createGraphics(width, height).fill(50).stroke(200).strokeWeight(2);

  const
    cx = width  >> 1,
    cy = height >> 1,
    length = { length: 4 };

  xCoords = Uint16Array.from(length, (_, i) => 100 * cos(120 * i) + cx);
  yCoords = Uint16Array.from(length, (_, i) => 100 * sin(120 * i) + cy);

  console.table(xCoords);
  console.table(yCoords);
}

function draw() {
  background(50);
  pg.clear();

  TURBO? turboLoop() : regularLoop();

  image(pg, 0, 0);
}

function regularLoop() {
  for (var i = 0; i < 3; ++i) {
    const
      x = xCoords[i],
      y = yCoords[i],
      a = xCoords[i + 1],
      b = yCoords[i + 1];

    pg.circle(x, y, 150);
    line(x, y, a, b);
  }
}

function turboLoop() {
  var i = 0, [ px ] = xCoords, [ py ] = yCoords;

  while (i++ < 3) {
    pg.circle(px, py, 150);
    line(px, py, px = xCoords[i], py = yCoords[i]);
  }
}
1 Like

The only way is to calculate the intersection points between the lines and the circles and then draw the circles and truncated lines in one loop. Unless you have millions of circles and lines to draw then using an offscreen buffer with createGraphics or calculating the intersection points will swamp any benefit to be had from using a single loop.

So in this instance the most efficient way is simply to draw all lines in one loop and the circles in a second (later) loop. :smile:

1 Like