import * as THREE from "three"; import { useMemo, useState } from "react"; import { Text, Billboard } from "@react-three/drei"; const TYPE_COLORS = { Galaxy: "#ff88cc", // Pink Nebula: "#88ffcc", // Teal Cluster: "#ffff88", // Yellow }; const TYPE_SHAPES = { Galaxy: "O", Nebula: "☁", Cluster: "::", }; export function DSOs({ data, visible, showLabels, onSelect }) { const [hovered, setHovered] = useState(null); const texture = useMemo(() => { const canvas = document.createElement("canvas"); canvas.width = 64; canvas.height = 64; const ctx = canvas.getContext("2d"); const grd = ctx.createRadialGradient(32, 32, 0, 32, 32, 32); grd.addColorStop(0, "rgba(255,255,255,1)"); grd.addColorStop(0.5, "rgba(255,255,255,0.2)"); grd.addColorStop(1, "rgba(255,255,255,0)"); ctx.fillStyle = grd; ctx.fillRect(0, 0, 64, 64); ctx.beginPath(); ctx.arc(32, 32, 28, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(255,255,255,0.8)"; ctx.lineWidth = 2; ctx.stroke(); return new THREE.CanvasTexture(canvas); }, []); const objects = useMemo(() => { if (!data || data.names.length === 0) return []; const radius = 95; const list = []; data.names.forEach((name, i) => { const alt = data.altitude[i]; const az = data.azimuth[i]; const phi = (90 - alt) * (Math.PI / 180); const theta = az * (Math.PI / 180); list.push({ name: name, type: data.types[i], mag: data.magnitude[i], pos: [ radius * Math.sin(phi) * Math.sin(theta), radius * Math.cos(phi), -radius * Math.sin(phi) * Math.cos(theta), ], }); }); return list; }, [data]); return ( {objects.map((obj, i) => ( { e.stopPropagation(); onSelect({ name: obj.name, description: `${obj.type} - Mag ${obj.mag}`, }); }} onPointerOver={(e) => { e.stopPropagation(); setHovered(obj.name); document.body.style.cursor = "pointer"; }} onPointerOut={(e) => { setHovered(null); document.body.style.cursor = "auto"; }} > {(showLabels || hovered === obj.name) && ( {obj.name} )} ))} ); }