Adds the proprietary all-rights-reserved license that applies to this library. The text is identical to the engine LICENSE — this lib is Tier-1 official platform content, distributed under the same terms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lib-core.camera
2D camera state + begin/finish render wrapper, with free-pan (keys/edge/drag), bounds-clamping, and screen-to-world unproject. Wraps engine.render.camera_2d_begin/end with current target + zoom.
Version: 0.3.0
Lib-ID: lib-core.camera
Requires: lib-core.input v>=0.4.0
Tags: camera, viewport, transform, pan, bounds
Topology
graph LR
this["lib-core.camera"]
lib_core_input["lib-core.input"]
this --> lib_core_input
engine["engine.*"]
this --> engine
API
camera.set_target(x, y)
Syntax: camera.set_target(x: number, y: number) -> void
Example:
camera.set_target(256, 256) -- world-coords for screen-center
Description: Sets the world-coords the camera centers on. Clamps to current bounds (if set).
camera.set_zoom(z)
Syntax: camera.set_zoom(z: number) -> void
Example:
camera.set_zoom(1.0)
Description: Sets zoom factor. Must be a positive number.
camera.target()
Syntax: camera.target() -> number, number
Example:
local x, y = camera.target()
Description: Returns current target world-coords.
camera.zoom()
Syntax: camera.zoom() -> number
Example:
local z = camera.zoom()
Description: Returns current zoom factor.
camera.begin()
Syntax: camera.begin() -> void
Example:
camera.begin()
-- draw world here
camera.finish()
Description: Pushes the camera transform onto the render stack. Must be called inside a render hook.
camera.finish()
Syntax: camera.finish() -> void
Description: Pops the camera transform. Named finish because end is a Lua keyword.
camera.bind_pan_keys(left, right, up, down)
Syntax: camera.bind_pan_keys(left: string, right: string, up: string, down: string) -> void
Example:
input.bind("cam_left", { "a" })
input.bind("cam_right", { "d" })
input.bind("cam_up", { "w" })
input.bind("cam_down", { "s" })
camera.bind_pan_keys("cam_left", "cam_right", "cam_up", "cam_down")
Description: Binds 4 input-actions for keyboard pan. Per-frame summation happens in update(dt).
camera.bind_pan_drag(action)
Syntax: camera.bind_pan_drag(action: string) -> void
Example:
input.bind("cam_drag", { "mouse_middle" })
camera.bind_pan_drag("cam_drag")
Description: Binds an input-action that, while held, pans by mouse-delta (zoom-scaled).
camera.enable_pan_edge()
Syntax: camera.enable_pan_edge() -> void
Description: Enables edge-scroll panning: cursor near a window-edge pans the camera at pan_speed. Threshold via set_edge_threshold.
camera.set_pan_speed(n)
Syntax: camera.set_pan_speed(n: number) -> void
Description: Sets pan-speed in world-units per second for keys + edge-scroll.
camera.pan_speed()
Syntax: camera.pan_speed() -> number
Description: Returns current pan-speed.
camera.set_edge_threshold(n)
Syntax: camera.set_edge_threshold(n: number) -> void
Description: Sets edge-scroll threshold in screen-pixels.
camera.edge_threshold()
Syntax: camera.edge_threshold() -> number
Description: Returns current edge-scroll threshold.
camera.set_pan_invert(b)
Syntax: camera.set_pan_invert(b: bool) -> void
Description: Inverts drag-pan direction. False = "drag world" feel, true = "drag camera" feel.
camera.pan_invert()
Syntax: camera.pan_invert() -> bool
Description: Returns current drag-invert setting.
camera.set_pan_enabled(source, b)
Syntax: camera.set_pan_enabled(source: string, b: bool) -> void
Example:
camera.set_pan_enabled("edge", false) -- disable edge-scroll only
Description: Per-source toggle. source is one of "keys", "edge", "drag". All sources default to enabled.
camera.pan_enabled(source)
Syntax: camera.pan_enabled(source: string) -> bool
Description: Returns current toggle-state for the given source.
camera.set_bounds(xmin, ymin, xmax, ymax)
Syntax: camera.set_bounds(xmin: number, ymin: number, xmax: number, ymax: number) -> void
Example:
camera.set_bounds(0, 0, 4096, 4096)
Description: Sets world-space bounds. Once set, set_target and update clamp to keep the target inside. xmin < xmax and ymin < ymax required.
camera.clear_bounds()
Syntax: camera.clear_bounds() -> void
Description: Removes any active bounds; camera becomes free-roaming.
camera.bounds()
Syntax: camera.bounds() -> number, number, number, number | nil
Description: Returns xmin, ymin, xmax, ymax if bounds active, else nil.
camera.update(dt)
Syntax: camera.update(dt: number) -> void
Example:
function update(ctx, dt)
camera.update(dt)
end
Description: Per-frame pan summation: applies keys + edge + drag contributions (each gated by pan_enabled[source]), then clamps to bounds.
camera.screen_to_world(sx, sy)
Syntax: camera.screen_to_world(sx: number, sy: number) -> number, number
Example:
local mx, my = engine.input.get_mouse_pos()
local wx, wy = camera.screen_to_world(mx, my)
Description: Inverse of the Raylib 2D camera transform (origin = window-center, no rotation). Useful for picking, world-space cursors. v0.3.0+.
Conventions
- Y-down-positive per ADR-0031.
- Pan-sources are additive: keys + edge + drag contributions sum each frame.
- Bounds-clamping is always-on once set; use
clear_boundsto disable. - Drag-pan is zoom-scaled so on-screen mouse-distance maps to consistent world-distance.
Consumer pattern
local input = require("lib-core.input")
local camera = require("lib-core.camera")
camera.set_target(256, 256)
camera.set_zoom(1.0)
camera.set_pan_speed(400)
camera.set_bounds(0, 0, 4096, 4096)
input.bind("cam_left", { "a" })
input.bind("cam_right", { "d" })
input.bind("cam_up", { "w" })
input.bind("cam_down", { "s" })
input.bind("cam_drag", { "mouse_middle" })
camera.bind_pan_keys("cam_left", "cam_right", "cam_up", "cam_down")
camera.bind_pan_drag("cam_drag")
camera.enable_pan_edge()
function update(ctx, dt) camera.update(dt) end
function render(ctx)
engine.render.clear_color(0, 0, 0)
camera.begin()
-- draw world here
camera.finish()
end
CHANGELOG
v0.3.0 (P.3.5)
- Added
screen_to_world(sx, sy)— unproject screen-coord to world-coord.
v0.2.0
- Pan-API (keys / edge / drag, additive, per-source toggle).
- Bounds-clamping (always-on once set;
clear_boundsto disable). - Drag-invert + zoom-scaling.
update(dt)for per-frame pan-summation.
v0.1.0 (P.0)
- Initial release: target/zoom + begin/finish.
References
- Spec v0.3.0 (P.3.5):
meta/docs/superpowers/specs/2026-05-13-p3-5-camera-free-pan-design.md - Spec v0.1.0 (P.0):
meta/docs/superpowers/specs/2026-05-09-p0-lib-camera-design.md - Architecture:
meta/docs/architecture/render-pipeline.md,meta/docs/architecture/map-topology.md§7.3 - ADR-0001 (engine knows verbs, libs bring nouns)
- ADR-0031 (pixel-convention: Y-down-positive)
- ADR-0038 (API-Doc-Convention)