fuzzyworld

tower / cascadia / files / test / raster.test.ts

Branch cascadia · default branch · all branches

raster.test.ts

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

5a57ebae 2026-09-07 Pavel Safronov1import { readFileSync } from 'node:fs'
2import { describe, expect, it } from 'vitest'
3import {
4 RASTER_HEIGHT, RASTER_MAX_HEIGHT_M, RASTER_MIN_HEIGHT_M, RASTER_WIDTH,
1affced2 2026-09-08 Pavel Safronov5 isSurface, rasterSet,
5a57ebae 2026-09-07 Pavel Safronov6} from '../src/views/raster.js'
7import { MAP_CUT_WITHOUT_SLATS } from '../src/canon/geometry.js'
8
9const geo = JSON.parse(readFileSync('content/geography.json', 'utf8')) as {
10 render: {
11 gridWidth: number; gridHeight: number
12 minHeightM: number; maxHeightM: number; mapThetaOrigin: number
13 }
14}
15
16/**
17 * The interior view displaces its shell by the height raster and paints it with the
18 * colour raster. Both are read as fractions of numbers the app cannot see, so if
19 * the generator's idea of them ever drifts, the terrain moves or flattens with no
20 * error anywhere.
21 */
22describe('raster metadata matches the generator', () => {
23 it('agrees on the grid', () => {
24 expect(geo.render.gridWidth).toBe(RASTER_WIDTH)
25 expect(geo.render.gridHeight).toBe(RASTER_HEIGHT)
26 })
27
28 it('agrees on what black and white mean in the height map', () => {
29 expect(geo.render.minHeightM).toBe(RASTER_MIN_HEIGHT_M)
30 expect(geo.render.maxHeightM).toBe(RASTER_MAX_HEIGHT_M)
31 })
32
33 /**
34 * The range is declared rather than fitted so the raster keeps its meaning as the
35 * geography is edited. That only works if it actually contains the terrain, so
36 * check it still does — the generator warns, but nobody watches a build log.
37 */
38 it('still covers the terrain it is meant to cover', () => {
9a621732 2026-09-08 Pavel Safronov39 // The extremes the generator last measured. These are a second line of defence
40 // and a weak one — they are numbers written down when they were true, not the
41 // terrain that came out of the most recent run, so they cannot notice the
42 // geography growing past them. `npm run terrain` now exits non-zero when a
43 // pixel is clamped, which is the check that actually holds.
44 expect(RASTER_MIN_HEIGHT_M).toBeLessThanOrEqual(-521)
45 expect(RASTER_MAX_HEIGHT_M).toBeGreaterThanOrEqual(4381)
5a57ebae 2026-09-07 Pavel Safronov46 })
47
48 it('agrees on where the map is cut', () => {
49 expect(geo.render.mapThetaOrigin).toBeCloseTo(MAP_CUT_WITHOUT_SLATS, 6)
50 })
51})
1affced2 2026-09-08 Pavel Safronov52
53/**
54 * The Cascadia surface is a second pair of rasters on the same cylinder (D-055).
55 * It only works because it shares canon's grid and height window: the interior
56 * view bakes that window into its GLSL as literals, so a surface that quietly
57 * declared its own would displace its ground by the wrong scale — silently, which
58 * is D-024's failure mode exactly. tools/cascadia.mjs cannot import this module,
59 * so read the constants back out of it and hold it to them.
60 */
61describe('every surface shares one height window', () => {
62 const src = readFileSync('tools/cascadia.mjs', 'utf8')
63 const constant = (name: string): number => {
64 // The declarations carry trailing comments, so match the number, not the line.
65 const m = new RegExp(`^const ${name} = (-?\\d+)\\b`, 'm').exec(src)
66 if (!m) throw new Error(`tools/cascadia.mjs no longer declares ${name}`)
67 return Number(m[1])
68 }
69
70 it('cascadia borrows canon\u2019s range rather than declaring its own', () => {
71 expect(constant('HEIGHT_MIN_M')).toBe(RASTER_MIN_HEIGHT_M)
72 expect(constant('HEIGHT_MAX_M')).toBe(RASTER_MAX_HEIGHT_M)
73 })
74
75 it('cascadia is sampled onto canon\u2019s grid', () => {
76 expect(constant('GRID_W')).toBe(RASTER_WIDTH)
77 expect(constant('GRID_H')).toBe(RASTER_HEIGHT)
78 })
79
80 it('names a distinct pair of rasters per surface', () => {
81 expect(rasterSet('canon')).not.toEqual(rasterSet('cascadia'))
82 for (const s of ['canon', 'cascadia'] as const) {
83 expect(rasterSet(s).color).toMatch(/\.png$/)
84 expect(rasterSet(s).height).toMatch(/\.png$/)
85 }
86 })
87
88 it('rejects a surface name it does not know', () => {
89 expect(isSurface('canon')).toBe(true)
90 expect(isSurface('cascadia')).toBe(true)
91 expect(isSurface('wisconsin')).toBe(false)
92 expect(isSurface(null)).toBe(false)
93 })
94})