@zendir/ui 0.1.7 → 0.1.9

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 (79) hide show
  1. package/dist/index.js +0 -137
  2. package/dist/index.js.map +1 -1
  3. package/dist/react/context/DisplaySettingsContext.js +0 -12
  4. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  5. package/dist/react/index.d.ts +0 -30
  6. package/dist/react/utils/index.js +0 -8
  7. package/dist/react/utils/index.js.map +1 -1
  8. package/dist/react.js +0 -137
  9. package/dist/react.js.map +1 -1
  10. package/package.json +2 -2
  11. package/dist/react/3d/EarthViewer.js +0 -836
  12. package/dist/react/3d/EarthViewer.js.map +0 -1
  13. package/dist/react/3d/SolarSystemViewer.js +0 -372
  14. package/dist/react/3d/SolarSystemViewer.js.map +0 -1
  15. package/dist/react/3d/ZenSpace3D.js +0 -1253
  16. package/dist/react/3d/ZenSpace3D.js.map +0 -1
  17. package/dist/react/3d/ZenSpace3DCesium.js +0 -186
  18. package/dist/react/3d/ZenSpace3DCesium.js.map +0 -1
  19. package/dist/react/3d/ZenSpace3DShaders.js +0 -94
  20. package/dist/react/3d/ZenSpace3DShaders.js.map +0 -1
  21. package/dist/react/3d/ZenSpace3DUtils.js +0 -213
  22. package/dist/react/3d/ZenSpace3DUtils.js.map +0 -1
  23. package/dist/react/3d/threeLoader.js +0 -18
  24. package/dist/react/3d/threeLoader.js.map +0 -1
  25. package/dist/react/cards/AccessCard.js +0 -410
  26. package/dist/react/cards/AccessCard.js.map +0 -1
  27. package/dist/react/cards/OrbitCard.js +0 -372
  28. package/dist/react/cards/OrbitCard.js.map +0 -1
  29. package/dist/react/cards/SpacecraftCard.js +0 -941
  30. package/dist/react/cards/SpacecraftCard.js.map +0 -1
  31. package/dist/react/cards/TelemetryCard.js +0 -742
  32. package/dist/react/cards/TelemetryCard.js.map +0 -1
  33. package/dist/react/cards/TelemetryStreamCard.js +0 -309
  34. package/dist/react/cards/TelemetryStreamCard.js.map +0 -1
  35. package/dist/react/charts/GroundTrackMap.js +0 -1123
  36. package/dist/react/charts/GroundTrackMap.js.map +0 -1
  37. package/dist/react/charts/GroundTrackMapLeaflet.js +0 -571
  38. package/dist/react/charts/GroundTrackMapLeaflet.js.map +0 -1
  39. package/dist/react/charts/groundTrackMapLeafletTiles.js +0 -11
  40. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +0 -1
  41. package/dist/react/charts/groundTrackMapLeafletUtils.js +0 -109
  42. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +0 -1
  43. package/dist/react/charts/unified/AstroChart.js +0 -1405
  44. package/dist/react/charts/unified/AstroChart.js.map +0 -1
  45. package/dist/react/charts/unified/PowerOverviewChart.js +0 -488
  46. package/dist/react/charts/unified/PowerOverviewChart.js.map +0 -1
  47. package/dist/react/charts/unified/domain.js +0 -3168
  48. package/dist/react/charts/unified/domain.js.map +0 -1
  49. package/dist/react/charts/unified/generators.js +0 -518
  50. package/dist/react/charts/unified/generators.js.map +0 -1
  51. package/dist/react/charts/unified/presets.js +0 -999
  52. package/dist/react/charts/unified/presets.js.map +0 -1
  53. package/dist/react/charts/unified/sync.js +0 -219
  54. package/dist/react/charts/unified/sync.js.map +0 -1
  55. package/dist/react/charts/unified/theme.js +0 -562
  56. package/dist/react/charts/unified/theme.js.map +0 -1
  57. package/dist/react/charts/unified/useChartStream.js +0 -226
  58. package/dist/react/charts/unified/useChartStream.js.map +0 -1
  59. package/dist/react/visualizations/EclipseTimerCard.js +0 -250
  60. package/dist/react/visualizations/EclipseTimerCard.js.map +0 -1
  61. package/dist/react/visualizations/LinkBudgetCard.js +0 -444
  62. package/dist/react/visualizations/LinkBudgetCard.js.map +0 -1
  63. package/dist/react/visualizations/NavBallCard.js +0 -243
  64. package/dist/react/visualizations/NavBallCard.js.map +0 -1
  65. package/dist/react/visualizations/PropulsionCard.js +0 -298
  66. package/dist/react/visualizations/PropulsionCard.js.map +0 -1
  67. package/dist/react/visualizations/SensorFootprintCard.js +0 -326
  68. package/dist/react/visualizations/SensorFootprintCard.js.map +0 -1
  69. package/dist/react/visualizations/ThermalHeatmapCard.js +0 -372
  70. package/dist/react/visualizations/ThermalHeatmapCard.js.map +0 -1
  71. package/dist/shaders/atmosphere.frag.js +0 -5
  72. package/dist/shaders/atmosphere.frag.js.map +0 -1
  73. package/dist/shaders/atmosphere.vert.js +0 -5
  74. package/dist/shaders/atmosphere.vert.js.map +0 -1
  75. package/dist/shaders/stars.frag.js +0 -5
  76. package/dist/shaders/stars.frag.js.map +0 -1
  77. package/dist/shaders/stars.vert.js +0 -5
  78. package/dist/shaders/stars.vert.js.map +0 -1
  79. package/dist/style.css +0 -143
@@ -1,1253 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import React, { forwardRef, useState, useEffect, useRef, useMemo, useCallback, useImperativeHandle } from "react";
3
- import { AstroIcon } from "../core/AstroIcon.js";
4
- import { SCENE_EARTH_RADIUS, latLonAltToCartesian, KM_TO_SCENE, easeOutCubic } from "./ZenSpace3DUtils.js";
5
- import atmosphereVertexShader from "../../shaders/atmosphere.vert.js";
6
- import atmosphereFragmentShader from "../../shaders/atmosphere.frag.js";
7
- import starsVertexShader from "../../shaders/stars.vert.js";
8
- import starsFragmentShader from "../../shaders/stars.frag.js";
9
- import { loadThree } from "./threeLoader.js";
10
- import { ZenSpace3DCesium } from "./ZenSpace3DCesium.js";
11
- import { useTheme } from "../theme/ThemeProvider.js";
12
- const DEFAULT_LAYERS = {
13
- planets: true,
14
- moons: true,
15
- satellites: true,
16
- debris: false,
17
- stations: true,
18
- asteroids: false,
19
- groundStations: true,
20
- orbits: true,
21
- labels: true,
22
- constellations: false,
23
- coverage: true,
24
- atmosphere: true,
25
- stars: true,
26
- grid: false,
27
- terminator: true
28
- };
29
- const CAMERA_TRANSITION_DURATION = 2e3;
30
- const STARS_COUNT = 5e3;
31
- const EARTH_SEGMENTS = 64;
32
- const EARTH_SEGMENTS_V = 32;
33
- const SPHERICAL_SUBDIVISIONS = 16;
34
- function checkWebGLCapabilities() {
35
- try {
36
- const canvas = document.createElement("canvas");
37
- const gl2 = canvas.getContext("webgl2");
38
- if (gl2) return { available: true, version: 2 };
39
- const gl = canvas.getContext("webgl");
40
- if (gl) return { available: true, version: 1 };
41
- } catch {
42
- }
43
- return { available: false, version: 0 };
44
- }
45
- function slerp(v1, v2, t) {
46
- const len1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
47
- const len2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z);
48
- if (len1 < 1e-4 || len2 < 1e-4) {
49
- return { x: v1.x, y: v1.y, z: v1.z };
50
- }
51
- const n1 = { x: v1.x / len1, y: v1.y / len1, z: v1.z / len1 };
52
- const n2 = { x: v2.x / len2, y: v2.y / len2, z: v2.z / len2 };
53
- let dot = n1.x * n2.x + n1.y * n2.y + n1.z * n2.z;
54
- dot = Math.max(-1, Math.min(1, dot));
55
- const theta = Math.acos(dot);
56
- if (theta < 1e-3) {
57
- return {
58
- x: n1.x + t * (n2.x - n1.x),
59
- y: n1.y + t * (n2.y - n1.y),
60
- z: n1.z + t * (n2.z - n1.z)
61
- };
62
- }
63
- const sinTheta = Math.sin(theta);
64
- if (Math.abs(sinTheta) < 1e-4) {
65
- return {
66
- x: n1.x + t * (n2.x - n1.x),
67
- y: n1.y + t * (n2.y - n1.y),
68
- z: n1.z + t * (n2.z - n1.z)
69
- };
70
- }
71
- const a = Math.sin((1 - t) * theta) / sinTheta;
72
- const b = Math.sin(t * theta) / sinTheta;
73
- return {
74
- x: a * n1.x + b * n2.x,
75
- y: a * n1.y + b * n2.y,
76
- z: a * n1.z + b * n2.z
77
- };
78
- }
79
- function subdivideSphericalPolygon(corners, subdivisions, radius) {
80
- if (corners.length < 3 || subdivisions < 1 || radius <= 0) {
81
- return corners;
82
- }
83
- const result = [];
84
- for (let i = 0; i < corners.length; i++) {
85
- const p1 = corners[i];
86
- const p2 = corners[(i + 1) % corners.length];
87
- if (!isFinite(p1.x) || !isFinite(p1.y) || !isFinite(p1.z) || !isFinite(p2.x) || !isFinite(p2.y) || !isFinite(p2.z)) {
88
- continue;
89
- }
90
- for (let j = 0; j < subdivisions; j++) {
91
- const t = j / subdivisions;
92
- const interpolated = slerp(p1, p2, t);
93
- if (!isFinite(interpolated.x) || !isFinite(interpolated.y) || !isFinite(interpolated.z)) {
94
- continue;
95
- }
96
- const len = Math.sqrt(interpolated.x ** 2 + interpolated.y ** 2 + interpolated.z ** 2);
97
- if (len < 1e-4) continue;
98
- result.push({
99
- x: interpolated.x / len * radius,
100
- y: interpolated.y / len * radius,
101
- z: interpolated.z / len * radius
102
- });
103
- }
104
- }
105
- return result.length > 0 ? result : corners;
106
- }
107
- function createSatelliteShape(THREE, category, size) {
108
- const group = new THREE.Group();
109
- if (category === "station") {
110
- const geo = new THREE.BoxGeometry(size * 1.2, size * 0.6, size * 0.6);
111
- const mat = new THREE.MeshBasicMaterial({ color: 13421772 });
112
- group.add(new THREE.Mesh(geo, mat));
113
- const panelGeo = new THREE.BoxGeometry(size * 2.5, size * 0.03, size * 0.6);
114
- const panelMat = new THREE.MeshBasicMaterial({ color: 1723002 });
115
- const leftPanel = new THREE.Mesh(panelGeo, panelMat);
116
- leftPanel.position.x = -size * 1.5;
117
- group.add(leftPanel);
118
- const rightPanel = new THREE.Mesh(panelGeo, panelMat);
119
- rightPanel.position.x = size * 1.5;
120
- group.add(rightPanel);
121
- } else if (category === "satellite") {
122
- const geo = new THREE.BoxGeometry(size * 0.5, size * 0.5, size * 0.6);
123
- const mat = new THREE.MeshBasicMaterial({ color: 11184810 });
124
- group.add(new THREE.Mesh(geo, mat));
125
- const panelGeo = new THREE.BoxGeometry(size * 1.2, size * 0.02, size * 0.4);
126
- const panelMat = new THREE.MeshBasicMaterial({ color: 1718890 });
127
- const leftPanel = new THREE.Mesh(panelGeo, panelMat);
128
- leftPanel.position.x = -size * 0.7;
129
- group.add(leftPanel);
130
- const rightPanel = new THREE.Mesh(panelGeo, panelMat);
131
- rightPanel.position.x = size * 0.7;
132
- group.add(rightPanel);
133
- } else if (category === "debris") {
134
- const geo = new THREE.IcosahedronGeometry(size * 0.3, 0);
135
- const mat = new THREE.MeshBasicMaterial({ color: 6710886 });
136
- group.add(new THREE.Mesh(geo, mat));
137
- } else {
138
- const geo = new THREE.SphereGeometry(size * 0.4, 8, 6);
139
- const mat = new THREE.MeshBasicMaterial({ color: 8947848 });
140
- group.add(new THREE.Mesh(geo, mat));
141
- }
142
- return group;
143
- }
144
- const ZenSpace3D = forwardRef(
145
- function ZenSpace3D2(props, ref) {
146
- const [cesiumModule, setCesiumModule] = useState(null);
147
- useEffect(() => {
148
- import("cesium").then((m) => setCesiumModule(m)).catch(() => setCesiumModule(null));
149
- }, []);
150
- if (cesiumModule) {
151
- return /* @__PURE__ */ jsx(ZenSpace3DCesium, { ref, cesium: cesiumModule, ...props });
152
- }
153
- return /* @__PURE__ */ jsx(ZenSpace3DThree, { ref, ...props });
154
- }
155
- );
156
- const ZenSpace3DThree = forwardRef(
157
- function ZenSpace3DThree2(props, ref) {
158
- const {
159
- satellites = [],
160
- debris = [],
161
- stations = [],
162
- groundStations = [],
163
- customObjects = [],
164
- view = "earth-orbit",
165
- focusedObjectId,
166
- initialCamera,
167
- sunDirection,
168
- planetPositions,
169
- visibilityCones = [],
170
- satelliteCoverages = [],
171
- overpasses: _overpasses = [],
172
- orbitPaths = [],
173
- orbitDesignArcs: _orbitDesignArcs = [],
174
- communicationLinks = [],
175
- showAxisHelper = false,
176
- layers: layerOverrides,
177
- showTools = true,
178
- showInfoPanel = true,
179
- width = "100%",
180
- height = 600,
181
- callbacks,
182
- maxObjects = 1e4,
183
- className = "",
184
- style
185
- } = props;
186
- const { tokens: _tokens } = useTheme();
187
- const [webglCapabilities] = useState(() => checkWebGLCapabilities());
188
- const containerRef = useRef(null);
189
- const rendererRef = useRef(null);
190
- const sceneRef = useRef(null);
191
- const cameraRef = useRef(null);
192
- const controlsRef = useRef(null);
193
- const animationRef = useRef(null);
194
- const raycasterRef = useRef(null);
195
- const mouseRef = useRef(null);
196
- const earthRef = useRef(null);
197
- const starsMaterialRef = useRef(null);
198
- const atmosphereMaterialRef = useRef(null);
199
- const objectsGroupRef = useRef(null);
200
- const linksGroupRef = useRef(null);
201
- const conesGroupRef = useRef(null);
202
- const orbitsGroupRef = useRef(null);
203
- const planetsGroupRef = useRef(null);
204
- const timeRef = useRef(0);
205
- const focusTransitionRef = useRef(null);
206
- const isUserInteractingRef = useRef(false);
207
- const objectMeshMapRef = useRef(/* @__PURE__ */ new Map());
208
- const isAnimatingRef = useRef(false);
209
- useRef(false);
210
- const [THREE, setTHREE] = useState(null);
211
- const threeRef = useRef(null);
212
- threeRef.current = THREE;
213
- const [OrbitControlsClass, setOrbitControlsClass] = useState(null);
214
- const [isLoading, setIsLoading] = useState(true);
215
- const [error, setError] = useState(null);
216
- const [layers, setLayers] = useState({ ...DEFAULT_LAYERS, ...layerOverrides });
217
- const [selection, setSelection] = useState({ selectedId: null, selectedObject: null, hoveredId: null });
218
- const [hoverTooltip, setHoverTooltip] = useState({ visible: false, x: 0, y: 0, object: null });
219
- const [currentView, setCurrentView] = useState(view);
220
- const currentViewRef = useRef(view);
221
- const effectiveLayers = useMemo(() => ({ ...layers, ...layerOverrides }), [layers, layerOverrides]);
222
- useEffect(() => {
223
- setCurrentView(view);
224
- currentViewRef.current = view;
225
- }, [view]);
226
- useEffect(() => {
227
- if (!webglCapabilities.available) {
228
- setIsLoading(false);
229
- return;
230
- }
231
- loadThree().then(({ THREE: threeModule, OrbitControls: controls }) => {
232
- setTHREE(threeModule);
233
- setOrbitControlsClass(() => controls);
234
- }).catch((err) => {
235
- setError("Failed to load 3D library");
236
- setIsLoading(false);
237
- });
238
- }, [webglCapabilities.available]);
239
- const initScene = useCallback(() => {
240
- var _a, _b;
241
- if (!THREE || !containerRef.current || !OrbitControlsClass) return;
242
- if (rendererRef.current) {
243
- isAnimatingRef.current = false;
244
- if (animationRef.current) {
245
- cancelAnimationFrame(animationRef.current);
246
- animationRef.current = null;
247
- }
248
- (_a = controlsRef.current) == null ? void 0 : _a.dispose();
249
- rendererRef.current.dispose();
250
- if ((_b = containerRef.current) == null ? void 0 : _b.contains(rendererRef.current.domElement)) {
251
- containerRef.current.removeChild(rendererRef.current.domElement);
252
- }
253
- }
254
- const container = containerRef.current;
255
- const rect = container.getBoundingClientRect();
256
- const w = rect.width;
257
- const h = typeof height === "number" ? height : rect.height;
258
- const scene = new THREE.Scene();
259
- sceneRef.current = scene;
260
- const viewMode = currentViewRef.current;
261
- const farClip = viewMode === "solar-system" ? 1e6 : 1e5;
262
- const camera = new THREE.PerspectiveCamera(45, w / h, 1, farClip);
263
- const initialDistance = viewMode === "solar-system" ? 5e3 : 1e4;
264
- camera.position.set(0, initialDistance * 0.3, initialDistance);
265
- cameraRef.current = camera;
266
- if (initialCamera == null ? void 0 : initialCamera.position) {
267
- camera.position.set(initialCamera.position.x, initialCamera.position.y, initialCamera.position.z);
268
- }
269
- const renderer = new THREE.WebGLRenderer({
270
- antialias: true,
271
- alpha: true,
272
- powerPreference: "high-performance",
273
- preserveDrawingBuffer: true
274
- // Prevents flickering from buffer swaps
275
- });
276
- renderer.setSize(w, h);
277
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
278
- container.appendChild(renderer.domElement);
279
- rendererRef.current = renderer;
280
- const controls = new OrbitControlsClass(camera, renderer.domElement);
281
- controls.enableDamping = true;
282
- controls.dampingFactor = 0.05;
283
- controls.rotateSpeed = 0.5;
284
- controls.minDistance = viewMode === "solar-system" ? 500 : 3050;
285
- controls.maxDistance = viewMode === "solar-system" ? 1e5 : 25e3;
286
- controls.enablePan = viewMode === "solar-system";
287
- let interactionTimer;
288
- const handleControlStart = () => {
289
- isUserInteractingRef.current = true;
290
- };
291
- const handleControlEnd = () => {
292
- clearTimeout(interactionTimer);
293
- interactionTimer = setTimeout(() => {
294
- isUserInteractingRef.current = false;
295
- }, 3e3);
296
- };
297
- controls.addEventListener("start", handleControlStart);
298
- controls.addEventListener("end", handleControlEnd);
299
- controlsRef.current = controls;
300
- raycasterRef.current = new THREE.Raycaster();
301
- mouseRef.current = new THREE.Vector2();
302
- objectsGroupRef.current = new THREE.Group();
303
- objectsGroupRef.current.name = "objects";
304
- scene.add(objectsGroupRef.current);
305
- linksGroupRef.current = new THREE.Group();
306
- linksGroupRef.current.name = "links";
307
- scene.add(linksGroupRef.current);
308
- conesGroupRef.current = new THREE.Group();
309
- conesGroupRef.current.name = "cones";
310
- scene.add(conesGroupRef.current);
311
- orbitsGroupRef.current = new THREE.Group();
312
- orbitsGroupRef.current.name = "orbits";
313
- scene.add(orbitsGroupRef.current);
314
- planetsGroupRef.current = new THREE.Group();
315
- planetsGroupRef.current.name = "planets";
316
- scene.add(planetsGroupRef.current);
317
- if (viewMode === "solar-system") {
318
- createSolarSystem(THREE, scene);
319
- } else {
320
- createEarth(THREE, scene);
321
- createAtmosphere(THREE, scene);
322
- }
323
- createStars(THREE, scene);
324
- createLighting(THREE, scene);
325
- if (showAxisHelper && viewMode !== "solar-system") {
326
- createAxisHelper(THREE, scene);
327
- }
328
- setIsLoading(false);
329
- return () => {
330
- clearTimeout(interactionTimer);
331
- if (animationRef.current) cancelAnimationFrame(animationRef.current);
332
- controls.removeEventListener("start", handleControlStart);
333
- controls.removeEventListener("end", handleControlEnd);
334
- controls.dispose();
335
- renderer.dispose();
336
- if (container.contains(renderer.domElement)) container.removeChild(renderer.domElement);
337
- };
338
- }, [THREE, OrbitControlsClass, height, initialCamera, showAxisHelper]);
339
- const createSolarSystem = useCallback((THREE2, _scene) => {
340
- const planetsGroup = planetsGroupRef.current;
341
- if (!planetsGroup) return;
342
- const DISTANCE_SCALE = 5e-6;
343
- const SIZE_SCALE = 0.01;
344
- const sunRadius = 696340 * SIZE_SCALE;
345
- const sunGeo = new THREE2.SphereGeometry(sunRadius, 32, 24);
346
- const sunMat = new THREE2.MeshBasicMaterial({ color: 16768324 });
347
- const sunMesh = new THREE2.Mesh(sunGeo, sunMat);
348
- sunMesh.name = "sun";
349
- sunMesh.userData = { objectId: "sun", planetId: "sun", isPlanet: true, isSelectable: true };
350
- planetsGroup.add(sunMesh);
351
- const glowGeo = new THREE2.SphereGeometry(sunRadius * 1.3, 24, 16);
352
- const glowMat = new THREE2.MeshBasicMaterial({ color: 16755200, transparent: true, opacity: 0.2 });
353
- planetsGroup.add(new THREE2.Mesh(glowGeo, glowMat));
354
- const configs = [
355
- { id: "mercury", color: 9205843, radius: 2439.7, distance: 579e5 },
356
- { id: "venus", color: 16762441, radius: 6051.8, distance: 1082e5 },
357
- { id: "earth", color: 7050198, radius: 6371, distance: 1496e5 },
358
- { id: "mars", color: 12665870, radius: 3389.5, distance: 2279e5 },
359
- { id: "jupiter", color: 14207645, radius: 69911, distance: 7785e5 },
360
- { id: "saturn", color: 16045470, radius: 58232, distance: 1434e6 },
361
- { id: "uranus", color: 13756391, radius: 25362, distance: 2871e6 },
362
- { id: "neptune", color: 5987807, radius: 24622, distance: 4495e6 }
363
- ];
364
- configs.forEach((c) => {
365
- const visualScale = c.distance < 3e8 ? 15 : 5;
366
- const planetRadius = c.radius * SIZE_SCALE * visualScale;
367
- const geo = new THREE2.SphereGeometry(planetRadius, 24, 16);
368
- const mat = new THREE2.MeshPhongMaterial({ color: c.color, shininess: 30 });
369
- const mesh = new THREE2.Mesh(geo, mat);
370
- const orbitDist = c.distance * DISTANCE_SCALE;
371
- mesh.position.x = orbitDist;
372
- mesh.name = c.id;
373
- mesh.userData = { objectId: c.id, planetId: c.id, isPlanet: true, isSelectable: true, data: { id: c.id, name: c.id.charAt(0).toUpperCase() + c.id.slice(1), category: "planet" } };
374
- planetsGroup.add(mesh);
375
- objectMeshMapRef.current.set(c.id, mesh);
376
- if (c.id === "saturn") {
377
- const ringGeo = new THREE2.RingGeometry(planetRadius * 1.4, planetRadius * 2.2, 32);
378
- const ringMat = new THREE2.MeshBasicMaterial({ color: 13219990, transparent: true, opacity: 0.6, side: THREE2.DoubleSide });
379
- const ring = new THREE2.Mesh(ringGeo, ringMat);
380
- ring.rotation.x = Math.PI / 2.5;
381
- ring.position.copy(mesh.position);
382
- planetsGroup.add(ring);
383
- }
384
- const points = [];
385
- for (let i = 0; i <= 64; i++) {
386
- const angle = i / 64 * Math.PI * 2;
387
- points.push(new THREE2.Vector3(orbitDist * Math.cos(angle), 0, orbitDist * Math.sin(angle)));
388
- }
389
- const orbitGeo = new THREE2.BufferGeometry().setFromPoints(points);
390
- const orbitMat = new THREE2.LineBasicMaterial({ color: 4473958, transparent: true, opacity: 0.3 });
391
- planetsGroup.add(new THREE2.Line(orbitGeo, orbitMat));
392
- });
393
- const sunLight = new THREE2.PointLight(16777198, 2, 1e5);
394
- sunLight.position.set(0, 0, 0);
395
- planetsGroup.add(sunLight);
396
- }, []);
397
- const createEarth = useCallback((THREE2, scene) => {
398
- const earthGeometry = new THREE2.SphereGeometry(SCENE_EARTH_RADIUS, EARTH_SEGMENTS, EARTH_SEGMENTS_V);
399
- const earthMaterial = new THREE2.MeshPhongMaterial({ color: 2245802, shininess: 10 });
400
- const textureLoader = new THREE2.TextureLoader();
401
- textureLoader.load("/world.topo.jpg", (texture) => {
402
- texture.wrapS = THREE2.RepeatWrapping;
403
- texture.wrapT = THREE2.ClampToEdgeWrapping;
404
- texture.minFilter = THREE2.LinearFilter;
405
- texture.magFilter = THREE2.LinearFilter;
406
- earthMaterial.map = texture;
407
- earthMaterial.needsUpdate = true;
408
- }, void 0, () => {
409
- });
410
- const earth = new THREE2.Mesh(earthGeometry, earthMaterial);
411
- earth.rotation.y = -Math.PI / 2 + Math.PI;
412
- earth.name = "earth";
413
- scene.add(earth);
414
- earthRef.current = earth;
415
- }, []);
416
- const createAtmosphere = useCallback((THREE2, scene) => {
417
- if (!effectiveLayers.atmosphere) return;
418
- const defaultSunDir = new THREE2.Vector3(1, 0.3, 0.5).normalize();
419
- const atmosphereGeometry = new THREE2.SphereGeometry(SCENE_EARTH_RADIUS * 1.15, 32, 16);
420
- const atmosphereMaterial = new THREE2.ShaderMaterial({
421
- uniforms: {
422
- uSunDirection: { value: defaultSunDir }
423
- },
424
- vertexShader: atmosphereVertexShader,
425
- fragmentShader: atmosphereFragmentShader,
426
- blending: THREE2.AdditiveBlending,
427
- side: THREE2.BackSide,
428
- transparent: true,
429
- depthWrite: false
430
- });
431
- const atmosphere = new THREE2.Mesh(atmosphereGeometry, atmosphereMaterial);
432
- atmosphere.rotation.y = -Math.PI / 2;
433
- atmosphere.name = "atmosphere";
434
- scene.add(atmosphere);
435
- atmosphereMaterialRef.current = atmosphereMaterial;
436
- }, [effectiveLayers.atmosphere]);
437
- const createStars = useCallback((THREE2, scene) => {
438
- const starsGeometry = new THREE2.BufferGeometry();
439
- const starsDist = currentView === "solar-system" ? 5e10 : 5e4;
440
- const positions = new Float32Array(STARS_COUNT * 3);
441
- const opacities = new Float32Array(STARS_COUNT);
442
- for (let i = 0; i < STARS_COUNT; i++) {
443
- const radius = starsDist + Math.random() * starsDist;
444
- const theta = Math.random() * Math.PI * 2;
445
- const phi = Math.acos(2 * Math.random() - 1);
446
- positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
447
- positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
448
- positions[i * 3 + 2] = radius * Math.cos(phi);
449
- opacities[i] = Math.random();
450
- }
451
- starsGeometry.setAttribute("position", new THREE2.BufferAttribute(positions, 3));
452
- starsGeometry.setAttribute("opacity", new THREE2.BufferAttribute(opacities, 1));
453
- const starsMaterial = new THREE2.ShaderMaterial({
454
- uniforms: { time: { value: 0 } },
455
- vertexShader: starsVertexShader,
456
- fragmentShader: starsFragmentShader,
457
- transparent: true,
458
- depthWrite: false,
459
- blending: THREE2.AdditiveBlending
460
- });
461
- scene.add(new THREE2.Points(starsGeometry, starsMaterial));
462
- starsMaterialRef.current = starsMaterial;
463
- }, [currentView]);
464
- const createLighting = useCallback((THREE2, scene) => {
465
- scene.add(new THREE2.AmbientLight(4210752, 0.6));
466
- const sunLight = new THREE2.DirectionalLight(16777215, 1.2);
467
- sunLight.position.set(1e4, 5e3, 1e4);
468
- scene.add(sunLight);
469
- }, []);
470
- const createAxisHelper = useCallback((THREE2, scene) => {
471
- const len = SCENE_EARTH_RADIUS * 1.5;
472
- const group = new THREE2.Group();
473
- [
474
- { color: 16724787, dir: [1, 0, 0] },
475
- { color: 3407667, dir: [0, 1, 0] },
476
- { color: 3355647, dir: [0, 0, 1] }
477
- ].forEach(({ color, dir }) => {
478
- const points = [new THREE2.Vector3(0, 0, 0), new THREE2.Vector3(dir[0] * len, dir[1] * len, dir[2] * len)];
479
- const geo = new THREE2.BufferGeometry().setFromPoints(points);
480
- const mat = new THREE2.LineBasicMaterial({ color });
481
- group.add(new THREE2.Line(geo, mat));
482
- });
483
- scene.add(group);
484
- }, []);
485
- const updateObjects = useCallback(() => {
486
- if (!THREE || !objectsGroupRef.current || currentView === "solar-system") return;
487
- const group = objectsGroupRef.current;
488
- const meshMap = objectMeshMapRef.current;
489
- while (group.children.length > 0) {
490
- const child = group.children[0];
491
- group.remove(child);
492
- }
493
- meshMap.clear();
494
- const allObjects = [...satellites, ...debris, ...stations, ...customObjects];
495
- allObjects.slice(0, maxObjects).forEach((obj) => {
496
- const categoryLayer = obj.category === "satellite" ? "satellites" : obj.category === "debris" ? "debris" : obj.category === "station" ? "stations" : "satellites";
497
- if (!effectiveLayers[categoryLayer]) return;
498
- let size = 50;
499
- if (obj.category === "station") size = 100;
500
- else if (obj.category === "debris") size = 25;
501
- const satelliteGroup = createSatelliteShape(THREE, obj.category, size);
502
- satelliteGroup.position.set(obj.position.x, obj.position.y, obj.position.z);
503
- satelliteGroup.lookAt(0, 0, 0);
504
- satelliteGroup.userData = { objectId: obj.id, category: obj.category, data: obj, isSelectable: true };
505
- satelliteGroup.name = obj.id;
506
- group.add(satelliteGroup);
507
- meshMap.set(obj.id, satelliteGroup);
508
- });
509
- }, [THREE, satellites, debris, stations, customObjects, maxObjects, effectiveLayers, currentView]);
510
- const updateGroundStations = useCallback(() => {
511
- if (!THREE || !objectsGroupRef.current || currentView === "solar-system") return;
512
- if (!effectiveLayers.groundStations) return;
513
- groundStations.forEach((gs) => {
514
- const pos = latLonAltToCartesian(gs.latitude, gs.longitude, 0);
515
- const geo = new THREE.SphereGeometry(35, 8, 6);
516
- const mat = new THREE.MeshBasicMaterial({ color: 65535 });
517
- const marker = new THREE.Mesh(geo, mat);
518
- marker.position.set(pos.x, pos.y, pos.z);
519
- marker.userData = { objectId: gs.id, category: "groundStation", data: gs, isSelectable: true };
520
- objectsGroupRef.current.add(marker);
521
- objectMeshMapRef.current.set(gs.id, marker);
522
- const ringGeo = new THREE.RingGeometry(45, 65, 16);
523
- const ringMat = new THREE.MeshBasicMaterial({ color: 65535, transparent: true, opacity: 0.4, side: THREE.DoubleSide });
524
- const ring = new THREE.Mesh(ringGeo, ringMat);
525
- ring.position.set(pos.x, pos.y, pos.z);
526
- ring.lookAt(0, 0, 0);
527
- objectsGroupRef.current.add(ring);
528
- });
529
- }, [THREE, groundStations, effectiveLayers.groundStations, currentView]);
530
- const updateCoverage = useCallback(() => {
531
- const T = threeRef.current;
532
- if (!T || !conesGroupRef.current || currentView === "solar-system") return;
533
- while (conesGroupRef.current.children.length > 0) {
534
- conesGroupRef.current.remove(conesGroupRef.current.children[0]);
535
- }
536
- if (!effectiveLayers.coverage) return;
537
- satelliteCoverages.forEach((coverage) => {
538
- let satPos;
539
- if (coverage.satellitePosition) {
540
- satPos = new T.Vector3(coverage.satellitePosition.x, coverage.satellitePosition.y, coverage.satellitePosition.z);
541
- } else {
542
- const satMesh = objectMeshMapRef.current.get(coverage.satelliteId);
543
- if (!satMesh) return;
544
- satPos = satMesh.position.clone();
545
- }
546
- const satDistance = satPos.length();
547
- const coneColor = new T.Color(coverage.color || "#22d3ee");
548
- const highlightColor = new T.Color(coverage.color || "#22d3ee").multiplyScalar(1.3);
549
- if (coverage.footprintPolygon && coverage.footprintPolygon.length >= 3 && coverage.showFootprint !== false) {
550
- const cornerPoints = coverage.footprintPolygon.map((pt) => latLonAltToCartesian(pt.latitude, pt.longitude, 0));
551
- const borderPts = subdivideSphericalPolygon(cornerPoints, SPHERICAL_SUBDIVISIONS, SCENE_EARTH_RADIUS * 1.003).map((pt) => new T.Vector3(pt.x, pt.y, pt.z));
552
- borderPts.push(borderPts[0].clone());
553
- const borderGeo = new T.BufferGeometry().setFromPoints(borderPts);
554
- const borderMat = new T.LineBasicMaterial({ color: highlightColor, transparent: true, opacity: 0.95, linewidth: 2 });
555
- conesGroupRef.current.add(new T.Line(borderGeo, borderMat));
556
- const glowPts = subdivideSphericalPolygon(cornerPoints, SPHERICAL_SUBDIVISIONS, SCENE_EARTH_RADIUS * 1.005).map((pt) => new T.Vector3(pt.x, pt.y, pt.z));
557
- glowPts.push(glowPts[0].clone());
558
- const glowGeo = new T.BufferGeometry().setFromPoints(glowPts);
559
- const glowMat = new T.LineBasicMaterial({ color: coneColor, transparent: true, opacity: 0.4 });
560
- conesGroupRef.current.add(new T.Line(glowGeo, glowMat));
561
- if (coverage.showCone !== false) {
562
- for (const corner of cornerPoints) {
563
- const edgePt = new T.Vector3(corner.x, corner.y, corner.z).normalize().multiplyScalar(SCENE_EARTH_RADIUS);
564
- const lineGeo = new T.BufferGeometry().setFromPoints([satPos, edgePt]);
565
- const lineMat = new T.LineBasicMaterial({ color: coneColor, transparent: true, opacity: 0.4 });
566
- conesGroupRef.current.add(new T.Line(lineGeo, lineMat));
567
- }
568
- }
569
- } else if (coverage.halfAngle && coverage.showCone !== false) {
570
- const halfAngleRad = coverage.halfAngle * (Math.PI / 180);
571
- const coneHeight = satDistance - SCENE_EARTH_RADIUS * 0.98;
572
- const coneRadius = coneHeight * Math.tan(halfAngleRad);
573
- const coneGeo = new T.ConeGeometry(coneRadius, coneHeight, 48, 1, true);
574
- const coneMat = new T.MeshBasicMaterial({
575
- color: coneColor,
576
- transparent: true,
577
- opacity: 0.12,
578
- side: T.DoubleSide,
579
- depthWrite: false
580
- });
581
- const cone = new T.Mesh(coneGeo, coneMat);
582
- cone.position.copy(satPos);
583
- cone.lookAt(0, 0, 0);
584
- cone.rotateX(Math.PI / 2);
585
- cone.translateY(-coneHeight / 2);
586
- conesGroupRef.current.add(cone);
587
- if (coverage.showFootprint !== false) {
588
- const nadirDir = satPos.clone().normalize();
589
- const footprintPos = nadirDir.clone().multiplyScalar(SCENE_EARTH_RADIUS * 1.003);
590
- const footprintRadius = coneRadius * (SCENE_EARTH_RADIUS / satDistance);
591
- const footGeo = new T.CircleGeometry(footprintRadius, 64);
592
- const footMat = new T.MeshBasicMaterial({
593
- color: coneColor,
594
- transparent: true,
595
- opacity: 0.3,
596
- side: T.DoubleSide,
597
- depthWrite: false
598
- });
599
- const footprint = new T.Mesh(footGeo, footMat);
600
- footprint.position.copy(footprintPos);
601
- footprint.lookAt(satPos);
602
- conesGroupRef.current.add(footprint);
603
- const ringGeo = new T.RingGeometry(footprintRadius * 0.97, footprintRadius, 64);
604
- const ringMat = new T.MeshBasicMaterial({
605
- color: highlightColor,
606
- transparent: true,
607
- opacity: 0.7,
608
- side: T.DoubleSide
609
- });
610
- const ring = new T.Mesh(ringGeo, ringMat);
611
- ring.position.copy(footprintPos);
612
- ring.lookAt(satPos);
613
- conesGroupRef.current.add(ring);
614
- }
615
- }
616
- if (coverage.showNadirLine !== false) {
617
- let nadirGroundPoint;
618
- if (coverage.nadirPoint) {
619
- const np = latLonAltToCartesian(coverage.nadirPoint.latitude, coverage.nadirPoint.longitude, 0);
620
- nadirGroundPoint = new T.Vector3(np.x, np.y, np.z);
621
- } else {
622
- nadirGroundPoint = satPos.clone().normalize().multiplyScalar(SCENE_EARTH_RADIUS);
623
- }
624
- const lineGeo = new T.BufferGeometry().setFromPoints([satPos, nadirGroundPoint]);
625
- const lineMat = new T.LineBasicMaterial({ color: highlightColor, transparent: true, opacity: 0.7 });
626
- conesGroupRef.current.add(new T.Line(lineGeo, lineMat));
627
- }
628
- });
629
- visibilityCones.forEach((cone) => {
630
- const pos = latLonAltToCartesian(cone.center.latitude, cone.center.longitude, 0);
631
- const coneHeight = 1e3 * KM_TO_SCENE;
632
- const minElev = cone.minElevation || 10;
633
- const coneRadius = coneHeight * Math.tan((90 - minElev) * (Math.PI / 180) * 0.4);
634
- const coneColor = new T.Color(cone.color || "#22d3ee");
635
- const coneGeo = new T.ConeGeometry(coneRadius, coneHeight, 24, 1, true);
636
- const coneMat = new T.MeshBasicMaterial({
637
- color: coneColor,
638
- transparent: true,
639
- opacity: cone.opacity || 0.12,
640
- side: T.DoubleSide,
641
- depthWrite: false
642
- });
643
- const coneMesh = new T.Mesh(coneGeo, coneMat);
644
- coneMesh.position.set(pos.x, pos.y, pos.z);
645
- coneMesh.lookAt(0, 0, 0);
646
- coneMesh.rotateX(-Math.PI / 2);
647
- coneMesh.translateY(coneHeight / 2);
648
- conesGroupRef.current.add(coneMesh);
649
- const footGeo = new T.RingGeometry(coneRadius * 0.7, coneRadius * 0.9, 32);
650
- const footMat = new T.MeshBasicMaterial({
651
- color: coneColor,
652
- transparent: true,
653
- opacity: 0.3,
654
- side: T.DoubleSide
655
- });
656
- const foot = new T.Mesh(footGeo, footMat);
657
- foot.position.set(pos.x * 1.002, pos.y * 1.002, pos.z * 1.002);
658
- foot.lookAt(0, 0, 0);
659
- conesGroupRef.current.add(foot);
660
- });
661
- }, [visibilityCones, satelliteCoverages, effectiveLayers.coverage, currentView]);
662
- const updateLinks = useCallback(() => {
663
- if (!THREE || !linksGroupRef.current || currentView === "solar-system") return;
664
- while (linksGroupRef.current.children.length > 0) {
665
- linksGroupRef.current.remove(linksGroupRef.current.children[0]);
666
- }
667
- communicationLinks.forEach((link) => {
668
- const fromMesh = objectMeshMapRef.current.get(link.fromId);
669
- const toMesh = objectMeshMapRef.current.get(link.toId);
670
- if (!fromMesh || !toMesh) return;
671
- const color = link.color || (link.status === "active" ? "#00ff88" : "#666666");
672
- const geo = new THREE.BufferGeometry().setFromPoints([fromMesh.position.clone(), toMesh.position.clone()]);
673
- const mat = new THREE.LineDashedMaterial({
674
- color: new THREE.Color(color),
675
- transparent: true,
676
- opacity: link.status === "active" ? 0.8 : 0.4,
677
- dashSize: 30,
678
- gapSize: 20
679
- });
680
- const line = new THREE.Line(geo, mat);
681
- line.computeLineDistances();
682
- line.userData = { isAnimatedLink: true };
683
- linksGroupRef.current.add(line);
684
- });
685
- }, [THREE, communicationLinks, currentView]);
686
- const updateOrbits = useCallback(() => {
687
- if (!THREE || !orbitsGroupRef.current || currentView === "solar-system") return;
688
- while (orbitsGroupRef.current.children.length > 0) {
689
- orbitsGroupRef.current.remove(orbitsGroupRef.current.children[0]);
690
- }
691
- if (!effectiveLayers.orbits) return;
692
- orbitPaths.forEach((path) => {
693
- if (path.points.length < 2) return;
694
- const points = path.points.map((p) => new THREE.Vector3(p.x, p.y, p.z));
695
- const geo = new THREE.BufferGeometry().setFromPoints(points);
696
- const mat = new THREE.LineBasicMaterial({ color: new THREE.Color(path.color || "#888888"), transparent: true, opacity: 0.5 });
697
- orbitsGroupRef.current.add(new THREE.Line(geo, mat));
698
- });
699
- }, [THREE, orbitPaths, effectiveLayers.orbits, currentView]);
700
- const handleMouseMove = useCallback((event) => {
701
- var _a, _b;
702
- if (!containerRef.current || !raycasterRef.current || !cameraRef.current) return;
703
- const rect = containerRef.current.getBoundingClientRect();
704
- mouseRef.current.set(
705
- (event.clientX - rect.left) / rect.width * 2 - 1,
706
- -((event.clientY - rect.top) / rect.height) * 2 + 1
707
- );
708
- raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);
709
- const selectables = [];
710
- (_a = objectsGroupRef.current) == null ? void 0 : _a.traverse((obj) => {
711
- var _a2;
712
- if ((_a2 = obj.userData) == null ? void 0 : _a2.isSelectable) selectables.push(obj);
713
- });
714
- const intersects = raycasterRef.current.intersectObjects(selectables, true);
715
- if (intersects.length > 0) {
716
- let hit = intersects[0].object;
717
- while (hit && !((_b = hit.userData) == null ? void 0 : _b.objectId)) hit = hit.parent;
718
- if (hit == null ? void 0 : hit.userData) {
719
- setHoverTooltip({ visible: true, x: event.clientX - rect.left, y: event.clientY - rect.top, object: hit.userData.data });
720
- if (containerRef.current) containerRef.current.style.cursor = "pointer";
721
- return;
722
- }
723
- }
724
- setHoverTooltip({ visible: false, x: 0, y: 0, object: null });
725
- if (containerRef.current) containerRef.current.style.cursor = "grab";
726
- }, []);
727
- const handleClick = useCallback((event) => {
728
- var _a, _b, _c, _d, _e, _f;
729
- if (!containerRef.current || !raycasterRef.current || !cameraRef.current) return;
730
- const rect = containerRef.current.getBoundingClientRect();
731
- mouseRef.current.set(
732
- (event.clientX - rect.left) / rect.width * 2 - 1,
733
- -((event.clientY - rect.top) / rect.height) * 2 + 1
734
- );
735
- raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);
736
- const selectables = [];
737
- (_a = objectsGroupRef.current) == null ? void 0 : _a.traverse((obj) => {
738
- var _a2;
739
- if ((_a2 = obj.userData) == null ? void 0 : _a2.isSelectable) selectables.push(obj);
740
- });
741
- (_b = planetsGroupRef.current) == null ? void 0 : _b.traverse((obj) => {
742
- var _a2;
743
- if ((_a2 = obj.userData) == null ? void 0 : _a2.isSelectable) selectables.push(obj);
744
- });
745
- const intersects = raycasterRef.current.intersectObjects(selectables, true);
746
- if (intersects.length > 0) {
747
- let hit = intersects[0].object;
748
- while (hit && !((_c = hit.userData) == null ? void 0 : _c.objectId)) hit = hit.parent;
749
- if ((_d = hit == null ? void 0 : hit.userData) == null ? void 0 : _d.objectId) {
750
- const objectId = hit.userData.objectId;
751
- setSelection({ selectedId: objectId, selectedObject: hit.userData.data, hoveredId: null });
752
- (_e = callbacks == null ? void 0 : callbacks.onSelect) == null ? void 0 : _e.call(callbacks, hit.userData.data);
753
- flyTo(objectId, { animate: true });
754
- return;
755
- }
756
- }
757
- setSelection({ selectedId: null, selectedObject: null, hoveredId: null });
758
- (_f = callbacks == null ? void 0 : callbacks.onSelect) == null ? void 0 : _f.call(callbacks, null);
759
- }, [callbacks]);
760
- const flyTo = useCallback((objectId, options) => {
761
- var _a, _b, _c, _d, _e, _f;
762
- if (!THREE || !cameraRef.current || !controlsRef.current) return;
763
- const mesh = objectMeshMapRef.current.get(objectId);
764
- if (!mesh) {
765
- return;
766
- }
767
- const targetPos = mesh.position.clone();
768
- const camera = cameraRef.current;
769
- const controls = controlsRef.current;
770
- const category = (_a = mesh.userData) == null ? void 0 : _a.category;
771
- const isGroundStation = category === "groundStation";
772
- let viewDistance;
773
- if (options == null ? void 0 : options.distance) {
774
- viewDistance = options.distance;
775
- } else {
776
- const isPlanet = (_b = mesh.userData) == null ? void 0 : _b.isPlanet;
777
- const targetDist = targetPos.length();
778
- if (isPlanet) {
779
- const planetRadius = ((_d = (_c = mesh.geometry) == null ? void 0 : _c.parameters) == null ? void 0 : _d.radius) || 100;
780
- viewDistance = planetRadius * 5;
781
- } else if (isGroundStation) {
782
- viewDistance = 200;
783
- } else if (category === "station") {
784
- viewDistance = 80;
785
- } else if (category === "satellite") {
786
- viewDistance = 60;
787
- } else if (targetDist > SCENE_EARTH_RADIUS * 2) {
788
- viewDistance = 150;
789
- } else {
790
- viewDistance = 100 * ((options == null ? void 0 : options.zoom) || 1);
791
- }
792
- }
793
- let cameraTargetPos;
794
- if (options == null ? void 0 : options.offset) {
795
- cameraTargetPos = targetPos.clone().add(new THREE.Vector3(options.offset.x, options.offset.y, options.offset.z));
796
- } else if (isGroundStation) {
797
- const upDir = targetPos.clone().normalize();
798
- cameraTargetPos = targetPos.clone().add(upDir.multiplyScalar(viewDistance));
799
- } else {
800
- const dir = targetPos.clone().normalize();
801
- const side = new THREE.Vector3(-dir.z, 0.2, dir.x).normalize();
802
- cameraTargetPos = targetPos.clone().add(side.multiplyScalar(viewDistance));
803
- }
804
- if ((options == null ? void 0 : options.animate) !== false) {
805
- focusTransitionRef.current = {
806
- startTime: Date.now(),
807
- startPos: { x: camera.position.x, y: camera.position.y, z: camera.position.z },
808
- targetPos: { x: cameraTargetPos.x, y: cameraTargetPos.y, z: cameraTargetPos.z },
809
- startTarget: { x: controls.target.x, y: controls.target.y, z: controls.target.z },
810
- targetTarget: { x: targetPos.x, y: targetPos.y, z: targetPos.z },
811
- duration: CAMERA_TRANSITION_DURATION,
812
- isActive: true
813
- };
814
- } else {
815
- camera.position.copy(cameraTargetPos);
816
- controls.target.copy(targetPos);
817
- controls.update();
818
- }
819
- (_f = callbacks == null ? void 0 : callbacks.onFocus) == null ? void 0 : _f.call(callbacks, ((_e = mesh.userData) == null ? void 0 : _e.data) || { id: objectId });
820
- }, [THREE, callbacks]);
821
- const focusOn = flyTo;
822
- const animate = useCallback(() => {
823
- var _a;
824
- if (!isAnimatingRef.current) return;
825
- if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;
826
- const camera = cameraRef.current;
827
- const controls = controlsRef.current;
828
- if ((_a = focusTransitionRef.current) == null ? void 0 : _a.isActive) {
829
- const t = focusTransitionRef.current;
830
- const progress = Math.min((Date.now() - t.startTime) / t.duration, 1);
831
- const e = easeOutCubic(progress);
832
- camera.position.x = t.startPos.x + (t.targetPos.x - t.startPos.x) * e;
833
- camera.position.y = t.startPos.y + (t.targetPos.y - t.startPos.y) * e;
834
- camera.position.z = t.startPos.z + (t.targetPos.z - t.startPos.z) * e;
835
- controls.target.x = t.startTarget.x + (t.targetTarget.x - t.startTarget.x) * e;
836
- controls.target.y = t.startTarget.y + (t.targetTarget.y - t.startTarget.y) * e;
837
- controls.target.z = t.startTarget.z + (t.targetTarget.z - t.startTarget.z) * e;
838
- if (progress >= 1) t.isActive = false;
839
- }
840
- if (controls) controls.update();
841
- timeRef.current += 0.016;
842
- if (starsMaterialRef.current) starsMaterialRef.current.uniforms.time.value = timeRef.current;
843
- if (linksGroupRef.current) {
844
- linksGroupRef.current.children.forEach((child) => {
845
- var _a2;
846
- if (((_a2 = child.userData) == null ? void 0 : _a2.isAnimatedLink) && child.material) {
847
- child.material.dashOffset -= 1.5;
848
- }
849
- });
850
- }
851
- rendererRef.current.render(sceneRef.current, camera);
852
- animationRef.current = requestAnimationFrame(animate);
853
- }, []);
854
- useImperativeHandle(ref, () => ({
855
- focusOn,
856
- flyTo,
857
- setView: (mode) => setCurrentView(mode),
858
- setTime: () => {
859
- },
860
- setTimeScale: () => {
861
- },
862
- togglePlay: () => {
863
- },
864
- setLayerVisibility: (layer, visible) => setLayers((prev) => ({ ...prev, [layer]: visible })),
865
- getCameraState: () => {
866
- var _a, _b, _c;
867
- if (!cameraRef.current) return { position: { x: 0, y: 0, z: 1e4 }, target: { x: 0, y: 0, z: 0 }, up: { x: 0, y: 1, z: 0 }, fov: 45, near: 0.1, far: 1e5 };
868
- const c = cameraRef.current;
869
- return { position: { x: c.position.x, y: c.position.y, z: c.position.z }, target: { x: ((_a = controlsRef.current) == null ? void 0 : _a.target.x) || 0, y: ((_b = controlsRef.current) == null ? void 0 : _b.target.y) || 0, z: ((_c = controlsRef.current) == null ? void 0 : _c.target.z) || 0 }, up: { x: c.up.x, y: c.up.y, z: c.up.z }, fov: c.fov, near: c.near, far: c.far };
870
- },
871
- setCameraState: (state) => {
872
- if (!cameraRef.current) return;
873
- if (state.position) cameraRef.current.position.set(state.position.x, state.position.y, state.position.z);
874
- if (state.target && controlsRef.current) controlsRef.current.target.set(state.target.x, state.target.y, state.target.z);
875
- },
876
- exportImage: (format = "png") => {
877
- var _a;
878
- return ((_a = rendererRef.current) == null ? void 0 : _a.domElement.toDataURL(`image/${format}`)) || "";
879
- },
880
- exportScene: () => new Blob(),
881
- getVisibleObjects: () => [...satellites, ...debris, ...stations, ...customObjects],
882
- findObjects: (q) => [...satellites, ...debris, ...stations, ...customObjects].filter((obj) => obj.name.toLowerCase().includes(q.toLowerCase()) || obj.id.toLowerCase().includes(q.toLowerCase())),
883
- addObject: () => {
884
- },
885
- removeObject: () => {
886
- },
887
- updateObject: () => {
888
- },
889
- measureDistance: (_from, _to) => 0
890
- }), [satellites, debris, stations, customObjects, focusOn]);
891
- useEffect(() => {
892
- if (THREE && OrbitControlsClass) return initScene();
893
- }, [THREE, OrbitControlsClass, initScene]);
894
- useEffect(() => {
895
- if (!isLoading && THREE) {
896
- if (animationRef.current) {
897
- cancelAnimationFrame(animationRef.current);
898
- animationRef.current = null;
899
- }
900
- isAnimatingRef.current = true;
901
- animate();
902
- return () => {
903
- isAnimatingRef.current = false;
904
- if (animationRef.current) {
905
- cancelAnimationFrame(animationRef.current);
906
- animationRef.current = null;
907
- }
908
- };
909
- }
910
- }, [isLoading, THREE, animate]);
911
- useEffect(() => {
912
- if (THREE && !isLoading) {
913
- updateObjects();
914
- updateGroundStations();
915
- updateCoverage();
916
- updateOrbits();
917
- updateLinks();
918
- }
919
- }, [THREE, isLoading, updateObjects, updateGroundStations, updateCoverage, updateOrbits, updateLinks]);
920
- useEffect(() => {
921
- const container = containerRef.current;
922
- if (!container || isLoading) return;
923
- container.addEventListener("mousemove", handleMouseMove);
924
- container.addEventListener("click", handleClick);
925
- return () => {
926
- container.removeEventListener("mousemove", handleMouseMove);
927
- container.removeEventListener("click", handleClick);
928
- };
929
- }, [isLoading, handleMouseMove, handleClick]);
930
- useEffect(() => {
931
- const handleResize = () => {
932
- if (!containerRef.current || !rendererRef.current || !cameraRef.current) return;
933
- const rect = containerRef.current.getBoundingClientRect();
934
- const h = typeof height === "number" ? height : rect.height;
935
- cameraRef.current.aspect = rect.width / h;
936
- cameraRef.current.updateProjectionMatrix();
937
- rendererRef.current.setSize(rect.width, h);
938
- };
939
- window.addEventListener("resize", handleResize);
940
- return () => window.removeEventListener("resize", handleResize);
941
- }, [height]);
942
- useEffect(() => {
943
- if (focusedObjectId) focusOn(focusedObjectId, { animate: true });
944
- }, [focusedObjectId, focusOn]);
945
- useEffect(() => {
946
- if (THREE && atmosphereMaterialRef.current && sunDirection) {
947
- const dir = new THREE.Vector3(sunDirection.x, sunDirection.y, sunDirection.z).normalize();
948
- atmosphereMaterialRef.current.uniforms.uSunDirection.value = dir;
949
- }
950
- }, [THREE, sunDirection]);
951
- useEffect(() => {
952
- if (!THREE || !planetPositions || !planetsGroupRef.current) return;
953
- planetsGroupRef.current.traverse((child) => {
954
- var _a;
955
- if (((_a = child.userData) == null ? void 0 : _a.planetId) && planetPositions[child.userData.planetId]) {
956
- const pos = planetPositions[child.userData.planetId];
957
- child.position.set(pos.x, pos.y, pos.z);
958
- }
959
- });
960
- }, [THREE, planetPositions]);
961
- if (!webglCapabilities.available) {
962
- return /* @__PURE__ */ jsxs("div", { className: `zenspace3d-fallback ${className}`, style: { width, height: typeof height === "number" ? height : 600, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", backgroundColor: "#030508", borderRadius: 8, fontFamily: "ui-monospace, monospace", ...style }, children: [
963
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx(AstroIcon, { name: "public", size: "large", label: "Globe", style: { color: "#22d3ee", opacity: 0.5 } }) }),
964
- /* @__PURE__ */ jsx("div", { style: { color: "#b8bcc8", fontSize: 14 }, children: "WebGL is not available" })
965
- ] });
966
- }
967
- if (error) return /* @__PURE__ */ jsx("div", { style: { width, height, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#0a0a0f", color: "#ff4444" }, children: /* @__PURE__ */ jsxs("p", { children: [
968
- "Error: ",
969
- error
970
- ] }) });
971
- return /* @__PURE__ */ jsxs("div", { className: `zenspace3d ${className}`, style: { width, height: typeof height === "number" ? height : "100%", position: "relative", backgroundColor: "#000", borderRadius: 8, overflow: "hidden", cursor: "grab", ...style }, children: [
972
- /* @__PURE__ */ jsx("div", { ref: containerRef, style: { width: "100%", height: "100%" } }),
973
- isLoading && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "rgba(0,0,0,0.9)" }, children: /* @__PURE__ */ jsx("div", { style: { color: "#22d3ee", fontSize: 14 }, children: "Loading..." }) }),
974
- showTools && !isLoading && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", top: 12, left: 12, display: "flex", flexDirection: "column", gap: 4 }, children: [
975
- /* @__PURE__ */ jsx(LayerBtn, { icon: "satellite", label: "Satellites", active: layers.satellites, onClick: () => setLayers((l) => ({ ...l, satellites: !l.satellites })) }),
976
- /* @__PURE__ */ jsx(LayerBtn, { icon: "home", label: "Stations", active: layers.stations, onClick: () => setLayers((l) => ({ ...l, stations: !l.stations })) }),
977
- /* @__PURE__ */ jsx(LayerBtn, { icon: "antenna", label: "Ground", active: layers.groundStations, onClick: () => setLayers((l) => ({ ...l, groundStations: !l.groundStations })) }),
978
- /* @__PURE__ */ jsx(LayerBtn, { icon: "timeline", label: "Orbits", active: layers.orbits, onClick: () => setLayers((l) => ({ ...l, orbits: !l.orbits })) }),
979
- /* @__PURE__ */ jsx(LayerBtn, { icon: "signal-cellular-4-bar", label: "Coverage", active: layers.coverage, onClick: () => setLayers((l) => ({ ...l, coverage: !l.coverage })) })
980
- ] }),
981
- !isLoading && /* @__PURE__ */ jsx(
982
- ObjectLegend,
983
- {
984
- currentView,
985
- satellites,
986
- stations,
987
- groundStations,
988
- onObjectClick: (id, objectData) => {
989
- var _a;
990
- setSelection({ selectedId: id, selectedObject: objectData, hoveredId: null });
991
- (_a = callbacks == null ? void 0 : callbacks.onSelect) == null ? void 0 : _a.call(callbacks, objectData);
992
- flyTo(id, { animate: true });
993
- }
994
- }
995
- ),
996
- hoverTooltip.visible && hoverTooltip.object && /* @__PURE__ */ jsxs("div", { style: {
997
- position: "absolute",
998
- left: hoverTooltip.x + 15,
999
- top: hoverTooltip.y - 10,
1000
- backgroundColor: "rgba(20, 25, 35, 0.95)",
1001
- color: "#e4e4e7",
1002
- padding: "10px 14px",
1003
- borderRadius: 4,
1004
- fontSize: 12,
1005
- fontFamily: "system-ui, -apple-system, sans-serif",
1006
- boxShadow: "0 4px 20px rgba(0,0,0,0.5)",
1007
- pointerEvents: "none",
1008
- zIndex: 100,
1009
- minWidth: 140
1010
- }, children: [
1011
- /* @__PURE__ */ jsx("div", { style: { color: "#ffffff", fontWeight: 500, marginBottom: 4 }, children: hoverTooltip.object.name || hoverTooltip.object.targetName || "Unknown" }),
1012
- hoverTooltip.object.category && /* @__PURE__ */ jsx("div", { style: { color: "#9ca3af", fontSize: 11 }, children: hoverTooltip.object.category }),
1013
- hoverTooltip.object.overpassTime && /* @__PURE__ */ jsxs("div", { style: { color: "#9ca3af", fontSize: 11, marginTop: 4 }, children: [
1014
- "Overpass time: ",
1015
- hoverTooltip.object.overpassTime
1016
- ] })
1017
- ] }),
1018
- showInfoPanel && selection.selectedObject && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", bottom: 16, right: 12, backgroundColor: "rgba(0,0,0,0.9)", color: "#e4e4e7", padding: "14px 18px", borderRadius: 10, fontSize: 12, fontFamily: "ui-monospace, monospace", border: "1px solid rgba(34,211,238,0.3)", minWidth: 180 }, children: [
1019
- /* @__PURE__ */ jsx("div", { style: { color: "#22d3ee", fontWeight: 500, marginBottom: 8 }, children: selection.selectedObject.name }),
1020
- /* @__PURE__ */ jsx("div", { style: { color: "#b8bcc8", fontSize: 10 }, children: selection.selectedObject.category }),
1021
- /* @__PURE__ */ jsx("button", { onClick: () => setSelection({ selectedId: null, selectedObject: null, hoveredId: null }), style: { marginTop: 10, padding: "5px 10px", backgroundColor: "rgba(255,255,255,0.1)", border: "none", borderRadius: 4, color: "#a1a1aa", cursor: "pointer", fontSize: 10, width: "100%" }, children: "Close" })
1022
- ] }),
1023
- !isLoading && /* @__PURE__ */ jsx("div", { style: { position: "absolute", bottom: 12, left: 12, backgroundColor: "rgba(0,0,0,0.5)", color: "#b8bcc8", padding: "5px 10px", borderRadius: 6, fontSize: 10 }, children: "Drag to rotate • Scroll to zoom" })
1024
- ] });
1025
- }
1026
- );
1027
- function LayerBtn({ icon, label, active, onClick }) {
1028
- return /* @__PURE__ */ jsxs(
1029
- "button",
1030
- {
1031
- onClick,
1032
- "aria-label": `Toggle ${label} layer`,
1033
- "aria-pressed": active,
1034
- style: {
1035
- display: "flex",
1036
- alignItems: "center",
1037
- gap: 6,
1038
- padding: "6px 10px",
1039
- backgroundColor: active ? "rgba(34,211,238,0.15)" : "rgba(0,0,0,0.7)",
1040
- border: active ? "1px solid rgba(34,211,238,0.4)" : "1px solid rgba(255,255,255,0.1)",
1041
- borderRadius: 6,
1042
- cursor: "pointer",
1043
- fontSize: "0.6875rem",
1044
- // 11px in rem
1045
- color: active ? "#22d3ee" : "#b8bcc8",
1046
- // WCAG: improved contrast from #71717a
1047
- minWidth: 110
1048
- },
1049
- children: [
1050
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(AstroIcon, { name: icon, size: "extra-small", label: "" }) }),
1051
- /* @__PURE__ */ jsx("span", { children: label })
1052
- ]
1053
- }
1054
- );
1055
- }
1056
- function ObjectLegend({ currentView, satellites, stations, groundStations, onObjectClick }) {
1057
- const [hoveredSection, setHoveredSection] = React.useState(null);
1058
- const [pinnedSection, setPinnedSection] = React.useState(null);
1059
- const isExpanded = (section) => hoveredSection === section || pinnedSection === section;
1060
- const togglePin = (section) => {
1061
- setPinnedSection((prev) => prev === section ? null : section);
1062
- };
1063
- const sectionStyle = {
1064
- cursor: "pointer",
1065
- padding: "4px 0",
1066
- display: "flex",
1067
- alignItems: "center",
1068
- justifyContent: "space-between",
1069
- transition: "background-color 0.15s"
1070
- };
1071
- const itemStyle = {
1072
- padding: "3px 8px",
1073
- marginLeft: 12,
1074
- cursor: "pointer",
1075
- borderRadius: 4,
1076
- fontSize: 10,
1077
- color: "#a1a1aa",
1078
- transition: "all 0.15s"
1079
- };
1080
- return /* @__PURE__ */ jsxs("div", { style: {
1081
- position: "absolute",
1082
- top: 12,
1083
- right: 12,
1084
- backgroundColor: "rgba(0,0,0,0.85)",
1085
- color: "#e4e4e7",
1086
- padding: "10px 14px",
1087
- borderRadius: 8,
1088
- fontSize: 11,
1089
- fontFamily: "ui-monospace, monospace",
1090
- minWidth: 160,
1091
- maxHeight: 400,
1092
- overflowY: "auto"
1093
- }, children: [
1094
- /* @__PURE__ */ jsx("div", { style: { color: "#b8bcc8", fontSize: 10, marginBottom: 8 }, children: currentView === "solar-system" ? "Solar System" : "Earth Orbit" }),
1095
- satellites.length > 0 && /* @__PURE__ */ jsxs(
1096
- "div",
1097
- {
1098
- onMouseEnter: () => setHoveredSection("satellites"),
1099
- onMouseLeave: () => setHoveredSection(null),
1100
- children: [
1101
- /* @__PURE__ */ jsxs(
1102
- "div",
1103
- {
1104
- style: {
1105
- ...sectionStyle,
1106
- backgroundColor: isExpanded("satellites") ? "rgba(255,255,255,0.05)" : "transparent"
1107
- },
1108
- onClick: () => togglePin("satellites"),
1109
- children: [
1110
- /* @__PURE__ */ jsxs("span", { children: [
1111
- /* @__PURE__ */ jsx("span", { style: { color: "#00ff88" }, children: "●" }),
1112
- " ",
1113
- satellites.length,
1114
- " Satellites"
1115
- ] }),
1116
- /* @__PURE__ */ jsxs("span", { style: { color: pinnedSection === "satellites" ? "#00ff88" : "#666", fontSize: 9 }, children: [
1117
- isExpanded("satellites") ? "▼" : "▶",
1118
- pinnedSection === "satellites" ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AstroIcon, { name: "bookmark", size: "extra-small", label: "", style: { marginLeft: 4, verticalAlign: "middle" } }) }) : null
1119
- ] })
1120
- ]
1121
- }
1122
- ),
1123
- isExpanded("satellites") && /* @__PURE__ */ jsx("div", { style: { marginBottom: 4, paddingBottom: 4 }, children: satellites.map((sat) => /* @__PURE__ */ jsx(
1124
- "div",
1125
- {
1126
- style: itemStyle,
1127
- onClick: (e) => {
1128
- e.stopPropagation();
1129
- onObjectClick(sat.id, sat);
1130
- },
1131
- onMouseEnter: (e) => {
1132
- e.currentTarget.style.backgroundColor = "rgba(0,255,136,0.15)";
1133
- e.currentTarget.style.color = "#00ff88";
1134
- },
1135
- onMouseLeave: (e) => {
1136
- e.currentTarget.style.backgroundColor = "transparent";
1137
- e.currentTarget.style.color = "#a1a1aa";
1138
- },
1139
- children: sat.name || sat.id
1140
- },
1141
- sat.id
1142
- )) })
1143
- ]
1144
- }
1145
- ),
1146
- stations.length > 0 && /* @__PURE__ */ jsxs(
1147
- "div",
1148
- {
1149
- onMouseEnter: () => setHoveredSection("stations"),
1150
- onMouseLeave: () => setHoveredSection(null),
1151
- children: [
1152
- /* @__PURE__ */ jsxs(
1153
- "div",
1154
- {
1155
- style: {
1156
- ...sectionStyle,
1157
- backgroundColor: isExpanded("stations") ? "rgba(255,255,255,0.05)" : "transparent"
1158
- },
1159
- onClick: () => togglePin("stations"),
1160
- children: [
1161
- /* @__PURE__ */ jsxs("span", { children: [
1162
- /* @__PURE__ */ jsx("span", { style: { color: "#ffd700" }, children: "●" }),
1163
- " ",
1164
- stations.length,
1165
- " Stations"
1166
- ] }),
1167
- /* @__PURE__ */ jsxs("span", { style: { color: pinnedSection === "stations" ? "#ffd700" : "#666", fontSize: 9 }, children: [
1168
- isExpanded("stations") ? "▼" : "▶",
1169
- pinnedSection === "stations" ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AstroIcon, { name: "bookmark", size: "extra-small", label: "", style: { marginLeft: 4, verticalAlign: "middle" } }) }) : null
1170
- ] })
1171
- ]
1172
- }
1173
- ),
1174
- isExpanded("stations") && /* @__PURE__ */ jsx("div", { style: { marginBottom: 4, paddingBottom: 4 }, children: stations.map((st) => /* @__PURE__ */ jsx(
1175
- "div",
1176
- {
1177
- style: itemStyle,
1178
- onClick: (e) => {
1179
- e.stopPropagation();
1180
- onObjectClick(st.id, st);
1181
- },
1182
- onMouseEnter: (e) => {
1183
- e.currentTarget.style.backgroundColor = "rgba(255,215,0,0.15)";
1184
- e.currentTarget.style.color = "#ffd700";
1185
- },
1186
- onMouseLeave: (e) => {
1187
- e.currentTarget.style.backgroundColor = "transparent";
1188
- e.currentTarget.style.color = "#a1a1aa";
1189
- },
1190
- children: st.name || st.id
1191
- },
1192
- st.id
1193
- )) })
1194
- ]
1195
- }
1196
- ),
1197
- groundStations.length > 0 && /* @__PURE__ */ jsxs(
1198
- "div",
1199
- {
1200
- onMouseEnter: () => setHoveredSection("groundStations"),
1201
- onMouseLeave: () => setHoveredSection(null),
1202
- children: [
1203
- /* @__PURE__ */ jsxs(
1204
- "div",
1205
- {
1206
- style: {
1207
- ...sectionStyle,
1208
- backgroundColor: isExpanded("groundStations") ? "rgba(255,255,255,0.05)" : "transparent"
1209
- },
1210
- onClick: () => togglePin("groundStations"),
1211
- children: [
1212
- /* @__PURE__ */ jsxs("span", { children: [
1213
- /* @__PURE__ */ jsx("span", { style: { color: "#00ffff" }, children: "▲" }),
1214
- " ",
1215
- groundStations.length,
1216
- " Ground Stations"
1217
- ] }),
1218
- /* @__PURE__ */ jsxs("span", { style: { color: pinnedSection === "groundStations" ? "#00ffff" : "#666", fontSize: 9 }, children: [
1219
- isExpanded("groundStations") ? "▼" : "▶",
1220
- pinnedSection === "groundStations" ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AstroIcon, { name: "bookmark", size: "extra-small", label: "", style: { marginLeft: 4, verticalAlign: "middle" } }) }) : null
1221
- ] })
1222
- ]
1223
- }
1224
- ),
1225
- isExpanded("groundStations") && /* @__PURE__ */ jsx("div", { style: { marginBottom: 4, paddingBottom: 4 }, children: groundStations.map((gs) => /* @__PURE__ */ jsx(
1226
- "div",
1227
- {
1228
- style: itemStyle,
1229
- onClick: (e) => {
1230
- e.stopPropagation();
1231
- onObjectClick(gs.id, gs);
1232
- },
1233
- onMouseEnter: (e) => {
1234
- e.currentTarget.style.backgroundColor = "rgba(0,255,255,0.15)";
1235
- e.currentTarget.style.color = "#00ffff";
1236
- },
1237
- onMouseLeave: (e) => {
1238
- e.currentTarget.style.backgroundColor = "transparent";
1239
- e.currentTarget.style.color = "#a1a1aa";
1240
- },
1241
- children: gs.name || gs.id
1242
- },
1243
- gs.id
1244
- )) })
1245
- ]
1246
- }
1247
- )
1248
- ] });
1249
- }
1250
- export {
1251
- ZenSpace3D
1252
- };
1253
- //# sourceMappingURL=ZenSpace3D.js.map