Frame the Sun's system on Eris's aphelion, the furthest it draws, not on the grid ring inside it

The scene framed the grid's outer ring, sized from the largest semi-major axis, and two comments
said the ring was "always the wider of the two, by construction". That held until Eris came in:
its a = 67.93 AU gives an 80 AU ring, but with e = 0.438 its orbit reaches 97.7 AU, and Eris is
95.5 AU out now. The 12 per cent margin protected the ring, not Eris. The review measured Eris's
orbit at 0.982 of the half-width on a 390x844 phone (3.5 px from the edge), 0.987 on 1000x1400,
and on a 1000x1000 window Eris's marker at NDC 1.002, off screen on arrival.

SystemOrbitsRenderer.gridOuterRadiusAu becomes outermostRadiusAu: the ring, or the largest
top-level aphelion a(1 + e) where that runs past it. The scene frames that. Some orbit runs past
its ring in 303 of the 1 190 exoplanet systems too (counted on exoplanets.json), and they are
framed the same way. The 500 AU ceiling rises to 600: the aphelion needs 508 AU on a 390x844 phone,
and 600 holds it with its whole margin down to an aspect of 0.39. The comments are corrected.

Measured in the app on :4301 after entering the Sun, Eris's drawn orbit, largest |NDC x| over its
129 vertices (review's figures before):
  390x844    camera 507.9 AU  0.804  (0.982)
  1000x1400  camera 328.5 AU  0.805  (0.987)
  1000x1000  camera 234.7 AU  0.812  (1.004, marker off screen)
  950x1000   camera 247.0 AU  0.810
  1600x1000  camera 234.7 AU  0.508  (0.627)
No orbit vertex of Neptune, Pluto, Eris, Haumea or Makemake is off screen at any of them. The
cost: inner bodies arrive smaller, the landscape camera 235 AU out instead of 192.

Tests: renderer 'reaches as far as an eccentric orbit goes past the grid: Eris's aphelion, 97.7
AU, not the 80 AU ring' (and the ring where every orbit stays inside it), and framing 'leaves
Eris's aphelion its whole margin in every window shape'. Controls, each failing its named test
only (1 failed, 839 passed): framing on the ring alone; the ceiling back at 500 AU.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 21:24:23 +02:00
co-authored by Claude Opus 5.5
parent 3453cd5d9f
commit c45d9160e3
5 changed files with 59 additions and 21 deletions
@@ -329,10 +329,12 @@ export class SystemOrbitsRenderer {
*/
readonly referenceFrame: THREE.Quaternion;
/**
* Outer radius (AU) of the reference grid, or 0 where there is none. This — not the outermost
* orbit — is the widest thing the system draws, so it is what the camera has to frame.
* How far (AU) from the star the system draws anything, or 0 where it draws nothing: what the
* camera has to frame. The reference grid's outer ring, which runs 15 per cent past the largest
* semi-major axis, unless an eccentric orbit reaches further at its aphelion — Eris's, 97.7 AU,
* does past the solar system's 80 AU ring, and some orbit does in 303 of the 1 190 exoplanet systems.
*/
readonly gridOuterRadiusAu: number;
readonly outermostRadiusAu: number;
private readonly topLevelBodies: TrackedTopLevelBody[] = [];
private readonly moons: TrackedMoon[] = [];
@@ -376,10 +378,11 @@ export class SystemOrbitsRenderer {
const members: SystemMember[] = [];
const topLevelBodiesById = new Map<string, BodyRecord>();
const topLevelAxes = [
...bodies.filter((body) => !body.parentBodyId).map((body) => body.orbit.semiMajorAxisAu),
...exoplanets.filter((exoplanet) => isPropagatableOrbit(exoplanet.orbit)).map((exoplanet) => exoplanet.orbit.semiMajorAxisAu!)
].filter((axis) => Number.isFinite(axis) && axis > 0);
const topLevelOrbits = [
...bodies.filter((body) => !body.parentBodyId).map(({ orbit }) => ({ axis: orbit.semiMajorAxisAu, eccentricity: orbit.eccentricity })),
...exoplanets.filter((exoplanet) => isPropagatableOrbit(exoplanet.orbit)).map(({ orbit }) => ({ axis: orbit.semiMajorAxisAu!, eccentricity: orbit.eccentricity ?? 0 }))
].filter(({ axis }) => Number.isFinite(axis) && axis > 0);
const topLevelAxes = topLevelOrbits.map(({ axis }) => axis);
this.maxTopLevelSemiMajorAxisAu = topLevelAxes.length > 0 ? Math.max(...topLevelAxes) : 0;
this.minTopLevelSemiMajorAxisAu = topLevelAxes.length > 0 ? Math.min(...topLevelAxes) : 0;
@@ -453,7 +456,7 @@ export class SystemOrbitsRenderer {
this.referenceFrame = bodies.some((body) => !body.parentBodyId) ? ECLIPTIC_FRAME.clone() : exoplanetFrame;
const rings = systemGridRingsAu(this.maxTopLevelSemiMajorAxisAu);
this.gridOuterRadiusAu = rings.length > 0 ? rings[rings.length - 1] : 0;
this.outermostRadiusAu = Math.max(rings.length > 0 ? rings[rings.length - 1] : 0, ...topLevelOrbits.map(({ axis, eccentricity }) => axis * (1 + eccentricity)));
if (rings.length > 0) {
this.grid = new PolarGridPlane({
ringRadii: rings,