Mapping a point a "sub" matrix to main/default matrix?

Hi,
Can anyone advise how to map a point, calculated within a matrix, to a point in the “main” (top-level) matrix?

More specifically, I have a situation where separate points are calculated within two separate matrices (one point in each matrix), and where I then want to draw a line from one point to the next. So, I think I need to have a realtionship between the points in the matrices and the matrix above them, but am unsure how to achieve that

For example

  1. First “sub” matrix: pushMatrix(), based on some inputs, translate/rotate etc, then calculate point A, draw a circle at point A, popMatrix()

  2. Second “sub” matrix: pushMatrix(), based on some other inputs, translate/rotate etc, then calculate point B, draw a circle at point B, popMatrix()

  3. From the CURRENT matrix (I.E. the default/main matrix we return to after popping previous matrix) draw a line from point A to point B - but how to map those points from the matrices in which they were calculated?

Thanks

1 Like

Hi,

Welcome to the community! :slight_smile:

I think that you can use PMatrix2D to store your “sub” matrix transforms so you can compute the coordinates of your two points A and B.

1 Like

H,

Thanks for the welcome and your answer.

I’ve no idea how to do that (yet!), but I’ll take a look @ PMatrix2D.

All the best…

The easiest way to do this without actually having to manipulate PMatrix objects is to use screenX and screenY.

First, pushMatrix, translate, rotate, etc, then store your first point expressed in the top-level coordinate space rather than local coordinates:

PVector p1 = new PVector(screenX(0,0), screenY(0,0));

Then push another matrix or three, do more transformations, and store your second point – again expressed in top-level coordinates.

PVector p2 = new PVector(screenX(0,0), screenY(0,0));

Now pop all your matrices and draw a line using those stored top-level coordinates.

line(p1.x, p1.y, p2.x, p2.y);

Here is an example sketch:

void draw() {
  noLoop();

  pushMatrix();
  translate(20,random(10,30));
  PVector p1 = new PVector(screenX(0,0), screenY(0,0));

  pushMatrix();
  rotate(random(QUARTER_PI, HALF_PI));
  translate(75, 0);
  PVector p2 = new PVector(screenX(0,0), screenY(0,0));
  
  popMatrix();
  popMatrix();
  
  println(p1);
  println(p2);
  line(p1.x, p1.y, p2.x, p2.y);
}

Whether this approach is useful depends a lot on drawing contexts – if your sketch is drawing in layers, etc. There could also be performance considerations to check for. But, broadly, you can store all kinds of coordinates from different translation / rotation contexts, then iterate over all those coordinates and render them in a second pass once you are done. This includes creating objects – it is a pretty flexible strategy.

One catch is that you need to either separately track your rotation each or retrieve it from the current stack if you want to use this strategy to create objects with orientation, as there is a screenX and screenY, but there isn’t anything like “screenR”.

For older discussions of accessing PMatrix, see also:

3 Likes

Hi, Jeremy

Thanks for the help. I’d actually managed to get it working with PMatrix2D, but this approach looks very interesting, so I will also give that a try - always good to know of other options.

Much appreciated!

2 Likes

Great to hear!

If you have any demo code from your solution that you would be willing to share, that might help future forum visitors searching for PMatrix.

1 Like

Good idea Jeremy (it had kind of crossed my mind to do that anyway).

I’m not a teacher by nature, so any comments/advice on clarity/ease-of-understanding woud be helpful.

Meanwhile, here’s an example of using PMatrix2D to map from within a matrix to screen points…

// This is an example of how to map from points calculated in one matrix to
// another matrix.  It is a simple visualisation of inputs from two IR
// distance sensors (although we're not going to read any sensors - we'll
// just hardcode the values, for now).
// Each "sensor" is positioned at an angle and represented by a blue rectangle
// The distance from each "sensor" to a detected object is represented by a
// green line drawn perpendicular to the "sensor", with length DISTANCE_A or
// DISTANCE_B
// The code is dumb and simply assumes whatever is being detected is a straight
// wall, which runs (at least) between the endpoints of the green "distance"
// lines.
// The detected "wall" is represented by a red line drawn between those 2
// endpoints.
// Each "sensor" and "distance" line is drawn within its own matrix. The
// reason for that is it's easier to translate and rotate the canvas, than
// to mess around with trigonometry that would otherwise be involved due to
// the sensors being at an angle to each other - ANGLE_A and ANGLE B
// (note these are in mirrored positions - hence a positive angle and a
// negative angle).
//
// Although use of transform/rotate makes it easier to code the drawing of
// "sensors" and "distance" lines, there is a challenge in drawing the red
// "wall" line between the ends of the two "distance" lines - 
//   line(x1, y1, x2, y2).
//
// Can you specify a line's start point in one matrix, change matrix, then
// specify its end point? NO!
// (you must specify the start and end points in a SINGLE command)
//
// Fortunately we can use getMatrix() and multX()/multY() to map our endpoint
// to the screen....

// ONCE YOU GET THIS WORKING, TRY CHANGING THE VALUES FOR THE FOLLOWING
// - keep the values sensible ;-)
//     ANGLE_A
//     ANGLE_B
//     DISTANCE_A
//     DISTANCE_B
// (the red "wall" line should always join the ends of the 2 "distance" lines)

// Rectangle (sensor) size
int SENSOR_WIDTH = 50;
int SENSOR_HEIGHT = 20;
// Mid-point along rectangle (sensor) edge
int HALF_SENSOR_WIDTH = SENSOR_WIDTH / 2;

// Half the distance between the two "sensors"
int SENSOR_SEPARATION = 100;

// Sensor A angle
int ANGLE_A = -55;
// Sensor A distance (hardcoded for this example - no real input, here)
int DISTANCE_A = 450;

// Sensor B angle
int ANGLE_B = 55;
// Sensor B distance (hardcoded for this example - no real input, here)
int DISTANCE_B = 350;

void setup() {
  PMatrix2D currMatrix;
  float x1, y1, x2, y2;

  // Set our canvas size and background colour (white) 
  size(800, 500);
  background(255);

 // Save initial matrix
  pushMatrix();
  
  // Create a common offset in which to draw each "sensor" & "distance"
  translate(100, 250);
  
  //////////////////////////////////////////////////////////////////////
  // "SENSOR" A ////////////////////////////////////////////////////////
  pushMatrix(); // Start a new matrix just for this "sensor"
  // Move origin UP 100
  translate(0,-SENSOR_SEPARATION);
  // Rotate canvas to correct angle for this sensor
  rotate(radians(ANGLE_A));
  // Draw the "sensor"
  stroke(0);
  fill(0, 0, 255);
  rect(0, 0, SENSOR_WIDTH,SENSOR_HEIGHT);
  // Draw the "distance" line for this sensor
  stroke(0,255,0);
  line(HALF_SENSOR_WIDTH, SENSOR_HEIGHT, HALF_SENSOR_WIDTH, DISTANCE_A);
  // Get the current matrix and use it to map the end of the "distance" line
  // to a screen/top-level matrix point - using multX & multY on the endpoint
  currMatrix = (PMatrix2D)getMatrix();
  x1 = currMatrix.multX(HALF_SENSOR_WIDTH, DISTANCE_A);
  y1 = currMatrix.multY(HALF_SENSOR_WIDTH, DISTANCE_A);
  // Label it "A"
  fill(255, 255, 255);
  text("A", HALF_SENSOR_WIDTH - 5, SENSOR_HEIGHT - 5);
  popMatrix(); // Finsh with this matrix
  
  //////////////////////////////////////////////////////////////////////
  // "SENSOR" B ////////////////////////////////////////////////////////
  pushMatrix(); // Start a new matrix just for this "sensor"
  // Move origin DOWN 100
  translate(0,SENSOR_SEPARATION);
  // Rotate canvas to correct angle for this sensor
  rotate(radians(ANGLE_B));
  // Draw the "sensor"
  stroke(0);
  fill(0, 0, 255);
  rect(0,-SENSOR_HEIGHT, SENSOR_WIDTH, SENSOR_HEIGHT);
  // Draw the "distance" line for this sensor
  stroke(0,255,0);
  line(HALF_SENSOR_WIDTH, -SENSOR_HEIGHT, HALF_SENSOR_WIDTH, -DISTANCE_B);
  // Get the current matrix and use it to map the end of the "distance" line
  // to a screen/top-level matrix point - using multX & multY on the endpoint
  currMatrix = (PMatrix2D)getMatrix();
  x2 = currMatrix.multX(HALF_SENSOR_WIDTH, -DISTANCE_B);
  y2 = currMatrix.multY(HALF_SENSOR_WIDTH, -DISTANCE_B);
  // Label it "B"
  fill(255, 255, 255);
  text("B", HALF_SENSOR_WIDTH - 5, -SENSOR_HEIGHT + 10);
  popMatrix(); // Finsh with this matrix

  //////////////////////////////////////////////////////////////////////
  // "WALL" (line between the endpoints of the two "distance" lines ////
  // x1,y1 was mapped from "sensor" A's matrix
  // x2,y2 was mapped from "sensor" B's matrix
  // Thanks to PMatrix2D & multX/multY those points are now screen points
  popMatrix(); // Back to normal [undoes the initial translate(100, 250) ]
  fill(0, 0, 255);
  text(String.format("A: dist=%d\nmatrix-point=(%d, %d)\nmapped-point=(%d, %d)",
    DISTANCE_A, HALF_SENSOR_WIDTH, DISTANCE_A, (int)x1, (int)y1), x1 + 5, y1 + 5);
  text(String.format("B: dist=%d\nmatrix-point=(%d, %d)\nmapped-point=(%d, %d)",
    DISTANCE_B, HALF_SENSOR_WIDTH, -DISTANCE_B, (int)x2, (int)y2), x2 + 5, y2 + 5);
  stroke(255,0,0);
  line(x1, y1, x2, y2);
}
2 Likes