diff --git a/src/app/features/galaxy-system/galaxy-system-scene.component.spec.ts b/src/app/features/galaxy-system/galaxy-system-scene.component.spec.ts index fbcc633..6867d19 100644 --- a/src/app/features/galaxy-system/galaxy-system-scene.component.spec.ts +++ b/src/app/features/galaxy-system/galaxy-system-scene.component.spec.ts @@ -989,6 +989,8 @@ describe('GalaxySystemSceneComponent camera-flight transitions', () => { expect(radius / SUN_RADIUS_AU).toBeLessThan(760); expect(scene.controls.minDistance).toBeCloseTo(closestApproachAu(radius), 9); expect(scene.controls.minDistance).toBeGreaterThan(9); + // Settled where its disc stays inside the ring of its neighbours' names, not pressed up to it. + expect(engine.getCamera().position.length()).toBeGreaterThan(1.2 * scene.controls.minDistance); }); it('draws a star nothing gives a size or temperature for as a grey point, not as the Sun', async () => { diff --git a/src/app/features/galaxy-system/system-framing.spec.ts b/src/app/features/galaxy-system/system-framing.spec.ts index 1f38a90..ee65dba 100644 --- a/src/app/features/galaxy-system/system-framing.spec.ts +++ b/src/app/features/galaxy-system/system-framing.spec.ts @@ -63,6 +63,17 @@ describe('systemFramingDistanceAu', () => { expect(systemFramingDistanceAu(0, undefined, 0.00465)).toBe(systemFramingDistanceAu(0)); }); + it("holds a giant's disc inside the ring its neighbours are named on, and the camera clear of its closest approach", () => { + // The ring is at 0.74 of the tighter half-extent; the disc is kept to half of it, in either window. + const betelgeuseAu = 2.72; + for (const viewport of [{ fovDegrees: 50, aspect: 1.6 }, { fovDegrees: 50, aspect: 0.6 }]) { + const distance = systemFramingDistanceAu(0, viewport, betelgeuseAu); + const tight = Math.tan((25 * Math.PI) / 180) * Math.min(1, viewport.aspect); + expect(Math.tan(Math.asin(betelgeuseAu / distance)) / tight).toBeCloseTo(0.5, 9); + expect(distance).toBeGreaterThan(closestApproachAu(betelgeuseAu)); + } + }); + it('caps the distance so a far-flung companion cannot shrink the star to nothing', () => { expect(systemFramingDistanceAu(1000)).toBe(systemFramingDistanceAu(5000)); }); diff --git a/src/app/features/galaxy-system/system-framing.ts b/src/app/features/galaxy-system/system-framing.ts index 73e221f..5718542 100644 --- a/src/app/features/galaxy-system/system-framing.ts +++ b/src/app/features/galaxy-system/system-framing.ts @@ -59,6 +59,15 @@ const MIN_FRAMING_DISTANCE_AU = 0.06; */ const MAX_FRAMING_DISTANCE_AU = 200; +/** + * How much of the view's tighter half-extent a giant's disc may take on arrival: inside the ring + * the system view names the star's neighbours on, at 0.74 of it, and clear of the names hung + * inward from it, whose nearest corners come within 292 px of the centre on a 1 000 px view. + * Framed to fill the frame instead, Betelgeuse settled at the three-radius closest approach with + * a disc of 379 px, past the 370 px ring, and its neighbours' names on it. + */ +const STAR_FRAME_FRACTION = 0.5; + /** Framing for a star with no known planets, where there is nothing to fit. */ const EMPTY_SYSTEM_FRAMING_DISTANCE_AU = 3; @@ -116,8 +125,9 @@ export function systemFrameRadiusAu(distanceAu: number, viewport: SystemViewport */ export function systemFramingDistanceAu(framedRadiusAu: number, viewport: SystemViewport = DEFAULT_SYSTEM_VIEWPORT, starRadiusAu = 0): number { // A giant drawn at its own radius can be wider than the system around it — Betelgeuse's 584 - // solar radii are 2.7 AU — or than the empty framing, and the camera must not settle inside it. - const star = (starRadiusAu * (1 + FRAME_MARGIN)) / tightHalfExtent(viewport); + // solar radii are 2.7 AU — or than the empty framing. Its disc is a sphere's, whose silhouette + // from d subtends asin(R / d): the distance that makes it the fraction above of the view. + const star = starRadiusAu * Math.sqrt(1 + 1 / (STAR_FRAME_FRACTION * tightHalfExtent(viewport)) ** 2); if (!Number.isFinite(framedRadiusAu) || framedRadiusAu <= 0) { return Math.max(EMPTY_SYSTEM_FRAMING_DISTANCE_AU, star); }