Created
January 18, 2021 02:49
-
-
Save MarcinKonowalczyk/a89b82a7712bf49dbd7efa599374c70b to your computer and use it in GitHub Desktop.
[Processing] Fast collision detection between axis-aligned rectangle and a circle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Circle c; | |
Rectangle r; | |
void setup() { | |
size(500,500); | |
frameRate(600); | |
rectMode(CORNERS); | |
c = new Circle(width/2, height/2, 80); | |
r = new Rectangle(width/2, height/2, 160, 80); | |
} | |
void draw() { | |
// Background | |
fill(#c1c1c1); noStroke(); | |
rect(0,0,width,height); | |
// Draw rectangle | |
r.show(); | |
// Update circle position | |
c.move(mouseX,mouseY); | |
// Collide calc + draw shadows | |
boolean result = collide(c,r); | |
c.state = result; | |
// Draw circle | |
c.show(); | |
// Draw info text | |
draw_info_text(result); | |
} | |
void keyPressed() { | |
if (keyCode == UP) { | |
; //r.move(r.center.add; | |
} else if (keyCode == DOWN) { | |
; // delta_time = max(delta_time-0.1,0.5); | |
} else if (keyCode == LEFT) { | |
; // neighbour_radius = max(neighbour_radius-5,10); | |
} else if (keyCode == RIGHT) { | |
; // neighbour_radius = min(neighbour_radius+5,100); | |
} | |
} | |
////////////// | |
// COLLIDER // | |
////////////// | |
boolean collide(Circle circ, Rectangle rect) { | |
boolean result = false; | |
float cx = circ.center.x; | |
float cy = circ.center.y; | |
float r = circ.radius; | |
float r2 = circ.radius_squared; | |
float[] edges = rect.edges(); | |
float vl = edges[0]; float vr = edges[1]; | |
float ht = edges[2]; float hb = edges[3]; | |
// I - top left of the top corner | |
// II - top, between top left and top right corners | |
// III - top right of the top right corner | |
// IV - left, between top left and bottom left corners | |
// V - center (trivial case) | |
// VI - right, between top right and top bottom right corner | |
// VII - bottom left of the bottom left corner | |
// VIII - bottom, between bottom left and bottom right corner | |
// IX - bottom right of the bottom right corner | |
fill(#111111,30); | |
if (cx<vl) { // I, IV or VII | |
if (cy<ht) { // I | |
result = sq(vl-cx)+sq(ht-cy) < r2; | |
rect(0,0,vl,ht); arc(vl, ht, 2*r, 2*r, PI, PI+HALF_PI); | |
} else if (cy<hb) { // IV | |
result = cx>(vl-r); | |
rect(0,ht,vl,hb); rect(vl-r,ht,vl,hb); | |
} else { // VII | |
result = sq(vl-cx)+sq(hb-cy) < r2; | |
rect(0,hb,vl,height); arc(vl, hb, 2*r, 2*r, HALF_PI, PI); | |
} | |
} else if (cx<vr) { // II, V or VII | |
if (cy<ht) { // II | |
result = cy>(ht-r); | |
rect(vl,0,vr,ht); rect(vl,ht-r,vr,ht); | |
} else if (cy<hb) { // V | |
result = true; | |
rect(vl,ht,vr,hb); | |
} else { // VIII | |
result = cy<(hb+r); | |
rect(vl,hb,vr,height); rect(vl,hb,vr,hb+r); | |
} | |
} else { // III, VI or IX | |
if (cy<ht) { // III | |
result = sq(vr-cx)+sq(ht-cy) < r2; | |
rect(vr,0,width,ht); arc(vr, ht, 2*r, 2*r, PI+HALF_PI, TWO_PI); | |
} else if (cy<hb) { // VI | |
result = cx<(vr+r); | |
rect(vr,ht,width,hb); rect(vr,ht,vr+r,hb); | |
} else { // IX | |
result = sq(vr-cx)+sq(hb-cy) < r2; | |
rect(vr,hb,width,height); arc(vr, hb, 2*r, 2*r, 0, HALF_PI); | |
} | |
} | |
return result; | |
} | |
//////////// | |
// SHAPES // | |
//////////// | |
class Point { | |
float x,y; | |
Point(float x, float y) { | |
this.x = x; | |
this.y = y; | |
} | |
} | |
class Circle { | |
Point center; | |
float radius, radius_squared; | |
boolean state = false; | |
Circle(float cx, float cy, float r) { | |
center = new Point(cx,cy); | |
radius = r; | |
radius_squared = r*r; | |
} | |
void move(float x, float y) { | |
center.x = x; center.y = y; | |
} | |
void show() { | |
push(); | |
stroke(#121212); strokeWeight(1.5); | |
color c; | |
if (state) {c = color(#c2452c,150);} | |
else {c = color(#45acc2,150);} | |
fill(c); | |
ellipse(center.x,center.y,2*radius,2*radius); | |
strokeWeight(2.5); | |
point(center.x,center.y); | |
pop(); | |
} | |
} | |
class Rectangle { | |
Point center = new Point(0,0); // Needs to be initialised here | |
float width, height; | |
float vl, vr, ht, hb; // Edges (no need to recalculate these) | |
Rectangle(float cx, float cy, float w, float h) { | |
width = w; | |
height = h; | |
move(cx,cy); | |
} | |
void move(float x, float y) { | |
center.x = x; center.y = y; | |
recalculate_edges(); | |
} | |
void recalculate_edges() { | |
vl = center.x - width/2; | |
vr = center.x + width/2; | |
ht = center.y - height/2; | |
hb = center.y + height/2; | |
} | |
void show() { | |
push(); | |
stroke(#121212); strokeWeight(1.5); | |
fill(#acc245,150); | |
rectMode(CORNER); | |
rect(center.x-width/2,center.x-height/2,width,height); | |
strokeWeight(2.5); | |
point(center.x,center.y); | |
pop(); | |
} | |
float[] edges() { | |
// Bounding edges | |
// Vertical left, V. right, Horizontal top, H. bottom | |
return new float[] {vl,vr,ht,hb}; | |
} | |
Point[] corners() { | |
// Rectangle corners | |
// Top left, Top right, Bottom right, Bottom left | |
float[] e = edges(); | |
return new Point[] { | |
new Point(e[0],e[2]), // top-left | |
new Point(e[1],e[2]), // top-right | |
new Point(e[1],e[3]), // bottom-right | |
new Point(e[0],e[3]) // bottom-left | |
}; | |
} | |
} | |
///////////// | |
// HELPERS // | |
///////////// | |
void draw_info_text(boolean result) { | |
push(); | |
fill(128); | |
strokeWeight(2.5); | |
textSize(12); | |
textAlign(RIGHT, TOP); | |
String t = "fps: " + nf(frameRate,3,-1) + "\n"; | |
if (result) {t += "Collision";} | |
else {t += "No collision";} | |
text(t, width-5, 0+5); | |
pop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment