Passing Graphic variable to classes that execute other classes

edited December 2017 in Questions about Code

I’d like to ask you which do you think it’s the best way to do what i’ll explain you in OOP.

The idea is simple: i’ll have a simple class that will draw something very simple, receiving two points, for example.

And i’ll have other classes that will draw something using the first class.

In the main sketch there will be a continuous call to draw everything.

And i’d like it to be drawn in a graphic.

I’m thinking about passing a graphic variable to all the classes… but I think it’s not the best way.

For example:

//class to draw a simple thing, like a line or a curve or whatever

class LineExample { 

  PVector point1, point2;

  PGraphics im;

  LineExample (PGraphics imO, PVector point1O, PVector point2O){
    im = imO;
    point1 = point1O;
    point2 = point2O;
  }

  void show() { 
    // a simple example
     im.beginDraw();
       line(point1.x,point1.y,point2.x,point2.y);
       line(point1.x+10,point1.y,point2.x+10,point2.y);
     im.endDraw();
  } 
} 

// class that uses the first class to make a more complex drawing

class Something{ 

  PVector pointStart;
  PVector pointEnd;

  LineExample liExample ;
  PGraphics im;

  Something (PGraphics imO, PVector pointStartO, PVector pointEndO){

    im = imO;
    pointStart = pointStartO;
    pointEnd = pointEndO;



    liExample = new LineExample(im, pointStart, pointEnd);
  }


  void show() { 
     liExample.show();
  } 
} 


// main sketch


PGraphics im;

Something something;
PVector start1;
PVector end1;

void setup(){
  size(500, 500, P3D);
  smooth();
  im = createGraphics(1000,1000,P3D);
  start1 = new PVector(10,10);
end1 = new PVector(50,50);
something = new Something(im, start1, end1);
}

void draw(){

  background(0);
  stroke(255);                

    something.show();

  image(im,0,0,500,500);
}

I think it’s not a very elegant way.

How could do it in a more clever way?

Is there any way to avoid passing the graphic variable?

Thank you very much

Answers

  • And you don't want to just draw everything in the main sketch window?

  • The idea is to draw a more complex drawing and save it in a tiff file, so I thought about divide it in classes and just show it in the main screen with image(im,0,0,500,500);

    Am i really wrong?

  • It’s OOP not poo

  • Are you going to have more than one Something object? If not there is no need to have a class for it. Same for the LineExample class.

    If you want to combine the line image with the output of the Something class then it needs to have a transparent background.

    The show method is badly named because it doesn't show anything, better call it something like update.

    It seems wasteful for each object to have its own PGraphics buffer just so you can save it off as a TIFF file.

  • Yes, I'll need several 'something' objects, and some other objects that will need to use lots of LineExample objects.

    I see that i'll need to change the show method, yes! thanks!

    but i don't really understand what you say about the transparent background

    And i do mot understand what you explain aboutbthe PGraphics... this is what i can not see clearly... although my version it works, i do not think it is well coded...

    Thank you

  • edited December 2017

    Are you planning to create 3D drawings? If not then use P2D.

    Yes, I'll need several 'something' objects, and some other objects that will need to use lots of LineExample objects.

    Your Something class can only have one LineExample object, is that what you want or do you want it to have several LineExample objects.

    but i don't really understand what you say about the transparent background

    If the Something class is going to have several LineExample objects then you need to layer the LineExample images. If they don't have clear backgrounds then you will only see the last one drawn.

    And i do mot understand what you explain about the PGraphics...

    Consider your statement

    im = createGraphics(1000,1000,P3D);

    This will create an offscreen buffer of 1000x1000 pixels and each pixel requires 4 bytes. So a single LineExample object requires ~4MB.

  • My code here has only been a simple example, but yes, the idea will be, probably, to draw 3d (but if it's not possible i'll do it 2d).

    I mean, the abstract is that my idea was to create a graphic and to store it in an external file (tiff, png... or pdf).

    The drawing may be different made by differnt elements (drawings) which may be made by different elements.

    So imagine my LineExample like a class that only draws a very simple drawing (a line, a curve, or whatever) and imagine Something class as a class that draw a lot of LineExample to create an element.

    Consider that I only have one im graphic.

    And the im graphic element will be the "canvas" where to draw all the elements.

    So I'm sure it's not the best way... but... I do not know how to approach it

  • Consider that I only have one im graphic.

    Interesting, have you looked at the get() method. It returns the current frame as a PImage which can then be stored as png, jpg ...

  • I was wondeing if i'm doing it in the best way... i mean, all the code to perform the idea...

    Another think is that the PGraphic may be bigger that canvas...

  • edited December 2017

    Skip the PGraphics and just draw normally

    Then use get() to get the content as image as quark said

    Since you are in 3D, you can just move the camera away from the scene a bit, so the entire scene is visible

    Enough theory, start with doing it

    There‘ll be enough obstacles on the way...

  • I'll try it with get(), then... thank you!

  • but with get() the image can't be bigger than canvas...?

  • get() will get you the current PImage of the associated PGraphics. The main sketch is a PGraphics itself. Calling get() by itself gives you a copy of the current drawing buffer. If you are using another buffer, as for example:

    PGraphics secondBuffer=createGraphics(width*2,height,JAVA2D);

    then secondBuffer.get() will return a copy of this buffer. Notice this buffer is bigger than the current canvas.

    I have not followed all these posts but I think what you could do is draw everything on a second buffer and then get() a section of that buffer. For instance:

    secondBuffer.get(0,0,width/2,height/2); Gets you one quarter of your image store in secondBuffer. As a matter of fact, there are previous post in the forum related to using bigger PGraphics than the current canvas. Use the keyword panning.

    Kf

  • Interesting... but... if I write everything on a second buffer, isn't it the same initial idea I tried to explain when I wrote everything in a PGraphics variable?

  • It depends on what you want to do... I was addressing your last question, in case you were drawing something bigger than your current sketch.

    It was also suggested you draw everything in the sketch. This is also an option: You just draw what fits in the sketch and you update in each new draw cycle.

    Thus, you have two options. However, notice this post does not address your initial question....

    Kf

  • Yes, exactly my question was how you can see the best way to carry out the idea.

    Maybe I have not expressed myself clearly.

    The idea is to make a drawing that I want that can be stored in a file (tiff, png, pdf ...). I have created a PGraphics.

    This drawing can be large (1000x1000,5000x5000 ...), although the canvas is not (500x500 ...)

    My way of doing it has been to create a class that makes a very small part of the drawing (a line or curve or whatever ...)

    Then I created different classes that create drawings calling the first class different times.

    And from the main sketch it is updated each time.

    This requires me to send the PGraphics as a parameter from the main sketch to the classes that create drawings, which, in turn, send the PGraphics as a parameter to the class that makes the basic drawing.

    That's how it works, but it does not seem very elegant ...

    (you can see the basic code in my first message)

  • Let us specify some terms to simplify the discussion

    buffer - the large graphic image we want to draw all our smaller images on and can be saved off as a TIFF, png or whatever.
    primitives - the smaller graphic shapes to be drawn on the buffer.

    So the basic idea of passing the buffer between primitives (e.g. lines, curves etc) is straightforward enough but before we do that there are 2 issues that need resolving.

    1) The primitives, do we specify the primitives vertices etc
    a) using the buffer's global XY axis or
    b) using local coordinates and translate the values when drawing onto the buffer.

    2) The data structure needed to hold the primitives. I suggest a tree data structure because you want some primitives to comprise of other primitives.

    My personal preference would be 1a and a tree data structure as it far more flexible and extensible approach, especially if you decide to animate the shapes.

  • Since you are in 3D, you can just move the camera away from the scene a bit, so the entire scene is visible

  • Or you could use a 3D buffer and copy part of the graphic to a 2D sketch window.

  • I'll investigate... I can't see it clear enough... thank you!

  • The sketch below demonstrates what I mean using 2D graphics. I suggest you try experimenting with it and then come back here with any questions.

    Type the 's' key to save the buffer image.

    import java.util.*;
    
    PGraphics buffer;
    List<BaseShape> shapes = new ArrayList<BaseShape>();
    Face face0, face1;
    
    public void setup() {
      size(400, 300, P2D);
      buffer = createGraphics(1000, 1000, P2D);
      face0 = new Face();
      face0.moveTo(200, 100);
      shapes.add(face0);
      // face1 will not be visible on the screen but will 
      // be on the saved buffer
      face1 = new Face();
      face1.moveTo(600, 600);
      shapes.add(face1);
    }
    
    public void draw() {
      background(0);
      for (BaseShape shape : shapes) {
        shape.display(g);
      }
    }
    
    /**
     * Draw and save the graphic buffer on demand. This is executed 
     * when the 's' key is typed.
     * There is no need to update the buffer image except when saving
     * the buffer to disk.
     */
    public void saveGraphic(String filename, PGraphics graphic) {
      graphic.beginDraw();
      // Only use one to the two following lines
      graphic.background(0, 96, 0); // Use this line for a green background
      //graphic.clear(); // Use this line for a transparent background
      for (BaseShape shape : shapes) {
        shape.display(graphic);
      }
      graphic.endDraw();
      graphic.save(filename);
    }
    
    public void keyTyped() {
      if (key == 's') {
        saveGraphic("shapes " + millis() + ".png", buffer);
      }
    }
    
    /**
     * This is the base class for ALL shapes. Any primitive or composite shape
     * must extend this class.
     * The main attribute is a list of 'primitive' shapes (e.g. Line, Ellipse etc)
     * used to make a 'composite' shape (e.g. Face). Note that a composite shape 
     * can have other composite shapes as children. For instance a Body shape 
     * could have a Face shape as one of the children.
     * Note the attribute 'pos' defines the position of the shape with respect to
     * its parent shape or the buffer coordinates if it has no parent. 
     * 
     *
     */
    class BaseShape {
      List<BaseShape> children = new ArrayList<BaseShape>();
    
      // Position in global image coordinates
      PVector pos = new PVector();
    
      void display(PGraphics pg) {
        pg.pushMatrix();
        pg.translate(pos.x, pos.y);
        for (BaseShape shape : children) {
          shape.display(pg);
        }
        pg.popMatrix();
      };
    
      void addShape(BaseShape shape) {
        children.add(shape);
      }
    
      void moveTo(float x, float y) {
        pos.x = x;
        pos.y = y;
      }
    }
    
    /**
     * A 'primitive' shape representing a line.
     */
    class Line extends BaseShape {
      // Vertices in local coordinates
      PVector p0 = new PVector();
      PVector p1 = new PVector();
    
      Line(float x0, float y0, float x1, float y1) {
        p0.x = x0;
        p0.y = y0;
        p1.x = x1;
        p1.y = y1;
      }
    
      void display(PGraphics pg) {
        pg.pushMatrix();
        pg.translate(pos.x, pos.y);
        pg.stroke(255, 255, 0); // Yellow
        pg.strokeWeight(5);
        pg.line(p0.x, p0.y, p1.x, p1.y);
        pg.popMatrix();      
        // Now draw any children
        for (BaseShape shape : children) {
          shape.display(pg);
        }
      }
    }
    
    /**
     * A 'primitive' shape representing an ellipse.
     */
    class Ellipse extends BaseShape {
      // Vertices in local coordinates
      PVector c = new PVector();
      float w, h;
    
      Ellipse(float x0, float y0, float w, float h) {
        c.x = x0;
        c.y = y0;
        this.w = w;
        this.h = h;
      }
    
      void display(PGraphics pg) {
        pg.pushMatrix();
        pg.translate(pos.x, pos.y);
        pg.stroke(100, 100, 255); // Yellow
        pg.strokeWeight(5);
        pg.fill(100, 100, 255, 128); // Yellow
        pg.ellipse(c.x, c.y, w, h);
        pg.popMatrix();  
        // Now draw any children
        for (BaseShape shape : children) {
          shape.display(pg);
        }
      }
    }
    
    /**
     * A composite shape created from Lines and Ellipses to 
     * represent a face.
     */
    class Face extends BaseShape {
    
      Face() {
        // Nose
        children.add(new Line(-40, 100, 40, 100));
        children.add(new Line(0, 0, -40, 100));
        children.add(new Line(0, 0, 40, 100));
        // Eyes
        children.add(new Ellipse( -60, -10, 80, 80));
        children.add(new Ellipse( 60, -10, 80, 80));
        // Mouth
        children.add(new Ellipse( 0, 140, 150, 40));
      }
    }
    
  • this is amazing! definitely i'll need to study it slowly... my idea was so much simple, this example is really interesting!

Sign In or Register to comment.