Resolves which part of a world is on screen.
Cauldron2D.Camera.view(
focus: {x, y},
bounds: {width, height},
size: {columns, rows},
cell: fn {x, y} -> cell end
):cell is called exactly once per visible tile, with that tile's world coordinate, and
returns a Cauldron2D.Surface.cell/0. It is the only thing the engine asks of a game.
With :bounds, the viewport is clamped so it never shows anything outside the world, and a
world smaller than the viewport is centred in it. With :unbounded, the viewport is centred
on :focus and world coordinates may be negative.
A tile occupies two terminal cells across and one down. columns_for/1 and rows_for/1
convert a pane's size in cells into a viewport size in tiles; zoomed/2 scales that
size for a zoom — how many times larger than native a tile is drawn — and tile_at/3
and zoom_about/6 map cells back through it. A :size may be fractional: the origin
centres the focus exactly, and the tiles read cover the fraction.
Summary
Functions
How many tiles wide a pane width terminal cells across can show; always at least 1.
Where a viewport size tiles across sits in world coordinates when focus is at its
exact centre, as floats.
The view as glyphs and colours, for renderers that draw characters.
Where a viewport size tiles across sits in world coordinates when centred on focus.
How many tiles tall a pane height terminal cells high can show; always at least 1.
The world tile under {column, row}, given in terminal cells relative to the pane's
top-left corner, for a viewport whose origin is the first argument.
tile_at/2 through a zoom: a tile drawn zoom times its native size spans 2 * zoom cells across and zoom down.
Resolve the viewport and read every tile in it.
The focus that keeps the world under cell where it is on screen when a pane of native
size goes from from to to zoom, centred on focus before; bounds as view/1
takes them, so that a clamped view stays clamped.
The viewport in tiles of a pane size tiles across at native size, drawn at zoom.
Types
@type t() :: %Cauldron2D.Camera{ cells: [[Cauldron2D.Surface.cell()]], columns: pos_integer(), origin: point(), rows: pos_integer(), scroll: {float(), float()} }
Functions
@spec columns_for(non_neg_integer()) :: pos_integer()
How many tiles wide a pane width terminal cells across can show; always at least 1.
@spec exact_origin( {number(), number()}, {number(), number()}, {pos_integer(), pos_integer()} | :unbounded ) :: {float(), float()}
Where a viewport size tiles across sits in world coordinates when focus is at its
exact centre, as floats.
Clamped to the world like origin/3; a world smaller than the viewport is centred.
@spec glyphs(Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(), t()) :: [ [{String.t(), Cauldron2D.Atlas.rgb()}] ]
The view as glyphs and colours, for renderers that draw characters.
Each cell becomes {glyph, {r, g, b}} with the tint already applied, in the same row and
column order as :cells. A cell with overlays takes the glyph and colour of its topmost
overlay; :void becomes two spaces in the atlas's void colour.
@spec origin( point(), {pos_integer(), pos_integer()}, {pos_integer(), pos_integer()} | :unbounded ) :: point()
Where a viewport size tiles across sits in world coordinates when centred on focus.
This is the :origin view/1 produces for the same arguments, so a caller mapping a screen
position back to the world resolves against the same tiles that were drawn.
A fractional focus is rounded to the nearest tile first.
With {width, height} bounds the result is clamped to 0..width - columns horizontally and
0..height - rows vertically. When the world is narrower or shorter than the viewport, that
axis is centred instead, which puts the origin at a negative coordinate.
@spec rows_for(non_neg_integer()) :: pos_integer()
How many tiles tall a pane height terminal cells high can show; always at least 1.
The world tile under {column, row}, given in terminal cells relative to the pane's
top-left corner, for a viewport whose origin is the first argument.
The result is fractional and resolves to the centre of the cell, so trunc/1 on either
coordinate gives the whole tile the cell belongs to. The column is halved, since a tile is
two cells wide: cells 0 and 1 both fall in tile 0, at 0.25 and 0.75.
Pair with origin/3 to map a pointer position back to the world it is over.
tile_at/2 through a zoom: a tile drawn zoom times its native size spans 2 * zoom cells across and zoom down.
Resolve the viewport and read every tile in it.
Returns a t/0 whose :cells is a list of rows rows, each of columns cells, in
top-to-bottom and left-to-right order, and whose :origin is the world coordinate of the
top-left tile.
Options
:focus— the{x, y}world point to centre on, required:size—{columns, rows}in tiles, required, both positive, whole or not:cell—fn {x, y} -> cell end, required:bounds—{width, height}of the world in tiles, or:unbounded. Default:unbounded:scroll—trueto keep the focus at the exact centre::originis then the exact origin rounded down,:cellsholds one more column and one more row than:size, and:scrollis the fraction of a tile,{0.0..1.0, 0.0..1.0}, the cells are to be shifted left and up by. Defaultfalse::originisorigin/3,:cellsis exactly:size,:scrollis{0.0, 0.0}
@spec zoom_about( {number(), number()}, {number(), number()}, {number(), number()}, number(), number(), {pos_integer(), pos_integer()} | :unbounded ) :: {float(), float()}
The focus that keeps the world under cell where it is on screen when a pane of native
size goes from from to to zoom, centred on focus before; bounds as view/1
takes them, so that a clamped view stays clamped.
The viewport in tiles of a pane size tiles across at native size, drawn at zoom.