Hi
I have a problem with a collision detection method I have written. I use the separating axis theorem to detect collisions and the detection is working well.
The problem is that the minimum translation vector I get from the method is sometimes wrong if there are parallel edges in the polygons.
Parallel edges, although in opposite directions, give the same overlap distance, so it’s just a matter of chance if the returned vector will point in the right direction.
How would I go about to fix this?
My code:
/**
* Checks to see it there is a collision between two convex polygons.
* If there is a collision it returns a vector representing the minimal
* translation necessary to move 'a' so that it is not overlapping 'b',
* else a vector with x and y equal to 0.
*
* The returned vector is pointing towards the first polygon 'a'.
*
* Uses the separating axis theorem for convex polygons.
*
* @param a The first polygon
* @param b The second polygon
* @return The minimum translation vector directed towards the first
* polygon, or a Vector with (x,y)==(0,0) if there is no overlap.
*/
Vector getCollisionSAT(Polygon a, Polygon b) {
ArrayList<Vector> aVerts = new ArrayList<Vector>(a.getVertices());
ArrayList<Vector> bVerts = new ArrayList<Vector>(b.getVertices());
ArrayList<Vector> normals = new ArrayList<Vector>();
// Add normals of a to the list of normals
for (int i = 0; i < aVerts.size(); i++) {
Vector p1, p2;
p1 = aVerts.get(i);
p2 = aVerts.get((i + 1 == aVerts.size()) ? 0 : i + 1);
Vector normal = p2.subtract(p1).perpendicularCCW();
normal = normal.normalize();
normals.add(normal);
}
// Add normals of b to the list normals
for (int i = 0; i < bVerts.size(); i++) {
Vector p1, p2;
p1 = bVerts.get(i);
p2 = bVerts.get((i + 1 == bVerts.size()) ? 0 : i + 1);
Vector normal = p2.subtract(p1).perpendicularCW();
normal = normal.normalize();
normals.add(normal);
}
Vector minOverlapVector = null;
double minOverlap = Double.MAX_VALUE;
for (Vector normal : normals) {
double aMin = aVerts.get(0).dotProduct(normal); // some value in the range
double aMax = aMin;
// Project each vertex of a onto the current normal and save the min and max value
for (Vector v : aVerts) {
double dot = v.dotProduct(normal);
if (dot < aMin) {
aMin = dot;
}
else if (dot > aMax) {
aMax = dot;
}
}
double bMin = bVerts.get(0).dotProduct(normal); // some value in the range
double bMax = bMin;
// Do the same for b
for (Vector v : bVerts) {
double dot = v.dotProduct(normal);
if (dot < bMin) {
bMin = dot;
}
if (dot > bMax) {
bMax = dot;
}
}
// Compare min and max values to see if there is an overlap
if (aMin > bMax || bMin > aMax) {
return new Vector(0,0); // no overlap - no collision
}
else {
double overlap = Math.min(aMax - bMin, bMax - aMin);
if (overlap <= minOverlap) {
minOverlap = overlap;
minOverlapVector = normal.scalarMultiplication(overlap);
}
}
}
return minOverlapVector;
}