solargod/js/lib.js
monsterrobotparty b42473a055 🔭 review: post-merge integration — sbdb_lookup into CONFIG.proxy, era-aware BC dates in lib formatters (fable)
Merged suite: 21/21 gates in-browser incl. NEO real-orbit (err 1.16%),
Jupiter@1000BC Δ0.004 AU, 2b-proof. Deep-time readout now '1000 BC-01-15'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:32:38 +10:00

99 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SOLARGOD shared helpers — time (JD / centuries), math, the ecliptic→world
// frame map, and human formatters. Pure and dependency-free so verify.html can
// import it (and ephem.js) with zero Three.js in scope.
// ---- constants ----
export const AU_KM = 149597870.7; // 1 astronomical unit in km (IAU 2012)
export const C_KMS = 299792.458; // speed of light, km/s
export const MS_PER_DAY = 86400000;
export const J2000_JD = 2451545.0; // 2000-Jan-01 12:00 TT
export const UNIX_EPOCH_JD = 2440587.5; // JD of 1970-Jan-01 00:00 UTC
export const DEG = Math.PI / 180;
export const RAD = 180 / Math.PI;
// ---- time ----
// We treat the sim clock as UTC and ignore the TT/TDBUTC offset (~69 s in 2026).
// At interplanetary scale that is < 1e-5 AU even for Mars — noted, deliberately
// dropped (brief §2). JD from unix-ms: divide by ms/day, add the unix-epoch JD.
export function jdFromUnixMs(ms) { return ms / MS_PER_DAY + UNIX_EPOCH_JD; }
export function unixMsFromJd(jd) { return (jd - UNIX_EPOCH_JD) * MS_PER_DAY; }
// Julian centuries since J2000.0.
export function centuriesSinceJ2000(jd) { return (jd - J2000_JD) / 36525; }
// ---- scalar math ----
export const clamp = (x, lo, hi) => (x < lo ? lo : x > hi ? hi : x);
export const lerp = (a, b, t) => a + (b - a) * t;
export function smoothstep(t) { t = clamp(t, 0, 1); return t * t * (3 - 2 * t); }
// Normalize degrees to (180, +180]. Must run in float64 AFTER any large
// subtraction (Mercury's L rate is 149473°/Cy → M can be tens of thousands of
// degrees before reduction; brief §12).
export function normDegPM180(d) {
d = d % 360;
if (d <= -180) d += 360;
else if (d > 180) d -= 360;
return d;
}
export const normDeg360 = (d) => ((d % 360) + 360) % 360;
// ---- frame convention (fixed here once; nothing else maps by hand) ----
// Heliocentric J2000 ecliptic {x,y,z} [AU or view units] → Three.js world:
// ecliptic X → world x, ecliptic Z (north) → world y, ecliptic Y → world z.
// Keeps the frame right-handed with Y up. `out` is any {x,y,z} sink (Vector3 ok).
export function eclToWorld(ecl, out = {}) {
out.x = ecl.x;
out.y = ecl.z;
out.z = -ecl.y;
return out;
}
// Inverse, for round-trip checks and picking rays.
export function worldToEcl(w, out = {}) {
out.x = w.x;
out.y = -w.z;
out.z = w.y;
return out;
}
// ---- formatters ----
const pad = (n, w = 2) => String(n).padStart(w, '0');
// Astronomical year → human era label: 0 = "1 BC", 999 = "1000 BC" (deep time,
// wave 2 VESSEL — a raw getUTCFullYear() would render 1000 BC as "-0999").
const eraYear = (y) => (y <= 0 ? `${1 - y} BC` : String(y).padStart(4, '0'));
// "2026-07-15 00:00:00 UTC" from unix-ms.
export function formatUTC(ms) {
const d = new Date(ms);
return `${eraYear(d.getUTCFullYear())}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ` +
`${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} UTC`;
}
export function formatUTCDate(ms) {
const d = new Date(ms);
return `${eraYear(d.getUTCFullYear())}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
}
export function fmtAU(au) {
const a = Math.abs(au);
if (a < 0.001) return `${(au * AU_KM).toFixed(0)} km`;
if (a < 100) return `${au.toFixed(3)} AU`;
return `${au.toFixed(1)} AU`;
}
export function fmtKm(km) {
const a = Math.abs(km);
if (a >= 1e7) return `${(km / 1e6).toLocaleString('en-US', { maximumFractionDigits: 2 })} M km`;
return `${Math.round(km).toLocaleString('en-US')} km`;
}
// Light-time for a distance in AU → "43 min 12 s" / "1 h 08 m" / "8.3 s".
export function fmtLightTime(au) {
let s = (au * AU_KM) / C_KMS;
if (s < 90) return `${s.toFixed(1)} s`;
const h = Math.floor(s / 3600); s -= h * 3600;
const m = Math.floor(s / 60); s -= m * 60;
if (h > 0) return `${h} h ${pad(m)} m`;
return `${m} min ${pad(Math.floor(s))} s`;
}
// Duration in days → "27.3 d" / "1.88 yr".
export function fmtDuration(days) {
const a = Math.abs(days);
if (a < 2) return `${(a * 24).toFixed(1)} h`;
if (a < 900) return `${a.toFixed(1)} d`;
return `${(a / 365.25).toFixed(2)} yr`;
}
export function fmtSpeed(kms) { return `${kms.toFixed(2)} km/s`; }