fuzzyworld

tower / files / test / water.test.ts

water.test.ts

3.1 KB · history · blame

1import { describe, expect, it } from 'vitest'
2import { freeFallDriftM } from '../src/canon/motion.js'
3import { fallPoint } from '../src/terrain/falls.js'
4import { fetchOf, foamOf, manningSpeed, windSea } from '../src/terrain/water.js'
5
6/** The figures world-generation.md §4.2 and §4.4 print, so the doc and the code agree. */
7describe('wind waves', () => {
8 it('a 10 m/s wind across 30 km of lake raises 0.9 m waves at a 4.2 s period', () => {
9 const s = windSea(10, 30_000)
10 expect(s.heightM).toBeCloseTo(0.9, 1)
11 expect(s.periodS).toBeCloseTo(4.2, 1)
12 })
13
14 it('a mountain tarn 2 km across raises 0.2 m', () => {
15 expect(windSea(10, 2000).heightM).toBeCloseTo(0.23, 2)
16 })
17
18 it('a ring sea, with unlimited fetch, is fully developed: 2.1 m at 10 m/s, 8.6 m at 20', () => {
19 expect(windSea(10, Infinity).heightM).toBeCloseTo(2.14, 2)
20 expect(windSea(20, Infinity).heightM).toBeCloseTo(8.57, 2)
21 })
22
23 it('never builds a sea past fully developed, however far the wind blows', () => {
24 expect(windSea(15, 5e6).heightM).toBeLessThanOrEqual(windSea(15, Infinity).heightM + 1e-9)
25 })
26
27 it('gives deep-water wavelength for the peak period', () => {
28 const s = windSea(10, 30_000)
29 expect(s.lengthM).toBeCloseTo((9.80665 * s.periodS ** 2) / (2 * Math.PI), 6)
30 })
31
32 it('makes no waves without wind', () => {
33 expect(windSea(0, 1e5).heightM).toBe(0)
34 })
35})
36
37describe('fetch', () => {
38 const band = { type: 'band', zFrom: 0, zTo: 42_000 } as const
39 it('is unlimited around a ring sea', () => {
40 expect(fetchOf(band, Math.PI / 2)).toBe(Infinity)
41 })
42 it('is the width of the band for a wind straight along the axis', () => {
43 expect(fetchOf(band, 0)).toBeCloseTo(42_000, 6)
44 })
45})
46
47describe('rivers', () => {
48 it('runs a lowland river at a walking pace and a mountain one fast', () => {
49 expect(manningSpeed(2.7, 0.002)).toBeGreaterThan(1)
50 expect(manningSpeed(2.7, 0.002)).toBeLessThan(3)
51 expect(manningSpeed(1.5, 0.08)).toBeGreaterThan(manningSpeed(1.5, 0.002))
52 })
53 it('turns white as it steepens', () => {
54 expect(foamOf(0.002)).toBe(0)
55 expect(foamOf(0.2)).toBe(1)
56 })
57})
58
59describe('waterfalls', () => {
60 const fall = { river: 'r', z: 100_000, a: 5000, dz: 1, da: 0, topM: 1700, bottomM: 700, widthM: 60, speedMS: 3 }
61
62 it('lands antispinward of the lip by the free-fall drift: 133 m for a kilometre', () => {
63 const lip = fallPoint(fall, 0, 0)
64 const foot = fallPoint(fall, 0, 1000)
65 // A river running deepward: spinward is +a, so the drift is a loss of a.
66 expect(lip.a - foot.a).toBeCloseTo(freeFallDriftM(1000), 6)
67 expect(lip.a - foot.a).toBeCloseTo(133, -1)
68 })
69
70 it('carries the river forward over the lip at its own speed', () => {
71 const t = Math.sqrt((2 * 1000) / 9.80665)
72 expect(fallPoint(fall, 0, 1000).z - fall.z).toBeCloseTo(3 * t, 6)
73 })
74
75 it('leans the same way whichever way the river runs', () => {
76 const spinwardRiver = { ...fall, dz: 0, da: 1 }
77 const foot = fallPoint(spinwardRiver, 0, 500)
78 const noSpin = spinwardRiver.a + spinwardRiver.da * spinwardRiver.speedMS * Math.sqrt((2 * 500) / 9.80665)
79 expect(noSpin - foot.a).toBeCloseTo(freeFallDriftM(500), 6)
80 })
81})