PGraphics and layering behavior

edited November 2013 in Questions about Code

Hello, In lieu of the layers library not being quite up and running for Processing2.0, I thought I'd give implementing a simple layering mechanism a try. Of course, much easier said than done. The sketch below is meant to have two layers where boxes and circles can be written to in the same order (boxes always on top of layers). While the result is kind of working, it just looks redundant, and I'm sure not efficient at all. Not only that, but I can't get the layers to erase when I invoke the clear() method. Additionally, nothing draws when I set noLoop() which I'd like to have set up by default.

Ideally, I would like to define a layer class that is an extension of PGraphics, so I can use custom objects, but at this point, that is a little beyond me.

Could someone take a look at this code and give me any pointers? Thanks in advance.

    spot s; // declare object spot
    box b; // declare object box
    PGraphics layer_spots; // layer for spots
    PGraphics layer_boxes; // layer for boxes
    void setup() {
      size (200, 200);
      background (50);
      //noLoop();

      layer_spots = createGraphics (width, height);
      layer_boxes = createGraphics (width, height);

// is there a way to set the layer order here so I don't have to set it up twice for each object below?

    } // end of setup()

    void draw() {
    } // end of draw()

    void keyPressed() {
      if (key == 'x') { 
        println ("...pressed clear!");
        layer_spots.clear();
        layer_boxes.clear();
      }
      if (key == 'b') { 
        println ("...make box!");

        layer_spots.beginDraw();  
        layer_spots.endDraw();

        layer_boxes.beginDraw();
        b = new box (random(width), random(height), int (15 + random(25)), int (random(255)) );
        b.display(layer_boxes);
        layer_boxes.endDraw();

        image(layer_spots, 0, 0);
        image(layer_boxes, 0, 0);
      }
        if (key == 's') { 
        println ("...make spot!");
        layer_spots.beginDraw();
        s = new spot (random(width), random(height), int (15 + random(25)), int (random(255)) );
        s.display(layer_spots);
        layer_spots.endDraw();

        layer_boxes.beginDraw();
        layer_boxes.endDraw();

        image(layer_spots, 0, 0);
        image(layer_boxes, 0, 0);
      }
    } // end of keyPressed()

    //////////////classes

    class box {
      float x;
      float y;
      float side;
      int value;

      // constructor
      box (float xp, float yp, float w,int v){
        x = xp;
        y = yp;
        side = w;
        value = v;
      } // end of constructor

      void display(PGraphics pg){
        pg.fill (value);
        pg.rect (x,y,side,side); 
      }
    } // end of box

    class spot {
      float x;
      float y;
      float diameter;
      int value;

      // constructor
      spot (float xp, float yp, float diam,int v){
        x = xp;
        y = yp;
        diameter = diam;
        value = v;
      } // end of constructor

      void display(PGraphics pg){
        pg.fill (value);
        pg.ellipse (x,y,diameter,diameter); 
      }
    } // end of spot

Answers

  • "While the result is kind of working, it just looks redundant, and I'm sure not efficient at all."

    What looks redundant and inefficient?

    "Not only that, but I can't get the layers to erase when I invoke the clear() method._"

    You erase the layers, but not the drawing surface...

    "Additionally, nothing draws when I set noLoop() which I'd like to have set up by default."

    Why set it by default? You have to invoke redraw() to update the screen.

    Updated sketch (without the unchanged classes):

    spot s; // declare object spot
    box b; // declare object box
    PGraphics layer_spots; // layer for spots
    PGraphics layer_boxes; // layer for boxes
    
    void setup() {
      size(200, 200);
      //noLoop();
    
      layer_spots = createGraphics(width, height);
      layer_spots.beginDraw(); layer_spots.endDraw();
      layer_boxes = createGraphics(width, height);
      layer_boxes.beginDraw(); layer_boxes.endDraw();
    } // end of setup()
    
    void draw() {
      background(250);
    
      image(layer_spots, 0, 0);
      image(layer_boxes, 0, 0);
    } // end of draw()
    
    void keyPressed() {
      if (key == 'x') {
        println ("...pressed clear!");
    
        layer_spots.beginDraw(); 
        layer_spots.clear();
        layer_spots.endDraw();
        layer_boxes.beginDraw();
        layer_boxes.clear();
        layer_boxes.endDraw();
      }
    
      if (key == 'b') {
        println ("...make box!");
    
        layer_boxes.beginDraw();
        b = new box(random(width), random(height), int(15 + random(25)), int(random(255)) );
        b.display(layer_boxes);
        layer_boxes.endDraw();
      }
      if (key == 's') {
        println("...make spot!");
    
        layer_spots.beginDraw();
        s = new spot(random(width), random(height), int(15 + random(25)), int(random(255)) );
        s.display(layer_spots);
        layer_spots.endDraw();
      }
    } // end of keyPressed()
    
  • Thanks PhilHo, this is fantastic.

    I thought my code was redundant, and inefficient, because the way I had written it before, I had to redraw both layers whether i was inserting a box or a spot. But seeing the layer order defined just once, as you did was what I was looking for:

    void draw() {
      frame.setTitle(int(frameRate) + " fps");
      background(250);
      image(layer_spots, 0, 0);
      image(layer_boxes, 0, 0);
    } // end of draw()
    

    Thanks for pointing out that if I setup my sketch with noLoop, I have to invoke redraw() whenever I make a change. That's what I was looking for.

    Finally, I see how you got the code to clear the screen to work, but I still don't quite understand why you have to call beginDraw() and endDraw() before and after.

    layer_spots.beginDraw(); 
    layer_spots.clear();
    layer_spots.endDraw();
    layer_boxes.beginDraw();
    layer_boxes.clear();
    layer_boxes.endDraw();
    

    The bit of code below from the PGraphics documentation calls the clear() function successfully without having to call beginDraw()/endDraw() before and after. Why does it work there, but not in my code?

    PGraphics pg;
    
    void setup() {
      size(200, 200);
      pg = createGraphics(100, 100);
    }
    
    void draw() {
      background(204);
      pg.beginDraw();
      pg.stroke(0, 102, 153);
      pg.line(0, 0, mouseX, mouseY);
      pg.endDraw();
      image(pg, 50, 50); 
    }
    
    // Click to clear the PGraphics object
    void mousePressed() {
      pg.clear();
    }
    

    Thanks for your insights

  • edited November 2013 Answer ✓

    In my short experience, beginDraw() is needed once only, before starting using it.
    But endDraw() is always necessary to get updated result ASAP! 3:-O
    That is, until endDraw() is issued, any modifications to it stay dormant! (~~)

  • Answer ✓

    clear() actually calls background(), which is a drawing operation like another, so it need to be withing begin/endDraw().

    I first tried without and got an error, so I added it, it is as simple as that.

    Even if it can work without, better stick to best practice to call them: some drawing surfaces (PGraphics implementations, like Java2D, OpenGL, PDF...) might not need it, but for others it can be mandatory.

  • Thank you guys. Most illuminating

Sign In or Register to comment.