Object &Pixel Collision detection

Hey, I am a real newbie in coding in processing and I need help for my hw. I am trying to develop an agent-based simulation in which, the agents move in a closed space with obstacles. For the obstacles I am trying to use a plan image, taking the black pixels as obstacles to change the direction of the agents when they collide. But I couldn’t found a way to achieve it, yet.If anyone could help me I would appreciate it <3
I am pasting down the code that I tried to write for now.

void move () {
marketplan = loadImage(“marketplan.png”);
position.add(velocity);

for (int j=0; j< height; j++)
{
for(int i=0; i<width ; i ++)
{
colors [i][j] = marketplan.get(i,j);
if(hue(colors[i][j]) < 100)
{
black[i][j] = 1;
obstacle= true;
}
else
{
black[i][j]=0;
obstacle= false;

  }
}

}

if(position.x > width || position.x <0)
{
velocity.x= velocity.x*-1;
}

if(position.y > height || position.y <0 )
{
velocity.y= velocity.y*-1;
}

}

Interesting approach to check for black / white pixels, but probably not the most efficient.
1st, you should make a global variable PImage marketplan or something, where you load marketplan.png once in void setup().
2nd, (and this only works if it is a .png image, so a pixel is either perfectly white or perfectly black), you can check if a there is an obstacle at any given position (x, y) with the following code:

boolean obstacleAt(int x, int y) {
  return marketplan.pixels[y*marketplan.width+x] == color(0);
}

In Processing, each PImage object has a one-dimensional color arry called pixels[]. It’s simply a list of all pixel colors, starting from the top left, going left to right, top to bottm (just like you would read the letters in a book). The way you get any color at (x, y) of any PImage image is to index this array with image.pixels[y+image.width+x]. This does the same as image.get(x,y), though it is even faster. Next, we check if this color is black, which in Processing we write as color(0). If the pixel is black, then this statement is true, this method returns true. If the pixel is white, this statement is false, so false is returned.
Or at least, that would be the primitive approach. The image might contain pixels that aren’t perfectly black, but should still be considered an obstacle. This is why the method should actually look like this:

boolean obstacleAt(int x, int y) {
  color c = marketplan.pixels[y*marketplan.width+x];
  return red(c)+green(c)+blue(c) < 3*255/2;
}

That way, any pixel that is lighter than a 50% midtone grey will be considered free, and any pixel darker than that will be considered an obstacle.

Since your lines seem to be only a few pixels wide, make sure that the agents’ speed is only at around 2-3, otherwise they might clip trough the wall (by being on one side of the wall on one frame and on the other side on the next frame).

Your approach for preventing the agents from exiting the boundaries of the window is good, but you missed one small detail. You actually need to write it like this, or else they will get stuck at the boundaries once they hit them, with no way to escape.

if (position.x < 0) {
  position.x = 0;
  velocity.x *= -1;
}
if (position.y < 0) {
  position.y = 0;
  velocity.y *= -1;
}
if (position.x > width) {
  position.x = width;
  velocity.x *= -1;
}
if (position.y > height) {
  position.y = height;
  velocity.y *= -1;
}

Note that with all of this, all you can do so far is to check for an obstacle at a certain point (x, y), the actually collision hasn’t been implemented yet.

This explanation ended up quite a bit longer than I expected, I hope I could help you.

Best, Simon

1 Like

I just reread you message and realized I completely missed that the agents should turn when they hit an obstacle. Interestingly, this is kind of hard to implement. A naaive but simple approach would be to just move the agent back to its previous position with

position.sub(velocity);

and to have it turn 180° with

velocity.mult(-1);

Depending on the rest of the movement code of the agents, this might either look acceptable or horrible.

A much more elegant solution on the other hand would be to have them move along the wall, by moving them to a position right next to the wall, and changing the angle to face the direction of the wall. This would be doable if the walls were stored in a format where the two endpoints of the lines are specified, but this is not the case right here. You could use some advanced math (the gradient of the area around the collision point) to figure out the normal vector of the wall at that point, then projecting the previous velocity vector onto the normal vector, normalizing it and scaling it by speed, but I feel like this far exceeds the requirements for your homework. It also far exceeds my skills in math or programming.

But there is another approach for the movement of the agents. You could sample a few sample points in front of the agent, and check for collisions on each of them. (In the example shown it’s 5, something like 10 would probably make more sense). That gives each agent a rough idea of its surroundings. Based off of that, you could rotate the velocity vector to the left or right. You can rotate a PVector with the .rotate() method.


The way you get these sample points looks something like this:

ArrayList<PVector> generateSamplePoints(int numpoints, float fov) {
  ArrayList<PVector> ret = new ArrayList<PVector> ();
  for (int i=0; i<numpoints; i++) {
    ret.add(new PVector().add(velocity).rotate(((float)i/(numpoints-1)-0.5)*fov).add(position));
  }
  return ret;
}

Using the boolean obstacleAt() method from above, you could then count the obstacles to the left and to the right of the agent. Depending on the difference between the ones on the left and the ones on the right of the agent, it should turn respectively.

void move() {
  ArrayList<PVector> samplePoints = generateSamplePoints(10, PI/3);
  int leftcount = 0;  //points in the first half of the ArrayList are to the left of the agent
  int rightcount = 0;  //ones in the second half are to the right
  for (int i=0; i<samplePoints.size(); i++) {
    PVector s = samplePoints.get(i);
    if (s.x >= 0 && s.x < marketplan.width && s.y >= 0 && s.y < marketplan.height) {
     if (obstacleAt(floor(s.x), floor(s.y)) {
        if (i < samplePoints.size()/2) {
          leftcount ++;
        } else {
          rightcount ++;
        }
      }
    }
  }
  int delta = leftcount-rightcount;
  velocity.rotate(delta*0.05);

  //all the code from before (add velocity to position, check position for out of bounds)
}

This approach isn’t perfect, as agents can still walk through the walls if the face them head on, but combining it with the naaive approach from above as a failsafe should work pretty good.

I really hope all of this wasn’t too overwhelming for you, feel free to hit me up if you have any questions. (“WeinSim aka Carrot#3361” on discord if you prefer that, I sure do ;)). In case you’ve never heard of ArrayLists: ArrayList \ Language (API) \ Processing 3+

Simon

2 Likes