pushMatrix() cannot use push more than 32 times

Why is this not a valid patch?
I get a “RuntimeException: pushMatrix() cannot use push more than 32 times” error in a snippet that seems perfectly valid for me:

void setup() {
  size(700, 500);
}
        
    
void draw() {
  fill(0);
  rect(0, 0, width, height);
  int N = 33;
  fill(255);
  for (int i = 0; i < N; ++i) {
    pushMatrix();
    translate(0, 15);
    rect(0, 0, 5, 5);
  }
  for (int i = 0; i < N; ++i) {
    popMatrix();
  }
}

Is this a known thing? Am I not supposed to use the pushMatrix-translate-popMatrix idiom when I juggle more than 32 objects?

1 Like

Hey There !
Wouldn’t you just keep Push and Pop within draw() ? And all objects between them would be affected ?

Sorry, I don’t understand your wording. Could you, please, rephrase your answer?

There’s an equal amount of push and pop operations within draw function.

Hello,

From what I have gleaned there is a limit of 32.

https://forum.processing.org/one/topic/pushmatrix-popmatrix-depth-extension.html


“static final protected int MATRIX_STACK_DEPTH = 32;”

:slight_smile:

2 Likes



/**
 * Change PGraphics Max Pushes (v1.0)
 * GoToLoop (2019/Jul/21)
 *
 * https://Discourse.Processing.org/t/
 * pushmatrix-cannot-use-push-more-than-32-times/12860/5
 */

import processing.awt.PGraphicsJava2D;
import java.lang.reflect.Field;

static final int PUSHES = 35, DIAM = 8, OFFSET_Y = 15;

void setup() {
  size(100, 550);
  noLoop();

  fill(#FFFF00);
  stroke(#FF0000);

  setMaxPushes((PGraphicsJava2D) getGraphics(), PUSHES);
}

void draw() {
  clear();

  for (int i = 0; i < PUSHES; ++i) {
    pushMatrix();
    translate(0, OFFSET_Y);
    square(0, 0, DIAM);
  }

  for (int i = 0; i < PUSHES; ++i)  popMatrix();
}

static final void setMaxPushes(final PGraphicsJava2D pg, final int pushes) {
  try {
    final Field f = PGraphicsJava2D.class.getDeclaredField("transformStack");
    f.setAccessible(true);
    f.set(pg, expand(f.get(pg), abs(pushes)));
  }
  catch (final ReflectiveOperationException ex) {
    throw new RuntimeException(ex);
  }
}
2 Likes

Do not use pushMatrix within the for loops. But only have it within the draw loop.

@GoToLoop @glv Thanks for digging it out. Is this a crucial constant? Can we bump it to something higher (e.g. 256)?

I find it a bit odd that I cannot do many transformations. The tutorial here:
https://processing.org/tutorials/transform2d/ describes how I can draw a house using push-translate-pop. So if the house has more than 32 parts, this is not possible. Same if I want to translate and draw some elements relative to each other. I redid a bit of my code to use the absolute coordinate system instead, but the idiom is very neat to use, pity I had to give it up.

I don’t understand you still – the loops are in the draw function – could you make an example snippet of what you mean?

How do else could you use pushMatrix in the draw to draw more than 32 objects?

1 Like

pushMatrix and popMatrix come as a pair and should not be used in separate loops. This program demonstrates how they might be used correctly.

float[] x, y, w, h;
int n = 30;

void setup() {
  size(400, 320);
  x = new float[n];
  y = new float[n];
  w = new float[n];
  h = new float[n];
  for (int i = 0; i < n; i++) {
    x[i] = (int) random(10, width - 40);
    y[i] = (int) random(10, height - 40);
    w[i] = (int) random(15, 35);
    h[i] = (int) random(10, 25);
  }
}

void draw() {
  background(0);
  fill(255, 50, 50);
  strokeWeight(2);
  stroke(255, 255, 0);
  for (int i = 0; i < n; ++i) {
    pushMatrix();
    translate(x[i], y[i]);
    rect(0, 0, w[i], h[1]);
    popMatrix();
  }
}
1 Like

I was thinking about something like this. Still seems to behave correctly?

void setup() {
  size(700, 500);
}


void draw() {
  fill(0);
  rect(0, 0, width, height);
  int N = 34;
  fill(255);
  pushMatrix();
  for (int i = 0; i < N; ++i) {
    translate(0, 15);
    rect(0, 0, 5, 5);
  }
  popMatrix();
}

Just try it! Change static final int PUSHES = 35; to static final int PUSHES = 256;. :wink:

1 Like

Your original example had 32 “nested” push\pops.
An example in a loop of 2500 push\pops that are NOT nested:

int inc = 0;

void setup()
  {
  size(1020, 1020, P2D);
  ortho();
  }

void draw()
  {
  background(0);
  noStroke();
  lights();
  fill(255, 255, 0);
  int num= 50;
  for (int i = 0; i< num*num; i++)
    {
    pushMatrix();
    int x = i%num*20+20;
    if (i%num == 0)
      inc ++;  
    int y = inc*20;
    translate(x, y);
    rotate(TAU/8);
    rectMode(CENTER);
    rect(0, 0, 10, 10);
    popMatrix();
    }
  inc = 0;
  println(frameRate);
  }

:slight_smile:

1 Like

Here is another example where they are all rotating:

int inc = 0;
float th = 0;

void setup()
  {
  size(1020, 1020, P3D);
  ortho();
  }

void draw()
  {
  background(0);
  noStroke();
  lights();
  fill(255, 255, 0);
  th += TAU/300;  
  
  int num= 50;
  for (int i = 0; i< num*num; i++)
    {
    pushMatrix();
    int x = i%num*20+20;
    if (i%num == 0)
      inc ++;  
    int y = inc*20;
    translate(x, y);
    rotateX(th);
    rotateY(th);
//    rectMode(CENTER);
//    rect(0, 0, 10, 10);
    box(10, 10, 10);
    popMatrix();
    }
  inc = 0;
  //println(frameRate);
  }

Over 2500 or so and my frame rates started to drop below 60 fps.

Also, I use println() for debugging but displaying frameRate() every loop you can miss important console messages at start.

This will fix that:

  if ((frameCount>300) && (frameCount%60 == 0))
    println(frameRate);

Example of “messages” when I was using P2D mode when I should have been using P3D.
Sketch still ran but was a blank screen!

:slight_smile:

1 Like

For those who might wonder how to pull that out in Python Mode, here’s the converted sketch for it: :snake:

"""
 * Change PGraphics Max Pushes (v1.0)
 * GoToLoop (2019/Jul/21)
 *
 * https://Discourse.Processing.org/t/
 * pushmatrix-cannot-use-push-more-than-32-times/12860/14
"""

PUSHES, DIAM, OFFSET_Y = 35, 8, 15
PUSHES_RANGE = tuple(range(PUSHES))

def setup():
    size(100, 550)
    noLoop()

    fill(0xffFFFF00)
    stroke(0xffFF0000)

    setMaxPushes(this.getGraphics(), PUSHES)


def draw():
    clear()

    for i in PUSHES_RANGE:
        pushMatrix()
        translate(0, OFFSET_Y)
        square(0, 0, DIAM)

    for i in PUSHES_RANGE: popMatrix()


def setMaxPushes(pg, pushes):
    pg.transformStack = expand(pg.transformStack, abs(pushes))
2 Likes

“Wonder is the beginning of wisdom” - Socrates

This post was a classic:

Thanks! It really helped me at that time.

glv

1 Like

In your case, you definitely should not.

Here is an example of what you want:

y = 5 + 4 - 2 + 7 - 4 - 5 + 1
…, then, to pop back to 5 when you are done.

You are doing that like this:

y = 5 push + 4 push - 2 push + 7 push - 4 push - 5 push + 1 pop pop pop pop pop pop
= 5

…but you could just do this:

y = 5 push + 4 - 2 + 7 - 4 - 5 + 1 pop
= 5

pushMatrix();
for (int i = 0; i < N; ++i) {
  translate(0, 15);
  rect(0, 0, 5, 5);
}
popMatrix();

Push isn’t for every transformation you make. It saves every place in a transformation stream that you want to back out to and re-branch from. You could make 100 story skyscrapers with thousands of complex components per story, and you might only need to push 3 levels deep–(building(floor(window))). The solar system has lots of objects with nested relative rotations, but you don’t push a new level for each object – any satellite of a moon of a moon of a planet around the sun is still only a few levels deep.

sun
push
  Earth
  push
    moon
    push
       moonsat1
    pop
    push
       moonsat2
    pop
  pop
  push
    spacestation
  pop
pop
push
  Venus
pop

So, if there is a wart on a frog on a bump on a log in a hole in the bottom of the sea, you only need to push seven deep – even if there are seven seas, dozens of holes, hundreds of logs … a thousands frogs, and millions of warts. Growing random branches on the logs and pushing 4000 relative frog legs still only takes you to 9 deep.

The matrix stack is also relatively expensive way of tracking transforms if you aren’t doing rotation or scaling–it is heavy duty and general purpose. If you really need to walk and unwind translate statements nested 300 deep–like some kind of relative fractal–consider something like an ArrayDeque<PVector>

https://docs.oracle.com/javase/8/docs/api/java/util/ArrayDeque.html

2 Likes