/** * Constellations component - renders major constellations with connecting lines */ import { useEffect, useState, useMemo } from 'react'; import { Line, Text, Billboard } from '@react-three/drei'; import * as THREE from 'three'; import { fetchStaticData } from '../utils/api'; interface ConstellationStar { name: string; ra: number; // Right Ascension in degrees dec: number; // Declination in degrees } interface Constellation { name: string; name_zh: string; stars: ConstellationStar[]; lines: [number, number][]; // Indices of stars to connect } /** * Convert RA/Dec to Cartesian coordinates * Use fixed distance for constellation stars to create celestial sphere effect */ function raDecToCartesian(ra: number, dec: number, distance: number = 100) { const raRad = (ra * Math.PI) / 180; const decRad = (dec * Math.PI) / 180; const x = distance * Math.cos(decRad) * Math.cos(raRad); const y = distance * Math.cos(decRad) * Math.sin(raRad); const z = distance * Math.sin(decRad); return new THREE.Vector3(x, y, z); } export function Constellations() { const [constellations, setConstellations] = useState([]); useEffect(() => { // Load constellation data from API fetchStaticData('constellation') .then((response) => { // Convert API response to Constellation format const constellationData = response.items.map((item) => ({ name: item.name, name_zh: item.name_zh, stars: item.data.stars, lines: item.data.lines, })); setConstellations(constellationData); }) .catch((err) => console.error('Failed to load constellations:', err)); }, []); const constellationLines = useMemo(() => { return constellations.map((constellation) => { // Convert all stars to 3D positions const starPositions = constellation.stars.map((star) => raDecToCartesian(star.ra, star.dec) ); // Create line segments based on connection indices const lineSegments = constellation.lines.map(([startIdx, endIdx]) => ({ start: starPositions[startIdx], end: starPositions[endIdx], })); // Calculate center position for label (average of all stars) const center = starPositions.reduce( (acc, pos) => acc.add(pos), new THREE.Vector3() ).divideScalar(starPositions.length); return { name: constellation.name, nameZh: constellation.name_zh, starPositions, lineSegments, center, }; }); }, [constellations]); if (constellationLines.length === 0) { return null; } return ( {constellationLines.map((constellation) => ( {/* Render constellation stars */} {constellation.starPositions.map((pos, idx) => ( ))} {/* Render connecting lines */} {constellation.lineSegments.map((segment, idx) => ( ))} {/* Constellation name label */} {constellation.nameZh} ))} ); }