first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `atmospheric` module provides the two physics primitives every planetary renderer, sky-colour solver, and observation-quality estimator needs: the local density of an atmospheric column at altitude, and the relative path length light travels through that column for a given elevation angle. Both functions accept a `PlanetConfig` so the same code path serves Earth, Mars, Titan, or any other modelled body.
These primitives are intentionally pure and free of vendor coupling so they can be reused by `@suaveplan/universe`, `@suaveplan/gaming-environment-lighting`, server-side astronomical computations, and offline analysis without dragging a graphics runtime into the call site.
## Features
- `atmosphericDensity(planet, altitudeMetres)` — exponential isothermal scale-height model that returns `0` for airless bodies
- `airMass(altitudeDeg)` — Kasten–Young 1989 air-mass approximation valid from zenith down to the horizon
- Numerical-stable horizon handling: negative altitudes are clamped to `0` so the formula remains finite at the bound
- Pure functions: no I/O, no side effects, no global state — safe for `Worker` threads, SSR, and deterministic tests
## Usage
### Basic Example
```ts
import { atmosphericDensity } from "@suaveplan/geo-planet";
import type { PlanetConfig } from "@suaveplan/geo-planet";
const earth: PlanetConfig = {
name: "Earth",
mass: 5.972e24,
radius: 6_378_137,
polarRadius: 6_356_752,
surfaceGravity: 9.81,
rotationPeriod: 86_164,
axialTilt: 23.44,
atmosphere: {
surfacePressure: 101_325,
scaleHeight: 8_500,
rayleighCoefficient: 5.8e-6,
mieCoefficient: 21e-6,
extinctionPerAirMass: 0.13,
},
satellites: [],
};
const surface = atmosphericDensity(earth, 0); // 101325 Pa
const cruise = atmosphericDensity(earth, 11_000); // ~27 kPa
```
### Advanced Example
```ts
import { airMass, atmosphericDensity } from "@suaveplan/geo-planet";
import type { PlanetConfig } from "@suaveplan/geo-planet";
function transmittance(
planet: PlanetConfig,
altitudeMetres: number,
sunElevationDeg: number,
): number {
if (!planet.atmosphere) return 1;
const density = atmosphericDensity(planet, altitudeMetres);
const am = airMass(sunElevationDeg);
const tau = planet.atmosphere.extinctionPerAirMass * am;
return Math.exp(-tau) * (density / planet.atmosphere.surfacePressure);
}
// Use at every camera step in a sky shader, or to score
// observability for ground-station pass scheduling.
```
## API Reference
### `atmosphericDensity(planet: PlanetConfig, altitudeMetres: number): number`
Returns the local pressure (`Pa`) using `p₀ · exp(-h / H)` where `p₀` is `planet.atmosphere.surfacePressure` and `H` is `planet.atmosphere.scaleHeight`. Returns `0` when `planet.atmosphere === null`. No clamping is applied to altitude; negative altitudes (subterranean, sub-sea-level) yield pressures above `p₀`, which is the physically correct extrapolation for a barometric law.
### `airMass(altitudeDeg: number): number`
Returns the Kasten–Young air mass:
`1 / (sin(alt) + 0.50572 · (alt + 6.07995)^-1.6364)`
with `alt` in degrees. Negative altitudes are clamped to `0` so the result remains finite at the horizon (returning ≈ `38`). At zenith the return value approaches `1`.
## Implementation Notes
The density function uses the isothermal barometric equation rather than a layered ISA model because every consumer in this project treats density as a smooth analytic function for shader sampling. The Kasten–Young formulation was chosen over the simpler `1 / sin(alt)` secant model because it remains stable down to the horizon and only diverges by ≈ `0.4%` from full atmospheric ray-tracing above 5° elevation. Both functions are allocation-free hot-path code; consumers can call them once per pixel without GC pressure.