Scale the system view to the system it is showing
Star size, planet marker size and camera distance were all fixed constants in
AU, tuned against the solar system's 30 AU span. Real systems span four orders
of magnitude, and the fixed values served only the wide end. Measured across the
370 systems that draw planets:
- 170 had their innermost orbit inside the 0.2 AU star sphere, and for 107 of
those every orbit was inside it, so the system rendered as a lone sphere.
- 193 were framed from the 3 AU distance floor — for TRAPPIST-1 that is 48x
the width of the entire system, reducing it to a cluster of specks.
- Planet markers were effectively a flat 0.09 AU, since almost every body
clamps to the maximum. Inside Gl 357's 0.204 AU system that is wider than
the orbits themselves: one planet swallowed the whole view.
All three are now derived from the system's own measurements. The star is a
fraction of the innermost orbit, so it can never reach the closest one. The
camera is a multiple of the outermost orbit, so everything fits. Markers scale
with the span against the solar system as the reference, so the constants that
were tuned by eye keep their meaning. Because star, markers and camera all
scale together, a compact system now looks like a wide one — same apparent star,
same legible spread of orbits.
Gl 357 is the case that motivated this. It gained three planets in the previous
commit and still rendered as a bare star, because all three orbits were inside
the star sphere. It now shows its star and all three orbits.
The renderer measures the span before building anything, since markers are sized
against it as they are created, which also removes the reduce over tracked
bodies that used to compute it afterwards. The star sphere is rebuilt per system
rather than shared, so its geometry is now disposed on each transition.
Sol is deliberately unchanged: its innermost orbit is Mercury at 0.387 AU, so
the star lands just under the old fixed radius, and the reference span makes the
marker scale factor 1. Verified side by side.
Tests: 206 passing, up from 199. Verified in a real browser against both ends of
the range — Gl 357 at 0.2 AU and Sol at 30 AU.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaySiNst4HhDXBHnMy8p5G
This commit is contained in:
@@ -15,6 +15,7 @@ import { StarRecord } from '../../shared/models/star.model';
|
||||
import { NavigationStore } from '../../shared/state/navigation.store';
|
||||
import { CameraRigController } from './camera-rig-controller';
|
||||
import { DeepSkyRenderer } from './deep-sky-renderer';
|
||||
import { starMarkerRadiusAu, systemFramingDistanceAu } from './system-framing';
|
||||
import { colorIndexToRgb, StarFieldRenderer } from './star-field-renderer';
|
||||
import { LabeledPoint, StarLabelOverlay } from './star-label-overlay';
|
||||
import { SystemOrbitsRenderer } from './system-orbits-renderer';
|
||||
@@ -55,16 +56,12 @@ const SYSTEM_MAX_DISTANCE_AU = 5000;
|
||||
const SYSTEM_ENTRY_DISTANCE_AU = 200;
|
||||
/** How far out (AU) the camera flies before swapping back to galaxy/parsec space. */
|
||||
const SYSTEM_EXIT_DISTANCE_AU = 400;
|
||||
const MIN_SYSTEM_FRAMING_DISTANCE_AU = 3;
|
||||
const MAX_SYSTEM_FRAMING_DISTANCE_AU = 80;
|
||||
|
||||
const APPROACH_DURATION_SECONDS = 1.0;
|
||||
const SETTLE_DURATION_SECONDS = 0.9;
|
||||
const EXIT_DURATION_SECONDS = 0.9;
|
||||
const RETURN_DURATION_SECONDS = 1.1;
|
||||
|
||||
const STAR_MARKER_RADIUS_AU = 0.2;
|
||||
|
||||
/**
|
||||
* Hosts the shared galaxy + system scene: pan/zoom/rotate camera controls, click-to-select
|
||||
* picking, proximity-based name labels, and — once a star is selected — a camera-flight
|
||||
@@ -102,7 +99,8 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
private readonly galaxyGroup = new THREE.Group();
|
||||
private readonly systemGroup = new THREE.Group();
|
||||
private readonly starMarkerMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
|
||||
private readonly starMarkerGeometry = new THREE.SphereGeometry(STAR_MARKER_RADIUS_AU, 24, 16);
|
||||
/** Rebuilt per system, since the star's radius is derived from that system's innermost orbit. */
|
||||
private starMarkerGeometry?: THREE.SphereGeometry;
|
||||
|
||||
private controls?: OrbitControls;
|
||||
private rig?: CameraRigController;
|
||||
@@ -157,7 +155,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
this.systemRenderer?.dispose();
|
||||
(this.starMarker?.material as THREE.Material | undefined)?.dispose();
|
||||
(this.starGlow?.material as THREE.SpriteMaterial | undefined)?.dispose();
|
||||
this.starMarkerGeometry.dispose();
|
||||
this.starMarkerGeometry?.dispose();
|
||||
this.starMarkerMaterial.dispose();
|
||||
this.engine.dispose();
|
||||
}
|
||||
@@ -397,6 +395,11 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
this.systemRenderer = new SystemOrbitsRenderer(systemBodies, systemExoplanets);
|
||||
this.systemGroup.add(this.systemRenderer.object);
|
||||
|
||||
// Sized against this system's innermost orbit, so the star never swallows its own planets.
|
||||
const starRadiusAu = starMarkerRadiusAu(this.systemRenderer.minTopLevelSemiMajorAxisAu);
|
||||
this.starMarkerGeometry?.dispose();
|
||||
this.starMarkerGeometry = new THREE.SphereGeometry(starRadiusAu, 24, 16);
|
||||
|
||||
const starMarkerMaterial = this.starMarkerMaterial.clone();
|
||||
const starColor = colorIndexToRgb(star.colorIndex, star.spectralType);
|
||||
if (star.id === SOL_STAR_ID) {
|
||||
@@ -404,10 +407,10 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
// other point in the galaxy view is far too distant to be resolved as a disk.
|
||||
starMarkerMaterial.map = loadCachedTexture(SUN_TEXTURE_PATH);
|
||||
starMarkerMaterial.color.set(0xffffff);
|
||||
this.starGlow = createGlowSprite(0xfff2c0, STAR_MARKER_RADIUS_AU, SUN_GLOW_SCALE);
|
||||
this.starGlow = createGlowSprite(0xfff2c0, starRadiusAu, SUN_GLOW_SCALE);
|
||||
} else {
|
||||
starMarkerMaterial.color.copy(starColor);
|
||||
this.starGlow = createGlowSprite(starColor, STAR_MARKER_RADIUS_AU, SUN_GLOW_SCALE * 0.6);
|
||||
this.starGlow = createGlowSprite(starColor, starRadiusAu, SUN_GLOW_SCALE * 0.6);
|
||||
}
|
||||
this.starMarker = new THREE.Mesh(this.starMarkerGeometry, starMarkerMaterial);
|
||||
this.systemGroup.add(this.starMarker, this.starGlow);
|
||||
@@ -427,11 +430,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
this.rig!.setImmediate({ position: direction.clone().multiplyScalar(SYSTEM_ENTRY_DISTANCE_AU), target: new THREE.Vector3(0, 0, 0) });
|
||||
|
||||
const framingDistance = THREE.MathUtils.clamp(
|
||||
this.systemRenderer.maxTopLevelSemiMajorAxisAu * 2.4 || MIN_SYSTEM_FRAMING_DISTANCE_AU,
|
||||
MIN_SYSTEM_FRAMING_DISTANCE_AU,
|
||||
MAX_SYSTEM_FRAMING_DISTANCE_AU
|
||||
);
|
||||
const framingDistance = systemFramingDistanceAu(this.systemRenderer.maxTopLevelSemiMajorAxisAu);
|
||||
|
||||
this.rig!.flyTo({ position: direction.clone().multiplyScalar(framingDistance), target: new THREE.Vector3(0, 0, 0) }, SETTLE_DURATION_SECONDS, () => {
|
||||
this.currentStarId = star.id;
|
||||
|
||||
Reference in New Issue
Block a user