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; } 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; } 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; } 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; } 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; } 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 => { }); }