fuzzyworld

explodorama / files / src / render / reveal.ts

reveal.ts

Blame: the commit that last changed each line · file · history

ebcd0a8f 2026-11-02 Pavel Safronov1/**
2 * How far to move a view so a tile is not within `margin` tiles of its edge (the controller's target).
3 * Pure, so it can be tested without the engine. `aim` is the view's centre and `tile` the tile's
4 * centre, both in world units; `cols` and `rows` are how many tiles the view shows. Zero on an
5 * axis where the tile is already clear of the edge.
6 */
7export function revealShift(
8 aim: { x: number; y: number },
9 tile: { x: number; y: number },
10 cols: number,
11 rows: number,
12 margin = 1,
13 board?: { w: number; h: number },
14): { x: number; y: number } {
15 const axis = (c: number, a: number, size: number): number => {
16 const d = c - a
17 // The gap between the tile's near edge and the view's edge is half the view minus the
18 // tile's distance from the centre minus half a tile; push it back out to `margin`.
19 const over = Math.abs(d) + 0.5 + margin - size / 2
20 return over > 0 ? Math.sign(d) * over : 0
21 }
22 // With `board` (world units, the view stays inside it; a board smaller than the view is centred
23 // and never moves), only the part of the shift the view can really make is returned.
24 const fit = (shift: number, a: number, view: number, size: number | undefined): number => {
25 if (shift === 0 || size === undefined) return shift
26 if (size <= view) return 0
27 return Math.min(size - view / 2, Math.max(view / 2, a + shift)) - a
28 }
29 return {
30 x: fit(axis(tile.x, aim.x, cols), aim.x, cols, board?.w),
31 y: fit(axis(tile.y, aim.y, rows), aim.y, rows, board?.h),
32 }
33}