One-time translation for sketch setup?

I want to set up a Processing sketch with a one-time translation. Is this possible?

To be clear, I already know how to achieve a sketch-wide translation effect (see below). What I want to know if there are ways to translate once efficiently (i.e. without constant push-pop-ing) and clearly (e.g. defining a setup translation in setup() or somewhere else outside the draw() loop.).

So, given this sketch of an upper-left rectangle:

void draw(){
  rect(0,0,10,10);
}

...I want to shift everything once, then program the sketch within the translated frame of reference. The translate() documentation says "If translate() is called within draw(), the transformation is reset when the loop begins again." -- which implied to me that calling translate() from before draw in setup() would not reset the translation. That approach does not appear to work, however:

void setup(){
  //// THIS DOES NOT WORK
  translate(20,20);
}
void draw(){
  rect(0,0,10,10);
}

I'm not sure why it doesn't work. My best guess is that setup's translate is cleared at the end of the first draw loop. I also tried a setup() with translate() followed by a single pushMatrix() -- this doesn't work either, perhaps because the end of the draw loop clears the entire matrix stack.

The common working example I see is to push-and-pop every single draw loop, like this:

void draw(){
 pushMatrix();
   translate(20,20);
   rect(0,0,10,10);
 popMatrix();
}

That creates the desired effect, but it pushes-and-pops n times a second to no purpose -- I just want to setup the translation once.

I've also seen some examples of using pushMatrix() from setup(), then using an inverted popMatrix()/pushMatrix() loop to retrieve it in the draw (e.g. for recording cumulative translations), like this:

void setup(){
  translate(20,20);
  pushMatrix();
}
void draw(){
  popMatrix();
  rect(0,0,10,10);
  pushMatrix();
}

...and, like the previous example, that creates the desired effect, but again it pushes-and-pops n times a second for no reason.

Are there other ways to define a one-time translation? Any suggestions or feedback would be appreciated!

Answers

  • Not 100%, but I guess PGraphics objects keep their transformations. :-??

  • Interesting idea! Doesn't look like it, though -- it looks like each new beginDraw resets the translation, so my call from the draw loop (updatePage()) would also need to translate every time to keep its position.

    PGraphics pg;
    int margin = 20;
    
    void setup(){
      pg = setupPage();
    }
    void draw() {
      image(pg,0,0);
      updatePage(pg);
    }
    
    PGraphics setupPage(){
      PGraphics pg_ = createGraphics(width, height);
      pg_.beginDraw();
      pg_.translate(margin,margin);
      pg_.fill(255);
      pg_.rect(0,0,10,10);
      pg_.endDraw();
      return pg_;
    }
    
    void updatePage(PGraphics pg_){
      pg_.beginDraw();
      pg_.fill(0);
      pg_.rect(0,0,10,10); //// not translated by earlier call
      pg_.endDraw();
    }
    

    An alternative would be to put the translation offsets in the draw() image() call: image(pg,20,20). However this would have to be done for every single object in the draw loop, which defeats the purpose of a one-time setup translating and keeping all the render code clean.

    I suppose the one exception is if there is no updating at all of the PGraphics -- then the margin is only used once in a single call from setup, and never referred to from draw().

    PGraphics pg;
    int margin = 20;
    
    void setup(){ pg = setupPage(); }
    void draw() { image(pg,0,0);    }
    
    PGraphics setupPage(){
      PGraphics pg_ = createGraphics(width, height);
      pg_.beginDraw();
      pg_.translate(margin,margin);
      pg_.rect(0,0,10,10);
      pg_.endDraw();
      return pg_;
    }
    

    Otherwise it seems like needing to reassert translation offsets every single draw cycle is inevitable, whether on the main sketch canvas or in a PGraphics object.

  • Answer ✓

    The transformation matrix is reset before the draw method is executed. So you will always have to change the transformation matrix at the start of the draw method.

    In your example

    void setup(){
      translate(20,20);
      pushMatrix();
    }
    void draw(){
      popMatrix();
      rect(0,0,10,10);
      pushMatrix();
    }
    

    you do this with the popMatrix.

    Personally I don't like to split the pushMatrix/popMatrix pair into different methods, in larger programs this can lead to stack empty and stack overflow errors that can be difficult to debug.

    In the other example you have

    void draw(){
     pushMatrix();
       translate(20,20);
       rect(0,0,10,10);
     popMatrix();
    }
    

    The purpose of the push/popMatrix methods are to isolate transformations and that is not happening here so they can be removed.

    I can see no significant benefit in a one time transformation.

  • Answer ✓

    Can I ask why you want to do this?

    I have no problem with a single translate at the top of draw, often to centre the origin. It's quite normal to the point of not needing a comment.

    You don't need push and pop around the whole of draw as the matrix gets reset anyway.

  • (Quark already said a lot of that)

  • edited September 2016

    quark, koogs, thanks for the additional feedback -- in particular for pointing out that the "push/pop" are unneeded if there is just a single translate at the top of draw.

    Re: "I can see no significant benefit in a one time transformation" --that is definitely fair, and we all agree that putting translate at the top of draw does work just fine. If in a future version of Processing "translate" worked in setup without being wiped it might be more intuitive to learners... but I have no idea. I was only curious because a common programming practice is not to set a variable 60 times per second if you only intend to set it once. Still, fussing about that one repeated assignment may be unnecessary micro-optimization. Even if there is an impact, I'm personally not trying to squeeze a few more milliseconds out of my Processing sketches.

  • P.S. re: GoToLoop's suggestion, just an aside to note that while translate() resets in the main sketch, PGraphics, and PImage -- PShape.translate() does not clear; it is cumulative. Not relevant in the general case, but perhaps interesting.

  • PGraphics, PImage and PShape are all offscreen graphics so there is no need to reset the transformation matrix. The draw() method is responsible for rendering the frame many times a second so it makes sense that it resets the matrix otherwise the user would have to do it.

  • edited September 2016

    Quark -- yes, it does make sense. Translate can get called in setup, but anything put there gets blown away when draw resets the matrix stack. Adding the ability to setup base matrix transforms that draw() doesn't autoclear might be unnecessarily complex to implement for no added functionality.

    A small correction: I believe PGraphics does also reset its matrix stack:

    • draw() -- translate() resets -- each loop
    • PGraphics -- translate() resets -- each endDraw()
    • PShape -- translate() does not reset

    See my example above.

Sign In or Register to comment.