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,111 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `planet-surface` module provides the `PlanetSurface` class, an oblate-ellipsoid wrapper around a `PlanetConfig` that exposes the geodetic operations every multi-planet system needs: great-circle distance, initial bearing, terminal-point projection from a heading and distance, and point-in-polygon tests. It is the single bridge between body-specific configuration (Earth, Mars, Titan, …) and the body-agnostic geometry primitives in `@suaveplan/geo`.
By deriving the ellipsoid once per `PlanetSurface` instance from the `radius` and `polarRadius` fields of `PlanetConfig`, callers never have to thread an ellipsoid through every distance call, and they cannot accidentally mix Earth-flattening into a Mars computation.
## Features
- `PlanetSurface` class — owns the planet config and the cached `Ellipsoid`
- `distance(a, b, unit?)` — geodesic great-circle distance with selectable distance unit
- `bearing(from, to)` — initial forward azimuth in degrees from north, clockwise
- `destination(start, bearingDeg, distanceM)` — terminal-point projection by direct-geodesic solution
- `pointInPolygon(point, polygon)` — spherical-coordinate ray-cast / winding test for arbitrary closed rings
- Body-agnostic — works for any planet whose oblateness is well-modelled by an ellipsoid of revolution
## Usage
### Basic Example
```ts
import { PlanetSurface } 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: null,
satellites: [],
};
const surface = new PlanetSurface(earth);
const km = surface.distance(
{ latitude: 51.5, longitude: -0.13 }, // London
{ latitude: 40.71, longitude: -74.0 }, // New York
"kilometre",
);
```
### Advanced Example
```ts
import { PlanetSurface } from "@suaveplan/geo-planet";
import type { PlanetConfig } from "@suaveplan/geo-planet";
const mars: PlanetConfig = {
name: "Mars",
mass: 6.39e23,
radius: 3_396_200,
polarRadius: 3_376_200,
surfaceGravity: 3.72,
rotationPeriod: 88_620,
axialTilt: 25.2,
atmosphere: null,
satellites: [],
};
const surface = new PlanetSurface(mars);
// Project a 100 km traverse from Jezero crater on a 45° heading.
const start = { latitude: 18.44, longitude: 77.45 };
const end = surface.destination(start, 45, 100_000);
// Heading toward Olympus Mons.
const heading = surface.bearing(start, { latitude: 18.65, longitude: -133.8 });
// Region containment for science-target zones.
const aoi = [
{ latitude: 18.0, longitude: 77.0 },
{ latitude: 19.0, longitude: 77.0 },
{ latitude: 19.0, longitude: 78.0 },
{ latitude: 18.0, longitude: 78.0 },
];
const inside = surface.pointInPolygon(end, aoi);
```
## API Reference
### `class PlanetSurface`
#### `constructor(planet: PlanetConfig)`
Stores the planet on `surface.planet` and constructs an `Ellipsoid` via `defineEllipsoid(planet.radius, planet.polarRadius)` once per instance.
#### `distance(a, b, unit?: DistanceUnit): number`
Returns the great-circle distance between two `SphericalCoord` points along the cached ellipsoid. `unit` defaults to `"metre"`; pass `"kilometre"`, `"mile"`, etc. as supported by `@suaveplan/geo`.
#### `bearing(from, to): number`
Returns the initial forward azimuth in degrees clockwise from north. Note: the spherical bearing in `@suaveplan/geo` does not depend on the ellipsoid.
#### `destination(start, bearingDeg, distanceM): SphericalCoord`
Solves the direct geodesic problem on the cached ellipsoid: given a start point, an initial bearing, and a distance in metres, returns the terminal point.
#### `pointInPolygon(point, polygon: SphericalCoord[]): boolean`
Tests containment of `point` inside the closed polygon ring; uses spherical-coordinate point-in-polygon math from `@suaveplan/geo`.
## Implementation Notes
The ellipsoid is built once at construction and then reused on every method call, which keeps every distance / destination call allocation-free in steady state. Bearing intentionally does not pass the ellipsoid because the underlying `bearing` from `@suaveplan/geo` is a pure spherical primitive — for the precision needed in mission-planning UIs, the difference between spherical and geodetic bearing is below display granularity, and the geometric simplicity is worth more than the marginal accuracy gain. Storing `planet` as a public readonly property lets consumers retain the original config (for atmosphere, satellites, textures, etc.) without round-tripping through a separate registry.