Finding the normal of OBB face with an OBB penetrating
- by Milo
Below is an illustration:
I have an OBB in an OBB (see below for OBB2D code if needed).
What I need to determine is, what face it is in, and what direction do I point the normal? The goal is to get the OBB out of the OBB so the normal needs to face outward of the OBB.
How could I go about:
Finding what face the line is penetrating given the 4 corners of the OBB and the class below:
if we define dx=x2-x1 and dy=y2-y1, then the normals are (-dy, dx) and (dy, -dx). Which normal points outward of the OBB?
Thanks
public class OBB2D
{
// Corners of the box, where 0 is the lower left.
private Vector2D corner[] = new Vector2D[4];
private Vector2D center = new Vector2D();
private Vector2D extents = new Vector2D();
private RectF boundingRect = new RectF();
private float angle;
//Two edges of the box extended away from corner[0].
private Vector2D axis[] = new Vector2D[2];
private double origin[] = new double[2];
public OBB2D(Vector2D center, float w, float h, float angle)
{
set(center,w,h,angle);
}
public OBB2D(float left, float top, float width, float height)
{
set(new Vector2D(left + (width / 2), top + (height / 2)),width,height,0.0f);
}
public void set(Vector2D center,float w, float h,float angle)
{
Vector2D X = new Vector2D( (float)Math.cos(angle), (float)Math.sin(angle));
Vector2D Y = new Vector2D((float)-Math.sin(angle), (float)Math.cos(angle));
X = X.multiply( w / 2);
Y = Y.multiply( h / 2);
corner[0] = center.subtract(X).subtract(Y);
corner[1] = center.add(X).subtract(Y);
corner[2] = center.add(X).add(Y);
corner[3] = center.subtract(X).add(Y);
computeAxes();
extents.x = w / 2;
extents.y = h / 2;
computeDimensions(center,angle);
}
private void computeDimensions(Vector2D center,float angle)
{
this.center.x = center.x;
this.center.y = center.y;
this.angle = angle;
boundingRect.left = Math.min(Math.min(corner[0].x, corner[3].x), Math.min(corner[1].x, corner[2].x));
boundingRect.top = Math.min(Math.min(corner[0].y, corner[1].y),Math.min(corner[2].y, corner[3].y));
boundingRect.right = Math.max(Math.max(corner[1].x, corner[2].x), Math.max(corner[0].x, corner[3].x));
boundingRect.bottom = Math.max(Math.max(corner[2].y, corner[3].y),Math.max(corner[0].y, corner[1].y));
}
public void set(RectF rect)
{
set(new Vector2D(rect.centerX(),rect.centerY()),rect.width(),rect.height(),0.0f);
}
// Returns true if other overlaps one dimension of this.
private boolean overlaps1Way(OBB2D other)
{
for (int a = 0; a < axis.length; ++a) {
double t = other.corner[0].dot(axis[a]);
// Find the extent of box 2 on axis a
double tMin = t;
double tMax = t;
for (int c = 1; c < corner.length; ++c) {
t = other.corner[c].dot(axis[a]);
if (t < tMin) {
tMin = t;
} else if (t > tMax) {
tMax = t;
}
}
// We have to subtract off the origin
// See if [tMin, tMax] intersects [0, 1]
if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
// There was no intersection along this dimension;
// the boxes cannot possibly overlap.
return false;
}
}
// There was no dimension along which there is no intersection.
// Therefore the boxes overlap.
return true;
}
//Updates the axes after the corners move. Assumes the
//corners actually form a rectangle.
private void computeAxes()
{
axis[0] = corner[1].subtract(corner[0]);
axis[1] = corner[3].subtract(corner[0]);
// Make the length of each axis 1/edge length so we know any
// dot product must be less than 1 to fall within the edge.
for (int a = 0; a < axis.length; ++a) {
axis[a] = axis[a].divide((axis[a].length() * axis[a].length()));
origin[a] = corner[0].dot(axis[a]);
}
}
public void moveTo(Vector2D center)
{
Vector2D centroid = (corner[0].add(corner[1]).add(corner[2]).add(corner[3])).divide(4.0f);
Vector2D translation = center.subtract(centroid);
for (int c = 0; c < 4; ++c)
{
corner[c] = corner[c].add(translation);
}
computeAxes();
computeDimensions(center,angle);
}
// Returns true if the intersection of the boxes is non-empty.
public boolean overlaps(OBB2D other)
{
if(right() < other.left())
{
return false;
}
if(bottom() < other.top())
{
return false;
}
if(left() > other.right())
{
return false;
}
if(top() > other.bottom())
{
return false;
}
if(other.getAngle() == 0.0f && getAngle() == 0.0f)
{
return true;
}
return overlaps1Way(other) && other.overlaps1Way(this);
}
public Vector2D getCenter()
{
return center;
}
public float getWidth()
{
return extents.x * 2;
}
public float getHeight()
{
return extents.y * 2;
}
public void setAngle(float angle)
{
set(center,getWidth(),getHeight(),angle);
}
public float getAngle()
{
return angle;
}
public void setSize(float w,float h)
{
set(center,w,h,angle);
}
public float left()
{
return boundingRect.left;
}
public float right()
{
return boundingRect.right;
}
public float bottom()
{
return boundingRect.bottom;
}
public float top()
{
return boundingRect.top;
}
public RectF getBoundingRect()
{
return boundingRect;
}
public boolean overlaps(float left, float top, float right, float bottom)
{
if(right() < left)
{
return false;
}
if(bottom() < top)
{
return false;
}
if(left() > right)
{
return false;
}
if(top() > bottom)
{
return false;
}
return true;
}
};