@zendir/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +589 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +421 -0
- package/dist/index.js.map +1 -0
- package/dist/react/3d/EarthViewer.d.ts +46 -0
- package/dist/react/3d/EarthViewer.js +836 -0
- package/dist/react/3d/EarthViewer.js.map +1 -0
- package/dist/react/3d/SolarSystemViewer.d.ts +43 -0
- package/dist/react/3d/SolarSystemViewer.js +372 -0
- package/dist/react/3d/SolarSystemViewer.js.map +1 -0
- package/dist/react/3d/ZenSpace3D.d.ts +16 -0
- package/dist/react/3d/ZenSpace3D.js +1253 -0
- package/dist/react/3d/ZenSpace3D.js.map +1 -0
- package/dist/react/3d/ZenSpace3DCesium.d.ts +9 -0
- package/dist/react/3d/ZenSpace3DCesium.js +186 -0
- package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
- package/dist/react/3d/ZenSpace3DShaders.d.ts +78 -0
- package/dist/react/3d/ZenSpace3DShaders.js +94 -0
- package/dist/react/3d/ZenSpace3DShaders.js.map +1 -0
- package/dist/react/3d/ZenSpace3DTypes.d.ts +614 -0
- package/dist/react/3d/ZenSpace3DUtils.d.ts +183 -0
- package/dist/react/3d/ZenSpace3DUtils.js +213 -0
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
- package/dist/react/3d/index.d.ts +23 -0
- package/dist/react/3d/threeLoader.d.ts +22 -0
- package/dist/react/3d/threeLoader.js +18 -0
- package/dist/react/3d/threeLoader.js.map +1 -0
- package/dist/react/astro/ClassificationBanner.d.ts +25 -0
- package/dist/react/astro/ClassificationBanner.js +83 -0
- package/dist/react/astro/ClassificationBanner.js.map +1 -0
- package/dist/react/astro/GlobalStatusBar.d.ts +42 -0
- package/dist/react/astro/GlobalStatusBar.js +165 -0
- package/dist/react/astro/GlobalStatusBar.js.map +1 -0
- package/dist/react/astro/MissionClock.d.ts +169 -0
- package/dist/react/astro/MissionClock.js +411 -0
- package/dist/react/astro/MissionClock.js.map +1 -0
- package/dist/react/astro/MonitoringIcon.d.ts +60 -0
- package/dist/react/astro/MonitoringIcon.js +369 -0
- package/dist/react/astro/MonitoringIcon.js.map +1 -0
- package/dist/react/astro/Notification.d.ts +42 -0
- package/dist/react/astro/Notification.js +156 -0
- package/dist/react/astro/Notification.js.map +1 -0
- package/dist/react/astro/Progress.d.ts +39 -0
- package/dist/react/astro/Progress.js +149 -0
- package/dist/react/astro/Progress.js.map +1 -0
- package/dist/react/astro/SimulationControls.d.ts +136 -0
- package/dist/react/astro/SimulationControls.js +668 -0
- package/dist/react/astro/SimulationControls.js.map +1 -0
- package/dist/react/astro/StatusIndicator.d.ts +34 -0
- package/dist/react/astro/StatusIndicator.js +189 -0
- package/dist/react/astro/StatusIndicator.js.map +1 -0
- package/dist/react/astro/UnifiedTimeline.d.ts +106 -0
- package/dist/react/astro/UnifiedTimeline.js +1768 -0
- package/dist/react/astro/UnifiedTimeline.js.map +1 -0
- package/dist/react/astro/index.d.ts +63 -0
- package/dist/react/cards/AccessCard.d.ts +37 -0
- package/dist/react/cards/AccessCard.js +410 -0
- package/dist/react/cards/AccessCard.js.map +1 -0
- package/dist/react/cards/OrbitCard.d.ts +31 -0
- package/dist/react/cards/OrbitCard.js +372 -0
- package/dist/react/cards/OrbitCard.js.map +1 -0
- package/dist/react/cards/SpacecraftCard.d.ts +54 -0
- package/dist/react/cards/SpacecraftCard.js +941 -0
- package/dist/react/cards/SpacecraftCard.js.map +1 -0
- package/dist/react/cards/TelemetryCard.d.ts +40 -0
- package/dist/react/cards/TelemetryCard.js +742 -0
- package/dist/react/cards/TelemetryCard.js.map +1 -0
- package/dist/react/cards/TelemetryStreamCard.d.ts +59 -0
- package/dist/react/cards/TelemetryStreamCard.js +309 -0
- package/dist/react/cards/TelemetryStreamCard.js.map +1 -0
- package/dist/react/cards/index.d.ts +13 -0
- package/dist/react/charts/GroundTrackMap.d.ts +112 -0
- package/dist/react/charts/GroundTrackMap.js +1123 -0
- package/dist/react/charts/GroundTrackMap.js.map +1 -0
- package/dist/react/charts/GroundTrackMapLeaflet.d.ts +26 -0
- package/dist/react/charts/GroundTrackMapLeaflet.js +571 -0
- package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -0
- package/dist/react/charts/groundTrackMapLeafletTiles.d.ts +22 -0
- package/dist/react/charts/groundTrackMapLeafletTiles.js +11 -0
- package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -0
- package/dist/react/charts/groundTrackMapLeafletUtils.d.ts +24 -0
- package/dist/react/charts/groundTrackMapLeafletUtils.js +109 -0
- package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -0
- package/dist/react/charts/index.d.ts +10 -0
- package/dist/react/charts/unified/AstroChart.d.ts +24 -0
- package/dist/react/charts/unified/AstroChart.js +1405 -0
- package/dist/react/charts/unified/AstroChart.js.map +1 -0
- package/dist/react/charts/unified/PowerOverviewChart.d.ts +73 -0
- package/dist/react/charts/unified/PowerOverviewChart.js +488 -0
- package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -0
- package/dist/react/charts/unified/domain.d.ts +845 -0
- package/dist/react/charts/unified/domain.js +3168 -0
- package/dist/react/charts/unified/domain.js.map +1 -0
- package/dist/react/charts/unified/generators.d.ts +276 -0
- package/dist/react/charts/unified/generators.js +518 -0
- package/dist/react/charts/unified/generators.js.map +1 -0
- package/dist/react/charts/unified/index.d.ts +55 -0
- package/dist/react/charts/unified/presets.d.ts +290 -0
- package/dist/react/charts/unified/presets.js +999 -0
- package/dist/react/charts/unified/presets.js.map +1 -0
- package/dist/react/charts/unified/sync.d.ts +69 -0
- package/dist/react/charts/unified/sync.js +219 -0
- package/dist/react/charts/unified/sync.js.map +1 -0
- package/dist/react/charts/unified/theme.d.ts +447 -0
- package/dist/react/charts/unified/theme.js +562 -0
- package/dist/react/charts/unified/theme.js.map +1 -0
- package/dist/react/charts/unified/types.d.ts +826 -0
- package/dist/react/charts/unified/useChartStream.d.ts +58 -0
- package/dist/react/charts/unified/useChartStream.js +226 -0
- package/dist/react/charts/unified/useChartStream.js.map +1 -0
- package/dist/react/chatgpt/AppCard.d.ts +59 -0
- package/dist/react/chatgpt/AppCard.js +306 -0
- package/dist/react/chatgpt/AppCard.js.map +1 -0
- package/dist/react/chatgpt/ChatGPTCard.d.ts +6 -0
- package/dist/react/chatgpt/index.d.ts +167 -0
- package/dist/react/chatgpt/index.js +166 -0
- package/dist/react/chatgpt/index.js.map +1 -0
- package/dist/react/context/DisplaySettingsContext.d.ts +107 -0
- package/dist/react/context/DisplaySettingsContext.js +169 -0
- package/dist/react/context/DisplaySettingsContext.js.map +1 -0
- package/dist/react/context/index.d.ts +5 -0
- package/dist/react/core/ActivityPlanner.d.ts +45 -0
- package/dist/react/core/ActivityPlanner.js +532 -0
- package/dist/react/core/ActivityPlanner.js.map +1 -0
- package/dist/react/core/AppBar.d.ts +71 -0
- package/dist/react/core/AppBar.js +817 -0
- package/dist/react/core/AppBar.js.map +1 -0
- package/dist/react/core/AstroIcon.d.ts +84 -0
- package/dist/react/core/AstroIcon.js +1243 -0
- package/dist/react/core/AstroIcon.js.map +1 -0
- package/dist/react/core/Badge.d.ts +27 -0
- package/dist/react/core/Badge.js +134 -0
- package/dist/react/core/Badge.js.map +1 -0
- package/dist/react/core/Button.d.ts +26 -0
- package/dist/react/core/Button.js +306 -0
- package/dist/react/core/Button.js.map +1 -0
- package/dist/react/core/CardHeader.d.ts +34 -0
- package/dist/react/core/CardHeader.js +316 -0
- package/dist/react/core/CardHeader.js.map +1 -0
- package/dist/react/core/ChatPanel.d.ts +627 -0
- package/dist/react/core/ChatPanel.js +1144 -0
- package/dist/react/core/ChatPanel.js.map +1 -0
- package/dist/react/core/Checkbox.d.ts +26 -0
- package/dist/react/core/Checkbox.js +130 -0
- package/dist/react/core/Checkbox.js.map +1 -0
- package/dist/react/core/ColorPickerPanel.d.ts +25 -0
- package/dist/react/core/ColorPickerPanel.js +293 -0
- package/dist/react/core/ColorPickerPanel.js.map +1 -0
- package/dist/react/core/CommandBuilder.d.ts +74 -0
- package/dist/react/core/CommandBuilder.js +518 -0
- package/dist/react/core/CommandBuilder.js.map +1 -0
- package/dist/react/core/ConfirmDialog.d.ts +45 -0
- package/dist/react/core/ConfirmDialog.js +315 -0
- package/dist/react/core/ConfirmDialog.js.map +1 -0
- package/dist/react/core/ConnectionForm.d.ts +57 -0
- package/dist/react/core/ConnectionForm.js +496 -0
- package/dist/react/core/ConnectionForm.js.map +1 -0
- package/dist/react/core/Container.d.ts +51 -0
- package/dist/react/core/Container.js +670 -0
- package/dist/react/core/Container.js.map +1 -0
- package/dist/react/core/CopyButton.d.ts +39 -0
- package/dist/react/core/CopyButton.js +105 -0
- package/dist/react/core/CopyButton.js.map +1 -0
- package/dist/react/core/DataTable.d.ts +113 -0
- package/dist/react/core/DataTable.js +446 -0
- package/dist/react/core/DataTable.js.map +1 -0
- package/dist/react/core/DataValue.d.ts +64 -0
- package/dist/react/core/DataValue.js +545 -0
- package/dist/react/core/DataValue.js.map +1 -0
- package/dist/react/core/Dialog.d.ts +32 -0
- package/dist/react/core/Dialog.js +201 -0
- package/dist/react/core/Dialog.js.map +1 -0
- package/dist/react/core/FileExplorer.d.ts +65 -0
- package/dist/react/core/FileExplorer.js +520 -0
- package/dist/react/core/FileExplorer.js.map +1 -0
- package/dist/react/core/GlassCard.d.ts +129 -0
- package/dist/react/core/GlassCard.js +375 -0
- package/dist/react/core/GlassCard.js.map +1 -0
- package/dist/react/core/HeaderIconWithStatus.d.ts +39 -0
- package/dist/react/core/HeaderIconWithStatus.js +157 -0
- package/dist/react/core/HeaderIconWithStatus.js.map +1 -0
- package/dist/react/core/HexViewer.d.ts +143 -0
- package/dist/react/core/HexViewer.js +1106 -0
- package/dist/react/core/HexViewer.js.map +1 -0
- package/dist/react/core/Icon.d.ts +32 -0
- package/dist/react/core/Icon.js +142 -0
- package/dist/react/core/Icon.js.map +1 -0
- package/dist/react/core/ImageGallery.d.ts +41 -0
- package/dist/react/core/ImageGallery.js +320 -0
- package/dist/react/core/ImageGallery.js.map +1 -0
- package/dist/react/core/Input.d.ts +38 -0
- package/dist/react/core/Input.js +288 -0
- package/dist/react/core/Input.js.map +1 -0
- package/dist/react/core/LimitsBar.d.ts +51 -0
- package/dist/react/core/LimitsBar.js +200 -0
- package/dist/react/core/LimitsBar.js.map +1 -0
- package/dist/react/core/LogViewer.d.ts +61 -0
- package/dist/react/core/LogViewer.js +599 -0
- package/dist/react/core/LogViewer.js.map +1 -0
- package/dist/react/core/MessageStream.d.ts +58 -0
- package/dist/react/core/MessageStream.js +455 -0
- package/dist/react/core/MessageStream.js.map +1 -0
- package/dist/react/core/MissionCalendar.d.ts +81 -0
- package/dist/react/core/MissionCalendar.js +1049 -0
- package/dist/react/core/MissionCalendar.js.map +1 -0
- package/dist/react/core/NumberInput.d.ts +85 -0
- package/dist/react/core/NumberInput.js +507 -0
- package/dist/react/core/NumberInput.js.map +1 -0
- package/dist/react/core/PacketViewer.d.ts +73 -0
- package/dist/react/core/PacketViewer.js +431 -0
- package/dist/react/core/PacketViewer.js.map +1 -0
- package/dist/react/core/Pagination.d.ts +30 -0
- package/dist/react/core/Pagination.js +190 -0
- package/dist/react/core/Pagination.js.map +1 -0
- package/dist/react/core/PinInput.d.ts +41 -0
- package/dist/react/core/PinInput.js +210 -0
- package/dist/react/core/PinInput.js.map +1 -0
- package/dist/react/core/Popover.d.ts +55 -0
- package/dist/react/core/Popover.js +288 -0
- package/dist/react/core/Popover.js.map +1 -0
- package/dist/react/core/Select.d.ts +42 -0
- package/dist/react/core/Select.js +303 -0
- package/dist/react/core/Select.js.map +1 -0
- package/dist/react/core/SideNav.d.ts +103 -0
- package/dist/react/core/SideNav.js +551 -0
- package/dist/react/core/SideNav.js.map +1 -0
- package/dist/react/core/SidePanel.d.ts +33 -0
- package/dist/react/core/SidePanel.js +199 -0
- package/dist/react/core/SidePanel.js.map +1 -0
- package/dist/react/core/Tabs.d.ts +47 -0
- package/dist/react/core/Tabs.js +129 -0
- package/dist/react/core/Tabs.js.map +1 -0
- package/dist/react/core/Toast.d.ts +56 -0
- package/dist/react/core/Toast.js +229 -0
- package/dist/react/core/Toast.js.map +1 -0
- package/dist/react/core/Toggle.d.ts +22 -0
- package/dist/react/core/Toggle.js +151 -0
- package/dist/react/core/Toggle.js.map +1 -0
- package/dist/react/core/Tooltip.d.ts +19 -0
- package/dist/react/core/Tooltip.js +179 -0
- package/dist/react/core/Tooltip.js.map +1 -0
- package/dist/react/core/Typography.d.ts +127 -0
- package/dist/react/core/Typography.js +187 -0
- package/dist/react/core/Typography.js.map +1 -0
- package/dist/react/core/index.d.ts +108 -0
- package/dist/react/core/layout/Box.d.ts +77 -0
- package/dist/react/core/layout/Box.js +126 -0
- package/dist/react/core/layout/Box.js.map +1 -0
- package/dist/react/core/layout/Center.d.ts +20 -0
- package/dist/react/core/layout/Center.js +34 -0
- package/dist/react/core/layout/Center.js.map +1 -0
- package/dist/react/core/layout/Divider.d.ts +16 -0
- package/dist/react/core/layout/Divider.js +108 -0
- package/dist/react/core/layout/Divider.js.map +1 -0
- package/dist/react/core/layout/Flex.d.ts +30 -0
- package/dist/react/core/layout/Flex.js +128 -0
- package/dist/react/core/layout/Flex.js.map +1 -0
- package/dist/react/core/layout/Grid.d.ts +36 -0
- package/dist/react/core/layout/Grid.js +142 -0
- package/dist/react/core/layout/Grid.js.map +1 -0
- package/dist/react/core/layout/Spacer.d.ts +8 -0
- package/dist/react/core/layout/Spacer.js +31 -0
- package/dist/react/core/layout/Spacer.js.map +1 -0
- package/dist/react/core/layout/Stack.d.ts +54 -0
- package/dist/react/core/layout/Stack.js +74 -0
- package/dist/react/core/layout/Stack.js.map +1 -0
- package/dist/react/core/layout/index.d.ts +38 -0
- package/dist/react/core/layout/responsive.d.ts +23 -0
- package/dist/react/core/layout/responsive.js +26 -0
- package/dist/react/core/layout/responsive.js.map +1 -0
- package/dist/react/core/layout/useBreakpoint.d.ts +77 -0
- package/dist/react/core/layout/useBreakpoint.js +73 -0
- package/dist/react/core/layout/useBreakpoint.js.map +1 -0
- package/dist/react/core/propertyConfig.d.ts +443 -0
- package/dist/react/core/propertyConfig.js +399 -0
- package/dist/react/core/propertyConfig.js.map +1 -0
- package/dist/react/hooks/index.d.ts +21 -0
- package/dist/react/hooks/useAccessWindows.d.ts +66 -0
- package/dist/react/hooks/useCompactMode.d.ts +82 -0
- package/dist/react/hooks/useCompactMode.js +62 -0
- package/dist/react/hooks/useCompactMode.js.map +1 -0
- package/dist/react/hooks/useLiveSelection.d.ts +57 -0
- package/dist/react/hooks/useSimulationPlayback.d.ts +97 -0
- package/dist/react/hooks/useSimulationTime.d.ts +61 -0
- package/dist/react/hooks/useSpacecraftPosition.d.ts +50 -0
- package/dist/react/hooks/useSpacecraftPosition.js +89 -0
- package/dist/react/hooks/useSpacecraftPosition.js.map +1 -0
- package/dist/react/hooks/useTelemetry.d.ts +55 -0
- package/dist/react/hooks/useTelemetry.js +73 -0
- package/dist/react/hooks/useTelemetry.js.map +1 -0
- package/dist/react/hooks/useZendirSession.d.ts +109 -0
- package/dist/react/hooks/useZendirSession.js +148 -0
- package/dist/react/hooks/useZendirSession.js.map +1 -0
- package/dist/react/index.d.ts +74 -0
- package/dist/react/shared/ErrorBoundary.d.ts +63 -0
- package/dist/react/shared/ErrorBoundary.js +142 -0
- package/dist/react/shared/ErrorBoundary.js.map +1 -0
- package/dist/react/shared/Skeleton.d.ts +110 -0
- package/dist/react/shared/Skeleton.js +324 -0
- package/dist/react/shared/Skeleton.js.map +1 -0
- package/dist/react/shared/index.d.ts +12 -0
- package/dist/react/theme/ThemeProvider.d.ts +385 -0
- package/dist/react/theme/ThemeProvider.js +1096 -0
- package/dist/react/theme/ThemeProvider.js.map +1 -0
- package/dist/react/theme/astro-tokens.d.ts +153 -0
- package/dist/react/theme/cardAccent.d.ts +75 -0
- package/dist/react/theme/cardAccent.js +137 -0
- package/dist/react/theme/cardAccent.js.map +1 -0
- package/dist/react/theme/config.d.ts +39 -0
- package/dist/react/theme/index.d.ts +9 -0
- package/dist/react/types.d.ts +360 -0
- package/dist/react/types.js +58 -0
- package/dist/react/types.js.map +1 -0
- package/dist/react/utils/index.d.ts +247 -0
- package/dist/react/utils/index.js +423 -0
- package/dist/react/utils/index.js.map +1 -0
- package/dist/react/visualizations/EclipseTimerCard.d.ts +17 -0
- package/dist/react/visualizations/EclipseTimerCard.js +250 -0
- package/dist/react/visualizations/EclipseTimerCard.js.map +1 -0
- package/dist/react/visualizations/LinkBudgetCard.d.ts +50 -0
- package/dist/react/visualizations/LinkBudgetCard.js +444 -0
- package/dist/react/visualizations/LinkBudgetCard.js.map +1 -0
- package/dist/react/visualizations/NavBallCard.d.ts +17 -0
- package/dist/react/visualizations/NavBallCard.js +243 -0
- package/dist/react/visualizations/NavBallCard.js.map +1 -0
- package/dist/react/visualizations/PropulsionCard.d.ts +37 -0
- package/dist/react/visualizations/PropulsionCard.js +298 -0
- package/dist/react/visualizations/PropulsionCard.js.map +1 -0
- package/dist/react/visualizations/SensorFootprintCard.d.ts +33 -0
- package/dist/react/visualizations/SensorFootprintCard.js +326 -0
- package/dist/react/visualizations/SensorFootprintCard.js.map +1 -0
- package/dist/react/visualizations/ThermalHeatmapCard.d.ts +38 -0
- package/dist/react/visualizations/ThermalHeatmapCard.js +372 -0
- package/dist/react/visualizations/ThermalHeatmapCard.js.map +1 -0
- package/dist/react/visualizations/index.d.ts +17 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +421 -0
- package/dist/react.js.map +1 -0
- package/dist/shaders/atmosphere.frag.js +5 -0
- package/dist/shaders/atmosphere.frag.js.map +1 -0
- package/dist/shaders/atmosphere.vert.js +5 -0
- package/dist/shaders/atmosphere.vert.js.map +1 -0
- package/dist/shaders/stars.frag.js +5 -0
- package/dist/shaders/stars.frag.js.map +1 -0
- package/dist/shaders/stars.vert.js +5 -0
- package/dist/shaders/stars.vert.js.map +1 -0
- package/dist/style.css +143 -0
- package/dist/tokens/index.d.ts +296 -0
- package/dist/tokens/index.js +263 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/tokens.css +155 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +220 -0
- package/sdk-stub.js +22 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EarthViewer.js","sources":["../../../src/react/3d/EarthViewer.tsx"],"sourcesContent":["/**\n * @zendir/ui - EarthViewer Component\n * \n * Interactive 3D Earth visualization with:\n * - Theme-integrated via useTheme() for container styling\n * - High-quality Earth texture from api-viewer\n * - Shader-based atmosphere glow effect\n * - Twinkling stars with animation\n * - Multiple spacecraft with orbit lines\n * - Ground stations with visibility cones\n * - Hover tooltips with live data\n * - Camera controls (drag/zoom)\n * - MCP Apps SDK compatible\n * \n * Falls back to 2D GroundTrackMap if WebGL unavailable.\n */\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { useTheme } from '../theme';\nimport { AstroIcon } from '../core/AstroIcon';\nimport type { SpacecraftPosition, GroundStation, GroundTrackPoint } from '../types';\nimport { GroundTrackMap } from '../charts/GroundTrackMap';\n\n// Import shaders as raw text (Vite)\nimport atmosphereVertexShader from '../../shaders/atmosphere.vert?raw';\nimport atmosphereFragmentShader from '../../shaders/atmosphere.frag?raw';\nimport starsVertexShader from '../../shaders/stars.vert?raw';\nimport starsFragmentShader from '../../shaders/stars.frag?raw';\nimport { loadThree, ThreeModule } from './threeLoader';\n\nexport interface EarthViewerProps {\n /** Spacecraft positions to display */\n spacecraft?: SpacecraftPosition[];\n /** Ground stations to display */\n groundStations?: GroundStation[];\n /** Ground track for selected spacecraft */\n groundTrack?: GroundTrackPoint[];\n /** Selected spacecraft ID */\n selectedSpacecraftId?: string;\n /** Canvas width */\n width?: number | string;\n /** Canvas height */\n height?: number;\n /** Auto-rotate globe (disabled when user interacts) */\n autoRotate?: boolean;\n /** Show atmosphere effect */\n showAtmosphere?: boolean;\n /** Show orbit lines */\n showOrbits?: boolean;\n /** Show ground station visibility cones */\n showVisibilityCones?: boolean;\n /** Callback when spacecraft is clicked */\n onSpacecraftClick?: (spacecraft: SpacecraftPosition) => void;\n /** Callback when ground station is clicked */\n onGroundStationClick?: (station: GroundStation) => void;\n /** Custom className */\n className?: string;\n /** Focus target (from focus_earth_view tool) - camera will smoothly transition to this */\n focusTarget?: {\n type: 'spacecraft' | 'groundStation' | 'region' | 'overview';\n id?: string;\n name?: string;\n latitude?: number;\n longitude?: number;\n zoom?: number;\n };\n /** Camera transition duration in milliseconds (default: 2000) */\n transitionDuration?: number;\n /** Auto-orbit mode: continuous camera movement around target */\n autoOrbit?: boolean;\n}\n\ninterface HoveredObject {\n type: 'spacecraft' | 'groundStation';\n id: string;\n name?: string;\n data?: SpacecraftPosition | GroundStation;\n screenX: number;\n screenY: number;\n}\n\ninterface WebGLCapabilities {\n available: boolean;\n version: number;\n maxTextureSize: number;\n}\n\nfunction checkWebGLCapabilities(): WebGLCapabilities {\n try {\n const canvas = document.createElement('canvas');\n const gl2 = canvas.getContext('webgl2');\n if (gl2) {\n return {\n available: true,\n version: 2,\n maxTextureSize: gl2.getParameter(gl2.MAX_TEXTURE_SIZE),\n };\n }\n \n const gl = canvas.getContext('webgl');\n if (gl) {\n return {\n available: true,\n version: 1,\n maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),\n };\n }\n } catch {\n // WebGL not available\n }\n \n return { available: false, version: 0, maxTextureSize: 0 };\n}\n\n// === Constants (from api-viewer/EarthConstants.ts) ===\nconst EARTH_RADIUS = 3000;\nconst EARTH_RADIUS_KM = 6371;\nconst KM_TO_SCENE = EARTH_RADIUS / EARTH_RADIUS_KM;\nconst DEG_TO_RAD = Math.PI / 180;\n\n// Convert lat/lon/alt to 3D Cartesian\nfunction latLonAltToCartesian(lat: number, lon: number, altKm: number = 0): [number, number, number] {\n const r = EARTH_RADIUS + altKm * KM_TO_SCENE;\n const phi = (90 - lat) * DEG_TO_RAD;\n const theta = (lon + 180) * DEG_TO_RAD;\n \n const x = -r * Math.sin(phi) * Math.cos(theta);\n const y = r * Math.cos(phi);\n const z = r * Math.sin(phi) * Math.sin(theta);\n \n return [x, y, z];\n}\n\nexport function EarthViewer({\n spacecraft = [],\n groundStations = [],\n groundTrack = [],\n selectedSpacecraftId,\n width = '100%',\n height = 400,\n autoRotate = true,\n showAtmosphere = true,\n showOrbits = true,\n showVisibilityCones = false,\n onSpacecraftClick,\n onGroundStationClick,\n className = '',\n focusTarget,\n transitionDuration = 2000,\n autoOrbit = false,\n}: EarthViewerProps): React.ReactElement {\n const { tokens: _tokens } = useTheme();\n const containerRef = useRef<HTMLDivElement>(null);\n const rendererRef = useRef<any>(null);\n const sceneRef = useRef<any>(null);\n const cameraRef = useRef<any>(null);\n const earthRef = useRef<any>(null);\n const controlsRef = useRef<any>(null);\n const raycasterRef = useRef<any>(null);\n const mouseRef = useRef<any>(null);\n const animationRef = useRef<number | null>(null);\n const starsMaterialRef = useRef<any>(null);\n const timeRef = useRef(0);\n const isUserInteractingRef = useRef(false);\n \n // Camera focus/transition state\n const focusTransitionRef = useRef<{\n startTime: number;\n startPos: [number, number, number];\n targetPos: [number, number, number];\n startTarget: [number, number, number];\n targetTarget: [number, number, number];\n duration: number;\n isActive: boolean;\n } | null>(null);\n const orbitAngleRef = useRef(0);\n const lastFocusTargetRef = useRef<typeof focusTarget | null>(null) as React.MutableRefObject<typeof focusTarget | null>;\n const isAnimatingRef = useRef(false); // Guard against multiple animation loops\n \n // Refs for animation-dependent values to prevent animation loop restart (fixes flickering)\n const autoRotateRef = useRef(autoRotate);\n const autoOrbitRef = useRef(autoOrbit);\n const focusTargetRef = useRef(focusTarget);\n \n // Keep refs in sync with props\n useEffect(() => { autoRotateRef.current = autoRotate; }, [autoRotate]);\n useEffect(() => { autoOrbitRef.current = autoOrbit; }, [autoOrbit]);\n useEffect(() => { focusTargetRef.current = focusTarget; }, [focusTarget]);\n \n const [webglCapabilities] = useState(() => checkWebGLCapabilities());\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [threeLoaded, setThreeLoaded] = useState(false);\n const [THREE, setTHREE] = useState<ThreeModule | null>(null);\n const [OrbitControlsClass, setOrbitControlsClass] = useState<any>(null);\n const [hoveredObject, setHoveredObject] = useState<HoveredObject | null>(null);\n\n // Load Three.js and OrbitControls dynamically (shared loader prevents multiple instances)\n useEffect(() => {\n if (!webglCapabilities.available) {\n setIsLoading(false);\n return;\n }\n\n loadThree()\n .then(({ THREE: threeModule, OrbitControls: controls }) => {\n setTHREE(threeModule);\n setOrbitControlsClass(() => controls);\n setThreeLoaded(true);\n })\n .catch((err) => {\n if (import.meta.env.DEV) {\n console.error('Failed to load Three.js:', err);\n }\n setError('Failed to load 3D library');\n setIsLoading(false);\n });\n }, [webglCapabilities.available]);\n\n // Initialize Three.js scene\n const initScene = useCallback(() => {\n if (!THREE || !containerRef.current || !OrbitControlsClass) return;\n\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const w = rect.width;\n const h = height;\n\n // Scene\n const scene = new THREE.Scene();\n sceneRef.current = scene;\n\n // Camera\n const camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 100000);\n camera.position.set(0, 0, 10000);\n cameraRef.current = camera;\n\n // Renderer (preserveDrawingBuffer prevents flickering)\n const renderer = new THREE.WebGLRenderer({ \n antialias: true, \n alpha: true,\n powerPreference: 'high-performance',\n preserveDrawingBuffer: true, // Prevents flickering from buffer swaps\n });\n renderer.setSize(w, h);\n renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n container.appendChild(renderer.domElement);\n rendererRef.current = renderer;\n\n // OrbitControls for mouse interaction\n const controls = new OrbitControlsClass(camera, renderer.domElement);\n controls.enableDamping = true;\n controls.dampingFactor = 0.05;\n controls.minDistance = 4500;\n controls.maxDistance = 20000;\n controls.enablePan = false;\n let interactionTimer: ReturnType<typeof setTimeout>;\n const handleControlStart = () => { isUserInteractingRef.current = true; };\n const handleControlEnd = () => {\n clearTimeout(interactionTimer);\n interactionTimer = setTimeout(() => { isUserInteractingRef.current = false; }, 3000);\n };\n controls.addEventListener('start', handleControlStart);\n controls.addEventListener('end', handleControlEnd);\n controlsRef.current = controls;\n\n // Raycaster for hover/click detection\n raycasterRef.current = new THREE.Raycaster();\n mouseRef.current = new THREE.Vector2();\n\n // === EARTH ===\n const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 64, 32);\n const earthMaterial = new THREE.MeshPhongMaterial({\n color: 0x2244aa,\n shininess: 10,\n });\n \n // Load high-quality texture (from api-viewer)\n const textureLoader = new THREE.TextureLoader();\n textureLoader.load(\n '/world.topo.jpg',\n (texture: any) => {\n // Configure texture for better quality (from api-viewer)\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.ClampToEdgeWrapping;\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.flipY = true;\n \n earthMaterial.map = texture;\n earthMaterial.needsUpdate = true;\n setIsLoading(false);\n },\n undefined,\n () => {\n setIsLoading(false);\n }\n );\n \n const earth = new THREE.Mesh(earthGeometry, earthMaterial);\n // Rotate to align with coordinate conversion (from api-viewer)\n earth.rotation.y = -Math.PI / 2 + Math.PI;\n scene.add(earth);\n earthRef.current = earth;\n\n // === ATMOSPHERE (Shader-based from api-viewer) ===\n if (showAtmosphere) {\n const atmosphereGeometry = new THREE.SphereGeometry(EARTH_RADIUS * 1.25, 24, 12);\n const atmosphereMaterial = new THREE.ShaderMaterial({\n vertexShader: atmosphereVertexShader,\n fragmentShader: atmosphereFragmentShader,\n blending: THREE.AdditiveBlending,\n side: THREE.BackSide,\n transparent: true,\n depthWrite: false,\n });\n const atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);\n atmosphere.rotation.y = -Math.PI / 2;\n scene.add(atmosphere);\n }\n\n // === STARS (Twinkling shader-based from api-viewer) ===\n const starsCount = 5000;\n const starsGeometry = new THREE.BufferGeometry();\n const starsPositions = new Float32Array(starsCount * 3);\n const starsOpacities = new Float32Array(starsCount);\n \n for (let i = 0; i < starsCount * 3; i += 3) {\n const radius = 50000 + Math.random() * 50000;\n const theta = Math.random() * Math.PI * 2;\n const phi = Math.acos(2 * Math.random() - 1);\n starsPositions[i] = radius * Math.sin(phi) * Math.cos(theta);\n starsPositions[i + 1] = radius * Math.sin(phi) * Math.sin(theta);\n starsPositions[i + 2] = radius * Math.cos(phi);\n starsOpacities[i / 3] = Math.random();\n }\n \n starsGeometry.setAttribute('position', new THREE.BufferAttribute(starsPositions, 3));\n starsGeometry.setAttribute('opacity', new THREE.BufferAttribute(starsOpacities, 1));\n \n const starsMaterial = new THREE.ShaderMaterial({\n uniforms: { time: { value: 0 } },\n vertexShader: starsVertexShader,\n fragmentShader: starsFragmentShader,\n transparent: true,\n depthWrite: false,\n blending: THREE.AdditiveBlending,\n });\n starsMaterialRef.current = starsMaterial;\n \n const stars = new THREE.Points(starsGeometry, starsMaterial);\n scene.add(stars);\n\n // === LIGHTING ===\n const ambientLight = new THREE.AmbientLight(0x404040, 0.6);\n scene.add(ambientLight);\n\n const sunLight = new THREE.DirectionalLight(0xffffff, 1.2);\n sunLight.position.set(10000, 5000, 10000);\n scene.add(sunLight);\n\n // Cleanup\n return () => {\n clearTimeout(interactionTimer);\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n }\n controls.removeEventListener('start', handleControlStart);\n controls.removeEventListener('end', handleControlEnd);\n controls.dispose();\n renderer.dispose();\n if (container.contains(renderer.domElement)) {\n container.removeChild(renderer.domElement);\n }\n };\n }, [THREE, OrbitControlsClass, height, showAtmosphere]);\n\n // Update all scene objects (spacecraft, ground stations, orbits)\n const updateSceneObjects = useCallback(() => {\n if (!THREE || !sceneRef.current) return;\n\n const scene = sceneRef.current;\n\n // Remove existing dynamic objects\n const toRemove = scene.children.filter((obj: any) => \n obj.userData?.isSpacecraft || \n obj.userData?.isGroundStation || \n obj.userData?.isOrbit ||\n obj.userData?.isVisibilityCone\n );\n toRemove.forEach((obj: any) => {\n if (obj.geometry) obj.geometry.dispose();\n if (obj.material) obj.material.dispose();\n scene.remove(obj);\n });\n\n // ========================================\n // Add Spacecraft Markers\n // ========================================\n spacecraft.forEach((sc) => {\n const [x, y, z] = latLonAltToCartesian(sc.latitude, sc.longitude, sc.altitude);\n const isSelected = sc.id === selectedSpacecraftId;\n \n // Spacecraft sphere\n const geometry = new THREE.SphereGeometry(isSelected ? 80 : 55, 12, 12);\n const material = new THREE.MeshBasicMaterial({\n color: isSelected ? 0xffd700 : 0x00ff88,\n });\n const marker = new THREE.Mesh(geometry, material);\n marker.position.set(x, y, z);\n marker.userData = { \n isSpacecraft: true, \n spacecraftId: sc.id,\n data: sc,\n };\n scene.add(marker);\n\n // Spacecraft glow ring\n const glowGeometry = new THREE.RingGeometry(isSelected ? 100 : 80, isSelected ? 130 : 100, 16);\n const glowMaterial = new THREE.MeshBasicMaterial({\n color: isSelected ? 0xffd700 : 0x00ff88,\n transparent: true,\n opacity: 0.4,\n side: THREE.DoubleSide,\n });\n const glow = new THREE.Mesh(glowGeometry, glowMaterial);\n glow.position.set(x, y, z);\n glow.lookAt(0, 0, 0);\n glow.userData = { isSpacecraft: true, isGlow: true };\n scene.add(glow);\n\n // Line from Earth center to spacecraft\n const lineGeometry = new THREE.BufferGeometry().setFromPoints([\n new THREE.Vector3(0, 0, 0),\n new THREE.Vector3(x, y, z),\n ]);\n const lineMaterial = new THREE.LineBasicMaterial({ \n color: isSelected ? 0xffd700 : 0x00ff88, \n transparent: true, \n opacity: 0.12 \n });\n const line = new THREE.Line(lineGeometry, lineMaterial);\n line.userData = { isSpacecraft: true, isLine: true };\n scene.add(line);\n });\n\n // ========================================\n // Add Ground Station Markers\n // ========================================\n groundStations.forEach((gs) => {\n const [x, y, z] = latLonAltToCartesian(gs.latitude, gs.longitude, 0);\n \n // Ground station pyramid (cone pointing up)\n const geometry = new THREE.ConeGeometry(45, 90, 4);\n const material = new THREE.MeshBasicMaterial({ color: 0x00ffff });\n const marker = new THREE.Mesh(geometry, material);\n marker.position.set(x, y, z);\n \n // Point cone away from Earth center\n marker.lookAt(0, 0, 0);\n marker.rotateX(Math.PI / 2);\n \n marker.userData = { \n isGroundStation: true, \n stationId: gs.id,\n data: gs,\n };\n scene.add(marker);\n\n // Ground station base ring\n const ringGeometry = new THREE.RingGeometry(55, 80, 16);\n const ringMaterial = new THREE.MeshBasicMaterial({ \n color: 0x00ffff, \n transparent: true, \n opacity: 0.5,\n side: THREE.DoubleSide,\n });\n const ring = new THREE.Mesh(ringGeometry, ringMaterial);\n ring.position.set(x, y, z);\n ring.lookAt(0, 0, 0);\n ring.userData = { isGroundStation: true, isRing: true };\n scene.add(ring);\n\n // Visibility cone (if enabled)\n if (showVisibilityCones) {\n const coneHeight = 1200;\n const coneRadius = 600;\n const coneGeometry = new THREE.ConeGeometry(coneRadius, coneHeight, 16, 1, true);\n const coneMaterial = new THREE.MeshBasicMaterial({\n color: 0x00ffff,\n transparent: true,\n opacity: 0.06,\n side: THREE.DoubleSide,\n });\n const cone = new THREE.Mesh(coneGeometry, coneMaterial);\n cone.position.set(x, y, z);\n cone.lookAt(0, 0, 0);\n cone.rotateX(-Math.PI / 2);\n cone.translateY(coneHeight / 2);\n cone.userData = { isVisibilityCone: true };\n scene.add(cone);\n }\n });\n\n // ========================================\n // Add Orbit Lines (Ground Track)\n // ========================================\n if (showOrbits && groundTrack.length > 1) {\n const points: any[] = [];\n \n groundTrack.forEach((pt, i) => {\n const alt = (pt as any).alt || \n (spacecraft.length > 0 ? spacecraft[0].altitude : 400);\n const [x, y, z] = latLonAltToCartesian(pt.latitude, pt.longitude, alt);\n \n // Handle date-line wrapping\n if (i > 0) {\n const prevPt = groundTrack[i - 1];\n const lonDiff = Math.abs(pt.longitude - prevPt.longitude);\n if (lonDiff > 180) {\n if (points.length > 1) {\n const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);\n const lineMaterial = new THREE.LineBasicMaterial({ \n color: 0x22d3ee, \n transparent: true, \n opacity: 0.7 \n });\n const line = new THREE.Line(lineGeometry, lineMaterial);\n line.userData = { isOrbit: true };\n scene.add(line);\n }\n points.length = 0;\n }\n }\n \n points.push(new THREE.Vector3(x, y, z));\n });\n\n if (points.length > 1) {\n const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);\n const lineMaterial = new THREE.LineBasicMaterial({ \n color: 0x22d3ee, \n transparent: true, \n opacity: 0.7,\n });\n const line = new THREE.Line(lineGeometry, lineMaterial);\n line.userData = { isOrbit: true };\n scene.add(line);\n }\n }\n\n }, [THREE, spacecraft, groundStations, groundTrack, selectedSpacecraftId, showOrbits, showVisibilityCones]);\n\n // Handle mouse move for hover detection\n const handleMouseMove = useCallback((event: MouseEvent) => {\n if (!containerRef.current || !raycasterRef.current || !cameraRef.current || !sceneRef.current) return;\n\n const rect = containerRef.current.getBoundingClientRect();\n const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n mouseRef.current.set(x, y);\n\n raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);\n \n const interactables = sceneRef.current.children.filter((obj: any) => \n (obj.userData?.isSpacecraft && !obj.userData?.isGlow && !obj.userData?.isLine) ||\n (obj.userData?.isGroundStation && !obj.userData?.isRing)\n );\n \n const intersects = raycasterRef.current.intersectObjects(interactables);\n\n if (intersects.length > 0) {\n const hit = intersects[0].object;\n const userData = hit.userData;\n \n if (userData.isSpacecraft) {\n const sc = userData.data as SpacecraftPosition;\n setHoveredObject({\n type: 'spacecraft',\n id: userData.spacecraftId,\n name: sc?.name || userData.spacecraftId,\n data: sc,\n screenX: event.clientX - rect.left,\n screenY: event.clientY - rect.top,\n });\n } else if (userData.isGroundStation) {\n const gs = userData.data as GroundStation;\n setHoveredObject({\n type: 'groundStation',\n id: userData.stationId,\n name: gs?.name || userData.stationId,\n data: gs,\n screenX: event.clientX - rect.left,\n screenY: event.clientY - rect.top,\n });\n }\n \n if (containerRef.current) {\n containerRef.current.style.cursor = 'pointer';\n }\n } else {\n setHoveredObject(null);\n if (containerRef.current) {\n containerRef.current.style.cursor = 'grab';\n }\n }\n }, []);\n\n // Handle click for selection\n const handleClick = useCallback((event: MouseEvent) => {\n if (!containerRef.current || !raycasterRef.current || !cameraRef.current || !sceneRef.current) return;\n\n const rect = containerRef.current.getBoundingClientRect();\n const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n mouseRef.current.set(x, y);\n\n raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);\n \n const interactables = sceneRef.current.children.filter((obj: any) => \n (obj.userData?.isSpacecraft && !obj.userData?.isGlow && !obj.userData?.isLine) ||\n (obj.userData?.isGroundStation && !obj.userData?.isRing)\n );\n \n const intersects = raycasterRef.current.intersectObjects(interactables);\n\n if (intersects.length > 0) {\n const hit = intersects[0].object;\n const userData = hit.userData;\n \n if (userData.isSpacecraft && onSpacecraftClick) {\n onSpacecraftClick(userData.data);\n } else if (userData.isGroundStation && onGroundStationClick) {\n onGroundStationClick(userData.data);\n }\n }\n }, [onSpacecraftClick, onGroundStationClick]);\n\n // Handle camera focus transitions (inspired by Solar System widget)\n useEffect(() => {\n if (!THREE || !cameraRef.current || !controlsRef.current || !focusTarget) return;\n \n // Check if focus target changed\n const targetKey = focusTarget.id || focusTarget.name || JSON.stringify(focusTarget);\n const lastKey = lastFocusTargetRef.current \n ? (lastFocusTargetRef.current.id || lastFocusTargetRef.current.name || JSON.stringify(lastFocusTargetRef.current))\n : null;\n \n if (targetKey === lastKey) return; // No change\n lastFocusTargetRef.current = focusTarget;\n \n const camera = cameraRef.current;\n const controls = controlsRef.current;\n \n // Calculate target position\n let targetLat = 0;\n let targetLon = 0;\n let targetZoom = 2.0;\n \n if (focusTarget.latitude !== undefined && focusTarget.longitude !== undefined) {\n targetLat = focusTarget.latitude;\n targetLon = focusTarget.longitude;\n targetZoom = focusTarget.zoom ?? 2.0;\n } else if (focusTarget.type === 'spacecraft' && focusTarget.id) {\n // Find spacecraft position\n const sc = spacecraft.find(s => s.id === focusTarget.id);\n if (sc) {\n targetLat = sc.latitude;\n targetLon = sc.longitude;\n targetZoom = 2.5;\n }\n } else if (focusTarget.type === 'groundStation' && focusTarget.id) {\n // Find ground station position\n const gs = groundStations.find(g => g.id === focusTarget.id);\n if (gs) {\n targetLat = gs.latitude;\n targetLon = gs.longitude;\n targetZoom = 2.0;\n }\n } else if (focusTarget.type === 'overview') {\n // Default overview position\n targetLat = 0;\n targetLon = 0;\n targetZoom = 1.0;\n }\n \n // Convert lat/lon to 3D position on Earth surface\n const [targetX, targetY, targetZ] = latLonAltToCartesian(targetLat, targetLon, 0);\n \n // Calculate camera position (offset from target, zoomed based on zoomLevel)\n const baseDistance = 10000;\n const zoomFactor = 1 / targetZoom;\n const cameraDistance = baseDistance * zoomFactor;\n \n // Position camera looking at target from a good angle\n const offset = new THREE.Vector3(targetX, targetY, targetZ).normalize().multiplyScalar(cameraDistance);\n const targetPos: [number, number, number] = [\n targetX + offset.x,\n targetY + offset.y,\n targetZ + offset.z,\n ];\n \n // Start transition\n const startPos: [number, number, number] = [\n camera.position.x,\n camera.position.y,\n camera.position.z,\n ];\n \n const startTarget: [number, number, number] = [\n controls.target.x,\n controls.target.y,\n controls.target.z,\n ];\n \n const targetTarget: [number, number, number] = [targetX, targetY, targetZ];\n \n focusTransitionRef.current = {\n startTime: Date.now(),\n startPos,\n targetPos,\n startTarget,\n targetTarget,\n duration: transitionDuration,\n isActive: true,\n };\n \n // Reset orbit angle when focusing\n orbitAngleRef.current = 0;\n }, [THREE, focusTarget, transitionDuration, spacecraft, groundStations]);\n\n // Animation loop\n const animate = useCallback(() => {\n // Guard: Only one animation loop should run at a time\n if (!isAnimatingRef.current) return;\n if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;\n\n const camera = cameraRef.current;\n const controls = controlsRef.current;\n \n // Handle camera focus transition\n if (focusTransitionRef.current && focusTransitionRef.current.isActive) {\n const transition = focusTransitionRef.current;\n const elapsed = Date.now() - transition.startTime;\n const progress = Math.min(elapsed / transition.duration, 1);\n \n // Easing function (ease-out cubic, similar to Solar System widget)\n const easeProgress = 1 - Math.pow(1 - progress, 3);\n \n // Interpolate camera position\n camera.position.x = transition.startPos[0] + (transition.targetPos[0] - transition.startPos[0]) * easeProgress;\n camera.position.y = transition.startPos[1] + (transition.targetPos[1] - transition.startPos[1]) * easeProgress;\n camera.position.z = transition.startPos[2] + (transition.targetPos[2] - transition.startPos[2]) * easeProgress;\n \n // Interpolate controls target\n controls.target.x = transition.startTarget[0] + (transition.targetTarget[0] - transition.startTarget[0]) * easeProgress;\n controls.target.y = transition.startTarget[1] + (transition.targetTarget[1] - transition.startTarget[1]) * easeProgress;\n controls.target.z = transition.startTarget[2] + (transition.targetTarget[2] - transition.startTarget[2]) * easeProgress;\n \n if (progress >= 1) {\n transition.isActive = false;\n }\n }\n \n // Handle auto-orbit mode (continuous camera movement around target)\n // Use refs to prevent animation loop restart when props change (fixes flickering)\n const currentFocusTarget = focusTargetRef.current;\n const currentAutoOrbit = autoOrbitRef.current;\n const currentAutoRotate = autoRotateRef.current;\n \n if (currentAutoOrbit && currentFocusTarget && !focusTransitionRef.current?.isActive && !isUserInteractingRef.current) {\n orbitAngleRef.current += 0.005; // Orbit speed\n \n if (currentFocusTarget.latitude !== undefined && currentFocusTarget.longitude !== undefined) {\n const [targetX, targetY, targetZ] = latLonAltToCartesian(\n currentFocusTarget.latitude,\n currentFocusTarget.longitude,\n 0\n );\n \n const baseDistance = 10000 / (currentFocusTarget.zoom ?? 2.0);\n const orbitRadius = baseDistance * 0.3; // Orbit around target\n \n // Circular orbit around target\n camera.position.x = targetX + Math.cos(orbitAngleRef.current) * orbitRadius;\n camera.position.y = targetY + Math.sin(orbitAngleRef.current * 0.5) * orbitRadius * 0.5;\n camera.position.z = targetZ + Math.sin(orbitAngleRef.current) * orbitRadius;\n \n controls.target.set(targetX, targetY, targetZ);\n }\n }\n\n // Update controls\n if (controls) {\n controls.update();\n }\n\n // Update star twinkling\n timeRef.current += 0.01;\n if (starsMaterialRef.current) {\n starsMaterialRef.current.uniforms.time.value = timeRef.current;\n }\n\n // Auto-rotate Earth (only if user isn't interacting and not in auto-orbit mode)\n if (currentAutoRotate && earthRef.current && !isUserInteractingRef.current && !currentAutoOrbit) {\n earthRef.current.rotation.y += 0.0006;\n }\n\n rendererRef.current.render(sceneRef.current, camera);\n animationRef.current = requestAnimationFrame(animate);\n }, []); // Empty deps - uses refs to access current values (prevents flickering)\n\n // Initialize when Three.js is loaded\n useEffect(() => {\n if (!threeLoaded || !webglCapabilities.available || !OrbitControlsClass) return;\n \n // Cancel any existing animation first (prevents stacking)\n isAnimatingRef.current = false;\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n animationRef.current = null;\n }\n \n const cleanup = initScene();\n \n // Start animation with guard\n isAnimatingRef.current = true;\n animate();\n \n return () => {\n // Stop animation cleanly\n isAnimatingRef.current = false;\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n animationRef.current = null;\n }\n cleanup?.();\n };\n }, [threeLoaded, webglCapabilities.available, OrbitControlsClass, initScene, animate]);\n\n // Update scene objects when data changes\n useEffect(() => {\n if (threeLoaded) {\n updateSceneObjects();\n }\n }, [threeLoaded, updateSceneObjects]);\n\n // Add mouse event listeners\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !threeLoaded) return;\n\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('click', handleClick);\n\n return () => {\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeEventListener('click', handleClick);\n };\n }, [threeLoaded, handleMouseMove, handleClick]);\n\n // Handle window resize\n useEffect(() => {\n const handleResize = () => {\n if (!containerRef.current || !rendererRef.current || !cameraRef.current) return;\n \n const rect = containerRef.current.getBoundingClientRect();\n cameraRef.current.aspect = rect.width / height;\n cameraRef.current.updateProjectionMatrix();\n rendererRef.current.setSize(rect.width, height);\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [height]);\n\n // Fallback to 2D map if WebGL not available\n if (!webglCapabilities.available || error) {\n return (\n <div className={`zendir-earth-viewer fallback ${className}`} style={{ position: 'relative' }}>\n <GroundTrackMap\n groundTrack={groundTrack}\n groundStations={groundStations}\n width={width}\n height={height}\n />\n <div style={{\n position: 'absolute',\n bottom: 8,\n right: 8,\n backgroundColor: 'rgba(0,0,0,0.7)',\n color: '#d1d5db' /* WCAG: improved contrast */,\n padding: '4px 8px',\n borderRadius: 4,\n fontSize: 10,\n }}>\n {error || '2D Mode (WebGL unavailable)'}\n </div>\n </div>\n );\n }\n\n return (\n <div\n className={`zendir-earth-viewer ${className}`}\n style={{\n width,\n height,\n position: 'relative',\n backgroundColor: '#030508',\n borderRadius: 8,\n overflow: 'hidden',\n cursor: 'grab',\n }}\n >\n <div\n ref={containerRef}\n style={{ width: '100%', height: '100%' }}\n />\n \n {/* Loading indicator */}\n {isLoading && (\n <div style={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n color: '#22d3ee',\n fontSize: 14,\n fontFamily: 'ui-monospace, monospace',\n }}>\n Loading 3D Earth...\n </div>\n )}\n \n {/* Stats overlay */}\n {!isLoading && (\n <div style={{\n position: 'absolute',\n top: 8,\n left: 8,\n backgroundColor: 'rgba(0,0,0,0.75)',\n color: '#e4e4e7',\n padding: '10px 14px',\n borderRadius: 8,\n fontSize: 11,\n fontFamily: 'ui-monospace, monospace',\n display: 'flex',\n flexDirection: 'column',\n gap: 5,\n backdropFilter: 'blur(4px)',\n }}>\n {spacecraft.length > 0 && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ color: '#00ff88', fontSize: 14 }}>●</span>\n <span>{spacecraft.length} spacecraft</span>\n </div>\n )}\n {groundStations.length > 0 && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ color: '#00ffff', fontSize: 12 }}>▲</span>\n <span>{groundStations.length} ground stations</span>\n </div>\n )}\n </div>\n )}\n\n {/* Controls hint */}\n {!isLoading && (\n <div style={{\n position: 'absolute',\n bottom: 8,\n right: 8,\n backgroundColor: 'rgba(0,0,0,0.6)',\n color: '#b8bcc8' /* WCAG: improved contrast */,\n padding: '5px 10px',\n borderRadius: 6,\n fontSize: 9,\n fontFamily: 'system-ui',\n }}>\n Drag to rotate • Scroll to zoom\n </div>\n )}\n\n {/* Hover Tooltip */}\n {hoveredObject && (\n <div\n style={{\n position: 'absolute',\n left: Math.min(hoveredObject.screenX + 15, (typeof width === 'number' ? width : 400) - 200),\n top: Math.max(hoveredObject.screenY - 10, 10),\n backgroundColor: 'rgba(9, 9, 11, 0.95)',\n color: '#e4e4e7',\n padding: '12px 16px',\n borderRadius: 10,\n fontSize: 11,\n fontFamily: 'ui-monospace, monospace',\n border: hoveredObject.type === 'spacecraft' ? '1px solid #22c55e' : '1px solid #22d3ee',\n boxShadow: '0 12px 40px rgba(0,0,0,0.6)',\n pointerEvents: 'none',\n zIndex: 100,\n minWidth: 180,\n backdropFilter: 'blur(8px)',\n }}\n >\n {hoveredObject.type === 'spacecraft' ? (\n <>\n <div style={{ fontWeight: 500, marginBottom: 8, color: '#22c55e', fontSize: '0.8125rem', display: 'flex', alignItems: 'center', gap: 6 }}>\n <AstroIcon name=\"satellite\" size=\"small\" label=\"\" /> {hoveredObject.name}\n </div>\n {hoveredObject.data && (\n <div style={{ display: 'grid', gap: 4 }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Latitude</span>\n <span>{(hoveredObject.data as SpacecraftPosition).latitude.toFixed(3)}°</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Longitude</span>\n <span>{(hoveredObject.data as SpacecraftPosition).longitude.toFixed(3)}°</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Altitude</span>\n <span style={{ color: '#22d3ee' }}>{(hoveredObject.data as SpacecraftPosition).altitude.toFixed(1)} km</span>\n </div>\n {(hoveredObject.data as SpacecraftPosition).velocity && (\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Velocity</span>\n <span>{((hoveredObject.data as SpacecraftPosition).velocity || 0).toFixed(3)} km/s</span>\n </div>\n )}\n </div>\n )}\n </>\n ) : (\n <>\n <div style={{ fontWeight: 500, marginBottom: 8, color: '#22d3ee', fontSize: '0.8125rem', display: 'flex', alignItems: 'center', gap: 6 }}>\n <AstroIcon name=\"antenna\" size=\"small\" label=\"\" /> {hoveredObject.name}\n </div>\n {hoveredObject.data && (\n <div style={{ display: 'grid', gap: 4 }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Latitude</span>\n <span>{(hoveredObject.data as GroundStation).latitude.toFixed(3)}°</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Longitude</span>\n <span>{(hoveredObject.data as GroundStation).longitude.toFixed(3)}°</span>\n </div>\n {(hoveredObject.data as GroundStation).minElevation !== undefined && (\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: '#b8bcc8' /* WCAG: improved contrast */ }}>Min Elevation</span>\n <span>{(hoveredObject.data as GroundStation).minElevation}°</span>\n </div>\n )}\n </div>\n )}\n </>\n )}\n <div style={{ \n marginTop: 8, \n paddingTop: 8, \n borderTop: '1px solid rgba(113,113,122,0.3)',\n color: '#9ca3af' /* WCAG: improved contrast */,\n fontSize: 9,\n }}>\n Click for details\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport default EarthViewer;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { PlanetId } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface PlanetPosition {
|
|
5
|
+
id: PlanetId;
|
|
6
|
+
name: string;
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
z: number;
|
|
10
|
+
radius: number;
|
|
11
|
+
color: string;
|
|
12
|
+
}
|
|
13
|
+
export interface SolarSystemViewerProps {
|
|
14
|
+
/** Planet positions (if not provided, uses default orbital positions) */
|
|
15
|
+
planets?: PlanetPosition[];
|
|
16
|
+
/** Currently focused planet */
|
|
17
|
+
focusedPlanet?: PlanetId | string;
|
|
18
|
+
/** Auto-orbit mode: continuous camera movement around focused planet */
|
|
19
|
+
autoOrbit?: boolean;
|
|
20
|
+
/** Camera transition duration in milliseconds */
|
|
21
|
+
transitionDuration?: number;
|
|
22
|
+
/** Canvas width */
|
|
23
|
+
width?: number | string;
|
|
24
|
+
/** Canvas height */
|
|
25
|
+
height?: number;
|
|
26
|
+
/** Show planet labels */
|
|
27
|
+
showLabels?: boolean;
|
|
28
|
+
/** Show orbits */
|
|
29
|
+
showOrbits?: boolean;
|
|
30
|
+
/** Show spacecraft (if any) */
|
|
31
|
+
spacecraft?: Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
z: number;
|
|
37
|
+
}>;
|
|
38
|
+
/** Custom className */
|
|
39
|
+
className?: string;
|
|
40
|
+
/** Callback when planet is clicked */
|
|
41
|
+
onPlanetClick?: (planetId: PlanetId) => void;
|
|
42
|
+
}
|
|
43
|
+
export declare function SolarSystemViewer({ planets, focusedPlanet, autoOrbit, transitionDuration, width, height, showLabels, showOrbits, spacecraft, className, onPlanetClick: _onPlanetClick, }: SolarSystemViewerProps): React.ReactElement;
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useEffect, useState, useCallback } from "react";
|
|
3
|
+
import { getPlanet, normalizePlanetName, PLANETS, auToKm } from "../types.js";
|
|
4
|
+
import { loadThree } from "./threeLoader.js";
|
|
5
|
+
import { useTheme } from "../theme/ThemeProvider.js";
|
|
6
|
+
function SolarSystemViewer({
|
|
7
|
+
planets,
|
|
8
|
+
focusedPlanet,
|
|
9
|
+
autoOrbit = false,
|
|
10
|
+
transitionDuration = 2e3,
|
|
11
|
+
width = "100%",
|
|
12
|
+
height = 600,
|
|
13
|
+
showLabels = true,
|
|
14
|
+
showOrbits = true,
|
|
15
|
+
spacecraft = [],
|
|
16
|
+
className = "",
|
|
17
|
+
onPlanetClick: _onPlanetClick
|
|
18
|
+
}) {
|
|
19
|
+
const { tokens: _tokens } = useTheme();
|
|
20
|
+
const containerRef = useRef(null);
|
|
21
|
+
const rendererRef = useRef(null);
|
|
22
|
+
const sceneRef = useRef(null);
|
|
23
|
+
const cameraRef = useRef(null);
|
|
24
|
+
const controlsRef = useRef(null);
|
|
25
|
+
const animationRef = useRef(null);
|
|
26
|
+
const raycasterRef = useRef(null);
|
|
27
|
+
const mouseRef = useRef(null);
|
|
28
|
+
const focusTransitionRef = useRef(null);
|
|
29
|
+
const orbitAngleRef = useRef(0);
|
|
30
|
+
const lastFocusedPlanetRef = useRef(null);
|
|
31
|
+
const isAnimatingRef = useRef(false);
|
|
32
|
+
const autoOrbitRef = useRef(autoOrbit);
|
|
33
|
+
const focusedPlanetRef = useRef(focusedPlanet);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
autoOrbitRef.current = autoOrbit;
|
|
36
|
+
}, [autoOrbit]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
focusedPlanetRef.current = focusedPlanet;
|
|
39
|
+
}, [focusedPlanet]);
|
|
40
|
+
const planetMeshesRef = useRef(/* @__PURE__ */ new Map());
|
|
41
|
+
const orbitLinesRef = useRef([]);
|
|
42
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
const [threeLoaded, setThreeLoaded] = useState(false);
|
|
45
|
+
const [THREE, setTHREE] = useState(null);
|
|
46
|
+
const [OrbitControlsClass, setOrbitControlsClass] = useState(null);
|
|
47
|
+
const [_hoveredPlanet, _setHoveredPlanet] = useState(null);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
loadThree().then(({ THREE: threeModule, OrbitControls: controls }) => {
|
|
50
|
+
setTHREE(threeModule);
|
|
51
|
+
setOrbitControlsClass(() => controls);
|
|
52
|
+
setThreeLoaded(true);
|
|
53
|
+
}).catch((err) => {
|
|
54
|
+
setError("Failed to load 3D library");
|
|
55
|
+
setIsLoading(false);
|
|
56
|
+
});
|
|
57
|
+
}, []);
|
|
58
|
+
const initScene = useCallback(() => {
|
|
59
|
+
if (!THREE || !containerRef.current || !OrbitControlsClass) return;
|
|
60
|
+
const container = containerRef.current;
|
|
61
|
+
const rect = container.getBoundingClientRect();
|
|
62
|
+
const w = rect.width;
|
|
63
|
+
const h = height;
|
|
64
|
+
const scene = new THREE.Scene();
|
|
65
|
+
scene.background = new THREE.Color(17);
|
|
66
|
+
sceneRef.current = scene;
|
|
67
|
+
const camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 1e9);
|
|
68
|
+
camera.position.set(0, 0, 1e7);
|
|
69
|
+
cameraRef.current = camera;
|
|
70
|
+
const renderer = new THREE.WebGLRenderer({
|
|
71
|
+
antialias: true,
|
|
72
|
+
alpha: true,
|
|
73
|
+
powerPreference: "high-performance",
|
|
74
|
+
preserveDrawingBuffer: true
|
|
75
|
+
// Prevents flickering from buffer swaps
|
|
76
|
+
});
|
|
77
|
+
renderer.setSize(w, h);
|
|
78
|
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
79
|
+
container.appendChild(renderer.domElement);
|
|
80
|
+
rendererRef.current = renderer;
|
|
81
|
+
const controls = new OrbitControlsClass(camera, renderer.domElement);
|
|
82
|
+
controls.enableDamping = true;
|
|
83
|
+
controls.dampingFactor = 0.05;
|
|
84
|
+
controls.minDistance = 1e3;
|
|
85
|
+
controls.maxDistance = 1e8;
|
|
86
|
+
controlsRef.current = controls;
|
|
87
|
+
raycasterRef.current = new THREE.Raycaster();
|
|
88
|
+
mouseRef.current = new THREE.Vector2();
|
|
89
|
+
const starsGeometry = new THREE.BufferGeometry();
|
|
90
|
+
const starsCount = 1e4;
|
|
91
|
+
const starsPositions = new Float32Array(starsCount * 3);
|
|
92
|
+
for (let i = 0; i < starsCount * 3; i++) {
|
|
93
|
+
starsPositions[i] = (Math.random() - 0.5) * 2e9;
|
|
94
|
+
}
|
|
95
|
+
starsGeometry.setAttribute("position", new THREE.BufferAttribute(starsPositions, 3));
|
|
96
|
+
const starsMaterial = new THREE.PointsMaterial({ color: 16777215, size: 0.5 });
|
|
97
|
+
const stars = new THREE.Points(starsGeometry, starsMaterial);
|
|
98
|
+
scene.add(stars);
|
|
99
|
+
setIsLoading(false);
|
|
100
|
+
}, [THREE, OrbitControlsClass, height]);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!THREE || !sceneRef.current) return;
|
|
103
|
+
const scene = sceneRef.current;
|
|
104
|
+
const planetMeshes = planetMeshesRef.current;
|
|
105
|
+
planetMeshes.forEach((mesh) => {
|
|
106
|
+
scene.remove(mesh);
|
|
107
|
+
if (mesh.geometry) mesh.geometry.dispose();
|
|
108
|
+
if (mesh.material) mesh.material.dispose();
|
|
109
|
+
});
|
|
110
|
+
planetMeshes.clear();
|
|
111
|
+
const planetData = planets || createDefaultPlanetPositions();
|
|
112
|
+
planetData.forEach((planetPos) => {
|
|
113
|
+
const planetInfo = getPlanet(planetPos.id);
|
|
114
|
+
if (!planetInfo) return;
|
|
115
|
+
const geometry = new THREE.SphereGeometry(planetPos.radius, 32, 16);
|
|
116
|
+
let material;
|
|
117
|
+
if (planetPos.id === "sun") {
|
|
118
|
+
material = new THREE.MeshBasicMaterial({
|
|
119
|
+
color: planetPos.color || planetInfo.color
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
material = new THREE.MeshStandardMaterial({
|
|
123
|
+
color: planetPos.color || planetInfo.color,
|
|
124
|
+
metalness: 0.1,
|
|
125
|
+
roughness: 0.8
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
129
|
+
mesh.position.set(planetPos.x, planetPos.y, planetPos.z);
|
|
130
|
+
mesh.userData = { planetId: planetPos.id, isPlanet: true };
|
|
131
|
+
scene.add(mesh);
|
|
132
|
+
planetMeshes.set(planetPos.id, mesh);
|
|
133
|
+
});
|
|
134
|
+
if (showOrbits) {
|
|
135
|
+
orbitLinesRef.current.forEach((line) => scene.remove(line));
|
|
136
|
+
orbitLinesRef.current = [];
|
|
137
|
+
planetData.forEach((planetPos) => {
|
|
138
|
+
const planetInfo = getPlanet(planetPos.id);
|
|
139
|
+
if (!planetInfo || (planetInfo.distanceFromSunAU ?? planetInfo.semiMajorAxis) === 0) return;
|
|
140
|
+
const orbitRadius = Math.sqrt(planetPos.x ** 2 + planetPos.y ** 2 + planetPos.z ** 2);
|
|
141
|
+
const segments = 128;
|
|
142
|
+
const points = [];
|
|
143
|
+
for (let i = 0; i <= segments; i++) {
|
|
144
|
+
const angle = i / segments * Math.PI * 2;
|
|
145
|
+
const x = Math.cos(angle) * orbitRadius;
|
|
146
|
+
const y = 0;
|
|
147
|
+
const z = Math.sin(angle) * orbitRadius;
|
|
148
|
+
points.push(new THREE.Vector3(x, y, z));
|
|
149
|
+
}
|
|
150
|
+
const orbitGeometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
151
|
+
const orbitMaterial = new THREE.LineBasicMaterial({
|
|
152
|
+
color: 4473924,
|
|
153
|
+
transparent: true,
|
|
154
|
+
opacity: 0.3
|
|
155
|
+
});
|
|
156
|
+
const orbitLine = new THREE.Line(orbitGeometry, orbitMaterial);
|
|
157
|
+
orbitLine.userData = { isOrbit: true };
|
|
158
|
+
scene.add(orbitLine);
|
|
159
|
+
orbitLinesRef.current.push(orbitLine);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
spacecraft.forEach((sc) => {
|
|
163
|
+
const geometry = new THREE.SphereGeometry(100, 16, 8);
|
|
164
|
+
const material = new THREE.MeshBasicMaterial({ color: 65280 });
|
|
165
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
166
|
+
mesh.position.set(sc.x, sc.y, sc.z);
|
|
167
|
+
mesh.userData = { spacecraftId: sc.id, isSpacecraft: true };
|
|
168
|
+
scene.add(mesh);
|
|
169
|
+
});
|
|
170
|
+
const ambientLight = new THREE.AmbientLight(16777215, 0.4);
|
|
171
|
+
scene.add(ambientLight);
|
|
172
|
+
const sunLight = new THREE.PointLight(16777215, 3, 1e12);
|
|
173
|
+
sunLight.position.set(0, 0, 0);
|
|
174
|
+
scene.add(sunLight);
|
|
175
|
+
const directionalLight1 = new THREE.DirectionalLight(16777215, 1.5);
|
|
176
|
+
directionalLight1.position.set(0, 0, 1);
|
|
177
|
+
scene.add(directionalLight1);
|
|
178
|
+
const directionalLight2 = new THREE.DirectionalLight(8956671, 0.5);
|
|
179
|
+
directionalLight2.position.set(0, 0, -1);
|
|
180
|
+
scene.add(directionalLight2);
|
|
181
|
+
const hemiLight = new THREE.HemisphereLight(16777215, 4473924, 0.3);
|
|
182
|
+
scene.add(hemiLight);
|
|
183
|
+
}, [THREE, planets, showLabels, showOrbits, spacecraft]);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
if (!THREE || !cameraRef.current || !controlsRef.current || !focusedPlanet) return;
|
|
186
|
+
const normalized = normalizePlanetName(focusedPlanet);
|
|
187
|
+
if (!normalized) return;
|
|
188
|
+
if (lastFocusedPlanetRef.current === normalized) return;
|
|
189
|
+
lastFocusedPlanetRef.current = normalized;
|
|
190
|
+
const planetInfo = getPlanet(normalized);
|
|
191
|
+
const planetMesh = planetMeshesRef.current.get(normalized);
|
|
192
|
+
if (!planetMesh || !planetInfo) return;
|
|
193
|
+
const camera = cameraRef.current;
|
|
194
|
+
const controls = controlsRef.current;
|
|
195
|
+
const planetPos = planetMesh.position;
|
|
196
|
+
const baseDistance = (planetInfo.radiusKm ?? planetInfo.radius) * 10;
|
|
197
|
+
const offset = new THREE.Vector3(0, 0, baseDistance);
|
|
198
|
+
const targetPos = [
|
|
199
|
+
planetPos.x + offset.x,
|
|
200
|
+
planetPos.y + offset.y,
|
|
201
|
+
planetPos.z + offset.z
|
|
202
|
+
];
|
|
203
|
+
const startPos = [
|
|
204
|
+
camera.position.x,
|
|
205
|
+
camera.position.y,
|
|
206
|
+
camera.position.z
|
|
207
|
+
];
|
|
208
|
+
const startTarget = [
|
|
209
|
+
controls.target.x,
|
|
210
|
+
controls.target.y,
|
|
211
|
+
controls.target.z
|
|
212
|
+
];
|
|
213
|
+
const targetTarget = [
|
|
214
|
+
planetPos.x,
|
|
215
|
+
planetPos.y,
|
|
216
|
+
planetPos.z
|
|
217
|
+
];
|
|
218
|
+
focusTransitionRef.current = {
|
|
219
|
+
startTime: Date.now(),
|
|
220
|
+
startPos,
|
|
221
|
+
targetPos,
|
|
222
|
+
startTarget,
|
|
223
|
+
targetTarget,
|
|
224
|
+
duration: transitionDuration,
|
|
225
|
+
isActive: true
|
|
226
|
+
};
|
|
227
|
+
orbitAngleRef.current = 0;
|
|
228
|
+
}, [THREE, focusedPlanet, transitionDuration]);
|
|
229
|
+
const animate = useCallback(() => {
|
|
230
|
+
var _a;
|
|
231
|
+
if (!isAnimatingRef.current) return;
|
|
232
|
+
if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;
|
|
233
|
+
const camera = cameraRef.current;
|
|
234
|
+
const controls = controlsRef.current;
|
|
235
|
+
if (focusTransitionRef.current && focusTransitionRef.current.isActive) {
|
|
236
|
+
const transition = focusTransitionRef.current;
|
|
237
|
+
const elapsed = Date.now() - transition.startTime;
|
|
238
|
+
const progress = Math.min(elapsed / transition.duration, 1);
|
|
239
|
+
const easeProgress = 1 - Math.pow(1 - progress, 3);
|
|
240
|
+
camera.position.x = transition.startPos[0] + (transition.targetPos[0] - transition.startPos[0]) * easeProgress;
|
|
241
|
+
camera.position.y = transition.startPos[1] + (transition.targetPos[1] - transition.startPos[1]) * easeProgress;
|
|
242
|
+
camera.position.z = transition.startPos[2] + (transition.targetPos[2] - transition.startPos[2]) * easeProgress;
|
|
243
|
+
controls.target.x = transition.startTarget[0] + (transition.targetTarget[0] - transition.startTarget[0]) * easeProgress;
|
|
244
|
+
controls.target.y = transition.startTarget[1] + (transition.targetTarget[1] - transition.startTarget[1]) * easeProgress;
|
|
245
|
+
controls.target.z = transition.startTarget[2] + (transition.targetTarget[2] - transition.startTarget[2]) * easeProgress;
|
|
246
|
+
if (progress >= 1) {
|
|
247
|
+
transition.isActive = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const currentAutoOrbit = autoOrbitRef.current;
|
|
251
|
+
const currentFocusedPlanet = focusedPlanetRef.current;
|
|
252
|
+
if (currentAutoOrbit && currentFocusedPlanet && !((_a = focusTransitionRef.current) == null ? void 0 : _a.isActive)) {
|
|
253
|
+
const normalized = normalizePlanetName(currentFocusedPlanet);
|
|
254
|
+
if (normalized) {
|
|
255
|
+
const planetMesh = planetMeshesRef.current.get(normalized);
|
|
256
|
+
if (planetMesh) {
|
|
257
|
+
orbitAngleRef.current += 0.01;
|
|
258
|
+
const planetInfo = getPlanet(normalized);
|
|
259
|
+
if (!planetInfo) return;
|
|
260
|
+
const orbitRadius = (planetInfo.radiusKm ?? planetInfo.radius) * 10;
|
|
261
|
+
const planetPos = planetMesh.position;
|
|
262
|
+
camera.position.x = planetPos.x + Math.cos(orbitAngleRef.current) * orbitRadius;
|
|
263
|
+
camera.position.y = planetPos.y + Math.sin(orbitAngleRef.current * 0.5) * orbitRadius * 0.3;
|
|
264
|
+
camera.position.z = planetPos.z + Math.sin(orbitAngleRef.current) * orbitRadius;
|
|
265
|
+
controls.target.set(planetPos.x, planetPos.y, planetPos.z);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (controls) {
|
|
270
|
+
controls.update();
|
|
271
|
+
}
|
|
272
|
+
rendererRef.current.render(sceneRef.current, camera);
|
|
273
|
+
animationRef.current = requestAnimationFrame(animate);
|
|
274
|
+
}, []);
|
|
275
|
+
useEffect(() => {
|
|
276
|
+
if (threeLoaded) {
|
|
277
|
+
initScene();
|
|
278
|
+
}
|
|
279
|
+
}, [threeLoaded, initScene]);
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
if (!isLoading && threeLoaded) {
|
|
282
|
+
isAnimatingRef.current = false;
|
|
283
|
+
if (animationRef.current) {
|
|
284
|
+
cancelAnimationFrame(animationRef.current);
|
|
285
|
+
animationRef.current = null;
|
|
286
|
+
}
|
|
287
|
+
isAnimatingRef.current = true;
|
|
288
|
+
animate();
|
|
289
|
+
}
|
|
290
|
+
return () => {
|
|
291
|
+
isAnimatingRef.current = false;
|
|
292
|
+
if (animationRef.current) {
|
|
293
|
+
cancelAnimationFrame(animationRef.current);
|
|
294
|
+
animationRef.current = null;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}, [isLoading, threeLoaded, animate]);
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
if (!rendererRef.current || !cameraRef.current || !containerRef.current) return;
|
|
300
|
+
const handleResize = () => {
|
|
301
|
+
const container = containerRef.current;
|
|
302
|
+
if (!container) return;
|
|
303
|
+
const rect = container.getBoundingClientRect();
|
|
304
|
+
cameraRef.current.aspect = rect.width / height;
|
|
305
|
+
cameraRef.current.updateProjectionMatrix();
|
|
306
|
+
rendererRef.current.setSize(rect.width, height);
|
|
307
|
+
};
|
|
308
|
+
window.addEventListener("resize", handleResize);
|
|
309
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
310
|
+
}, [height]);
|
|
311
|
+
const hasWebGL = (() => {
|
|
312
|
+
try {
|
|
313
|
+
const canvas = document.createElement("canvas");
|
|
314
|
+
return !!(canvas.getContext("webgl2") || canvas.getContext("webgl"));
|
|
315
|
+
} catch {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
})();
|
|
319
|
+
if (!hasWebGL) {
|
|
320
|
+
return /* @__PURE__ */ jsx("div", { className, style: { width, height, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#030508", borderRadius: 8, color: "#b8bcc8", fontFamily: "ui-monospace, monospace", fontSize: 12 }, children: "WebGL not available" });
|
|
321
|
+
}
|
|
322
|
+
if (error) {
|
|
323
|
+
return /* @__PURE__ */ jsxs("div", { className, style: { width, height, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#030508", borderRadius: 8, color: "#ff4444", fontFamily: "ui-monospace, monospace", fontSize: 12 }, children: [
|
|
324
|
+
"Error: ",
|
|
325
|
+
error
|
|
326
|
+
] });
|
|
327
|
+
}
|
|
328
|
+
if (isLoading) {
|
|
329
|
+
return /* @__PURE__ */ jsx("div", { className, style: { width, height, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#000011", borderRadius: 8, color: "#b8bcc8", fontFamily: "ui-monospace, monospace", fontSize: 12 }, children: "Loading Solar System..." });
|
|
330
|
+
}
|
|
331
|
+
return /* @__PURE__ */ jsx(
|
|
332
|
+
"div",
|
|
333
|
+
{
|
|
334
|
+
ref: containerRef,
|
|
335
|
+
className,
|
|
336
|
+
style: { width, height, position: "relative", backgroundColor: "#000011", borderRadius: 8, overflow: "hidden" }
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
function createDefaultPlanetPositions() {
|
|
341
|
+
const positions = [];
|
|
342
|
+
Object.values(PLANETS).forEach((planet) => {
|
|
343
|
+
if (planet.id === "sun") {
|
|
344
|
+
positions.push({
|
|
345
|
+
id: planet.id,
|
|
346
|
+
name: planet.name,
|
|
347
|
+
x: 0,
|
|
348
|
+
y: 0,
|
|
349
|
+
z: 0,
|
|
350
|
+
radius: planet.radiusKm ?? planet.radius,
|
|
351
|
+
color: planet.color
|
|
352
|
+
});
|
|
353
|
+
} else {
|
|
354
|
+
const distance = auToKm(planet.distanceFromSunAU ?? planet.semiMajorAxis);
|
|
355
|
+
const angle = Math.random() * Math.PI * 2;
|
|
356
|
+
positions.push({
|
|
357
|
+
id: planet.id,
|
|
358
|
+
name: planet.name,
|
|
359
|
+
x: Math.cos(angle) * distance,
|
|
360
|
+
y: 0,
|
|
361
|
+
z: Math.sin(angle) * distance,
|
|
362
|
+
radius: planet.radiusKm ?? planet.radius,
|
|
363
|
+
color: planet.color
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
return positions;
|
|
368
|
+
}
|
|
369
|
+
export {
|
|
370
|
+
SolarSystemViewer
|
|
371
|
+
};
|
|
372
|
+
//# sourceMappingURL=SolarSystemViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SolarSystemViewer.js","sources":["../../../src/react/3d/SolarSystemViewer.tsx"],"sourcesContent":["/**\n * @zendir/ui - SolarSystemViewer Component\n * \n * Interactive 3D Solar System visualization with:\n * - Theme-integrated via useTheme() for container styling\n * - All 8 planets plus Sun, Moon, and Pluto\n * - Smooth camera focus transitions (inspired by OpenAI Solar System widget)\n * - Auto-orbit mode for continuous camera movement\n * - Planet name normalization and aliases\n * - Planet descriptions and metadata\n * - Real-time positions from Zendir API (when available)\n * \n * Uses Three.js for 3D rendering with WebGL fallback.\n */\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { useTheme } from '../theme';\nimport type { PlanetId } from '../types';\nimport { PLANETS, normalizePlanetName, getPlanet, auToKm } from '../types';\n\nimport { loadThree, ThreeModule } from './threeLoader';\n\nexport interface PlanetPosition {\n id: PlanetId;\n name: string;\n x: number; // km (relative to Sun at origin)\n y: number;\n z: number;\n radius: number; // km\n color: string;\n}\n\nexport interface SolarSystemViewerProps {\n /** Planet positions (if not provided, uses default orbital positions) */\n planets?: PlanetPosition[];\n /** Currently focused planet */\n focusedPlanet?: PlanetId | string;\n /** Auto-orbit mode: continuous camera movement around focused planet */\n autoOrbit?: boolean;\n /** Camera transition duration in milliseconds */\n transitionDuration?: number;\n /** Canvas width */\n width?: number | string;\n /** Canvas height */\n height?: number;\n /** Show planet labels */\n showLabels?: boolean;\n /** Show orbits */\n showOrbits?: boolean;\n /** Show spacecraft (if any) */\n spacecraft?: Array<{\n id: string;\n name: string;\n x: number;\n y: number;\n z: number;\n }>;\n /** Custom className */\n className?: string;\n /** Callback when planet is clicked */\n onPlanetClick?: (planetId: PlanetId) => void;\n}\n\ninterface CameraTransition {\n startTime: number;\n startPos: [number, number, number];\n targetPos: [number, number, number];\n startTarget: [number, number, number];\n targetTarget: [number, number, number];\n duration: number;\n isActive: boolean;\n}\n\nexport function SolarSystemViewer({\n planets,\n focusedPlanet,\n autoOrbit = false,\n transitionDuration = 2000,\n width = '100%',\n height = 600,\n showLabels = true,\n showOrbits = true,\n spacecraft = [],\n className = '',\n onPlanetClick: _onPlanetClick,\n}: SolarSystemViewerProps): React.ReactElement {\n const { tokens: _tokens } = useTheme();\n const containerRef = useRef<HTMLDivElement>(null);\n const rendererRef = useRef<any>(null);\n const sceneRef = useRef<any>(null);\n const cameraRef = useRef<any>(null);\n const controlsRef = useRef<any>(null);\n const animationRef = useRef<number | null>(null);\n const raycasterRef = useRef<any>(null);\n const mouseRef = useRef<any>(null);\n \n // Camera transition state\n const focusTransitionRef = useRef<CameraTransition | null>(null);\n const orbitAngleRef = useRef(0);\n const lastFocusedPlanetRef = useRef<PlanetId | null>(null);\n const isAnimatingRef = useRef(false); // Guard against multiple animation loops\n \n // Refs for animation-dependent values to prevent animation loop restart (fixes flickering)\n const autoOrbitRef = useRef(autoOrbit);\n const focusedPlanetRef = useRef(focusedPlanet);\n \n // Keep refs in sync with props\n useEffect(() => { autoOrbitRef.current = autoOrbit; }, [autoOrbit]);\n useEffect(() => { focusedPlanetRef.current = focusedPlanet; }, [focusedPlanet]);\n \n // Planet mesh references\n const planetMeshesRef = useRef<Map<PlanetId, any>>(new Map());\n const orbitLinesRef = useRef<any[]>([]);\n \n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [threeLoaded, setThreeLoaded] = useState(false);\n const [THREE, setTHREE] = useState<ThreeModule | null>(null);\n const [OrbitControlsClass, setOrbitControlsClass] = useState<any>(null);\n const [_hoveredPlanet, _setHoveredPlanet] = useState<PlanetId | null>(null);\n\n // Load Three.js dynamically (shared loader prevents multiple instances)\n useEffect(() => {\n loadThree()\n .then(({ THREE: threeModule, OrbitControls: controls }) => {\n setTHREE(threeModule);\n setOrbitControlsClass(() => controls);\n setThreeLoaded(true);\n })\n .catch((err) => {\n if (import.meta.env.DEV) {\n console.error('Failed to load Three.js:', err);\n }\n setError('Failed to load 3D library');\n setIsLoading(false);\n });\n }, []);\n\n // Initialize Three.js scene\n const initScene = useCallback(() => {\n if (!THREE || !containerRef.current || !OrbitControlsClass) return;\n\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const w = rect.width;\n const h = height;\n\n // Scene\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(0x000011); // Deep space blue\n sceneRef.current = scene;\n\n // Camera (positioned to see entire solar system)\n const camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 1000000000); // Very far for outer planets\n camera.position.set(0, 0, 10000000); // 10M km from origin\n cameraRef.current = camera;\n\n // Renderer (preserveDrawingBuffer prevents flickering)\n const renderer = new THREE.WebGLRenderer({ \n antialias: true, \n alpha: true,\n powerPreference: 'high-performance',\n preserveDrawingBuffer: true, // Prevents flickering from buffer swaps\n });\n renderer.setSize(w, h);\n renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n container.appendChild(renderer.domElement);\n rendererRef.current = renderer;\n\n // OrbitControls\n const controls = new OrbitControlsClass(camera, renderer.domElement);\n controls.enableDamping = true;\n controls.dampingFactor = 0.05;\n controls.minDistance = 1000; // km\n controls.maxDistance = 100000000; // km\n controlsRef.current = controls;\n\n // Raycaster for hover/click\n raycasterRef.current = new THREE.Raycaster();\n mouseRef.current = new THREE.Vector2();\n\n // Add stars background\n const starsGeometry = new THREE.BufferGeometry();\n const starsCount = 10000;\n const starsPositions = new Float32Array(starsCount * 3);\n for (let i = 0; i < starsCount * 3; i++) {\n starsPositions[i] = (Math.random() - 0.5) * 2000000000; // 2 billion km spread\n }\n starsGeometry.setAttribute('position', new THREE.BufferAttribute(starsPositions, 3));\n const starsMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.5 });\n const stars = new THREE.Points(starsGeometry, starsMaterial);\n scene.add(stars);\n\n setIsLoading(false);\n }, [THREE, OrbitControlsClass, height]);\n\n // Create or update planets\n useEffect(() => {\n if (!THREE || !sceneRef.current) return;\n\n const scene = sceneRef.current;\n const planetMeshes = planetMeshesRef.current;\n\n // Clear existing planets\n planetMeshes.forEach((mesh) => {\n scene.remove(mesh);\n if (mesh.geometry) mesh.geometry.dispose();\n if (mesh.material) mesh.material.dispose();\n });\n planetMeshes.clear();\n\n // Use provided planets or create default positions\n const planetData = planets || createDefaultPlanetPositions();\n\n planetData.forEach((planetPos) => {\n const planetInfo = getPlanet(planetPos.id as PlanetId);\n if (!planetInfo) return;\n\n // Create sphere for planet\n const geometry = new THREE.SphereGeometry(planetPos.radius, 32, 16);\n \n let material;\n if (planetPos.id === 'sun') {\n // Sun is self-illuminating (MeshBasicMaterial = unlit, appears bright)\n material = new THREE.MeshBasicMaterial({\n color: planetPos.color || planetInfo.color,\n });\n } else {\n // Planets use standard material that works with lights\n material = new THREE.MeshStandardMaterial({\n color: planetPos.color || planetInfo.color,\n metalness: 0.1,\n roughness: 0.8,\n });\n }\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.position.set(planetPos.x, planetPos.y, planetPos.z);\n mesh.userData = { planetId: planetPos.id, isPlanet: true };\n\n scene.add(mesh);\n planetMeshes.set(planetPos.id as PlanetId, mesh);\n\n // Add label (if enabled)\n if (showLabels) {\n // Simple text sprite would go here (requires TextGeometry or sprite)\n // For now, we'll use the planet name in hover tooltip\n }\n });\n\n // Create orbit lines (if enabled)\n if (showOrbits) {\n // Clear existing orbit lines\n orbitLinesRef.current.forEach((line) => scene.remove(line));\n orbitLinesRef.current = [];\n\n planetData.forEach((planetPos) => {\n const planetInfo = getPlanet(planetPos.id as PlanetId);\n if (!planetInfo || (planetInfo.distanceFromSunAU ?? planetInfo.semiMajorAxis) === 0) return; // Skip Sun\n\n // Create elliptical orbit path\n const orbitRadius = Math.sqrt(planetPos.x ** 2 + planetPos.y ** 2 + planetPos.z ** 2);\n const segments = 128;\n const points: any[] = [];\n\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2;\n const x = Math.cos(angle) * orbitRadius;\n const y = 0; // Simplified to XY plane\n const z = Math.sin(angle) * orbitRadius;\n points.push(new THREE.Vector3(x, y, z));\n }\n\n const orbitGeometry = new THREE.BufferGeometry().setFromPoints(points);\n const orbitMaterial = new THREE.LineBasicMaterial({\n color: 0x444444,\n transparent: true,\n opacity: 0.3,\n });\n const orbitLine = new THREE.Line(orbitGeometry, orbitMaterial);\n orbitLine.userData = { isOrbit: true };\n scene.add(orbitLine);\n orbitLinesRef.current.push(orbitLine);\n });\n }\n\n // Add spacecraft (if any)\n spacecraft.forEach((sc) => {\n const geometry = new THREE.SphereGeometry(100, 16, 8); // 100 km radius\n const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });\n const mesh = new THREE.Mesh(geometry, material);\n mesh.position.set(sc.x, sc.y, sc.z);\n mesh.userData = { spacecraftId: sc.id, isSpacecraft: true };\n scene.add(mesh);\n });\n\n // Lighting setup - multiple lights to illuminate all planets\n // Ambient light for base illumination\n const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);\n scene.add(ambientLight);\n\n // Sun as main light source (PointLight at origin)\n const sunLight = new THREE.PointLight(0xffffff, 3, 1000000000000); // Very far reach\n sunLight.position.set(0, 0, 0);\n scene.add(sunLight);\n\n // Additional directional lights to ensure planets are visible\n // Main directional light (simulating sunlight)\n const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);\n directionalLight1.position.set(0, 0, 1);\n scene.add(directionalLight1);\n\n // Fill light from opposite side\n const directionalLight2 = new THREE.DirectionalLight(0x88aaff, 0.5);\n directionalLight2.position.set(0, 0, -1);\n scene.add(directionalLight2);\n\n // Hemisphere light for natural sky/ground illumination\n const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.3);\n scene.add(hemiLight);\n }, [THREE, planets, showLabels, showOrbits, spacecraft]);\n\n // Handle camera focus transitions\n useEffect(() => {\n if (!THREE || !cameraRef.current || !controlsRef.current || !focusedPlanet) return;\n\n const normalized = normalizePlanetName(focusedPlanet);\n if (!normalized) return;\n\n // Check if focus changed\n if (lastFocusedPlanetRef.current === normalized) return;\n lastFocusedPlanetRef.current = normalized;\n\n const planetInfo = getPlanet(normalized);\n const planetMesh = planetMeshesRef.current.get(normalized);\n\n if (!planetMesh || !planetInfo) return;\n\n const camera = cameraRef.current;\n const controls = controlsRef.current;\n\n // Calculate target position (offset from planet)\n const planetPos = planetMesh.position;\n const baseDistance = (planetInfo.radiusKm ?? planetInfo.radius) * 10; // 10x planet radius away\n const offset = new THREE.Vector3(0, 0, baseDistance);\n const targetPos: [number, number, number] = [\n planetPos.x + offset.x,\n planetPos.y + offset.y,\n planetPos.z + offset.z,\n ];\n\n // Start transition\n const startPos: [number, number, number] = [\n camera.position.x,\n camera.position.y,\n camera.position.z,\n ];\n\n const startTarget: [number, number, number] = [\n controls.target.x,\n controls.target.y,\n controls.target.z,\n ];\n\n const targetTarget: [number, number, number] = [\n planetPos.x,\n planetPos.y,\n planetPos.z,\n ];\n\n focusTransitionRef.current = {\n startTime: Date.now(),\n startPos,\n targetPos,\n startTarget,\n targetTarget,\n duration: transitionDuration,\n isActive: true,\n };\n\n orbitAngleRef.current = 0;\n }, [THREE, focusedPlanet, transitionDuration]);\n\n // Animation loop\n const animate = useCallback(() => {\n // Guard: Only one animation loop should run at a time\n if (!isAnimatingRef.current) return;\n if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;\n\n const camera = cameraRef.current;\n const controls = controlsRef.current;\n\n // Handle camera transition\n if (focusTransitionRef.current && focusTransitionRef.current.isActive) {\n const transition = focusTransitionRef.current;\n const elapsed = Date.now() - transition.startTime;\n const progress = Math.min(elapsed / transition.duration, 1);\n\n // Ease-out cubic (same as Solar System widget)\n const easeProgress = 1 - Math.pow(1 - progress, 3);\n\n camera.position.x = transition.startPos[0] + (transition.targetPos[0] - transition.startPos[0]) * easeProgress;\n camera.position.y = transition.startPos[1] + (transition.targetPos[1] - transition.startPos[1]) * easeProgress;\n camera.position.z = transition.startPos[2] + (transition.targetPos[2] - transition.startPos[2]) * easeProgress;\n\n controls.target.x = transition.startTarget[0] + (transition.targetTarget[0] - transition.startTarget[0]) * easeProgress;\n controls.target.y = transition.startTarget[1] + (transition.targetTarget[1] - transition.startTarget[1]) * easeProgress;\n controls.target.z = transition.startTarget[2] + (transition.targetTarget[2] - transition.startTarget[2]) * easeProgress;\n\n if (progress >= 1) {\n transition.isActive = false;\n }\n }\n\n // Handle auto-orbit\n // Use refs to prevent animation loop restart when props change (fixes flickering)\n const currentAutoOrbit = autoOrbitRef.current;\n const currentFocusedPlanet = focusedPlanetRef.current;\n \n if (currentAutoOrbit && currentFocusedPlanet && !focusTransitionRef.current?.isActive) {\n const normalized = normalizePlanetName(currentFocusedPlanet);\n if (normalized) {\n const planetMesh = planetMeshesRef.current.get(normalized);\n if (planetMesh) {\n orbitAngleRef.current += 0.01;\n const planetInfo = getPlanet(normalized);\n if (!planetInfo) return;\n const orbitRadius = (planetInfo.radiusKm ?? planetInfo.radius) * 10;\n\n const planetPos = planetMesh.position;\n camera.position.x = planetPos.x + Math.cos(orbitAngleRef.current) * orbitRadius;\n camera.position.y = planetPos.y + Math.sin(orbitAngleRef.current * 0.5) * orbitRadius * 0.3;\n camera.position.z = planetPos.z + Math.sin(orbitAngleRef.current) * orbitRadius;\n\n controls.target.set(planetPos.x, planetPos.y, planetPos.z);\n }\n }\n }\n\n // Update controls\n if (controls) {\n controls.update();\n }\n\n rendererRef.current.render(sceneRef.current, camera);\n animationRef.current = requestAnimationFrame(animate);\n }, []); // Empty deps - uses refs to access current values (prevents flickering)\n\n // Initialize scene when Three.js loads\n useEffect(() => {\n if (threeLoaded) {\n initScene();\n }\n }, [threeLoaded, initScene]);\n\n // Start animation loop\n useEffect(() => {\n if (!isLoading && threeLoaded) {\n // Cancel any existing animation first (prevents stacking)\n isAnimatingRef.current = false;\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n animationRef.current = null;\n }\n \n // Start animation with guard\n isAnimatingRef.current = true;\n animate();\n }\n return () => {\n // Stop animation cleanly\n isAnimatingRef.current = false;\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n animationRef.current = null;\n }\n };\n }, [isLoading, threeLoaded, animate]);\n\n // Handle resize\n useEffect(() => {\n if (!rendererRef.current || !cameraRef.current || !containerRef.current) return;\n\n const handleResize = () => {\n const container = containerRef.current;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n cameraRef.current.aspect = rect.width / height;\n cameraRef.current.updateProjectionMatrix();\n rendererRef.current.setSize(rect.width, height);\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [height]);\n\n // Check WebGL capability\n const hasWebGL = (() => {\n try {\n const canvas = document.createElement('canvas');\n return !!(canvas.getContext('webgl2') || canvas.getContext('webgl'));\n } catch { return false; }\n })();\n\n if (!hasWebGL) {\n return (\n <div className={className} style={{ width, height, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: '#030508', borderRadius: 8, color: '#b8bcc8' /* WCAG: improved contrast */, fontFamily: 'ui-monospace, monospace', fontSize: 12 }}>\n WebGL not available\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={className} style={{ width, height, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: '#030508', borderRadius: 8, color: '#ff4444', fontFamily: 'ui-monospace, monospace', fontSize: 12 }}>\n Error: {error}\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <div className={className} style={{ width, height, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: '#000011', borderRadius: 8, color: '#b8bcc8' /* WCAG: improved contrast */, fontFamily: 'ui-monospace, monospace', fontSize: 12 }}>\n Loading Solar System...\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width, height, position: 'relative', backgroundColor: '#000011', borderRadius: 8, overflow: 'hidden' }}\n />\n );\n}\n\n/**\n * Create default planet positions based on average orbital distances\n */\nfunction createDefaultPlanetPositions(): PlanetPosition[] {\n const _AU_TO_KM = 149597870.7;\n const positions: PlanetPosition[] = [];\n\n Object.values(PLANETS).forEach((planet) => {\n if (planet.id === 'sun') {\n positions.push({\n id: planet.id,\n name: planet.name,\n x: 0,\n y: 0,\n z: 0,\n radius: planet.radiusKm ?? planet.radius,\n color: planet.color,\n });\n } else {\n // Place planets in a simplified orbital plane\n const distance = auToKm(planet.distanceFromSunAU ?? planet.semiMajorAxis);\n const angle = Math.random() * Math.PI * 2; // Random starting angle\n positions.push({\n id: planet.id,\n name: planet.name,\n x: Math.cos(angle) * distance,\n y: 0,\n z: Math.sin(angle) * distance,\n radius: planet.radiusKm ?? planet.radius,\n color: planet.color,\n });\n }\n });\n\n return positions;\n}\n\n\n\n\n"],"names":[],"mappings":"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"}
|