glemy/physics/rect

A plain axis-aligned rectangle and the circle-vs-rectangle overlap math a game needs to collide a physics.Entity against a static rectangular obstacle (a paddle, a brick, a platform). Originally built game-local to glemy/games/breakout (decision 0052, when it had exactly one real caller); promoted here once glemy/games/platformer needed the identical detection math for player-vs-platform collision – the same detection-vs-resolution split glemy/physics/collision.overlap already draws against collision.resolve: this module only ever answers “does a circle overlap this rectangle, and along which axis” – what a game does with that answer (reflect off a brick, stop dead on a platform) stays entirely up to the game. See decision 0058 for the full reasoning (a game may never import another game’s own games/<name>/ tree, so once a second game needed this exact math, duplicating it a second time was the only alternative to promoting it here).

A Rect is deliberately still its own type here, not glemy/physics/bounds.Bounds reused: a Bounds is specifically the physics world’s own containing box (what entities bounce inside of), while a Rect is an inert obstacle a circle collides against – different roles that happen to share a shape, not the same concept.

Types

Which axis a collision between ball and rect happened along – whichever axis ball’s center has penetrated further past the nearest point on rect, used by a game to decide which velocity component its own resolution logic should act on (e.g. glemy/games/breakout/brick.reflect flips it; a solid-landing game zeroes it instead).

pub type Axis {
  Horizontal
  Vertical
}

Constructors

  • Horizontal
  • Vertical

An axis-aligned rectangle, defined by its minimum and maximum corners – see this module’s own doc comment for why it’s a distinct type from glemy/physics/bounds.Bounds.

pub type Rect {
  Rect(min: vector2.Vector2, max: vector2.Vector2)
}

Constructors

Values

pub fn nearest_point(
  circle_center: vector2.Vector2,
  rect: Rect,
) -> vector2.Vector2

The point on rect’s boundary (or interior, if circle_center is already inside it) closest to circle_center – clamping each axis independently into rect’s range.

nearest_point(
  Vector2(15.0, 5.0),
  Rect(min: Vector2(0.0, 0.0), max: Vector2(10.0, 10.0)),
)
// -> Vector2(10.0, 5.0)
pub fn overlaps(ball: entity.Entity, rect: Rect) -> Bool

Whether ball overlaps rect – the standard clamp-to-nearest-point circle-vs-AABB test: true if the nearest point on rect to ball’s center is closer than ball.radius. Strict <., matching glemy/physics/collision.overlap’s own convention elsewhere in this project (>. 0.0, never >=.) – exactly touching doesn’t count as overlapping.

pub fn penetration_axis(ball: entity.Entity, rect: Rect) -> Axis

The degenerate case (ball’s center exactly at the nearest point – only reachable via tunneling on an unreasonably large dt) falls back to Horizontal, arbitrarily but harmlessly – same category of fallback as glemy/physics/collision.resolve’s own zero-distance normal case.

pub fn to_css(
  rect: Rect,
  bounds: bounds.Bounds,
) -> #(Float, Float, Float, Float)

Converts a world-space rect into a CSS box within bounds, as #(left_fraction, top_fraction, width_fraction, height_fraction) – each 0.0-1.0, ready to multiply by 100 for a %-based CSS position/size. Originally kept game-local to glemy/game_breakout (exactly one real caller, reused there for both the paddle and every brick); promoted here once glemy/game_platformer needed the identical conversion for its own platform/goal overlay.

to_css(
  Rect(min: Vector2(20.0, 10.0), max: Vector2(40.0, 30.0)),
  Bounds(min: Vector2(0.0, 0.0), max: Vector2(100.0, 100.0)),
)
// -> #(0.2, 0.7, 0.2, 0.2)
Search Document