tower / interior-flight / files / src / canon / motion.ts
Branch interior-flight · default branch · all branches
motion.ts
a08492ba 2026-09-07 Pavel Safronov | 1 | /** |
| 2 | * How things fall in Tower's rotating frame. See design.md §1.9.2. | |
| 3 | * | |
| 4 | * Coriolis never pushes surface water sideways (§1.9.1), so the spin is invisible | |
| 5 | * in every static thing. The hull is sealed, so there are no stars to wheel | |
| 6 | * overhead either. That leaves what falls as the only evidence in the sky that | |
| 7 | * Tower turns at all, which makes these two angles load-bearing for the interior | |
| 8 | * view rather than decoration. | |
| 9 | * | |
| 10 | * Constant-g plus Coriolis is the standard approximation and is what these use. | |
| 11 | * It is exact only for drops small against the radius: gravity is 2% weaker 1 km | |
| 12 | * up, and the true inertial-frame path is a straight line, not a parabola. | |
| 13 | */ | |
| 14 | import { G0, OMEGA_RAD_S } from './constants.js' | |
| 15 | ||
| 16 | /** Terminal velocity of an ordinary raindrop, m/s. */ | |
| 17 | export const RAINDROP_TERMINAL_M_S = 9 | |
| 18 | ||
| 19 | /** | |
| 20 | * Lean of something already falling at a steady speed, in radians, antispinward. | |
| 21 | * | |
| 22 | * Coriolis gives 2ωv antispinward against g outward, and at terminal velocity drag | |
| 23 | * holds both constant — so rain leans by a *fixed* angle rather than curving. That | |
| 24 | * angle is small: 1.5° for an ordinary raindrop. Rain is a poor compass here. | |
| 25 | */ | |
| 26 | export const steadyFallLeanRad = (speedMS: number): number => | |
| 27 | Math.atan((2 * OMEGA_RAD_S * speedMS) / G0) | |
| 28 | ||
| 29 | /** Time for something dropped from `heightM` to reach the floor. */ | |
| 30 | export const freeFallTimeS = (heightM: number): number => Math.sqrt((2 * heightM) / G0) | |
| 31 | ||
| 32 | /** | |
| 33 | * Antispinward drift of something in free fall from `heightM`. Integrating the | |
| 34 | * Coriolis acceleration 2ω(gt) twice gives ωgt³/3. | |
| 35 | */ | |
| 36 | export const freeFallDriftM = (heightM: number): number => | |
| 37 | (OMEGA_RAD_S * G0 * freeFallTimeS(heightM) ** 3) / 3 | |
| 38 | ||
| 39 | /** | |
| 40 | * Lean of the *path* of a free fall from `heightM` — where it lands, against how | |
| 41 | * far it dropped. This is the tilt of a waterfall's plume seen whole, and it is | |
| 42 | * the column design.md §1.9.2 tabulates: 7.6° over a kilometre. | |
| 43 | */ | |
| 44 | export const freeFallPathLeanRad = (heightM: number): number => | |
| 45 | Math.atan(freeFallDriftM(heightM) / heightM) | |
| 46 | ||
| 47 | /** | |
| 48 | * Lean of the *velocity* at the bottom of the same fall — the direction a streak | |
| 49 | * of it is moving as it arrives, and so the angle rain and spray are drawn at. | |
| 50 | * | |
| 51 | * It reduces to atan(ωt), independent of g, and it is exactly half again the path | |
| 52 | * lean in tangent: the drift integrates to ωgt³/3 over a drop of gt²/2, giving | |
| 53 | * (2/3)ωt, while the sideways speed reaches ωgt² against a fall speed of gt. | |
| 54 | * Both numbers are real and they describe different things, which is why | |
| 55 | * §1.9.2 says 7.6° for a kilometre drop and the interior view's readout says 11.3°. | |
| 56 | * | |
| 57 | * Unlike rain this keeps growing with the drop, so a tall waterfall *is* a compass | |
| 58 | * where a shower is not. | |
| 59 | */ | |
| 60 | export const freeFallLeanRad = (heightM: number): number => | |
| 61 | Math.atan(OMEGA_RAD_S * freeFallTimeS(heightM)) |