Draw the 1509 exoplanets that were being silently dropped

The system renderer required both a semi-major axis and an eccentricity before
it would place an exoplanet, even though resolveOrbitalElements already defaults
every other missing element. The archive publishes an axis far more often than
an eccentricity: 3895 records have one and only 2386 have both, so 1509 planets
were dropped for want of a value that can simply be assumed.

A missing eccentricity now defaults to 0, a circle. That is the conventional
assumption for an orbit whose shape has not been constrained, and it is the only
honest option available, since the axis alone says nothing about elongation.

The effect is not subtle. 18 systems gain planets, and seven of them previously
rendered as a bare star with nothing around it at all: Gl 357 goes from zero
planets to three, HD 176986 likewise. Beyond the effect today, a user could
already reach one of these planets through search and its detail page, then jump
to its system and find it missing from the very system it belongs to.

isPropagatableOrbit replaces the old inline guard and also rejects what the old
one never checked: a non-positive axis, and an eccentricity of 1 or more. Those
are escape trajectories that no ellipse describes, and propagating them anyway
does not throw — it yields NaN, which reaches the vertex buffer and poisons the
geometry's bounding sphere, disabling culling for the whole object rather than
just the bad orbit. Being a type guard, it also lets the caller drop a seven-line
field-by-field copy of the orbit.

Fixes a label leak found while verifying this in the browser. Galaxy star labels
were being cleared on entering system space but immediately recomputed, because
the tick gated them on `currentStarId`, which is not assigned until the arrival
flight finishes a second later — so parsec-scale names sat pinned over the
system. Both label and orbit updates now gate on which group is actually visible,
which is true throughout the transition rather than only at the end of it.

Tests: 185 passing, up from 171. Verified in a real browser: GJ 1151 draws the
orbit and marker it gained, and no labels survive into the system view.

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 11:31:59 +00:00
parent 8d8c65bdb2
commit f241b093eb
5 changed files with 157 additions and 20 deletions
@@ -88,6 +88,54 @@ describe('SystemOrbitsRenderer exoplanet propagation', () => {
renderer.dispose();
});
describe('orbits with no published eccentricity', () => {
// The archive publishes a semi-major axis far more often than an eccentricity. Requiring
// both dropped 1509 otherwise drawable planets.
it('draws a planet that has an axis but no eccentricity', () => {
const renderer = new SystemOrbitsRenderer([], [exoplanet({ orbit: { semiMajorAxisAu: 0.4 } })]);
expect(renderer.members).toHaveLength(1);
renderer.dispose();
});
it('places it on a circle of the right radius', () => {
const renderer = new SystemOrbitsRenderer([], [exoplanet({ orbit: { semiMajorAxisAu: 0.4 } })]);
for (const offset of [0, 5, 20, 60]) {
expect(positionAt(renderer, DEFAULT_EPOCH_JD + offset).length()).toBeCloseTo(0.4, 6);
}
renderer.dispose();
});
it('still honours the measured period', () => {
const renderer = new SystemOrbitsRenderer(
[],
[exoplanet({ orbit: { semiMajorAxisAu: TRAPPIST_1B_SEMI_MAJOR_AXIS_AU }, periodDays: TRAPPIST_1B_PERIOD_DAYS })]
);
const start = positionAt(renderer, DEFAULT_EPOCH_JD);
const afterOnePeriod = positionAt(renderer, DEFAULT_EPOCH_JD + TRAPPIST_1B_PERIOD_DAYS);
expect(afterOnePeriod.distanceTo(start)).toBeLessThan(1e-6);
renderer.dispose();
});
});
it('skips an escape trajectory rather than emitting NaN positions', () => {
// e >= 1 is not an ellipse; propagating it anyway yields NaN, which poisons the geometry's
// bounding sphere and disables culling for the whole object.
const renderer = new SystemOrbitsRenderer([], [exoplanet({ orbit: { semiMajorAxisAu: 1, eccentricity: 1.4 } })]);
expect(renderer.members).toHaveLength(0);
renderer.dispose();
});
it('skips a non-positive semi-major axis', () => {
const renderer = new SystemOrbitsRenderer([], [exoplanet({ orbit: { semiMajorAxisAu: 0, eccentricity: 0.1 } })]);
expect(renderer.members).toHaveLength(0);
renderer.dispose();
});
it('keeps every propagated position finite', () => {
const renderer = new SystemOrbitsRenderer([], [exoplanet({ periodDays: TRAPPIST_1B_PERIOD_DAYS, orbit: { semiMajorAxisAu: TRAPPIST_1B_SEMI_MAJOR_AXIS_AU, eccentricity: 0.62 } })]);