Added common shape collision detection
Triangle will probably be the next one. I decided against using rays and did line segments instead. Probably much more broadly viable.
This commit is contained in:
parent
81ef85e1bf
commit
c143b4d789
3 changed files with 169 additions and 37 deletions
|
|
@ -74,6 +74,16 @@ asin :: (ang: float) -> float #expand {
|
|||
return from_rad(math.asin(ang));
|
||||
}
|
||||
|
||||
max :: (x: float, y: float) -> float {
|
||||
if x < y return y;
|
||||
return x;
|
||||
}
|
||||
|
||||
min :: (x: float, y: float) -> float {
|
||||
if x < y return x;
|
||||
return y;
|
||||
}
|
||||
|
||||
abs :: (v: $T) -> T
|
||||
#modify { return meta.type_is_scalar(T), "Only accepting scalar types, integer and float"; } {
|
||||
return ifx v < 0 then -v else v;
|
||||
|
|
|
|||
169
math/shape.jai
169
math/shape.jai
|
|
@ -1,51 +1,160 @@
|
|||
|
||||
Rect :: #type,distinct Vec(4, RECT_TYPE);
|
||||
|
||||
Circle :: struct {
|
||||
x,y: float;
|
||||
r: float;
|
||||
#place x;
|
||||
pos: Vec2;
|
||||
}
|
||||
|
||||
Line :: struct {
|
||||
x0,y0,x1,y1: float;
|
||||
#place x0;
|
||||
a: Vec2;
|
||||
#place x1;
|
||||
b: Vec2;
|
||||
}
|
||||
|
||||
make_rect :: (x: RECT_TYPE, y: RECT_TYPE, w: RECT_TYPE, h: RECT_TYPE) -> Rect {
|
||||
r: Rect = ---;
|
||||
r.x = x;
|
||||
r.y = y;
|
||||
r.width = w;
|
||||
r.height = h;
|
||||
return r;
|
||||
r: Rect = ---;
|
||||
r.x = x;
|
||||
r.y = y;
|
||||
r.width = w;
|
||||
r.height = h;
|
||||
return r;
|
||||
}
|
||||
|
||||
cut_left :: (rect: *Rect, want: RECT_TYPE) -> Rect {
|
||||
amnt := basic.min(want, rect.width);
|
||||
r := rect.*;
|
||||
r.width = amnt;
|
||||
rect.x += amnt;
|
||||
rect.width -= amnt;
|
||||
return r;
|
||||
amnt := basic.min(want, rect.width);
|
||||
r := rect.*;
|
||||
r.width = amnt;
|
||||
rect.x += amnt;
|
||||
rect.width -= amnt;
|
||||
return r;
|
||||
}
|
||||
|
||||
cut_right :: (rect: *Rect, want: RECT_TYPE) -> Rect {
|
||||
amnt := basic.min(want, rect.width);
|
||||
r := rect.*;
|
||||
r.x += rect.width - amnt;
|
||||
r.width = amnt;
|
||||
rect.width -= r.width;
|
||||
return r;
|
||||
amnt := basic.min(want, rect.width);
|
||||
r := rect.*;
|
||||
r.x += rect.width - amnt;
|
||||
r.width = amnt;
|
||||
rect.width -= r.width;
|
||||
return r;
|
||||
}
|
||||
|
||||
cut_top :: (rect: *Rect, want: RECT_TYPE) -> Rect {
|
||||
amnt := basic.min(rect.height, want);
|
||||
r := rect.*;
|
||||
r.height -= amnt;
|
||||
rect.y += amnt;
|
||||
rect.height -= amnt;
|
||||
return r;
|
||||
amnt := basic.min(rect.height, want);
|
||||
r := rect.*;
|
||||
r.height -= amnt;
|
||||
rect.y += amnt;
|
||||
rect.height -= amnt;
|
||||
return r;
|
||||
}
|
||||
|
||||
cut_bottom :: (rect: *Rect, want: RECT_TYPE) -> Rect {
|
||||
amnt := basic.min(want, rect.height);
|
||||
r := rect.*;
|
||||
r.y += r.height - amnt;
|
||||
r.height = amnt;
|
||||
rect.height -= amnt;
|
||||
return r;
|
||||
amnt := basic.min(want, rect.height);
|
||||
r := rect.*;
|
||||
r.y += r.height - amnt;
|
||||
r.height = amnt;
|
||||
rect.height -= amnt;
|
||||
return r;
|
||||
}
|
||||
|
||||
area :: (r: Rect) -> float {
|
||||
return r.width*r.height;
|
||||
}
|
||||
|
||||
area :: (c: Circle) -> float {
|
||||
return PI*c.r*c.r;
|
||||
}
|
||||
|
||||
inside :: (r: Rect, p: Vec2) -> bool #symmetric {
|
||||
return p.x >= r.x && p.x <= r.x + r.width &&
|
||||
p.y >= r.y && p.y <= r.y + r.height;
|
||||
}
|
||||
|
||||
inside :: (c: Circle, p: Vec2) -> bool #symmetric {
|
||||
dp := p - c.pos;
|
||||
return dp.x*dp.x + dp.y*dp.y <= c.r*c.r;
|
||||
}
|
||||
|
||||
collides :: (c1: Circle, c2: Circle) -> bool {
|
||||
dp := c2.pos - c1.pos;
|
||||
return c1.r + c2.r >= length(dp);
|
||||
}
|
||||
|
||||
// Note(Jesse): This is using sdfs. Very elegant
|
||||
collides :: (c: Circle, r: Rect) -> bool #symmetric {
|
||||
// We need to 'transpose' all the math to the origin (centered at 0,0)
|
||||
r_center: Vec2 = get_center(r);
|
||||
p := r_center - c.pos;
|
||||
d: Vec2 = abs(p) - v2f(r.width/2, r.height/2);
|
||||
dist := length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
|
||||
dist -= c.r; // 'adding' the distance of the circle
|
||||
return dist <= 0.0;
|
||||
}
|
||||
|
||||
// Note(Jesse): Minkowski difference
|
||||
collides :: (r1: Rect, r2: Rect) -> bool {
|
||||
r: Rect = make_rect(r1.x - r2.width/2, r1.y - r2.width/2, r1.width + r2.width, r1.height + r2.height);
|
||||
return inside(r, get_center(r2));
|
||||
}
|
||||
|
||||
collides :: (c: Circle, seg: Line) -> bool #symmetric {
|
||||
pa := c.pos - seg.a;
|
||||
ba := seg.b - seg.a;
|
||||
// Note(Jesse): Using clamp here will cause small segments to collide even when they don't
|
||||
h: float = min(max(dot(pa,ba)/dot(ba,ba), 0.0), 1.0);
|
||||
dist := length(pa - ba*h);
|
||||
return dist - c.r <= 0.0;
|
||||
}
|
||||
|
||||
collides :: (s1: Line, s2: Line) -> bool {
|
||||
denom := ((s2.y1 - s2.y0)*(s1.x1 - s1.x0) - (s2.x1 - s2.x0)*(s1.y1 - s1.y0));
|
||||
|
||||
a := ((s2.x1 - s2.x0)*(s1.y0 - s2.y0) - (s2.y1 - s2.y0)*(s1.x0 - s2.x0));
|
||||
b := ((s1.x1 - s1.x0)*(s1.y0 - s2.y0) - (s1.y1 - s1.y0)*(s1.x0 - s2.x0));
|
||||
|
||||
if denom == 0.0
|
||||
return false;
|
||||
|
||||
a /= denom;
|
||||
b /= denom;
|
||||
|
||||
return (a >= 0 && a <= 1) && (b >= 0 && b <= 1);
|
||||
}
|
||||
|
||||
// Todo(Jesse): I want to find a better line segment rectangle algorithm.
|
||||
// one option is a line clipping algorithm. Cohen-Sutherland algorithm
|
||||
// would be the best option. It does accepts/rejects fastest
|
||||
// but still has a while in it, and a few ifs.
|
||||
collides :: (r: Rect, seg: Line) -> bool #symmetric {
|
||||
p1 := Vec2.{r.x, r.y};
|
||||
p2 := Vec2.{r.x + r.width, r.y};
|
||||
p3 := Vec2.{r.x, r.y + r.height};
|
||||
p4 := Vec2.{r.x + r.width, r.y + r.height};
|
||||
|
||||
l1 := Line.{a=p1, b=p2};
|
||||
l2 := Line.{a=p1, b=p3};
|
||||
l3 := Line.{a=p3, b=p4};
|
||||
l4 := Line.{a=p2, b=p4};
|
||||
|
||||
return collides(seg, l1) || collides(seg, l2) || collides(seg, l3) || collides(seg, l4);
|
||||
}
|
||||
|
||||
///////
|
||||
// Helpers and operators
|
||||
|
||||
get_center :: (r: Rect) -> Vec2 {
|
||||
return v2f(r.x + r.width/2, r.y + r.height/2);
|
||||
}
|
||||
|
||||
#scope_file
|
||||
|
||||
basic :: #import "Basic";
|
||||
|
||||
#if RUN_TESTS #run,stallable {
|
||||
test.run(basic.tprint("%: Vec2", UNITS), t => {
|
||||
});
|
||||
}
|
||||
|
|
|
|||
27
math/vec.jai
27
math/vec.jai
|
|
@ -434,13 +434,26 @@ length :: (v: Vec) -> float #no_abc {
|
|||
|
||||
abs :: (v: Vec) -> Vec(v.N, v.T) #no_abc {
|
||||
r: Vec(v.N, v.T) = ---;
|
||||
n := v.N - 1;
|
||||
while n >= 0 {
|
||||
if v[n] < 0
|
||||
r[n] = -v[n];
|
||||
else
|
||||
r[n] = v[n];
|
||||
n -= 1;
|
||||
#if v.N <= 4 {
|
||||
r = v;
|
||||
if r.x < 0 r.x = -r.x;
|
||||
if r.y < 0 r.y = -r.y;
|
||||
#if v.N >= 3 {
|
||||
if r.z < 0 r.z = -r.z;
|
||||
}
|
||||
#if v.N == 4 {
|
||||
if r.w < 0 r.w = -r.w;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n := v.N - 1;
|
||||
while n >= 0 {
|
||||
if v[n] < 0
|
||||
r[n] = -v[n];
|
||||
else
|
||||
r[n] = v[n];
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue