spacemap 0.1.3 → 0.1.5
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/README.md +173 -5
- package/dist/spacemap.iife.js +78 -58
- package/dist/spacemap.js +78 -58
- package/index.d.ts +209 -3
- package/index.js +2013 -108
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -37,7 +37,17 @@ var DEFAULT_HOST = {
|
|
|
37
37
|
layer_nomenclature: () => "Named features",
|
|
38
38
|
cooperative_wheel: () => "Use ctrl + scroll to zoom the map",
|
|
39
39
|
cooperative_wheel_mac: () => "Use ⌘ + scroll to zoom the map",
|
|
40
|
-
cooperative_touch: () => "Use two fingers to move the map"
|
|
40
|
+
cooperative_touch: () => "Use two fingers to move the map",
|
|
41
|
+
planetary_system_primary: () => "primary",
|
|
42
|
+
planetary_system_retrograde: () => "retrograde",
|
|
43
|
+
planetary_system_axis_unit: () => "primary radii · log",
|
|
44
|
+
tab_rings: () => "Rings",
|
|
45
|
+
system_map_solar_system: () => "Solar System",
|
|
46
|
+
system_map_moons: ({ count }) => count === 1 ? "1 moon" : `${count} moons`,
|
|
47
|
+
system_map_zone_inner: () => "Inner asteroids & comets",
|
|
48
|
+
system_map_zone_outer: () => "Outer asteroids & comets",
|
|
49
|
+
unit_symbol_astronomical_unit: () => "AU",
|
|
50
|
+
system_map_axis_au: () => "AU · log"
|
|
41
51
|
},
|
|
42
52
|
bodyHref: () => "",
|
|
43
53
|
textures: "open"
|
|
@@ -6213,6 +6223,9 @@ var BodyIndex = class {
|
|
|
6213
6223
|
set orientationVersion(value) {
|
|
6214
6224
|
set(this.#orientationVersion, value, true);
|
|
6215
6225
|
}
|
|
6226
|
+
orientationRead = /* @__PURE__ */ new Set();
|
|
6227
|
+
hostRead = /* @__PURE__ */ new Map();
|
|
6228
|
+
hostReadVersion = 0;
|
|
6216
6229
|
childrenByParent = /* @__PURE__ */ new Map();
|
|
6217
6230
|
moonMaxAByParent = /* @__PURE__ */ new Map();
|
|
6218
6231
|
addListeners = /* @__PURE__ */ new Set();
|
|
@@ -6776,6 +6789,7 @@ var VisibilityController = class {
|
|
|
6776
6789
|
scaledSystem = SYSTEM_DISTANCE_RATIO_THRESHOLDS;
|
|
6777
6790
|
fullMoonIds = /* @__PURE__ */ new Set();
|
|
6778
6791
|
moonVisibilityCache = /* @__PURE__ */ new Map();
|
|
6792
|
+
pinned = /* @__PURE__ */ new Set();
|
|
6779
6793
|
constructor(bodies, getProbeStore = () => null, getEarthSatGroupFilter = () => null, getSmallBodyFilter = () => null, getLayers = () => ALL_LAYERS) {
|
|
6780
6794
|
this.bodies = bodies;
|
|
6781
6795
|
this.getProbeStore = getProbeStore;
|
|
@@ -6872,8 +6886,19 @@ var VisibilityController = class {
|
|
|
6872
6886
|
this.moonVisibilityCache.set(moon.data.id, vis);
|
|
6873
6887
|
return vis;
|
|
6874
6888
|
}
|
|
6889
|
+
setPinned(ids) {
|
|
6890
|
+
this.pinned = new Set(ids);
|
|
6891
|
+
}
|
|
6892
|
+
isPinned(id) {
|
|
6893
|
+
return this.pinned.has(id);
|
|
6894
|
+
}
|
|
6875
6895
|
getPlanetVisibility(body, camDistThreeJS) {
|
|
6876
|
-
|
|
6896
|
+
const pinned = this.pinned.has(body.data.id);
|
|
6897
|
+
const vis = this.ratioVisibility(body, camDistThreeJS, pinned);
|
|
6898
|
+
return pinned && vis !== VISIBILITY.CLOSE ? VISIBILITY.FULL : vis;
|
|
6899
|
+
}
|
|
6900
|
+
ratioVisibility(body, camDistThreeJS, pinned) {
|
|
6901
|
+
if (!pinned && this.getLayers().hidesBody(body.data.objectType, body.data.parentId)) return VISIBILITY.HIDE;
|
|
6877
6902
|
if (body.data.orbitalSource === OrbitalSource.SPICE_PROBE) return this.getProbeVisibility(body);
|
|
6878
6903
|
const earthFilter = this.getEarthSatGroupFilter();
|
|
6879
6904
|
if (earthFilter && body.data.parentId === "naif-399" && !earthFilter.has(body.data.id)) return VISIBILITY.HIDE;
|
|
@@ -6926,7 +6951,7 @@ var VisibilityController = class {
|
|
|
6926
6951
|
return sysNaif !== null && this.isInFocusedSystem(`naif-${sysNaif}`);
|
|
6927
6952
|
}
|
|
6928
6953
|
hasFullRendering(body) {
|
|
6929
|
-
if (!this.activeSystemId) return true;
|
|
6954
|
+
if (!this.activeSystemId || this.pinned.has(body.data.id)) return true;
|
|
6930
6955
|
if (this.isInActiveSystem(isTopLevelParent(body.data.parentId) ? body.data.id : body.data.parentId)) return true;
|
|
6931
6956
|
if (body.data.orbitalSource !== OrbitalSource.SPICE_PROBE) return false;
|
|
6932
6957
|
const ps = this.getProbeStore();
|
|
@@ -10311,20 +10336,35 @@ async function loadTierBitmaps(id, tier, priority) {
|
|
|
10311
10336
|
}));
|
|
10312
10337
|
}
|
|
10313
10338
|
var tierPrefetch = /* @__PURE__ */ new Map();
|
|
10339
|
+
var tierClaims = /* @__PURE__ */ new Map();
|
|
10314
10340
|
function tierKey(id, tier) {
|
|
10315
10341
|
return `${id}:${tier}`;
|
|
10316
10342
|
}
|
|
10317
|
-
function tierBitmaps(id, tier, priority) {
|
|
10343
|
+
function tierBitmaps(id, tier, priority, claim = true) {
|
|
10318
10344
|
const key = tierKey(id, tier);
|
|
10319
10345
|
let p = tierPrefetch.get(key);
|
|
10320
10346
|
if (!p) {
|
|
10321
|
-
|
|
10347
|
+
const loading = loadTierBitmaps(id, tier, priority);
|
|
10348
|
+
p = loading;
|
|
10322
10349
|
tierPrefetch.set(key, p);
|
|
10350
|
+
loading.catch(() => {
|
|
10351
|
+
if (tierPrefetch.get(key) !== loading) return;
|
|
10352
|
+
tierPrefetch.delete(key);
|
|
10353
|
+
tierClaims.delete(key);
|
|
10354
|
+
});
|
|
10323
10355
|
}
|
|
10356
|
+
if (claim) tierClaims.set(key, (tierClaims.get(key) ?? 0) + 1);
|
|
10324
10357
|
return p;
|
|
10325
10358
|
}
|
|
10326
10359
|
function releaseTierBitmaps(id, tier, bitmaps) {
|
|
10327
|
-
|
|
10360
|
+
const key = tierKey(id, tier);
|
|
10361
|
+
const left = (tierClaims.get(key) ?? 1) - 1;
|
|
10362
|
+
if (left > 0) {
|
|
10363
|
+
tierClaims.set(key, left);
|
|
10364
|
+
return;
|
|
10365
|
+
}
|
|
10366
|
+
tierClaims.delete(key);
|
|
10367
|
+
tierPrefetch.delete(key);
|
|
10328
10368
|
for (const b of bitmaps) b.close();
|
|
10329
10369
|
}
|
|
10330
10370
|
function makeCube(bitmaps) {
|
|
@@ -10339,10 +10379,10 @@ function pickLowTier(meta) {
|
|
|
10339
10379
|
}
|
|
10340
10380
|
function prefetchSkyboxTiers(meta) {
|
|
10341
10381
|
const low = pickLowTier(meta);
|
|
10342
|
-
if (low) tierBitmaps(meta.id, low).catch(() =>
|
|
10382
|
+
if (low) tierBitmaps(meta.id, low, void 0, false).catch(() => {});
|
|
10343
10383
|
}
|
|
10344
10384
|
var FULL_TIER_GATE_TIMEOUT_MS = 12e3;
|
|
10345
|
-
async function loadFromMeta(scene, renderer, meta) {
|
|
10385
|
+
async function loadFromMeta(scene, renderer, meta, disposed) {
|
|
10346
10386
|
const tier = pickTier$1(meta, Math.min(renderer.capabilities.maxTextureSize, currentRenderTier().maxSkyboxFace));
|
|
10347
10387
|
if (!tier) {
|
|
10348
10388
|
console.warn(`Skybox ${meta.id}: no tiers declared`);
|
|
@@ -10355,7 +10395,7 @@ async function loadFromMeta(scene, renderer, meta) {
|
|
|
10355
10395
|
};
|
|
10356
10396
|
let fullInstalled = false;
|
|
10357
10397
|
if (lowTier && lowTier !== tier) tierBitmaps(meta.id, lowTier).then((bitmaps) => {
|
|
10358
|
-
if (fullInstalled) {
|
|
10398
|
+
if (fullInstalled || disposed()) {
|
|
10359
10399
|
releaseTierBitmaps(meta.id, lowTier, bitmaps);
|
|
10360
10400
|
return;
|
|
10361
10401
|
}
|
|
@@ -10363,11 +10403,38 @@ async function loadFromMeta(scene, renderer, meta) {
|
|
|
10363
10403
|
lowRef.bitmaps = bitmaps;
|
|
10364
10404
|
scene.background = lowRef.cube;
|
|
10365
10405
|
performance.mark("sm-skybox-low");
|
|
10406
|
+
if (fullFailed) keepLow();
|
|
10366
10407
|
}).catch(() => {});
|
|
10408
|
+
let fullFailed = false;
|
|
10409
|
+
const keepLow = () => {
|
|
10410
|
+
if (!lowTier || !lowRef.cube || !lowRef.bitmaps) return;
|
|
10411
|
+
renderer.initTexture(lowRef.cube);
|
|
10412
|
+
releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10413
|
+
lowRef.bitmaps = null;
|
|
10414
|
+
};
|
|
10367
10415
|
if (tier !== lowTier) await Promise.race([eagerMinorsDone, new Promise((resolve) => setTimeout(resolve, FULL_TIER_GATE_TIMEOUT_MS))]);
|
|
10368
|
-
|
|
10369
|
-
|
|
10416
|
+
let fullBitmaps;
|
|
10417
|
+
try {
|
|
10418
|
+
fullBitmaps = await tierBitmaps(meta.id, tier, tier === lowTier ? void 0 : "low");
|
|
10419
|
+
} catch (err) {
|
|
10420
|
+
fullFailed = true;
|
|
10421
|
+
if (!disposed()) keepLow();
|
|
10422
|
+
else if (lowTier && lowRef.bitmaps) {
|
|
10423
|
+
lowRef.cube?.dispose();
|
|
10424
|
+
releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10425
|
+
lowRef.bitmaps = null;
|
|
10426
|
+
}
|
|
10427
|
+
throw err;
|
|
10428
|
+
}
|
|
10370
10429
|
await new Promise((resolve) => "requestIdleCallback" in window ? requestIdleCallback(() => resolve(), { timeout: 2e3 }) : setTimeout(resolve, 500));
|
|
10430
|
+
if (disposed()) {
|
|
10431
|
+
fullInstalled = true;
|
|
10432
|
+
lowRef.cube?.dispose();
|
|
10433
|
+
releaseTierBitmaps(meta.id, tier, fullBitmaps);
|
|
10434
|
+
if (lowTier && lowTier !== tier && lowRef.bitmaps) releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10435
|
+
return;
|
|
10436
|
+
}
|
|
10437
|
+
const full = makeCube(fullBitmaps);
|
|
10371
10438
|
renderer.initTexture(full);
|
|
10372
10439
|
fullInstalled = true;
|
|
10373
10440
|
const prevBackground = scene.background;
|
|
@@ -10378,7 +10445,7 @@ async function loadFromMeta(scene, renderer, meta) {
|
|
|
10378
10445
|
releaseTierBitmaps(meta.id, tier, fullBitmaps);
|
|
10379
10446
|
if (lowTier && lowTier !== tier && lowRef.bitmaps) releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10380
10447
|
}
|
|
10381
|
-
async function loadSkybox(scene, renderer, ctx) {
|
|
10448
|
+
async function loadSkybox(scene, renderer, ctx, disposed = () => false) {
|
|
10382
10449
|
if (ctx?.layers.skipped.has("stars")) return;
|
|
10383
10450
|
try {
|
|
10384
10451
|
const meta = await fetchMetadata();
|
|
@@ -10390,7 +10457,7 @@ async function loadSkybox(scene, renderer, ctx) {
|
|
|
10390
10457
|
attribution: meta.skybox.attribution,
|
|
10391
10458
|
description: meta.skybox.description
|
|
10392
10459
|
};
|
|
10393
|
-
await loadFromMeta(scene, renderer, meta.skybox);
|
|
10460
|
+
await loadFromMeta(scene, renderer, meta.skybox, disposed);
|
|
10394
10461
|
} catch (err) {
|
|
10395
10462
|
console.warn("Failed to load skybox:", err);
|
|
10396
10463
|
}
|
|
@@ -12231,8 +12298,9 @@ function makeTrailMaterial(color, brightness = TRAIL_DIM) {
|
|
|
12231
12298
|
`
|
|
12232
12299
|
});
|
|
12233
12300
|
}
|
|
12234
|
-
function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
|
|
12301
|
+
function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM, dash) {
|
|
12235
12302
|
return new ShaderMaterial({
|
|
12303
|
+
defines: dash ? { DASHED: "" } : {},
|
|
12236
12304
|
transparent: true,
|
|
12237
12305
|
depthWrite: false,
|
|
12238
12306
|
side: DoubleSide,
|
|
@@ -12243,7 +12311,8 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
|
|
|
12243
12311
|
uAlphaMin: { value: 0 },
|
|
12244
12312
|
uShowFull: { value: 0 },
|
|
12245
12313
|
uLineWidth: { value: lineWidth },
|
|
12246
|
-
uResolution: { value: TRAIL_RESOLUTION }
|
|
12314
|
+
uResolution: { value: TRAIL_RESOLUTION },
|
|
12315
|
+
uDash: { value: new Vector2(dash?.[0] ?? 0, dash?.[1] ?? 0) }
|
|
12247
12316
|
},
|
|
12248
12317
|
vertexShader: `
|
|
12249
12318
|
#include <common>
|
|
@@ -12257,6 +12326,11 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
|
|
|
12257
12326
|
attribute float trailAlpha;
|
|
12258
12327
|
attribute float fullAlpha;
|
|
12259
12328
|
varying float vAlpha;
|
|
12329
|
+
#ifdef DASHED
|
|
12330
|
+
attribute float lineDistance;
|
|
12331
|
+
varying float vDistW;
|
|
12332
|
+
varying float vW;
|
|
12333
|
+
#endif
|
|
12260
12334
|
void main() {
|
|
12261
12335
|
vAlpha = mix(trailAlpha, fullAlpha, uShowFull);
|
|
12262
12336
|
vec3 currRel = position + uCenterOffset;
|
|
@@ -12273,6 +12347,13 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
|
|
|
12273
12347
|
vec2 perp = vec2(-dirN.y, dirN.x) * side * uLineWidth * 0.5;
|
|
12274
12348
|
vec2 offsetNDC = perp / (uResolution * 0.5);
|
|
12275
12349
|
gl_Position = vec4((currNDC + offsetNDC) * currClip.w, currClip.zw);
|
|
12350
|
+
#ifdef DASHED
|
|
12351
|
+
// Carried times w and divided back out per fragment, which undoes
|
|
12352
|
+
// the perspective a varying is interpolated with: the dashes are
|
|
12353
|
+
// even on screen however much nearer one end is.
|
|
12354
|
+
vDistW = lineDistance * currClip.w;
|
|
12355
|
+
vW = currClip.w;
|
|
12356
|
+
#endif
|
|
12276
12357
|
#include <logdepthbuf_vertex>
|
|
12277
12358
|
}
|
|
12278
12359
|
`,
|
|
@@ -12282,7 +12363,15 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
|
|
|
12282
12363
|
uniform float uAlphaMultiplier;
|
|
12283
12364
|
uniform float uAlphaMin;
|
|
12284
12365
|
varying float vAlpha;
|
|
12366
|
+
#ifdef DASHED
|
|
12367
|
+
uniform vec2 uDash;
|
|
12368
|
+
varying float vDistW;
|
|
12369
|
+
varying float vW;
|
|
12370
|
+
#endif
|
|
12285
12371
|
void main() {
|
|
12372
|
+
#ifdef DASHED
|
|
12373
|
+
if (mod(vDistW / vW, uDash.x + uDash.y) > uDash.x) discard;
|
|
12374
|
+
#endif
|
|
12286
12375
|
gl_FragColor = vec4(uColor, clamp(max(vAlpha * uAlphaMultiplier, uAlphaMin), 0.0, 1.0));
|
|
12287
12376
|
#include <logdepthbuf_fragment>
|
|
12288
12377
|
}
|
|
@@ -12393,6 +12482,7 @@ function bootThree(canvas, labelContainer, ctx) {
|
|
|
12393
12482
|
//#region src/lib/scene/minor-body-position.ts
|
|
12394
12483
|
function refreshMinorBodyPosition(body, jd, ctx) {
|
|
12395
12484
|
const d = body.data;
|
|
12485
|
+
body.placedJd = jd;
|
|
12396
12486
|
const isParabolic = d.q != null;
|
|
12397
12487
|
let parentPos;
|
|
12398
12488
|
if (d.parentId === "naif-0") parentPos = [
|
|
@@ -12431,6 +12521,47 @@ function refreshMinorBodyPosition(body, jd, ctx) {
|
|
|
12431
12521
|
}
|
|
12432
12522
|
body.positionUnknown = false;
|
|
12433
12523
|
}
|
|
12524
|
+
function refreshMoonPosition(body, jd, ctx) {
|
|
12525
|
+
const d = body.data;
|
|
12526
|
+
body.placedJd = jd;
|
|
12527
|
+
body.positionUnknown = true;
|
|
12528
|
+
if (d.unplaceable) return;
|
|
12529
|
+
if (d.visibleFromDays !== void 0 && jd - 2451545 < d.visibleFromDays) return;
|
|
12530
|
+
const parent = ctx.getBody(d.parentId);
|
|
12531
|
+
if (!parent || !placeForRead(parent, jd, ctx)) return;
|
|
12532
|
+
const parentPos = parent.position;
|
|
12533
|
+
const offset = ctx.chebStore?.has(d.id) ? ctx.chebStore.positionScene(d.id, jd) : d.a === 0 ? [
|
|
12534
|
+
0,
|
|
12535
|
+
0,
|
|
12536
|
+
0
|
|
12537
|
+
] : orbitalElementsToPositionJD(d, jd);
|
|
12538
|
+
if (!offset) return;
|
|
12539
|
+
body.position[0] = parentPos[0] + offset[0];
|
|
12540
|
+
body.position[1] = parentPos[1] + offset[1];
|
|
12541
|
+
body.position[2] = parentPos[2] + offset[2];
|
|
12542
|
+
if (body.orbitCenter) {
|
|
12543
|
+
body.orbitCenter[0] = parentPos[0];
|
|
12544
|
+
body.orbitCenter[1] = parentPos[1];
|
|
12545
|
+
body.orbitCenter[2] = parentPos[2];
|
|
12546
|
+
}
|
|
12547
|
+
body.positionUnknown = false;
|
|
12548
|
+
}
|
|
12549
|
+
var HOST_READ_MS = 2e3;
|
|
12550
|
+
function placeForRead(body, jd, ctx) {
|
|
12551
|
+
const d = body.data;
|
|
12552
|
+
const stale = body.placedJd !== jd;
|
|
12553
|
+
if (d.orbitalSource === OrbitalSource.SPICE_PROBE) {
|
|
12554
|
+
const { hostRead } = ctx.bodies;
|
|
12555
|
+
if (!stale && !hostRead.has(d.id)) return !body.positionUnknown;
|
|
12556
|
+
if (!hostRead.has(d.id)) ctx.bodies.hostReadVersion++;
|
|
12557
|
+
hostRead.set(d.id, performance.now());
|
|
12558
|
+
if (stale) return false;
|
|
12559
|
+
} else if (stale) {
|
|
12560
|
+
if (d.objectType === ObjectType.MOON) refreshMoonPosition(body, jd, ctx);
|
|
12561
|
+
else if (!ctx.bodies.bodiesById.has(d.id) && !isSurfaceFeature(body)) refreshMinorBodyPosition(body, jd, ctx);
|
|
12562
|
+
}
|
|
12563
|
+
return !body.positionUnknown;
|
|
12564
|
+
}
|
|
12434
12565
|
|
|
12435
12566
|
//#endregion
|
|
12436
12567
|
//#region src/lib/fetch/attitude/parse.ts
|
|
@@ -14045,7 +14176,7 @@ function cullOverlappingLabels(bodyObjects, screenWidth, screenHeight, camera, f
|
|
|
14045
14176
|
c.isCapped = body.data.objectType === ObjectType.MOON ? ctx.visibility.getMoonVisibility(body) === VISIBILITY.CAPPED : false;
|
|
14046
14177
|
c.isMinor = bo.isMinor;
|
|
14047
14178
|
c.isFocused = isFocused;
|
|
14048
|
-
c.isSelected = isFocused || isHovered;
|
|
14179
|
+
c.isSelected = isFocused || isHovered || ctx.visibility.isPinned(body.data.id);
|
|
14049
14180
|
c.dist = bo.cachedDist;
|
|
14050
14181
|
}
|
|
14051
14182
|
_candidates.length = _candidatesActive;
|
|
@@ -16766,6 +16897,7 @@ function applyRadiiToMesh(bo, radii) {
|
|
|
16766
16897
|
async function loadBodyTexture(bo, textureLoader, currentJd, scene, maxTextureSize, ctx) {
|
|
16767
16898
|
if (bo.textureTier || bo.textureLoading) return;
|
|
16768
16899
|
const detail = await fetchObjectDetail(bo.body.data.id, false);
|
|
16900
|
+
ctx?.bodies.orientationRead.add(bo.body.data.id);
|
|
16769
16901
|
if (!detail.global) return;
|
|
16770
16902
|
if (detail.global.orientation && !bo.body.orientation) applyBodyOrientation(bo, detail.global.orientation, ctx);
|
|
16771
16903
|
if (detail.global.radii && bo.radiusScene > 0 && !bo.radiiApplied && !detail.global.displacement?.absolute_radius) applyRadiiToMesh(bo, detail.global.radii);
|
|
@@ -17670,10 +17802,18 @@ function buildThinLineFromArrays(posArr, trailAlphas, fullAlphas, total, color)
|
|
|
17670
17802
|
line.renderOrder = TRAIL_RENDER_ORDER;
|
|
17671
17803
|
return line;
|
|
17672
17804
|
}
|
|
17673
|
-
function
|
|
17805
|
+
function writeLineDistances(geometry, src, n) {
|
|
17806
|
+
const attr = geometry.getAttribute("lineDistance");
|
|
17807
|
+
const arr = attr.array;
|
|
17808
|
+
const m = Math.min(n, arr.length / 2);
|
|
17809
|
+
for (let i = 0; i < m; i++) arr[2 * i] = arr[2 * i + 1] = src[i];
|
|
17810
|
+
attr.needsUpdate = true;
|
|
17811
|
+
}
|
|
17812
|
+
function buildFatLineFromThin(capacity, posArr, trailAlphas, fullAlphas, total, color, lineWidth, brightness, dash) {
|
|
17674
17813
|
const geometry = makeFatTrailGeometry(capacity);
|
|
17814
|
+
if (dash) geometry.setAttribute("lineDistance", new Float32BufferAttribute(new Float32Array(2 * capacity), 1));
|
|
17675
17815
|
writeFatTrailVertices(geometry, posArr, trailAlphas, fullAlphas, total);
|
|
17676
|
-
const mesh = new Mesh(geometry, makeFatTrailMaterial(color, lineWidth, brightness));
|
|
17816
|
+
const mesh = new Mesh(geometry, makeFatTrailMaterial(color, lineWidth, brightness, dash));
|
|
17677
17817
|
mesh.matrixAutoUpdate = false;
|
|
17678
17818
|
mesh.renderOrder = TRAIL_RENDER_ORDER;
|
|
17679
17819
|
return mesh;
|
|
@@ -17934,6 +18074,7 @@ async function loadSystemData(barycenterId, bodyObjects, scene, textureLoader, c
|
|
|
17934
18074
|
if (surface) ctx?.credits.registerImagery("surface", bodyId, barycenterId, surface);
|
|
17935
18075
|
const bo = bodyObjects.get(bodyId);
|
|
17936
18076
|
if (!bo?.mesh) continue;
|
|
18077
|
+
ctx?.bodies.orientationRead.add(bodyId);
|
|
17937
18078
|
if (bodyMeta.orientation) {
|
|
17938
18079
|
applyBodyOrientation(bo, bodyMeta.orientation, ctx, barycenterId);
|
|
17939
18080
|
if (bodyMeta.nut_prec) {
|
|
@@ -19098,6 +19239,7 @@ var SMALL_BODY_MAX_OPACITY = EMPHASIS_MAX_OPACITY;
|
|
|
19098
19239
|
var PointCloudSystem = class {
|
|
19099
19240
|
orbitPool = new OrbitWorkerPool();
|
|
19100
19241
|
pickRegistry = new PickRegistry();
|
|
19242
|
+
disposed = false;
|
|
19101
19243
|
workerPoints = {
|
|
19102
19244
|
asteroid: /* @__PURE__ */ new Map(),
|
|
19103
19245
|
spacecraft: /* @__PURE__ */ new Map()
|
|
@@ -19283,7 +19425,7 @@ var PointCloudSystem = class {
|
|
|
19283
19425
|
return true;
|
|
19284
19426
|
}
|
|
19285
19427
|
async rebuildMinorPass() {
|
|
19286
|
-
if (this.ctx.bodies.dirtyAsteroidZones.size === 0 && this.ctx.bodies.dirtySpacecraftGroups.size === 0) return;
|
|
19428
|
+
if (this.disposed || this.ctx.bodies.dirtyAsteroidZones.size === 0 && this.ctx.bodies.dirtySpacecraftGroups.size === 0) return;
|
|
19287
19429
|
const seedBasis = [
|
|
19288
19430
|
this.basisPos[0],
|
|
19289
19431
|
this.basisPos[1],
|
|
@@ -19303,6 +19445,7 @@ var PointCloudSystem = class {
|
|
|
19303
19445
|
groups: [],
|
|
19304
19446
|
baseWorker: 0
|
|
19305
19447
|
};
|
|
19448
|
+
if (this.disposed) return;
|
|
19306
19449
|
const cloudColor = bucket && groups.length > 0 ? bucket.cloudColor() : "#888888";
|
|
19307
19450
|
for (let i = 0; i < k; i++) {
|
|
19308
19451
|
const key = `${zone}#${i}`;
|
|
@@ -19330,6 +19473,7 @@ var PointCloudSystem = class {
|
|
|
19330
19473
|
this.ctx.bodies.dirtySpacecraftGroups.delete(gid);
|
|
19331
19474
|
drainedGroups.push(gid);
|
|
19332
19475
|
const { buckets, baseWorker } = await partitionForWorkersSliced(gid, drawnSpacecraft(this.ctx.layers, gid, bucket ? Array.from(bucket.values()) : []), k);
|
|
19476
|
+
if (this.disposed) return;
|
|
19333
19477
|
const skip = new Set(this.bodyObjects.keys());
|
|
19334
19478
|
for (let i = 0; i < k; i++) {
|
|
19335
19479
|
const key = `${gid}#${i}`;
|
|
@@ -19651,6 +19795,7 @@ var PointCloudSystem = class {
|
|
|
19651
19795
|
return this.moonPoints;
|
|
19652
19796
|
}
|
|
19653
19797
|
dispose() {
|
|
19798
|
+
this.disposed = true;
|
|
19654
19799
|
this.orbitPool.destroy();
|
|
19655
19800
|
this.pickRegistry.clear();
|
|
19656
19801
|
for (const kind of WORKER_CLOUD_KINDS) this.disposeClouds(this.workerPoints[kind]);
|
|
@@ -20080,7 +20225,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
20080
20225
|
isClose = !subPixel;
|
|
20081
20226
|
showLabel = !isClose;
|
|
20082
20227
|
}
|
|
20083
|
-
if (bo.isMinor && !isFocused && trail) trail.visible = false;
|
|
20228
|
+
if (bo.isMinor && !isFocused && trail && !ctx.visibility.isPinned(body.data.id)) trail.visible = false;
|
|
20084
20229
|
if (suppressHeliocentricTrails && trail?.visible) {
|
|
20085
20230
|
const oc = trail.userData.orbitCenter;
|
|
20086
20231
|
if (oc && oc.distanceToSquared(_focusV3.set(fp[0], fp[1], fp[2])) > HELIO_CENTER_DIST_SQ) trail.visible = false;
|
|
@@ -21271,18 +21416,32 @@ function updatePositions(params) {
|
|
|
21271
21416
|
for (const body of ctx.bodies.bodiesById.values()) positionMap.set(body.data.id, body.position);
|
|
21272
21417
|
for (const bo of bodyObjects.values()) if (!ctx.bodies.bodiesById.has(bo.body.data.id)) positionMap.set(bo.body.data.id, bo.body.position);
|
|
21273
21418
|
const computed = /* @__PURE__ */ new Set();
|
|
21419
|
+
const placeFirst = (id) => {
|
|
21420
|
+
if (computed.has(id)) return;
|
|
21421
|
+
const body = ctx.bodies.bodiesById.get(id) ?? bodyObjects.get(id)?.body;
|
|
21422
|
+
if (body) computePosition(body);
|
|
21423
|
+
};
|
|
21424
|
+
const now = performance.now();
|
|
21425
|
+
const hostReads = (id) => {
|
|
21426
|
+
const at = ctx.bodies.hostRead.get(id);
|
|
21427
|
+
if (at === void 0) return false;
|
|
21428
|
+
if (now - at < 2e3) return true;
|
|
21429
|
+
ctx.bodies.hostRead.delete(id);
|
|
21430
|
+
return false;
|
|
21431
|
+
};
|
|
21274
21432
|
const computePosition = (body) => {
|
|
21275
21433
|
if (computed.has(body.data.id)) return;
|
|
21276
21434
|
computed.add(body.data.id);
|
|
21277
21435
|
const d = body.data;
|
|
21278
21436
|
const bo = bodyObjects.get(d.id);
|
|
21279
|
-
if (bo && frame !== void 0 && d.id !== focusedId && !bo.group.visible && !bo.trail?.visible && (d.orbitalSource === OrbitalSource.SPICE_PROBE || !isMajorBody(d.objectType))) {
|
|
21437
|
+
if (bo && frame !== void 0 && d.id !== focusedId && !hostReads(d.id) && !bo.group.visible && !bo.trail?.visible && (d.orbitalSource === OrbitalSource.SPICE_PROBE || !isMajorBody(d.objectType))) {
|
|
21280
21438
|
bo.staggerSlot ??= nextStaggerSlot++ % HIDDEN_STRIDE;
|
|
21281
21439
|
if (frame % HIDDEN_STRIDE !== bo.staggerSlot) return;
|
|
21282
21440
|
}
|
|
21283
21441
|
const hide = (notForNotice = false) => {
|
|
21284
21442
|
if (bo) bo.outOfRange = true;
|
|
21285
21443
|
body.positionUnknown = true;
|
|
21444
|
+
body.placedJd = jd;
|
|
21286
21445
|
if (!notForNotice && d.id === focusedId) oorState.focusedOutOfRange = true;
|
|
21287
21446
|
};
|
|
21288
21447
|
if (d.unplaceable) {
|
|
@@ -21363,6 +21522,7 @@ function updatePositions(params) {
|
|
|
21363
21522
|
const probeLanded = located.probe.landed;
|
|
21364
21523
|
if (probeLanded && isLandedAt(located.probe, jd)) {
|
|
21365
21524
|
const flyingFrameId = d.parentId;
|
|
21525
|
+
placeFirst(`naif-${probeLanded.bodyNaifId}`);
|
|
21366
21526
|
const landedRender = renderLandedProbe(d, located.probe, probeLanded, jd, positionMap, ctx, bodyObjects);
|
|
21367
21527
|
if (!landedRender) {
|
|
21368
21528
|
hide();
|
|
@@ -21370,6 +21530,7 @@ function updatePositions(params) {
|
|
|
21370
21530
|
}
|
|
21371
21531
|
diagnostics.clear("probe-unavailable", d.id);
|
|
21372
21532
|
body.positionUnknown = false;
|
|
21533
|
+
body.placedJd = jd;
|
|
21373
21534
|
if (flyingFrameId !== d.parentId) body.trailBuffer?.clear();
|
|
21374
21535
|
if (bo) {
|
|
21375
21536
|
bo.outOfRange = false;
|
|
@@ -21449,10 +21610,7 @@ function updatePositions(params) {
|
|
|
21449
21610
|
populateProbeTrailBuffer(buf, ctx.probeStore, ctx.chebStore ?? null, d.id, probeParentKey, jd);
|
|
21450
21611
|
}
|
|
21451
21612
|
if (probeParentChanged) d.parentId = probeParentKey;
|
|
21452
|
-
|
|
21453
|
-
const parentBody = ctx.bodies.bodiesById.get(probeParentKey) ?? bodyObjects.get(probeParentKey)?.body;
|
|
21454
|
-
if (parentBody) computePosition(parentBody);
|
|
21455
|
-
}
|
|
21613
|
+
placeFirst(probeParentKey);
|
|
21456
21614
|
const probeParentPos = positionMap.get(probeParentKey);
|
|
21457
21615
|
if (!probeParentPos) {
|
|
21458
21616
|
hide();
|
|
@@ -21507,6 +21665,7 @@ function updatePositions(params) {
|
|
|
21507
21665
|
}
|
|
21508
21666
|
if (bo) bo.outOfRange = false;
|
|
21509
21667
|
body.positionUnknown = false;
|
|
21668
|
+
body.placedJd = jd;
|
|
21510
21669
|
body.position[0] = x;
|
|
21511
21670
|
body.position[1] = y;
|
|
21512
21671
|
body.position[2] = z;
|
|
@@ -22615,6 +22774,8 @@ var PromotionRegistry = class {
|
|
|
22615
22774
|
defaults = /* @__PURE__ */ new Set();
|
|
22616
22775
|
userPromoted = /* @__PURE__ */ new Set();
|
|
22617
22776
|
autoPromoted = /* @__PURE__ */ new Set();
|
|
22777
|
+
pinned = /* @__PURE__ */ new Set();
|
|
22778
|
+
pinPromoted = /* @__PURE__ */ new Set();
|
|
22618
22779
|
groupTargets = null;
|
|
22619
22780
|
groupMode = "none";
|
|
22620
22781
|
smallBodyZone = null;
|
|
@@ -22624,6 +22785,7 @@ var PromotionRegistry = class {
|
|
|
22624
22785
|
this.deps = deps;
|
|
22625
22786
|
deps.ctx.bodies.onBodiesAdded(() => {
|
|
22626
22787
|
this.onBodiesAdded();
|
|
22788
|
+
this.buildPinned();
|
|
22627
22789
|
this.autoPromoteAsteroidMoons();
|
|
22628
22790
|
this.reevaluateEarthSatMode();
|
|
22629
22791
|
if (this.smallBodyZone !== null) this.refreshSmallBodyEmphasis();
|
|
@@ -22813,6 +22975,31 @@ var PromotionRegistry = class {
|
|
|
22813
22975
|
if (toRemove.length === 0) return;
|
|
22814
22976
|
this.tearDownBodies(toRemove, this.autoPromoted);
|
|
22815
22977
|
}
|
|
22978
|
+
setPinned(ids) {
|
|
22979
|
+
this.pinned = new Set(ids);
|
|
22980
|
+
const focusedId = this.deps.getFocusedId();
|
|
22981
|
+
const dropped = [...this.pinPromoted].filter((id) => !this.pinned.has(id));
|
|
22982
|
+
if (focusedId && dropped.includes(focusedId)) {
|
|
22983
|
+
this.pinPromoted.delete(focusedId);
|
|
22984
|
+
this.userPromoted.add(focusedId);
|
|
22985
|
+
this.emitUserPromotedCount();
|
|
22986
|
+
}
|
|
22987
|
+
this.tearDownBodies(dropped.filter((id) => id !== focusedId), this.pinPromoted);
|
|
22988
|
+
this.buildPinned();
|
|
22989
|
+
}
|
|
22990
|
+
buildPinned() {
|
|
22991
|
+
let built = false;
|
|
22992
|
+
for (const id of this.pinned) {
|
|
22993
|
+
const body = this.deps.ctx.getBody(id);
|
|
22994
|
+
if (!body || isSurfaceFeature(body) || body.data.objectType === ObjectType.MOON) continue;
|
|
22995
|
+
if (!this.buildBodyInstance(body)) continue;
|
|
22996
|
+
built = true;
|
|
22997
|
+
if (this.userPromoted.delete(id)) this.pinPromoted.add(id);
|
|
22998
|
+
}
|
|
22999
|
+
if (!built) return;
|
|
23000
|
+
this.emitUserPromotedCount();
|
|
23001
|
+
this.finalizeBuilds();
|
|
23002
|
+
}
|
|
22816
23003
|
clearUserPromoted() {
|
|
22817
23004
|
const focusedId = this.deps.getFocusedId();
|
|
22818
23005
|
const toRemove = [...this.userPromoted].filter((id) => id !== focusedId);
|
|
@@ -23180,7 +23367,7 @@ function upgradeTargets(focus, ctx) {
|
|
|
23180
23367
|
//#endregion
|
|
23181
23368
|
//#region src/lib/scene/probe-coverage-watch.ts
|
|
23182
23369
|
var STOP_INSET_JD = 1e-6;
|
|
23183
|
-
function displayName(body) {
|
|
23370
|
+
function displayName$1(body) {
|
|
23184
23371
|
return body.data.name ?? body.data.id;
|
|
23185
23372
|
}
|
|
23186
23373
|
var ProbeCoverageWatch = class {
|
|
@@ -23223,7 +23410,7 @@ var ProbeCoverageWatch = class {
|
|
|
23223
23410
|
onHit(body, hitJd, forwardJd) {
|
|
23224
23411
|
this.notices.notify({
|
|
23225
23412
|
topic: "coverage-pause",
|
|
23226
|
-
name: displayName(body),
|
|
23413
|
+
name: displayName$1(body),
|
|
23227
23414
|
direction: hitJd >= forwardJd ? "forward" : "backward",
|
|
23228
23415
|
jd: hitJd
|
|
23229
23416
|
});
|
|
@@ -23299,10 +23486,14 @@ function rotateByQuaternion(q, x, y, z, out, at) {
|
|
|
23299
23486
|
out[at + 1] = y + q.w * ty + (q.z * tx - q.x * tz);
|
|
23300
23487
|
out[at + 2] = z + q.w * tz + (q.x * ty - q.y * tx);
|
|
23301
23488
|
}
|
|
23489
|
+
function bodyCentre(body, ctx, jd) {
|
|
23490
|
+
return placeForRead(body, jd, ctx) ? body.position : null;
|
|
23491
|
+
}
|
|
23302
23492
|
function resolveAnchor(anchor, ctx, jd) {
|
|
23303
23493
|
const body = ctx.getBody(anchor.body);
|
|
23304
|
-
|
|
23305
|
-
|
|
23494
|
+
const centre = body && bodyCentre(body, ctx, jd);
|
|
23495
|
+
if (!centre) return null;
|
|
23496
|
+
const [cx, cy, cz] = centre;
|
|
23306
23497
|
if (isSurface(anchor)) {
|
|
23307
23498
|
const distance = kmToScene(effectiveRadiusKm(body.data) + (anchor.altitudeKm ?? 0));
|
|
23308
23499
|
const [dx, dy, dz] = surfaceDirection(anchor.latitude, anchor.longitude);
|
|
@@ -23338,12 +23529,13 @@ function resolveAnchor(anchor, ctx, jd) {
|
|
|
23338
23529
|
cz + oz
|
|
23339
23530
|
];
|
|
23340
23531
|
}
|
|
23341
|
-
function anchorNormal(anchor, world, ctx) {
|
|
23532
|
+
function anchorNormal(anchor, world, ctx, jd) {
|
|
23342
23533
|
const body = ctx.getBody(anchor.body);
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
const
|
|
23346
|
-
const
|
|
23534
|
+
const centre = body && bodyCentre(body, ctx, jd);
|
|
23535
|
+
if (!centre) return null;
|
|
23536
|
+
const dx = world[0] - centre[0];
|
|
23537
|
+
const dy = world[1] - centre[1];
|
|
23538
|
+
const dz = world[2] - centre[2];
|
|
23347
23539
|
const length = Math.hypot(dx, dy, dz);
|
|
23348
23540
|
if (length === 0) return null;
|
|
23349
23541
|
return [
|
|
@@ -23568,9 +23760,14 @@ var SceneRenderer = class SceneRenderer {
|
|
|
23568
23760
|
focusDurationMs: FOCUS_DURATION_MS
|
|
23569
23761
|
};
|
|
23570
23762
|
lastUpdatedJd = NaN;
|
|
23763
|
+
get placedJd() {
|
|
23764
|
+
return this.lastUpdatedJd;
|
|
23765
|
+
}
|
|
23571
23766
|
lastUpdatedSystemId = null;
|
|
23572
23767
|
lastSeatConfigKey = null;
|
|
23573
23768
|
lastProbeVersion = 0;
|
|
23769
|
+
disposed = false;
|
|
23770
|
+
lastHostReadVersion = 0;
|
|
23574
23771
|
focusWasOutOfRange = false;
|
|
23575
23772
|
_positionMapScratch = /* @__PURE__ */ new Map();
|
|
23576
23773
|
frameIndex = 0;
|
|
@@ -23673,7 +23870,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
23673
23870
|
this.orbitPreview = new OrbitPreviewOverlay(this.scene);
|
|
23674
23871
|
this.orbitPreview.setLayer(SceneRenderer.MAP_LAYER);
|
|
23675
23872
|
this.skyboxAdjuster.set(0, 0, 0);
|
|
23676
|
-
loadSkybox(this.scene, this.renderer, ctx);
|
|
23873
|
+
loadSkybox(this.scene, this.renderer, ctx, () => this.disposed);
|
|
23677
23874
|
const sunBody = ctx.bodies.majorBodies.find((b) => b.data.id === SUN_ID);
|
|
23678
23875
|
const resident = ctx.getBody(initialView.id);
|
|
23679
23876
|
const matchedBody = resident && !resident.positionUnknown ? resident : void 0;
|
|
@@ -24542,7 +24739,8 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24542
24739
|
const systemId = this.ctx.visibility.focusedSystemId;
|
|
24543
24740
|
const seatKey = this.focusedSeatConfigKey();
|
|
24544
24741
|
const probeVersion = this.ctx.probeStore?.version ?? 0;
|
|
24545
|
-
|
|
24742
|
+
const hostReadVersion = this.ctx.bodies.hostReadVersion;
|
|
24743
|
+
if (this.clock.jd === this.lastUpdatedJd && systemId === this.lastUpdatedSystemId && seatKey === this.lastSeatConfigKey && probeVersion === this.lastProbeVersion && hostReadVersion === this.lastHostReadVersion) {
|
|
24546
24744
|
this.clock.seeked = false;
|
|
24547
24745
|
return;
|
|
24548
24746
|
}
|
|
@@ -24552,6 +24750,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24552
24750
|
this.lastUpdatedSystemId = systemId;
|
|
24553
24751
|
this.lastSeatConfigKey = seatKey;
|
|
24554
24752
|
this.lastProbeVersion = probeVersion;
|
|
24753
|
+
this.lastHostReadVersion = hostReadVersion;
|
|
24555
24754
|
this.ctx.refreshTick(jdToDate(this.clock.jd));
|
|
24556
24755
|
this.focusController.promotion.onSimTimeChanged();
|
|
24557
24756
|
const result = updatePositions({
|
|
@@ -24737,6 +24936,10 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24737
24936
|
setBodyAppearance(id, appearance) {
|
|
24738
24937
|
this.appearances.set(id, appearance);
|
|
24739
24938
|
}
|
|
24939
|
+
setPinnedBodies(ids) {
|
|
24940
|
+
this.ctx.visibility.setPinned(ids);
|
|
24941
|
+
this.focusController.promotion.setPinned(ids);
|
|
24942
|
+
}
|
|
24740
24943
|
setNorthReference(id) {
|
|
24741
24944
|
this.cameraUp.setNorthReference(id);
|
|
24742
24945
|
}
|
|
@@ -24887,7 +25090,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24887
25090
|
this.renderer.setPixelRatio(renderPixelRatio());
|
|
24888
25091
|
this.composer.setPixelRatio(renderPixelRatio());
|
|
24889
25092
|
this.resize(this.canvas.clientWidth, this.canvas.clientHeight);
|
|
24890
|
-
loadSkybox(this.scene, this.renderer, this.ctx);
|
|
25093
|
+
loadSkybox(this.scene, this.renderer, this.ctx, () => this.disposed);
|
|
24891
25094
|
this.resume();
|
|
24892
25095
|
}
|
|
24893
25096
|
recoverWorkersIfDead(timeoutMs) {
|
|
@@ -24918,6 +25121,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24918
25121
|
setTrailResolution(width, height);
|
|
24919
25122
|
}
|
|
24920
25123
|
dispose() {
|
|
25124
|
+
this.disposed = true;
|
|
24921
25125
|
cancelAnimationFrame(this.rafId);
|
|
24922
25126
|
this.pointerInteraction.detach();
|
|
24923
25127
|
this.gpuPick.dispose();
|
|
@@ -25339,8 +25543,8 @@ var MarkerExtension = class {
|
|
|
25339
25543
|
this.label.visible = this.occlude ? this.facesCamera(world, frame) : true;
|
|
25340
25544
|
this.object.updateMatrixWorld(true);
|
|
25341
25545
|
}
|
|
25342
|
-
facesCamera(world, { camera, ctx }) {
|
|
25343
|
-
const normal = anchorNormal(this.anchor, world, ctx);
|
|
25546
|
+
facesCamera(world, { camera, ctx, jd }) {
|
|
25547
|
+
const normal = anchorNormal(this.anchor, world, ctx, jd);
|
|
25344
25548
|
if (!normal) return true;
|
|
25345
25549
|
const { position } = this.label;
|
|
25346
25550
|
const dx = camera.position.x - position.x;
|
|
@@ -25354,6 +25558,153 @@ var MarkerExtension = class {
|
|
|
25354
25558
|
}
|
|
25355
25559
|
};
|
|
25356
25560
|
|
|
25561
|
+
//#endregion
|
|
25562
|
+
//#region src/lib/scene/extensions/screen.ts
|
|
25563
|
+
var REACH = 3;
|
|
25564
|
+
var right = new Vector3();
|
|
25565
|
+
var up = new Vector3();
|
|
25566
|
+
var forward = new Vector3();
|
|
25567
|
+
function parseDash(pattern) {
|
|
25568
|
+
const [dash, gap = dash] = (pattern ?? "").split(/[\s,]+/).filter(Boolean).map(Number);
|
|
25569
|
+
return dash > 0 && gap > 0 ? [dash, gap] : null;
|
|
25570
|
+
}
|
|
25571
|
+
function aim(camera, viewportPx) {
|
|
25572
|
+
right.set(1, 0, 0).applyQuaternion(camera.quaternion);
|
|
25573
|
+
up.set(0, 1, 0).applyQuaternion(camera.quaternion);
|
|
25574
|
+
forward.set(0, 0, -1).applyQuaternion(camera.quaternion);
|
|
25575
|
+
return viewportPx / (2 * Math.tan(camera.getEffectiveFOV() * Math.PI / 360));
|
|
25576
|
+
}
|
|
25577
|
+
function view(points, i, camera, out) {
|
|
25578
|
+
const x = points[i * 3] - camera.position.x;
|
|
25579
|
+
const y = points[i * 3 + 1] - camera.position.y;
|
|
25580
|
+
const z = points[i * 3 + 2] - camera.position.z;
|
|
25581
|
+
out[0] = x * right.x + y * right.y + z * right.z;
|
|
25582
|
+
out[1] = x * up.x + y * up.y + z * up.z;
|
|
25583
|
+
out[2] = x * forward.x + y * forward.y + z * forward.z;
|
|
25584
|
+
}
|
|
25585
|
+
function reachesBehind(points, count, camera) {
|
|
25586
|
+
forward.set(0, 0, -1).applyQuaternion(camera.quaternion);
|
|
25587
|
+
const { x, y, z } = camera.position;
|
|
25588
|
+
for (let i = 0; i < count; i++) if ((points[i * 3] - x) * forward.x + (points[i * 3 + 1] - y) * forward.y + (points[i * 3 + 2] - z) * forward.z < camera.near * 2) return true;
|
|
25589
|
+
return false;
|
|
25590
|
+
}
|
|
25591
|
+
function screenLengths(points, count, camera, viewportPx, out) {
|
|
25592
|
+
const focal = aim(camera, viewportPx);
|
|
25593
|
+
const at = [
|
|
25594
|
+
0,
|
|
25595
|
+
0,
|
|
25596
|
+
0
|
|
25597
|
+
];
|
|
25598
|
+
let length = 0;
|
|
25599
|
+
let lastX = 0;
|
|
25600
|
+
let lastY = 0;
|
|
25601
|
+
let seen = false;
|
|
25602
|
+
for (let i = 0; i < count; i++) {
|
|
25603
|
+
view(points, i, camera, at);
|
|
25604
|
+
if (at[2] > camera.near) {
|
|
25605
|
+
const px = at[0] / at[2] * focal;
|
|
25606
|
+
const py = at[1] / at[2] * focal;
|
|
25607
|
+
if (seen) length += Math.hypot(px - lastX, py - lastY);
|
|
25608
|
+
lastX = px;
|
|
25609
|
+
lastY = py;
|
|
25610
|
+
seen = true;
|
|
25611
|
+
}
|
|
25612
|
+
out[i] = length;
|
|
25613
|
+
}
|
|
25614
|
+
}
|
|
25615
|
+
var SeenLine = class {
|
|
25616
|
+
points = new Float32Array(0);
|
|
25617
|
+
alphas = new Float32Array(0);
|
|
25618
|
+
lengths = new Float32Array(0);
|
|
25619
|
+
count = 0;
|
|
25620
|
+
a = [
|
|
25621
|
+
0,
|
|
25622
|
+
0,
|
|
25623
|
+
0
|
|
25624
|
+
];
|
|
25625
|
+
b = [
|
|
25626
|
+
0,
|
|
25627
|
+
0,
|
|
25628
|
+
0
|
|
25629
|
+
];
|
|
25630
|
+
see(points, alphas, count, camera, viewportPx) {
|
|
25631
|
+
if (this.alphas.length < count * 4) {
|
|
25632
|
+
this.points = new Float32Array(count * 12);
|
|
25633
|
+
this.alphas = new Float32Array(count * 4);
|
|
25634
|
+
this.lengths = new Float32Array(count * 4);
|
|
25635
|
+
}
|
|
25636
|
+
this.count = 0;
|
|
25637
|
+
const focal = aim(camera, viewportPx);
|
|
25638
|
+
const halfW = viewportPx * camera.aspect * REACH / 2;
|
|
25639
|
+
const halfH = viewportPx * REACH / 2;
|
|
25640
|
+
const near = camera.near * 2;
|
|
25641
|
+
let { a, b } = this;
|
|
25642
|
+
let joined = false;
|
|
25643
|
+
let length = 0;
|
|
25644
|
+
let lastX = 0;
|
|
25645
|
+
let lastY = 0;
|
|
25646
|
+
if (count > 0) view(points, 0, camera, a);
|
|
25647
|
+
for (let i = 0; i + 1 < count; i++, [a, b] = [b, a]) {
|
|
25648
|
+
view(points, i + 1, camera, b);
|
|
25649
|
+
let from = 0;
|
|
25650
|
+
let to = 1;
|
|
25651
|
+
for (let plane = 0; plane < 5 && from <= to; plane++) {
|
|
25652
|
+
const ga = edge(plane, a, focal, halfW, halfH, near);
|
|
25653
|
+
const gb = edge(plane, b, focal, halfW, halfH, near);
|
|
25654
|
+
if (ga < 0 && gb < 0) to = -1;
|
|
25655
|
+
else if (ga < 0) from = Math.max(from, ga / (ga - gb));
|
|
25656
|
+
else if (gb < 0) to = Math.min(to, ga / (ga - gb));
|
|
25657
|
+
}
|
|
25658
|
+
if (from > to) {
|
|
25659
|
+
joined = false;
|
|
25660
|
+
continue;
|
|
25661
|
+
}
|
|
25662
|
+
const depth = a[2] + (b[2] - a[2]) * from;
|
|
25663
|
+
const x = (a[0] + (b[0] - a[0]) * from) / depth * focal;
|
|
25664
|
+
const y = (a[1] + (b[1] - a[1]) * from) / depth * focal;
|
|
25665
|
+
if (!joined || from > 0) {
|
|
25666
|
+
if (this.count > 0) {
|
|
25667
|
+
this.copy(this.count - 1, 0);
|
|
25668
|
+
this.put(points, alphas, i, from, length, 0);
|
|
25669
|
+
}
|
|
25670
|
+
this.put(points, alphas, i, from, length, 1);
|
|
25671
|
+
lastX = x;
|
|
25672
|
+
lastY = y;
|
|
25673
|
+
}
|
|
25674
|
+
const endDepth = a[2] + (b[2] - a[2]) * to;
|
|
25675
|
+
const endX = (a[0] + (b[0] - a[0]) * to) / endDepth * focal;
|
|
25676
|
+
const endY = (a[1] + (b[1] - a[1]) * to) / endDepth * focal;
|
|
25677
|
+
length += Math.hypot(endX - lastX, endY - lastY);
|
|
25678
|
+
lastX = endX;
|
|
25679
|
+
lastY = endY;
|
|
25680
|
+
this.put(points, alphas, i, to, length, 1);
|
|
25681
|
+
joined = to === 1;
|
|
25682
|
+
}
|
|
25683
|
+
}
|
|
25684
|
+
put(points, alphas, i, t, length, shown) {
|
|
25685
|
+
const at = this.count++;
|
|
25686
|
+
for (let k = 0; k < 3; k++) {
|
|
25687
|
+
const from = points[i * 3 + k];
|
|
25688
|
+
this.points[at * 3 + k] = from + (points[i * 3 + 3 + k] - from) * t;
|
|
25689
|
+
}
|
|
25690
|
+
this.alphas[at] = (alphas[i] + (alphas[i + 1] - alphas[i]) * t) * shown;
|
|
25691
|
+
this.lengths[at] = length;
|
|
25692
|
+
}
|
|
25693
|
+
copy(from, shown) {
|
|
25694
|
+
const at = this.count++;
|
|
25695
|
+
this.points.copyWithin(at * 3, from * 3, from * 3 + 3);
|
|
25696
|
+
this.alphas[at] = this.alphas[from] * shown;
|
|
25697
|
+
this.lengths[at] = this.lengths[from];
|
|
25698
|
+
}
|
|
25699
|
+
};
|
|
25700
|
+
function edge(plane, at, focal, halfW, halfH, near) {
|
|
25701
|
+
if (plane === 0) return at[2] - near;
|
|
25702
|
+
if (plane === 1) return halfW * at[2] - focal * at[0];
|
|
25703
|
+
if (plane === 2) return halfW * at[2] + focal * at[0];
|
|
25704
|
+
if (plane === 3) return halfH * at[2] - focal * at[1];
|
|
25705
|
+
return halfH * at[2] + focal * at[1];
|
|
25706
|
+
}
|
|
25707
|
+
|
|
25357
25708
|
//#endregion
|
|
25358
25709
|
//#region src/lib/scene/extensions/polyline.ts
|
|
25359
25710
|
var REDRAW_EPSILON$1 = 1e-9;
|
|
@@ -25363,6 +25714,7 @@ var PolylineExtension = class {
|
|
|
25363
25714
|
color;
|
|
25364
25715
|
widthPx;
|
|
25365
25716
|
opacity;
|
|
25717
|
+
dash;
|
|
25366
25718
|
fade;
|
|
25367
25719
|
closed;
|
|
25368
25720
|
local;
|
|
@@ -25370,6 +25722,7 @@ var PolylineExtension = class {
|
|
|
25370
25722
|
mesh = null;
|
|
25371
25723
|
scratch = new Float32Array(0);
|
|
25372
25724
|
alphas = new Float32Array(0);
|
|
25725
|
+
seen = new SeenLine();
|
|
25373
25726
|
capacity = 0;
|
|
25374
25727
|
lastOrigin = [
|
|
25375
25728
|
0,
|
|
@@ -25384,6 +25737,7 @@ var PolylineExtension = class {
|
|
|
25384
25737
|
this.color = options.color ?? "#ffffff";
|
|
25385
25738
|
this.widthPx = options.widthPx ?? 2;
|
|
25386
25739
|
this.opacity = options.opacity ?? 1;
|
|
25740
|
+
this.dash = parseDash(options.dash);
|
|
25387
25741
|
this.fade = options.fade ?? false;
|
|
25388
25742
|
this.closed = options.closed ?? false;
|
|
25389
25743
|
this.local = new Float64Array(0);
|
|
@@ -25404,6 +25758,10 @@ var PolylineExtension = class {
|
|
|
25404
25758
|
}
|
|
25405
25759
|
if (count > points.length) this.local.copyWithin(points.length * 3, 0, 3);
|
|
25406
25760
|
this.count = count;
|
|
25761
|
+
if (this.alphas.length < count) {
|
|
25762
|
+
this.scratch = new Float32Array(count * 3);
|
|
25763
|
+
this.alphas = new Float32Array(count);
|
|
25764
|
+
}
|
|
25407
25765
|
if (count > this.capacity) this.rebuild(count);
|
|
25408
25766
|
this.fillAlphas();
|
|
25409
25767
|
this.dirty = true;
|
|
@@ -25422,38 +25780,35 @@ var PolylineExtension = class {
|
|
|
25422
25780
|
this.removeSelf?.();
|
|
25423
25781
|
this.removeSelf = null;
|
|
25424
25782
|
}
|
|
25425
|
-
update({ jd, basis, camera, ctx }) {
|
|
25426
|
-
const mesh = this.mesh;
|
|
25427
|
-
if (!mesh) return;
|
|
25783
|
+
update({ jd, basis, camera, viewportPx, ctx }) {
|
|
25428
25784
|
const world = this.wanted && this.count >= 2 ? resolveAnchor(this.anchor, ctx, jd) : null;
|
|
25429
|
-
mesh.visible = world !== null;
|
|
25430
|
-
if (!world) return;
|
|
25785
|
+
if (this.mesh) this.mesh.visible = world !== null;
|
|
25786
|
+
if (!this.mesh || !world) return;
|
|
25431
25787
|
const origin = [
|
|
25432
25788
|
world[0] - basis[0],
|
|
25433
25789
|
world[1] - basis[1],
|
|
25434
25790
|
world[2] - basis[2]
|
|
25435
25791
|
];
|
|
25436
|
-
|
|
25437
|
-
|
|
25792
|
+
const moved = this.dirty || Math.abs(origin[0] - this.lastOrigin[0]) > REDRAW_EPSILON$1 || Math.abs(origin[1] - this.lastOrigin[1]) > REDRAW_EPSILON$1 || Math.abs(origin[2] - this.lastOrigin[2]) > REDRAW_EPSILON$1;
|
|
25793
|
+
if (moved) {
|
|
25794
|
+
for (let i = 0; i < this.count * 3; i++) this.scratch[i] = this.local[i] + origin[i % 3];
|
|
25438
25795
|
this.lastOrigin = origin;
|
|
25439
|
-
this.dirty = false;
|
|
25440
25796
|
}
|
|
25441
|
-
|
|
25442
|
-
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
this.
|
|
25447
|
-
this.
|
|
25448
|
-
}
|
|
25449
|
-
|
|
25797
|
+
const cut = this.dash !== null || reachesBehind(this.scratch, this.count, camera);
|
|
25798
|
+
if (cut) {
|
|
25799
|
+
const { seen } = this;
|
|
25800
|
+
seen.see(this.scratch, this.alphas, this.count, camera, viewportPx);
|
|
25801
|
+
if (seen.count > this.capacity) this.rebuild(seen.count);
|
|
25802
|
+
writeFatTrailVertices(this.mesh.geometry, seen.points, seen.alphas, seen.alphas, seen.count);
|
|
25803
|
+
if (this.dash) writeLineDistances(this.mesh.geometry, seen.lengths, seen.count);
|
|
25804
|
+
} else if (moved) writeFatTrailVertices(this.mesh.geometry, this.scratch, this.alphas, this.alphas, this.count);
|
|
25805
|
+
this.dirty = cut;
|
|
25806
|
+
this.mesh.material.uniforms.uCenterOffset.value.copy(camera.position).negate();
|
|
25450
25807
|
}
|
|
25451
25808
|
rebuild(capacity) {
|
|
25452
25809
|
this.disposeMesh();
|
|
25453
25810
|
this.dirty = true;
|
|
25454
|
-
this.scratch
|
|
25455
|
-
this.alphas = new Float32Array(capacity);
|
|
25456
|
-
const mesh = buildFatLineFromThin(capacity, this.scratch, this.alphas, this.alphas, capacity, this.color, this.widthPx, 1);
|
|
25811
|
+
const mesh = buildFatLineFromThin(capacity, this.scratch, this.alphas, this.alphas, 0, this.color, this.widthPx, 1, this.dash ?? void 0);
|
|
25457
25812
|
mesh.frustumCulled = false;
|
|
25458
25813
|
this.object.add(mesh);
|
|
25459
25814
|
this.mesh = mesh;
|
|
@@ -26642,6 +26997,7 @@ var SurfaceShapeExtension = class {
|
|
|
26642
26997
|
color;
|
|
26643
26998
|
widthPx;
|
|
26644
26999
|
opacity;
|
|
27000
|
+
dash;
|
|
26645
27001
|
fillColor;
|
|
26646
27002
|
fillOpacity;
|
|
26647
27003
|
dirs = new Float64Array(0);
|
|
@@ -26649,6 +27005,7 @@ var SurfaceShapeExtension = class {
|
|
|
26649
27005
|
line = null;
|
|
26650
27006
|
scratch = new Float32Array(0);
|
|
26651
27007
|
alphas = new Float32Array(0);
|
|
27008
|
+
lengths = new Float32Array(0);
|
|
26652
27009
|
fanDirs = new Float64Array(0);
|
|
26653
27010
|
fill = null;
|
|
26654
27011
|
fillScratch = new Float32Array(0);
|
|
@@ -26668,6 +27025,7 @@ var SurfaceShapeExtension = class {
|
|
|
26668
27025
|
this.color = options.color ?? "#ffffff";
|
|
26669
27026
|
this.widthPx = options.widthPx ?? 2;
|
|
26670
27027
|
this.opacity = options.opacity ?? 1;
|
|
27028
|
+
this.dash = parseDash(options.dash);
|
|
26671
27029
|
this.fillColor = options.fill ?? null;
|
|
26672
27030
|
this.fillOpacity = options.fillOpacity ?? .25;
|
|
26673
27031
|
this.object.matrixAutoUpdate = false;
|
|
@@ -26700,22 +27058,27 @@ var SurfaceShapeExtension = class {
|
|
|
26700
27058
|
this.removeSelf?.();
|
|
26701
27059
|
this.removeSelf = null;
|
|
26702
27060
|
}
|
|
26703
|
-
update({ jd, basis, camera, ctx }) {
|
|
27061
|
+
update({ jd, basis, camera, viewportPx, ctx }) {
|
|
26704
27062
|
const body = this.wanted && this.count >= 2 ? ctx.getBody(this.body) : void 0;
|
|
26705
|
-
|
|
26706
|
-
if (this.
|
|
26707
|
-
if (
|
|
27063
|
+
const centre = body && bodyCentre(body, ctx, jd);
|
|
27064
|
+
if (this.line) this.line.visible = !!centre;
|
|
27065
|
+
if (this.fill) this.fill.visible = !!centre;
|
|
27066
|
+
if (!body || !centre) return;
|
|
26708
27067
|
const radiusKm = effectiveRadiusKm(body.data);
|
|
26709
27068
|
const scale = kmToScene(radiusKm + (this.altitudeKm ?? radiusKm * DEFAULT_LIFT));
|
|
26710
27069
|
const q = body.orientation ? bodyQuaternion(body.orientation, jd, body.nutPrec) : null;
|
|
26711
27070
|
const origin = [
|
|
26712
|
-
|
|
26713
|
-
|
|
26714
|
-
|
|
27071
|
+
centre[0] - basis[0],
|
|
27072
|
+
centre[1] - basis[1],
|
|
27073
|
+
centre[2] - basis[2]
|
|
26715
27074
|
];
|
|
26716
27075
|
if (this.line) {
|
|
26717
27076
|
this.place(this.dirs, this.count, q, scale, origin, this.scratch);
|
|
26718
27077
|
writeFatTrailVertices(this.line.geometry, this.scratch, this.alphas, this.alphas, this.count);
|
|
27078
|
+
if (this.dash) {
|
|
27079
|
+
screenLengths(this.scratch, this.count, camera, viewportPx, this.lengths);
|
|
27080
|
+
writeLineDistances(this.line.geometry, this.lengths, this.count);
|
|
27081
|
+
}
|
|
26719
27082
|
this.line.material.uniforms.uCenterOffset.value.copy(camera.position).negate();
|
|
26720
27083
|
}
|
|
26721
27084
|
if (this.fill) {
|
|
@@ -26744,7 +27107,8 @@ var SurfaceShapeExtension = class {
|
|
|
26744
27107
|
if (this.count < 2 || this.widthPx <= 0) return;
|
|
26745
27108
|
this.scratch = new Float32Array(this.count * 3);
|
|
26746
27109
|
this.alphas = new Float32Array(this.count).fill(this.opacity);
|
|
26747
|
-
|
|
27110
|
+
this.lengths = new Float32Array(this.count);
|
|
27111
|
+
const line = buildFatLineFromThin(this.count, this.scratch, this.alphas, this.alphas, this.count, this.color, this.widthPx, 1, this.dash ?? void 0);
|
|
26748
27112
|
line.frustumCulled = false;
|
|
26749
27113
|
line.renderOrder = SURFACE_RENDER_ORDER;
|
|
26750
27114
|
this.object.add(line);
|
|
@@ -26941,6 +27305,50 @@ var MapObjectCollection = class {
|
|
|
26941
27305
|
}
|
|
26942
27306
|
};
|
|
26943
27307
|
|
|
27308
|
+
//#endregion
|
|
27309
|
+
//#region src/lib/scene/extensions/measure.ts
|
|
27310
|
+
function anchorOffsetKm(a, b, ctx, jd) {
|
|
27311
|
+
const from = resolveAnchor(a, ctx, jd);
|
|
27312
|
+
const to = resolveAnchor(b, ctx, jd);
|
|
27313
|
+
if (!from || !to) return null;
|
|
27314
|
+
return sceneToEcliptic([
|
|
27315
|
+
to[0] - from[0],
|
|
27316
|
+
to[1] - from[1],
|
|
27317
|
+
to[2] - from[2]
|
|
27318
|
+
]);
|
|
27319
|
+
}
|
|
27320
|
+
function anchorDistanceKm(a, b, ctx, jd) {
|
|
27321
|
+
const offset = anchorOffsetKm(a, b, ctx, jd);
|
|
27322
|
+
return offset && Math.hypot(...offset);
|
|
27323
|
+
}
|
|
27324
|
+
function subsolarPoint(id, ctx, jd) {
|
|
27325
|
+
const body = ctx.getBody(id);
|
|
27326
|
+
const sun = ctx.getBody(SUN_ID);
|
|
27327
|
+
if (!body || !sun || body === sun) return null;
|
|
27328
|
+
if (!body.orientation && !ctx.bodies.orientationRead.has(id)) return null;
|
|
27329
|
+
const centre = bodyCentre(body, ctx, jd);
|
|
27330
|
+
if (!centre) return null;
|
|
27331
|
+
const turn = body.orientation ? bodyQuaternion(body.orientation, jd, body.nutPrec).toArray() : [
|
|
27332
|
+
0,
|
|
27333
|
+
0,
|
|
27334
|
+
0,
|
|
27335
|
+
1
|
|
27336
|
+
];
|
|
27337
|
+
const { latitude, longitude, distance } = cartesianToSpherical([
|
|
27338
|
+
sun.position[0],
|
|
27339
|
+
sun.position[1],
|
|
27340
|
+
sun.position[2]
|
|
27341
|
+
], [
|
|
27342
|
+
centre[0],
|
|
27343
|
+
centre[1],
|
|
27344
|
+
centre[2]
|
|
27345
|
+
], turn);
|
|
27346
|
+
return distance > 0 ? {
|
|
27347
|
+
lon: longitude,
|
|
27348
|
+
lat: latitude
|
|
27349
|
+
} : null;
|
|
27350
|
+
}
|
|
27351
|
+
|
|
26944
27352
|
//#endregion
|
|
26945
27353
|
//#region src/lib/interaction/gesture.ts
|
|
26946
27354
|
var GestureHandler = class {
|
|
@@ -27121,7 +27529,8 @@ var KEY_ROTATE_RAD = .05;
|
|
|
27121
27529
|
var KEY_ZOOM_FACTOR = 1.15;
|
|
27122
27530
|
var CONTEXT_LOST_PANEL_DELAY_MS = 2e3;
|
|
27123
27531
|
var RESTORE_PING_TIMEOUT_MS = 4e3;
|
|
27124
|
-
var
|
|
27532
|
+
var mapsIn = /* @__PURE__ */ new WeakMap();
|
|
27533
|
+
var onceTarget$2 = /* @__PURE__ */ new WeakMap();
|
|
27125
27534
|
var SpaceMap = class {
|
|
27126
27535
|
ctx = new ContextManager();
|
|
27127
27536
|
clock;
|
|
@@ -27222,14 +27631,14 @@ var SpaceMap = class {
|
|
|
27222
27631
|
off();
|
|
27223
27632
|
listener(...args);
|
|
27224
27633
|
};
|
|
27225
|
-
onceTarget$
|
|
27634
|
+
onceTarget$2.set(wrapped, listener);
|
|
27226
27635
|
const off = this.on(event, wrapped);
|
|
27227
27636
|
return off;
|
|
27228
27637
|
}
|
|
27229
27638
|
off(event, listener) {
|
|
27230
27639
|
const set = this.listeners[event];
|
|
27231
27640
|
set.delete(listener);
|
|
27232
|
-
for (const held of set) if (onceTarget$
|
|
27641
|
+
for (const held of set) if (onceTarget$2.get(held) === listener) set.delete(held);
|
|
27233
27642
|
}
|
|
27234
27643
|
emit(event, ...args) {
|
|
27235
27644
|
for (const listener of this.listeners[event]) listener(...args);
|
|
@@ -27247,6 +27656,19 @@ var SpaceMap = class {
|
|
|
27247
27656
|
getChildren(id) {
|
|
27248
27657
|
return [...this.ctx.bodies.getChildren(id) ?? []];
|
|
27249
27658
|
}
|
|
27659
|
+
get placedJd() {
|
|
27660
|
+
const jd = this.renderer?.placedJd;
|
|
27661
|
+
return jd === void 0 || Number.isNaN(jd) ? this.clock.jd : jd;
|
|
27662
|
+
}
|
|
27663
|
+
distanceKm(from, to) {
|
|
27664
|
+
return anchorDistanceKm(from, to, this.ctx, this.placedJd);
|
|
27665
|
+
}
|
|
27666
|
+
offsetKm(from, to) {
|
|
27667
|
+
return anchorOffsetKm(from, to, this.ctx, this.placedJd);
|
|
27668
|
+
}
|
|
27669
|
+
getSubsolarPoint(id) {
|
|
27670
|
+
return subsolarPoint(id, this.ctx, this.placedJd);
|
|
27671
|
+
}
|
|
27250
27672
|
load(targetId = this.initialView.id) {
|
|
27251
27673
|
return this.ctx.load(jdToDate(this.clock.jd), targetId);
|
|
27252
27674
|
}
|
|
@@ -27278,6 +27700,7 @@ var SpaceMap = class {
|
|
|
27278
27700
|
if (this.container) throw new Error("SpaceMap is already mounted");
|
|
27279
27701
|
this.container = container;
|
|
27280
27702
|
container.classList.add("sm-map");
|
|
27703
|
+
mapsIn.set(container, (mapsIn.get(container) ?? 0) + 1);
|
|
27281
27704
|
this.controls = new ControlHost(this, container);
|
|
27282
27705
|
const canvas = document.createElement("canvas");
|
|
27283
27706
|
canvas.className = "sm-map__canvas";
|
|
@@ -27357,7 +27780,9 @@ var SpaceMap = class {
|
|
|
27357
27780
|
this.renderer = null;
|
|
27358
27781
|
canvas.remove();
|
|
27359
27782
|
this.labelLayer?.remove();
|
|
27360
|
-
|
|
27783
|
+
const left = (mapsIn.get(container) ?? 1) - 1;
|
|
27784
|
+
mapsIn.set(container, left);
|
|
27785
|
+
if (left === 0) container.classList.remove("sm-map");
|
|
27361
27786
|
this.container = this.canvas = this.labelLayer = null;
|
|
27362
27787
|
}
|
|
27363
27788
|
watch(renderer) {
|
|
@@ -27722,6 +28147,10 @@ var SpaceMap = class {
|
|
|
27722
28147
|
this.requireRenderer().setBodyAppearance(id, appearance);
|
|
27723
28148
|
return this;
|
|
27724
28149
|
}
|
|
28150
|
+
setPinnedBodies(ids) {
|
|
28151
|
+
this.requireRenderer().setPinnedBodies(ids);
|
|
28152
|
+
return this;
|
|
28153
|
+
}
|
|
27725
28154
|
capRadiusDeg(options) {
|
|
27726
28155
|
if (options.radiusDeg !== void 0) return options.radiusDeg;
|
|
27727
28156
|
const bodyRadiusKm = this.getBody(options.body)?.radiusKm;
|
|
@@ -27778,7 +28207,7 @@ var SpaceMap = class {
|
|
|
27778
28207
|
|
|
27779
28208
|
//#endregion
|
|
27780
28209
|
//#region src/lib/flatmap/layers.ts
|
|
27781
|
-
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
28210
|
+
var SVG_NS$1 = "http://www.w3.org/2000/svg";
|
|
27782
28211
|
function layerInfo(layer) {
|
|
27783
28212
|
return {
|
|
27784
28213
|
id: layer.id,
|
|
@@ -27796,7 +28225,7 @@ function creditOf(bundle) {
|
|
|
27796
28225
|
};
|
|
27797
28226
|
}
|
|
27798
28227
|
function pathElement(d, className) {
|
|
27799
|
-
const path = document.createElementNS(SVG_NS, "path");
|
|
28228
|
+
const path = document.createElementNS(SVG_NS$1, "path");
|
|
27800
28229
|
path.setAttribute("d", d);
|
|
27801
28230
|
path.setAttribute("class", className);
|
|
27802
28231
|
return path;
|
|
@@ -27815,7 +28244,7 @@ function graticuleLayer(label, stepDeg = 30) {
|
|
|
27815
28244
|
if (viewport.projection.azimuthal) {
|
|
27816
28245
|
const centre = viewport.toScreen(0, 0);
|
|
27817
28246
|
const edge = viewport.toScreen(viewport.projection.extent.maxX, 0);
|
|
27818
|
-
const circle = document.createElementNS(SVG_NS, "circle");
|
|
28247
|
+
const circle = document.createElementNS(SVG_NS$1, "circle");
|
|
27819
28248
|
circle.setAttribute("cx", String(centre[0]));
|
|
27820
28249
|
circle.setAttribute("cy", String(centre[1]));
|
|
27821
28250
|
circle.setAttribute("r", String(Math.abs(edge[0] - centre[0])));
|
|
@@ -27852,12 +28281,12 @@ function nomenclatureLayer(bodyId, label) {
|
|
|
27852
28281
|
for (const shift of shifts) {
|
|
27853
28282
|
const x = at[0] + shift;
|
|
27854
28283
|
if (x < 0 || x > viewport.width) continue;
|
|
27855
|
-
const dot = document.createElementNS(SVG_NS, "circle");
|
|
28284
|
+
const dot = document.createElementNS(SVG_NS$1, "circle");
|
|
27856
28285
|
dot.setAttribute("cx", x.toFixed(1));
|
|
27857
28286
|
dot.setAttribute("cy", at[1].toFixed(1));
|
|
27858
28287
|
dot.setAttribute("r", "1.6");
|
|
27859
28288
|
dot.setAttribute("class", "sm-flat__feature-dot");
|
|
27860
|
-
const text = document.createElementNS(SVG_NS, "text");
|
|
28289
|
+
const text = document.createElementNS(SVG_NS$1, "text");
|
|
27861
28290
|
text.setAttribute("x", (x + 4).toFixed(1));
|
|
27862
28291
|
text.setAttribute("y", (at[1] + 3).toFixed(1));
|
|
27863
28292
|
text.setAttribute("class", "sm-flat__feature-label");
|
|
@@ -27904,12 +28333,12 @@ var PathDrawing = class {
|
|
|
27904
28333
|
this.onRemove = onRemove;
|
|
27905
28334
|
this.onChange = onChange;
|
|
27906
28335
|
this.points = points;
|
|
27907
|
-
this.node = document.createElementNS(SVG_NS, "g");
|
|
28336
|
+
this.node = document.createElementNS(SVG_NS$1, "g");
|
|
27908
28337
|
applyStyle(this.node, style);
|
|
27909
28338
|
bindEvents(this.node, style);
|
|
27910
|
-
this.area = closed ? document.createElementNS(SVG_NS, "path") : null;
|
|
28339
|
+
this.area = closed ? document.createElementNS(SVG_NS$1, "path") : null;
|
|
27911
28340
|
this.area?.setAttribute("stroke", "none");
|
|
27912
|
-
this.edge = document.createElementNS(SVG_NS, "path");
|
|
28341
|
+
this.edge = document.createElementNS(SVG_NS$1, "path");
|
|
27913
28342
|
this.edge.setAttribute("fill", "none");
|
|
27914
28343
|
if (this.area) this.node.append(this.area);
|
|
27915
28344
|
this.node.append(this.edge);
|
|
@@ -28230,27 +28659,33 @@ async function fetchJson(url) {
|
|
|
28230
28659
|
return null;
|
|
28231
28660
|
}
|
|
28232
28661
|
}
|
|
28233
|
-
async function
|
|
28234
|
-
const
|
|
28235
|
-
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
].includes(k));
|
|
28242
|
-
if (tiers.length === 0) return {};
|
|
28243
|
-
return { surface: {
|
|
28662
|
+
async function tiersOf(bundleId) {
|
|
28663
|
+
const found = await Promise.all(TIER_ORDER.map((tier) => fetch(versionedUrl(`/v1/textures/${bundleId}/${tier}.webp`, "textures"), { method: "HEAD" }).then((response) => response.ok).catch(() => false)));
|
|
28664
|
+
return TIER_ORDER.filter((_, i) => found[i]);
|
|
28665
|
+
}
|
|
28666
|
+
async function fromRecord(bodyId, record, tiers = tiersOf) {
|
|
28667
|
+
if (!record?.map_texture_available || !record.texture) return {};
|
|
28668
|
+
const surface = pickTexture([{
|
|
28669
|
+
...record.texture,
|
|
28244
28670
|
id: bodyId,
|
|
28245
|
-
tiers
|
|
28246
|
-
|
|
28247
|
-
|
|
28248
|
-
|
|
28249
|
-
|
|
28250
|
-
|
|
28251
|
-
|
|
28252
|
-
|
|
28253
|
-
|
|
28671
|
+
tiers: void 0
|
|
28672
|
+
}, ...record.alternates ?? []]);
|
|
28673
|
+
if (!surface) return {};
|
|
28674
|
+
const written = surface.tiers ?? await tiers(surface.id);
|
|
28675
|
+
if (written.length === 0) return {};
|
|
28676
|
+
const { frames, ...bundle } = surface;
|
|
28677
|
+
const radii = record.radii;
|
|
28678
|
+
return {
|
|
28679
|
+
surface: {
|
|
28680
|
+
...bundle,
|
|
28681
|
+
tiers: written,
|
|
28682
|
+
monthlyFrames: frames
|
|
28683
|
+
},
|
|
28684
|
+
radiusKm: radii ? (radii.a + radii.b + radii.c) / 3 : void 0
|
|
28685
|
+
};
|
|
28686
|
+
}
|
|
28687
|
+
async function fromBundle(bodyId) {
|
|
28688
|
+
return fromRecord(bodyId, (await fetchObjectDetail(bodyId, false).catch(() => null))?.global);
|
|
28254
28689
|
}
|
|
28255
28690
|
async function loadBodySources(bodyId) {
|
|
28256
28691
|
const systemId = systemIdFor(bodyId);
|
|
@@ -28551,14 +28986,14 @@ var RASTER_LAYERS = [
|
|
|
28551
28986
|
function isRaster(layer) {
|
|
28552
28987
|
return layer.kind === "raster";
|
|
28553
28988
|
}
|
|
28554
|
-
var onceTarget = /* @__PURE__ */ new WeakMap();
|
|
28989
|
+
var onceTarget$1 = /* @__PURE__ */ new WeakMap();
|
|
28555
28990
|
var FlatMap = class {
|
|
28556
28991
|
container = null;
|
|
28557
28992
|
root = document.createElement("div");
|
|
28558
28993
|
canvas = document.createElement("canvas");
|
|
28559
|
-
svg = document.createElementNS(SVG_NS, "svg");
|
|
28560
|
-
layerGroup = document.createElementNS(SVG_NS, "g");
|
|
28561
|
-
shapeGroup = document.createElementNS(SVG_NS, "g");
|
|
28994
|
+
svg = document.createElementNS(SVG_NS$1, "svg");
|
|
28995
|
+
layerGroup = document.createElementNS(SVG_NS$1, "g");
|
|
28996
|
+
shapeGroup = document.createElementNS(SVG_NS$1, "g");
|
|
28562
28997
|
markerLayer = document.createElement("div");
|
|
28563
28998
|
ctx;
|
|
28564
28999
|
overlay;
|
|
@@ -28929,7 +29364,7 @@ var FlatMap = class {
|
|
|
28929
29364
|
off();
|
|
28930
29365
|
listener(...args);
|
|
28931
29366
|
});
|
|
28932
|
-
onceTarget.set(wrapped, listener);
|
|
29367
|
+
onceTarget$1.set(wrapped, listener);
|
|
28933
29368
|
const off = this.on(event, wrapped);
|
|
28934
29369
|
return off;
|
|
28935
29370
|
}
|
|
@@ -28937,7 +29372,7 @@ var FlatMap = class {
|
|
|
28937
29372
|
const set = this.listeners.get(event);
|
|
28938
29373
|
if (!set) return;
|
|
28939
29374
|
set.delete(listener);
|
|
28940
|
-
for (const held of set) if (onceTarget.get(held) === listener) set.delete(held);
|
|
29375
|
+
for (const held of set) if (onceTarget$1.get(held) === listener) set.delete(held);
|
|
28941
29376
|
}
|
|
28942
29377
|
emit(event, ...args) {
|
|
28943
29378
|
const set = this.listeners.get(event);
|
|
@@ -29141,7 +29576,7 @@ var FlatMap = class {
|
|
|
29141
29576
|
this.layerGroup.replaceChildren();
|
|
29142
29577
|
for (const layer of this.layerList) {
|
|
29143
29578
|
if (isRaster(layer) || !layer.visible) continue;
|
|
29144
|
-
const group = document.createElementNS(SVG_NS, "g");
|
|
29579
|
+
const group = document.createElementNS(SVG_NS$1, "g");
|
|
29145
29580
|
group.setAttribute("data-layer", layer.id);
|
|
29146
29581
|
layer.render(viewport, group);
|
|
29147
29582
|
this.layerGroup.append(group);
|
|
@@ -30074,6 +30509,1455 @@ var PanoramaView = class {
|
|
|
30074
30509
|
};
|
|
30075
30510
|
};
|
|
30076
30511
|
|
|
30512
|
+
//#endregion
|
|
30513
|
+
//#region src/lib/fetch/groups/planetary-systems-map.ts
|
|
30514
|
+
var fetchPlanetarySystemsMap = memoizedGzJson(() => `${dataBase()}/v1/groups/__planetary_systems_map__.json.gz`, { error: (res) => `Failed to fetch planetary systems map: ${res.status}` });
|
|
30515
|
+
|
|
30516
|
+
//#endregion
|
|
30517
|
+
//#region src/lib/fetch/groups/solar-system-map.ts
|
|
30518
|
+
var fetchSolarSystemMap = memoizedGzJson(() => `${dataBase()}/v1/groups/__solar_system_map__.json.gz`, { error: (res) => `Failed to fetch solar system map: ${res.status}` });
|
|
30519
|
+
|
|
30520
|
+
//#endregion
|
|
30521
|
+
//#region src/lib/charts/scrub.ts
|
|
30522
|
+
var DRAG_SLOP = 8;
|
|
30523
|
+
function createScrub({ onScrub, onEnd }) {
|
|
30524
|
+
let downX = null;
|
|
30525
|
+
let downY = 0;
|
|
30526
|
+
let scrubbing = false;
|
|
30527
|
+
function end() {
|
|
30528
|
+
if (scrubbing) onEnd();
|
|
30529
|
+
downX = null;
|
|
30530
|
+
scrubbing = false;
|
|
30531
|
+
}
|
|
30532
|
+
return {
|
|
30533
|
+
onpointerdown(e) {
|
|
30534
|
+
if (e.pointerType === "mouse") return;
|
|
30535
|
+
downX = e.clientX;
|
|
30536
|
+
downY = e.clientY;
|
|
30537
|
+
scrubbing = false;
|
|
30538
|
+
},
|
|
30539
|
+
onpointermove(e) {
|
|
30540
|
+
if (e.pointerType === "mouse" || downX === null) return;
|
|
30541
|
+
if (!scrubbing && Math.hypot(e.clientX - downX, e.clientY - downY) < 8) return;
|
|
30542
|
+
scrubbing = true;
|
|
30543
|
+
onScrub(e.clientX, e.clientY);
|
|
30544
|
+
},
|
|
30545
|
+
onpointerup: end,
|
|
30546
|
+
onpointercancel: end,
|
|
30547
|
+
onpointerleave: end
|
|
30548
|
+
};
|
|
30549
|
+
}
|
|
30550
|
+
|
|
30551
|
+
//#endregion
|
|
30552
|
+
//#region src/lib/systemmap/layout.ts
|
|
30553
|
+
var VIEW_H = 240;
|
|
30554
|
+
var X_LEFT = 44;
|
|
30555
|
+
var X_RIGHT = 700;
|
|
30556
|
+
var CY = 120;
|
|
30557
|
+
var MIN_R = 2.2;
|
|
30558
|
+
var MAX_OFFSET = 84;
|
|
30559
|
+
var PRIMARY_LIMB_X = 44 - 4;
|
|
30560
|
+
var MOON_GAP = 4;
|
|
30561
|
+
var MOON_SPACING = 3;
|
|
30562
|
+
var HIT_MARGIN = 16;
|
|
30563
|
+
var INSIDE = 1e4;
|
|
30564
|
+
var MOON_ZONE_W = 34;
|
|
30565
|
+
var MOON_ZONE_PAD = 7;
|
|
30566
|
+
var RING_FORESHORTEN = .42;
|
|
30567
|
+
var BAND_Y = 26;
|
|
30568
|
+
var BAND_H = 240 - 60;
|
|
30569
|
+
var BAND_MIN_HIT = 30;
|
|
30570
|
+
function minDistance(cx, cy, placed) {
|
|
30571
|
+
let min = Infinity;
|
|
30572
|
+
for (const p of placed) min = Math.min(min, Math.hypot(p.cx - cx, p.cy - cy));
|
|
30573
|
+
return min;
|
|
30574
|
+
}
|
|
30575
|
+
function foldTilt(tiltDeg) {
|
|
30576
|
+
return tiltDeg > 90 ? 180 - tiltDeg : tiltDeg;
|
|
30577
|
+
}
|
|
30578
|
+
function layout(model) {
|
|
30579
|
+
const logLo = Math.log10(model.domain[0]);
|
|
30580
|
+
const logSpan = Math.log10(model.domain[1]) - logLo;
|
|
30581
|
+
const xOf = (km) => {
|
|
30582
|
+
return 44 + (Math.log10(Math.max(km / model.unitKm, 1e-9)) - logLo) / logSpan * (700 - 44);
|
|
30583
|
+
};
|
|
30584
|
+
const bodies = [];
|
|
30585
|
+
for (const b of [...model.bodies].sort((a, b) => a.aKm - b.aKm)) {
|
|
30586
|
+
const cx = xOf(b.aKm);
|
|
30587
|
+
const r = Math.max(MIN_R, b.radiusKm * model.pxPerKm);
|
|
30588
|
+
let cy = 120;
|
|
30589
|
+
if (b.tiltDeg > 0) {
|
|
30590
|
+
const offset = Math.min(foldTilt(b.tiltDeg) * model.pxPerDeg, MAX_OFFSET);
|
|
30591
|
+
const up = 120 - offset;
|
|
30592
|
+
const down = 120 + offset;
|
|
30593
|
+
cy = minDistance(cx, up, bodies) >= minDistance(cx, down, bodies) ? up : down;
|
|
30594
|
+
}
|
|
30595
|
+
bodies.push({
|
|
30596
|
+
...b,
|
|
30597
|
+
cx,
|
|
30598
|
+
cy,
|
|
30599
|
+
r
|
|
30600
|
+
});
|
|
30601
|
+
}
|
|
30602
|
+
const moonZones = [];
|
|
30603
|
+
for (const parent of bodies) {
|
|
30604
|
+
if (!parent.satellites?.length) continue;
|
|
30605
|
+
const ringTop = parent.rings ? parent.r * parent.rings.outer * RING_FORESHORTEN : 0;
|
|
30606
|
+
let y = parent.cy - Math.max(parent.r, ringTop) - MOON_GAP;
|
|
30607
|
+
const moons = [];
|
|
30608
|
+
for (const s of [...parent.satellites].sort((a, b) => b.radiusKm - a.radiusKm)) {
|
|
30609
|
+
const r = Math.max(MIN_R, s.radiusKm * model.pxPerKm);
|
|
30610
|
+
y -= r;
|
|
30611
|
+
moons.push({
|
|
30612
|
+
...s,
|
|
30613
|
+
cx: parent.cx,
|
|
30614
|
+
cy: y,
|
|
30615
|
+
r,
|
|
30616
|
+
hitY: 0,
|
|
30617
|
+
hitHeight: 0
|
|
30618
|
+
});
|
|
30619
|
+
y -= r + MOON_SPACING;
|
|
30620
|
+
}
|
|
30621
|
+
const top = Math.min(...moons.map((mn) => mn.cy - mn.r)) - MOON_ZONE_PAD;
|
|
30622
|
+
const bottom = Math.max(...moons.map((mn) => mn.cy + mn.r)) + MOON_ZONE_PAD;
|
|
30623
|
+
moons.forEach((mn, idx) => {
|
|
30624
|
+
const below = idx === 0 ? bottom : (moons[idx - 1].cy + mn.cy) / 2;
|
|
30625
|
+
const above = idx === moons.length - 1 ? top : (moons[idx + 1].cy + mn.cy) / 2;
|
|
30626
|
+
mn.hitY = above;
|
|
30627
|
+
mn.hitHeight = below - above;
|
|
30628
|
+
});
|
|
30629
|
+
moonZones.push({
|
|
30630
|
+
parent,
|
|
30631
|
+
moons,
|
|
30632
|
+
x: parent.cx - MOON_ZONE_W / 2,
|
|
30633
|
+
y: top,
|
|
30634
|
+
width: MOON_ZONE_W,
|
|
30635
|
+
height: bottom - top,
|
|
30636
|
+
perMoon: !parent.grouped && !parent.satellitesLink && moons.some((mn) => mn.link)
|
|
30637
|
+
});
|
|
30638
|
+
}
|
|
30639
|
+
const bands = model.bands.map((b) => {
|
|
30640
|
+
const x = xOf(b.innerKm);
|
|
30641
|
+
const width = xOf(b.outerKm) - x;
|
|
30642
|
+
const hitWidth = Math.max(width, BAND_MIN_HIT);
|
|
30643
|
+
return {
|
|
30644
|
+
...b,
|
|
30645
|
+
x,
|
|
30646
|
+
width,
|
|
30647
|
+
hitX: x + (width - hitWidth) / 2,
|
|
30648
|
+
hitWidth
|
|
30649
|
+
};
|
|
30650
|
+
}).sort((a, b) => b.width - a.width);
|
|
30651
|
+
let cloudPath = "";
|
|
30652
|
+
model.cloud?.points.forEach((p, idx) => {
|
|
30653
|
+
const aUnits = p.aKm / model.unitKm;
|
|
30654
|
+
if (aUnits < model.domain[0] || aUnits > model.domain[1]) return;
|
|
30655
|
+
const offset = Math.min(Math.max(foldTilt(p.tiltDeg), 0) * model.pxPerDeg, MAX_OFFSET);
|
|
30656
|
+
const cy = 120 + (idx % 2 === 0 ? -offset : offset);
|
|
30657
|
+
cloudPath += `M${xOf(p.aKm).toFixed(1)} ${cy.toFixed(1)}h0.01`;
|
|
30658
|
+
});
|
|
30659
|
+
const primaryR = model.primary.radiusKm * model.pxPerKm;
|
|
30660
|
+
return {
|
|
30661
|
+
bodies,
|
|
30662
|
+
moonZones,
|
|
30663
|
+
bands,
|
|
30664
|
+
cloudPath,
|
|
30665
|
+
primaryR,
|
|
30666
|
+
primaryCx: PRIMARY_LIMB_X - primaryR,
|
|
30667
|
+
xOf
|
|
30668
|
+
};
|
|
30669
|
+
}
|
|
30670
|
+
function inside(z, vx, vy) {
|
|
30671
|
+
return vx >= z.x && vx <= z.x + z.width && vy >= z.y && vy <= z.y + z.height;
|
|
30672
|
+
}
|
|
30673
|
+
function nearestBody(l, vx, vy, reach) {
|
|
30674
|
+
for (const pickable of [true, false]) {
|
|
30675
|
+
let nearest = null;
|
|
30676
|
+
let gap = reach;
|
|
30677
|
+
for (const b of l.bodies) {
|
|
30678
|
+
if (!!b.link !== pickable) continue;
|
|
30679
|
+
const centre = Math.hypot(vx - b.cx, vy - b.cy);
|
|
30680
|
+
const edge = centre <= b.r ? centre - INSIDE : centre - b.r;
|
|
30681
|
+
if (edge > gap) continue;
|
|
30682
|
+
nearest = b;
|
|
30683
|
+
gap = edge;
|
|
30684
|
+
}
|
|
30685
|
+
if (nearest) return nearest;
|
|
30686
|
+
}
|
|
30687
|
+
return null;
|
|
30688
|
+
}
|
|
30689
|
+
function hitTest(l, vx, vy) {
|
|
30690
|
+
const on = nearestBody(l, vx, vy, 0);
|
|
30691
|
+
if (on) return {
|
|
30692
|
+
kind: "body",
|
|
30693
|
+
id: on.id
|
|
30694
|
+
};
|
|
30695
|
+
for (const z of l.moonZones) {
|
|
30696
|
+
if (!inside(z, vx, vy)) continue;
|
|
30697
|
+
if (z.parent.grouped) return {
|
|
30698
|
+
kind: "body",
|
|
30699
|
+
id: z.parent.id
|
|
30700
|
+
};
|
|
30701
|
+
if (!z.perMoon) return {
|
|
30702
|
+
kind: "stack",
|
|
30703
|
+
id: z.parent.id
|
|
30704
|
+
};
|
|
30705
|
+
const moon = z.moons.find((mn) => vy >= mn.hitY && vy <= mn.hitY + mn.hitHeight);
|
|
30706
|
+
if (moon) return {
|
|
30707
|
+
kind: "moon",
|
|
30708
|
+
parent: z.parent.id,
|
|
30709
|
+
id: moon.id
|
|
30710
|
+
};
|
|
30711
|
+
}
|
|
30712
|
+
const near = nearestBody(l, vx, vy, 16);
|
|
30713
|
+
if (near) return {
|
|
30714
|
+
kind: "body",
|
|
30715
|
+
id: near.id
|
|
30716
|
+
};
|
|
30717
|
+
for (let i = l.bands.length - 1; i >= 0; i--) {
|
|
30718
|
+
const band = l.bands[i];
|
|
30719
|
+
if (vx >= band.hitX && vx <= band.hitX + band.hitWidth && vy >= 26 && vy <= 26 + 180) return {
|
|
30720
|
+
kind: "band",
|
|
30721
|
+
key: band.key
|
|
30722
|
+
};
|
|
30723
|
+
}
|
|
30724
|
+
if (Math.hypot(vx - l.primaryCx, vy - 120) <= l.primaryR) return { kind: "primary" };
|
|
30725
|
+
return null;
|
|
30726
|
+
}
|
|
30727
|
+
function tipFor(model, l, hover) {
|
|
30728
|
+
if (!hover) return null;
|
|
30729
|
+
const { text } = model;
|
|
30730
|
+
switch (hover.kind) {
|
|
30731
|
+
case "primary": return {
|
|
30732
|
+
cx: Math.max(44, l.primaryCx),
|
|
30733
|
+
cy: 120 - Math.min(l.primaryR, 120 - 40),
|
|
30734
|
+
title: model.primary.name,
|
|
30735
|
+
sub: text.primary
|
|
30736
|
+
};
|
|
30737
|
+
case "body": {
|
|
30738
|
+
const b = l.bodies.find((p) => p.id === hover.id);
|
|
30739
|
+
if (!b) return null;
|
|
30740
|
+
const parts = [text.distance(b.aKm)];
|
|
30741
|
+
if (b.tiltDeg > 90) parts.push(text.retrograde);
|
|
30742
|
+
if (b.grouped && b.satellites?.length) parts.push(text.moons(b.satelliteCount ?? b.satellites.length));
|
|
30743
|
+
return {
|
|
30744
|
+
cx: b.cx,
|
|
30745
|
+
cy: b.cy - b.r - 4,
|
|
30746
|
+
title: b.name,
|
|
30747
|
+
sub: parts.join(" · ")
|
|
30748
|
+
};
|
|
30749
|
+
}
|
|
30750
|
+
case "stack": {
|
|
30751
|
+
const zone = l.moonZones.find((z) => z.parent.id === hover.id);
|
|
30752
|
+
if (!zone) return null;
|
|
30753
|
+
const cx = zone.x + zone.width / 2;
|
|
30754
|
+
return zone.parent.satellitesTab ? {
|
|
30755
|
+
cx,
|
|
30756
|
+
cy: zone.y,
|
|
30757
|
+
title: zone.parent.name,
|
|
30758
|
+
sub: text.moons(zone.parent.satelliteCount ?? zone.moons.length)
|
|
30759
|
+
} : {
|
|
30760
|
+
cx,
|
|
30761
|
+
cy: zone.y,
|
|
30762
|
+
title: zone.moons[0].name,
|
|
30763
|
+
sub: zone.parent.name
|
|
30764
|
+
};
|
|
30765
|
+
}
|
|
30766
|
+
case "moon": {
|
|
30767
|
+
const zone = l.moonZones.find((z) => z.parent.id === hover.parent);
|
|
30768
|
+
const moon = zone?.moons.find((mn) => mn.id === hover.id);
|
|
30769
|
+
if (!zone || !moon) return null;
|
|
30770
|
+
return {
|
|
30771
|
+
cx: moon.cx,
|
|
30772
|
+
cy: moon.cy - moon.r - 4,
|
|
30773
|
+
title: moon.name,
|
|
30774
|
+
sub: zone.parent.name
|
|
30775
|
+
};
|
|
30776
|
+
}
|
|
30777
|
+
case "band": {
|
|
30778
|
+
const band = l.bands.find((bd) => bd.key === hover.key);
|
|
30779
|
+
if (!band) return null;
|
|
30780
|
+
return {
|
|
30781
|
+
cx: band.x + band.width / 2,
|
|
30782
|
+
cy: 44,
|
|
30783
|
+
title: band.name ?? band.label,
|
|
30784
|
+
sub: band.sub ?? `${text.distance(band.innerKm)}–${text.distance(band.outerKm)}`
|
|
30785
|
+
};
|
|
30786
|
+
}
|
|
30787
|
+
}
|
|
30788
|
+
}
|
|
30789
|
+
|
|
30790
|
+
//#endregion
|
|
30791
|
+
//#region src/lib/systemmap/chart.ts
|
|
30792
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
30793
|
+
var PRIMARY_RIM = 10;
|
|
30794
|
+
var RING_TILT = -18;
|
|
30795
|
+
var CLOUD_R = 1.2;
|
|
30796
|
+
var TIP_H = 28;
|
|
30797
|
+
var TIP_MARGIN = 6;
|
|
30798
|
+
var LABEL_MARGIN = 4;
|
|
30799
|
+
var LABEL_SIZE = 18;
|
|
30800
|
+
var LABEL_MIN_SIZE = 12;
|
|
30801
|
+
var LABEL_MAX_W = 190;
|
|
30802
|
+
function svg(tag, attrs = {}, ...children) {
|
|
30803
|
+
const el = document.createElementNS(SVG_NS, tag);
|
|
30804
|
+
for (const [name, value] of Object.entries(attrs)) if (value !== void 0) el.setAttribute(name, String(value));
|
|
30805
|
+
for (const child of children) if (child) el.append(child);
|
|
30806
|
+
return el;
|
|
30807
|
+
}
|
|
30808
|
+
function sameHover(a, b) {
|
|
30809
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
30810
|
+
}
|
|
30811
|
+
function linksOf(model, l) {
|
|
30812
|
+
const links = /* @__PURE__ */ new Map();
|
|
30813
|
+
const add = (hover, link) => {
|
|
30814
|
+
if (link?.href || link?.onclick) links.set(JSON.stringify(hover), link);
|
|
30815
|
+
};
|
|
30816
|
+
for (const band of l.bands) add({
|
|
30817
|
+
kind: "band",
|
|
30818
|
+
key: band.key
|
|
30819
|
+
}, band);
|
|
30820
|
+
add({ kind: "primary" }, model.primary.link);
|
|
30821
|
+
for (const b of l.bodies) add({
|
|
30822
|
+
kind: "body",
|
|
30823
|
+
id: b.id
|
|
30824
|
+
}, b.link);
|
|
30825
|
+
for (const z of l.moonZones) {
|
|
30826
|
+
if (z.parent.grouped) continue;
|
|
30827
|
+
if (!z.perMoon) add({
|
|
30828
|
+
kind: "stack",
|
|
30829
|
+
id: z.parent.id
|
|
30830
|
+
}, z.parent.satellitesLink);
|
|
30831
|
+
else for (const mn of z.moons) add({
|
|
30832
|
+
kind: "moon",
|
|
30833
|
+
parent: z.parent.id,
|
|
30834
|
+
id: mn.id
|
|
30835
|
+
}, mn.link);
|
|
30836
|
+
}
|
|
30837
|
+
return links;
|
|
30838
|
+
}
|
|
30839
|
+
var nextUid = 0;
|
|
30840
|
+
var SystemMapChart = class {
|
|
30841
|
+
root = document.createElement("div");
|
|
30842
|
+
svg = svg("svg");
|
|
30843
|
+
tip = document.createElement("div");
|
|
30844
|
+
tipTitle = document.createElement("span");
|
|
30845
|
+
tipSub = document.createElement("span");
|
|
30846
|
+
uid = `sm-system-map-${nextUid++}`;
|
|
30847
|
+
resize = new ResizeObserver(() => {
|
|
30848
|
+
this.fitLabels();
|
|
30849
|
+
this.placeTip();
|
|
30850
|
+
});
|
|
30851
|
+
model = null;
|
|
30852
|
+
placed = null;
|
|
30853
|
+
background = false;
|
|
30854
|
+
hover = null;
|
|
30855
|
+
highlights = /* @__PURE__ */ new Map();
|
|
30856
|
+
targets = /* @__PURE__ */ new Map();
|
|
30857
|
+
links = /* @__PURE__ */ new Map();
|
|
30858
|
+
shape = "";
|
|
30859
|
+
labels = [];
|
|
30860
|
+
constructor() {
|
|
30861
|
+
this.tip.className = "sm-system-map__tip";
|
|
30862
|
+
this.tipTitle.className = "sm-system-map__tip-title";
|
|
30863
|
+
this.tipSub.className = "sm-system-map__tip-sub";
|
|
30864
|
+
this.tip.append(this.tipTitle, this.tipSub);
|
|
30865
|
+
this.tip.hidden = true;
|
|
30866
|
+
this.root.append(this.svg, this.tip);
|
|
30867
|
+
this.resize.observe(this.root);
|
|
30868
|
+
const scrub = createScrub({
|
|
30869
|
+
onScrub: (x, y) => this.setHover(this.hitAt(x, y)),
|
|
30870
|
+
onEnd: () => this.setHover(null)
|
|
30871
|
+
});
|
|
30872
|
+
this.svg.addEventListener("pointerdown", scrub.onpointerdown);
|
|
30873
|
+
this.svg.addEventListener("pointermove", scrub.onpointermove);
|
|
30874
|
+
this.svg.addEventListener("pointerup", scrub.onpointerup);
|
|
30875
|
+
this.svg.addEventListener("pointercancel", scrub.onpointercancel);
|
|
30876
|
+
this.svg.addEventListener("pointerleave", scrub.onpointerleave);
|
|
30877
|
+
let at = [0, 0];
|
|
30878
|
+
const track = (e) => {
|
|
30879
|
+
at = [e.clientX, e.clientY];
|
|
30880
|
+
};
|
|
30881
|
+
this.svg.addEventListener("pointerdown", track);
|
|
30882
|
+
this.svg.addEventListener("pointerup", track);
|
|
30883
|
+
this.svg.addEventListener("pointermove", (e) => {
|
|
30884
|
+
track(e);
|
|
30885
|
+
if (e.pointerType === "mouse") this.setHover(this.hitAt(e.clientX, e.clientY));
|
|
30886
|
+
});
|
|
30887
|
+
this.svg.addEventListener("pointerleave", (e) => {
|
|
30888
|
+
if (e.pointerType === "mouse") this.setHover(null);
|
|
30889
|
+
});
|
|
30890
|
+
this.svg.addEventListener("click", (e) => {
|
|
30891
|
+
if (e.detail === 0) return;
|
|
30892
|
+
const hit = Math.abs(at[0] - e.clientX) < 1 && Math.abs(at[1] - e.clientY) < 1 ? this.hitAt(...at) : this.hitAt(e.clientX, e.clientY);
|
|
30893
|
+
if (!hit) return;
|
|
30894
|
+
const meant = this.targets.get(JSON.stringify(hit));
|
|
30895
|
+
if (meant?.contains(e.target)) return;
|
|
30896
|
+
e.stopPropagation();
|
|
30897
|
+
e.preventDefault();
|
|
30898
|
+
meant?.dispatchEvent(new MouseEvent("click", {
|
|
30899
|
+
bubbles: true,
|
|
30900
|
+
cancelable: true,
|
|
30901
|
+
ctrlKey: e.ctrlKey,
|
|
30902
|
+
metaKey: e.metaKey,
|
|
30903
|
+
shiftKey: e.shiftKey,
|
|
30904
|
+
altKey: e.altKey,
|
|
30905
|
+
button: e.button
|
|
30906
|
+
}));
|
|
30907
|
+
}, true);
|
|
30908
|
+
}
|
|
30909
|
+
destroy() {
|
|
30910
|
+
this.resize.disconnect();
|
|
30911
|
+
this.root.remove();
|
|
30912
|
+
}
|
|
30913
|
+
update(model, options) {
|
|
30914
|
+
this.model = model;
|
|
30915
|
+
this.placed = layout(model);
|
|
30916
|
+
const links = linksOf(model, this.placed);
|
|
30917
|
+
const shape = JSON.stringify([
|
|
30918
|
+
JSON.stringify(model, (key, value) => key === "href" ? void 0 : value),
|
|
30919
|
+
options,
|
|
30920
|
+
[...links].map(([key, link]) => [
|
|
30921
|
+
key,
|
|
30922
|
+
!!link.href,
|
|
30923
|
+
!!link.onclick
|
|
30924
|
+
])
|
|
30925
|
+
]);
|
|
30926
|
+
this.links = links;
|
|
30927
|
+
if (shape === this.shape) {
|
|
30928
|
+
for (const [key, el] of this.targets) {
|
|
30929
|
+
const href = links.get(key)?.href;
|
|
30930
|
+
if (href) el.setAttribute("href", href);
|
|
30931
|
+
}
|
|
30932
|
+
return;
|
|
30933
|
+
}
|
|
30934
|
+
this.shape = shape;
|
|
30935
|
+
this.background = options.variant === "background";
|
|
30936
|
+
this.hover = null;
|
|
30937
|
+
this.root.className = this.background ? "sm-system-map sm-system-map--background" : "sm-system-map";
|
|
30938
|
+
this.draw(model, this.placed, options.ariaLabel);
|
|
30939
|
+
this.fitLabels();
|
|
30940
|
+
this.placeTip();
|
|
30941
|
+
}
|
|
30942
|
+
draw(model, l, ariaLabel) {
|
|
30943
|
+
const bg = this.background;
|
|
30944
|
+
this.highlights = /* @__PURE__ */ new Map();
|
|
30945
|
+
this.targets = /* @__PURE__ */ new Map();
|
|
30946
|
+
this.labels = [];
|
|
30947
|
+
const root = this.svg;
|
|
30948
|
+
root.replaceChildren();
|
|
30949
|
+
root.setAttribute("viewBox", bg && model.backgroundView || `0 0 ${720} ${240}`);
|
|
30950
|
+
root.setAttribute("preserveAspectRatio", bg ? model.backgroundFit === "fit" ? "xMidYMid meet" : model.backgroundView ? "xMinYMid slice" : "xMidYMid slice" : "xMidYMid meet");
|
|
30951
|
+
root.setAttribute("class", "sm-system-map__svg");
|
|
30952
|
+
root.setAttribute("role", "group");
|
|
30953
|
+
root.setAttribute("aria-label", ariaLabel);
|
|
30954
|
+
root.setAttribute("data-vaul-no-drag", "");
|
|
30955
|
+
const stop = (offset, opacity) => svg("stop", {
|
|
30956
|
+
offset,
|
|
30957
|
+
"stop-color": model.primary.color,
|
|
30958
|
+
"stop-opacity": opacity
|
|
30959
|
+
});
|
|
30960
|
+
root.append(svg("defs", {}, svg("radialGradient", {
|
|
30961
|
+
id: `${this.uid}-primary`,
|
|
30962
|
+
gradientUnits: "userSpaceOnUse",
|
|
30963
|
+
cx: l.primaryCx,
|
|
30964
|
+
cy: 120,
|
|
30965
|
+
r: l.primaryR
|
|
30966
|
+
}, stop("0%", 1), stop(Math.max(0, 1 - PRIMARY_RIM / l.primaryR), .85), stop("100%", .35)), svg("pattern", {
|
|
30967
|
+
id: `${this.uid}-band`,
|
|
30968
|
+
width: 9,
|
|
30969
|
+
height: 9,
|
|
30970
|
+
patternUnits: "userSpaceOnUse"
|
|
30971
|
+
}, svg("circle", {
|
|
30972
|
+
cx: 2,
|
|
30973
|
+
cy: 2,
|
|
30974
|
+
r: .9,
|
|
30975
|
+
fill: "currentColor",
|
|
30976
|
+
opacity: .55
|
|
30977
|
+
}), svg("circle", {
|
|
30978
|
+
cx: 6.5,
|
|
30979
|
+
cy: 6,
|
|
30980
|
+
r: .9,
|
|
30981
|
+
fill: "currentColor",
|
|
30982
|
+
opacity: .55
|
|
30983
|
+
}))));
|
|
30984
|
+
if (!bg) this.drawAxis(model, l);
|
|
30985
|
+
if (l.cloudPath) root.append(svg("path", {
|
|
30986
|
+
d: l.cloudPath,
|
|
30987
|
+
stroke: model.cloud?.color,
|
|
30988
|
+
"stroke-width": CLOUD_R * 2,
|
|
30989
|
+
"stroke-linecap": "round",
|
|
30990
|
+
fill: "none",
|
|
30991
|
+
opacity: .45,
|
|
30992
|
+
"pointer-events": "none"
|
|
30993
|
+
}));
|
|
30994
|
+
for (const band of l.bands) {
|
|
30995
|
+
const fill = svg("rect", {
|
|
30996
|
+
x: band.x,
|
|
30997
|
+
y: 26,
|
|
30998
|
+
width: band.width,
|
|
30999
|
+
height: 180,
|
|
31000
|
+
fill: `url(#${this.uid}-band)`,
|
|
31001
|
+
class: `sm-system-map__band sm-system-map__band--${band.tone}`
|
|
31002
|
+
});
|
|
31003
|
+
if (bg) {
|
|
31004
|
+
root.append(fill);
|
|
31005
|
+
continue;
|
|
31006
|
+
}
|
|
31007
|
+
const label = svg("text", {
|
|
31008
|
+
x: band.x + band.width / 2,
|
|
31009
|
+
y: 240 - 8,
|
|
31010
|
+
"text-anchor": "middle",
|
|
31011
|
+
"font-size": LABEL_SIZE,
|
|
31012
|
+
class: `sm-system-map__band-label sm-system-map__band-label--${band.tone}`
|
|
31013
|
+
});
|
|
31014
|
+
label.textContent = band.label;
|
|
31015
|
+
this.labels.push(label);
|
|
31016
|
+
root.append(this.target(band, band.name ?? band.label, {
|
|
31017
|
+
kind: "band",
|
|
31018
|
+
key: band.key
|
|
31019
|
+
}, fill, svg("rect", {
|
|
31020
|
+
x: band.hitX,
|
|
31021
|
+
y: 26,
|
|
31022
|
+
width: band.hitWidth,
|
|
31023
|
+
height: 180,
|
|
31024
|
+
fill: "transparent"
|
|
31025
|
+
}), label));
|
|
31026
|
+
}
|
|
31027
|
+
const disc = svg("circle", {
|
|
31028
|
+
cx: l.primaryCx,
|
|
31029
|
+
cy: 120,
|
|
31030
|
+
r: l.primaryR,
|
|
31031
|
+
fill: `url(#${this.uid}-primary)`
|
|
31032
|
+
});
|
|
31033
|
+
root.append(bg ? disc : this.target(model.primary.link, model.primary.name, { kind: "primary" }, disc));
|
|
31034
|
+
for (const b of l.bodies) {
|
|
31035
|
+
if (!b.rings) continue;
|
|
31036
|
+
const mid = b.r * (b.rings.inner + b.rings.outer) / 2;
|
|
31037
|
+
root.append(svg("ellipse", {
|
|
31038
|
+
cx: b.cx,
|
|
31039
|
+
cy: b.cy,
|
|
31040
|
+
rx: mid,
|
|
31041
|
+
ry: mid * RING_FORESHORTEN,
|
|
31042
|
+
transform: `rotate(${RING_TILT} ${b.cx} ${b.cy})`,
|
|
31043
|
+
fill: "none",
|
|
31044
|
+
stroke: "#d9c89f",
|
|
31045
|
+
"stroke-width": b.r * (b.rings.outer - b.rings.inner),
|
|
31046
|
+
opacity: .45
|
|
31047
|
+
}));
|
|
31048
|
+
}
|
|
31049
|
+
const zoneOf = new Map(l.moonZones.map((z) => [z.parent.id, z]));
|
|
31050
|
+
for (const b of l.bodies) root.append(this.body(b, zoneOf.get(b.id)));
|
|
31051
|
+
for (const z of l.moonZones) if (!z.parent.grouped) this.stack(z);
|
|
31052
|
+
}
|
|
31053
|
+
drawAxis(model, l) {
|
|
31054
|
+
for (const t of model.ticks) {
|
|
31055
|
+
const x = l.xOf(t * model.unitKm);
|
|
31056
|
+
const tick = svg("text", {
|
|
31057
|
+
x,
|
|
31058
|
+
y: 16,
|
|
31059
|
+
"text-anchor": "middle",
|
|
31060
|
+
"font-size": 16,
|
|
31061
|
+
fill: "currentColor",
|
|
31062
|
+
opacity: .85
|
|
31063
|
+
});
|
|
31064
|
+
tick.textContent = String(t);
|
|
31065
|
+
this.svg.append(svg("line", {
|
|
31066
|
+
x1: x,
|
|
31067
|
+
x2: x,
|
|
31068
|
+
y1: 20,
|
|
31069
|
+
y2: 240 - 28,
|
|
31070
|
+
stroke: "currentColor",
|
|
31071
|
+
"stroke-width": .5,
|
|
31072
|
+
opacity: .12
|
|
31073
|
+
}), tick);
|
|
31074
|
+
}
|
|
31075
|
+
const unit = svg("text", {
|
|
31076
|
+
x: 700,
|
|
31077
|
+
y: 16,
|
|
31078
|
+
"text-anchor": "end",
|
|
31079
|
+
"font-size": 14,
|
|
31080
|
+
fill: "currentColor",
|
|
31081
|
+
opacity: .65
|
|
31082
|
+
});
|
|
31083
|
+
unit.textContent = model.axisLabel;
|
|
31084
|
+
this.svg.append(unit);
|
|
31085
|
+
}
|
|
31086
|
+
body(b, zone) {
|
|
31087
|
+
const dot = b.reference ? svg("circle", {
|
|
31088
|
+
cx: b.cx,
|
|
31089
|
+
cy: b.cy,
|
|
31090
|
+
r: b.r + 1,
|
|
31091
|
+
fill: "none",
|
|
31092
|
+
stroke: b.color,
|
|
31093
|
+
"stroke-width": 1.2,
|
|
31094
|
+
class: "sm-system-map__reference"
|
|
31095
|
+
}) : svg("circle", {
|
|
31096
|
+
cx: b.cx,
|
|
31097
|
+
cy: b.cy,
|
|
31098
|
+
r: b.r,
|
|
31099
|
+
fill: b.color
|
|
31100
|
+
});
|
|
31101
|
+
if (this.background) {
|
|
31102
|
+
if (!zone || !b.grouped) return dot;
|
|
31103
|
+
return svg("g", {}, dot, ...this.moonDots(zone));
|
|
31104
|
+
}
|
|
31105
|
+
const hover = {
|
|
31106
|
+
kind: "body",
|
|
31107
|
+
id: b.id
|
|
31108
|
+
};
|
|
31109
|
+
const ring = this.highlight(hover, svg("circle", {
|
|
31110
|
+
cx: b.cx,
|
|
31111
|
+
cy: b.cy,
|
|
31112
|
+
r: b.r + 3.5,
|
|
31113
|
+
fill: "none",
|
|
31114
|
+
stroke: "currentColor",
|
|
31115
|
+
"stroke-width": 1,
|
|
31116
|
+
opacity: .8
|
|
31117
|
+
}));
|
|
31118
|
+
const hit = svg("circle", {
|
|
31119
|
+
cx: b.cx,
|
|
31120
|
+
cy: b.cy,
|
|
31121
|
+
r: b.r + 16,
|
|
31122
|
+
fill: "transparent"
|
|
31123
|
+
});
|
|
31124
|
+
if (!zone || !b.grouped) return this.target(b.link, b.name, hover, ring, dot, hit);
|
|
31125
|
+
return this.target(b.link, b.name, hover, this.highlight(hover, this.zoneWash(zone)), ring, dot, hit, ...this.moonDots(zone), this.zoneHit(zone));
|
|
31126
|
+
}
|
|
31127
|
+
stack(z) {
|
|
31128
|
+
if (this.background) {
|
|
31129
|
+
this.svg.append(...this.moonDots(z));
|
|
31130
|
+
return;
|
|
31131
|
+
}
|
|
31132
|
+
const p = z.parent;
|
|
31133
|
+
if (z.perMoon) {
|
|
31134
|
+
this.svg.append(...this.moonDots(z));
|
|
31135
|
+
for (const mn of z.moons) {
|
|
31136
|
+
const hover = {
|
|
31137
|
+
kind: "moon",
|
|
31138
|
+
parent: p.id,
|
|
31139
|
+
id: mn.id
|
|
31140
|
+
};
|
|
31141
|
+
this.svg.append(this.target(mn.link, mn.name, hover, this.highlight(hover, svg("circle", {
|
|
31142
|
+
cx: mn.cx,
|
|
31143
|
+
cy: mn.cy,
|
|
31144
|
+
r: mn.r + 2.5,
|
|
31145
|
+
fill: "none",
|
|
31146
|
+
stroke: "currentColor",
|
|
31147
|
+
"stroke-width": 1,
|
|
31148
|
+
opacity: .8
|
|
31149
|
+
})), svg("rect", {
|
|
31150
|
+
x: z.x,
|
|
31151
|
+
y: mn.hitY,
|
|
31152
|
+
width: z.width,
|
|
31153
|
+
height: mn.hitHeight,
|
|
31154
|
+
fill: "transparent"
|
|
31155
|
+
})));
|
|
31156
|
+
}
|
|
31157
|
+
return;
|
|
31158
|
+
}
|
|
31159
|
+
const hover = {
|
|
31160
|
+
kind: "stack",
|
|
31161
|
+
id: p.id
|
|
31162
|
+
};
|
|
31163
|
+
const first = z.moons[0];
|
|
31164
|
+
this.svg.append(this.highlight(hover, this.zoneWash(z)), ...this.moonDots(z), this.target(p.satellitesLink, p.satellitesTab ? `${p.name} · ${this.model.text.moons(p.satelliteCount ?? z.moons.length)}` : first.name, hover, this.zoneHit(z)));
|
|
31165
|
+
}
|
|
31166
|
+
moonDots(z) {
|
|
31167
|
+
return z.moons.map((mn) => svg("circle", {
|
|
31168
|
+
cx: mn.cx,
|
|
31169
|
+
cy: mn.cy,
|
|
31170
|
+
r: mn.r,
|
|
31171
|
+
fill: mn.color
|
|
31172
|
+
}));
|
|
31173
|
+
}
|
|
31174
|
+
zoneWash(z) {
|
|
31175
|
+
return svg("rect", {
|
|
31176
|
+
x: z.x,
|
|
31177
|
+
y: z.y,
|
|
31178
|
+
width: z.width,
|
|
31179
|
+
height: z.height,
|
|
31180
|
+
rx: 4,
|
|
31181
|
+
fill: "currentColor",
|
|
31182
|
+
opacity: .1,
|
|
31183
|
+
"pointer-events": "none"
|
|
31184
|
+
});
|
|
31185
|
+
}
|
|
31186
|
+
zoneHit(z) {
|
|
31187
|
+
return svg("rect", {
|
|
31188
|
+
x: z.x,
|
|
31189
|
+
y: z.y,
|
|
31190
|
+
width: z.width,
|
|
31191
|
+
height: z.height,
|
|
31192
|
+
fill: "transparent"
|
|
31193
|
+
});
|
|
31194
|
+
}
|
|
31195
|
+
highlight(hover, el) {
|
|
31196
|
+
const key = JSON.stringify(hover);
|
|
31197
|
+
const list = this.highlights.get(key);
|
|
31198
|
+
if (list) list.push(el);
|
|
31199
|
+
else this.highlights.set(key, [el]);
|
|
31200
|
+
el.style.display = "none";
|
|
31201
|
+
return el;
|
|
31202
|
+
}
|
|
31203
|
+
target(link, label, hover, ...children) {
|
|
31204
|
+
const linked = !!(link?.href || link?.onclick);
|
|
31205
|
+
const el = link?.href ? svg("a", {
|
|
31206
|
+
href: link.href,
|
|
31207
|
+
"aria-label": label
|
|
31208
|
+
}) : linked ? svg("g", {
|
|
31209
|
+
role: "button",
|
|
31210
|
+
tabindex: 0,
|
|
31211
|
+
"aria-label": label,
|
|
31212
|
+
class: "sm-system-map__button"
|
|
31213
|
+
}) : svg("g");
|
|
31214
|
+
el.append(...children);
|
|
31215
|
+
if (!linked) return el;
|
|
31216
|
+
const key = JSON.stringify(hover);
|
|
31217
|
+
this.targets.set(key, el);
|
|
31218
|
+
el.addEventListener("focus", () => this.setHover(hover));
|
|
31219
|
+
el.addEventListener("blur", () => {
|
|
31220
|
+
if (sameHover(this.hover, hover)) this.setHover(null);
|
|
31221
|
+
});
|
|
31222
|
+
el.addEventListener("click", (e) => this.links.get(key)?.onclick?.(e));
|
|
31223
|
+
if (!link?.href) el.addEventListener("keydown", (e) => {
|
|
31224
|
+
const key = e.key;
|
|
31225
|
+
if (key !== "Enter" && key !== " ") return;
|
|
31226
|
+
e.preventDefault();
|
|
31227
|
+
el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
31228
|
+
});
|
|
31229
|
+
return el;
|
|
31230
|
+
}
|
|
31231
|
+
setHover(hover) {
|
|
31232
|
+
if (sameHover(this.hover, hover)) return;
|
|
31233
|
+
for (const el of this.highlights.get(JSON.stringify(this.hover)) ?? []) el.style.display = "none";
|
|
31234
|
+
this.targets.get(JSON.stringify(this.hover))?.removeAttribute("data-near");
|
|
31235
|
+
this.hover = hover;
|
|
31236
|
+
for (const el of this.highlights.get(JSON.stringify(hover)) ?? []) el.style.display = "";
|
|
31237
|
+
const near = this.targets.get(JSON.stringify(hover));
|
|
31238
|
+
near?.setAttribute("data-near", "");
|
|
31239
|
+
this.svg.classList.toggle("sm-system-map__svg--near", !!near);
|
|
31240
|
+
this.placeTip();
|
|
31241
|
+
}
|
|
31242
|
+
hitAt(clientX, clientY) {
|
|
31243
|
+
if (!this.placed || this.background) return null;
|
|
31244
|
+
const rect = this.svg.getBoundingClientRect();
|
|
31245
|
+
if (!rect.width || !rect.height) return null;
|
|
31246
|
+
const vx = (clientX - rect.left) / rect.width * 720;
|
|
31247
|
+
const vy = (clientY - rect.top) / rect.height * 240;
|
|
31248
|
+
return hitTest(this.placed, vx, vy);
|
|
31249
|
+
}
|
|
31250
|
+
fitLabels() {
|
|
31251
|
+
for (const label of this.labels) {
|
|
31252
|
+
let width = 0;
|
|
31253
|
+
label.setAttribute("font-size", String(LABEL_SIZE));
|
|
31254
|
+
try {
|
|
31255
|
+
width = label.getComputedTextLength();
|
|
31256
|
+
} catch {}
|
|
31257
|
+
if (!width) continue;
|
|
31258
|
+
if (width > LABEL_MAX_W) {
|
|
31259
|
+
const size = Math.max(LABEL_MIN_SIZE, LABEL_SIZE * LABEL_MAX_W / width);
|
|
31260
|
+
label.setAttribute("font-size", String(size));
|
|
31261
|
+
width *= size / LABEL_SIZE;
|
|
31262
|
+
}
|
|
31263
|
+
const centre = Number(label.dataset.centre ?? label.getAttribute("x"));
|
|
31264
|
+
label.dataset.centre = String(centre);
|
|
31265
|
+
const half = width / 2;
|
|
31266
|
+
const x = Math.min(Math.max(centre, half + LABEL_MARGIN), Math.max(half, 720 - half - LABEL_MARGIN));
|
|
31267
|
+
label.setAttribute("x", String(x));
|
|
31268
|
+
}
|
|
31269
|
+
}
|
|
31270
|
+
placeTip() {
|
|
31271
|
+
const tip = this.model && this.placed && !this.background ? tipFor(this.model, this.placed, this.hover) : null;
|
|
31272
|
+
this.tip.hidden = !tip;
|
|
31273
|
+
if (!tip) return;
|
|
31274
|
+
this.tipTitle.textContent = tip.title;
|
|
31275
|
+
this.tipSub.textContent = tip.sub ? ` · ${tip.sub}` : "";
|
|
31276
|
+
const containerW = this.root.clientWidth;
|
|
31277
|
+
const scale = containerW / 720;
|
|
31278
|
+
this.tip.style.maxWidth = `${Math.max(0, containerW - 2 * TIP_MARGIN)}px`;
|
|
31279
|
+
const half = this.tip.offsetWidth / 2;
|
|
31280
|
+
const left = Math.min(Math.max(tip.cx * scale, half + TIP_MARGIN), Math.max(half, containerW - half - TIP_MARGIN));
|
|
31281
|
+
this.tip.style.left = `${left}px`;
|
|
31282
|
+
this.tip.style.top = `${Math.max(4, tip.cy * scale - TIP_H)}px`;
|
|
31283
|
+
}
|
|
31284
|
+
};
|
|
31285
|
+
|
|
31286
|
+
//#endregion
|
|
31287
|
+
//#region src/lib/systemmap/planetary.ts
|
|
31288
|
+
function systemFromMapEntry(entry, name) {
|
|
31289
|
+
return {
|
|
31290
|
+
planetId: entry.primary.id,
|
|
31291
|
+
planetName: name,
|
|
31292
|
+
planetRadiusKm: entry.primary.radius_km,
|
|
31293
|
+
planetColor: BODY_COLORS[entry.primary.id] ?? "#cccccc",
|
|
31294
|
+
moons: entry.moons.map((mn) => ({
|
|
31295
|
+
id: mn.id,
|
|
31296
|
+
name: mn.id,
|
|
31297
|
+
aRp: mn.a_rp,
|
|
31298
|
+
tiltDeg: mn.tilt_deg,
|
|
31299
|
+
radiusKm: mn.radius_km,
|
|
31300
|
+
color: BODY_COLORS[mn.id] ?? mn.color ?? "#cccccc"
|
|
31301
|
+
})),
|
|
31302
|
+
rings: entry.rings ? {
|
|
31303
|
+
innerRp: entry.rings.inner_rp,
|
|
31304
|
+
outerRp: entry.rings.outer_rp
|
|
31305
|
+
} : null,
|
|
31306
|
+
moonCount: entry.moon_count
|
|
31307
|
+
};
|
|
31308
|
+
}
|
|
31309
|
+
var TOP_MOON_R = 14;
|
|
31310
|
+
var AT_SURFACE = 1.001;
|
|
31311
|
+
var PX_PER_DEG = .62;
|
|
31312
|
+
var TICK_LADDER = [
|
|
31313
|
+
1,
|
|
31314
|
+
2,
|
|
31315
|
+
3,
|
|
31316
|
+
5,
|
|
31317
|
+
10,
|
|
31318
|
+
20,
|
|
31319
|
+
30,
|
|
31320
|
+
50,
|
|
31321
|
+
100,
|
|
31322
|
+
200,
|
|
31323
|
+
300,
|
|
31324
|
+
500,
|
|
31325
|
+
1e3,
|
|
31326
|
+
2e3,
|
|
31327
|
+
5e3
|
|
31328
|
+
];
|
|
31329
|
+
var BG_VIEW = "0 96 320 144";
|
|
31330
|
+
var BG_VIEW_WIDE = "0 96 720 144";
|
|
31331
|
+
var EMPTY_DOMAIN = [1, 100];
|
|
31332
|
+
function domainOf(system, bands) {
|
|
31333
|
+
let lo = Math.min(...system.moons.map((mn) => mn.aRp));
|
|
31334
|
+
let hi = Math.max(...system.moons.map((mn) => mn.aRp));
|
|
31335
|
+
for (const b of bands) {
|
|
31336
|
+
lo = Math.min(lo, b.innerKm / system.planetRadiusKm);
|
|
31337
|
+
hi = Math.max(hi, b.outerKm / system.planetRadiusKm);
|
|
31338
|
+
}
|
|
31339
|
+
if (!Number.isFinite(lo) || !Number.isFinite(hi)) return EMPTY_DOMAIN;
|
|
31340
|
+
if (hi <= lo) {
|
|
31341
|
+
lo /= 2;
|
|
31342
|
+
hi *= 2;
|
|
31343
|
+
}
|
|
31344
|
+
lo = lo <= AT_SURFACE ? 1 : lo;
|
|
31345
|
+
return [lo > 1 ? 10 ** (Math.log10(lo) - .12) : 1, 10 ** (Math.log10(hi) + .12)];
|
|
31346
|
+
}
|
|
31347
|
+
function planetarySystemModel(system, parts) {
|
|
31348
|
+
const rKm = system.planetRadiusKm;
|
|
31349
|
+
const domain = domainOf(system, parts.bands);
|
|
31350
|
+
return {
|
|
31351
|
+
primary: {
|
|
31352
|
+
id: system.planetId,
|
|
31353
|
+
name: system.planetName,
|
|
31354
|
+
radiusKm: rKm,
|
|
31355
|
+
color: system.planetColor,
|
|
31356
|
+
link: parts.primaryLink
|
|
31357
|
+
},
|
|
31358
|
+
bodies: system.moons.map((mn) => ({
|
|
31359
|
+
id: mn.id,
|
|
31360
|
+
name: mn.name,
|
|
31361
|
+
aKm: mn.aRp * rKm,
|
|
31362
|
+
tiltDeg: mn.tiltDeg,
|
|
31363
|
+
radiusKm: mn.radiusKm,
|
|
31364
|
+
color: mn.color,
|
|
31365
|
+
link: parts.moonLink?.(mn)
|
|
31366
|
+
})),
|
|
31367
|
+
bands: parts.bands,
|
|
31368
|
+
cloud: parts.cloud,
|
|
31369
|
+
unitKm: rKm,
|
|
31370
|
+
domain,
|
|
31371
|
+
ticks: TICK_LADDER.filter((t) => t >= domain[0] && t <= domain[1]),
|
|
31372
|
+
axisLabel: parts.axisLabel,
|
|
31373
|
+
pxPerKm: TOP_MOON_R / Math.max(...system.moons.map((mn) => mn.radiusKm).filter((r) => r > 0), 1),
|
|
31374
|
+
pxPerDeg: PX_PER_DEG,
|
|
31375
|
+
text: parts.text,
|
|
31376
|
+
backgroundView: parts.wide ? BG_VIEW_WIDE : BG_VIEW,
|
|
31377
|
+
backgroundFit: parts.wide ? "fit" : "slice"
|
|
31378
|
+
};
|
|
31379
|
+
}
|
|
31380
|
+
|
|
31381
|
+
//#endregion
|
|
31382
|
+
//#region src/lib/systemmap/solar.ts
|
|
31383
|
+
var SOLAR_PX_PER_KM = 224e-6;
|
|
31384
|
+
var SOLAR_PX_PER_DEG = 1.8;
|
|
31385
|
+
var DOMAIN = [.3, 50];
|
|
31386
|
+
var TICKS = [
|
|
31387
|
+
.3,
|
|
31388
|
+
1,
|
|
31389
|
+
3,
|
|
31390
|
+
10,
|
|
31391
|
+
30
|
|
31392
|
+
];
|
|
31393
|
+
var SUN_COLOR = "#ffdd44";
|
|
31394
|
+
function displayName(name) {
|
|
31395
|
+
const stripped = name.replace(/^\d+\s+/, "");
|
|
31396
|
+
if (stripped === stripped.toUpperCase() && /[A-Z]/.test(stripped)) return stripped.charAt(0) + stripped.slice(1).toLowerCase();
|
|
31397
|
+
return stripped;
|
|
31398
|
+
}
|
|
31399
|
+
function solarColor(o) {
|
|
31400
|
+
return o.color || BODY_COLORS[o.id] || "#cccccc";
|
|
31401
|
+
}
|
|
31402
|
+
function fileStacks(file, name) {
|
|
31403
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
31404
|
+
const planets = new Map(file.objects.map((o) => [o.id, o]));
|
|
31405
|
+
for (const o of file.objects) {
|
|
31406
|
+
if (o.kind !== "moon" || !o.parent) continue;
|
|
31407
|
+
const stack = stacks.get(o.parent) ?? {
|
|
31408
|
+
satellites: [],
|
|
31409
|
+
count: planets.get(o.parent)?.moon_count,
|
|
31410
|
+
tab: false
|
|
31411
|
+
};
|
|
31412
|
+
stack.satellites.push({
|
|
31413
|
+
id: o.id,
|
|
31414
|
+
name: name(o),
|
|
31415
|
+
radiusKm: o.diameter_km / 2,
|
|
31416
|
+
color: solarColor(o)
|
|
31417
|
+
});
|
|
31418
|
+
stack.tab ||= !!o.link_parent;
|
|
31419
|
+
stacks.set(o.parent, stack);
|
|
31420
|
+
}
|
|
31421
|
+
return stacks;
|
|
31422
|
+
}
|
|
31423
|
+
function solarSystemModel(file, parts) {
|
|
31424
|
+
const sun = file.objects.find((o) => o.kind === "star");
|
|
31425
|
+
if (!sun) return null;
|
|
31426
|
+
const listed = fileStacks(file, parts.name);
|
|
31427
|
+
const stackOf = parts.stack ?? ((o) => listed.get(o.id));
|
|
31428
|
+
const bodies = file.objects.filter((o) => o.kind !== "moon" && o.kind !== "star" && (parts.keep?.(o) ?? true)).map((o) => {
|
|
31429
|
+
const stack = stackOf(o);
|
|
31430
|
+
return {
|
|
31431
|
+
id: o.id,
|
|
31432
|
+
name: parts.name(o),
|
|
31433
|
+
aKm: o.a * AU_KM,
|
|
31434
|
+
tiltDeg: o.i,
|
|
31435
|
+
radiusKm: o.diameter_km / 2,
|
|
31436
|
+
color: solarColor(o),
|
|
31437
|
+
rings: o.rings,
|
|
31438
|
+
satellites: stack?.satellites,
|
|
31439
|
+
satelliteCount: stack?.count,
|
|
31440
|
+
satellitesTab: stack?.tab ?? false,
|
|
31441
|
+
...parts.links(o, stack)
|
|
31442
|
+
};
|
|
31443
|
+
});
|
|
31444
|
+
return {
|
|
31445
|
+
primary: {
|
|
31446
|
+
id: sun.id,
|
|
31447
|
+
name: parts.name(sun),
|
|
31448
|
+
radiusKm: sun.diameter_km / 2,
|
|
31449
|
+
color: SUN_COLOR,
|
|
31450
|
+
link: parts.primaryLink
|
|
31451
|
+
},
|
|
31452
|
+
bodies,
|
|
31453
|
+
bands: file.belts.map((b) => ({
|
|
31454
|
+
key: b.slug,
|
|
31455
|
+
innerKm: b.inner_au * AU_KM,
|
|
31456
|
+
outerKm: b.outer_au * AU_KM,
|
|
31457
|
+
tone: b.kind === "kuiper_belt" ? "sky" : "muted",
|
|
31458
|
+
...parts.band(b)
|
|
31459
|
+
})),
|
|
31460
|
+
unitKm: AU_KM,
|
|
31461
|
+
domain: DOMAIN,
|
|
31462
|
+
ticks: TICKS,
|
|
31463
|
+
axisLabel: parts.text.axisAu,
|
|
31464
|
+
pxPerKm: SOLAR_PX_PER_KM,
|
|
31465
|
+
pxPerDeg: SOLAR_PX_PER_DEG,
|
|
31466
|
+
text: parts.text
|
|
31467
|
+
};
|
|
31468
|
+
}
|
|
31469
|
+
|
|
31470
|
+
//#endregion
|
|
31471
|
+
//#region src/lib/systemmap/views.ts
|
|
31472
|
+
function extraFromPlace(place) {
|
|
31473
|
+
return {
|
|
31474
|
+
id: place.id,
|
|
31475
|
+
name: place.name ?? place.id,
|
|
31476
|
+
moon: place.moon ?? false,
|
|
31477
|
+
aAu: place.aAu ?? 0,
|
|
31478
|
+
tiltDeg: place.tiltDeg ?? 0,
|
|
31479
|
+
radiusKm: place.radiusKm ?? 0,
|
|
31480
|
+
color: place.color ?? BODY_COLORS[place.id] ?? "#cccccc",
|
|
31481
|
+
parent: place.parent
|
|
31482
|
+
};
|
|
31483
|
+
}
|
|
31484
|
+
var MAX_STACK = 4;
|
|
31485
|
+
var ZONE_TOP_R = 10;
|
|
31486
|
+
var ZONE_DOMAIN = {
|
|
31487
|
+
inner: [.6, 5.5],
|
|
31488
|
+
outer: [4.5, 55]
|
|
31489
|
+
};
|
|
31490
|
+
var ZONE_TICKS = [
|
|
31491
|
+
.1,
|
|
31492
|
+
.2,
|
|
31493
|
+
.3,
|
|
31494
|
+
.5,
|
|
31495
|
+
1,
|
|
31496
|
+
2,
|
|
31497
|
+
3,
|
|
31498
|
+
5,
|
|
31499
|
+
10,
|
|
31500
|
+
20,
|
|
31501
|
+
30,
|
|
31502
|
+
50,
|
|
31503
|
+
100,
|
|
31504
|
+
200,
|
|
31505
|
+
500,
|
|
31506
|
+
1e3
|
|
31507
|
+
];
|
|
31508
|
+
var LAST_AU_TICK_AT = .9;
|
|
31509
|
+
var LAST_RADII_TICK_AT = .76;
|
|
31510
|
+
function tickAt(tick, domain) {
|
|
31511
|
+
return Math.log(tick / domain[0]) / Math.log(domain[1] / domain[0]);
|
|
31512
|
+
}
|
|
31513
|
+
var JUPITER_ID = "naif-599";
|
|
31514
|
+
function unplacedIds(solar, systems, bodies, placed = []) {
|
|
31515
|
+
if (!bodies) return [];
|
|
31516
|
+
const known = new Set([...solar.objects.map((o) => o.id), ...placed]);
|
|
31517
|
+
for (const entry of Object.values(systems)) {
|
|
31518
|
+
known.add(entry.primary.id);
|
|
31519
|
+
for (const moon of entry.moons) known.add(moon.id);
|
|
31520
|
+
}
|
|
31521
|
+
return [...bodies].filter((id) => !known.has(id));
|
|
31522
|
+
}
|
|
31523
|
+
function extraFromBundle(global) {
|
|
31524
|
+
const moon = global.type === "moon";
|
|
31525
|
+
const parent = global.orbit?.parent_id;
|
|
31526
|
+
const a = parent === void 0 || parent === "naif-10" || parent === "naif-0" ? global.orbit?.a ?? global.orbit?.q : void 0;
|
|
31527
|
+
if (a === void 0 && !moon) return null;
|
|
31528
|
+
const radii = global.radii;
|
|
31529
|
+
return {
|
|
31530
|
+
id: global.id,
|
|
31531
|
+
name: displayName(global.name ?? global.id),
|
|
31532
|
+
moon,
|
|
31533
|
+
aAu: a ?? 0,
|
|
31534
|
+
tiltDeg: global.orbit?.i ?? 0,
|
|
31535
|
+
radiusKm: radii ? (radii.a + radii.b + radii.c) / 3 : (global.sbdb?.diameter ?? 0) / 2,
|
|
31536
|
+
color: BODY_COLORS[global.id] ?? global.sbdb?.color ?? global.color ?? "#cccccc",
|
|
31537
|
+
parent: global.orbit?.parent_id
|
|
31538
|
+
};
|
|
31539
|
+
}
|
|
31540
|
+
var ZONE_CUT = .92;
|
|
31541
|
+
function zoneOf(aAu, jupiterAu) {
|
|
31542
|
+
return aAu < jupiterAu * ZONE_CUT ? "inner" : "outer";
|
|
31543
|
+
}
|
|
31544
|
+
function systemEntry(systems, primaryId) {
|
|
31545
|
+
return Object.values(systems).find((entry) => entry.primary.id === primaryId);
|
|
31546
|
+
}
|
|
31547
|
+
function targetKey(target) {
|
|
31548
|
+
return target.kind === "zone" ? `zone:${target.zone}` : `${target.kind}:${target.id}`;
|
|
31549
|
+
}
|
|
31550
|
+
var Scope = class {
|
|
31551
|
+
targets = /* @__PURE__ */ new Map();
|
|
31552
|
+
constructor(data, options, pick) {
|
|
31553
|
+
this.data = data;
|
|
31554
|
+
this.options = options;
|
|
31555
|
+
this.pick = pick;
|
|
31556
|
+
}
|
|
31557
|
+
has(id) {
|
|
31558
|
+
return !this.options.bodies || this.options.bodies.has(id);
|
|
31559
|
+
}
|
|
31560
|
+
name(id, exported) {
|
|
31561
|
+
return this.options.names[id] ?? (exported ? displayName(exported) : id);
|
|
31562
|
+
}
|
|
31563
|
+
named(id, name) {
|
|
31564
|
+
return this.options.names[id] ?? name;
|
|
31565
|
+
}
|
|
31566
|
+
link(target) {
|
|
31567
|
+
this.targets.set(targetKey(target), target);
|
|
31568
|
+
return { onclick: () => this.pick(target) };
|
|
31569
|
+
}
|
|
31570
|
+
body(id, name) {
|
|
31571
|
+
return this.has(id) ? this.link({
|
|
31572
|
+
kind: "body",
|
|
31573
|
+
id,
|
|
31574
|
+
name
|
|
31575
|
+
}) : void 0;
|
|
31576
|
+
}
|
|
31577
|
+
stack(primaryId, listed) {
|
|
31578
|
+
if (listed && !this.options.bodies) return listed;
|
|
31579
|
+
const moons = (systemEntry(this.data.systems, primaryId)?.moons ?? []).filter((moon) => this.has(moon.id)).map((moon) => ({
|
|
31580
|
+
id: moon.id,
|
|
31581
|
+
name: this.name(moon.id),
|
|
31582
|
+
radiusKm: moon.radius_km,
|
|
31583
|
+
color: BODY_COLORS[moon.id] ?? moon.color ?? "#cccccc"
|
|
31584
|
+
}));
|
|
31585
|
+
if (!moons.length) return void 0;
|
|
31586
|
+
moons.sort((a, b) => b.radiusKm - a.radiusKm);
|
|
31587
|
+
return {
|
|
31588
|
+
satellites: moons.slice(0, MAX_STACK),
|
|
31589
|
+
count: moons.length,
|
|
31590
|
+
tab: true
|
|
31591
|
+
};
|
|
31592
|
+
}
|
|
31593
|
+
links(id, name, stack) {
|
|
31594
|
+
if (!stack) return { link: this.body(id, name) };
|
|
31595
|
+
const system = () => this.link({
|
|
31596
|
+
kind: "system",
|
|
31597
|
+
id,
|
|
31598
|
+
name
|
|
31599
|
+
});
|
|
31600
|
+
if (this.options.grouping === "systems") return {
|
|
31601
|
+
link: system(),
|
|
31602
|
+
grouped: true
|
|
31603
|
+
};
|
|
31604
|
+
const largest = stack.satellites[0];
|
|
31605
|
+
return {
|
|
31606
|
+
link: this.body(id, name),
|
|
31607
|
+
satellitesLink: stack.tab ? system() : this.body(largest.id, largest.name)
|
|
31608
|
+
};
|
|
31609
|
+
}
|
|
31610
|
+
built(model, label) {
|
|
31611
|
+
return {
|
|
31612
|
+
model,
|
|
31613
|
+
targets: [...this.targets.values()],
|
|
31614
|
+
label
|
|
31615
|
+
};
|
|
31616
|
+
}
|
|
31617
|
+
};
|
|
31618
|
+
function buildSolarView(data, options, text, pick) {
|
|
31619
|
+
const scope = new Scope(data, options, pick);
|
|
31620
|
+
const name = (o) => scope.name(o.id, o.name);
|
|
31621
|
+
const listed = fileStacks(data.solar, name);
|
|
31622
|
+
const stacks = new Map(data.solar.objects.map((o) => [o.id, scope.stack(o.id, listed.get(o.id))]));
|
|
31623
|
+
const sun = data.solar.objects.find((o) => o.kind === "star");
|
|
31624
|
+
const zoneBand = (zone) => {
|
|
31625
|
+
const label = zone === "inner" ? text.zoneInner : text.zoneOuter;
|
|
31626
|
+
return {
|
|
31627
|
+
label,
|
|
31628
|
+
sub: "",
|
|
31629
|
+
...scope.link({
|
|
31630
|
+
kind: "zone",
|
|
31631
|
+
zone,
|
|
31632
|
+
name: label
|
|
31633
|
+
})
|
|
31634
|
+
};
|
|
31635
|
+
};
|
|
31636
|
+
const model = solarSystemModel(data.solar, {
|
|
31637
|
+
text,
|
|
31638
|
+
name,
|
|
31639
|
+
primaryLink: sun && scope.body(sun.id, name(sun)),
|
|
31640
|
+
keep: (o) => scope.has(o.id) || !!stacks.get(o.id),
|
|
31641
|
+
stack: (o) => stacks.get(o.id),
|
|
31642
|
+
links: (o, stack) => scope.links(o.id, name(o), stack),
|
|
31643
|
+
band: (belt) => options.zones === "belts" ? { label: belt.label } : zoneBand(belt.kind === "kuiper_belt" ? "outer" : "inner")
|
|
31644
|
+
});
|
|
31645
|
+
if (!model) throw new Error("spacemap: the Solar System map names no Sun");
|
|
31646
|
+
return scope.built(model, text.solarSystem);
|
|
31647
|
+
}
|
|
31648
|
+
function buildSystemView(data, primaryId, exported, options, text, pick) {
|
|
31649
|
+
const entry = systemEntry(data.systems, primaryId);
|
|
31650
|
+
if (!entry) throw new Error(`spacemap: no planetary system around ${primaryId}`);
|
|
31651
|
+
const scope = new Scope(data, options, pick);
|
|
31652
|
+
const system = systemFromMapEntry(entry, scope.name(primaryId, exported[primaryId]));
|
|
31653
|
+
system.moons = system.moons.filter((moon) => scope.has(moon.id)).map((moon) => ({
|
|
31654
|
+
...moon,
|
|
31655
|
+
name: scope.name(moon.id, exported[moon.id])
|
|
31656
|
+
}));
|
|
31657
|
+
const rKm = system.planetRadiusKm;
|
|
31658
|
+
const bands = system.rings ? [{
|
|
31659
|
+
key: "rings",
|
|
31660
|
+
label: text.rings,
|
|
31661
|
+
innerKm: system.rings.innerRp * rKm,
|
|
31662
|
+
outerKm: system.rings.outerRp * rKm,
|
|
31663
|
+
tone: "amber"
|
|
31664
|
+
}] : [];
|
|
31665
|
+
const model = planetarySystemModel(system, {
|
|
31666
|
+
text,
|
|
31667
|
+
axisLabel: text.axisRadii,
|
|
31668
|
+
bands,
|
|
31669
|
+
primaryLink: scope.body(primaryId, system.planetName),
|
|
31670
|
+
moonLink: (moon) => scope.body(moon.id, moon.name)
|
|
31671
|
+
});
|
|
31672
|
+
model.ticks = model.ticks.filter((t) => tickAt(t, model.domain) < LAST_RADII_TICK_AT);
|
|
31673
|
+
return scope.built(model, system.planetName);
|
|
31674
|
+
}
|
|
31675
|
+
function buildZoneView(data, zone, options, text, pick) {
|
|
31676
|
+
const scope = new Scope(data, options, pick);
|
|
31677
|
+
const { solar, extras } = data;
|
|
31678
|
+
const sun = solar.objects.find((o) => o.kind === "star");
|
|
31679
|
+
const jupiterAu = solar.objects.find((o) => o.id === JUPITER_ID)?.a;
|
|
31680
|
+
if (!sun || jupiterAu === void 0) throw new Error("spacemap: the Solar System map names no Sun or no Jupiter");
|
|
31681
|
+
const small = solar.objects.filter((o) => o.kind === "dwarf" || o.kind === "asteroid").map((o) => ({
|
|
31682
|
+
id: o.id,
|
|
31683
|
+
name: scope.name(o.id, o.name),
|
|
31684
|
+
aAu: o.a,
|
|
31685
|
+
tiltDeg: o.i,
|
|
31686
|
+
radiusKm: o.diameter_km / 2,
|
|
31687
|
+
color: solarColor(o)
|
|
31688
|
+
}));
|
|
31689
|
+
for (const extra of extras) {
|
|
31690
|
+
if (extra.moon || small.some((s) => s.id === extra.id)) continue;
|
|
31691
|
+
if (!(extra.aAu > 0)) continue;
|
|
31692
|
+
small.push({
|
|
31693
|
+
...extra,
|
|
31694
|
+
name: scope.named(extra.id, extra.name)
|
|
31695
|
+
});
|
|
31696
|
+
}
|
|
31697
|
+
const bodies = [];
|
|
31698
|
+
for (const s of small) {
|
|
31699
|
+
if (zoneOf(s.aAu, jupiterAu) !== zone) continue;
|
|
31700
|
+
const base = {
|
|
31701
|
+
...s,
|
|
31702
|
+
aKm: s.aAu * AU_KM
|
|
31703
|
+
};
|
|
31704
|
+
const stack = scope.stack(s.id);
|
|
31705
|
+
if (stack) {
|
|
31706
|
+
bodies.push({
|
|
31707
|
+
...base,
|
|
31708
|
+
satellites: stack.satellites,
|
|
31709
|
+
satelliteCount: stack.count,
|
|
31710
|
+
satellitesTab: true,
|
|
31711
|
+
...scope.links(s.id, s.name, stack)
|
|
31712
|
+
});
|
|
31713
|
+
continue;
|
|
31714
|
+
}
|
|
31715
|
+
const moons = extras.filter((extra) => extra.moon && extra.parent === s.id && scope.has(extra.id)).map((extra) => {
|
|
31716
|
+
const name = scope.named(extra.id, extra.name);
|
|
31717
|
+
return {
|
|
31718
|
+
...extra,
|
|
31719
|
+
name,
|
|
31720
|
+
link: scope.body(extra.id, name)
|
|
31721
|
+
};
|
|
31722
|
+
});
|
|
31723
|
+
if (!scope.has(s.id) && !moons.length) continue;
|
|
31724
|
+
bodies.push({
|
|
31725
|
+
...base,
|
|
31726
|
+
link: scope.body(s.id, s.name),
|
|
31727
|
+
satellites: moons.length ? moons : void 0
|
|
31728
|
+
});
|
|
31729
|
+
}
|
|
31730
|
+
const [lo, hi] = ZONE_DOMAIN[zone];
|
|
31731
|
+
const domain = [Math.min(lo, ...bodies.map((b) => b.aKm / AU_KM * .85)), Math.max(hi, ...bodies.map((b) => b.aKm / AU_KM * 1.15))];
|
|
31732
|
+
const within = (au) => au >= domain[0] && au <= domain[1];
|
|
31733
|
+
const pxPerKm = ZONE_TOP_R / Math.max(...bodies.map((b) => b.radiusKm), 1);
|
|
31734
|
+
for (const o of solar.objects) {
|
|
31735
|
+
if (o.kind !== "planet" || !within(o.a)) continue;
|
|
31736
|
+
bodies.push({
|
|
31737
|
+
id: o.id,
|
|
31738
|
+
name: scope.name(o.id, o.name),
|
|
31739
|
+
aKm: o.a * AU_KM,
|
|
31740
|
+
tiltDeg: 0,
|
|
31741
|
+
radiusKm: 0,
|
|
31742
|
+
color: solarColor(o),
|
|
31743
|
+
reference: true
|
|
31744
|
+
});
|
|
31745
|
+
}
|
|
31746
|
+
const label = zone === "inner" ? text.zoneInner : text.zoneOuter;
|
|
31747
|
+
return scope.built({
|
|
31748
|
+
primary: {
|
|
31749
|
+
id: sun.id,
|
|
31750
|
+
name: scope.name(sun.id, sun.name),
|
|
31751
|
+
radiusKm: sun.diameter_km / 2 * SOLAR_PX_PER_KM / pxPerKm,
|
|
31752
|
+
color: SUN_COLOR
|
|
31753
|
+
},
|
|
31754
|
+
bodies,
|
|
31755
|
+
bands: solar.belts.filter((belt) => within(belt.inner_au) && within(belt.outer_au)).map((belt) => ({
|
|
31756
|
+
key: belt.slug,
|
|
31757
|
+
label: belt.label,
|
|
31758
|
+
innerKm: belt.inner_au * AU_KM,
|
|
31759
|
+
outerKm: belt.outer_au * AU_KM,
|
|
31760
|
+
tone: belt.kind === "kuiper_belt" ? "sky" : "muted"
|
|
31761
|
+
})),
|
|
31762
|
+
unitKm: AU_KM,
|
|
31763
|
+
domain,
|
|
31764
|
+
ticks: ZONE_TICKS.filter((t) => within(t) && tickAt(t, domain) < LAST_AU_TICK_AT),
|
|
31765
|
+
axisLabel: text.axisAu,
|
|
31766
|
+
pxPerKm,
|
|
31767
|
+
pxPerDeg: SOLAR_PX_PER_DEG,
|
|
31768
|
+
text
|
|
31769
|
+
}, label);
|
|
31770
|
+
}
|
|
31771
|
+
|
|
31772
|
+
//#endregion
|
|
31773
|
+
//#region src/lib/systemmap/system-map.ts
|
|
31774
|
+
var onceTarget = /* @__PURE__ */ new WeakMap();
|
|
31775
|
+
var AU_TO_KM_THRESHOLD = .01;
|
|
31776
|
+
function viewText() {
|
|
31777
|
+
const { messages, locale } = host();
|
|
31778
|
+
const number = new Intl.NumberFormat(locale(), { maximumSignificantDigits: 3 });
|
|
31779
|
+
const kilometres = new Intl.NumberFormat(locale(), {
|
|
31780
|
+
style: "unit",
|
|
31781
|
+
unit: "kilometer",
|
|
31782
|
+
maximumSignificantDigits: 3
|
|
31783
|
+
});
|
|
31784
|
+
const au = messages.unit_symbol_astronomical_unit();
|
|
31785
|
+
return {
|
|
31786
|
+
primary: messages.planetary_system_primary(),
|
|
31787
|
+
retrograde: messages.planetary_system_retrograde(),
|
|
31788
|
+
moons: (count) => messages.system_map_moons({ count }),
|
|
31789
|
+
distance: (km) => {
|
|
31790
|
+
const distance = km / AU_KM;
|
|
31791
|
+
return distance > 0 && distance < AU_TO_KM_THRESHOLD ? kilometres.format(km) : `${number.format(distance)} ${au}`;
|
|
31792
|
+
},
|
|
31793
|
+
axisAu: messages.system_map_axis_au(),
|
|
31794
|
+
solarSystem: messages.system_map_solar_system(),
|
|
31795
|
+
rings: messages.tab_rings(),
|
|
31796
|
+
axisRadii: messages.planetary_system_axis_unit(),
|
|
31797
|
+
zoneInner: messages.system_map_zone_inner(),
|
|
31798
|
+
zoneOuter: messages.system_map_zone_outer()
|
|
31799
|
+
};
|
|
31800
|
+
}
|
|
31801
|
+
var SystemMap = class {
|
|
31802
|
+
chart = new SystemMapChart();
|
|
31803
|
+
listeners = /* @__PURE__ */ new Map();
|
|
31804
|
+
view;
|
|
31805
|
+
bodies;
|
|
31806
|
+
names;
|
|
31807
|
+
grouping;
|
|
31808
|
+
zones;
|
|
31809
|
+
places;
|
|
31810
|
+
targets = [];
|
|
31811
|
+
extras = /* @__PURE__ */ new Map();
|
|
31812
|
+
queue = Promise.resolve();
|
|
31813
|
+
disposed = false;
|
|
31814
|
+
constructor(options = {}) {
|
|
31815
|
+
this.view = options.view ?? { kind: "solar-system" };
|
|
31816
|
+
this.bodies = options.bodies ? new Set(options.bodies) : null;
|
|
31817
|
+
this.names = options.names ?? {};
|
|
31818
|
+
this.grouping = options.grouping ?? "bodies";
|
|
31819
|
+
this.zones = options.zones ?? "belts";
|
|
31820
|
+
this.places = (options.places ?? []).map(extraFromPlace);
|
|
31821
|
+
}
|
|
31822
|
+
mount(container) {
|
|
31823
|
+
container.append(this.chart.root);
|
|
31824
|
+
}
|
|
31825
|
+
remove() {
|
|
31826
|
+
this.disposed = true;
|
|
31827
|
+
this.chart.destroy();
|
|
31828
|
+
}
|
|
31829
|
+
load() {
|
|
31830
|
+
return this.draw();
|
|
31831
|
+
}
|
|
31832
|
+
async setView(view) {
|
|
31833
|
+
await this.draw(view);
|
|
31834
|
+
}
|
|
31835
|
+
getView() {
|
|
31836
|
+
return this.view;
|
|
31837
|
+
}
|
|
31838
|
+
setBodies(ids) {
|
|
31839
|
+
const bodies = ids ? new Set(ids) : null;
|
|
31840
|
+
return this.draw(void 0, bodies).catch(() => {});
|
|
31841
|
+
}
|
|
31842
|
+
getTargets() {
|
|
31843
|
+
return [...this.targets];
|
|
31844
|
+
}
|
|
31845
|
+
draw(view, bodies) {
|
|
31846
|
+
const turn = this.queue.then(() => {
|
|
31847
|
+
if (bodies !== void 0) this.bodies = bodies;
|
|
31848
|
+
return this.drawNow(view ?? this.view);
|
|
31849
|
+
});
|
|
31850
|
+
this.queue = turn.catch(() => {});
|
|
31851
|
+
return turn;
|
|
31852
|
+
}
|
|
31853
|
+
async drawNow(view) {
|
|
31854
|
+
let built;
|
|
31855
|
+
try {
|
|
31856
|
+
built = await this.build(view);
|
|
31857
|
+
} catch (cause) {
|
|
31858
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
31859
|
+
if (!this.disposed) this.emit("error", error);
|
|
31860
|
+
throw error;
|
|
31861
|
+
}
|
|
31862
|
+
if (this.disposed) return;
|
|
31863
|
+
const changed = JSON.stringify(view) !== JSON.stringify(this.view);
|
|
31864
|
+
this.view = view;
|
|
31865
|
+
this.targets = built.targets;
|
|
31866
|
+
this.chart.update(built.model, { ariaLabel: built.label });
|
|
31867
|
+
if (changed) this.emit("viewchange", view);
|
|
31868
|
+
}
|
|
31869
|
+
async build(view) {
|
|
31870
|
+
const [solar, systems] = await Promise.all([fetchSolarSystemMap(), fetchPlanetarySystemsMap()]);
|
|
31871
|
+
const options = {
|
|
31872
|
+
bodies: this.bodies,
|
|
31873
|
+
names: this.names,
|
|
31874
|
+
grouping: this.grouping,
|
|
31875
|
+
zones: this.zones
|
|
31876
|
+
};
|
|
31877
|
+
const text = viewText();
|
|
31878
|
+
const pick = (target) => this.emit("select", target);
|
|
31879
|
+
const data = {
|
|
31880
|
+
solar,
|
|
31881
|
+
systems,
|
|
31882
|
+
extras: []
|
|
31883
|
+
};
|
|
31884
|
+
switch (view.kind) {
|
|
31885
|
+
case "solar-system": return buildSolarView(data, options, text, pick);
|
|
31886
|
+
case "system": return buildSystemView(data, view.id, await this.exportedNames(view.id, systems), options, text, pick);
|
|
31887
|
+
case "zone": {
|
|
31888
|
+
const ids = unplacedIds(solar, systems, this.bodies, this.places.map((place) => place.id));
|
|
31889
|
+
const read = (await Promise.all(ids.map((id) => this.extra(id)))).filter((extra) => extra !== null);
|
|
31890
|
+
const placed = [...this.places, ...read];
|
|
31891
|
+
const parents = unplacedIds(solar, systems, new Set(placed.flatMap((extra) => extra.moon && extra.parent ? [extra.parent] : [])), placed.map((extra) => extra.id));
|
|
31892
|
+
const hosts = await Promise.all(parents.map((id) => this.extra(id)));
|
|
31893
|
+
const extras = [...placed, ...hosts.filter((extra) => extra !== null)];
|
|
31894
|
+
return buildZoneView({
|
|
31895
|
+
...data,
|
|
31896
|
+
extras
|
|
31897
|
+
}, view.zone, options, text, pick);
|
|
31898
|
+
}
|
|
31899
|
+
}
|
|
31900
|
+
}
|
|
31901
|
+
extra(id) {
|
|
31902
|
+
let pending = this.extras.get(id);
|
|
31903
|
+
if (!pending) {
|
|
31904
|
+
pending = fetchObjectDetail(id, false).then((detail) => {
|
|
31905
|
+
if (!detail.global) throw new Error(`spacemap: no such body as ${id}`);
|
|
31906
|
+
return extraFromBundle(detail.global);
|
|
31907
|
+
}).catch((cause) => {
|
|
31908
|
+
this.extras.delete(id);
|
|
31909
|
+
this.emit("error", cause instanceof Error ? cause : new Error(String(cause)));
|
|
31910
|
+
return null;
|
|
31911
|
+
});
|
|
31912
|
+
this.extras.set(id, pending);
|
|
31913
|
+
}
|
|
31914
|
+
return pending;
|
|
31915
|
+
}
|
|
31916
|
+
async exportedNames(primaryId, systems) {
|
|
31917
|
+
if (![primaryId, ...(systemEntry(systems, primaryId)?.moons ?? []).map((m) => m.id)].some((id) => !(id in this.names) && (id === primaryId || !this.bodies || this.bodies.has(id)))) return {};
|
|
31918
|
+
try {
|
|
31919
|
+
const { global, localized } = await fetchObjectDetail(primaryId, true, host().locale());
|
|
31920
|
+
const names = {};
|
|
31921
|
+
for (const moon of global?.notable_moons ?? []) if (moon.id) names[moon.id] = moon.name;
|
|
31922
|
+
Object.assign(names, localized?.notable_moon_names);
|
|
31923
|
+
const name = localized?.name ?? global?.name;
|
|
31924
|
+
if (name) names[primaryId] = name;
|
|
31925
|
+
return names;
|
|
31926
|
+
} catch {
|
|
31927
|
+
return {};
|
|
31928
|
+
}
|
|
31929
|
+
}
|
|
31930
|
+
on(event, listener) {
|
|
31931
|
+
let set = this.listeners.get(event);
|
|
31932
|
+
if (!set) {
|
|
31933
|
+
set = /* @__PURE__ */ new Set();
|
|
31934
|
+
this.listeners.set(event, set);
|
|
31935
|
+
}
|
|
31936
|
+
set.add(listener);
|
|
31937
|
+
return () => set.delete(listener);
|
|
31938
|
+
}
|
|
31939
|
+
once(event, listener) {
|
|
31940
|
+
const wrapped = ((...args) => {
|
|
31941
|
+
off();
|
|
31942
|
+
listener(...args);
|
|
31943
|
+
});
|
|
31944
|
+
onceTarget.set(wrapped, listener);
|
|
31945
|
+
const off = this.on(event, wrapped);
|
|
31946
|
+
return off;
|
|
31947
|
+
}
|
|
31948
|
+
off(event, listener) {
|
|
31949
|
+
const set = this.listeners.get(event);
|
|
31950
|
+
if (!set) return;
|
|
31951
|
+
set.delete(listener);
|
|
31952
|
+
for (const held of set) if (onceTarget.get(held) === listener) set.delete(held);
|
|
31953
|
+
}
|
|
31954
|
+
emit(event, ...args) {
|
|
31955
|
+
const set = this.listeners.get(event);
|
|
31956
|
+
if (!set) return;
|
|
31957
|
+
for (const listener of [...set]) listener(...args);
|
|
31958
|
+
}
|
|
31959
|
+
};
|
|
31960
|
+
|
|
30077
31961
|
//#endregion
|
|
30078
31962
|
//#region src/lib/scene/state/attribution.ts
|
|
30079
31963
|
var DEFAULT_ORBIT_LABELS = Object.fromEntries(ORBIT_SOURCE_ORDER.map((s) => [s, ORBIT_SOURCES[s].label]));
|
|
@@ -30414,6 +32298,27 @@ async function createPanorama(options) {
|
|
|
30414
32298
|
await view.load();
|
|
30415
32299
|
return view;
|
|
30416
32300
|
}
|
|
32301
|
+
async function createSystemMap(options) {
|
|
32302
|
+
const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
|
|
32303
|
+
const element = resolveContainer(container);
|
|
32304
|
+
applyHost({
|
|
32305
|
+
dataUrl,
|
|
32306
|
+
imagesUrl,
|
|
32307
|
+
locale,
|
|
32308
|
+
messages,
|
|
32309
|
+
includeNonCommercial
|
|
32310
|
+
});
|
|
32311
|
+
const map = new SystemMap(rest);
|
|
32312
|
+
map.mount(element);
|
|
32313
|
+
for (const [event, listener] of Object.entries(events ?? {})) map.on(event, listener);
|
|
32314
|
+
try {
|
|
32315
|
+
await map.load();
|
|
32316
|
+
} catch (error) {
|
|
32317
|
+
map.remove();
|
|
32318
|
+
throw error;
|
|
32319
|
+
}
|
|
32320
|
+
return map;
|
|
32321
|
+
}
|
|
30417
32322
|
function resolveContainer(container) {
|
|
30418
32323
|
if (typeof container !== "string") return container;
|
|
30419
32324
|
const element = document.querySelector(container);
|
|
@@ -30431,5 +32336,5 @@ function applyHost(options) {
|
|
|
30431
32336
|
}
|
|
30432
32337
|
|
|
30433
32338
|
//#endregion
|
|
30434
|
-
export { AttributionControl, FlatAttributionControl, FlatMap, GestureHandler, MAP_LAYERS, PROJECTION_IDS, PanoramaAttributionControl, PanoramaView, SpaceMap, Viewport, bearingDeg, boxRing, circlePoints, configureHost, createFlatMap, createMap, createPanorama, createProjection, dateToJD, defaultSceneSettings, elements, fetchPanoramaIndex, fetchPanoramas, findPanorama, fixed, graticule, groundDistanceM, jdToDate, kmToAu, neighboursOf, panoramaAt, pathFor, planeBasis, projectionAspect, samples, smallCircle, tle };
|
|
30435
|
-
(function(){if(typeof document==='undefined')return;var s=document.createElement('style');s.textContent=".scene-label{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;color:inherit;touch-action:none;z-index:1;width:32px;height:32px;text-decoration:none;display:block;position:relative;left:0}.scene-label__halo{border-radius:50%;width:100%;height:100%;transition:transform .1s}.scene-label__halo--spacecraft,.scene-label__halo--debris{overflow:visible}.scene-label__loader{box-sizing:border-box;pointer-events:none;border:2px solid #fff;border-color:#fff #fff #fff3 #fff3;border-radius:50%;width:32px;height:32px;margin:auto;animation:.9s linear infinite scene-label-loader-spin;position:fixed;inset:0}@keyframes scene-label-loader-spin{to{transform:rotate(360deg)}}.reduce-motion .scene-label__loader{animation-duration:.9s!important;animation-iteration-count:infinite!important}.scene-label__name{white-space:nowrap;pointer-events:auto;cursor:pointer;color:#fff;text-shadow:0 0 4px #000,0 0 4px #000;padding-left:8px;font-size:14px;font-weight:700;text-decoration:none;position:absolute;top:50%;left:32px;transform:translateY(-50%)}.scene-label__name--large{font-size:16px}.scene-label__annotations{top:28px;pointer-events:none;flex-direction:column;padding-inline-start:8px;display:flex;position:absolute;inset-inline-start:32px}.scene-label__annotation{opacity:0;white-space:nowrap;color:#fff9;text-shadow:0 0 3px #000,0 0 3px #000;transition:opacity .15s, display .15s allow-discrete;font-size:11px;line-height:1.3;display:none}.scene-label__annotation--visible{opacity:1;display:block}@starting-style{.scene-label__annotation--visible{opacity:0}}.scene-feature-label{color:#ffffffb3;white-space:nowrap;pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 0 2px #000,0 0 2px #000;z-index:0;font-size:12px;transition:color .1s;left:0}.scene-feature-label:hover{color:#fff;text-shadow:0 0 3px #000,0 0 3px #000}.scene-feature-label--active{color:#fff;font-size:14px;font-weight:600}.scene-path-label{color:#fff;font:inherit;white-space:nowrap;text-align:start;text-shadow:0 0 4px #000,0 0 4px #000;pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none;z-index:2;background:0 0;border:0;align-items:center;gap:8px;padding:0;line-height:1.15;display:flex;left:0}.scene-path-label--static{pointer-events:none;cursor:default}.scene-path-label__halo{border-radius:50%;flex-shrink:0;width:32px;height:32px;margin-inline-start:-16px;transition:transform .1s}.scene-path-label:hover .scene-path-label__halo{transform:scale(1.15)}.scene-path-label__text{flex-direction:column;gap:1px;display:flex}.scene-path-label__name{font-size:14px;font-weight:700}.scene-path-label__when{opacity:.8;font-variant-numeric:tabular-nums;font-size:12px}.scene-path-label--dim .scene-path-label__text{display:none}.scene-path-label--dim .scene-path-label__halo{transform:scale(.55)}.scene-path-label--dim:hover .scene-path-label__halo{transform:scale(.75)}.scene-path-label--step .scene-path-label__halo{background:0 0;border:0}.scene-path-label--step:hover .scene-path-label__halo{background:#ffffff1f}.scene-path-label--step .scene-path-label__name{font-size:12px;font-weight:600}.scene-path-label--step .scene-path-label__when{font-size:11px}.scene-path-label__hazard{font-size:11px;font-weight:700}.scene-path-label--lit{color:#fff}.scene-path-label--lit .scene-path-label__halo{transform:scale(1.15)}.scene-path-label--lit.scene-path-label--dim .scene-path-label__halo{transform:scale(.75)}.scene-path-label--faint .scene-path-label__text{display:none}.scene-path-label--faint .scene-path-label__halo{opacity:.45;transform:scale(.5)}.scene-path-label--faint:hover .scene-path-label__halo{opacity:1;transform:scale(.75)}.scene-overlay--no-names .scene-label__name,.scene-overlay--no-names .scene-label__annotations{display:none}.scene-hazard-label{white-space:nowrap;pointer-events:none;-webkit-user-select:none;user-select:none;text-shadow:0 0 4px #000,0 0 4px #000;margin-inline-start:16px;font-size:12px;font-weight:700;position:relative;left:0}.user-location-marker{pointer-events:none;background:#1a73e8;border:2px solid #fff;border-radius:50%;width:16px;height:16px;box-shadow:0 0 6px #0006}.sm-drawing-label{white-space:nowrap;color:#fff;text-shadow:0 0 4px #000,0 0 4px #000;-webkit-user-select:none;user-select:none;pointer-events:none;font:700 14px/1.2 system-ui,sans-serif}.sm-drawing-icon{-webkit-user-select:none;user-select:none;pointer-events:none;display:block}.sm-drawing--interactive{pointer-events:auto;cursor:pointer;touch-action:none}.sm-cooperative-hint{z-index:4;color:#fff;text-align:center;pointer-events:none;opacity:0;background:#0000008c;place-content:center;padding:16px;font:15px/1.4 system-ui,sans-serif;transition:opacity .2s;display:grid;position:absolute;inset:0}.sm-cooperative-hint--shown{opacity:1}@media (prefers-reduced-motion:reduce){.sm-cooperative-hint{transition:none}}.sm-ctrl{z-index:2;pointer-events:none;flex-direction:column;gap:6px;max-width:100%;padding:6px;display:flex;position:absolute}.sm-ctrl>*{pointer-events:auto}.sm-ctrl--top-left{align-items:flex-start;top:0;left:0}.sm-ctrl--top-right{align-items:flex-end;top:0;right:0}.sm-ctrl--bottom-left{align-items:flex-start;bottom:0;left:0}.sm-ctrl--bottom-right{align-items:flex-end;bottom:0;right:0}.sm-map{-webkit-user-select:none;user-select:none
|
|
32339
|
+
export { AttributionControl, FlatAttributionControl, FlatMap, GestureHandler, MAP_LAYERS, PROJECTION_IDS, PanoramaAttributionControl, PanoramaView, SpaceMap, SystemMap, Viewport, bearingDeg, boxRing, circlePoints, configureHost, createFlatMap, createMap, createPanorama, createProjection, createSystemMap, dateToJD, defaultSceneSettings, elements, fetchPanoramaIndex, fetchPanoramas, findPanorama, fixed, graticule, groundDistanceM, isViewable, jdToDate, kmToAu, neighboursOf, panoramaAt, pathFor, planeBasis, projectionAspect, samples, smallCircle, tle };
|
|
32340
|
+
(function(){if(typeof document==='undefined')return;var s=document.createElement('style');s.textContent=".scene-label{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;color:inherit;touch-action:none;z-index:1;width:32px;height:32px;text-decoration:none;display:block;position:relative;left:0}.scene-label__halo{border-radius:50%;width:100%;height:100%;transition:transform .1s}.scene-label__halo--spacecraft,.scene-label__halo--debris{overflow:visible}.scene-label__loader{box-sizing:border-box;pointer-events:none;border:2px solid #fff;border-color:#fff #fff #fff3 #fff3;border-radius:50%;width:32px;height:32px;margin:auto;animation:.9s linear infinite scene-label-loader-spin;position:fixed;inset:0}@keyframes scene-label-loader-spin{to{transform:rotate(360deg)}}.reduce-motion .scene-label__loader{animation-duration:.9s!important;animation-iteration-count:infinite!important}.scene-label__name{white-space:nowrap;pointer-events:auto;cursor:pointer;color:#fff;text-shadow:0 0 4px #000,0 0 4px #000;padding-left:8px;font-size:14px;font-weight:700;text-decoration:none;position:absolute;top:50%;left:32px;transform:translateY(-50%)}.scene-label__name--large{font-size:16px}.scene-label__annotations{top:28px;pointer-events:none;flex-direction:column;padding-inline-start:8px;display:flex;position:absolute;inset-inline-start:32px}.scene-label__annotation{opacity:0;white-space:nowrap;color:#fff9;text-shadow:0 0 3px #000,0 0 3px #000;transition:opacity .15s, display .15s allow-discrete;font-size:11px;line-height:1.3;display:none}.scene-label__annotation--visible{opacity:1;display:block}@starting-style{.scene-label__annotation--visible{opacity:0}}.scene-feature-label{color:#ffffffb3;white-space:nowrap;pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 0 2px #000,0 0 2px #000;z-index:0;font-size:12px;transition:color .1s;left:0}.scene-feature-label:hover{color:#fff;text-shadow:0 0 3px #000,0 0 3px #000}.scene-feature-label--active{color:#fff;font-size:14px;font-weight:600}.scene-path-label{color:#fff;font:inherit;white-space:nowrap;text-align:start;text-shadow:0 0 4px #000,0 0 4px #000;pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none;z-index:2;background:0 0;border:0;align-items:center;gap:8px;padding:0;line-height:1.15;display:flex;left:0}.scene-path-label--static{pointer-events:none;cursor:default}.scene-path-label__halo{border-radius:50%;flex-shrink:0;width:32px;height:32px;margin-inline-start:-16px;transition:transform .1s}.scene-path-label:hover .scene-path-label__halo{transform:scale(1.15)}.scene-path-label__text{flex-direction:column;gap:1px;display:flex}.scene-path-label__name{font-size:14px;font-weight:700}.scene-path-label__when{opacity:.8;font-variant-numeric:tabular-nums;font-size:12px}.scene-path-label--dim .scene-path-label__text{display:none}.scene-path-label--dim .scene-path-label__halo{transform:scale(.55)}.scene-path-label--dim:hover .scene-path-label__halo{transform:scale(.75)}.scene-path-label--step .scene-path-label__halo{background:0 0;border:0}.scene-path-label--step:hover .scene-path-label__halo{background:#ffffff1f}.scene-path-label--step .scene-path-label__name{font-size:12px;font-weight:600}.scene-path-label--step .scene-path-label__when{font-size:11px}.scene-path-label__hazard{font-size:11px;font-weight:700}.scene-path-label--lit{color:#fff}.scene-path-label--lit .scene-path-label__halo{transform:scale(1.15)}.scene-path-label--lit.scene-path-label--dim .scene-path-label__halo{transform:scale(.75)}.scene-path-label--faint .scene-path-label__text{display:none}.scene-path-label--faint .scene-path-label__halo{opacity:.45;transform:scale(.5)}.scene-path-label--faint:hover .scene-path-label__halo{opacity:1;transform:scale(.75)}.scene-overlay--no-names .scene-label__name,.scene-overlay--no-names .scene-label__annotations{display:none}.scene-hazard-label{white-space:nowrap;pointer-events:none;-webkit-user-select:none;user-select:none;text-shadow:0 0 4px #000,0 0 4px #000;margin-inline-start:16px;font-size:12px;font-weight:700;position:relative;left:0}.user-location-marker{pointer-events:none;background:#1a73e8;border:2px solid #fff;border-radius:50%;width:16px;height:16px;box-shadow:0 0 6px #0006}.sm-drawing-label{white-space:nowrap;color:#fff;text-shadow:0 0 4px #000,0 0 4px #000;-webkit-user-select:none;user-select:none;pointer-events:none;font:700 14px/1.2 system-ui,sans-serif}.sm-drawing-icon{-webkit-user-select:none;user-select:none;pointer-events:none;display:block}.sm-drawing--interactive{pointer-events:auto;cursor:pointer;touch-action:none}.sm-cooperative-hint{z-index:4;color:#fff;text-align:center;pointer-events:none;opacity:0;background:#0000008c;place-content:center;padding:16px;font:15px/1.4 system-ui,sans-serif;transition:opacity .2s;display:grid;position:absolute;inset:0}.sm-cooperative-hint--shown{opacity:1}@media (prefers-reduced-motion:reduce){.sm-cooperative-hint{transition:none}}.sm-ctrl{z-index:2;pointer-events:none;flex-direction:column;gap:6px;max-width:100%;padding:6px;display:flex;position:absolute}.sm-ctrl>*{pointer-events:auto}.sm-ctrl--top-left{align-items:flex-start;top:0;left:0}.sm-ctrl--top-right{align-items:flex-end;top:0;right:0}.sm-ctrl--bottom-left{align-items:flex-start;bottom:0;left:0}.sm-ctrl--bottom-right{align-items:flex-end;bottom:0;right:0}.sm-map{-webkit-user-select:none;user-select:none}:where(.sm-map){width:100%;height:100%;position:relative}.sm-map__canvas{pointer-events:auto;touch-action:none;width:100%;height:100%;display:block}.sm-map__canvas:focus-visible{outline:2px solid var(--ring,Highlight)}.sm-map .scene-overlay{pointer-events:none;z-index:0;position:absolute;inset:0}.sm-flat{touch-action:none;background:#05070e;width:100%;height:100%;position:relative;overflow:hidden}.sm-flat__raster,.sm-flat__vector,.sm-flat__markers{width:100%;height:100%;position:absolute;inset:0}.sm-flat__raster{display:block}.sm-flat__vector,.sm-flat__markers{pointer-events:none}.sm-flat--interactive{cursor:grab}.sm-flat--cooperative{touch-action:pan-x pan-y}.sm-flat--dragging{cursor:grabbing}.sm-flat__graticule{fill:none;stroke:#ffffff47;stroke-width:1px}.sm-flat__frame{fill:none;stroke:#ffffff73;stroke-width:1px}.sm-flat__feature-dot{fill:#fffc}.sm-flat__feature-label{fill:#ffffffd9;paint-order:stroke;stroke:#000000a6;stroke-width:2.5px;font:10px/1 system-ui,sans-serif}.sm-flat__marker-dot{background:#4ea3ff;border:2px solid #fff;border-radius:50%;width:10px;height:10px;box-shadow:0 0 0 1px #00000073}.sm-system-map{border-radius:var(--sm-system-map-radius,6px);background:var(--sm-system-map-background,#ffffff0d);width:100%;color:var(--sm-system-map-ink,#ffffff9e);position:relative;overflow:hidden}.sm-system-map--background{pointer-events:none;background:0 0;border-radius:0;height:100%}.sm-system-map__svg{touch-action:pan-y;width:100%;height:auto;display:block}.sm-system-map--background .sm-system-map__svg{height:100%}.sm-system-map__svg--near :is(a,.sm-system-map__button):not([data-near]){pointer-events:none}.sm-system-map__button{cursor:pointer}.sm-system-map__button:focus{outline:none}.sm-system-map__reference{opacity:.6}.sm-system-map__band--muted{color:var(--sm-system-map-ink,#ffffff9e)}.sm-system-map__band--sky{color:var(--sm-system-map-sky,oklch(74.6% .16 232.661/.7))}.sm-system-map__band--amber{color:var(--sm-system-map-amber,oklch(92.4% .12 95.746/.8))}.sm-system-map__band-label{font-weight:600}.sm-system-map__band-label--muted{fill:var(--sm-system-map-ink,#ffffff9e)}.sm-system-map__band-label--sky{fill:var(--sm-system-map-sky-label,oklch(74.6% .16 232.661))}.sm-system-map__band-label--amber{fill:var(--sm-system-map-amber-label,oklch(92.4% .12 95.746/.9))}.sm-system-map__tip{z-index:10;background:var(--sm-system-map-tip-background,#0a0c14e6);width:max-content;color:var(--sm-system-map-tip-ink,#fffffff2);white-space:nowrap;pointer-events:none;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1.33333;position:absolute;transform:translate(-50%);box-shadow:0 1px 3px #0000001a,0 1px 2px -1px #0000001a}.sm-system-map__tip[hidden]{display:none}.sm-system-map__tip-title{font-weight:500}.sm-system-map__tip-sub{color:var(--sm-system-map-ink,#ffffff9e)}.sm-ctrl:has(>.sm-attribution:only-child){padding:0}.sm-ctrl--top-left:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:0 0 var(--sm-attribution-radius) 0}.sm-ctrl--top-right:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:0 0 0 var(--sm-attribution-radius)}.sm-ctrl--bottom-left:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:0 var(--sm-attribution-radius) 0 0}.sm-ctrl--bottom-right:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:var(--sm-attribution-radius) 0 0 0}.sm-attribution{--sm-attribution-radius:3px;border-radius:var(--sm-attribution-radius);color:#ffffffbf;white-space:nowrap;background:#0006;align-items:center;gap:.5em;max-width:100%;padding:1px 6px;font:11px/1.45 system-ui,sans-serif;display:flex}.sm-attribution a{color:inherit;text-decoration:none}.sm-attribution a:hover{color:#fff;text-decoration:underline}.sm-attribution__home{font-weight:600}.sm-attribution__group{text-overflow:ellipsis;overflow:hidden}.sm-attribution__label{color:#ffffff80}\n/*$vite$:1*/";document.head.appendChild(s);})();
|