Merge branch 'feat/drawn-set-follows-view' into perf/routing-worker
The label and star-field review fixes arrive under the routing client: the scene keeps constructing RoutingClient beside the neighbourhood, and builds the brightness index where it built the order. Both sides' new scene tests are kept. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
This commit is contained in:
@@ -38,7 +38,7 @@ import { StarmapHudComponent } from './starmap-hud.component';
|
||||
import { SystemObjectCardComponent } from './system-object-card.component';
|
||||
import { RoutingClient } from './routing-client';
|
||||
import { colorIndexToRgb, FOCUS_RADIUS_PC, StarFieldRenderer, starRenderBudgetFromUrl } from './star-field-renderer';
|
||||
import { brightestWithin, brightnessOrder } from '../../shared/astro/brightest';
|
||||
import { BrightnessIndex, brightestWithin, brightnessIndex } from '../../shared/astro/brightest';
|
||||
import { StarNeighbourhood } from '../../shared/astro/star-neighbourhood';
|
||||
import { MAX_JUMP_RANGE_PC } from '../hud/routes-panel.component';
|
||||
import { HostStarRings } from './host-star-rings';
|
||||
@@ -374,7 +374,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
/** Stars with at least one catalogued body, which are the ones the map can be flown into. */
|
||||
private starIdsWithBodies = new Set<number>();
|
||||
/** Catalogue indices, brightest first, for the labels to walk rather than sort. See `brightestWithin`. */
|
||||
private starsByBrightness: Uint32Array = new Uint32Array(0);
|
||||
private starsByBrightness: BrightnessIndex = brightnessIndex([]);
|
||||
/** Stars alone, normalised once, for the two routing fields. Empty until the catalogue lands. */
|
||||
private readonly starSearchIndex = signal<IndexedSearchEntry[]>([]);
|
||||
private milkyWay?: MilkyWayRenderer;
|
||||
@@ -539,7 +539,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
this.starsById = new Map(stars.map((star) => [star.id, star]));
|
||||
this.neighbourhood = new StarNeighbourhood(stars);
|
||||
this.routing = new RoutingClient(stars, positions, this.neighbourhood);
|
||||
this.starsByBrightness = brightnessOrder(stars);
|
||||
this.starsByBrightness = brightnessIndex(stars);
|
||||
this.starSearchIndex.set(
|
||||
buildSearchIndex(stars.map((star) => ({ kind: 'star' as const, name: star.name, subtitle: star.spectralType, starId: star.id })))
|
||||
);
|
||||
@@ -553,7 +553,9 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
(id): id is number => id !== null && id !== undefined
|
||||
));
|
||||
|
||||
this.starField = new StarFieldRenderer(stars, positions, starRenderBudgetFromUrl(window.location.search), this.starsByBrightness);
|
||||
this.starField = new StarFieldRenderer(stars, positions, starRenderBudgetFromUrl(window.location.search), this.starsByBrightness.order);
|
||||
// It has just chosen around the Sun, which is where the view opens: the first label pass need not choose again.
|
||||
this.starFieldFocus = GALAXY_OVERVIEW_TARGET.clone();
|
||||
this.galaxyGroup.add(this.starField.object);
|
||||
this.hostRings = new HostStarRings(stars.filter((star) => this.starIdsWithBodies.has(star.id)), HUD_ACCENT);
|
||||
this.galaxyGroup.add(this.hostRings.object);
|
||||
@@ -786,7 +788,9 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
* of it is always drawn, however faint.
|
||||
*/
|
||||
private refocusStarField(): void {
|
||||
if (!this.starField) {
|
||||
// At galactic scale the whole catalogue is a smudge a few pixels across, and the view's centre
|
||||
// sweeps hundreds of parsecs a pass across empty space: nothing to choose, and nothing to see.
|
||||
if (!this.starField || !this.neighbourhood || this.galacticStrength >= GALACTIC_LEVEL_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
const centre = this.controls?.target ?? GALAXY_OVERVIEW_TARGET;
|
||||
@@ -796,7 +800,11 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
if (this.starFieldFocus && this.starFieldFocus.distanceTo(centre) <= STAR_FIELD_REFOCUS_PC && pins === this.starFieldPins) {
|
||||
return;
|
||||
}
|
||||
this.starField.refocus({ centre, pinnedIds });
|
||||
// By catalogue index, through the lookup the neighbourhood already holds: building a second
|
||||
// one of 423 651 entries on the first pin stalled the first flight of a session for 50-140 ms.
|
||||
const neighbourhood = this.neighbourhood;
|
||||
const pinned = pinnedIds.map((id) => neighbourhood.indexOf(id)).filter((index): index is number => index !== undefined);
|
||||
this.starField.refocus({ centre, pinned });
|
||||
this.starFieldFocus = centre.clone();
|
||||
this.starFieldPins = pins;
|
||||
}
|
||||
@@ -826,8 +834,8 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
// for anything with catalogued bodies: it is the one distinction the second line can draw that
|
||||
// the map cannot otherwise show, since it says which of these points is somewhere you can go.
|
||||
const starIdsWithBodies = this.starIdsWithBodies;
|
||||
const candidates = function* (stars: readonly StarRecord[], order: Uint32Array): Generator<LabeledPoint> {
|
||||
for (const star of brightestWithin(stars, order, target, labelRadius, selectedId)) {
|
||||
const candidates = function* (stars: readonly StarRecord[], index: BrightnessIndex): Generator<LabeledPoint> {
|
||||
for (const star of brightestWithin(stars, index, target, labelRadius, selectedId)) {
|
||||
yield { id: star.id, name: star.name, kind: starIdsWithBodies.has(star.id) ? 'System' : 'Star', x: star.x, y: star.y, z: star.z };
|
||||
}
|
||||
};
|
||||
@@ -862,10 +870,6 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
const projected = new THREE.Vector3();
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (chosen.length >= LABEL_MAX_COUNT) {
|
||||
break;
|
||||
}
|
||||
|
||||
projected.set(candidate.x, candidate.y, candidate.z).project(camera);
|
||||
const isKept = candidate.id === keepId;
|
||||
// Offscreen or behind the camera.
|
||||
@@ -892,6 +896,11 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
placed.push(point);
|
||||
chosen.push({ ...candidate, side });
|
||||
// Here rather than at the top of the loop: there, taking the fifteenth label asked the
|
||||
// candidates for a sixteenth first, and near the Sun finding one walks most of the catalogue.
|
||||
if (chosen.length >= LABEL_MAX_COUNT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return chosen;
|
||||
|
||||
Reference in New Issue
Block a user