spacemap 0.1.16 → 0.1.17

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
@@ -1104,6 +1104,12 @@ export declare interface MapEvents {
1104
1104
  loading: (loading: boolean) => void;
1105
1105
  /** Boot progress, 0 to 1, monotonic within one load. */
1106
1106
  progress: (fraction: number) => void;
1107
+ /** The map is at rest. The camera does not move, and the view has every
1108
+ * picture, model, tile and terrain grid that it needs. The event fires once
1109
+ * each time that the map comes to rest after a change. The clock can run. A
1110
+ * covered map is not at rest. The catalogue points and the star background
1111
+ * do not count. */
1112
+ idle: () => void;
1107
1113
  /** The data could not be loaded. The map stays mounted and empty. */
1108
1114
  error: (message: string) => void;
1109
1115
  /** The export was republished while this map was open: its data is now a
package/index.js CHANGED
@@ -110,7 +110,7 @@ var noop = () => {};
110
110
  function run_all(arr) {
111
111
  for (var i = 0; i < arr.length; i++) arr[i]();
112
112
  }
113
- function deferred() {
113
+ function deferred$1() {
114
114
  var resolve;
115
115
  var reject;
116
116
  return {
@@ -902,7 +902,7 @@ var Batch = class Batch {
902
902
  this.#discard_callbacks.add(fn);
903
903
  }
904
904
  settled() {
905
- return (this.#deferred ??= deferred()).promise;
905
+ return (this.#deferred ??= deferred$1()).promise;
906
906
  }
907
907
  static ensure() {
908
908
  if (current_batch === null) {
@@ -13517,6 +13517,11 @@ function disposeChain(chain) {
13517
13517
  chain.uniforms.map.value = null;
13518
13518
  chain.uniforms.mask.value = null;
13519
13519
  }
13520
+ function chainSettled(chain, tiles) {
13521
+ const store = chain.store;
13522
+ if (!store || chain.dead || chain.uniforms.window.value[0].w === 0) return true;
13523
+ return store.windows.every((win) => settled(chain, store, tiles, win));
13524
+ }
13520
13525
  function restChain(chain, now) {
13521
13526
  switchOff(chain);
13522
13527
  if (now - chain.lastUsed > IDLE_DISPOSE_S * 1e3) disposeChain(chain);
@@ -21100,6 +21105,7 @@ var SystemDataLoader = class {
21100
21105
  lastBaryId = null;
21101
21106
  pendingUnloads = /* @__PURE__ */ new Set();
21102
21107
  prefetched = /* @__PURE__ */ new Set();
21108
+ loads = 0;
21103
21109
  constructor(scene, ctx, renderer, textureLoader, bodyObjects, clock, onLoaded) {
21104
21110
  this.scene = scene;
21105
21111
  this.ctx = ctx;
@@ -21145,10 +21151,14 @@ var SystemDataLoader = class {
21145
21151
  return isTopLevelParent(baryId) ? null : baryId;
21146
21152
  }
21147
21153
  load(baryId) {
21154
+ this.loads++;
21148
21155
  loadSystemData(baryId, this.bodyObjects, this.scene, this.textureLoader, this.clock.jd, this.renderer.capabilities.maxTextureSize, this.ctx).then(() => {
21149
21156
  this.onLoaded();
21150
21157
  this.ctx.bodies.orientationVersion++;
21151
- });
21158
+ }).finally(() => this.loads--);
21159
+ }
21160
+ isLoading() {
21161
+ return this.loads > 0;
21152
21162
  }
21153
21163
  drainPendingUnloads() {
21154
21164
  if (this.pendingUnloads.size === 0) return;
@@ -25571,6 +25581,10 @@ function gridGeometry(grid) {
25571
25581
  return geometry;
25572
25582
  }
25573
25583
  var tiledStates = /* @__PURE__ */ new WeakMap();
25584
+ var deferred = /* @__PURE__ */ new WeakSet();
25585
+ function terrainSettled(bo) {
25586
+ return !tiledStates.get(bo)?.abort && !deferred.has(bo);
25587
+ }
25574
25588
  var MAX_BUILD_FAILURES = 2;
25575
25589
  function heightTilesOf(bo) {
25576
25590
  const tiles = bo.displacementMeta?.tiles;
@@ -25625,7 +25639,10 @@ function updateTiledWindow(bo, mesh, tiles, altitude, centerTheta, centerPhi, in
25625
25639
  if (held) {
25626
25640
  const still = drift(centerTheta, centerPhi, held.centerTheta, held.centerPhi);
25627
25641
  if (kept === stepLevel && still < RECENTER_FRAC * held.angRadius) return true;
25628
- if (performance.now() - held.builtAt < REBUILD_MIN_MS) return true;
25642
+ if (performance.now() - held.builtAt < REBUILD_MIN_MS) {
25643
+ deferred.add(bo);
25644
+ return true;
25645
+ }
25629
25646
  }
25630
25647
  const grid = planNestedGrid(centerTheta, centerPhi, altitude, .5 * meta.scale_km / radiusKm, finest, bandCells, stepLevel);
25631
25648
  const thetas = grid.rows.at;
@@ -25687,6 +25704,7 @@ function updateTiledWindow(bo, mesh, tiles, altitude, centerTheta, centerPhi, in
25687
25704
  return !!held;
25688
25705
  }
25689
25706
  function updateTerrainWindow(bo, camera, eligible, invalidate) {
25707
+ deferred.delete(bo);
25690
25708
  const mesh = bo.mesh;
25691
25709
  const tex = bo.displacementMap;
25692
25710
  if (!eligible || !mesh || !tex) {
@@ -25725,7 +25743,10 @@ function updateTerrainWindow(bo, camera, eligible, invalidate) {
25725
25743
  if (w.stepLevel === stepLevel) {
25726
25744
  if (drift(centerTheta, centerPhi, w.centerTheta, w.centerPhi) < RECENTER_FRAC * w.angRadius) return true;
25727
25745
  }
25728
- if (performance.now() - w.builtAt < REBUILD_MIN_MS) return true;
25746
+ if (performance.now() - w.builtAt < REBUILD_MIN_MS) {
25747
+ deferred.add(bo);
25748
+ return true;
25749
+ }
25729
25750
  }
25730
25751
  const thetaLo = Math.max(0, centerTheta - angRadius);
25731
25752
  const thetaHi = Math.min(Math.PI, centerTheta + angRadius);
@@ -25822,6 +25843,15 @@ function desiredDemTier(altitudeRadii, current) {
25822
25843
  if (tierRank(down) < tierRank(current)) return down;
25823
25844
  return current;
25824
25845
  }
25846
+ function bodyLoading(bo) {
25847
+ if (bo.textureLoading || bo.displacementLoading || bo.clouds?.textureLoading) return true;
25848
+ if (bo.appearance?.loading || bo.modelLoadPromise) return true;
25849
+ const surface = bo.tileOverlay;
25850
+ if (surface && bo.surfaceTiles && !chainSettled(surface.chain, bo.surfaceTiles)) return true;
25851
+ const relief = bo.reliefTiles;
25852
+ if (relief && !chainSettled(relief.chain, relief.tiles)) return true;
25853
+ return !terrainSettled(bo);
25854
+ }
25825
25855
  function updateTextureLOD(bodyObjects, camera, renderer, ctx, textureLoader, focusedId, jd, appearances, invalidate) {
25826
25856
  appearances.apply(bodyObjects, textureLoader);
25827
25857
  const fovRad = camera.fov * Math.PI / 180;
@@ -25876,6 +25906,13 @@ function updateTextureLOD(bodyObjects, camera, renderer, ctx, textureLoader, foc
25876
25906
  }
25877
25907
  }
25878
25908
 
25909
+ //#endregion
25910
+ //#region src/lib/scene/perf/calibration-state.svelte.ts
25911
+ var calibrationUi = proxy({
25912
+ progress: null,
25913
+ bootPending: false
25914
+ });
25915
+
25879
25916
  //#endregion
25880
25917
  //#region src/lib/scene/animation/focus.ts
25881
25918
  var FOCUS_DURATION_MS = FOCUS_ROT_PACING.refMs;
@@ -27262,6 +27299,11 @@ var SceneRenderer = class SceneRenderer {
27262
27299
  lastRenderedFocusKey = "";
27263
27300
  lastRenderedCamPos = new Vector3(NaN, NaN, NaN);
27264
27301
  lastRenderedCamQuat = new Quaternion();
27302
+ restCamPos = new Vector3(NaN, NaN, NaN);
27303
+ restCamQuat = new Quaternion();
27304
+ restFocusKey = "";
27305
+ atRest = false;
27306
+ focusLoads = 0;
27265
27307
  unsubscribeBodiesAdded = null;
27266
27308
  _trailView = {
27267
27309
  camX: 0,
@@ -27766,7 +27808,27 @@ var SceneRenderer = class SceneRenderer {
27766
27808
  this.renderModelOverlay();
27767
27809
  this.labelRenderer.render(this.scene, this.camera);
27768
27810
  if (this.haloDebug.active) this.haloDebug.draw(this.bodyObjects, this.camera, this.focus.focusTruePos, this.focusController.current?.data.id, this.renderer.domElement.clientWidth, this.renderer.domElement.clientHeight);
27769
- };
27811
+ this.checkRest(nowMs);
27812
+ };
27813
+ checkRest(nowMs) {
27814
+ const moved = this.cameraMovedFrom(this.restCamPos, this.restCamQuat) || this.focusKey() !== this.restFocusKey;
27815
+ this.restCamPos.copy(this.camera.position);
27816
+ this.restCamQuat.copy(this.camera.quaternion);
27817
+ this.restFocusKey = this.focusKey();
27818
+ if (moved || this.settling(nowMs)) this.atRest = false;
27819
+ else if (!this.atRest) {
27820
+ this.atRest = true;
27821
+ this.callbacks.onIdle?.();
27822
+ }
27823
+ }
27824
+ settling(nowMs) {
27825
+ if (this.covered || this.ctx.loading || calibrationUi.bootPending) return true;
27826
+ if (nowMs - this.focus.focusStartTime < this.focus.focusDurationMs) return true;
27827
+ if (this.jumpJd !== null || this.seekAwaitsData) return true;
27828
+ if (this.focusLoads > 0 || this.systemData.isLoading()) return true;
27829
+ for (const bo of this.bodyObjects.values()) if (bo.group.visible && bodyLoading(bo)) return true;
27830
+ return false;
27831
+ }
27770
27832
  syncSceneSamples(insideShell) {
27771
27833
  const target = this.composer.readBuffer;
27772
27834
  const samples = insideShell ? 0 : Math.min(this.tier.sceneSamples, this.maxSceneSamples);
@@ -28167,11 +28229,14 @@ var SceneRenderer = class SceneRenderer {
28167
28229
  if (nowMs - this.focus.focusStartTime < this.focus.focusDurationMs || this.focusKey() !== this.lastRenderedFocusKey || this.ctx.bodies.minorStreaming || this.pointClouds.hasPendingSceneAdds() || this.systemData.hasPendingUnloads()) return true;
28168
28230
  if (this.covered) return false;
28169
28231
  if (this.frameIndex - this.lastRenderedFrame >= SceneRenderer.IDLE_HEARTBEAT_FRAMES) return true;
28170
- const cam = this.camera;
28171
- if (cam.position.distanceToSquared(this.lastRenderedCamPos) > cam.position.lengthSq() * 1e-10 || 8 * (1 - Math.abs(cam.quaternion.dot(this.lastRenderedCamQuat))) > 1e-8 || this.frameIndex < this.wakeUntilFrame || this.haloDebug.active) return true;
28232
+ if (this.cameraMovedFrom(this.lastRenderedCamPos, this.lastRenderedCamQuat) || this.frameIndex < this.wakeUntilFrame || this.haloDebug.active) return true;
28172
28233
  if (this.clock.jd === this.lastRenderedJd) return false;
28173
28234
  return Math.abs(this.clock.timeScale) > 1 || nowMs - this.lastRenderMs >= SceneRenderer.REALTIME_FRAME_MS;
28174
28235
  }
28236
+ cameraMovedFrom(pos, quat) {
28237
+ const cam = this.camera;
28238
+ return !(cam.position.distanceToSquared(pos) <= cam.position.lengthSq() * 1e-10 && 8 * (1 - Math.abs(cam.quaternion.dot(quat))) <= 1e-8);
28239
+ }
28175
28240
  onControlsEnd = () => {
28176
28241
  this.pendingUrlWrite = true;
28177
28242
  this.focusController.clearPendingInitialView();
@@ -28187,13 +28252,15 @@ var SceneRenderer = class SceneRenderer {
28187
28252
  maybeLoadTexture(body) {
28188
28253
  const bo = this.bodyObjects.get(body.data.id);
28189
28254
  if (!bo) return;
28255
+ this.focusLoads += 2;
28256
+ const done = () => this.focusLoads--;
28190
28257
  loadBodyTexture(bo, this.textureLoader, this.sceneJd, this.scene, this.renderer.capabilities.maxTextureSize, this.ctx).then(() => {
28191
28258
  if (this.focusController.current?.data.id !== body.data.id) return;
28192
28259
  if (body.orientation && bo.mesh) {
28193
28260
  applyOrientation(bo.mesh, body.orientation, this.sceneJd, body.nutPrec);
28194
28261
  this.focusController.reapplyInitialViewIfPending();
28195
28262
  }
28196
- });
28263
+ }).finally(done);
28197
28264
  loadBodyModel(bo, this.modelScene, this.ctx).then(() => {
28198
28265
  if (this.focusController.current?.data.id === bo.body.data.id) this.controls.minDistance = minCameraDistance(bo.body, modelMinRadiusKm(bo));
28199
28266
  if (this.ctx.layers.skipped.has("nomenclature")) return;
@@ -28203,7 +28270,7 @@ var SceneRenderer = class SceneRenderer {
28203
28270
  });
28204
28271
  }).then(() => {
28205
28272
  if (this.selectedFeatureId !== null) setActiveFeatureLabel(bo, this.selectedFeatureId);
28206
- });
28273
+ }).finally(done);
28207
28274
  }
28208
28275
  reapplyBodyLayers() {
28209
28276
  const focused = this.focusController.current;
@@ -28661,7 +28728,8 @@ var SceneRenderer = class SceneRenderer {
28661
28728
  setCovered(covered) {
28662
28729
  if (this.covered === covered) return;
28663
28730
  this.covered = covered;
28664
- if (!covered) this.invalidate();
28731
+ if (covered) this.atRest = false;
28732
+ else this.invalidate();
28665
28733
  }
28666
28734
  isContextLost() {
28667
28735
  return this.renderer.getContext().isContextLost();
@@ -28754,13 +28822,6 @@ function disposeTexture(value) {
28754
28822
  if (value && typeof value === "object" && value.isTexture) value.dispose();
28755
28823
  }
28756
28824
 
28757
- //#endregion
28758
- //#region src/lib/scene/perf/calibration-state.svelte.ts
28759
- var calibrationUi = proxy({
28760
- progress: null,
28761
- bootPending: false
28762
- });
28763
-
28764
28825
  //#endregion
28765
28826
  //#region src/lib/scene/perf/atmosphere-benchmark.ts
28766
28827
  var EARTH_RADIUS_KM = 6371;
@@ -31223,6 +31284,7 @@ var SpaceMap = class {
31223
31284
  clock: /* @__PURE__ */ new Set(),
31224
31285
  loading: /* @__PURE__ */ new Set(),
31225
31286
  progress: /* @__PURE__ */ new Set(),
31287
+ idle: /* @__PURE__ */ new Set(),
31226
31288
  error: /* @__PURE__ */ new Set(),
31227
31289
  datastale: /* @__PURE__ */ new Set(),
31228
31290
  notice: /* @__PURE__ */ new Set(),
@@ -31494,6 +31556,7 @@ var SpaceMap = class {
31494
31556
  dtMs
31495
31557
  });
31496
31558
  },
31559
+ onIdle: () => this.emit("idle"),
31497
31560
  onCameraPosition: (latitude, longitude, zoom) => {
31498
31561
  const body = this.focusedBody?.data.id;
31499
31562
  if (body === void 0) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacemap",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
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",