Frame the system view from the camera it actually has

The grid overflowed the frame in 368 of the 371 systems the datasets
contain — median fill 1.11, and the outermost ring cut off by the viewport
edge in almost every one.

Two compounding causes. The framing distance was a fixed multiple of the
outermost orbit, tuned by eye against a 55-degree field of view; the
engine's camera is 50. And it framed the outermost *orbit*, while the
widest thing actually drawn is the grid's outer ring, which by
construction always sits beyond it.

Neither is fixable by adjusting the multiple, because a multiple is the
wrong shape of answer: what has to fit is a radius on screen, and how much
radius a given distance buys depends entirely on the lens. So the distance
now comes from the camera's own vertical field of view and aspect —
picking whichever screen axis is the tighter one, so a portrait window
backs off further rather than clipping — applied to the grid's outer ring
with an explicit margin around it.

The ceiling goes up with it. Eighty AU could not frame the solar system
out to Pluto once the real field of view was accounted for; that needs 120
on a landscape display and 140 on a portrait one. Only companions hundreds
of AU out reach the new ceiling, and those still arrive framed on their
inner region.

Measured across every system in the data, at three window shapes: the
overflow count drops from 368 to 2, the fill settles at exactly 0.89 —
the margin, uniformly — and the outer ring still encloses the outermost
orbit everywhere, so neither invariant was traded for the other.

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-05 07:30:02 +00:00
parent ac296f5133
commit 6019987fc4
6 changed files with 152 additions and 18 deletions
@@ -169,6 +169,11 @@ export class SystemOrbitsRenderer {
* frame: the ecliptic for the solar system, the plane of the sky for everything else.
*/
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.
*/
readonly gridOuterRadiusAu: number;
private readonly topLevelBodies: TrackedTopLevelBody[] = [];
private readonly moons: TrackedMoon[] = [];
@@ -266,6 +271,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;
if (rings.length > 0) {
this.grid = new PolarGridPlane({
ringRadii: rings,