/** * OrbitRenderer - Unified orbit rendering component * Renders precomputed orbital paths for all celestial bodies (planets and dwarf planets) */ import { useEffect, useState } from 'react'; import { Line } from '@react-three/drei'; import * as THREE from 'three'; import { scalePosition } from '../utils/scaleDistance'; import { request } from '../utils/request'; interface OrbitData { bodyId: string; bodyName: string; bodyNameZh: string | null; points: THREE.Vector3[]; color: string; numPoints: number; periodDays: number | null; } interface OrbitRendererProps { visible?: boolean; } export function OrbitRenderer({ visible = true }: OrbitRendererProps) { const [orbits, setOrbits] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchOrbits = async () => { try { // Fetch precomputed orbits from backend const response = await request.get('/celestial/orbits'); const data = response.data; if (!data.orbits || data.orbits.length === 0) { console.warn('⚠️ No orbital data found in database'); setLoading(false); setError('No orbital data available. Please generate orbits first.'); return; } // Convert to Three.js format const orbitData: OrbitData[] = data.orbits.map((orbit: any) => { // Convert position points to Vector3 with scaling const points = orbit.points.map((p: any) => { const scaled = scalePosition(p.x, p.y, p.z); // Convert to Three.js coordinate system (x, z, y) return new THREE.Vector3(scaled.x, scaled.z, scaled.y); }); // Close the orbit loop if first and last points are close if (points.length > 1) { const firstPoint = points[0]; const lastPoint = points[points.length - 1]; const distance = firstPoint.distanceTo(lastPoint); // If endpoints are close (within 5 units), close the loop if (distance < 5) { points.push(firstPoint.clone()); } } return { bodyId: orbit.body_id, bodyName: orbit.body_name, bodyNameZh: orbit.body_name_zh, points, color: orbit.color || '#CCCCCC', numPoints: orbit.num_points, periodDays: orbit.period_days, }; }); setOrbits(orbitData); setLoading(false); } catch (err) { console.error('❌ Failed to load orbits:', err); setError(err instanceof Error ? err.message : 'Unknown error'); setLoading(false); } }; fetchOrbits(); }, []); if (loading) { return null; } if (error) { console.error('⚠️ Orbit rendering error:', error); return null; } if (orbits.length === 0) { return null; } return ( {orbits.map((orbit) => ( ))} ); }