Building a Collision Engine Part 2: 2D Penetration Vectors

In my last post, I discussed how to determine if any two convex shapes in two dimensions overlap. The result of this calculation is a boolean state—either the two are colliding or they aren't. This is great for things such as triggers, but if you want to use the collision engine for any type of physics calculations, you'll need at least one more crucial piece of information: the penetration vector.

The penetration vector of two shapes is a vector which describes the overlap of those two shapes. By adding the penetration vector to one of the shapes, the shapes can be separated so that they are just touching, but not really intersecting. Without any extra information, this vector chosen as the one that separate the two shapes along the shortest path possible, which can lead to some issues at the corners of polygons but is often exactly what we want.

In my collision engine, I've decided to implement the calculation of this penetration vector using the EPA method. This method again makes use of the same support functions we used to calculate whether we were colliding or not. The reason for this is that the penetration vector is the distance from the origin to the Minkowski difference, whether the Minkowski difference contains the origin or not.

The penetration vector of two overlapping shapes is the distance of the Minkowski difference to the origin.

The EPA Method

The EPA (Expanding Polytope Algorithm) works well with Minkowski differences because it uses many of the same concepts—namely support functions and simplexes. However, instead of trying to use the support functions to construct a simplex which encases the origin, EPA seeks to use the support functions to find the boundaries of the Minkowski difference in an attempt to find the boundary that is closest to the origin. In order to do this, the algorithm starts with a simplex contained within the Minkowski difference (we might as well use the one from the previous step!), then "expands" it into higher vertex count polygons (gradually approaching the true Minkowski difference polygon) until one of the edges is found to be the closest to the origin.

EPA "searches" for the edge of the polytope which is closest to the origin.

Once we have the edge in our polygon which is the closest to the origin, try to find a support point in the direction of the edge's normal direction to see if there might be something closer to the origin (that exists in the Minkowski difference, but not our simplex yet):

var edge:Edge = findClosestEdge(simplexWinding);
var support:Vec2 = calculateSupport(edge.normal);
var distance:Float = support.dot(edge.normal);
if(Math.abs(distance - edge.distance) <= EPSILON) {
// we found the edge in the Minkowski difference that is closest to the origin!
}
else {
// there's likely an edge in the Minkowski difference that is closer to the origin..
}

If there is a support point sufficiently far away from our edge in it's normal direction, we haven't found the edge closest to the origin.

If we did find the edge closest to the origin, the support point in the direction of the edge's normal should lie on the edge itself. In this case, we can return the penetration vector as:

On the other hand, if we found that we didn't find the edge closest to the origin, we can expand the simplex toward the support point and try again:

vertices.insert(edge.index, support);

Expand the simplex to try again.

Once we've expanded our simplex (for the first iteration the simplex will go from having 3 to having 4 vertices), we run the algorithm again, searching for the closest edge and determining if the support point along its normal lies on the edge or not. We can continue this process until we find the de-facto closest edge or we hit some upper limit of iterations that we're willing to try. The code for calculating the intersection now looks like this:

Putting it All Together

We can now extend the class from the previous post to include penetration vector calculation by adding the following. What you do with the penetration vector depends on your goal. If you're creating a physics system, you can use these vectors to resolve collision responses (pushing objects apart based on physical properties like mass and elasticity for example).