@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.
Files changed (260) hide show
  1. package/CHANGELOG.md +192 -1
  2. package/README.md +70 -28
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +57 -41
  5. package/dist/index.js.map +1 -1
  6. package/dist/react/3d/CesiumCaptureSource.d.ts +119 -0
  7. package/dist/react/3d/CesiumCaptureSource.js +307 -0
  8. package/dist/react/3d/CesiumCaptureSource.js.map +1 -0
  9. package/dist/react/3d/ZenSpace3D.js +1253 -0
  10. package/dist/react/3d/ZenSpace3D.js.map +1 -0
  11. package/dist/react/3d/ZenSpace3DCesium.js +579 -0
  12. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
  13. package/dist/react/3d/ZenSpace3DTypes.d.ts +28 -1
  14. package/dist/react/3d/ZenSpace3DUtils.d.ts +17 -173
  15. package/dist/react/3d/ZenSpace3DUtils.js +28 -0
  16. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
  17. package/dist/react/3d/capturePngAnalysis.d.ts +16 -0
  18. package/dist/react/3d/index.d.ts +10 -12
  19. package/dist/react/3d/threeLoader.js +18 -0
  20. package/dist/react/3d/threeLoader.js.map +1 -0
  21. package/dist/react/astro/MonitoringIcon.js +1 -1
  22. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  23. package/dist/react/astro/SimulationControls.js +2 -2
  24. package/dist/react/astro/SimulationControls.js.map +1 -1
  25. package/dist/react/astro/UnifiedTimeline.js +4 -4
  26. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  27. package/dist/react/charts/GroundTrackMap.d.ts +2 -15
  28. package/dist/react/charts/GroundTrackMap.js +1 -1
  29. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  30. package/dist/react/charts/unified/AstroChart.js +34 -13
  31. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  32. package/dist/react/chatgpt/AppCard.d.ts +0 -4
  33. package/dist/react/chatgpt/index.d.ts +0 -19
  34. package/dist/react/context/SpatialSelectionContext.d.ts +40 -0
  35. package/dist/react/context/SpatialSelectionContext.js +10 -0
  36. package/dist/react/context/SpatialSelectionContext.js.map +1 -0
  37. package/dist/react/context/index.d.ts +2 -0
  38. package/dist/react/core/{DataTable.d.ts → data/DataTable.d.ts} +1 -1
  39. package/dist/react/core/{DataTable.js → data/DataTable.js} +4 -4
  40. package/dist/react/core/data/DataTable.js.map +1 -0
  41. package/dist/react/core/{DataValue.d.ts → data/DataValue.d.ts} +2 -2
  42. package/dist/react/core/{DataValue.js → data/DataValue.js} +2 -2
  43. package/dist/react/core/data/DataValue.js.map +1 -0
  44. package/dist/react/core/{propertyConfig.d.ts → data/propertyConfig.d.ts} +2 -2
  45. package/dist/react/core/data/propertyConfig.js.map +1 -0
  46. package/dist/react/core/{AstroIcon.js → display/AstroIcon.js} +1 -1
  47. package/dist/react/core/display/AstroIcon.js.map +1 -0
  48. package/dist/react/core/{Badge.d.ts → display/Badge.d.ts} +1 -1
  49. package/dist/react/core/{Badge.js → display/Badge.js} +2 -2
  50. package/dist/react/core/display/Badge.js.map +1 -0
  51. package/dist/react/core/{CardHeader.d.ts → display/CardHeader.d.ts} +1 -1
  52. package/dist/react/core/{CardHeader.js → display/CardHeader.js} +2 -2
  53. package/dist/react/core/display/CardHeader.js.map +1 -0
  54. package/dist/react/core/{Container.d.ts → display/Container.d.ts} +1 -1
  55. package/dist/react/core/{Container.js → display/Container.js} +3 -3
  56. package/dist/react/core/display/Container.js.map +1 -0
  57. package/dist/react/core/{CopyButton.js → display/CopyButton.js} +1 -1
  58. package/dist/react/core/display/CopyButton.js.map +1 -0
  59. package/dist/react/core/{GlassCard.d.ts → display/GlassCard.d.ts} +1 -1
  60. package/dist/react/core/{GlassCard.js → display/GlassCard.js} +2 -2
  61. package/dist/react/core/display/GlassCard.js.map +1 -0
  62. package/dist/react/core/{HeaderIconWithStatus.d.ts → display/HeaderIconWithStatus.d.ts} +1 -1
  63. package/dist/react/core/{HeaderIconWithStatus.js → display/HeaderIconWithStatus.js} +1 -1
  64. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -0
  65. package/dist/react/core/{Icon.d.ts → display/Icon.d.ts} +1 -1
  66. package/dist/react/core/{Icon.js → display/Icon.js} +1 -1
  67. package/dist/react/core/display/Icon.js.map +1 -0
  68. package/dist/react/core/{Typography.d.ts → display/Typography.d.ts} +13 -4
  69. package/dist/react/core/{Typography.js → display/Typography.js} +1 -1
  70. package/dist/react/core/display/Typography.js.map +1 -0
  71. package/dist/react/core/{ConfirmDialog.js → feedback/ConfirmDialog.js} +1 -1
  72. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -0
  73. package/dist/react/core/{Dialog.js → feedback/Dialog.js} +2 -2
  74. package/dist/react/core/feedback/Dialog.js.map +1 -0
  75. package/dist/react/core/{Toast.js → feedback/Toast.js} +3 -3
  76. package/dist/react/core/feedback/Toast.js.map +1 -0
  77. package/dist/react/core/index.d.ts +85 -83
  78. package/dist/react/core/{Button.js → inputs/Button.js} +2 -2
  79. package/dist/react/core/inputs/Button.js.map +1 -0
  80. package/dist/react/core/{Checkbox.js → inputs/Checkbox.js} +2 -2
  81. package/dist/react/core/inputs/Checkbox.js.map +1 -0
  82. package/dist/react/core/{Input.d.ts → inputs/Input.d.ts} +1 -1
  83. package/dist/react/core/{Input.js → inputs/Input.js} +3 -3
  84. package/dist/react/core/inputs/Input.js.map +1 -0
  85. package/dist/react/core/{LimitsBar.js → inputs/LimitsBar.js} +1 -1
  86. package/dist/react/core/inputs/LimitsBar.js.map +1 -0
  87. package/dist/react/core/{NumberInput.d.ts → inputs/NumberInput.d.ts} +2 -2
  88. package/dist/react/core/{NumberInput.js → inputs/NumberInput.js} +3 -3
  89. package/dist/react/core/inputs/NumberInput.js.map +1 -0
  90. package/dist/react/core/{PinInput.js → inputs/PinInput.js} +2 -2
  91. package/dist/react/core/inputs/PinInput.js.map +1 -0
  92. package/dist/react/core/{Select.js → inputs/Select.js} +3 -3
  93. package/dist/react/core/inputs/Select.js.map +1 -0
  94. package/dist/react/core/{Toggle.js → inputs/Toggle.js} +2 -2
  95. package/dist/react/core/inputs/Toggle.js.map +1 -0
  96. package/dist/react/core/{AppBar.d.ts → navigation/AppBar.d.ts} +1 -1
  97. package/dist/react/core/{AppBar.js → navigation/AppBar.js} +7 -7
  98. package/dist/react/core/navigation/AppBar.js.map +1 -0
  99. package/dist/react/core/{Pagination.js → navigation/Pagination.js} +2 -2
  100. package/dist/react/core/navigation/Pagination.js.map +1 -0
  101. package/dist/react/core/{SideNav.d.ts → navigation/SideNav.d.ts} +1 -1
  102. package/dist/react/core/{SideNav.js → navigation/SideNav.js} +8 -9
  103. package/dist/react/core/navigation/SideNav.js.map +1 -0
  104. package/dist/react/core/{Tabs.js → navigation/Tabs.js} +2 -2
  105. package/dist/react/core/navigation/Tabs.js.map +1 -0
  106. package/dist/react/core/{Popover.js → overlays/Popover.js} +1 -1
  107. package/dist/react/core/overlays/Popover.js.map +1 -0
  108. package/dist/react/core/{SidePanel.js → overlays/SidePanel.js} +7 -7
  109. package/dist/react/core/overlays/SidePanel.js.map +1 -0
  110. package/dist/react/core/{Tooltip.js → overlays/Tooltip.js} +2 -2
  111. package/dist/react/core/overlays/Tooltip.js.map +1 -0
  112. package/dist/react/core/{ActivityPlanner.js → widgets/ActivityPlanner.js} +1 -1
  113. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -0
  114. package/dist/react/core/widgets/Capture.d.ts +140 -0
  115. package/dist/react/core/widgets/Capture.js +804 -0
  116. package/dist/react/core/widgets/Capture.js.map +1 -0
  117. package/dist/react/core/{ChatPanel.d.ts → widgets/ChatPanel.d.ts} +1 -1
  118. package/dist/react/core/{ChatPanel.js → widgets/ChatPanel.js} +5 -4
  119. package/dist/react/core/widgets/ChatPanel.js.map +1 -0
  120. package/dist/react/core/{ColorPickerPanel.d.ts → widgets/ColorPickerPanel.d.ts} +1 -1
  121. package/dist/react/core/{ColorPickerPanel.js → widgets/ColorPickerPanel.js} +3 -3
  122. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -0
  123. package/dist/react/core/{CommandBuilder.js → widgets/CommandBuilder.js} +1 -1
  124. package/dist/react/core/widgets/CommandBuilder.js.map +1 -0
  125. package/dist/react/core/{ConnectionForm.d.ts → widgets/ConnectionForm.d.ts} +1 -1
  126. package/dist/react/core/{ConnectionForm.js → widgets/ConnectionForm.js} +2 -2
  127. package/dist/react/core/widgets/ConnectionForm.js.map +1 -0
  128. package/dist/react/core/{FileExplorer.js → widgets/FileExplorer.js} +2 -2
  129. package/dist/react/core/widgets/FileExplorer.js.map +1 -0
  130. package/dist/react/core/{HexViewer.js → widgets/HexViewer.js} +1 -1
  131. package/dist/react/core/widgets/HexViewer.js.map +1 -0
  132. package/dist/react/core/{ImageGallery.d.ts → widgets/ImageGallery.d.ts} +1 -1
  133. package/dist/react/core/{ImageGallery.js → widgets/ImageGallery.js} +3 -3
  134. package/dist/react/core/widgets/ImageGallery.js.map +1 -0
  135. package/dist/react/core/{LogViewer.d.ts → widgets/LogViewer.d.ts} +13 -3
  136. package/dist/react/core/{LogViewer.js → widgets/LogViewer.js} +28 -8
  137. package/dist/react/core/widgets/LogViewer.js.map +1 -0
  138. package/dist/react/core/{MessageStream.d.ts → widgets/MessageStream.d.ts} +2 -2
  139. package/dist/react/core/{MessageStream.js → widgets/MessageStream.js} +4 -4
  140. package/dist/react/core/widgets/MessageStream.js.map +1 -0
  141. package/dist/react/core/{MissionCalendar.js → widgets/MissionCalendar.js} +2 -2
  142. package/dist/react/core/widgets/MissionCalendar.js.map +1 -0
  143. package/dist/react/core/{PacketViewer.js → widgets/PacketViewer.js} +1 -1
  144. package/dist/react/core/widgets/PacketViewer.js.map +1 -0
  145. package/dist/react/core/widgets/capture-placeholder.png.js +5 -0
  146. package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -0
  147. package/dist/react/hooks/index.d.ts +9 -11
  148. package/dist/react/hooks/useAccessWindows.d.ts +15 -19
  149. package/dist/react/hooks/useGroundTrackHistory.d.ts +34 -0
  150. package/dist/react/hooks/useSimulationScene.d.ts +141 -0
  151. package/dist/react/hooks/useSimulationScene.js +401 -0
  152. package/dist/react/hooks/useSimulationScene.js.map +1 -0
  153. package/dist/react/hooks/useZendirSession.d.ts +44 -69
  154. package/dist/react/index.d.ts +10 -5
  155. package/dist/react/panels/LayerControlPanel.d.ts +54 -0
  156. package/dist/react/panels/LayerControlPanel.js +184 -0
  157. package/dist/react/panels/LayerControlPanel.js.map +1 -0
  158. package/dist/react/panels/ObjectInventoryPanel.d.ts +57 -0
  159. package/dist/react/panels/ObjectInventoryPanel.js +261 -0
  160. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -0
  161. package/dist/react/panels/index.d.ts +15 -0
  162. package/dist/react/theme/ThemeProvider.d.ts +2 -0
  163. package/dist/react/theme/ThemeProvider.js +50 -72
  164. package/dist/react/theme/ThemeProvider.js.map +1 -1
  165. package/dist/react/types.d.ts +32 -3
  166. package/dist/react/types.js.map +1 -1
  167. package/dist/react.js +57 -41
  168. package/dist/react.js.map +1 -1
  169. package/dist/shaders/atmosphere.frag.js +5 -0
  170. package/dist/shaders/atmosphere.frag.js.map +1 -0
  171. package/dist/shaders/atmosphere.vert.js +5 -0
  172. package/dist/shaders/atmosphere.vert.js.map +1 -0
  173. package/dist/shaders/stars.frag.js +5 -0
  174. package/dist/shaders/stars.frag.js.map +1 -0
  175. package/dist/shaders/stars.vert.js +5 -0
  176. package/dist/shaders/stars.vert.js.map +1 -0
  177. package/dist/style.css +6 -4
  178. package/dist/tokens/css-vars.d.ts +91 -0
  179. package/dist/tokens/css-vars.js +228 -0
  180. package/dist/tokens/css-vars.js.map +1 -0
  181. package/dist/tokens/index.d.ts +71 -18
  182. package/dist/tokens/index.js +206 -97
  183. package/dist/tokens/index.js.map +1 -1
  184. package/dist/tokens/tokens.css +50 -50
  185. package/package.json +27 -22
  186. package/sdk-stub.js +10 -5
  187. package/dist/react/3d/EarthViewer.d.ts +0 -46
  188. package/dist/react/3d/SolarSystemViewer.d.ts +0 -43
  189. package/dist/react/chatgpt/ChatGPTCard.d.ts +0 -6
  190. package/dist/react/core/ActivityPlanner.js.map +0 -1
  191. package/dist/react/core/AppBar.js.map +0 -1
  192. package/dist/react/core/AstroIcon.js.map +0 -1
  193. package/dist/react/core/Badge.js.map +0 -1
  194. package/dist/react/core/Button.js.map +0 -1
  195. package/dist/react/core/CardHeader.js.map +0 -1
  196. package/dist/react/core/ChatPanel.js.map +0 -1
  197. package/dist/react/core/Checkbox.js.map +0 -1
  198. package/dist/react/core/ColorPickerPanel.js.map +0 -1
  199. package/dist/react/core/CommandBuilder.js.map +0 -1
  200. package/dist/react/core/ConfirmDialog.js.map +0 -1
  201. package/dist/react/core/ConnectionForm.js.map +0 -1
  202. package/dist/react/core/Container.js.map +0 -1
  203. package/dist/react/core/CopyButton.js.map +0 -1
  204. package/dist/react/core/DataTable.js.map +0 -1
  205. package/dist/react/core/DataValue.js.map +0 -1
  206. package/dist/react/core/Dialog.js.map +0 -1
  207. package/dist/react/core/FileExplorer.js.map +0 -1
  208. package/dist/react/core/GlassCard.js.map +0 -1
  209. package/dist/react/core/HeaderIconWithStatus.js.map +0 -1
  210. package/dist/react/core/HexViewer.js.map +0 -1
  211. package/dist/react/core/Icon.js.map +0 -1
  212. package/dist/react/core/ImageGallery.js.map +0 -1
  213. package/dist/react/core/Input.js.map +0 -1
  214. package/dist/react/core/LimitsBar.js.map +0 -1
  215. package/dist/react/core/LogViewer.js.map +0 -1
  216. package/dist/react/core/MessageStream.js.map +0 -1
  217. package/dist/react/core/MissionCalendar.js.map +0 -1
  218. package/dist/react/core/NumberInput.js.map +0 -1
  219. package/dist/react/core/PacketViewer.js.map +0 -1
  220. package/dist/react/core/Pagination.js.map +0 -1
  221. package/dist/react/core/PinInput.js.map +0 -1
  222. package/dist/react/core/Popover.js.map +0 -1
  223. package/dist/react/core/Select.js.map +0 -1
  224. package/dist/react/core/SideNav.js.map +0 -1
  225. package/dist/react/core/SidePanel.js.map +0 -1
  226. package/dist/react/core/Tabs.js.map +0 -1
  227. package/dist/react/core/Toast.js.map +0 -1
  228. package/dist/react/core/Toggle.js.map +0 -1
  229. package/dist/react/core/Tooltip.js.map +0 -1
  230. package/dist/react/core/Typography.js.map +0 -1
  231. package/dist/react/core/propertyConfig.js.map +0 -1
  232. package/dist/react/hooks/useSimulationTime.d.ts +0 -61
  233. package/dist/react/hooks/useSpacecraftPosition.d.ts +0 -50
  234. package/dist/react/hooks/useTelemetry.d.ts +0 -55
  235. package/dist/types.d.ts +0 -1
  236. package/dist/types.js +0 -2
  237. package/dist/types.js.map +0 -1
  238. /package/dist/react/core/{propertyConfig.js → data/propertyConfig.js} +0 -0
  239. /package/dist/react/core/{AstroIcon.d.ts → display/AstroIcon.d.ts} +0 -0
  240. /package/dist/react/core/{CopyButton.d.ts → display/CopyButton.d.ts} +0 -0
  241. /package/dist/react/core/{ConfirmDialog.d.ts → feedback/ConfirmDialog.d.ts} +0 -0
  242. /package/dist/react/core/{Dialog.d.ts → feedback/Dialog.d.ts} +0 -0
  243. /package/dist/react/core/{Toast.d.ts → feedback/Toast.d.ts} +0 -0
  244. /package/dist/react/core/{Button.d.ts → inputs/Button.d.ts} +0 -0
  245. /package/dist/react/core/{Checkbox.d.ts → inputs/Checkbox.d.ts} +0 -0
  246. /package/dist/react/core/{LimitsBar.d.ts → inputs/LimitsBar.d.ts} +0 -0
  247. /package/dist/react/core/{PinInput.d.ts → inputs/PinInput.d.ts} +0 -0
  248. /package/dist/react/core/{Select.d.ts → inputs/Select.d.ts} +0 -0
  249. /package/dist/react/core/{Toggle.d.ts → inputs/Toggle.d.ts} +0 -0
  250. /package/dist/react/core/{Pagination.d.ts → navigation/Pagination.d.ts} +0 -0
  251. /package/dist/react/core/{Tabs.d.ts → navigation/Tabs.d.ts} +0 -0
  252. /package/dist/react/core/{Popover.d.ts → overlays/Popover.d.ts} +0 -0
  253. /package/dist/react/core/{SidePanel.d.ts → overlays/SidePanel.d.ts} +0 -0
  254. /package/dist/react/core/{Tooltip.d.ts → overlays/Tooltip.d.ts} +0 -0
  255. /package/dist/react/core/{ActivityPlanner.d.ts → widgets/ActivityPlanner.d.ts} +0 -0
  256. /package/dist/react/core/{CommandBuilder.d.ts → widgets/CommandBuilder.d.ts} +0 -0
  257. /package/dist/react/core/{FileExplorer.d.ts → widgets/FileExplorer.d.ts} +0 -0
  258. /package/dist/react/core/{HexViewer.d.ts → widgets/HexViewer.d.ts} +0 -0
  259. /package/dist/react/core/{MissionCalendar.d.ts → widgets/MissionCalendar.d.ts} +0 -0
  260. /package/dist/react/core/{PacketViewer.d.ts → widgets/PacketViewer.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZenSpace3DCesium.js","sources":["../../../src/react/3d/ZenSpace3DCesium.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - ZenSpace3D Cesium Backend\r\n *\r\n * Cesium-based 3D Earth/orbit view. Entities are reconciled **in-place** on\r\n * each data update (position changes only) to avoid the WebGL context churn\r\n * that occurs when entities are removed and re-added every polling tick.\r\n *\r\n * Layer visibility is controlled via the `layers` prop\r\n * (all default to \"sensible\" if the key is absent):\r\n *\r\n * layers.labels — spacecraft / GS name labels (default: false)\r\n * layers.coverage — coverage footprint ellipses + cones (default: false)\r\n * layers.orbits — orbit-path polylines (default: true)\r\n *\r\n * Coverage geometry sources (checked in order per satellite):\r\n * 1. Explicit `satelliteCoverages[].footprintPolygon` — ground polygon from SDK\r\n * 2. Explicit `satelliteCoverages[].halfAngle` — circular cone from half-angle\r\n * 3. Auto-computed circular ellipse from altitude (when layers.coverage = true)\r\n *\r\n * Additional explicit coverage options per `SatelliteCoverage`:\r\n * showCone — 3-D sensor cone from satellite to footprint\r\n * showNadirLine — dashed line from satellite to sub-satellite point\r\n */\r\n\r\nimport React, {\r\n useRef,\r\n useEffect,\r\n useImperativeHandle,\r\n forwardRef,\r\n useCallback,\r\n} from 'react';\r\nimport type {\r\n ZenSpace3DProps,\r\n ZenSpace3DHandle,\r\n ViewMode,\r\n Vector3D,\r\n SatelliteCoverage,\r\n} from './ZenSpace3DTypes';\r\n\r\n// ── Constants ─────────────────────────────────────────────────────────────────\r\n\r\nconst EARTH_RADIUS_KM = 6371;\r\n\r\n// ── Coordinate helpers ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert an ECI / J2000 position (km, Earth-centred) into a Cesium\r\n * `Cartesian3`. Cesium's Cartesian3 lives in the **ECEF (Earth-Fixed)**\r\n * frame — the same frame Cesium for Unreal exposes when its\r\n * `CesiumGeoreference` origin is set to `TrueOrigin (0,0,0)`. We rotate\r\n * ECI → ECEF using `Transforms.computeTemeToPseudoFixedMatrix(jd)`,\r\n * which is GMST-based and accurate to sub-arcsec for visualisation\r\n * (no IERS data required, fully synchronous).\r\n *\r\n * Returns `null` for non-finite inputs so callers can skip stale ticks\r\n * without crashing Cesium's Cartesian primitives.\r\n *\r\n * @param positionKm ECI position in km (Earth-centred).\r\n * @param julianDate Cesium JulianDate at which to evaluate Earth's rotation.\r\n * Caller is responsible for advancing this with sim time.\r\n */\r\nfunction eciKmToFixedCartesian3(\r\n Cesium: any,\r\n positionKm: Vector3D,\r\n julianDate: any,\r\n): any | null {\r\n if (\r\n !positionKm ||\r\n !Number.isFinite(positionKm.x) ||\r\n !Number.isFinite(positionKm.y) ||\r\n !Number.isFinite(positionKm.z)\r\n ) {\r\n return null;\r\n }\r\n // ECI position in metres — Cesium works in metres throughout.\r\n const eciM = new Cesium.Cartesian3(\r\n positionKm.x * 1000,\r\n positionKm.y * 1000,\r\n positionKm.z * 1000,\r\n );\r\n const rot = Cesium.Transforms.computeTemeToPseudoFixedMatrix(julianDate);\r\n if (!rot) {\r\n // Should never happen, but Transforms can return undefined for invalid\r\n // dates — fall back to treating the input as already-fixed so we never\r\n // crash the viewer on a stray NaN date.\r\n return eciM;\r\n }\r\n return Cesium.Matrix3.multiplyByVector(rot, eciM, new Cesium.Cartesian3());\r\n}\r\n\r\n/**\r\n * Sub-satellite (lat/lon) point + altitude (km) of an ECI position at a\r\n * given epoch. Used by coverage/cone code that needs to draw on Earth's\r\n * surface beneath the satellite.\r\n */\r\nfunction eciKmToSubSatellite(\r\n Cesium: any,\r\n positionKm: Vector3D,\r\n julianDate: any,\r\n): { latitude: number; longitude: number; altitude: number } | null {\r\n const fixed = eciKmToFixedCartesian3(Cesium, positionKm, julianDate);\r\n if (!fixed) return null;\r\n const carto = Cesium.Cartographic.fromCartesian(fixed);\r\n if (!carto || !Number.isFinite(carto.height)) return null;\r\n return {\r\n latitude: Cesium.Math.toDegrees(carto.latitude),\r\n longitude: Cesium.Math.toDegrees(carto.longitude),\r\n altitude: carto.height / 1000, // m → km\r\n };\r\n}\r\n\r\n// ── Coverage geometry ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Circular coverage footprint radius (m) on Earth surface.\r\n *\r\n * Spherical geometry:\r\n * α = arcsin(R·cos(ε) / (R+h)) — nadir angle\r\n * λ = π/2 − ε − α — Earth central angle\r\n * radius = R · λ — arc length on surface\r\n */\r\nfunction autoFootprintRadiusM(altitudeKm: number, minElevDeg: number): number {\r\n if (altitudeKm <= 0) return 0;\r\n const R = EARTH_RADIUS_KM;\r\n const eps = (minElevDeg * Math.PI) / 180;\r\n const sinAlpha = (R * Math.cos(eps)) / (R + altitudeKm);\r\n if (sinAlpha >= 1) return 0;\r\n const alpha = Math.asin(sinAlpha);\r\n const lambda = Math.PI / 2 - eps - alpha;\r\n return R * 1000 * lambda;\r\n}\r\n\r\n/** Radius (m) of the footprint circle for a sensor with the given half-angle. */\r\nfunction halfAngleFootprintRadiusM(altitudeKm: number, halfAngleDeg: number): number {\r\n return altitudeKm * 1000 * Math.tan((halfAngleDeg * Math.PI) / 180);\r\n}\r\n\r\n// ── Entity helpers ────────────────────────────────────────────────────────────\r\n\r\ntype EntityMap = { current: Map<string, any> };\r\n\r\nfunction removeFromMap(viewer: any, id: string, map: EntityMap): void {\r\n if (map.current.has(id)) {\r\n try { viewer.entities.removeById(id); } catch (_) { /* already gone */ }\r\n map.current.delete(id);\r\n }\r\n}\r\n\r\nfunction makePointGraphics(Cesium: any, color: any, pixelSize = 8) {\r\n return {\r\n pixelSize,\r\n color,\r\n outlineColor: Cesium.Color.WHITE,\r\n outlineWidth: 1,\r\n scaleByDistance: new Cesium.NearFarScalar(1e6, 1.5, 5e7, 0.5),\r\n // Always render on top so satellites hidden behind Earth are still visible\r\n disableDepthTestDistance: Number.POSITIVE_INFINITY,\r\n };\r\n}\r\n\r\nfunction makeLabelOpts(Cesium: any, text: string, fillColor?: any) {\r\n return {\r\n text,\r\n font: '12px monospace',\r\n fillColor: fillColor ?? Cesium.Color.WHITE,\r\n outlineColor: Cesium.Color.BLACK,\r\n outlineWidth: 2,\r\n style: Cesium.LabelStyle.FILL_AND_OUTLINE,\r\n pixelOffset: new Cesium.Cartesian2(12, 0),\r\n scaleByDistance: new Cesium.NearFarScalar(1e6, 1.0, 5e7, 0.3),\r\n // Fade out label when camera is very far away\r\n distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 3e7),\r\n disableDepthTestDistance: Number.POSITIVE_INFINITY,\r\n };\r\n}\r\n\r\n/** Add or update a circular ellipse entity on the Earth surface. */\r\nfunction upsertEllipse(\r\n viewer: any,\r\n Cesium: any,\r\n id: string,\r\n position: any,\r\n radiusM: number,\r\n color: any,\r\n map: EntityMap,\r\n): void {\r\n const existing = map.current.get(id);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(position);\r\n existing.ellipse.semiMajorAxis = new Cesium.ConstantProperty(radiusM);\r\n existing.ellipse.semiMinorAxis = new Cesium.ConstantProperty(radiusM);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id,\r\n position,\r\n ellipse: {\r\n semiMajorAxis: radiusM,\r\n semiMinorAxis: radiusM,\r\n material: color.withAlpha(0.08),\r\n outline: true,\r\n outlineColor: color.withAlpha(0.5),\r\n outlineWidth: 1,\r\n height: 0,\r\n },\r\n });\r\n map.current.set(id, entity);\r\n }\r\n}\r\n\r\n/**\r\n * Render (or update in-place) the full coverage geometry for one satellite\r\n * from an explicit `SatelliteCoverage` record supplied by the Zendir SDK.\r\n */\r\nfunction renderExplicitCoverage(\r\n viewer: any,\r\n Cesium: any,\r\n satId: string,\r\n satPos3: Vector3D,\r\n cov: SatelliteCoverage,\r\n julianDate: any,\r\n covMap: EntityMap,\r\n coneMap: EntityMap,\r\n nadirMap: EntityMap,\r\n): void {\r\n const baseColor = cov.color\r\n ? Cesium.Color.fromCssColorString(cov.color)\r\n : Cesium.Color.CYAN;\r\n const opacity = cov.opacity ?? 0.2;\r\n\r\n const resolvedPos = cov.satellitePosition ?? satPos3;\r\n const satCesPos = eciKmToFixedCartesian3(Cesium, resolvedPos, julianDate);\r\n const subSat = eciKmToSubSatellite(Cesium, resolvedPos, julianDate);\r\n const latitude = subSat?.latitude ?? NaN;\r\n const longitude = subSat?.longitude ?? NaN;\r\n const altitude = subSat?.altitude ?? NaN;\r\n\r\n const nadirLat = cov.nadirPoint?.latitude ?? latitude;\r\n const nadirLon = cov.nadirPoint?.longitude ?? longitude;\r\n\r\n // ── Footprint polygon or circular ellipse ──────────────────────────────────\r\n const covId = `cov-${satId}`;\r\n if (cov.showFootprint !== false && cov.footprintPolygon && cov.footprintPolygon.length >= 3) {\r\n const positions = Cesium.Cartesian3.fromDegreesArray(\r\n cov.footprintPolygon.flatMap((pt) => [pt.longitude, pt.latitude]),\r\n );\r\n const existing = covMap.current.get(covId);\r\n if (existing) {\r\n existing.polygon.hierarchy = new Cesium.ConstantProperty(\r\n new Cesium.PolygonHierarchy(positions),\r\n );\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: covId,\r\n polygon: {\r\n hierarchy: new Cesium.PolygonHierarchy(positions),\r\n material: baseColor.withAlpha(opacity),\r\n outline: true,\r\n outlineColor: baseColor.withAlpha(opacity * 3),\r\n height: 0,\r\n clampToGround: true,\r\n },\r\n });\r\n covMap.current.set(covId, entity);\r\n }\r\n } else if (\r\n cov.showFootprint !== false &&\r\n cov.halfAngle != null &&\r\n Number.isFinite(altitude) &&\r\n altitude > 0\r\n ) {\r\n const subSatPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, 0);\r\n const radius = halfAngleFootprintRadiusM(altitude, cov.halfAngle);\r\n upsertEllipse(viewer, Cesium, covId, subSatPos, radius, baseColor, covMap);\r\n } else {\r\n removeFromMap(viewer, covId, covMap);\r\n }\r\n\r\n // ── Sensor cone (satellite → footprint, rendered as a Cesium cylinder) ─────\r\n const coneId = `cone-${satId}`;\r\n if (cov.showCone && satCesPos && Number.isFinite(altitude) && altitude > 0) {\r\n const radius = cov.halfAngle != null\r\n ? halfAngleFootprintRadiusM(altitude, cov.halfAngle)\r\n : autoFootprintRadiusM(altitude, 5);\r\n const coneLength = altitude * 1000; // km → m\r\n // Cylinder centered at mid-altitude between satellite and ground point\r\n const midPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, coneLength / 2);\r\n const existing = coneMap.current.get(coneId);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(midPos);\r\n existing.cylinder.length = new Cesium.ConstantProperty(coneLength);\r\n existing.cylinder.bottomRadius = new Cesium.ConstantProperty(radius);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: coneId,\r\n position: midPos,\r\n cylinder: {\r\n length: coneLength,\r\n topRadius: 0,\r\n bottomRadius: radius,\r\n material: baseColor.withAlpha(0.06),\r\n outline: true,\r\n outlineColor: baseColor.withAlpha(0.25),\r\n numberOfVerticalLines: 12,\r\n },\r\n });\r\n coneMap.current.set(coneId, entity);\r\n }\r\n } else {\r\n removeFromMap(viewer, coneId, coneMap);\r\n }\r\n\r\n // ── Nadir line (satellite → sub-satellite point) ───────────────────────────\r\n const nadirId = `nadir-${satId}`;\r\n if (cov.showNadirLine && satCesPos) {\r\n const groundPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, 0);\r\n const existing = nadirMap.current.get(nadirId);\r\n if (existing) {\r\n existing.polyline.positions = new Cesium.ConstantProperty([satCesPos, groundPos]);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: nadirId,\r\n polyline: {\r\n positions: [satCesPos, groundPos],\r\n width: 1,\r\n material: new Cesium.PolylineDashMaterialProperty({\r\n color: baseColor.withAlpha(0.5),\r\n dashLength: 8,\r\n }),\r\n },\r\n });\r\n nadirMap.current.set(nadirId, entity);\r\n }\r\n } else {\r\n removeFromMap(viewer, nadirId, nadirMap);\r\n }\r\n}\r\n\r\n// ── Component ─────────────────────────────────────────────────────────────────\r\n\r\nexport interface ZenSpace3DCesiumProps extends ZenSpace3DProps {\r\n /** Cesium module from dynamic import('cesium') */\r\n cesium: any;\r\n}\r\n\r\nexport const ZenSpace3DCesium = forwardRef<ZenSpace3DHandle, ZenSpace3DCesiumProps>(\r\n function ZenSpace3DCesium(\r\n {\r\n satellites = [],\r\n stations = [],\r\n groundStations = [],\r\n customObjects = [],\r\n orbitPaths = [],\r\n satelliteCoverages = [],\r\n view = 'earth-orbit',\r\n height = 600,\r\n callbacks,\r\n showTools = false,\r\n layers,\r\n visibility,\r\n referenceTime,\r\n cesium: Cesium,\r\n },\r\n ref,\r\n ) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const viewerRef = useRef<any>(null);\r\n\r\n // Stash callbacks in a ref so click/hover handlers never close over a stale\r\n // function reference — and never make the mount effect depend on them.\r\n const callbacksRef = useRef(callbacks);\r\n useEffect(() => { callbacksRef.current = callbacks; }, [callbacks]);\r\n\r\n // ── Entity maps for in-place reconciliation ────────────────────────────────\r\n // Each map: entity-id → Cesium entity object (only live entities live here).\r\n const satMapRef = useRef<Map<string, any>>(new Map()); // spacecraft + station dots\r\n const gsMapRef = useRef<Map<string, any>>(new Map()); // ground station dots\r\n const covMapRef = useRef<Map<string, any>>(new Map()); // coverage ellipses / polygons\r\n const coneMapRef = useRef<Map<string, any>>(new Map()); // sensor cones\r\n const nadirMapRef = useRef<Map<string, any>>(new Map()); // nadir lines\r\n const orbitMapRef = useRef<Map<string, any>>(new Map()); // orbit polylines\r\n\r\n // ── Latest ECI positions for satellite callback-properties ─────────────────\r\n // Satellites use CallbackPositionProperty so Cesium re-derives ECEF from the\r\n // stored ECI position on every rendered frame (using the current JulianDate).\r\n // This makes satellites track Earth's rotation smoothly between API poll ticks,\r\n // fixing the \"satellite frozen while Earth rotates\" bug.\r\n const eciPositionsRef = useRef<Map<string, Vector3D>>(new Map());\r\n\r\n // ── Layer visibility ──────────────────────────────────────────────────────\r\n // Render-flag layers (always-on by default for orbits; off by default for\r\n // labels/coverage). Derive primitives so the sync effect deps are cheap.\r\n const showLabels = layers?.labels === true; // off by default\r\n const showCoverage = layers?.coverage === true; // off by default\r\n const showOrbits = layers?.orbits !== false; // on by default\r\n\r\n // Object-group visibility — toggle whole categories of markers off without\r\n // unmounting them. Defaults to \"everything visible\" so existing consumers\r\n // who never set `visibility` are unaffected.\r\n const showSpacecraft = visibility?.spacecraft !== false;\r\n const showGroundStations = visibility?.groundStations !== false;\r\n const showCelestialBodies = visibility?.celestialBodies !== false;\r\n\r\n // ── Imperative handle ──────────────────────────────────────────────────────\r\n const focusOn = useCallback(\r\n (objectId: string, options?: { zoom?: number; animate?: boolean }) => {\r\n const viewer = viewerRef.current;\r\n if (!viewer) return;\r\n const entity = viewer.entities.getById(objectId);\r\n if (entity) {\r\n viewer.zoomTo(entity, { duration: options?.animate !== false ? 1.5 : 0 });\r\n }\r\n },\r\n [],\r\n );\r\n\r\n const setView = useCallback(\r\n (mode: ViewMode) => {\r\n const viewer = viewerRef.current;\r\n if (!viewer) return;\r\n if (mode === 'earth-orbit') {\r\n viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(0, 0, 2e7) });\r\n } else if (mode === 'solar-system') {\r\n viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(0, 0, 5e7) });\r\n }\r\n },\r\n [Cesium],\r\n );\r\n\r\n useImperativeHandle(\r\n ref,\r\n (): ZenSpace3DHandle => ({\r\n focusOn,\r\n flyTo: focusOn,\r\n setView,\r\n setTime: () => {},\r\n setTimeScale: () => {},\r\n togglePlay: () => {},\r\n setLayerVisibility: () => {},\r\n getCameraState: () => ({} as any),\r\n setCameraState: () => {},\r\n exportImage: () => '',\r\n exportScene: () => new Blob(),\r\n getVisibleObjects: () => [],\r\n findObjects: () => [],\r\n addObject: () => {},\r\n removeObject: () => {},\r\n updateObject: () => {},\r\n measureDistance: () => 0,\r\n }),\r\n [focusOn, setView],\r\n );\r\n\r\n // ── Mount effect: create Cesium Viewer + event handlers ONCE ──────────────\r\n //\r\n // Does NOT depend on the data arrays (satellites, orbitPaths, etc.) or on\r\n // `callbacks`. Both produce fresh references on every parent render / poll\r\n // tick. Including them would tear down + rebuild Cesium every 2 s, leaking\r\n // WebGL contexts until Chrome hits its hard cap (~16).\r\n useEffect(() => {\r\n if (!containerRef.current || !Cesium) return;\r\n\r\n // Clear maps in case this is a hot-reload remount\r\n satMapRef.current.clear();\r\n gsMapRef.current.clear();\r\n covMapRef.current.clear();\r\n coneMapRef.current.clear();\r\n nadirMapRef.current.clear();\r\n orbitMapRef.current.clear();\r\n eciPositionsRef.current.clear();\r\n\r\n const viewer = new Cesium.Viewer(containerRef.current, {\r\n baseLayerPicker: false,\r\n fullscreenButton: false,\r\n vrButton: false,\r\n geocoder: false,\r\n homeButton: !!showTools,\r\n sceneModePicker: false,\r\n timeline: false,\r\n navigationHelpButton: !!showTools,\r\n animation: false,\r\n scene3DOnly: true,\r\n useDefaultRenderLoop: true,\r\n requestRenderMode: false,\r\n });\r\n\r\n viewer.scene.globe.enableLighting = true;\r\n viewer.scene.backgroundColor = Cesium.Color.BLACK;\r\n\r\n // Surface the actual exception text on render-loop crashes. Without\r\n // this listener Cesium only shows \"An error occurred while rendering.\"\r\n // and the stack trace is buried.\r\n viewer.scene.renderError.addEventListener((scene: any, err: unknown) => {\r\n // eslint-disable-next-line no-console\r\n console.error('[ZenSpace3D] Cesium render error:', err);\r\n });\r\n\r\n // Advance the clock at 1× real time so Cesium continuously evaluates\r\n // ECI→ECEF for the correct current moment. Without this, the JulianDate\r\n // passed to satellite CallbackPositionProperty callbacks would be frozen\r\n // at the viewer-creation instant and satellites would drift relative to\r\n // Earth as wall-clock time advanced.\r\n viewer.clock.currentTime = Cesium.JulianDate.now();\r\n viewer.clock.multiplier = 1;\r\n viewer.clock.shouldAnimate = true;\r\n\r\n viewerRef.current = viewer;\r\n\r\n const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);\r\n\r\n // Click → select\r\n handler.setInputAction((movement: any) => {\r\n const picked = viewer.scene.pick(movement.position);\r\n if (Cesium.defined(picked) && picked.id && callbacksRef.current?.onSelect) {\r\n callbacksRef.current.onSelect({\r\n id: picked.id.id,\r\n name: picked.id.name,\r\n category: 'satellite',\r\n position: { x: 0, y: 0, z: 0 },\r\n visualRadius: 0,\r\n });\r\n }\r\n }, Cesium.ScreenSpaceEventType.LEFT_CLICK);\r\n\r\n // Hover → onHover callback\r\n handler.setInputAction((movement: any) => {\r\n const picked = viewer.scene.pick(movement.endPosition);\r\n if (Cesium.defined(picked) && picked.id) {\r\n callbacksRef.current?.onHover?.({\r\n id: picked.id.id,\r\n name: picked.id.name,\r\n category: 'satellite',\r\n position: { x: 0, y: 0, z: 0 },\r\n visualRadius: 0,\r\n });\r\n } else {\r\n callbacksRef.current?.onHover?.(null);\r\n }\r\n }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);\r\n\r\n return () => {\r\n handler.destroy();\r\n satMapRef.current.clear();\r\n gsMapRef.current.clear();\r\n covMapRef.current.clear();\r\n coneMapRef.current.clear();\r\n nadirMapRef.current.clear();\r\n orbitMapRef.current.clear();\r\n eciPositionsRef.current.clear();\r\n viewer.destroy();\r\n viewerRef.current = null;\r\n };\r\n }, [Cesium, showTools]);\r\n\r\n // ── Sync effect: reconcile entities on data / layer changes ───────────────\r\n //\r\n // For existing satellites (same ID, new position): entity.position is\r\n // updated in-place via ConstantPositionProperty — no remove/add required.\r\n // Only genuinely new or removed satellites trigger entity creation/deletion.\r\n useEffect(() => {\r\n const viewer = viewerRef.current;\r\n if (!viewer || !Cesium) return;\r\n\r\n // JulianDate used for static ECI→ECEF snapshots in coverage/orbit\r\n // geometry. Satellite dot positions use CallbackPositionProperty and\r\n // re-evaluate this conversion per-frame from the viewer's live clock,\r\n // so they don't depend on `jd` here.\r\n const jd = referenceTime\r\n ? Cesium.JulianDate.fromDate(referenceTime)\r\n : Cesium.JulianDate.now();\r\n\r\n // ── Satellites + stations ────────────────────────────────────────────\r\n // When `visibility.spacecraft === false` the consumer wants the whole\r\n // group hidden — feed the loop an empty list so the cleanup pass below\r\n // sweeps existing entities without us having to duplicate the removal\r\n // logic here.\r\n const seenSats = new Set<string>();\r\n const renderedSats = showSpacecraft ? [...satellites, ...stations] : [];\r\n\r\n renderedSats.forEach((obj) => {\r\n const id = obj.id;\r\n const pos3 = (obj as any).position as Vector3D;\r\n if (!pos3) return;\r\n // Guard: reject placeholder/zero positions that haven't been seeded yet.\r\n if (!Number.isFinite(pos3.x) || !Number.isFinite(pos3.y) || !Number.isFinite(pos3.z)) return;\r\n\r\n // Store the latest ECI km position so the per-frame callback can\r\n // convert it to ECEF at the actual render time rather than at the\r\n // (stale) poll-tick time.\r\n eciPositionsRef.current.set(id, pos3);\r\n seenSats.add(id);\r\n\r\n const name = (obj as any).name ?? id;\r\n const existing = satMapRef.current.get(id);\r\n\r\n if (existing) {\r\n // ECI position already written to eciPositionsRef above — the\r\n // CallbackPositionProperty will pick it up on the next frame.\r\n // Only sync label visibility here.\r\n if (existing.label) {\r\n existing.label.show = new Cesium.ConstantProperty(showLabels);\r\n } else if (showLabels) {\r\n existing.label = new Cesium.LabelGraphics(makeLabelOpts(Cesium, name));\r\n }\r\n } else {\r\n // Use CallbackPositionProperty so Cesium re-evaluates the ECI→ECEF\r\n // transform on every rendered frame using the viewer's current\r\n // JulianDate. This keeps the satellite locked to the correct point\r\n // on the rotating Earth rather than drifting in inertial space.\r\n const capturedId = id; // capture for closure\r\n // The callback runs inside Cesium's render loop on every frame.\r\n // ANY exception here halts rendering (\"An error occurred while\r\n // rendering. Rendering has stopped.\"), so we defensively catch\r\n // and return undefined — Cesium treats that as \"position\r\n // unavailable\" and skips the entity for that frame.\r\n const posProperty = new Cesium.CallbackPositionProperty(\r\n (time: any) => {\r\n try {\r\n const eciKm = eciPositionsRef.current.get(capturedId);\r\n if (!eciKm) return undefined;\r\n const fixed = eciKmToFixedCartesian3(Cesium, eciKm, time);\r\n if (\r\n !fixed ||\r\n !Number.isFinite(fixed.x) ||\r\n !Number.isFinite(fixed.y) ||\r\n !Number.isFinite(fixed.z)\r\n ) {\r\n return undefined;\r\n }\r\n return fixed;\r\n } catch {\r\n return undefined;\r\n }\r\n },\r\n false, // not constant — must be re-evaluated every frame\r\n Cesium.ReferenceFrame.FIXED,\r\n );\r\n const entity = viewer.entities.add({\r\n id,\r\n name,\r\n position: posProperty,\r\n point: makePointGraphics(Cesium, Cesium.Color.CYAN),\r\n label: showLabels\r\n ? new Cesium.LabelGraphics(makeLabelOpts(Cesium, name))\r\n : undefined,\r\n });\r\n satMapRef.current.set(id, entity);\r\n }\r\n\r\n // ── Coverage ─────────────────────────────────────────────────────────\r\n const explicitCov = (satelliteCoverages ?? []).find((c) => c.satelliteId === id);\r\n if (explicitCov) {\r\n renderExplicitCoverage(\r\n viewer, Cesium, id, pos3, explicitCov, jd,\r\n covMapRef, coneMapRef, nadirMapRef,\r\n );\r\n } else if (showCoverage) {\r\n // Auto coverage: circular ellipse computed from sub-satellite point\r\n const subSat = eciKmToSubSatellite(Cesium, pos3, jd);\r\n if (subSat && subSat.altitude > 0) {\r\n const subSatPos = Cesium.Cartesian3.fromDegrees(subSat.longitude, subSat.latitude, 0);\r\n const radius = autoFootprintRadiusM(subSat.altitude, 5);\r\n upsertEllipse(viewer, Cesium, `cov-${id}`, subSatPos, radius, Cesium.Color.CYAN, covMapRef);\r\n }\r\n } else {\r\n removeFromMap(viewer, `cov-${id}`, covMapRef);\r\n removeFromMap(viewer, `cone-${id}`, coneMapRef);\r\n removeFromMap(viewer, `nadir-${id}`, nadirMapRef);\r\n }\r\n });\r\n\r\n // Remove stale satellite entities (+ associated coverage geometry + ECI position).\r\n satMapRef.current.forEach((_, id) => {\r\n if (!seenSats.has(id)) {\r\n eciPositionsRef.current.delete(id);\r\n removeFromMap(viewer, id, satMapRef);\r\n removeFromMap(viewer, `cov-${id}`, covMapRef);\r\n removeFromMap(viewer, `cone-${id}`, coneMapRef);\r\n removeFromMap(viewer, `nadir-${id}`, nadirMapRef);\r\n }\r\n });\r\n\r\n // ── Custom objects (planets, celestial bodies, etc.) ────────────────\r\n // Earth is already rendered by Cesium's globe — skip it by name.\r\n // Other celestial bodies (Moon, etc.) are rendered as orange dots.\r\n // When `visibility.celestialBodies === false`, feed an empty list so\r\n // the cleanup pass removes any existing custom-object entities.\r\n const seenCustom = new Set<string>();\r\n const renderedCustom = showCelestialBodies ? (customObjects ?? []) : [];\r\n renderedCustom.forEach((obj) => {\r\n if (/^earth$/i.test(obj.name ?? '')) return; // skip Earth\r\n const id = obj.id;\r\n const cesPos = eciKmToFixedCartesian3(Cesium, obj.position, jd);\r\n if (!cesPos) return;\r\n seenCustom.add(id);\r\n\r\n const existing = satMapRef.current.get(`custom-${id}`);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(cesPos);\r\n if (existing.label) {\r\n existing.label.show = new Cesium.ConstantProperty(showLabels);\r\n } else if (showLabels) {\r\n existing.label = new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, obj.name ?? id, Cesium.Color.ORANGE),\r\n );\r\n }\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: `custom-${id}`,\r\n name: obj.name,\r\n position: cesPos,\r\n point: makePointGraphics(Cesium, Cesium.Color.ORANGE, 10),\r\n label: showLabels\r\n ? new Cesium.LabelGraphics(makeLabelOpts(Cesium, obj.name ?? id, Cesium.Color.ORANGE))\r\n : undefined,\r\n });\r\n satMapRef.current.set(`custom-${id}`, entity);\r\n }\r\n });\r\n // Remove stale custom objects\r\n satMapRef.current.forEach((_, key) => {\r\n if (key.startsWith('custom-') && !seenCustom.has(key.slice(7))) {\r\n removeFromMap(viewer, key, satMapRef);\r\n }\r\n });\r\n\r\n // ── Ground stations ──────────────────────────────────────────────────\r\n // When `visibility.groundStations === false`, feed an empty list so\r\n // the cleanup pass removes any existing ground-station entities.\r\n const seenGs = new Set<string>();\r\n const renderedGs = showGroundStations ? (groundStations ?? []) : [];\r\n renderedGs.forEach((gs) => {\r\n if (!Number.isFinite(gs.longitude) || !Number.isFinite(gs.latitude)) return;\r\n seenGs.add(gs.id);\r\n const heightM = Number.isFinite((gs as any).elevation) ? (gs as any).elevation : 0;\r\n const gsPos = Cesium.Cartesian3.fromDegrees(gs.longitude, gs.latitude, heightM);\r\n const name = gs.name ?? gs.id;\r\n\r\n const existing = gsMapRef.current.get(gs.id);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(gsPos);\r\n if (existing.label) {\r\n existing.label.show = new Cesium.ConstantProperty(showLabels);\r\n } else if (showLabels) {\r\n existing.label = new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, name, Cesium.Color.YELLOW),\r\n );\r\n }\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: gs.id,\r\n name,\r\n position: gsPos,\r\n point: makePointGraphics(Cesium, Cesium.Color.YELLOW, 6),\r\n label: showLabels\r\n ? new Cesium.LabelGraphics(makeLabelOpts(Cesium, name, Cesium.Color.YELLOW))\r\n : undefined,\r\n });\r\n gsMapRef.current.set(gs.id, entity);\r\n }\r\n });\r\n\r\n gsMapRef.current.forEach((_, id) => {\r\n if (!seenGs.has(id)) removeFromMap(viewer, id, gsMapRef);\r\n });\r\n\r\n // ── Orbit paths ──────────────────────────────────────────────────────\r\n if (showOrbits) {\r\n const seenOrbits = new Set<string>();\r\n\r\n (orbitPaths ?? []).forEach((path) => {\r\n const orbitId = `orbit-${path.objectId}`;\r\n seenOrbits.add(orbitId);\r\n const positions = path.points\r\n .map((p) => eciKmToFixedCartesian3(Cesium, p, jd))\r\n .filter((p): p is NonNullable<typeof p> => p != null);\r\n if (positions.length < 2) return;\r\n\r\n const orbitColor = path.color\r\n ? Cesium.Color.fromCssColorString(path.color).withAlpha(0.65)\r\n : Cesium.Color.YELLOW.withAlpha(0.55);\r\n\r\n const existing = orbitMapRef.current.get(orbitId);\r\n if (existing) {\r\n existing.polyline.positions = new Cesium.ConstantProperty(positions);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: orbitId,\r\n name: path.objectId,\r\n polyline: {\r\n positions,\r\n width: 1.5,\r\n material: new Cesium.PolylineDashMaterialProperty({\r\n color: orbitColor,\r\n dashLength: 16,\r\n }),\r\n },\r\n });\r\n orbitMapRef.current.set(orbitId, entity);\r\n }\r\n });\r\n\r\n orbitMapRef.current.forEach((_, id) => {\r\n if (!seenOrbits.has(id)) removeFromMap(viewer, id, orbitMapRef);\r\n });\r\n } else {\r\n // Orbits turned off: remove all\r\n orbitMapRef.current.forEach((_, id) => removeFromMap(viewer, id, orbitMapRef));\r\n }\r\n }, [\r\n Cesium,\r\n satellites,\r\n stations,\r\n customObjects,\r\n groundStations,\r\n orbitPaths,\r\n satelliteCoverages,\r\n showLabels,\r\n showCoverage,\r\n showOrbits,\r\n showSpacecraft,\r\n showGroundStations,\r\n showCelestialBodies,\r\n referenceTime,\r\n ]);\r\n\r\n // ── React to `view` prop changes ──────────────────────────────────────────\r\n // The imperative `setView()` handle is exposed for ad-hoc fly-tos. When\r\n // the consumer drives the camera declaratively via the `view` prop\r\n // instead, fly to the matching default destination on each change.\r\n useEffect(() => {\r\n setView(view);\r\n // setView itself depends on Cesium (constant once mounted), so it's\r\n // stable enough to omit from the dep array — but include `view` itself.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [view]);\r\n\r\n const h = typeof height === 'number' ? `${height}px` : height;\r\n return (\r\n <div\r\n ref={containerRef}\r\n className=\"zendir-zenspace3d-cesium\"\r\n style={{ width: '100%', height: h, minHeight: 200 }}\r\n />\r\n );\r\n },\r\n);\r\n\r\nexport default ZenSpace3DCesium;\r\n"],"names":["ZenSpace3DCesium"],"mappings":"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"}
@@ -436,6 +436,17 @@ export interface ZenSpace3DProps {
436
436
  timeScale?: number;
437
437
  /** Auto-play on mount */
438
438
  autoPlay?: boolean;
439
+ /**
440
+ * Reference time used to convert ECI / J2000 positions into the
441
+ * Earth-Fixed (ECEF) frame Cesium renders. Should be the **simulation
442
+ * clock as a real UTC Date** (sim epoch + sim seconds) so the
443
+ * sub-satellite point on Earth lines up with the actual ground track.
444
+ *
445
+ * If omitted, the Cesium backend falls back to wall-clock now —
446
+ * geometry stays self-consistent but the ground track will drift from
447
+ * the engine's truth as Earth rotates.
448
+ */
449
+ referenceTime?: Date;
439
450
  /**
440
451
  * Sun direction vector for lighting and atmosphere effects
441
452
  * Typically from Zendir SDK sun position calculation
@@ -485,8 +496,24 @@ export interface ZenSpace3DProps {
485
496
  showAxisHelper?: boolean;
486
497
  /** Maneuver nodes */
487
498
  maneuverNodes?: ManeuverNode[];
488
- /** Layer visibility overrides */
499
+ /**
500
+ * Render-flag layers — affect HOW objects are drawn.
501
+ * `labels` — name labels next to satellite / GS markers (default off)
502
+ * `coverage` — sensor footprint ellipses + cones (default off)
503
+ * `orbits` — historical orbit polylines (default on)
504
+ */
489
505
  layers?: Partial<LayerVisibility>;
506
+ /**
507
+ * Object-group visibility — toggle WHOLE categories on/off without
508
+ * unmounting the viewer. Defaults to "everything visible". Drives the
509
+ * upcoming `<LayerControlPanel />` — also usable directly for headless
510
+ * apps that want pre-set visibility.
511
+ */
512
+ visibility?: {
513
+ spacecraft?: boolean;
514
+ groundStations?: boolean;
515
+ celestialBodies?: boolean;
516
+ };
490
517
  /** Show controls panel */
491
518
  showControls?: boolean;
492
519
  /** Show timeline */
@@ -1,183 +1,27 @@
1
- import { Vector3D, LatLonAlt, SphericalCoords, TLEData, Satellite, OrbitPath, CoverageHexGrid } from './ZenSpace3DTypes';
1
+ import { Vector3D } from './ZenSpace3DTypes';
2
2
 
3
- /** Earth's radius in km */
3
+ /** Earth's mean radius in km. */
4
4
  export declare const EARTH_RADIUS_KM = 6371;
5
- /** Earth's gravitational parameter (km³/s²) */
6
- export declare const EARTH_MU = 398600.4418;
7
- /** Astronomical Unit in km */
8
- export declare const AU_KM = 149597870.7;
9
- /** Degrees to radians */
5
+ /** Degrees radians. */
10
6
  export declare const DEG_TO_RAD: number;
11
- /** Radians to degrees */
7
+ /** Radians degrees. */
12
8
  export declare const RAD_TO_DEG: number;
13
- /** J2000 epoch (January 1, 2000, 12:00 TT) */
14
- export declare const J2000_EPOCH: Date;
15
- /** Seconds per day */
16
- export declare const SECONDS_PER_DAY = 86400;
17
- /** Minutes per day */
18
- export declare const MINUTES_PER_DAY = 1440;
19
- /** Scene Earth radius (render units) */
9
+ /** Earth radius in scene units (Three.js render space). */
20
10
  export declare const SCENE_EARTH_RADIUS = 3000;
21
- /** Scene scale factor (km to render units) */
11
+ /** Conversion factor: 1 km scene units. */
22
12
  export declare const KM_TO_SCENE: number;
23
- /** Scene scale for Solar System (different scale) */
24
- export declare const SOLAR_SYSTEM_SCALE = 1;
25
13
  /**
26
- * Convert latitude/longitude/altitude to Cartesian (ECI-like for rendering)
27
- * @param lat Latitude in degrees
28
- * @param lon Longitude in degrees
29
- * @param altKm Altitude in kilometers
30
- * @param earthRadius Scene radius for Earth
31
- * @returns Cartesian coordinates
14
+ * Convert lat/lon/alt to a Cartesian point in scene space.
15
+ *
16
+ * Result is in *render units* (scaled by `KM_TO_SCENE`), suitable for
17
+ * direct use in the Three.js scene graph. For ECI→ECEF on the Cesium
18
+ * backend, use `Cesium.Transforms.computeTemeToPseudoFixedMatrix`.
19
+ *
20
+ * @param lat Latitude in degrees.
21
+ * @param lon Longitude in degrees.
22
+ * @param altKm Altitude above Earth surface in km (default 0).
23
+ * @param earthRadius Scene radius for Earth (default `SCENE_EARTH_RADIUS`).
32
24
  */
33
25
  export declare function latLonAltToCartesian(lat: number, lon: number, altKm?: number, earthRadius?: number): Vector3D;
34
- /**
35
- * Convert Cartesian to latitude/longitude/altitude
36
- * @param position Cartesian position
37
- * @param earthRadius Scene radius for Earth
38
- * @returns Lat/Lon/Alt
39
- */
40
- export declare function cartesianToLatLonAlt(position: Vector3D, earthRadius?: number): LatLonAlt;
41
- /**
42
- * Convert Cartesian to spherical coordinates
43
- */
44
- export declare function cartesianToSpherical(position: Vector3D): SphericalCoords;
45
- /**
46
- * Convert spherical to Cartesian coordinates
47
- */
48
- export declare function sphericalToCartesian(coords: SphericalCoords): Vector3D;
49
- /**
50
- * Convert ECI (Earth-Centered Inertial) to ECEF (Earth-Centered Earth-Fixed)
51
- * @param eci ECI position (km)
52
- * @param gmst Greenwich Mean Sidereal Time (radians)
53
- */
54
- export declare function eciToEcef(eci: Vector3D, gmst: number): Vector3D;
55
- /**
56
- * Calculate Greenwich Mean Sidereal Time
57
- * @param date Date/time
58
- * @returns GMST in radians
59
- */
60
- export declare function calculateGMST(date: Date): number;
61
- /**
62
- * Convert Date to Julian Date
63
- */
64
- export declare function dateToJulianDate(date: Date): number;
65
- /**
66
- * Parse Two-Line Element set
67
- */
68
- export declare function parseTLE(line1: string, line2: string): TLEData | null;
69
- /**
70
- * Simplified SGP4 propagation
71
- * @param tle TLE data
72
- * @param minutesFromEpoch Minutes from TLE epoch
73
- * @returns Position in km (ECI coordinates)
74
- */
75
- export declare function propagateSGP4Simple(tle: TLEData, minutesFromEpoch: number): Vector3D;
76
- /**
77
- * Propagate satellite to specific time
78
- * @param satellite Satellite with TLE
79
- * @param time Target time
80
- * @returns Position in km (ECI)
81
- */
82
- export declare function propagateSatellite(satellite: Satellite, time: Date): Vector3D | null;
83
- /**
84
- * Generate orbit path for a satellite
85
- * @param satellite Satellite with TLE
86
- * @param startTime Start time
87
- * @param duration Duration in minutes
88
- * @param steps Number of steps
89
- */
90
- export declare function generateOrbitPath(satellite: Satellite, startTime: Date, duration?: number, steps?: number): OrbitPath | null;
91
- /**
92
- * Calculate if a satellite is visible from a ground station
93
- * @param satPos Satellite position (scene units)
94
- * @param gsLatLon Ground station lat/lon
95
- * @param minElevation Minimum elevation angle (degrees)
96
- */
97
- export declare function isVisible(satPos: Vector3D, gsLatLon: {
98
- latitude: number;
99
- longitude: number;
100
- }, minElevation?: number): boolean;
101
- /**
102
- * Calculate visibility cone vertices for rendering
103
- * @param gsLatLon Ground station position
104
- * @param minElevation Minimum elevation angle
105
- * @param altitude Altitude for cone apex (km)
106
- * @param segments Number of segments for cone circle
107
- */
108
- export declare function calculateVisibilityConeGeometry(gsLatLon: {
109
- latitude: number;
110
- longitude: number;
111
- }, minElevation?: number, altitude?: number, segments?: number): Vector3D[];
112
- /**
113
- * Generate footprint polygon for a satellite
114
- * @param satPos Satellite position (scene units)
115
- * @param minElevation Minimum elevation angle
116
- * @param segments Number of segments
117
- */
118
- export declare function calculateFootprint(satPos: Vector3D, minElevation?: number, segments?: number): Array<{
119
- latitude: number;
120
- longitude: number;
121
- }>;
122
- /**
123
- * Generate a hexagonal grid covering the globe
124
- * @param resolution Grid resolution (lower = fewer hexagons)
125
- */
126
- export declare function generateHexGrid(resolution?: number): CoverageHexGrid;
127
- /**
128
- * Update hex grid coverage values based on satellite footprints
129
- */
130
- export declare function updateHexGridCoverage(grid: CoverageHexGrid, satellites: Satellite[], time: Date): CoverageHexGrid;
131
- export declare function dot(a: Vector3D, b: Vector3D): number;
132
- export declare function cross(a: Vector3D, b: Vector3D): Vector3D;
133
- export declare function magnitude(v: Vector3D): number;
134
- export declare function normalize(v: Vector3D): Vector3D;
135
- export declare function subtract(a: Vector3D, b: Vector3D): Vector3D;
136
- export declare function add(a: Vector3D, b: Vector3D): Vector3D;
137
- export declare function scale(v: Vector3D, s: number): Vector3D;
138
- export declare function distance(a: Vector3D, b: Vector3D): number;
139
- export declare function lerp(a: Vector3D, b: Vector3D, t: number): Vector3D;
140
- /**
141
- * Ease-out cubic function
142
- */
26
+ /** Cubic ease-out curve, used by camera fly-to animations. */
143
27
  export declare function easeOutCubic(t: number): number;
144
- /**
145
- * Ease-in-out cubic function
146
- */
147
- export declare function easeInOutCubic(t: number): number;
148
- /**
149
- * Smooth step function
150
- */
151
- export declare function smoothstep(edge0: number, edge1: number, x: number): number;
152
- /**
153
- * Interpolate between two colors
154
- */
155
- export declare function lerpColor(color1: string, color2: string, t: number): string;
156
- /**
157
- * Hex to RGB
158
- */
159
- export declare function hexToRgb(hex: string): {
160
- r: number;
161
- g: number;
162
- b: number;
163
- } | null;
164
- /**
165
- * Get color from value (0-1) on a gradient scale
166
- */
167
- export declare function getGradientColor(value: number, colors?: string[]): string;
168
- /**
169
- * Format time duration as HH:MM:SS
170
- */
171
- export declare function formatDuration(seconds: number): string;
172
- /**
173
- * Format distance in appropriate units
174
- */
175
- export declare function formatDistance(km: number): string;
176
- /**
177
- * Format velocity
178
- */
179
- export declare function formatVelocity(kmPerSec: number): string;
180
- /**
181
- * Format date/time for display
182
- */
183
- export declare function formatDateTime(date: Date): string;
@@ -0,0 +1,28 @@
1
+ const EARTH_RADIUS_KM = 6371;
2
+ const DEG_TO_RAD = Math.PI / 180;
3
+ const RAD_TO_DEG = 180 / Math.PI;
4
+ const SCENE_EARTH_RADIUS = 3e3;
5
+ const KM_TO_SCENE = SCENE_EARTH_RADIUS / EARTH_RADIUS_KM;
6
+ function latLonAltToCartesian(lat, lon, altKm = 0, earthRadius = SCENE_EARTH_RADIUS) {
7
+ const r = earthRadius + altKm * KM_TO_SCENE;
8
+ const phi = (90 - lat) * DEG_TO_RAD;
9
+ const theta = (lon + 180) * DEG_TO_RAD;
10
+ return {
11
+ x: -r * Math.sin(phi) * Math.cos(theta),
12
+ y: r * Math.cos(phi),
13
+ z: r * Math.sin(phi) * Math.sin(theta)
14
+ };
15
+ }
16
+ function easeOutCubic(t) {
17
+ return 1 - Math.pow(1 - t, 3);
18
+ }
19
+ export {
20
+ DEG_TO_RAD,
21
+ EARTH_RADIUS_KM,
22
+ KM_TO_SCENE,
23
+ RAD_TO_DEG,
24
+ SCENE_EARTH_RADIUS,
25
+ easeOutCubic,
26
+ latLonAltToCartesian
27
+ };
28
+ //# sourceMappingURL=ZenSpace3DUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZenSpace3DUtils.js","sources":["../../../src/react/3d/ZenSpace3DUtils.ts"],"sourcesContent":["/**\n * @zendir/ui - ZenSpace3D rendering math\n *\n * The minimal helper set needed by the live 3D backends:\n * • Earth + scene scaling constants used by the Three.js path\n * (`ZenSpace3D.tsx`) and the Cesium capture source\n * (`CesiumCaptureSource.ts`)\n * • `latLonAltToCartesian` — lat/lon/alt → render-space Cartesian\n * • `easeOutCubic` — camera-fly-to easing\n *\n * Higher-level orbital math (TLE/SGP4 propagation, GMST, visibility cones,\n * hex-grid coverage) was removed — those weren't consumed by either\n * backend. The Cesium backend uses Cesium's own `Transforms` for\n * ECI→ECEF, and the Three.js backend draws coverage from explicit\n * `SatelliteCoverage` props rather than computing it locally.\n */\n\nimport type { Vector3D } from './ZenSpace3DTypes';\n\n// ── Constants ─────────────────────────────────────────────────────────────────\n\n/** Earth's mean radius in km. */\nexport const EARTH_RADIUS_KM = 6371;\n\n/** Degrees → radians. */\nexport const DEG_TO_RAD = Math.PI / 180;\n\n/** Radians → degrees. */\nexport const RAD_TO_DEG = 180 / Math.PI;\n\n/** Earth radius in scene units (Three.js render space). */\nexport const SCENE_EARTH_RADIUS = 3000;\n\n/** Conversion factor: 1 km → scene units. */\nexport const KM_TO_SCENE = SCENE_EARTH_RADIUS / EARTH_RADIUS_KM;\n\n// ── Coordinate conversion ─────────────────────────────────────────────────────\n\n/**\n * Convert lat/lon/alt to a Cartesian point in scene space.\n *\n * Result is in *render units* (scaled by `KM_TO_SCENE`), suitable for\n * direct use in the Three.js scene graph. For ECI→ECEF on the Cesium\n * backend, use `Cesium.Transforms.computeTemeToPseudoFixedMatrix`.\n *\n * @param lat Latitude in degrees.\n * @param lon Longitude in degrees.\n * @param altKm Altitude above Earth surface in km (default 0).\n * @param earthRadius Scene radius for Earth (default `SCENE_EARTH_RADIUS`).\n */\nexport function latLonAltToCartesian(\n lat: number,\n lon: number,\n altKm: number = 0,\n earthRadius: number = SCENE_EARTH_RADIUS,\n): Vector3D {\n const r = earthRadius + altKm * KM_TO_SCENE;\n const phi = (90 - lat) * DEG_TO_RAD;\n const theta = (lon + 180) * DEG_TO_RAD;\n return {\n x: -r * Math.sin(phi) * Math.cos(theta),\n y: r * Math.cos(phi),\n z: r * Math.sin(phi) * Math.sin(theta),\n };\n}\n\n// ── Easing ────────────────────────────────────────────────────────────────────\n\n/** Cubic ease-out curve, used by camera fly-to animations. */\nexport function easeOutCubic(t: number): number {\n return 1 - Math.pow(1 - t, 3);\n}\n"],"names":[],"mappings":"AAsBO,MAAM,kBAAkB;AAGxB,MAAM,aAAa,KAAK,KAAK;AAG7B,MAAM,aAAa,MAAM,KAAK;AAG9B,MAAM,qBAAqB;AAG3B,MAAM,cAAc,qBAAqB;AAgBzC,SAAS,qBACd,KACA,KACA,QAAgB,GAChB,cAAsB,oBACZ;AACV,QAAM,IAAI,cAAc,QAAQ;AAChC,QAAM,OAAO,KAAK,OAAO;AACzB,QAAM,SAAS,MAAM,OAAO;AAC5B,SAAO;AAAA,IACL,GAAG,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;AAAA,IACtC,GAAG,IAAI,KAAK,IAAI,GAAG;AAAA,IACnB,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;AAAA,EAAA;AAEzC;AAKO,SAAS,aAAa,GAAmB;AAC9C,SAAO,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC;AAC9B;"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Lightweight PNG analysis for capture QA (brightness / "all black" detection).
3
+ * Used by tests and optional runtime checks — keep dependency-free (no pngjs).
4
+ */
5
+ /** ITU-R BT.709 luma from RGB */
6
+ export declare function rgbToLuma(r: number, g: number, b: number): number;
7
+ /**
8
+ * Sample mean luma of PNG bytes by drawing to a small canvas (browser / happy-dom).
9
+ * Returns 0 if decode fails. For very large images, samples a grid (max ~4096 samples).
10
+ */
11
+ export declare function meanLuminanceFromPngBytes(png: Uint8Array): Promise<number>;
12
+ /**
13
+ * True if the image is uniformly dark (likely failed render / empty GL buffer).
14
+ * Threshold is mean luma on 0–255 scale (default 5 ≈ pure black).
15
+ */
16
+ export declare function isPngMostlyBlack(png: Uint8Array, threshold?: number): Promise<boolean>;
@@ -1,23 +1,21 @@
1
1
  /**
2
2
  * @zendir/ui - 3D Components
3
3
  *
4
- * ZenSpace3D uses Cesium by default when installed (optional peer); falls back to Three.js.
5
- * Install `cesium` for the best globe/orbit experience; otherwise Three.js is used.
6
- *
7
- * Components:
8
- * - ZenSpace3D: Unified 3D space visualization (Cesium default, Three.js fallback)
9
- * - EarthViewer: Earth-focused 3D (Three.js, legacy)
10
- * - SolarSystemViewer: Solar system (Three.js, legacy)
4
+ * `ZenSpace3D` is the single canonical 3D viewer. Cesium is used when the
5
+ * peer dep is installed (production path); a Three.js renderer is used
6
+ * otherwise. Pair it with `useSimulationScene` for live-data integration.
11
7
  */
12
8
  export { ZenSpace3D, default as ZenSpace3DDefault } from './ZenSpace3D';
13
9
  export { ZenSpace3DCesium } from './ZenSpace3DCesium';
14
10
  export type { ZenSpace3DCesiumProps } from './ZenSpace3DCesium';
15
11
  export type { ZenSpace3DProps, ZenSpace3DHandle, ViewMode, CameraMode, ObjectCategory, SpaceObject, Satellite, Debris, SpaceStation, Asteroid, TLEData, OrbitPath, ManeuverNode, VisibilityCone, SatelliteCoverage, CoverageRegion, CoverageHexGrid, OverpassInfo, OrbitDesignArc, CoverageCell, TimeState, SelectionState, CameraState, LayerVisibility, ToolType, ToolState, Vector3D, LatLonAlt, SphericalCoords, SceneConfig, SceneObject, ZenSpace3DCallbacks, } from './ZenSpace3DTypes';
16
- export { latLonAltToCartesian, cartesianToLatLonAlt, cartesianToSpherical, sphericalToCartesian, eciToEcef, calculateGMST, dateToJulianDate, parseTLE, propagateSGP4Simple, propagateSatellite, generateOrbitPath, isVisible, calculateVisibilityConeGeometry, calculateFootprint, generateHexGrid, updateHexGridCoverage, dot, cross, magnitude, normalize, subtract, add, scale, distance, lerp, easeOutCubic, easeInOutCubic, smoothstep, lerpColor, hexToRgb, getGradientColor, formatDuration, formatDistance, formatVelocity, formatDateTime, EARTH_RADIUS_KM, EARTH_MU, AU_KM, DEG_TO_RAD, RAD_TO_DEG, J2000_EPOCH, SCENE_EARTH_RADIUS, KM_TO_SCENE, } from './ZenSpace3DUtils';
12
+ export { latLonAltToCartesian, easeOutCubic, EARTH_RADIUS_KM, DEG_TO_RAD, RAD_TO_DEG, SCENE_EARTH_RADIUS, KM_TO_SCENE, } from './ZenSpace3DUtils';
17
13
  export { atmosphereVertexShader, atmosphereFragmentShader, starsVertexShader, starsFragmentShader, coverageVertexShader, coverageFragmentShader, visibilityConeVertexShader, visibilityConeFragmentShader, orbitLineVertexShader, orbitLineFragmentShader, objectGlowVertexShader, objectGlowFragmentShader, selectionRingVertexShader, selectionRingFragmentShader, terminatorVertexShader, terminatorFragmentShader, gridVertexShader, gridFragmentShader, planetRingVertexShader, planetRingFragmentShader, debrisVertexShader, debrisFragmentShader, createAtmosphereUniforms, createStarsUniforms, createVisibilityConeUniforms, } from './ZenSpace3DShaders';
14
+ export { createCesiumCaptureSource, ecefMetersToSdkPosition, eulerXyzToHpr, ecefEulerXyzToHpr, computeDofBlurPx } from './CesiumCaptureSource';
15
+ export type { CesiumCaptureSourceOptions, CesiumCaptureSourceHandle } from './CesiumCaptureSource';
16
+ /** PNG brightness helpers for capture QA (e.g. detect all-black frames) */
17
+ export { rgbToLuma, meanLuminanceFromPngBytes, isPngMostlyBlack } from './capturePngAnalysis';
18
18
  export { loadThree, isThreeLoaded } from './threeLoader';
19
19
  export type { ThreeModule, OrbitControlsType } from './threeLoader';
20
- export { EarthViewer } from './EarthViewer';
21
- export type { EarthViewerProps } from './EarthViewer';
22
- export { SolarSystemViewer } from './SolarSystemViewer';
23
- export type { SolarSystemViewerProps, PlanetPosition } from './SolarSystemViewer';
20
+ export { useSimulationScene } from '../hooks/useSimulationScene';
21
+ export type { CelestialBody, PositionTickDiag, SimulationSceneClient, SimulationSceneObject, StructureDiag, UseSimulationSceneOptions, UseSimulationSceneResult, } from '../hooks/useSimulationScene';
@@ -0,0 +1,18 @@
1
+ let loadPromise = null;
2
+ async function loadThree() {
3
+ if (loadPromise) {
4
+ return loadPromise;
5
+ }
6
+ loadPromise = (async () => {
7
+ const [THREE, { OrbitControls }] = await Promise.all([
8
+ import("three"),
9
+ import("three/examples/jsm/controls/OrbitControls.js")
10
+ ]);
11
+ return { THREE, OrbitControls };
12
+ })();
13
+ return loadPromise;
14
+ }
15
+ export {
16
+ loadThree
17
+ };
18
+ //# sourceMappingURL=threeLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threeLoader.js","sources":["../../../src/react/3d/threeLoader.ts"],"sourcesContent":["/**\r\n * Shared Three.js loader\r\n * \r\n * Ensures only one instance of Three.js is loaded across all 3D components.\r\n * This prevents the \"Multiple instances of Three.js being imported\" warning.\r\n */\r\n\r\nexport type ThreeModule = typeof import('three');\r\nexport type OrbitControlsType = typeof import('three/examples/jsm/controls/OrbitControls.js').OrbitControls;\r\n\r\ninterface ThreeLoaderResult {\r\n THREE: ThreeModule;\r\n OrbitControls: OrbitControlsType;\r\n}\r\n\r\n// Cached promise to ensure single load\r\nlet loadPromise: Promise<ThreeLoaderResult> | null = null;\r\n\r\n/**\r\n * Load Three.js and OrbitControls\r\n * Returns cached instance if already loaded\r\n */\r\nexport async function loadThree(): Promise<ThreeLoaderResult> {\r\n if (loadPromise) {\r\n return loadPromise;\r\n }\r\n\r\n loadPromise = (async () => {\r\n const [THREE, { OrbitControls }] = await Promise.all([\r\n import('three'),\r\n import('three/examples/jsm/controls/OrbitControls.js'),\r\n ]);\r\n \r\n return { THREE, OrbitControls };\r\n })();\r\n\r\n return loadPromise;\r\n}\r\n\r\n/**\r\n * Check if Three.js is already loaded\r\n */\r\nexport function isThreeLoaded(): boolean {\r\n return loadPromise !== null;\r\n}\r\n"],"names":[],"mappings":"AAgBA,IAAI,cAAiD;AAMrD,eAAsB,YAAwC;AAC5D,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAEA,iBAAe,YAAY;AACzB,UAAM,CAAC,OAAO,EAAE,cAAA,CAAe,IAAI,MAAM,QAAQ,IAAI;AAAA,MACnD,OAAO,OAAO;AAAA,MACd,OAAO,8CAA8C;AAAA,IAAA,CACtD;AAED,WAAO,EAAE,OAAO,cAAA;AAAA,EAClB,GAAA;AAEA,SAAO;AACT;"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import React, { memo, useState } from "react";
3
3
  import { classNames } from "../utils/index.js";
4
- import { isValidIconName, Icon } from "../core/Icon.js";
4
+ import { isValidIconName, Icon } from "../core/display/Icon.js";
5
5
  import { useTheme } from "../theme/ThemeProvider.js";
6
6
  const STATUS_BORDER_COLORS = {
7
7
  off: "#3c3e42",