spacemap 0.1.3 → 0.1.4
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 +153 -5
- package/dist/spacemap.iife.js +58 -58
- package/dist/spacemap.js +58 -58
- package/index.d.ts +192 -3
- package/index.js +1746 -78
- 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();
|
|
@@ -10311,20 +10324,35 @@ async function loadTierBitmaps(id, tier, priority) {
|
|
|
10311
10324
|
}));
|
|
10312
10325
|
}
|
|
10313
10326
|
var tierPrefetch = /* @__PURE__ */ new Map();
|
|
10327
|
+
var tierClaims = /* @__PURE__ */ new Map();
|
|
10314
10328
|
function tierKey(id, tier) {
|
|
10315
10329
|
return `${id}:${tier}`;
|
|
10316
10330
|
}
|
|
10317
|
-
function tierBitmaps(id, tier, priority) {
|
|
10331
|
+
function tierBitmaps(id, tier, priority, claim = true) {
|
|
10318
10332
|
const key = tierKey(id, tier);
|
|
10319
10333
|
let p = tierPrefetch.get(key);
|
|
10320
10334
|
if (!p) {
|
|
10321
|
-
|
|
10335
|
+
const loading = loadTierBitmaps(id, tier, priority);
|
|
10336
|
+
p = loading;
|
|
10322
10337
|
tierPrefetch.set(key, p);
|
|
10338
|
+
loading.catch(() => {
|
|
10339
|
+
if (tierPrefetch.get(key) !== loading) return;
|
|
10340
|
+
tierPrefetch.delete(key);
|
|
10341
|
+
tierClaims.delete(key);
|
|
10342
|
+
});
|
|
10323
10343
|
}
|
|
10344
|
+
if (claim) tierClaims.set(key, (tierClaims.get(key) ?? 0) + 1);
|
|
10324
10345
|
return p;
|
|
10325
10346
|
}
|
|
10326
10347
|
function releaseTierBitmaps(id, tier, bitmaps) {
|
|
10327
|
-
|
|
10348
|
+
const key = tierKey(id, tier);
|
|
10349
|
+
const left = (tierClaims.get(key) ?? 1) - 1;
|
|
10350
|
+
if (left > 0) {
|
|
10351
|
+
tierClaims.set(key, left);
|
|
10352
|
+
return;
|
|
10353
|
+
}
|
|
10354
|
+
tierClaims.delete(key);
|
|
10355
|
+
tierPrefetch.delete(key);
|
|
10328
10356
|
for (const b of bitmaps) b.close();
|
|
10329
10357
|
}
|
|
10330
10358
|
function makeCube(bitmaps) {
|
|
@@ -10339,10 +10367,10 @@ function pickLowTier(meta) {
|
|
|
10339
10367
|
}
|
|
10340
10368
|
function prefetchSkyboxTiers(meta) {
|
|
10341
10369
|
const low = pickLowTier(meta);
|
|
10342
|
-
if (low) tierBitmaps(meta.id, low).catch(() =>
|
|
10370
|
+
if (low) tierBitmaps(meta.id, low, void 0, false).catch(() => {});
|
|
10343
10371
|
}
|
|
10344
10372
|
var FULL_TIER_GATE_TIMEOUT_MS = 12e3;
|
|
10345
|
-
async function loadFromMeta(scene, renderer, meta) {
|
|
10373
|
+
async function loadFromMeta(scene, renderer, meta, disposed) {
|
|
10346
10374
|
const tier = pickTier$1(meta, Math.min(renderer.capabilities.maxTextureSize, currentRenderTier().maxSkyboxFace));
|
|
10347
10375
|
if (!tier) {
|
|
10348
10376
|
console.warn(`Skybox ${meta.id}: no tiers declared`);
|
|
@@ -10355,7 +10383,7 @@ async function loadFromMeta(scene, renderer, meta) {
|
|
|
10355
10383
|
};
|
|
10356
10384
|
let fullInstalled = false;
|
|
10357
10385
|
if (lowTier && lowTier !== tier) tierBitmaps(meta.id, lowTier).then((bitmaps) => {
|
|
10358
|
-
if (fullInstalled) {
|
|
10386
|
+
if (fullInstalled || disposed()) {
|
|
10359
10387
|
releaseTierBitmaps(meta.id, lowTier, bitmaps);
|
|
10360
10388
|
return;
|
|
10361
10389
|
}
|
|
@@ -10363,11 +10391,38 @@ async function loadFromMeta(scene, renderer, meta) {
|
|
|
10363
10391
|
lowRef.bitmaps = bitmaps;
|
|
10364
10392
|
scene.background = lowRef.cube;
|
|
10365
10393
|
performance.mark("sm-skybox-low");
|
|
10394
|
+
if (fullFailed) keepLow();
|
|
10366
10395
|
}).catch(() => {});
|
|
10396
|
+
let fullFailed = false;
|
|
10397
|
+
const keepLow = () => {
|
|
10398
|
+
if (!lowTier || !lowRef.cube || !lowRef.bitmaps) return;
|
|
10399
|
+
renderer.initTexture(lowRef.cube);
|
|
10400
|
+
releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10401
|
+
lowRef.bitmaps = null;
|
|
10402
|
+
};
|
|
10367
10403
|
if (tier !== lowTier) await Promise.race([eagerMinorsDone, new Promise((resolve) => setTimeout(resolve, FULL_TIER_GATE_TIMEOUT_MS))]);
|
|
10368
|
-
|
|
10369
|
-
|
|
10404
|
+
let fullBitmaps;
|
|
10405
|
+
try {
|
|
10406
|
+
fullBitmaps = await tierBitmaps(meta.id, tier, tier === lowTier ? void 0 : "low");
|
|
10407
|
+
} catch (err) {
|
|
10408
|
+
fullFailed = true;
|
|
10409
|
+
if (!disposed()) keepLow();
|
|
10410
|
+
else if (lowTier && lowRef.bitmaps) {
|
|
10411
|
+
lowRef.cube?.dispose();
|
|
10412
|
+
releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10413
|
+
lowRef.bitmaps = null;
|
|
10414
|
+
}
|
|
10415
|
+
throw err;
|
|
10416
|
+
}
|
|
10370
10417
|
await new Promise((resolve) => "requestIdleCallback" in window ? requestIdleCallback(() => resolve(), { timeout: 2e3 }) : setTimeout(resolve, 500));
|
|
10418
|
+
if (disposed()) {
|
|
10419
|
+
fullInstalled = true;
|
|
10420
|
+
lowRef.cube?.dispose();
|
|
10421
|
+
releaseTierBitmaps(meta.id, tier, fullBitmaps);
|
|
10422
|
+
if (lowTier && lowTier !== tier && lowRef.bitmaps) releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10423
|
+
return;
|
|
10424
|
+
}
|
|
10425
|
+
const full = makeCube(fullBitmaps);
|
|
10371
10426
|
renderer.initTexture(full);
|
|
10372
10427
|
fullInstalled = true;
|
|
10373
10428
|
const prevBackground = scene.background;
|
|
@@ -10378,7 +10433,7 @@ async function loadFromMeta(scene, renderer, meta) {
|
|
|
10378
10433
|
releaseTierBitmaps(meta.id, tier, fullBitmaps);
|
|
10379
10434
|
if (lowTier && lowTier !== tier && lowRef.bitmaps) releaseTierBitmaps(meta.id, lowTier, lowRef.bitmaps);
|
|
10380
10435
|
}
|
|
10381
|
-
async function loadSkybox(scene, renderer, ctx) {
|
|
10436
|
+
async function loadSkybox(scene, renderer, ctx, disposed = () => false) {
|
|
10382
10437
|
if (ctx?.layers.skipped.has("stars")) return;
|
|
10383
10438
|
try {
|
|
10384
10439
|
const meta = await fetchMetadata();
|
|
@@ -10390,7 +10445,7 @@ async function loadSkybox(scene, renderer, ctx) {
|
|
|
10390
10445
|
attribution: meta.skybox.attribution,
|
|
10391
10446
|
description: meta.skybox.description
|
|
10392
10447
|
};
|
|
10393
|
-
await loadFromMeta(scene, renderer, meta.skybox);
|
|
10448
|
+
await loadFromMeta(scene, renderer, meta.skybox, disposed);
|
|
10394
10449
|
} catch (err) {
|
|
10395
10450
|
console.warn("Failed to load skybox:", err);
|
|
10396
10451
|
}
|
|
@@ -12393,6 +12448,7 @@ function bootThree(canvas, labelContainer, ctx) {
|
|
|
12393
12448
|
//#region src/lib/scene/minor-body-position.ts
|
|
12394
12449
|
function refreshMinorBodyPosition(body, jd, ctx) {
|
|
12395
12450
|
const d = body.data;
|
|
12451
|
+
body.placedJd = jd;
|
|
12396
12452
|
const isParabolic = d.q != null;
|
|
12397
12453
|
let parentPos;
|
|
12398
12454
|
if (d.parentId === "naif-0") parentPos = [
|
|
@@ -12431,6 +12487,47 @@ function refreshMinorBodyPosition(body, jd, ctx) {
|
|
|
12431
12487
|
}
|
|
12432
12488
|
body.positionUnknown = false;
|
|
12433
12489
|
}
|
|
12490
|
+
function refreshMoonPosition(body, jd, ctx) {
|
|
12491
|
+
const d = body.data;
|
|
12492
|
+
body.placedJd = jd;
|
|
12493
|
+
body.positionUnknown = true;
|
|
12494
|
+
if (d.unplaceable) return;
|
|
12495
|
+
if (d.visibleFromDays !== void 0 && jd - 2451545 < d.visibleFromDays) return;
|
|
12496
|
+
const parent = ctx.getBody(d.parentId);
|
|
12497
|
+
if (!parent || !placeForRead(parent, jd, ctx)) return;
|
|
12498
|
+
const parentPos = parent.position;
|
|
12499
|
+
const offset = ctx.chebStore?.has(d.id) ? ctx.chebStore.positionScene(d.id, jd) : d.a === 0 ? [
|
|
12500
|
+
0,
|
|
12501
|
+
0,
|
|
12502
|
+
0
|
|
12503
|
+
] : orbitalElementsToPositionJD(d, jd);
|
|
12504
|
+
if (!offset) return;
|
|
12505
|
+
body.position[0] = parentPos[0] + offset[0];
|
|
12506
|
+
body.position[1] = parentPos[1] + offset[1];
|
|
12507
|
+
body.position[2] = parentPos[2] + offset[2];
|
|
12508
|
+
if (body.orbitCenter) {
|
|
12509
|
+
body.orbitCenter[0] = parentPos[0];
|
|
12510
|
+
body.orbitCenter[1] = parentPos[1];
|
|
12511
|
+
body.orbitCenter[2] = parentPos[2];
|
|
12512
|
+
}
|
|
12513
|
+
body.positionUnknown = false;
|
|
12514
|
+
}
|
|
12515
|
+
var HOST_READ_MS = 2e3;
|
|
12516
|
+
function placeForRead(body, jd, ctx) {
|
|
12517
|
+
const d = body.data;
|
|
12518
|
+
const stale = body.placedJd !== jd;
|
|
12519
|
+
if (d.orbitalSource === OrbitalSource.SPICE_PROBE) {
|
|
12520
|
+
const { hostRead } = ctx.bodies;
|
|
12521
|
+
if (!stale && !hostRead.has(d.id)) return !body.positionUnknown;
|
|
12522
|
+
if (!hostRead.has(d.id)) ctx.bodies.hostReadVersion++;
|
|
12523
|
+
hostRead.set(d.id, performance.now());
|
|
12524
|
+
if (stale) return false;
|
|
12525
|
+
} else if (stale) {
|
|
12526
|
+
if (d.objectType === ObjectType.MOON) refreshMoonPosition(body, jd, ctx);
|
|
12527
|
+
else if (!ctx.bodies.bodiesById.has(d.id) && !isSurfaceFeature(body)) refreshMinorBodyPosition(body, jd, ctx);
|
|
12528
|
+
}
|
|
12529
|
+
return !body.positionUnknown;
|
|
12530
|
+
}
|
|
12434
12531
|
|
|
12435
12532
|
//#endregion
|
|
12436
12533
|
//#region src/lib/fetch/attitude/parse.ts
|
|
@@ -16766,6 +16863,7 @@ function applyRadiiToMesh(bo, radii) {
|
|
|
16766
16863
|
async function loadBodyTexture(bo, textureLoader, currentJd, scene, maxTextureSize, ctx) {
|
|
16767
16864
|
if (bo.textureTier || bo.textureLoading) return;
|
|
16768
16865
|
const detail = await fetchObjectDetail(bo.body.data.id, false);
|
|
16866
|
+
ctx?.bodies.orientationRead.add(bo.body.data.id);
|
|
16769
16867
|
if (!detail.global) return;
|
|
16770
16868
|
if (detail.global.orientation && !bo.body.orientation) applyBodyOrientation(bo, detail.global.orientation, ctx);
|
|
16771
16869
|
if (detail.global.radii && bo.radiusScene > 0 && !bo.radiiApplied && !detail.global.displacement?.absolute_radius) applyRadiiToMesh(bo, detail.global.radii);
|
|
@@ -17934,6 +18032,7 @@ async function loadSystemData(barycenterId, bodyObjects, scene, textureLoader, c
|
|
|
17934
18032
|
if (surface) ctx?.credits.registerImagery("surface", bodyId, barycenterId, surface);
|
|
17935
18033
|
const bo = bodyObjects.get(bodyId);
|
|
17936
18034
|
if (!bo?.mesh) continue;
|
|
18035
|
+
ctx?.bodies.orientationRead.add(bodyId);
|
|
17937
18036
|
if (bodyMeta.orientation) {
|
|
17938
18037
|
applyBodyOrientation(bo, bodyMeta.orientation, ctx, barycenterId);
|
|
17939
18038
|
if (bodyMeta.nut_prec) {
|
|
@@ -19098,6 +19197,7 @@ var SMALL_BODY_MAX_OPACITY = EMPHASIS_MAX_OPACITY;
|
|
|
19098
19197
|
var PointCloudSystem = class {
|
|
19099
19198
|
orbitPool = new OrbitWorkerPool();
|
|
19100
19199
|
pickRegistry = new PickRegistry();
|
|
19200
|
+
disposed = false;
|
|
19101
19201
|
workerPoints = {
|
|
19102
19202
|
asteroid: /* @__PURE__ */ new Map(),
|
|
19103
19203
|
spacecraft: /* @__PURE__ */ new Map()
|
|
@@ -19283,7 +19383,7 @@ var PointCloudSystem = class {
|
|
|
19283
19383
|
return true;
|
|
19284
19384
|
}
|
|
19285
19385
|
async rebuildMinorPass() {
|
|
19286
|
-
if (this.ctx.bodies.dirtyAsteroidZones.size === 0 && this.ctx.bodies.dirtySpacecraftGroups.size === 0) return;
|
|
19386
|
+
if (this.disposed || this.ctx.bodies.dirtyAsteroidZones.size === 0 && this.ctx.bodies.dirtySpacecraftGroups.size === 0) return;
|
|
19287
19387
|
const seedBasis = [
|
|
19288
19388
|
this.basisPos[0],
|
|
19289
19389
|
this.basisPos[1],
|
|
@@ -19303,6 +19403,7 @@ var PointCloudSystem = class {
|
|
|
19303
19403
|
groups: [],
|
|
19304
19404
|
baseWorker: 0
|
|
19305
19405
|
};
|
|
19406
|
+
if (this.disposed) return;
|
|
19306
19407
|
const cloudColor = bucket && groups.length > 0 ? bucket.cloudColor() : "#888888";
|
|
19307
19408
|
for (let i = 0; i < k; i++) {
|
|
19308
19409
|
const key = `${zone}#${i}`;
|
|
@@ -19330,6 +19431,7 @@ var PointCloudSystem = class {
|
|
|
19330
19431
|
this.ctx.bodies.dirtySpacecraftGroups.delete(gid);
|
|
19331
19432
|
drainedGroups.push(gid);
|
|
19332
19433
|
const { buckets, baseWorker } = await partitionForWorkersSliced(gid, drawnSpacecraft(this.ctx.layers, gid, bucket ? Array.from(bucket.values()) : []), k);
|
|
19434
|
+
if (this.disposed) return;
|
|
19333
19435
|
const skip = new Set(this.bodyObjects.keys());
|
|
19334
19436
|
for (let i = 0; i < k; i++) {
|
|
19335
19437
|
const key = `${gid}#${i}`;
|
|
@@ -19651,6 +19753,7 @@ var PointCloudSystem = class {
|
|
|
19651
19753
|
return this.moonPoints;
|
|
19652
19754
|
}
|
|
19653
19755
|
dispose() {
|
|
19756
|
+
this.disposed = true;
|
|
19654
19757
|
this.orbitPool.destroy();
|
|
19655
19758
|
this.pickRegistry.clear();
|
|
19656
19759
|
for (const kind of WORKER_CLOUD_KINDS) this.disposeClouds(this.workerPoints[kind]);
|
|
@@ -21271,18 +21374,32 @@ function updatePositions(params) {
|
|
|
21271
21374
|
for (const body of ctx.bodies.bodiesById.values()) positionMap.set(body.data.id, body.position);
|
|
21272
21375
|
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
21376
|
const computed = /* @__PURE__ */ new Set();
|
|
21377
|
+
const placeFirst = (id) => {
|
|
21378
|
+
if (computed.has(id)) return;
|
|
21379
|
+
const body = ctx.bodies.bodiesById.get(id) ?? bodyObjects.get(id)?.body;
|
|
21380
|
+
if (body) computePosition(body);
|
|
21381
|
+
};
|
|
21382
|
+
const now = performance.now();
|
|
21383
|
+
const hostReads = (id) => {
|
|
21384
|
+
const at = ctx.bodies.hostRead.get(id);
|
|
21385
|
+
if (at === void 0) return false;
|
|
21386
|
+
if (now - at < 2e3) return true;
|
|
21387
|
+
ctx.bodies.hostRead.delete(id);
|
|
21388
|
+
return false;
|
|
21389
|
+
};
|
|
21274
21390
|
const computePosition = (body) => {
|
|
21275
21391
|
if (computed.has(body.data.id)) return;
|
|
21276
21392
|
computed.add(body.data.id);
|
|
21277
21393
|
const d = body.data;
|
|
21278
21394
|
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))) {
|
|
21395
|
+
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
21396
|
bo.staggerSlot ??= nextStaggerSlot++ % HIDDEN_STRIDE;
|
|
21281
21397
|
if (frame % HIDDEN_STRIDE !== bo.staggerSlot) return;
|
|
21282
21398
|
}
|
|
21283
21399
|
const hide = (notForNotice = false) => {
|
|
21284
21400
|
if (bo) bo.outOfRange = true;
|
|
21285
21401
|
body.positionUnknown = true;
|
|
21402
|
+
body.placedJd = jd;
|
|
21286
21403
|
if (!notForNotice && d.id === focusedId) oorState.focusedOutOfRange = true;
|
|
21287
21404
|
};
|
|
21288
21405
|
if (d.unplaceable) {
|
|
@@ -21363,6 +21480,7 @@ function updatePositions(params) {
|
|
|
21363
21480
|
const probeLanded = located.probe.landed;
|
|
21364
21481
|
if (probeLanded && isLandedAt(located.probe, jd)) {
|
|
21365
21482
|
const flyingFrameId = d.parentId;
|
|
21483
|
+
placeFirst(`naif-${probeLanded.bodyNaifId}`);
|
|
21366
21484
|
const landedRender = renderLandedProbe(d, located.probe, probeLanded, jd, positionMap, ctx, bodyObjects);
|
|
21367
21485
|
if (!landedRender) {
|
|
21368
21486
|
hide();
|
|
@@ -21370,6 +21488,7 @@ function updatePositions(params) {
|
|
|
21370
21488
|
}
|
|
21371
21489
|
diagnostics.clear("probe-unavailable", d.id);
|
|
21372
21490
|
body.positionUnknown = false;
|
|
21491
|
+
body.placedJd = jd;
|
|
21373
21492
|
if (flyingFrameId !== d.parentId) body.trailBuffer?.clear();
|
|
21374
21493
|
if (bo) {
|
|
21375
21494
|
bo.outOfRange = false;
|
|
@@ -21449,10 +21568,7 @@ function updatePositions(params) {
|
|
|
21449
21568
|
populateProbeTrailBuffer(buf, ctx.probeStore, ctx.chebStore ?? null, d.id, probeParentKey, jd);
|
|
21450
21569
|
}
|
|
21451
21570
|
if (probeParentChanged) d.parentId = probeParentKey;
|
|
21452
|
-
|
|
21453
|
-
const parentBody = ctx.bodies.bodiesById.get(probeParentKey) ?? bodyObjects.get(probeParentKey)?.body;
|
|
21454
|
-
if (parentBody) computePosition(parentBody);
|
|
21455
|
-
}
|
|
21571
|
+
placeFirst(probeParentKey);
|
|
21456
21572
|
const probeParentPos = positionMap.get(probeParentKey);
|
|
21457
21573
|
if (!probeParentPos) {
|
|
21458
21574
|
hide();
|
|
@@ -21507,6 +21623,7 @@ function updatePositions(params) {
|
|
|
21507
21623
|
}
|
|
21508
21624
|
if (bo) bo.outOfRange = false;
|
|
21509
21625
|
body.positionUnknown = false;
|
|
21626
|
+
body.placedJd = jd;
|
|
21510
21627
|
body.position[0] = x;
|
|
21511
21628
|
body.position[1] = y;
|
|
21512
21629
|
body.position[2] = z;
|
|
@@ -23180,7 +23297,7 @@ function upgradeTargets(focus, ctx) {
|
|
|
23180
23297
|
//#endregion
|
|
23181
23298
|
//#region src/lib/scene/probe-coverage-watch.ts
|
|
23182
23299
|
var STOP_INSET_JD = 1e-6;
|
|
23183
|
-
function displayName(body) {
|
|
23300
|
+
function displayName$1(body) {
|
|
23184
23301
|
return body.data.name ?? body.data.id;
|
|
23185
23302
|
}
|
|
23186
23303
|
var ProbeCoverageWatch = class {
|
|
@@ -23223,7 +23340,7 @@ var ProbeCoverageWatch = class {
|
|
|
23223
23340
|
onHit(body, hitJd, forwardJd) {
|
|
23224
23341
|
this.notices.notify({
|
|
23225
23342
|
topic: "coverage-pause",
|
|
23226
|
-
name: displayName(body),
|
|
23343
|
+
name: displayName$1(body),
|
|
23227
23344
|
direction: hitJd >= forwardJd ? "forward" : "backward",
|
|
23228
23345
|
jd: hitJd
|
|
23229
23346
|
});
|
|
@@ -23299,10 +23416,14 @@ function rotateByQuaternion(q, x, y, z, out, at) {
|
|
|
23299
23416
|
out[at + 1] = y + q.w * ty + (q.z * tx - q.x * tz);
|
|
23300
23417
|
out[at + 2] = z + q.w * tz + (q.x * ty - q.y * tx);
|
|
23301
23418
|
}
|
|
23419
|
+
function bodyCentre(body, ctx, jd) {
|
|
23420
|
+
return placeForRead(body, jd, ctx) ? body.position : null;
|
|
23421
|
+
}
|
|
23302
23422
|
function resolveAnchor(anchor, ctx, jd) {
|
|
23303
23423
|
const body = ctx.getBody(anchor.body);
|
|
23304
|
-
|
|
23305
|
-
|
|
23424
|
+
const centre = body && bodyCentre(body, ctx, jd);
|
|
23425
|
+
if (!centre) return null;
|
|
23426
|
+
const [cx, cy, cz] = centre;
|
|
23306
23427
|
if (isSurface(anchor)) {
|
|
23307
23428
|
const distance = kmToScene(effectiveRadiusKm(body.data) + (anchor.altitudeKm ?? 0));
|
|
23308
23429
|
const [dx, dy, dz] = surfaceDirection(anchor.latitude, anchor.longitude);
|
|
@@ -23338,12 +23459,13 @@ function resolveAnchor(anchor, ctx, jd) {
|
|
|
23338
23459
|
cz + oz
|
|
23339
23460
|
];
|
|
23340
23461
|
}
|
|
23341
|
-
function anchorNormal(anchor, world, ctx) {
|
|
23462
|
+
function anchorNormal(anchor, world, ctx, jd) {
|
|
23342
23463
|
const body = ctx.getBody(anchor.body);
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
const
|
|
23346
|
-
const
|
|
23464
|
+
const centre = body && bodyCentre(body, ctx, jd);
|
|
23465
|
+
if (!centre) return null;
|
|
23466
|
+
const dx = world[0] - centre[0];
|
|
23467
|
+
const dy = world[1] - centre[1];
|
|
23468
|
+
const dz = world[2] - centre[2];
|
|
23347
23469
|
const length = Math.hypot(dx, dy, dz);
|
|
23348
23470
|
if (length === 0) return null;
|
|
23349
23471
|
return [
|
|
@@ -23568,9 +23690,14 @@ var SceneRenderer = class SceneRenderer {
|
|
|
23568
23690
|
focusDurationMs: FOCUS_DURATION_MS
|
|
23569
23691
|
};
|
|
23570
23692
|
lastUpdatedJd = NaN;
|
|
23693
|
+
get placedJd() {
|
|
23694
|
+
return this.lastUpdatedJd;
|
|
23695
|
+
}
|
|
23571
23696
|
lastUpdatedSystemId = null;
|
|
23572
23697
|
lastSeatConfigKey = null;
|
|
23573
23698
|
lastProbeVersion = 0;
|
|
23699
|
+
disposed = false;
|
|
23700
|
+
lastHostReadVersion = 0;
|
|
23574
23701
|
focusWasOutOfRange = false;
|
|
23575
23702
|
_positionMapScratch = /* @__PURE__ */ new Map();
|
|
23576
23703
|
frameIndex = 0;
|
|
@@ -23673,7 +23800,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
23673
23800
|
this.orbitPreview = new OrbitPreviewOverlay(this.scene);
|
|
23674
23801
|
this.orbitPreview.setLayer(SceneRenderer.MAP_LAYER);
|
|
23675
23802
|
this.skyboxAdjuster.set(0, 0, 0);
|
|
23676
|
-
loadSkybox(this.scene, this.renderer, ctx);
|
|
23803
|
+
loadSkybox(this.scene, this.renderer, ctx, () => this.disposed);
|
|
23677
23804
|
const sunBody = ctx.bodies.majorBodies.find((b) => b.data.id === SUN_ID);
|
|
23678
23805
|
const resident = ctx.getBody(initialView.id);
|
|
23679
23806
|
const matchedBody = resident && !resident.positionUnknown ? resident : void 0;
|
|
@@ -24542,7 +24669,8 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24542
24669
|
const systemId = this.ctx.visibility.focusedSystemId;
|
|
24543
24670
|
const seatKey = this.focusedSeatConfigKey();
|
|
24544
24671
|
const probeVersion = this.ctx.probeStore?.version ?? 0;
|
|
24545
|
-
|
|
24672
|
+
const hostReadVersion = this.ctx.bodies.hostReadVersion;
|
|
24673
|
+
if (this.clock.jd === this.lastUpdatedJd && systemId === this.lastUpdatedSystemId && seatKey === this.lastSeatConfigKey && probeVersion === this.lastProbeVersion && hostReadVersion === this.lastHostReadVersion) {
|
|
24546
24674
|
this.clock.seeked = false;
|
|
24547
24675
|
return;
|
|
24548
24676
|
}
|
|
@@ -24552,6 +24680,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24552
24680
|
this.lastUpdatedSystemId = systemId;
|
|
24553
24681
|
this.lastSeatConfigKey = seatKey;
|
|
24554
24682
|
this.lastProbeVersion = probeVersion;
|
|
24683
|
+
this.lastHostReadVersion = hostReadVersion;
|
|
24555
24684
|
this.ctx.refreshTick(jdToDate(this.clock.jd));
|
|
24556
24685
|
this.focusController.promotion.onSimTimeChanged();
|
|
24557
24686
|
const result = updatePositions({
|
|
@@ -24887,7 +25016,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24887
25016
|
this.renderer.setPixelRatio(renderPixelRatio());
|
|
24888
25017
|
this.composer.setPixelRatio(renderPixelRatio());
|
|
24889
25018
|
this.resize(this.canvas.clientWidth, this.canvas.clientHeight);
|
|
24890
|
-
loadSkybox(this.scene, this.renderer, this.ctx);
|
|
25019
|
+
loadSkybox(this.scene, this.renderer, this.ctx, () => this.disposed);
|
|
24891
25020
|
this.resume();
|
|
24892
25021
|
}
|
|
24893
25022
|
recoverWorkersIfDead(timeoutMs) {
|
|
@@ -24918,6 +25047,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24918
25047
|
setTrailResolution(width, height);
|
|
24919
25048
|
}
|
|
24920
25049
|
dispose() {
|
|
25050
|
+
this.disposed = true;
|
|
24921
25051
|
cancelAnimationFrame(this.rafId);
|
|
24922
25052
|
this.pointerInteraction.detach();
|
|
24923
25053
|
this.gpuPick.dispose();
|
|
@@ -25339,8 +25469,8 @@ var MarkerExtension = class {
|
|
|
25339
25469
|
this.label.visible = this.occlude ? this.facesCamera(world, frame) : true;
|
|
25340
25470
|
this.object.updateMatrixWorld(true);
|
|
25341
25471
|
}
|
|
25342
|
-
facesCamera(world, { camera, ctx }) {
|
|
25343
|
-
const normal = anchorNormal(this.anchor, world, ctx);
|
|
25472
|
+
facesCamera(world, { camera, ctx, jd }) {
|
|
25473
|
+
const normal = anchorNormal(this.anchor, world, ctx, jd);
|
|
25344
25474
|
if (!normal) return true;
|
|
25345
25475
|
const { position } = this.label;
|
|
25346
25476
|
const dx = camera.position.x - position.x;
|
|
@@ -26702,16 +26832,17 @@ var SurfaceShapeExtension = class {
|
|
|
26702
26832
|
}
|
|
26703
26833
|
update({ jd, basis, camera, ctx }) {
|
|
26704
26834
|
const body = this.wanted && this.count >= 2 ? ctx.getBody(this.body) : void 0;
|
|
26705
|
-
|
|
26706
|
-
if (this.
|
|
26707
|
-
if (
|
|
26835
|
+
const centre = body && bodyCentre(body, ctx, jd);
|
|
26836
|
+
if (this.line) this.line.visible = !!centre;
|
|
26837
|
+
if (this.fill) this.fill.visible = !!centre;
|
|
26838
|
+
if (!body || !centre) return;
|
|
26708
26839
|
const radiusKm = effectiveRadiusKm(body.data);
|
|
26709
26840
|
const scale = kmToScene(radiusKm + (this.altitudeKm ?? radiusKm * DEFAULT_LIFT));
|
|
26710
26841
|
const q = body.orientation ? bodyQuaternion(body.orientation, jd, body.nutPrec) : null;
|
|
26711
26842
|
const origin = [
|
|
26712
|
-
|
|
26713
|
-
|
|
26714
|
-
|
|
26843
|
+
centre[0] - basis[0],
|
|
26844
|
+
centre[1] - basis[1],
|
|
26845
|
+
centre[2] - basis[2]
|
|
26715
26846
|
];
|
|
26716
26847
|
if (this.line) {
|
|
26717
26848
|
this.place(this.dirs, this.count, q, scale, origin, this.scratch);
|
|
@@ -26941,6 +27072,50 @@ var MapObjectCollection = class {
|
|
|
26941
27072
|
}
|
|
26942
27073
|
};
|
|
26943
27074
|
|
|
27075
|
+
//#endregion
|
|
27076
|
+
//#region src/lib/scene/extensions/measure.ts
|
|
27077
|
+
function anchorOffsetKm(a, b, ctx, jd) {
|
|
27078
|
+
const from = resolveAnchor(a, ctx, jd);
|
|
27079
|
+
const to = resolveAnchor(b, ctx, jd);
|
|
27080
|
+
if (!from || !to) return null;
|
|
27081
|
+
return sceneToEcliptic([
|
|
27082
|
+
to[0] - from[0],
|
|
27083
|
+
to[1] - from[1],
|
|
27084
|
+
to[2] - from[2]
|
|
27085
|
+
]);
|
|
27086
|
+
}
|
|
27087
|
+
function anchorDistanceKm(a, b, ctx, jd) {
|
|
27088
|
+
const offset = anchorOffsetKm(a, b, ctx, jd);
|
|
27089
|
+
return offset && Math.hypot(...offset);
|
|
27090
|
+
}
|
|
27091
|
+
function subsolarPoint(id, ctx, jd) {
|
|
27092
|
+
const body = ctx.getBody(id);
|
|
27093
|
+
const sun = ctx.getBody(SUN_ID);
|
|
27094
|
+
if (!body || !sun || body === sun) return null;
|
|
27095
|
+
if (!body.orientation && !ctx.bodies.orientationRead.has(id)) return null;
|
|
27096
|
+
const centre = bodyCentre(body, ctx, jd);
|
|
27097
|
+
if (!centre) return null;
|
|
27098
|
+
const turn = body.orientation ? bodyQuaternion(body.orientation, jd, body.nutPrec).toArray() : [
|
|
27099
|
+
0,
|
|
27100
|
+
0,
|
|
27101
|
+
0,
|
|
27102
|
+
1
|
|
27103
|
+
];
|
|
27104
|
+
const { latitude, longitude, distance } = cartesianToSpherical([
|
|
27105
|
+
sun.position[0],
|
|
27106
|
+
sun.position[1],
|
|
27107
|
+
sun.position[2]
|
|
27108
|
+
], [
|
|
27109
|
+
centre[0],
|
|
27110
|
+
centre[1],
|
|
27111
|
+
centre[2]
|
|
27112
|
+
], turn);
|
|
27113
|
+
return distance > 0 ? {
|
|
27114
|
+
lon: longitude,
|
|
27115
|
+
lat: latitude
|
|
27116
|
+
} : null;
|
|
27117
|
+
}
|
|
27118
|
+
|
|
26944
27119
|
//#endregion
|
|
26945
27120
|
//#region src/lib/interaction/gesture.ts
|
|
26946
27121
|
var GestureHandler = class {
|
|
@@ -27121,7 +27296,8 @@ var KEY_ROTATE_RAD = .05;
|
|
|
27121
27296
|
var KEY_ZOOM_FACTOR = 1.15;
|
|
27122
27297
|
var CONTEXT_LOST_PANEL_DELAY_MS = 2e3;
|
|
27123
27298
|
var RESTORE_PING_TIMEOUT_MS = 4e3;
|
|
27124
|
-
var
|
|
27299
|
+
var mapsIn = /* @__PURE__ */ new WeakMap();
|
|
27300
|
+
var onceTarget$2 = /* @__PURE__ */ new WeakMap();
|
|
27125
27301
|
var SpaceMap = class {
|
|
27126
27302
|
ctx = new ContextManager();
|
|
27127
27303
|
clock;
|
|
@@ -27222,14 +27398,14 @@ var SpaceMap = class {
|
|
|
27222
27398
|
off();
|
|
27223
27399
|
listener(...args);
|
|
27224
27400
|
};
|
|
27225
|
-
onceTarget$
|
|
27401
|
+
onceTarget$2.set(wrapped, listener);
|
|
27226
27402
|
const off = this.on(event, wrapped);
|
|
27227
27403
|
return off;
|
|
27228
27404
|
}
|
|
27229
27405
|
off(event, listener) {
|
|
27230
27406
|
const set = this.listeners[event];
|
|
27231
27407
|
set.delete(listener);
|
|
27232
|
-
for (const held of set) if (onceTarget$
|
|
27408
|
+
for (const held of set) if (onceTarget$2.get(held) === listener) set.delete(held);
|
|
27233
27409
|
}
|
|
27234
27410
|
emit(event, ...args) {
|
|
27235
27411
|
for (const listener of this.listeners[event]) listener(...args);
|
|
@@ -27247,6 +27423,19 @@ var SpaceMap = class {
|
|
|
27247
27423
|
getChildren(id) {
|
|
27248
27424
|
return [...this.ctx.bodies.getChildren(id) ?? []];
|
|
27249
27425
|
}
|
|
27426
|
+
get placedJd() {
|
|
27427
|
+
const jd = this.renderer?.placedJd;
|
|
27428
|
+
return jd === void 0 || Number.isNaN(jd) ? this.clock.jd : jd;
|
|
27429
|
+
}
|
|
27430
|
+
distanceKm(from, to) {
|
|
27431
|
+
return anchorDistanceKm(from, to, this.ctx, this.placedJd);
|
|
27432
|
+
}
|
|
27433
|
+
offsetKm(from, to) {
|
|
27434
|
+
return anchorOffsetKm(from, to, this.ctx, this.placedJd);
|
|
27435
|
+
}
|
|
27436
|
+
getSubsolarPoint(id) {
|
|
27437
|
+
return subsolarPoint(id, this.ctx, this.placedJd);
|
|
27438
|
+
}
|
|
27250
27439
|
load(targetId = this.initialView.id) {
|
|
27251
27440
|
return this.ctx.load(jdToDate(this.clock.jd), targetId);
|
|
27252
27441
|
}
|
|
@@ -27278,6 +27467,7 @@ var SpaceMap = class {
|
|
|
27278
27467
|
if (this.container) throw new Error("SpaceMap is already mounted");
|
|
27279
27468
|
this.container = container;
|
|
27280
27469
|
container.classList.add("sm-map");
|
|
27470
|
+
mapsIn.set(container, (mapsIn.get(container) ?? 0) + 1);
|
|
27281
27471
|
this.controls = new ControlHost(this, container);
|
|
27282
27472
|
const canvas = document.createElement("canvas");
|
|
27283
27473
|
canvas.className = "sm-map__canvas";
|
|
@@ -27357,7 +27547,9 @@ var SpaceMap = class {
|
|
|
27357
27547
|
this.renderer = null;
|
|
27358
27548
|
canvas.remove();
|
|
27359
27549
|
this.labelLayer?.remove();
|
|
27360
|
-
|
|
27550
|
+
const left = (mapsIn.get(container) ?? 1) - 1;
|
|
27551
|
+
mapsIn.set(container, left);
|
|
27552
|
+
if (left === 0) container.classList.remove("sm-map");
|
|
27361
27553
|
this.container = this.canvas = this.labelLayer = null;
|
|
27362
27554
|
}
|
|
27363
27555
|
watch(renderer) {
|
|
@@ -27778,7 +27970,7 @@ var SpaceMap = class {
|
|
|
27778
27970
|
|
|
27779
27971
|
//#endregion
|
|
27780
27972
|
//#region src/lib/flatmap/layers.ts
|
|
27781
|
-
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
27973
|
+
var SVG_NS$1 = "http://www.w3.org/2000/svg";
|
|
27782
27974
|
function layerInfo(layer) {
|
|
27783
27975
|
return {
|
|
27784
27976
|
id: layer.id,
|
|
@@ -27796,7 +27988,7 @@ function creditOf(bundle) {
|
|
|
27796
27988
|
};
|
|
27797
27989
|
}
|
|
27798
27990
|
function pathElement(d, className) {
|
|
27799
|
-
const path = document.createElementNS(SVG_NS, "path");
|
|
27991
|
+
const path = document.createElementNS(SVG_NS$1, "path");
|
|
27800
27992
|
path.setAttribute("d", d);
|
|
27801
27993
|
path.setAttribute("class", className);
|
|
27802
27994
|
return path;
|
|
@@ -27815,7 +28007,7 @@ function graticuleLayer(label, stepDeg = 30) {
|
|
|
27815
28007
|
if (viewport.projection.azimuthal) {
|
|
27816
28008
|
const centre = viewport.toScreen(0, 0);
|
|
27817
28009
|
const edge = viewport.toScreen(viewport.projection.extent.maxX, 0);
|
|
27818
|
-
const circle = document.createElementNS(SVG_NS, "circle");
|
|
28010
|
+
const circle = document.createElementNS(SVG_NS$1, "circle");
|
|
27819
28011
|
circle.setAttribute("cx", String(centre[0]));
|
|
27820
28012
|
circle.setAttribute("cy", String(centre[1]));
|
|
27821
28013
|
circle.setAttribute("r", String(Math.abs(edge[0] - centre[0])));
|
|
@@ -27852,12 +28044,12 @@ function nomenclatureLayer(bodyId, label) {
|
|
|
27852
28044
|
for (const shift of shifts) {
|
|
27853
28045
|
const x = at[0] + shift;
|
|
27854
28046
|
if (x < 0 || x > viewport.width) continue;
|
|
27855
|
-
const dot = document.createElementNS(SVG_NS, "circle");
|
|
28047
|
+
const dot = document.createElementNS(SVG_NS$1, "circle");
|
|
27856
28048
|
dot.setAttribute("cx", x.toFixed(1));
|
|
27857
28049
|
dot.setAttribute("cy", at[1].toFixed(1));
|
|
27858
28050
|
dot.setAttribute("r", "1.6");
|
|
27859
28051
|
dot.setAttribute("class", "sm-flat__feature-dot");
|
|
27860
|
-
const text = document.createElementNS(SVG_NS, "text");
|
|
28052
|
+
const text = document.createElementNS(SVG_NS$1, "text");
|
|
27861
28053
|
text.setAttribute("x", (x + 4).toFixed(1));
|
|
27862
28054
|
text.setAttribute("y", (at[1] + 3).toFixed(1));
|
|
27863
28055
|
text.setAttribute("class", "sm-flat__feature-label");
|
|
@@ -27904,12 +28096,12 @@ var PathDrawing = class {
|
|
|
27904
28096
|
this.onRemove = onRemove;
|
|
27905
28097
|
this.onChange = onChange;
|
|
27906
28098
|
this.points = points;
|
|
27907
|
-
this.node = document.createElementNS(SVG_NS, "g");
|
|
28099
|
+
this.node = document.createElementNS(SVG_NS$1, "g");
|
|
27908
28100
|
applyStyle(this.node, style);
|
|
27909
28101
|
bindEvents(this.node, style);
|
|
27910
|
-
this.area = closed ? document.createElementNS(SVG_NS, "path") : null;
|
|
28102
|
+
this.area = closed ? document.createElementNS(SVG_NS$1, "path") : null;
|
|
27911
28103
|
this.area?.setAttribute("stroke", "none");
|
|
27912
|
-
this.edge = document.createElementNS(SVG_NS, "path");
|
|
28104
|
+
this.edge = document.createElementNS(SVG_NS$1, "path");
|
|
27913
28105
|
this.edge.setAttribute("fill", "none");
|
|
27914
28106
|
if (this.area) this.node.append(this.area);
|
|
27915
28107
|
this.node.append(this.edge);
|
|
@@ -28230,27 +28422,33 @@ async function fetchJson(url) {
|
|
|
28230
28422
|
return null;
|
|
28231
28423
|
}
|
|
28232
28424
|
}
|
|
28233
|
-
async function
|
|
28234
|
-
const
|
|
28235
|
-
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
].includes(k));
|
|
28242
|
-
if (tiers.length === 0) return {};
|
|
28243
|
-
return { surface: {
|
|
28425
|
+
async function tiersOf(bundleId) {
|
|
28426
|
+
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)));
|
|
28427
|
+
return TIER_ORDER.filter((_, i) => found[i]);
|
|
28428
|
+
}
|
|
28429
|
+
async function fromRecord(bodyId, record, tiers = tiersOf) {
|
|
28430
|
+
if (!record?.map_texture_available || !record.texture) return {};
|
|
28431
|
+
const surface = pickTexture([{
|
|
28432
|
+
...record.texture,
|
|
28244
28433
|
id: bodyId,
|
|
28245
|
-
tiers
|
|
28246
|
-
|
|
28247
|
-
|
|
28248
|
-
|
|
28249
|
-
|
|
28250
|
-
|
|
28251
|
-
|
|
28252
|
-
|
|
28253
|
-
|
|
28434
|
+
tiers: void 0
|
|
28435
|
+
}, ...record.alternates ?? []]);
|
|
28436
|
+
if (!surface) return {};
|
|
28437
|
+
const written = surface.tiers ?? await tiers(surface.id);
|
|
28438
|
+
if (written.length === 0) return {};
|
|
28439
|
+
const { frames, ...bundle } = surface;
|
|
28440
|
+
const radii = record.radii;
|
|
28441
|
+
return {
|
|
28442
|
+
surface: {
|
|
28443
|
+
...bundle,
|
|
28444
|
+
tiers: written,
|
|
28445
|
+
monthlyFrames: frames
|
|
28446
|
+
},
|
|
28447
|
+
radiusKm: radii ? (radii.a + radii.b + radii.c) / 3 : void 0
|
|
28448
|
+
};
|
|
28449
|
+
}
|
|
28450
|
+
async function fromBundle(bodyId) {
|
|
28451
|
+
return fromRecord(bodyId, (await fetchObjectDetail(bodyId, false).catch(() => null))?.global);
|
|
28254
28452
|
}
|
|
28255
28453
|
async function loadBodySources(bodyId) {
|
|
28256
28454
|
const systemId = systemIdFor(bodyId);
|
|
@@ -28551,14 +28749,14 @@ var RASTER_LAYERS = [
|
|
|
28551
28749
|
function isRaster(layer) {
|
|
28552
28750
|
return layer.kind === "raster";
|
|
28553
28751
|
}
|
|
28554
|
-
var onceTarget = /* @__PURE__ */ new WeakMap();
|
|
28752
|
+
var onceTarget$1 = /* @__PURE__ */ new WeakMap();
|
|
28555
28753
|
var FlatMap = class {
|
|
28556
28754
|
container = null;
|
|
28557
28755
|
root = document.createElement("div");
|
|
28558
28756
|
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");
|
|
28757
|
+
svg = document.createElementNS(SVG_NS$1, "svg");
|
|
28758
|
+
layerGroup = document.createElementNS(SVG_NS$1, "g");
|
|
28759
|
+
shapeGroup = document.createElementNS(SVG_NS$1, "g");
|
|
28562
28760
|
markerLayer = document.createElement("div");
|
|
28563
28761
|
ctx;
|
|
28564
28762
|
overlay;
|
|
@@ -28929,7 +29127,7 @@ var FlatMap = class {
|
|
|
28929
29127
|
off();
|
|
28930
29128
|
listener(...args);
|
|
28931
29129
|
});
|
|
28932
|
-
onceTarget.set(wrapped, listener);
|
|
29130
|
+
onceTarget$1.set(wrapped, listener);
|
|
28933
29131
|
const off = this.on(event, wrapped);
|
|
28934
29132
|
return off;
|
|
28935
29133
|
}
|
|
@@ -28937,7 +29135,7 @@ var FlatMap = class {
|
|
|
28937
29135
|
const set = this.listeners.get(event);
|
|
28938
29136
|
if (!set) return;
|
|
28939
29137
|
set.delete(listener);
|
|
28940
|
-
for (const held of set) if (onceTarget.get(held) === listener) set.delete(held);
|
|
29138
|
+
for (const held of set) if (onceTarget$1.get(held) === listener) set.delete(held);
|
|
28941
29139
|
}
|
|
28942
29140
|
emit(event, ...args) {
|
|
28943
29141
|
const set = this.listeners.get(event);
|
|
@@ -29141,7 +29339,7 @@ var FlatMap = class {
|
|
|
29141
29339
|
this.layerGroup.replaceChildren();
|
|
29142
29340
|
for (const layer of this.layerList) {
|
|
29143
29341
|
if (isRaster(layer) || !layer.visible) continue;
|
|
29144
|
-
const group = document.createElementNS(SVG_NS, "g");
|
|
29342
|
+
const group = document.createElementNS(SVG_NS$1, "g");
|
|
29145
29343
|
group.setAttribute("data-layer", layer.id);
|
|
29146
29344
|
layer.render(viewport, group);
|
|
29147
29345
|
this.layerGroup.append(group);
|
|
@@ -30074,6 +30272,1455 @@ var PanoramaView = class {
|
|
|
30074
30272
|
};
|
|
30075
30273
|
};
|
|
30076
30274
|
|
|
30275
|
+
//#endregion
|
|
30276
|
+
//#region src/lib/fetch/groups/planetary-systems-map.ts
|
|
30277
|
+
var fetchPlanetarySystemsMap = memoizedGzJson(() => `${dataBase()}/v1/groups/__planetary_systems_map__.json.gz`, { error: (res) => `Failed to fetch planetary systems map: ${res.status}` });
|
|
30278
|
+
|
|
30279
|
+
//#endregion
|
|
30280
|
+
//#region src/lib/fetch/groups/solar-system-map.ts
|
|
30281
|
+
var fetchSolarSystemMap = memoizedGzJson(() => `${dataBase()}/v1/groups/__solar_system_map__.json.gz`, { error: (res) => `Failed to fetch solar system map: ${res.status}` });
|
|
30282
|
+
|
|
30283
|
+
//#endregion
|
|
30284
|
+
//#region src/lib/charts/scrub.ts
|
|
30285
|
+
var DRAG_SLOP = 8;
|
|
30286
|
+
function createScrub({ onScrub, onEnd }) {
|
|
30287
|
+
let downX = null;
|
|
30288
|
+
let downY = 0;
|
|
30289
|
+
let scrubbing = false;
|
|
30290
|
+
function end() {
|
|
30291
|
+
if (scrubbing) onEnd();
|
|
30292
|
+
downX = null;
|
|
30293
|
+
scrubbing = false;
|
|
30294
|
+
}
|
|
30295
|
+
return {
|
|
30296
|
+
onpointerdown(e) {
|
|
30297
|
+
if (e.pointerType === "mouse") return;
|
|
30298
|
+
downX = e.clientX;
|
|
30299
|
+
downY = e.clientY;
|
|
30300
|
+
scrubbing = false;
|
|
30301
|
+
},
|
|
30302
|
+
onpointermove(e) {
|
|
30303
|
+
if (e.pointerType === "mouse" || downX === null) return;
|
|
30304
|
+
if (!scrubbing && Math.hypot(e.clientX - downX, e.clientY - downY) < 8) return;
|
|
30305
|
+
scrubbing = true;
|
|
30306
|
+
onScrub(e.clientX, e.clientY);
|
|
30307
|
+
},
|
|
30308
|
+
onpointerup: end,
|
|
30309
|
+
onpointercancel: end,
|
|
30310
|
+
onpointerleave: end
|
|
30311
|
+
};
|
|
30312
|
+
}
|
|
30313
|
+
|
|
30314
|
+
//#endregion
|
|
30315
|
+
//#region src/lib/systemmap/layout.ts
|
|
30316
|
+
var VIEW_H = 240;
|
|
30317
|
+
var X_LEFT = 44;
|
|
30318
|
+
var X_RIGHT = 700;
|
|
30319
|
+
var CY = 120;
|
|
30320
|
+
var MIN_R = 2.2;
|
|
30321
|
+
var MAX_OFFSET = 84;
|
|
30322
|
+
var PRIMARY_LIMB_X = 44 - 4;
|
|
30323
|
+
var MOON_GAP = 4;
|
|
30324
|
+
var MOON_SPACING = 3;
|
|
30325
|
+
var HIT_MARGIN = 16;
|
|
30326
|
+
var INSIDE = 1e4;
|
|
30327
|
+
var MOON_ZONE_W = 34;
|
|
30328
|
+
var MOON_ZONE_PAD = 7;
|
|
30329
|
+
var RING_FORESHORTEN = .42;
|
|
30330
|
+
var BAND_Y = 26;
|
|
30331
|
+
var BAND_H = 240 - 60;
|
|
30332
|
+
var BAND_MIN_HIT = 30;
|
|
30333
|
+
function minDistance(cx, cy, placed) {
|
|
30334
|
+
let min = Infinity;
|
|
30335
|
+
for (const p of placed) min = Math.min(min, Math.hypot(p.cx - cx, p.cy - cy));
|
|
30336
|
+
return min;
|
|
30337
|
+
}
|
|
30338
|
+
function foldTilt(tiltDeg) {
|
|
30339
|
+
return tiltDeg > 90 ? 180 - tiltDeg : tiltDeg;
|
|
30340
|
+
}
|
|
30341
|
+
function layout(model) {
|
|
30342
|
+
const logLo = Math.log10(model.domain[0]);
|
|
30343
|
+
const logSpan = Math.log10(model.domain[1]) - logLo;
|
|
30344
|
+
const xOf = (km) => {
|
|
30345
|
+
return 44 + (Math.log10(Math.max(km / model.unitKm, 1e-9)) - logLo) / logSpan * (700 - 44);
|
|
30346
|
+
};
|
|
30347
|
+
const bodies = [];
|
|
30348
|
+
for (const b of [...model.bodies].sort((a, b) => a.aKm - b.aKm)) {
|
|
30349
|
+
const cx = xOf(b.aKm);
|
|
30350
|
+
const r = Math.max(MIN_R, b.radiusKm * model.pxPerKm);
|
|
30351
|
+
let cy = 120;
|
|
30352
|
+
if (b.tiltDeg > 0) {
|
|
30353
|
+
const offset = Math.min(foldTilt(b.tiltDeg) * model.pxPerDeg, MAX_OFFSET);
|
|
30354
|
+
const up = 120 - offset;
|
|
30355
|
+
const down = 120 + offset;
|
|
30356
|
+
cy = minDistance(cx, up, bodies) >= minDistance(cx, down, bodies) ? up : down;
|
|
30357
|
+
}
|
|
30358
|
+
bodies.push({
|
|
30359
|
+
...b,
|
|
30360
|
+
cx,
|
|
30361
|
+
cy,
|
|
30362
|
+
r
|
|
30363
|
+
});
|
|
30364
|
+
}
|
|
30365
|
+
const moonZones = [];
|
|
30366
|
+
for (const parent of bodies) {
|
|
30367
|
+
if (!parent.satellites?.length) continue;
|
|
30368
|
+
const ringTop = parent.rings ? parent.r * parent.rings.outer * RING_FORESHORTEN : 0;
|
|
30369
|
+
let y = parent.cy - Math.max(parent.r, ringTop) - MOON_GAP;
|
|
30370
|
+
const moons = [];
|
|
30371
|
+
for (const s of [...parent.satellites].sort((a, b) => b.radiusKm - a.radiusKm)) {
|
|
30372
|
+
const r = Math.max(MIN_R, s.radiusKm * model.pxPerKm);
|
|
30373
|
+
y -= r;
|
|
30374
|
+
moons.push({
|
|
30375
|
+
...s,
|
|
30376
|
+
cx: parent.cx,
|
|
30377
|
+
cy: y,
|
|
30378
|
+
r,
|
|
30379
|
+
hitY: 0,
|
|
30380
|
+
hitHeight: 0
|
|
30381
|
+
});
|
|
30382
|
+
y -= r + MOON_SPACING;
|
|
30383
|
+
}
|
|
30384
|
+
const top = Math.min(...moons.map((mn) => mn.cy - mn.r)) - MOON_ZONE_PAD;
|
|
30385
|
+
const bottom = Math.max(...moons.map((mn) => mn.cy + mn.r)) + MOON_ZONE_PAD;
|
|
30386
|
+
moons.forEach((mn, idx) => {
|
|
30387
|
+
const below = idx === 0 ? bottom : (moons[idx - 1].cy + mn.cy) / 2;
|
|
30388
|
+
const above = idx === moons.length - 1 ? top : (moons[idx + 1].cy + mn.cy) / 2;
|
|
30389
|
+
mn.hitY = above;
|
|
30390
|
+
mn.hitHeight = below - above;
|
|
30391
|
+
});
|
|
30392
|
+
moonZones.push({
|
|
30393
|
+
parent,
|
|
30394
|
+
moons,
|
|
30395
|
+
x: parent.cx - MOON_ZONE_W / 2,
|
|
30396
|
+
y: top,
|
|
30397
|
+
width: MOON_ZONE_W,
|
|
30398
|
+
height: bottom - top,
|
|
30399
|
+
perMoon: !parent.grouped && !parent.satellitesLink && moons.some((mn) => mn.link)
|
|
30400
|
+
});
|
|
30401
|
+
}
|
|
30402
|
+
const bands = model.bands.map((b) => {
|
|
30403
|
+
const x = xOf(b.innerKm);
|
|
30404
|
+
const width = xOf(b.outerKm) - x;
|
|
30405
|
+
const hitWidth = Math.max(width, BAND_MIN_HIT);
|
|
30406
|
+
return {
|
|
30407
|
+
...b,
|
|
30408
|
+
x,
|
|
30409
|
+
width,
|
|
30410
|
+
hitX: x + (width - hitWidth) / 2,
|
|
30411
|
+
hitWidth
|
|
30412
|
+
};
|
|
30413
|
+
}).sort((a, b) => b.width - a.width);
|
|
30414
|
+
let cloudPath = "";
|
|
30415
|
+
model.cloud?.points.forEach((p, idx) => {
|
|
30416
|
+
const aUnits = p.aKm / model.unitKm;
|
|
30417
|
+
if (aUnits < model.domain[0] || aUnits > model.domain[1]) return;
|
|
30418
|
+
const offset = Math.min(Math.max(foldTilt(p.tiltDeg), 0) * model.pxPerDeg, MAX_OFFSET);
|
|
30419
|
+
const cy = 120 + (idx % 2 === 0 ? -offset : offset);
|
|
30420
|
+
cloudPath += `M${xOf(p.aKm).toFixed(1)} ${cy.toFixed(1)}h0.01`;
|
|
30421
|
+
});
|
|
30422
|
+
const primaryR = model.primary.radiusKm * model.pxPerKm;
|
|
30423
|
+
return {
|
|
30424
|
+
bodies,
|
|
30425
|
+
moonZones,
|
|
30426
|
+
bands,
|
|
30427
|
+
cloudPath,
|
|
30428
|
+
primaryR,
|
|
30429
|
+
primaryCx: PRIMARY_LIMB_X - primaryR,
|
|
30430
|
+
xOf
|
|
30431
|
+
};
|
|
30432
|
+
}
|
|
30433
|
+
function inside(z, vx, vy) {
|
|
30434
|
+
return vx >= z.x && vx <= z.x + z.width && vy >= z.y && vy <= z.y + z.height;
|
|
30435
|
+
}
|
|
30436
|
+
function nearestBody(l, vx, vy, reach) {
|
|
30437
|
+
for (const pickable of [true, false]) {
|
|
30438
|
+
let nearest = null;
|
|
30439
|
+
let gap = reach;
|
|
30440
|
+
for (const b of l.bodies) {
|
|
30441
|
+
if (!!b.link !== pickable) continue;
|
|
30442
|
+
const centre = Math.hypot(vx - b.cx, vy - b.cy);
|
|
30443
|
+
const edge = centre <= b.r ? centre - INSIDE : centre - b.r;
|
|
30444
|
+
if (edge > gap) continue;
|
|
30445
|
+
nearest = b;
|
|
30446
|
+
gap = edge;
|
|
30447
|
+
}
|
|
30448
|
+
if (nearest) return nearest;
|
|
30449
|
+
}
|
|
30450
|
+
return null;
|
|
30451
|
+
}
|
|
30452
|
+
function hitTest(l, vx, vy) {
|
|
30453
|
+
const on = nearestBody(l, vx, vy, 0);
|
|
30454
|
+
if (on) return {
|
|
30455
|
+
kind: "body",
|
|
30456
|
+
id: on.id
|
|
30457
|
+
};
|
|
30458
|
+
for (const z of l.moonZones) {
|
|
30459
|
+
if (!inside(z, vx, vy)) continue;
|
|
30460
|
+
if (z.parent.grouped) return {
|
|
30461
|
+
kind: "body",
|
|
30462
|
+
id: z.parent.id
|
|
30463
|
+
};
|
|
30464
|
+
if (!z.perMoon) return {
|
|
30465
|
+
kind: "stack",
|
|
30466
|
+
id: z.parent.id
|
|
30467
|
+
};
|
|
30468
|
+
const moon = z.moons.find((mn) => vy >= mn.hitY && vy <= mn.hitY + mn.hitHeight);
|
|
30469
|
+
if (moon) return {
|
|
30470
|
+
kind: "moon",
|
|
30471
|
+
parent: z.parent.id,
|
|
30472
|
+
id: moon.id
|
|
30473
|
+
};
|
|
30474
|
+
}
|
|
30475
|
+
const near = nearestBody(l, vx, vy, 16);
|
|
30476
|
+
if (near) return {
|
|
30477
|
+
kind: "body",
|
|
30478
|
+
id: near.id
|
|
30479
|
+
};
|
|
30480
|
+
for (let i = l.bands.length - 1; i >= 0; i--) {
|
|
30481
|
+
const band = l.bands[i];
|
|
30482
|
+
if (vx >= band.hitX && vx <= band.hitX + band.hitWidth && vy >= 26 && vy <= 26 + 180) return {
|
|
30483
|
+
kind: "band",
|
|
30484
|
+
key: band.key
|
|
30485
|
+
};
|
|
30486
|
+
}
|
|
30487
|
+
if (Math.hypot(vx - l.primaryCx, vy - 120) <= l.primaryR) return { kind: "primary" };
|
|
30488
|
+
return null;
|
|
30489
|
+
}
|
|
30490
|
+
function tipFor(model, l, hover) {
|
|
30491
|
+
if (!hover) return null;
|
|
30492
|
+
const { text } = model;
|
|
30493
|
+
switch (hover.kind) {
|
|
30494
|
+
case "primary": return {
|
|
30495
|
+
cx: Math.max(44, l.primaryCx),
|
|
30496
|
+
cy: 120 - Math.min(l.primaryR, 120 - 40),
|
|
30497
|
+
title: model.primary.name,
|
|
30498
|
+
sub: text.primary
|
|
30499
|
+
};
|
|
30500
|
+
case "body": {
|
|
30501
|
+
const b = l.bodies.find((p) => p.id === hover.id);
|
|
30502
|
+
if (!b) return null;
|
|
30503
|
+
const parts = [text.distance(b.aKm)];
|
|
30504
|
+
if (b.tiltDeg > 90) parts.push(text.retrograde);
|
|
30505
|
+
if (b.grouped && b.satellites?.length) parts.push(text.moons(b.satelliteCount ?? b.satellites.length));
|
|
30506
|
+
return {
|
|
30507
|
+
cx: b.cx,
|
|
30508
|
+
cy: b.cy - b.r - 4,
|
|
30509
|
+
title: b.name,
|
|
30510
|
+
sub: parts.join(" · ")
|
|
30511
|
+
};
|
|
30512
|
+
}
|
|
30513
|
+
case "stack": {
|
|
30514
|
+
const zone = l.moonZones.find((z) => z.parent.id === hover.id);
|
|
30515
|
+
if (!zone) return null;
|
|
30516
|
+
const cx = zone.x + zone.width / 2;
|
|
30517
|
+
return zone.parent.satellitesTab ? {
|
|
30518
|
+
cx,
|
|
30519
|
+
cy: zone.y,
|
|
30520
|
+
title: zone.parent.name,
|
|
30521
|
+
sub: text.moons(zone.parent.satelliteCount ?? zone.moons.length)
|
|
30522
|
+
} : {
|
|
30523
|
+
cx,
|
|
30524
|
+
cy: zone.y,
|
|
30525
|
+
title: zone.moons[0].name,
|
|
30526
|
+
sub: zone.parent.name
|
|
30527
|
+
};
|
|
30528
|
+
}
|
|
30529
|
+
case "moon": {
|
|
30530
|
+
const zone = l.moonZones.find((z) => z.parent.id === hover.parent);
|
|
30531
|
+
const moon = zone?.moons.find((mn) => mn.id === hover.id);
|
|
30532
|
+
if (!zone || !moon) return null;
|
|
30533
|
+
return {
|
|
30534
|
+
cx: moon.cx,
|
|
30535
|
+
cy: moon.cy - moon.r - 4,
|
|
30536
|
+
title: moon.name,
|
|
30537
|
+
sub: zone.parent.name
|
|
30538
|
+
};
|
|
30539
|
+
}
|
|
30540
|
+
case "band": {
|
|
30541
|
+
const band = l.bands.find((bd) => bd.key === hover.key);
|
|
30542
|
+
if (!band) return null;
|
|
30543
|
+
return {
|
|
30544
|
+
cx: band.x + band.width / 2,
|
|
30545
|
+
cy: 44,
|
|
30546
|
+
title: band.name ?? band.label,
|
|
30547
|
+
sub: band.sub ?? `${text.distance(band.innerKm)}–${text.distance(band.outerKm)}`
|
|
30548
|
+
};
|
|
30549
|
+
}
|
|
30550
|
+
}
|
|
30551
|
+
}
|
|
30552
|
+
|
|
30553
|
+
//#endregion
|
|
30554
|
+
//#region src/lib/systemmap/chart.ts
|
|
30555
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
30556
|
+
var PRIMARY_RIM = 10;
|
|
30557
|
+
var RING_TILT = -18;
|
|
30558
|
+
var CLOUD_R = 1.2;
|
|
30559
|
+
var TIP_H = 28;
|
|
30560
|
+
var TIP_MARGIN = 6;
|
|
30561
|
+
var LABEL_MARGIN = 4;
|
|
30562
|
+
var LABEL_SIZE = 18;
|
|
30563
|
+
var LABEL_MIN_SIZE = 12;
|
|
30564
|
+
var LABEL_MAX_W = 190;
|
|
30565
|
+
function svg(tag, attrs = {}, ...children) {
|
|
30566
|
+
const el = document.createElementNS(SVG_NS, tag);
|
|
30567
|
+
for (const [name, value] of Object.entries(attrs)) if (value !== void 0) el.setAttribute(name, String(value));
|
|
30568
|
+
for (const child of children) if (child) el.append(child);
|
|
30569
|
+
return el;
|
|
30570
|
+
}
|
|
30571
|
+
function sameHover(a, b) {
|
|
30572
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
30573
|
+
}
|
|
30574
|
+
function linksOf(model, l) {
|
|
30575
|
+
const links = /* @__PURE__ */ new Map();
|
|
30576
|
+
const add = (hover, link) => {
|
|
30577
|
+
if (link?.href || link?.onclick) links.set(JSON.stringify(hover), link);
|
|
30578
|
+
};
|
|
30579
|
+
for (const band of l.bands) add({
|
|
30580
|
+
kind: "band",
|
|
30581
|
+
key: band.key
|
|
30582
|
+
}, band);
|
|
30583
|
+
add({ kind: "primary" }, model.primary.link);
|
|
30584
|
+
for (const b of l.bodies) add({
|
|
30585
|
+
kind: "body",
|
|
30586
|
+
id: b.id
|
|
30587
|
+
}, b.link);
|
|
30588
|
+
for (const z of l.moonZones) {
|
|
30589
|
+
if (z.parent.grouped) continue;
|
|
30590
|
+
if (!z.perMoon) add({
|
|
30591
|
+
kind: "stack",
|
|
30592
|
+
id: z.parent.id
|
|
30593
|
+
}, z.parent.satellitesLink);
|
|
30594
|
+
else for (const mn of z.moons) add({
|
|
30595
|
+
kind: "moon",
|
|
30596
|
+
parent: z.parent.id,
|
|
30597
|
+
id: mn.id
|
|
30598
|
+
}, mn.link);
|
|
30599
|
+
}
|
|
30600
|
+
return links;
|
|
30601
|
+
}
|
|
30602
|
+
var nextUid = 0;
|
|
30603
|
+
var SystemMapChart = class {
|
|
30604
|
+
root = document.createElement("div");
|
|
30605
|
+
svg = svg("svg");
|
|
30606
|
+
tip = document.createElement("div");
|
|
30607
|
+
tipTitle = document.createElement("span");
|
|
30608
|
+
tipSub = document.createElement("span");
|
|
30609
|
+
uid = `sm-system-map-${nextUid++}`;
|
|
30610
|
+
resize = new ResizeObserver(() => {
|
|
30611
|
+
this.fitLabels();
|
|
30612
|
+
this.placeTip();
|
|
30613
|
+
});
|
|
30614
|
+
model = null;
|
|
30615
|
+
placed = null;
|
|
30616
|
+
background = false;
|
|
30617
|
+
hover = null;
|
|
30618
|
+
highlights = /* @__PURE__ */ new Map();
|
|
30619
|
+
targets = /* @__PURE__ */ new Map();
|
|
30620
|
+
links = /* @__PURE__ */ new Map();
|
|
30621
|
+
shape = "";
|
|
30622
|
+
labels = [];
|
|
30623
|
+
constructor() {
|
|
30624
|
+
this.tip.className = "sm-system-map__tip";
|
|
30625
|
+
this.tipTitle.className = "sm-system-map__tip-title";
|
|
30626
|
+
this.tipSub.className = "sm-system-map__tip-sub";
|
|
30627
|
+
this.tip.append(this.tipTitle, this.tipSub);
|
|
30628
|
+
this.tip.hidden = true;
|
|
30629
|
+
this.root.append(this.svg, this.tip);
|
|
30630
|
+
this.resize.observe(this.root);
|
|
30631
|
+
const scrub = createScrub({
|
|
30632
|
+
onScrub: (x, y) => this.setHover(this.hitAt(x, y)),
|
|
30633
|
+
onEnd: () => this.setHover(null)
|
|
30634
|
+
});
|
|
30635
|
+
this.svg.addEventListener("pointerdown", scrub.onpointerdown);
|
|
30636
|
+
this.svg.addEventListener("pointermove", scrub.onpointermove);
|
|
30637
|
+
this.svg.addEventListener("pointerup", scrub.onpointerup);
|
|
30638
|
+
this.svg.addEventListener("pointercancel", scrub.onpointercancel);
|
|
30639
|
+
this.svg.addEventListener("pointerleave", scrub.onpointerleave);
|
|
30640
|
+
let at = [0, 0];
|
|
30641
|
+
const track = (e) => {
|
|
30642
|
+
at = [e.clientX, e.clientY];
|
|
30643
|
+
};
|
|
30644
|
+
this.svg.addEventListener("pointerdown", track);
|
|
30645
|
+
this.svg.addEventListener("pointerup", track);
|
|
30646
|
+
this.svg.addEventListener("pointermove", (e) => {
|
|
30647
|
+
track(e);
|
|
30648
|
+
if (e.pointerType === "mouse") this.setHover(this.hitAt(e.clientX, e.clientY));
|
|
30649
|
+
});
|
|
30650
|
+
this.svg.addEventListener("pointerleave", (e) => {
|
|
30651
|
+
if (e.pointerType === "mouse") this.setHover(null);
|
|
30652
|
+
});
|
|
30653
|
+
this.svg.addEventListener("click", (e) => {
|
|
30654
|
+
if (e.detail === 0) return;
|
|
30655
|
+
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);
|
|
30656
|
+
if (!hit) return;
|
|
30657
|
+
const meant = this.targets.get(JSON.stringify(hit));
|
|
30658
|
+
if (meant?.contains(e.target)) return;
|
|
30659
|
+
e.stopPropagation();
|
|
30660
|
+
e.preventDefault();
|
|
30661
|
+
meant?.dispatchEvent(new MouseEvent("click", {
|
|
30662
|
+
bubbles: true,
|
|
30663
|
+
cancelable: true,
|
|
30664
|
+
ctrlKey: e.ctrlKey,
|
|
30665
|
+
metaKey: e.metaKey,
|
|
30666
|
+
shiftKey: e.shiftKey,
|
|
30667
|
+
altKey: e.altKey,
|
|
30668
|
+
button: e.button
|
|
30669
|
+
}));
|
|
30670
|
+
}, true);
|
|
30671
|
+
}
|
|
30672
|
+
destroy() {
|
|
30673
|
+
this.resize.disconnect();
|
|
30674
|
+
this.root.remove();
|
|
30675
|
+
}
|
|
30676
|
+
update(model, options) {
|
|
30677
|
+
this.model = model;
|
|
30678
|
+
this.placed = layout(model);
|
|
30679
|
+
const links = linksOf(model, this.placed);
|
|
30680
|
+
const shape = JSON.stringify([
|
|
30681
|
+
JSON.stringify(model, (key, value) => key === "href" ? void 0 : value),
|
|
30682
|
+
options,
|
|
30683
|
+
[...links].map(([key, link]) => [
|
|
30684
|
+
key,
|
|
30685
|
+
!!link.href,
|
|
30686
|
+
!!link.onclick
|
|
30687
|
+
])
|
|
30688
|
+
]);
|
|
30689
|
+
this.links = links;
|
|
30690
|
+
if (shape === this.shape) {
|
|
30691
|
+
for (const [key, el] of this.targets) {
|
|
30692
|
+
const href = links.get(key)?.href;
|
|
30693
|
+
if (href) el.setAttribute("href", href);
|
|
30694
|
+
}
|
|
30695
|
+
return;
|
|
30696
|
+
}
|
|
30697
|
+
this.shape = shape;
|
|
30698
|
+
this.background = options.variant === "background";
|
|
30699
|
+
this.hover = null;
|
|
30700
|
+
this.root.className = this.background ? "sm-system-map sm-system-map--background" : "sm-system-map";
|
|
30701
|
+
this.draw(model, this.placed, options.ariaLabel);
|
|
30702
|
+
this.fitLabels();
|
|
30703
|
+
this.placeTip();
|
|
30704
|
+
}
|
|
30705
|
+
draw(model, l, ariaLabel) {
|
|
30706
|
+
const bg = this.background;
|
|
30707
|
+
this.highlights = /* @__PURE__ */ new Map();
|
|
30708
|
+
this.targets = /* @__PURE__ */ new Map();
|
|
30709
|
+
this.labels = [];
|
|
30710
|
+
const root = this.svg;
|
|
30711
|
+
root.replaceChildren();
|
|
30712
|
+
root.setAttribute("viewBox", bg && model.backgroundView || `0 0 ${720} ${240}`);
|
|
30713
|
+
root.setAttribute("preserveAspectRatio", bg ? model.backgroundFit === "fit" ? "xMidYMid meet" : model.backgroundView ? "xMinYMid slice" : "xMidYMid slice" : "xMidYMid meet");
|
|
30714
|
+
root.setAttribute("class", "sm-system-map__svg");
|
|
30715
|
+
root.setAttribute("role", "group");
|
|
30716
|
+
root.setAttribute("aria-label", ariaLabel);
|
|
30717
|
+
root.setAttribute("data-vaul-no-drag", "");
|
|
30718
|
+
const stop = (offset, opacity) => svg("stop", {
|
|
30719
|
+
offset,
|
|
30720
|
+
"stop-color": model.primary.color,
|
|
30721
|
+
"stop-opacity": opacity
|
|
30722
|
+
});
|
|
30723
|
+
root.append(svg("defs", {}, svg("radialGradient", {
|
|
30724
|
+
id: `${this.uid}-primary`,
|
|
30725
|
+
gradientUnits: "userSpaceOnUse",
|
|
30726
|
+
cx: l.primaryCx,
|
|
30727
|
+
cy: 120,
|
|
30728
|
+
r: l.primaryR
|
|
30729
|
+
}, stop("0%", 1), stop(Math.max(0, 1 - PRIMARY_RIM / l.primaryR), .85), stop("100%", .35)), svg("pattern", {
|
|
30730
|
+
id: `${this.uid}-band`,
|
|
30731
|
+
width: 9,
|
|
30732
|
+
height: 9,
|
|
30733
|
+
patternUnits: "userSpaceOnUse"
|
|
30734
|
+
}, svg("circle", {
|
|
30735
|
+
cx: 2,
|
|
30736
|
+
cy: 2,
|
|
30737
|
+
r: .9,
|
|
30738
|
+
fill: "currentColor",
|
|
30739
|
+
opacity: .55
|
|
30740
|
+
}), svg("circle", {
|
|
30741
|
+
cx: 6.5,
|
|
30742
|
+
cy: 6,
|
|
30743
|
+
r: .9,
|
|
30744
|
+
fill: "currentColor",
|
|
30745
|
+
opacity: .55
|
|
30746
|
+
}))));
|
|
30747
|
+
if (!bg) this.drawAxis(model, l);
|
|
30748
|
+
if (l.cloudPath) root.append(svg("path", {
|
|
30749
|
+
d: l.cloudPath,
|
|
30750
|
+
stroke: model.cloud?.color,
|
|
30751
|
+
"stroke-width": CLOUD_R * 2,
|
|
30752
|
+
"stroke-linecap": "round",
|
|
30753
|
+
fill: "none",
|
|
30754
|
+
opacity: .45,
|
|
30755
|
+
"pointer-events": "none"
|
|
30756
|
+
}));
|
|
30757
|
+
for (const band of l.bands) {
|
|
30758
|
+
const fill = svg("rect", {
|
|
30759
|
+
x: band.x,
|
|
30760
|
+
y: 26,
|
|
30761
|
+
width: band.width,
|
|
30762
|
+
height: 180,
|
|
30763
|
+
fill: `url(#${this.uid}-band)`,
|
|
30764
|
+
class: `sm-system-map__band sm-system-map__band--${band.tone}`
|
|
30765
|
+
});
|
|
30766
|
+
if (bg) {
|
|
30767
|
+
root.append(fill);
|
|
30768
|
+
continue;
|
|
30769
|
+
}
|
|
30770
|
+
const label = svg("text", {
|
|
30771
|
+
x: band.x + band.width / 2,
|
|
30772
|
+
y: 240 - 8,
|
|
30773
|
+
"text-anchor": "middle",
|
|
30774
|
+
"font-size": LABEL_SIZE,
|
|
30775
|
+
class: `sm-system-map__band-label sm-system-map__band-label--${band.tone}`
|
|
30776
|
+
});
|
|
30777
|
+
label.textContent = band.label;
|
|
30778
|
+
this.labels.push(label);
|
|
30779
|
+
root.append(this.target(band, band.name ?? band.label, {
|
|
30780
|
+
kind: "band",
|
|
30781
|
+
key: band.key
|
|
30782
|
+
}, fill, svg("rect", {
|
|
30783
|
+
x: band.hitX,
|
|
30784
|
+
y: 26,
|
|
30785
|
+
width: band.hitWidth,
|
|
30786
|
+
height: 180,
|
|
30787
|
+
fill: "transparent"
|
|
30788
|
+
}), label));
|
|
30789
|
+
}
|
|
30790
|
+
const disc = svg("circle", {
|
|
30791
|
+
cx: l.primaryCx,
|
|
30792
|
+
cy: 120,
|
|
30793
|
+
r: l.primaryR,
|
|
30794
|
+
fill: `url(#${this.uid}-primary)`
|
|
30795
|
+
});
|
|
30796
|
+
root.append(bg ? disc : this.target(model.primary.link, model.primary.name, { kind: "primary" }, disc));
|
|
30797
|
+
for (const b of l.bodies) {
|
|
30798
|
+
if (!b.rings) continue;
|
|
30799
|
+
const mid = b.r * (b.rings.inner + b.rings.outer) / 2;
|
|
30800
|
+
root.append(svg("ellipse", {
|
|
30801
|
+
cx: b.cx,
|
|
30802
|
+
cy: b.cy,
|
|
30803
|
+
rx: mid,
|
|
30804
|
+
ry: mid * RING_FORESHORTEN,
|
|
30805
|
+
transform: `rotate(${RING_TILT} ${b.cx} ${b.cy})`,
|
|
30806
|
+
fill: "none",
|
|
30807
|
+
stroke: "#d9c89f",
|
|
30808
|
+
"stroke-width": b.r * (b.rings.outer - b.rings.inner),
|
|
30809
|
+
opacity: .45
|
|
30810
|
+
}));
|
|
30811
|
+
}
|
|
30812
|
+
const zoneOf = new Map(l.moonZones.map((z) => [z.parent.id, z]));
|
|
30813
|
+
for (const b of l.bodies) root.append(this.body(b, zoneOf.get(b.id)));
|
|
30814
|
+
for (const z of l.moonZones) if (!z.parent.grouped) this.stack(z);
|
|
30815
|
+
}
|
|
30816
|
+
drawAxis(model, l) {
|
|
30817
|
+
for (const t of model.ticks) {
|
|
30818
|
+
const x = l.xOf(t * model.unitKm);
|
|
30819
|
+
const tick = svg("text", {
|
|
30820
|
+
x,
|
|
30821
|
+
y: 16,
|
|
30822
|
+
"text-anchor": "middle",
|
|
30823
|
+
"font-size": 16,
|
|
30824
|
+
fill: "currentColor",
|
|
30825
|
+
opacity: .85
|
|
30826
|
+
});
|
|
30827
|
+
tick.textContent = String(t);
|
|
30828
|
+
this.svg.append(svg("line", {
|
|
30829
|
+
x1: x,
|
|
30830
|
+
x2: x,
|
|
30831
|
+
y1: 20,
|
|
30832
|
+
y2: 240 - 28,
|
|
30833
|
+
stroke: "currentColor",
|
|
30834
|
+
"stroke-width": .5,
|
|
30835
|
+
opacity: .12
|
|
30836
|
+
}), tick);
|
|
30837
|
+
}
|
|
30838
|
+
const unit = svg("text", {
|
|
30839
|
+
x: 700,
|
|
30840
|
+
y: 16,
|
|
30841
|
+
"text-anchor": "end",
|
|
30842
|
+
"font-size": 14,
|
|
30843
|
+
fill: "currentColor",
|
|
30844
|
+
opacity: .65
|
|
30845
|
+
});
|
|
30846
|
+
unit.textContent = model.axisLabel;
|
|
30847
|
+
this.svg.append(unit);
|
|
30848
|
+
}
|
|
30849
|
+
body(b, zone) {
|
|
30850
|
+
const dot = b.reference ? svg("circle", {
|
|
30851
|
+
cx: b.cx,
|
|
30852
|
+
cy: b.cy,
|
|
30853
|
+
r: b.r + 1,
|
|
30854
|
+
fill: "none",
|
|
30855
|
+
stroke: b.color,
|
|
30856
|
+
"stroke-width": 1.2,
|
|
30857
|
+
class: "sm-system-map__reference"
|
|
30858
|
+
}) : svg("circle", {
|
|
30859
|
+
cx: b.cx,
|
|
30860
|
+
cy: b.cy,
|
|
30861
|
+
r: b.r,
|
|
30862
|
+
fill: b.color
|
|
30863
|
+
});
|
|
30864
|
+
if (this.background) {
|
|
30865
|
+
if (!zone || !b.grouped) return dot;
|
|
30866
|
+
return svg("g", {}, dot, ...this.moonDots(zone));
|
|
30867
|
+
}
|
|
30868
|
+
const hover = {
|
|
30869
|
+
kind: "body",
|
|
30870
|
+
id: b.id
|
|
30871
|
+
};
|
|
30872
|
+
const ring = this.highlight(hover, svg("circle", {
|
|
30873
|
+
cx: b.cx,
|
|
30874
|
+
cy: b.cy,
|
|
30875
|
+
r: b.r + 3.5,
|
|
30876
|
+
fill: "none",
|
|
30877
|
+
stroke: "currentColor",
|
|
30878
|
+
"stroke-width": 1,
|
|
30879
|
+
opacity: .8
|
|
30880
|
+
}));
|
|
30881
|
+
const hit = svg("circle", {
|
|
30882
|
+
cx: b.cx,
|
|
30883
|
+
cy: b.cy,
|
|
30884
|
+
r: b.r + 16,
|
|
30885
|
+
fill: "transparent"
|
|
30886
|
+
});
|
|
30887
|
+
if (!zone || !b.grouped) return this.target(b.link, b.name, hover, ring, dot, hit);
|
|
30888
|
+
return this.target(b.link, b.name, hover, this.highlight(hover, this.zoneWash(zone)), ring, dot, hit, ...this.moonDots(zone), this.zoneHit(zone));
|
|
30889
|
+
}
|
|
30890
|
+
stack(z) {
|
|
30891
|
+
if (this.background) {
|
|
30892
|
+
this.svg.append(...this.moonDots(z));
|
|
30893
|
+
return;
|
|
30894
|
+
}
|
|
30895
|
+
const p = z.parent;
|
|
30896
|
+
if (z.perMoon) {
|
|
30897
|
+
this.svg.append(...this.moonDots(z));
|
|
30898
|
+
for (const mn of z.moons) {
|
|
30899
|
+
const hover = {
|
|
30900
|
+
kind: "moon",
|
|
30901
|
+
parent: p.id,
|
|
30902
|
+
id: mn.id
|
|
30903
|
+
};
|
|
30904
|
+
this.svg.append(this.target(mn.link, mn.name, hover, this.highlight(hover, svg("circle", {
|
|
30905
|
+
cx: mn.cx,
|
|
30906
|
+
cy: mn.cy,
|
|
30907
|
+
r: mn.r + 2.5,
|
|
30908
|
+
fill: "none",
|
|
30909
|
+
stroke: "currentColor",
|
|
30910
|
+
"stroke-width": 1,
|
|
30911
|
+
opacity: .8
|
|
30912
|
+
})), svg("rect", {
|
|
30913
|
+
x: z.x,
|
|
30914
|
+
y: mn.hitY,
|
|
30915
|
+
width: z.width,
|
|
30916
|
+
height: mn.hitHeight,
|
|
30917
|
+
fill: "transparent"
|
|
30918
|
+
})));
|
|
30919
|
+
}
|
|
30920
|
+
return;
|
|
30921
|
+
}
|
|
30922
|
+
const hover = {
|
|
30923
|
+
kind: "stack",
|
|
30924
|
+
id: p.id
|
|
30925
|
+
};
|
|
30926
|
+
const first = z.moons[0];
|
|
30927
|
+
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)));
|
|
30928
|
+
}
|
|
30929
|
+
moonDots(z) {
|
|
30930
|
+
return z.moons.map((mn) => svg("circle", {
|
|
30931
|
+
cx: mn.cx,
|
|
30932
|
+
cy: mn.cy,
|
|
30933
|
+
r: mn.r,
|
|
30934
|
+
fill: mn.color
|
|
30935
|
+
}));
|
|
30936
|
+
}
|
|
30937
|
+
zoneWash(z) {
|
|
30938
|
+
return svg("rect", {
|
|
30939
|
+
x: z.x,
|
|
30940
|
+
y: z.y,
|
|
30941
|
+
width: z.width,
|
|
30942
|
+
height: z.height,
|
|
30943
|
+
rx: 4,
|
|
30944
|
+
fill: "currentColor",
|
|
30945
|
+
opacity: .1,
|
|
30946
|
+
"pointer-events": "none"
|
|
30947
|
+
});
|
|
30948
|
+
}
|
|
30949
|
+
zoneHit(z) {
|
|
30950
|
+
return svg("rect", {
|
|
30951
|
+
x: z.x,
|
|
30952
|
+
y: z.y,
|
|
30953
|
+
width: z.width,
|
|
30954
|
+
height: z.height,
|
|
30955
|
+
fill: "transparent"
|
|
30956
|
+
});
|
|
30957
|
+
}
|
|
30958
|
+
highlight(hover, el) {
|
|
30959
|
+
const key = JSON.stringify(hover);
|
|
30960
|
+
const list = this.highlights.get(key);
|
|
30961
|
+
if (list) list.push(el);
|
|
30962
|
+
else this.highlights.set(key, [el]);
|
|
30963
|
+
el.style.display = "none";
|
|
30964
|
+
return el;
|
|
30965
|
+
}
|
|
30966
|
+
target(link, label, hover, ...children) {
|
|
30967
|
+
const linked = !!(link?.href || link?.onclick);
|
|
30968
|
+
const el = link?.href ? svg("a", {
|
|
30969
|
+
href: link.href,
|
|
30970
|
+
"aria-label": label
|
|
30971
|
+
}) : linked ? svg("g", {
|
|
30972
|
+
role: "button",
|
|
30973
|
+
tabindex: 0,
|
|
30974
|
+
"aria-label": label,
|
|
30975
|
+
class: "sm-system-map__button"
|
|
30976
|
+
}) : svg("g");
|
|
30977
|
+
el.append(...children);
|
|
30978
|
+
if (!linked) return el;
|
|
30979
|
+
const key = JSON.stringify(hover);
|
|
30980
|
+
this.targets.set(key, el);
|
|
30981
|
+
el.addEventListener("focus", () => this.setHover(hover));
|
|
30982
|
+
el.addEventListener("blur", () => {
|
|
30983
|
+
if (sameHover(this.hover, hover)) this.setHover(null);
|
|
30984
|
+
});
|
|
30985
|
+
el.addEventListener("click", (e) => this.links.get(key)?.onclick?.(e));
|
|
30986
|
+
if (!link?.href) el.addEventListener("keydown", (e) => {
|
|
30987
|
+
const key = e.key;
|
|
30988
|
+
if (key !== "Enter" && key !== " ") return;
|
|
30989
|
+
e.preventDefault();
|
|
30990
|
+
el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
30991
|
+
});
|
|
30992
|
+
return el;
|
|
30993
|
+
}
|
|
30994
|
+
setHover(hover) {
|
|
30995
|
+
if (sameHover(this.hover, hover)) return;
|
|
30996
|
+
for (const el of this.highlights.get(JSON.stringify(this.hover)) ?? []) el.style.display = "none";
|
|
30997
|
+
this.targets.get(JSON.stringify(this.hover))?.removeAttribute("data-near");
|
|
30998
|
+
this.hover = hover;
|
|
30999
|
+
for (const el of this.highlights.get(JSON.stringify(hover)) ?? []) el.style.display = "";
|
|
31000
|
+
const near = this.targets.get(JSON.stringify(hover));
|
|
31001
|
+
near?.setAttribute("data-near", "");
|
|
31002
|
+
this.svg.classList.toggle("sm-system-map__svg--near", !!near);
|
|
31003
|
+
this.placeTip();
|
|
31004
|
+
}
|
|
31005
|
+
hitAt(clientX, clientY) {
|
|
31006
|
+
if (!this.placed || this.background) return null;
|
|
31007
|
+
const rect = this.svg.getBoundingClientRect();
|
|
31008
|
+
if (!rect.width || !rect.height) return null;
|
|
31009
|
+
const vx = (clientX - rect.left) / rect.width * 720;
|
|
31010
|
+
const vy = (clientY - rect.top) / rect.height * 240;
|
|
31011
|
+
return hitTest(this.placed, vx, vy);
|
|
31012
|
+
}
|
|
31013
|
+
fitLabels() {
|
|
31014
|
+
for (const label of this.labels) {
|
|
31015
|
+
let width = 0;
|
|
31016
|
+
label.setAttribute("font-size", String(LABEL_SIZE));
|
|
31017
|
+
try {
|
|
31018
|
+
width = label.getComputedTextLength();
|
|
31019
|
+
} catch {}
|
|
31020
|
+
if (!width) continue;
|
|
31021
|
+
if (width > LABEL_MAX_W) {
|
|
31022
|
+
const size = Math.max(LABEL_MIN_SIZE, LABEL_SIZE * LABEL_MAX_W / width);
|
|
31023
|
+
label.setAttribute("font-size", String(size));
|
|
31024
|
+
width *= size / LABEL_SIZE;
|
|
31025
|
+
}
|
|
31026
|
+
const centre = Number(label.dataset.centre ?? label.getAttribute("x"));
|
|
31027
|
+
label.dataset.centre = String(centre);
|
|
31028
|
+
const half = width / 2;
|
|
31029
|
+
const x = Math.min(Math.max(centre, half + LABEL_MARGIN), Math.max(half, 720 - half - LABEL_MARGIN));
|
|
31030
|
+
label.setAttribute("x", String(x));
|
|
31031
|
+
}
|
|
31032
|
+
}
|
|
31033
|
+
placeTip() {
|
|
31034
|
+
const tip = this.model && this.placed && !this.background ? tipFor(this.model, this.placed, this.hover) : null;
|
|
31035
|
+
this.tip.hidden = !tip;
|
|
31036
|
+
if (!tip) return;
|
|
31037
|
+
this.tipTitle.textContent = tip.title;
|
|
31038
|
+
this.tipSub.textContent = tip.sub ? ` · ${tip.sub}` : "";
|
|
31039
|
+
const containerW = this.root.clientWidth;
|
|
31040
|
+
const scale = containerW / 720;
|
|
31041
|
+
this.tip.style.maxWidth = `${Math.max(0, containerW - 2 * TIP_MARGIN)}px`;
|
|
31042
|
+
const half = this.tip.offsetWidth / 2;
|
|
31043
|
+
const left = Math.min(Math.max(tip.cx * scale, half + TIP_MARGIN), Math.max(half, containerW - half - TIP_MARGIN));
|
|
31044
|
+
this.tip.style.left = `${left}px`;
|
|
31045
|
+
this.tip.style.top = `${Math.max(4, tip.cy * scale - TIP_H)}px`;
|
|
31046
|
+
}
|
|
31047
|
+
};
|
|
31048
|
+
|
|
31049
|
+
//#endregion
|
|
31050
|
+
//#region src/lib/systemmap/planetary.ts
|
|
31051
|
+
function systemFromMapEntry(entry, name) {
|
|
31052
|
+
return {
|
|
31053
|
+
planetId: entry.primary.id,
|
|
31054
|
+
planetName: name,
|
|
31055
|
+
planetRadiusKm: entry.primary.radius_km,
|
|
31056
|
+
planetColor: BODY_COLORS[entry.primary.id] ?? "#cccccc",
|
|
31057
|
+
moons: entry.moons.map((mn) => ({
|
|
31058
|
+
id: mn.id,
|
|
31059
|
+
name: mn.id,
|
|
31060
|
+
aRp: mn.a_rp,
|
|
31061
|
+
tiltDeg: mn.tilt_deg,
|
|
31062
|
+
radiusKm: mn.radius_km,
|
|
31063
|
+
color: BODY_COLORS[mn.id] ?? mn.color ?? "#cccccc"
|
|
31064
|
+
})),
|
|
31065
|
+
rings: entry.rings ? {
|
|
31066
|
+
innerRp: entry.rings.inner_rp,
|
|
31067
|
+
outerRp: entry.rings.outer_rp
|
|
31068
|
+
} : null,
|
|
31069
|
+
moonCount: entry.moon_count
|
|
31070
|
+
};
|
|
31071
|
+
}
|
|
31072
|
+
var TOP_MOON_R = 14;
|
|
31073
|
+
var AT_SURFACE = 1.001;
|
|
31074
|
+
var PX_PER_DEG = .62;
|
|
31075
|
+
var TICK_LADDER = [
|
|
31076
|
+
1,
|
|
31077
|
+
2,
|
|
31078
|
+
3,
|
|
31079
|
+
5,
|
|
31080
|
+
10,
|
|
31081
|
+
20,
|
|
31082
|
+
30,
|
|
31083
|
+
50,
|
|
31084
|
+
100,
|
|
31085
|
+
200,
|
|
31086
|
+
300,
|
|
31087
|
+
500,
|
|
31088
|
+
1e3,
|
|
31089
|
+
2e3,
|
|
31090
|
+
5e3
|
|
31091
|
+
];
|
|
31092
|
+
var BG_VIEW = "0 96 320 144";
|
|
31093
|
+
var BG_VIEW_WIDE = "0 96 720 144";
|
|
31094
|
+
var EMPTY_DOMAIN = [1, 100];
|
|
31095
|
+
function domainOf(system, bands) {
|
|
31096
|
+
let lo = Math.min(...system.moons.map((mn) => mn.aRp));
|
|
31097
|
+
let hi = Math.max(...system.moons.map((mn) => mn.aRp));
|
|
31098
|
+
for (const b of bands) {
|
|
31099
|
+
lo = Math.min(lo, b.innerKm / system.planetRadiusKm);
|
|
31100
|
+
hi = Math.max(hi, b.outerKm / system.planetRadiusKm);
|
|
31101
|
+
}
|
|
31102
|
+
if (!Number.isFinite(lo) || !Number.isFinite(hi)) return EMPTY_DOMAIN;
|
|
31103
|
+
if (hi <= lo) {
|
|
31104
|
+
lo /= 2;
|
|
31105
|
+
hi *= 2;
|
|
31106
|
+
}
|
|
31107
|
+
lo = lo <= AT_SURFACE ? 1 : lo;
|
|
31108
|
+
return [lo > 1 ? 10 ** (Math.log10(lo) - .12) : 1, 10 ** (Math.log10(hi) + .12)];
|
|
31109
|
+
}
|
|
31110
|
+
function planetarySystemModel(system, parts) {
|
|
31111
|
+
const rKm = system.planetRadiusKm;
|
|
31112
|
+
const domain = domainOf(system, parts.bands);
|
|
31113
|
+
return {
|
|
31114
|
+
primary: {
|
|
31115
|
+
id: system.planetId,
|
|
31116
|
+
name: system.planetName,
|
|
31117
|
+
radiusKm: rKm,
|
|
31118
|
+
color: system.planetColor,
|
|
31119
|
+
link: parts.primaryLink
|
|
31120
|
+
},
|
|
31121
|
+
bodies: system.moons.map((mn) => ({
|
|
31122
|
+
id: mn.id,
|
|
31123
|
+
name: mn.name,
|
|
31124
|
+
aKm: mn.aRp * rKm,
|
|
31125
|
+
tiltDeg: mn.tiltDeg,
|
|
31126
|
+
radiusKm: mn.radiusKm,
|
|
31127
|
+
color: mn.color,
|
|
31128
|
+
link: parts.moonLink?.(mn)
|
|
31129
|
+
})),
|
|
31130
|
+
bands: parts.bands,
|
|
31131
|
+
cloud: parts.cloud,
|
|
31132
|
+
unitKm: rKm,
|
|
31133
|
+
domain,
|
|
31134
|
+
ticks: TICK_LADDER.filter((t) => t >= domain[0] && t <= domain[1]),
|
|
31135
|
+
axisLabel: parts.axisLabel,
|
|
31136
|
+
pxPerKm: TOP_MOON_R / Math.max(...system.moons.map((mn) => mn.radiusKm).filter((r) => r > 0), 1),
|
|
31137
|
+
pxPerDeg: PX_PER_DEG,
|
|
31138
|
+
text: parts.text,
|
|
31139
|
+
backgroundView: parts.wide ? BG_VIEW_WIDE : BG_VIEW,
|
|
31140
|
+
backgroundFit: parts.wide ? "fit" : "slice"
|
|
31141
|
+
};
|
|
31142
|
+
}
|
|
31143
|
+
|
|
31144
|
+
//#endregion
|
|
31145
|
+
//#region src/lib/systemmap/solar.ts
|
|
31146
|
+
var SOLAR_PX_PER_KM = 224e-6;
|
|
31147
|
+
var SOLAR_PX_PER_DEG = 1.8;
|
|
31148
|
+
var DOMAIN = [.3, 50];
|
|
31149
|
+
var TICKS = [
|
|
31150
|
+
.3,
|
|
31151
|
+
1,
|
|
31152
|
+
3,
|
|
31153
|
+
10,
|
|
31154
|
+
30
|
|
31155
|
+
];
|
|
31156
|
+
var SUN_COLOR = "#ffdd44";
|
|
31157
|
+
function displayName(name) {
|
|
31158
|
+
const stripped = name.replace(/^\d+\s+/, "");
|
|
31159
|
+
if (stripped === stripped.toUpperCase() && /[A-Z]/.test(stripped)) return stripped.charAt(0) + stripped.slice(1).toLowerCase();
|
|
31160
|
+
return stripped;
|
|
31161
|
+
}
|
|
31162
|
+
function solarColor(o) {
|
|
31163
|
+
return o.color || BODY_COLORS[o.id] || "#cccccc";
|
|
31164
|
+
}
|
|
31165
|
+
function fileStacks(file, name) {
|
|
31166
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
31167
|
+
const planets = new Map(file.objects.map((o) => [o.id, o]));
|
|
31168
|
+
for (const o of file.objects) {
|
|
31169
|
+
if (o.kind !== "moon" || !o.parent) continue;
|
|
31170
|
+
const stack = stacks.get(o.parent) ?? {
|
|
31171
|
+
satellites: [],
|
|
31172
|
+
count: planets.get(o.parent)?.moon_count,
|
|
31173
|
+
tab: false
|
|
31174
|
+
};
|
|
31175
|
+
stack.satellites.push({
|
|
31176
|
+
id: o.id,
|
|
31177
|
+
name: name(o),
|
|
31178
|
+
radiusKm: o.diameter_km / 2,
|
|
31179
|
+
color: solarColor(o)
|
|
31180
|
+
});
|
|
31181
|
+
stack.tab ||= !!o.link_parent;
|
|
31182
|
+
stacks.set(o.parent, stack);
|
|
31183
|
+
}
|
|
31184
|
+
return stacks;
|
|
31185
|
+
}
|
|
31186
|
+
function solarSystemModel(file, parts) {
|
|
31187
|
+
const sun = file.objects.find((o) => o.kind === "star");
|
|
31188
|
+
if (!sun) return null;
|
|
31189
|
+
const listed = fileStacks(file, parts.name);
|
|
31190
|
+
const stackOf = parts.stack ?? ((o) => listed.get(o.id));
|
|
31191
|
+
const bodies = file.objects.filter((o) => o.kind !== "moon" && o.kind !== "star" && (parts.keep?.(o) ?? true)).map((o) => {
|
|
31192
|
+
const stack = stackOf(o);
|
|
31193
|
+
return {
|
|
31194
|
+
id: o.id,
|
|
31195
|
+
name: parts.name(o),
|
|
31196
|
+
aKm: o.a * AU_KM,
|
|
31197
|
+
tiltDeg: o.i,
|
|
31198
|
+
radiusKm: o.diameter_km / 2,
|
|
31199
|
+
color: solarColor(o),
|
|
31200
|
+
rings: o.rings,
|
|
31201
|
+
satellites: stack?.satellites,
|
|
31202
|
+
satelliteCount: stack?.count,
|
|
31203
|
+
satellitesTab: stack?.tab ?? false,
|
|
31204
|
+
...parts.links(o, stack)
|
|
31205
|
+
};
|
|
31206
|
+
});
|
|
31207
|
+
return {
|
|
31208
|
+
primary: {
|
|
31209
|
+
id: sun.id,
|
|
31210
|
+
name: parts.name(sun),
|
|
31211
|
+
radiusKm: sun.diameter_km / 2,
|
|
31212
|
+
color: SUN_COLOR,
|
|
31213
|
+
link: parts.primaryLink
|
|
31214
|
+
},
|
|
31215
|
+
bodies,
|
|
31216
|
+
bands: file.belts.map((b) => ({
|
|
31217
|
+
key: b.slug,
|
|
31218
|
+
innerKm: b.inner_au * AU_KM,
|
|
31219
|
+
outerKm: b.outer_au * AU_KM,
|
|
31220
|
+
tone: b.kind === "kuiper_belt" ? "sky" : "muted",
|
|
31221
|
+
...parts.band(b)
|
|
31222
|
+
})),
|
|
31223
|
+
unitKm: AU_KM,
|
|
31224
|
+
domain: DOMAIN,
|
|
31225
|
+
ticks: TICKS,
|
|
31226
|
+
axisLabel: parts.text.axisAu,
|
|
31227
|
+
pxPerKm: SOLAR_PX_PER_KM,
|
|
31228
|
+
pxPerDeg: SOLAR_PX_PER_DEG,
|
|
31229
|
+
text: parts.text
|
|
31230
|
+
};
|
|
31231
|
+
}
|
|
31232
|
+
|
|
31233
|
+
//#endregion
|
|
31234
|
+
//#region src/lib/systemmap/views.ts
|
|
31235
|
+
function extraFromPlace(place) {
|
|
31236
|
+
return {
|
|
31237
|
+
id: place.id,
|
|
31238
|
+
name: place.name ?? place.id,
|
|
31239
|
+
moon: place.moon ?? false,
|
|
31240
|
+
aAu: place.aAu ?? 0,
|
|
31241
|
+
tiltDeg: place.tiltDeg ?? 0,
|
|
31242
|
+
radiusKm: place.radiusKm ?? 0,
|
|
31243
|
+
color: place.color ?? BODY_COLORS[place.id] ?? "#cccccc",
|
|
31244
|
+
parent: place.parent
|
|
31245
|
+
};
|
|
31246
|
+
}
|
|
31247
|
+
var MAX_STACK = 4;
|
|
31248
|
+
var ZONE_TOP_R = 10;
|
|
31249
|
+
var ZONE_DOMAIN = {
|
|
31250
|
+
inner: [.6, 5.5],
|
|
31251
|
+
outer: [4.5, 55]
|
|
31252
|
+
};
|
|
31253
|
+
var ZONE_TICKS = [
|
|
31254
|
+
.1,
|
|
31255
|
+
.2,
|
|
31256
|
+
.3,
|
|
31257
|
+
.5,
|
|
31258
|
+
1,
|
|
31259
|
+
2,
|
|
31260
|
+
3,
|
|
31261
|
+
5,
|
|
31262
|
+
10,
|
|
31263
|
+
20,
|
|
31264
|
+
30,
|
|
31265
|
+
50,
|
|
31266
|
+
100,
|
|
31267
|
+
200,
|
|
31268
|
+
500,
|
|
31269
|
+
1e3
|
|
31270
|
+
];
|
|
31271
|
+
var LAST_AU_TICK_AT = .9;
|
|
31272
|
+
var LAST_RADII_TICK_AT = .76;
|
|
31273
|
+
function tickAt(tick, domain) {
|
|
31274
|
+
return Math.log(tick / domain[0]) / Math.log(domain[1] / domain[0]);
|
|
31275
|
+
}
|
|
31276
|
+
var JUPITER_ID = "naif-599";
|
|
31277
|
+
function unplacedIds(solar, systems, bodies, placed = []) {
|
|
31278
|
+
if (!bodies) return [];
|
|
31279
|
+
const known = new Set([...solar.objects.map((o) => o.id), ...placed]);
|
|
31280
|
+
for (const entry of Object.values(systems)) {
|
|
31281
|
+
known.add(entry.primary.id);
|
|
31282
|
+
for (const moon of entry.moons) known.add(moon.id);
|
|
31283
|
+
}
|
|
31284
|
+
return [...bodies].filter((id) => !known.has(id));
|
|
31285
|
+
}
|
|
31286
|
+
function extraFromBundle(global) {
|
|
31287
|
+
const moon = global.type === "moon";
|
|
31288
|
+
const parent = global.orbit?.parent_id;
|
|
31289
|
+
const a = parent === void 0 || parent === "naif-10" || parent === "naif-0" ? global.orbit?.a ?? global.orbit?.q : void 0;
|
|
31290
|
+
if (a === void 0 && !moon) return null;
|
|
31291
|
+
const radii = global.radii;
|
|
31292
|
+
return {
|
|
31293
|
+
id: global.id,
|
|
31294
|
+
name: displayName(global.name ?? global.id),
|
|
31295
|
+
moon,
|
|
31296
|
+
aAu: a ?? 0,
|
|
31297
|
+
tiltDeg: global.orbit?.i ?? 0,
|
|
31298
|
+
radiusKm: radii ? (radii.a + radii.b + radii.c) / 3 : (global.sbdb?.diameter ?? 0) / 2,
|
|
31299
|
+
color: BODY_COLORS[global.id] ?? global.sbdb?.color ?? global.color ?? "#cccccc",
|
|
31300
|
+
parent: global.orbit?.parent_id
|
|
31301
|
+
};
|
|
31302
|
+
}
|
|
31303
|
+
var ZONE_CUT = .92;
|
|
31304
|
+
function zoneOf(aAu, jupiterAu) {
|
|
31305
|
+
return aAu < jupiterAu * ZONE_CUT ? "inner" : "outer";
|
|
31306
|
+
}
|
|
31307
|
+
function systemEntry(systems, primaryId) {
|
|
31308
|
+
return Object.values(systems).find((entry) => entry.primary.id === primaryId);
|
|
31309
|
+
}
|
|
31310
|
+
function targetKey(target) {
|
|
31311
|
+
return target.kind === "zone" ? `zone:${target.zone}` : `${target.kind}:${target.id}`;
|
|
31312
|
+
}
|
|
31313
|
+
var Scope = class {
|
|
31314
|
+
targets = /* @__PURE__ */ new Map();
|
|
31315
|
+
constructor(data, options, pick) {
|
|
31316
|
+
this.data = data;
|
|
31317
|
+
this.options = options;
|
|
31318
|
+
this.pick = pick;
|
|
31319
|
+
}
|
|
31320
|
+
has(id) {
|
|
31321
|
+
return !this.options.bodies || this.options.bodies.has(id);
|
|
31322
|
+
}
|
|
31323
|
+
name(id, exported) {
|
|
31324
|
+
return this.options.names[id] ?? (exported ? displayName(exported) : id);
|
|
31325
|
+
}
|
|
31326
|
+
named(id, name) {
|
|
31327
|
+
return this.options.names[id] ?? name;
|
|
31328
|
+
}
|
|
31329
|
+
link(target) {
|
|
31330
|
+
this.targets.set(targetKey(target), target);
|
|
31331
|
+
return { onclick: () => this.pick(target) };
|
|
31332
|
+
}
|
|
31333
|
+
body(id, name) {
|
|
31334
|
+
return this.has(id) ? this.link({
|
|
31335
|
+
kind: "body",
|
|
31336
|
+
id,
|
|
31337
|
+
name
|
|
31338
|
+
}) : void 0;
|
|
31339
|
+
}
|
|
31340
|
+
stack(primaryId, listed) {
|
|
31341
|
+
if (listed && !this.options.bodies) return listed;
|
|
31342
|
+
const moons = (systemEntry(this.data.systems, primaryId)?.moons ?? []).filter((moon) => this.has(moon.id)).map((moon) => ({
|
|
31343
|
+
id: moon.id,
|
|
31344
|
+
name: this.name(moon.id),
|
|
31345
|
+
radiusKm: moon.radius_km,
|
|
31346
|
+
color: BODY_COLORS[moon.id] ?? moon.color ?? "#cccccc"
|
|
31347
|
+
}));
|
|
31348
|
+
if (!moons.length) return void 0;
|
|
31349
|
+
moons.sort((a, b) => b.radiusKm - a.radiusKm);
|
|
31350
|
+
return {
|
|
31351
|
+
satellites: moons.slice(0, MAX_STACK),
|
|
31352
|
+
count: moons.length,
|
|
31353
|
+
tab: true
|
|
31354
|
+
};
|
|
31355
|
+
}
|
|
31356
|
+
links(id, name, stack) {
|
|
31357
|
+
if (!stack) return { link: this.body(id, name) };
|
|
31358
|
+
const system = () => this.link({
|
|
31359
|
+
kind: "system",
|
|
31360
|
+
id,
|
|
31361
|
+
name
|
|
31362
|
+
});
|
|
31363
|
+
if (this.options.grouping === "systems") return {
|
|
31364
|
+
link: system(),
|
|
31365
|
+
grouped: true
|
|
31366
|
+
};
|
|
31367
|
+
const largest = stack.satellites[0];
|
|
31368
|
+
return {
|
|
31369
|
+
link: this.body(id, name),
|
|
31370
|
+
satellitesLink: stack.tab ? system() : this.body(largest.id, largest.name)
|
|
31371
|
+
};
|
|
31372
|
+
}
|
|
31373
|
+
built(model, label) {
|
|
31374
|
+
return {
|
|
31375
|
+
model,
|
|
31376
|
+
targets: [...this.targets.values()],
|
|
31377
|
+
label
|
|
31378
|
+
};
|
|
31379
|
+
}
|
|
31380
|
+
};
|
|
31381
|
+
function buildSolarView(data, options, text, pick) {
|
|
31382
|
+
const scope = new Scope(data, options, pick);
|
|
31383
|
+
const name = (o) => scope.name(o.id, o.name);
|
|
31384
|
+
const listed = fileStacks(data.solar, name);
|
|
31385
|
+
const stacks = new Map(data.solar.objects.map((o) => [o.id, scope.stack(o.id, listed.get(o.id))]));
|
|
31386
|
+
const sun = data.solar.objects.find((o) => o.kind === "star");
|
|
31387
|
+
const zoneBand = (zone) => {
|
|
31388
|
+
const label = zone === "inner" ? text.zoneInner : text.zoneOuter;
|
|
31389
|
+
return {
|
|
31390
|
+
label,
|
|
31391
|
+
sub: "",
|
|
31392
|
+
...scope.link({
|
|
31393
|
+
kind: "zone",
|
|
31394
|
+
zone,
|
|
31395
|
+
name: label
|
|
31396
|
+
})
|
|
31397
|
+
};
|
|
31398
|
+
};
|
|
31399
|
+
const model = solarSystemModel(data.solar, {
|
|
31400
|
+
text,
|
|
31401
|
+
name,
|
|
31402
|
+
primaryLink: sun && scope.body(sun.id, name(sun)),
|
|
31403
|
+
keep: (o) => scope.has(o.id) || !!stacks.get(o.id),
|
|
31404
|
+
stack: (o) => stacks.get(o.id),
|
|
31405
|
+
links: (o, stack) => scope.links(o.id, name(o), stack),
|
|
31406
|
+
band: (belt) => options.zones === "belts" ? { label: belt.label } : zoneBand(belt.kind === "kuiper_belt" ? "outer" : "inner")
|
|
31407
|
+
});
|
|
31408
|
+
if (!model) throw new Error("spacemap: the Solar System map names no Sun");
|
|
31409
|
+
return scope.built(model, text.solarSystem);
|
|
31410
|
+
}
|
|
31411
|
+
function buildSystemView(data, primaryId, exported, options, text, pick) {
|
|
31412
|
+
const entry = systemEntry(data.systems, primaryId);
|
|
31413
|
+
if (!entry) throw new Error(`spacemap: no planetary system around ${primaryId}`);
|
|
31414
|
+
const scope = new Scope(data, options, pick);
|
|
31415
|
+
const system = systemFromMapEntry(entry, scope.name(primaryId, exported[primaryId]));
|
|
31416
|
+
system.moons = system.moons.filter((moon) => scope.has(moon.id)).map((moon) => ({
|
|
31417
|
+
...moon,
|
|
31418
|
+
name: scope.name(moon.id, exported[moon.id])
|
|
31419
|
+
}));
|
|
31420
|
+
const rKm = system.planetRadiusKm;
|
|
31421
|
+
const bands = system.rings ? [{
|
|
31422
|
+
key: "rings",
|
|
31423
|
+
label: text.rings,
|
|
31424
|
+
innerKm: system.rings.innerRp * rKm,
|
|
31425
|
+
outerKm: system.rings.outerRp * rKm,
|
|
31426
|
+
tone: "amber"
|
|
31427
|
+
}] : [];
|
|
31428
|
+
const model = planetarySystemModel(system, {
|
|
31429
|
+
text,
|
|
31430
|
+
axisLabel: text.axisRadii,
|
|
31431
|
+
bands,
|
|
31432
|
+
primaryLink: scope.body(primaryId, system.planetName),
|
|
31433
|
+
moonLink: (moon) => scope.body(moon.id, moon.name)
|
|
31434
|
+
});
|
|
31435
|
+
model.ticks = model.ticks.filter((t) => tickAt(t, model.domain) < LAST_RADII_TICK_AT);
|
|
31436
|
+
return scope.built(model, system.planetName);
|
|
31437
|
+
}
|
|
31438
|
+
function buildZoneView(data, zone, options, text, pick) {
|
|
31439
|
+
const scope = new Scope(data, options, pick);
|
|
31440
|
+
const { solar, extras } = data;
|
|
31441
|
+
const sun = solar.objects.find((o) => o.kind === "star");
|
|
31442
|
+
const jupiterAu = solar.objects.find((o) => o.id === JUPITER_ID)?.a;
|
|
31443
|
+
if (!sun || jupiterAu === void 0) throw new Error("spacemap: the Solar System map names no Sun or no Jupiter");
|
|
31444
|
+
const small = solar.objects.filter((o) => o.kind === "dwarf" || o.kind === "asteroid").map((o) => ({
|
|
31445
|
+
id: o.id,
|
|
31446
|
+
name: scope.name(o.id, o.name),
|
|
31447
|
+
aAu: o.a,
|
|
31448
|
+
tiltDeg: o.i,
|
|
31449
|
+
radiusKm: o.diameter_km / 2,
|
|
31450
|
+
color: solarColor(o)
|
|
31451
|
+
}));
|
|
31452
|
+
for (const extra of extras) {
|
|
31453
|
+
if (extra.moon || small.some((s) => s.id === extra.id)) continue;
|
|
31454
|
+
if (!(extra.aAu > 0)) continue;
|
|
31455
|
+
small.push({
|
|
31456
|
+
...extra,
|
|
31457
|
+
name: scope.named(extra.id, extra.name)
|
|
31458
|
+
});
|
|
31459
|
+
}
|
|
31460
|
+
const bodies = [];
|
|
31461
|
+
for (const s of small) {
|
|
31462
|
+
if (zoneOf(s.aAu, jupiterAu) !== zone) continue;
|
|
31463
|
+
const base = {
|
|
31464
|
+
...s,
|
|
31465
|
+
aKm: s.aAu * AU_KM
|
|
31466
|
+
};
|
|
31467
|
+
const stack = scope.stack(s.id);
|
|
31468
|
+
if (stack) {
|
|
31469
|
+
bodies.push({
|
|
31470
|
+
...base,
|
|
31471
|
+
satellites: stack.satellites,
|
|
31472
|
+
satelliteCount: stack.count,
|
|
31473
|
+
satellitesTab: true,
|
|
31474
|
+
...scope.links(s.id, s.name, stack)
|
|
31475
|
+
});
|
|
31476
|
+
continue;
|
|
31477
|
+
}
|
|
31478
|
+
const moons = extras.filter((extra) => extra.moon && extra.parent === s.id && scope.has(extra.id)).map((extra) => {
|
|
31479
|
+
const name = scope.named(extra.id, extra.name);
|
|
31480
|
+
return {
|
|
31481
|
+
...extra,
|
|
31482
|
+
name,
|
|
31483
|
+
link: scope.body(extra.id, name)
|
|
31484
|
+
};
|
|
31485
|
+
});
|
|
31486
|
+
if (!scope.has(s.id) && !moons.length) continue;
|
|
31487
|
+
bodies.push({
|
|
31488
|
+
...base,
|
|
31489
|
+
link: scope.body(s.id, s.name),
|
|
31490
|
+
satellites: moons.length ? moons : void 0
|
|
31491
|
+
});
|
|
31492
|
+
}
|
|
31493
|
+
const [lo, hi] = ZONE_DOMAIN[zone];
|
|
31494
|
+
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))];
|
|
31495
|
+
const within = (au) => au >= domain[0] && au <= domain[1];
|
|
31496
|
+
const pxPerKm = ZONE_TOP_R / Math.max(...bodies.map((b) => b.radiusKm), 1);
|
|
31497
|
+
for (const o of solar.objects) {
|
|
31498
|
+
if (o.kind !== "planet" || !within(o.a)) continue;
|
|
31499
|
+
bodies.push({
|
|
31500
|
+
id: o.id,
|
|
31501
|
+
name: scope.name(o.id, o.name),
|
|
31502
|
+
aKm: o.a * AU_KM,
|
|
31503
|
+
tiltDeg: 0,
|
|
31504
|
+
radiusKm: 0,
|
|
31505
|
+
color: solarColor(o),
|
|
31506
|
+
reference: true
|
|
31507
|
+
});
|
|
31508
|
+
}
|
|
31509
|
+
const label = zone === "inner" ? text.zoneInner : text.zoneOuter;
|
|
31510
|
+
return scope.built({
|
|
31511
|
+
primary: {
|
|
31512
|
+
id: sun.id,
|
|
31513
|
+
name: scope.name(sun.id, sun.name),
|
|
31514
|
+
radiusKm: sun.diameter_km / 2 * SOLAR_PX_PER_KM / pxPerKm,
|
|
31515
|
+
color: SUN_COLOR
|
|
31516
|
+
},
|
|
31517
|
+
bodies,
|
|
31518
|
+
bands: solar.belts.filter((belt) => within(belt.inner_au) && within(belt.outer_au)).map((belt) => ({
|
|
31519
|
+
key: belt.slug,
|
|
31520
|
+
label: belt.label,
|
|
31521
|
+
innerKm: belt.inner_au * AU_KM,
|
|
31522
|
+
outerKm: belt.outer_au * AU_KM,
|
|
31523
|
+
tone: belt.kind === "kuiper_belt" ? "sky" : "muted"
|
|
31524
|
+
})),
|
|
31525
|
+
unitKm: AU_KM,
|
|
31526
|
+
domain,
|
|
31527
|
+
ticks: ZONE_TICKS.filter((t) => within(t) && tickAt(t, domain) < LAST_AU_TICK_AT),
|
|
31528
|
+
axisLabel: text.axisAu,
|
|
31529
|
+
pxPerKm,
|
|
31530
|
+
pxPerDeg: SOLAR_PX_PER_DEG,
|
|
31531
|
+
text
|
|
31532
|
+
}, label);
|
|
31533
|
+
}
|
|
31534
|
+
|
|
31535
|
+
//#endregion
|
|
31536
|
+
//#region src/lib/systemmap/system-map.ts
|
|
31537
|
+
var onceTarget = /* @__PURE__ */ new WeakMap();
|
|
31538
|
+
var AU_TO_KM_THRESHOLD = .01;
|
|
31539
|
+
function viewText() {
|
|
31540
|
+
const { messages, locale } = host();
|
|
31541
|
+
const number = new Intl.NumberFormat(locale(), { maximumSignificantDigits: 3 });
|
|
31542
|
+
const kilometres = new Intl.NumberFormat(locale(), {
|
|
31543
|
+
style: "unit",
|
|
31544
|
+
unit: "kilometer",
|
|
31545
|
+
maximumSignificantDigits: 3
|
|
31546
|
+
});
|
|
31547
|
+
const au = messages.unit_symbol_astronomical_unit();
|
|
31548
|
+
return {
|
|
31549
|
+
primary: messages.planetary_system_primary(),
|
|
31550
|
+
retrograde: messages.planetary_system_retrograde(),
|
|
31551
|
+
moons: (count) => messages.system_map_moons({ count }),
|
|
31552
|
+
distance: (km) => {
|
|
31553
|
+
const distance = km / AU_KM;
|
|
31554
|
+
return distance > 0 && distance < AU_TO_KM_THRESHOLD ? kilometres.format(km) : `${number.format(distance)} ${au}`;
|
|
31555
|
+
},
|
|
31556
|
+
axisAu: messages.system_map_axis_au(),
|
|
31557
|
+
solarSystem: messages.system_map_solar_system(),
|
|
31558
|
+
rings: messages.tab_rings(),
|
|
31559
|
+
axisRadii: messages.planetary_system_axis_unit(),
|
|
31560
|
+
zoneInner: messages.system_map_zone_inner(),
|
|
31561
|
+
zoneOuter: messages.system_map_zone_outer()
|
|
31562
|
+
};
|
|
31563
|
+
}
|
|
31564
|
+
var SystemMap = class {
|
|
31565
|
+
chart = new SystemMapChart();
|
|
31566
|
+
listeners = /* @__PURE__ */ new Map();
|
|
31567
|
+
view;
|
|
31568
|
+
bodies;
|
|
31569
|
+
names;
|
|
31570
|
+
grouping;
|
|
31571
|
+
zones;
|
|
31572
|
+
places;
|
|
31573
|
+
targets = [];
|
|
31574
|
+
extras = /* @__PURE__ */ new Map();
|
|
31575
|
+
queue = Promise.resolve();
|
|
31576
|
+
disposed = false;
|
|
31577
|
+
constructor(options = {}) {
|
|
31578
|
+
this.view = options.view ?? { kind: "solar-system" };
|
|
31579
|
+
this.bodies = options.bodies ? new Set(options.bodies) : null;
|
|
31580
|
+
this.names = options.names ?? {};
|
|
31581
|
+
this.grouping = options.grouping ?? "bodies";
|
|
31582
|
+
this.zones = options.zones ?? "belts";
|
|
31583
|
+
this.places = (options.places ?? []).map(extraFromPlace);
|
|
31584
|
+
}
|
|
31585
|
+
mount(container) {
|
|
31586
|
+
container.append(this.chart.root);
|
|
31587
|
+
}
|
|
31588
|
+
remove() {
|
|
31589
|
+
this.disposed = true;
|
|
31590
|
+
this.chart.destroy();
|
|
31591
|
+
}
|
|
31592
|
+
load() {
|
|
31593
|
+
return this.draw();
|
|
31594
|
+
}
|
|
31595
|
+
async setView(view) {
|
|
31596
|
+
await this.draw(view);
|
|
31597
|
+
}
|
|
31598
|
+
getView() {
|
|
31599
|
+
return this.view;
|
|
31600
|
+
}
|
|
31601
|
+
setBodies(ids) {
|
|
31602
|
+
const bodies = ids ? new Set(ids) : null;
|
|
31603
|
+
return this.draw(void 0, bodies).catch(() => {});
|
|
31604
|
+
}
|
|
31605
|
+
getTargets() {
|
|
31606
|
+
return [...this.targets];
|
|
31607
|
+
}
|
|
31608
|
+
draw(view, bodies) {
|
|
31609
|
+
const turn = this.queue.then(() => {
|
|
31610
|
+
if (bodies !== void 0) this.bodies = bodies;
|
|
31611
|
+
return this.drawNow(view ?? this.view);
|
|
31612
|
+
});
|
|
31613
|
+
this.queue = turn.catch(() => {});
|
|
31614
|
+
return turn;
|
|
31615
|
+
}
|
|
31616
|
+
async drawNow(view) {
|
|
31617
|
+
let built;
|
|
31618
|
+
try {
|
|
31619
|
+
built = await this.build(view);
|
|
31620
|
+
} catch (cause) {
|
|
31621
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
31622
|
+
if (!this.disposed) this.emit("error", error);
|
|
31623
|
+
throw error;
|
|
31624
|
+
}
|
|
31625
|
+
if (this.disposed) return;
|
|
31626
|
+
const changed = JSON.stringify(view) !== JSON.stringify(this.view);
|
|
31627
|
+
this.view = view;
|
|
31628
|
+
this.targets = built.targets;
|
|
31629
|
+
this.chart.update(built.model, { ariaLabel: built.label });
|
|
31630
|
+
if (changed) this.emit("viewchange", view);
|
|
31631
|
+
}
|
|
31632
|
+
async build(view) {
|
|
31633
|
+
const [solar, systems] = await Promise.all([fetchSolarSystemMap(), fetchPlanetarySystemsMap()]);
|
|
31634
|
+
const options = {
|
|
31635
|
+
bodies: this.bodies,
|
|
31636
|
+
names: this.names,
|
|
31637
|
+
grouping: this.grouping,
|
|
31638
|
+
zones: this.zones
|
|
31639
|
+
};
|
|
31640
|
+
const text = viewText();
|
|
31641
|
+
const pick = (target) => this.emit("select", target);
|
|
31642
|
+
const data = {
|
|
31643
|
+
solar,
|
|
31644
|
+
systems,
|
|
31645
|
+
extras: []
|
|
31646
|
+
};
|
|
31647
|
+
switch (view.kind) {
|
|
31648
|
+
case "solar-system": return buildSolarView(data, options, text, pick);
|
|
31649
|
+
case "system": return buildSystemView(data, view.id, await this.exportedNames(view.id, systems), options, text, pick);
|
|
31650
|
+
case "zone": {
|
|
31651
|
+
const ids = unplacedIds(solar, systems, this.bodies, this.places.map((place) => place.id));
|
|
31652
|
+
const read = (await Promise.all(ids.map((id) => this.extra(id)))).filter((extra) => extra !== null);
|
|
31653
|
+
const placed = [...this.places, ...read];
|
|
31654
|
+
const parents = unplacedIds(solar, systems, new Set(placed.flatMap((extra) => extra.moon && extra.parent ? [extra.parent] : [])), placed.map((extra) => extra.id));
|
|
31655
|
+
const hosts = await Promise.all(parents.map((id) => this.extra(id)));
|
|
31656
|
+
const extras = [...placed, ...hosts.filter((extra) => extra !== null)];
|
|
31657
|
+
return buildZoneView({
|
|
31658
|
+
...data,
|
|
31659
|
+
extras
|
|
31660
|
+
}, view.zone, options, text, pick);
|
|
31661
|
+
}
|
|
31662
|
+
}
|
|
31663
|
+
}
|
|
31664
|
+
extra(id) {
|
|
31665
|
+
let pending = this.extras.get(id);
|
|
31666
|
+
if (!pending) {
|
|
31667
|
+
pending = fetchObjectDetail(id, false).then((detail) => {
|
|
31668
|
+
if (!detail.global) throw new Error(`spacemap: no such body as ${id}`);
|
|
31669
|
+
return extraFromBundle(detail.global);
|
|
31670
|
+
}).catch((cause) => {
|
|
31671
|
+
this.extras.delete(id);
|
|
31672
|
+
this.emit("error", cause instanceof Error ? cause : new Error(String(cause)));
|
|
31673
|
+
return null;
|
|
31674
|
+
});
|
|
31675
|
+
this.extras.set(id, pending);
|
|
31676
|
+
}
|
|
31677
|
+
return pending;
|
|
31678
|
+
}
|
|
31679
|
+
async exportedNames(primaryId, systems) {
|
|
31680
|
+
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 {};
|
|
31681
|
+
try {
|
|
31682
|
+
const { global, localized } = await fetchObjectDetail(primaryId, true, host().locale());
|
|
31683
|
+
const names = {};
|
|
31684
|
+
for (const moon of global?.notable_moons ?? []) if (moon.id) names[moon.id] = moon.name;
|
|
31685
|
+
Object.assign(names, localized?.notable_moon_names);
|
|
31686
|
+
const name = localized?.name ?? global?.name;
|
|
31687
|
+
if (name) names[primaryId] = name;
|
|
31688
|
+
return names;
|
|
31689
|
+
} catch {
|
|
31690
|
+
return {};
|
|
31691
|
+
}
|
|
31692
|
+
}
|
|
31693
|
+
on(event, listener) {
|
|
31694
|
+
let set = this.listeners.get(event);
|
|
31695
|
+
if (!set) {
|
|
31696
|
+
set = /* @__PURE__ */ new Set();
|
|
31697
|
+
this.listeners.set(event, set);
|
|
31698
|
+
}
|
|
31699
|
+
set.add(listener);
|
|
31700
|
+
return () => set.delete(listener);
|
|
31701
|
+
}
|
|
31702
|
+
once(event, listener) {
|
|
31703
|
+
const wrapped = ((...args) => {
|
|
31704
|
+
off();
|
|
31705
|
+
listener(...args);
|
|
31706
|
+
});
|
|
31707
|
+
onceTarget.set(wrapped, listener);
|
|
31708
|
+
const off = this.on(event, wrapped);
|
|
31709
|
+
return off;
|
|
31710
|
+
}
|
|
31711
|
+
off(event, listener) {
|
|
31712
|
+
const set = this.listeners.get(event);
|
|
31713
|
+
if (!set) return;
|
|
31714
|
+
set.delete(listener);
|
|
31715
|
+
for (const held of set) if (onceTarget.get(held) === listener) set.delete(held);
|
|
31716
|
+
}
|
|
31717
|
+
emit(event, ...args) {
|
|
31718
|
+
const set = this.listeners.get(event);
|
|
31719
|
+
if (!set) return;
|
|
31720
|
+
for (const listener of [...set]) listener(...args);
|
|
31721
|
+
}
|
|
31722
|
+
};
|
|
31723
|
+
|
|
30077
31724
|
//#endregion
|
|
30078
31725
|
//#region src/lib/scene/state/attribution.ts
|
|
30079
31726
|
var DEFAULT_ORBIT_LABELS = Object.fromEntries(ORBIT_SOURCE_ORDER.map((s) => [s, ORBIT_SOURCES[s].label]));
|
|
@@ -30414,6 +32061,27 @@ async function createPanorama(options) {
|
|
|
30414
32061
|
await view.load();
|
|
30415
32062
|
return view;
|
|
30416
32063
|
}
|
|
32064
|
+
async function createSystemMap(options) {
|
|
32065
|
+
const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
|
|
32066
|
+
const element = resolveContainer(container);
|
|
32067
|
+
applyHost({
|
|
32068
|
+
dataUrl,
|
|
32069
|
+
imagesUrl,
|
|
32070
|
+
locale,
|
|
32071
|
+
messages,
|
|
32072
|
+
includeNonCommercial
|
|
32073
|
+
});
|
|
32074
|
+
const map = new SystemMap(rest);
|
|
32075
|
+
map.mount(element);
|
|
32076
|
+
for (const [event, listener] of Object.entries(events ?? {})) map.on(event, listener);
|
|
32077
|
+
try {
|
|
32078
|
+
await map.load();
|
|
32079
|
+
} catch (error) {
|
|
32080
|
+
map.remove();
|
|
32081
|
+
throw error;
|
|
32082
|
+
}
|
|
32083
|
+
return map;
|
|
32084
|
+
}
|
|
30417
32085
|
function resolveContainer(container) {
|
|
30418
32086
|
if (typeof container !== "string") return container;
|
|
30419
32087
|
const element = document.querySelector(container);
|
|
@@ -30431,5 +32099,5 @@ function applyHost(options) {
|
|
|
30431
32099
|
}
|
|
30432
32100
|
|
|
30433
32101
|
//#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
|
|
32102
|
+
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 };
|
|
32103
|
+
(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);})();
|