A camera is a value, not a host-owned resource. Build the camera you want
for the current frame and pass it to Draw.with_camera!, which applies it
for the duration of a nested scope.
Every constructor and builder here is total: it answers with a Camera2D
rather than a Try. A camera has exactly two invariants -- every transform
field is finite, and the zoom is non-zero so screen_to_world can invert it
-- and the only inputs that can break them are inputs no caller means to
pass. Rather than make every app carry an unreachable Err branch for
those, they are sanitized on the way in. A target or offset component
that is not finite becomes 0, as does a rotation that is not finite; a
zoom that is zero or not finite becomes fallback_zoom, keeping its sign
so a mirrored camera stays mirrored.
Nothing else is touched. Sanitizing only ever rewrites a value a validating
API would have refused outright, so a camera built from ordinary numbers
comes back with its fields unchanged -- negative, axis-mirroring zooms
included.
These types live in the companion roc-ray-types package and are re-exported
here, so a value passes between the two spellings freely and a library can
depend on the package without depending on this platform.
The zoom substituted for a zoom that cannot be used, meaning zero (which
has no inverse) or a non-finite one (which has no meaning).
Deliberately tiny rather than 1: a camera that had to be rescued
collapses the world to a dot at its offset, which reads on screen as the
mistake it is, instead of quietly looking plausible.
Total: non-finite target, offset, and rotation components become
0, and a zoom of zero or one that is not finite becomes
fallback_zoom with the same sign. Every usable setting is kept exactly.
Immutable camera value accepted by drawing and coordinate transforms.
Camera.Camera2D and Draw.CameraMode on the platform are this same
type, re-exported.
Its representation is opaque so non-finite transform fields, or a zero
zoom that has no inverse, cannot bypass the sanitizing every constructor
applies. The module header states what is rewritten and what is left
alone.