The pack
visibility2d
function visibility2d(): Pack<{ visibility: Visibility2dApi }>
The 2D visibility pack: visibility polygons, line of sight and raycasts against wall segments. Headless, no
columns, no passes — a factory for Visibility2d sets. The lighting pack uses the same class for its shadows.
Classes
Visibility2d
class Visibility2d
A set of wall segments and the queries on them: the VISIBILITY POLYGON from a point (rays to every segment
corner ±ε, the nearest hit each, sorted by angle — the classic fan), line of sight between two points, and a
raycast. Pure geometry, no kernel state: game.visibility.create() makes one; the lighting pack keeps one as
game.lights.occluders.
readonly segments: Segment[]— The occluders. Add withaddSegment/addRect/addPoly, or mutate a returned Segment to move a wall.constructor(bounds?: { x: number; y: number; w: number; h: number })— Optional starting bounds (seesetBounds).setBounds(x: number, y: number, w: number, h: number): void— A rectangle rays always stop at, so a polygon in an open world is finite. The lighting pack sets it to the camera's view each frame.addSegment(x1: number, y1: number, x2: number, y2: number): Segment— Add one wall. Returns it, to move or to find and remove later.addRect(x: number, y: number, w: number, h: number): void— Add the four walls of an axis-aligned rectangle (top-left x, y; width, height).addPoly(points: ReadonlyArray<Vec2> | ReadonlyArray<number>, closed = true): void— Add a polyline's edges, as points or a flat [x, y, x, y…] list;closed(default true) joins the last point to the first.remove(s: Segment): void— Remove one segment returned byaddSegment; unknown segments are ignored.clear(): void— Drop every segment (the bounds stay).visibility(lx: number, ly: number, maxDist = Infinity): Vec2[]— The visibility polygon from (lx, ly): its vertices in angle order, ready to fan into triangles.maxDistcaps the rays (a light's radius) and culls segments beyond it. Empty when nothing bounds the rays (no segments in reach, no bounds, no maxDist).canSee(ax: number, ay: number, bx: number, by: number, maxDist = Infinity): boolean— True when the straight line from a to b crosses no segment (bounds ignored) and is withinmaxDist.raycast(ox: number, oy: number, dirX: number, dirY: number, maxDist = Infinity): RayHit | null— The nearest segment (or bounds) hit by a ray from (ox, oy) along (dirX, dirY), withinmaxDist; null when nothing is hit.
Types
RayHit
interface RayHit
What raycast returns when the ray hits something.
x: number— Hit point x.y: number— Hit point y.dist: number— Distance from the origin along the ray.
Segment
interface Segment
One occluding wall segment, from (x1, y1) to (x2, y2). Mutate the fields to move it.
x1: number— Start x.y1: number— Start y.x2: number— End x.y2: number— End y.
Vec2
interface Vec2
A point in world units.
x: number— x, world units.y: number— y, world units.
Visibility2dApi
interface Visibility2dApi
game.visibility.
create(bounds?: { x: number; y: number; w: number; h: number }): Visibility2d— A fresh, empty occluder set. Not scene-scoped: the game holds it (drop it when the level ends).


