145 lines
4.4 KiB
TypeScript
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>
|
|
);
|
|
}
|