How to access actual vertex coordinates for transformed PShape in a Group

I’ve got this PShape Group composed of four sided polygons which have been arranged in a circle and I’m trying to access their coordinates so I can check if the mouse is over each one individually. The problem is that each polygon shows the same coordinates for its vertices.

How can I get the coordinates of each polygon’s vertices “as displayed” when they have been translated in this way when the Group is created?

Edit: I’m not asking for advice about how to check for mouse over, just how to obtain the correct locations of the shape vertices after translation.

the println() statement in setup() outputs:

wheelGroup child: 0 Vertex: 0 [ 230.0, 0.0, 0.0 ]
wheelGroup child: 0 Vertex: 1 [ 199.18584, 115.0, 0.0 ]
wheelGroup child: 0 Vertex: 2 [ 112.5833, 65.0, 0.0 ]
wheelGroup child: 0 Vertex: 3 [ 130.0, 0.0, 0.0 ]
wheelGroup child: 1 Vertex: 0 [ 230.0, 0.0, 0.0 ]
wheelGroup child: 1 Vertex: 1 [ 199.18584, 115.0, 0.0 ]
wheelGroup child: 1 Vertex: 2 [ 112.5833, 65.0, 0.0 ]
wheelGroup child: 1 Vertex: 3 [ 130.0, 0.0, 0.0 ]

… etc.

Wheel wheel;
float speed;
color[] RYB = { #FE2712, #FC600A, #FB9902, #FCCC1A, 
  #FEFE33, #B2D732, #66B032, #347C98, 
  #0247FE, #4424D6, #8601AF, #C21460 };
void setup() {
  size(800, 480);
  wheel = new Wheel(new PVector(width/2, height/2), 460, 260, 12);
  speed = 200;
  for (int s = 0; s < wheel.wheelGroup.getChildCount(); s++) {
    for (int n = 0; n < wheel.segments[s].getVertexCount(); n++) {
      println("wheelGroup child:", s, "Vertex:", n, wheel.segments[s].getVertex(n));
    }
  }
}
void draw() {
  background(50);
  wheel.display();
}
class Wheel {
  PShape wheelGroup;
  PShape[] segments;
  PVector location;
  float oD, iD, angle;
  int nSides;
  Wheel(PVector location, float oD, float iD, int nSides) {
    segments = new PShape[nSides];
    wheelGroup = createShape(GROUP);
    this.location = location;
    angle = TWO_PI / nSides;
    this.oD = oD;
    this.iD = iD;
    this.nSides = nSides;

    createWheel();
  }
  void display() {
    pushMatrix();
    translate(location.x, location.y);
    //rotate(frameCount / speed);
    shape(wheelGroup);
    popMatrix();
  }
  void createWheel() {
    for (int n = 0; n < nSides; n++) {
      segments[n] = createSegment(n);
      segments[n].rotate(TWO_PI / nSides * n);
      wheelGroup.addChild(segments[n]);
    }
  }
  PShape createSegment(int n) {
    PShape segment;
    segment = createShape();
    segment.beginShape();
    for (float a = 0; a <= angle; a += angle) {
      float outx = 0 + cos(a) * oD/2;
      float outy = 0 + sin(a) * oD/2;
      segment.vertex(outx, outy);
    }
    for (float b = angle; b >= 0; b -= angle) {
      float inx = 0 + cos(b) * iD/2;
      float iny = 0 + sin(b) * iD/2;
      segment.vertex(inx, iny);
    }
    segment.endShape(CLOSE);
    segment.setStroke(RYB[n]);
    segment.setFill(RYB[n]);

    return segment;
  }
}

I’m having trouble understanding how this works.

  1. createWheel() calls createSegment() to make a PShape, translates it, and adds it to the group.
  2. When shape(wheelGroup) is drawn, all the children are displayed in their translated positions.
  3. When the child is asked for it’s vertex coordinates, it shows the original ones from createSegment()

So I guess the translations are stored in the wheelGroup somehow, and the translation is not actually applied to the segment object, but it’s really unclear to me what’s actually happening. I don’t see how shape(wheelGroup) knows how to arrange all the children, if querying them individually doesn’t reflect the translation that was applied when they were created.

I also tried accessing them this way:

  for (int i = 0; i < wheel.wheelGroup.getChildCount(); i++) {
    int child = i;
    for (int j = 0; j < wheel.wheelGroup.getChild(child).getVertexCount(); j++) {
      int vertex = j;
      println("Child", child, "vertex", vertex, "coordinates", 
         wheel.wheelGroup.getChild(child).getVertex(vertex));
    }
  }

… but it gives the same vertex coordinates as asking for wheel.segments[s].getVertex(n).

Is there some kind of inaccessible magic that happens in createShape(GROUP) so it remembers the translations for when it comes time to call shape(wheelGroup)?

Hi. Each PShape contains a transformation matrix, which is a way to store its translations, rotations and scaling. I didn’t check, but probably this matrix is sent to the GPU at the time of rendering to transform all the subsequent vertices you send for rendering. Here you can see all the methods from PShape:
https://processing.github.io/processing-javadocs/core/processing/core/PShape.html

I see methods like translate, rotate, scale and resetMatrix. Unfortunately I don’t see any way to get that matrix, nor screenX, screenY, etc which could give you the screen coordinates for the vertices.

I wonder if you can still use methods like the global screenX, screenY and screenZ to get the screen space coordinates of those points. I’m skeptical, because the transformations are stored in the matrices inside the PShapes, and maybe those transforms are not currently active to influence how screenX etc work.

One workaround would be to keep track yourself of the transformations you apply to your PShapes (store the rotation & translation values somewhere), then you could call rotate(), translate() etc using those values to influence the values produced by screenX…Z to get the right screen coordinates.

Have you used screenX…Z before? OpenFrameworks does have an object called ofNode with methods like getGlobalPosition, getGlobalOrientation, getGlobalTransformMatrix which enable what you need. Maybe you need to build yourself the equivalents in Processing and can look there for inspiration.

That seems like a good solution. Or find a way to create the shape in the location I want it in the first place, which won’t be as simple as I hoped.

No, haven’t heard of that before. I’m still pretty new to this, and to be honest I can’t really say I fully understand how sin/cos work, so I guess I’ll be reading up on trig pretty soon.

Here a small example showing how one can use screenX and screenY:

void setup() {
  size(600, 600, P3D);
  noFill();
}
void draw() {
  background(255);
  pushMatrix();
  translate(width/2, height/2);
  rotateY(frameCount * 0.01);
  box(200);
  float x = screenX(100, 100, 100);
  float y = screenY(100, 100, 100);
  popMatrix();
  
  ellipse(x, y, 50, 50);
}

2018-11-15-101028_600x600_scrot

I know that one corner of the cube is at 100, 100, 100, so I get the screen position of that rotating corner to draw the circle.