Put orbits and stars in the same reference frame

The app's two sources disagree about which frame they are in, and nothing
reconciled them. HYG star positions are equatorial J2000 — that is what
raDecDistanceToXyz produces and what the galaxy view renders directly. Orbital
elements come from JPL Horizons, whose default reference plane for element
output is the ecliptic, and the ETL never overrides it. The two are tilted 23.4
degrees apart, so the orbits sat that far off the sky they are drawn against.

Confirmed rather than assumed, from both ends: the Horizons request in
lib/horizons.ts sets no REF_PLANE, and the resulting solar-system inclinations
are 0 to 17 degrees with Earth exactly 0.00 — which is only true of the ecliptic,
since Earth's orbit defines it.

eclipticToEquatorial now rotates orbit positions into the scene frame, so a
direction means the same thing in the galaxy view and the system view. The
rotation is about the vernal-equinox axis, which both frames share.

That exposed a presentation problem the old code had been hiding. The renderer
mapped the propagator's z straight onto the scene's vertical, which silently
redefined the frame but did make systems render flat. In a properly equatorial
scene, orbital planes lie 23.4 degrees off the scene's own axes, so a system
would be presented edge-on. Rather than rotate the world back into a comfortable
pose — which would only put the orbits at odds with the sky again — the camera
now settles relative to the orbital plane: a three-quarter view about 37 degrees
off the ecliptic normal. The arrival still begins along the approach direction
and swings round as it settles, so the transition stays continuous, and the
framing is now the same every time rather than inherited from wherever the
camera happened to be.

Tests: 247 passing, up from 237. The frame tests are the discriminating kind —
Earth's orbit must lie perpendicular to the ecliptic pole rather than to the
scene's vertical, and must reach 23.4 degrees of declination a quarter orbit on,
where it used to read zero. Verified in a browser against Sol and Gl 357.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G
This commit is contained in:
Claude
2026-08-04 16:10:17 +00:00
parent dad4b93e8a
commit 2293585940
7 changed files with 214 additions and 10 deletions
@@ -1,3 +1,5 @@
import { CartesianCoordinates, eclipticToEquatorial } from '../../shared/astro/coordinates';
/**
* How the system view sizes itself to whatever system it is showing.
*
@@ -42,6 +44,19 @@ function clamp(value: number, min: number, max: number): number {
return Math.min(max, Math.max(min, value));
}
/**
* Where the camera settles when arriving at a system, as a unit direction from the star in the
* scene's equatorial frame.
*
* The scene is equatorial so that orbits and stars share one frame, but orbital planes lie
* close to the *ecliptic*, which is tilted 23.4 degrees out of it. Left to the equatorial axes,
* a system would be presented edge-on. Rather than rotate the world into a comfortable pose —
* which would put the orbits back at odds with the sky — the camera is placed relative to the
* plane instead: this is a three-quarter view, about 37 degrees off the ecliptic normal, so a
* system reads as a disc while staying where it truly sits.
*/
export const SYSTEM_VIEW_DIRECTION: CartesianCoordinates = eclipticToEquatorial({ x: 0, y: 0.6, z: 0.8 });
/**
* Radius (AU) to draw the system's star at, given its innermost orbit.
*