spacemap 0.1.4 → 0.1.6

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*! spacemap SDK — https://spacemap.co — Mozilla Public License 2.0. Bundles MIT and ISC libraries: THIRD_PARTY_NOTICES.txt in the spacemap npm package. */
2
- import { ACESFilmicToneMapping, AdditiveBlending, AmbientLight, ArrowHelper, BackSide, Box3, BufferAttribute, BufferGeometry, CanvasTexture, Color, CubeTexture, CustomBlending, DataTexture, DepthTexture, DirectionalLight, DoubleSide, Euler, Float32BufferAttribute, FloatType, FrontSide, Group, HalfFloatType, InstancedBufferAttribute, InstancedMesh, Line, LineBasicMaterial, LineLoop, LinearFilter, LinearMipmapLinearFilter, LinearSRGBColorSpace, MathUtils, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshStandardMaterial, NoBlending, NoColorSpace, NormalBlending, OneFactor, OneMinusSrcAlphaFactor, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, RGBAFormat, RawShaderMaterial, Raycaster, RedFormat, RingGeometry, SRGBColorSpace, Scene, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapeUtils, SphereGeometry, Sprite, SpriteMaterial, TOUCH, Texture, TextureLoader, Uint16BufferAttribute, UnsignedByteType, Vector2, Vector3, Vector4, WebGLRenderTarget, WebGLRenderer } from "three";
2
+ import { ACESFilmicToneMapping, AdditiveBlending, AmbientLight, ArrowHelper, BackSide, Box3, BufferAttribute, BufferGeometry, CanvasTexture, Color, CubeTexture, CustomBlending, DataTexture, DepthTexture, DirectionalLight, DoubleSide, Euler, Float32BufferAttribute, FloatType, FrontSide, Group, HalfFloatType, InstancedBufferAttribute, InstancedMesh, Line, LineBasicMaterial, LineLoop, LinearFilter, LinearMipmapLinearFilter, LinearSRGBColorSpace, MathUtils, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshStandardMaterial, NoBlending, NoColorSpace, NormalBlending, OneFactor, OneMinusSrcAlphaFactor, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, RGBAFormat, RawShaderMaterial, Raycaster, RedFormat, RingGeometry, SRGBColorSpace, Scene, ShaderChunk, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapeUtils, SphereGeometry, Sprite, SpriteMaterial, TOUCH, Texture, TextureLoader, Uint16BufferAttribute, UnsignedByteType, Vector2, Vector3, Vector4, WebGLRenderTarget, WebGLRenderer } from "three";
3
3
  import { OrbitControls } from "three/addons/controls/OrbitControls.js";
4
4
  import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
5
5
  import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
@@ -6287,6 +6287,12 @@ var BodyIndex = class {
6287
6287
  findAsteroidZone(id) {
6288
6288
  for (const [zone, byId] of this.asteroidBodiesByZone) if (byId.has(id)) return zone;
6289
6289
  }
6290
+ elementRow(body) {
6291
+ const d = body.data;
6292
+ if (d.orbitalSource !== OrbitalSource.SPICE) return d;
6293
+ const zone = this.findAsteroidZone(d.id);
6294
+ return zone === void 0 ? void 0 : this.asteroidBodiesByZone.get(zone)?.get(d.id)?.data;
6295
+ }
6290
6296
  maxMoonA(parentId) {
6291
6297
  return this.moonMaxAByParent.get(parentId);
6292
6298
  }
@@ -6789,6 +6795,7 @@ var VisibilityController = class {
6789
6795
  scaledSystem = SYSTEM_DISTANCE_RATIO_THRESHOLDS;
6790
6796
  fullMoonIds = /* @__PURE__ */ new Set();
6791
6797
  moonVisibilityCache = /* @__PURE__ */ new Map();
6798
+ pinned = /* @__PURE__ */ new Set();
6792
6799
  constructor(bodies, getProbeStore = () => null, getEarthSatGroupFilter = () => null, getSmallBodyFilter = () => null, getLayers = () => ALL_LAYERS) {
6793
6800
  this.bodies = bodies;
6794
6801
  this.getProbeStore = getProbeStore;
@@ -6885,8 +6892,19 @@ var VisibilityController = class {
6885
6892
  this.moonVisibilityCache.set(moon.data.id, vis);
6886
6893
  return vis;
6887
6894
  }
6895
+ setPinned(ids) {
6896
+ this.pinned = new Set(ids);
6897
+ }
6898
+ isPinned(id) {
6899
+ return this.pinned.has(id);
6900
+ }
6888
6901
  getPlanetVisibility(body, camDistThreeJS) {
6889
- if (this.getLayers().hidesBody(body.data.objectType, body.data.parentId)) return VISIBILITY.HIDE;
6902
+ const pinned = this.pinned.has(body.data.id);
6903
+ const vis = this.ratioVisibility(body, camDistThreeJS, pinned);
6904
+ return pinned && vis !== VISIBILITY.CLOSE ? VISIBILITY.FULL : vis;
6905
+ }
6906
+ ratioVisibility(body, camDistThreeJS, pinned) {
6907
+ if (!pinned && this.getLayers().hidesBody(body.data.objectType, body.data.parentId)) return VISIBILITY.HIDE;
6890
6908
  if (body.data.orbitalSource === OrbitalSource.SPICE_PROBE) return this.getProbeVisibility(body);
6891
6909
  const earthFilter = this.getEarthSatGroupFilter();
6892
6910
  if (earthFilter && body.data.parentId === "naif-399" && !earthFilter.has(body.data.id)) return VISIBILITY.HIDE;
@@ -6939,7 +6957,7 @@ var VisibilityController = class {
6939
6957
  return sysNaif !== null && this.isInFocusedSystem(`naif-${sysNaif}`);
6940
6958
  }
6941
6959
  hasFullRendering(body) {
6942
- if (!this.activeSystemId) return true;
6960
+ if (!this.activeSystemId || this.pinned.has(body.data.id)) return true;
6943
6961
  if (this.isInActiveSystem(isTopLevelParent(body.data.parentId) ? body.data.id : body.data.parentId)) return true;
6944
6962
  if (body.data.orbitalSource !== OrbitalSource.SPICE_PROBE) return false;
6945
6963
  const ps = this.getProbeStore();
@@ -12089,6 +12107,26 @@ function isReversedDepth() {
12089
12107
  function ndcZVisible(z) {
12090
12108
  return reversed ? z >= 0 && z <= 1 : z >= -1 && z <= 1;
12091
12109
  }
12110
+ var LOG_DEPTH_FLOOR = -44;
12111
+ function installLogDepthChunks() {
12112
+ ShaderChunk.logdepthbuf_vertex = `
12113
+ #ifdef USE_LOGARITHMIC_DEPTH_BUFFER
12114
+ vFragDepth = gl_Position.w;
12115
+ vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );
12116
+ #endif
12117
+ `;
12118
+ ShaderChunk.logdepthbuf_fragment = `
12119
+ #if defined( USE_LOGARITHMIC_DEPTH_BUFFER )
12120
+ gl_FragDepth = vIsPerspective == 0.0
12121
+ ? gl_FragCoord.z
12122
+ : max( log2( vFragDepth ) - ${LOG_DEPTH_FLOOR.toFixed(1)}, 0.0 ) / ( 2.0 / logDepthBufFC - ${LOG_DEPTH_FLOOR.toFixed(1)} );
12123
+ #endif
12124
+ `;
12125
+ }
12126
+ function logDepthToEyeGlsl(depth, far) {
12127
+ const floor = LOG_DEPTH_FLOOR.toFixed(1);
12128
+ return `exp2(${depth} * (log2(${far} + 1.0) - ${floor}) + ${floor})`;
12129
+ }
12092
12130
 
12093
12131
  //#endregion
12094
12132
  //#region src/lib/scene/label/throttled-renderer.ts
@@ -12286,8 +12324,9 @@ function makeTrailMaterial(color, brightness = TRAIL_DIM) {
12286
12324
  `
12287
12325
  });
12288
12326
  }
12289
- function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
12327
+ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM, dash) {
12290
12328
  return new ShaderMaterial({
12329
+ defines: dash ? { DASHED: "" } : {},
12291
12330
  transparent: true,
12292
12331
  depthWrite: false,
12293
12332
  side: DoubleSide,
@@ -12298,7 +12337,8 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
12298
12337
  uAlphaMin: { value: 0 },
12299
12338
  uShowFull: { value: 0 },
12300
12339
  uLineWidth: { value: lineWidth },
12301
- uResolution: { value: TRAIL_RESOLUTION }
12340
+ uResolution: { value: TRAIL_RESOLUTION },
12341
+ uDash: { value: new Vector2(dash?.[0] ?? 0, dash?.[1] ?? 0) }
12302
12342
  },
12303
12343
  vertexShader: `
12304
12344
  #include <common>
@@ -12312,6 +12352,11 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
12312
12352
  attribute float trailAlpha;
12313
12353
  attribute float fullAlpha;
12314
12354
  varying float vAlpha;
12355
+ #ifdef DASHED
12356
+ attribute float lineDistance;
12357
+ varying float vDistW;
12358
+ varying float vW;
12359
+ #endif
12315
12360
  void main() {
12316
12361
  vAlpha = mix(trailAlpha, fullAlpha, uShowFull);
12317
12362
  vec3 currRel = position + uCenterOffset;
@@ -12328,6 +12373,13 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
12328
12373
  vec2 perp = vec2(-dirN.y, dirN.x) * side * uLineWidth * 0.5;
12329
12374
  vec2 offsetNDC = perp / (uResolution * 0.5);
12330
12375
  gl_Position = vec4((currNDC + offsetNDC) * currClip.w, currClip.zw);
12376
+ #ifdef DASHED
12377
+ // Carried times w and divided back out per fragment, which undoes
12378
+ // the perspective a varying is interpolated with: the dashes are
12379
+ // even on screen however much nearer one end is.
12380
+ vDistW = lineDistance * currClip.w;
12381
+ vW = currClip.w;
12382
+ #endif
12331
12383
  #include <logdepthbuf_vertex>
12332
12384
  }
12333
12385
  `,
@@ -12337,7 +12389,15 @@ function makeFatTrailMaterial(color, lineWidth, brightness = TRAIL_DIM) {
12337
12389
  uniform float uAlphaMultiplier;
12338
12390
  uniform float uAlphaMin;
12339
12391
  varying float vAlpha;
12392
+ #ifdef DASHED
12393
+ uniform vec2 uDash;
12394
+ varying float vDistW;
12395
+ varying float vW;
12396
+ #endif
12340
12397
  void main() {
12398
+ #ifdef DASHED
12399
+ if (mod(vDistW / vW, uDash.x + uDash.y) > uDash.x) discard;
12400
+ #endif
12341
12401
  gl_FragColor = vec4(uColor, clamp(max(vAlpha * uAlphaMultiplier, uAlphaMin), 0.0, 1.0));
12342
12402
  #include <logdepthbuf_fragment>
12343
12403
  }
@@ -12385,6 +12445,7 @@ var WebGLUnavailableError = class extends Error {
12385
12445
  function bootThree(canvas, labelContainer, ctx) {
12386
12446
  let renderer;
12387
12447
  const reversedDepth = probeClipControl();
12448
+ if (!reversedDepth) installLogDepthChunks();
12388
12449
  try {
12389
12450
  renderer = new WebGLRenderer(reversedDepth ? {
12390
12451
  canvas,
@@ -14142,7 +14203,7 @@ function cullOverlappingLabels(bodyObjects, screenWidth, screenHeight, camera, f
14142
14203
  c.isCapped = body.data.objectType === ObjectType.MOON ? ctx.visibility.getMoonVisibility(body) === VISIBILITY.CAPPED : false;
14143
14204
  c.isMinor = bo.isMinor;
14144
14205
  c.isFocused = isFocused;
14145
- c.isSelected = isFocused || isHovered;
14206
+ c.isSelected = isFocused || isHovered || ctx.visibility.isPinned(body.data.id);
14146
14207
  c.dist = bo.cachedDist;
14147
14208
  }
14148
14209
  _candidates.length = _candidatesActive;
@@ -15260,7 +15321,7 @@ var FRAGMENT_SHADER = `
15260
15321
  if (hitGeom && fwd > 1e-4) {
15261
15322
  float terrainW = uReversedDepth > 0.5
15262
15323
  ? uCameraNear * uCameraFar / (d * (uCameraFar - uCameraNear) + uCameraNear)
15263
- : exp2(d * log2(uCameraFar + 1.0)) - 1.0;
15324
+ : ${logDepthToEyeGlsl("d", "uCameraFar")};
15264
15325
  float tTerrain = terrainW * dLen / (uPlanetRadiusScene * fwd);
15265
15326
  if (tTerrain < tEnd) {
15266
15327
  tEnd = tTerrain;
@@ -17768,10 +17829,18 @@ function buildThinLineFromArrays(posArr, trailAlphas, fullAlphas, total, color)
17768
17829
  line.renderOrder = TRAIL_RENDER_ORDER;
17769
17830
  return line;
17770
17831
  }
17771
- function buildFatLineFromThin(capacity, posArr, trailAlphas, fullAlphas, total, color, lineWidth, brightness) {
17832
+ function writeLineDistances(geometry, src, n) {
17833
+ const attr = geometry.getAttribute("lineDistance");
17834
+ const arr = attr.array;
17835
+ const m = Math.min(n, arr.length / 2);
17836
+ for (let i = 0; i < m; i++) arr[2 * i] = arr[2 * i + 1] = src[i];
17837
+ attr.needsUpdate = true;
17838
+ }
17839
+ function buildFatLineFromThin(capacity, posArr, trailAlphas, fullAlphas, total, color, lineWidth, brightness, dash) {
17772
17840
  const geometry = makeFatTrailGeometry(capacity);
17841
+ if (dash) geometry.setAttribute("lineDistance", new Float32BufferAttribute(new Float32Array(2 * capacity), 1));
17773
17842
  writeFatTrailVertices(geometry, posArr, trailAlphas, fullAlphas, total);
17774
- const mesh = new Mesh(geometry, makeFatTrailMaterial(color, lineWidth, brightness));
17843
+ const mesh = new Mesh(geometry, makeFatTrailMaterial(color, lineWidth, brightness, dash));
17775
17844
  mesh.matrixAutoUpdate = false;
17776
17845
  mesh.renderOrder = TRAIL_RENDER_ORDER;
17777
17846
  return mesh;
@@ -20183,7 +20252,7 @@ function updateBodyVisibility(bodyObjects, camera, ctx, focusTruePos, focusedBod
20183
20252
  isClose = !subPixel;
20184
20253
  showLabel = !isClose;
20185
20254
  }
20186
- if (bo.isMinor && !isFocused && trail) trail.visible = false;
20255
+ if (bo.isMinor && !isFocused && trail && !ctx.visibility.isPinned(body.data.id)) trail.visible = false;
20187
20256
  if (suppressHeliocentricTrails && trail?.visible) {
20188
20257
  const oc = trail.userData.orbitCenter;
20189
20258
  if (oc && oc.distanceToSquared(_focusV3.set(fp[0], fp[1], fp[2])) > HELIO_CENTER_DIST_SQ) trail.visible = false;
@@ -21387,6 +21456,18 @@ function updatePositions(params) {
21387
21456
  ctx.bodies.hostRead.delete(id);
21388
21457
  return false;
21389
21458
  };
21459
+ const elementRowOffset = (body, parentPos) => {
21460
+ const row = ctx.bodies.elementRow(body);
21461
+ const rowParentPos = row && positionMap.get(row.parentId);
21462
+ if (!rowParentPos) return null;
21463
+ const offset = row.q != null ? parabolicToPositionJD(row, jd) : orbitalElementsToPositionJD(row, jd);
21464
+ if (!offset) return null;
21465
+ return [
21466
+ rowParentPos[0] - parentPos[0] + offset[0],
21467
+ rowParentPos[1] - parentPos[1] + offset[1],
21468
+ rowParentPos[2] - parentPos[2] + offset[2]
21469
+ ];
21470
+ };
21390
21471
  const computePosition = (body) => {
21391
21472
  if (computed.has(body.data.id)) return;
21392
21473
  computed.add(body.data.id);
@@ -21447,7 +21528,7 @@ function updatePositions(params) {
21447
21528
  let y;
21448
21529
  let z;
21449
21530
  if (isChebTracked) {
21450
- const chebOffset = ctx.chebStore.positionScene(d.id, jd);
21531
+ const chebOffset = ctx.chebStore.positionScene(d.id, jd) ?? elementRowOffset(body, parentPos);
21451
21532
  if (!chebOffset) {
21452
21533
  hide(true);
21453
21534
  const coverage = ctx.chebStore.zoneCoverage(d.id);
@@ -22732,6 +22813,8 @@ var PromotionRegistry = class {
22732
22813
  defaults = /* @__PURE__ */ new Set();
22733
22814
  userPromoted = /* @__PURE__ */ new Set();
22734
22815
  autoPromoted = /* @__PURE__ */ new Set();
22816
+ pinned = /* @__PURE__ */ new Set();
22817
+ pinPromoted = /* @__PURE__ */ new Set();
22735
22818
  groupTargets = null;
22736
22819
  groupMode = "none";
22737
22820
  smallBodyZone = null;
@@ -22741,6 +22824,7 @@ var PromotionRegistry = class {
22741
22824
  this.deps = deps;
22742
22825
  deps.ctx.bodies.onBodiesAdded(() => {
22743
22826
  this.onBodiesAdded();
22827
+ this.buildPinned();
22744
22828
  this.autoPromoteAsteroidMoons();
22745
22829
  this.reevaluateEarthSatMode();
22746
22830
  if (this.smallBodyZone !== null) this.refreshSmallBodyEmphasis();
@@ -22930,6 +23014,31 @@ var PromotionRegistry = class {
22930
23014
  if (toRemove.length === 0) return;
22931
23015
  this.tearDownBodies(toRemove, this.autoPromoted);
22932
23016
  }
23017
+ setPinned(ids) {
23018
+ this.pinned = new Set(ids);
23019
+ const focusedId = this.deps.getFocusedId();
23020
+ const dropped = [...this.pinPromoted].filter((id) => !this.pinned.has(id));
23021
+ if (focusedId && dropped.includes(focusedId)) {
23022
+ this.pinPromoted.delete(focusedId);
23023
+ this.userPromoted.add(focusedId);
23024
+ this.emitUserPromotedCount();
23025
+ }
23026
+ this.tearDownBodies(dropped.filter((id) => id !== focusedId), this.pinPromoted);
23027
+ this.buildPinned();
23028
+ }
23029
+ buildPinned() {
23030
+ let built = false;
23031
+ for (const id of this.pinned) {
23032
+ const body = this.deps.ctx.getBody(id);
23033
+ if (!body || isSurfaceFeature(body) || body.data.objectType === ObjectType.MOON) continue;
23034
+ if (!this.buildBodyInstance(body)) continue;
23035
+ built = true;
23036
+ if (this.userPromoted.delete(id)) this.pinPromoted.add(id);
23037
+ }
23038
+ if (!built) return;
23039
+ this.emitUserPromotedCount();
23040
+ this.finalizeBuilds();
23041
+ }
22933
23042
  clearUserPromoted() {
22934
23043
  const focusedId = this.deps.getFocusedId();
22935
23044
  const toRemove = [...this.userPromoted].filter((id) => id !== focusedId);
@@ -23419,10 +23528,7 @@ function rotateByQuaternion(q, x, y, z, out, at) {
23419
23528
  function bodyCentre(body, ctx, jd) {
23420
23529
  return placeForRead(body, jd, ctx) ? body.position : null;
23421
23530
  }
23422
- function resolveAnchor(anchor, ctx, jd) {
23423
- const body = ctx.getBody(anchor.body);
23424
- const centre = body && bodyCentre(body, ctx, jd);
23425
- if (!centre) return null;
23531
+ function placeAnchor(anchor, body, centre, jd) {
23426
23532
  const [cx, cy, cz] = centre;
23427
23533
  if (isSurface(anchor)) {
23428
23534
  const distance = kmToScene(effectiveRadiusKm(body.data) + (anchor.altitudeKm ?? 0));
@@ -23459,6 +23565,11 @@ function resolveAnchor(anchor, ctx, jd) {
23459
23565
  cz + oz
23460
23566
  ];
23461
23567
  }
23568
+ function resolveAnchor(anchor, ctx, jd) {
23569
+ const body = ctx.getBody(anchor.body);
23570
+ const centre = body && bodyCentre(body, ctx, jd);
23571
+ return centre ? placeAnchor(anchor, body, centre, jd) : null;
23572
+ }
23462
23573
  function anchorNormal(anchor, world, ctx, jd) {
23463
23574
  const body = ctx.getBody(anchor.body);
23464
23575
  const centre = body && bodyCentre(body, ctx, jd);
@@ -24866,6 +24977,10 @@ var SceneRenderer = class SceneRenderer {
24866
24977
  setBodyAppearance(id, appearance) {
24867
24978
  this.appearances.set(id, appearance);
24868
24979
  }
24980
+ setPinnedBodies(ids) {
24981
+ this.ctx.visibility.setPinned(ids);
24982
+ this.focusController.promotion.setPinned(ids);
24983
+ }
24869
24984
  setNorthReference(id) {
24870
24985
  this.cameraUp.setNorthReference(id);
24871
24986
  }
@@ -25484,6 +25599,153 @@ var MarkerExtension = class {
25484
25599
  }
25485
25600
  };
25486
25601
 
25602
+ //#endregion
25603
+ //#region src/lib/scene/extensions/screen.ts
25604
+ var REACH = 3;
25605
+ var right = new Vector3();
25606
+ var up = new Vector3();
25607
+ var forward = new Vector3();
25608
+ function parseDash(pattern) {
25609
+ const [dash, gap = dash] = (pattern ?? "").split(/[\s,]+/).filter(Boolean).map(Number);
25610
+ return dash > 0 && gap > 0 ? [dash, gap] : null;
25611
+ }
25612
+ function aim(camera, viewportPx) {
25613
+ right.set(1, 0, 0).applyQuaternion(camera.quaternion);
25614
+ up.set(0, 1, 0).applyQuaternion(camera.quaternion);
25615
+ forward.set(0, 0, -1).applyQuaternion(camera.quaternion);
25616
+ return viewportPx / (2 * Math.tan(camera.getEffectiveFOV() * Math.PI / 360));
25617
+ }
25618
+ function view(points, i, camera, out) {
25619
+ const x = points[i * 3] - camera.position.x;
25620
+ const y = points[i * 3 + 1] - camera.position.y;
25621
+ const z = points[i * 3 + 2] - camera.position.z;
25622
+ out[0] = x * right.x + y * right.y + z * right.z;
25623
+ out[1] = x * up.x + y * up.y + z * up.z;
25624
+ out[2] = x * forward.x + y * forward.y + z * forward.z;
25625
+ }
25626
+ function reachesBehind(points, count, camera) {
25627
+ forward.set(0, 0, -1).applyQuaternion(camera.quaternion);
25628
+ const { x, y, z } = camera.position;
25629
+ for (let i = 0; i < count; i++) if ((points[i * 3] - x) * forward.x + (points[i * 3 + 1] - y) * forward.y + (points[i * 3 + 2] - z) * forward.z < camera.near * 2) return true;
25630
+ return false;
25631
+ }
25632
+ function screenLengths(points, count, camera, viewportPx, out) {
25633
+ const focal = aim(camera, viewportPx);
25634
+ const at = [
25635
+ 0,
25636
+ 0,
25637
+ 0
25638
+ ];
25639
+ let length = 0;
25640
+ let lastX = 0;
25641
+ let lastY = 0;
25642
+ let seen = false;
25643
+ for (let i = 0; i < count; i++) {
25644
+ view(points, i, camera, at);
25645
+ if (at[2] > camera.near) {
25646
+ const px = at[0] / at[2] * focal;
25647
+ const py = at[1] / at[2] * focal;
25648
+ if (seen) length += Math.hypot(px - lastX, py - lastY);
25649
+ lastX = px;
25650
+ lastY = py;
25651
+ seen = true;
25652
+ }
25653
+ out[i] = length;
25654
+ }
25655
+ }
25656
+ var SeenLine = class {
25657
+ points = new Float32Array(0);
25658
+ alphas = new Float32Array(0);
25659
+ lengths = new Float32Array(0);
25660
+ count = 0;
25661
+ a = [
25662
+ 0,
25663
+ 0,
25664
+ 0
25665
+ ];
25666
+ b = [
25667
+ 0,
25668
+ 0,
25669
+ 0
25670
+ ];
25671
+ see(points, alphas, count, camera, viewportPx) {
25672
+ if (this.alphas.length < count * 4) {
25673
+ this.points = new Float32Array(count * 12);
25674
+ this.alphas = new Float32Array(count * 4);
25675
+ this.lengths = new Float32Array(count * 4);
25676
+ }
25677
+ this.count = 0;
25678
+ const focal = aim(camera, viewportPx);
25679
+ const halfW = viewportPx * camera.aspect * REACH / 2;
25680
+ const halfH = viewportPx * REACH / 2;
25681
+ const near = camera.near * 2;
25682
+ let { a, b } = this;
25683
+ let joined = false;
25684
+ let length = 0;
25685
+ let lastX = 0;
25686
+ let lastY = 0;
25687
+ if (count > 0) view(points, 0, camera, a);
25688
+ for (let i = 0; i + 1 < count; i++, [a, b] = [b, a]) {
25689
+ view(points, i + 1, camera, b);
25690
+ let from = 0;
25691
+ let to = 1;
25692
+ for (let plane = 0; plane < 5 && from <= to; plane++) {
25693
+ const ga = edge(plane, a, focal, halfW, halfH, near);
25694
+ const gb = edge(plane, b, focal, halfW, halfH, near);
25695
+ if (ga < 0 && gb < 0) to = -1;
25696
+ else if (ga < 0) from = Math.max(from, ga / (ga - gb));
25697
+ else if (gb < 0) to = Math.min(to, ga / (ga - gb));
25698
+ }
25699
+ if (from > to) {
25700
+ joined = false;
25701
+ continue;
25702
+ }
25703
+ const depth = a[2] + (b[2] - a[2]) * from;
25704
+ const x = (a[0] + (b[0] - a[0]) * from) / depth * focal;
25705
+ const y = (a[1] + (b[1] - a[1]) * from) / depth * focal;
25706
+ if (!joined || from > 0) {
25707
+ if (this.count > 0) {
25708
+ this.copy(this.count - 1, 0);
25709
+ this.put(points, alphas, i, from, length, 0);
25710
+ }
25711
+ this.put(points, alphas, i, from, length, 1);
25712
+ lastX = x;
25713
+ lastY = y;
25714
+ }
25715
+ const endDepth = a[2] + (b[2] - a[2]) * to;
25716
+ const endX = (a[0] + (b[0] - a[0]) * to) / endDepth * focal;
25717
+ const endY = (a[1] + (b[1] - a[1]) * to) / endDepth * focal;
25718
+ length += Math.hypot(endX - lastX, endY - lastY);
25719
+ lastX = endX;
25720
+ lastY = endY;
25721
+ this.put(points, alphas, i, to, length, 1);
25722
+ joined = to === 1;
25723
+ }
25724
+ }
25725
+ put(points, alphas, i, t, length, shown) {
25726
+ const at = this.count++;
25727
+ for (let k = 0; k < 3; k++) {
25728
+ const from = points[i * 3 + k];
25729
+ this.points[at * 3 + k] = from + (points[i * 3 + 3 + k] - from) * t;
25730
+ }
25731
+ this.alphas[at] = (alphas[i] + (alphas[i + 1] - alphas[i]) * t) * shown;
25732
+ this.lengths[at] = length;
25733
+ }
25734
+ copy(from, shown) {
25735
+ const at = this.count++;
25736
+ this.points.copyWithin(at * 3, from * 3, from * 3 + 3);
25737
+ this.alphas[at] = this.alphas[from] * shown;
25738
+ this.lengths[at] = this.lengths[from];
25739
+ }
25740
+ };
25741
+ function edge(plane, at, focal, halfW, halfH, near) {
25742
+ if (plane === 0) return at[2] - near;
25743
+ if (plane === 1) return halfW * at[2] - focal * at[0];
25744
+ if (plane === 2) return halfW * at[2] + focal * at[0];
25745
+ if (plane === 3) return halfH * at[2] - focal * at[1];
25746
+ return halfH * at[2] + focal * at[1];
25747
+ }
25748
+
25487
25749
  //#endregion
25488
25750
  //#region src/lib/scene/extensions/polyline.ts
25489
25751
  var REDRAW_EPSILON$1 = 1e-9;
@@ -25493,6 +25755,7 @@ var PolylineExtension = class {
25493
25755
  color;
25494
25756
  widthPx;
25495
25757
  opacity;
25758
+ dash;
25496
25759
  fade;
25497
25760
  closed;
25498
25761
  local;
@@ -25500,6 +25763,7 @@ var PolylineExtension = class {
25500
25763
  mesh = null;
25501
25764
  scratch = new Float32Array(0);
25502
25765
  alphas = new Float32Array(0);
25766
+ seen = new SeenLine();
25503
25767
  capacity = 0;
25504
25768
  lastOrigin = [
25505
25769
  0,
@@ -25514,6 +25778,7 @@ var PolylineExtension = class {
25514
25778
  this.color = options.color ?? "#ffffff";
25515
25779
  this.widthPx = options.widthPx ?? 2;
25516
25780
  this.opacity = options.opacity ?? 1;
25781
+ this.dash = parseDash(options.dash);
25517
25782
  this.fade = options.fade ?? false;
25518
25783
  this.closed = options.closed ?? false;
25519
25784
  this.local = new Float64Array(0);
@@ -25534,6 +25799,10 @@ var PolylineExtension = class {
25534
25799
  }
25535
25800
  if (count > points.length) this.local.copyWithin(points.length * 3, 0, 3);
25536
25801
  this.count = count;
25802
+ if (this.alphas.length < count) {
25803
+ this.scratch = new Float32Array(count * 3);
25804
+ this.alphas = new Float32Array(count);
25805
+ }
25537
25806
  if (count > this.capacity) this.rebuild(count);
25538
25807
  this.fillAlphas();
25539
25808
  this.dirty = true;
@@ -25552,38 +25821,35 @@ var PolylineExtension = class {
25552
25821
  this.removeSelf?.();
25553
25822
  this.removeSelf = null;
25554
25823
  }
25555
- update({ jd, basis, camera, ctx }) {
25556
- const mesh = this.mesh;
25557
- if (!mesh) return;
25824
+ update({ jd, basis, camera, viewportPx, ctx }) {
25558
25825
  const world = this.wanted && this.count >= 2 ? resolveAnchor(this.anchor, ctx, jd) : null;
25559
- mesh.visible = world !== null;
25560
- if (!world) return;
25826
+ if (this.mesh) this.mesh.visible = world !== null;
25827
+ if (!this.mesh || !world) return;
25561
25828
  const origin = [
25562
25829
  world[0] - basis[0],
25563
25830
  world[1] - basis[1],
25564
25831
  world[2] - basis[2]
25565
25832
  ];
25566
- if (this.dirty || Math.abs(origin[0] - this.lastOrigin[0]) > REDRAW_EPSILON$1 || Math.abs(origin[1] - this.lastOrigin[1]) > REDRAW_EPSILON$1 || Math.abs(origin[2] - this.lastOrigin[2]) > REDRAW_EPSILON$1) {
25567
- this.writeVertices(mesh, origin);
25833
+ const moved = this.dirty || Math.abs(origin[0] - this.lastOrigin[0]) > REDRAW_EPSILON$1 || Math.abs(origin[1] - this.lastOrigin[1]) > REDRAW_EPSILON$1 || Math.abs(origin[2] - this.lastOrigin[2]) > REDRAW_EPSILON$1;
25834
+ if (moved) {
25835
+ for (let i = 0; i < this.count * 3; i++) this.scratch[i] = this.local[i] + origin[i % 3];
25568
25836
  this.lastOrigin = origin;
25569
- this.dirty = false;
25570
25837
  }
25571
- mesh.material.uniforms.uCenterOffset.value.copy(camera.position).negate();
25572
- }
25573
- writeVertices(mesh, origin) {
25574
- for (let i = 0; i < this.count; i++) {
25575
- this.scratch[i * 3] = this.local[i * 3] + origin[0];
25576
- this.scratch[i * 3 + 1] = this.local[i * 3 + 1] + origin[1];
25577
- this.scratch[i * 3 + 2] = this.local[i * 3 + 2] + origin[2];
25578
- }
25579
- writeFatTrailVertices(mesh.geometry, this.scratch, this.alphas, this.alphas, this.count);
25838
+ const cut = this.dash !== null || reachesBehind(this.scratch, this.count, camera);
25839
+ if (cut) {
25840
+ const { seen } = this;
25841
+ seen.see(this.scratch, this.alphas, this.count, camera, viewportPx);
25842
+ if (seen.count > this.capacity) this.rebuild(seen.count);
25843
+ writeFatTrailVertices(this.mesh.geometry, seen.points, seen.alphas, seen.alphas, seen.count);
25844
+ if (this.dash) writeLineDistances(this.mesh.geometry, seen.lengths, seen.count);
25845
+ } else if (moved) writeFatTrailVertices(this.mesh.geometry, this.scratch, this.alphas, this.alphas, this.count);
25846
+ this.dirty = cut;
25847
+ this.mesh.material.uniforms.uCenterOffset.value.copy(camera.position).negate();
25580
25848
  }
25581
25849
  rebuild(capacity) {
25582
25850
  this.disposeMesh();
25583
25851
  this.dirty = true;
25584
- this.scratch = new Float32Array(capacity * 3);
25585
- this.alphas = new Float32Array(capacity);
25586
- const mesh = buildFatLineFromThin(capacity, this.scratch, this.alphas, this.alphas, capacity, this.color, this.widthPx, 1);
25852
+ const mesh = buildFatLineFromThin(capacity, this.scratch, this.alphas, this.alphas, 0, this.color, this.widthPx, 1, this.dash ?? void 0);
25587
25853
  mesh.frustumCulled = false;
25588
25854
  this.object.add(mesh);
25589
25855
  this.mesh = mesh;
@@ -26772,6 +27038,7 @@ var SurfaceShapeExtension = class {
26772
27038
  color;
26773
27039
  widthPx;
26774
27040
  opacity;
27041
+ dash;
26775
27042
  fillColor;
26776
27043
  fillOpacity;
26777
27044
  dirs = new Float64Array(0);
@@ -26779,6 +27046,7 @@ var SurfaceShapeExtension = class {
26779
27046
  line = null;
26780
27047
  scratch = new Float32Array(0);
26781
27048
  alphas = new Float32Array(0);
27049
+ lengths = new Float32Array(0);
26782
27050
  fanDirs = new Float64Array(0);
26783
27051
  fill = null;
26784
27052
  fillScratch = new Float32Array(0);
@@ -26798,6 +27066,7 @@ var SurfaceShapeExtension = class {
26798
27066
  this.color = options.color ?? "#ffffff";
26799
27067
  this.widthPx = options.widthPx ?? 2;
26800
27068
  this.opacity = options.opacity ?? 1;
27069
+ this.dash = parseDash(options.dash);
26801
27070
  this.fillColor = options.fill ?? null;
26802
27071
  this.fillOpacity = options.fillOpacity ?? .25;
26803
27072
  this.object.matrixAutoUpdate = false;
@@ -26830,7 +27099,7 @@ var SurfaceShapeExtension = class {
26830
27099
  this.removeSelf?.();
26831
27100
  this.removeSelf = null;
26832
27101
  }
26833
- update({ jd, basis, camera, ctx }) {
27102
+ update({ jd, basis, camera, viewportPx, ctx }) {
26834
27103
  const body = this.wanted && this.count >= 2 ? ctx.getBody(this.body) : void 0;
26835
27104
  const centre = body && bodyCentre(body, ctx, jd);
26836
27105
  if (this.line) this.line.visible = !!centre;
@@ -26847,6 +27116,10 @@ var SurfaceShapeExtension = class {
26847
27116
  if (this.line) {
26848
27117
  this.place(this.dirs, this.count, q, scale, origin, this.scratch);
26849
27118
  writeFatTrailVertices(this.line.geometry, this.scratch, this.alphas, this.alphas, this.count);
27119
+ if (this.dash) {
27120
+ screenLengths(this.scratch, this.count, camera, viewportPx, this.lengths);
27121
+ writeLineDistances(this.line.geometry, this.lengths, this.count);
27122
+ }
26850
27123
  this.line.material.uniforms.uCenterOffset.value.copy(camera.position).negate();
26851
27124
  }
26852
27125
  if (this.fill) {
@@ -26875,7 +27148,8 @@ var SurfaceShapeExtension = class {
26875
27148
  if (this.count < 2 || this.widthPx <= 0) return;
26876
27149
  this.scratch = new Float32Array(this.count * 3);
26877
27150
  this.alphas = new Float32Array(this.count).fill(this.opacity);
26878
- const line = buildFatLineFromThin(this.count, this.scratch, this.alphas, this.alphas, this.count, this.color, this.widthPx, 1);
27151
+ this.lengths = new Float32Array(this.count);
27152
+ const line = buildFatLineFromThin(this.count, this.scratch, this.alphas, this.alphas, this.count, this.color, this.widthPx, 1, this.dash ?? void 0);
26879
27153
  line.frustumCulled = false;
26880
27154
  line.renderOrder = SURFACE_RENDER_ORDER;
26881
27155
  this.object.add(line);
@@ -27088,13 +27362,7 @@ function anchorDistanceKm(a, b, ctx, jd) {
27088
27362
  const offset = anchorOffsetKm(a, b, ctx, jd);
27089
27363
  return offset && Math.hypot(...offset);
27090
27364
  }
27091
- function subsolarPoint(id, ctx, jd) {
27092
- const body = ctx.getBody(id);
27093
- const sun = ctx.getBody(SUN_ID);
27094
- if (!body || !sun || body === sun) return null;
27095
- if (!body.orientation && !ctx.bodies.orientationRead.has(id)) return null;
27096
- const centre = bodyCentre(body, ctx, jd);
27097
- if (!centre) return null;
27365
+ function subsolarAt(body, centre, sun, jd) {
27098
27366
  const turn = body.orientation ? bodyQuaternion(body.orientation, jd, body.nutPrec).toArray() : [
27099
27367
  0,
27100
27368
  0,
@@ -27102,9 +27370,9 @@ function subsolarPoint(id, ctx, jd) {
27102
27370
  1
27103
27371
  ];
27104
27372
  const { latitude, longitude, distance } = cartesianToSpherical([
27105
- sun.position[0],
27106
- sun.position[1],
27107
- sun.position[2]
27373
+ sun[0],
27374
+ sun[1],
27375
+ sun[2]
27108
27376
  ], [
27109
27377
  centre[0],
27110
27378
  centre[1],
@@ -27115,6 +27383,14 @@ function subsolarPoint(id, ctx, jd) {
27115
27383
  lat: latitude
27116
27384
  } : null;
27117
27385
  }
27386
+ function subsolarPoint$1(id, ctx, jd) {
27387
+ const body = ctx.getBody(id);
27388
+ const sun = ctx.getBody(SUN_ID);
27389
+ if (!body || !sun || body === sun) return null;
27390
+ if (!body.orientation && !ctx.bodies.orientationRead.has(id)) return null;
27391
+ const centre = bodyCentre(body, ctx, jd);
27392
+ return centre && subsolarAt(body, centre, sun.position, jd);
27393
+ }
27118
27394
 
27119
27395
  //#endregion
27120
27396
  //#region src/lib/interaction/gesture.ts
@@ -27434,7 +27710,7 @@ var SpaceMap = class {
27434
27710
  return anchorOffsetKm(from, to, this.ctx, this.placedJd);
27435
27711
  }
27436
27712
  getSubsolarPoint(id) {
27437
- return subsolarPoint(id, this.ctx, this.placedJd);
27713
+ return subsolarPoint$1(id, this.ctx, this.placedJd);
27438
27714
  }
27439
27715
  load(targetId = this.initialView.id) {
27440
27716
  return this.ctx.load(jdToDate(this.clock.jd), targetId);
@@ -27914,6 +28190,10 @@ var SpaceMap = class {
27914
28190
  this.requireRenderer().setBodyAppearance(id, appearance);
27915
28191
  return this;
27916
28192
  }
28193
+ setPinnedBodies(ids) {
28194
+ this.requireRenderer().setPinnedBodies(ids);
28195
+ return this;
28196
+ }
27917
28197
  capRadiusDeg(options) {
27918
28198
  if (options.radiusDeg !== void 0) return options.radiusDeg;
27919
28199
  const bodyRadiusKm = this.getBody(options.body)?.radiusKm;
@@ -31979,6 +32259,158 @@ function kmToAu(km) {
31979
32259
  return km / AU_KM;
31980
32260
  }
31981
32261
 
32262
+ //#endregion
32263
+ //#region src/lib/scene/extensions/ephemeris.ts
32264
+ var chebyshev = null;
32265
+ var queue = Promise.resolve();
32266
+ function inTurn(read) {
32267
+ const turn = queue.then(read, read);
32268
+ queue = turn.catch(() => {});
32269
+ return turn;
32270
+ }
32271
+ async function open(date) {
32272
+ const jd = dateToJD(date);
32273
+ const metadata = await fetchMetadata();
32274
+ if (!chebyshev) {
32275
+ const params = chebyshevZoneParams(metadata);
32276
+ if (params.size > 0) chebyshev = new ChebyshevStore(params);
32277
+ }
32278
+ try {
32279
+ await chebyshev?.ensure(jd).done;
32280
+ } catch (error) {
32281
+ chebyshev = null;
32282
+ throw error;
32283
+ }
32284
+ return {
32285
+ jd,
32286
+ metadata,
32287
+ chebyshev
32288
+ };
32289
+ }
32290
+ function discovered(visibleFromDays, jd) {
32291
+ return !(visibleFromDays !== void 0 && jd - 2451545 < visibleFromDays);
32292
+ }
32293
+ async function moonRow(id, { jd, metadata }) {
32294
+ const zone = metadata.position.zones.moons;
32295
+ const zoom = zone && flatZoom(zone);
32296
+ if (!zoom) return null;
32297
+ const cols = await fetchElements("moons", null, 0, isChunkIndexed(zoom) ? String(chunkIndexForJd(zoom, jd)) : null);
32298
+ for (let i = 0; i < cols.rowCount; i++) if (rowId(cols, i) === id) return materializeBodyData(cols, i, /* @__PURE__ */ new Map(), "naif");
32299
+ return null;
32300
+ }
32301
+ function elementsOffset(data, jd) {
32302
+ if (!discovered(data.visibleFromDays, jd)) return null;
32303
+ if (jd < data.validityStart || jd > data.validityEnd) return null;
32304
+ if (data.satrec) return sgp4PositionScene(data.satrec, jd);
32305
+ if (data.q != null) return parabolicToPositionJD(data, jd);
32306
+ if (data.a === 0) return [
32307
+ 0,
32308
+ 0,
32309
+ 0
32310
+ ];
32311
+ return orbitalElementsToPositionJD(data, jd);
32312
+ }
32313
+ async function locate(id, at) {
32314
+ const { jd } = at;
32315
+ if (at.chebyshev?.has(id)) {
32316
+ const body = at.chebyshev.body(id, jd);
32317
+ if (body && !discovered(body.visibleFromDays, jd)) return null;
32318
+ const offset = body && chebyshevPositionScene(body, jd);
32319
+ if (offset) {
32320
+ const { radiusKm, objectType } = body;
32321
+ return {
32322
+ data: {
32323
+ radiusKm,
32324
+ objectType,
32325
+ orbitalSource: OrbitalSource.SPICE
32326
+ },
32327
+ parentId: `naif-${body.parentId}`,
32328
+ offset
32329
+ };
32330
+ }
32331
+ if (id.startsWith("naif-")) return null;
32332
+ }
32333
+ const data = (id.startsWith("naif-") ? await moonRow(id, at) : null) ?? bodyDataFromGlobal(id, await fetchObjectDetail(id, false));
32334
+ const offset = data && elementsOffset(data, jd);
32335
+ return offset ? {
32336
+ data,
32337
+ parentId: data.parentId,
32338
+ offset
32339
+ } : null;
32340
+ }
32341
+ async function place(id, at, above = /* @__PURE__ */ new Set()) {
32342
+ if (above.has(id)) return null;
32343
+ const own = await locate(id, at);
32344
+ if (!own) return null;
32345
+ if (own.parentId === "naif-0") return {
32346
+ data: own.data,
32347
+ centre: own.offset
32348
+ };
32349
+ const parent = await place(own.parentId, at, new Set(above).add(id));
32350
+ if (!parent) return null;
32351
+ const [x, y, z] = parent.centre;
32352
+ return {
32353
+ data: own.data,
32354
+ centre: [
32355
+ x + own.offset[0],
32356
+ y + own.offset[1],
32357
+ z + own.offset[2]
32358
+ ]
32359
+ };
32360
+ }
32361
+ async function spinOf(id) {
32362
+ const [detail] = await Promise.all([fetchObjectDetail(id, false), loadSystemsGlobal()]);
32363
+ const orientation = detail.global?.orientation;
32364
+ const sums = detail.global?.nut_prec;
32365
+ const naif = /^naif-(-?\d+)$/.exec(id);
32366
+ const angles = naif ? getNutPrecAngles(ownerIdFor(parseInt(naif[1], 10))) : void 0;
32367
+ return {
32368
+ orientation,
32369
+ nutPrec: sums && angles ? {
32370
+ ...sums,
32371
+ angles
32372
+ } : void 0
32373
+ };
32374
+ }
32375
+ async function resolve(anchor, at) {
32376
+ const placed = await place(anchor.body, at);
32377
+ if (!placed) return null;
32378
+ const spin = "latitude" in anchor ? await spinOf(anchor.body) : {};
32379
+ return placeAnchor(anchor, {
32380
+ data: placed.data,
32381
+ ...spin
32382
+ }, placed.centre, at.jd);
32383
+ }
32384
+ function offsetKm(from, to, date) {
32385
+ return inTurn(async () => {
32386
+ const at = await open(date);
32387
+ const a = await resolve(from, at);
32388
+ const b = a && await resolve(to, at);
32389
+ return a && b ? sceneToEcliptic([
32390
+ b[0] - a[0],
32391
+ b[1] - a[1],
32392
+ b[2] - a[2]
32393
+ ]) : null;
32394
+ });
32395
+ }
32396
+ async function distanceKm(from, to, date) {
32397
+ const offset = await offsetKm(from, to, date);
32398
+ return offset && Math.hypot(...offset);
32399
+ }
32400
+ function subsolarPoint(id, date) {
32401
+ return inTurn(async () => {
32402
+ if (id === "naif-10") return null;
32403
+ const at = await open(date);
32404
+ const body = await place(id, at);
32405
+ const sun = body && await place("naif-10", at);
32406
+ if (!body || !sun) return null;
32407
+ return subsolarAt({
32408
+ data: body.data,
32409
+ ...await spinOf(id)
32410
+ }, body.centre, sun.centre, at.jd);
32411
+ });
32412
+ }
32413
+
31982
32414
  //#endregion
31983
32415
  //#region src/lib/fetch/panoramas.ts
31984
32416
  var index = null;
@@ -32099,5 +32531,5 @@ function applyHost(options) {
32099
32531
  }
32100
32532
 
32101
32533
  //#endregion
32102
- export { AttributionControl, FlatAttributionControl, FlatMap, GestureHandler, MAP_LAYERS, PROJECTION_IDS, PanoramaAttributionControl, PanoramaView, SpaceMap, SystemMap, Viewport, bearingDeg, boxRing, circlePoints, configureHost, createFlatMap, createMap, createPanorama, createProjection, createSystemMap, dateToJD, defaultSceneSettings, elements, fetchPanoramaIndex, fetchPanoramas, findPanorama, fixed, graticule, groundDistanceM, isViewable, jdToDate, kmToAu, neighboursOf, panoramaAt, pathFor, planeBasis, projectionAspect, samples, smallCircle, tle };
32534
+ 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 };
32103
32535
  (function(){if(typeof document==='undefined')return;var s=document.createElement('style');s.textContent=".scene-label{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;color:inherit;touch-action:none;z-index:1;width:32px;height:32px;text-decoration:none;display:block;position:relative;left:0}.scene-label__halo{border-radius:50%;width:100%;height:100%;transition:transform .1s}.scene-label__halo--spacecraft,.scene-label__halo--debris{overflow:visible}.scene-label__loader{box-sizing:border-box;pointer-events:none;border:2px solid #fff;border-color:#fff #fff #fff3 #fff3;border-radius:50%;width:32px;height:32px;margin:auto;animation:.9s linear infinite scene-label-loader-spin;position:fixed;inset:0}@keyframes scene-label-loader-spin{to{transform:rotate(360deg)}}.reduce-motion .scene-label__loader{animation-duration:.9s!important;animation-iteration-count:infinite!important}.scene-label__name{white-space:nowrap;pointer-events:auto;cursor:pointer;color:#fff;text-shadow:0 0 4px #000,0 0 4px #000;padding-left:8px;font-size:14px;font-weight:700;text-decoration:none;position:absolute;top:50%;left:32px;transform:translateY(-50%)}.scene-label__name--large{font-size:16px}.scene-label__annotations{top:28px;pointer-events:none;flex-direction:column;padding-inline-start:8px;display:flex;position:absolute;inset-inline-start:32px}.scene-label__annotation{opacity:0;white-space:nowrap;color:#fff9;text-shadow:0 0 3px #000,0 0 3px #000;transition:opacity .15s, display .15s allow-discrete;font-size:11px;line-height:1.3;display:none}.scene-label__annotation--visible{opacity:1;display:block}@starting-style{.scene-label__annotation--visible{opacity:0}}.scene-feature-label{color:#ffffffb3;white-space:nowrap;pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 0 2px #000,0 0 2px #000;z-index:0;font-size:12px;transition:color .1s;left:0}.scene-feature-label:hover{color:#fff;text-shadow:0 0 3px #000,0 0 3px #000}.scene-feature-label--active{color:#fff;font-size:14px;font-weight:600}.scene-path-label{color:#fff;font:inherit;white-space:nowrap;text-align:start;text-shadow:0 0 4px #000,0 0 4px #000;pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none;z-index:2;background:0 0;border:0;align-items:center;gap:8px;padding:0;line-height:1.15;display:flex;left:0}.scene-path-label--static{pointer-events:none;cursor:default}.scene-path-label__halo{border-radius:50%;flex-shrink:0;width:32px;height:32px;margin-inline-start:-16px;transition:transform .1s}.scene-path-label:hover .scene-path-label__halo{transform:scale(1.15)}.scene-path-label__text{flex-direction:column;gap:1px;display:flex}.scene-path-label__name{font-size:14px;font-weight:700}.scene-path-label__when{opacity:.8;font-variant-numeric:tabular-nums;font-size:12px}.scene-path-label--dim .scene-path-label__text{display:none}.scene-path-label--dim .scene-path-label__halo{transform:scale(.55)}.scene-path-label--dim:hover .scene-path-label__halo{transform:scale(.75)}.scene-path-label--step .scene-path-label__halo{background:0 0;border:0}.scene-path-label--step:hover .scene-path-label__halo{background:#ffffff1f}.scene-path-label--step .scene-path-label__name{font-size:12px;font-weight:600}.scene-path-label--step .scene-path-label__when{font-size:11px}.scene-path-label__hazard{font-size:11px;font-weight:700}.scene-path-label--lit{color:#fff}.scene-path-label--lit .scene-path-label__halo{transform:scale(1.15)}.scene-path-label--lit.scene-path-label--dim .scene-path-label__halo{transform:scale(.75)}.scene-path-label--faint .scene-path-label__text{display:none}.scene-path-label--faint .scene-path-label__halo{opacity:.45;transform:scale(.5)}.scene-path-label--faint:hover .scene-path-label__halo{opacity:1;transform:scale(.75)}.scene-overlay--no-names .scene-label__name,.scene-overlay--no-names .scene-label__annotations{display:none}.scene-hazard-label{white-space:nowrap;pointer-events:none;-webkit-user-select:none;user-select:none;text-shadow:0 0 4px #000,0 0 4px #000;margin-inline-start:16px;font-size:12px;font-weight:700;position:relative;left:0}.user-location-marker{pointer-events:none;background:#1a73e8;border:2px solid #fff;border-radius:50%;width:16px;height:16px;box-shadow:0 0 6px #0006}.sm-drawing-label{white-space:nowrap;color:#fff;text-shadow:0 0 4px #000,0 0 4px #000;-webkit-user-select:none;user-select:none;pointer-events:none;font:700 14px/1.2 system-ui,sans-serif}.sm-drawing-icon{-webkit-user-select:none;user-select:none;pointer-events:none;display:block}.sm-drawing--interactive{pointer-events:auto;cursor:pointer;touch-action:none}.sm-cooperative-hint{z-index:4;color:#fff;text-align:center;pointer-events:none;opacity:0;background:#0000008c;place-content:center;padding:16px;font:15px/1.4 system-ui,sans-serif;transition:opacity .2s;display:grid;position:absolute;inset:0}.sm-cooperative-hint--shown{opacity:1}@media (prefers-reduced-motion:reduce){.sm-cooperative-hint{transition:none}}.sm-ctrl{z-index:2;pointer-events:none;flex-direction:column;gap:6px;max-width:100%;padding:6px;display:flex;position:absolute}.sm-ctrl>*{pointer-events:auto}.sm-ctrl--top-left{align-items:flex-start;top:0;left:0}.sm-ctrl--top-right{align-items:flex-end;top:0;right:0}.sm-ctrl--bottom-left{align-items:flex-start;bottom:0;left:0}.sm-ctrl--bottom-right{align-items:flex-end;bottom:0;right:0}.sm-map{-webkit-user-select:none;user-select:none}:where(.sm-map){width:100%;height:100%;position:relative}.sm-map__canvas{pointer-events:auto;touch-action:none;width:100%;height:100%;display:block}.sm-map__canvas:focus-visible{outline:2px solid var(--ring,Highlight)}.sm-map .scene-overlay{pointer-events:none;z-index:0;position:absolute;inset:0}.sm-flat{touch-action:none;background:#05070e;width:100%;height:100%;position:relative;overflow:hidden}.sm-flat__raster,.sm-flat__vector,.sm-flat__markers{width:100%;height:100%;position:absolute;inset:0}.sm-flat__raster{display:block}.sm-flat__vector,.sm-flat__markers{pointer-events:none}.sm-flat--interactive{cursor:grab}.sm-flat--cooperative{touch-action:pan-x pan-y}.sm-flat--dragging{cursor:grabbing}.sm-flat__graticule{fill:none;stroke:#ffffff47;stroke-width:1px}.sm-flat__frame{fill:none;stroke:#ffffff73;stroke-width:1px}.sm-flat__feature-dot{fill:#fffc}.sm-flat__feature-label{fill:#ffffffd9;paint-order:stroke;stroke:#000000a6;stroke-width:2.5px;font:10px/1 system-ui,sans-serif}.sm-flat__marker-dot{background:#4ea3ff;border:2px solid #fff;border-radius:50%;width:10px;height:10px;box-shadow:0 0 0 1px #00000073}.sm-system-map{border-radius:var(--sm-system-map-radius,6px);background:var(--sm-system-map-background,#ffffff0d);width:100%;color:var(--sm-system-map-ink,#ffffff9e);position:relative;overflow:hidden}.sm-system-map--background{pointer-events:none;background:0 0;border-radius:0;height:100%}.sm-system-map__svg{touch-action:pan-y;width:100%;height:auto;display:block}.sm-system-map--background .sm-system-map__svg{height:100%}.sm-system-map__svg--near :is(a,.sm-system-map__button):not([data-near]){pointer-events:none}.sm-system-map__button{cursor:pointer}.sm-system-map__button:focus{outline:none}.sm-system-map__reference{opacity:.6}.sm-system-map__band--muted{color:var(--sm-system-map-ink,#ffffff9e)}.sm-system-map__band--sky{color:var(--sm-system-map-sky,oklch(74.6% .16 232.661/.7))}.sm-system-map__band--amber{color:var(--sm-system-map-amber,oklch(92.4% .12 95.746/.8))}.sm-system-map__band-label{font-weight:600}.sm-system-map__band-label--muted{fill:var(--sm-system-map-ink,#ffffff9e)}.sm-system-map__band-label--sky{fill:var(--sm-system-map-sky-label,oklch(74.6% .16 232.661))}.sm-system-map__band-label--amber{fill:var(--sm-system-map-amber-label,oklch(92.4% .12 95.746/.9))}.sm-system-map__tip{z-index:10;background:var(--sm-system-map-tip-background,#0a0c14e6);width:max-content;color:var(--sm-system-map-tip-ink,#fffffff2);white-space:nowrap;pointer-events:none;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1.33333;position:absolute;transform:translate(-50%);box-shadow:0 1px 3px #0000001a,0 1px 2px -1px #0000001a}.sm-system-map__tip[hidden]{display:none}.sm-system-map__tip-title{font-weight:500}.sm-system-map__tip-sub{color:var(--sm-system-map-ink,#ffffff9e)}.sm-ctrl:has(>.sm-attribution:only-child){padding:0}.sm-ctrl--top-left:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:0 0 var(--sm-attribution-radius) 0}.sm-ctrl--top-right:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:0 0 0 var(--sm-attribution-radius)}.sm-ctrl--bottom-left:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:0 var(--sm-attribution-radius) 0 0}.sm-ctrl--bottom-right:has(>.sm-attribution:only-child)>.sm-attribution{border-radius:var(--sm-attribution-radius) 0 0 0}.sm-attribution{--sm-attribution-radius:3px;border-radius:var(--sm-attribution-radius);color:#ffffffbf;white-space:nowrap;background:#0006;align-items:center;gap:.5em;max-width:100%;padding:1px 6px;font:11px/1.45 system-ui,sans-serif;display:flex}.sm-attribution a{color:inherit;text-decoration:none}.sm-attribution a:hover{color:#fff;text-decoration:underline}.sm-attribution__home{font-weight:600}.sm-attribution__group{text-overflow:ellipsis;overflow:hidden}.sm-attribution__label{color:#ffffff80}\n/*$vite$:1*/";document.head.appendChild(s);})();