@jjlmoya/utils-science 1.45.0 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -242,21 +242,184 @@
|
|
|
242
242
|
color: #f87171;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
.asteroid-observer-marker {
|
|
246
|
+
position: relative;
|
|
247
|
+
background: transparent;
|
|
248
|
+
border: 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.asteroid-observer-pulse {
|
|
252
|
+
position: absolute;
|
|
253
|
+
left: 50%;
|
|
254
|
+
bottom: 0.15rem;
|
|
255
|
+
width: 2.35rem;
|
|
256
|
+
height: 2.35rem;
|
|
257
|
+
border: 2px solid rgba(37, 99, 235, 0.42);
|
|
258
|
+
border-radius: 50%;
|
|
259
|
+
background: rgba(59, 130, 246, 0.14);
|
|
260
|
+
transform: translateX(-50%);
|
|
261
|
+
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.32);
|
|
262
|
+
animation: asteroid-observer-pulse 1.8s ease-out infinite;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.asteroid-observer-pin {
|
|
266
|
+
position: absolute;
|
|
267
|
+
left: 50%;
|
|
268
|
+
bottom: 0.62rem;
|
|
269
|
+
width: 1.75rem;
|
|
270
|
+
height: 2.05rem;
|
|
271
|
+
border: 2px solid #fff;
|
|
272
|
+
border-radius: 999px 999px 0.85rem 0.85rem;
|
|
273
|
+
background: #2563eb;
|
|
274
|
+
transform: translateX(-50%);
|
|
275
|
+
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.32);
|
|
276
|
+
display: flex;
|
|
277
|
+
flex-direction: column;
|
|
278
|
+
align-items: center;
|
|
279
|
+
justify-content: center;
|
|
280
|
+
gap: 0.12rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.asteroid-observer-head {
|
|
284
|
+
width: 0.48rem;
|
|
285
|
+
height: 0.48rem;
|
|
286
|
+
border-radius: 50%;
|
|
287
|
+
background: #fff;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.asteroid-observer-body {
|
|
291
|
+
width: 0.72rem;
|
|
292
|
+
height: 0.56rem;
|
|
293
|
+
border-radius: 999px 999px 0.25rem 0.25rem;
|
|
294
|
+
background: #fff;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.asteroid-observer-label {
|
|
298
|
+
position: absolute;
|
|
299
|
+
left: 50%;
|
|
300
|
+
bottom: 2.7rem;
|
|
301
|
+
padding: 0.18rem 0.42rem;
|
|
302
|
+
border-radius: var(--asteroid-radius-full);
|
|
303
|
+
background: rgba(15, 23, 42, 0.88);
|
|
304
|
+
color: #fff;
|
|
305
|
+
font-size: 0.55rem;
|
|
306
|
+
font-weight: 900;
|
|
307
|
+
letter-spacing: 0.05em;
|
|
308
|
+
transform: translateX(-50%);
|
|
309
|
+
box-shadow: 0 6px 16px rgba(15, 23, 42, 0.25);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@keyframes asteroid-observer-pulse {
|
|
313
|
+
70% {
|
|
314
|
+
box-shadow: 0 0 0 0.75rem rgba(59, 130, 246, 0);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
100% {
|
|
318
|
+
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
245
322
|
.asteroid-desktop-lab {
|
|
246
|
-
|
|
323
|
+
position: absolute;
|
|
324
|
+
left: 0.85rem;
|
|
325
|
+
right: 0.85rem;
|
|
326
|
+
bottom: 0;
|
|
327
|
+
z-index: 45;
|
|
328
|
+
display: flex;
|
|
329
|
+
max-height: min(82vh, 43rem);
|
|
330
|
+
padding: 0 0 0.9rem;
|
|
331
|
+
pointer-events: none;
|
|
332
|
+
transform: translateY(calc(100% - 4.85rem));
|
|
333
|
+
transition: transform var(--asteroid-transition-slow);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.asteroid-app.controls-open .asteroid-desktop-lab {
|
|
337
|
+
transform: translateY(0);
|
|
247
338
|
}
|
|
248
339
|
|
|
249
340
|
@media (min-width: 768px) {
|
|
250
341
|
.asteroid-desktop-lab {
|
|
251
|
-
display: flex;
|
|
252
|
-
position: absolute;
|
|
253
342
|
top: 1.5rem;
|
|
254
343
|
left: 1.5rem;
|
|
344
|
+
right: auto;
|
|
255
345
|
bottom: 1.5rem;
|
|
256
346
|
width: 20rem;
|
|
257
347
|
z-index: 30;
|
|
258
348
|
flex-direction: column;
|
|
349
|
+
max-height: none;
|
|
350
|
+
padding: 0;
|
|
259
351
|
pointer-events: none;
|
|
352
|
+
transform: none;
|
|
353
|
+
transition: none;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.asteroid-controls-toggle {
|
|
358
|
+
position: absolute;
|
|
359
|
+
left: 50%;
|
|
360
|
+
bottom: 2.15rem;
|
|
361
|
+
z-index: 50;
|
|
362
|
+
display: inline-flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: center;
|
|
365
|
+
gap: 0.5rem;
|
|
366
|
+
min-height: 2.35rem;
|
|
367
|
+
padding: 0 1.05rem;
|
|
368
|
+
border: 1px solid rgba(148, 163, 184, 0.35);
|
|
369
|
+
border-radius: var(--asteroid-radius-full);
|
|
370
|
+
background: rgba(248, 250, 252, 0.92);
|
|
371
|
+
color: #1e293b;
|
|
372
|
+
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.18);
|
|
373
|
+
backdrop-filter: blur(12px);
|
|
374
|
+
font-size: 0.7rem;
|
|
375
|
+
font-weight: 900;
|
|
376
|
+
text-transform: uppercase;
|
|
377
|
+
letter-spacing: 0.04em;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
pointer-events: auto;
|
|
380
|
+
transform: translateX(-50%);
|
|
381
|
+
transition: var(--asteroid-transition);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.asteroid-controls-toggle:active {
|
|
385
|
+
transform: translateX(-50%) scale(0.97);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.theme-dark .asteroid-controls-toggle {
|
|
389
|
+
border-color: rgba(148, 163, 184, 0.45);
|
|
390
|
+
background: rgba(15, 23, 42, 0.88);
|
|
391
|
+
color: #f8fafc;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.asteroid-app.controls-open .asteroid-controls-toggle {
|
|
395
|
+
opacity: 0;
|
|
396
|
+
pointer-events: none;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@media (min-width: 768px) {
|
|
400
|
+
.asteroid-controls-toggle {
|
|
401
|
+
display: none;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.asteroid-controls-backdrop {
|
|
406
|
+
position: absolute;
|
|
407
|
+
inset: 0;
|
|
408
|
+
z-index: 35;
|
|
409
|
+
background: rgba(15, 23, 42, 0.22);
|
|
410
|
+
opacity: 0;
|
|
411
|
+
pointer-events: none;
|
|
412
|
+
transition: opacity var(--asteroid-transition-slow);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.asteroid-app.controls-open .asteroid-controls-backdrop {
|
|
416
|
+
opacity: 1;
|
|
417
|
+
pointer-events: auto;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
@media (min-width: 768px) {
|
|
421
|
+
.asteroid-controls-backdrop {
|
|
422
|
+
display: none;
|
|
260
423
|
}
|
|
261
424
|
}
|
|
262
425
|
|
|
@@ -274,6 +437,15 @@
|
|
|
274
437
|
height: 100%;
|
|
275
438
|
overflow: hidden;
|
|
276
439
|
color: var(--asteroid-text-primary);
|
|
440
|
+
width: 100%;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
@media (max-width: 767px) {
|
|
444
|
+
.asteroid-lab-panel {
|
|
445
|
+
max-height: min(82vh, 43rem);
|
|
446
|
+
border-radius: 1.4rem;
|
|
447
|
+
padding: 0.85rem 1rem 1rem;
|
|
448
|
+
}
|
|
277
449
|
}
|
|
278
450
|
|
|
279
451
|
.asteroid-lab-header {
|
|
@@ -285,12 +457,90 @@
|
|
|
285
457
|
flex-shrink: 0;
|
|
286
458
|
}
|
|
287
459
|
|
|
460
|
+
@media (max-width: 767px) {
|
|
461
|
+
.asteroid-lab-header {
|
|
462
|
+
min-height: 3.1rem;
|
|
463
|
+
padding-bottom: 0.65rem;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.asteroid-controls-close {
|
|
468
|
+
width: 0;
|
|
469
|
+
height: 2.25rem;
|
|
470
|
+
border: 0;
|
|
471
|
+
border-radius: var(--asteroid-radius-sm);
|
|
472
|
+
background: var(--asteroid-bg-light);
|
|
473
|
+
color: var(--asteroid-text-secondary);
|
|
474
|
+
display: inline-flex;
|
|
475
|
+
align-items: center;
|
|
476
|
+
justify-content: center;
|
|
477
|
+
cursor: pointer;
|
|
478
|
+
opacity: 0;
|
|
479
|
+
overflow: hidden;
|
|
480
|
+
pointer-events: none;
|
|
481
|
+
transition: var(--asteroid-transition);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.asteroid-app.controls-open .asteroid-controls-close {
|
|
485
|
+
width: 2.25rem;
|
|
486
|
+
border: 1px solid var(--asteroid-border-light);
|
|
487
|
+
opacity: 1;
|
|
488
|
+
pointer-events: auto;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.asteroid-controls-close:hover {
|
|
492
|
+
color: var(--asteroid-text-primary);
|
|
493
|
+
border-color: var(--asteroid-primary);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
@media (min-width: 768px) {
|
|
497
|
+
.asteroid-controls-close {
|
|
498
|
+
display: none;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
288
502
|
.asteroid-lab-title {
|
|
289
503
|
display: flex;
|
|
290
504
|
align-items: center;
|
|
291
505
|
gap: 0.5rem;
|
|
292
506
|
}
|
|
293
507
|
|
|
508
|
+
.asteroid-lab-actions {
|
|
509
|
+
display: flex;
|
|
510
|
+
align-items: center;
|
|
511
|
+
gap: 0.55rem;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.asteroid-drop-center-btn {
|
|
515
|
+
display: inline-flex;
|
|
516
|
+
align-items: center;
|
|
517
|
+
justify-content: center;
|
|
518
|
+
gap: 0.45rem;
|
|
519
|
+
min-height: 2.35rem;
|
|
520
|
+
padding: 0 0.9rem;
|
|
521
|
+
border: 1px solid rgba(249, 115, 22, 0.5);
|
|
522
|
+
border-radius: var(--asteroid-radius-full);
|
|
523
|
+
background: linear-gradient(135deg, var(--asteroid-primary) 0%, var(--asteroid-secondary) 100%);
|
|
524
|
+
color: #fff;
|
|
525
|
+
box-shadow: 0 10px 24px rgba(220, 38, 38, 0.28);
|
|
526
|
+
font-size: 0.7rem;
|
|
527
|
+
font-weight: 900;
|
|
528
|
+
text-transform: uppercase;
|
|
529
|
+
letter-spacing: 0.04em;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
transition: var(--asteroid-transition);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.asteroid-drop-center-btn:active {
|
|
535
|
+
transform: scale(0.97);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
@media (min-width: 768px) {
|
|
539
|
+
.asteroid-drop-center-btn {
|
|
540
|
+
display: none;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
294
544
|
.asteroid-lab-icon {
|
|
295
545
|
width: 2rem;
|
|
296
546
|
height: 2rem;
|
|
@@ -807,4 +1057,4 @@
|
|
|
807
1057
|
width: 1px;
|
|
808
1058
|
background: var(--asteroid-border-light);
|
|
809
1059
|
margin: 0 0.5rem;
|
|
810
|
-
}
|
|
1060
|
+
}
|
|
@@ -38,8 +38,15 @@ const { ui } = Astro.props;
|
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
40
|
|
|
41
|
-
<
|
|
42
|
-
<
|
|
41
|
+
<button id="asteroid-controls-toggle" class="asteroid-controls-toggle" type="button" aria-controls="asteroid-lab-panel" aria-expanded="false">
|
|
42
|
+
<Icon name="mdi:tune-variant" class="w-5 h-5" />
|
|
43
|
+
<span>Controls</span>
|
|
44
|
+
</button>
|
|
45
|
+
|
|
46
|
+
<div id="asteroid-controls-backdrop" class="asteroid-controls-backdrop"></div>
|
|
47
|
+
|
|
48
|
+
<div class="asteroid-desktop-lab" id="asteroid-controls-shell">
|
|
49
|
+
<div class="asteroid-lab-panel" id="asteroid-lab-panel">
|
|
43
50
|
<div class="asteroid-lab-header">
|
|
44
51
|
<div class="asteroid-lab-title">
|
|
45
52
|
<div class="asteroid-lab-icon">
|
|
@@ -52,6 +59,12 @@ const { ui } = Astro.props;
|
|
|
52
59
|
<span class="asteroid-lab-subtitle">Factory</span>
|
|
53
60
|
</div>
|
|
54
61
|
</div>
|
|
62
|
+
<div class="asteroid-lab-actions">
|
|
63
|
+
<button id="asteroid-drop-center-btn" class="asteroid-drop-center-btn" type="button"><Icon name="mdi:meteor" class="w-5 h-5" /><span>Impact</span></button>
|
|
64
|
+
<button id="asteroid-controls-close" class="asteroid-controls-close" type="button" aria-label="Close controls">
|
|
65
|
+
<Icon name="mdi:close" class="w-5 h-5" />
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
55
68
|
</div>
|
|
56
69
|
|
|
57
70
|
<div class="asteroid-lab-content">
|
|
@@ -146,26 +159,10 @@ const { ui } = Astro.props;
|
|
|
146
159
|
</div>
|
|
147
160
|
|
|
148
161
|
<div style="display: none;">
|
|
149
|
-
<div id="icon-safe">
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
</div>
|
|
154
|
-
<div id="icon-shock">
|
|
155
|
-
<svg class="w-6 h-6" style="color: #60a5fa;" fill="currentColor" viewBox="0 0 24 24">
|
|
156
|
-
<path d="M3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12A9,9 0 0,0 12,3A9,9 0 0,0 3,12M5,12A7,7 0 0,1 12,5A7,7 0 0,1 19,12A7,7 0 0,1 12,19A7,7 0 0,1 5,12M11,17H13V15H11V17M11,13H13V7H11V13Z"></path>
|
|
157
|
-
</svg>
|
|
158
|
-
</div>
|
|
159
|
-
<div id="icon-burn">
|
|
160
|
-
<svg class="w-6 h-6" style="color: #fb923c;" fill="currentColor" viewBox="0 0 24 24">
|
|
161
|
-
<path d="M17.66,11.2C17.43,10.9 17.15,10.64 16.89,10.38C16.22,9.78 15.46,9.35 14.82,8.72C13.33,7.26 13,4.85 13.95,3C13,3.23 12.17,3.75 11.46,4.32C8.87,6.4 7.85,10.07 9.07,13.22C9.11,13.32 9.15,13.42 9.15,13.55C9.15,13.77 9,13.97 8.8,14.05C8.57,14.15 8.33,14.09 8.14,13.93C8.08,13.88 8.04,13.83 8,13.76C6.87,12.33 6.69,10.28 7.45,8.64C5.78,10 4.87,12.3 5,14.47C5.06,14.97 5.12,15.47 5.29,15.97C5.43,16.57 5.7,17.17 6,17.7C7.08,19.43 8.95,20.67 10.96,20.92C13.1,21.19 15.39,20.8 17.03,19.32C18.86,17.66 19.5,15 18.56,12.72L18.43,12.46C18.22,12 17.66,11.2 17.66,11.2Z"></path>
|
|
162
|
-
</svg>
|
|
163
|
-
</div>
|
|
164
|
-
<div id="icon-death">
|
|
165
|
-
<svg class="w-6 h-6" style="color: #f87171;" fill="currentColor" viewBox="0 0 24 24">
|
|
166
|
-
<path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"></path>
|
|
167
|
-
</svg>
|
|
168
|
-
</div>
|
|
162
|
+
<div id="icon-safe"><svg class="w-6 h-6" style="color: #34d399;" fill="currentColor" viewBox="0 0 24 24"><path d="M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1M10,17L6,13L7.41,11.59L10,14.17L16.59,7.58L18,9L10,17Z"></path></svg></div>
|
|
163
|
+
<div id="icon-shock"><svg class="w-6 h-6" style="color: #60a5fa;" fill="currentColor" viewBox="0 0 24 24"><path d="M3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12A9,9 0 0,0 12,3A9,9 0 0,0 3,12M5,12A7,7 0 0,1 12,5A7,7 0 0,1 19,12A7,7 0 0,1 12,19A7,7 0 0,1 5,12M11,17H13V15H11V17M11,13H13V7H11V13Z"></path></svg></div>
|
|
164
|
+
<div id="icon-burn"><svg class="w-6 h-6" style="color: #fb923c;" fill="currentColor" viewBox="0 0 24 24"><path d="M17.66,11.2C17.43,10.9 17.15,10.64 16.89,10.38C16.22,9.78 15.46,9.35 14.82,8.72C13.33,7.26 13,4.85 13.95,3C13,3.23 12.17,3.75 11.46,4.32C8.87,6.4 7.85,10.07 9.07,13.22C9.11,13.32 9.15,13.42 9.15,13.55C9.15,13.77 9,13.97 8.8,14.05C8.57,14.15 8.33,14.09 8.14,13.93C8.08,13.88 8.04,13.83 8,13.76C6.87,12.33 6.69,10.28 7.45,8.64C5.78,10 4.87,12.3 5,14.47C5.06,14.97 5.12,15.47 5.29,15.97C5.43,16.57 5.7,17.17 6,17.7C7.08,19.43 8.95,20.67 10.96,20.92C13.1,21.19 15.39,20.8 17.03,19.32C18.86,17.66 19.5,15 18.56,12.72L18.43,12.46C18.22,12 17.66,11.2 17.66,11.2Z"></path></svg></div>
|
|
165
|
+
<div id="icon-death"><svg class="w-6 h-6" style="color: #f87171;" fill="currentColor" viewBox="0 0 24 24"><path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"></path></svg></div>
|
|
169
166
|
</div>
|
|
170
167
|
|
|
171
168
|
<script>
|
|
@@ -177,25 +174,13 @@ const { ui } = Astro.props;
|
|
|
177
174
|
|
|
178
175
|
let map: L.Map;
|
|
179
176
|
|
|
180
|
-
interface ImpactData {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
shockwave1psi: number;
|
|
184
|
-
}
|
|
177
|
+
interface ImpactData { craterDiameter: number; thermalRadius: number; shockwave1psi: number; }
|
|
178
|
+
|
|
179
|
+
const impacts: { id: string; circles: L.LayerGroup; marker: L.Marker; data: ImpactData; latlng: L.LatLng; config: typeof config; }[] = [];
|
|
185
180
|
|
|
186
|
-
|
|
187
|
-
id: string;
|
|
188
|
-
circles: L.LayerGroup;
|
|
189
|
-
marker: L.Marker;
|
|
190
|
-
data: ImpactData;
|
|
191
|
-
config: typeof config;
|
|
192
|
-
}[] = [];
|
|
181
|
+
let observerLatLng: L.LatLng, observerMarker: L.Marker;
|
|
193
182
|
|
|
194
|
-
const config = {
|
|
195
|
-
diameter: 10000,
|
|
196
|
-
velocity: 20,
|
|
197
|
-
composition: "rock" as Composition,
|
|
198
|
-
};
|
|
183
|
+
const config = { diameter: 10000, velocity: 20, composition: "rock" as Composition };
|
|
199
184
|
|
|
200
185
|
function init() {
|
|
201
186
|
map = L.map("asteroid-game-map", {
|
|
@@ -210,15 +195,33 @@ const { ui } = Astro.props;
|
|
|
210
195
|
attribution: "© OpenStreetMap © CARTO",
|
|
211
196
|
}).addTo(map);
|
|
212
197
|
|
|
198
|
+
setObserver(map.getCenter());
|
|
213
199
|
setupControls();
|
|
214
200
|
setupGhostDrag();
|
|
215
201
|
setupGPS();
|
|
202
|
+
setupMobileControls();
|
|
216
203
|
|
|
217
204
|
map.on("click", () => {
|
|
218
205
|
map.dragging.enable();
|
|
219
206
|
});
|
|
220
207
|
}
|
|
221
208
|
|
|
209
|
+
function setControlsOpen(open: boolean) {
|
|
210
|
+
document.getElementById("asteroid-app")?.classList.toggle("controls-open", open);
|
|
211
|
+
document.getElementById("asteroid-controls-toggle")?.setAttribute("aria-expanded", open ? "true" : "false");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function setupMobileControls() {
|
|
215
|
+
const toggle = document.getElementById("asteroid-controls-toggle");
|
|
216
|
+
const close = document.getElementById("asteroid-controls-close");
|
|
217
|
+
const backdrop = document.getElementById("asteroid-controls-backdrop"), dropCenterBtn = document.getElementById("asteroid-drop-center-btn");
|
|
218
|
+
|
|
219
|
+
toggle?.addEventListener("click", () => setControlsOpen(!document.getElementById("asteroid-app")?.classList.contains("controls-open")));
|
|
220
|
+
const closeControls = () => setControlsOpen(false);
|
|
221
|
+
close?.addEventListener("click", closeControls); backdrop?.addEventListener("click", closeControls);
|
|
222
|
+
dropCenterBtn?.addEventListener("click", () => { spawnImpact(map.getCenter()); closeControls(); });
|
|
223
|
+
}
|
|
224
|
+
|
|
222
225
|
function setupControls() {
|
|
223
226
|
const sizeInput = document.getElementById("input-size") as HTMLInputElement;
|
|
224
227
|
const velInput = document.getElementById("input-velocity") as HTMLInputElement;
|
|
@@ -308,6 +311,15 @@ const { ui } = Astro.props;
|
|
|
308
311
|
});
|
|
309
312
|
}
|
|
310
313
|
|
|
314
|
+
function createObserverIcon(): L.DivIcon {
|
|
315
|
+
return L.divIcon({ className: "asteroid-observer-marker", iconSize: [42, 52], iconAnchor: [21, 48], html: '<div class="asteroid-observer-pulse"></div><div class="asteroid-observer-pin"><div class="asteroid-observer-head"></div><div class="asteroid-observer-body"></div></div><div class="asteroid-observer-label">YOU</div>' });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function setObserver(latlng: L.LatLng) {
|
|
319
|
+
observerLatLng = latlng; if (observerMarker) observerMarker.setLatLng(latlng);
|
|
320
|
+
else observerMarker = L.marker(latlng, { icon: createObserverIcon(), zIndexOffset: 1000 }).addTo(map); updateVerdict();
|
|
321
|
+
}
|
|
322
|
+
|
|
311
323
|
function setupMarkerDragHandlers(marker: L.Marker, group: L.LayerGroup, physics: ImpactData) {
|
|
312
324
|
marker.on("dragstart", () => {
|
|
313
325
|
map.dragging.disable();
|
|
@@ -315,6 +327,7 @@ const { ui } = Astro.props;
|
|
|
315
327
|
|
|
316
328
|
marker.on("drag", (e: L.LeafletEvent) => {
|
|
317
329
|
const newPos = (e.target as L.Marker).getLatLng();
|
|
330
|
+
const impact = impacts.find((item) => item.marker === marker); if (impact) impact.latlng = newPos;
|
|
318
331
|
group.clearLayers();
|
|
319
332
|
renderCircles(newPos, physics, group);
|
|
320
333
|
updateVerdict();
|
|
@@ -323,6 +336,7 @@ const { ui } = Astro.props;
|
|
|
323
336
|
marker.on("dragend", (e: L.LeafletEvent) => {
|
|
324
337
|
map.dragging.enable();
|
|
325
338
|
const newPos = (e.target as L.Marker).getLatLng();
|
|
339
|
+
const impact = impacts.find((item) => item.marker === marker); if (impact) impact.latlng = newPos;
|
|
326
340
|
group.clearLayers();
|
|
327
341
|
renderCircles(newPos, physics, group);
|
|
328
342
|
updateVerdict();
|
|
@@ -347,7 +361,7 @@ const { ui } = Astro.props;
|
|
|
347
361
|
icon: createMarkerIcon(markerColor),
|
|
348
362
|
}).addTo(map);
|
|
349
363
|
|
|
350
|
-
impacts.push({ id: Date.now().toString(), circles: group, marker, data: physics, config });
|
|
364
|
+
impacts.push({ id: Date.now().toString(), circles: group, marker, data: physics, latlng, config });
|
|
351
365
|
setupMarkerDragHandlers(marker, group, physics);
|
|
352
366
|
updateVerdict();
|
|
353
367
|
}
|
|
@@ -379,9 +393,7 @@ const { ui } = Astro.props;
|
|
|
379
393
|
}).addTo(group);
|
|
380
394
|
}
|
|
381
395
|
|
|
382
|
-
function renderCircles(latlng: L.LatLng, physics: ImpactData, group: L.LayerGroup) {
|
|
383
|
-
addCircleLayers(latlng, physics, group);
|
|
384
|
-
}
|
|
396
|
+
function renderCircles(latlng: L.LatLng, physics: ImpactData, group: L.LayerGroup) { addCircleLayers(latlng, physics, group); }
|
|
385
397
|
|
|
386
398
|
function setupGPS() {
|
|
387
399
|
const btn = document.getElementById("asteroid-gps-btn");
|
|
@@ -392,6 +404,7 @@ const { ui } = Astro.props;
|
|
|
392
404
|
navigator.geolocation.getCurrentPosition(
|
|
393
405
|
(pos) => {
|
|
394
406
|
const ll = new L.LatLng(pos.coords.latitude, pos.coords.longitude);
|
|
407
|
+
setObserver(ll);
|
|
395
408
|
if (gpsText) gpsText.textContent = "GPS Activo";
|
|
396
409
|
map.flyTo(ll, 9);
|
|
397
410
|
},
|
|
@@ -421,13 +434,20 @@ const { ui } = Astro.props;
|
|
|
421
434
|
|
|
422
435
|
function updateVerdict() {
|
|
423
436
|
const pill = document.getElementById("asteroid-verdict-pill");
|
|
424
|
-
if (impacts.length === 0) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
437
|
+
if (impacts.length === 0) { pill?.classList.remove("active"); return; }
|
|
438
|
+
pill?.classList.add("active"); updateVerdictUI(getWorstVerdict());
|
|
439
|
+
}
|
|
428
440
|
|
|
429
|
-
|
|
430
|
-
|
|
441
|
+
function getWorstVerdict() {
|
|
442
|
+
const observer = observerLatLng;
|
|
443
|
+
const severity = { safe: 0, shook: 1, burned: 2, vaporized: 3 };
|
|
444
|
+
return impacts.reduce<keyof typeof severity>((worst, impact) => {
|
|
445
|
+
const distance = map.distance(observer, impact.latlng); let verdict: keyof typeof severity = "safe";
|
|
446
|
+
if (distance < impact.data.shockwave1psi) verdict = "shook";
|
|
447
|
+
if (distance < impact.data.thermalRadius) verdict = "burned";
|
|
448
|
+
if (distance < impact.data.craterDiameter / 2) verdict = "vaporized";
|
|
449
|
+
return severity[verdict] > severity[worst] ? verdict : worst;
|
|
450
|
+
}, "safe");
|
|
431
451
|
}
|
|
432
452
|
|
|
433
453
|
function clearAll() {
|