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
@@ -3641,12 +3641,18 @@ class AstroSphere {
3641
3641
  _activeHiPS = null;
3642
3642
  _activeHiPSLayers = [];
3643
3643
  _activeXYZ2 = null;
3644
+ _earthRasterOverlays = [];
3645
+ _earthRasterOverlaySequence = 0;
3644
3646
  _activeMeshHiPS = null;
3645
3647
  _activeBaseLayer = null;
3648
+ _activeDomain = "astronomy";
3649
+ domainCameraStates = {};
3646
3650
  startup = true;
3647
3651
  fov;
3648
- activeCatalogues = [];
3649
- activeFootprintSets = [];
3652
+ astronomyCatalogues = [];
3653
+ earthPointSets = [];
3654
+ astronomyFootprintSets = [];
3655
+ earthFootprintSets = [];
3650
3656
  activePolylineSets = [];
3651
3657
  activeSensorCones = [];
3652
3658
  activeSatelliteObjects = [];
@@ -3662,6 +3668,7 @@ class AstroSphere {
3662
3668
  lockedNorthSouthDecDeg = null;
3663
3669
  keepCameraNorthUp = true;
3664
3670
  gridLabelContainers;
3671
+ fovAnimationResolve = null;
3665
3672
  constructor(canvas, webgl, options = {}) {
3666
3673
  console.log("[AstroSphere] new instance for canvas", canvas.id);
3667
3674
  // Keep global GL context (as in original JS)
@@ -3879,9 +3886,7 @@ class AstroSphere {
3879
3886
  return true;
3880
3887
  }
3881
3888
  emitCameraChanged(reason) {
3882
- // avoid dispatch before scene is ready
3883
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
3884
- return;
3889
+ // Camera interaction remains meaningful even when no base layer is active.
3885
3890
  if (!this._healpixGrid?.fovObj)
3886
3891
  return;
3887
3892
  const detail = this.getCurrentStatus();
@@ -3908,6 +3913,7 @@ class AstroSphere {
3908
3913
  canvas.setPointerCapture(event.pointerId);
3909
3914
  this.mouseDown = true;
3910
3915
  this._camera.cancelFlyTo();
3916
+ this.cancelFoVAnimation();
3911
3917
  const rect = canvas.getBoundingClientRect();
3912
3918
  this.lastMouseX = event.clientX - rect.left; // locale al canvas
3913
3919
  this.lastMouseY = event.clientY - rect.top; // locale al canvas
@@ -3946,8 +3952,11 @@ class AstroSphere {
3946
3952
  if (mousePoint && mousePoint.length > 0) {
3947
3953
  this.mouseHelper.update(mousePoint);
3948
3954
  this.updateLastMousePoint();
3949
- for (const cat of this.activeCatalogues) {
3950
- const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper);
3955
+ for (const cat of this.getActivePointSets()) {
3956
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
3957
+ if (!activeModelMatrix)
3958
+ continue;
3959
+ const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
3951
3960
  if (!clickResult?.sources.length)
3952
3961
  continue;
3953
3962
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-clicked", {
@@ -3960,7 +3969,7 @@ class AstroSphere {
3960
3969
  composed: true,
3961
3970
  }));
3962
3971
  }
3963
- for (const fset of this.activeFootprintSets) {
3972
+ for (const fset of this.getActiveFootprintSets()) {
3964
3973
  const clickResult = fset.selectPrimaryFootprintFromClick(this.mouseHelper);
3965
3974
  if (!clickResult?.footprints.length)
3966
3975
  continue;
@@ -4035,6 +4044,7 @@ class AstroSphere {
4035
4044
  const currentFov = this._healpixGrid.getMinFoV();
4036
4045
  const zoomStep = this.computeZoomStep(currentFov, event.deltaY);
4037
4046
  this._camera.cancelFlyTo();
4047
+ this.cancelFoVAnimation();
4038
4048
  // Apply wheel zoom immediately and discard any queued inertia so reversing
4039
4049
  // direction feels responsive instead of "buffered".
4040
4050
  this.zoomInertia = 0;
@@ -4057,8 +4067,11 @@ class AstroSphere {
4057
4067
  }
4058
4068
  this.mouseHelper.update(mousePoint);
4059
4069
  this.updateLastMousePoint();
4060
- for (const cat of this.activeCatalogues) {
4061
- const pickResult = cat.getSourcesFromPointer(this.mouseHelper);
4070
+ for (const cat of this.getActivePointSets()) {
4071
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4072
+ if (!activeModelMatrix)
4073
+ continue;
4074
+ const pickResult = cat.getSourcesFromPointer(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4062
4075
  if (!pickResult?.sources.length)
4063
4076
  continue;
4064
4077
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-contextmenu", {
@@ -4073,7 +4086,7 @@ class AstroSphere {
4073
4086
  }));
4074
4087
  break;
4075
4088
  }
4076
- for (const fset of this.activeFootprintSets) {
4089
+ for (const fset of this.getActiveFootprintSets()) {
4077
4090
  const pickResult = fset.getFootprintsFromPointer(this.mouseHelper);
4078
4091
  if (!pickResult?.footprints.length)
4079
4092
  continue;
@@ -4163,19 +4176,22 @@ class AstroSphere {
4163
4176
  return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
4164
4177
  }
4165
4178
  activateHiPS(hipsDescriptor) {
4179
+ if (this._activeHiPSLayers.length > 0) {
4180
+ throw new Error("Cannot load a HiPS base layer while stacked layers are active.");
4181
+ }
4166
4182
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4167
- for (const hips of this._activeHiPSLayers) {
4168
- tileBuffer.removeHiPS(hips);
4183
+ if (this._activeHiPS) {
4184
+ tileBuffer.removeHiPS(this._activeHiPS);
4169
4185
  }
4170
- this._activeHiPSLayers = [];
4171
4186
  const hips = this.createHiPS(hipsDescriptor);
4172
- this._activeHiPSLayers.push(hips);
4173
4187
  this._activeHiPS = hips;
4188
+ this._activeDomain = "astronomy";
4174
4189
  this._activeBaseLayer = "hips";
4175
4190
  return hips;
4176
4191
  }
4177
4192
  setHiPSOpacity(hips, opacity) {
4178
- if (!this._activeHiPSLayers.includes(hips)) {
4193
+ const isBaseLayer = this._activeHiPSLayers.length === 0 && this._activeHiPS === hips;
4194
+ if (!isBaseLayer && !this._activeHiPSLayers.includes(hips)) {
4179
4195
  throw new Error("HiPS layer is not active in this AstroSphere.");
4180
4196
  }
4181
4197
  hips.setOpacity(opacity);
@@ -4183,6 +4199,13 @@ class AstroSphere {
4183
4199
  addHiPS(hipsDescriptor) {
4184
4200
  const normalizeURL = (url) => String(url).replace(/\/+$/, "");
4185
4201
  const descriptorURL = normalizeURL(hipsDescriptor.url);
4202
+ // Entering stack mode discards the standalone base HiPS. Base mode and
4203
+ // stack mode are intentionally mutually exclusive.
4204
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4205
+ const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4206
+ tileBuffer.removeHiPS(this._activeHiPS);
4207
+ this._activeHiPS = null;
4208
+ }
4186
4209
  const existing = this._activeHiPSLayers.find((hips) => normalizeURL(hips.baseURL) === descriptorURL);
4187
4210
  if (existing) {
4188
4211
  throw new Error(`HiPS already active: ${String(hipsDescriptor.url)}`);
@@ -4190,6 +4213,7 @@ class AstroSphere {
4190
4213
  const hips = this.createHiPS(hipsDescriptor);
4191
4214
  this._activeHiPSLayers.push(hips);
4192
4215
  this._activeHiPS = hips;
4216
+ this._activeDomain = "astronomy";
4193
4217
  this._activeBaseLayer = "hips";
4194
4218
  return hips;
4195
4219
  }
@@ -4214,8 +4238,13 @@ class AstroSphere {
4214
4238
  }
4215
4239
  removeAllHiPS() {
4216
4240
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4217
- for (const hips of this._activeHiPSLayers) {
4218
- tileBuffer.removeHiPS(hips);
4241
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4242
+ tileBuffer.removeHiPS(this._activeHiPS);
4243
+ }
4244
+ else {
4245
+ for (const hips of this._activeHiPSLayers) {
4246
+ tileBuffer.removeHiPS(hips);
4247
+ }
4219
4248
  }
4220
4249
  this._activeHiPSLayers = [];
4221
4250
  this._activeHiPS = null;
@@ -4228,39 +4257,169 @@ class AstroSphere {
4228
4257
  this._activeBaseLayer = "xyz";
4229
4258
  }
4230
4259
  activateXYZ2(config) {
4260
+ this._activeDomain = "earth";
4231
4261
  this._activeXYZ2 = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4232
4262
  this._activeBaseLayer = "xyz";
4233
4263
  }
4234
4264
  activateMeshHiPS(descriptor) {
4265
+ this._activeDomain = "mesh";
4235
4266
  this._activeMeshHiPS = new MeshHiPS_js_1.MeshHiPS(descriptor.meshRadius, [0.0, 0.0, 0.0], 0, 0, descriptor, this._webgl, this._healpixGrid);
4236
4267
  this._activeBaseLayer = "meships";
4237
4268
  }
4269
+ setMeshHiPSColor(color) {
4270
+ this._activeMeshHiPS?.setColor(color);
4271
+ }
4272
+ setMeshHiPSWireframe(wireframe) {
4273
+ this._activeMeshHiPS?.setWireframe(wireframe);
4274
+ }
4238
4275
  activateWMTS(config) {
4276
+ this._activeDomain = "earth";
4239
4277
  const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4240
4278
  const xyzConfig = adapter.toXYZLayerConfig();
4241
4279
  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);
4242
4280
  this._activeBaseLayer = "xyz";
4243
4281
  }
4244
- // Catalogue section
4282
+ addXYZRasterOverlay(config) {
4283
+ const map = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4284
+ return this.addEarthRasterOverlay(config.name, "xyz", map);
4285
+ }
4286
+ addWMTSRasterOverlay(config) {
4287
+ const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4288
+ const xyzConfig = adapter.toXYZLayerConfig();
4289
+ const name = config.layer ? `WMTS ${config.layer}` : "WMTS Earth Raster Overlay";
4290
+ 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);
4291
+ return this.addEarthRasterOverlay(name, "wmts", map);
4292
+ }
4293
+ getEarthRasterOverlays() {
4294
+ return this._earthRasterOverlays.map(({ map: _map, ...overlay }) => ({ ...overlay }));
4295
+ }
4296
+ setEarthRasterOverlayOpacity(id, opacity) {
4297
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4298
+ if (!overlay)
4299
+ return;
4300
+ overlay.opacity = Math.min(1, Math.max(0, opacity));
4301
+ overlay.map.setOpacity(overlay.opacity);
4302
+ }
4303
+ setEarthRasterOverlayVisible(id, visible) {
4304
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4305
+ if (overlay)
4306
+ overlay.visible = visible;
4307
+ }
4308
+ removeEarthRasterOverlay(id) {
4309
+ this._earthRasterOverlays = this._earthRasterOverlays.filter((entry) => entry.id !== id);
4310
+ }
4311
+ removeAllEarthRasterOverlays() {
4312
+ this._earthRasterOverlays = [];
4313
+ }
4314
+ addEarthRasterOverlay(name, sourceType, map) {
4315
+ const overlay = {
4316
+ id: `earth-raster-${++this._earthRasterOverlaySequence}`,
4317
+ name,
4318
+ sourceType,
4319
+ visible: true,
4320
+ opacity: 0.65,
4321
+ map,
4322
+ };
4323
+ map.setOpacity(overlay.opacity);
4324
+ this._earthRasterOverlays.push(overlay);
4325
+ const { map: _map, ...info } = overlay;
4326
+ return { ...info };
4327
+ }
4328
+ setActiveDomain(domain) {
4329
+ if (domain !== this._activeDomain) {
4330
+ this.clearGridLabelsForDomain(this._activeDomain);
4331
+ this.domainCameraStates[this._activeDomain] = this._camera.getViewState();
4332
+ const targetCameraState = this.domainCameraStates[domain];
4333
+ if (targetCameraState) {
4334
+ this._camera.restoreViewState(targetCameraState);
4335
+ }
4336
+ this.inertiaX = 0;
4337
+ this.inertiaY = 0;
4338
+ this.zoomInertia = 0;
4339
+ this._cameraStatusChanged = true;
4340
+ }
4341
+ this._activeDomain = domain;
4342
+ if (domain === "astronomy") {
4343
+ this._activeBaseLayer = this._activeHiPS ? "hips" : null;
4344
+ }
4345
+ else if (domain === "earth") {
4346
+ this._activeBaseLayer = this._activeXYZ2 ? "xyz" : null;
4347
+ }
4348
+ else {
4349
+ this._activeBaseLayer = this._activeMeshHiPS ? "meships" : null;
4350
+ }
4351
+ // A hover from the previous vertical must not leak into the new one.
4352
+ this.lastHoveredSource = null;
4353
+ this.lastHoveredCatalogue = null;
4354
+ }
4355
+ clearGridLabelsForDomain(domain) {
4356
+ if (domain === "astronomy") {
4357
+ this._healpixGrid?.clearLabels();
4358
+ this._equatorialGrid?.clearLabels();
4359
+ return;
4360
+ }
4361
+ if (domain === "earth") {
4362
+ this._activeXYZ2?.clearLonLatGridLabels?.();
4363
+ }
4364
+ }
4365
+ get activeDomain() {
4366
+ return this._activeDomain;
4367
+ }
4368
+ getActivePointSets() {
4369
+ if (this._activeDomain === "astronomy")
4370
+ return this.astronomyCatalogues;
4371
+ if (this._activeDomain === "earth")
4372
+ return this.earthPointSets;
4373
+ return [];
4374
+ }
4375
+ getActiveFootprintSets() {
4376
+ if (this._activeDomain === "astronomy")
4377
+ return this.astronomyFootprintSets;
4378
+ if (this._activeDomain === "earth")
4379
+ return this.earthFootprintSets;
4380
+ return [];
4381
+ }
4382
+ // Astronomy catalogue ownership
4245
4383
  async showCatalogue(cat) {
4246
- // console.log(cat)
4247
- if (cat)
4248
- this.activeCatalogues.push(cat);
4384
+ if (cat && !this.astronomyCatalogues.includes(cat)) {
4385
+ this.astronomyCatalogues.push(cat);
4386
+ }
4249
4387
  return cat;
4250
4388
  }
4251
4389
  deleteCatalogue(catalogue) {
4252
- this.activeCatalogues = this.activeCatalogues.filter((c) => c !== catalogue);
4390
+ this.astronomyCatalogues = this.astronomyCatalogues.filter((c) => c !== catalogue);
4253
4391
  }
4254
- // End Catalogue section
4255
- // Footprint section
4392
+ // Earth point-set ownership. TerraPointSetGL still extends CatalogueGL in
4393
+ // 3.12, but it no longer shares the Astronomy lifecycle collection.
4394
+ async showTerraPointSet(pointSet) {
4395
+ if (pointSet && !this.earthPointSets.includes(pointSet)) {
4396
+ this.earthPointSets.push(pointSet);
4397
+ }
4398
+ return pointSet;
4399
+ }
4400
+ deleteTerraPointSet(pointSet) {
4401
+ this.earthPointSets = this.earthPointSets.filter((set) => set !== pointSet);
4402
+ }
4403
+ // Astronomy footprint ownership
4256
4404
  async showFootprintSet(fset) {
4257
- // console.log(fset)
4258
- if (fset)
4259
- this.activeFootprintSets.push(fset);
4405
+ if (fset && !this.astronomyFootprintSets.includes(fset)) {
4406
+ this.astronomyFootprintSets.push(fset);
4407
+ }
4260
4408
  return fset;
4261
4409
  }
4262
4410
  deleteFootprintSet(footprintSet) {
4263
- this.activeFootprintSets = this.activeFootprintSets.filter((fst) => fst !== footprintSet);
4411
+ this.astronomyFootprintSets = this.astronomyFootprintSets.filter((fst) => fst !== footprintSet);
4412
+ }
4413
+ // Earth footprint ownership. TerraFootprintSetGL still reuses the common
4414
+ // FootprintSetGL rendering implementation, but has an independent lifecycle.
4415
+ async showTerraFootprintSet(footprintSet) {
4416
+ if (footprintSet && !this.earthFootprintSets.includes(footprintSet)) {
4417
+ this.earthFootprintSets.push(footprintSet);
4418
+ }
4419
+ return footprintSet;
4420
+ }
4421
+ deleteTerraFootprintSet(footprintSet) {
4422
+ this.earthFootprintSets = this.earthFootprintSets.filter((fst) => fst !== footprintSet);
4264
4423
  }
4265
4424
  async showPolylineSet(polylineSet) {
4266
4425
  if (polylineSet)
@@ -4290,8 +4449,8 @@ class AstroSphere {
4290
4449
  satelliteObject.dispose();
4291
4450
  }
4292
4451
  getHoveredFootprints() {
4293
- let footprintsHovered = [];
4294
- this.activeFootprintSets.forEach((fset) => {
4452
+ const footprintsHovered = [];
4453
+ this.getActiveFootprintSets().forEach((fset) => {
4295
4454
  footprintsHovered.push(fset.hoveredFootprints);
4296
4455
  });
4297
4456
  return footprintsHovered;
@@ -4343,25 +4502,128 @@ class AstroSphere {
4343
4502
  throw new Error(`healpixGrid is ${this.healpixGrid}`);
4344
4503
  return FoVUtils_js_1.FoVUtils.getFoVPolygon(this._camera, this.canvas, this._healpixGrid, this._healpixGrid, this._webgl, this._perspectiveMatrixManager.pMatrix);
4345
4504
  }
4346
- changeFoV(deg) {
4347
- const distance = this._healpixGrid.getFoV().computeDistanceFromAngle(deg);
4348
- this._camera.translate(distance);
4505
+ validateFoV(deg) {
4506
+ if (!Number.isFinite(deg) || deg <= 0 || deg >= 180) {
4507
+ throw new RangeError(`FoV must be > 0 and < 180 degrees. Received ${deg}.`);
4508
+ }
4509
+ if (Global_js_1.default.insideSphere) {
4510
+ throw new Error("FoV navigation is currently supported only outside the sphere.");
4511
+ }
4512
+ }
4513
+ /**
4514
+ * Resolve the radial camera distance that produces the requested minimum FoV.
4515
+ *
4516
+ * The search temporarily samples radial distances, then restores the original
4517
+ * camera distance before returning. Callers can therefore either apply the
4518
+ * result immediately or animate toward it.
4519
+ */
4520
+ findRadialDistanceForFoV(deg) {
4521
+ this.validateFoV(deg);
4522
+ const originalDistance = this._camera.getRadialDistance();
4523
+ const minDistance = 1.000001;
4524
+ const maxDistance = 4.0;
4525
+ const tolerance = Math.max(1e-5, deg * 0.001);
4526
+ const maxIterations = 40;
4527
+ let low = minDistance;
4528
+ let high = maxDistance;
4529
+ let bestDistance = originalDistance;
4530
+ let bestError = Number.POSITIVE_INFINITY;
4531
+ for (let iteration = 0; iteration < maxIterations; iteration += 1) {
4532
+ const candidateDistance = (low + high) / 2;
4533
+ this._camera.setRadialDistance(candidateDistance);
4534
+ 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);
4535
+ const candidateFoV = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
4536
+ const measuredFoV = candidateFoV.minFoV;
4537
+ const error = Math.abs(measuredFoV - deg);
4538
+ if (error < bestError) {
4539
+ bestError = error;
4540
+ bestDistance = candidateDistance;
4541
+ }
4542
+ if (error <= tolerance) {
4543
+ break;
4544
+ }
4545
+ if (measuredFoV >= deg || measuredFoV >= 179.999) {
4546
+ high = candidateDistance;
4547
+ }
4548
+ else {
4549
+ low = candidateDistance;
4550
+ }
4551
+ }
4552
+ this._camera.setRadialDistance(originalDistance);
4553
+ 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);
4554
+ this.fov = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
4555
+ this._camera.refreshFoV(this.fov.minFoV);
4556
+ return bestDistance;
4557
+ }
4558
+ finishFoVAnimation() {
4559
+ const resolve = this.fovAnimationResolve;
4560
+ this.fovAnimationResolve = null;
4561
+ resolve?.();
4562
+ }
4563
+ cancelFoVAnimation() {
4564
+ this._camera.cancelRadialAnimation();
4565
+ this.finishFoVAnimation();
4566
+ }
4567
+ /** Set the minimum angular field of view immediately, in degrees. */
4568
+ setFoV(deg) {
4569
+ this.validateFoV(deg);
4570
+ this.cancelFoVAnimation();
4571
+ this._camera.cancelFlyTo();
4572
+ this.zoomInertia = 0;
4573
+ const targetDistance = this.findRadialDistanceForFoV(deg);
4574
+ this._camera.setRadialDistance(targetDistance);
4575
+ 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);
4349
4576
  this.fov = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
4350
4577
  this._camera.refreshFoV(this.fov.minFoV);
4578
+ this.lastCameraMotionAt = performance.now();
4579
+ this._cameraStatusChanged = true;
4580
+ this.emitCameraChanged("set-fov");
4581
+ }
4582
+ async flyToFoV(deg, durationMs = 1200) {
4583
+ this.validateFoV(deg);
4584
+ if (!Number.isFinite(durationMs) || durationMs < 0) {
4585
+ throw new RangeError(`FoV animation duration must be >= 0 ms. Received ${durationMs}.`);
4586
+ }
4587
+ if (durationMs === 0) {
4588
+ this.setFoV(deg);
4589
+ return;
4590
+ }
4591
+ this.cancelFoVAnimation();
4592
+ this.zoomInertia = 0;
4593
+ const targetDistance = this.findRadialDistanceForFoV(deg);
4594
+ this._camera.flyToRadialDistance(targetDistance, durationMs);
4595
+ this.lastCameraMotionAt = performance.now();
4596
+ this._cameraStatusChanged = true;
4597
+ return new Promise((resolve) => {
4598
+ this.fovAnimationResolve = resolve;
4599
+ });
4600
+ }
4601
+ async zoomIn(steps = 1, durationMs = 500) {
4602
+ this.validateZoomSteps(steps);
4603
+ const targetFoV = this.getFoV().minFoV / Math.pow(2, steps);
4604
+ return this.flyToFoV(targetFoV, durationMs);
4605
+ }
4606
+ async zoomOut(steps = 1, durationMs = 500) {
4607
+ this.validateZoomSteps(steps);
4608
+ const targetFoV = this.getFoV().minFoV * Math.pow(2, steps);
4609
+ return this.flyToFoV(targetFoV, durationMs);
4610
+ }
4611
+ validateZoomSteps(steps) {
4612
+ if (!Number.isInteger(steps) || steps < 1) {
4613
+ throw new RangeError(`Zoom steps must be a positive integer. Received ${steps}.`);
4614
+ }
4615
+ }
4616
+ /** @deprecated Use setFoV(). */
4617
+ changeFoV(deg) {
4618
+ this.setFoV(deg);
4351
4619
  }
4620
+ /** @deprecated Use setFoV(). */
4352
4621
  changeFoV2(deg) {
4353
- const newCameraPos = this._healpixGrid
4354
- .getFoV()
4355
- .computeCameraPositionForFoV(deg);
4356
- this._camera.setCameraPosition(newCameraPos);
4622
+ this.setFoV(deg);
4357
4623
  }
4624
+ /** @deprecated Use setFoV(). */
4358
4625
  changeFoV3(deg) {
4359
- const newPos = this._healpixGrid
4360
- .getFoV()
4361
- .computeCameraPositionForAngularDiameter(deg);
4362
- this._camera.setCameraPosition(newPos);
4363
- // Recompute projection after moving the camera
4364
- this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, Config_js_1.bootSetup.camera_fov_deg, Config_js_1.bootSetup.camera_near_plane, false);
4626
+ this.setFoV(deg);
4365
4627
  }
4366
4628
  getInsideSphere() {
4367
4629
  return Global_js_1.default.insideSphere;
@@ -4371,6 +4633,7 @@ class AstroSphere {
4371
4633
  this.inertiaX = 0;
4372
4634
  this.inertiaY = 0;
4373
4635
  this.zoomInertia = 0;
4636
+ this.cancelFoVAnimation();
4374
4637
  Global_js_1.default.insideSphere = !Global_js_1.default.insideSphere;
4375
4638
  // console.log(global.insideSphere)
4376
4639
  this._camera.toggleInsideSphere();
@@ -4523,8 +4786,6 @@ class AstroSphere {
4523
4786
  return;
4524
4787
  if (!this._webgl)
4525
4788
  return;
4526
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
4527
- return;
4528
4789
  if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
4529
4790
  return;
4530
4791
  if (this._healpixGrid.fovObj === undefined)
@@ -4548,6 +4809,14 @@ class AstroSphere {
4548
4809
  this.updateCentralPoint();
4549
4810
  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);
4550
4811
  }
4812
+ const fovFlying = this._camera.updateRadialAnimation(now);
4813
+ const fovAnimationFinished = fovFlying && !this._camera.isRadialAnimationActive();
4814
+ if (fovFlying) {
4815
+ this.zoomInertia = 0;
4816
+ this.lastCameraMotionAt = now;
4817
+ this._cameraStatusChanged = true;
4818
+ 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);
4819
+ }
4551
4820
  // Zoom inertia
4552
4821
  if (this.zoomInertia !== 0) {
4553
4822
  if (Math.abs(this.zoomInertia) > 0.0001) {
@@ -4596,6 +4865,9 @@ class AstroSphere {
4596
4865
  this._camera.refreshFoV(this.fov.minFoV);
4597
4866
  this.prevFov = this.fov.minFoV;
4598
4867
  }
4868
+ if (fovAnimationFinished) {
4869
+ this.finishFoVAnimation();
4870
+ }
4599
4871
  // Se la camera è ruotata (anche solo per inerzia), aggiorna punto centrale + emetti cameraChanged
4600
4872
  if (cameraRotated) {
4601
4873
  // Ricalcola il punto centrale
@@ -4636,8 +4908,14 @@ class AstroSphere {
4636
4908
  this._webgl.enable(this._webgl.CULL_FACE);
4637
4909
  this._webgl.cullFace(Global_js_1.default.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
4638
4910
  this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
4639
- if (this._activeBaseLayer === "hips" && this._activeHiPSLayers.length > 0) {
4640
- const maxHiPSOrder = Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder));
4911
+ if (this._activeBaseLayer === "hips" && this._activeHiPS) {
4912
+ // During directional flyTo navigation, keep visibility current but render
4913
+ // only HiPS tiles that are already available. Final-view refinement
4914
+ // resumes automatically on the first frame after the flight.
4915
+ this._healpixGrid.visibleTilesManager.tileBuffer.setTileAcquisitionEnabled(!cameraFlying);
4916
+ const maxHiPSOrder = this._activeHiPSLayers.length > 0
4917
+ ? Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder))
4918
+ : this._activeHiPS.maxOrder;
4641
4919
  const visibleOrder = Math.min(this._healpixGrid.visibleorder, maxHiPSOrder);
4642
4920
  this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
4643
4921
  }
@@ -4652,6 +4930,7 @@ class AstroSphere {
4652
4930
  Math.abs(this.zoomInertia) > 0.0001 ||
4653
4931
  Math.abs(this.inertiaX) > 0.02 ||
4654
4932
  Math.abs(this.inertiaY) > 0.02 ||
4933
+ this._camera.isRadialAnimationActive() ||
4655
4934
  nowForGrid - this.lastCameraMotionAt < 220;
4656
4935
  const skyEntityDrawInput = {
4657
4936
  fovDeg: stableFovDeg,
@@ -4672,12 +4951,19 @@ class AstroSphere {
4672
4951
  }
4673
4952
  if (this._activeBaseLayer === "xyz") {
4674
4953
  this._activeXYZ2?.draw(skyEntityDrawInput);
4954
+ for (const overlay of this._earthRasterOverlays) {
4955
+ if (overlay.visible) {
4956
+ overlay.map.draw(skyEntityDrawInput, true);
4957
+ }
4958
+ }
4675
4959
  }
4676
4960
  if (this._activeBaseLayer === "meships") {
4677
4961
  this._activeMeshHiPS?.draw(skyEntityDrawInput);
4678
4962
  }
4679
- this._healpixGrid.draw(skyEntityDrawInput);
4680
- this._equatorialGrid.draw(skyEntityDrawInput);
4963
+ if (this._activeDomain === "astronomy") {
4964
+ this._healpixGrid.draw(skyEntityDrawInput);
4965
+ this._equatorialGrid.draw(skyEntityDrawInput);
4966
+ }
4681
4967
  this._webgl.enable(this._webgl.DEPTH_TEST);
4682
4968
  this._webgl.disable(this._webgl.CULL_FACE);
4683
4969
  if (this.startup) {
@@ -4696,52 +4982,53 @@ class AstroSphere {
4696
4982
  decDMS,
4697
4983
  });
4698
4984
  }
4699
- this.activeCatalogues.forEach((cat) => {
4700
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4701
- this._activeXYZ2?.getModelMatrix() ??
4702
- this._activeMeshHiPS?.getModelMatrix();
4985
+ this.getActivePointSets().forEach((cat) => {
4986
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4703
4987
  if (activeModelMatrix) {
4704
4988
  cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4705
4989
  }
4706
4990
  });
4707
4991
  this.emitHoveredSourceIfChanged();
4708
- this.activeFootprintSets.forEach((fst) => {
4709
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4710
- this._activeXYZ2?.getModelMatrix() ??
4711
- this._activeMeshHiPS?.getModelMatrix();
4992
+ this.getActiveFootprintSets().forEach((fst) => {
4993
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4712
4994
  if (activeModelMatrix) {
4713
4995
  fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4714
4996
  }
4715
4997
  });
4716
- this.activePolylineSets.forEach((polylineSet) => {
4717
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4718
- this._activeXYZ2?.getModelMatrix() ??
4719
- this._activeMeshHiPS?.getModelMatrix();
4720
- if (activeModelMatrix) {
4721
- polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4722
- }
4723
- });
4724
- this.activeSensorCones.forEach((sensorCone) => {
4725
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4726
- this._activeXYZ2?.getModelMatrix() ??
4727
- this._activeMeshHiPS?.getModelMatrix();
4728
- if (activeModelMatrix) {
4729
- sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4730
- }
4731
- });
4732
- this.activeSatelliteObjects.forEach((satelliteObject) => {
4733
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4734
- this._activeXYZ2?.getModelMatrix() ??
4735
- this._activeMeshHiPS?.getModelMatrix();
4736
- if (activeModelMatrix) {
4737
- satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4738
- }
4739
- });
4998
+ if (this._activeDomain === "earth") {
4999
+ this.activePolylineSets.forEach((polylineSet) => {
5000
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
5001
+ if (activeModelMatrix) {
5002
+ polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
5003
+ }
5004
+ });
5005
+ this.activeSensorCones.forEach((sensorCone) => {
5006
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
5007
+ if (activeModelMatrix) {
5008
+ sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
5009
+ }
5010
+ });
5011
+ this.activeSatelliteObjects.forEach((satelliteObject) => {
5012
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
5013
+ if (activeModelMatrix) {
5014
+ satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
5015
+ }
5016
+ });
5017
+ }
5018
+ }
5019
+ getActiveDomainModelMatrix() {
5020
+ if (this._activeDomain === "astronomy") {
5021
+ return (this._activeHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
5022
+ }
5023
+ if (this._activeDomain === "earth") {
5024
+ return (this._activeXYZ2?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
5025
+ }
5026
+ return (this._activeMeshHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4740
5027
  }
4741
5028
  emitHoveredSourceIfChanged() {
4742
5029
  let nextHoveredSource = null;
4743
5030
  let nextHoveredCatalogue = null;
4744
- for (const cat of this.activeCatalogues) {
5031
+ for (const cat of this.getActivePointSets()) {
4745
5032
  const hovered = cat.getPrimaryHoveredSource();
4746
5033
  if (!hovered)
4747
5034
  continue;
@@ -4870,25 +5157,25 @@ class AstroViewer {
4870
5157
  return new TerraPointSetGL_js_1.TerraPointSetGL(pointSetName, pointSetDescription, providerUrl, metadataManager, this.webgl, this.astroSphere.healpixGrid.visibleTilesManager);
4871
5158
  }
4872
5159
  showTerraPointSet(pointSet) {
4873
- this.astroSphere.showCatalogue(pointSet);
5160
+ this.astroSphere.showTerraPointSet(pointSet);
4874
5161
  }
4875
5162
  hideTerraPointSet(pointSet, isVisible) {
4876
5163
  pointSet.setIsVisible(isVisible);
4877
5164
  }
4878
5165
  deleteTerraPointSet(pointSet) {
4879
- this.astroSphere.deleteCatalogue(pointSet);
5166
+ this.astroSphere.deleteTerraPointSet(pointSet);
4880
5167
  }
4881
5168
  createTerraFootprintSet(footprintSetName, footprintSetDescription, providerUrl, metadataManager) {
4882
5169
  return new TerraFootprintSetGL_js_1.TerraFootprintSetGL(footprintSetName, footprintSetDescription, providerUrl, metadataManager, this.webgl);
4883
5170
  }
4884
5171
  showTerraFootprintSet(footprintSet) {
4885
- this.astroSphere.showFootprintSet(footprintSet);
5172
+ this.astroSphere.showTerraFootprintSet(footprintSet);
4886
5173
  }
4887
5174
  hideTerraFootprintSet(footprintSet, isVisible) {
4888
5175
  footprintSet.setIsVisible(isVisible);
4889
5176
  }
4890
5177
  deleteTerraFootprintSet(footprintSet) {
4891
- this.astroSphere.deleteFootprintSet(footprintSet);
5178
+ this.astroSphere.deleteTerraFootprintSet(footprintSet);
4892
5179
  }
4893
5180
  createTerraPolylineSet(polylineSetName, polylineSetDescription, providerUrl, metadataManager) {
4894
5181
  return new TerraPolylineSetGL_js_1.TerraPolylineSetGL(polylineSetName, polylineSetDescription, providerUrl, metadataManager, this.webgl);
@@ -4941,6 +5228,12 @@ class AstroViewer {
4941
5228
  getDefaultHiPSURL() {
4942
5229
  return Config_js_1.bootSetup.defaultHipsUrl;
4943
5230
  }
5231
+ setActiveDomain(domain) {
5232
+ this.astroSphere.setActiveDomain(domain);
5233
+ }
5234
+ getActiveDomain() {
5235
+ return this.astroSphere.activeDomain;
5236
+ }
4944
5237
  activateHiPS(hipsDescriptor) {
4945
5238
  this.astroSphere.activateHiPS(hipsDescriptor);
4946
5239
  }
@@ -4954,9 +5247,37 @@ class AstroViewer {
4954
5247
  activateWMTS(config) {
4955
5248
  this.astroSphere.activateWMTS(config);
4956
5249
  }
5250
+ addXYZRasterOverlay(config) {
5251
+ 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);
5252
+ return this.astroSphere.addXYZRasterOverlay(descriptor);
5253
+ }
5254
+ addWMTSRasterOverlay(config) {
5255
+ return this.astroSphere.addWMTSRasterOverlay(config);
5256
+ }
5257
+ getEarthRasterOverlays() {
5258
+ return this.astroSphere.getEarthRasterOverlays();
5259
+ }
5260
+ setEarthRasterOverlayOpacity(id, opacity) {
5261
+ this.astroSphere.setEarthRasterOverlayOpacity(id, opacity);
5262
+ }
5263
+ setEarthRasterOverlayVisible(id, visible) {
5264
+ this.astroSphere.setEarthRasterOverlayVisible(id, visible);
5265
+ }
5266
+ removeEarthRasterOverlay(id) {
5267
+ this.astroSphere.removeEarthRasterOverlay(id);
5268
+ }
5269
+ removeAllEarthRasterOverlays() {
5270
+ this.astroSphere.removeAllEarthRasterOverlays();
5271
+ }
4957
5272
  activateMeshHiPS(config) {
4958
5273
  this.astroSphere.activateMeshHiPS(new MeshHiPSDescriptor_js_1.MeshHiPSDescriptor(config));
4959
5274
  }
5275
+ setMeshHiPSColor(color) {
5276
+ this.astroSphere.setMeshHiPSColor(color);
5277
+ }
5278
+ setMeshHiPSWireframe(wireframe) {
5279
+ this.astroSphere.setMeshHiPSWireframe(wireframe);
5280
+ }
4960
5281
  setXYZMaxConcurrentRequests(value) {
4961
5282
  XYZTileRequestScheduler_js_1.xyzTileRequestScheduler.setMaxConcurrent(value);
4962
5283
  }
@@ -5145,14 +5466,29 @@ class AstroViewer {
5145
5466
  getFoVPolygon() {
5146
5467
  return this.astroSphere.getFoVPolygon();
5147
5468
  }
5469
+ setFoV(deg) {
5470
+ this.astroSphere.setFoV(deg);
5471
+ }
5472
+ flyToFoV(deg, durationMs = 1200) {
5473
+ return this.astroSphere.flyToFoV(deg, durationMs);
5474
+ }
5475
+ zoomIn(steps = 1, durationMs = 500) {
5476
+ return this.astroSphere.zoomIn(steps, durationMs);
5477
+ }
5478
+ zoomOut(steps = 1, durationMs = 500) {
5479
+ return this.astroSphere.zoomOut(steps, durationMs);
5480
+ }
5481
+ /** @deprecated Use setFoV(). */
5148
5482
  changeFoV(deg) {
5149
- (this, this.astroSphere.changeFoV(deg));
5483
+ this.setFoV(deg);
5150
5484
  }
5485
+ /** @deprecated Use setFoV(). */
5151
5486
  changeFoV2(deg) {
5152
- (this, this.astroSphere.changeFoV2(deg));
5487
+ this.setFoV(deg);
5153
5488
  }
5489
+ /** @deprecated Use setFoV(). */
5154
5490
  changeFoV3(deg) {
5155
- (this, this.astroSphere.changeFoV3(deg));
5491
+ this.setFoV(deg);
5156
5492
  }
5157
5493
  getInsideSphere() {
5158
5494
  return this.astroSphere.getInsideSphere();
@@ -5405,6 +5741,7 @@ class Camera {
5405
5741
  lockRotY = false;
5406
5742
  lockRotZ = false;
5407
5743
  flyToAnimation = null;
5744
+ radialAnimation = null;
5408
5745
  constructor(in_position, in_sphere) {
5409
5746
  this.init(in_position, in_sphere);
5410
5747
  }
@@ -5539,6 +5876,42 @@ class Camera {
5539
5876
  cancelFlyTo() {
5540
5877
  this.flyToAnimation = null;
5541
5878
  }
5879
+ flyToRadialDistance(distance, durationMs = 1200) {
5880
+ if (!Number.isFinite(distance) || distance <= 1) {
5881
+ throw new RangeError(`Camera radial distance must be > 1. Received ${distance}.`);
5882
+ }
5883
+ this.radialAnimation = {
5884
+ startDistance: this.cam_pos[2],
5885
+ targetDistance: distance,
5886
+ startTime: performance.now(),
5887
+ durationMs: Math.max(1, durationMs),
5888
+ };
5889
+ }
5890
+ updateRadialAnimation(now) {
5891
+ const animation = this.radialAnimation;
5892
+ if (!animation) {
5893
+ return false;
5894
+ }
5895
+ const elapsed = now - animation.startTime;
5896
+ const progress = Math.min(1, elapsed / animation.durationMs);
5897
+ const easedProgress = progress < 0.5
5898
+ ? 4 * progress * progress * progress
5899
+ : 1 - Math.pow(-2 * progress + 2, 3) / 2;
5900
+ const distance = animation.startDistance +
5901
+ (animation.targetDistance - animation.startDistance) * easedProgress;
5902
+ this.setRadialDistance(distance);
5903
+ if (progress >= 1) {
5904
+ this.setRadialDistance(animation.targetDistance);
5905
+ this.radialAnimation = null;
5906
+ }
5907
+ return true;
5908
+ }
5909
+ isRadialAnimationActive() {
5910
+ return this.radialAnimation !== null;
5911
+ }
5912
+ cancelRadialAnimation() {
5913
+ this.radialAnimation = null;
5914
+ }
5542
5915
  toggleInsideSphere() {
5543
5916
  // if (inside !== global.insideSphere) {
5544
5917
  // global.insideSphere = inside;
@@ -5760,6 +6133,22 @@ class Camera {
5760
6133
  }
5761
6134
  return [inv[12], inv[13], inv[14]];
5762
6135
  }
6136
+ getViewState() {
6137
+ return {
6138
+ position: [this.cam_pos[0], this.cam_pos[1], this.cam_pos[2]],
6139
+ viewMatrix: gl_matrix_1.mat4.clone(this.vMatrix),
6140
+ translationMatrix: gl_matrix_1.mat4.clone(this.T),
6141
+ rotationMatrix: gl_matrix_1.mat4.clone(this.R),
6142
+ };
6143
+ }
6144
+ restoreViewState(state) {
6145
+ this.cancelFlyTo();
6146
+ this.cam_pos = gl_matrix_1.vec3.fromValues(...state.position);
6147
+ this.vMatrix = gl_matrix_1.mat4.clone(state.viewMatrix);
6148
+ this.T = gl_matrix_1.mat4.clone(state.translationMatrix);
6149
+ this.R = gl_matrix_1.mat4.clone(state.rotationMatrix);
6150
+ this.move = gl_matrix_1.vec3.create();
6151
+ }
5763
6152
  setCameraMatrix(viewMatrix) {
5764
6153
  this.vMatrix = viewMatrix;
5765
6154
  }
@@ -5772,6 +6161,24 @@ class Camera {
5772
6161
  // Recompute view: vMatrix = inv(T) * inv(R)
5773
6162
  this.refreshViewMatrix();
5774
6163
  }
6164
+ /**
6165
+ * Set the radial camera distance while preserving the current pointing.
6166
+ *
6167
+ * Outside-sphere navigation stores the camera radius in cam_pos[2];
6168
+ * changing only that component is the same radial motion used by zoom(),
6169
+ * without changing the current rotation matrix.
6170
+ */
6171
+ setRadialDistance(distance) {
6172
+ if (!Number.isFinite(distance) || distance <= 1) {
6173
+ throw new RangeError(`Camera radial distance must be > 1. Received ${distance}.`);
6174
+ }
6175
+ this.cam_pos[2] = distance;
6176
+ gl_matrix_1.mat4.translate(this.T, gl_matrix_1.mat4.create(), this.cam_pos);
6177
+ this.refreshViewMatrix();
6178
+ }
6179
+ getRadialDistance() {
6180
+ return this.cam_pos[2];
6181
+ }
5775
6182
  getCameraAngle() {
5776
6183
  const [x, y, z] = this.getCameraPosition();
5777
6184
  const posVec = gl_matrix_1.vec3.fromValues(x, y, z);
@@ -5977,7 +6384,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5977
6384
  return (mod && mod.__esModule) ? mod : { "default": mod };
5978
6385
  };
5979
6386
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5980
- 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;
6387
+ 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;
5981
6388
  var AstroViewer_js_1 = __webpack_require__(772);
5982
6389
  Object.defineProperty(exports, "AstroViewer", ({ enumerable: true, get: function () { return AstroViewer_js_1.AstroViewer; } }));
5983
6390
  var HiPSDescriptor_js_1 = __webpack_require__(5087);
@@ -6018,6 +6425,9 @@ Object.defineProperty(exports, "SphericalOpts", ({ enumerable: true, get: functi
6018
6425
  Object.defineProperty(exports, "AstroOpts", ({ enumerable: true, get: function () { return Point_js_1.AstroOpts; } }));
6019
6426
  Object.defineProperty(exports, "PointInitOpts", ({ enumerable: true, get: function () { return Point_js_1.PointInitOpts; } }));
6020
6427
  Object.defineProperty(exports, "CartesianOpts", ({ enumerable: true, get: function () { return Point_js_1.CartesianOpts; } }));
6428
+ var Utils_js_1 = __webpack_require__(7930);
6429
+ Object.defineProperty(exports, "raHMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.raHMSToDeg; } }));
6430
+ Object.defineProperty(exports, "decDMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.decDMSToDeg; } }));
6021
6431
  var FoVUtils_js_1 = __webpack_require__(8083);
6022
6432
  Object.defineProperty(exports, "FoVUtils", ({ enumerable: true, get: function () { return FoVUtils_js_1.FoVUtils; } }));
6023
6433
  var CoordsType_js_1 = __webpack_require__(8145);
@@ -8025,7 +8435,7 @@ class Source {
8025
8435
  this._point = in_point;
8026
8436
  this._details = in_details;
8027
8437
  this._shapesize = 16.0;
8028
- this._brightnessFactor = -99;
8438
+ this._brightnessFactor = 3.0;
8029
8439
  this.computeHealpixPixel();
8030
8440
  }
8031
8441
  getDetailByindex(index) {
@@ -8314,12 +8724,13 @@ const Utils_js_1 = __webpack_require__(7930);
8314
8724
  const CatalogueShaderProgram_js_1 = __webpack_require__(3559);
8315
8725
  const MetadataManager_js_1 = __webpack_require__(5403);
8316
8726
  const gl_matrix_1 = __webpack_require__(3890);
8727
+ const healpix_1 = __webpack_require__(4851);
8317
8728
  const Global_js_1 = __importDefault(__webpack_require__(4382));
8318
8729
  class CatalogueGL {
8319
8730
  _kind = "CatalogueGL";
8320
8731
  static ELEM_SIZE = 6;
8321
8732
  static BYTES_X_ELEM = new Float32Array().BYTES_PER_ELEMENT;
8322
- static STANDARD_SHAPE_SIZE = 10.0;
8733
+ static STANDARD_SHAPE_SIZE = 16.0;
8323
8734
  static STANDARD_SHAPE_HUE = 3.0;
8324
8735
  _ready;
8325
8736
  _name;
@@ -8562,7 +8973,7 @@ class CatalogueGL {
8562
8973
  getColumnIndex(columnsmeta, columnName) {
8563
8974
  if (!columnName)
8564
8975
  return -1;
8565
- return columnsmeta.find((column) => column.name === columnName)?.index ?? -1;
8976
+ return (columnsmeta.find((column) => column.name === columnName)?.index ?? -1);
8566
8977
  }
8567
8978
  readCell(row, columnsmeta, columnName) {
8568
8979
  const idx = this.getColumnIndex(columnsmeta, columnName);
@@ -8575,7 +8986,9 @@ class CatalogueGL {
8575
8986
  const src = String(rawSrc ?? "").trim();
8576
8987
  if (!src)
8577
8988
  return undefined;
8578
- const rawKind = String(this.readCell(row, columnsmeta, mediaColumns.type) ?? "sprite").trim().toLowerCase();
8989
+ const rawKind = String(this.readCell(row, columnsmeta, mediaColumns.type) ?? "sprite")
8990
+ .trim()
8991
+ .toLowerCase();
8579
8992
  const kind = rawKind === "model"
8580
8993
  ? "model"
8581
8994
  : rawKind === "image"
@@ -8595,7 +9008,9 @@ class CatalogueGL {
8595
9008
  src,
8596
9009
  scale: Number.isFinite(scale) && scale > 0 ? scale : undefined,
8597
9010
  rotationDeg: Number.isFinite(rotationDeg) ? rotationDeg : undefined,
8598
- opacity: Number.isFinite(opacity) ? Math.max(0, Math.min(1, opacity)) : undefined,
9011
+ opacity: Number.isFinite(opacity)
9012
+ ? Math.max(0, Math.min(1, opacity))
9013
+ : undefined,
8599
9014
  };
8600
9015
  }
8601
9016
  clearSources() {
@@ -8886,33 +9301,46 @@ class CatalogueGL {
8886
9301
  this._webgl.bufferData(this._webgl.ARRAY_BUFFER, this.vertexCataloguePosition, this._webgl.STATIC_DRAW);
8887
9302
  this._bufferInitialised = true;
8888
9303
  }
8889
- getSelectionRadius() {
8890
- const order = this._visibleTilesManager.getVisibleOrder();
8891
- switch (order) {
8892
- case 0:
8893
- case 1:
8894
- case 2:
8895
- // return 0.005;
8896
- return 0.01;
8897
- case 3:
8898
- // return 0.001;
8899
- case 4:
8900
- // return 0.0009;
8901
- case 5:
8902
- // return 0.0005;
8903
- return 0.005;
8904
- case 6:
8905
- // return 0.0001;
8906
- case 7:
8907
- // return 0.00009;
8908
- case 8:
8909
- // return 0.00005;
8910
- return 0.001;
8911
- case 9:
8912
- return 0.0005;
8913
- default:
8914
- return 0.0001;
9304
+ static MIN_HIT_RADIUS_PX = 6;
9305
+ static HIT_PADDING_PX = 3;
9306
+ static CANDIDATE_RADIUS_PX = 16;
9307
+ getCanvas() {
9308
+ return this._webgl.canvas;
9309
+ }
9310
+ getHitRadiusPx(source) {
9311
+ const markerSize = source.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
9312
+ return Math.max(CatalogueGL.MIN_HIT_RADIUS_PX, markerSize / 2 + CatalogueGL.HIT_PADDING_PX);
9313
+ }
9314
+ getCandidateSearchRadiusRad(pMatrix) {
9315
+ const canvas = this._webgl.canvas;
9316
+ const width = Math.max(1, canvas.clientWidth);
9317
+ const height = Math.max(1, canvas.clientHeight);
9318
+ // pMatrix[0] and pMatrix[5] are the perspective scale factors for X/Y.
9319
+ // Convert a small screen-space radius to a conservative angular radius.
9320
+ const fovXRad = 2 * Math.atan(1 / Math.abs(pMatrix[0]));
9321
+ const fovYRad = 2 * Math.atan(1 / Math.abs(pMatrix[5]));
9322
+ const radPerPixel = Math.max(fovXRad / width, fovYRad / height);
9323
+ return CatalogueGL.CANDIDATE_RADIUS_PX * radPerPixel;
9324
+ }
9325
+ projectPointToScreen(point, mvp, width, height) {
9326
+ const projected = gl_matrix_1.vec4.fromValues(point.x, point.y, point.z, 1);
9327
+ gl_matrix_1.vec4.transformMat4(projected, projected, mvp);
9328
+ if (projected[3] <= 0)
9329
+ return null;
9330
+ const ndcX = projected[0] / projected[3];
9331
+ const ndcY = projected[1] / projected[3];
9332
+ return [(ndcX * 0.5 + 0.5) * width, (1 - (ndcY * 0.5 + 0.5)) * height];
9333
+ }
9334
+ clearSelection() {
9335
+ for (const idx of this.selectedIndexes) {
9336
+ const base = idx * CatalogueGL.ELEM_SIZE;
9337
+ if (base + 4 >= this.vertexCataloguePosition.length)
9338
+ continue;
9339
+ this.vertexCataloguePosition[base + 3] = 0.0;
9340
+ this.vertexCataloguePosition[base + 4] =
9341
+ this._sources[idx]?.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
8915
9342
  }
9343
+ this.selectedIndexes = [];
8916
9344
  }
8917
9345
  setSelectedIndexes(selectedIndex) {
8918
9346
  selectedIndex.forEach((idx) => {
@@ -8939,8 +9367,8 @@ class CatalogueGL {
8939
9367
  * Run click-picking and update selection with the nearest candidate in current pixel.
8940
9368
  * Returns the selected source or null if no source was hit.
8941
9369
  */
8942
- getSourcesFromPointer(in_mouseHelper) {
8943
- const pickedIndexes = this.checkClicking(in_mouseHelper);
9370
+ getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9371
+ const pickedIndexes = this.checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8944
9372
  if (!pickedIndexes.length) {
8945
9373
  return {
8946
9374
  sources: [],
@@ -8959,8 +9387,8 @@ class CatalogueGL {
8959
9387
  * Run click-picking and update selection with the nearest candidate in current pixel.
8960
9388
  * Returns the selected source or null if no source was hit.
8961
9389
  */
8962
- selectPrimarySourceFromClick(in_mouseHelper) {
8963
- const picked = this.getSourcesFromPointer(in_mouseHelper);
9390
+ selectPrimarySourceFromClick(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9391
+ const picked = this.getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8964
9392
  const clickedIndexes = picked?.pickedIndexes ?? [];
8965
9393
  // if (!clickedIndexes.length) {
8966
9394
  // this.setSelectedIndexes([]);
@@ -8996,42 +9424,65 @@ class CatalogueGL {
8996
9424
  const idx = this.hoveredIndexes[0];
8997
9425
  return this._sources[idx] ?? null;
8998
9426
  }
8999
- findNearestSourceIndex(in_mouseHelper) {
9000
- if (in_mouseHelper.x == null ||
9001
- in_mouseHelper.y == null ||
9002
- in_mouseHelper.z == null) {
9427
+ findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9428
+ if (in_mouseHelper.xyz == null)
9003
9429
  return null;
9430
+ const healpix = Global_js_1.default.getHealpix(Global_js_1.default.selectionOrder);
9431
+ const mouseVec = new healpix_1.Vec3(...in_mouseHelper.xyz);
9432
+ const mousePointing = new healpix_1.Pointing(mouseVec, false);
9433
+ const searchRadiusRad = this.getCandidateSearchRadiusRad(pMatrix);
9434
+ const rangeSet = healpix.queryDiscInclusive(mousePointing, searchRadiusRad, 4);
9435
+ const pixels = rangeSet.r;
9436
+ const candidateIndexes = new Set();
9437
+ for (const pixel of pixels) {
9438
+ const indexes = this._healpixDensityMap.get(pixel);
9439
+ if (!indexes)
9440
+ continue;
9441
+ indexes.forEach((idx) => candidateIndexes.add(idx));
9004
9442
  }
9005
- const mousePix = in_mouseHelper.computeNpix();
9006
- if (mousePix == null)
9443
+ if (!candidateIndexes.size)
9007
9444
  return null;
9008
- const candidates = this._healpixDensityMap.get(mousePix);
9009
- if (!candidates?.length)
9445
+ const canvas = this._webgl.canvas;
9446
+ const width = canvas.clientWidth;
9447
+ const height = canvas.clientHeight;
9448
+ if (!width || !height)
9449
+ return null;
9450
+ const modelView = gl_matrix_1.mat4.create();
9451
+ const mvp = gl_matrix_1.mat4.create();
9452
+ gl_matrix_1.mat4.multiply(modelView, vMatrix, in_mMatrix);
9453
+ gl_matrix_1.mat4.multiply(mvp, pMatrix, modelView);
9454
+ const mousePoint = new Point_js_1.Point({
9455
+ x: in_mouseHelper.xyz[0],
9456
+ y: in_mouseHelper.xyz[1],
9457
+ z: in_mouseHelper.xyz[2],
9458
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
9459
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
9460
+ if (!mouseScreen)
9010
9461
  return null;
9011
- const selR = this.getSelectionRadius();
9012
9462
  let nearestIndex = null;
9013
- let nearestDistance = Number.POSITIVE_INFINITY;
9014
- for (const sourceIdx of candidates) {
9463
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
9464
+ for (const sourceIdx of candidateIndexes) {
9015
9465
  const source = this._sources[sourceIdx];
9016
9466
  if (!source)
9017
9467
  continue;
9018
- const dx = source.point.x - in_mouseHelper.x;
9019
- const dy = source.point.y - in_mouseHelper.y;
9020
- const dz = source.point.z - in_mouseHelper.z;
9021
- const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
9022
- if (dist <= selR && dist < nearestDistance) {
9023
- nearestDistance = dist;
9468
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
9469
+ if (!sourceScreen)
9470
+ continue;
9471
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
9472
+ if (distancePx <= this.getHitRadiusPx(source) &&
9473
+ distancePx < nearestDistancePx) {
9474
+ nearestDistancePx = distancePx;
9024
9475
  nearestIndex = sourceIdx;
9025
9476
  }
9026
9477
  }
9027
9478
  return nearestIndex;
9028
9479
  }
9029
- checkClicking(in_mouseHelper) {
9030
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9480
+ checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9481
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
9031
9482
  return nearestIndex == null ? [] : [nearestIndex];
9032
9483
  }
9033
- checkHovering(in_mouseHelper) {
9034
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9484
+ checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9485
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
9035
9486
  return nearestIndex == null ? [] : [nearestIndex];
9036
9487
  }
9037
9488
  /**
@@ -9067,6 +9518,13 @@ class CatalogueGL {
9067
9518
  if (this._catalogueShaderProgram.locations.color) {
9068
9519
  this._webgl.uniform4f(this._catalogueShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], 1.0);
9069
9520
  }
9521
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
9522
+ if (this._catalogueShaderProgram.locations.hoverColor) {
9523
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.hoverColor, ...interactionColors.hover, 1.0);
9524
+ }
9525
+ if (this._catalogueShaderProgram.locations.selectedColor) {
9526
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.selectedColor, ...interactionColors.selected, 1.0);
9527
+ }
9070
9528
  // selected flags
9071
9529
  for (let s = 0; s < this.selectedIndexes.length; s++) {
9072
9530
  const idx = this.selectedIndexes[s];
@@ -9077,7 +9535,8 @@ class CatalogueGL {
9077
9535
  // clear old hovered
9078
9536
  for (let k = 0; k < this.hoveredIndexes.length; k++) {
9079
9537
  const base = this.hoveredIndexes[k] * CatalogueGL.ELEM_SIZE;
9080
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9538
+ if (this.selectedIndexes.includes(this.hoveredIndexes[k]))
9539
+ continue;
9081
9540
  this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
9082
9541
  this.vertexCataloguePosition[base + 4] =
9083
9542
  this._sources[this.hoveredIndexes[k]].shapeSize; // size
@@ -9091,7 +9550,7 @@ class CatalogueGL {
9091
9550
  // this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
9092
9551
  // this.vertexCataloguePosition[base + 4] = this._sources[this.hoveredIndexes[k]].shapeSize; // size
9093
9552
  // }
9094
- this.hoveredIndexes = this.checkHovering(in_mouseHelper);
9553
+ this.hoveredIndexes = this.checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
9095
9554
  // // new hovered
9096
9555
  // for (let i = 0; i < this.hoveredIndexes.length; i++) {
9097
9556
  // const idx = this.hoveredIndexes[i];
@@ -9105,7 +9564,8 @@ class CatalogueGL {
9105
9564
  for (let i = 0; i < this.hoveredIndexes.length; i++) {
9106
9565
  const idx = this.hoveredIndexes[i];
9107
9566
  const base = idx * CatalogueGL.ELEM_SIZE;
9108
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9567
+ if (this.selectedIndexes.includes(idx))
9568
+ continue;
9109
9569
  this.vertexCataloguePosition[base + 3] = 1.0; // hovered
9110
9570
  this.vertexCataloguePosition[base + 4] = this._sources[idx].shapeSize; // size
9111
9571
  }
@@ -9513,6 +9973,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9513
9973
  _latLonGrid;
9514
9974
  _colorMapIdx = 0;
9515
9975
  _colorMap = ColorMaps_js_1.ColorMaps['native'];
9976
+ _opacity = 1;
9516
9977
  constructor(radius, position, xrad, yrad, descriptor, webgl, gridLabelContainers) {
9517
9978
  super(radius, position, xrad, yrad, descriptor.name, webgl, false);
9518
9979
  this._descriptor = descriptor;
@@ -9527,6 +9988,10 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9527
9988
  const fov = 180;
9528
9989
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9529
9990
  }
9991
+ setOpacity(opacity) {
9992
+ this._opacity = Math.min(1, Math.max(0, opacity));
9993
+ this._xyzShaderProgram.setOpacity(this._opacity);
9994
+ }
9530
9995
  changeColorMap(colorMap) {
9531
9996
  this._colorMap = colorMap;
9532
9997
  switch (colorMap.name) {
@@ -9574,6 +10039,9 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9574
10039
  initShaders() {
9575
10040
  this._xyzShaderProgram.enableProgram();
9576
10041
  }
10042
+ clearLonLatGridLabels() {
10043
+ this._latLonGrid.clearLabels();
10044
+ }
9577
10045
  isLonLatGridVisible() {
9578
10046
  return this._latLonGrid.isVisible();
9579
10047
  }
@@ -9588,7 +10056,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9588
10056
  // this._zoom = this.resolveVisibleZoom(fov)
9589
10057
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9590
10058
  }
9591
- draw(input) {
10059
+ draw(input, asOverlay = false) {
9592
10060
  const vMatrix = input.camera.getCameraMatrix();
9593
10061
  if (!vMatrix)
9594
10062
  return;
@@ -9598,6 +10066,18 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9598
10066
  this.refresh(input);
9599
10067
  const mMatrix = this.getModelMatrix();
9600
10068
  this._xyzShaderProgram.setRuntimeColorMap(this._colorMap);
10069
+ this._xyzShaderProgram.setOpacity(this._opacity);
10070
+ const depthTestWasEnabled = this._webgl.isEnabled(this._webgl.DEPTH_TEST);
10071
+ const blendWasEnabled = this._webgl.isEnabled(this._webgl.BLEND);
10072
+ const blendSrcRgb = this._webgl.getParameter(this._webgl.BLEND_SRC_RGB);
10073
+ const blendDstRgb = this._webgl.getParameter(this._webgl.BLEND_DST_RGB);
10074
+ const blendSrcAlpha = this._webgl.getParameter(this._webgl.BLEND_SRC_ALPHA);
10075
+ const blendDstAlpha = this._webgl.getParameter(this._webgl.BLEND_DST_ALPHA);
10076
+ if (asOverlay) {
10077
+ this._webgl.disable(this._webgl.DEPTH_TEST);
10078
+ this._webgl.enable(this._webgl.BLEND);
10079
+ this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
10080
+ }
9601
10081
  const tileSelection = this._visibleTilesManager.computeVisibleTiles(this._zoom, this, this._webgl, input.camera, input.pMatrix);
9602
10082
  const visibleTiles = tileSelection.visibleTiles;
9603
10083
  const ancestorsMap = tileSelection.ancestorsMap;
@@ -9615,7 +10095,20 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9615
10095
  const ancestorTile = this.findBestAvailableAncestor(tile.coord);
9616
10096
  ancestorTile?.draw(tileSelection.currentZoom, [tile.coord], ancestorsMap, pMatrix, vMatrix, mMatrix, this._colorMapIdx);
9617
10097
  }
9618
- this._latLonGrid.draw(input);
10098
+ if (asOverlay) {
10099
+ if (depthTestWasEnabled)
10100
+ this._webgl.enable(this._webgl.DEPTH_TEST);
10101
+ else
10102
+ this._webgl.disable(this._webgl.DEPTH_TEST);
10103
+ this._webgl.blendFuncSeparate(blendSrcRgb, blendDstRgb, blendSrcAlpha, blendDstAlpha);
10104
+ if (blendWasEnabled)
10105
+ this._webgl.enable(this._webgl.BLEND);
10106
+ else
10107
+ this._webgl.disable(this._webgl.BLEND);
10108
+ }
10109
+ else {
10110
+ this._latLonGrid.draw(input);
10111
+ }
9619
10112
  }
9620
10113
  createTile(coord) {
9621
10114
  return new XYZAnchestorTile_js_1.XYZAnchestorTile(coord, this.resolveTileUrl(coord), this._webgl, this._xyzShaderProgram, this._meshBuilder, this._descriptor.segmentsPerSide);
@@ -10909,6 +11402,10 @@ class FootprintSetGL {
10909
11402
  }
10910
11403
  return clickedIndexes;
10911
11404
  }
11405
+ clearSelection() {
11406
+ this.selectedIndexes = [];
11407
+ this.refreshSelectedFootprints();
11408
+ }
10912
11409
  setSelectedIndexes(selectedIndex) {
10913
11410
  selectedIndex.forEach((idx) => {
10914
11411
  if (idx < 0 || idx >= this.footprintPolygons.length)
@@ -11210,9 +11707,10 @@ class FootprintSetGL {
11210
11707
  if (in_mouseHelper != null && in_mouseHelper.xyz != this.oldMouseCoords) {
11211
11708
  this.checkSelection(in_mouseHelper);
11212
11709
  }
11710
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
11213
11711
  if (this._hoveredFootprints.length > 0) {
11214
11712
  // TODO POINT_SIZE doesn't have any effect on line thickness!! it only applies to points
11215
- const rgb = (0, Utils_js_1.colorHex2RGB)("#00FF00");
11713
+ const rgb = interactionColors.hover;
11216
11714
  const alpha = 1.0;
11217
11715
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
11218
11716
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -11227,7 +11725,7 @@ class FootprintSetGL {
11227
11725
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.hoveredVertexPosition.length / 3 + this.nHoveredPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11228
11726
  }
11229
11727
  if (this._selectedFootprints.length > 0) {
11230
- const rgb = (0, Utils_js_1.colorHex2RGB)("#ECB462");
11728
+ const rgb = interactionColors.selected;
11231
11729
  const alpha = 1.0;
11232
11730
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
11233
11731
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -11240,6 +11738,11 @@ class FootprintSetGL {
11240
11738
  this._webgl.bufferData(this._webgl.ELEMENT_ARRAY_BUFFER, this.selectedElementIndexes, this._webgl.STATIC_DRAW);
11241
11739
  // this._gl.drawElements (this._gl.LINE_LOOP, this._selectedVertexPosition.length / 3 + this._nSlectedPrimitiveFlags,this._gl.UNSIGNED_SHORT, 0);
11242
11740
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11741
+ // Keep selection visually distinct from hover without relying on
11742
+ // non-portable line widths. Reuse the selected geometry as small
11743
+ // vertex markers; POINT_SIZE is supported for POINTS in WebGL2.
11744
+ this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 4.0);
11745
+ this._webgl.drawElements(this._webgl.POINTS, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11243
11746
  }
11244
11747
  this._webgl.bindBuffer(this._webgl.ARRAY_BUFFER, this.vertexCataloguePositionBuffer);
11245
11748
  this._webgl.vertexAttribPointer(this._footprintShaderProgram.locations.position, FootprintSetGL.ELEM_SIZE, this._webgl.FLOAT, false, FootprintSetGL.BYTES_X_ELEM * FootprintSetGL.ELEM_SIZE, 0);
@@ -11473,6 +11976,9 @@ class EquatorialGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11473
11976
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
11474
11977
  }
11475
11978
  }
11979
+ clearLabels() {
11980
+ this.gridText.resetDivSets();
11981
+ }
11476
11982
  isVisible() {
11477
11983
  return this.showGrid;
11478
11984
  }
@@ -12069,6 +12575,9 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12069
12575
  this.gridText.resetDivSets();
12070
12576
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
12071
12577
  }
12578
+ clearLabels() {
12579
+ this.gridText.resetDivSets();
12580
+ }
12072
12581
  get visibleorder() {
12073
12582
  return this._visibleorder;
12074
12583
  }
@@ -12303,6 +12812,9 @@ class LatLonGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12303
12812
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
12304
12813
  }
12305
12814
  }
12815
+ clearLabels() {
12816
+ this.gridText.resetDivSets();
12817
+ }
12306
12818
  draw(input) {
12307
12819
  if (!this._showGrid) {
12308
12820
  this.gridText.resetDivSets();
@@ -12684,8 +13196,10 @@ class AllSky {
12684
13196
  continue;
12685
13197
  }
12686
13198
  const childTile = this._isGalacticHips
12687
- ? this._tileBuffer.getGalTile(tileno, childrenOrder, this._hips)
12688
- : this._tileBuffer.getTile(tileno, childrenOrder, this._hips);
13199
+ ? this._tileBuffer.getRenderableGalTile(tileno, childrenOrder, this._hips)
13200
+ : this._tileBuffer.getRenderableTile(tileno, childrenOrder, this._hips);
13201
+ if (!childTile)
13202
+ continue;
12689
13203
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, this._hipsShaderProgram);
12690
13204
  if (childTile.getReadyState()) {
12691
13205
  allSkyTiles2Skip.push(tileno);
@@ -12974,8 +13488,10 @@ class AncestorTile {
12974
13488
  continue;
12975
13489
  }
12976
13490
  const childTile = this._isGalacticHips
12977
- ? this._tileBuffer.getGalTile(childTileNo, childrenOrder, this._hips)
12978
- : this._tileBuffer.getTile(childTileNo, childrenOrder, this._hips);
13491
+ ? this._tileBuffer.getRenderableGalTile(childTileNo, childrenOrder, this._hips)
13492
+ : this._tileBuffer.getRenderableTile(childTileNo, childrenOrder, this._hips);
13493
+ if (!childTile)
13494
+ continue;
12979
13495
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, this._hipsShaderProgram);
12980
13496
  if (childTile.getReadyState()) {
12981
13497
  quadrantsToDraw.delete(c);
@@ -14499,8 +15015,10 @@ class Tile {
14499
15015
  continue;
14500
15016
  }
14501
15017
  const childTile = this._isGalacticHips
14502
- ? this._tileBuffer.getGalTile(childTileNo, childrenOrder, this._hips)
14503
- : this._tileBuffer.getTile(childTileNo, childrenOrder, this._hips);
15018
+ ? this._tileBuffer.getRenderableGalTile(childTileNo, childrenOrder, this._hips)
15019
+ : this._tileBuffer.getRenderableTile(childTileNo, childrenOrder, this._hips);
15020
+ if (!childTile)
15021
+ continue;
14504
15022
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, hipsShaderProgram);
14505
15023
  if (childTile.getReadyState()) {
14506
15024
  quadrantsToDraw.delete(c);
@@ -14552,6 +15070,7 @@ class TileBuffer {
14552
15070
  _webgl;
14553
15071
  _visibleTileManager;
14554
15072
  _hipsShaderProgram;
15073
+ _tileAcquisitionEnabled = true;
14555
15074
  constructor(minutesToLiveInCache = 1, webgl, hipsShaderProgram, visibleTileManager) {
14556
15075
  this._hipsShaderProgram = hipsShaderProgram;
14557
15076
  this._visibleTileManager = visibleTileManager;
@@ -14583,8 +15102,14 @@ class TileBuffer {
14583
15102
  }
14584
15103
  this._galActiveHiPS.set(hips, new Map());
14585
15104
  }
15105
+ /** Enable or suspend creation of missing HiPS tiles. */
15106
+ setTileAcquisitionEnabled(enabled) {
15107
+ this._tileAcquisitionEnabled = enabled;
15108
+ }
14586
15109
  /** Preload/add tile for every registered equatorial HiPS. */
14587
15110
  addTile(order, tileno) {
15111
+ if (!this._tileAcquisitionEnabled)
15112
+ return;
14588
15113
  for (const hips of this._activeHiPS.keys()) {
14589
15114
  if (order > hips.maxOrder) {
14590
15115
  continue;
@@ -14597,6 +15122,8 @@ class TileBuffer {
14597
15122
  }
14598
15123
  /** Preload/add tile for every registered galactic HiPS. */
14599
15124
  addGalTile(order, tileno) {
15125
+ if (!this._tileAcquisitionEnabled)
15126
+ return;
14600
15127
  for (const hips of this._galActiveHiPS.keys()) {
14601
15128
  if (order > hips.maxOrder) {
14602
15129
  continue;
@@ -14643,6 +15170,44 @@ class TileBuffer {
14643
15170
  }
14644
15171
  return this._galTiles.get(tileKey);
14645
15172
  }
15173
+ /**
15174
+ * Return an equatorial tile for rendering. While acquisition is suspended,
15175
+ * only tiles already active or cached are returned; missing tiles are not
15176
+ * created.
15177
+ */
15178
+ getRenderableTile(tileno, order, hips) {
15179
+ if (this._tileAcquisitionEnabled) {
15180
+ return this.getTile(tileno, order, hips);
15181
+ }
15182
+ const tileKey = this.key(order, tileno, hips);
15183
+ const activeTile = this._tiles.get(tileKey);
15184
+ if (activeTile)
15185
+ return activeTile;
15186
+ const cachedTile = this._cachedTiles.get(tileKey);
15187
+ if (!cachedTile)
15188
+ return undefined;
15189
+ this._tiles.set(tileKey, cachedTile);
15190
+ this._cachedTiles.delete(tileKey);
15191
+ cachedTile.resetCacheTime0();
15192
+ return cachedTile;
15193
+ }
15194
+ /** Galactic counterpart of getRenderableTile(). */
15195
+ getRenderableGalTile(tileno, order, hips) {
15196
+ if (this._tileAcquisitionEnabled) {
15197
+ return this.getGalTile(tileno, order, hips);
15198
+ }
15199
+ const tileKey = this.key(order, tileno, hips);
15200
+ const activeTile = this._galTiles.get(tileKey);
15201
+ if (activeTile)
15202
+ return activeTile;
15203
+ const cachedTile = this._galCachedTiles.get(tileKey);
15204
+ if (!cachedTile)
15205
+ return undefined;
15206
+ this._galTiles.set(tileKey, cachedTile);
15207
+ this._galCachedTiles.delete(tileKey);
15208
+ cachedTile.resetCacheTime0();
15209
+ return cachedTile;
15210
+ }
14646
15211
  /** Move a tile (equatorial or galactic) into cache. */
14647
15212
  moveTileToCache(tileno, order, hips) {
14648
15213
  const tileKey = this.key(order, tileno, hips);
@@ -14851,6 +15416,10 @@ class VisibleTilesManager {
14851
15416
  if (Global_js_1.default.insideSphere && order < 3) {
14852
15417
  order = 3;
14853
15418
  }
15419
+ // Visibility state is frame-local. Do not retain ancestors from previous
15420
+ // camera positions, otherwise a flyTo accumulates the whole travelled path.
15421
+ this._ancestorsMap.clear();
15422
+ this._galAncestorsMap.clear();
14854
15423
  this._ancestorsMap.set(order, []);
14855
15424
  this._galAncestorsMap.set(order, []);
14856
15425
  let pixels = [];
@@ -14867,8 +15436,10 @@ class VisibleTilesManager {
14867
15436
  }
14868
15437
  else {
14869
15438
  const geomhealpix = Global_js_1.default.getHealpix(order);
14870
- const maxX = webgl.canvas.width;
14871
- const maxY = webgl.canvas.height;
15439
+ const canvas = webgl.canvas;
15440
+ const canvasRect = canvas.getBoundingClientRect();
15441
+ const maxX = canvasRect.width;
15442
+ const maxY = canvasRect.height;
14872
15443
  // Sample a grid of screen points, project to the sphere, then to galactic
14873
15444
  for (let i = 0; i <= maxX; i += maxX / 30) {
14874
15445
  for (let j = 0; j <= maxY; j += maxY / 30) {
@@ -14971,6 +15542,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14971
15542
  _currentOrder;
14972
15543
  _visibleTiles = [];
14973
15544
  _coverageTiles = [];
15545
+ _color;
15546
+ _wireframe;
14974
15547
  constructor(radius, position, xrad, yrad, _descriptor, webgl, _healpixGrid) {
14975
15548
  super(radius, position, xrad, yrad, _descriptor.name, webgl, false);
14976
15549
  this._descriptor = _descriptor;
@@ -14979,6 +15552,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14979
15552
  this._shaderProgram = new MeshHiPSShaderProgram_js_1.MeshHiPSShaderProgram(webgl);
14980
15553
  this._shaderProgram.enableProgram();
14981
15554
  this._currentOrder = _descriptor.selectedOrder;
15555
+ this._color = [..._descriptor.color];
15556
+ this._wireframe = _descriptor.wireframe;
14982
15557
  }
14983
15558
  get currentOrder() {
14984
15559
  return this._currentOrder;
@@ -15001,6 +15576,12 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
15001
15576
  getProperty(key) {
15002
15577
  return this._descriptor.getProperty(key);
15003
15578
  }
15579
+ setColor(color) {
15580
+ this._color = [...color];
15581
+ }
15582
+ setWireframe(wireframe) {
15583
+ this._wireframe = wireframe;
15584
+ }
15004
15585
  refreshOrder(fovDeg) {
15005
15586
  if (this._descriptor.fixedOrder) {
15006
15587
  this._currentOrder = this._descriptor.selectedOrder;
@@ -15040,7 +15621,7 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
15040
15621
  for (const coord of byOrder.get(order) ?? []) {
15041
15622
  this._tiles
15042
15623
  .get(this.tileKey(coord))
15043
- ?.draw(pMatrix, vMatrix, mMatrix, this._descriptor.color, this._descriptor.wireframe);
15624
+ ?.draw(pMatrix, vMatrix, mMatrix, this._color, this._wireframe);
15044
15625
  }
15045
15626
  }
15046
15627
  if (wasCullFace)
@@ -16831,6 +17412,7 @@ exports.TerraFootprintSetGL = TerraFootprintSetGL;
16831
17412
  Object.defineProperty(exports, "__esModule", ({ value: true }));
16832
17413
  exports.TerraPointSetGL = void 0;
16833
17414
  const CatalogueGL_js_1 = __webpack_require__(1232);
17415
+ const gl_matrix_1 = __webpack_require__(3890);
16834
17416
  const MetadataManager_js_1 = __webpack_require__(5403);
16835
17417
  const Point_js_1 = __webpack_require__(6553);
16836
17418
  const CoordsType_js_1 = __webpack_require__(8145);
@@ -16865,8 +17447,45 @@ class TerraPointSetGL extends CatalogueGL_js_1.CatalogueGL {
16865
17447
  this._ready = true;
16866
17448
  this._bufferInitialised = false;
16867
17449
  }
17450
+ findNearestSourceIndex(inMouseHelper, inMatrix, viewMatrix, projectionMatrix) {
17451
+ if (inMouseHelper.xyz == null)
17452
+ return null;
17453
+ const canvas = this.getCanvas();
17454
+ const width = canvas.clientWidth;
17455
+ const height = canvas.clientHeight;
17456
+ if (!width || !height)
17457
+ return null;
17458
+ const modelView = gl_matrix_1.mat4.create();
17459
+ const mvp = gl_matrix_1.mat4.create();
17460
+ gl_matrix_1.mat4.multiply(modelView, viewMatrix, inMatrix);
17461
+ gl_matrix_1.mat4.multiply(mvp, projectionMatrix, modelView);
17462
+ const mousePoint = new Point_js_1.Point({
17463
+ x: inMouseHelper.xyz[0],
17464
+ y: inMouseHelper.xyz[1],
17465
+ z: inMouseHelper.xyz[2],
17466
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
17467
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
17468
+ if (!mouseScreen)
17469
+ return null;
17470
+ let nearestIndex = null;
17471
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
17472
+ for (let index = 0; index < this._sources.length; index += 1) {
17473
+ const source = this._sources[index];
17474
+ if (!source)
17475
+ continue;
17476
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
17477
+ if (!sourceScreen)
17478
+ continue;
17479
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
17480
+ if (distancePx <= this.getHitRadiusPx(source) &&
17481
+ distancePx < nearestDistancePx) {
17482
+ nearestDistancePx = distancePx;
17483
+ nearestIndex = index;
17484
+ }
17485
+ }
17486
+ return nearestIndex;
17487
+ }
16868
17488
  draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix) {
16869
- this._oldMouseCoords = inMouseHelper?.xyz ?? null;
16870
17489
  super.draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix);
16871
17490
  }
16872
17491
  }
@@ -17098,6 +17717,8 @@ class CatalogueShaderProgram {
17098
17717
  this._webgl = webgl;
17099
17718
  this.gl_uniforms = {
17100
17719
  vertex_color: 'u_fragcolor',
17720
+ hover_color: 'u_hovercolor',
17721
+ selected_color: 'u_selectedcolor',
17101
17722
  m_perspective: 'uPMatrix',
17102
17723
  m_model_view: 'uMVMatrix'
17103
17724
  };
@@ -17111,6 +17732,8 @@ class CatalogueShaderProgram {
17111
17732
  pMatrix: null,
17112
17733
  mvMatrix: null,
17113
17734
  color: null,
17735
+ hoverColor: null,
17736
+ selectedColor: null,
17114
17737
  position: -1,
17115
17738
  hovered: -1,
17116
17739
  pointSize: -1,
@@ -17160,6 +17783,8 @@ class CatalogueShaderProgram {
17160
17783
  this.locations.pointSize = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_size);
17161
17784
  this.locations.brightness = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_hue);
17162
17785
  this.locations.color = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.vertex_color);
17786
+ this.locations.hoverColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.hover_color);
17787
+ this.locations.selectedColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.selected_color);
17163
17788
  }
17164
17789
  enableShaders(pMatrix, modelMatrix, viewMatrix) {
17165
17790
  // const gl = global.gl as GL
@@ -17850,6 +18475,8 @@ class ShaderManager {
17850
18475
  in float v_brightness;
17851
18476
 
17852
18477
  uniform vec4 u_fragcolor;
18478
+ uniform vec4 u_hovercolor;
18479
+ uniform vec4 u_selectedcolor;
17853
18480
 
17854
18481
  out vec4 fragColor;
17855
18482
 
@@ -17906,11 +18533,12 @@ class ShaderManager {
17906
18533
  #endif
17907
18534
 
17908
18535
  if (v_selected == 1.0){
17909
- // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
17910
- fragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
18536
+ fragColor = u_hovercolor * alpha;
17911
18537
  } else if (v_selected == 2.0){
17912
- // gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
17913
- fragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
18538
+ // Persistent selection: keep the dataset colour as an outer ring
18539
+ // and use the hover colour for the fill. This remains visible for
18540
+ // both light and dark user-selected base colours.
18541
+ fragColor = (r > 0.72 ? u_fragcolor : u_hovercolor) * alpha;
17914
18542
  }else{
17915
18543
  if (r < 0.4) {
17916
18544
  discard;
@@ -18318,6 +18946,7 @@ class XYZShaderProgram {
18318
18946
  _shaderProgram;
18319
18947
  _colorMapBlockIndex = null;
18320
18948
  _colorMapBuffer = null;
18949
+ _opacity = 1;
18321
18950
  _runtimeColorMap;
18322
18951
  _colorMapVariableInfo = {
18323
18952
  r_palette: { index: 0, offset: 0 },
@@ -18332,6 +18961,7 @@ class XYZShaderProgram {
18332
18961
  vMatrix: null,
18333
18962
  sampler: null,
18334
18963
  colorMapIdx: null,
18964
+ opacity: null,
18335
18965
  vertexPositionAttribute: -1,
18336
18966
  textureCoordAttribute: -1,
18337
18967
  };
@@ -18352,6 +18982,9 @@ class XYZShaderProgram {
18352
18982
  enableProgram() {
18353
18983
  this._webgl.useProgram(this.shaderProgram);
18354
18984
  }
18985
+ setOpacity(opacity) {
18986
+ this._opacity = Math.min(1, Math.max(0, opacity));
18987
+ }
18355
18988
  setRuntimeColorMap(colorMap) {
18356
18989
  this._runtimeColorMap = colorMap;
18357
18990
  }
@@ -18364,6 +18997,7 @@ class XYZShaderProgram {
18364
18997
  this.locations.vMatrix = gl.getUniformLocation(program, 'uVMatrix');
18365
18998
  this.locations.sampler = gl.getUniformLocation(program, 'uSampler');
18366
18999
  this.locations.colorMapIdx = gl.getUniformLocation(program, 'cmapIdx');
19000
+ this.locations.opacity = gl.getUniformLocation(program, 'uOpacity');
18367
19001
  this.locations.vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
18368
19002
  this.locations.textureCoordAttribute = gl.getAttribLocation(program, 'aTextureCoord');
18369
19003
  gl.uniformMatrix4fv(this.locations.pMatrix, false, pMatrix);
@@ -18371,6 +19005,7 @@ class XYZShaderProgram {
18371
19005
  gl.uniformMatrix4fv(this.locations.mMatrix, false, mMatrix);
18372
19006
  gl.uniform1i(this.locations.sampler, 0);
18373
19007
  gl.uniform1i(this.locations.colorMapIdx, colorMapIdx);
19008
+ gl.uniform1f(this.locations.opacity, this._opacity);
18374
19009
  if (colorMapIdx >= 2) {
18375
19010
  this.uploadColorMap(colorMapIdx);
18376
19011
  }
@@ -18393,6 +19028,7 @@ class XYZShaderProgram {
18393
19028
  in vec2 vTextureCoord;
18394
19029
  uniform sampler2D uSampler;
18395
19030
  uniform int cmapIdx;
19031
+ uniform float uOpacity;
18396
19032
 
18397
19033
  layout (std140) uniform colormap {
18398
19034
  float r_palette[256];
@@ -18407,7 +19043,7 @@ class XYZShaderProgram {
18407
19043
 
18408
19044
  if (cmapIdx == 1) {
18409
19045
  float gray = 0.21 * color.r + 0.71 * color.g + 0.07 * color.b;
18410
- outColor = vec4(vec3(gray), color.a);
19046
+ outColor = vec4(vec3(gray), color.a * uOpacity);
18411
19047
  return;
18412
19048
  }
18413
19049
 
@@ -18420,12 +19056,12 @@ class XYZShaderProgram {
18420
19056
  r_palette[rIndex] / 256.0,
18421
19057
  g_palette[gIndex] / 256.0,
18422
19058
  b_palette[bIndex] / 256.0,
18423
- color.a
19059
+ color.a * uOpacity
18424
19060
  );
18425
19061
  return;
18426
19062
  }
18427
19063
 
18428
- outColor = color;
19064
+ outColor = vec4(color.rgb, color.a * uOpacity);
18429
19065
  }`);
18430
19066
  gl.attachShader(this._shaderProgram, vertexShader);
18431
19067
  gl.attachShader(this._shaderProgram, fragmentShader);
@@ -19636,7 +20272,6 @@ exports["default"] = STCSParser;
19636
20272
  (__unused_webpack_module, exports, __webpack_require__) {
19637
20273
 
19638
20274
  "use strict";
19639
- var __webpack_unused_export__;
19640
20275
 
19641
20276
  /*
19642
20277
  * AstroViewer
@@ -19648,9 +20283,10 @@ var __webpack_unused_export__;
19648
20283
  *
19649
20284
  * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
19650
20285
  */
19651
- __webpack_unused_export__ = ({ value: true });
20286
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
19652
20287
  exports.cartesianToSpherical = cartesianToSpherical;
19653
20288
  exports.colorHex2RGB = colorHex2RGB;
20289
+ exports.interactionColorsFromHex = interactionColorsFromHex;
19654
20290
  exports.degToRad = degToRad;
19655
20291
  exports.radToDeg = radToDeg;
19656
20292
  exports.sphericalToAstroDeg = sphericalToAstroDeg;
@@ -19658,6 +20294,8 @@ exports.sphericalToCartesian = sphericalToCartesian;
19658
20294
  exports.astroDegToSpherical = astroDegToSpherical;
19659
20295
  exports.raDegToHMS = raDegToHMS;
19660
20296
  exports.decDegToDMS = decDegToDMS;
20297
+ exports.raHMSToDeg = raHMSToDeg;
20298
+ exports.decDMSToDeg = decDMSToDeg;
19661
20299
  /**
19662
20300
  * @author Fabrizio Giordano (Fab)
19663
20301
  */
@@ -19688,6 +20326,25 @@ function colorHex2RGB(hexColor) {
19688
20326
  const rgb3 = (dec3 / 255).toFixed(2);
19689
20327
  return [parseFloat(rgb1), parseFloat(rgb2), parseFloat(rgb3)];
19690
20328
  }
20329
+ /**
20330
+ * Derive interaction colours from the object's normal colour.
20331
+ * Dark colours move towards white; light colours move towards black.
20332
+ * Selection uses the stronger contrast so selected > hover > normal.
20333
+ */
20334
+ function interactionColorsFromHex(hexColor) {
20335
+ const base = colorHex2RGB(hexColor);
20336
+ const luminance = 0.2126 * base[0] + 0.7152 * base[1] + 0.0722 * base[2];
20337
+ const target = luminance < 0.55 ? 1.0 : 0.0;
20338
+ const mix = (amount) => [
20339
+ base[0] + (target - base[0]) * amount,
20340
+ base[1] + (target - base[1]) * amount,
20341
+ base[2] + (target - base[2]) * amount,
20342
+ ];
20343
+ return {
20344
+ hover: mix(0.45),
20345
+ selected: mix(0.45),
20346
+ };
20347
+ }
19691
20348
  function degToRad(degrees) {
19692
20349
  return (degrees / 180) * Math.PI;
19693
20350
  }
@@ -19734,6 +20391,13 @@ function decDegToDMS(decDeg) {
19734
20391
  d = d * sign;
19735
20392
  return { d, m, s };
19736
20393
  }
20394
+ function raHMSToDeg(hms) {
20395
+ return 15 * (hms.h + hms.m / 60 + hms.s / 3600);
20396
+ }
20397
+ function decDMSToDeg(dms) {
20398
+ const sign = dms.d < 0 || Object.is(dms.d, -0) ? -1 : 1;
20399
+ return sign * (Math.abs(dms.d) + dms.m / 60 + dms.s / 3600);
20400
+ }
19737
20401
 
19738
20402
 
19739
20403
  /***/ },