Collision detection doesn't work when using translate() and rotate() - p5.collide2d

@feelthemall

I recently made a “reverse” screenX / Y for processing in this thread. I did try to convert it to p5.js, but was unsuccessful in my first attempts (I really don’t get js class quirky syntax. And now the code link is gone in the first post) You’re welcome to try and convert it if you like.

Whether or not it works might still depend on how you do the collision detection I guess.

Slightly changed version here (processing sketch), demo:

/* Reverse matrix transformation tests


  2019.05.05 raron
  
  TODO:
   Shear X / Y
   Scale
*/

// mouse clicks make a test point that follows the following matrix "level"
final int testPointAtLevel = 2; // zero-based

// Number of Matrix transforms (push/pop "levels")
final int levels = 3;

// Object to keep track of matrix transforms
MatrixTracker2D matrix = new MatrixTracker2D();

// Test figure, to show matrix transforms
TestFigure figureTest[] = new TestFigure[levels];


// Just the test point initial coordinates
float tpx = 15;
float tpy = 20;

 
void setup() {
  // test figures
  figureTest[0] = new TestFigure( 300, 300,   0, 0,  50, 100, 0, 0.01);
  figureTest[1] = new TestFigure( 200,   0,   0, 0,  25,  50, 0, 0.02);
  figureTest[2] = new TestFigure(  50,  50, -10, 20, 25,  10, 0, 0.015);
  //figureTest[3] = new TestFigure(  25,  -25,  20, 10, 0, 0.03);

  size(600, 600);
  ellipseMode(RADIUS);
}

 
void draw() {
  background(128);
  fill(255);


  // Testing a few matrix push/pop levels
  for(int i=0; i<levels; i++) {
    stroke(0);
    strokeWeight(1);
    
    // track a new matrix (save old)
    matrix.push();
    
    // transform new current matrix
    matrix.move(figureTest[i].x, figureTest[i].y);
    matrix.turn(figureTest[i].ang);

    // Indicate a matrix "level" with a figure
    figureTest[i].display();
    figureTest[i].update();
  
    // Test point that follows a figure (matrix)
    if (testPointAtLevel == i) testPoint();
    
    // Unrelated test: Ellipsoid line towards mouse pointer
    if (mousePressed) testLine(i);
    //printMatrixInfo(1);
    
    
  }
 
  // remove old matrix transforms before new frame
  for (int i=0; i<levels; i++) {
    matrix.pop();
  }
}


void printMatrixInfo(int i) {
  println(matrix.txList.get(i), matrix.tyList.get(i), matrix.angList.get(i));
}



// Mouse click makes a point that follows current "level" of matrix transforms
void testPoint() {
    if (mousePressed) {
      tpx = matrix.posX(mouseX, mouseY);  // reverse screenX
      tpy = matrix.posY(mouseX, mouseY);  // reverse screenY
    }
    strokeWeight(3);
    stroke(#FF0000);
    point(tpx, tpy);
}


// A line as from center of an ellipse towards the mouse pointer (just because)
void testLine(int i) {
  matrix.push();
  matrix.move(figureTest[i].xt, figureTest[i].yt);  // testing off-center ellipses

  // Turn towards mouse pointer
  float mAng = matrix.getAngle(mouseX, mouseY, 0, 0);
  matrix.turn(mAng);
  
  // Ellipse radius at angle mAng (pointing at mouse pointer)
  float ra = figureTest[i].ra;
  float rb = figureTest[i].rb;
  float radiusAtAngle = (ra*rb)/sqrt(pow(ra*sin(mAng),2) + pow(rb*cos(mAng),2));
  
  strokeWeight(3);
  stroke(#FF0000);       // red line inside ellipse
  line(0,0,radiusAtAngle,0);
  strokeWeight(1);
  stroke(#0000FF);       // black line outside ellipse
  line(radiusAtAngle,0,200,0);
    
  matrix.pop();
}




// Test figure class, to show matrix transforms and rotations
class TestFigure {
  float x, y;      // x, y matrix translation
  float xt, yt;    // x, y additional offset
  float ra, rb;    // radius A and B (of figure)
  float ang;       // angle
  float turnSpeed; 
  
  TestFigure( float ex, float ey, float ext, float eyt,
              float era, float erb, float angle, float ts) {
    x = ex;
    y = ey;
    xt = ext;
    yt = eyt;
    ra = era;
    rb = erb;
    ang = angle;
    turnSpeed = ts;
  }
  
  void display() {
    // Draw around offset from wherever origo (of matrix) is 
    //ellipse(xt, yt, ra, rb);
    rect(xt-ra, yt-rb, 2*ra, 2*rb);
  }
  
  void update() {
    ang += turnSpeed;
  }
}

And the class:

/** Class to keep track of 2D matrix transformations
   (to reverse-transform screen coordinates to matrix coordinates)

    To keep track of matrix transformations:
      Instead of:     Use:
      --------------  -----------------------------
      pushMatrix()    MatrixTracker2D.push()
      popMatrix()     MatrixTracker2D.pop()
      resetMatrix()   MatrixTracker2D.reset()
      translate(x,y)  MatrixTracker2D.move(x,y)
      rotate(angle)   MatrixTracker2D.turn(angle)
      
    Where "MatrixTracker2D" is an object of that type.

    To get a transformed position from a screen position (reverse of screenX / screenY):
  
      MatrixTracker2D.posX(x,y)
      MatrixTracker2D.posY(x,y)   where x,y = screen coordinates

    To get the angle between a transformed screen position 
    and an already transformed position, relative to the matrix X-axis, use:

      MatrixTracker2D.getAngle(sx, sy, mx, my)

    where:
      sx, sy is a screen position, and
      mx, my is a position in the current matrix


    2019.05.05 raron - First version
    (No guarantee that it actually works as intended)
*/

class MatrixTracker2D {
  int level = 0;
  // Processing allows a maximum of 32 pushMatrix()'es afaik
  // I'm including the "base" matrix here, totalling 33.
  final int max = 33;
  FloatList txList;
  FloatList tyList;
  FloatList angList;
  
  MatrixTracker2D() {
    // pre-allocating max matrix transformations
    txList = new FloatList(max);
    tyList = new FloatList(max);
    angList = new FloatList(max);
    // initialize one entry for the "base" matrix (assumed reset)
    txList.append(0);
    tyList.append(0);
    angList.append(0);
    level = txList.size(); // is 1 at instantiation
  }
  
  // Make space for new matrix transform data
  void push() {
    if (level>0 && level<max) {
      int i = level-1;
      txList.append(txList.get(i));
      tyList.append(tyList.get(i));
      angList.append(angList.get(i));
      level = txList.size();
      pushMatrix();
    }
  }
  
  // Translate matrix
  void move(float x, float y) {
    if (level > 0) {
      int i = level-1;
      float tempX = txList.get(i);
      float tempY = tyList.get(i);
      float ang = angList.get(i);
      txList.set(i, tempX + x*cos(ang) + y*cos(ang+PI/2));
      tyList.set(i, tempY + x*sin(ang) + y*sin(ang+PI/2));
      translate(x,y);
    }
  }
  
  // Rotate matrix
  void turn(float angle) {
    if (level > 0) {
      int i = level-1;
      angList.set(i, angList.get(i) + angle);
      rotate(angle);
    }
  }

  void reset() {
    txList.set(level-1, 0);
    tyList.set(level-1, 0);
    angList.set(level-1, 0);
    resetMatrix();
  }
  
  // remove last matrix and data
  void pop() {
    if (level>0) {
      txList.remove(level-1);
      tyList.remove(level-1);
      angList.remove(level-1);
      level = txList.size();
      popMatrix();
    }
  }

  // Get matrix X position from screen position
  // (reverse screenX)
  float posX(float x, float y) {
    int i = level-1;
    float tx = txList.get(i);
    float ty = tyList.get(i);
    float ang = atan2(y-ty, x-tx) - angList.get(i);
    float pDist = sqrt(pow(x-tx,2) + pow(y-ty,2));
    float mx = pDist * cos(ang);
    return mx;
  }
  
  // Get matrix Y position from screen position
  // (reverse screenY)
  float posY(float x, float y) {
    int i = level-1;
    float tx = txList.get(i);
    float ty = tyList.get(i);
    float ang = atan2(y-ty, x-tx) - angList.get(i);
    float pDist = sqrt(pow(x-tx,2) + pow(y-ty,2));
    float my = pDist * sin(ang);
    return my;
  }
  
  // Angle of line between a transformed screen position and 
  //a matrix position relative to the matrix X-axis.
  float getAngle(float sx, float sy, float mx, float my) {
    int i = level-1;
    float tx = screenX(mx, my);
    float ty = screenY(mx, my);
    float ang = atan2(sy-ty, sx-tx) - angList.get(i);
    // Also works (slower?)
    // float tx = posX(sx, sy);
    // float ty = posY(sx, sy);
    // float ang = atan2(ty-my, tx-mx);
    return ang;
  }
}
1 Like