126 lines
3.6 KiB
TypeScript
126 lines
3.6 KiB
TypeScript
|
|
/**
|
||
|
|
* 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';
|
||
|
|
|
||
|
|
interface OrbitData {
|
||
|
|
bodyId: string;
|
||
|
|
bodyName: string;
|
||
|
|
bodyNameZh: string | null;
|
||
|
|
points: THREE.Vector3[];
|
||
|
|
color: string;
|
||
|
|
numPoints: number;
|
||
|
|
periodDays: number | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function OrbitRenderer() {
|
||
|
|
const [orbits, setOrbits] = useState<OrbitData[]>([]);
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
const [error, setError] = useState<string | null>(null);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const fetchOrbits = async () => {
|
||
|
|
console.log('🌌 Fetching orbital data from backend...');
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Fetch precomputed orbits from backend
|
||
|
|
const response = await fetch('http://localhost:8000/api/celestial/orbits');
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
throw new Error(`Failed to fetch orbits: ${response.statusText}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const data = await response.json();
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`📊 Processing ${data.orbits.length} orbits...`);
|
||
|
|
|
||
|
|
// 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());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(` ✅ ${orbit.body_name_zh || orbit.body_name}: ${points.length} points`);
|
||
|
|
|
||
|
|
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);
|
||
|
|
console.log(`🎉 Loaded ${orbitData.length} orbits successfully`);
|
||
|
|
|
||
|
|
} catch (err) {
|
||
|
|
console.error('❌ Failed to load orbits:', err);
|
||
|
|
setError(err instanceof Error ? err.message : 'Unknown error');
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
fetchOrbits();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
if (loading) {
|
||
|
|
console.log('⏳ Loading orbits...');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
console.error('⚠️ Orbit rendering error:', error);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (orbits.length === 0) {
|
||
|
|
console.warn('⚠️ No orbits to render');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<group>
|
||
|
|
{orbits.map((orbit) => (
|
||
|
|
<Line
|
||
|
|
key={orbit.bodyId}
|
||
|
|
points={orbit.points}
|
||
|
|
color={orbit.color}
|
||
|
|
lineWidth={1.5}
|
||
|
|
opacity={0.4}
|
||
|
|
transparent
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</group>
|
||
|
|
);
|
||
|
|
}
|