spacemap 0.1.10 → 0.1.12

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/index.d.ts CHANGED
@@ -227,6 +227,12 @@ declare interface CommonOptions {
227
227
  * origin does not serve pictures; a mirror that serves both sets this to
228
228
  * the same place as `dataUrl`. */
229
229
  imagesUrl?: string;
230
+ /** Root of the live tracking feed, for a mirror that writes its own. */
231
+ liveUrl?: string;
232
+ /** Root of the surface tile pyramids, for a mirror that serves them. The
233
+ * production CDN serves none: without it a body draws its whole-globe
234
+ * maps alone. */
235
+ tilesUrl?: string;
230
236
  /** BCP-47 tag that picks localized names; English when omitted. */
231
237
  locale?: string;
232
238
  /** Replaces the English wording the map renders itself, one key at a time. */
@@ -2133,6 +2139,19 @@ export declare interface SpaceMapOptions {
2133
2139
  */
2134
2140
  export declare function subsolarPoint(id: string, date: Date): Promise<LonLat | null>;
2135
2141
 
2142
+ /**
2143
+ * The part of the light of the Sun that reaches `id` at `date`, from 0 to 1.
2144
+ * It is 1 in full sunlight and 0 when all of the body is in the shadow of
2145
+ * another. Between the two, a part of its sunlit face is in shadow.
2146
+ *
2147
+ * Only the bodies of its own system can be in the way. These are the body it
2148
+ * orbits, and the planet and the moons that orbit the same body. Each is a
2149
+ * ball of the radius the map draws it with. The value is a mean over 1024
2150
+ * points of the sunlit face. Null and rejection are those of
2151
+ * {@link subsolarPoint}.
2152
+ */
2153
+ export declare function sunlight(id: string, date: Date): Promise<number | null>;
2154
+
2136
2155
  /** A place on (or above) the body's surface, turning with it. */
2137
2156
  export declare interface SurfaceAnchor {
2138
2157
  body: string;
package/index.js CHANGED
@@ -3225,6 +3225,14 @@ function orbitSourceInfo(source) {
3225
3225
  return ORBIT_SOURCES[source];
3226
3226
  }
3227
3227
 
3228
+ //#endregion
3229
+ //#region src/lib/credits/tile-credit.ts
3230
+ function tileCredit(map) {
3231
+ const tiles = map.tiles;
3232
+ if (!tiles || tilesBase() === "" || !textureAllowed(tiles.distribution)) return void 0;
3233
+ return tiles.source === map.source && tiles.attribution === map.attribution ? void 0 : tiles;
3234
+ }
3235
+
3228
3236
  //#endregion
3229
3237
  //#region src/lib/scene/state/credits.svelte.ts
3230
3238
  var CreditsStore = class {
@@ -3274,8 +3282,16 @@ var CreditsStore = class {
3274
3282
  set(this.#orbitSourceParents, value, true);
3275
3283
  }
3276
3284
  registerImagery(layer, bodyId, systemId, meta) {
3277
- const sep = "\0";
3278
- const key = layer === "rings" ? `${layer}${sep}${bodyId}${sep}${meta.source}` : `${layer}${sep}${bodyId}`;
3285
+ this.addImagery(layer, bodyId, systemId, meta, layer === "rings");
3286
+ const tiles = layer === "surface" ? tileCredit(meta) : void 0;
3287
+ if (tiles) this.addImagery(layer, bodyId, systemId, tiles, true);
3288
+ }
3289
+ addImagery(layer, bodyId, systemId, meta, bySource) {
3290
+ const key = [
3291
+ layer,
3292
+ bodyId,
3293
+ ...bySource ? [meta.source] : []
3294
+ ].join("\0");
3279
3295
  if (this.imagery.has(key)) return;
3280
3296
  const { source, organisation, license, attribution, description } = meta;
3281
3297
  this.imagery.set(key, {
@@ -8967,6 +8983,12 @@ var ChunkWindow = class {
8967
8983
  done
8968
8984
  };
8969
8985
  }
8986
+ request(jd) {
8987
+ for (const [zone, params] of this.zoneParams) {
8988
+ const idx = chunkIndexForJd(params, jd);
8989
+ if (this.isLoadable(params, idx)) this.loadChunk(zone, params, idx, "high")?.catch(() => {});
8990
+ }
8991
+ }
8970
8992
  loadingAt(jd, zone) {
8971
8993
  if (this.inflight.size === 0) return false;
8972
8994
  for (const [name, params] of this.zoneParams) {
@@ -10378,7 +10400,7 @@ function dedupeById(bodies) {
10378
10400
  async function loadScene(ctx, date, targetId) {
10379
10401
  performance.mark("sm-load-start");
10380
10402
  loadProgress.reset();
10381
- loadSystemsGlobal().catch((e) => console.warn("scene-load: systems-global (GMs/nutation) failed to load:", e));
10403
+ const systemsGlobalPromise = loadSystemsGlobal().catch((e) => console.warn("scene-load: systems-global (GMs/nutation) failed to load:", e));
10382
10404
  const atmospheresPromise = loadAtmospheres().catch((e) => console.warn("scene-load: atmospheres failed to load — rendering without shells:", e));
10383
10405
  const jd = dateToJD(date);
10384
10406
  const metadataPromise = fetchMetadata();
@@ -10410,6 +10432,7 @@ async function loadScene(ctx, date, targetId) {
10410
10432
  loadProgress.reach("ephemeris");
10411
10433
  const loader = new ChunkLoader(ctx.chebStore);
10412
10434
  if (ctx.probeStore) ctx.probeStore.fitCenterUsable = (id) => (ctx.chebStore?.has(id) ?? false) || ctx.getBody(id) !== void 0;
10435
+ await systemsGlobalPromise;
10413
10436
  const major = await loadMajorBodies(ctx, loader, metadata, date, jd, await passengerPromise);
10414
10437
  loadProgress.reach("majors");
10415
10438
  await atmospheresPromise;
@@ -11952,6 +11975,23 @@ function cullOccludersFor(dst, center, radius, sunDir) {
11952
11975
  }
11953
11976
  return n;
11954
11977
  }
11978
+ function sunInView(aSun, aOc, sep) {
11979
+ if (aOc > 10 * aSun) {
11980
+ const t = (aOc - sep) / aSun;
11981
+ if (t >= 1) return 0;
11982
+ if (t <= -1) return 1;
11983
+ return 1 - (Math.acos(-t) + t * Math.sqrt(Math.max(1 - t * t, 0))) / Math.PI;
11984
+ }
11985
+ if (sep >= aSun + aOc) return 1;
11986
+ if (sep + aSun <= aOc) return 0;
11987
+ if (sep + aOc <= aSun) return 1 - aOc * aOc / (aSun * aSun);
11988
+ const a = aSun;
11989
+ const b = aOc;
11990
+ const c = sep;
11991
+ const x = (c * c + a * a - b * b) / (2 * c);
11992
+ const y = Math.sqrt(Math.max(a * a - x * x, 0));
11993
+ 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);
11994
+ }
11955
11995
  function evaluateEclipseFactor(receiverPos, selfPos) {
11956
11996
  const aSun = SHARED.uSunAngularRadius.value;
11957
11997
  if (aSun <= 0) return 1;
@@ -11974,28 +12014,8 @@ function evaluateEclipseFactor(receiverPos, selfPos) {
11974
12014
  if (dOc < 1e-5) continue;
11975
12015
  const aOc = Math.asin(Math.min(r / dOc, 1));
11976
12016
  const cosSep = Math.max(-1, Math.min(1, (sunDir.x * tx + sunDir.y * ty + sunDir.z * tz) / dOc));
11977
- const sep = Math.acos(cosSep);
11978
- if (aOc > 10 * aSun) {
11979
- const t = (aOc - sep) / aSun;
11980
- if (t >= 1) return 0;
11981
- if (t <= -1) continue;
11982
- const covered = (Math.acos(-t) + t * Math.sqrt(Math.max(1 - t * t, 0))) / Math.PI;
11983
- result *= 1 - covered;
11984
- continue;
11985
- }
11986
- if (sep >= aSun + aOc) continue;
11987
- if (sep + aSun <= aOc) return 0;
11988
- if (sep + aOc <= aSun) {
11989
- result *= 1 - aOc * aOc / (aSun * aSun);
11990
- continue;
11991
- }
11992
- const a = aSun;
11993
- const b = aOc;
11994
- const c = sep;
11995
- const x = (c * c + a * a - b * b) / (2 * c);
11996
- const y = Math.sqrt(Math.max(a * a - x * x, 0));
11997
- 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;
11998
- result *= 1 - A / (Math.PI * a * a);
12017
+ result *= sunInView(aSun, aOc, Math.acos(cosSep));
12018
+ if (result === 0) return 0;
11999
12019
  }
12000
12020
  return result;
12001
12021
  }
@@ -15722,7 +15742,8 @@ function createStore(tileSize, layers, renderer) {
15722
15742
  texture.type = UnsignedByteType;
15723
15743
  texture.colorSpace = SRGBColorSpace;
15724
15744
  texture.wrapS = texture.wrapT = RepeatWrapping;
15725
- texture.minFilter = texture.magFilter = LinearFilter;
15745
+ texture.magFilter = LinearFilter;
15746
+ texture.minFilter = LinearMipmapLinearFilter;
15726
15747
  texture.generateMipmaps = false;
15727
15748
  texture.anisotropy = Math.min(MAX_ANISOTROPY, renderer.capabilities.getMaxAnisotropy());
15728
15749
  texture.needsUpdate = true;
@@ -15874,7 +15895,7 @@ function disposeTileOverlay(bo) {
15874
15895
  }
15875
15896
  function tilesServe(bo) {
15876
15897
  const tiles = bo.surfaceTiles;
15877
- return !!tiles && tiles.max_level >= 3 && tilesBase() !== "" && maxTileWindows() > 0 && !bo.tileOverlay?.dead && textureAllowed(tiles.distribution);
15898
+ return !!tiles && !bo.model && tiles.max_level >= 3 && tilesBase() !== "" && maxTileWindows() > 0 && !bo.tileOverlay?.dead && textureAllowed(tiles.distribution);
15878
15899
  }
15879
15900
  var inverse = new Matrix4();
15880
15901
  var over = new Vector3();
@@ -21895,10 +21916,11 @@ var Placer = class {
21895
21916
  ride = planRideMarkers([], void 0, () => void 0);
21896
21917
  rideJd = NaN;
21897
21918
  rideFocus;
21898
- constructor(ctx, bodyObjects, diagnostics) {
21919
+ constructor(ctx, bodyObjects, diagnostics, held = () => false) {
21899
21920
  this.ctx = ctx;
21900
21921
  this.bodyObjects = bodyObjects;
21901
21922
  this.diagnostics = diagnostics;
21923
+ this.held = held;
21902
21924
  }
21903
21925
  begin(jd, focusedId, frame) {
21904
21926
  this.passJd = jd;
@@ -21922,6 +21944,7 @@ var Placer = class {
21922
21944
  this.place(body, this.passJd);
21923
21945
  }
21924
21946
  place(body, jd) {
21947
+ if (jd !== this.passJd && this.held()) jd = this.passJd;
21925
21948
  const id = body.data.id;
21926
21949
  if (jd === this.passJd && this.settled.has(id)) return body.position;
21927
21950
  if (this.settling.has(id)) {
@@ -22822,7 +22845,7 @@ function updateTextureLOD(bodyObjects, camera, renderer, ctx, textureLoader, foc
22822
22845
  const target = wantsUpgrade ? highestAvailableTier(desiredRank, bo.availableTiers) : bo.textureTier;
22823
22846
  if (target) loadBodyTextureTier(bo, target, desiredFrame, textureLoader);
22824
22847
  }
22825
- updateTileOverlay(bo, camera, renderer, projScale * renderer.getPixelRatio(), settings.showSurfaceTexture && !bo.appearance?.surfaceOwned && !bo.model, invalidate);
22848
+ updateTileOverlay(bo, camera, renderer, projScale * renderer.getPixelRatio(), settings.showSurfaceTexture && !bo.appearance?.surfaceOwned, invalidate);
22826
22849
  const dispTiers = bo.displacementMeta?.tiers;
22827
22850
  if (bo.displacementMap && dispTiers?.length && !bo.displacementLoading) {
22828
22851
  const maxTex = renderer.capabilities.maxTextureSize;
@@ -24169,6 +24192,8 @@ var SceneRenderer = class SceneRenderer {
24169
24192
  disposed = false;
24170
24193
  lastDataVersion = -1;
24171
24194
  dateHolds = 0;
24195
+ seenJumps = 0;
24196
+ jumpJd = null;
24172
24197
  focusWasOutOfRange = false;
24173
24198
  seekAwaitsData = false;
24174
24199
  frameIndex = 0;
@@ -24224,6 +24249,7 @@ var SceneRenderer = class SceneRenderer {
24224
24249
  this.ctx = ctx;
24225
24250
  ctx.hasMeshBody = (id) => this.bodyObjects.has(id);
24226
24251
  this.clock = clock;
24252
+ this.seenJumps = clock.jumps;
24227
24253
  this.callbacks = callbacks;
24228
24254
  this.outOfRange = new OutOfRangeNotifier(callbacks.notices);
24229
24255
  this.labelContainer = labelContainer;
@@ -24273,7 +24299,7 @@ var SceneRenderer = class SceneRenderer {
24273
24299
  this.orbitPreview.setLayer(SceneRenderer.MAP_LAYER);
24274
24300
  this.skyboxAdjuster.set(0, 0, 0);
24275
24301
  loadSkybox(this.scene, this.renderer, ctx, () => this.disposed);
24276
- this.placer = new Placer(ctx, this.bodyObjects, this.positionDiagnostics);
24302
+ this.placer = new Placer(ctx, this.bodyObjects, this.positionDiagnostics, () => this.dateHeld());
24277
24303
  ctx.placer = this.placer;
24278
24304
  const focusBody = [
24279
24305
  ctx.getBody(initialView.id),
@@ -24413,7 +24439,7 @@ var SceneRenderer = class SceneRenderer {
24413
24439
  return;
24414
24440
  }
24415
24441
  this.travelPath.setVisible(true);
24416
- this.travelPath.setClock(this.clock.jd);
24442
+ this.travelPath.setClock(this.sceneJd);
24417
24443
  this.travelPath.reposition(center, this.focus.focusTruePos, (id) => this.where(id));
24418
24444
  this.travelPath.updateCameraOffset(this.camera.position);
24419
24445
  this.reserveTravelLabelSpace();
@@ -24545,10 +24571,10 @@ var SceneRenderer = class SceneRenderer {
24545
24571
  return this.focus.focusDurationMs;
24546
24572
  }
24547
24573
  where(id) {
24548
- return this.placer.at(id, this.clock.jd);
24574
+ return this.placer.at(id, this.sceneJd);
24549
24575
  }
24550
24576
  whereBody(body) {
24551
- return this.placer.place(body, this.clock.jd);
24577
+ return this.placer.place(body, this.sceneJd);
24552
24578
  }
24553
24579
  repositionBodies() {
24554
24580
  const [fx, fy, fz] = this.focus.focusTruePos;
@@ -24566,7 +24592,7 @@ var SceneRenderer = class SceneRenderer {
24566
24592
  if (!line) continue;
24567
24593
  const trailBuffer = line.userData.trailBuffer;
24568
24594
  if (trailBuffer) {
24569
- refreshBufferTrail(bo.body, line, trailBuffer, basis, this.clock.jd);
24595
+ refreshBufferTrail(bo.body, line, trailBuffer, basis, this.sceneJd);
24570
24596
  continue;
24571
24597
  }
24572
24598
  const localPositions = line.userData.trailLocalPositions;
@@ -24581,7 +24607,7 @@ var SceneRenderer = class SceneRenderer {
24581
24607
  const nowMs = performance.now();
24582
24608
  const frameDtMs = this.lastTickMs ? nowMs - this.lastTickMs : 0;
24583
24609
  this.lastTickMs = nowMs;
24584
- if (!this.holdingCamera) this.cameraUp.update(this.clock.jd);
24610
+ if (!this.holdingCamera) this.cameraUp.update(this.sceneJd);
24585
24611
  if (this.firstFrame) {
24586
24612
  this.firstFrame = false;
24587
24613
  this.controls.target.set(0, 0, 0);
@@ -24608,7 +24634,7 @@ var SceneRenderer = class SceneRenderer {
24608
24634
  this.lastRenderedCamQuat.copy(this.camera.quaternion);
24609
24635
  this.applyJdUpdate(true);
24610
24636
  this.refreshTravelFocus();
24611
- this.callbacks.onFrame?.(this.clock.jd, renderedDtMs);
24637
+ this.callbacks.onFrame?.(this.sceneJd, renderedDtMs);
24612
24638
  const controlsSettled = this.holdingCamera ? this.applyHeldCamera() : stepFocusAnimation(this.focus, this.camera, this.controls, () => this.repositionAll(), () => this.pointClouds.rebuildBasis());
24613
24639
  this.applyReaderLimits(nowMs);
24614
24640
  const focused = this.holdingCamera ? void 0 : this.focusController.current;
@@ -24631,13 +24657,13 @@ var SceneRenderer = class SceneRenderer {
24631
24657
  if (this.systemData.hasPendingUnloads() && performance.now() - this.focus.focusStartTime >= this.focus.focusDurationMs) this.systemData.drainPendingUnloads();
24632
24658
  const { distance } = this.getCameraState();
24633
24659
  this.updateTravelCraftSystem();
24634
- this.ctx.visibility.updateCamera(distance, this.clock.jd);
24660
+ this.ctx.visibility.updateCamera(distance, this.sceneJd);
24635
24661
  this.updateTightFar(distance);
24636
24662
  this.systemData.syncToFocus();
24637
24663
  this.refreshTravelPath();
24638
24664
  this.refreshOrbitPreview();
24639
24665
  this.extensions.update({
24640
- jd: this.clock.jd,
24666
+ jd: this.sceneJd,
24641
24667
  basis: this.focus.focusTruePos,
24642
24668
  camera: this.camera,
24643
24669
  viewportPx: this.viewportH,
@@ -24648,7 +24674,7 @@ var SceneRenderer = class SceneRenderer {
24648
24674
  refreshDeferredTrails(this.bodyObjects, this.focus, this.lastUpdatedJd, this.trailView());
24649
24675
  updateRingShaders(this.bodyObjects, this.focus.focusTruePos, sceneSettings().realisticLighting, sceneSettings().overexposeRings, this.camera.position, this.pxPerRad());
24650
24676
  updateEclipseUniforms(this.bodyObjects, this.focus.focusTruePos);
24651
- const atmoState = updateAtmosphereShaders(this.bodyObjects, this.camera.position, sceneSettings().showAtmospheres, sceneSettings().realisticLighting, this.sunIntensityScale, currentAtmosphereConfig(), this.clock.jd);
24677
+ const atmoState = updateAtmosphereShaders(this.bodyObjects, this.camera.position, sceneSettings().showAtmospheres, sceneSettings().realisticLighting, this.sunIntensityScale, currentAtmosphereConfig(), this.sceneJd);
24652
24678
  recordAtmospherePerf(renderedDtMs, atmoState.shellProminent);
24653
24679
  this.scene.backgroundIntensity = atmoState.skyboxIntensity;
24654
24680
  this.applyLayerChrome();
@@ -24665,7 +24691,7 @@ var SceneRenderer = class SceneRenderer {
24665
24691
  for (const light of this.ambientLights) light.intensity = ambient;
24666
24692
  updateUserLocationOcclusion(this.userLocationMarker, this.bodyObjects, this.camera);
24667
24693
  const focusedIdLod = nomenclatureBodyId(this.focusController.current, this.bodyObjects);
24668
- updateTextureLOD(this.bodyObjects, this.camera, this.renderer, this.ctx, this.textureLoader, focusedIdLod, this.clock.jd, this.appearances, this.invalidate);
24694
+ updateTextureLOD(this.bodyObjects, this.camera, this.renderer, this.ctx, this.textureLoader, focusedIdLod, this.sceneJd, this.appearances, this.invalidate);
24669
24695
  updateSphereLOD(this.bodyObjects, this.camera, this.renderer, this.ctx, focusedIdLod);
24670
24696
  updateSunShadowLight(this.bodyObjects, this.focus.focusTruePos, this.ctx, this.shadowLight, this.sunPointLight, distance, this._tmpV3, sceneSettings().realisticLighting, this.sunIntensityScale);
24671
24697
  this.updateModelMounts();
@@ -24792,7 +24818,7 @@ var SceneRenderer = class SceneRenderer {
24792
24818
  };
24793
24819
  if (!seated || !body.orientation) return void 0;
24794
24820
  return {
24795
- invQuat: bodyQuaternion(body.orientation, this.clock.jd, body.nutPrec).invert(),
24821
+ invQuat: bodyQuaternion(body.orientation, this.sceneJd, body.nutPrec).invert(),
24796
24822
  seated,
24797
24823
  radialKm: (dir) => renderedSurfaceRadialKm(bo, body.data.id, body.data.radiusKm, dir)
24798
24824
  };
@@ -25048,7 +25074,7 @@ var SceneRenderer = class SceneRenderer {
25048
25074
  }
25049
25075
  applyHeldCamera() {
25050
25076
  const pose = this.cameraHold;
25051
- const resolved = pose && resolvePose(pose, this.ctx, this.clock.jd);
25077
+ const resolved = pose && resolvePose(pose, this.ctx, this.sceneJd);
25052
25078
  if (!resolved) return true;
25053
25079
  const basis = this.focus.focusTruePos;
25054
25080
  this.camera.up.set(resolved.up[0], resolved.up[1], resolved.up[2]).normalize();
@@ -25081,10 +25107,10 @@ var SceneRenderer = class SceneRenderer {
25081
25107
  maybeLoadTexture(body) {
25082
25108
  const bo = this.bodyObjects.get(body.data.id);
25083
25109
  if (!bo) return;
25084
- loadBodyTexture(bo, this.textureLoader, this.clock.jd, this.scene, this.renderer.capabilities.maxTextureSize, this.ctx).then(() => {
25110
+ loadBodyTexture(bo, this.textureLoader, this.sceneJd, this.scene, this.renderer.capabilities.maxTextureSize, this.ctx).then(() => {
25085
25111
  if (this.focusController.current?.data.id !== body.data.id) return;
25086
25112
  if (body.orientation && bo.mesh) {
25087
- applyOrientation(bo.mesh, body.orientation, this.clock.jd, body.nutPrec);
25113
+ applyOrientation(bo.mesh, body.orientation, this.sceneJd, body.nutPrec);
25088
25114
  this.focusController.reapplyInitialViewIfPending();
25089
25115
  }
25090
25116
  });
@@ -25171,8 +25197,32 @@ var SceneRenderer = class SceneRenderer {
25171
25197
  held = false;
25172
25198
  };
25173
25199
  }
25200
+ jumpLoads() {
25201
+ const { clock, ctx } = this;
25202
+ if (clock.jumps !== this.seenJumps) {
25203
+ this.seenJumps = clock.jumps;
25204
+ this.jumpJd = clock.jd;
25205
+ ctx.refreshTick(jdToDate(clock.jd));
25206
+ ctx.chebStore?.request(clock.jd);
25207
+ ctx.probeStore?.request(clock.jd);
25208
+ }
25209
+ const jd = this.jumpJd;
25210
+ if (jd === null) return false;
25211
+ if (!ctx.chebStore?.loadingAt(jd) && !ctx.probeStore?.loadingAt(jd)) {
25212
+ this.jumpJd = null;
25213
+ return false;
25214
+ }
25215
+ this.invalidate();
25216
+ return true;
25217
+ }
25218
+ dateHeld() {
25219
+ return !Number.isNaN(this.lastUpdatedJd) && (this.dateHolds > 0 || this.jumpLoads());
25220
+ }
25221
+ get sceneJd() {
25222
+ return this.dateHeld() ? this.lastUpdatedJd : this.clock.jd;
25223
+ }
25174
25224
  applyJdUpdate(allowOorRefocus = false) {
25175
- if (this.dateHolds > 0) return;
25225
+ if (this.dateHolds > 0 || this.jumpLoads()) return;
25176
25226
  this.ensureFocusedProbeTarget();
25177
25227
  const systemId = this.ctx.visibility.focusedSystemId;
25178
25228
  const seatKey = this.focusedSeatConfigKey();
@@ -27793,6 +27843,43 @@ function subsolarPoint$1(id, ctx, jd) {
27793
27843
  const sunCentre = bodyCentre(sun, ctx, jd);
27794
27844
  return centre && sunCentre && subsolarAt(body, centre, sunCentre, jd);
27795
27845
  }
27846
+ var SUNLIGHT_SAMPLES = 1024;
27847
+ var GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
27848
+ function sunlightAt(radiusKm, sun, occluders) {
27849
+ const sunKm = Math.hypot(...sun.centre);
27850
+ const aSun = Math.asin(Math.min(sun.radiusKm / sunKm, 1));
27851
+ if (!(aSun > 0)) return 1;
27852
+ const toSun = sun.centre.map((km) => km / sunKm);
27853
+ const along = (v) => v[0] * toSun[0] + v[1] * toSun[1] + v[2] * toSun[2];
27854
+ const near = occluders.filter(({ centre, radiusKm: r }) => {
27855
+ const ahead = along(centre);
27856
+ const aside = Math.sqrt(Math.max(Math.hypot(...centre) ** 2 - ahead ** 2, 0));
27857
+ return ahead > -(radiusKm + r) && aside < radiusKm + r + 2 * aSun * (ahead + radiusKm);
27858
+ });
27859
+ if (near.length === 0) return 1;
27860
+ const [u, v] = planeBasis(sun.centre);
27861
+ let sum = 0;
27862
+ for (let i = 0; i < SUNLIGHT_SAMPLES; i++) {
27863
+ const rho = Math.sqrt((i + .5) / SUNLIGHT_SAMPLES);
27864
+ const x = radiusKm * rho * Math.cos(i * GOLDEN_ANGLE);
27865
+ const y = radiusKm * rho * Math.sin(i * GOLDEN_ANGLE);
27866
+ const z = radiusKm * Math.sqrt(1 - rho * rho);
27867
+ let lit = 1;
27868
+ for (const { centre, radiusKm: r } of near) {
27869
+ const to = [
27870
+ centre[0] - x * u[0] - y * v[0] - z * toSun[0],
27871
+ centre[1] - x * u[1] - y * v[1] - z * toSun[1],
27872
+ centre[2] - x * u[2] - y * v[2] - z * toSun[2]
27873
+ ];
27874
+ const km = Math.hypot(...to);
27875
+ const sep = Math.acos(Math.max(-1, Math.min(1, along(to) / km)));
27876
+ lit *= sunInView(aSun, Math.asin(Math.min(r / km, 1)), sep);
27877
+ if (lit === 0) break;
27878
+ }
27879
+ sum += lit;
27880
+ }
27881
+ return sum / SUNLIGHT_SAMPLES;
27882
+ }
27796
27883
 
27797
27884
  //#endregion
27798
27885
  //#region src/lib/interaction/gesture.ts
@@ -32883,7 +32970,8 @@ async function place(id, at, above = /* @__PURE__ */ new Set()) {
32883
32970
  if (!own) return null;
32884
32971
  if (own.parentId === "naif-0") return {
32885
32972
  data: own.data,
32886
- centre: own.offset
32973
+ centre: own.offset,
32974
+ parents: []
32887
32975
  };
32888
32976
  const parent = await place(own.parentId, at, new Set(above).add(id));
32889
32977
  if (!parent) return null;
@@ -32894,9 +32982,16 @@ async function place(id, at, above = /* @__PURE__ */ new Set()) {
32894
32982
  x + own.offset[0],
32895
32983
  y + own.offset[1],
32896
32984
  z + own.offset[2]
32897
- ]
32985
+ ],
32986
+ parents: [own.parentId, ...parent.parents]
32898
32987
  };
32899
32988
  }
32989
+ function neighbours(id, { parents }, at) {
32990
+ const ids = new Set(parents);
32991
+ for (const { body } of at.chebyshev?.bodiesAt(at.jd) ?? []) if (parents.includes(`naif-${body.parentId}`)) ids.add(body.id);
32992
+ ids.delete(id);
32993
+ return [...ids];
32994
+ }
32900
32995
  async function spinOf(id) {
32901
32996
  const [detail] = await Promise.all([fetchObjectDetail(id, false), loadSystemsGlobal()]);
32902
32997
  const orientation = detail.global?.orientation;
@@ -32949,6 +33044,29 @@ function subsolarPoint(id, date) {
32949
33044
  }, body.centre, sun.centre, at.jd);
32950
33045
  });
32951
33046
  }
33047
+ function sunlight(id, date) {
33048
+ return inTurn(async () => {
33049
+ if (id === "naif-10") return null;
33050
+ const at = await open(date);
33051
+ const body = await place(id, at);
33052
+ const sun = body && await place("naif-10", at);
33053
+ if (!body || !sun) return null;
33054
+ const ball = ({ data, centre }) => ({
33055
+ centre: sceneToEcliptic([
33056
+ centre[0] - body.centre[0],
33057
+ centre[1] - body.centre[1],
33058
+ centre[2] - body.centre[2]
33059
+ ]),
33060
+ radiusKm: data.radiusKm
33061
+ });
33062
+ const occluders = [];
33063
+ for (const other of neighbours(id, body, at)) {
33064
+ const placed = await place(other, at);
33065
+ if (placed && placed.data.radiusKm > 0) occluders.push(ball(placed));
33066
+ }
33067
+ return sunlightAt(body.data.radiusKm > 0 ? body.data.radiusKm : 0, ball(sun), occluders);
33068
+ });
33069
+ }
32952
33070
 
32953
33071
  //#endregion
32954
33072
  //#region src/lib/fetch/panoramas.ts
@@ -32970,11 +33088,13 @@ async function fetchPanoramas(bodyId) {
32970
33088
  //#endregion
32971
33089
  //#region src/sdk/index.ts
32972
33090
  async function createMap(options) {
32973
- const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
33091
+ const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
32974
33092
  const element = resolveContainer(container);
32975
33093
  applyHost({
32976
33094
  dataUrl,
32977
33095
  imagesUrl,
33096
+ liveUrl,
33097
+ tilesUrl,
32978
33098
  locale,
32979
33099
  messages,
32980
33100
  includeNonCommercial
@@ -32995,11 +33115,13 @@ async function createMap(options) {
32995
33115
  return map;
32996
33116
  }
32997
33117
  async function createFlatMap(options) {
32998
- const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
33118
+ const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
32999
33119
  const element = resolveContainer(container);
33000
33120
  applyHost({
33001
33121
  dataUrl,
33002
33122
  imagesUrl,
33123
+ liveUrl,
33124
+ tilesUrl,
33003
33125
  locale,
33004
33126
  messages,
33005
33127
  includeNonCommercial
@@ -33014,11 +33136,13 @@ async function createFlatMap(options) {
33014
33136
  return map;
33015
33137
  }
33016
33138
  async function createPanorama(options) {
33017
- const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
33139
+ const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, controls, events, ...rest } = options;
33018
33140
  const element = resolveContainer(container);
33019
33141
  applyHost({
33020
33142
  dataUrl,
33021
33143
  imagesUrl,
33144
+ liveUrl,
33145
+ tilesUrl,
33022
33146
  locale,
33023
33147
  messages,
33024
33148
  includeNonCommercial
@@ -33033,11 +33157,13 @@ async function createPanorama(options) {
33033
33157
  return view;
33034
33158
  }
33035
33159
  async function createSystemMap(options) {
33036
- const { container, dataUrl, imagesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
33160
+ const { container, dataUrl, imagesUrl, liveUrl, tilesUrl, locale, messages, includeNonCommercial, events, ...rest } = options;
33037
33161
  const element = resolveContainer(container);
33038
33162
  applyHost({
33039
33163
  dataUrl,
33040
33164
  imagesUrl,
33165
+ liveUrl,
33166
+ tilesUrl,
33041
33167
  locale,
33042
33168
  messages,
33043
33169
  includeNonCommercial
@@ -33063,6 +33189,8 @@ function applyHost(options) {
33063
33189
  const overrides = {};
33064
33190
  if (options.dataUrl !== void 0) overrides.dataUrl = options.dataUrl;
33065
33191
  if (options.imagesUrl !== void 0) overrides.imagesUrl = options.imagesUrl;
33192
+ if (options.liveUrl !== void 0) overrides.liveUrl = options.liveUrl;
33193
+ if (options.tilesUrl !== void 0) overrides.tilesUrl = options.tilesUrl;
33066
33194
  if (options.locale !== void 0) overrides.locale = () => options.locale;
33067
33195
  if (options.messages !== void 0) overrides.messages = options.messages;
33068
33196
  if (options.includeNonCommercial) overrides.textures = "non-commercial";
@@ -33070,5 +33198,5 @@ function applyHost(options) {
33070
33198
  }
33071
33199
 
33072
33200
  //#endregion
33073
- 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 };
33201
+ 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 };
33074
33202
  (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);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacemap",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "The map of the Solar System from spacemap.co, on your website!",
5
5
  "keywords": ["solar system", "spacecraft", "orbits", "map", "three.js"],
6
6
  "homepage": "https://spacemap.co",