@
Add star-map Angular app, ETL pipeline, and caveman plugin Angular 3D star map (galaxy/system/body views, Three.js rendering, navigation store) plus the NASA ETL tooling that builds the star, exoplanet and solar-system datasets, Playwright e2e suite, and the cs:caveman Claude Code plugin (command, agent, skill). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> @
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import * as THREE from 'three/webgpu';
|
||||
import { CSS2DObject, CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';
|
||||
|
||||
export interface LabeledPoint {
|
||||
id: number;
|
||||
name: string;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders DOM-based (CSS2D) name labels anchored to 3D star positions. Labels are added as
|
||||
* children of the main scene (so `CSS2DRenderer` can project them with the same camera) and
|
||||
* diffed against the previous frame's set so the DOM is only touched when the visible set
|
||||
* of stars actually changes, not every frame.
|
||||
*/
|
||||
export class StarLabelOverlay {
|
||||
readonly domElement: HTMLElement;
|
||||
|
||||
private readonly cssRenderer = new CSS2DRenderer();
|
||||
private readonly labelObjects = new Map<number, CSS2DObject>();
|
||||
|
||||
constructor(private readonly scene: THREE.Scene) {
|
||||
this.cssRenderer.domElement.classList.add('star-label-layer');
|
||||
this.domElement = this.cssRenderer.domElement;
|
||||
}
|
||||
|
||||
setSize(width: number, height: number): void {
|
||||
this.cssRenderer.setSize(width, height);
|
||||
}
|
||||
|
||||
/** Shows exactly these labels, adding/removing DOM elements only for a changed set. */
|
||||
update(points: readonly LabeledPoint[]): void {
|
||||
const idsToShow = new Set(points.map((point) => point.id));
|
||||
|
||||
for (const [id, object] of this.labelObjects) {
|
||||
if (!idsToShow.has(id)) {
|
||||
this.removeLabel(id, object);
|
||||
}
|
||||
}
|
||||
|
||||
for (const point of points) {
|
||||
if (!this.labelObjects.has(point.id)) {
|
||||
this.addLabel(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(camera: THREE.Camera): void {
|
||||
this.cssRenderer.render(this.scene, camera);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const [id, object] of this.labelObjects) {
|
||||
this.removeLabel(id, object);
|
||||
}
|
||||
}
|
||||
|
||||
private addLabel(point: LabeledPoint): void {
|
||||
const element = document.createElement('div');
|
||||
// Tailwind utility classes assigned directly since this element lives outside Angular's
|
||||
// view encapsulation (see the class comment above) rather than through a component template.
|
||||
element.className = 'translate-x-1.5 -translate-y-1.5 whitespace-nowrap font-body text-[11px] text-accent [text-shadow:0_0_4px_rgba(0,0,0,0.9)]';
|
||||
element.textContent = point.name;
|
||||
|
||||
const object = new CSS2DObject(element);
|
||||
object.position.set(point.x, point.y, point.z);
|
||||
this.scene.add(object);
|
||||
this.labelObjects.set(point.id, object);
|
||||
}
|
||||
|
||||
private removeLabel(id: number, object: CSS2DObject): void {
|
||||
this.scene.remove(object);
|
||||
object.element.remove();
|
||||
this.labelObjects.delete(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user