docs: migrate README to API-Doc-Convention (S2-cascade.libs)
Restructured per ADR-0038: Abstract + bold-list Badges + Topology H2 + H3-API-Subsections (one per public function) + Conventions/Consumer pattern/CHANGELOG/References. Topology auto-populated via Sporel.exe --lint --fix. Pre-commit hook installed via --install-hooks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
257
README.md
257
README.md
@@ -1,33 +1,258 @@
|
||||
# lib-core.camera
|
||||
|
||||
P.0 2D camera state + begin/finish wrapper. Holds `target = {x, y}` + `zoom`,
|
||||
wraps `engine.render.camera_2d_begin/end` with current state.
|
||||
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.
|
||||
|
||||
- Lib-ID: `lib-core.camera`
|
||||
- Version: `0.1.0`
|
||||
- Spec: `meta/docs/superpowers/specs/2026-05-09-p0-lib-camera-design.md`
|
||||
**Version:** 0.3.0
|
||||
**Lib-ID:** lib-core.camera
|
||||
**Requires:** lib-core.input v>=0.4.0
|
||||
**Tags:** camera, viewport, transform, pan, bounds
|
||||
|
||||
Forward-compat stubs (DEPRECATED-MVP) for follow, set_bounds, lerp_to, shake,
|
||||
set_rotation. See `meta/docs/architecture/render-pipeline.md` and
|
||||
`meta/docs/architecture/map-topology.md §7.3` for full target context.
|
||||
## Topology
|
||||
|
||||
<!-- topology:start (auto-generated; do not edit) -->
|
||||
```mermaid
|
||||
graph LR
|
||||
this["lib-core.camera"]
|
||||
lib_core_input["lib-core.input"]
|
||||
this --> lib_core_input
|
||||
engine["engine.*"]
|
||||
this --> engine
|
||||
```
|
||||
<!-- topology:end -->
|
||||
|
||||
## API
|
||||
- `camera.set_target(x, y)` — set world-coords for screen-center
|
||||
- `camera.set_zoom(z)` — set zoom (positive number)
|
||||
- `camera.target() → {x, y}` — current target
|
||||
- `camera.zoom() → number` — current zoom
|
||||
- `camera.begin()` — push camera transform (must be inside render hook)
|
||||
- `camera.finish()` — pop camera transform (`end` is Lua keyword)
|
||||
|
||||
### `camera.set_target(x, y)`
|
||||
**Syntax:** `camera.set_target(x: number, y: number) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
camera.set_zoom(1.0)
|
||||
```
|
||||
|
||||
**Description:** Sets zoom factor. Must be a positive number.
|
||||
|
||||
### `camera.target()`
|
||||
**Syntax:** `camera.target() -> number, number`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
local x, y = camera.target()
|
||||
```
|
||||
|
||||
**Description:** Returns current target world-coords.
|
||||
|
||||
### `camera.zoom()`
|
||||
**Syntax:** `camera.zoom() -> number`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
local z = camera.zoom()
|
||||
```
|
||||
|
||||
**Description:** Returns current zoom factor.
|
||||
|
||||
### `camera.begin()`
|
||||
**Syntax:** `camera.begin() -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
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:**
|
||||
```lua
|
||||
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_bounds` to disable.
|
||||
- Drag-pan is zoom-scaled so on-screen mouse-distance maps to consistent world-distance.
|
||||
|
||||
## Consumer pattern
|
||||
|
||||
```lua
|
||||
local input = require("lib-core.input")
|
||||
local camera = require("lib-core.camera")
|
||||
camera.set_target(256, 256) -- world-coords
|
||||
|
||||
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(...)
|
||||
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_bounds` to 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)
|
||||
|
||||
Reference in New Issue
Block a user