entangled-worlds-orbiters-shared 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +10 -0
  2. package/dist/star/blackbody.d.ts +12 -0
  3. package/dist/star/blackbody.js +38 -0
  4. package/dist/star/bounds.d.ts +39 -0
  5. package/dist/star/bounds.js +47 -0
  6. package/dist/star/framing.d.ts +31 -0
  7. package/dist/star/framing.js +68 -0
  8. package/dist/star/index.d.ts +21 -0
  9. package/dist/star/index.js +15 -0
  10. package/dist/star/layout.d.ts +121 -0
  11. package/dist/star/layout.js +307 -0
  12. package/dist/star/lighting.d.ts +65 -0
  13. package/dist/star/lighting.js +115 -0
  14. package/dist/star/params.d.ts +79 -0
  15. package/dist/star/params.js +122 -0
  16. package/dist/star/quality.d.ts +16 -0
  17. package/dist/star/quality.js +16 -0
  18. package/dist/star/shaders.d.ts +20 -0
  19. package/dist/star/shaders.js +186 -0
  20. package/dist/star/starBody.d.ts +58 -0
  21. package/dist/star/starBody.js +308 -0
  22. package/dist/star/starManager.d.ts +73 -0
  23. package/dist/star/starManager.js +190 -0
  24. package/dist/star/stellarData.d.ts +82 -0
  25. package/dist/star/stellarData.js +347 -0
  26. package/package.json +12 -4
  27. package/src/core/index.js +1 -1
  28. package/src/core/lights.js +17 -1
  29. package/src/loaders/glbLoader.js +5 -2
  30. package/src/moons/index.js +11 -1
  31. package/src/moons/moonsGeneration.js +24 -8
  32. package/src/moons/moonsManager.js +18 -0
  33. package/src/star/blackbody.ts +46 -0
  34. package/src/star/bounds.ts +73 -0
  35. package/src/star/framing.ts +96 -0
  36. package/src/star/index.ts +59 -0
  37. package/src/star/layout.ts +400 -0
  38. package/src/star/lighting.ts +142 -0
  39. package/src/star/params.ts +161 -0
  40. package/src/star/quality.ts +33 -0
  41. package/src/star/shaders.ts +195 -0
  42. package/src/star/starBody.ts +385 -0
  43. package/src/star/starManager.ts +224 -0
  44. package/src/star/stellarData.ts +387 -0
  45. package/src/world/cloudShell.js +2 -3
  46. package/src/world/worldGLB.js +3 -2
  47. package/src/world/worldManager.js +1 -0
  48. package/src/world/worldTexture.js +4 -3
package/README.md CHANGED
@@ -71,6 +71,16 @@ import * as EntangledWorlds from 'entangled-worlds-orbiters-shared';
71
71
  ### Configuration
72
72
  - `textureConfig` - Texture configuration system
73
73
 
74
+ ### Star (`entangled-worlds-orbiters-shared/star`)
75
+ - `createStar`, `StarManager` - The host star of a world: single, binary or trinary, emissive, shader-only glow (two draw calls for any multiplicity; lights nothing)
76
+ - `DEFAULT_STAR_PARAMS`, `normalizeStarParams` - Author params (temperature, radius, luminosity, activity, multiplicity, separation, seed, placement) + validator for stored params
77
+ - `starParamsFromStellarData` - Catalogue host-star data (Teff, radius, mass, luminosity, spectral type, star count or a "(AB)b" name suffix) → params
78
+ - `blackbodyColor`, `spectralClass`, `starLayout`, `starPositions` - Pure colour / layout / orbit helpers
79
+ - `getFocus` (on `createStar` / `StarManager`), `framingDistance`, `defaultStarPlacement` - Camera focus on the system barycentre and host-camera-aware placement
80
+ - The star lights the scene: `StarManager` key + camera-side fill lights, `setLighting`, `lightingFromLegacy`, `DEFAULT_AMBIENT` (pair with `setHemisphereEnabled(scene, false)` from `./core`; moons `emissiveLift`, GLB `emissiveIntensity` for night sides)
81
+ - Real binaries (close pair from component masses, a distant companion for wide pairs) and an optional debris disc; `mainSequenceTemperature` / `Radius` / `Luminosity` helpers
82
+ - Perf + look harness: `npm run bench:star` (see `bench/star/results/REPORT.md`)
83
+
74
84
  ### Capabilities (`entangled-worlds-orbiters-shared/capabilities`)
75
85
  - `createCapabilityRegistry` - Validated, named registry of studio actions (query / mutation / generative)
76
86
  - `validate`, `assertValid` - JSON-Schema-subset validator used for capability inputs
@@ -0,0 +1,12 @@
1
+ export interface RGB {
2
+ r: number;
3
+ g: number;
4
+ b: number;
5
+ }
6
+ /**
7
+ * sRGB colour (channels 0..1, brightest channel = 1) of a blackbody at `temperatureK`.
8
+ * Clamped to 1000 K … 40000 K, the range the fit is valid for.
9
+ */
10
+ export declare function blackbodyColor(temperatureK: number): RGB;
11
+ /** Linear-light luminance-ish weight of an sRGB colour — handy for keeping companions balanced. */
12
+ export declare function relativeLuminance({ r, g, b }: RGB): number;
@@ -0,0 +1,38 @@
1
+ // Blackbody temperature → display colour.
2
+ //
3
+ // A fitted approximation of the Planck locus in sRGB (Tanner Helland's curve fit of Mitchell
4
+ // Charity's blackbody table), normalised so the brightest channel is 1. It is a *chromaticity*:
5
+ // brightness is the renderer's job. Pure and cheap — called on param changes, never per frame.
6
+ function clamp01(v) {
7
+ return v < 0 ? 0 : v > 1 ? 1 : v;
8
+ }
9
+ /**
10
+ * sRGB colour (channels 0..1, brightest channel = 1) of a blackbody at `temperatureK`.
11
+ * Clamped to 1000 K … 40000 K, the range the fit is valid for.
12
+ */
13
+ export function blackbodyColor(temperatureK) {
14
+ const tk = Number.isFinite(temperatureK) ? temperatureK : 5778;
15
+ const t = Math.min(40000, Math.max(1000, tk)) / 100;
16
+ let r;
17
+ let g;
18
+ let b;
19
+ if (t <= 66) {
20
+ r = 255;
21
+ g = 99.4708025861 * Math.log(t) - 161.1195681661;
22
+ b = t <= 19 ? 0 : 138.5177312231 * Math.log(t - 10) - 305.0447927307;
23
+ }
24
+ else {
25
+ r = 329.698727446 * Math.pow(t - 60, -0.1332047592);
26
+ g = 288.1221695283 * Math.pow(t - 60, -0.0755148492);
27
+ b = 255;
28
+ }
29
+ r = clamp01(r / 255);
30
+ g = clamp01(g / 255);
31
+ b = clamp01(b / 255);
32
+ const m = Math.max(r, g, b) || 1;
33
+ return { r: r / m, g: g / m, b: b / m };
34
+ }
35
+ /** Linear-light luminance-ish weight of an sRGB colour — handy for keeping companions balanced. */
36
+ export function relativeLuminance({ r, g, b }) {
37
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
38
+ }
@@ -0,0 +1,39 @@
1
+ /** Largest moon radius (0.1 geometry × up to 0.22 scale). */
2
+ export declare const DEFAULT_MOON_RADIUS = 0.022;
3
+ /** How far a moon's orbit radius may stray from its nominal one, either way. */
4
+ export declare const MOON_ORBIT_HALF_SCATTER = 0.11;
5
+ /** Clear-space factor on (scene outer radius + system extent). */
6
+ export declare const STAR_DISTANCE_MARGIN = 2;
7
+ /** Largest angular radius the primary's disc may span seen from the planet, degrees. */
8
+ export declare const MAX_STAR_ANGULAR_RADIUS_DEG = 10;
9
+ export interface SceneSizes {
10
+ planetRadius: number;
11
+ /** Moon orbit radius (0 / omitted = no moons). */
12
+ moonOrbitRadius?: number;
13
+ moonRadius?: number;
14
+ /** Planet glow / cloud shell scale relative to the planet radius (e.g. 1.18). */
15
+ cloudOrGlowScale?: number;
16
+ }
17
+ /** Radius of the sphere around the planet that holds the planet, its glow and its moons. */
18
+ export declare function sceneOuterRadius(s: SceneSizes): number;
19
+ export interface MinStarDistanceInput extends Partial<SceneSizes> {
20
+ /** Or pass the outer radius directly (wins over the sizes above). */
21
+ outerRadius?: number;
22
+ /** The star system's full extent from its barycentre (every disc, orbit and corona). */
23
+ systemExtent?: number;
24
+ /** The primary's radius, for the angular-size cap. */
25
+ primaryRadius?: number;
26
+ margin?: number;
27
+ }
28
+ /**
29
+ * Minimum barycentre distance from the planet: `margin × (outer + systemExtent)`, and far enough
30
+ * that the primary spans at most `MAX_STAR_ANGULAR_RADIUS_DEG`. Pure.
31
+ */
32
+ export declare function minStarDistance(input: MinStarDistanceInput): number;
33
+ /** Sky margin: the system keeps this fraction of the sky radius clear of the skybox. */
34
+ export declare const SKY_MARGIN_FRACTION = 0.02;
35
+ /**
36
+ * Farthest barycentre distance that keeps a system of `systemExtent` inside a sky of `skyRadius`
37
+ * (with `SKY_MARGIN_FRACTION` of clearance). Never negative. Pure.
38
+ */
39
+ export declare function maxStarDistance(skyRadius: number, systemExtent: number): number;
@@ -0,0 +1,47 @@
1
+ // Scene bounds (pure): how far the star system must sit so it never touches the planet, its glow
2
+ // or its moons, and never looms over the planet. Hosts pass their scene sizes; hosts that pass
3
+ // nothing keep the author's distance as-is.
4
+ // The shared moons' sizes (`./moons` MOON_MAX_RADIUS and MOON_ORBIT_SCATTER / 2). Copied, not
5
+ // imported: this subpath is built to dist/ and the moons ship as source; a test pins them together.
6
+ /** Largest moon radius (0.1 geometry × up to 0.22 scale). */
7
+ export const DEFAULT_MOON_RADIUS = 0.022;
8
+ /** How far a moon's orbit radius may stray from its nominal one, either way. */
9
+ export const MOON_ORBIT_HALF_SCATTER = 0.11;
10
+ /** Clear-space factor on (scene outer radius + system extent). */
11
+ export const STAR_DISTANCE_MARGIN = 2;
12
+ /** Largest angular radius the primary's disc may span seen from the planet, degrees. */
13
+ export const MAX_STAR_ANGULAR_RADIUS_DEG = 10;
14
+ function pos(v, fallback) {
15
+ return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : fallback;
16
+ }
17
+ /** Radius of the sphere around the planet that holds the planet, its glow and its moons. */
18
+ export function sceneOuterRadius(s) {
19
+ const planet = pos(s.planetRadius, 0) * Math.max(1, pos(s.cloudOrGlowScale, 1));
20
+ const orbit = pos(s.moonOrbitRadius, 0);
21
+ const moons = orbit > 0 ? orbit + MOON_ORBIT_HALF_SCATTER + pos(s.moonRadius, DEFAULT_MOON_RADIUS) : 0;
22
+ return Math.max(planet, moons);
23
+ }
24
+ /**
25
+ * Minimum barycentre distance from the planet: `margin × (outer + systemExtent)`, and far enough
26
+ * that the primary spans at most `MAX_STAR_ANGULAR_RADIUS_DEG`. Pure.
27
+ */
28
+ export function minStarDistance(input) {
29
+ const outer = typeof input.outerRadius === 'number' && Number.isFinite(input.outerRadius)
30
+ ? Math.max(0, input.outerRadius)
31
+ : sceneOuterRadius({ planetRadius: pos(input.planetRadius, 0), ...input });
32
+ const margin = typeof input.margin === 'number' && input.margin > 0 ? input.margin : STAR_DISTANCE_MARGIN;
33
+ const clear = margin * (outer + pos(input.systemExtent, 0));
34
+ const angular = pos(input.primaryRadius, 0) / Math.tan((MAX_STAR_ANGULAR_RADIUS_DEG * Math.PI) / 180);
35
+ return Math.max(clear, angular);
36
+ }
37
+ /** Sky margin: the system keeps this fraction of the sky radius clear of the skybox. */
38
+ export const SKY_MARGIN_FRACTION = 0.02;
39
+ /**
40
+ * Farthest barycentre distance that keeps a system of `systemExtent` inside a sky of `skyRadius`
41
+ * (with `SKY_MARGIN_FRACTION` of clearance). Never negative. Pure.
42
+ */
43
+ export function maxStarDistance(skyRadius, systemExtent) {
44
+ const sky = typeof skyRadius === 'number' && Number.isFinite(skyRadius) ? Math.max(0, skyRadius) : Infinity;
45
+ const ext = typeof systemExtent === 'number' && Number.isFinite(systemExtent) ? Math.max(0, systemExtent) : 0;
46
+ return Math.max(0, sky * (1 - SKY_MARGIN_FRACTION) - ext);
47
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Camera distance (from the sphere's centre) at which a sphere of `radius` fits inside both the
3
+ * vertical and the horizontal field of view, with `margin` (1.15 = 15% breathing room).
4
+ */
5
+ export declare function framingDistance(radius: number, fovDeg: number, aspect: number, margin?: number): number;
6
+ export interface StarPlacementInput {
7
+ /** Vertical field of view of the host camera, degrees. */
8
+ fovDeg: number;
9
+ /** Width / height of the viewport. */
10
+ aspect: number;
11
+ /** Distance of the host camera from the planet (the scene origin), on +Z looking at it. */
12
+ cameraDistance: number;
13
+ planetRadius: number;
14
+ /** Primary radius, scene units (default: `DEFAULT_STAR_PARAMS.radius`). */
15
+ starRadius?: number;
16
+ /** Star system distance from the origin (default: `DEFAULT_STAR_PARAMS.distance`). */
17
+ distance?: number;
18
+ }
19
+ export interface StarPlacement {
20
+ azimuth: number;
21
+ elevation: number;
22
+ /** False when no in-frame spot clears the planet (it fills the frame); the star is then just above it. */
23
+ fits: boolean;
24
+ }
25
+ /**
26
+ * Azimuth / elevation that put the primary's disc (plus its bright glow) just outside the planet's
27
+ * angular disc and inside the frame of a camera on +Z looking at the origin — upper-right when there
28
+ * is room, swinging towards straight up as the frame narrows (portrait). Hosts use it only when the
29
+ * author has not set azimuth/elevation; `DEFAULT_STAR_PARAMS` stays the host-agnostic fallback.
30
+ */
31
+ export declare function defaultStarPlacement(input: StarPlacementInput): StarPlacement;
@@ -0,0 +1,68 @@
1
+ // Camera framing helpers (pure, no three): how far a camera must sit to fit a focus sphere, and a
2
+ // host-camera-aware default placement for the star so it reads beside the planet, not behind it.
3
+ import { FOCUS_GLOW_RADII } from './layout.js';
4
+ import { DEFAULT_STAR_PARAMS, STAR_PARAM_RANGES } from './params.js';
5
+ const DEG = Math.PI / 180;
6
+ /**
7
+ * Camera distance (from the sphere's centre) at which a sphere of `radius` fits inside both the
8
+ * vertical and the horizontal field of view, with `margin` (1.15 = 15% breathing room).
9
+ */
10
+ export function framingDistance(radius, fovDeg, aspect, margin = 1.15) {
11
+ const r = Math.max(0, Number.isFinite(radius) ? radius : 0) * (Number.isFinite(margin) && margin > 0 ? margin : 1.15);
12
+ const halfV = (Math.min(179, Math.max(1, Number.isFinite(fovDeg) ? fovDeg : 45)) * DEG) / 2;
13
+ const a = Number.isFinite(aspect) && aspect > 0 ? aspect : 1;
14
+ const halfH = Math.atan(Math.tan(halfV) * a);
15
+ return r / Math.sin(Math.min(halfV, halfH));
16
+ }
17
+ /**
18
+ * Azimuth / elevation that put the primary's disc (plus its bright glow) just outside the planet's
19
+ * angular disc and inside the frame of a camera on +Z looking at the origin — upper-right when there
20
+ * is room, swinging towards straight up as the frame narrows (portrait). Hosts use it only when the
21
+ * author has not set azimuth/elevation; `DEFAULT_STAR_PARAMS` stays the host-agnostic fallback.
22
+ */
23
+ export function defaultStarPlacement(input) {
24
+ const fov = Number.isFinite(input.fovDeg) && input.fovDeg > 0 ? input.fovDeg : 45;
25
+ const aspect = Number.isFinite(input.aspect) && input.aspect > 0 ? input.aspect : 1;
26
+ const cd = Number.isFinite(input.cameraDistance) && input.cameraDistance > 0 ? input.cameraDistance : 4;
27
+ const pr = Math.max(0, Number.isFinite(input.planetRadius) ? input.planetRadius : 1);
28
+ const starRadius = input.starRadius ?? DEFAULT_STAR_PARAMS.radius;
29
+ const [dLo, dHi] = STAR_PARAM_RANGES.distance;
30
+ const D = Math.min(dHi, Math.max(dLo, input.distance ?? DEFAULT_STAR_PARAMS.distance));
31
+ // Work on the image plane at unit distance (tangent units): positions and radii both scale by tan.
32
+ const tanV = Math.tan((fov * DEG) / 2);
33
+ const tanH = tanV * aspect;
34
+ const planetTan = cd > pr ? pr / Math.sqrt(cd * cd - pr * pr) : Infinity;
35
+ const starTan = (starRadius * FOCUS_GLOW_RADII) / Math.sqrt(D * D + cd * cd);
36
+ const rho = planetTan + starTan * 1.35; // centre distance from the view axis: disc + glow clear the planet
37
+ const maxX = tanH * 0.97 - starTan * 0.5; // centre in frame, disc mostly in frame
38
+ const maxY = tanV * 0.97 - starTan * 0.5;
39
+ let x = 0;
40
+ let y = Number.isFinite(rho) ? rho : maxY;
41
+ let fits = false;
42
+ for (let phiDeg = 30; phiDeg <= 90; phiDeg += 2) {
43
+ const px = rho * Math.cos(phiDeg * DEG);
44
+ const py = rho * Math.sin(phiDeg * DEG);
45
+ if (px <= maxX && py <= maxY) {
46
+ x = px;
47
+ y = py;
48
+ fits = true;
49
+ break;
50
+ }
51
+ }
52
+ // Ray from the camera through (x, y) on the image plane, hit on the sphere |P| = D.
53
+ const len = Math.hypot(x, y, 1);
54
+ const ux = x / len;
55
+ const uy = y / len;
56
+ const uz = -1 / len;
57
+ const cu = cd * uz; // C · u with C = (0, 0, cd)
58
+ const t = -cu + Math.sqrt(Math.max(0, cu * cu - cd * cd + D * D));
59
+ const px = ux * t;
60
+ const py = uy * t;
61
+ const pz = cd + uz * t;
62
+ const round = (v) => Math.round(v * 10) / 10;
63
+ return {
64
+ azimuth: round(Math.atan2(px, -pz) / DEG),
65
+ elevation: round(Math.asin(Math.max(-1, Math.min(1, py / D))) / DEG),
66
+ fits,
67
+ };
68
+ }
@@ -0,0 +1,21 @@
1
+ export { createStar } from './starBody.js';
2
+ export type { StarBody, StarFocus, CreateStarOptions } from './starBody.js';
3
+ export { StarManager } from './starManager.js';
4
+ export type { StarManagerOptions, StarSceneBounds } from './starManager.js';
5
+ export { STAR_QUALITY, normalizeStarQuality } from './quality.js';
6
+ export type { StarQuality, StarQualityProfile } from './quality.js';
7
+ export { DEFAULT_STAR_PARAMS, STAR_PARAM_RANGES, normalizeStarParams, spectralClass, } from './params.js';
8
+ export type { StarParams, StarMultiplicity, SpectralClass } from './params.js';
9
+ export { blackbodyColor } from './blackbody.js';
10
+ export type { RGB } from './blackbody.js';
11
+ export { starLayout, starPositions, starSystemPosition, starFocusRadius, flareAt } from './layout.js';
12
+ export { framingDistance, defaultStarPlacement } from './framing.js';
13
+ export type { StarPlacement, StarPlacementInput } from './framing.js';
14
+ export type { StarLayout, LaidOutStar, StarOrbit, StarRole, FlareState } from './layout.js';
15
+ export { starParamsFromStellarData, temperatureFromSpectralType, starCountFromPlanetName, luminosityClassFromSpectralType, mainSequenceLuminosity, mainSequenceRadius, mainSequenceTemperature, separationFromAu, youthFromAge, } from './stellarData.js';
16
+ export type { StellarData, StellarBinary, LuminosityClass } from './stellarData.js';
17
+ export { DEFAULT_AMBIENT, DEFAULT_STAR_LIGHTING, systemLightColor, keyLightIntensity, fillLightIntensity, lightingFromLegacy, LEGACY_AMBIENT_REFERENCE, LEGACY_AMBIENT_MAX, normalizeStarLighting, parseTint, } from './lighting.js';
18
+ export type { StarLighting, LegacyLighting, LegacyLightingResult } from './lighting.js';
19
+ export { minStarDistance, maxStarDistance, SKY_MARGIN_FRACTION, sceneOuterRadius, STAR_DISTANCE_MARGIN, MAX_STAR_ANGULAR_RADIUS_DEG, } from './bounds.js';
20
+ export type { SceneSizes, MinStarDistanceInput } from './bounds.js';
21
+ export { separateDirection, starsReach } from './layout.js';
@@ -0,0 +1,15 @@
1
+ // ./star — the host star of a world: an emissive, shader-glowing body (single, binary or trinary,
2
+ // plus an optional distant companion) in two draw calls, and — through StarManager — the scene's key
3
+ // light and camera-side fill. Pure param/layout/colour helpers are exported too so hosts can validate
4
+ // stored params and preview colours without WebGL.
5
+ export { createStar } from './starBody.js';
6
+ export { StarManager } from './starManager.js';
7
+ export { STAR_QUALITY, normalizeStarQuality } from './quality.js';
8
+ export { DEFAULT_STAR_PARAMS, STAR_PARAM_RANGES, normalizeStarParams, spectralClass, } from './params.js';
9
+ export { blackbodyColor } from './blackbody.js';
10
+ export { starLayout, starPositions, starSystemPosition, starFocusRadius, flareAt } from './layout.js';
11
+ export { framingDistance, defaultStarPlacement } from './framing.js';
12
+ export { starParamsFromStellarData, temperatureFromSpectralType, starCountFromPlanetName, luminosityClassFromSpectralType, mainSequenceLuminosity, mainSequenceRadius, mainSequenceTemperature, separationFromAu, youthFromAge, } from './stellarData.js';
13
+ export { DEFAULT_AMBIENT, DEFAULT_STAR_LIGHTING, systemLightColor, keyLightIntensity, fillLightIntensity, lightingFromLegacy, LEGACY_AMBIENT_REFERENCE, LEGACY_AMBIENT_MAX, normalizeStarLighting, parseTint, } from './lighting.js';
14
+ export { minStarDistance, maxStarDistance, SKY_MARGIN_FRACTION, sceneOuterRadius, STAR_DISTANCE_MARGIN, MAX_STAR_ANGULAR_RADIUS_DEG, } from './bounds.js';
15
+ export { separateDirection, starsReach } from './layout.js';
@@ -0,0 +1,121 @@
1
+ import { type RGB } from './blackbody.js';
2
+ import { type StarParams } from './params.js';
3
+ export type StarRole = 'primary' | 'companion' | 'wide' | 'distant';
4
+ export interface StarOrbit {
5
+ /** Distance from the barycentre it orbits, scene units. */
6
+ radius: number;
7
+ /** Phase at t = 0, radians. */
8
+ phase: number;
9
+ /** Radians per second (0 for a single star). */
10
+ angularSpeed: number;
11
+ }
12
+ export interface LaidOutStar {
13
+ role: StarRole;
14
+ temperatureK: number;
15
+ color: RGB;
16
+ radius: number;
17
+ /** 0..1 corona intensity. */
18
+ luminosity: number;
19
+ /** 0..1 surface activity. */
20
+ activity: number;
21
+ /** Corona reach in radii (the billboard's half-size). */
22
+ coronaExtent: number;
23
+ /** Linear limb-darkening coefficient (cooler stars darken more towards the limb). */
24
+ limbDarkening: number;
25
+ /** 0..1 per-star noise offset so two stars never boil in sync. */
26
+ noiseSeed: number;
27
+ /** Photosphere brightness gain (1 for the system's stars; the distant companion is dimmer). */
28
+ gain: number;
29
+ orbit: StarOrbit;
30
+ }
31
+ export interface StarLayout {
32
+ stars: LaidOutStar[];
33
+ /** Tilt of the orbital plane from the local XZ plane, radians. */
34
+ inclination: number;
35
+ /** Rotation of the orbital plane about local Y, radians. */
36
+ node: number;
37
+ /** For trinaries: the close pair's barycentre orbit around the system barycentre. */
38
+ pairOrbit: StarOrbit | null;
39
+ /**
40
+ * The distant companion (S-type pair), or null: a lone star elsewhere in the sky at the system's
41
+ * distance, in the direction below (degrees). Not part of `stars`, orbits or the focus sphere.
42
+ */
43
+ distant: {
44
+ star: LaidOutStar;
45
+ azimuth: number;
46
+ elevation: number;
47
+ } | null;
48
+ /** Radius of a sphere (around the local origin) that contains every star and its corona. */
49
+ boundingRadius: number;
50
+ /**
51
+ * Camera-focus radius: a sphere around the barycentre enclosing every star's orbit + disc and the
52
+ * bright inner glow (`FOCUS_GLOW_RADII` radii), not the faint outer corona — frames the system.
53
+ */
54
+ focusRadius: number;
55
+ }
56
+ /** How much of the glow (in star radii) the focus sphere keeps around each disc. */
57
+ export declare const FOCUS_GLOW_RADII = 1.5;
58
+ /** Focus radius of a layout (pure): max over stars of (distance from barycentre + glow radius). */
59
+ export declare function starFocusRadius(layout: Pick<StarLayout, 'stars' | 'pairOrbit'>): number;
60
+ /** 32-bit string hash (the same one `generateMoons` uses). */
61
+ export declare function hashSeed(seed: number | string): number;
62
+ /** Linear congruential generator in [0, 1) (Numerical Recipes constants, as in the moons). */
63
+ export declare function createLcg(seed: number | string): () => number;
64
+ /** Linear limb-darkening coefficient vs temperature: ~0.85 for M dwarfs, ~0.6 Sun, ~0.3 O stars. */
65
+ export declare function limbDarkeningFor(temperatureK: number): number;
66
+ /** Corona half-size in radii for a luminosity 0..1. */
67
+ export declare function coronaExtentFor(luminosity: number): number;
68
+ /**
69
+ * Debris-disc orientation (pure, from the seed): the ring's plane is near-horizontal, tilted
70
+ * `tilt` radians towards a viewer on +Z (so it reads as a thin ellipse, ~0.2–0.45 axis ratio) and
71
+ * rolled `roll` radians about the view axis. Independent of the orbital plane so a face-on-ish
72
+ * binary orbit never turns the disc into a flat halo.
73
+ */
74
+ export declare function discOrientation(seed: number | string): {
75
+ tilt: number;
76
+ roll: number;
77
+ };
78
+ /**
79
+ * Keep the distant companion's direction at least `minAngle` (radians) away from the system's,
80
+ * rotating it away along the great circle (or straight up when they coincide). Pure.
81
+ */
82
+ export declare function separateDirection(sys: {
83
+ azimuth: number;
84
+ elevation: number;
85
+ }, far: {
86
+ azimuth: number;
87
+ elevation: number;
88
+ }, minAngle: number): {
89
+ azimuth: number;
90
+ elevation: number;
91
+ };
92
+ /** Farthest any system star's disc reaches from the barycentre (orbits + radius; no glow). */
93
+ export declare function starsReach(layout: Pick<StarLayout, 'stars' | 'pairOrbit'>): number;
94
+ /**
95
+ * Lay out the system for (possibly raw) params. Companions are always cooler and smaller than the
96
+ * primary and derived from it + the seed; the trinary's third is a small, cool, wide companion
97
+ * (an Alpha Centauri-like hierarchy). Deterministic: same params → same layout.
98
+ */
99
+ export declare function starLayout(rawParams: Partial<StarParams> | unknown): StarLayout;
100
+ /**
101
+ * Local positions (relative to the system barycentre) of every star at `timeSec`, written as
102
+ * xyz triplets into `out` (allocated when omitted). Orbits are circular, in a plane tilted by
103
+ * `inclination` about X and rotated by `node` about Y. Allocation-free when `out` is passed.
104
+ */
105
+ export declare function starPositions(layout: StarLayout, timeSec: number, out?: Float32Array | number[]): Float32Array | number[];
106
+ /** World-space position of the system barycentre for a distance/azimuth/elevation placement. */
107
+ export declare function starSystemPosition(p: Pick<StarParams, 'distance' | 'azimuth' | 'elevation'>): [number, number, number];
108
+ /** Length of one flare slot; at most one flare starts per slot. */
109
+ export declare const FLARE_SLOT_SEC = 6;
110
+ export interface FlareState {
111
+ /** 0..1 flare brightness right now. */
112
+ intensity: number;
113
+ /** Position angle of the flare on the limb, radians. */
114
+ angle: number;
115
+ }
116
+ /**
117
+ * Deterministic flare envelope for a star at `timeSec`: fast rise, exponential decay. The chance a
118
+ * slot holds a flare grows with `activity` (0 → never). Same (seed, time) → same answer on every
119
+ * machine, so all views of one world flare together.
120
+ */
121
+ export declare function flareAt(noiseSeed: number, activity: number, timeSec: number, out?: FlareState): FlareState;