astro-viewer 3.11.0 → 3.13.0

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.
Files changed (88) hide show
  1. package/dist/astroviewer.cjs +837 -173
  2. package/dist/astroviewer.cjs.map +1 -1
  3. package/dist/astroviewer.js +837 -173
  4. package/dist/astroviewer.js.map +1 -1
  5. package/dist/astroviewer.min.js +1 -1
  6. package/dist/astroviewer.min.js.map +1 -1
  7. package/lib-esm/AstroSphere.d.ts +57 -2
  8. package/lib-esm/AstroSphere.d.ts.map +1 -1
  9. package/lib-esm/AstroSphere.js +370 -83
  10. package/lib-esm/AstroSphere.js.map +1 -1
  11. package/lib-esm/AstroViewer.d.ts +21 -1
  12. package/lib-esm/AstroViewer.d.ts.map +1 -1
  13. package/lib-esm/AstroViewer.js +56 -7
  14. package/lib-esm/AstroViewer.js.map +1 -1
  15. package/lib-esm/Camera.d.ts +22 -0
  16. package/lib-esm/Camera.d.ts.map +1 -1
  17. package/lib-esm/Camera.js +71 -0
  18. package/lib-esm/Camera.js.map +1 -1
  19. package/lib-esm/index.d.ts +1 -0
  20. package/lib-esm/index.d.ts.map +1 -1
  21. package/lib-esm/index.js +1 -0
  22. package/lib-esm/index.js.map +1 -1
  23. package/lib-esm/model/Source.js +1 -1
  24. package/lib-esm/model/Source.js.map +1 -1
  25. package/lib-esm/model/catalogues/CatalogueGL.d.ts +13 -4
  26. package/lib-esm/model/catalogues/CatalogueGL.d.ts.map +1 -1
  27. package/lib-esm/model/catalogues/CatalogueGL.js +109 -59
  28. package/lib-esm/model/catalogues/CatalogueGL.js.map +1 -1
  29. package/lib-esm/model/earth/XYZMap.d.ts +4 -1
  30. package/lib-esm/model/earth/XYZMap.d.ts.map +1 -1
  31. package/lib-esm/model/earth/XYZMap.js +35 -2
  32. package/lib-esm/model/earth/XYZMap.js.map +1 -1
  33. package/lib-esm/model/footprints/FootprintSetGL.d.ts +1 -0
  34. package/lib-esm/model/footprints/FootprintSetGL.d.ts.map +1 -1
  35. package/lib-esm/model/footprints/FootprintSetGL.js +13 -3
  36. package/lib-esm/model/footprints/FootprintSetGL.js.map +1 -1
  37. package/lib-esm/model/grid/EquatorialGrid.d.ts +1 -0
  38. package/lib-esm/model/grid/EquatorialGrid.d.ts.map +1 -1
  39. package/lib-esm/model/grid/EquatorialGrid.js +3 -0
  40. package/lib-esm/model/grid/EquatorialGrid.js.map +1 -1
  41. package/lib-esm/model/grid/HealpixGrid.d.ts +1 -0
  42. package/lib-esm/model/grid/HealpixGrid.d.ts.map +1 -1
  43. package/lib-esm/model/grid/HealpixGrid.js +3 -0
  44. package/lib-esm/model/grid/HealpixGrid.js.map +1 -1
  45. package/lib-esm/model/grid/LonLatGrid.d.ts +1 -0
  46. package/lib-esm/model/grid/LonLatGrid.d.ts.map +1 -1
  47. package/lib-esm/model/grid/LonLatGrid.js +3 -0
  48. package/lib-esm/model/grid/LonLatGrid.js.map +1 -1
  49. package/lib-esm/model/hips/AllSky.d.ts.map +1 -1
  50. package/lib-esm/model/hips/AllSky.js +4 -2
  51. package/lib-esm/model/hips/AllSky.js.map +1 -1
  52. package/lib-esm/model/hips/AncestorTile.d.ts.map +1 -1
  53. package/lib-esm/model/hips/AncestorTile.js +4 -2
  54. package/lib-esm/model/hips/AncestorTile.js.map +1 -1
  55. package/lib-esm/model/hips/Tile.d.ts.map +1 -1
  56. package/lib-esm/model/hips/Tile.js +4 -2
  57. package/lib-esm/model/hips/Tile.js.map +1 -1
  58. package/lib-esm/model/hips/TileBuffer.d.ts +11 -0
  59. package/lib-esm/model/hips/TileBuffer.d.ts.map +1 -1
  60. package/lib-esm/model/hips/TileBuffer.js +47 -0
  61. package/lib-esm/model/hips/TileBuffer.js.map +1 -1
  62. package/lib-esm/model/hips/VisibleTilesManager.d.ts.map +1 -1
  63. package/lib-esm/model/hips/VisibleTilesManager.js +8 -2
  64. package/lib-esm/model/hips/VisibleTilesManager.js.map +1 -1
  65. package/lib-esm/model/meships/MeshHiPS.d.ts +4 -0
  66. package/lib-esm/model/meships/MeshHiPS.d.ts.map +1 -1
  67. package/lib-esm/model/meships/MeshHiPS.js +11 -1
  68. package/lib-esm/model/meships/MeshHiPS.js.map +1 -1
  69. package/lib-esm/model/terra/TerraPointSetGL.d.ts +1 -0
  70. package/lib-esm/model/terra/TerraPointSetGL.d.ts.map +1 -1
  71. package/lib-esm/model/terra/TerraPointSetGL.js +39 -1
  72. package/lib-esm/model/terra/TerraPointSetGL.js.map +1 -1
  73. package/lib-esm/shader/CatalogueShaderProgram.d.ts +4 -0
  74. package/lib-esm/shader/CatalogueShaderProgram.d.ts.map +1 -1
  75. package/lib-esm/shader/CatalogueShaderProgram.js +6 -0
  76. package/lib-esm/shader/CatalogueShaderProgram.js.map +1 -1
  77. package/lib-esm/shader/ShaderManager.d.ts.map +1 -1
  78. package/lib-esm/shader/ShaderManager.js +7 -4
  79. package/lib-esm/shader/ShaderManager.js.map +1 -1
  80. package/lib-esm/shader/XYZShaderProgram.d.ts +3 -0
  81. package/lib-esm/shader/XYZShaderProgram.d.ts.map +1 -1
  82. package/lib-esm/shader/XYZShaderProgram.js +11 -3
  83. package/lib-esm/shader/XYZShaderProgram.js.map +1 -1
  84. package/lib-esm/utils/Utils.d.ts +12 -0
  85. package/lib-esm/utils/Utils.d.ts.map +1 -1
  86. package/lib-esm/utils/Utils.js +26 -0
  87. package/lib-esm/utils/Utils.js.map +1 -1
  88. package/package.json +2 -2
@@ -3630,12 +3630,18 @@ class AstroSphere {
3630
3630
  _activeHiPS = null;
3631
3631
  _activeHiPSLayers = [];
3632
3632
  _activeXYZ2 = null;
3633
+ _earthRasterOverlays = [];
3634
+ _earthRasterOverlaySequence = 0;
3633
3635
  _activeMeshHiPS = null;
3634
3636
  _activeBaseLayer = null;
3637
+ _activeDomain = "astronomy";
3638
+ domainCameraStates = {};
3635
3639
  startup = true;
3636
3640
  fov;
3637
- activeCatalogues = [];
3638
- activeFootprintSets = [];
3641
+ astronomyCatalogues = [];
3642
+ earthPointSets = [];
3643
+ astronomyFootprintSets = [];
3644
+ earthFootprintSets = [];
3639
3645
  activePolylineSets = [];
3640
3646
  activeSensorCones = [];
3641
3647
  activeSatelliteObjects = [];
@@ -3651,6 +3657,7 @@ class AstroSphere {
3651
3657
  lockedNorthSouthDecDeg = null;
3652
3658
  keepCameraNorthUp = true;
3653
3659
  gridLabelContainers;
3660
+ fovAnimationResolve = null;
3654
3661
  constructor(canvas, webgl, options = {}) {
3655
3662
  console.log("[AstroSphere] new instance for canvas", canvas.id);
3656
3663
  // Keep global GL context (as in original JS)
@@ -3868,9 +3875,7 @@ class AstroSphere {
3868
3875
  return true;
3869
3876
  }
3870
3877
  emitCameraChanged(reason) {
3871
- // avoid dispatch before scene is ready
3872
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
3873
- return;
3878
+ // Camera interaction remains meaningful even when no base layer is active.
3874
3879
  if (!this._healpixGrid?.fovObj)
3875
3880
  return;
3876
3881
  const detail = this.getCurrentStatus();
@@ -3897,6 +3902,7 @@ class AstroSphere {
3897
3902
  canvas.setPointerCapture(event.pointerId);
3898
3903
  this.mouseDown = true;
3899
3904
  this._camera.cancelFlyTo();
3905
+ this.cancelFoVAnimation();
3900
3906
  const rect = canvas.getBoundingClientRect();
3901
3907
  this.lastMouseX = event.clientX - rect.left; // locale al canvas
3902
3908
  this.lastMouseY = event.clientY - rect.top; // locale al canvas
@@ -3935,8 +3941,11 @@ class AstroSphere {
3935
3941
  if (mousePoint && mousePoint.length > 0) {
3936
3942
  this.mouseHelper.update(mousePoint);
3937
3943
  this.updateLastMousePoint();
3938
- for (const cat of this.activeCatalogues) {
3939
- const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper);
3944
+ for (const cat of this.getActivePointSets()) {
3945
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
3946
+ if (!activeModelMatrix)
3947
+ continue;
3948
+ const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
3940
3949
  if (!clickResult?.sources.length)
3941
3950
  continue;
3942
3951
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-clicked", {
@@ -3949,7 +3958,7 @@ class AstroSphere {
3949
3958
  composed: true,
3950
3959
  }));
3951
3960
  }
3952
- for (const fset of this.activeFootprintSets) {
3961
+ for (const fset of this.getActiveFootprintSets()) {
3953
3962
  const clickResult = fset.selectPrimaryFootprintFromClick(this.mouseHelper);
3954
3963
  if (!clickResult?.footprints.length)
3955
3964
  continue;
@@ -4024,6 +4033,7 @@ class AstroSphere {
4024
4033
  const currentFov = this._healpixGrid.getMinFoV();
4025
4034
  const zoomStep = this.computeZoomStep(currentFov, event.deltaY);
4026
4035
  this._camera.cancelFlyTo();
4036
+ this.cancelFoVAnimation();
4027
4037
  // Apply wheel zoom immediately and discard any queued inertia so reversing
4028
4038
  // direction feels responsive instead of "buffered".
4029
4039
  this.zoomInertia = 0;
@@ -4046,8 +4056,11 @@ class AstroSphere {
4046
4056
  }
4047
4057
  this.mouseHelper.update(mousePoint);
4048
4058
  this.updateLastMousePoint();
4049
- for (const cat of this.activeCatalogues) {
4050
- const pickResult = cat.getSourcesFromPointer(this.mouseHelper);
4059
+ for (const cat of this.getActivePointSets()) {
4060
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4061
+ if (!activeModelMatrix)
4062
+ continue;
4063
+ const pickResult = cat.getSourcesFromPointer(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4051
4064
  if (!pickResult?.sources.length)
4052
4065
  continue;
4053
4066
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-contextmenu", {
@@ -4062,7 +4075,7 @@ class AstroSphere {
4062
4075
  }));
4063
4076
  break;
4064
4077
  }
4065
- for (const fset of this.activeFootprintSets) {
4078
+ for (const fset of this.getActiveFootprintSets()) {
4066
4079
  const pickResult = fset.getFootprintsFromPointer(this.mouseHelper);
4067
4080
  if (!pickResult?.footprints.length)
4068
4081
  continue;
@@ -4152,19 +4165,22 @@ class AstroSphere {
4152
4165
  return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
4153
4166
  }
4154
4167
  activateHiPS(hipsDescriptor) {
4168
+ if (this._activeHiPSLayers.length > 0) {
4169
+ throw new Error("Cannot load a HiPS base layer while stacked layers are active.");
4170
+ }
4155
4171
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4156
- for (const hips of this._activeHiPSLayers) {
4157
- tileBuffer.removeHiPS(hips);
4172
+ if (this._activeHiPS) {
4173
+ tileBuffer.removeHiPS(this._activeHiPS);
4158
4174
  }
4159
- this._activeHiPSLayers = [];
4160
4175
  const hips = this.createHiPS(hipsDescriptor);
4161
- this._activeHiPSLayers.push(hips);
4162
4176
  this._activeHiPS = hips;
4177
+ this._activeDomain = "astronomy";
4163
4178
  this._activeBaseLayer = "hips";
4164
4179
  return hips;
4165
4180
  }
4166
4181
  setHiPSOpacity(hips, opacity) {
4167
- if (!this._activeHiPSLayers.includes(hips)) {
4182
+ const isBaseLayer = this._activeHiPSLayers.length === 0 && this._activeHiPS === hips;
4183
+ if (!isBaseLayer && !this._activeHiPSLayers.includes(hips)) {
4168
4184
  throw new Error("HiPS layer is not active in this AstroSphere.");
4169
4185
  }
4170
4186
  hips.setOpacity(opacity);
@@ -4172,6 +4188,13 @@ class AstroSphere {
4172
4188
  addHiPS(hipsDescriptor) {
4173
4189
  const normalizeURL = (url) => String(url).replace(/\/+$/, "");
4174
4190
  const descriptorURL = normalizeURL(hipsDescriptor.url);
4191
+ // Entering stack mode discards the standalone base HiPS. Base mode and
4192
+ // stack mode are intentionally mutually exclusive.
4193
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4194
+ const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4195
+ tileBuffer.removeHiPS(this._activeHiPS);
4196
+ this._activeHiPS = null;
4197
+ }
4175
4198
  const existing = this._activeHiPSLayers.find((hips) => normalizeURL(hips.baseURL) === descriptorURL);
4176
4199
  if (existing) {
4177
4200
  throw new Error(`HiPS already active: ${String(hipsDescriptor.url)}`);
@@ -4179,6 +4202,7 @@ class AstroSphere {
4179
4202
  const hips = this.createHiPS(hipsDescriptor);
4180
4203
  this._activeHiPSLayers.push(hips);
4181
4204
  this._activeHiPS = hips;
4205
+ this._activeDomain = "astronomy";
4182
4206
  this._activeBaseLayer = "hips";
4183
4207
  return hips;
4184
4208
  }
@@ -4203,8 +4227,13 @@ class AstroSphere {
4203
4227
  }
4204
4228
  removeAllHiPS() {
4205
4229
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4206
- for (const hips of this._activeHiPSLayers) {
4207
- tileBuffer.removeHiPS(hips);
4230
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4231
+ tileBuffer.removeHiPS(this._activeHiPS);
4232
+ }
4233
+ else {
4234
+ for (const hips of this._activeHiPSLayers) {
4235
+ tileBuffer.removeHiPS(hips);
4236
+ }
4208
4237
  }
4209
4238
  this._activeHiPSLayers = [];
4210
4239
  this._activeHiPS = null;
@@ -4217,39 +4246,169 @@ class AstroSphere {
4217
4246
  this._activeBaseLayer = "xyz";
4218
4247
  }
4219
4248
  activateXYZ2(config) {
4249
+ this._activeDomain = "earth";
4220
4250
  this._activeXYZ2 = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4221
4251
  this._activeBaseLayer = "xyz";
4222
4252
  }
4223
4253
  activateMeshHiPS(descriptor) {
4254
+ this._activeDomain = "mesh";
4224
4255
  this._activeMeshHiPS = new MeshHiPS_js_1.MeshHiPS(descriptor.meshRadius, [0.0, 0.0, 0.0], 0, 0, descriptor, this._webgl, this._healpixGrid);
4225
4256
  this._activeBaseLayer = "meships";
4226
4257
  }
4258
+ setMeshHiPSColor(color) {
4259
+ this._activeMeshHiPS?.setColor(color);
4260
+ }
4261
+ setMeshHiPSWireframe(wireframe) {
4262
+ this._activeMeshHiPS?.setWireframe(wireframe);
4263
+ }
4227
4264
  activateWMTS(config) {
4265
+ this._activeDomain = "earth";
4228
4266
  const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4229
4267
  const xyzConfig = adapter.toXYZLayerConfig();
4230
4268
  this._activeXYZ2 = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, new XYZMapDescriptor_js_1.XYZMapDescriptor(config.layer ? `WMTS ${config.layer}` : "WMTS Earth2 Layer", xyzConfig.urlTemplate, xyzConfig.minZoom ?? 0, xyzConfig.maxZoom ?? 8, xyzConfig.segmentsPerSide ?? 48, xyzConfig.maxCachedTiles ?? 384, 8, xyzConfig.urlResolver), this._webgl, this.gridLabelContainers);
4231
4269
  this._activeBaseLayer = "xyz";
4232
4270
  }
4233
- // Catalogue section
4271
+ addXYZRasterOverlay(config) {
4272
+ const map = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4273
+ return this.addEarthRasterOverlay(config.name, "xyz", map);
4274
+ }
4275
+ addWMTSRasterOverlay(config) {
4276
+ const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4277
+ const xyzConfig = adapter.toXYZLayerConfig();
4278
+ const name = config.layer ? `WMTS ${config.layer}` : "WMTS Earth Raster Overlay";
4279
+ const map = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, new XYZMapDescriptor_js_1.XYZMapDescriptor(name, xyzConfig.urlTemplate, xyzConfig.minZoom ?? 0, xyzConfig.maxZoom ?? 8, xyzConfig.segmentsPerSide ?? 48, xyzConfig.maxCachedTiles ?? 384, 8, xyzConfig.urlResolver), this._webgl, this.gridLabelContainers);
4280
+ return this.addEarthRasterOverlay(name, "wmts", map);
4281
+ }
4282
+ getEarthRasterOverlays() {
4283
+ return this._earthRasterOverlays.map(({ map: _map, ...overlay }) => ({ ...overlay }));
4284
+ }
4285
+ setEarthRasterOverlayOpacity(id, opacity) {
4286
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4287
+ if (!overlay)
4288
+ return;
4289
+ overlay.opacity = Math.min(1, Math.max(0, opacity));
4290
+ overlay.map.setOpacity(overlay.opacity);
4291
+ }
4292
+ setEarthRasterOverlayVisible(id, visible) {
4293
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4294
+ if (overlay)
4295
+ overlay.visible = visible;
4296
+ }
4297
+ removeEarthRasterOverlay(id) {
4298
+ this._earthRasterOverlays = this._earthRasterOverlays.filter((entry) => entry.id !== id);
4299
+ }
4300
+ removeAllEarthRasterOverlays() {
4301
+ this._earthRasterOverlays = [];
4302
+ }
4303
+ addEarthRasterOverlay(name, sourceType, map) {
4304
+ const overlay = {
4305
+ id: `earth-raster-${++this._earthRasterOverlaySequence}`,
4306
+ name,
4307
+ sourceType,
4308
+ visible: true,
4309
+ opacity: 0.65,
4310
+ map,
4311
+ };
4312
+ map.setOpacity(overlay.opacity);
4313
+ this._earthRasterOverlays.push(overlay);
4314
+ const { map: _map, ...info } = overlay;
4315
+ return { ...info };
4316
+ }
4317
+ setActiveDomain(domain) {
4318
+ if (domain !== this._activeDomain) {
4319
+ this.clearGridLabelsForDomain(this._activeDomain);
4320
+ this.domainCameraStates[this._activeDomain] = this._camera.getViewState();
4321
+ const targetCameraState = this.domainCameraStates[domain];
4322
+ if (targetCameraState) {
4323
+ this._camera.restoreViewState(targetCameraState);
4324
+ }
4325
+ this.inertiaX = 0;
4326
+ this.inertiaY = 0;
4327
+ this.zoomInertia = 0;
4328
+ this._cameraStatusChanged = true;
4329
+ }
4330
+ this._activeDomain = domain;
4331
+ if (domain === "astronomy") {
4332
+ this._activeBaseLayer = this._activeHiPS ? "hips" : null;
4333
+ }
4334
+ else if (domain === "earth") {
4335
+ this._activeBaseLayer = this._activeXYZ2 ? "xyz" : null;
4336
+ }
4337
+ else {
4338
+ this._activeBaseLayer = this._activeMeshHiPS ? "meships" : null;
4339
+ }
4340
+ // A hover from the previous vertical must not leak into the new one.
4341
+ this.lastHoveredSource = null;
4342
+ this.lastHoveredCatalogue = null;
4343
+ }
4344
+ clearGridLabelsForDomain(domain) {
4345
+ if (domain === "astronomy") {
4346
+ this._healpixGrid?.clearLabels();
4347
+ this._equatorialGrid?.clearLabels();
4348
+ return;
4349
+ }
4350
+ if (domain === "earth") {
4351
+ this._activeXYZ2?.clearLonLatGridLabels?.();
4352
+ }
4353
+ }
4354
+ get activeDomain() {
4355
+ return this._activeDomain;
4356
+ }
4357
+ getActivePointSets() {
4358
+ if (this._activeDomain === "astronomy")
4359
+ return this.astronomyCatalogues;
4360
+ if (this._activeDomain === "earth")
4361
+ return this.earthPointSets;
4362
+ return [];
4363
+ }
4364
+ getActiveFootprintSets() {
4365
+ if (this._activeDomain === "astronomy")
4366
+ return this.astronomyFootprintSets;
4367
+ if (this._activeDomain === "earth")
4368
+ return this.earthFootprintSets;
4369
+ return [];
4370
+ }
4371
+ // Astronomy catalogue ownership
4234
4372
  async showCatalogue(cat) {
4235
- // console.log(cat)
4236
- if (cat)
4237
- this.activeCatalogues.push(cat);
4373
+ if (cat && !this.astronomyCatalogues.includes(cat)) {
4374
+ this.astronomyCatalogues.push(cat);
4375
+ }
4238
4376
  return cat;
4239
4377
  }
4240
4378
  deleteCatalogue(catalogue) {
4241
- this.activeCatalogues = this.activeCatalogues.filter((c) => c !== catalogue);
4379
+ this.astronomyCatalogues = this.astronomyCatalogues.filter((c) => c !== catalogue);
4242
4380
  }
4243
- // End Catalogue section
4244
- // Footprint section
4381
+ // Earth point-set ownership. TerraPointSetGL still extends CatalogueGL in
4382
+ // 3.12, but it no longer shares the Astronomy lifecycle collection.
4383
+ async showTerraPointSet(pointSet) {
4384
+ if (pointSet && !this.earthPointSets.includes(pointSet)) {
4385
+ this.earthPointSets.push(pointSet);
4386
+ }
4387
+ return pointSet;
4388
+ }
4389
+ deleteTerraPointSet(pointSet) {
4390
+ this.earthPointSets = this.earthPointSets.filter((set) => set !== pointSet);
4391
+ }
4392
+ // Astronomy footprint ownership
4245
4393
  async showFootprintSet(fset) {
4246
- // console.log(fset)
4247
- if (fset)
4248
- this.activeFootprintSets.push(fset);
4394
+ if (fset && !this.astronomyFootprintSets.includes(fset)) {
4395
+ this.astronomyFootprintSets.push(fset);
4396
+ }
4249
4397
  return fset;
4250
4398
  }
4251
4399
  deleteFootprintSet(footprintSet) {
4252
- this.activeFootprintSets = this.activeFootprintSets.filter((fst) => fst !== footprintSet);
4400
+ this.astronomyFootprintSets = this.astronomyFootprintSets.filter((fst) => fst !== footprintSet);
4401
+ }
4402
+ // Earth footprint ownership. TerraFootprintSetGL still reuses the common
4403
+ // FootprintSetGL rendering implementation, but has an independent lifecycle.
4404
+ async showTerraFootprintSet(footprintSet) {
4405
+ if (footprintSet && !this.earthFootprintSets.includes(footprintSet)) {
4406
+ this.earthFootprintSets.push(footprintSet);
4407
+ }
4408
+ return footprintSet;
4409
+ }
4410
+ deleteTerraFootprintSet(footprintSet) {
4411
+ this.earthFootprintSets = this.earthFootprintSets.filter((fst) => fst !== footprintSet);
4253
4412
  }
4254
4413
  async showPolylineSet(polylineSet) {
4255
4414
  if (polylineSet)
@@ -4279,8 +4438,8 @@ class AstroSphere {
4279
4438
  satelliteObject.dispose();
4280
4439
  }
4281
4440
  getHoveredFootprints() {
4282
- let footprintsHovered = [];
4283
- this.activeFootprintSets.forEach((fset) => {
4441
+ const footprintsHovered = [];
4442
+ this.getActiveFootprintSets().forEach((fset) => {
4284
4443
  footprintsHovered.push(fset.hoveredFootprints);
4285
4444
  });
4286
4445
  return footprintsHovered;
@@ -4332,25 +4491,128 @@ class AstroSphere {
4332
4491
  throw new Error(`healpixGrid is ${this.healpixGrid}`);
4333
4492
  return FoVUtils_js_1.FoVUtils.getFoVPolygon(this._camera, this.canvas, this._healpixGrid, this._healpixGrid, this._webgl, this._perspectiveMatrixManager.pMatrix);
4334
4493
  }
4335
- changeFoV(deg) {
4336
- const distance = this._healpixGrid.getFoV().computeDistanceFromAngle(deg);
4337
- this._camera.translate(distance);
4494
+ validateFoV(deg) {
4495
+ if (!Number.isFinite(deg) || deg <= 0 || deg >= 180) {
4496
+ throw new RangeError(`FoV must be > 0 and < 180 degrees. Received ${deg}.`);
4497
+ }
4498
+ if (Global_js_1.default.insideSphere) {
4499
+ throw new Error("FoV navigation is currently supported only outside the sphere.");
4500
+ }
4501
+ }
4502
+ /**
4503
+ * Resolve the radial camera distance that produces the requested minimum FoV.
4504
+ *
4505
+ * The search temporarily samples radial distances, then restores the original
4506
+ * camera distance before returning. Callers can therefore either apply the
4507
+ * result immediately or animate toward it.
4508
+ */
4509
+ findRadialDistanceForFoV(deg) {
4510
+ this.validateFoV(deg);
4511
+ const originalDistance = this._camera.getRadialDistance();
4512
+ const minDistance = 1.000001;
4513
+ const maxDistance = 4.0;
4514
+ const tolerance = Math.max(1e-5, deg * 0.001);
4515
+ const maxIterations = 40;
4516
+ let low = minDistance;
4517
+ let high = maxDistance;
4518
+ let bestDistance = originalDistance;
4519
+ let bestError = Number.POSITIVE_INFINITY;
4520
+ for (let iteration = 0; iteration < maxIterations; iteration += 1) {
4521
+ const candidateDistance = (low + high) / 2;
4522
+ this._camera.setRadialDistance(candidateDistance);
4523
+ this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, Global_js_1.default.insideSphere);
4524
+ const candidateFoV = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
4525
+ const measuredFoV = candidateFoV.minFoV;
4526
+ const error = Math.abs(measuredFoV - deg);
4527
+ if (error < bestError) {
4528
+ bestError = error;
4529
+ bestDistance = candidateDistance;
4530
+ }
4531
+ if (error <= tolerance) {
4532
+ break;
4533
+ }
4534
+ if (measuredFoV >= deg || measuredFoV >= 179.999) {
4535
+ high = candidateDistance;
4536
+ }
4537
+ else {
4538
+ low = candidateDistance;
4539
+ }
4540
+ }
4541
+ this._camera.setRadialDistance(originalDistance);
4542
+ this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, Global_js_1.default.insideSphere);
4543
+ this.fov = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
4544
+ this._camera.refreshFoV(this.fov.minFoV);
4545
+ return bestDistance;
4546
+ }
4547
+ finishFoVAnimation() {
4548
+ const resolve = this.fovAnimationResolve;
4549
+ this.fovAnimationResolve = null;
4550
+ resolve?.();
4551
+ }
4552
+ cancelFoVAnimation() {
4553
+ this._camera.cancelRadialAnimation();
4554
+ this.finishFoVAnimation();
4555
+ }
4556
+ /** Set the minimum angular field of view immediately, in degrees. */
4557
+ setFoV(deg) {
4558
+ this.validateFoV(deg);
4559
+ this.cancelFoVAnimation();
4560
+ this._camera.cancelFlyTo();
4561
+ this.zoomInertia = 0;
4562
+ const targetDistance = this.findRadialDistanceForFoV(deg);
4563
+ this._camera.setRadialDistance(targetDistance);
4564
+ this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, Global_js_1.default.insideSphere);
4338
4565
  this.fov = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
4339
4566
  this._camera.refreshFoV(this.fov.minFoV);
4567
+ this.lastCameraMotionAt = performance.now();
4568
+ this._cameraStatusChanged = true;
4569
+ this.emitCameraChanged("set-fov");
4570
+ }
4571
+ async flyToFoV(deg, durationMs = 1200) {
4572
+ this.validateFoV(deg);
4573
+ if (!Number.isFinite(durationMs) || durationMs < 0) {
4574
+ throw new RangeError(`FoV animation duration must be >= 0 ms. Received ${durationMs}.`);
4575
+ }
4576
+ if (durationMs === 0) {
4577
+ this.setFoV(deg);
4578
+ return;
4579
+ }
4580
+ this.cancelFoVAnimation();
4581
+ this.zoomInertia = 0;
4582
+ const targetDistance = this.findRadialDistanceForFoV(deg);
4583
+ this._camera.flyToRadialDistance(targetDistance, durationMs);
4584
+ this.lastCameraMotionAt = performance.now();
4585
+ this._cameraStatusChanged = true;
4586
+ return new Promise((resolve) => {
4587
+ this.fovAnimationResolve = resolve;
4588
+ });
4589
+ }
4590
+ async zoomIn(steps = 1, durationMs = 500) {
4591
+ this.validateZoomSteps(steps);
4592
+ const targetFoV = this.getFoV().minFoV / Math.pow(2, steps);
4593
+ return this.flyToFoV(targetFoV, durationMs);
4594
+ }
4595
+ async zoomOut(steps = 1, durationMs = 500) {
4596
+ this.validateZoomSteps(steps);
4597
+ const targetFoV = this.getFoV().minFoV * Math.pow(2, steps);
4598
+ return this.flyToFoV(targetFoV, durationMs);
4599
+ }
4600
+ validateZoomSteps(steps) {
4601
+ if (!Number.isInteger(steps) || steps < 1) {
4602
+ throw new RangeError(`Zoom steps must be a positive integer. Received ${steps}.`);
4603
+ }
4604
+ }
4605
+ /** @deprecated Use setFoV(). */
4606
+ changeFoV(deg) {
4607
+ this.setFoV(deg);
4340
4608
  }
4609
+ /** @deprecated Use setFoV(). */
4341
4610
  changeFoV2(deg) {
4342
- const newCameraPos = this._healpixGrid
4343
- .getFoV()
4344
- .computeCameraPositionForFoV(deg);
4345
- this._camera.setCameraPosition(newCameraPos);
4611
+ this.setFoV(deg);
4346
4612
  }
4613
+ /** @deprecated Use setFoV(). */
4347
4614
  changeFoV3(deg) {
4348
- const newPos = this._healpixGrid
4349
- .getFoV()
4350
- .computeCameraPositionForAngularDiameter(deg);
4351
- this._camera.setCameraPosition(newPos);
4352
- // Recompute projection after moving the camera
4353
- this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, false);
4615
+ this.setFoV(deg);
4354
4616
  }
4355
4617
  getInsideSphere() {
4356
4618
  return Global_js_1.default.insideSphere;
@@ -4360,6 +4622,7 @@ class AstroSphere {
4360
4622
  this.inertiaX = 0;
4361
4623
  this.inertiaY = 0;
4362
4624
  this.zoomInertia = 0;
4625
+ this.cancelFoVAnimation();
4363
4626
  Global_js_1.default.insideSphere = !Global_js_1.default.insideSphere;
4364
4627
  // console.log(global.insideSphere)
4365
4628
  this._camera.toggleInsideSphere();
@@ -4512,8 +4775,6 @@ class AstroSphere {
4512
4775
  return;
4513
4776
  if (!this._webgl)
4514
4777
  return;
4515
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
4516
- return;
4517
4778
  if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
4518
4779
  return;
4519
4780
  if (this._healpixGrid.fovObj === undefined)
@@ -4537,6 +4798,14 @@ class AstroSphere {
4537
4798
  this.updateCentralPoint();
4538
4799
  this._perspectiveMatrixManager.computePerspectiveMatrix(canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, Global_js_1.default.insideSphere);
4539
4800
  }
4801
+ const fovFlying = this._camera.updateRadialAnimation(now);
4802
+ const fovAnimationFinished = fovFlying && !this._camera.isRadialAnimationActive();
4803
+ if (fovFlying) {
4804
+ this.zoomInertia = 0;
4805
+ this.lastCameraMotionAt = now;
4806
+ this._cameraStatusChanged = true;
4807
+ this._perspectiveMatrixManager.computePerspectiveMatrix(canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, Global_js_1.default.insideSphere);
4808
+ }
4540
4809
  // Zoom inertia
4541
4810
  if (this.zoomInertia !== 0) {
4542
4811
  if (Math.abs(this.zoomInertia) > 0.0001) {
@@ -4585,6 +4854,9 @@ class AstroSphere {
4585
4854
  this._camera.refreshFoV(this.fov.minFoV);
4586
4855
  this.prevFov = this.fov.minFoV;
4587
4856
  }
4857
+ if (fovAnimationFinished) {
4858
+ this.finishFoVAnimation();
4859
+ }
4588
4860
  // Se la camera è ruotata (anche solo per inerzia), aggiorna punto centrale + emetti cameraChanged
4589
4861
  if (cameraRotated) {
4590
4862
  // Ricalcola il punto centrale
@@ -4625,8 +4897,14 @@ class AstroSphere {
4625
4897
  this._webgl.enable(this._webgl.CULL_FACE);
4626
4898
  this._webgl.cullFace(Global_js_1.default.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
4627
4899
  this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
4628
- if (this._activeBaseLayer === "hips" && this._activeHiPSLayers.length > 0) {
4629
- const maxHiPSOrder = Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder));
4900
+ if (this._activeBaseLayer === "hips" && this._activeHiPS) {
4901
+ // During directional flyTo navigation, keep visibility current but render
4902
+ // only HiPS tiles that are already available. Final-view refinement
4903
+ // resumes automatically on the first frame after the flight.
4904
+ this._healpixGrid.visibleTilesManager.tileBuffer.setTileAcquisitionEnabled(!cameraFlying);
4905
+ const maxHiPSOrder = this._activeHiPSLayers.length > 0
4906
+ ? Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder))
4907
+ : this._activeHiPS.maxOrder;
4630
4908
  const visibleOrder = Math.min(this._healpixGrid.visibleorder, maxHiPSOrder);
4631
4909
  this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
4632
4910
  }
@@ -4641,6 +4919,7 @@ class AstroSphere {
4641
4919
  Math.abs(this.zoomInertia) > 0.0001 ||
4642
4920
  Math.abs(this.inertiaX) > 0.02 ||
4643
4921
  Math.abs(this.inertiaY) > 0.02 ||
4922
+ this._camera.isRadialAnimationActive() ||
4644
4923
  nowForGrid - this.lastCameraMotionAt < 220;
4645
4924
  const skyEntityDrawInput = {
4646
4925
  fovDeg: stableFovDeg,
@@ -4661,12 +4940,19 @@ class AstroSphere {
4661
4940
  }
4662
4941
  if (this._activeBaseLayer === "xyz") {
4663
4942
  this._activeXYZ2?.draw(skyEntityDrawInput);
4943
+ for (const overlay of this._earthRasterOverlays) {
4944
+ if (overlay.visible) {
4945
+ overlay.map.draw(skyEntityDrawInput, true);
4946
+ }
4947
+ }
4664
4948
  }
4665
4949
  if (this._activeBaseLayer === "meships") {
4666
4950
  this._activeMeshHiPS?.draw(skyEntityDrawInput);
4667
4951
  }
4668
- this._healpixGrid.draw(skyEntityDrawInput);
4669
- this._equatorialGrid.draw(skyEntityDrawInput);
4952
+ if (this._activeDomain === "astronomy") {
4953
+ this._healpixGrid.draw(skyEntityDrawInput);
4954
+ this._equatorialGrid.draw(skyEntityDrawInput);
4955
+ }
4670
4956
  this._webgl.enable(this._webgl.DEPTH_TEST);
4671
4957
  this._webgl.disable(this._webgl.CULL_FACE);
4672
4958
  if (this.startup) {
@@ -4685,52 +4971,53 @@ class AstroSphere {
4685
4971
  decDMS,
4686
4972
  });
4687
4973
  }
4688
- this.activeCatalogues.forEach((cat) => {
4689
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4690
- this._activeXYZ2?.getModelMatrix() ??
4691
- this._activeMeshHiPS?.getModelMatrix();
4974
+ this.getActivePointSets().forEach((cat) => {
4975
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4692
4976
  if (activeModelMatrix) {
4693
4977
  cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4694
4978
  }
4695
4979
  });
4696
4980
  this.emitHoveredSourceIfChanged();
4697
- this.activeFootprintSets.forEach((fst) => {
4698
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4699
- this._activeXYZ2?.getModelMatrix() ??
4700
- this._activeMeshHiPS?.getModelMatrix();
4981
+ this.getActiveFootprintSets().forEach((fst) => {
4982
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4701
4983
  if (activeModelMatrix) {
4702
4984
  fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4703
4985
  }
4704
4986
  });
4705
- this.activePolylineSets.forEach((polylineSet) => {
4706
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4707
- this._activeXYZ2?.getModelMatrix() ??
4708
- this._activeMeshHiPS?.getModelMatrix();
4709
- if (activeModelMatrix) {
4710
- polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4711
- }
4712
- });
4713
- this.activeSensorCones.forEach((sensorCone) => {
4714
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4715
- this._activeXYZ2?.getModelMatrix() ??
4716
- this._activeMeshHiPS?.getModelMatrix();
4717
- if (activeModelMatrix) {
4718
- sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4719
- }
4720
- });
4721
- this.activeSatelliteObjects.forEach((satelliteObject) => {
4722
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4723
- this._activeXYZ2?.getModelMatrix() ??
4724
- this._activeMeshHiPS?.getModelMatrix();
4725
- if (activeModelMatrix) {
4726
- satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4727
- }
4728
- });
4987
+ if (this._activeDomain === "earth") {
4988
+ this.activePolylineSets.forEach((polylineSet) => {
4989
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4990
+ if (activeModelMatrix) {
4991
+ polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4992
+ }
4993
+ });
4994
+ this.activeSensorCones.forEach((sensorCone) => {
4995
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4996
+ if (activeModelMatrix) {
4997
+ sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4998
+ }
4999
+ });
5000
+ this.activeSatelliteObjects.forEach((satelliteObject) => {
5001
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
5002
+ if (activeModelMatrix) {
5003
+ satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
5004
+ }
5005
+ });
5006
+ }
5007
+ }
5008
+ getActiveDomainModelMatrix() {
5009
+ if (this._activeDomain === "astronomy") {
5010
+ return (this._activeHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
5011
+ }
5012
+ if (this._activeDomain === "earth") {
5013
+ return (this._activeXYZ2?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
5014
+ }
5015
+ return (this._activeMeshHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4729
5016
  }
4730
5017
  emitHoveredSourceIfChanged() {
4731
5018
  let nextHoveredSource = null;
4732
5019
  let nextHoveredCatalogue = null;
4733
- for (const cat of this.activeCatalogues) {
5020
+ for (const cat of this.getActivePointSets()) {
4734
5021
  const hovered = cat.getPrimaryHoveredSource();
4735
5022
  if (!hovered)
4736
5023
  continue;
@@ -4858,25 +5145,25 @@ class AstroViewer {
4858
5145
  return new TerraPointSetGL_js_1.TerraPointSetGL(pointSetName, pointSetDescription, providerUrl, metadataManager, this.webgl, this.astroSphere.healpixGrid.visibleTilesManager);
4859
5146
  }
4860
5147
  showTerraPointSet(pointSet) {
4861
- this.astroSphere.showCatalogue(pointSet);
5148
+ this.astroSphere.showTerraPointSet(pointSet);
4862
5149
  }
4863
5150
  hideTerraPointSet(pointSet, isVisible) {
4864
5151
  pointSet.setIsVisible(isVisible);
4865
5152
  }
4866
5153
  deleteTerraPointSet(pointSet) {
4867
- this.astroSphere.deleteCatalogue(pointSet);
5154
+ this.astroSphere.deleteTerraPointSet(pointSet);
4868
5155
  }
4869
5156
  createTerraFootprintSet(footprintSetName, footprintSetDescription, providerUrl, metadataManager) {
4870
5157
  return new TerraFootprintSetGL_js_1.TerraFootprintSetGL(footprintSetName, footprintSetDescription, providerUrl, metadataManager, this.webgl);
4871
5158
  }
4872
5159
  showTerraFootprintSet(footprintSet) {
4873
- this.astroSphere.showFootprintSet(footprintSet);
5160
+ this.astroSphere.showTerraFootprintSet(footprintSet);
4874
5161
  }
4875
5162
  hideTerraFootprintSet(footprintSet, isVisible) {
4876
5163
  footprintSet.setIsVisible(isVisible);
4877
5164
  }
4878
5165
  deleteTerraFootprintSet(footprintSet) {
4879
- this.astroSphere.deleteFootprintSet(footprintSet);
5166
+ this.astroSphere.deleteTerraFootprintSet(footprintSet);
4880
5167
  }
4881
5168
  createTerraPolylineSet(polylineSetName, polylineSetDescription, providerUrl, metadataManager) {
4882
5169
  return new TerraPolylineSetGL_js_1.TerraPolylineSetGL(polylineSetName, polylineSetDescription, providerUrl, metadataManager, this.webgl);
@@ -4929,6 +5216,12 @@ class AstroViewer {
4929
5216
  getDefaultHiPSURL() {
4930
5217
  return Config_js_1.bootSetup.defaultHipsUrl;
4931
5218
  }
5219
+ setActiveDomain(domain) {
5220
+ this.astroSphere.setActiveDomain(domain);
5221
+ }
5222
+ getActiveDomain() {
5223
+ return this.astroSphere.activeDomain;
5224
+ }
4932
5225
  activateHiPS(hipsDescriptor) {
4933
5226
  this.astroSphere.activateHiPS(hipsDescriptor);
4934
5227
  }
@@ -4942,9 +5235,37 @@ class AstroViewer {
4942
5235
  activateWMTS(config) {
4943
5236
  this.astroSphere.activateWMTS(config);
4944
5237
  }
5238
+ addXYZRasterOverlay(config) {
5239
+ const descriptor = new XYZMapDescriptor_js_1.XYZMapDescriptor(config.name ?? "XYZ Earth Raster Overlay", config.urlTemplate, config.minZoom ?? 0, config.maxZoom ?? 8, config.segmentsPerSide ?? 48, config.maxCachedTiles ?? 384, 8, config.urlResolver);
5240
+ return this.astroSphere.addXYZRasterOverlay(descriptor);
5241
+ }
5242
+ addWMTSRasterOverlay(config) {
5243
+ return this.astroSphere.addWMTSRasterOverlay(config);
5244
+ }
5245
+ getEarthRasterOverlays() {
5246
+ return this.astroSphere.getEarthRasterOverlays();
5247
+ }
5248
+ setEarthRasterOverlayOpacity(id, opacity) {
5249
+ this.astroSphere.setEarthRasterOverlayOpacity(id, opacity);
5250
+ }
5251
+ setEarthRasterOverlayVisible(id, visible) {
5252
+ this.astroSphere.setEarthRasterOverlayVisible(id, visible);
5253
+ }
5254
+ removeEarthRasterOverlay(id) {
5255
+ this.astroSphere.removeEarthRasterOverlay(id);
5256
+ }
5257
+ removeAllEarthRasterOverlays() {
5258
+ this.astroSphere.removeAllEarthRasterOverlays();
5259
+ }
4945
5260
  activateMeshHiPS(config) {
4946
5261
  this.astroSphere.activateMeshHiPS(new MeshHiPSDescriptor_js_1.MeshHiPSDescriptor(config));
4947
5262
  }
5263
+ setMeshHiPSColor(color) {
5264
+ this.astroSphere.setMeshHiPSColor(color);
5265
+ }
5266
+ setMeshHiPSWireframe(wireframe) {
5267
+ this.astroSphere.setMeshHiPSWireframe(wireframe);
5268
+ }
4948
5269
  setXYZMaxConcurrentRequests(value) {
4949
5270
  XYZTileRequestScheduler_js_1.xyzTileRequestScheduler.setMaxConcurrent(value);
4950
5271
  }
@@ -5133,14 +5454,29 @@ class AstroViewer {
5133
5454
  getFoVPolygon() {
5134
5455
  return this.astroSphere.getFoVPolygon();
5135
5456
  }
5457
+ setFoV(deg) {
5458
+ this.astroSphere.setFoV(deg);
5459
+ }
5460
+ flyToFoV(deg, durationMs = 1200) {
5461
+ return this.astroSphere.flyToFoV(deg, durationMs);
5462
+ }
5463
+ zoomIn(steps = 1, durationMs = 500) {
5464
+ return this.astroSphere.zoomIn(steps, durationMs);
5465
+ }
5466
+ zoomOut(steps = 1, durationMs = 500) {
5467
+ return this.astroSphere.zoomOut(steps, durationMs);
5468
+ }
5469
+ /** @deprecated Use setFoV(). */
5136
5470
  changeFoV(deg) {
5137
- (this, this.astroSphere.changeFoV(deg));
5471
+ this.setFoV(deg);
5138
5472
  }
5473
+ /** @deprecated Use setFoV(). */
5139
5474
  changeFoV2(deg) {
5140
- (this, this.astroSphere.changeFoV2(deg));
5475
+ this.setFoV(deg);
5141
5476
  }
5477
+ /** @deprecated Use setFoV(). */
5142
5478
  changeFoV3(deg) {
5143
- (this, this.astroSphere.changeFoV3(deg));
5479
+ this.setFoV(deg);
5144
5480
  }
5145
5481
  getInsideSphere() {
5146
5482
  return this.astroSphere.getInsideSphere();
@@ -5392,6 +5728,7 @@ class Camera {
5392
5728
  lockRotY = false;
5393
5729
  lockRotZ = false;
5394
5730
  flyToAnimation = null;
5731
+ radialAnimation = null;
5395
5732
  constructor(in_position, in_sphere) {
5396
5733
  this.init(in_position, in_sphere);
5397
5734
  }
@@ -5526,6 +5863,42 @@ class Camera {
5526
5863
  cancelFlyTo() {
5527
5864
  this.flyToAnimation = null;
5528
5865
  }
5866
+ flyToRadialDistance(distance, durationMs = 1200) {
5867
+ if (!Number.isFinite(distance) || distance <= 1) {
5868
+ throw new RangeError(`Camera radial distance must be > 1. Received ${distance}.`);
5869
+ }
5870
+ this.radialAnimation = {
5871
+ startDistance: this.cam_pos[2],
5872
+ targetDistance: distance,
5873
+ startTime: performance.now(),
5874
+ durationMs: Math.max(1, durationMs),
5875
+ };
5876
+ }
5877
+ updateRadialAnimation(now) {
5878
+ const animation = this.radialAnimation;
5879
+ if (!animation) {
5880
+ return false;
5881
+ }
5882
+ const elapsed = now - animation.startTime;
5883
+ const progress = Math.min(1, elapsed / animation.durationMs);
5884
+ const easedProgress = progress < 0.5
5885
+ ? 4 * progress * progress * progress
5886
+ : 1 - Math.pow(-2 * progress + 2, 3) / 2;
5887
+ const distance = animation.startDistance +
5888
+ (animation.targetDistance - animation.startDistance) * easedProgress;
5889
+ this.setRadialDistance(distance);
5890
+ if (progress >= 1) {
5891
+ this.setRadialDistance(animation.targetDistance);
5892
+ this.radialAnimation = null;
5893
+ }
5894
+ return true;
5895
+ }
5896
+ isRadialAnimationActive() {
5897
+ return this.radialAnimation !== null;
5898
+ }
5899
+ cancelRadialAnimation() {
5900
+ this.radialAnimation = null;
5901
+ }
5529
5902
  toggleInsideSphere() {
5530
5903
  // if (inside !== global.insideSphere) {
5531
5904
  // global.insideSphere = inside;
@@ -5747,6 +6120,22 @@ class Camera {
5747
6120
  }
5748
6121
  return [inv[12], inv[13], inv[14]];
5749
6122
  }
6123
+ getViewState() {
6124
+ return {
6125
+ position: [this.cam_pos[0], this.cam_pos[1], this.cam_pos[2]],
6126
+ viewMatrix: gl_matrix_1.mat4.clone(this.vMatrix),
6127
+ translationMatrix: gl_matrix_1.mat4.clone(this.T),
6128
+ rotationMatrix: gl_matrix_1.mat4.clone(this.R),
6129
+ };
6130
+ }
6131
+ restoreViewState(state) {
6132
+ this.cancelFlyTo();
6133
+ this.cam_pos = gl_matrix_1.vec3.fromValues(...state.position);
6134
+ this.vMatrix = gl_matrix_1.mat4.clone(state.viewMatrix);
6135
+ this.T = gl_matrix_1.mat4.clone(state.translationMatrix);
6136
+ this.R = gl_matrix_1.mat4.clone(state.rotationMatrix);
6137
+ this.move = gl_matrix_1.vec3.create();
6138
+ }
5750
6139
  setCameraMatrix(viewMatrix) {
5751
6140
  this.vMatrix = viewMatrix;
5752
6141
  }
@@ -5759,6 +6148,24 @@ class Camera {
5759
6148
  // Recompute view: vMatrix = inv(T) * inv(R)
5760
6149
  this.refreshViewMatrix();
5761
6150
  }
6151
+ /**
6152
+ * Set the radial camera distance while preserving the current pointing.
6153
+ *
6154
+ * Outside-sphere navigation stores the camera radius in cam_pos[2];
6155
+ * changing only that component is the same radial motion used by zoom(),
6156
+ * without changing the current rotation matrix.
6157
+ */
6158
+ setRadialDistance(distance) {
6159
+ if (!Number.isFinite(distance) || distance <= 1) {
6160
+ throw new RangeError(`Camera radial distance must be > 1. Received ${distance}.`);
6161
+ }
6162
+ this.cam_pos[2] = distance;
6163
+ gl_matrix_1.mat4.translate(this.T, gl_matrix_1.mat4.create(), this.cam_pos);
6164
+ this.refreshViewMatrix();
6165
+ }
6166
+ getRadialDistance() {
6167
+ return this.cam_pos[2];
6168
+ }
5762
6169
  getCameraAngle() {
5763
6170
  const [x, y, z] = this.getCameraPosition();
5764
6171
  const posVec = gl_matrix_1.vec3.fromValues(x, y, z);
@@ -5961,7 +6368,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5961
6368
  return (mod && mod.__esModule) ? mod : { "default": mod };
5962
6369
  };
5963
6370
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5964
- exports.Footprint = exports.Source = exports.MeshHiPSDescriptor = exports.MeshHiPS = exports.WMTSAdapter = exports.XYZMap = exports.HiPS = exports.createColorMapFromSamples = exports.COLOR_MAP_SAMPLE_COUNT = exports.ColorMaps = exports.GeoJSONParser = exports.CoordsType = exports.FoVUtils = exports.CartesianOpts = exports.PointInitOpts = exports.AstroOpts = exports.SphericalOpts = exports.Point = exports.ColumnType = exports.MetadataInit = exports.MetadataColumn = exports.MetadataManager = exports.ObservationTrackViewerAdapter = exports.ObservationTrackTimeline = exports.footprintToRing = exports.createObservationTrackTimeline = exports.SensorConeGL = exports.SatelliteObjectGL = exports.TerraPolylineSetGL = exports.TerraFootprintSetGL = exports.TerraPointSetGL = exports.CatalogueGL = exports.FootprintSetGL = exports.HoveredFootprintDetail = exports.SphereFoV = exports.FoV = exports.HiPSDescriptor = exports.AstroViewer = void 0;
6371
+ exports.Footprint = exports.Source = exports.MeshHiPSDescriptor = exports.MeshHiPS = exports.WMTSAdapter = exports.XYZMap = exports.HiPS = exports.createColorMapFromSamples = exports.COLOR_MAP_SAMPLE_COUNT = exports.ColorMaps = exports.GeoJSONParser = exports.CoordsType = exports.FoVUtils = exports.decDMSToDeg = exports.raHMSToDeg = exports.CartesianOpts = exports.PointInitOpts = exports.AstroOpts = exports.SphericalOpts = exports.Point = exports.ColumnType = exports.MetadataInit = exports.MetadataColumn = exports.MetadataManager = exports.ObservationTrackViewerAdapter = exports.ObservationTrackTimeline = exports.footprintToRing = exports.createObservationTrackTimeline = exports.SensorConeGL = exports.SatelliteObjectGL = exports.TerraPolylineSetGL = exports.TerraFootprintSetGL = exports.TerraPointSetGL = exports.CatalogueGL = exports.FootprintSetGL = exports.HoveredFootprintDetail = exports.SphereFoV = exports.FoV = exports.HiPSDescriptor = exports.AstroViewer = void 0;
5965
6372
  var AstroViewer_js_1 = __webpack_require__(772);
5966
6373
  Object.defineProperty(exports, "AstroViewer", ({ enumerable: true, get: function () { return AstroViewer_js_1.AstroViewer; } }));
5967
6374
  var HiPSDescriptor_js_1 = __webpack_require__(5087);
@@ -6002,6 +6409,9 @@ Object.defineProperty(exports, "SphericalOpts", ({ enumerable: true, get: functi
6002
6409
  Object.defineProperty(exports, "AstroOpts", ({ enumerable: true, get: function () { return Point_js_1.AstroOpts; } }));
6003
6410
  Object.defineProperty(exports, "PointInitOpts", ({ enumerable: true, get: function () { return Point_js_1.PointInitOpts; } }));
6004
6411
  Object.defineProperty(exports, "CartesianOpts", ({ enumerable: true, get: function () { return Point_js_1.CartesianOpts; } }));
6412
+ var Utils_js_1 = __webpack_require__(7930);
6413
+ Object.defineProperty(exports, "raHMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.raHMSToDeg; } }));
6414
+ Object.defineProperty(exports, "decDMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.decDMSToDeg; } }));
6005
6415
  var FoVUtils_js_1 = __webpack_require__(8083);
6006
6416
  Object.defineProperty(exports, "FoVUtils", ({ enumerable: true, get: function () { return FoVUtils_js_1.FoVUtils; } }));
6007
6417
  var CoordsType_js_1 = __webpack_require__(8145);
@@ -8002,7 +8412,7 @@ class Source {
8002
8412
  this._point = in_point;
8003
8413
  this._details = in_details;
8004
8414
  this._shapesize = 16.0;
8005
- this._brightnessFactor = -99;
8415
+ this._brightnessFactor = 3.0;
8006
8416
  this.computeHealpixPixel();
8007
8417
  }
8008
8418
  getDetailByindex(index) {
@@ -8289,12 +8699,13 @@ const Utils_js_1 = __webpack_require__(7930);
8289
8699
  const CatalogueShaderProgram_js_1 = __webpack_require__(3559);
8290
8700
  const MetadataManager_js_1 = __webpack_require__(5403);
8291
8701
  const gl_matrix_1 = __webpack_require__(3890);
8702
+ const healpix_1 = __webpack_require__(4851);
8292
8703
  const Global_js_1 = __importDefault(__webpack_require__(4382));
8293
8704
  class CatalogueGL {
8294
8705
  _kind = "CatalogueGL";
8295
8706
  static ELEM_SIZE = 6;
8296
8707
  static BYTES_X_ELEM = new Float32Array().BYTES_PER_ELEMENT;
8297
- static STANDARD_SHAPE_SIZE = 10.0;
8708
+ static STANDARD_SHAPE_SIZE = 16.0;
8298
8709
  static STANDARD_SHAPE_HUE = 3.0;
8299
8710
  _ready;
8300
8711
  _name;
@@ -8537,7 +8948,7 @@ class CatalogueGL {
8537
8948
  getColumnIndex(columnsmeta, columnName) {
8538
8949
  if (!columnName)
8539
8950
  return -1;
8540
- return columnsmeta.find((column) => column.name === columnName)?.index ?? -1;
8951
+ return (columnsmeta.find((column) => column.name === columnName)?.index ?? -1);
8541
8952
  }
8542
8953
  readCell(row, columnsmeta, columnName) {
8543
8954
  const idx = this.getColumnIndex(columnsmeta, columnName);
@@ -8550,7 +8961,9 @@ class CatalogueGL {
8550
8961
  const src = String(rawSrc ?? "").trim();
8551
8962
  if (!src)
8552
8963
  return undefined;
8553
- const rawKind = String(this.readCell(row, columnsmeta, mediaColumns.type) ?? "sprite").trim().toLowerCase();
8964
+ const rawKind = String(this.readCell(row, columnsmeta, mediaColumns.type) ?? "sprite")
8965
+ .trim()
8966
+ .toLowerCase();
8554
8967
  const kind = rawKind === "model"
8555
8968
  ? "model"
8556
8969
  : rawKind === "image"
@@ -8570,7 +8983,9 @@ class CatalogueGL {
8570
8983
  src,
8571
8984
  scale: Number.isFinite(scale) && scale > 0 ? scale : undefined,
8572
8985
  rotationDeg: Number.isFinite(rotationDeg) ? rotationDeg : undefined,
8573
- opacity: Number.isFinite(opacity) ? Math.max(0, Math.min(1, opacity)) : undefined,
8986
+ opacity: Number.isFinite(opacity)
8987
+ ? Math.max(0, Math.min(1, opacity))
8988
+ : undefined,
8574
8989
  };
8575
8990
  }
8576
8991
  clearSources() {
@@ -8861,33 +9276,46 @@ class CatalogueGL {
8861
9276
  this._webgl.bufferData(this._webgl.ARRAY_BUFFER, this.vertexCataloguePosition, this._webgl.STATIC_DRAW);
8862
9277
  this._bufferInitialised = true;
8863
9278
  }
8864
- getSelectionRadius() {
8865
- const order = this._visibleTilesManager.getVisibleOrder();
8866
- switch (order) {
8867
- case 0:
8868
- case 1:
8869
- case 2:
8870
- // return 0.005;
8871
- return 0.01;
8872
- case 3:
8873
- // return 0.001;
8874
- case 4:
8875
- // return 0.0009;
8876
- case 5:
8877
- // return 0.0005;
8878
- return 0.005;
8879
- case 6:
8880
- // return 0.0001;
8881
- case 7:
8882
- // return 0.00009;
8883
- case 8:
8884
- // return 0.00005;
8885
- return 0.001;
8886
- case 9:
8887
- return 0.0005;
8888
- default:
8889
- return 0.0001;
9279
+ static MIN_HIT_RADIUS_PX = 6;
9280
+ static HIT_PADDING_PX = 3;
9281
+ static CANDIDATE_RADIUS_PX = 16;
9282
+ getCanvas() {
9283
+ return this._webgl.canvas;
9284
+ }
9285
+ getHitRadiusPx(source) {
9286
+ const markerSize = source.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
9287
+ return Math.max(CatalogueGL.MIN_HIT_RADIUS_PX, markerSize / 2 + CatalogueGL.HIT_PADDING_PX);
9288
+ }
9289
+ getCandidateSearchRadiusRad(pMatrix) {
9290
+ const canvas = this._webgl.canvas;
9291
+ const width = Math.max(1, canvas.clientWidth);
9292
+ const height = Math.max(1, canvas.clientHeight);
9293
+ // pMatrix[0] and pMatrix[5] are the perspective scale factors for X/Y.
9294
+ // Convert a small screen-space radius to a conservative angular radius.
9295
+ const fovXRad = 2 * Math.atan(1 / Math.abs(pMatrix[0]));
9296
+ const fovYRad = 2 * Math.atan(1 / Math.abs(pMatrix[5]));
9297
+ const radPerPixel = Math.max(fovXRad / width, fovYRad / height);
9298
+ return CatalogueGL.CANDIDATE_RADIUS_PX * radPerPixel;
9299
+ }
9300
+ projectPointToScreen(point, mvp, width, height) {
9301
+ const projected = gl_matrix_1.vec4.fromValues(point.x, point.y, point.z, 1);
9302
+ gl_matrix_1.vec4.transformMat4(projected, projected, mvp);
9303
+ if (projected[3] <= 0)
9304
+ return null;
9305
+ const ndcX = projected[0] / projected[3];
9306
+ const ndcY = projected[1] / projected[3];
9307
+ return [(ndcX * 0.5 + 0.5) * width, (1 - (ndcY * 0.5 + 0.5)) * height];
9308
+ }
9309
+ clearSelection() {
9310
+ for (const idx of this.selectedIndexes) {
9311
+ const base = idx * CatalogueGL.ELEM_SIZE;
9312
+ if (base + 4 >= this.vertexCataloguePosition.length)
9313
+ continue;
9314
+ this.vertexCataloguePosition[base + 3] = 0.0;
9315
+ this.vertexCataloguePosition[base + 4] =
9316
+ this._sources[idx]?.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
8890
9317
  }
9318
+ this.selectedIndexes = [];
8891
9319
  }
8892
9320
  setSelectedIndexes(selectedIndex) {
8893
9321
  selectedIndex.forEach((idx) => {
@@ -8914,8 +9342,8 @@ class CatalogueGL {
8914
9342
  * Run click-picking and update selection with the nearest candidate in current pixel.
8915
9343
  * Returns the selected source or null if no source was hit.
8916
9344
  */
8917
- getSourcesFromPointer(in_mouseHelper) {
8918
- const pickedIndexes = this.checkClicking(in_mouseHelper);
9345
+ getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9346
+ const pickedIndexes = this.checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8919
9347
  if (!pickedIndexes.length) {
8920
9348
  return {
8921
9349
  sources: [],
@@ -8934,8 +9362,8 @@ class CatalogueGL {
8934
9362
  * Run click-picking and update selection with the nearest candidate in current pixel.
8935
9363
  * Returns the selected source or null if no source was hit.
8936
9364
  */
8937
- selectPrimarySourceFromClick(in_mouseHelper) {
8938
- const picked = this.getSourcesFromPointer(in_mouseHelper);
9365
+ selectPrimarySourceFromClick(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9366
+ const picked = this.getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8939
9367
  const clickedIndexes = picked?.pickedIndexes ?? [];
8940
9368
  // if (!clickedIndexes.length) {
8941
9369
  // this.setSelectedIndexes([]);
@@ -8971,42 +9399,65 @@ class CatalogueGL {
8971
9399
  const idx = this.hoveredIndexes[0];
8972
9400
  return this._sources[idx] ?? null;
8973
9401
  }
8974
- findNearestSourceIndex(in_mouseHelper) {
8975
- if (in_mouseHelper.x == null ||
8976
- in_mouseHelper.y == null ||
8977
- in_mouseHelper.z == null) {
9402
+ findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9403
+ if (in_mouseHelper.xyz == null)
8978
9404
  return null;
9405
+ const healpix = Global_js_1.default.getHealpix(Global_js_1.default.selectionOrder);
9406
+ const mouseVec = new healpix_1.Vec3(...in_mouseHelper.xyz);
9407
+ const mousePointing = new healpix_1.Pointing(mouseVec, false);
9408
+ const searchRadiusRad = this.getCandidateSearchRadiusRad(pMatrix);
9409
+ const rangeSet = healpix.queryDiscInclusive(mousePointing, searchRadiusRad, 4);
9410
+ const pixels = rangeSet.r;
9411
+ const candidateIndexes = new Set();
9412
+ for (const pixel of pixels) {
9413
+ const indexes = this._healpixDensityMap.get(pixel);
9414
+ if (!indexes)
9415
+ continue;
9416
+ indexes.forEach((idx) => candidateIndexes.add(idx));
8979
9417
  }
8980
- const mousePix = in_mouseHelper.computeNpix();
8981
- if (mousePix == null)
9418
+ if (!candidateIndexes.size)
8982
9419
  return null;
8983
- const candidates = this._healpixDensityMap.get(mousePix);
8984
- if (!candidates?.length)
9420
+ const canvas = this._webgl.canvas;
9421
+ const width = canvas.clientWidth;
9422
+ const height = canvas.clientHeight;
9423
+ if (!width || !height)
9424
+ return null;
9425
+ const modelView = gl_matrix_1.mat4.create();
9426
+ const mvp = gl_matrix_1.mat4.create();
9427
+ gl_matrix_1.mat4.multiply(modelView, vMatrix, in_mMatrix);
9428
+ gl_matrix_1.mat4.multiply(mvp, pMatrix, modelView);
9429
+ const mousePoint = new Point_js_1.Point({
9430
+ x: in_mouseHelper.xyz[0],
9431
+ y: in_mouseHelper.xyz[1],
9432
+ z: in_mouseHelper.xyz[2],
9433
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
9434
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
9435
+ if (!mouseScreen)
8985
9436
  return null;
8986
- const selR = this.getSelectionRadius();
8987
9437
  let nearestIndex = null;
8988
- let nearestDistance = Number.POSITIVE_INFINITY;
8989
- for (const sourceIdx of candidates) {
9438
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
9439
+ for (const sourceIdx of candidateIndexes) {
8990
9440
  const source = this._sources[sourceIdx];
8991
9441
  if (!source)
8992
9442
  continue;
8993
- const dx = source.point.x - in_mouseHelper.x;
8994
- const dy = source.point.y - in_mouseHelper.y;
8995
- const dz = source.point.z - in_mouseHelper.z;
8996
- const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
8997
- if (dist <= selR && dist < nearestDistance) {
8998
- nearestDistance = dist;
9443
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
9444
+ if (!sourceScreen)
9445
+ continue;
9446
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
9447
+ if (distancePx <= this.getHitRadiusPx(source) &&
9448
+ distancePx < nearestDistancePx) {
9449
+ nearestDistancePx = distancePx;
8999
9450
  nearestIndex = sourceIdx;
9000
9451
  }
9001
9452
  }
9002
9453
  return nearestIndex;
9003
9454
  }
9004
- checkClicking(in_mouseHelper) {
9005
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9455
+ checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9456
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
9006
9457
  return nearestIndex == null ? [] : [nearestIndex];
9007
9458
  }
9008
- checkHovering(in_mouseHelper) {
9009
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9459
+ checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9460
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
9010
9461
  return nearestIndex == null ? [] : [nearestIndex];
9011
9462
  }
9012
9463
  /**
@@ -9042,6 +9493,13 @@ class CatalogueGL {
9042
9493
  if (this._catalogueShaderProgram.locations.color) {
9043
9494
  this._webgl.uniform4f(this._catalogueShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], 1.0);
9044
9495
  }
9496
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
9497
+ if (this._catalogueShaderProgram.locations.hoverColor) {
9498
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.hoverColor, ...interactionColors.hover, 1.0);
9499
+ }
9500
+ if (this._catalogueShaderProgram.locations.selectedColor) {
9501
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.selectedColor, ...interactionColors.selected, 1.0);
9502
+ }
9045
9503
  // selected flags
9046
9504
  for (let s = 0; s < this.selectedIndexes.length; s++) {
9047
9505
  const idx = this.selectedIndexes[s];
@@ -9052,7 +9510,8 @@ class CatalogueGL {
9052
9510
  // clear old hovered
9053
9511
  for (let k = 0; k < this.hoveredIndexes.length; k++) {
9054
9512
  const base = this.hoveredIndexes[k] * CatalogueGL.ELEM_SIZE;
9055
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9513
+ if (this.selectedIndexes.includes(this.hoveredIndexes[k]))
9514
+ continue;
9056
9515
  this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
9057
9516
  this.vertexCataloguePosition[base + 4] =
9058
9517
  this._sources[this.hoveredIndexes[k]].shapeSize; // size
@@ -9066,7 +9525,7 @@ class CatalogueGL {
9066
9525
  // this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
9067
9526
  // this.vertexCataloguePosition[base + 4] = this._sources[this.hoveredIndexes[k]].shapeSize; // size
9068
9527
  // }
9069
- this.hoveredIndexes = this.checkHovering(in_mouseHelper);
9528
+ this.hoveredIndexes = this.checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
9070
9529
  // // new hovered
9071
9530
  // for (let i = 0; i < this.hoveredIndexes.length; i++) {
9072
9531
  // const idx = this.hoveredIndexes[i];
@@ -9080,7 +9539,8 @@ class CatalogueGL {
9080
9539
  for (let i = 0; i < this.hoveredIndexes.length; i++) {
9081
9540
  const idx = this.hoveredIndexes[i];
9082
9541
  const base = idx * CatalogueGL.ELEM_SIZE;
9083
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9542
+ if (this.selectedIndexes.includes(idx))
9543
+ continue;
9084
9544
  this.vertexCataloguePosition[base + 3] = 1.0; // hovered
9085
9545
  this.vertexCataloguePosition[base + 4] = this._sources[idx].shapeSize; // size
9086
9546
  }
@@ -9484,6 +9944,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9484
9944
  _latLonGrid;
9485
9945
  _colorMapIdx = 0;
9486
9946
  _colorMap = ColorMaps_js_1.ColorMaps['native'];
9947
+ _opacity = 1;
9487
9948
  constructor(radius, position, xrad, yrad, descriptor, webgl, gridLabelContainers) {
9488
9949
  super(radius, position, xrad, yrad, descriptor.name, webgl, false);
9489
9950
  this._descriptor = descriptor;
@@ -9498,6 +9959,10 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9498
9959
  const fov = 180;
9499
9960
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9500
9961
  }
9962
+ setOpacity(opacity) {
9963
+ this._opacity = Math.min(1, Math.max(0, opacity));
9964
+ this._xyzShaderProgram.setOpacity(this._opacity);
9965
+ }
9501
9966
  changeColorMap(colorMap) {
9502
9967
  this._colorMap = colorMap;
9503
9968
  switch (colorMap.name) {
@@ -9545,6 +10010,9 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9545
10010
  initShaders() {
9546
10011
  this._xyzShaderProgram.enableProgram();
9547
10012
  }
10013
+ clearLonLatGridLabels() {
10014
+ this._latLonGrid.clearLabels();
10015
+ }
9548
10016
  isLonLatGridVisible() {
9549
10017
  return this._latLonGrid.isVisible();
9550
10018
  }
@@ -9559,7 +10027,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9559
10027
  // this._zoom = this.resolveVisibleZoom(fov)
9560
10028
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9561
10029
  }
9562
- draw(input) {
10030
+ draw(input, asOverlay = false) {
9563
10031
  const vMatrix = input.camera.getCameraMatrix();
9564
10032
  if (!vMatrix)
9565
10033
  return;
@@ -9569,6 +10037,18 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9569
10037
  this.refresh(input);
9570
10038
  const mMatrix = this.getModelMatrix();
9571
10039
  this._xyzShaderProgram.setRuntimeColorMap(this._colorMap);
10040
+ this._xyzShaderProgram.setOpacity(this._opacity);
10041
+ const depthTestWasEnabled = this._webgl.isEnabled(this._webgl.DEPTH_TEST);
10042
+ const blendWasEnabled = this._webgl.isEnabled(this._webgl.BLEND);
10043
+ const blendSrcRgb = this._webgl.getParameter(this._webgl.BLEND_SRC_RGB);
10044
+ const blendDstRgb = this._webgl.getParameter(this._webgl.BLEND_DST_RGB);
10045
+ const blendSrcAlpha = this._webgl.getParameter(this._webgl.BLEND_SRC_ALPHA);
10046
+ const blendDstAlpha = this._webgl.getParameter(this._webgl.BLEND_DST_ALPHA);
10047
+ if (asOverlay) {
10048
+ this._webgl.disable(this._webgl.DEPTH_TEST);
10049
+ this._webgl.enable(this._webgl.BLEND);
10050
+ this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
10051
+ }
9572
10052
  const tileSelection = this._visibleTilesManager.computeVisibleTiles(this._zoom, this, this._webgl, input.camera, input.pMatrix);
9573
10053
  const visibleTiles = tileSelection.visibleTiles;
9574
10054
  const ancestorsMap = tileSelection.ancestorsMap;
@@ -9586,7 +10066,20 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9586
10066
  const ancestorTile = this.findBestAvailableAncestor(tile.coord);
9587
10067
  ancestorTile?.draw(tileSelection.currentZoom, [tile.coord], ancestorsMap, pMatrix, vMatrix, mMatrix, this._colorMapIdx);
9588
10068
  }
9589
- this._latLonGrid.draw(input);
10069
+ if (asOverlay) {
10070
+ if (depthTestWasEnabled)
10071
+ this._webgl.enable(this._webgl.DEPTH_TEST);
10072
+ else
10073
+ this._webgl.disable(this._webgl.DEPTH_TEST);
10074
+ this._webgl.blendFuncSeparate(blendSrcRgb, blendDstRgb, blendSrcAlpha, blendDstAlpha);
10075
+ if (blendWasEnabled)
10076
+ this._webgl.enable(this._webgl.BLEND);
10077
+ else
10078
+ this._webgl.disable(this._webgl.BLEND);
10079
+ }
10080
+ else {
10081
+ this._latLonGrid.draw(input);
10082
+ }
9590
10083
  }
9591
10084
  createTile(coord) {
9592
10085
  return new XYZAnchestorTile_js_1.XYZAnchestorTile(coord, this.resolveTileUrl(coord), this._webgl, this._xyzShaderProgram, this._meshBuilder, this._descriptor.segmentsPerSide);
@@ -10872,6 +11365,10 @@ class FootprintSetGL {
10872
11365
  }
10873
11366
  return clickedIndexes;
10874
11367
  }
11368
+ clearSelection() {
11369
+ this.selectedIndexes = [];
11370
+ this.refreshSelectedFootprints();
11371
+ }
10875
11372
  setSelectedIndexes(selectedIndex) {
10876
11373
  selectedIndex.forEach((idx) => {
10877
11374
  if (idx < 0 || idx >= this.footprintPolygons.length)
@@ -11173,9 +11670,10 @@ class FootprintSetGL {
11173
11670
  if (in_mouseHelper != null && in_mouseHelper.xyz != this.oldMouseCoords) {
11174
11671
  this.checkSelection(in_mouseHelper);
11175
11672
  }
11673
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
11176
11674
  if (this._hoveredFootprints.length > 0) {
11177
11675
  // TODO POINT_SIZE doesn't have any effect on line thickness!! it only applies to points
11178
- const rgb = (0, Utils_js_1.colorHex2RGB)("#00FF00");
11676
+ const rgb = interactionColors.hover;
11179
11677
  const alpha = 1.0;
11180
11678
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
11181
11679
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -11190,7 +11688,7 @@ class FootprintSetGL {
11190
11688
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.hoveredVertexPosition.length / 3 + this.nHoveredPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11191
11689
  }
11192
11690
  if (this._selectedFootprints.length > 0) {
11193
- const rgb = (0, Utils_js_1.colorHex2RGB)("#ECB462");
11691
+ const rgb = interactionColors.selected;
11194
11692
  const alpha = 1.0;
11195
11693
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
11196
11694
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -11203,6 +11701,11 @@ class FootprintSetGL {
11203
11701
  this._webgl.bufferData(this._webgl.ELEMENT_ARRAY_BUFFER, this.selectedElementIndexes, this._webgl.STATIC_DRAW);
11204
11702
  // this._gl.drawElements (this._gl.LINE_LOOP, this._selectedVertexPosition.length / 3 + this._nSlectedPrimitiveFlags,this._gl.UNSIGNED_SHORT, 0);
11205
11703
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11704
+ // Keep selection visually distinct from hover without relying on
11705
+ // non-portable line widths. Reuse the selected geometry as small
11706
+ // vertex markers; POINT_SIZE is supported for POINTS in WebGL2.
11707
+ this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 4.0);
11708
+ this._webgl.drawElements(this._webgl.POINTS, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11206
11709
  }
11207
11710
  this._webgl.bindBuffer(this._webgl.ARRAY_BUFFER, this.vertexCataloguePositionBuffer);
11208
11711
  this._webgl.vertexAttribPointer(this._footprintShaderProgram.locations.position, FootprintSetGL.ELEM_SIZE, this._webgl.FLOAT, false, FootprintSetGL.BYTES_X_ELEM * FootprintSetGL.ELEM_SIZE, 0);
@@ -11435,6 +11938,9 @@ class EquatorialGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11435
11938
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
11436
11939
  }
11437
11940
  }
11941
+ clearLabels() {
11942
+ this.gridText.resetDivSets();
11943
+ }
11438
11944
  isVisible() {
11439
11945
  return this.showGrid;
11440
11946
  }
@@ -12029,6 +12535,9 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12029
12535
  this.gridText.resetDivSets();
12030
12536
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
12031
12537
  }
12538
+ clearLabels() {
12539
+ this.gridText.resetDivSets();
12540
+ }
12032
12541
  get visibleorder() {
12033
12542
  return this._visibleorder;
12034
12543
  }
@@ -12262,6 +12771,9 @@ class LatLonGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12262
12771
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
12263
12772
  }
12264
12773
  }
12774
+ clearLabels() {
12775
+ this.gridText.resetDivSets();
12776
+ }
12265
12777
  draw(input) {
12266
12778
  if (!this._showGrid) {
12267
12779
  this.gridText.resetDivSets();
@@ -12642,8 +13154,10 @@ class AllSky {
12642
13154
  continue;
12643
13155
  }
12644
13156
  const childTile = this._isGalacticHips
12645
- ? this._tileBuffer.getGalTile(tileno, childrenOrder, this._hips)
12646
- : this._tileBuffer.getTile(tileno, childrenOrder, this._hips);
13157
+ ? this._tileBuffer.getRenderableGalTile(tileno, childrenOrder, this._hips)
13158
+ : this._tileBuffer.getRenderableTile(tileno, childrenOrder, this._hips);
13159
+ if (!childTile)
13160
+ continue;
12647
13161
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, this._hipsShaderProgram);
12648
13162
  if (childTile.getReadyState()) {
12649
13163
  allSkyTiles2Skip.push(tileno);
@@ -12931,8 +13445,10 @@ class AncestorTile {
12931
13445
  continue;
12932
13446
  }
12933
13447
  const childTile = this._isGalacticHips
12934
- ? this._tileBuffer.getGalTile(childTileNo, childrenOrder, this._hips)
12935
- : this._tileBuffer.getTile(childTileNo, childrenOrder, this._hips);
13448
+ ? this._tileBuffer.getRenderableGalTile(childTileNo, childrenOrder, this._hips)
13449
+ : this._tileBuffer.getRenderableTile(childTileNo, childrenOrder, this._hips);
13450
+ if (!childTile)
13451
+ continue;
12936
13452
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, this._hipsShaderProgram);
12937
13453
  if (childTile.getReadyState()) {
12938
13454
  quadrantsToDraw.delete(c);
@@ -14450,8 +14966,10 @@ class Tile {
14450
14966
  continue;
14451
14967
  }
14452
14968
  const childTile = this._isGalacticHips
14453
- ? this._tileBuffer.getGalTile(childTileNo, childrenOrder, this._hips)
14454
- : this._tileBuffer.getTile(childTileNo, childrenOrder, this._hips);
14969
+ ? this._tileBuffer.getRenderableGalTile(childTileNo, childrenOrder, this._hips)
14970
+ : this._tileBuffer.getRenderableTile(childTileNo, childrenOrder, this._hips);
14971
+ if (!childTile)
14972
+ continue;
14455
14973
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, hipsShaderProgram);
14456
14974
  if (childTile.getReadyState()) {
14457
14975
  quadrantsToDraw.delete(c);
@@ -14502,6 +15020,7 @@ class TileBuffer {
14502
15020
  _webgl;
14503
15021
  _visibleTileManager;
14504
15022
  _hipsShaderProgram;
15023
+ _tileAcquisitionEnabled = true;
14505
15024
  constructor(minutesToLiveInCache = 1, webgl, hipsShaderProgram, visibleTileManager) {
14506
15025
  this._hipsShaderProgram = hipsShaderProgram;
14507
15026
  this._visibleTileManager = visibleTileManager;
@@ -14533,8 +15052,14 @@ class TileBuffer {
14533
15052
  }
14534
15053
  this._galActiveHiPS.set(hips, new Map());
14535
15054
  }
15055
+ /** Enable or suspend creation of missing HiPS tiles. */
15056
+ setTileAcquisitionEnabled(enabled) {
15057
+ this._tileAcquisitionEnabled = enabled;
15058
+ }
14536
15059
  /** Preload/add tile for every registered equatorial HiPS. */
14537
15060
  addTile(order, tileno) {
15061
+ if (!this._tileAcquisitionEnabled)
15062
+ return;
14538
15063
  for (const hips of this._activeHiPS.keys()) {
14539
15064
  if (order > hips.maxOrder) {
14540
15065
  continue;
@@ -14547,6 +15072,8 @@ class TileBuffer {
14547
15072
  }
14548
15073
  /** Preload/add tile for every registered galactic HiPS. */
14549
15074
  addGalTile(order, tileno) {
15075
+ if (!this._tileAcquisitionEnabled)
15076
+ return;
14550
15077
  for (const hips of this._galActiveHiPS.keys()) {
14551
15078
  if (order > hips.maxOrder) {
14552
15079
  continue;
@@ -14593,6 +15120,44 @@ class TileBuffer {
14593
15120
  }
14594
15121
  return this._galTiles.get(tileKey);
14595
15122
  }
15123
+ /**
15124
+ * Return an equatorial tile for rendering. While acquisition is suspended,
15125
+ * only tiles already active or cached are returned; missing tiles are not
15126
+ * created.
15127
+ */
15128
+ getRenderableTile(tileno, order, hips) {
15129
+ if (this._tileAcquisitionEnabled) {
15130
+ return this.getTile(tileno, order, hips);
15131
+ }
15132
+ const tileKey = this.key(order, tileno, hips);
15133
+ const activeTile = this._tiles.get(tileKey);
15134
+ if (activeTile)
15135
+ return activeTile;
15136
+ const cachedTile = this._cachedTiles.get(tileKey);
15137
+ if (!cachedTile)
15138
+ return undefined;
15139
+ this._tiles.set(tileKey, cachedTile);
15140
+ this._cachedTiles.delete(tileKey);
15141
+ cachedTile.resetCacheTime0();
15142
+ return cachedTile;
15143
+ }
15144
+ /** Galactic counterpart of getRenderableTile(). */
15145
+ getRenderableGalTile(tileno, order, hips) {
15146
+ if (this._tileAcquisitionEnabled) {
15147
+ return this.getGalTile(tileno, order, hips);
15148
+ }
15149
+ const tileKey = this.key(order, tileno, hips);
15150
+ const activeTile = this._galTiles.get(tileKey);
15151
+ if (activeTile)
15152
+ return activeTile;
15153
+ const cachedTile = this._galCachedTiles.get(tileKey);
15154
+ if (!cachedTile)
15155
+ return undefined;
15156
+ this._galTiles.set(tileKey, cachedTile);
15157
+ this._galCachedTiles.delete(tileKey);
15158
+ cachedTile.resetCacheTime0();
15159
+ return cachedTile;
15160
+ }
14596
15161
  /** Move a tile (equatorial or galactic) into cache. */
14597
15162
  moveTileToCache(tileno, order, hips) {
14598
15163
  const tileKey = this.key(order, tileno, hips);
@@ -14800,6 +15365,10 @@ class VisibleTilesManager {
14800
15365
  if (Global_js_1.default.insideSphere && order < 3) {
14801
15366
  order = 3;
14802
15367
  }
15368
+ // Visibility state is frame-local. Do not retain ancestors from previous
15369
+ // camera positions, otherwise a flyTo accumulates the whole travelled path.
15370
+ this._ancestorsMap.clear();
15371
+ this._galAncestorsMap.clear();
14803
15372
  this._ancestorsMap.set(order, []);
14804
15373
  this._galAncestorsMap.set(order, []);
14805
15374
  let pixels = [];
@@ -14816,8 +15385,10 @@ class VisibleTilesManager {
14816
15385
  }
14817
15386
  else {
14818
15387
  const geomhealpix = Global_js_1.default.getHealpix(order);
14819
- const maxX = webgl.canvas.width;
14820
- const maxY = webgl.canvas.height;
15388
+ const canvas = webgl.canvas;
15389
+ const canvasRect = canvas.getBoundingClientRect();
15390
+ const maxX = canvasRect.width;
15391
+ const maxY = canvasRect.height;
14821
15392
  // Sample a grid of screen points, project to the sphere, then to galactic
14822
15393
  for (let i = 0; i <= maxX; i += maxX / 30) {
14823
15394
  for (let j = 0; j <= maxY; j += maxY / 30) {
@@ -14919,6 +15490,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14919
15490
  _currentOrder;
14920
15491
  _visibleTiles = [];
14921
15492
  _coverageTiles = [];
15493
+ _color;
15494
+ _wireframe;
14922
15495
  constructor(radius, position, xrad, yrad, _descriptor, webgl, _healpixGrid) {
14923
15496
  super(radius, position, xrad, yrad, _descriptor.name, webgl, false);
14924
15497
  this._descriptor = _descriptor;
@@ -14927,6 +15500,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14927
15500
  this._shaderProgram = new MeshHiPSShaderProgram_js_1.MeshHiPSShaderProgram(webgl);
14928
15501
  this._shaderProgram.enableProgram();
14929
15502
  this._currentOrder = _descriptor.selectedOrder;
15503
+ this._color = [..._descriptor.color];
15504
+ this._wireframe = _descriptor.wireframe;
14930
15505
  }
14931
15506
  get currentOrder() {
14932
15507
  return this._currentOrder;
@@ -14949,6 +15524,12 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14949
15524
  getProperty(key) {
14950
15525
  return this._descriptor.getProperty(key);
14951
15526
  }
15527
+ setColor(color) {
15528
+ this._color = [...color];
15529
+ }
15530
+ setWireframe(wireframe) {
15531
+ this._wireframe = wireframe;
15532
+ }
14952
15533
  refreshOrder(fovDeg) {
14953
15534
  if (this._descriptor.fixedOrder) {
14954
15535
  this._currentOrder = this._descriptor.selectedOrder;
@@ -14988,7 +15569,7 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14988
15569
  for (const coord of byOrder.get(order) ?? []) {
14989
15570
  this._tiles
14990
15571
  .get(this.tileKey(coord))
14991
- ?.draw(pMatrix, vMatrix, mMatrix, this._descriptor.color, this._descriptor.wireframe);
15572
+ ?.draw(pMatrix, vMatrix, mMatrix, this._color, this._wireframe);
14992
15573
  }
14993
15574
  }
14994
15575
  if (wasCullFace)
@@ -16769,6 +17350,7 @@ exports.TerraFootprintSetGL = TerraFootprintSetGL;
16769
17350
  Object.defineProperty(exports, "__esModule", ({ value: true }));
16770
17351
  exports.TerraPointSetGL = void 0;
16771
17352
  const CatalogueGL_js_1 = __webpack_require__(1232);
17353
+ const gl_matrix_1 = __webpack_require__(3890);
16772
17354
  const MetadataManager_js_1 = __webpack_require__(5403);
16773
17355
  const Point_js_1 = __webpack_require__(6553);
16774
17356
  const CoordsType_js_1 = __webpack_require__(8145);
@@ -16803,8 +17385,45 @@ class TerraPointSetGL extends CatalogueGL_js_1.CatalogueGL {
16803
17385
  this._ready = true;
16804
17386
  this._bufferInitialised = false;
16805
17387
  }
17388
+ findNearestSourceIndex(inMouseHelper, inMatrix, viewMatrix, projectionMatrix) {
17389
+ if (inMouseHelper.xyz == null)
17390
+ return null;
17391
+ const canvas = this.getCanvas();
17392
+ const width = canvas.clientWidth;
17393
+ const height = canvas.clientHeight;
17394
+ if (!width || !height)
17395
+ return null;
17396
+ const modelView = gl_matrix_1.mat4.create();
17397
+ const mvp = gl_matrix_1.mat4.create();
17398
+ gl_matrix_1.mat4.multiply(modelView, viewMatrix, inMatrix);
17399
+ gl_matrix_1.mat4.multiply(mvp, projectionMatrix, modelView);
17400
+ const mousePoint = new Point_js_1.Point({
17401
+ x: inMouseHelper.xyz[0],
17402
+ y: inMouseHelper.xyz[1],
17403
+ z: inMouseHelper.xyz[2],
17404
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
17405
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
17406
+ if (!mouseScreen)
17407
+ return null;
17408
+ let nearestIndex = null;
17409
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
17410
+ for (let index = 0; index < this._sources.length; index += 1) {
17411
+ const source = this._sources[index];
17412
+ if (!source)
17413
+ continue;
17414
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
17415
+ if (!sourceScreen)
17416
+ continue;
17417
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
17418
+ if (distancePx <= this.getHitRadiusPx(source) &&
17419
+ distancePx < nearestDistancePx) {
17420
+ nearestDistancePx = distancePx;
17421
+ nearestIndex = index;
17422
+ }
17423
+ }
17424
+ return nearestIndex;
17425
+ }
16806
17426
  draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix) {
16807
- this._oldMouseCoords = inMouseHelper?.xyz ?? null;
16808
17427
  super.draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix);
16809
17428
  }
16810
17429
  }
@@ -17034,6 +17653,8 @@ class CatalogueShaderProgram {
17034
17653
  this._webgl = webgl;
17035
17654
  this.gl_uniforms = {
17036
17655
  vertex_color: 'u_fragcolor',
17656
+ hover_color: 'u_hovercolor',
17657
+ selected_color: 'u_selectedcolor',
17037
17658
  m_perspective: 'uPMatrix',
17038
17659
  m_model_view: 'uMVMatrix'
17039
17660
  };
@@ -17047,6 +17668,8 @@ class CatalogueShaderProgram {
17047
17668
  pMatrix: null,
17048
17669
  mvMatrix: null,
17049
17670
  color: null,
17671
+ hoverColor: null,
17672
+ selectedColor: null,
17050
17673
  position: -1,
17051
17674
  hovered: -1,
17052
17675
  pointSize: -1,
@@ -17096,6 +17719,8 @@ class CatalogueShaderProgram {
17096
17719
  this.locations.pointSize = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_size);
17097
17720
  this.locations.brightness = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_hue);
17098
17721
  this.locations.color = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.vertex_color);
17722
+ this.locations.hoverColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.hover_color);
17723
+ this.locations.selectedColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.selected_color);
17099
17724
  }
17100
17725
  enableShaders(pMatrix, modelMatrix, viewMatrix) {
17101
17726
  // const gl = global.gl as GL
@@ -17781,6 +18406,8 @@ class ShaderManager {
17781
18406
  in float v_brightness;
17782
18407
 
17783
18408
  uniform vec4 u_fragcolor;
18409
+ uniform vec4 u_hovercolor;
18410
+ uniform vec4 u_selectedcolor;
17784
18411
 
17785
18412
  out vec4 fragColor;
17786
18413
 
@@ -17837,11 +18464,12 @@ class ShaderManager {
17837
18464
  #endif
17838
18465
 
17839
18466
  if (v_selected == 1.0){
17840
- // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
17841
- fragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
18467
+ fragColor = u_hovercolor * alpha;
17842
18468
  } else if (v_selected == 2.0){
17843
- // gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
17844
- fragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
18469
+ // Persistent selection: keep the dataset colour as an outer ring
18470
+ // and use the hover colour for the fill. This remains visible for
18471
+ // both light and dark user-selected base colours.
18472
+ fragColor = (r > 0.72 ? u_fragcolor : u_hovercolor) * alpha;
17845
18473
  }else{
17846
18474
  if (r < 0.4) {
17847
18475
  discard;
@@ -18248,6 +18876,7 @@ class XYZShaderProgram {
18248
18876
  _shaderProgram;
18249
18877
  _colorMapBlockIndex = null;
18250
18878
  _colorMapBuffer = null;
18879
+ _opacity = 1;
18251
18880
  _runtimeColorMap;
18252
18881
  _colorMapVariableInfo = {
18253
18882
  r_palette: { index: 0, offset: 0 },
@@ -18262,6 +18891,7 @@ class XYZShaderProgram {
18262
18891
  vMatrix: null,
18263
18892
  sampler: null,
18264
18893
  colorMapIdx: null,
18894
+ opacity: null,
18265
18895
  vertexPositionAttribute: -1,
18266
18896
  textureCoordAttribute: -1,
18267
18897
  };
@@ -18282,6 +18912,9 @@ class XYZShaderProgram {
18282
18912
  enableProgram() {
18283
18913
  this._webgl.useProgram(this.shaderProgram);
18284
18914
  }
18915
+ setOpacity(opacity) {
18916
+ this._opacity = Math.min(1, Math.max(0, opacity));
18917
+ }
18285
18918
  setRuntimeColorMap(colorMap) {
18286
18919
  this._runtimeColorMap = colorMap;
18287
18920
  }
@@ -18294,6 +18927,7 @@ class XYZShaderProgram {
18294
18927
  this.locations.vMatrix = gl.getUniformLocation(program, 'uVMatrix');
18295
18928
  this.locations.sampler = gl.getUniformLocation(program, 'uSampler');
18296
18929
  this.locations.colorMapIdx = gl.getUniformLocation(program, 'cmapIdx');
18930
+ this.locations.opacity = gl.getUniformLocation(program, 'uOpacity');
18297
18931
  this.locations.vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
18298
18932
  this.locations.textureCoordAttribute = gl.getAttribLocation(program, 'aTextureCoord');
18299
18933
  gl.uniformMatrix4fv(this.locations.pMatrix, false, pMatrix);
@@ -18301,6 +18935,7 @@ class XYZShaderProgram {
18301
18935
  gl.uniformMatrix4fv(this.locations.mMatrix, false, mMatrix);
18302
18936
  gl.uniform1i(this.locations.sampler, 0);
18303
18937
  gl.uniform1i(this.locations.colorMapIdx, colorMapIdx);
18938
+ gl.uniform1f(this.locations.opacity, this._opacity);
18304
18939
  if (colorMapIdx >= 2) {
18305
18940
  this.uploadColorMap(colorMapIdx);
18306
18941
  }
@@ -18323,6 +18958,7 @@ class XYZShaderProgram {
18323
18958
  in vec2 vTextureCoord;
18324
18959
  uniform sampler2D uSampler;
18325
18960
  uniform int cmapIdx;
18961
+ uniform float uOpacity;
18326
18962
 
18327
18963
  layout (std140) uniform colormap {
18328
18964
  float r_palette[256];
@@ -18337,7 +18973,7 @@ class XYZShaderProgram {
18337
18973
 
18338
18974
  if (cmapIdx == 1) {
18339
18975
  float gray = 0.21 * color.r + 0.71 * color.g + 0.07 * color.b;
18340
- outColor = vec4(vec3(gray), color.a);
18976
+ outColor = vec4(vec3(gray), color.a * uOpacity);
18341
18977
  return;
18342
18978
  }
18343
18979
 
@@ -18350,12 +18986,12 @@ class XYZShaderProgram {
18350
18986
  r_palette[rIndex] / 256.0,
18351
18987
  g_palette[gIndex] / 256.0,
18352
18988
  b_palette[bIndex] / 256.0,
18353
- color.a
18989
+ color.a * uOpacity
18354
18990
  );
18355
18991
  return;
18356
18992
  }
18357
18993
 
18358
- outColor = color;
18994
+ outColor = vec4(color.rgb, color.a * uOpacity);
18359
18995
  }`);
18360
18996
  gl.attachShader(this._shaderProgram, vertexShader);
18361
18997
  gl.attachShader(this._shaderProgram, fragmentShader);
@@ -19557,7 +20193,6 @@ exports["default"] = STCSParser;
19557
20193
  /***/ 7930
19558
20194
  (__unused_webpack_module, exports, __webpack_require__) {
19559
20195
 
19560
- var __webpack_unused_export__;
19561
20196
 
19562
20197
  /*
19563
20198
  * AstroViewer
@@ -19569,9 +20204,10 @@ var __webpack_unused_export__;
19569
20204
  *
19570
20205
  * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
19571
20206
  */
19572
- __webpack_unused_export__ = ({ value: true });
20207
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
19573
20208
  exports.cartesianToSpherical = cartesianToSpherical;
19574
20209
  exports.colorHex2RGB = colorHex2RGB;
20210
+ exports.interactionColorsFromHex = interactionColorsFromHex;
19575
20211
  exports.degToRad = degToRad;
19576
20212
  exports.radToDeg = radToDeg;
19577
20213
  exports.sphericalToAstroDeg = sphericalToAstroDeg;
@@ -19579,6 +20215,8 @@ exports.sphericalToCartesian = sphericalToCartesian;
19579
20215
  exports.astroDegToSpherical = astroDegToSpherical;
19580
20216
  exports.raDegToHMS = raDegToHMS;
19581
20217
  exports.decDegToDMS = decDegToDMS;
20218
+ exports.raHMSToDeg = raHMSToDeg;
20219
+ exports.decDMSToDeg = decDMSToDeg;
19582
20220
  /**
19583
20221
  * @author Fabrizio Giordano (Fab)
19584
20222
  */
@@ -19609,6 +20247,25 @@ function colorHex2RGB(hexColor) {
19609
20247
  const rgb3 = (dec3 / 255).toFixed(2);
19610
20248
  return [parseFloat(rgb1), parseFloat(rgb2), parseFloat(rgb3)];
19611
20249
  }
20250
+ /**
20251
+ * Derive interaction colours from the object's normal colour.
20252
+ * Dark colours move towards white; light colours move towards black.
20253
+ * Selection uses the stronger contrast so selected > hover > normal.
20254
+ */
20255
+ function interactionColorsFromHex(hexColor) {
20256
+ const base = colorHex2RGB(hexColor);
20257
+ const luminance = 0.2126 * base[0] + 0.7152 * base[1] + 0.0722 * base[2];
20258
+ const target = luminance < 0.55 ? 1.0 : 0.0;
20259
+ const mix = (amount) => [
20260
+ base[0] + (target - base[0]) * amount,
20261
+ base[1] + (target - base[1]) * amount,
20262
+ base[2] + (target - base[2]) * amount,
20263
+ ];
20264
+ return {
20265
+ hover: mix(0.45),
20266
+ selected: mix(0.45),
20267
+ };
20268
+ }
19612
20269
  function degToRad(degrees) {
19613
20270
  return (degrees / 180) * Math.PI;
19614
20271
  }
@@ -19655,6 +20312,13 @@ function decDegToDMS(decDeg) {
19655
20312
  d = d * sign;
19656
20313
  return { d, m, s };
19657
20314
  }
20315
+ function raHMSToDeg(hms) {
20316
+ return 15 * (hms.h + hms.m / 60 + hms.s / 3600);
20317
+ }
20318
+ function decDMSToDeg(dms) {
20319
+ const sign = dms.d < 0 || Object.is(dms.d, -0) ? -1 : 1;
20320
+ return sign * (Math.abs(dms.d) + dms.m / 60 + dms.s / 3600);
20321
+ }
19658
20322
 
19659
20323
 
19660
20324
  /***/ },