Collision

Controls

Grab discs and throw them around.

Background

Detecting and resolving collisions is an aspect of many simulations and games. While truly physically accurate handling of object interaction is complicated and computationally intensive, simplistic treatments can lead to plausible behaviour. This simple simulation demonstrates some basic principles of collision detection and handling based on discrete collision events with impulse resolution.

The Simulation Loop

From CFD to graphics and games, simulations work around the concept of timestepping. We divide a span of time into discrete steps, resolve constraints and update a system. When done quickly enough, the progression of states looks like smooth evolution and can even be interactive. Much like frames in a film, we present slightly differing snapshots to give the illusion of motion. A collision simulation can then be implemented as a loop. Every iteration we need to move objects based on their velocity, detect interactions and update their information.

For the projects on this site, the main simulation loop is the draw function which is called every frame. Ideally, the code executes around 60 iterations every second, so that the transition betweeen states is imperceptible. However, the number of frames per second (fps) depends on the work that needs to be done, which includes calculations, updates as well as drawing. We leave the frequency to the browser by calling requestAnimationFrame at the end of the frame, pointing it back to the start of the draw function.

function draw() {
  fps = 1000 / (Date.now() - time);
  dt = 1 / fps;
  collision();
  gravity(g_const, dt);
  move();

  DRAW FRAME...

  time = Date.now();
  window.requestAnimationFrame(draw);
}
The variable dt is the timestep between frames and g_const is the gravitation constant which is set to 9.8.

Movement
We define a disc object that has a mass, x and y coordinates of its centre of mass and velocities x_vel and y_vel. Every frame, the centre of mass coordinates are updated by the velocities and by an additional acceleration in the y direction to simulate gravity. At the start of each frame, a rectangle is drawn over the previous scene and all elements are drawn at their updated positions, thereby creating an animation of a dynamic scene.

Collision Detection

Two discs are colliding when the distance between their centres is less than the sum of their radii. The objects exist in an array and we test the distance from every disc to the ones postioned after it in the container. In the event of an overlap, we reposition the disc pair to be just touching and update their velocities by applying an impulse. Detecting collision with a wall is done by finding the vector scalar product of the wall normal and the vector from the wall to the centre of mass of a disc. If this value is less than the radius of the disc, we need to resolve a collision.

As the number of pairs to check increases, the work can quickly become cumbersome. We can utilise bounding boxes and sweep algorithms to reduce the amount of calculation we need to do every frame, however, in the interest of simplicity, we assume that the current code will only ever need to simulate a small number of discs.

Collision Resolution

The impulse j to be applied is expressed as [1]:

j =
-(1 + e) vr ⋅ n̂
m1-1 + m2-1 + (I1-1(r1 × n̂) × r1 + I2-1(r2 × n̂) × r2) ⋅ n̂

where e is the coefficient of restitution (describing the elasticity of the collision), m1, 2 are the masses of the colliding discs, is the normal of collision surface (unit vector), I1, 2 are the moments of inertia of the objects, r1, 2 are the distances of collision points from the centres of mass of the discs, × is the vector cross product, is the vector dot product and vr is the relative velocity of the collision points (the difference in collision point velocities ⋅ n̂).

The moment of inertia of a disc is expressed as [2]:

I =
m (radius2)
2

And the moment of inertia of a rectangle rotating around its centre is given by:

I =
m (height2 + width2)
12

The impulse is applied with equal magnitude but opposite sign to each disc. The updated velocity 2 is expressed in terms of the previous velocity 1:

2 = v̄1 ±
j n̂
m

The (I1-1(r1 × n̂) × r1 + I2-1(r2 × n̂) × r2) ⋅ n̂ term is for angular movement which we can ignore for collisions between two discs with no friction. For the simplified case we can also ignore any angular velocity changes. Collisions with the walls can be resolved with either applying an impulse to a disc as if it is hitting a revolving rectangle with width 1 or by simply reflecting the velocity of the disc according to the inward pointing normal of the wall.