bodies

package
v0.36.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// G is the universal gravitational constant in m^3 kg^-1 s^-2.
	G = 6.67430e-11
	// AU is one astronomical unit in meters.
	AU = 1.495978707e11
	// SecondsPerDay is exactly 86400.
	SecondsPerDay = 86400.0
	// EarthMassKg is the Earth's mass in kilograms.
	EarthMassKg = 5.9722e24
	// SunMassKg is the Sun's mass in kilograms.
	SunMassKg = 1.98892e30
)

Physical constants (SI).

Variables

View Source
var J2000 = time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC)

J2000 is the J2000.0 epoch: January 1, 2000 12:00 TT (UTC-approximate).

Functions

func CatalogHash added in v0.4.0

func CatalogHash() (string, error)

CatalogHash returns a sha256 hex of the canonical JSON encoding of every loaded system, sorted by name (Sol first). Stamped into save files so a load can reject saves built against a stale universe — e.g. after a v0.5 systems-JSON edit adds Luna and rewrites Body indices, the old save's body references no longer line up.

Hash is over re-marshalled structs, not raw file bytes, so cosmetic JSON whitespace edits don't churn the hash. Only semantic catalog changes (new bodies, mass / element edits) bump it.

The body Texture spec (ADR 0024) is cosmetic, not semantic, so it is zeroed in a hash-specific view before hashing — adding or editing a texture must never reject an existing save. `json:"-"` can't be used on the field itself because that would also block *loading* it; the exclusion lives here, at the hash boundary.

func LoadAllWithWarnings added in v0.7.0

func LoadAllWithWarnings() ([]System, []LoadWarning, error)

LoadAllWithWarnings is the warning-aware variant of LoadAll. The returned warnings slice contains LoadWarning entries for any user overlay files that failed to parse; embedded-catalog parse failures surface as a hard error (returned as the err value) since the embedded set must always load.

Types

type Atmosphere added in v0.8.4

type Atmosphere struct {
	ScaleHeight    float64 `json:"scaleHeight"`    // m
	SurfaceDensity float64 `json:"surfaceDensity"` // kg/m³ at altitude 0
	CutoffAltitude float64 `json:"cutoffAltitude"` // m above surface — drag = 0 above
	Color          string  `json:"color,omitempty"`
}

Atmosphere is an exponential-density atmospheric model: ρ(h) = SurfaceDensity · exp(-h/ScaleHeight) for altitudes below CutoffAltitude, zero above. Color is the haze tint used by the renderer; defaults to body Color when empty.

type CelestialBody

type CelestialBody struct {
	ID              string  `json:"id"`
	Name            string  `json:"name"`
	EnglishName     string  `json:"englishName"`
	BodyType        string  `json:"bodyType"`
	IsPlanet        bool    `json:"isPlanet"`
	Moons           []Moon  `json:"moons,omitempty"`
	SemimajorAxis   float64 `json:"semimajorAxis"`
	Perihelion      float64 `json:"perihelion,omitempty"`
	Aphelion        float64 `json:"aphelion,omitempty"`
	Eccentricity    float64 `json:"eccentricity"`
	Inclination     float64 `json:"inclination"`
	Mass            Mass    `json:"mass"`
	Density         float64 `json:"density,omitempty"`
	Gravity         float64 `json:"gravity,omitempty"`
	Escape          float64 `json:"escape,omitempty"`
	MeanRadius      float64 `json:"meanRadius"`
	SideralOrbit    float64 `json:"sideralOrbit,omitempty"`
	SideralRotation float64 `json:"sideralRotation,omitempty"`
	AroundPlanet    *Planet `json:"aroundPlanet,omitempty"`
	DiscoveredBy    string  `json:"discoveredBy,omitempty"`
	DiscoveryDate   string  `json:"discoveryDate,omitempty"`

	// Stellar-only properties
	Temperature  float64 `json:"temperature,omitempty"`
	StellarClass string  `json:"stellarClass,omitempty"`
	Age          float64 `json:"age,omitempty"`

	// Optional precise Keplerian elements (overrides semimajor/eccentricity/etc.)
	OrbitalElements *OrbitalElement `json:"orbitalElements,omitempty"`

	// Longitude of ascending node (Ω) and argument of periapsis (ω),
	// in degrees. Zero when unknown — flat-plane approximation.
	LongitudeOfAscendingNode float64 `json:"longitudeOfAscendingNode,omitempty"`
	ArgumentOfPeriapsis      float64 `json:"argumentOfPeriapsis,omitempty"`

	// ParentID identifies this body's gravitational parent. Empty
	// means "system primary" (e.g. the Sun for Sol bodies). Set on
	// moons (e.g. Luna.ParentID = "earth"). Drives hierarchical
	// BodyPosition recursion and FindPrimary's nested-SOI walk.
	// v0.5.0+.
	ParentID string `json:"parentId,omitempty"`

	// Color is the rendered display color as a hex string (e.g.
	// "#5BB3FF"). When set, render.ColorFor prefers this over the
	// hardcoded bodyPalette table; when empty, the table fallback +
	// stellar-tint / bodyType-default chain still applies. v0.7.1+.
	Color string `json:"color,omitempty"`

	// SurfaceColor is the horizon-fill colour used by ViewLaunch
	// (the chase-cam scene below the horizon curve). Hex string;
	// empty falls back to Color via SurfaceColorHex. v0.11.0+.
	SurfaceColor string `json:"surfaceColor,omitempty"`

	// Atmosphere, when non-nil, declares an exponential-density
	// atmosphere for this body — drives drag (v0.8.4) and haze
	// rendering. Bodies without atmospheres leave this nil.
	Atmosphere *Atmosphere `json:"atmosphere,omitempty"`

	// TidallyLocked, when true, ties this body's rotation to its
	// orbital period — the same face always points at the parent.
	// SideralRotation is ignored for these bodies; the renderer
	// derives sub-observer longitude from orbital phase. v0.8.5+.
	TidallyLocked bool `json:"tidallyLocked,omitempty"`

	// AxialTilt is the body's obliquity (rotation-axis angle from
	// the orbital-plane normal), in degrees. Drives view-aware
	// texture projection (v0.8.5.7+) — ViewTop on a tilted body
	// reveals polar regions; Uranus's 97° tilt makes it roll
	// pole-on along its orbit.
	AxialTilt float64 `json:"axialTilt,omitempty"`

	// AxialAzimuth is the body's spin-axis azimuth in the world
	// inertial frame, in degrees. The axis projects onto the world
	// X-Y plane at this angle measured counterclockwise from world
	// +X (so 0° tips toward +X, 90° toward +Y, 180° toward -X).
	// Combined with AxialTilt the unit spin axis is
	//
	//	n = (sin(tilt)·cos(azimuth), sin(tilt)·sin(azimuth), cos(tilt))
	//
	// Defaults to 0 — same as the v0.8.5.7 launch behaviour where
	// every body's axis lay in the X-Z plane. Real bodies have
	// varied pole directions; populating this field lets each one
	// tip the right way once we have data.
	AxialAzimuth float64 `json:"axialAzimuth,omitempty"`

	// Texture is the optional data-driven surface-texture spec
	// (ADR 0024). nil renders a flat solid disk; the generic render
	// engine consumes a non-nil block. Cosmetic, not semantic —
	// deliberately excluded from CatalogHash (see catalog.go).
	Texture *Texture `json:"texture,omitempty"`
}

CelestialBody describes a star, planet, or moon by its physical and orbital properties. Units: km for lengths, days for periods, degrees for angles, kg (via Mass.Value * 10^Exponent) for mass.

func LookupByID added in v0.4.0

func LookupByID(systems []System, id string) (CelestialBody, bool)

LookupByID searches every system for a body with the given ID and returns it by value. Used by the save/load layer to rehydrate the craft's primary across system boundaries — v0.1 craft is locked to Sol, but the save schema doesn't bake that assumption in.

func (*CelestialBody) GravitationalParameter

func (cb *CelestialBody) GravitationalParameter() float64

GravitationalParameter returns GM in m^3/s^2.

func (*CelestialBody) MassKg

func (cb *CelestialBody) MassKg() float64

MassKg returns the body's mass in kilograms.

func (*CelestialBody) RadiusMeters

func (cb *CelestialBody) RadiusMeters() float64

RadiusMeters converts the stored mean radius (km) to meters.

func (*CelestialBody) SemimajorAxisMeters

func (cb *CelestialBody) SemimajorAxisMeters() float64

SemimajorAxisMeters converts the stored semimajor axis (km) to meters.

func (*CelestialBody) SideralOrbitSeconds added in v0.8.5

func (cb *CelestialBody) SideralOrbitSeconds() float64

SideralOrbitSeconds converts the stored sidereal orbital period (days) to seconds. Returns 0 when no orbital period is known.

func (*CelestialBody) SideralRotationSeconds added in v0.8.5

func (cb *CelestialBody) SideralRotationSeconds() float64

SideralRotationSeconds converts the stored sidereal rotation period (hours, signed for prograde / retrograde) to seconds. Returns 0 when no rotation period is known.

func (CelestialBody) SurfaceColorHex added in v0.11.0

func (cb CelestialBody) SurfaceColorHex() string

SurfaceColorHex returns the body's launch-view horizon-fill colour, falling back to Color when SurfaceColor is unset.

type LatLonPair added in v0.20.0

type LatLonPair struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

LatLonPair is a single (lat, lon) vertex in degrees.

type LoadWarning added in v0.7.0

type LoadWarning struct {
	Path string
	Err  error
}

LoadWarning is returned by LoadAllWithWarnings for user-supplied overlay files that failed to parse. Embedded systems must always load — a parse failure there is a hard error, not a warning.

func (LoadWarning) Error added in v0.7.0

func (w LoadWarning) Error() string

type Mass

type Mass struct {
	Value    float64 `json:"massValue"`
	Exponent int     `json:"massExponent"`
}

type Moon

type Moon struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	EnglishName string `json:"englishName"`
}

type OrbitalElement

type OrbitalElement struct {
	SemimajorAxis            float64   `json:"semimajorAxis"`
	Eccentricity             float64   `json:"eccentricity"`
	Inclination              float64   `json:"inclination"`
	ArgumentOfPeriapsis      float64   `json:"argumentOfPeriapsis"`
	LongitudeOfAscendingNode float64   `json:"longitudeOfAscendingNode"`
	MeanAnomaly              float64   `json:"meanAnomaly"`
	Epoch                    time.Time `json:"epoch"`
}

type Planet

type Planet struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	EnglishName string `json:"englishName"`
}

type ScaleClass added in v0.16.0

type ScaleClass string

ScaleClass is a coarse size/difficulty tag shared by a System and a Loadout (ADR 0014). It is purely a classification surfaced as the spawn form's craft hint — the integrator derives all dynamics from a Body's mass and radius, so a System needs no ScaleClass to work and craft are never filtered by it: any Loadout can fly in any System.

const (
	// ScaleReal is the Sol-scale tag: Earth-class bodies, ~9.4 km/s to
	// orbit. The zero value / default — every System and Loadout that
	// does not set one normalizes to real via Scale().
	ScaleReal ScaleClass = "real"
	// ScaleStrippedBack is the Lumen-scale tag: ~1/10-linear bodies with
	// Earth-like surface gravity, ~3.4 km/s to orbit, modelled on the
	// Kerbal Space Program stock system.
	ScaleStrippedBack ScaleClass = "stripped-back"
)

func (ScaleClass) Normalize added in v0.16.0

func (c ScaleClass) Normalize() ScaleClass

Normalize maps the empty/unset value to ScaleReal and returns any other value unchanged, so callers can compare scale classes without special- casing the zero value. Unknown non-empty strings pass through verbatim (forward-compatible with overlay-supplied tags).

type System

type System struct {
	Name        string          `json:"systemName"`
	Description string          `json:"description"`
	Distance    string          `json:"distance"`
	Galaxy      string          `json:"galaxy"`
	Bodies      []CelestialBody `json:"bodies"`

	// ScaleClass is the System's spawn-form scale hint (ADR 0014).
	// Optional in JSON: an absent/empty value normalizes to ScaleReal
	// via Scale(), so the pre-Lumen catalog stays real untouched. Lumen
	// sets "stripped-back". Never used for filtering.
	ScaleClass ScaleClass `json:"scaleClass,omitempty"`

	// Source is a runtime annotation: "embedded" for the built-in
	// catalog, "user" for files loaded from
	// $XDG_CONFIG_HOME/terminal-space-program/systems/*.json (v0.7.0+).
	// Excluded from JSON marshaling so CatalogHash is stable across
	// identical-data overlays.
	Source string `json:"-"`
}

System is a named collection of celestial bodies orbiting a common primary. The first body (index 0) is treated as the primary (star or barycenter).

func LoadAll

func LoadAll() ([]System, error)

LoadAll reads every embedded system JSON, merges any user overlay files from $XDG_CONFIG_HOME/terminal-space-program/systems/*.json (or ~/.config/... if XDG is unset), and returns the merged set sorted by name with Sol always first. Warnings from malformed user files are dropped — call LoadAllWithWarnings to inspect them.

func (*System) FindBody

func (s *System) FindBody(query string) *CelestialBody

FindBody returns a pointer to the body with matching id or englishName. Case-insensitive on englishName; exact match on id.

func (*System) ParentOf added in v0.5.0

func (s *System) ParentOf(b CelestialBody) *CelestialBody

ParentOf returns the gravitational parent of body `b` in this system. For top-level bodies (ParentID empty) the system primary (index 0) is returned. Returns nil if b's ParentID is set but unresolvable, which signals a malformed system.

func (*System) Primary

func (s *System) Primary() *CelestialBody

Primary returns the body treated as the gravitational primary (index 0).

func (*System) Scale added in v0.16.0

func (s *System) Scale() ScaleClass

Scale returns the System's normalized ScaleClass (empty => real). Systems loaded from JSON without a scaleClass field — the entire pre-Lumen catalog — report real.

type Texture added in v0.20.0

type Texture struct {
	// Base is the disk's underlying surface color (hex, e.g. "#6E5F50").
	// Empty falls back to the body's Color.
	Base string `json:"base,omitempty"`

	// Bands are latitude sweeps for gas/ice giants — ordered, first
	// match wins per pixel.
	Bands []TextureBand `json:"bands,omitempty"`

	// Continents are filled ellipse features (maria, albedo regions,
	// land masses). Layered in table order — last match wins.
	Continents []TextureEllipse `json:"continents,omitempty"`

	// Craters are ellipse features with optional rim + rayed ejecta —
	// rendered on top of continents/bands. Last match wins.
	Craters []TextureEllipse `json:"craters,omitempty"`

	// Spots are storm/feature ellipses (e.g. a Great Red Spot)
	// layered over bands. Last match wins.
	Spots []TextureEllipse `json:"spots,omitempty"`

	// Mask is the Earth-class polygon land/ocean/biome mask kind.
	// Carried from PR1; rendered starting PR3.
	Mask *TextureMask `json:"mask,omitempty"`

	// LimbTint is an atmospheric-halo color painted over the outer
	// ring of the disk (hex). Carried from PR1; rendered starting PR3.
	LimbTint string `json:"limbTint,omitempty"`

	// Star is the self-luminous star-surface kind (limb darkening +
	// granulation, light-source exempt). Carried from PR1; rendered
	// starting PR3.
	Star *TextureStar `json:"star,omitempty"`
}

Texture is the optional data-driven surface-texture spec for a body (ADR 0024). It replaces the hardcoded per-body Go shaders in internal/render: a single generic engine consumes this block, so any system — including user overlays in $XDG_CONFIG_HOME — can be textured, which the old `switch b.ID` model structurally could not.

The spec carries *typed* feature kinds rather than one flat list, so each kind can render kind-specific detail. The presence of a kind selects the look; an empty block (or a body with no Texture at all) renders as a flat base-color disk.

PR1 (this slice) renders the ellipse kinds (continents / craters / spots) and bands. The mask / limb-tint / star kinds are carried in the schema from PR1 — so the field round-trips through JSON and is covered by the catalog-hash exclusion (see catalog.go) — but are consumed by the engine starting in PR3 (ADR 0024 rollout).

Texture is excluded from bodies.CatalogHash: it is cosmetic, not semantic, so it must never bump the hash and reject existing saves.

func (*Texture) Validate added in v0.20.0

func (t *Texture) Validate() error

Validate reports the first structural problem in a texture spec, or nil when it is renderable. It is intentionally lenient: an empty block is valid (flat disk), and absent colors fall back at render time. It flags only the cases that would otherwise render as silent garbage — malformed hex colors, non-positive ellipse radii, and inverted band ranges. Used by the loader to fail-soft on user-overlay systems (warn + drop the texture → flat disk) rather than hard-error.

type TextureBand added in v0.20.0

type TextureBand struct {
	LatMin float64 `json:"latMin"`
	LatMax float64 `json:"latMax"`
	Color  string  `json:"color"`
}

TextureBand is a latitude sweep: every pixel whose body-latitude is in [LatMin, LatMax) takes Color.

type TextureEllipse added in v0.20.0

type TextureEllipse struct {
	Lat   float64 `json:"lat"`
	Lon   float64 `json:"lon"`
	LatR  float64 `json:"latR"`
	LonR  float64 `json:"lonR"`
	Color string  `json:"color,omitempty"`
	Rim   string  `json:"rim,omitempty"`
	Rays  bool    `json:"rays,omitempty"`
}

TextureEllipse is a lat/lon-axis-aligned ellipse feature. Lat/Lon is the center (degrees); LatR/LonR are the semi-axes along latitude and longitude (degrees). Color fills the ellipse. For craters, Rim (when set) colors the outer ring and Rays marks bright ejecta.

type TextureMask added in v0.20.0

type TextureMask struct {
	Polys  []TextureRegion   `json:"polys,omitempty"`
	Biomes map[string]string `json:"biomes,omitempty"`
}

TextureMask is the Earth-class polygon land/ocean mask kind. Polys is a list of named regions (land / desert / ice) given as polygon vertex lists; Biomes maps a region kind to a hex color. Carried in the schema from PR1; the engine consumes it starting PR3.

type TextureRegion added in v0.20.0

type TextureRegion struct {
	Kind     string       `json:"kind,omitempty"`
	Vertices []LatLonPair `json:"vertices,omitempty"`
}

TextureRegion is one named polygon in a TextureMask.

type TextureStar added in v0.20.0

type TextureStar struct {
	Core        string  `json:"core,omitempty"`
	Surface     string  `json:"surface,omitempty"`
	Limb        string  `json:"limb,omitempty"`
	Spot        string  `json:"spot,omitempty"`
	Granulation float64 `json:"granulation,omitempty"`
	Seed        int64   `json:"seed,omitempty"`
}

TextureStar is the self-luminous star-surface kind: concentric limb darkening (bright Core → Surface → darker Limb) with optional granulation jitter and sunspots. A body carrying a Star block is exempt from day/night shading — it is the light source. Core / Surface / Limb are hex colors; empty ones derive from the body's Color. Granulation is the surface mottling amplitude (0..1); Seed makes the (deterministic) granulation pattern reproducible. Spot is the sunspot color; spot *positions* are taken from the texture's Spots ellipses.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL