/** * Main 3D Scene component */ import { Canvas } from '@react-three/fiber'; import { OrbitControls, Stars as BackgroundStars, Html } from '@react-three/drei'; import { useMemo, useState, useEffect } from 'react'; import { CelestialBody } from './CelestialBody'; import { Probe } from './Probe'; import { CameraController } from './CameraController'; import { Trajectory } from './Trajectory'; import { OrbitRenderer } from './OrbitRenderer'; import { Stars } from './Stars'; import { Constellations } from './Constellations'; import { Galaxies } from './Galaxies'; import { Nebulae } from './Nebulae'; import { FocusInfo } from './FocusInfo'; import { AsteroidBelts } from './AsteroidBelts'; import { scalePosition } from '../utils/scaleDistance'; import { calculateRenderPosition } from '../utils/renderPosition'; import type { CelestialBody as CelestialBodyType, Position } from '../types'; interface SceneProps { bodies: CelestialBodyType[]; selectedBody: CelestialBodyType | null; trajectoryPositions?: Position[]; showOrbits?: boolean; onBodySelect?: (body: CelestialBodyType | null) => void; resetTrigger?: number; } export function Scene({ bodies, selectedBody, trajectoryPositions = [], showOrbits = true, onBodySelect, resetTrigger = 0 }: SceneProps) { // State to control info panel visibility (independent of selection) const [showInfoPanel, setShowInfoPanel] = useState(true); // Reset info panel visibility when selected body changes useEffect(() => { if (selectedBody) { setShowInfoPanel(true); } }, [selectedBody]); // Separate natural celestial bodies (planets/dwarf planets/satellites/stars) from probes const celestialBodies = bodies.filter((b) => b.type !== 'probe'); const probes = bodies.filter((b) => b.type === 'probe'); // Always show all probes (changed from previous behavior) const visibleProbes = probes; // Calculate target position for OrbitControls const controlsTarget = useMemo(() => { if (selectedBody) { const pos = selectedBody.positions[0]; const scaledPos = scalePosition(pos.x, pos.y, pos.z); return [scaledPos.x, scaledPos.z, scaledPos.y] as [number, number, number]; } return [0, 0, 0] as [number, number, number]; }, [selectedBody]); // Calculate position for FocusInfo (needs to match rendered position of body) const focusInfoPosition = useMemo(() => { if (!selectedBody) return [0, 0, 0] as [number, number, number]; // We need to use the EXACT same logic as CelestialBody/Probe components // to ensure the label sticks to the object const renderPos = calculateRenderPosition(selectedBody, bodies); // Convert to Three.js coordinates (x, z, y) return [renderPos.x, renderPos.z, renderPos.y] as [number, number, number]; }, [selectedBody, bodies]); return (