1
votes

I create a project in which I manipulate balls. I have some problems that I don't know how to fix. I'm using processing.

In my project, circles collide with each other. At this point, to check if the objects have collided with each other, I go through all the objects, which is very under-optimized. I want to improve this by creating a NetForCircum class in which I put objects to compare only those that are close together. My main problem is that inserting objects into the mesh doesn't work if the circle has a center in a different part of the mesh. Also, I don't know how to change the mesh size if the window size changes.

void checkForCollision()
{
  for (int i = 0; i<cir.size() -1; i++)
  {
    for (int j = i + 1; j<cir.size(); j++)
    {
      //calculating distance between object
      PVector lengthFrom_i_to_j= PVector.sub( cir.get(j).point, cir.get(i).point);
      float oldDist = lengthFrom_i_to_j.mag();
      float min_dyst = cir.get(j).radius + cir.get(i).radius;
      //checking for collision
      if (oldDist <= min_dyst)
      {    
        collision(cir.get(i), cir.get(j), oldDist, min_dyst, lengthFrom_i_to_j);
      }
    }
  }
}

void collision(Circum con1, Circum con2, float dist_, float min_, PVector lock)
{
  float u1, u2, distance = dist_, min_dyst = min_;

  //static collision
  float distanceCorrection = (min_dyst-distance)/2.0;
  PVector correctionVector = lock.normalize().mult(distanceCorrection);
  con2.point.add(correctionVector);
  con1.point.sub(correctionVector);

  //dynamic collision

  // Defining the X axis
  PVector dirX = lock.copy();
  dirX.normalize();
  // Defining the Y axis
  PVector dirY = new PVector(dirX.y, -dirX.x);

  // X coordinates of velocities
  float vx1 = dirX.dot(con1.velocity);
  float vx2 = dirX.dot(con2.velocity);
  // Y coordinates of velocities
  float vy1 = dirY.dot(con1.velocity);
  float vy2 = dirY.dot(con2.velocity);

  // Applying the collision to X coordinates
  u1 = (2 * vx2 * con2.mass + vx1 * (con1.mass - con2.mass)) / (con1.mass + con2.mass);
  u2 = (2 * vx1 * con1.mass + vx2 * (con2.mass - con1.mass)) / (con1.mass + con2.mass);

  // Turning velocities back into vectors
  PVector vel1 = PVector.mult(dirX, u1);
  PVector vel2 = PVector.mult(dirX, u2);
  vel1.add(PVector.mult(dirY, vy1));
  vel2.add(PVector.mult(dirY, vy2));

  con1.velocity = vel1;
  con2.velocity = vel2;
}

class NetForCircum {
  int cell = 8;
  PVector size = new PVector(0, 0);
  IntList[][] objectsInCell = new IntList[cell][cell];


  NetForCircum(ArrayList<Circum> cir)
  {
    size.x = width/cell;
    size.y = height/cell;

    for (int i = 0; i<cell; i++)
    {
      for (int j = 0; j<cell; j++)
      {
        objectsInCell[i][j] = new IntList();
      }
    }

    for (int i = 0; i<cir.size() - 1; i++)
    {
      float pozLeft = cir.get(i).point.x - cir.get(i).radius, pozRight = cir.get(i).point.x + cir.get(i).radius, pozUp = cir.get(i).point.y - cir.get(i).radius, pozDown = cir.get(i).point.y + cir.get(i).radius;

      for (int j = 1; j<=cell; j++)
      {
        for (int k = 1; k<=cell; k++)
        {

          float curentSizeX = size.x * j, curentSizeY = size.y * k, previousSizeX = size.x * (j - 1), previousSizeY = size.y * (k - 1);

          if ((pozLeft>previousSizeX && pozRight<curentSizeX && pozUp>previousSizeY && pozDown<curentSizeY) || (cir.get(i).point.x>previousSizeX && cir.get(i).point.x<curentSizeX && cir.get(i).point.y>previousSizeY && cir.get(i).point.y<curentSizeY))  
          {
            cir.get(i).cellNumber.add(new PVector(j, k));
            cir.get(i).cellBorderX.set(previousSizeX, curentSizeX);
            cir.get(i).cellBorderY.set(previousSizeY, curentSizeY);
            objectsInCell[j-1][k-1].append(i);
          } 
         }
        }
       }
      }
     }
2

2 Answers

2
votes

A cheap optimization is to compare the square of the distances rather than the distances. For the computation of the Euclidean distance the square root is required:

distance = sqrt( (x2-x1) ^ 2 + (y2-y1) ^ 2 )

Calculating the square root is very expensive even for today's computers. For the square of the distance the square root is not required:

distance_square = (x2-x1) ^ 2 + (y2-y1) ^ 2

Use magSq() rather than mag() and compare it to min_dyst*min_dyst rather than min_dyst:

PVector lengthFrom_i_to_j= PVector.sub( cir.get(j).point, cir.get(i).point);

// Calculates the magnitude (length) of the vector, squared.
float oldDistSq = lengthFrom_i_to_j.magSq();

float min_dyst = cir.get(j).radius + cir.get(i).radius;

// Compare squared distances
if (oldDistSq <= min_dyst * min_dyst) { 
    // [...]
}
2
votes

In addition to @Rabbid76's excellent answer I can suggest avoiding looping twice (e.g. checking i and j, then j and i):

void checkForCollision()
{
  final int numCircles = cir.size();
  
  for(int i = 0; i < numCircles - 1; i++)
  {
    
    Circum circumI = cir.get(i);
    
    for(int j = i + 1; j < numCircles; j++)
    {
     
      Circum circumJ = cir.get(j);
      
      float dx = circumJ.point.x - circumI.point.x;
      float dy = circumJ.point.y - circumI.point.y;
      float distanceSquared = ((dx * dx) + (dy * dy));
      float radii = circumJ.radius + circumI.radius;
      float thresholdDistanceSquared = radii * radii;
      
      if(distanceSquared < thresholdDistanceSquared){
        //collision(circumI, circumJ, ...)
      }
      
      
    }
    
  } 
  
}

I've learned this ages ago from Keith Peter's Making Things Move in actionscript, where every bit of performance counted. Also notice how object properties are accessed only once and cached into local variables for re-use. This is another habit due to the costly nature of accessing nested data (getters) in actionscript (or javascript) virtual machines. Java Virtual Machine might be a bit more efficient, and this might not be required.

I would also urge to leave optimisations last and use a profiling tool such as VisualVM to determine what to optimise. What you've pointed out is a good candidate, but there may other places to look into. Start with what's slowest, the rest you might need to touch.

For example, based on the pastebin code it looks like the circle and line rendering is a bit slow. You can look into using PShape / createShape(ELLIPSE) and the Particle examples in Processing > Examples > Demos > Performance

visualvm rendering profiling