Skip to content

Instantly share code, notes, and snippets.

@greg-kennedy
Created August 7, 2024 04:17
Show Gist options
  • Save greg-kennedy/e1942d8e4ba5c716264d2212741b498f to your computer and use it in GitHub Desktop.
Save greg-kennedy/e1942d8e4ba5c716264d2212741b498f to your computer and use it in GitHub Desktop.
Stashing some 2d collision routines here
// AABB to AABB collision
// In case of a hit, the first object is pushed out
static int aabb_aabb(short *x, short *y, unsigned short w, unsigned short h,
short ox, short oy, unsigned short ow, unsigned short oh)
{
// find protrusion into each side
int p_l = *x + w - ox; if (p_l <= 0) return 0;
int p_r = ox + ow - *x; if (p_r <= 0) return 0;
int p_u = *y + h - oy; if (p_u <= 0) return 0;
int p_d = oy + oh - *y; if (p_d <= 0) return 0;
// collision. use minimum dist (above) to figure out which edge to lock to
if (p_l <= p_r && p_l <= p_u && p_l <= p_d)
*x = ox - w;
else if (p_r <= p_u && p_r <= p_d)
*x = ox + ow;
else if (p_u <= p_d)
*y = oy - h;
else
*y = oy + oh;
return 1;
}
// Point to Circle collision
// In case of a hit, the first object is pushed out
static int point_circle(float *x, float *y,
float ox, float oy, unsigned short or)
{
// check distance
float d_sqr = sqr(ox - *x) + sqr(oy - *y);
// too close?
if (d_sqr >= sqr(or)) return 0;
// determine angle of intrusion
float theta = atan2f(oy - *y, ox - *x);
// push first object away
*x = ox - or * cos(theta);
*y = oy - or * sin(theta);
return 1;
}
// Circle to Circle collision
// In case of a hit, the first object is pushed out
static int circle_circle(float *x, float *y, unsigned short r,
float ox, float oy, unsigned short or)
{
return point_circle(x, y, ox, oy, r + or);
}
// Circle to AABB collision
// In case of a hit, the first object is pushed out
static int circle_aabb(float *x, float *y, unsigned short r,
float ox0, float oy0, float ox1, float oy1)
{
// find protrusion into each side of an expanded square
float p_l = *x + r - ox0; if (p_l <= 0) return 0;
float p_r = ox1 + r - *x; if (p_r <= 0) return 0;
float p_u = *y + r - oy0; if (p_u <= 0) return 0;
float p_d = oy1 + r - *y; if (p_d <= 0) return 0;
// it's in the bounding square.
// based on the closest edges, test a corner, or don't
// collision. use minimum dist (above) to figure out which edge to lock to
if (p_l <= p_r && p_l <= p_u && p_l <= p_d)
{
// left edge
if (*y < oy0) return point_circle(x, y, ox0, oy0, r);
else if (*y > oy1) return point_circle(x, y, ox0, oy1, r);
else *x = ox0 - r;
} else if (p_r <= p_u && p_r <= p_d) {
// right edge
if (*y < oy0) return point_circle(x, y, ox1, oy0, r);
else if (*y > oy1) return point_circle(x, y, ox1, oy1, r);
else *x = ox1 + r;
} else if (p_u <= p_d) {
// top edge
if (*x < ox0) return point_circle(x, y, ox0, oy0, r);
else if (*x > ox1) return point_circle(x, y, ox1, oy0, r);
else *y = oy0 - r;
} else {
// bottom edge
// top edge
if (*x < ox0) return point_circle(x, y, ox0, oy1, r);
else if (*x > ox1) return point_circle(x, y, ox1, oy1, r);
else *y = oy1 + r;
}
return 1;
}
// Ray to AABB collision
// Return a distance at which the collision happens (or t_start if never)
static float ray_aabb(float x0, float y0, float dx, float dy,
float ox0, float oy0, float ox1, float oy1)
{
float tmin = -FLT_MAX; float tmax = FLT_MAX;
// float ray_dx = x1 - x0;
if (dx != 0.0) {
double tx1 = (ox0 - x0)/dx;
double tx2 = (ox1 - x0)/dx;
tmin = max(tmin, min(tx1, tx2));
tmax = min(tmax, max(tx1, tx2));
}
// float ray_dy = y1 - y0;
if (dy != 0.0) {
double ty1 = (oy0 - y0)/dy;
double ty2 = (oy1 - y0)/dy;
tmin = max(tmin, min(ty1, ty2));
tmax = min(tmax, max(ty1, ty2));
}
if (tmax >= tmin) return tmin;
else return FLT_MAX;
}
// Ray to Circle collision
// Return a distance at which the collision happens (or t_start if never)
static float ray_circle(float x0, float y0, float dx, float dy,
float ox, float oy, float or)
{
// determine the
float tmin = -FLT_MAX; float tmax = FLT_MAX;
// float ray_dx = x1 - x0;
if (dx != 0.0) {
double tx1 = (ox0 - x0)/dx;
double tx2 = (ox1 - x0)/dx;
tmin = max(tmin, min(tx1, tx2));
tmax = min(tmax, max(tx1, tx2));
}
// float ray_dy = y1 - y0;
if (dy != 0.0) {
double ty1 = (oy0 - y0)/dy;
double ty2 = (oy1 - y0)/dy;
tmin = max(tmin, min(ty1, ty2));
tmax = min(tmax, max(ty1, ty2));
}
if (tmax >= tmin) return tmin;
else return FLT_MAX;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment