Rect Overlap error

Hi,
I’m building a program to add rectangles in random locations that don’t overlap.
My issue is that for some reason I can’t see, the checking of the rectangles is happening incorrectly and then adding them with overlaps. If it doesn’t immediately create an overlapping situation, click until you get one.

What have I done wrong?
Any and all assistance is appreciated.

ArrayList<Panel> panels;
int x, y, w;
int index = 0;

void setup() {
  size(600, 600);
  initPanels();
}

void initPanels() {
  panels = new ArrayList<Panel>();

  for (int i = 0; i < 5; i++) {
    x = int(random(width-w));
    y = int(random(height-w));
    w = 50;
    index = i;
    if (i > 0) {
      check(x, y, w);
    } 
    println("Adding: " + index + " | ", x, y);
    panels.add(new Panel(x, y, w, index));
  }
}

void check(float x, float y, float w) {
  println("entry " + index);
  PVector o;
  for (int j = 0; j < index; j++) {
    o = panels.get(j).pos;

    if (panels.get(j).isOver(x, y)) {
      x = int(random(width-w));
      y = int(random(height-w));
      println("Failed: " + index+  " | ", x, y, " | ", j, o.x, o.y);
      for (int g = 0; g < panels.size(); g++) println(panels.get(g).pos);
      check(x, y, w);
    }
  }
}

void draw() {
  background(0);

  for (Panel p : panels) {
    p.draw();
  }
  fill(-1);
  text(mouseX + " | " + mouseY, 50, 25);
}

void mouseReleased() {
  println();
  initPanels();
}

class Panel {
  PVector pos;
  int wd, Id;
  color c = color(-1, 85);

  Panel(float x, float y, int wd, int Id) {
    pos = new PVector(x, y);
    this.wd = wd;
    this.Id = Id;
  }

  void draw() {
    fill(c);
    square(pos.x, pos.y, wd);
    fill(0);
    textAlign(CENTER);
    textSize(18);
    text(Id, pos.x+w/2, pos.y+w/2);
    if (isOver(mouseX, mouseY)) c = -1;
    else c = color(-1, 85);
  }

  void setX(float n) {
    pos.x = n;
  }

  void setY(float n) {
    pos.y = n;
  }

  void setFill(color c) {
    this.c = c;
  }

  boolean isOver(float cX, float cY) {
    if (cX >= pos.x && cX <= pos.x+wd && cY >= pos.y && cY <= pos.y+wd) {
      return true;
    } else return false;
  }
}
1 Like

i have to think of one of the gods of processing…

launch the applet, even if it doesn’t work, you get access to the source code :slight_smile:
and everyone should know Jared Tarbell anyway !

2 Likes

Hi Hyperion,
I’ve tried making a version that uses his style of checking pixel brightness and I still get overlaps.
So it’s definitely my code at fault.

Any ideas as to what I’ve done wrong?

(here is a really, really great site on collision detection, someone once posted:
http://jeffreythompson.org/collision-detection/matrix-transformations.php)

  1. one thing I notice with your sketch is that you have a chance for infinite recursion since you call check(x,y,w) inside itself. the good thing for now is, that the collision detection doesn’t work, but if it did it could become an issue, your code infinitely repeating when it doesn’t detect a free place for a new object…

  2. you are basically checking if a POINT is inside a rectangle.
    but you need to check if any part of a rectangle is within another rectangle.
    so it is possible that they can overlap, because the upper left corner of one rectangle can be outside another, but they still overlap.

if (cX >= pos.x && cX <= pos.x+wd && cY >= pos.y && cY <= pos.y+wd)

is cX in between pos.x and pos.x + wd = is the point inside, not the width

  1. its always your fault. :slight_smile: the code does what you tell it :-)))))
1 Like

Thanks for the assistance @Hyperion65, you’re amazing.

I got it working exactly for my purposes by modifying that example from Jeffrey Thompson.
It might not be pretty or well built, but it does what it needs to.

Thanks again for your assistance and patience.

My Solution:

//Jeffrey Thmpson
//http://jeffreythompson.org/collision-detection/matrix-transformations.php
ArrayList<Square> squares;
int w = 100;
int tmrStart;

void setup() {
  size(400, 400);

  init();
}

void init() {
  // squares defined as PVector 
  // arrays around the origin
  PVector[] sq = {
    new PVector(-50, -50), 
    new PVector(50, -50), 
    new PVector(50, 50), 
    new PVector(-50, 50)
  };
  squares = new ArrayList<Square>();
  boolean r = false;
  tmrStart = millis();
  for (int i = 0; i < 5; i++) {
    if (random(2) > 1) r = false;
    else r = true;

    squares.add(new Square(int(random(25, width-25)), int(random(25, height-25)), r, i, sq));

    if (i > 0) {
      //check for collision
      for (int j = 0; j < squares.size(); j++) {
        if (i != j) {
          boolean hit = polyPoly(squares.get(j).getSqScreen(), squares.get(i).getSqScreen());
          if (hit) squares.get(i).newPos();
        }
      }
    }
  }
}

void recheck() {

  for (int i = 0; i < squares.size(); i++) {
    if (millis() - tmrStart > 1000) break; 
    if (i > 0) {
      //check for collision
      for (int j = 0; j < squares.size(); j++) {
        if (i != j) {
          boolean hit = polyPoly(squares.get(j).getSqScreen(), squares.get(i).getSqScreen());
          if (hit) squares.get(i).newPos();
        }
      }
    }
  }
}

void mouseReleased() {
  init();
}

void draw() {
  background(255);

  for (Square s : squares) {
    s.draw();
  }
}

// a function that returns the actual screen coordinates
// after matrix transformations (like translate and rotate)
// we could do this up above but easier to make it a function
// that can take any number of polygon points
PVector[] pointsToScreenCoords(PVector[] points) {
  PVector[] screenPoints = new PVector[points.length];   // create output array
  for (int i=0; i<points.length; i++) {                  // go through all the points
    float x = screenX(points[i].x, points[i].y);         // get the screen x coordinate
    float y = screenY(points[i].x, points[i].y);
    screenPoints[i] = new PVector(x, y);
  }
  return screenPoints;
}

// POLYGON/POLYGON
boolean polyPoly(PVector[] p1, PVector[] p2) {

  // go through each of the vertices, plus the next vertex in the list
  int next = 0;
  for (int current=0; current<p1.length; current++) {

    // get next vertex in list
    // if we’ve hit the end, wrap around to 0
    next = current+1;
    if (next == p1.length) next = 0;

    // get the PVectors at our current position
    // this makes our if statement a little cleaner
    PVector vc = p1[current];    // c for “current”
    PVector vn = p1[next];       // n for “next”

    // now we can use these two points (a line) to compare to the
    // other polygon’s vertices using polyLine()
    boolean collision = polyLine(p2, vc.x, vc.y, vn.x, vn.y);
    if (collision) return true;

    // optional: check if the 2nd polygon is INSIDE the first
    collision = polyPoint(p1, p2[0].x, p2[0].y);
    if (collision) return true;
  }

  return false;
}


// POLYGON/LINE
boolean polyLine(PVector[] vertices, float x1, float y1, float x2, float y2) {

  // go through each of the vertices, plus the next vertex in the list
  int next = 0;
  for (int current=0; current<vertices.length; current++) {

    // get next vertex in list
    // if we’ve hit the end, wrap around to 0
    next = current+1;
    if (next == vertices.length) next = 0;

    // get the PVectors at our current position
    // extract X/Y coordinates from each
    float x3 = vertices[current].x;
    float y3 = vertices[current].y;
    float x4 = vertices[next].x;
    float y4 = vertices[next].y;

    // do a Line/Line comparison
    // if true, return ‘true’ immediately and stop testing (faster)
    boolean hit = lineLine(x1, y1, x2, y2, x3, y3, x4, y4);
    if (hit) {
      return true;
    }
  }

  // never got a hit
  return false;
}


// LINE/LINE
boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {

  // calculate the direction of the lines
  float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
  float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));

  // if uA and uB are between 0-1, lines are colliding
  if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
    return true;
  }
  return false;
}


// POLYGON/POINT
// used only to check if the second polygon is INSIDE the first
boolean polyPoint(PVector[] vertices, float px, float py) {
  boolean collision = false;

  // go through each of the vertices, plus the next vertex in the list
  int next = 0;
  for (int current=0; current<vertices.length; current++) {

    // get next vertex in list
    // if we’ve hit the end, wrap around to 0
    next = current+1;
    if (next == vertices.length) next = 0;

    // get the PVectors at our current position
    // this makes our if statement a little cleaner
    PVector vc = vertices[current];    // c for “current”
    PVector vn = vertices[next];       // n for “next”

    // compare position, flip ‘collision’ variable back and forth
    if ( ((vc.y > py && vn.y < py) || (vc.y < py && vn.y > py)) &&
      (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) {
      collision = !collision;
    }
  }
  return collision;
}

class Square {
  PVector[] arr;
  PVector pos;
  boolean rot;
  color c = color(0, 150);
  int id;

  Square(int x, int y, boolean rot, int id, PVector[] arr) {
    pos = new PVector(x, y);
    this.rot = rot;
    this.arr = arr;
    this.id = id;
  }

  void draw() {
    // move the origin to the position of the first square
    pushMatrix();
    translate(pos.x, pos.y);   
    if (rot) rotate(radians(45));

    // and draw the square!
    fill(c);
    noStroke();
    beginShape();
    for (PVector pt : arr) {
      vertex(pt.x, pt.y);
    }
    endShape(CLOSE);
    fill(0);
    text(id, 0, 0);
    popMatrix();
  }

  PVector[] getSqScreen() {
    pushMatrix();
    translate(pos.x, pos.y);
    if (rot) rotate(radians(45));
    PVector[] square1Screen = pointsToScreenCoords(arr);
    popMatrix();
    return square1Screen;
  }

  void setFill(color c) {
    this.c = c;
  }

  void newPos() {
    pos.x = int(random(50, width-50));
    pos.y = int(random(50, height-50));
    recheck();
  }
}

2 Likes

Glad you worked it out!

Just noting for posterity that there are two approaches here, and either approach will work on its own. The first is matrix transformations.

And the second is poly-poly collision detection:

If you express your rotated rects directly as polygons then you can avoid screenX-style matrix coordinate recovery entirely, and just compare then as polygons to other polygons – which works on any combination of rectangle (rotated or not), rhombus, triangle, hexagon, et cetera.