spacemap 0.1.11 → 0.1.13
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 +32 -9
- package/dist/spacemap.iife.js +167 -105
- package/dist/spacemap.js +167 -105
- package/index.d.ts +37 -15
- package/index.js +603 -274
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -3025,6 +3025,17 @@ var OrbitalSource = /* @__PURE__ */ function(OrbitalSource) {
|
|
|
3025
3025
|
OrbitalSource[OrbitalSource["UNKNOWN"] = 255] = "UNKNOWN";
|
|
3026
3026
|
return OrbitalSource;
|
|
3027
3027
|
}({});
|
|
3028
|
+
var PROBE_ARCHIVES = [
|
|
3029
|
+
void 0,
|
|
3030
|
+
"naif",
|
|
3031
|
+
"esa",
|
|
3032
|
+
"naif-pds3",
|
|
3033
|
+
"naif-pds4",
|
|
3034
|
+
"jaxa-darts",
|
|
3035
|
+
"horizons",
|
|
3036
|
+
"gcat-deepcat",
|
|
3037
|
+
"spacetrack"
|
|
3038
|
+
];
|
|
3028
3039
|
var IdType = /* @__PURE__ */ function(IdType) {
|
|
3029
3040
|
IdType[IdType["NAIF"] = 0] = "NAIF";
|
|
3030
3041
|
IdType[IdType["SPKID"] = 1] = "SPKID";
|
|
@@ -3174,40 +3185,24 @@ function isNaturalBody(type) {
|
|
|
3174
3185
|
//#endregion
|
|
3175
3186
|
//#region src/lib/scene/state/orbit-sources.ts
|
|
3176
3187
|
var ORBIT_SOURCES = {
|
|
3177
|
-
[OrbitalSource.HORIZONS]: {
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
},
|
|
3181
|
-
[OrbitalSource.SBDB]: {
|
|
3182
|
-
archive: "sbdb",
|
|
3183
|
-
label: "NASA"
|
|
3184
|
-
},
|
|
3185
|
-
[OrbitalSource.SPICE]: {
|
|
3186
|
-
archive: "naif",
|
|
3187
|
-
label: "NASA"
|
|
3188
|
-
},
|
|
3188
|
+
[OrbitalSource.HORIZONS]: { archive: "horizons" },
|
|
3189
|
+
[OrbitalSource.SBDB]: { archive: "sbdb" },
|
|
3190
|
+
[OrbitalSource.SPICE]: { archive: "naif" },
|
|
3189
3191
|
[OrbitalSource.SBDB_MOON]: {
|
|
3190
3192
|
archive: "sbdb",
|
|
3191
|
-
label: "NASA",
|
|
3192
3193
|
scoped: true
|
|
3193
3194
|
},
|
|
3194
3195
|
[OrbitalSource.ASTERSAT]: {
|
|
3195
3196
|
archive: "nsdb",
|
|
3196
|
-
label: "Natural Satellites Data Base",
|
|
3197
3197
|
scoped: true
|
|
3198
3198
|
},
|
|
3199
|
-
[OrbitalSource.SPICE_PROBE]: {
|
|
3200
|
-
archive: "naif",
|
|
3201
|
-
label: "NASA"
|
|
3202
|
-
},
|
|
3199
|
+
[OrbitalSource.SPICE_PROBE]: {},
|
|
3203
3200
|
[OrbitalSource.CELESTRAK]: {
|
|
3204
3201
|
archive: "celestrak",
|
|
3205
|
-
label: "CelesTrak",
|
|
3206
3202
|
scoped: true
|
|
3207
3203
|
},
|
|
3208
3204
|
[OrbitalSource.SPACETRACK]: {
|
|
3209
3205
|
archive: "spacetrack",
|
|
3210
|
-
label: "Space-Track.org",
|
|
3211
3206
|
scoped: true
|
|
3212
3207
|
}
|
|
3213
3208
|
};
|
|
@@ -3221,10 +3216,31 @@ var ORBIT_SOURCE_ORDER = [
|
|
|
3221
3216
|
OrbitalSource.CELESTRAK,
|
|
3222
3217
|
OrbitalSource.SPACETRACK
|
|
3223
3218
|
];
|
|
3219
|
+
var ORBIT_ARCHIVE_ORGANISATIONS = {
|
|
3220
|
+
horizons: "NASA",
|
|
3221
|
+
sbdb: "NASA",
|
|
3222
|
+
naif: "NASA",
|
|
3223
|
+
"naif-pds3": "NASA",
|
|
3224
|
+
"naif-pds4": "NASA",
|
|
3225
|
+
esa: "ESA",
|
|
3226
|
+
"jaxa-darts": "JAXA",
|
|
3227
|
+
"gcat-deepcat": "McDowell Deep Space Catalog",
|
|
3228
|
+
nsdb: "Natural Satellites Data Base",
|
|
3229
|
+
celestrak: "CelesTrak",
|
|
3230
|
+
spacetrack: "Space-Track.org"
|
|
3231
|
+
};
|
|
3224
3232
|
function orbitSourceInfo(source) {
|
|
3225
3233
|
return ORBIT_SOURCES[source];
|
|
3226
3234
|
}
|
|
3227
3235
|
|
|
3236
|
+
//#endregion
|
|
3237
|
+
//#region src/lib/credits/tile-credit.ts
|
|
3238
|
+
function tileCredit(map) {
|
|
3239
|
+
const tiles = map.tiles;
|
|
3240
|
+
if (!tiles || tilesBase() === "" || !textureAllowed(tiles.distribution)) return void 0;
|
|
3241
|
+
return tiles.source === map.source && tiles.attribution === map.attribution ? void 0 : tiles;
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3228
3244
|
//#endregion
|
|
3229
3245
|
//#region src/lib/scene/state/credits.svelte.ts
|
|
3230
3246
|
var CreditsStore = class {
|
|
@@ -3273,9 +3289,24 @@ var CreditsStore = class {
|
|
|
3273
3289
|
set orbitSourceParents(value) {
|
|
3274
3290
|
set(this.#orbitSourceParents, value, true);
|
|
3275
3291
|
}
|
|
3292
|
+
#probeArchives = state(proxy(/* @__PURE__ */ new Set()));
|
|
3293
|
+
get probeArchives() {
|
|
3294
|
+
return get(this.#probeArchives);
|
|
3295
|
+
}
|
|
3296
|
+
set probeArchives(value) {
|
|
3297
|
+
set(this.#probeArchives, value, true);
|
|
3298
|
+
}
|
|
3276
3299
|
registerImagery(layer, bodyId, systemId, meta) {
|
|
3277
|
-
|
|
3278
|
-
const
|
|
3300
|
+
this.addImagery(layer, bodyId, systemId, meta, layer === "rings");
|
|
3301
|
+
const tiles = layer === "surface" ? tileCredit(meta) : void 0;
|
|
3302
|
+
if (tiles) this.addImagery(layer, bodyId, systemId, tiles, true);
|
|
3303
|
+
}
|
|
3304
|
+
addImagery(layer, bodyId, systemId, meta, bySource) {
|
|
3305
|
+
const key = [
|
|
3306
|
+
layer,
|
|
3307
|
+
bodyId,
|
|
3308
|
+
...bySource ? [meta.source] : []
|
|
3309
|
+
].join("\0");
|
|
3279
3310
|
if (this.imagery.has(key)) return;
|
|
3280
3311
|
const { source, organisation, license, attribution, description } = meta;
|
|
3281
3312
|
this.imagery.set(key, {
|
|
@@ -3306,6 +3337,7 @@ var CreditsStore = class {
|
|
|
3306
3337
|
recordOrbitSources(bodies) {
|
|
3307
3338
|
let added = false;
|
|
3308
3339
|
let scoped = false;
|
|
3340
|
+
let archived = false;
|
|
3309
3341
|
for (const b of bodies) {
|
|
3310
3342
|
const src = b.data.orbitalSource;
|
|
3311
3343
|
if (src === OrbitalSource.UNKNOWN) continue;
|
|
@@ -3313,6 +3345,11 @@ var CreditsStore = class {
|
|
|
3313
3345
|
this.orbitSources.add(src);
|
|
3314
3346
|
added = true;
|
|
3315
3347
|
}
|
|
3348
|
+
const archive = b.data.orbitArchive;
|
|
3349
|
+
if (archive && !this.probeArchives.has(archive)) {
|
|
3350
|
+
this.probeArchives.add(archive);
|
|
3351
|
+
archived = true;
|
|
3352
|
+
}
|
|
3316
3353
|
if (!orbitSourceInfo(src)?.scoped) continue;
|
|
3317
3354
|
let parents = this.orbitSourceParents.get(src);
|
|
3318
3355
|
if (!parents) {
|
|
@@ -3325,6 +3362,7 @@ var CreditsStore = class {
|
|
|
3325
3362
|
}
|
|
3326
3363
|
if (added) this.orbitSources = new Set(this.orbitSources);
|
|
3327
3364
|
if (scoped) this.orbitSourceParents = new Map(this.orbitSourceParents);
|
|
3365
|
+
if (archived) this.probeArchives = new Set(this.probeArchives);
|
|
3328
3366
|
}
|
|
3329
3367
|
recordOrbitSource(src) {
|
|
3330
3368
|
if (src === OrbitalSource.UNKNOWN || this.orbitSources.has(src)) return;
|
|
@@ -5817,6 +5855,10 @@ var TYPE_COLOR_PROBE = "#d5cfe7";
|
|
|
5817
5855
|
var SUN_ID = "naif-10";
|
|
5818
5856
|
var EARTH_ID = "naif-399";
|
|
5819
5857
|
var SSB_ID = "naif-0";
|
|
5858
|
+
function dominantPlanetId(id) {
|
|
5859
|
+
const m = /^naif-([1-9])$/.exec(id);
|
|
5860
|
+
return m ? `naif-${m[1]}99` : null;
|
|
5861
|
+
}
|
|
5820
5862
|
var MINOR_PROMOTED_IDS = new Set([
|
|
5821
5863
|
"naif-0",
|
|
5822
5864
|
"naif-9",
|
|
@@ -6192,10 +6234,6 @@ var MinorBucket = class {
|
|
|
6192
6234
|
function isTopLevelParent(parentId) {
|
|
6193
6235
|
return parentId === "naif-0" || parentId === "naif-10";
|
|
6194
6236
|
}
|
|
6195
|
-
function dominantPlanetId(id) {
|
|
6196
|
-
const m = /^naif-([1-9])$/.exec(id);
|
|
6197
|
-
return m ? `naif-${m[1]}99` : null;
|
|
6198
|
-
}
|
|
6199
6237
|
function barycenterPrimaryId(id) {
|
|
6200
6238
|
if (id === "naif-0") return SUN_ID;
|
|
6201
6239
|
return dominantPlanetId(id);
|
|
@@ -6491,38 +6529,76 @@ var loadProgress = new LoadProgress();
|
|
|
6491
6529
|
//#endregion
|
|
6492
6530
|
//#region src/lib/fetch/fetch-timeout.ts
|
|
6493
6531
|
var BOOT_FETCH_TIMEOUT_MS = 3e4;
|
|
6494
|
-
function
|
|
6495
|
-
if (background || !loadProgress.active
|
|
6532
|
+
function bootByteCounter(res, background) {
|
|
6533
|
+
if (background || !loadProgress.active) return null;
|
|
6496
6534
|
const total = Number(res.headers.get("content-length"));
|
|
6497
|
-
if (!Number.isFinite(total) || total <= 0) return
|
|
6535
|
+
if (!Number.isFinite(total) || total <= 0) return null;
|
|
6498
6536
|
loadProgress.announce(total);
|
|
6499
|
-
|
|
6500
|
-
loadProgress.addBytes(chunk.byteLength);
|
|
6501
|
-
controller.enqueue(chunk);
|
|
6502
|
-
} });
|
|
6503
|
-
return new Response(res.body.pipeThrough(counter), {
|
|
6504
|
-
status: res.status,
|
|
6505
|
-
statusText: res.statusText,
|
|
6506
|
-
headers: res.headers
|
|
6507
|
-
});
|
|
6537
|
+
return (bytes) => loadProgress.addBytes(bytes);
|
|
6508
6538
|
}
|
|
6509
6539
|
async function fetchWithTimeout(input, init = {}, timeoutMs = BOOT_FETCH_TIMEOUT_MS) {
|
|
6510
6540
|
const controller = new AbortController();
|
|
6511
|
-
|
|
6541
|
+
let timer;
|
|
6542
|
+
let stalled = false;
|
|
6543
|
+
const arm = () => {
|
|
6544
|
+
clearTimeout(timer);
|
|
6545
|
+
timer = setTimeout(() => {
|
|
6546
|
+
stalled = true;
|
|
6547
|
+
controller.abort();
|
|
6548
|
+
}, timeoutMs);
|
|
6549
|
+
};
|
|
6512
6550
|
const onCallerAbort = () => controller.abort();
|
|
6513
6551
|
init.signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
6552
|
+
const release = () => {
|
|
6553
|
+
clearTimeout(timer);
|
|
6554
|
+
init.signal?.removeEventListener("abort", onCallerAbort);
|
|
6555
|
+
};
|
|
6556
|
+
const failure = (e) => stalled ? new Error(`Request stalled for ${timeoutMs} ms: ${input}`, { cause: e }) : e;
|
|
6557
|
+
arm();
|
|
6558
|
+
let res;
|
|
6514
6559
|
try {
|
|
6515
|
-
|
|
6560
|
+
res = await fetch(input, {
|
|
6516
6561
|
...init,
|
|
6517
6562
|
signal: controller.signal
|
|
6518
|
-
})
|
|
6563
|
+
});
|
|
6519
6564
|
} catch (e) {
|
|
6520
|
-
|
|
6521
|
-
throw e;
|
|
6522
|
-
}
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6565
|
+
release();
|
|
6566
|
+
throw failure(e);
|
|
6567
|
+
}
|
|
6568
|
+
if (!res.body) {
|
|
6569
|
+
release();
|
|
6570
|
+
return res;
|
|
6571
|
+
}
|
|
6572
|
+
const count = bootByteCounter(res, init.priority === "low");
|
|
6573
|
+
const reader = res.body.getReader();
|
|
6574
|
+
const body = new ReadableStream({
|
|
6575
|
+
async pull(out) {
|
|
6576
|
+
arm();
|
|
6577
|
+
try {
|
|
6578
|
+
const { done, value } = await reader.read();
|
|
6579
|
+
clearTimeout(timer);
|
|
6580
|
+
if (done) {
|
|
6581
|
+
release();
|
|
6582
|
+
out.close();
|
|
6583
|
+
return;
|
|
6584
|
+
}
|
|
6585
|
+
count?.(value.byteLength);
|
|
6586
|
+
out.enqueue(value);
|
|
6587
|
+
} catch (e) {
|
|
6588
|
+
release();
|
|
6589
|
+
out.error(failure(e));
|
|
6590
|
+
}
|
|
6591
|
+
},
|
|
6592
|
+
cancel(reason) {
|
|
6593
|
+
release();
|
|
6594
|
+
return reader.cancel(reason);
|
|
6595
|
+
}
|
|
6596
|
+
});
|
|
6597
|
+
return new Response(body, {
|
|
6598
|
+
status: res.status,
|
|
6599
|
+
statusText: res.statusText,
|
|
6600
|
+
headers: res.headers
|
|
6601
|
+
});
|
|
6526
6602
|
}
|
|
6527
6603
|
|
|
6528
6604
|
//#endregion
|
|
@@ -7131,7 +7207,7 @@ function parseLabels(text) {
|
|
|
7131
7207
|
return out;
|
|
7132
7208
|
}
|
|
7133
7209
|
var labelsByLang = /* @__PURE__ */ new Map();
|
|
7134
|
-
async function fetchLabels
|
|
7210
|
+
async function fetchLabels(lang = getLocale()) {
|
|
7135
7211
|
let p = labelsByLang.get(lang);
|
|
7136
7212
|
if (!p) {
|
|
7137
7213
|
p = (async () => {
|
|
@@ -7356,6 +7432,7 @@ function parseProbesPayload(buffer, startJd, endJd, float64Coeffs) {
|
|
|
7356
7432
|
const fitCenterIdValue = view.getInt32(offset + 12, true);
|
|
7357
7433
|
const fitCenterIdType = view.getUint8(offset + 16);
|
|
7358
7434
|
const nSystemIntervals = view.getUint8(offset + 17);
|
|
7435
|
+
const archive = PROBE_ARCHIVES[view.getUint8(offset + 18)];
|
|
7359
7436
|
offset += 20;
|
|
7360
7437
|
const id = buildObjectId(idTypeOrdinal, objIdValue) ?? "";
|
|
7361
7438
|
if (!id) console.warn(`probes payload: probe[${p}] (value=${objIdValue}) has unknown id-type ${idTypeOrdinal}; skipping`);
|
|
@@ -7408,6 +7485,7 @@ function parseProbesPayload(buffer, startJd, endJd, float64Coeffs) {
|
|
|
7408
7485
|
probeId: objIdValue,
|
|
7409
7486
|
hasLocalized,
|
|
7410
7487
|
objectType,
|
|
7488
|
+
archive,
|
|
7411
7489
|
fitCenter,
|
|
7412
7490
|
subStartEt,
|
|
7413
7491
|
subEndEt,
|
|
@@ -8281,7 +8359,7 @@ async function fetchElements(zone, zoom, part, time, priority) {
|
|
|
8281
8359
|
}
|
|
8282
8360
|
var ChunkLoader = class {
|
|
8283
8361
|
static prefetch(zone, zoom, part, time = null) {
|
|
8284
|
-
fetch(elementsUrl(zone, zoom, part, time));
|
|
8362
|
+
fetch(elementsUrl(zone, zoom, part, time)).catch(() => {});
|
|
8285
8363
|
}
|
|
8286
8364
|
barycenters = /* @__PURE__ */ new Map();
|
|
8287
8365
|
probeBuffers = /* @__PURE__ */ new Map();
|
|
@@ -8420,7 +8498,8 @@ var ChunkLoader = class {
|
|
|
8420
8498
|
equatorial: false,
|
|
8421
8499
|
validityStart: startJd,
|
|
8422
8500
|
validityEnd: endJd,
|
|
8423
|
-
orbitalSource: OrbitalSource.SPICE_PROBE
|
|
8501
|
+
orbitalSource: OrbitalSource.SPICE_PROBE,
|
|
8502
|
+
orbitArchive: probe.archive
|
|
8424
8503
|
};
|
|
8425
8504
|
const elements = probeOsculatingElements(probe, jd, primaryMu);
|
|
8426
8505
|
const ownId = id;
|
|
@@ -8468,7 +8547,7 @@ var ChunkLoader = class {
|
|
|
8468
8547
|
const firstPass = this.barycenters.size === 0;
|
|
8469
8548
|
const bodies = [];
|
|
8470
8549
|
let degenerate = 0;
|
|
8471
|
-
const [cols, labels] = await Promise.all([fetchElements(zone, zoom, part, time, priority), fetchLabels
|
|
8550
|
+
const [cols, labels] = await Promise.all([fetchElements(zone, zoom, part, time, priority), fetchLabels()]);
|
|
8472
8551
|
const isParabolic = cols.kind === "parabolic";
|
|
8473
8552
|
const isSGP4 = cols.kind === "sgp4";
|
|
8474
8553
|
let sliceStart = performance.now();
|
|
@@ -8910,6 +8989,41 @@ async function fetchChebyshev(zone, zoom, chunk, priority) {
|
|
|
8910
8989
|
};
|
|
8911
8990
|
}
|
|
8912
8991
|
|
|
8992
|
+
//#endregion
|
|
8993
|
+
//#region src/lib/fetch/retry-backoff.ts
|
|
8994
|
+
var FIRST_DELAY_MS = 2e3;
|
|
8995
|
+
var MAX_DELAY_MS = 6e4;
|
|
8996
|
+
var JITTER = .25;
|
|
8997
|
+
var RetryBackoff = class {
|
|
8998
|
+
failures = /* @__PURE__ */ new Map();
|
|
8999
|
+
constructor(onDue = () => {}, random = Math.random) {
|
|
9000
|
+
this.onDue = onDue;
|
|
9001
|
+
this.random = random;
|
|
9002
|
+
}
|
|
9003
|
+
waiting(key) {
|
|
9004
|
+
return this.failures.get(key)?.timer !== void 0;
|
|
9005
|
+
}
|
|
9006
|
+
fail(key) {
|
|
9007
|
+
const last = this.failures.get(key);
|
|
9008
|
+
clearTimeout(last?.timer);
|
|
9009
|
+
const failure = {
|
|
9010
|
+
count: (last?.count ?? 0) + 1,
|
|
9011
|
+
timer: void 0
|
|
9012
|
+
};
|
|
9013
|
+
const delay = Math.min(FIRST_DELAY_MS * 2 ** (failure.count - 1), MAX_DELAY_MS);
|
|
9014
|
+
const spread = 1 + JITTER * (2 * this.random() - 1);
|
|
9015
|
+
failure.timer = setTimeout(() => {
|
|
9016
|
+
failure.timer = void 0;
|
|
9017
|
+
this.onDue(key);
|
|
9018
|
+
}, delay * spread);
|
|
9019
|
+
this.failures.set(key, failure);
|
|
9020
|
+
}
|
|
9021
|
+
clear(key) {
|
|
9022
|
+
clearTimeout(this.failures.get(key)?.timer);
|
|
9023
|
+
this.failures.delete(key);
|
|
9024
|
+
}
|
|
9025
|
+
};
|
|
9026
|
+
|
|
8913
9027
|
//#endregion
|
|
8914
9028
|
//#region src/lib/fetch/position/chunk-window.ts
|
|
8915
9029
|
var NEIGHBOR_WINDOW = 1;
|
|
@@ -8920,6 +9034,11 @@ function chunkKey(zone, chunkIdx) {
|
|
|
8920
9034
|
var ChunkWindow = class {
|
|
8921
9035
|
chunks = /* @__PURE__ */ new Map();
|
|
8922
9036
|
inflight = /* @__PURE__ */ new Map();
|
|
9037
|
+
retries = new RetryBackoff(() => {
|
|
9038
|
+
this.retryDue = true;
|
|
9039
|
+
this._version++;
|
|
9040
|
+
});
|
|
9041
|
+
retryDue = false;
|
|
8923
9042
|
lastEnsuredJd = NaN;
|
|
8924
9043
|
_version = 0;
|
|
8925
9044
|
constructor(zoneParams) {
|
|
@@ -8932,19 +9051,21 @@ var ChunkWindow = class {
|
|
|
8932
9051
|
return Array.from(this.zoneParams.keys());
|
|
8933
9052
|
}
|
|
8934
9053
|
ensure(jd) {
|
|
8935
|
-
if (jd === this.lastEnsuredJd) return
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
9054
|
+
if (jd === this.lastEnsuredJd && !this.retryDue) return;
|
|
9055
|
+
this.pass(jd, false);
|
|
9056
|
+
}
|
|
9057
|
+
load(jd) {
|
|
9058
|
+
return this.pass(jd, true);
|
|
9059
|
+
}
|
|
9060
|
+
pass(jd, now) {
|
|
8939
9061
|
this.lastEnsuredJd = jd;
|
|
9062
|
+
this.retryDue = false;
|
|
8940
9063
|
const jobs = [];
|
|
8941
9064
|
const neighbors = [];
|
|
8942
|
-
let ready = true;
|
|
8943
9065
|
for (const [zone, params] of this.zoneParams) {
|
|
8944
9066
|
const center = chunkIndexForJd(params, jd);
|
|
8945
|
-
if (this.isLoadable(params, center)
|
|
8946
|
-
|
|
8947
|
-
const job = this.loadChunk(zone, params, center, "high");
|
|
9067
|
+
if (this.isLoadable(params, center)) {
|
|
9068
|
+
const job = this.loadChunk(zone, params, center, "high", now);
|
|
8948
9069
|
if (job) jobs.push(job);
|
|
8949
9070
|
}
|
|
8950
9071
|
for (let d = -NEIGHBOR_WINDOW; d <= NEIGHBOR_WINDOW; d++) {
|
|
@@ -8962,10 +9083,7 @@ var ChunkWindow = class {
|
|
|
8962
9083
|
done.catch(() => {}).then(() => {
|
|
8963
9084
|
for (const [zone, params, idx] of neighbors) this.loadChunk(zone, params, idx, "low")?.catch(() => {});
|
|
8964
9085
|
});
|
|
8965
|
-
return
|
|
8966
|
-
ready,
|
|
8967
|
-
done
|
|
8968
|
-
};
|
|
9086
|
+
return done;
|
|
8969
9087
|
}
|
|
8970
9088
|
request(jd) {
|
|
8971
9089
|
for (const [zone, params] of this.zoneParams) {
|
|
@@ -8981,23 +9099,6 @@ var ChunkWindow = class {
|
|
|
8981
9099
|
}
|
|
8982
9100
|
return false;
|
|
8983
9101
|
}
|
|
8984
|
-
pendingCurrent(jd) {
|
|
8985
|
-
if (this.inflight.size === 0) return RESOLVED;
|
|
8986
|
-
let pending = null;
|
|
8987
|
-
for (const [zone, params] of this.zoneParams) {
|
|
8988
|
-
const job = this.inflight.get(chunkKey(zone, chunkIndexForJd(params, jd)));
|
|
8989
|
-
if (job) (pending ??= []).push(job);
|
|
8990
|
-
}
|
|
8991
|
-
return pending ? Promise.all(pending).then(() => void 0) : RESOLVED;
|
|
8992
|
-
}
|
|
8993
|
-
allCurrentChunksLoaded(jd) {
|
|
8994
|
-
for (const [zone, params] of this.zoneParams) {
|
|
8995
|
-
const center = chunkIndexForJd(params, jd);
|
|
8996
|
-
if (!this.isLoadable(params, center)) continue;
|
|
8997
|
-
if (!this.isResident(zone, center)) return false;
|
|
8998
|
-
}
|
|
8999
|
-
return true;
|
|
9000
|
-
}
|
|
9001
9102
|
isResident(zone, chunkIdx) {
|
|
9002
9103
|
return this.chunks.get(zone)?.has(chunkIdx) ?? false;
|
|
9003
9104
|
}
|
|
@@ -9006,14 +9107,22 @@ var ChunkWindow = class {
|
|
|
9006
9107
|
if (!zoneMap) return;
|
|
9007
9108
|
for (const idx of zoneMap.keys()) if (idx < center - NEIGHBOR_WINDOW || idx > center + NEIGHBOR_WINDOW) zoneMap.delete(idx);
|
|
9008
9109
|
}
|
|
9009
|
-
loadChunk(zone, params, chunkIdx, priority) {
|
|
9110
|
+
loadChunk(zone, params, chunkIdx, priority, now = false) {
|
|
9010
9111
|
if (this.isResident(zone, chunkIdx)) return null;
|
|
9011
9112
|
const key = chunkKey(zone, chunkIdx);
|
|
9012
9113
|
const existing = this.inflight.get(key);
|
|
9013
9114
|
if (existing) return existing;
|
|
9014
|
-
|
|
9115
|
+
if (!now && this.retries.waiting(key)) return null;
|
|
9116
|
+
return this.track(key, this.fetchAndStore(zone, params, chunkIdx, priority));
|
|
9117
|
+
}
|
|
9118
|
+
track(key, job) {
|
|
9015
9119
|
this.inflight.set(key, job);
|
|
9016
|
-
|
|
9120
|
+
const settle = (ok) => {
|
|
9121
|
+
if (this.inflight.get(key) === job) this.inflight.delete(key);
|
|
9122
|
+
if (ok) this.retries.clear(key);
|
|
9123
|
+
else this.retries.fail(key);
|
|
9124
|
+
};
|
|
9125
|
+
job.then(() => settle(true), () => settle(false));
|
|
9017
9126
|
return job;
|
|
9018
9127
|
}
|
|
9019
9128
|
async fetchAndStore(zone, params, chunkIdx, priority) {
|
|
@@ -9144,15 +9253,7 @@ var ProbeStore = class extends ChunkWindow {
|
|
|
9144
9253
|
if (this.isResident(zone, idx)) continue;
|
|
9145
9254
|
const key = chunkKey(zone, idx);
|
|
9146
9255
|
if (this.warmed.has(key)) continue;
|
|
9147
|
-
|
|
9148
|
-
if (existing) {
|
|
9149
|
-
jobs.push(existing);
|
|
9150
|
-
continue;
|
|
9151
|
-
}
|
|
9152
|
-
const job = this.fetchAndWarm(key, zone, idx, params);
|
|
9153
|
-
this.inflight.set(key, job);
|
|
9154
|
-
job.finally(() => this.inflight.delete(key));
|
|
9155
|
-
jobs.push(job);
|
|
9256
|
+
jobs.push(this.inflight.get(key) ?? this.track(key, this.fetchAndWarm(key, zone, idx, params)));
|
|
9156
9257
|
}
|
|
9157
9258
|
await Promise.all(jobs);
|
|
9158
9259
|
}
|
|
@@ -9374,9 +9475,11 @@ function rowsFor(chunk, id) {
|
|
|
9374
9475
|
|
|
9375
9476
|
//#endregion
|
|
9376
9477
|
//#region src/lib/fetch/gz.ts
|
|
9478
|
+
function gunzip(res) {
|
|
9479
|
+
return new Response(res.body.pipeThrough(new DecompressionStream("gzip")));
|
|
9480
|
+
}
|
|
9377
9481
|
async function gunzipJson(res) {
|
|
9378
|
-
|
|
9379
|
-
return await new Response(res.body.pipeThrough(ds)).json();
|
|
9482
|
+
return await gunzip(res).json();
|
|
9380
9483
|
}
|
|
9381
9484
|
function memoizedGzJson(url, opts = {}) {
|
|
9382
9485
|
const { onMissing, fetcher = (u) => fetch(u), error = (res, u) => `memoizedGzJson: ${u} returned ${res.status} ${res.statusText}` } = opts;
|
|
@@ -9638,13 +9741,7 @@ async function createPlaceholderBody(targetId, ctx, visited = /* @__PURE__ */ ne
|
|
|
9638
9741
|
return [];
|
|
9639
9742
|
}
|
|
9640
9743
|
visited.add(targetId);
|
|
9641
|
-
|
|
9642
|
-
try {
|
|
9643
|
-
detail = await fetchObjectDetail(targetId);
|
|
9644
|
-
} catch {
|
|
9645
|
-
console.warn(`createPlaceholderBody: failed to fetch global data for ${targetId} — hiding`);
|
|
9646
|
-
return [];
|
|
9647
|
-
}
|
|
9744
|
+
const detail = await fetchObjectDetail(targetId);
|
|
9648
9745
|
const global = detail.global;
|
|
9649
9746
|
if (!global) {
|
|
9650
9747
|
console.warn(`createPlaceholderBody: no catalogue record for ${targetId} — hiding`);
|
|
@@ -9694,7 +9791,7 @@ async function ensureTargetStreamed(ctx, targetId) {
|
|
|
9694
9791
|
if (ctx.getBody(targetId)) return;
|
|
9695
9792
|
const placeholders = await createPlaceholderBody(targetId, ctx);
|
|
9696
9793
|
if (placeholders.length === 0) return;
|
|
9697
|
-
const added = routePlaceholders(ctx, placeholders, await fetchLabels
|
|
9794
|
+
const added = routePlaceholders(ctx, placeholders, await fetchLabels());
|
|
9698
9795
|
ctx.bodies.flushMinor(added > 0);
|
|
9699
9796
|
}
|
|
9700
9797
|
|
|
@@ -9755,11 +9852,17 @@ function setSceneSettings(settings) {
|
|
|
9755
9852
|
//#region src/lib/scene/zone-refresher.ts
|
|
9756
9853
|
var MIN_LOAD_INTERVAL_MS = 2e3;
|
|
9757
9854
|
var STALE_FAILURE_STREAK = 3;
|
|
9855
|
+
function loadKey(z, target) {
|
|
9856
|
+
return `${z.zone}:${z.zoom ?? ""}:${target}`;
|
|
9857
|
+
}
|
|
9758
9858
|
var ZoneRefresher = class {
|
|
9759
9859
|
zones = [];
|
|
9760
9860
|
latestDate;
|
|
9761
9861
|
failureStreak = 0;
|
|
9762
9862
|
staleNotified = false;
|
|
9863
|
+
retries = new RetryBackoff(() => this.wakes++);
|
|
9864
|
+
wakes = 0;
|
|
9865
|
+
wakeTimer;
|
|
9763
9866
|
constructor(ctx, metadata, loader, initialDate) {
|
|
9764
9867
|
this.ctx = ctx;
|
|
9765
9868
|
this.loader = loader;
|
|
@@ -9805,16 +9908,30 @@ var ZoneRefresher = class {
|
|
|
9805
9908
|
}
|
|
9806
9909
|
}
|
|
9807
9910
|
}
|
|
9808
|
-
recordSuccess() {
|
|
9911
|
+
recordSuccess(key) {
|
|
9912
|
+
this.retries.clear(key);
|
|
9809
9913
|
this.failureStreak = 0;
|
|
9810
9914
|
}
|
|
9811
|
-
recordFailure() {
|
|
9915
|
+
recordFailure(key) {
|
|
9916
|
+
this.retries.fail(key);
|
|
9812
9917
|
this.failureStreak++;
|
|
9813
9918
|
if (this.failureStreak >= STALE_FAILURE_STREAK && !this.staleNotified) {
|
|
9814
9919
|
this.staleNotified = true;
|
|
9815
9920
|
this.ctx.onDataStale?.();
|
|
9816
9921
|
}
|
|
9817
9922
|
}
|
|
9923
|
+
wakeIn(ms) {
|
|
9924
|
+
this.wakeTimer ??= setTimeout(() => {
|
|
9925
|
+
this.wakeTimer = void 0;
|
|
9926
|
+
this.wakes++;
|
|
9927
|
+
}, ms);
|
|
9928
|
+
}
|
|
9929
|
+
start(z, load) {
|
|
9930
|
+
z.inFlight = load.catch(() => {}).finally(() => {
|
|
9931
|
+
z.inFlight = null;
|
|
9932
|
+
});
|
|
9933
|
+
return load;
|
|
9934
|
+
}
|
|
9818
9935
|
invalidateZone(zoneName) {
|
|
9819
9936
|
for (const z of this.zones) {
|
|
9820
9937
|
if (z.zone !== zoneName || z.kind !== "time") continue;
|
|
@@ -9835,12 +9952,15 @@ var ZoneRefresher = class {
|
|
|
9835
9952
|
if (state.inFlight) continue;
|
|
9836
9953
|
const target = snapshotDate(state.zoomData, date);
|
|
9837
9954
|
if (target === state.currentTime) continue;
|
|
9955
|
+
if (this.retries.waiting(loadKey(state, target))) continue;
|
|
9838
9956
|
const nowMs = performance.now();
|
|
9839
|
-
|
|
9957
|
+
const held = state.lastLoadStartMs + MIN_LOAD_INTERVAL_MS - nowMs;
|
|
9958
|
+
if (held > 0) {
|
|
9959
|
+
this.wakeIn(held);
|
|
9960
|
+
continue;
|
|
9961
|
+
}
|
|
9840
9962
|
state.lastLoadStartMs = nowMs;
|
|
9841
|
-
state
|
|
9842
|
-
state.inFlight = null;
|
|
9843
|
-
});
|
|
9963
|
+
this.start(state, this.loadTime(state, target, date));
|
|
9844
9964
|
} else {
|
|
9845
9965
|
while (true) {
|
|
9846
9966
|
const target = chunkIndexForJd(state.zoomData, jd);
|
|
@@ -9855,18 +9975,18 @@ var ZoneRefresher = class {
|
|
|
9855
9975
|
if (state.inFlight) continue;
|
|
9856
9976
|
const target = chunkIndexForJd(state.zoomData, jd);
|
|
9857
9977
|
if (target === state.currentIdx) continue;
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
});
|
|
9978
|
+
if (this.retries.waiting(loadKey(state, target))) continue;
|
|
9979
|
+
this.start(state, this.loadChunk(state, target));
|
|
9861
9980
|
}
|
|
9862
9981
|
}
|
|
9863
9982
|
async ready(date = this.latestDate) {
|
|
9864
9983
|
this.latestDate = date;
|
|
9865
9984
|
const jd = dateToJD(date);
|
|
9866
9985
|
const waits = this.zones.map((z) => this.zoneReady(z, date, jd));
|
|
9867
|
-
if (this.ctx.chebStore) waits.push(this.ctx.chebStore.
|
|
9868
|
-
if (this.ctx.probeStore) waits.push(this.ctx.probeStore.
|
|
9869
|
-
await Promise.
|
|
9986
|
+
if (this.ctx.chebStore) waits.push(this.ctx.chebStore.load(jd));
|
|
9987
|
+
if (this.ctx.probeStore) waits.push(this.ctx.probeStore.load(jd));
|
|
9988
|
+
const failure = (await Promise.allSettled(waits)).find((w) => w.status === "rejected");
|
|
9989
|
+
if (failure) throw failure.reason;
|
|
9870
9990
|
}
|
|
9871
9991
|
async zoneReady(z, date, jd) {
|
|
9872
9992
|
while (z.inFlight) await z.inFlight;
|
|
@@ -9874,17 +9994,12 @@ var ZoneRefresher = class {
|
|
|
9874
9994
|
const target = snapshotDate(z.zoomData, date);
|
|
9875
9995
|
if (target === z.currentTime) return;
|
|
9876
9996
|
z.lastLoadStartMs = performance.now();
|
|
9877
|
-
z
|
|
9878
|
-
z.inFlight = null;
|
|
9879
|
-
});
|
|
9997
|
+
await this.start(z, this.loadTime(z, target, date));
|
|
9880
9998
|
} else {
|
|
9881
9999
|
const target = chunkIndexForJd(z.zoomData, jd);
|
|
9882
10000
|
if (target === z.currentIdx) return;
|
|
9883
|
-
z
|
|
9884
|
-
z.inFlight = null;
|
|
9885
|
-
});
|
|
10001
|
+
await this.start(z, this.loadChunk(z, target));
|
|
9886
10002
|
}
|
|
9887
|
-
await z.inFlight;
|
|
9888
10003
|
}
|
|
9889
10004
|
wanted = /* @__PURE__ */ new Set();
|
|
9890
10005
|
want(id) {
|
|
@@ -9897,7 +10012,7 @@ var ZoneRefresher = class {
|
|
|
9897
10012
|
this.want(targetId);
|
|
9898
10013
|
if (this.ctx.earthSatFilter || this.ctx.earthTypeFilter) {
|
|
9899
10014
|
this.invalidateZone("earth");
|
|
9900
|
-
await this.ready(date);
|
|
10015
|
+
await this.ready(date).catch(() => {});
|
|
9901
10016
|
if (this.ctx.getBody(targetId)) return;
|
|
9902
10017
|
}
|
|
9903
10018
|
}
|
|
@@ -9908,13 +10023,13 @@ var ZoneRefresher = class {
|
|
|
9908
10023
|
if (!store) return;
|
|
9909
10024
|
const jd = dateToJD(date);
|
|
9910
10025
|
const passengerPromise = passengerFor(targetId);
|
|
9911
|
-
await store.
|
|
10026
|
+
await store.load(jd).catch(() => {});
|
|
9912
10027
|
const passenger = await passengerPromise;
|
|
9913
10028
|
if (passenger) store.registerCarried(passenger);
|
|
9914
10029
|
const fcId = store.stampedFitCenterAt(targetId, jd);
|
|
9915
10030
|
if (fcId) await ensureTargetStreamed(this.ctx, fcId);
|
|
9916
10031
|
if (this.ctx.getBody(targetId)) return;
|
|
9917
|
-
const labels = await fetchLabels
|
|
10032
|
+
const labels = await fetchLabels();
|
|
9918
10033
|
const target = this.loader.processProbes(store, date, labels).find((b) => b.data.id === targetId);
|
|
9919
10034
|
if (!target) return;
|
|
9920
10035
|
this.ctx.bodies.addBodies([target]);
|
|
@@ -9975,10 +10090,11 @@ var ZoneRefresher = class {
|
|
|
9975
10090
|
if (z.zone === "earth") this.ctx.notifyEarthSatRollover();
|
|
9976
10091
|
console.log(`zone-refresher: ${z.zone}@${fromTime} → ${time} (+${added} ~${updated} -${removed})`);
|
|
9977
10092
|
this.prefetchTimeNeighbors(z, date);
|
|
9978
|
-
this.recordSuccess();
|
|
10093
|
+
this.recordSuccess(loadKey(z, time));
|
|
9979
10094
|
} catch (e) {
|
|
9980
10095
|
console.warn(`zone-refresher: failed to refresh ${z.zone}@${time}:`, e);
|
|
9981
|
-
this.recordFailure();
|
|
10096
|
+
this.recordFailure(loadKey(z, time));
|
|
10097
|
+
throw e;
|
|
9982
10098
|
}
|
|
9983
10099
|
}
|
|
9984
10100
|
async loadChunk(z, target) {
|
|
@@ -9991,11 +10107,12 @@ var ZoneRefresher = class {
|
|
|
9991
10107
|
else chunks = await Promise.all(Array.from({ length: z.parts }, (_, part) => this.loader.process(z.zone, z.zoom, part, String(target), z.parentIdType)));
|
|
9992
10108
|
this.applyChunkSwap(z, previous, target, chunks);
|
|
9993
10109
|
this.preloadChunkNeighbors(z);
|
|
9994
|
-
this.recordSuccess();
|
|
10110
|
+
this.recordSuccess(loadKey(z, target));
|
|
9995
10111
|
} catch (e) {
|
|
9996
10112
|
console.warn(`zone-refresher: ${z.zone} chunk reload failed (${previous} → ${target}):`, e);
|
|
9997
10113
|
z.currentIdx = previous;
|
|
9998
|
-
this.recordFailure();
|
|
10114
|
+
this.recordFailure(loadKey(z, target));
|
|
10115
|
+
throw e;
|
|
9999
10116
|
}
|
|
10000
10117
|
}
|
|
10001
10118
|
applyChunkSwap(z, previous, target, chunks) {
|
|
@@ -10344,18 +10461,18 @@ async function buildProbeStore(metadata, jd, layers) {
|
|
|
10344
10461
|
const missingCenter = Array.from(params).filter(([, p]) => p.fit_center_naif_id === void 0);
|
|
10345
10462
|
if (missingCenter.length > 0) console.warn(`ProbeStore: ${missingCenter.length} zone(s) missing fit_center_naif_id in metadata — re-export to refresh: ${missingCenter.map(([z]) => z).join(", ")}`);
|
|
10346
10463
|
const store = new ProbeStore(params);
|
|
10347
|
-
await store.
|
|
10464
|
+
await store.load(jd);
|
|
10348
10465
|
return store;
|
|
10349
10466
|
}
|
|
10350
10467
|
async function loadMajorBodies(ctx, loader, metadata, date, jd, passenger) {
|
|
10351
10468
|
const major = [];
|
|
10352
10469
|
let cachedLabels = null;
|
|
10353
10470
|
if (ctx.chebStore) {
|
|
10354
|
-
cachedLabels = await fetchLabels
|
|
10471
|
+
cachedLabels = await fetchLabels();
|
|
10355
10472
|
major.push(...loader.processChebyshev(date, cachedLabels));
|
|
10356
10473
|
}
|
|
10357
10474
|
if (ctx.probeStore) {
|
|
10358
|
-
cachedLabels ??= await fetchLabels
|
|
10475
|
+
cachedLabels ??= await fetchLabels();
|
|
10359
10476
|
if (passenger) ctx.probeStore.registerCarried(passenger);
|
|
10360
10477
|
major.push(...loader.processProbes(ctx.probeStore, date, cachedLabels));
|
|
10361
10478
|
}
|
|
@@ -10388,7 +10505,7 @@ async function loadScene(ctx, date, targetId) {
|
|
|
10388
10505
|
const atmospheresPromise = loadAtmospheres().catch((e) => console.warn("scene-load: atmospheres failed to load — rendering without shells:", e));
|
|
10389
10506
|
const jd = dateToJD(date);
|
|
10390
10507
|
const metadataPromise = fetchMetadata();
|
|
10391
|
-
fetchLabels
|
|
10508
|
+
fetchLabels().catch(() => {});
|
|
10392
10509
|
metadataPromise.then((metadata) => {
|
|
10393
10510
|
const moons = metadata.position.zones.moons;
|
|
10394
10511
|
if (!moons || ctx.layers.skipsZone("moons")) return;
|
|
@@ -10404,7 +10521,7 @@ async function loadScene(ctx, date, targetId) {
|
|
|
10404
10521
|
for (const zone of params.keys()) if (ctx.layers.skipsZone(zone)) params.delete(zone);
|
|
10405
10522
|
if (params.size === 0) return null;
|
|
10406
10523
|
const store = new ChebyshevStore(params);
|
|
10407
|
-
await store.
|
|
10524
|
+
await store.load(jd);
|
|
10408
10525
|
return store;
|
|
10409
10526
|
});
|
|
10410
10527
|
const probePromise = metadataPromise.then((metadata) => buildProbeStore(metadata, jd, ctx.layers));
|
|
@@ -10422,7 +10539,7 @@ async function loadScene(ctx, date, targetId) {
|
|
|
10422
10539
|
await atmospheresPromise;
|
|
10423
10540
|
ctx.bodies.addBodies(major);
|
|
10424
10541
|
ctx.credits.recordOrbitSources(major);
|
|
10425
|
-
const labels = await fetchLabels
|
|
10542
|
+
const labels = await fetchLabels();
|
|
10426
10543
|
loadProgress.reach("labels");
|
|
10427
10544
|
let addedSinceFlush = false;
|
|
10428
10545
|
const noteAdded = () => {
|
|
@@ -10473,8 +10590,8 @@ async function loadScene(ctx, date, targetId) {
|
|
|
10473
10590
|
console.warn(`scene-load: ${zone} part ${part} failed (skipped):`, e);
|
|
10474
10591
|
};
|
|
10475
10592
|
try {
|
|
10476
|
-
await Promise.all([...asteroidOtherArgs.map(({ zone, zoom, part, time, parentIdType }) => loader.fetchMinorColumns(zone, zoom, part, time).then((cols) => handleColumnChunk(zone, cols, parentIdType)).catch(onChunkFail(zone, part))), ctx.refresher?.ready().then(() => {
|
|
10477
|
-
|
|
10593
|
+
await Promise.all([...asteroidOtherArgs.map(({ zone, zoom, part, time, parentIdType }) => loader.fetchMinorColumns(zone, zoom, part, time).then((cols) => handleColumnChunk(zone, cols, parentIdType)).catch(onChunkFail(zone, part))), ctx.refresher?.ready().then(noteAdded, (e) => {
|
|
10594
|
+
console.warn("scene-load: the data of the opening date is not all in:", e);
|
|
10478
10595
|
})]);
|
|
10479
10596
|
await Promise.all(moonArgs.map(({ zone, zoom, part, time, parentIdType }) => loader.process(zone, zoom, part, time, parentIdType).then((chunk) => handleMoonChunk(zone, chunk)).catch(onChunkFail(zone, part))));
|
|
10480
10597
|
} finally {
|
|
@@ -10505,6 +10622,7 @@ var fetchEarthMembership = memoizedGzJson(async () => {
|
|
|
10505
10622
|
await fetchMetadata();
|
|
10506
10623
|
return versionedUrl("/v1/membership/earth.json.gz", "membership");
|
|
10507
10624
|
}, {
|
|
10625
|
+
fetcher: fetchWithTimeout,
|
|
10508
10626
|
onMissing: () => ({}),
|
|
10509
10627
|
error: (res) => `fetchEarthMembership: ${res.status} ${res.statusText}`
|
|
10510
10628
|
});
|
|
@@ -10557,6 +10675,7 @@ var ContextManager = class {
|
|
|
10557
10675
|
smallBodyFilter = null;
|
|
10558
10676
|
missionMemberIds = null;
|
|
10559
10677
|
currentGroupSlug = null;
|
|
10678
|
+
groupFilterJob = Promise.resolve();
|
|
10560
10679
|
groupFilterListeners = /* @__PURE__ */ new Set();
|
|
10561
10680
|
smallBodyFilterListeners = /* @__PURE__ */ new Set();
|
|
10562
10681
|
earthSatRolloverListeners = /* @__PURE__ */ new Set();
|
|
@@ -10597,12 +10716,15 @@ var ContextManager = class {
|
|
|
10597
10716
|
try {
|
|
10598
10717
|
await loadScene(this, date, targetId);
|
|
10599
10718
|
} catch (e) {
|
|
10600
|
-
this.
|
|
10601
|
-
this.error = e instanceof Error ? e.message : String(e);
|
|
10602
|
-
console.error("[scene-load]", e);
|
|
10719
|
+
this.fail(e);
|
|
10603
10720
|
throw e;
|
|
10604
10721
|
}
|
|
10605
10722
|
}
|
|
10723
|
+
fail(e) {
|
|
10724
|
+
this.loading = false;
|
|
10725
|
+
this.error = e instanceof Error ? e.message : String(e);
|
|
10726
|
+
console.error("[scene-load]", e);
|
|
10727
|
+
}
|
|
10606
10728
|
refreshTick(date) {
|
|
10607
10729
|
this.refresher?.tick(date);
|
|
10608
10730
|
}
|
|
@@ -10612,15 +10734,23 @@ var ContextManager = class {
|
|
|
10612
10734
|
async ensureBody(targetId, date) {
|
|
10613
10735
|
await this.refresher?.ensureBody(targetId, date);
|
|
10614
10736
|
}
|
|
10615
|
-
|
|
10616
|
-
if (slug === this.currentGroupSlug) return;
|
|
10737
|
+
applyGroupFilter(slug) {
|
|
10738
|
+
if (slug === this.currentGroupSlug) return this.groupFilterJob;
|
|
10617
10739
|
this.currentGroupSlug = slug;
|
|
10740
|
+
const job = this.installGroupFilter(slug);
|
|
10741
|
+
this.groupFilterJob = job;
|
|
10742
|
+
job.catch(() => {
|
|
10743
|
+
if (this.groupFilterJob === job) this.currentGroupSlug = void 0;
|
|
10744
|
+
});
|
|
10745
|
+
return job;
|
|
10746
|
+
}
|
|
10747
|
+
async installGroupFilter(slug) {
|
|
10618
10748
|
if (slug?.startsWith("mission-")) {
|
|
10619
10749
|
const detail = await fetchGroupDetail(slug);
|
|
10620
10750
|
if (slug !== this.currentGroupSlug) return;
|
|
10621
10751
|
this.missionMemberIds = new Set((detail.global?.notable_members ?? []).map((mm) => mm.id).filter((id) => !!id));
|
|
10622
10752
|
} else this.missionMemberIds = null;
|
|
10623
|
-
const entry = slug ? await
|
|
10753
|
+
const entry = slug ? (await fetchGroupIndex())[slug] ?? null : null;
|
|
10624
10754
|
if (slug !== this.currentGroupSlug) return;
|
|
10625
10755
|
const nextSmallBody = this.resolveSmallBodyFilter(slug, entry?.applies_to, entry?.n);
|
|
10626
10756
|
const nextEarthSlug = entry?.applies_to === "earth_sat" ? slug : null;
|
|
@@ -10669,13 +10799,6 @@ var ContextManager = class {
|
|
|
10669
10799
|
}
|
|
10670
10800
|
return null;
|
|
10671
10801
|
}
|
|
10672
|
-
async resolveIndexEntry(slug) {
|
|
10673
|
-
try {
|
|
10674
|
-
return (await fetchGroupIndex())[slug] ?? null;
|
|
10675
|
-
} catch {
|
|
10676
|
-
return null;
|
|
10677
|
-
}
|
|
10678
|
-
}
|
|
10679
10802
|
};
|
|
10680
10803
|
|
|
10681
10804
|
//#endregion
|
|
@@ -11291,6 +11414,51 @@ function makeEnvMap(renderer) {
|
|
|
11291
11414
|
return tex;
|
|
11292
11415
|
}
|
|
11293
11416
|
|
|
11417
|
+
//#endregion
|
|
11418
|
+
//#region src/lib/scene/setup/bloom-high-pass.ts
|
|
11419
|
+
var MAX_SPAN = 16;
|
|
11420
|
+
function installBloomHighPass(pass) {
|
|
11421
|
+
pass.materialHighPassFilter.dispose();
|
|
11422
|
+
pass.materialHighPassFilter = new ShaderMaterial({
|
|
11423
|
+
uniforms: pass.highPassUniforms,
|
|
11424
|
+
vertexShader: `
|
|
11425
|
+
varying vec2 vUv;
|
|
11426
|
+
void main() {
|
|
11427
|
+
vUv = uv;
|
|
11428
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
11429
|
+
}
|
|
11430
|
+
`,
|
|
11431
|
+
fragmentShader: `
|
|
11432
|
+
uniform sampler2D tDiffuse;
|
|
11433
|
+
uniform float luminosityThreshold;
|
|
11434
|
+
uniform float smoothWidth;
|
|
11435
|
+
varying vec2 vUv;
|
|
11436
|
+
void main() {
|
|
11437
|
+
vec2 size = vec2(textureSize(tDiffuse, 0));
|
|
11438
|
+
vec2 span = size * abs(vec2(dFdx(vUv.x), dFdy(vUv.y)));
|
|
11439
|
+
vec2 lo = vUv * size - 0.5 * span;
|
|
11440
|
+
vec2 hi = lo + span;
|
|
11441
|
+
// A sliver of a pixel is rounding, not light.
|
|
11442
|
+
ivec2 first = max(ivec2(floor(lo + 0.001)), ivec2(0));
|
|
11443
|
+
ivec2 last = min(ivec2(ceil(hi - 0.001)) - 1, ivec2(size) - 1);
|
|
11444
|
+
last = min(last, first + ${MAX_SPAN - 1});
|
|
11445
|
+
vec3 sum = vec3(0.0);
|
|
11446
|
+
for (int y = first.y; y <= last.y; y++) {
|
|
11447
|
+
float wy = min(float(y) + 1.0, hi.y) - max(float(y), lo.y);
|
|
11448
|
+
for (int x = first.x; x <= last.x; x++) {
|
|
11449
|
+
float wx = min(float(x) + 1.0, hi.x) - max(float(x), lo.x);
|
|
11450
|
+
vec3 texel = texelFetch(tDiffuse, ivec2(x, y), 0).rgb;
|
|
11451
|
+
float taken = smoothstep(
|
|
11452
|
+
luminosityThreshold, luminosityThreshold + smoothWidth, luminance(texel));
|
|
11453
|
+
sum += texel * (taken * wx * wy);
|
|
11454
|
+
}
|
|
11455
|
+
}
|
|
11456
|
+
gl_FragColor = vec4(sum / (span.x * span.y), 1.0);
|
|
11457
|
+
}
|
|
11458
|
+
`
|
|
11459
|
+
});
|
|
11460
|
+
}
|
|
11461
|
+
|
|
11294
11462
|
//#endregion
|
|
11295
11463
|
//#region src/lib/scene/setup/three-boot.ts
|
|
11296
11464
|
var CAMERA_FAR_DEFAULT = 1e5;
|
|
@@ -11357,6 +11525,7 @@ function bootThree(canvas, labelContainer, ctx) {
|
|
|
11357
11525
|
composer.setSize(canvas.clientWidth, canvas.clientHeight);
|
|
11358
11526
|
composer.addPass(new RenderPass(scene, camera));
|
|
11359
11527
|
const bloomPass = new UnrealBloomPass(new Vector2(canvas.clientWidth * tier.bloomScale, canvas.clientHeight * tier.bloomScale), .3, .5, 1);
|
|
11528
|
+
installBloomHighPass(bloomPass);
|
|
11360
11529
|
composer.addPass(bloomPass);
|
|
11361
11530
|
composer.addPass(new OutputPass());
|
|
11362
11531
|
return {
|
|
@@ -11431,7 +11600,7 @@ var AttitudeTrack = class {
|
|
|
11431
11600
|
files;
|
|
11432
11601
|
chunks = /* @__PURE__ */ new Map();
|
|
11433
11602
|
inflight = /* @__PURE__ */ new Set();
|
|
11434
|
-
|
|
11603
|
+
retries = new RetryBackoff();
|
|
11435
11604
|
constructor(probeId, manifest) {
|
|
11436
11605
|
this.probeId = probeId;
|
|
11437
11606
|
this.startJd = manifest.files.length ? manifest.files[0].start_jd : 0;
|
|
@@ -11511,7 +11680,7 @@ var AttitudeTrack = class {
|
|
|
11511
11680
|
if (i < 0 || i >= this.files.length) return void 0;
|
|
11512
11681
|
const have = this.chunks.get(i);
|
|
11513
11682
|
if (have) return have;
|
|
11514
|
-
if (this.inflight.has(i) || this.
|
|
11683
|
+
if (this.inflight.has(i) || this.retries.waiting(i)) return void 0;
|
|
11515
11684
|
this.inflight.add(i);
|
|
11516
11685
|
this.load(i);
|
|
11517
11686
|
}
|
|
@@ -11523,8 +11692,9 @@ var AttitudeTrack = class {
|
|
|
11523
11692
|
const ds = new DecompressionStream("gzip");
|
|
11524
11693
|
const buf = await new Response(res.body.pipeThrough(ds)).arrayBuffer();
|
|
11525
11694
|
this.chunks.set(i, parseAttitudeChunk(buf));
|
|
11695
|
+
this.retries.clear(i);
|
|
11526
11696
|
} catch (e) {
|
|
11527
|
-
this.
|
|
11697
|
+
this.retries.fail(i);
|
|
11528
11698
|
console.warn(`attitude: chunk ${this.probeId}/${file.name} failed to load:`, e);
|
|
11529
11699
|
} finally {
|
|
11530
11700
|
this.inflight.delete(i);
|
|
@@ -11959,6 +12129,23 @@ function cullOccludersFor(dst, center, radius, sunDir) {
|
|
|
11959
12129
|
}
|
|
11960
12130
|
return n;
|
|
11961
12131
|
}
|
|
12132
|
+
function sunInView(aSun, aOc, sep) {
|
|
12133
|
+
if (aOc > 10 * aSun) {
|
|
12134
|
+
const t = (aOc - sep) / aSun;
|
|
12135
|
+
if (t >= 1) return 0;
|
|
12136
|
+
if (t <= -1) return 1;
|
|
12137
|
+
return 1 - (Math.acos(-t) + t * Math.sqrt(Math.max(1 - t * t, 0))) / Math.PI;
|
|
12138
|
+
}
|
|
12139
|
+
if (sep >= aSun + aOc) return 1;
|
|
12140
|
+
if (sep + aSun <= aOc) return 0;
|
|
12141
|
+
if (sep + aOc <= aSun) return 1 - aOc * aOc / (aSun * aSun);
|
|
12142
|
+
const a = aSun;
|
|
12143
|
+
const b = aOc;
|
|
12144
|
+
const c = sep;
|
|
12145
|
+
const x = (c * c + a * a - b * b) / (2 * c);
|
|
12146
|
+
const y = Math.sqrt(Math.max(a * a - x * x, 0));
|
|
12147
|
+
return 1 - (a * a * Math.acos(Math.max(-1, Math.min(1, x / a))) + b * b * Math.acos(Math.max(-1, Math.min(1, (c - x) / b))) - c * y) / (Math.PI * a * a);
|
|
12148
|
+
}
|
|
11962
12149
|
function evaluateEclipseFactor(receiverPos, selfPos) {
|
|
11963
12150
|
const aSun = SHARED.uSunAngularRadius.value;
|
|
11964
12151
|
if (aSun <= 0) return 1;
|
|
@@ -11981,28 +12168,8 @@ function evaluateEclipseFactor(receiverPos, selfPos) {
|
|
|
11981
12168
|
if (dOc < 1e-5) continue;
|
|
11982
12169
|
const aOc = Math.asin(Math.min(r / dOc, 1));
|
|
11983
12170
|
const cosSep = Math.max(-1, Math.min(1, (sunDir.x * tx + sunDir.y * ty + sunDir.z * tz) / dOc));
|
|
11984
|
-
|
|
11985
|
-
if (
|
|
11986
|
-
const t = (aOc - sep) / aSun;
|
|
11987
|
-
if (t >= 1) return 0;
|
|
11988
|
-
if (t <= -1) continue;
|
|
11989
|
-
const covered = (Math.acos(-t) + t * Math.sqrt(Math.max(1 - t * t, 0))) / Math.PI;
|
|
11990
|
-
result *= 1 - covered;
|
|
11991
|
-
continue;
|
|
11992
|
-
}
|
|
11993
|
-
if (sep >= aSun + aOc) continue;
|
|
11994
|
-
if (sep + aSun <= aOc) return 0;
|
|
11995
|
-
if (sep + aOc <= aSun) {
|
|
11996
|
-
result *= 1 - aOc * aOc / (aSun * aSun);
|
|
11997
|
-
continue;
|
|
11998
|
-
}
|
|
11999
|
-
const a = aSun;
|
|
12000
|
-
const b = aOc;
|
|
12001
|
-
const c = sep;
|
|
12002
|
-
const x = (c * c + a * a - b * b) / (2 * c);
|
|
12003
|
-
const y = Math.sqrt(Math.max(a * a - x * x, 0));
|
|
12004
|
-
const A = a * a * Math.acos(Math.max(-1, Math.min(1, x / a))) + b * b * Math.acos(Math.max(-1, Math.min(1, (c - x) / b))) - c * y;
|
|
12005
|
-
result *= 1 - A / (Math.PI * a * a);
|
|
12171
|
+
result *= sunInView(aSun, aOc, Math.acos(cosSep));
|
|
12172
|
+
if (result === 0) return 0;
|
|
12006
12173
|
}
|
|
12007
12174
|
return result;
|
|
12008
12175
|
}
|
|
@@ -12272,12 +12439,20 @@ function fetchBundleMeta(slug) {
|
|
|
12272
12439
|
if (!p) {
|
|
12273
12440
|
p = fetch(versionedUrl(`/v1/models/${slug}/metadata.json`, "models")).then((r) => r.json());
|
|
12274
12441
|
_bundleMetaCache.set(slug, p);
|
|
12442
|
+
p.catch(() => _bundleMetaCache.delete(slug));
|
|
12275
12443
|
}
|
|
12276
12444
|
return p;
|
|
12277
12445
|
}
|
|
12446
|
+
function allowedTier(meta) {
|
|
12447
|
+
if (textureAllowed(meta.exports.high.distribution)) return "high";
|
|
12448
|
+
if (meta.exports.low && textureAllowed(meta.exports.low.distribution)) return "low";
|
|
12449
|
+
}
|
|
12278
12450
|
function shapeModelCredit(meta) {
|
|
12279
12451
|
return meta.credit ?? meta.exports.high.credit;
|
|
12280
12452
|
}
|
|
12453
|
+
function modelTierCredit(meta, tier) {
|
|
12454
|
+
return meta.exports[tier]?.credit ?? meta.exports.high.credit;
|
|
12455
|
+
}
|
|
12281
12456
|
function modelUnitScene(bo) {
|
|
12282
12457
|
return bo.modelUnitOverride ?? bo.radiusScene;
|
|
12283
12458
|
}
|
|
@@ -12332,22 +12507,21 @@ async function loadSpacecraftModel(bo, modelScene, ctx) {
|
|
|
12332
12507
|
if ((bo.modelLoadEpoch ?? 0) !== epoch) return;
|
|
12333
12508
|
if (bo.mesh) bo.mesh.visible = false;
|
|
12334
12509
|
setHaloLoading(bo, true);
|
|
12335
|
-
const
|
|
12336
|
-
const
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
} catch (e) {
|
|
12341
|
-
console.warn(`Failed to load model metadata for ${slug}:`, e);
|
|
12510
|
+
const meta = await fetchBundleMeta(slug);
|
|
12511
|
+
const tier = allowedTier(meta);
|
|
12512
|
+
if (!tier) {
|
|
12513
|
+
bo.noPhysical = "model";
|
|
12514
|
+
return;
|
|
12342
12515
|
}
|
|
12516
|
+
const gltf = await _loader.loadAsync(versionedUrl(`/v1/models/${slug}/${tier}.glb`, "models"));
|
|
12343
12517
|
if ((bo.modelLoadEpoch ?? 0) !== epoch || !bo.mesh) {
|
|
12344
12518
|
disposeGltf(gltf.scene);
|
|
12345
12519
|
return;
|
|
12346
12520
|
}
|
|
12347
|
-
const baseFrame = meta
|
|
12521
|
+
const baseFrame = meta.frame_map ? frameMapQuaternion(meta.frame_map) : null;
|
|
12348
12522
|
bo.body.modelBaseFrame = baseFrame ?? void 0;
|
|
12349
12523
|
const root = withBaseFrame(gltf.scene, baseFrame);
|
|
12350
|
-
const anchor = meta
|
|
12524
|
+
const anchor = meta.model_anchor ? new Vector3(...meta.model_anchor) : null;
|
|
12351
12525
|
fitToUnitRadius(root, anchor && baseFrame ? anchor.applyQuaternion(baseFrame) : anchor);
|
|
12352
12526
|
enableShadows(root);
|
|
12353
12527
|
modelScene.add(root);
|
|
@@ -12355,20 +12529,19 @@ async function loadSpacecraftModel(bo, modelScene, ctx) {
|
|
|
12355
12529
|
bo.model = root;
|
|
12356
12530
|
bo.modelName = slug;
|
|
12357
12531
|
setHaloLoading(bo, false);
|
|
12358
|
-
if (meta) {
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
bo.modelUnitOverride = meta.body_span_ratio ? kmToScene(spanKm) : void 0;
|
|
12364
|
-
}
|
|
12365
|
-
ctx?.credits.registerModel({
|
|
12366
|
-
bodyId: bo.body.data.id,
|
|
12367
|
-
source: meta.exports.high.credit.url,
|
|
12368
|
-
organisation: meta.exports.high.credit.name,
|
|
12369
|
-
license: meta.exports.high.credit.license
|
|
12370
|
-
});
|
|
12532
|
+
if (meta.scale_meters) {
|
|
12533
|
+
const spanKm = meta.scale_meters / 2e3;
|
|
12534
|
+
bo.body.data.radiusKm = spanKm * (meta.body_span_ratio ?? 1);
|
|
12535
|
+
bo.radiusScene = kmToScene(effectiveRadiusKm(bo.body.data));
|
|
12536
|
+
bo.modelUnitOverride = meta.body_span_ratio ? kmToScene(spanKm) : void 0;
|
|
12371
12537
|
}
|
|
12538
|
+
const credit = modelTierCredit(meta, tier);
|
|
12539
|
+
ctx?.credits.registerModel({
|
|
12540
|
+
bodyId: bo.body.data.id,
|
|
12541
|
+
source: credit.url,
|
|
12542
|
+
organisation: credit.name,
|
|
12543
|
+
license: credit.license
|
|
12544
|
+
});
|
|
12372
12545
|
} finally {
|
|
12373
12546
|
if (!bo.model) setHaloLoading(bo, false);
|
|
12374
12547
|
}
|
|
@@ -15162,8 +15335,11 @@ function sunPathTransmittance(params, camRelPos, sunDir, planetRadiusScene, plan
|
|
|
15162
15335
|
var SUN_HDR_MULTIPLIER = 6;
|
|
15163
15336
|
var LIMB_DISC_AVG = .8;
|
|
15164
15337
|
var STAR_POINT_SIZE_PX = 4;
|
|
15165
|
-
var
|
|
15166
|
-
var
|
|
15338
|
+
var STAR_POINT_LIGHT = 6 * LIMB_DISC_AVG * Math.PI * (4 / 2) ** 2;
|
|
15339
|
+
var STAR_POINT_FLOOR = .9;
|
|
15340
|
+
var STAR_POINT_SEAT_BIAS = .125;
|
|
15341
|
+
var CORONA_OPACITY = .6;
|
|
15342
|
+
var CORONA_WHOLE = 1.5;
|
|
15167
15343
|
function makeGlowTexture(color, size = 256) {
|
|
15168
15344
|
const canvas = document.createElement("canvas");
|
|
15169
15345
|
canvas.width = size;
|
|
@@ -15244,7 +15420,7 @@ function makeStarGlow(radius, color) {
|
|
|
15244
15420
|
map: makeGlowTexture(color.startsWith("#") ? hexToRgb(color) : color),
|
|
15245
15421
|
blending: AdditiveBlending,
|
|
15246
15422
|
transparent: true,
|
|
15247
|
-
opacity:
|
|
15423
|
+
opacity: CORONA_OPACITY,
|
|
15248
15424
|
depthWrite: false,
|
|
15249
15425
|
depthTest: true
|
|
15250
15426
|
}));
|
|
@@ -15252,23 +15428,44 @@ function makeStarGlow(radius, color) {
|
|
|
15252
15428
|
corona.scale.set(glowSize, glowSize, 1);
|
|
15253
15429
|
return corona;
|
|
15254
15430
|
}
|
|
15255
|
-
function
|
|
15431
|
+
function dotPixels(size) {
|
|
15432
|
+
const reach = Math.ceil(size / 2);
|
|
15433
|
+
let count = 0;
|
|
15434
|
+
for (let y = -reach; y < reach; y++) for (let x = -reach; x < reach; x++) if ((x + .5) ** 2 + (y + .5) ** 2 <= size * size / 4) count++;
|
|
15435
|
+
return count;
|
|
15436
|
+
}
|
|
15437
|
+
function makeStarPoint(color) {
|
|
15256
15438
|
const geometry = new BufferGeometry();
|
|
15257
15439
|
geometry.setAttribute("position", new Float32BufferAttribute(new Float32Array(3), 3));
|
|
15258
15440
|
const c = new Color(color);
|
|
15441
|
+
const disc = new Color(PHOTOSPHERE_CENTRE_COLOR);
|
|
15442
|
+
const viewport = new Vector4(0, 0, 1, 1);
|
|
15443
|
+
const size = 4 .toFixed(1);
|
|
15444
|
+
const uniforms = {
|
|
15445
|
+
uColor: { value: new Vector3(c.r, c.g, c.b) },
|
|
15446
|
+
uDiscColor: { value: new Vector3(disc.r, disc.g, disc.b) },
|
|
15447
|
+
uFlux: { value: 1 },
|
|
15448
|
+
uPixelRatio: { value: 1 },
|
|
15449
|
+
uPixels: { value: dotPixels(4) },
|
|
15450
|
+
uViewport: { value: viewport },
|
|
15451
|
+
uTint: { value: new Vector3(1, 1, 1) }
|
|
15452
|
+
};
|
|
15259
15453
|
const points = new Points(geometry, new ShaderMaterial({
|
|
15260
|
-
uniforms
|
|
15261
|
-
uColor: { value: new Vector3(c.r, c.g, c.b) },
|
|
15262
|
-
uIntensity: { value: STAR_POINT_HANDOFF_INTENSITY },
|
|
15263
|
-
uTint: { value: new Vector3(1, 1, 1) },
|
|
15264
|
-
uMap: { value: circleTexture }
|
|
15265
|
-
},
|
|
15454
|
+
uniforms,
|
|
15266
15455
|
vertexShader: `
|
|
15267
15456
|
#include <common>
|
|
15268
15457
|
#include <logdepthbuf_pars_vertex>
|
|
15458
|
+
uniform float uPixelRatio;
|
|
15459
|
+
uniform vec4 uViewport;
|
|
15460
|
+
varying vec2 vSeat;
|
|
15269
15461
|
void main() {
|
|
15270
15462
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
15271
|
-
|
|
15463
|
+
vec2 halfView = 0.5 * uViewport.zw;
|
|
15464
|
+
vec2 seat = floor((gl_Position.xy / gl_Position.w + 1.0) * halfView + ${(.5 + STAR_POINT_SEAT_BIAS).toFixed(3)});
|
|
15465
|
+
gl_Position.xy = (seat / halfView - 1.0) * gl_Position.w;
|
|
15466
|
+
vSeat = uViewport.xy + seat;
|
|
15467
|
+
// A margin: the square of the point must not cut the dot.
|
|
15468
|
+
gl_PointSize = ${size} * uPixelRatio + 2.0;
|
|
15272
15469
|
#include <logdepthbuf_vertex>
|
|
15273
15470
|
}
|
|
15274
15471
|
`,
|
|
@@ -15276,14 +15473,35 @@ function makeStarPoint(color, circleTexture) {
|
|
|
15276
15473
|
#include <common>
|
|
15277
15474
|
#include <logdepthbuf_pars_fragment>
|
|
15278
15475
|
uniform vec3 uColor;
|
|
15279
|
-
uniform
|
|
15476
|
+
uniform vec3 uDiscColor;
|
|
15477
|
+
uniform float uFlux;
|
|
15478
|
+
uniform float uPixelRatio;
|
|
15479
|
+
uniform float uPixels;
|
|
15280
15480
|
uniform vec3 uTint;
|
|
15281
|
-
|
|
15481
|
+
varying vec2 vSeat;
|
|
15282
15482
|
void main() {
|
|
15283
15483
|
#include <logdepthbuf_fragment>
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15484
|
+
vec2 p = gl_FragCoord.xy - vSeat;
|
|
15485
|
+
float radius = 0.5 * ${size} * uPixelRatio;
|
|
15486
|
+
if (dot(p, p) > radius * radius) discard;
|
|
15487
|
+
// The disc's colour at the handoff, so the glow keeps it there.
|
|
15488
|
+
vec3 colour = uTint * mix(uColor, uDiscColor, uFlux);
|
|
15489
|
+
float luma = max(luminance(colour), 0.0001);
|
|
15490
|
+
// What the bloom takes for sure, and what it leaves for sure.
|
|
15491
|
+
float over = ${(1 * 1.05).toFixed(2)} / luma;
|
|
15492
|
+
float under = ${(1 * .95).toFixed(2)} / luma;
|
|
15493
|
+
// The disc's light at the handoff, on all the pixels. With too little
|
|
15494
|
+
// for all, the four at the centre have it: the glow goes on for a
|
|
15495
|
+
// fainter star.
|
|
15496
|
+
float all = ${STAR_POINT_LIGHT.toFixed(3)} * uFlux * uPixelRatio * uPixelRatio / uPixels;
|
|
15497
|
+
float four = all * uPixels / 4.0;
|
|
15498
|
+
float value = clamp(all, ${STAR_POINT_FLOOR.toFixed(1)}, under);
|
|
15499
|
+
if (all >= over) {
|
|
15500
|
+
value = all;
|
|
15501
|
+
} else if (four >= over && max(abs(p.x), abs(p.y)) < 1.0) {
|
|
15502
|
+
value = four;
|
|
15503
|
+
}
|
|
15504
|
+
gl_FragColor = vec4(colour * value, 1.0);
|
|
15287
15505
|
}
|
|
15288
15506
|
`,
|
|
15289
15507
|
transparent: true,
|
|
@@ -15291,14 +15509,22 @@ function makeStarPoint(color, circleTexture) {
|
|
|
15291
15509
|
depthWrite: false
|
|
15292
15510
|
}));
|
|
15293
15511
|
points.frustumCulled = false;
|
|
15512
|
+
points.onBeforeRender = (renderer) => {
|
|
15513
|
+
renderer.getCurrentViewport(viewport).floor();
|
|
15514
|
+
const pixelRatio = renderer.getPixelRatio();
|
|
15515
|
+
if (pixelRatio !== uniforms.uPixelRatio.value) {
|
|
15516
|
+
uniforms.uPixelRatio.value = pixelRatio;
|
|
15517
|
+
uniforms.uPixels.value = dotPixels(4 * pixelRatio);
|
|
15518
|
+
}
|
|
15519
|
+
};
|
|
15294
15520
|
return points;
|
|
15295
15521
|
}
|
|
15296
|
-
function buildStarExtras(root, radius, color
|
|
15522
|
+
function buildStarExtras(root, radius, color) {
|
|
15297
15523
|
const light = new PointLight(16777215, SUN_LIGHT_INTENSITY, 0, 0);
|
|
15298
15524
|
root.add(light);
|
|
15299
15525
|
const corona = makeStarGlow(radius, color);
|
|
15300
15526
|
root.add(corona);
|
|
15301
|
-
const starPoint = makeStarPoint(color
|
|
15527
|
+
const starPoint = makeStarPoint(color);
|
|
15302
15528
|
root.add(starPoint);
|
|
15303
15529
|
return {
|
|
15304
15530
|
light,
|
|
@@ -15310,6 +15536,12 @@ function applyStarTint(corona, starPoint, tint) {
|
|
|
15310
15536
|
if (corona) corona.material.color.setRGB(tint.x, tint.y, tint.z);
|
|
15311
15537
|
if (starPoint) starPoint.material.uniforms.uTint.value.copy(tint);
|
|
15312
15538
|
}
|
|
15539
|
+
function fadeStarCorona(corona, t) {
|
|
15540
|
+
if (!corona) return;
|
|
15541
|
+
const part = Math.min(Math.max((t - 1) / (CORONA_WHOLE - 1), 0), 1);
|
|
15542
|
+
corona.visible = part > 0;
|
|
15543
|
+
corona.material.opacity = CORONA_OPACITY * part;
|
|
15544
|
+
}
|
|
15313
15545
|
function starViewTintUniforms(material) {
|
|
15314
15546
|
const uniforms = material?.uniforms;
|
|
15315
15547
|
return uniforms?.uAtmoTEnable ? uniforms : null;
|
|
@@ -15729,7 +15961,8 @@ function createStore(tileSize, layers, renderer) {
|
|
|
15729
15961
|
texture.type = UnsignedByteType;
|
|
15730
15962
|
texture.colorSpace = SRGBColorSpace;
|
|
15731
15963
|
texture.wrapS = texture.wrapT = RepeatWrapping;
|
|
15732
|
-
texture.
|
|
15964
|
+
texture.magFilter = LinearFilter;
|
|
15965
|
+
texture.minFilter = LinearMipmapLinearFilter;
|
|
15733
15966
|
texture.generateMipmaps = false;
|
|
15734
15967
|
texture.anisotropy = Math.min(MAX_ANISOTROPY, renderer.capabilities.getMaxAnisotropy());
|
|
15735
15968
|
texture.needsUpdate = true;
|
|
@@ -15881,7 +16114,7 @@ function disposeTileOverlay(bo) {
|
|
|
15881
16114
|
}
|
|
15882
16115
|
function tilesServe(bo) {
|
|
15883
16116
|
const tiles = bo.surfaceTiles;
|
|
15884
|
-
return !!tiles && tiles.max_level >= 3 && tilesBase() !== "" && maxTileWindows() > 0 && !bo.tileOverlay?.dead && textureAllowed(tiles.distribution);
|
|
16117
|
+
return !!tiles && !bo.model && tiles.max_level >= 3 && tilesBase() !== "" && maxTileWindows() > 0 && !bo.tileOverlay?.dead && textureAllowed(tiles.distribution);
|
|
15885
16118
|
}
|
|
15886
16119
|
var inverse = new Matrix4();
|
|
15887
16120
|
var over = new Vector3();
|
|
@@ -15965,7 +16198,7 @@ function disposeMaterial(mat) {
|
|
|
15965
16198
|
}
|
|
15966
16199
|
var STAR_SPHERE_SEGMENTS = 96;
|
|
15967
16200
|
var BODY_SPHERE_SEGMENTS = 24;
|
|
15968
|
-
function buildMajorBodies(bodies, scene, clickables, meshToBody, bodyObjects,
|
|
16201
|
+
function buildMajorBodies(bodies, scene, clickables, meshToBody, bodyObjects, rendererElement, handleFocus, onHoverChange, halfPromoteIds) {
|
|
15969
16202
|
for (const body of bodies) {
|
|
15970
16203
|
const id = body.data.id;
|
|
15971
16204
|
const isHalfPromoted = halfPromoteIds?.has(id) ?? false;
|
|
@@ -15987,7 +16220,7 @@ function buildMajorBodies(bodies, scene, clickables, meshToBody, bodyObjects, ci
|
|
|
15987
16220
|
let atmosphere = null;
|
|
15988
16221
|
let sunTint;
|
|
15989
16222
|
if (!isVirtual && !noPhysical) {
|
|
15990
|
-
if (isStar) starExtras = buildStarExtras(root, radius, color
|
|
16223
|
+
if (isStar) starExtras = buildStarExtras(root, radius, color);
|
|
15991
16224
|
const segments = isStar ? STAR_SPHERE_SEGMENTS : BODY_SPHERE_SEGMENTS;
|
|
15992
16225
|
const geometry = new SphereGeometry(radius, segments, segments);
|
|
15993
16226
|
const material = isStar ? makeStarSurfaceMaterial() : new MeshStandardMaterial({ color: bodyMeshColor(body.data) });
|
|
@@ -17032,30 +17265,31 @@ async function fetchPositions(bodyId) {
|
|
|
17032
17265
|
if (res.status === 404) return [];
|
|
17033
17266
|
throw new Error(`fetchPositions: ${url} returned ${res.status} ${res.statusText}`);
|
|
17034
17267
|
}
|
|
17035
|
-
|
|
17036
|
-
return parseNomenclature(await new Response(res.body.pipeThrough(ds)).arrayBuffer());
|
|
17268
|
+
return parseNomenclature(await gunzip(res).arrayBuffer());
|
|
17037
17269
|
}
|
|
17038
|
-
async function
|
|
17270
|
+
async function fetchLabelText(bodyId, lang) {
|
|
17039
17271
|
const url = versionedUrl(`/v1/nomenclature/labels/${lang}/${bodyId}.txt.gz`, "nomenclature");
|
|
17040
17272
|
const res = await fetch(url);
|
|
17041
17273
|
if (!res.ok) {
|
|
17042
|
-
if (res.status === 404) return
|
|
17274
|
+
if (res.status === 404) return "";
|
|
17043
17275
|
throw new Error(`fetchNomenclatureLabels: ${url} returned ${res.status} ${res.statusText}`);
|
|
17044
17276
|
}
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17277
|
+
return gunzip(res).text();
|
|
17278
|
+
}
|
|
17279
|
+
function nameFeatures(records, labelText) {
|
|
17280
|
+
const labels = labelText === "" ? [] : labelText.split("\n");
|
|
17281
|
+
return records.map((rec, i) => ({
|
|
17282
|
+
...rec,
|
|
17283
|
+
name: labels[i] ?? `Feature ${rec.featureId}`
|
|
17284
|
+
}));
|
|
17048
17285
|
}
|
|
17049
17286
|
function fetchBodyNomenclature(bodyId, lang = getLocale()) {
|
|
17050
17287
|
const key = `${bodyId}:${lang}`;
|
|
17051
17288
|
let p = cache$1.get(key);
|
|
17052
17289
|
if (!p) {
|
|
17053
17290
|
p = (async () => {
|
|
17054
|
-
const [records, labels] = await Promise.all([fetchPositions(bodyId),
|
|
17055
|
-
return records
|
|
17056
|
-
...rec,
|
|
17057
|
-
name: labels[i] ?? `Feature ${rec.featureId}`
|
|
17058
|
-
}));
|
|
17291
|
+
const [records, labels] = await Promise.all([fetchPositions(bodyId), fetchLabelText(bodyId, lang)]);
|
|
17292
|
+
return nameFeatures(records, labels);
|
|
17059
17293
|
})();
|
|
17060
17294
|
cache$1.set(key, p);
|
|
17061
17295
|
p.catch(() => {
|
|
@@ -20464,18 +20698,15 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
20464
20698
|
isClose = false;
|
|
20465
20699
|
}
|
|
20466
20700
|
if (trail && (!drawn || body.orbitCenter === null)) trail.visible = false;
|
|
20701
|
+
let starIsPoint = false;
|
|
20467
20702
|
if (bo.starPoint && drawn) {
|
|
20468
|
-
const
|
|
20469
|
-
|
|
20470
|
-
bo.starPoint.visible =
|
|
20471
|
-
if (
|
|
20472
|
-
|
|
20473
|
-
const t = screenR / STAR_POINT_HANDOFF_R;
|
|
20474
|
-
const intensity = Math.max(STAR_POINT_HANDOFF_INTENSITY * t * t, 3);
|
|
20475
|
-
material.uniforms.uIntensity.value = intensity;
|
|
20476
|
-
}
|
|
20703
|
+
const t = bo.radiusScene / dist * projScale / STAR_POINT_HANDOFF_R;
|
|
20704
|
+
starIsPoint = t < 1;
|
|
20705
|
+
bo.starPoint.visible = starIsPoint;
|
|
20706
|
+
if (starIsPoint) bo.starPoint.material.uniforms.uFlux.value = t * t;
|
|
20707
|
+
fadeStarCorona(bo.corona, t);
|
|
20477
20708
|
group.visible = true;
|
|
20478
|
-
isClose = !
|
|
20709
|
+
isClose = !starIsPoint;
|
|
20479
20710
|
showLabel = !isClose;
|
|
20480
20711
|
}
|
|
20481
20712
|
if (bo.isMinor && !isFocused && trail && !ctx.visibility.isPinned(body.data.id)) trail.visible = false;
|
|
@@ -20491,7 +20722,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
|
|
|
20491
20722
|
label.element.remove();
|
|
20492
20723
|
} else if (group.visible && label && label.parent !== group) group.add(label);
|
|
20493
20724
|
if (bo.mesh && bo.radiusScene > 0) {
|
|
20494
|
-
const subPixel = !isFocused && !isSystemRoot && bo.radiusScene / dist * projScale < .5;
|
|
20725
|
+
const subPixel = starIsPoint || !isFocused && !isSystemRoot && bo.radiusScene / dist * projScale < .5;
|
|
20495
20726
|
if (subPixel !== bo.meshSubPixel) {
|
|
20496
20727
|
bo.meshSubPixel = subPixel;
|
|
20497
20728
|
if (subPixel) bo.mesh.layers.disable(0);
|
|
@@ -22831,7 +23062,7 @@ function updateTextureLOD(bodyObjects, camera, renderer, ctx, textureLoader, foc
|
|
|
22831
23062
|
const target = wantsUpgrade ? highestAvailableTier(desiredRank, bo.availableTiers) : bo.textureTier;
|
|
22832
23063
|
if (target) loadBodyTextureTier(bo, target, desiredFrame, textureLoader);
|
|
22833
23064
|
}
|
|
22834
|
-
updateTileOverlay(bo, camera, renderer, projScale * renderer.getPixelRatio(), settings.showSurfaceTexture && !bo.appearance?.surfaceOwned
|
|
23065
|
+
updateTileOverlay(bo, camera, renderer, projScale * renderer.getPixelRatio(), settings.showSurfaceTexture && !bo.appearance?.surfaceOwned, invalidate);
|
|
22835
23066
|
const dispTiers = bo.displacementMeta?.tiers;
|
|
22836
23067
|
if (bo.displacementMap && dispTiers?.length && !bo.displacementLoading) {
|
|
22837
23068
|
const maxTex = renderer.capabilities.maxTextureSize;
|
|
@@ -23184,7 +23415,7 @@ var PromotionRegistry = class {
|
|
|
23184
23415
|
this.applyGroupFilter(deps.ctx.earthSatFilter);
|
|
23185
23416
|
deps.ctx.onSmallBodyFilterChange((f) => this.applySmallBodyFilter(f));
|
|
23186
23417
|
this.applySmallBodyFilter(deps.ctx.smallBodyFilter);
|
|
23187
|
-
fetchLabels
|
|
23418
|
+
fetchLabels().then((labels) => {
|
|
23188
23419
|
const alreadyLoaded = [];
|
|
23189
23420
|
const add = (id) => {
|
|
23190
23421
|
if (!this.defaults.add(id)) return;
|
|
@@ -23250,14 +23481,14 @@ var PromotionRegistry = class {
|
|
|
23250
23481
|
this.finalizeBuilds();
|
|
23251
23482
|
}
|
|
23252
23483
|
buildBodyInstance(body, halfPromoteIds, auto = false) {
|
|
23253
|
-
const { bodyObjects, ctx, clock, scene, clickables, meshToBody,
|
|
23484
|
+
const { bodyObjects, ctx, clock, scene, clickables, meshToBody, renderer } = this.deps;
|
|
23254
23485
|
if (bodyObjects.has(body.data.id)) return false;
|
|
23255
23486
|
if (!body.orbitElements && !body.data.pageOnly && body.data.orbitalSource !== OrbitalSource.SPICE_PROBE) {
|
|
23256
23487
|
body.orbitElements = body.data;
|
|
23257
23488
|
body.orbitCenterId = body.data.parentId;
|
|
23258
23489
|
}
|
|
23259
23490
|
ctx.place(body, clock.jd);
|
|
23260
|
-
buildMajorBodies([body], scene, clickables, meshToBody, bodyObjects,
|
|
23491
|
+
buildMajorBodies([body], scene, clickables, meshToBody, bodyObjects, renderer.domElement, this.deps.onBodyClick, (id, hovered) => hovered ? this.deps.hoveredBodyIds.add(id) : this.deps.hoveredBodyIds.delete(id), halfPromoteIds);
|
|
23261
23492
|
const bo = bodyObjects.get(body.data.id);
|
|
23262
23493
|
if (bo && !body.data.name) loadBodyLabel(bo);
|
|
23263
23494
|
if (body.data.objectType === ObjectType.SPACECRAFT) ctx.bodies.dirtySpacecraftGroups.add(body.data.parentId);
|
|
@@ -23462,7 +23693,7 @@ var FocusController = class {
|
|
|
23462
23693
|
_tmpV3 = new Vector3();
|
|
23463
23694
|
pendingInitialView = null;
|
|
23464
23695
|
upgradeTargetIds = /* @__PURE__ */ new Set();
|
|
23465
|
-
constructor(deps, initialFocus, hoveredBodyIds,
|
|
23696
|
+
constructor(deps, initialFocus, hoveredBodyIds, renderer) {
|
|
23466
23697
|
this.deps = deps;
|
|
23467
23698
|
this.focusedBody = initialFocus;
|
|
23468
23699
|
this.promotion = new PromotionRegistry({
|
|
@@ -23471,7 +23702,6 @@ var FocusController = class {
|
|
|
23471
23702
|
ctx: deps.ctx,
|
|
23472
23703
|
clock: deps.clock,
|
|
23473
23704
|
renderer,
|
|
23474
|
-
circleTexture,
|
|
23475
23705
|
clickables: deps.clickables,
|
|
23476
23706
|
meshToBody: deps.meshToBody,
|
|
23477
23707
|
hoveredBodyIds,
|
|
@@ -24332,7 +24562,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24332
24562
|
loadTexture: (b) => this.maybeLoadTexture(b),
|
|
24333
24563
|
repositionAll: () => this.repositionAll(),
|
|
24334
24564
|
assignMapLayerToTrails: () => this.assignMapLayerToTrails()
|
|
24335
|
-
}, focusBody, this.hoveredBodyIds, this.
|
|
24565
|
+
}, focusBody, this.hoveredBodyIds, this.renderer);
|
|
24336
24566
|
this.coverageWatch = new ProbeCoverageWatch(clock, callbacks.notices);
|
|
24337
24567
|
const framing = focusBody?.data.id === initialView.id ? initialView : {
|
|
24338
24568
|
latitude: 45,
|
|
@@ -24366,7 +24596,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
24366
24596
|
this.tick();
|
|
24367
24597
|
}
|
|
24368
24598
|
buildScene() {
|
|
24369
|
-
buildMajorBodies(this.ctx.bodies.majorBodies, this.scene, this.clickables, this.meshToBody, this.bodyObjects, this.
|
|
24599
|
+
buildMajorBodies(this.ctx.bodies.majorBodies, this.scene, this.clickables, this.meshToBody, this.bodyObjects, this.renderer.domElement, (body) => this.focusController.handleFocus(body), (id, hovered) => {
|
|
24370
24600
|
if (hovered) this.hoveredBodyIds.add(id);
|
|
24371
24601
|
else this.hoveredBodyIds.delete(id);
|
|
24372
24602
|
this.invalidate();
|
|
@@ -25213,7 +25443,7 @@ var SceneRenderer = class SceneRenderer {
|
|
|
25213
25443
|
const systemId = this.ctx.visibility.focusedSystemId;
|
|
25214
25444
|
const seatKey = this.focusedSeatConfigKey();
|
|
25215
25445
|
const probeVersion = this.ctx.probeStore?.version ?? 0;
|
|
25216
|
-
const dataVersion = this.ctx.bodies.dataVersion + (this.ctx.chebStore?.version ?? 0);
|
|
25446
|
+
const dataVersion = this.ctx.bodies.dataVersion + (this.ctx.chebStore?.version ?? 0) + (this.ctx.refresher?.wakes ?? 0);
|
|
25217
25447
|
if (this.clock.jd === this.lastUpdatedJd && systemId === this.lastUpdatedSystemId && seatKey === this.lastSeatConfigKey && probeVersion === this.lastProbeVersion && dataVersion === this.lastDataVersion) {
|
|
25218
25448
|
this.clock.seeked = false;
|
|
25219
25449
|
return;
|
|
@@ -27829,6 +28059,43 @@ function subsolarPoint$1(id, ctx, jd) {
|
|
|
27829
28059
|
const sunCentre = bodyCentre(sun, ctx, jd);
|
|
27830
28060
|
return centre && sunCentre && subsolarAt(body, centre, sunCentre, jd);
|
|
27831
28061
|
}
|
|
28062
|
+
var SUNLIGHT_SAMPLES = 1024;
|
|
28063
|
+
var GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
|
|
28064
|
+
function sunlightAt(radiusKm, sun, occluders) {
|
|
28065
|
+
const sunKm = Math.hypot(...sun.centre);
|
|
28066
|
+
const aSun = Math.asin(Math.min(sun.radiusKm / sunKm, 1));
|
|
28067
|
+
if (!(aSun > 0)) return 1;
|
|
28068
|
+
const toSun = sun.centre.map((km) => km / sunKm);
|
|
28069
|
+
const along = (v) => v[0] * toSun[0] + v[1] * toSun[1] + v[2] * toSun[2];
|
|
28070
|
+
const near = occluders.filter(({ centre, radiusKm: r }) => {
|
|
28071
|
+
const ahead = along(centre);
|
|
28072
|
+
const aside = Math.sqrt(Math.max(Math.hypot(...centre) ** 2 - ahead ** 2, 0));
|
|
28073
|
+
return ahead > -(radiusKm + r) && aside < radiusKm + r + 2 * aSun * (ahead + radiusKm);
|
|
28074
|
+
});
|
|
28075
|
+
if (near.length === 0) return 1;
|
|
28076
|
+
const [u, v] = planeBasis(sun.centre);
|
|
28077
|
+
let sum = 0;
|
|
28078
|
+
for (let i = 0; i < SUNLIGHT_SAMPLES; i++) {
|
|
28079
|
+
const rho = Math.sqrt((i + .5) / SUNLIGHT_SAMPLES);
|
|
28080
|
+
const x = radiusKm * rho * Math.cos(i * GOLDEN_ANGLE);
|
|
28081
|
+
const y = radiusKm * rho * Math.sin(i * GOLDEN_ANGLE);
|
|
28082
|
+
const z = radiusKm * Math.sqrt(1 - rho * rho);
|
|
28083
|
+
let lit = 1;
|
|
28084
|
+
for (const { centre, radiusKm: r } of near) {
|
|
28085
|
+
const to = [
|
|
28086
|
+
centre[0] - x * u[0] - y * v[0] - z * toSun[0],
|
|
28087
|
+
centre[1] - x * u[1] - y * v[1] - z * toSun[1],
|
|
28088
|
+
centre[2] - x * u[2] - y * v[2] - z * toSun[2]
|
|
28089
|
+
];
|
|
28090
|
+
const km = Math.hypot(...to);
|
|
28091
|
+
const sep = Math.acos(Math.max(-1, Math.min(1, along(to) / km)));
|
|
28092
|
+
lit *= sunInView(aSun, Math.asin(Math.min(r / km, 1)), sep);
|
|
28093
|
+
if (lit === 0) break;
|
|
28094
|
+
}
|
|
28095
|
+
sum += lit;
|
|
28096
|
+
}
|
|
28097
|
+
return sum / SUNLIGHT_SAMPLES;
|
|
28098
|
+
}
|
|
27832
28099
|
|
|
27833
28100
|
//#endregion
|
|
27834
28101
|
//#region src/lib/interaction/gesture.ts
|
|
@@ -28422,6 +28689,7 @@ var SpaceMap = class {
|
|
|
28422
28689
|
longitude: target.lon
|
|
28423
28690
|
})
|
|
28424
28691
|
});
|
|
28692
|
+
if (arrival.outcome === "failed") throw arrival.error;
|
|
28425
28693
|
return this.settled(arrival.flightMs);
|
|
28426
28694
|
}
|
|
28427
28695
|
async goTo(id, options = {}) {
|
|
@@ -28454,11 +28722,24 @@ var SpaceMap = class {
|
|
|
28454
28722
|
}
|
|
28455
28723
|
}
|
|
28456
28724
|
let renderer;
|
|
28725
|
+
let loadError;
|
|
28726
|
+
const note = (error) => {
|
|
28727
|
+
loadError ??= error;
|
|
28728
|
+
};
|
|
28729
|
+
const failed = () => {
|
|
28730
|
+
console.warn(`[map] goTo ${id}: its data did not load:`, loadError);
|
|
28731
|
+
return {
|
|
28732
|
+
outcome: "failed",
|
|
28733
|
+
error: loadError,
|
|
28734
|
+
clockMoved,
|
|
28735
|
+
flightMs: 0
|
|
28736
|
+
};
|
|
28737
|
+
};
|
|
28457
28738
|
try {
|
|
28458
28739
|
await this.open(id).catch(() => {});
|
|
28459
28740
|
const date = jdToDate(this.clock.jd);
|
|
28460
|
-
await this.ctx.ready(date);
|
|
28461
|
-
await this.ctx.ensureBody(id, date);
|
|
28741
|
+
await this.ctx.ready(date).catch(note);
|
|
28742
|
+
await this.ctx.ensureBody(id, date).catch(note);
|
|
28462
28743
|
renderer = await this.rendering();
|
|
28463
28744
|
} finally {
|
|
28464
28745
|
release();
|
|
@@ -28466,6 +28747,7 @@ var SpaceMap = class {
|
|
|
28466
28747
|
if (superseded()) return gone;
|
|
28467
28748
|
const body = this.ctx.getBody(id);
|
|
28468
28749
|
if (!body || !renderer) {
|
|
28750
|
+
if (loadError !== void 0) return failed();
|
|
28469
28751
|
console.warn(`[map] goTo ${id}: not an object of this map`);
|
|
28470
28752
|
return {
|
|
28471
28753
|
outcome: "missing",
|
|
@@ -28473,8 +28755,14 @@ var SpaceMap = class {
|
|
|
28473
28755
|
flightMs: 0
|
|
28474
28756
|
};
|
|
28475
28757
|
}
|
|
28758
|
+
const placed = renderer.placeOf(body) !== null;
|
|
28759
|
+
if (!placed && loadError !== void 0) {
|
|
28760
|
+
windows ??= await coverage();
|
|
28761
|
+
if (superseded()) return gone;
|
|
28762
|
+
if (nearestCoveredJd(windows, this.clock.jd) === this.clock.jd) return failed();
|
|
28763
|
+
}
|
|
28476
28764
|
options.commit?.(body);
|
|
28477
|
-
if (
|
|
28765
|
+
if (placed) {
|
|
28478
28766
|
const view = options.view?.(body) ?? {};
|
|
28479
28767
|
const distance = view.distance ?? (body.data.radiusKm > 0 ? this.framing({}, id) : distanceBefore);
|
|
28480
28768
|
let flightMs = 0;
|
|
@@ -32578,7 +32866,6 @@ var SystemMap = class {
|
|
|
32578
32866
|
|
|
32579
32867
|
//#endregion
|
|
32580
32868
|
//#region src/lib/scene/state/attribution.ts
|
|
32581
|
-
var DEFAULT_ORBIT_LABELS = Object.fromEntries(ORBIT_SOURCE_ORDER.map((s) => [s, ORBIT_SOURCES[s].label]));
|
|
32582
32869
|
function feedsFocusedSystem(ctx, source) {
|
|
32583
32870
|
const parents = ctx.credits.orbitSourceParents.get(source);
|
|
32584
32871
|
if (!parents) return true;
|
|
@@ -32586,22 +32873,28 @@ function feedsFocusedSystem(ctx, source) {
|
|
|
32586
32873
|
for (const parent of parents) if (ctx.bodies.isInSystem(parent, sysId)) return true;
|
|
32587
32874
|
return false;
|
|
32588
32875
|
}
|
|
32589
|
-
function
|
|
32590
|
-
|
|
32591
|
-
|
|
32592
|
-
|
|
32593
|
-
|
|
32876
|
+
function creditedOrbitArchives(ctx) {
|
|
32877
|
+
const credits = ctx.credits;
|
|
32878
|
+
const archives = /* @__PURE__ */ new Set();
|
|
32879
|
+
for (const source of ORBIT_SOURCE_ORDER) {
|
|
32880
|
+
if (!credits.orbitSources.has(source)) continue;
|
|
32881
|
+
const { archive, scoped } = ORBIT_SOURCES[source];
|
|
32882
|
+
if (scoped && !feedsFocusedSystem(ctx, source)) continue;
|
|
32883
|
+
if (archive) archives.add(archive);
|
|
32884
|
+
else for (const a of PROBE_ARCHIVES) if (a && credits.probeArchives.has(a)) archives.add(a);
|
|
32885
|
+
}
|
|
32886
|
+
return [...archives];
|
|
32594
32887
|
}
|
|
32595
32888
|
function scopedCredits(ctx, all) {
|
|
32596
32889
|
const systemId = ctx.visibility.focusedSystemId;
|
|
32597
32890
|
const bodyId = ctx.visibility.focusedBodyId;
|
|
32598
32891
|
return [...all].filter((c) => c.bodyId === bodyId || systemId && c.systemId === systemId);
|
|
32599
32892
|
}
|
|
32600
|
-
function attributionChips(ctx, labels =
|
|
32893
|
+
function attributionChips(ctx, labels = ORBIT_ARCHIVE_ORGANISATIONS) {
|
|
32601
32894
|
const credits = ctx.credits;
|
|
32602
32895
|
credits.imageryVersion;
|
|
32603
32896
|
credits.modelVersion;
|
|
32604
|
-
const orbits = [...new Set(
|
|
32897
|
+
const orbits = [...new Set(creditedOrbitArchives(ctx).map((archive) => labels[archive]))];
|
|
32605
32898
|
const organisations = /* @__PURE__ */ new Set();
|
|
32606
32899
|
for (const credit of scopedCredits(ctx, credits.imagery.values())) organisations.add(credit.organisation);
|
|
32607
32900
|
const bodyId = ctx.visibility.focusedBodyId;
|
|
@@ -32850,12 +33143,7 @@ async function open(date) {
|
|
|
32850
33143
|
const params = chebyshevZoneParams(metadata);
|
|
32851
33144
|
if (params.size > 0) chebyshev = new ChebyshevStore(params);
|
|
32852
33145
|
}
|
|
32853
|
-
|
|
32854
|
-
await chebyshev?.ensure(jd).done;
|
|
32855
|
-
} catch (error) {
|
|
32856
|
-
chebyshev = null;
|
|
32857
|
-
throw error;
|
|
32858
|
-
}
|
|
33146
|
+
await chebyshev?.load(jd);
|
|
32859
33147
|
return {
|
|
32860
33148
|
jd,
|
|
32861
33149
|
metadata,
|
|
@@ -32919,7 +33207,8 @@ async function place(id, at, above = /* @__PURE__ */ new Set()) {
|
|
|
32919
33207
|
if (!own) return null;
|
|
32920
33208
|
if (own.parentId === "naif-0") return {
|
|
32921
33209
|
data: own.data,
|
|
32922
|
-
centre: own.offset
|
|
33210
|
+
centre: own.offset,
|
|
33211
|
+
parents: []
|
|
32923
33212
|
};
|
|
32924
33213
|
const parent = await place(own.parentId, at, new Set(above).add(id));
|
|
32925
33214
|
if (!parent) return null;
|
|
@@ -32930,9 +33219,16 @@ async function place(id, at, above = /* @__PURE__ */ new Set()) {
|
|
|
32930
33219
|
x + own.offset[0],
|
|
32931
33220
|
y + own.offset[1],
|
|
32932
33221
|
z + own.offset[2]
|
|
32933
|
-
]
|
|
33222
|
+
],
|
|
33223
|
+
parents: [own.parentId, ...parent.parents]
|
|
32934
33224
|
};
|
|
32935
33225
|
}
|
|
33226
|
+
function neighbours(id, { parents }, at) {
|
|
33227
|
+
const ids = new Set(parents);
|
|
33228
|
+
for (const { body } of at.chebyshev?.bodiesAt(at.jd) ?? []) if (parents.includes(`naif-${body.parentId}`)) ids.add(body.id);
|
|
33229
|
+
ids.delete(id);
|
|
33230
|
+
return [...ids];
|
|
33231
|
+
}
|
|
32936
33232
|
async function spinOf(id) {
|
|
32937
33233
|
const [detail] = await Promise.all([fetchObjectDetail(id, false), loadSystemsGlobal()]);
|
|
32938
33234
|
const orientation = detail.global?.orientation;
|
|
@@ -32985,6 +33281,29 @@ function subsolarPoint(id, date) {
|
|
|
32985
33281
|
}, body.centre, sun.centre, at.jd);
|
|
32986
33282
|
});
|
|
32987
33283
|
}
|
|
33284
|
+
function sunlight(id, date) {
|
|
33285
|
+
return inTurn(async () => {
|
|
33286
|
+
if (id === "naif-10") return null;
|
|
33287
|
+
const at = await open(date);
|
|
33288
|
+
const body = await place(id, at);
|
|
33289
|
+
const sun = body && await place("naif-10", at);
|
|
33290
|
+
if (!body || !sun) return null;
|
|
33291
|
+
const ball = ({ data, centre }) => ({
|
|
33292
|
+
centre: sceneToEcliptic([
|
|
33293
|
+
centre[0] - body.centre[0],
|
|
33294
|
+
centre[1] - body.centre[1],
|
|
33295
|
+
centre[2] - body.centre[2]
|
|
33296
|
+
]),
|
|
33297
|
+
radiusKm: data.radiusKm
|
|
33298
|
+
});
|
|
33299
|
+
const occluders = [];
|
|
33300
|
+
for (const other of neighbours(id, body, at)) {
|
|
33301
|
+
const placed = await place(other, at);
|
|
33302
|
+
if (placed && placed.data.radiusKm > 0) occluders.push(ball(placed));
|
|
33303
|
+
}
|
|
33304
|
+
return sunlightAt(body.data.radiusKm > 0 ? body.data.radiusKm : 0, ball(sun), occluders);
|
|
33305
|
+
});
|
|
33306
|
+
}
|
|
32988
33307
|
|
|
32989
33308
|
//#endregion
|
|
32990
33309
|
//#region src/lib/fetch/panoramas.ts
|
|
@@ -33006,11 +33325,13 @@ async function fetchPanoramas(bodyId) {
|
|
|
33006
33325
|
//#endregion
|
|
33007
33326
|
//#region src/sdk/index.ts
|
|
33008
33327
|
async function createMap(options) {
|
|
33009
|
-
const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
33328
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
33010
33329
|
const element = resolveContainer(container);
|
|
33011
33330
|
applyHost({
|
|
33012
33331
|
dataUrl,
|
|
33013
33332
|
imagesUrl,
|
|
33333
|
+
liveUrl,
|
|
33334
|
+
tilesUrl,
|
|
33014
33335
|
locale,
|
|
33015
33336
|
messages,
|
|
33016
33337
|
includeNonCommercial
|
|
@@ -33031,11 +33352,13 @@ async function createMap(options) {
|
|
|
33031
33352
|
return map;
|
|
33032
33353
|
}
|
|
33033
33354
|
async function createFlatMap(options) {
|
|
33034
|
-
const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
33355
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
33035
33356
|
const element = resolveContainer(container);
|
|
33036
33357
|
applyHost({
|
|
33037
33358
|
dataUrl,
|
|
33038
33359
|
imagesUrl,
|
|
33360
|
+
liveUrl,
|
|
33361
|
+
tilesUrl,
|
|
33039
33362
|
locale,
|
|
33040
33363
|
messages,
|
|
33041
33364
|
includeNonCommercial
|
|
@@ -33050,11 +33373,13 @@ async function createFlatMap(options) {
|
|
|
33050
33373
|
return map;
|
|
33051
33374
|
}
|
|
33052
33375
|
async function createPanorama(options) {
|
|
33053
|
-
const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
33376
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
|
|
33054
33377
|
const element = resolveContainer(container);
|
|
33055
33378
|
applyHost({
|
|
33056
33379
|
dataUrl,
|
|
33057
33380
|
imagesUrl,
|
|
33381
|
+
liveUrl,
|
|
33382
|
+
tilesUrl,
|
|
33058
33383
|
locale,
|
|
33059
33384
|
messages,
|
|
33060
33385
|
includeNonCommercial
|
|
@@ -33069,11 +33394,13 @@ async function createPanorama(options) {
|
|
|
33069
33394
|
return view;
|
|
33070
33395
|
}
|
|
33071
33396
|
async function createSystemMap(options) {
|
|
33072
|
-
const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
|
|
33397
|
+
const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
|
|
33073
33398
|
const element = resolveContainer(container);
|
|
33074
33399
|
applyHost({
|
|
33075
33400
|
dataUrl,
|
|
33076
33401
|
imagesUrl,
|
|
33402
|
+
liveUrl,
|
|
33403
|
+
tilesUrl,
|
|
33077
33404
|
locale,
|
|
33078
33405
|
messages,
|
|
33079
33406
|
includeNonCommercial
|
|
@@ -33099,6 +33426,8 @@ function applyHost(options) {
|
|
|
33099
33426
|
const overrides = {};
|
|
33100
33427
|
if (options.dataUrl !== void 0) overrides.dataUrl = options.dataUrl;
|
|
33101
33428
|
if (options.imagesUrl !== void 0) overrides.imagesUrl = options.imagesUrl;
|
|
33429
|
+
if (options.liveUrl !== void 0) overrides.liveUrl = options.liveUrl;
|
|
33430
|
+
if (options.tilesUrl !== void 0) overrides.tilesUrl = options.tilesUrl;
|
|
33102
33431
|
if (options.locale !== void 0) overrides.locale = () => options.locale;
|
|
33103
33432
|
if (options.messages !== void 0) overrides.messages = options.messages;
|
|
33104
33433
|
if (options.includeNonCommercial) overrides.textures = "non-commercial";
|
|
@@ -33106,5 +33435,5 @@ function applyHost(options) {
|
|
|
33106
33435
|
}
|
|
33107
33436
|
|
|
33108
33437
|
//#endregion
|
|
33109
|
-
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, distanceKm, elements, fetchPanoramaIndex, fetchPanoramas, findPanorama, fixed, graticule, groundDistanceM, isViewable, jdToDate, kmToAu, neighboursOf, offsetKm, panoramaAt, pathFor, planeBasis, projectionAspect, samples, smallCircle, subsolarPoint, tle };
|
|
33438
|
+
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, distanceKm, elements, fetchPanoramaIndex, fetchPanoramas, findPanorama, fixed, graticule, groundDistanceM, isViewable, jdToDate, kmToAu, neighboursOf, offsetKm, panoramaAt, pathFor, planeBasis, projectionAspect, samples, smallCircle, subsolarPoint, sunlight, tle };
|
|
33110
33439
|
(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-overlay--no-halos .scene-label__halo{visibility:hidden}.scene-overlay--no-halos .scene-label{pointer-events: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;box-sizing:border-box;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;box-sizing:border-box;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);})();
|