@zendir/ui 0.2.20 → 0.3.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 +192 -1
- package/README.md +70 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.js +57 -41
- package/dist/index.js.map +1 -1
- package/dist/react/3d/CesiumCaptureSource.d.ts +119 -0
- package/dist/react/3d/CesiumCaptureSource.js +307 -0
- package/dist/react/3d/CesiumCaptureSource.js.map +1 -0
- package/dist/react/3d/ZenSpace3D.js +1253 -0
- package/dist/react/3d/ZenSpace3D.js.map +1 -0
- package/dist/react/3d/ZenSpace3DCesium.js +579 -0
- package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
- package/dist/react/3d/ZenSpace3DTypes.d.ts +28 -1
- package/dist/react/3d/ZenSpace3DUtils.d.ts +17 -173
- package/dist/react/3d/ZenSpace3DUtils.js +28 -0
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
- package/dist/react/3d/capturePngAnalysis.d.ts +16 -0
- package/dist/react/3d/index.d.ts +10 -12
- package/dist/react/3d/threeLoader.js +18 -0
- package/dist/react/3d/threeLoader.js.map +1 -0
- package/dist/react/astro/MonitoringIcon.js +1 -1
- package/dist/react/astro/MonitoringIcon.js.map +1 -1
- package/dist/react/astro/SimulationControls.js +2 -2
- package/dist/react/astro/SimulationControls.js.map +1 -1
- package/dist/react/astro/UnifiedTimeline.js +4 -4
- package/dist/react/astro/UnifiedTimeline.js.map +1 -1
- package/dist/react/charts/GroundTrackMap.d.ts +2 -15
- package/dist/react/charts/GroundTrackMap.js +1 -1
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/unified/AstroChart.js +34 -13
- package/dist/react/charts/unified/AstroChart.js.map +1 -1
- package/dist/react/chatgpt/AppCard.d.ts +0 -4
- package/dist/react/chatgpt/index.d.ts +0 -19
- package/dist/react/context/SpatialSelectionContext.d.ts +40 -0
- package/dist/react/context/SpatialSelectionContext.js +10 -0
- package/dist/react/context/SpatialSelectionContext.js.map +1 -0
- package/dist/react/context/index.d.ts +2 -0
- package/dist/react/core/{DataTable.d.ts → data/DataTable.d.ts} +1 -1
- package/dist/react/core/{DataTable.js → data/DataTable.js} +4 -4
- package/dist/react/core/data/DataTable.js.map +1 -0
- package/dist/react/core/{DataValue.d.ts → data/DataValue.d.ts} +2 -2
- package/dist/react/core/{DataValue.js → data/DataValue.js} +2 -2
- package/dist/react/core/data/DataValue.js.map +1 -0
- package/dist/react/core/{propertyConfig.d.ts → data/propertyConfig.d.ts} +2 -2
- package/dist/react/core/data/propertyConfig.js.map +1 -0
- package/dist/react/core/{AstroIcon.js → display/AstroIcon.js} +1 -1
- package/dist/react/core/display/AstroIcon.js.map +1 -0
- package/dist/react/core/{Badge.d.ts → display/Badge.d.ts} +1 -1
- package/dist/react/core/{Badge.js → display/Badge.js} +2 -2
- package/dist/react/core/display/Badge.js.map +1 -0
- package/dist/react/core/{CardHeader.d.ts → display/CardHeader.d.ts} +1 -1
- package/dist/react/core/{CardHeader.js → display/CardHeader.js} +2 -2
- package/dist/react/core/display/CardHeader.js.map +1 -0
- package/dist/react/core/{Container.d.ts → display/Container.d.ts} +1 -1
- package/dist/react/core/{Container.js → display/Container.js} +3 -3
- package/dist/react/core/display/Container.js.map +1 -0
- package/dist/react/core/{CopyButton.js → display/CopyButton.js} +1 -1
- package/dist/react/core/display/CopyButton.js.map +1 -0
- package/dist/react/core/{GlassCard.d.ts → display/GlassCard.d.ts} +1 -1
- package/dist/react/core/{GlassCard.js → display/GlassCard.js} +2 -2
- package/dist/react/core/display/GlassCard.js.map +1 -0
- package/dist/react/core/{HeaderIconWithStatus.d.ts → display/HeaderIconWithStatus.d.ts} +1 -1
- package/dist/react/core/{HeaderIconWithStatus.js → display/HeaderIconWithStatus.js} +1 -1
- package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -0
- package/dist/react/core/{Icon.d.ts → display/Icon.d.ts} +1 -1
- package/dist/react/core/{Icon.js → display/Icon.js} +1 -1
- package/dist/react/core/display/Icon.js.map +1 -0
- package/dist/react/core/{Typography.d.ts → display/Typography.d.ts} +13 -4
- package/dist/react/core/{Typography.js → display/Typography.js} +1 -1
- package/dist/react/core/display/Typography.js.map +1 -0
- package/dist/react/core/{ConfirmDialog.js → feedback/ConfirmDialog.js} +1 -1
- package/dist/react/core/feedback/ConfirmDialog.js.map +1 -0
- package/dist/react/core/{Dialog.js → feedback/Dialog.js} +2 -2
- package/dist/react/core/feedback/Dialog.js.map +1 -0
- package/dist/react/core/{Toast.js → feedback/Toast.js} +3 -3
- package/dist/react/core/feedback/Toast.js.map +1 -0
- package/dist/react/core/index.d.ts +85 -83
- package/dist/react/core/{Button.js → inputs/Button.js} +2 -2
- package/dist/react/core/inputs/Button.js.map +1 -0
- package/dist/react/core/{Checkbox.js → inputs/Checkbox.js} +2 -2
- package/dist/react/core/inputs/Checkbox.js.map +1 -0
- package/dist/react/core/{Input.d.ts → inputs/Input.d.ts} +1 -1
- package/dist/react/core/{Input.js → inputs/Input.js} +3 -3
- package/dist/react/core/inputs/Input.js.map +1 -0
- package/dist/react/core/{LimitsBar.js → inputs/LimitsBar.js} +1 -1
- package/dist/react/core/inputs/LimitsBar.js.map +1 -0
- package/dist/react/core/{NumberInput.d.ts → inputs/NumberInput.d.ts} +2 -2
- package/dist/react/core/{NumberInput.js → inputs/NumberInput.js} +3 -3
- package/dist/react/core/inputs/NumberInput.js.map +1 -0
- package/dist/react/core/{PinInput.js → inputs/PinInput.js} +2 -2
- package/dist/react/core/inputs/PinInput.js.map +1 -0
- package/dist/react/core/{Select.js → inputs/Select.js} +3 -3
- package/dist/react/core/inputs/Select.js.map +1 -0
- package/dist/react/core/{Toggle.js → inputs/Toggle.js} +2 -2
- package/dist/react/core/inputs/Toggle.js.map +1 -0
- package/dist/react/core/{AppBar.d.ts → navigation/AppBar.d.ts} +1 -1
- package/dist/react/core/{AppBar.js → navigation/AppBar.js} +7 -7
- package/dist/react/core/navigation/AppBar.js.map +1 -0
- package/dist/react/core/{Pagination.js → navigation/Pagination.js} +2 -2
- package/dist/react/core/navigation/Pagination.js.map +1 -0
- package/dist/react/core/{SideNav.d.ts → navigation/SideNav.d.ts} +1 -1
- package/dist/react/core/{SideNav.js → navigation/SideNav.js} +8 -9
- package/dist/react/core/navigation/SideNav.js.map +1 -0
- package/dist/react/core/{Tabs.js → navigation/Tabs.js} +2 -2
- package/dist/react/core/navigation/Tabs.js.map +1 -0
- package/dist/react/core/{Popover.js → overlays/Popover.js} +1 -1
- package/dist/react/core/overlays/Popover.js.map +1 -0
- package/dist/react/core/{SidePanel.js → overlays/SidePanel.js} +7 -7
- package/dist/react/core/overlays/SidePanel.js.map +1 -0
- package/dist/react/core/{Tooltip.js → overlays/Tooltip.js} +2 -2
- package/dist/react/core/overlays/Tooltip.js.map +1 -0
- package/dist/react/core/{ActivityPlanner.js → widgets/ActivityPlanner.js} +1 -1
- package/dist/react/core/widgets/ActivityPlanner.js.map +1 -0
- package/dist/react/core/widgets/Capture.d.ts +140 -0
- package/dist/react/core/widgets/Capture.js +804 -0
- package/dist/react/core/widgets/Capture.js.map +1 -0
- package/dist/react/core/{ChatPanel.d.ts → widgets/ChatPanel.d.ts} +1 -1
- package/dist/react/core/{ChatPanel.js → widgets/ChatPanel.js} +5 -4
- package/dist/react/core/widgets/ChatPanel.js.map +1 -0
- package/dist/react/core/{ColorPickerPanel.d.ts → widgets/ColorPickerPanel.d.ts} +1 -1
- package/dist/react/core/{ColorPickerPanel.js → widgets/ColorPickerPanel.js} +3 -3
- package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -0
- package/dist/react/core/{CommandBuilder.js → widgets/CommandBuilder.js} +1 -1
- package/dist/react/core/widgets/CommandBuilder.js.map +1 -0
- package/dist/react/core/{ConnectionForm.d.ts → widgets/ConnectionForm.d.ts} +1 -1
- package/dist/react/core/{ConnectionForm.js → widgets/ConnectionForm.js} +2 -2
- package/dist/react/core/widgets/ConnectionForm.js.map +1 -0
- package/dist/react/core/{FileExplorer.js → widgets/FileExplorer.js} +2 -2
- package/dist/react/core/widgets/FileExplorer.js.map +1 -0
- package/dist/react/core/{HexViewer.js → widgets/HexViewer.js} +1 -1
- package/dist/react/core/widgets/HexViewer.js.map +1 -0
- package/dist/react/core/{ImageGallery.d.ts → widgets/ImageGallery.d.ts} +1 -1
- package/dist/react/core/{ImageGallery.js → widgets/ImageGallery.js} +3 -3
- package/dist/react/core/widgets/ImageGallery.js.map +1 -0
- package/dist/react/core/{LogViewer.d.ts → widgets/LogViewer.d.ts} +13 -3
- package/dist/react/core/{LogViewer.js → widgets/LogViewer.js} +28 -8
- package/dist/react/core/widgets/LogViewer.js.map +1 -0
- package/dist/react/core/{MessageStream.d.ts → widgets/MessageStream.d.ts} +2 -2
- package/dist/react/core/{MessageStream.js → widgets/MessageStream.js} +4 -4
- package/dist/react/core/widgets/MessageStream.js.map +1 -0
- package/dist/react/core/{MissionCalendar.js → widgets/MissionCalendar.js} +2 -2
- package/dist/react/core/widgets/MissionCalendar.js.map +1 -0
- package/dist/react/core/{PacketViewer.js → widgets/PacketViewer.js} +1 -1
- package/dist/react/core/widgets/PacketViewer.js.map +1 -0
- package/dist/react/core/widgets/capture-placeholder.png.js +5 -0
- package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -0
- package/dist/react/hooks/index.d.ts +9 -11
- package/dist/react/hooks/useAccessWindows.d.ts +15 -19
- package/dist/react/hooks/useGroundTrackHistory.d.ts +34 -0
- package/dist/react/hooks/useSimulationScene.d.ts +141 -0
- package/dist/react/hooks/useSimulationScene.js +401 -0
- package/dist/react/hooks/useSimulationScene.js.map +1 -0
- package/dist/react/hooks/useZendirSession.d.ts +44 -69
- package/dist/react/index.d.ts +10 -5
- package/dist/react/panels/LayerControlPanel.d.ts +54 -0
- package/dist/react/panels/LayerControlPanel.js +184 -0
- package/dist/react/panels/LayerControlPanel.js.map +1 -0
- package/dist/react/panels/ObjectInventoryPanel.d.ts +57 -0
- package/dist/react/panels/ObjectInventoryPanel.js +261 -0
- package/dist/react/panels/ObjectInventoryPanel.js.map +1 -0
- package/dist/react/panels/index.d.ts +15 -0
- package/dist/react/theme/ThemeProvider.d.ts +2 -0
- package/dist/react/theme/ThemeProvider.js +50 -72
- package/dist/react/theme/ThemeProvider.js.map +1 -1
- package/dist/react/types.d.ts +32 -3
- package/dist/react/types.js.map +1 -1
- package/dist/react.js +57 -41
- package/dist/react.js.map +1 -1
- 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 +6 -4
- package/dist/tokens/css-vars.d.ts +91 -0
- package/dist/tokens/css-vars.js +228 -0
- package/dist/tokens/css-vars.js.map +1 -0
- package/dist/tokens/index.d.ts +71 -18
- package/dist/tokens/index.js +206 -97
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/tokens.css +50 -50
- package/package.json +27 -22
- package/sdk-stub.js +10 -5
- package/dist/react/3d/EarthViewer.d.ts +0 -46
- package/dist/react/3d/SolarSystemViewer.d.ts +0 -43
- package/dist/react/chatgpt/ChatGPTCard.d.ts +0 -6
- package/dist/react/core/ActivityPlanner.js.map +0 -1
- package/dist/react/core/AppBar.js.map +0 -1
- package/dist/react/core/AstroIcon.js.map +0 -1
- package/dist/react/core/Badge.js.map +0 -1
- package/dist/react/core/Button.js.map +0 -1
- package/dist/react/core/CardHeader.js.map +0 -1
- package/dist/react/core/ChatPanel.js.map +0 -1
- package/dist/react/core/Checkbox.js.map +0 -1
- package/dist/react/core/ColorPickerPanel.js.map +0 -1
- package/dist/react/core/CommandBuilder.js.map +0 -1
- package/dist/react/core/ConfirmDialog.js.map +0 -1
- package/dist/react/core/ConnectionForm.js.map +0 -1
- package/dist/react/core/Container.js.map +0 -1
- package/dist/react/core/CopyButton.js.map +0 -1
- package/dist/react/core/DataTable.js.map +0 -1
- package/dist/react/core/DataValue.js.map +0 -1
- package/dist/react/core/Dialog.js.map +0 -1
- package/dist/react/core/FileExplorer.js.map +0 -1
- package/dist/react/core/GlassCard.js.map +0 -1
- package/dist/react/core/HeaderIconWithStatus.js.map +0 -1
- package/dist/react/core/HexViewer.js.map +0 -1
- package/dist/react/core/Icon.js.map +0 -1
- package/dist/react/core/ImageGallery.js.map +0 -1
- package/dist/react/core/Input.js.map +0 -1
- package/dist/react/core/LimitsBar.js.map +0 -1
- package/dist/react/core/LogViewer.js.map +0 -1
- package/dist/react/core/MessageStream.js.map +0 -1
- package/dist/react/core/MissionCalendar.js.map +0 -1
- package/dist/react/core/NumberInput.js.map +0 -1
- package/dist/react/core/PacketViewer.js.map +0 -1
- package/dist/react/core/Pagination.js.map +0 -1
- package/dist/react/core/PinInput.js.map +0 -1
- package/dist/react/core/Popover.js.map +0 -1
- package/dist/react/core/Select.js.map +0 -1
- package/dist/react/core/SideNav.js.map +0 -1
- package/dist/react/core/SidePanel.js.map +0 -1
- package/dist/react/core/Tabs.js.map +0 -1
- package/dist/react/core/Toast.js.map +0 -1
- package/dist/react/core/Toggle.js.map +0 -1
- package/dist/react/core/Tooltip.js.map +0 -1
- package/dist/react/core/Typography.js.map +0 -1
- package/dist/react/core/propertyConfig.js.map +0 -1
- package/dist/react/hooks/useSimulationTime.d.ts +0 -61
- package/dist/react/hooks/useSpacecraftPosition.d.ts +0 -50
- package/dist/react/hooks/useTelemetry.d.ts +0 -55
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- /package/dist/react/core/{propertyConfig.js → data/propertyConfig.js} +0 -0
- /package/dist/react/core/{AstroIcon.d.ts → display/AstroIcon.d.ts} +0 -0
- /package/dist/react/core/{CopyButton.d.ts → display/CopyButton.d.ts} +0 -0
- /package/dist/react/core/{ConfirmDialog.d.ts → feedback/ConfirmDialog.d.ts} +0 -0
- /package/dist/react/core/{Dialog.d.ts → feedback/Dialog.d.ts} +0 -0
- /package/dist/react/core/{Toast.d.ts → feedback/Toast.d.ts} +0 -0
- /package/dist/react/core/{Button.d.ts → inputs/Button.d.ts} +0 -0
- /package/dist/react/core/{Checkbox.d.ts → inputs/Checkbox.d.ts} +0 -0
- /package/dist/react/core/{LimitsBar.d.ts → inputs/LimitsBar.d.ts} +0 -0
- /package/dist/react/core/{PinInput.d.ts → inputs/PinInput.d.ts} +0 -0
- /package/dist/react/core/{Select.d.ts → inputs/Select.d.ts} +0 -0
- /package/dist/react/core/{Toggle.d.ts → inputs/Toggle.d.ts} +0 -0
- /package/dist/react/core/{Pagination.d.ts → navigation/Pagination.d.ts} +0 -0
- /package/dist/react/core/{Tabs.d.ts → navigation/Tabs.d.ts} +0 -0
- /package/dist/react/core/{Popover.d.ts → overlays/Popover.d.ts} +0 -0
- /package/dist/react/core/{SidePanel.d.ts → overlays/SidePanel.d.ts} +0 -0
- /package/dist/react/core/{Tooltip.d.ts → overlays/Tooltip.d.ts} +0 -0
- /package/dist/react/core/{ActivityPlanner.d.ts → widgets/ActivityPlanner.d.ts} +0 -0
- /package/dist/react/core/{CommandBuilder.d.ts → widgets/CommandBuilder.d.ts} +0 -0
- /package/dist/react/core/{FileExplorer.d.ts → widgets/FileExplorer.d.ts} +0 -0
- /package/dist/react/core/{HexViewer.d.ts → widgets/HexViewer.d.ts} +0 -0
- /package/dist/react/core/{MissionCalendar.d.ts → widgets/MissionCalendar.d.ts} +0 -0
- /package/dist/react/core/{PacketViewer.d.ts → widgets/PacketViewer.d.ts} +0 -0
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @zendir/ui - React Hooks
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Integration hooks for Zendir simulations and UI helpers.
|
|
5
5
|
*/
|
|
6
|
-
export { useZendirSession } from './useZendirSession';
|
|
7
|
-
export type { UseZendirSessionOptions, UseZendirSessionResult } from './useZendirSession';
|
|
8
|
-
export {
|
|
9
|
-
export type {
|
|
10
|
-
export {
|
|
11
|
-
export type {
|
|
6
|
+
export { useZendirSession, isZendirSdkAvailable, preloadZendirSdk } from './useZendirSession';
|
|
7
|
+
export type { UseZendirSessionOptions, UseZendirSessionResult, SessionInfo } from './useZendirSession';
|
|
8
|
+
export { useSimulationScene } from './useSimulationScene';
|
|
9
|
+
export type { CelestialBody, PositionTickDiag, SimulationSceneClient, SimulationSceneObject, StructureDiag, UseSimulationSceneOptions, UseSimulationSceneResult, } from './useSimulationScene';
|
|
10
|
+
export { useGroundTrackHistory } from './useGroundTrackHistory';
|
|
11
|
+
export type { UseGroundTrackHistoryOptions } from './useGroundTrackHistory';
|
|
12
|
+
export { useAccessWindows, createDemoAccessPasses } from './useAccessWindows';
|
|
13
|
+
export type { AccessPass, AccessWindowsState, UseAccessWindowsOptions } from './useAccessWindows';
|
|
12
14
|
export { useCompactMode } from './useCompactMode';
|
|
13
15
|
export type { UseCompactModeOptions, UseCompactModeResult } from './useCompactMode';
|
|
14
|
-
export { useSimulationTime } from './useSimulationTime';
|
|
15
|
-
export type { SimulationTimeState, UseSimulationTimeOptions } from './useSimulationTime';
|
|
16
16
|
export { useSimulationPlayback } from './useSimulationPlayback';
|
|
17
17
|
export type { SimulationPlaybackState, UseSimulationPlaybackOptions, UseSimulationPlaybackResult, } from './useSimulationPlayback';
|
|
18
|
-
export { useAccessWindows, createDemoAccessWindows } from './useAccessWindows';
|
|
19
|
-
export type { AccessWindow, AccessWindowsState, UseAccessWindowsOptions } from './useAccessWindows';
|
|
20
18
|
export { useLiveSelection } from './useLiveSelection';
|
|
21
19
|
export type { UseLiveSelectionOptions, UseLiveSelectionResult } from './useLiveSelection';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TimerConfig } from '../astro/MissionClock';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface AccessPass {
|
|
4
4
|
/** Start time (AOS) */
|
|
5
5
|
startTime: Date;
|
|
6
6
|
/** End time (LOS) */
|
|
@@ -18,11 +18,11 @@ export interface AccessWindow {
|
|
|
18
18
|
}
|
|
19
19
|
export interface AccessWindowsState {
|
|
20
20
|
/** Currently active pass (if any) */
|
|
21
|
-
currentPass:
|
|
21
|
+
currentPass: AccessPass | null;
|
|
22
22
|
/** Next upcoming pass */
|
|
23
|
-
nextPass:
|
|
23
|
+
nextPass: AccessPass | null;
|
|
24
24
|
/** All upcoming passes */
|
|
25
|
-
upcomingPasses:
|
|
25
|
+
upcomingPasses: AccessPass[];
|
|
26
26
|
/** Whether currently in a pass */
|
|
27
27
|
isInPass: boolean;
|
|
28
28
|
/** Current elevation (degrees, if in pass) */
|
|
@@ -39,28 +39,24 @@ export interface AccessWindowsState {
|
|
|
39
39
|
losTimer: TimerConfig | null;
|
|
40
40
|
/** Timer config for TCA (for MissionClock) */
|
|
41
41
|
tcaTimer: TimerConfig | null;
|
|
42
|
-
/** Whether connected to SDK */
|
|
43
|
-
isConnected: boolean;
|
|
44
|
-
/** Whether SDK is available */
|
|
45
|
-
isSdkAvailable: boolean;
|
|
46
|
-
/** Error message if any */
|
|
47
|
-
error: string | null;
|
|
48
42
|
}
|
|
49
43
|
export interface UseAccessWindowsOptions {
|
|
50
|
-
/** Spacecraft ID to track */
|
|
51
|
-
spacecraftId?: string;
|
|
52
|
-
/** Ground station ID (or 'all' for all stations) */
|
|
53
|
-
groundStationId?: string;
|
|
54
44
|
/** Update interval in milliseconds (default: 1000) */
|
|
55
45
|
updateInterval?: number;
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Access windows to track. Provide pre-computed passes (from your own
|
|
48
|
+
* orbital propagator, SGP4, demo data, etc.). Pass `[]` or omit to
|
|
49
|
+
* yield an empty/idle state.
|
|
50
|
+
*/
|
|
51
|
+
windows?: AccessPass[];
|
|
52
|
+
/**
|
|
53
|
+
* @deprecated Use `windows`. Old alias kept for one minor for migration.
|
|
54
|
+
*/
|
|
55
|
+
manualWindows?: AccessPass[];
|
|
60
56
|
}
|
|
61
57
|
export declare function useAccessWindows(options?: UseAccessWindowsOptions): AccessWindowsState;
|
|
62
58
|
/**
|
|
63
59
|
* Generate demo access windows for testing
|
|
64
60
|
*/
|
|
65
|
-
export declare function
|
|
61
|
+
export declare function createDemoAccessPasses(count?: number): AccessPass[];
|
|
66
62
|
export default useAccessWindows;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Satellite } from '../3d/ZenSpace3DTypes';
|
|
2
|
+
import { SatelliteTrack } from '../charts/GroundTrackMap';
|
|
3
|
+
|
|
4
|
+
export interface UseGroundTrackHistoryOptions {
|
|
5
|
+
/**
|
|
6
|
+
* How long to retain ground-track history. Default 90 minutes (≈ one
|
|
7
|
+
* full LEO orbit). Older points fall off the front of the buffer.
|
|
8
|
+
*/
|
|
9
|
+
historyMinutes?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Hard cap on points per satellite. Default 600. Protects against
|
|
12
|
+
* runaway history when the upstream data source ticks faster than
|
|
13
|
+
* expected. The shorter of `historyMinutes` and `maxPoints` wins.
|
|
14
|
+
*/
|
|
15
|
+
maxPoints?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Optional default colour to assign to each track. The hook does not
|
|
18
|
+
* synthesise palette assignments — pass per-id via `colorBySatelliteId`
|
|
19
|
+
* if you want per-satellite colours.
|
|
20
|
+
*/
|
|
21
|
+
color?: string;
|
|
22
|
+
/** Map of satellite-id → colour string. */
|
|
23
|
+
colorBySatelliteId?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build per-satellite ground-track history from a live ECI position feed.
|
|
27
|
+
*
|
|
28
|
+
* @param satellites Live satellite array (ECI km positions). Pass
|
|
29
|
+
* `useSimulationScene().satellites` directly.
|
|
30
|
+
* @param options History length and palette options.
|
|
31
|
+
* @returns `SatelliteTrack[]` ready for `<GroundTrackMap />`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useGroundTrackHistory(satellites: Satellite[], options?: UseGroundTrackHistoryOptions): SatelliteTrack[];
|
|
34
|
+
export default useGroundTrackHistory;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { GroundStation } from '../types';
|
|
2
|
+
import { Satellite } from '../3d/ZenSpace3DTypes';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Minimal client interface this hook depends on. Compatible with the
|
|
6
|
+
* `zendir-ts` `ZendirClient` surface but typed structurally so the hook
|
|
7
|
+
* can be used with any compatible client (e.g. a test fake).
|
|
8
|
+
*/
|
|
9
|
+
export interface SimulationSceneClient {
|
|
10
|
+
getSimulationStructure(simulationId: string, containerId?: string, opts?: {
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
retry?: unknown;
|
|
13
|
+
}): Promise<Record<string, unknown>>;
|
|
14
|
+
getProperties(objectId: string, properties: string | string[], containerId?: string, opts?: {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
retry?: unknown;
|
|
17
|
+
}): Promise<unknown>;
|
|
18
|
+
getAllProperties(objectId: string, containerId?: string, opts?: {
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
retry?: unknown;
|
|
21
|
+
}): Promise<Record<string, unknown>>;
|
|
22
|
+
}
|
|
23
|
+
export interface UseSimulationSceneOptions {
|
|
24
|
+
/** ZendirClient (or any compatible client). Pass `null` to defer. */
|
|
25
|
+
client: SimulationSceneClient | null;
|
|
26
|
+
/** Container ID hosting the simulation. */
|
|
27
|
+
containerId: string;
|
|
28
|
+
/** Simulation ID to introspect. */
|
|
29
|
+
simulationId: string;
|
|
30
|
+
/** Polling interval in ms for spacecraft positions. Default: 2000. */
|
|
31
|
+
pollIntervalMs?: number;
|
|
32
|
+
/** Pause polling without unmounting. Default: true. */
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface SimulationSceneObject {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
kind: 'spacecraft' | 'groundStation' | 'celestialBody';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Lightweight diagnostic snapshot of the raw `getSimulationStructure`
|
|
42
|
+
* response — useful for debugging parsing mismatches in the UI without
|
|
43
|
+
* shipping the full raw blob.
|
|
44
|
+
*/
|
|
45
|
+
export interface StructureDiag {
|
|
46
|
+
/** Every top-level key the engine returned (e.g. "Objects","objects","Time"). */
|
|
47
|
+
topLevelKeys: string[];
|
|
48
|
+
/** Length of the `Objects` / `objects` array in the raw response (-1 if absent). */
|
|
49
|
+
rawObjectCount: number;
|
|
50
|
+
/** Length of the `Systems` / `systems` array in the raw response (-1 if absent). */
|
|
51
|
+
rawSystemCount: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Per-tick diagnostic — answers "did the position fetch succeed and what came back?".
|
|
55
|
+
* Surfaced in the debug UI so a failed-but-silent tick (parser miss, engine
|
|
56
|
+
* returning a different property name, all-zero positions, etc.) is visible.
|
|
57
|
+
*/
|
|
58
|
+
export interface PositionTickDiag {
|
|
59
|
+
/** Wall-clock instant of the most recent tick attempt. */
|
|
60
|
+
attemptedAt: Date;
|
|
61
|
+
/** Number of spacecraft IDs the tick requested positions for. */
|
|
62
|
+
requested: number;
|
|
63
|
+
/** Number of position responses that parsed successfully. */
|
|
64
|
+
parsedOk: number;
|
|
65
|
+
/** Number of position responses that arrived but couldn't be parsed. */
|
|
66
|
+
parseFailed: number;
|
|
67
|
+
/** Number of position responses rejected by the SDK (network / 4xx / 5xx). */
|
|
68
|
+
requestFailed: number;
|
|
69
|
+
/**
|
|
70
|
+
* The exact property names this tick asked the engine for. `null` means
|
|
71
|
+
* the schema probe failed and the tick fell back to `getAllProperties`.
|
|
72
|
+
* Surfaced so the debug UI can show which schema variant is in use.
|
|
73
|
+
*/
|
|
74
|
+
requestedProps: string[] | null;
|
|
75
|
+
/** All keys present on the first satellite's response (helps spot wrong property names). */
|
|
76
|
+
firstResponseKeys: string[];
|
|
77
|
+
/** Compact JSON of the first satellite's raw response (capped to 200 chars). */
|
|
78
|
+
firstResponseSample: string;
|
|
79
|
+
}
|
|
80
|
+
export interface UseSimulationSceneResult {
|
|
81
|
+
/** Spacecraft with live ECI position/velocity (km, km/s). */
|
|
82
|
+
satellites: Satellite[];
|
|
83
|
+
/** Static ground stations (lat/lon). */
|
|
84
|
+
groundStations: GroundStation[];
|
|
85
|
+
/**
|
|
86
|
+
* Celestial bodies (planets, moons, sun) with live ECI positions in km.
|
|
87
|
+
* The simulation's central body is filtered out — Cesium renders it
|
|
88
|
+
* natively as the focal globe. Pass directly to
|
|
89
|
+
* `<ZenSpace3D customObjects={scene.celestialBodies} />`.
|
|
90
|
+
*/
|
|
91
|
+
celestialBodies: CelestialBody[];
|
|
92
|
+
/** Objects discovered from `getSimulationStructure` (for diagnostics/UI lists). */
|
|
93
|
+
objects: SimulationSceneObject[];
|
|
94
|
+
/** True until the first structure fetch resolves. */
|
|
95
|
+
isLoading: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Set when the structure-fetch fails (polling stops). Per-tick
|
|
98
|
+
* polling errors do NOT surface here — they retry silently to keep
|
|
99
|
+
* the UI from flapping during transient network blips.
|
|
100
|
+
*/
|
|
101
|
+
error: string | null;
|
|
102
|
+
/** Re-run structure fetch and a position tick. */
|
|
103
|
+
refresh: () => Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* UTC `Date` representing wall-clock at the most recent successful
|
|
106
|
+
* tick. Pass straight into `<ZenSpace3D referenceTime={...} />` so
|
|
107
|
+
* ECI/J2000 positions rotate to ECEF at "now".
|
|
108
|
+
*/
|
|
109
|
+
referenceDate: Date;
|
|
110
|
+
/**
|
|
111
|
+
* Diagnostic snapshot of the raw structure response — null until the
|
|
112
|
+
* first fetch resolves. Use in debug panels to verify key casing and
|
|
113
|
+
* array lengths returned by the engine.
|
|
114
|
+
*/
|
|
115
|
+
structureDiag: StructureDiag | null;
|
|
116
|
+
/**
|
|
117
|
+
* Diagnostic for the most recent position-polling tick — null until
|
|
118
|
+
* the first tick has run. Use in debug panels to verify that
|
|
119
|
+
* `getProperties` is actually returning parsable position vectors.
|
|
120
|
+
*/
|
|
121
|
+
positionTickDiag: PositionTickDiag | null;
|
|
122
|
+
}
|
|
123
|
+
export declare function useSimulationScene(options: UseSimulationSceneOptions): UseSimulationSceneResult;
|
|
124
|
+
export default useSimulationScene;
|
|
125
|
+
/**
|
|
126
|
+
* Celestial body with the position polled from the engine's `Position`
|
|
127
|
+
* property (in km, ECI), suitable to feed straight into
|
|
128
|
+
* `<ZenSpace3D customObjects={...} />`. The simulation's central body
|
|
129
|
+
* (e.g. Earth in Earth-centric sims) is filtered out — Cesium renders
|
|
130
|
+
* the focal globe natively, so emitting it as a marker would collide.
|
|
131
|
+
*/
|
|
132
|
+
export interface CelestialBody {
|
|
133
|
+
id: string;
|
|
134
|
+
name: string;
|
|
135
|
+
/** ECI position in km. */
|
|
136
|
+
position: {
|
|
137
|
+
x: number;
|
|
138
|
+
y: number;
|
|
139
|
+
z: number;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
2
|
+
const PLACEHOLDER_SCENECRAFT_POSITION = { x: 6771, y: 0, z: 0 };
|
|
3
|
+
const TICK_RETRY = {
|
|
4
|
+
maxRetries: 2,
|
|
5
|
+
initialDelayMs: 500,
|
|
6
|
+
maxDelayMs: 1500
|
|
7
|
+
};
|
|
8
|
+
const DEFAULT_POLL_MS = 2e3;
|
|
9
|
+
const M_TO_KM = 1 / 1e3;
|
|
10
|
+
function useSimulationScene(options) {
|
|
11
|
+
const {
|
|
12
|
+
client,
|
|
13
|
+
containerId,
|
|
14
|
+
simulationId,
|
|
15
|
+
pollIntervalMs = DEFAULT_POLL_MS,
|
|
16
|
+
enabled = true
|
|
17
|
+
} = options;
|
|
18
|
+
const [satellites, setSatellites] = useState([]);
|
|
19
|
+
const [groundStations, setGroundStations] = useState([]);
|
|
20
|
+
const [celestialBodies, setCelestialBodies] = useState([]);
|
|
21
|
+
const [objects, setObjects] = useState([]);
|
|
22
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
23
|
+
const [error, setError] = useState(null);
|
|
24
|
+
const [referenceDate, setReferenceDate] = useState(() => /* @__PURE__ */ new Date());
|
|
25
|
+
const [structureDiag, setStructureDiag] = useState(null);
|
|
26
|
+
const [positionTickDiag, setPositionTickDiag] = useState(null);
|
|
27
|
+
const spacecraftIdsRef = useRef([]);
|
|
28
|
+
const celestialBodyIdsRef = useRef([]);
|
|
29
|
+
const celestialBodyNamesRef = useRef([]);
|
|
30
|
+
const intervalRef = useRef(null);
|
|
31
|
+
const abortRef = useRef(null);
|
|
32
|
+
const tickInFlightRef = useRef(false);
|
|
33
|
+
const positionPropsRef = useRef(null);
|
|
34
|
+
const tickPositions = useCallback(
|
|
35
|
+
async (signal) => {
|
|
36
|
+
if (!client) return;
|
|
37
|
+
const ids = spacecraftIdsRef.current;
|
|
38
|
+
if (ids.length === 0) return;
|
|
39
|
+
if (tickInFlightRef.current) return;
|
|
40
|
+
tickInFlightRef.current = true;
|
|
41
|
+
try {
|
|
42
|
+
const propsToFetch = positionPropsRef.current;
|
|
43
|
+
const useFallbackAll = !propsToFetch || propsToFetch.length === 0;
|
|
44
|
+
const updates = await Promise.allSettled(
|
|
45
|
+
ids.map(
|
|
46
|
+
(id) => useFallbackAll ? (
|
|
47
|
+
// `retry: TICK_RETRY` overrides the SDK's engineWarmup default
|
|
48
|
+
// for this one call. See the TICK_RETRY definition above for
|
|
49
|
+
// the rationale (avoid a stuck tick stalling the viewer).
|
|
50
|
+
client.getAllProperties(id, containerId, { signal, retry: TICK_RETRY })
|
|
51
|
+
) : client.getProperties(id, propsToFetch, containerId, {
|
|
52
|
+
signal,
|
|
53
|
+
retry: TICK_RETRY
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
if (signal.aborted) return;
|
|
58
|
+
let parsedOk = 0;
|
|
59
|
+
let parseFailed = 0;
|
|
60
|
+
let requestFailed = 0;
|
|
61
|
+
let firstResponseKeys = [];
|
|
62
|
+
let firstResponseSample = "";
|
|
63
|
+
for (let i = 0; i < ids.length; i++) {
|
|
64
|
+
const result = updates[i];
|
|
65
|
+
if (result.status !== "fulfilled") {
|
|
66
|
+
requestFailed += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (i === 0) {
|
|
70
|
+
const v = result.value;
|
|
71
|
+
if (v && typeof v === "object") firstResponseKeys = Object.keys(v);
|
|
72
|
+
try {
|
|
73
|
+
const json = JSON.stringify(v);
|
|
74
|
+
firstResponseSample = json.length > 200 ? `${json.slice(0, 200)}…` : json;
|
|
75
|
+
} catch {
|
|
76
|
+
firstResponseSample = String(v);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (parsePositionVelocity(result.value)) parsedOk += 1;
|
|
80
|
+
else parseFailed += 1;
|
|
81
|
+
}
|
|
82
|
+
setPositionTickDiag({
|
|
83
|
+
attemptedAt: /* @__PURE__ */ new Date(),
|
|
84
|
+
requested: ids.length,
|
|
85
|
+
parsedOk,
|
|
86
|
+
parseFailed,
|
|
87
|
+
requestFailed,
|
|
88
|
+
requestedProps: useFallbackAll ? null : propsToFetch,
|
|
89
|
+
firstResponseKeys,
|
|
90
|
+
firstResponseSample
|
|
91
|
+
});
|
|
92
|
+
setSatellites((prev) => {
|
|
93
|
+
if (prev.length === 0) return prev;
|
|
94
|
+
const next = prev.slice();
|
|
95
|
+
for (let i = 0; i < ids.length; i++) {
|
|
96
|
+
const result = updates[i];
|
|
97
|
+
if (result.status !== "fulfilled") continue;
|
|
98
|
+
const idx = next.findIndex((sc) => sc.id === ids[i]);
|
|
99
|
+
if (idx < 0) continue;
|
|
100
|
+
const parsed = parsePositionVelocity(result.value);
|
|
101
|
+
if (!parsed) continue;
|
|
102
|
+
next[idx] = { ...next[idx], position: parsed.position, velocity: parsed.velocity };
|
|
103
|
+
}
|
|
104
|
+
return next;
|
|
105
|
+
});
|
|
106
|
+
const celIds = celestialBodyIdsRef.current;
|
|
107
|
+
if (celIds.length > 0) {
|
|
108
|
+
const celResults = await Promise.allSettled(
|
|
109
|
+
celIds.map(
|
|
110
|
+
(id) => client.getProperties(id, ["Position", "IsCentralBody"], containerId, {
|
|
111
|
+
signal,
|
|
112
|
+
retry: TICK_RETRY
|
|
113
|
+
})
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
if (signal.aborted) return;
|
|
117
|
+
const next = [];
|
|
118
|
+
for (let i = 0; i < celIds.length; i++) {
|
|
119
|
+
const result = celResults[i];
|
|
120
|
+
if (result.status !== "fulfilled") continue;
|
|
121
|
+
const raw = result.value;
|
|
122
|
+
const obj = raw && typeof raw === "object" ? raw : null;
|
|
123
|
+
if (!obj) continue;
|
|
124
|
+
if (obj.IsCentralBody === true) continue;
|
|
125
|
+
const posVec = parseVec3(obj.Position);
|
|
126
|
+
if (!posVec) continue;
|
|
127
|
+
const positionKm = scaleVec(posVec, M_TO_KM);
|
|
128
|
+
const id = celIds[i];
|
|
129
|
+
const name = celestialBodyNamesRef.current[i] ?? `CelestialBody ${shortId(id)}`;
|
|
130
|
+
next.push({ id, name, position: positionKm });
|
|
131
|
+
}
|
|
132
|
+
setCelestialBodies(next);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
} finally {
|
|
136
|
+
tickInFlightRef.current = false;
|
|
137
|
+
if (!signal.aborted) {
|
|
138
|
+
setReferenceDate(/* @__PURE__ */ new Date());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
[client, containerId]
|
|
143
|
+
);
|
|
144
|
+
const loadStructure = useCallback(
|
|
145
|
+
async (signal) => {
|
|
146
|
+
if (!client) return;
|
|
147
|
+
setIsLoading(true);
|
|
148
|
+
setError(null);
|
|
149
|
+
try {
|
|
150
|
+
const structure = await client.getSimulationStructure(simulationId, containerId, { signal });
|
|
151
|
+
if (signal.aborted) return;
|
|
152
|
+
setStructureDiag({
|
|
153
|
+
topLevelKeys: Object.keys(structure),
|
|
154
|
+
rawObjectCount: Array.isArray(structure.Objects) ? structure.Objects.length : Array.isArray(structure.objects) ? structure.objects.length : -1,
|
|
155
|
+
rawSystemCount: Array.isArray(structure.Systems) ? structure.Systems.length : Array.isArray(structure.systems) ? structure.systems.length : -1
|
|
156
|
+
});
|
|
157
|
+
const buckets = bucketStructure(structure);
|
|
158
|
+
spacecraftIdsRef.current = buckets.spacecraft.map((s) => s.id);
|
|
159
|
+
celestialBodyIdsRef.current = buckets.celestialBodyIds;
|
|
160
|
+
celestialBodyNamesRef.current = buckets.celestialBodyNames;
|
|
161
|
+
setSatellites(buckets.spacecraft);
|
|
162
|
+
setObjects(buckets.objects);
|
|
163
|
+
if (buckets.spacecraft.length > 0) {
|
|
164
|
+
try {
|
|
165
|
+
const probeId = buckets.spacecraft[0].id;
|
|
166
|
+
const all = await client.getAllProperties(probeId, containerId, { signal });
|
|
167
|
+
if (signal.aborted) return;
|
|
168
|
+
positionPropsRef.current = detectPositionPropertyNames(all);
|
|
169
|
+
} catch {
|
|
170
|
+
positionPropsRef.current = null;
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
positionPropsRef.current = null;
|
|
174
|
+
}
|
|
175
|
+
const stationResults = await Promise.allSettled(
|
|
176
|
+
buckets.groundStationIds.map(
|
|
177
|
+
(id) => client.getAllProperties(id, containerId, { signal })
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
if (signal.aborted) return;
|
|
181
|
+
const stations = [];
|
|
182
|
+
for (let i = 0; i < buckets.groundStationIds.length; i++) {
|
|
183
|
+
const result = stationResults[i];
|
|
184
|
+
const id = buckets.groundStationIds[i];
|
|
185
|
+
const fallbackName = buckets.groundStationNames[i] ?? shortId(id);
|
|
186
|
+
if (result.status !== "fulfilled") {
|
|
187
|
+
stations.push({ id, name: fallbackName, latitude: 0, longitude: 0 });
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
stations.push(toGroundStation(id, fallbackName, result.value));
|
|
191
|
+
}
|
|
192
|
+
setGroundStations(stations);
|
|
193
|
+
setIsLoading(false);
|
|
194
|
+
if (spacecraftIdsRef.current.length > 0) {
|
|
195
|
+
await tickPositions(signal);
|
|
196
|
+
}
|
|
197
|
+
} catch (err) {
|
|
198
|
+
if (signal.aborted) return;
|
|
199
|
+
setError(err instanceof Error ? err.message : "Failed to load simulation structure");
|
|
200
|
+
setIsLoading(false);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
[client, containerId, simulationId, tickPositions]
|
|
204
|
+
);
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
if (!client || !containerId || !simulationId) {
|
|
207
|
+
setIsLoading(false);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const controller = new AbortController();
|
|
211
|
+
abortRef.current = controller;
|
|
212
|
+
void loadStructure(controller.signal);
|
|
213
|
+
return () => {
|
|
214
|
+
controller.abort();
|
|
215
|
+
if (abortRef.current === controller) abortRef.current = null;
|
|
216
|
+
};
|
|
217
|
+
}, [client, containerId, simulationId, loadStructure]);
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
if (!enabled || !client || error) {
|
|
220
|
+
if (intervalRef.current) {
|
|
221
|
+
clearInterval(intervalRef.current);
|
|
222
|
+
intervalRef.current = null;
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const interval = setInterval(() => {
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
void tickPositions(controller.signal).catch(() => void 0);
|
|
229
|
+
}, pollIntervalMs);
|
|
230
|
+
intervalRef.current = interval;
|
|
231
|
+
return () => {
|
|
232
|
+
clearInterval(interval);
|
|
233
|
+
if (intervalRef.current === interval) intervalRef.current = null;
|
|
234
|
+
};
|
|
235
|
+
}, [client, enabled, error, pollIntervalMs, tickPositions]);
|
|
236
|
+
const refresh = useCallback(async () => {
|
|
237
|
+
const controller = new AbortController();
|
|
238
|
+
await loadStructure(controller.signal);
|
|
239
|
+
}, [loadStructure]);
|
|
240
|
+
return {
|
|
241
|
+
satellites,
|
|
242
|
+
groundStations,
|
|
243
|
+
celestialBodies,
|
|
244
|
+
objects,
|
|
245
|
+
isLoading,
|
|
246
|
+
error,
|
|
247
|
+
refresh,
|
|
248
|
+
referenceDate,
|
|
249
|
+
structureDiag,
|
|
250
|
+
positionTickDiag
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function firstStringField(obj, keys) {
|
|
254
|
+
for (const key of keys) {
|
|
255
|
+
const v = obj[key];
|
|
256
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
257
|
+
}
|
|
258
|
+
return void 0;
|
|
259
|
+
}
|
|
260
|
+
function bucketStructure(structure) {
|
|
261
|
+
const out = {
|
|
262
|
+
spacecraft: [],
|
|
263
|
+
groundStationIds: [],
|
|
264
|
+
groundStationNames: [],
|
|
265
|
+
celestialBodyIds: [],
|
|
266
|
+
celestialBodyNames: [],
|
|
267
|
+
objects: []
|
|
268
|
+
};
|
|
269
|
+
const topObjects = pickArray(structure, ["Objects", "objects"]);
|
|
270
|
+
for (const obj of topObjects) walkObject(obj, out);
|
|
271
|
+
const topSystems = pickArray(structure, ["Systems", "systems"]);
|
|
272
|
+
for (const sys of topSystems) walkObject(sys, out);
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
function pickArray(obj, keys) {
|
|
276
|
+
for (const key of keys) {
|
|
277
|
+
const v = obj[key];
|
|
278
|
+
if (Array.isArray(v) && v.length > 0) return v;
|
|
279
|
+
}
|
|
280
|
+
for (const key of keys) {
|
|
281
|
+
const v = obj[key];
|
|
282
|
+
if (Array.isArray(v)) return v;
|
|
283
|
+
}
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
286
|
+
function walkObject(node, out) {
|
|
287
|
+
if (!node || typeof node !== "object") return;
|
|
288
|
+
const obj = node;
|
|
289
|
+
const id = firstStringField(obj, ["ID", "id", "Id"]);
|
|
290
|
+
const type = firstStringField(obj, ["Type", "type"]);
|
|
291
|
+
const name = firstStringField(obj, ["Name", "name"]);
|
|
292
|
+
if (id && type) {
|
|
293
|
+
if (type === "Spacecraft" || type.endsWith(".Spacecraft")) {
|
|
294
|
+
upsertObjectSummary(out, {
|
|
295
|
+
id,
|
|
296
|
+
name: name ?? `Spacecraft ${shortId(id)}`,
|
|
297
|
+
kind: "spacecraft"
|
|
298
|
+
});
|
|
299
|
+
out.spacecraft.push({
|
|
300
|
+
id,
|
|
301
|
+
name: name ?? `Spacecraft ${shortId(id)}`,
|
|
302
|
+
category: "satellite",
|
|
303
|
+
position: { ...PLACEHOLDER_SCENECRAFT_POSITION },
|
|
304
|
+
visualRadius: 50
|
|
305
|
+
});
|
|
306
|
+
} else if (type === "GroundStation" || type.endsWith(".GroundStation")) {
|
|
307
|
+
upsertObjectSummary(out, {
|
|
308
|
+
id,
|
|
309
|
+
name: name ?? `GroundStation ${shortId(id)}`,
|
|
310
|
+
kind: "groundStation"
|
|
311
|
+
});
|
|
312
|
+
out.groundStationIds.push(id);
|
|
313
|
+
out.groundStationNames.push(name);
|
|
314
|
+
} else if (type === "CelestialBody" || type.endsWith(".CelestialBody")) {
|
|
315
|
+
upsertObjectSummary(out, {
|
|
316
|
+
id,
|
|
317
|
+
name: name ?? `CelestialBody ${shortId(id)}`,
|
|
318
|
+
kind: "celestialBody"
|
|
319
|
+
});
|
|
320
|
+
out.celestialBodyIds.push(id);
|
|
321
|
+
out.celestialBodyNames.push(name);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const children = pickArray(obj, ["Children", "children"]);
|
|
325
|
+
for (const child of children) walkObject(child, out);
|
|
326
|
+
}
|
|
327
|
+
function upsertObjectSummary(out, next) {
|
|
328
|
+
const idx = out.objects.findIndex((o) => o.id === next.id);
|
|
329
|
+
if (idx >= 0) {
|
|
330
|
+
out.objects[idx] = next;
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
out.objects.push(next);
|
|
334
|
+
}
|
|
335
|
+
function detectPositionPropertyNames(props) {
|
|
336
|
+
const positionCandidates = ["Position_BN_N", "Position", "r_BN_N", "r_N"];
|
|
337
|
+
for (const name of positionCandidates) {
|
|
338
|
+
if (name in props && parseVec3(props[name]) !== null) {
|
|
339
|
+
return [name];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
function parsePositionVelocity(raw) {
|
|
345
|
+
if (!raw || typeof raw !== "object") return null;
|
|
346
|
+
const obj = raw;
|
|
347
|
+
const position = parseVec3(obj.Position_BN_N) ?? parseVec3(obj.Position) ?? parseVec3(obj.position);
|
|
348
|
+
if (!position) return null;
|
|
349
|
+
if (position.x === 0 && position.y === 0 && position.z === 0) return null;
|
|
350
|
+
const velocity = parseVec3(obj.Velocity_BN_N) ?? parseVec3(obj.Velocity) ?? parseVec3(obj.velocity);
|
|
351
|
+
return {
|
|
352
|
+
position: scaleVec(position, M_TO_KM),
|
|
353
|
+
velocity: velocity ? scaleVec(velocity, M_TO_KM) : void 0
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function parseVec3(raw) {
|
|
357
|
+
if (Array.isArray(raw) && raw.length >= 3) {
|
|
358
|
+
const [x, y, z] = raw;
|
|
359
|
+
if (typeof x === "number" && typeof y === "number" && typeof z === "number") {
|
|
360
|
+
return { x, y, z };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (raw && typeof raw === "object") {
|
|
364
|
+
const obj = raw;
|
|
365
|
+
const x = obj.X ?? obj.x;
|
|
366
|
+
const y = obj.Y ?? obj.y;
|
|
367
|
+
const z = obj.Z ?? obj.z;
|
|
368
|
+
if (typeof x === "number" && typeof y === "number" && typeof z === "number") {
|
|
369
|
+
return { x, y, z };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
function scaleVec(v, k) {
|
|
375
|
+
return { x: v.x * k, y: v.y * k, z: v.z * k };
|
|
376
|
+
}
|
|
377
|
+
function toGroundStation(id, fallbackName, props) {
|
|
378
|
+
const name = firstStringField(props, ["Name"]) ?? fallbackName;
|
|
379
|
+
const latitude = pickNumber(props, ["Latitude", "latitude"]) ?? 0;
|
|
380
|
+
const longitude = pickNumber(props, ["Longitude", "longitude"]) ?? 0;
|
|
381
|
+
const elevation = pickNumber(props, ["Altitude", "altitude", "Elevation"]);
|
|
382
|
+
const minElevation = pickNumber(props, ["MinElevation", "MinimumElevation"]);
|
|
383
|
+
const station = { id, name, latitude, longitude };
|
|
384
|
+
if (elevation !== void 0) station.elevation = elevation;
|
|
385
|
+
if (minElevation !== void 0) station.minElevation = minElevation;
|
|
386
|
+
return station;
|
|
387
|
+
}
|
|
388
|
+
function pickNumber(obj, keys) {
|
|
389
|
+
for (const key of keys) {
|
|
390
|
+
const v = obj[key];
|
|
391
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
392
|
+
}
|
|
393
|
+
return void 0;
|
|
394
|
+
}
|
|
395
|
+
function shortId(id) {
|
|
396
|
+
return id.length > 8 ? id.slice(0, 8) : id;
|
|
397
|
+
}
|
|
398
|
+
export {
|
|
399
|
+
useSimulationScene
|
|
400
|
+
};
|
|
401
|
+
//# sourceMappingURL=useSimulationScene.js.map
|