How to implement an hierarchy of views wherein each view restricts drawing to its assigned rectangle

edited February 2018 in How To...

I'm using the term view in roughly the same manner as Apple's NSView (I've had a bit of unsuccessful experience in iOS/macOS programming). A view is a thing which has a position and size and which can draw itself when instructed to do so, as well as hold a list of child views whose position is relative to the containing view.

I have views drawing themselves in the correct place with no problems, thanks to push/popMatrix() and translate(). The present challenge is to make sure that each view does not permit its own drawing commands and those of its child views to spill outside the view's rectangle.

The problem is that calls to clip() do not stack. A view with width/height 100 will fail to clip a child view with width/height 200 because the child view will call clip(0, 0, 200, 200) to make sure its own drawing commands don't fall outside its bounds.

The following diagram describes desired behavior. The grey areas are where a view's content should be drawn. White areas should not be drawn.

What is particularly notable is that the right-hand grandchild's view is constrained by both its parent and grandparent.

Can someone sketch for me a solution to this? I'm guessing I will need a given view to recurse upwards (or downwards?) through the transform matrix stack, but I don't know how transform matrices work.

Thanks in advance!

Answers

  • Why is the center of the Parent view not drawn?

  • Not sure why you'd need it. Do you mean you want to see which point I'm using as the origin for each view? I've been using rectMode(CORNER) for everything.

  • Answer ✓

    Proof of concept:

    class View {
      float x,y;
      PGraphics pg;
      ArrayList<View> children;
      color c;
      View() {
        x = 0;
        y = 0;
        pg = createGraphics(200,200);
        children = new ArrayList();
        c = color( random(255), random(255), random(255) );
      }
      void addChild() {
        children.add( new View() );
        children.get(children.size()-1).x = 50;
        children.get(children.size()-1).y = 50;
      }
      void prep(){
        for( int i = 0; i < children.size(); i++ ){
          children.get(i).prep();
        }
        pg.beginDraw();
        pg.background(c);
        for( int i = 0; i < children.size(); i++ ){
          pg.image(children.get(i).pg.get(), children.get(i).x, children.get(i).y ); 
        }
        pg.endDraw();
      }
    }
    
    View topView;
    
    void setup(){
      size(600,400);
      topView = new View();
      topView.addChild();
    }
    
    void draw(){
      background(0);
      topView.prep();
      image(topView.pg.get(), 0, 0);
    }
    
  • So to check that I understand this code, let me now explain how it works. Please let me know if I have made any errors.

    Basically, each view gets its own canvas to draw on. That's what each PGraphics is. Because each canvas is its own object, with its own size, all drawing commands can be made without worrying about drawing outside a View's bounds, and without using the transform matrix stack to figure out where things should be.

    The prep() method is called recursively on every Viewin the hierarchy. For each view, after each child view has finished drawing to its own canvas, that child view's resulting pixels are copied and pasted onto the canvas for the parent view, at the appropriate (x, y) coordinate.

    Is this correct?

  • edited February 2018

    Yes.

    The trickiest thing about this is that the child is storing the x and y positions that the parent should use to position the child's resulting image. This isn't easily intuitive.

    You may wish to add a means to modify a View's size and offsets.

    A diagram like the one you have posted is possible, but you will have to position the Views properly. You might consider creating a set of views from a JSON data structure, perhaps one loaded from a file.

Sign In or Register to comment.