cosmo/frontend/src/components/DwarfPlanetOrbits.tsx

145 lines
4.4 KiB
TypeScript

/**
* DwarfPlanetOrbits - renders orbital paths for dwarf planets using NASA data
*
* Dwarf planets have highly inclined orbits that deviate from the ecliptic plane
*/
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;
points: THREE.Vector3[];
color: string;
}
export function DwarfPlanetOrbits() {
const [orbits, setOrbits] = useState<OrbitData[]>([]);
const [loading, setLoading] = useState(true);
// Helper function to get default colors
const getDefaultColor = (name: string): string => {
const colorMap: Record<string, string> = {
'Pluto': '#8B7355',
'Ceres': '#9E9E9E',
'Eris': '#E0E0E0',
'Haumea': '#D4A574',
'Makemake': '#C49A6C',
};
return colorMap[name] || '#CCCCCC';
};
useEffect(() => {
const fetchOrbits = async () => {
console.log('🌌 Fetching dwarf planet orbits from NASA...');
const orbitData: OrbitData[] = [];
try {
// Step 1: Get list of dwarf planets from backend
const listResponse = await request.get('/celestial/list?body_type=dwarf_planet');
const listData = listResponse.data;
const dwarfPlanets = listData.bodies || [];
if (dwarfPlanets.length === 0) {
console.log('No dwarf planets found in database');
setLoading(false);
return;
}
console.log(`Found ${dwarfPlanets.length} dwarf planets:`, dwarfPlanets.map((p: any) => p.name_zh || p.name));
// Step 2: Fetch orbital data for all dwarf planets in ONE request
// Using a 10-year range with monthly samples
const startDate = new Date('2020-01-01');
const endDate = new Date('2030-01-01');
// Use body_ids parameter to fetch all dwarf planets
const bodyIds = dwarfPlanets.map((p: any) => p.id).join(',');
const response = await request.get('/celestial/positions', {
params: {
body_ids: bodyIds,
start_time: startDate.toISOString(),
end_time: endDate.toISOString(),
step: '30d',
},
});
const data = response.data;
// Step 3: Process each dwarf planet's orbital data
for (const planet of dwarfPlanets) {
const bodyData = data.bodies.find((b: any) => b.id === planet.id);
if (bodyData && bodyData.positions && bodyData.positions.length > 0) {
// Convert positions to Vector3 points with proper scaling
const points = bodyData.positions.map((pos: any) => {
// Apply the same non-linear scaling used by CelestialBody
const scaled = scalePosition(pos.x, pos.y, pos.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 endpoints are close enough
const firstPoint = points[0];
const lastPoint = points[points.length - 1];
if (firstPoint.distanceTo(lastPoint) < 5) {
points.push(firstPoint.clone());
}
// Use color from database or default color
const color = planet.color || getDefaultColor(planet.name);
orbitData.push({
bodyId: planet.id,
bodyName: planet.name_zh || planet.name,
points,
color,
});
console.log(`✅ Loaded orbit for ${planet.name_zh || planet.name}: ${points.length} points`);
}
}
} catch (error) {
console.error('Error fetching dwarf planet orbits:', error);
}
setOrbits(orbitData);
setLoading(false);
console.log(`🎉 Loaded ${orbitData.length} dwarf planet orbits`);
};
fetchOrbits();
}, []);
if (loading) {
console.log('⏳ Loading planet orbits...');
return null;
}
if (orbits.length === 0) {
console.warn('⚠️ No planet orbits loaded');
return null;
}
return (
<group>
{orbits.map((orbit) => (
<group key={orbit.bodyId}>
{/* Orbital path */}
<Line
points={orbit.points}
color={orbit.color}
lineWidth={1.5}
opacity={0.4}
transparent
/>
</group>
))}
</group>
);
}