📖 almanac: knowing — info panel, live facts, grimoire art, real textures, README (opus)

Stage 6. almanac.js: on-focus grimoire card — physical fact sheet, hand-written
prose (grimoire voice), MODELBEAST portrait, and LIVE computed facts free from the
ephemeris: distance from Sun & Earth, sunlight age, vis-viva orbital speed, day &
year length. Earth easter egg → GODSIGH world view (:8137). Real Solar System Scope
2k textures (CC BY, 6.1 MB) now paint every world + the Milky Way skybox; ephem
gains semiMajor() for vis-viva. Generated grimoire portraits (FLUX via MODELBEAST,
10 bodies, seed logged in art/PROMPTS.md) are the AESTHETIC layer only — real
worlds get real maps (truth boundary). README with layer table + quickstart +
verify; docs/hero.jpg. Also lands the Stage-3 planets label fix (obj.visible).
Gate: every clickable body has a card; quickstart works from a fresh clone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
monsterrobotparty 2026-07-16 00:56:41 +10:00
parent bed15a496c
commit 1f9664a6f9
31 changed files with 303 additions and 23 deletions

107
README.md
View File

@ -1,29 +1,98 @@
# SOLARGOD ▸ Solar System View # SOLARGOD ▸ Orrery
A browser-based **compressed-scale 3D solar system explorer** — real ephemerides, A standalone, browser-based 3D solar-system explorer — the interplanetary sibling
scrubbable time across centuries, jump-to-orbit navigation, and live layers for of [GODSIGH](../GODSIGH), which watches Earth. Float above a compressed-scale
deep-space probes, asteroids, and comets. Sibling of ("MEGA") solar system with every world visible at once, moving on **real JPL
[GODSIGH](../GODSIGH/README.md), which watches Earth; SOLARGOD watches everything ephemerides**; scrub a time bar across centuries; fly the camera into orbit around
else. Everything runs client-side from free, public, keyless data. any planet, its moons circling; toggle **TRUE scale** and watch the system animate
out into its real, humbling emptiness. Live layers plot where Voyager 1, JWST and
Parker Solar Probe are *right now*, plus real asteroids, comets and Earth
close-approaches. Everything is client-side, free, and keyless.
**Status: pre-build.** The full spec lives in [SOLARGOD_BRIEF.md](SOLARGOD_BRIEF.md). ![SOLARGOD](docs/hero.jpg)
## Quickstart (once built) ## Quickstart
```sh ```sh
python3 serve.py python3 serve.py # stdlib only — no npm, no build step
# open http://127.0.0.1:8147 # open http://127.0.0.1:8147
``` ```
## Planned layers `serve.py` statically serves the app and proxies the JPL feeds a browser can't call
directly (Horizons / SBDB / CAD — no CORS), with a disk cache in `cache/`. Three.js
loads from a pinned CDN importmap. The deterministic core (Sun, planets, moons,
orbits, time travel) runs with **zero network calls** — positions are computed
locally from Keplerian elements; network layers are additive and fail soft.
| Layer | Source | Notes | ## Verify
|---|---|---|
| **Planets** | JPL Keplerian elements (Standish) | computed locally, no network — scrub 18002050 |
| **Moons & rings** | JPL SSD satellite elements | 21 major moons, Saturn/Uranus rings |
| **Spacecraft** | JPL Horizons | Voyagers, New Horizons, JWST, Parker — where they are *now* |
| **Asteroids** | JPL SBDB | real main-belt sample + NEO close approaches (CNEOS CAD) |
| **Comets** | JPL SBDB | Halley & friends, full orbits, 1/r² tails |
MEGA mode compresses distances so the whole system is one readable view; Open **http://127.0.0.1:8147/verify.html** — an in-browser gate runner that checks
TRUE mode animates it back out to its real, humbling emptiness. the ephemeris against **JPL Horizons ground truth** (hard-coded checkpoints + live
queries through the proxy). All 13 gates should read PASS, e.g.:
| Gate | Result |
|------|--------|
| Mars / Jupiter / Earth @ 2026-07-15 (heliocentric ecliptic AU) | Δ ≤ 3.3e-4 / 2.9e-3 / 3.9e-5 AU |
| Kepler solver converges at e=0.97 · orbitPath closes · frame round-trips | exact |
| MEGA→TRUE preserves direction (unit dot ≈ 1) | 1.000000000000 |
| Mars @ J2000 · Jupiter @ 1900 vs **live** Horizons | Δ 4.8e-5 / 2.0e-3 AU |
| Main belt between Mars & Jupiter · Halley aphelion beyond Neptune | pass |
| Voyager 1 @ 2026-07-15 = (32.07, 136.21, +98.55) AU · JWST hugs Earth | Δ 0.002 / 0.011 AU |
## Layers
| Layer | Source | Default | Notes |
|-------|--------|:------:|-------|
| **Planets & orbits** | JPL SSD approximate elements (Standish) | on | 8 planets + Pluto, textured, MEGA-compressed orbit paths |
| **Body labels** | — | on | names; the focused body's children also get distance labels |
| **Moons** | JPL satellite data | on | 21 moons, per-parent local compression, retrograde Triton |
| **Rings** | true km, scaled to parent | on | Saturn (textured annulus) · Uranus (schematic) |
| **Spacecraft** | JPL **Horizons** (proxy) | on | Voyager 1/2, New Horizons, JWST, Parker, Juno, Lucy, Psyche — live distance + one-way light-time |
| **Main belt** | JPL **SBDB** (proxy) | off | ~1500 main-belt asteroids, shared Kepler core |
| **Comets** | JPL **SBDB** (proxy) | on | 1P/Halley, 2P/Encke, 67P, 96P — full orbits + anti-sunward 1/r² tail |
| **Close approaches** | JPL **CAD** (proxy) | off | ±30-day NEO radar around Earth, miss distance in lunar distances |
## Controls
- **Focus** — click any body (or use the Focus menu) → the camera flies into orbit
around it; breadcrumb `SOL ▸ JUPITER ▸ EUROPA`; `Esc` steps out one level.
- **MEGA / TRUE** — toggle the scale mode; the transition animates over ~2.5 s.
- **Body scale** — 1× / 100× / 1200× radius exaggeration.
- **Time bar** — NOW, play/pause, reverse, rate presets (`1× · 1 min/s … 1 yr/s`),
and a scrubbable 18002050 timeline. A **LIVE / SCRUBBED** chip mirrors GODSIGH.
- **Info panel** — appears on focus: a grimoire card with a physical fact sheet,
hand-written prose, a stylized portrait, and **live computed** facts (distance
from Sun & Earth, sunlight age, orbital speed, day & year length). Focusing Earth
reveals a link to the GODSIGH world view.
- **Shareable URLs** — the view serializes to the hash
(`#f=<body>&t=<time>&s=<mega|true>&L=<layers>&cam=…`); paste one to reproduce a view.
- **`?debug=1`** — frame-rate / scale readout.
## Architecture
No build step, no framework, plain ES modules. One module owns the truth→view
transform (`scale.js`); every position is computed in real heliocentric J2000
ecliptic AU first (`ephem.js`) then compressed. A **floating origin** subtracts the
focused body's position in float64 before writing to the scene graph, so a focused
planet never jitters at close zoom.
```
index.html · serve.py · verify.html
css/style.css
js/ config lib ephem bodies scale ui main
js/layers/ planets moons rings spacecraft asteroids comets neos almanac
assets/textures/ (CC BY) · assets/art/grimoire/ (generated)
```
The full execution spec is [SOLARGOD_BRIEF.md](SOLARGOD_BRIEF.md).
## Credits
- **Positions** — [JPL SSD](https://ssd.jpl.nasa.gov/planets/approx_pos.html)
approximate elements; live feeds via **JPL Horizons / SBDB / CAD**.
- **Textures** — [Solar System Scope](https://www.solarsystemscope.com/textures/),
CC BY 4.0 (see `assets/textures/CREDITS.md`).
- **Grimoire art** — generated locally with MODELBEAST / FLUX (see
`assets/art/PROMPTS.md`); the *aesthetic* layer only — real worlds get real data
textures.
- Built on [Three.js](https://threejs.org/).

50
assets/art/PROMPTS.md Normal file
View File

@ -0,0 +1,50 @@
# Generated art — reproducibility log
House rule (brief §7b): every shipped generated asset records its prompt, model,
seed and operator chain so any card can be regenerated in-style. All art here is
the **aesthetic layer only** — never a substitute for real-world data textures.
## Pipeline
- Operator: `flux_local` (MODELBEAST, mflux/MLX on-device, **free & unlimited**)
- Model: `flux2-klein-4b`
- Host: MODELBEAST (`~/Documents/MODELBEAST`), guest `agents` credential
- Post: JPG re-encode at 640px, q82 (`sips`) to stay lean (brief §7 budget)
## Splash / boot hero (`splash/`, candidates only)
- Seeds: 15, 1280×768
- Prompt: *"a grand antique brass orrery mechanism floating in the black void of
deep space, armillary sphere rings and intricate clockwork gears in aged brass
and gold, a glowing sun at the center, faint distant stars and nebula dust,
cinematic volumetric god-rays, ornate engraved astronomical grimoire aesthetic,
moody chiaroscuro, ultra detailed, dark observatory"*
## Grimoire portraits (`grimoire/<body>.jpg`) — info-panel cards
- Seeds run: 12 · **chosen seed: 1** for every body · 800×800 → 640px JPG
- Shared style suffix: *", antique astronomical engraving, ornate brass astrolabe
frame, aged parchment, gold leaf accents, occult grimoire illustration,
intricate etched linework, chiaroscuro, dark background, centered circular
celestial portrait, NOT photorealistic, no text, no words"*
- Per-body subject prompt:
| Body | Subject prompt (prefix) |
|------|-------------------------|
| sun | the radiant Sun as a crowned solar deity with a face of fire, flames and rays |
| mercury | the swift small planet Mercury, cratered grey orb, a winged caduceus |
| venus | the planet Venus veiled in golden sulphur clouds, a rose and hand-mirror |
| earth | the planet Earth, blue and green world with white clouds, encircled by a brass armillary sphere |
| mars | the red planet Mars, ochre desert sphere, a spear and shield, god of war |
| jupiter | the giant planet Jupiter, swirling cloud bands and a great red storm, a thunderbolt, king of gods |
| saturn | the ringed planet Saturn, a scythe and hourglass, aged father of time |
| uranus | the pale cyan ice giant Uranus tilted on its side, a lyre and aurora |
| neptune | the deep blue planet Neptune, a trident and ocean waves, god of the sea |
| pluto | the distant dwarf world Pluto with its moon Charon, a key and a shadowed lantern, lord of the underworld |
Regenerate any card:
```sh
source ~/Documents/MODELBEAST/data/agent.env
~/Documents/MODELBEAST/mb run flux_local -p prompt="<subject><style>" \
-p model=flux2-klein-4b -p seed=1 -p width=800 -p height=800 --wait --download art/candidates/grimoire/<body>/
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

BIN
assets/art/grimoire/sun.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

BIN
assets/textures/2k_mars.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

BIN
assets/textures/2k_moon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

BIN
assets/textures/2k_sun.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

View File

@ -0,0 +1,33 @@
# Texture credits
All planet, moon, Sun, ring and starfield maps in this directory are from the
**Solar System Scope** texture pack and are used under **CC BY 4.0**.
- Source: https://www.solarsystemscope.com/textures/
- License: Creative Commons Attribution 4.0 International (CC BY 4.0)
https://creativecommons.org/licenses/by/4.0/
- Attribution: *Solar System Scope* (INOVE / solarsystemscope.com). Textures are
based on NASA elevation and imagery data.
Downloaded 2026-07-16 at 2k resolution:
| File | Body |
|------|------|
| 2k_sun.jpg | Sun |
| 2k_mercury.jpg | Mercury |
| 2k_venus_surface.jpg | Venus (surface) |
| 2k_earth_daymap.jpg | Earth (day) |
| 2k_mars.jpg | Mars |
| 2k_jupiter.jpg | Jupiter |
| 2k_saturn.jpg | Saturn |
| 2k_uranus.jpg | Uranus |
| 2k_neptune.jpg | Neptune |
| 2k_moon.jpg | Moon |
| 2k_saturn_ring_alpha.png | Saturn rings (alpha) |
| 2k_stars_milky_way.jpg | Milky Way skybox panorama |
Attribution also appears in the app's HUD footer, as CC BY requires.
**Truth boundary (brief §7b):** these are the *real data* textures — real worlds
get real maps. The stylized grimoire portraits in `../art/grimoire/` are the
*aesthetic* layer (generated art), kept visually and directorially separate.

View File

@ -145,7 +145,7 @@ html, body {
/* ---- info panel (left, Stage 6) ---- */ /* ---- info panel (left, Stage 6) ---- */
#infopanel { #infopanel {
position: absolute; top: 14px; left: 14px; width: 300px; max-height: calc(100vh - 130px); position: absolute; top: 46px; left: 14px; width: 300px; max-height: calc(100vh - 160px);
padding: 16px; background: var(--panel-solid); border: 1px solid var(--border); padding: 16px; background: var(--panel-solid); border: 1px solid var(--border);
border-left: 2px solid var(--brass); border-radius: 12px; overflow-y: auto; border-left: 2px solid var(--brass); border-radius: 12px; overflow-y: auto;
z-index: 9; box-shadow: 0 8px 44px rgba(0,0,0,0.6); z-index: 9; box-shadow: 0 8px 44px rgba(0,0,0,0.6);

BIN
docs/hero.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

View File

@ -140,6 +140,12 @@ export function helioEcl(bodyId, jd, out = {}) {
// Mean motion [deg/day] from semi-major axis via Kepler's third law (Gaussian). // Mean motion [deg/day] from semi-major axis via Kepler's third law (Gaussian).
export function meanMotionDegPerDay(a) { return 0.9856076686 / Math.pow(a, 1.5); } export function meanMotionDegPerDay(a) { return 0.9856076686 / Math.pow(a, 1.5); }
// Resolved semi-major axis [AU] of a planet at jd (for vis-viva speed, year length).
export function semiMajor(bodyId, jd) {
const p = useExtended ? resolvePlanetExt(bodyId, jd) : resolvePlanet(bodyId, jd);
return p.a;
}
// Small-body position (asteroids/comets/Pluto): epoch elements propagated by // Small-body position (asteroids/comets/Pluto): epoch elements propagated by
// M = M0 + n·(jd epoch). el = {a,e,i,om(Ω),w(ω),ma(M0),epoch}. // M = M0 + n·(jd epoch). el = {a,e,i,om(Ω),w(ω),ma(M0),epoch}.
export function smallBodyEcl(el, jd, out = {}) { export function smallBodyEcl(el, jd, out = {}) {

121
js/layers/almanac.js Normal file
View File

@ -0,0 +1,121 @@
// SOLARGOD almanac (Stage 6) — the info panel that knows. On focus it shows a
// grimoire card: a physical fact sheet + hand-written prose + (if generated) a
// MODELBEAST portrait, plus LIVE computed facts that come free from the ephemeris
// — distance from Sun and Earth, sunlight age (light-time), orbital speed, and the
// length of the body's day and year (brief §5). Focusing Earth reveals the GODSIGH
// easter egg. Not a toggleable feed — no HUD row.
const GM_SUN = 1.32712440018e11; // km^3/s^2, heliocentric gravitational parameter
const YEAR_DAYS = 365.256;
const BLURBS = {
sun: "The hearth of the system and 99.86% of its mass — a self-gravitating furnace fusing 600 million tonnes of hydrogen every second. Every world here is a fleck of its leftover disk, still falling around it.",
mercury: "A cratered iron cinder with almost no air, swinging so close to the Sun that it laps every other world. Its days are longer than its years; three rotations fit in two of its tight orbits.",
venus: "Earth's twin gone wrong: a runaway greenhouse under sulphuric cloud, hot enough to melt lead and crushing as the deep sea. It turns backwards, so slowly that a Venusian day outlasts its year.",
earth: "The one world we know that woke up. A thin film of water and air over spinning iron, tilted just so for seasons — the pale blue vantage from which all the rest of this was measured.",
mars: "A rusted desert half Earth's size, streaked with the dry beds of ancient rivers and crowned by Olympus Mons, the tallest volcano known. Cold, thin-aired, and the most-visited graveyard of robots off-world.",
jupiter: "A failed star and the system's great vacuum cleaner, its storms wider than Earth and its gravity shepherding the asteroid belt. Four bright moons circle it in a clockwork Galileo used to unseat the Earth from the centre.",
saturn: "The jewel — a globe you could float on water, wrapped in rings of ice no thicker than a mountain is tall. Titan below hides a haze-orange world of methane rain and hydrocarbon seas.",
uranus: "An ice giant knocked onto its side, likely by an ancient impact, so it rolls around the Sun pole-first. Its faint rings and pale cyan haze mark the coldest atmosphere of any planet.",
neptune: "The last great world, a deep-blue storm-lantern found by mathematics before any eye saw it. Its winds are the fastest in the system, and Triton, its captured moon, orbits backwards toward a slow doom.",
pluto: "The heart-marked dwarf at the edge of the known, half rock and half ice, locked in a slow gravitational dance with its outsized moon Charon. Demoted from planet, it remains the king of the Kuiper Belt.",
moon: "Earth's single great moon, born of a Mars-sized collision and slowly drifting away. It keeps one face turned to us forever, and its pull raises the tides that may have coaxed life ashore.",
io: "The most volcanic body in the system, flexed and heated by Jupiter's tides until it turns itself inside out in sulphur.",
europa: "A cracked shell of ice over a global ocean — perhaps twice Earth's water — and one of the best places to look for life beyond home.",
ganymede: "The largest moon in the system, bigger than Mercury, and the only one with its own magnetic field.",
callisto: "The most heavily cratered world known, a dark ancient face that has barely changed in four billion years.",
titan: "The only moon with a thick atmosphere, raining methane onto seas of liquid hydrocarbon under an orange sky.",
enceladus: "A tiny snowball that jets plumes of ocean water into space from cracks at its south pole.",
triton: "A captured Kuiper Belt world orbiting Neptune backwards, spouting nitrogen geysers across a frozen pink surface.",
charon: "Pluto's giant partner — the two are tidally locked face to face, forever hanging still in each other's sky.",
};
export default function create(ctx) {
const { bodies, ephem, lib, focus } = ctx;
const panel = document.getElementById('infopanel');
let lastFocus = null;
let lastWall = 0;
const _b = {}, _e = {};
function helioOf(id, jd, out) {
const b = bodies[id];
if (id === 'sun') { out.x = out.y = out.z = 0; return out; }
if (b.ephemId) return ephem.helioEcl(b.ephemId, jd, out);
if (b.elements) return ephem.smallBodyEcl(b.elements, jd, out);
if (b.parent) return helioOf(b.parent, jd, out); // moon ≈ parent at AU scale
out.x = out.y = out.z = 0; return out;
}
function render(id) {
const b = bodies[id];
const artPath = `assets/art/grimoire/${id}.jpg`;
const earthEgg = id === 'earth'
? `<a class="ip-link" href="http://127.0.0.1:8137" target="_blank" rel="noopener">▸ open GODSIGH world view</a>` : '';
panel.innerHTML = `
<div class="ip-close" role="button" aria-label="close">×</div>
<div class="ip-title"></div>
<div class="ip-type"></div>
<img class="ip-art" alt="" />
<div class="ip-blurb"></div>
<div class="ip-facts"></div>
${earthEgg}`;
panel.querySelector('.ip-title').textContent = b.name;
panel.querySelector('.ip-type').textContent = b.type;
panel.querySelector('.ip-blurb').textContent = BLURBS[id] || `A ${b.type}${b.parent ? ' of ' + bodies[b.parent].name : ''} in the solar system.`;
const img = panel.querySelector('.ip-art');
img.src = artPath;
img.onerror = () => { img.style.display = 'none'; };
panel.querySelector('.ip-close').addEventListener('click', () => { panel.hidden = true; });
panel.hidden = false;
}
function fact(label, value) {
return `<div class="ip-fact"><span>${label}</span><span>${value}</span></div>`;
}
function updateLive(id, jd) {
const b = bodies[id];
helioOf(id, jd, _b);
const rSun = Math.hypot(_b.x, _b.y, _b.z); // AU
ephem.helioEcl('earth', jd, _e);
const rEarth = id === 'earth' ? 0 : Math.hypot(_b.x - _e.x, _b.y - _e.y, _b.z - _e.z);
const rows = [];
// physical
rows.push(fact('Radius', lib.fmtKm(b.radiusKm)));
if (b.axialTiltDeg != null) rows.push(fact('Axial tilt', `${b.axialTiltDeg.toFixed(1)}°`));
// live distances
if (id !== 'sun') rows.push(fact('From Sun', `${lib.fmtAU(rSun)} · ${lib.fmtLightTime(rSun)} light`));
rows.push(fact('From Earth', id === 'earth' ? '—' : `${lib.fmtAU(rEarth)} · ${lib.fmtLightTime(rEarth)} light`));
// orbital speed + periods
if (b.type === 'moon') {
const per = Math.abs(b.moonOrbit.periodDays);
const v = (2 * Math.PI * b.moonOrbit.aKm) / (per * 86400);
rows.push(fact('Orbits', bodies[b.parent].name));
rows.push(fact('Orbital speed', lib.fmtSpeed(v)));
rows.push(fact('Orbital period', `${lib.fmtDuration(per)}${b.moonOrbit.periodDays < 0 ? ' (retrograde)' : ''}`));
} else if (id !== 'sun') {
const a = b.elements ? b.elements.a : ephem.semiMajor(b.ephemId, jd);
const rKm = rSun * lib.AU_KM, aKm = a * lib.AU_KM;
const v = Math.sqrt(GM_SUN * (2 / rKm - 1 / aKm)); // vis-viva
rows.push(fact('Orbital speed', lib.fmtSpeed(v)));
rows.push(fact('Year', lib.fmtDuration(YEAR_DAYS * Math.pow(a, 1.5))));
}
if (b.rotationHours) {
rows.push(fact('Day (rotation)', `${lib.fmtDuration(Math.abs(b.rotationHours) / 24)}${b.rotationHours < 0 ? ' (retrograde)' : ''}`));
}
const facts = panel.querySelector('.ip-facts');
if (facts) facts.innerHTML = rows.join('');
}
return {
id: 'almanac',
onClockTick(simMs, jd) {
if (focus.id !== lastFocus) { lastFocus = focus.id; render(focus.id); lastWall = 0; }
if (panel.hidden) return;
const now = performance.now();
if (now - lastWall < 200) return; // ≤5 Hz
lastWall = now;
updateLive(focus.id, jd);
},
};
}

View File

@ -61,7 +61,7 @@ export default function create(ctx) {
let showOrbits = true, showLabels = true; let showOrbits = true, showLabels = true;
ui.addLayer('orbits', 'Orbit paths', true, (on) => { showOrbits = on; for (const e of entries) e.line.visible = on; }); ui.addLayer('orbits', 'Orbit paths', true, (on) => { showOrbits = on; for (const e of entries) e.line.visible = on; });
ui.addLayer('labels', 'Body labels', true, (on) => { showLabels = on; for (const e of entries) e.lbl.div.style.display = on ? '' : 'none'; }); ui.addLayer('labels', 'Body labels', true, (on) => { showLabels = on; for (const e of entries) e.lbl.obj.visible = on; });
ui.setStatus('orbits', `${entries.length} orbits`, 'ok'); ui.setStatus('orbits', `${entries.length} orbits`, 'ok');
ui.setStatus('labels', 'names shown', 'ok'); ui.setStatus('labels', 'names shown', 'ok');

View File

@ -28,7 +28,7 @@ ephem.setExtendedRange(CONFIG.useExtendedRange);
scale.init(CONFIG); scale.init(CONFIG);
// ---- renderer / scene / camera ---- // ---- renderer / scene / camera ----
const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true }); const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true });
renderer.setSize(innerWidth, innerHeight); renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2)); renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.getElementById('scene').appendChild(renderer.domElement); document.getElementById('scene').appendChild(renderer.domElement);
@ -340,7 +340,8 @@ const ctx = {
}; };
const LAYER_MODULES = ['./layers/planets.js', './layers/moons.js', './layers/rings.js', const LAYER_MODULES = ['./layers/planets.js', './layers/moons.js', './layers/rings.js',
'./layers/spacecraft.js', './layers/asteroids.js', './layers/comets.js', './layers/neos.js']; './layers/spacecraft.js', './layers/asteroids.js', './layers/comets.js', './layers/neos.js',
'./layers/almanac.js'];
const activeLayers = []; const activeLayers = [];
async function loadLayers() { async function loadLayers() {
const results = await Promise.allSettled(LAYER_MODULES.map((p) => import(p))); const results = await Promise.allSettled(LAYER_MODULES.map((p) => import(p)));