@zendir/ui 0.1.8 → 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 +1 -1
  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,836 +0,0 @@
1
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useRef, useEffect, useState, useCallback } from "react";
3
- import { AstroIcon } from "../core/AstroIcon.js";
4
- import { GroundTrackMap } from "../charts/GroundTrackMap.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 { useTheme } from "../theme/ThemeProvider.js";
11
- function checkWebGLCapabilities() {
12
- try {
13
- const canvas = document.createElement("canvas");
14
- const gl2 = canvas.getContext("webgl2");
15
- if (gl2) {
16
- return {
17
- available: true,
18
- version: 2,
19
- maxTextureSize: gl2.getParameter(gl2.MAX_TEXTURE_SIZE)
20
- };
21
- }
22
- const gl = canvas.getContext("webgl");
23
- if (gl) {
24
- return {
25
- available: true,
26
- version: 1,
27
- maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE)
28
- };
29
- }
30
- } catch {
31
- }
32
- return { available: false, version: 0, maxTextureSize: 0 };
33
- }
34
- const EARTH_RADIUS = 3e3;
35
- const EARTH_RADIUS_KM = 6371;
36
- const KM_TO_SCENE = EARTH_RADIUS / EARTH_RADIUS_KM;
37
- const DEG_TO_RAD = Math.PI / 180;
38
- function latLonAltToCartesian(lat, lon, altKm = 0) {
39
- const r = EARTH_RADIUS + altKm * KM_TO_SCENE;
40
- const phi = (90 - lat) * DEG_TO_RAD;
41
- const theta = (lon + 180) * DEG_TO_RAD;
42
- const x = -r * Math.sin(phi) * Math.cos(theta);
43
- const y = r * Math.cos(phi);
44
- const z = r * Math.sin(phi) * Math.sin(theta);
45
- return [x, y, z];
46
- }
47
- function EarthViewer({
48
- spacecraft = [],
49
- groundStations = [],
50
- groundTrack = [],
51
- selectedSpacecraftId,
52
- width = "100%",
53
- height = 400,
54
- autoRotate = true,
55
- showAtmosphere = true,
56
- showOrbits = true,
57
- showVisibilityCones = false,
58
- onSpacecraftClick,
59
- onGroundStationClick,
60
- className = "",
61
- focusTarget,
62
- transitionDuration = 2e3,
63
- autoOrbit = false
64
- }) {
65
- const { tokens: _tokens } = useTheme();
66
- const containerRef = useRef(null);
67
- const rendererRef = useRef(null);
68
- const sceneRef = useRef(null);
69
- const cameraRef = useRef(null);
70
- const earthRef = useRef(null);
71
- const controlsRef = useRef(null);
72
- const raycasterRef = useRef(null);
73
- const mouseRef = useRef(null);
74
- const animationRef = useRef(null);
75
- const starsMaterialRef = useRef(null);
76
- const timeRef = useRef(0);
77
- const isUserInteractingRef = useRef(false);
78
- const focusTransitionRef = useRef(null);
79
- const orbitAngleRef = useRef(0);
80
- const lastFocusTargetRef = useRef(null);
81
- const isAnimatingRef = useRef(false);
82
- const autoRotateRef = useRef(autoRotate);
83
- const autoOrbitRef = useRef(autoOrbit);
84
- const focusTargetRef = useRef(focusTarget);
85
- useEffect(() => {
86
- autoRotateRef.current = autoRotate;
87
- }, [autoRotate]);
88
- useEffect(() => {
89
- autoOrbitRef.current = autoOrbit;
90
- }, [autoOrbit]);
91
- useEffect(() => {
92
- focusTargetRef.current = focusTarget;
93
- }, [focusTarget]);
94
- const [webglCapabilities] = useState(() => checkWebGLCapabilities());
95
- const [isLoading, setIsLoading] = useState(true);
96
- const [error, setError] = useState(null);
97
- const [threeLoaded, setThreeLoaded] = useState(false);
98
- const [THREE, setTHREE] = useState(null);
99
- const [OrbitControlsClass, setOrbitControlsClass] = useState(null);
100
- const [hoveredObject, setHoveredObject] = useState(null);
101
- useEffect(() => {
102
- if (!webglCapabilities.available) {
103
- setIsLoading(false);
104
- return;
105
- }
106
- loadThree().then(({ THREE: threeModule, OrbitControls: controls }) => {
107
- setTHREE(threeModule);
108
- setOrbitControlsClass(() => controls);
109
- setThreeLoaded(true);
110
- }).catch((err) => {
111
- setError("Failed to load 3D library");
112
- setIsLoading(false);
113
- });
114
- }, [webglCapabilities.available]);
115
- const initScene = useCallback(() => {
116
- if (!THREE || !containerRef.current || !OrbitControlsClass) return;
117
- const container = containerRef.current;
118
- const rect = container.getBoundingClientRect();
119
- const w = rect.width;
120
- const h = height;
121
- const scene = new THREE.Scene();
122
- sceneRef.current = scene;
123
- const camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 1e5);
124
- camera.position.set(0, 0, 1e4);
125
- cameraRef.current = camera;
126
- const renderer = new THREE.WebGLRenderer({
127
- antialias: true,
128
- alpha: true,
129
- powerPreference: "high-performance",
130
- preserveDrawingBuffer: true
131
- // Prevents flickering from buffer swaps
132
- });
133
- renderer.setSize(w, h);
134
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
135
- container.appendChild(renderer.domElement);
136
- rendererRef.current = renderer;
137
- const controls = new OrbitControlsClass(camera, renderer.domElement);
138
- controls.enableDamping = true;
139
- controls.dampingFactor = 0.05;
140
- controls.minDistance = 4500;
141
- controls.maxDistance = 2e4;
142
- controls.enablePan = false;
143
- let interactionTimer;
144
- const handleControlStart = () => {
145
- isUserInteractingRef.current = true;
146
- };
147
- const handleControlEnd = () => {
148
- clearTimeout(interactionTimer);
149
- interactionTimer = setTimeout(() => {
150
- isUserInteractingRef.current = false;
151
- }, 3e3);
152
- };
153
- controls.addEventListener("start", handleControlStart);
154
- controls.addEventListener("end", handleControlEnd);
155
- controlsRef.current = controls;
156
- raycasterRef.current = new THREE.Raycaster();
157
- mouseRef.current = new THREE.Vector2();
158
- const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 64, 32);
159
- const earthMaterial = new THREE.MeshPhongMaterial({
160
- color: 2245802,
161
- shininess: 10
162
- });
163
- const textureLoader = new THREE.TextureLoader();
164
- textureLoader.load(
165
- "/world.topo.jpg",
166
- (texture) => {
167
- texture.wrapS = THREE.RepeatWrapping;
168
- texture.wrapT = THREE.ClampToEdgeWrapping;
169
- texture.minFilter = THREE.LinearFilter;
170
- texture.magFilter = THREE.LinearFilter;
171
- texture.flipY = true;
172
- earthMaterial.map = texture;
173
- earthMaterial.needsUpdate = true;
174
- setIsLoading(false);
175
- },
176
- void 0,
177
- () => {
178
- setIsLoading(false);
179
- }
180
- );
181
- const earth = new THREE.Mesh(earthGeometry, earthMaterial);
182
- earth.rotation.y = -Math.PI / 2 + Math.PI;
183
- scene.add(earth);
184
- earthRef.current = earth;
185
- if (showAtmosphere) {
186
- const atmosphereGeometry = new THREE.SphereGeometry(EARTH_RADIUS * 1.25, 24, 12);
187
- const atmosphereMaterial = new THREE.ShaderMaterial({
188
- vertexShader: atmosphereVertexShader,
189
- fragmentShader: atmosphereFragmentShader,
190
- blending: THREE.AdditiveBlending,
191
- side: THREE.BackSide,
192
- transparent: true,
193
- depthWrite: false
194
- });
195
- const atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);
196
- atmosphere.rotation.y = -Math.PI / 2;
197
- scene.add(atmosphere);
198
- }
199
- const starsCount = 5e3;
200
- const starsGeometry = new THREE.BufferGeometry();
201
- const starsPositions = new Float32Array(starsCount * 3);
202
- const starsOpacities = new Float32Array(starsCount);
203
- for (let i = 0; i < starsCount * 3; i += 3) {
204
- const radius = 5e4 + Math.random() * 5e4;
205
- const theta = Math.random() * Math.PI * 2;
206
- const phi = Math.acos(2 * Math.random() - 1);
207
- starsPositions[i] = radius * Math.sin(phi) * Math.cos(theta);
208
- starsPositions[i + 1] = radius * Math.sin(phi) * Math.sin(theta);
209
- starsPositions[i + 2] = radius * Math.cos(phi);
210
- starsOpacities[i / 3] = Math.random();
211
- }
212
- starsGeometry.setAttribute("position", new THREE.BufferAttribute(starsPositions, 3));
213
- starsGeometry.setAttribute("opacity", new THREE.BufferAttribute(starsOpacities, 1));
214
- const starsMaterial = new THREE.ShaderMaterial({
215
- uniforms: { time: { value: 0 } },
216
- vertexShader: starsVertexShader,
217
- fragmentShader: starsFragmentShader,
218
- transparent: true,
219
- depthWrite: false,
220
- blending: THREE.AdditiveBlending
221
- });
222
- starsMaterialRef.current = starsMaterial;
223
- const stars = new THREE.Points(starsGeometry, starsMaterial);
224
- scene.add(stars);
225
- const ambientLight = new THREE.AmbientLight(4210752, 0.6);
226
- scene.add(ambientLight);
227
- const sunLight = new THREE.DirectionalLight(16777215, 1.2);
228
- sunLight.position.set(1e4, 5e3, 1e4);
229
- scene.add(sunLight);
230
- return () => {
231
- clearTimeout(interactionTimer);
232
- if (animationRef.current) {
233
- cancelAnimationFrame(animationRef.current);
234
- }
235
- controls.removeEventListener("start", handleControlStart);
236
- controls.removeEventListener("end", handleControlEnd);
237
- controls.dispose();
238
- renderer.dispose();
239
- if (container.contains(renderer.domElement)) {
240
- container.removeChild(renderer.domElement);
241
- }
242
- };
243
- }, [THREE, OrbitControlsClass, height, showAtmosphere]);
244
- const updateSceneObjects = useCallback(() => {
245
- if (!THREE || !sceneRef.current) return;
246
- const scene = sceneRef.current;
247
- const toRemove = scene.children.filter(
248
- (obj) => {
249
- var _a, _b, _c, _d;
250
- return ((_a = obj.userData) == null ? void 0 : _a.isSpacecraft) || ((_b = obj.userData) == null ? void 0 : _b.isGroundStation) || ((_c = obj.userData) == null ? void 0 : _c.isOrbit) || ((_d = obj.userData) == null ? void 0 : _d.isVisibilityCone);
251
- }
252
- );
253
- toRemove.forEach((obj) => {
254
- if (obj.geometry) obj.geometry.dispose();
255
- if (obj.material) obj.material.dispose();
256
- scene.remove(obj);
257
- });
258
- spacecraft.forEach((sc) => {
259
- const [x, y, z] = latLonAltToCartesian(sc.latitude, sc.longitude, sc.altitude);
260
- const isSelected = sc.id === selectedSpacecraftId;
261
- const geometry = new THREE.SphereGeometry(isSelected ? 80 : 55, 12, 12);
262
- const material = new THREE.MeshBasicMaterial({
263
- color: isSelected ? 16766720 : 65416
264
- });
265
- const marker = new THREE.Mesh(geometry, material);
266
- marker.position.set(x, y, z);
267
- marker.userData = {
268
- isSpacecraft: true,
269
- spacecraftId: sc.id,
270
- data: sc
271
- };
272
- scene.add(marker);
273
- const glowGeometry = new THREE.RingGeometry(isSelected ? 100 : 80, isSelected ? 130 : 100, 16);
274
- const glowMaterial = new THREE.MeshBasicMaterial({
275
- color: isSelected ? 16766720 : 65416,
276
- transparent: true,
277
- opacity: 0.4,
278
- side: THREE.DoubleSide
279
- });
280
- const glow = new THREE.Mesh(glowGeometry, glowMaterial);
281
- glow.position.set(x, y, z);
282
- glow.lookAt(0, 0, 0);
283
- glow.userData = { isSpacecraft: true, isGlow: true };
284
- scene.add(glow);
285
- const lineGeometry = new THREE.BufferGeometry().setFromPoints([
286
- new THREE.Vector3(0, 0, 0),
287
- new THREE.Vector3(x, y, z)
288
- ]);
289
- const lineMaterial = new THREE.LineBasicMaterial({
290
- color: isSelected ? 16766720 : 65416,
291
- transparent: true,
292
- opacity: 0.12
293
- });
294
- const line = new THREE.Line(lineGeometry, lineMaterial);
295
- line.userData = { isSpacecraft: true, isLine: true };
296
- scene.add(line);
297
- });
298
- groundStations.forEach((gs) => {
299
- const [x, y, z] = latLonAltToCartesian(gs.latitude, gs.longitude, 0);
300
- const geometry = new THREE.ConeGeometry(45, 90, 4);
301
- const material = new THREE.MeshBasicMaterial({ color: 65535 });
302
- const marker = new THREE.Mesh(geometry, material);
303
- marker.position.set(x, y, z);
304
- marker.lookAt(0, 0, 0);
305
- marker.rotateX(Math.PI / 2);
306
- marker.userData = {
307
- isGroundStation: true,
308
- stationId: gs.id,
309
- data: gs
310
- };
311
- scene.add(marker);
312
- const ringGeometry = new THREE.RingGeometry(55, 80, 16);
313
- const ringMaterial = new THREE.MeshBasicMaterial({
314
- color: 65535,
315
- transparent: true,
316
- opacity: 0.5,
317
- side: THREE.DoubleSide
318
- });
319
- const ring = new THREE.Mesh(ringGeometry, ringMaterial);
320
- ring.position.set(x, y, z);
321
- ring.lookAt(0, 0, 0);
322
- ring.userData = { isGroundStation: true, isRing: true };
323
- scene.add(ring);
324
- if (showVisibilityCones) {
325
- const coneHeight = 1200;
326
- const coneRadius = 600;
327
- const coneGeometry = new THREE.ConeGeometry(coneRadius, coneHeight, 16, 1, true);
328
- const coneMaterial = new THREE.MeshBasicMaterial({
329
- color: 65535,
330
- transparent: true,
331
- opacity: 0.06,
332
- side: THREE.DoubleSide
333
- });
334
- const cone = new THREE.Mesh(coneGeometry, coneMaterial);
335
- cone.position.set(x, y, z);
336
- cone.lookAt(0, 0, 0);
337
- cone.rotateX(-Math.PI / 2);
338
- cone.translateY(coneHeight / 2);
339
- cone.userData = { isVisibilityCone: true };
340
- scene.add(cone);
341
- }
342
- });
343
- if (showOrbits && groundTrack.length > 1) {
344
- const points = [];
345
- groundTrack.forEach((pt, i) => {
346
- const alt = pt.alt || (spacecraft.length > 0 ? spacecraft[0].altitude : 400);
347
- const [x, y, z] = latLonAltToCartesian(pt.latitude, pt.longitude, alt);
348
- if (i > 0) {
349
- const prevPt = groundTrack[i - 1];
350
- const lonDiff = Math.abs(pt.longitude - prevPt.longitude);
351
- if (lonDiff > 180) {
352
- if (points.length > 1) {
353
- const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
354
- const lineMaterial = new THREE.LineBasicMaterial({
355
- color: 2282478,
356
- transparent: true,
357
- opacity: 0.7
358
- });
359
- const line = new THREE.Line(lineGeometry, lineMaterial);
360
- line.userData = { isOrbit: true };
361
- scene.add(line);
362
- }
363
- points.length = 0;
364
- }
365
- }
366
- points.push(new THREE.Vector3(x, y, z));
367
- });
368
- if (points.length > 1) {
369
- const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
370
- const lineMaterial = new THREE.LineBasicMaterial({
371
- color: 2282478,
372
- transparent: true,
373
- opacity: 0.7
374
- });
375
- const line = new THREE.Line(lineGeometry, lineMaterial);
376
- line.userData = { isOrbit: true };
377
- scene.add(line);
378
- }
379
- }
380
- }, [THREE, spacecraft, groundStations, groundTrack, selectedSpacecraftId, showOrbits, showVisibilityCones]);
381
- const handleMouseMove = useCallback((event) => {
382
- if (!containerRef.current || !raycasterRef.current || !cameraRef.current || !sceneRef.current) return;
383
- const rect = containerRef.current.getBoundingClientRect();
384
- const x = (event.clientX - rect.left) / rect.width * 2 - 1;
385
- const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
386
- mouseRef.current.set(x, y);
387
- raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);
388
- const interactables = sceneRef.current.children.filter(
389
- (obj) => {
390
- var _a, _b, _c, _d, _e;
391
- return ((_a = obj.userData) == null ? void 0 : _a.isSpacecraft) && !((_b = obj.userData) == null ? void 0 : _b.isGlow) && !((_c = obj.userData) == null ? void 0 : _c.isLine) || ((_d = obj.userData) == null ? void 0 : _d.isGroundStation) && !((_e = obj.userData) == null ? void 0 : _e.isRing);
392
- }
393
- );
394
- const intersects = raycasterRef.current.intersectObjects(interactables);
395
- if (intersects.length > 0) {
396
- const hit = intersects[0].object;
397
- const userData = hit.userData;
398
- if (userData.isSpacecraft) {
399
- const sc = userData.data;
400
- setHoveredObject({
401
- type: "spacecraft",
402
- id: userData.spacecraftId,
403
- name: (sc == null ? void 0 : sc.name) || userData.spacecraftId,
404
- data: sc,
405
- screenX: event.clientX - rect.left,
406
- screenY: event.clientY - rect.top
407
- });
408
- } else if (userData.isGroundStation) {
409
- const gs = userData.data;
410
- setHoveredObject({
411
- type: "groundStation",
412
- id: userData.stationId,
413
- name: (gs == null ? void 0 : gs.name) || userData.stationId,
414
- data: gs,
415
- screenX: event.clientX - rect.left,
416
- screenY: event.clientY - rect.top
417
- });
418
- }
419
- if (containerRef.current) {
420
- containerRef.current.style.cursor = "pointer";
421
- }
422
- } else {
423
- setHoveredObject(null);
424
- if (containerRef.current) {
425
- containerRef.current.style.cursor = "grab";
426
- }
427
- }
428
- }, []);
429
- const handleClick = useCallback((event) => {
430
- if (!containerRef.current || !raycasterRef.current || !cameraRef.current || !sceneRef.current) return;
431
- const rect = containerRef.current.getBoundingClientRect();
432
- const x = (event.clientX - rect.left) / rect.width * 2 - 1;
433
- const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
434
- mouseRef.current.set(x, y);
435
- raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);
436
- const interactables = sceneRef.current.children.filter(
437
- (obj) => {
438
- var _a, _b, _c, _d, _e;
439
- return ((_a = obj.userData) == null ? void 0 : _a.isSpacecraft) && !((_b = obj.userData) == null ? void 0 : _b.isGlow) && !((_c = obj.userData) == null ? void 0 : _c.isLine) || ((_d = obj.userData) == null ? void 0 : _d.isGroundStation) && !((_e = obj.userData) == null ? void 0 : _e.isRing);
440
- }
441
- );
442
- const intersects = raycasterRef.current.intersectObjects(interactables);
443
- if (intersects.length > 0) {
444
- const hit = intersects[0].object;
445
- const userData = hit.userData;
446
- if (userData.isSpacecraft && onSpacecraftClick) {
447
- onSpacecraftClick(userData.data);
448
- } else if (userData.isGroundStation && onGroundStationClick) {
449
- onGroundStationClick(userData.data);
450
- }
451
- }
452
- }, [onSpacecraftClick, onGroundStationClick]);
453
- useEffect(() => {
454
- if (!THREE || !cameraRef.current || !controlsRef.current || !focusTarget) return;
455
- const targetKey = focusTarget.id || focusTarget.name || JSON.stringify(focusTarget);
456
- const lastKey = lastFocusTargetRef.current ? lastFocusTargetRef.current.id || lastFocusTargetRef.current.name || JSON.stringify(lastFocusTargetRef.current) : null;
457
- if (targetKey === lastKey) return;
458
- lastFocusTargetRef.current = focusTarget;
459
- const camera = cameraRef.current;
460
- const controls = controlsRef.current;
461
- let targetLat = 0;
462
- let targetLon = 0;
463
- let targetZoom = 2;
464
- if (focusTarget.latitude !== void 0 && focusTarget.longitude !== void 0) {
465
- targetLat = focusTarget.latitude;
466
- targetLon = focusTarget.longitude;
467
- targetZoom = focusTarget.zoom ?? 2;
468
- } else if (focusTarget.type === "spacecraft" && focusTarget.id) {
469
- const sc = spacecraft.find((s) => s.id === focusTarget.id);
470
- if (sc) {
471
- targetLat = sc.latitude;
472
- targetLon = sc.longitude;
473
- targetZoom = 2.5;
474
- }
475
- } else if (focusTarget.type === "groundStation" && focusTarget.id) {
476
- const gs = groundStations.find((g) => g.id === focusTarget.id);
477
- if (gs) {
478
- targetLat = gs.latitude;
479
- targetLon = gs.longitude;
480
- targetZoom = 2;
481
- }
482
- } else if (focusTarget.type === "overview") {
483
- targetLat = 0;
484
- targetLon = 0;
485
- targetZoom = 1;
486
- }
487
- const [targetX, targetY, targetZ] = latLonAltToCartesian(targetLat, targetLon, 0);
488
- const baseDistance = 1e4;
489
- const zoomFactor = 1 / targetZoom;
490
- const cameraDistance = baseDistance * zoomFactor;
491
- const offset = new THREE.Vector3(targetX, targetY, targetZ).normalize().multiplyScalar(cameraDistance);
492
- const targetPos = [
493
- targetX + offset.x,
494
- targetY + offset.y,
495
- targetZ + offset.z
496
- ];
497
- const startPos = [
498
- camera.position.x,
499
- camera.position.y,
500
- camera.position.z
501
- ];
502
- const startTarget = [
503
- controls.target.x,
504
- controls.target.y,
505
- controls.target.z
506
- ];
507
- const targetTarget = [targetX, targetY, targetZ];
508
- focusTransitionRef.current = {
509
- startTime: Date.now(),
510
- startPos,
511
- targetPos,
512
- startTarget,
513
- targetTarget,
514
- duration: transitionDuration,
515
- isActive: true
516
- };
517
- orbitAngleRef.current = 0;
518
- }, [THREE, focusTarget, transitionDuration, spacecraft, groundStations]);
519
- const animate = useCallback(() => {
520
- var _a;
521
- if (!isAnimatingRef.current) return;
522
- if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;
523
- const camera = cameraRef.current;
524
- const controls = controlsRef.current;
525
- if (focusTransitionRef.current && focusTransitionRef.current.isActive) {
526
- const transition = focusTransitionRef.current;
527
- const elapsed = Date.now() - transition.startTime;
528
- const progress = Math.min(elapsed / transition.duration, 1);
529
- const easeProgress = 1 - Math.pow(1 - progress, 3);
530
- camera.position.x = transition.startPos[0] + (transition.targetPos[0] - transition.startPos[0]) * easeProgress;
531
- camera.position.y = transition.startPos[1] + (transition.targetPos[1] - transition.startPos[1]) * easeProgress;
532
- camera.position.z = transition.startPos[2] + (transition.targetPos[2] - transition.startPos[2]) * easeProgress;
533
- controls.target.x = transition.startTarget[0] + (transition.targetTarget[0] - transition.startTarget[0]) * easeProgress;
534
- controls.target.y = transition.startTarget[1] + (transition.targetTarget[1] - transition.startTarget[1]) * easeProgress;
535
- controls.target.z = transition.startTarget[2] + (transition.targetTarget[2] - transition.startTarget[2]) * easeProgress;
536
- if (progress >= 1) {
537
- transition.isActive = false;
538
- }
539
- }
540
- const currentFocusTarget = focusTargetRef.current;
541
- const currentAutoOrbit = autoOrbitRef.current;
542
- const currentAutoRotate = autoRotateRef.current;
543
- if (currentAutoOrbit && currentFocusTarget && !((_a = focusTransitionRef.current) == null ? void 0 : _a.isActive) && !isUserInteractingRef.current) {
544
- orbitAngleRef.current += 5e-3;
545
- if (currentFocusTarget.latitude !== void 0 && currentFocusTarget.longitude !== void 0) {
546
- const [targetX, targetY, targetZ] = latLonAltToCartesian(
547
- currentFocusTarget.latitude,
548
- currentFocusTarget.longitude,
549
- 0
550
- );
551
- const baseDistance = 1e4 / (currentFocusTarget.zoom ?? 2);
552
- const orbitRadius = baseDistance * 0.3;
553
- camera.position.x = targetX + Math.cos(orbitAngleRef.current) * orbitRadius;
554
- camera.position.y = targetY + Math.sin(orbitAngleRef.current * 0.5) * orbitRadius * 0.5;
555
- camera.position.z = targetZ + Math.sin(orbitAngleRef.current) * orbitRadius;
556
- controls.target.set(targetX, targetY, targetZ);
557
- }
558
- }
559
- if (controls) {
560
- controls.update();
561
- }
562
- timeRef.current += 0.01;
563
- if (starsMaterialRef.current) {
564
- starsMaterialRef.current.uniforms.time.value = timeRef.current;
565
- }
566
- if (currentAutoRotate && earthRef.current && !isUserInteractingRef.current && !currentAutoOrbit) {
567
- earthRef.current.rotation.y += 6e-4;
568
- }
569
- rendererRef.current.render(sceneRef.current, camera);
570
- animationRef.current = requestAnimationFrame(animate);
571
- }, []);
572
- useEffect(() => {
573
- if (!threeLoaded || !webglCapabilities.available || !OrbitControlsClass) return;
574
- isAnimatingRef.current = false;
575
- if (animationRef.current) {
576
- cancelAnimationFrame(animationRef.current);
577
- animationRef.current = null;
578
- }
579
- const cleanup = initScene();
580
- isAnimatingRef.current = true;
581
- animate();
582
- return () => {
583
- isAnimatingRef.current = false;
584
- if (animationRef.current) {
585
- cancelAnimationFrame(animationRef.current);
586
- animationRef.current = null;
587
- }
588
- cleanup == null ? void 0 : cleanup();
589
- };
590
- }, [threeLoaded, webglCapabilities.available, OrbitControlsClass, initScene, animate]);
591
- useEffect(() => {
592
- if (threeLoaded) {
593
- updateSceneObjects();
594
- }
595
- }, [threeLoaded, updateSceneObjects]);
596
- useEffect(() => {
597
- const container = containerRef.current;
598
- if (!container || !threeLoaded) return;
599
- container.addEventListener("mousemove", handleMouseMove);
600
- container.addEventListener("click", handleClick);
601
- return () => {
602
- container.removeEventListener("mousemove", handleMouseMove);
603
- container.removeEventListener("click", handleClick);
604
- };
605
- }, [threeLoaded, handleMouseMove, handleClick]);
606
- useEffect(() => {
607
- const handleResize = () => {
608
- if (!containerRef.current || !rendererRef.current || !cameraRef.current) return;
609
- const rect = containerRef.current.getBoundingClientRect();
610
- cameraRef.current.aspect = rect.width / height;
611
- cameraRef.current.updateProjectionMatrix();
612
- rendererRef.current.setSize(rect.width, height);
613
- };
614
- window.addEventListener("resize", handleResize);
615
- return () => window.removeEventListener("resize", handleResize);
616
- }, [height]);
617
- if (!webglCapabilities.available || error) {
618
- return /* @__PURE__ */ jsxs("div", { className: `zendir-earth-viewer fallback ${className}`, style: { position: "relative" }, children: [
619
- /* @__PURE__ */ jsx(
620
- GroundTrackMap,
621
- {
622
- groundTrack,
623
- groundStations,
624
- width,
625
- height
626
- }
627
- ),
628
- /* @__PURE__ */ jsx("div", { style: {
629
- position: "absolute",
630
- bottom: 8,
631
- right: 8,
632
- backgroundColor: "rgba(0,0,0,0.7)",
633
- color: "#d1d5db",
634
- padding: "4px 8px",
635
- borderRadius: 4,
636
- fontSize: 10
637
- }, children: error || "2D Mode (WebGL unavailable)" })
638
- ] });
639
- }
640
- return /* @__PURE__ */ jsxs(
641
- "div",
642
- {
643
- className: `zendir-earth-viewer ${className}`,
644
- style: {
645
- width,
646
- height,
647
- position: "relative",
648
- backgroundColor: "#030508",
649
- borderRadius: 8,
650
- overflow: "hidden",
651
- cursor: "grab"
652
- },
653
- children: [
654
- /* @__PURE__ */ jsx(
655
- "div",
656
- {
657
- ref: containerRef,
658
- style: { width: "100%", height: "100%" }
659
- }
660
- ),
661
- isLoading && /* @__PURE__ */ jsx("div", { style: {
662
- position: "absolute",
663
- top: "50%",
664
- left: "50%",
665
- transform: "translate(-50%, -50%)",
666
- color: "#22d3ee",
667
- fontSize: 14,
668
- fontFamily: "ui-monospace, monospace"
669
- }, children: "Loading 3D Earth..." }),
670
- !isLoading && /* @__PURE__ */ jsxs("div", { style: {
671
- position: "absolute",
672
- top: 8,
673
- left: 8,
674
- backgroundColor: "rgba(0,0,0,0.75)",
675
- color: "#e4e4e7",
676
- padding: "10px 14px",
677
- borderRadius: 8,
678
- fontSize: 11,
679
- fontFamily: "ui-monospace, monospace",
680
- display: "flex",
681
- flexDirection: "column",
682
- gap: 5,
683
- backdropFilter: "blur(4px)"
684
- }, children: [
685
- spacecraft.length > 0 && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
686
- /* @__PURE__ */ jsx("span", { style: { color: "#00ff88", fontSize: 14 }, children: "●" }),
687
- /* @__PURE__ */ jsxs("span", { children: [
688
- spacecraft.length,
689
- " spacecraft"
690
- ] })
691
- ] }),
692
- groundStations.length > 0 && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
693
- /* @__PURE__ */ jsx("span", { style: { color: "#00ffff", fontSize: 12 }, children: "▲" }),
694
- /* @__PURE__ */ jsxs("span", { children: [
695
- groundStations.length,
696
- " ground stations"
697
- ] })
698
- ] })
699
- ] }),
700
- !isLoading && /* @__PURE__ */ jsx("div", { style: {
701
- position: "absolute",
702
- bottom: 8,
703
- right: 8,
704
- backgroundColor: "rgba(0,0,0,0.6)",
705
- color: "#b8bcc8",
706
- padding: "5px 10px",
707
- borderRadius: 6,
708
- fontSize: 9,
709
- fontFamily: "system-ui"
710
- }, children: "Drag to rotate • Scroll to zoom" }),
711
- hoveredObject && /* @__PURE__ */ jsxs(
712
- "div",
713
- {
714
- style: {
715
- position: "absolute",
716
- left: Math.min(hoveredObject.screenX + 15, (typeof width === "number" ? width : 400) - 200),
717
- top: Math.max(hoveredObject.screenY - 10, 10),
718
- backgroundColor: "rgba(9, 9, 11, 0.95)",
719
- color: "#e4e4e7",
720
- padding: "12px 16px",
721
- borderRadius: 10,
722
- fontSize: 11,
723
- fontFamily: "ui-monospace, monospace",
724
- border: hoveredObject.type === "spacecraft" ? "1px solid #22c55e" : "1px solid #22d3ee",
725
- boxShadow: "0 12px 40px rgba(0,0,0,0.6)",
726
- pointerEvents: "none",
727
- zIndex: 100,
728
- minWidth: 180,
729
- backdropFilter: "blur(8px)"
730
- },
731
- children: [
732
- hoveredObject.type === "spacecraft" ? /* @__PURE__ */ jsxs(Fragment, { children: [
733
- /* @__PURE__ */ jsxs("div", { style: { fontWeight: 500, marginBottom: 8, color: "#22c55e", fontSize: "0.8125rem", display: "flex", alignItems: "center", gap: 6 }, children: [
734
- /* @__PURE__ */ jsx(AstroIcon, { name: "satellite", size: "small", label: "" }),
735
- " ",
736
- hoveredObject.name
737
- ] }),
738
- hoveredObject.data && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 4 }, children: [
739
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
740
- /* @__PURE__ */ jsx("span", { style: {
741
- color: "#b8bcc8"
742
- /* WCAG: improved contrast */
743
- }, children: "Latitude" }),
744
- /* @__PURE__ */ jsxs("span", { children: [
745
- hoveredObject.data.latitude.toFixed(3),
746
- "°"
747
- ] })
748
- ] }),
749
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
750
- /* @__PURE__ */ jsx("span", { style: {
751
- color: "#b8bcc8"
752
- /* WCAG: improved contrast */
753
- }, children: "Longitude" }),
754
- /* @__PURE__ */ jsxs("span", { children: [
755
- hoveredObject.data.longitude.toFixed(3),
756
- "°"
757
- ] })
758
- ] }),
759
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
760
- /* @__PURE__ */ jsx("span", { style: {
761
- color: "#b8bcc8"
762
- /* WCAG: improved contrast */
763
- }, children: "Altitude" }),
764
- /* @__PURE__ */ jsxs("span", { style: { color: "#22d3ee" }, children: [
765
- hoveredObject.data.altitude.toFixed(1),
766
- " km"
767
- ] })
768
- ] }),
769
- hoveredObject.data.velocity && /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
770
- /* @__PURE__ */ jsx("span", { style: {
771
- color: "#b8bcc8"
772
- /* WCAG: improved contrast */
773
- }, children: "Velocity" }),
774
- /* @__PURE__ */ jsxs("span", { children: [
775
- (hoveredObject.data.velocity || 0).toFixed(3),
776
- " km/s"
777
- ] })
778
- ] })
779
- ] })
780
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
781
- /* @__PURE__ */ jsxs("div", { style: { fontWeight: 500, marginBottom: 8, color: "#22d3ee", fontSize: "0.8125rem", display: "flex", alignItems: "center", gap: 6 }, children: [
782
- /* @__PURE__ */ jsx(AstroIcon, { name: "antenna", size: "small", label: "" }),
783
- " ",
784
- hoveredObject.name
785
- ] }),
786
- hoveredObject.data && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 4 }, children: [
787
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
788
- /* @__PURE__ */ jsx("span", { style: {
789
- color: "#b8bcc8"
790
- /* WCAG: improved contrast */
791
- }, children: "Latitude" }),
792
- /* @__PURE__ */ jsxs("span", { children: [
793
- hoveredObject.data.latitude.toFixed(3),
794
- "°"
795
- ] })
796
- ] }),
797
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
798
- /* @__PURE__ */ jsx("span", { style: {
799
- color: "#b8bcc8"
800
- /* WCAG: improved contrast */
801
- }, children: "Longitude" }),
802
- /* @__PURE__ */ jsxs("span", { children: [
803
- hoveredObject.data.longitude.toFixed(3),
804
- "°"
805
- ] })
806
- ] }),
807
- hoveredObject.data.minElevation !== void 0 && /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
808
- /* @__PURE__ */ jsx("span", { style: {
809
- color: "#b8bcc8"
810
- /* WCAG: improved contrast */
811
- }, children: "Min Elevation" }),
812
- /* @__PURE__ */ jsxs("span", { children: [
813
- hoveredObject.data.minElevation,
814
- "°"
815
- ] })
816
- ] })
817
- ] })
818
- ] }),
819
- /* @__PURE__ */ jsx("div", { style: {
820
- marginTop: 8,
821
- paddingTop: 8,
822
- borderTop: "1px solid rgba(113,113,122,0.3)",
823
- color: "#9ca3af",
824
- fontSize: 9
825
- }, children: "Click for details" })
826
- ]
827
- }
828
- )
829
- ]
830
- }
831
- );
832
- }
833
- export {
834
- EarthViewer
835
- };
836
- //# sourceMappingURL=EarthViewer.js.map