astro-viewer 3.10.0 → 3.12.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 (75) hide show
  1. package/README.md +66 -4
  2. package/dist/astroviewer.cjs +911 -154
  3. package/dist/astroviewer.cjs.map +1 -1
  4. package/dist/astroviewer.js +911 -154
  5. package/dist/astroviewer.js.map +1 -1
  6. package/dist/astroviewer.min.js +1 -1
  7. package/dist/astroviewer.min.js.map +1 -1
  8. package/lib-esm/AstroSphere.d.ts +37 -2
  9. package/lib-esm/AstroSphere.d.ts.map +1 -1
  10. package/lib-esm/AstroSphere.js +249 -70
  11. package/lib-esm/AstroSphere.js.map +1 -1
  12. package/lib-esm/AstroViewer.d.ts +15 -1
  13. package/lib-esm/AstroViewer.d.ts.map +1 -1
  14. package/lib-esm/AstroViewer.js +41 -4
  15. package/lib-esm/AstroViewer.js.map +1 -1
  16. package/lib-esm/Camera.d.ts +14 -0
  17. package/lib-esm/Camera.d.ts.map +1 -1
  18. package/lib-esm/Camera.js +118 -8
  19. package/lib-esm/Camera.js.map +1 -1
  20. package/lib-esm/index.d.ts +1 -0
  21. package/lib-esm/index.d.ts.map +1 -1
  22. package/lib-esm/index.js +1 -0
  23. package/lib-esm/index.js.map +1 -1
  24. package/lib-esm/model/Source.d.ts +11 -0
  25. package/lib-esm/model/Source.d.ts.map +1 -1
  26. package/lib-esm/model/Source.js +8 -1
  27. package/lib-esm/model/Source.js.map +1 -1
  28. package/lib-esm/model/catalogues/CatalogueGL.d.ts +36 -5
  29. package/lib-esm/model/catalogues/CatalogueGL.d.ts.map +1 -1
  30. package/lib-esm/model/catalogues/CatalogueGL.js +329 -57
  31. package/lib-esm/model/catalogues/CatalogueGL.js.map +1 -1
  32. package/lib-esm/model/earth/XYZMap.d.ts +4 -1
  33. package/lib-esm/model/earth/XYZMap.d.ts.map +1 -1
  34. package/lib-esm/model/earth/XYZMap.js +35 -2
  35. package/lib-esm/model/earth/XYZMap.js.map +1 -1
  36. package/lib-esm/model/footprints/FootprintSetGL.d.ts +1 -0
  37. package/lib-esm/model/footprints/FootprintSetGL.d.ts.map +1 -1
  38. package/lib-esm/model/footprints/FootprintSetGL.js +13 -3
  39. package/lib-esm/model/footprints/FootprintSetGL.js.map +1 -1
  40. package/lib-esm/model/grid/EquatorialGrid.d.ts +1 -0
  41. package/lib-esm/model/grid/EquatorialGrid.d.ts.map +1 -1
  42. package/lib-esm/model/grid/EquatorialGrid.js +3 -0
  43. package/lib-esm/model/grid/EquatorialGrid.js.map +1 -1
  44. package/lib-esm/model/grid/HealpixGrid.d.ts +1 -0
  45. package/lib-esm/model/grid/HealpixGrid.d.ts.map +1 -1
  46. package/lib-esm/model/grid/HealpixGrid.js +3 -0
  47. package/lib-esm/model/grid/HealpixGrid.js.map +1 -1
  48. package/lib-esm/model/grid/LonLatGrid.d.ts +1 -0
  49. package/lib-esm/model/grid/LonLatGrid.d.ts.map +1 -1
  50. package/lib-esm/model/grid/LonLatGrid.js +3 -0
  51. package/lib-esm/model/grid/LonLatGrid.js.map +1 -1
  52. package/lib-esm/model/meships/MeshHiPS.d.ts +4 -0
  53. package/lib-esm/model/meships/MeshHiPS.d.ts.map +1 -1
  54. package/lib-esm/model/meships/MeshHiPS.js +11 -1
  55. package/lib-esm/model/meships/MeshHiPS.js.map +1 -1
  56. package/lib-esm/model/terra/TerraPointSetGL.d.ts +1 -0
  57. package/lib-esm/model/terra/TerraPointSetGL.d.ts.map +1 -1
  58. package/lib-esm/model/terra/TerraPointSetGL.js +39 -1
  59. package/lib-esm/model/terra/TerraPointSetGL.js.map +1 -1
  60. package/lib-esm/shader/CatalogueShaderProgram.d.ts +4 -0
  61. package/lib-esm/shader/CatalogueShaderProgram.d.ts.map +1 -1
  62. package/lib-esm/shader/CatalogueShaderProgram.js +6 -0
  63. package/lib-esm/shader/CatalogueShaderProgram.js.map +1 -1
  64. package/lib-esm/shader/ShaderManager.d.ts.map +1 -1
  65. package/lib-esm/shader/ShaderManager.js +7 -4
  66. package/lib-esm/shader/ShaderManager.js.map +1 -1
  67. package/lib-esm/shader/XYZShaderProgram.d.ts +3 -0
  68. package/lib-esm/shader/XYZShaderProgram.d.ts.map +1 -1
  69. package/lib-esm/shader/XYZShaderProgram.js +11 -3
  70. package/lib-esm/shader/XYZShaderProgram.js.map +1 -1
  71. package/lib-esm/utils/Utils.d.ts +12 -0
  72. package/lib-esm/utils/Utils.d.ts.map +1 -1
  73. package/lib-esm/utils/Utils.js +26 -0
  74. package/lib-esm/utils/Utils.js.map +1 -1
  75. package/package.json +2 -2
@@ -53,6 +53,7 @@ __webpack_require__.d(vec3_namespaceObject, {
53
53
  dot: () => (dot),
54
54
  fromValues: () => (vec3_fromValues),
55
55
  length: () => (vec3_length),
56
+ lerp: () => (lerp),
56
57
  normalize: () => (normalize),
57
58
  scale: () => (vec3_scale),
58
59
  scaleAndAdd: () => (scaleAndAdd),
@@ -3640,12 +3641,18 @@ class AstroSphere {
3640
3641
  _activeHiPS = null;
3641
3642
  _activeHiPSLayers = [];
3642
3643
  _activeXYZ2 = null;
3644
+ _earthRasterOverlays = [];
3645
+ _earthRasterOverlaySequence = 0;
3643
3646
  _activeMeshHiPS = null;
3644
3647
  _activeBaseLayer = null;
3648
+ _activeDomain = "astronomy";
3649
+ domainCameraStates = {};
3645
3650
  startup = true;
3646
3651
  fov;
3647
- activeCatalogues = [];
3648
- activeFootprintSets = [];
3652
+ astronomyCatalogues = [];
3653
+ earthPointSets = [];
3654
+ astronomyFootprintSets = [];
3655
+ earthFootprintSets = [];
3649
3656
  activePolylineSets = [];
3650
3657
  activeSensorCones = [];
3651
3658
  activeSatelliteObjects = [];
@@ -3878,9 +3885,7 @@ class AstroSphere {
3878
3885
  return true;
3879
3886
  }
3880
3887
  emitCameraChanged(reason) {
3881
- // avoid dispatch before scene is ready
3882
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
3883
- return;
3888
+ // Camera interaction remains meaningful even when no base layer is active.
3884
3889
  if (!this._healpixGrid?.fovObj)
3885
3890
  return;
3886
3891
  const detail = this.getCurrentStatus();
@@ -3906,6 +3911,7 @@ class AstroSphere {
3906
3911
  const handleMouseDown = (event) => {
3907
3912
  canvas.setPointerCapture(event.pointerId);
3908
3913
  this.mouseDown = true;
3914
+ this._camera.cancelFlyTo();
3909
3915
  const rect = canvas.getBoundingClientRect();
3910
3916
  this.lastMouseX = event.clientX - rect.left; // locale al canvas
3911
3917
  this.lastMouseY = event.clientY - rect.top; // locale al canvas
@@ -3944,8 +3950,11 @@ class AstroSphere {
3944
3950
  if (mousePoint && mousePoint.length > 0) {
3945
3951
  this.mouseHelper.update(mousePoint);
3946
3952
  this.updateLastMousePoint();
3947
- for (const cat of this.activeCatalogues) {
3948
- const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper);
3953
+ for (const cat of this.getActivePointSets()) {
3954
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
3955
+ if (!activeModelMatrix)
3956
+ continue;
3957
+ const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
3949
3958
  if (!clickResult?.sources.length)
3950
3959
  continue;
3951
3960
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-clicked", {
@@ -3958,7 +3967,7 @@ class AstroSphere {
3958
3967
  composed: true,
3959
3968
  }));
3960
3969
  }
3961
- for (const fset of this.activeFootprintSets) {
3970
+ for (const fset of this.getActiveFootprintSets()) {
3962
3971
  const clickResult = fset.selectPrimaryFootprintFromClick(this.mouseHelper);
3963
3972
  if (!clickResult?.footprints.length)
3964
3973
  continue;
@@ -4032,6 +4041,7 @@ class AstroSphere {
4032
4041
  const handleMouseWheel = (event) => {
4033
4042
  const currentFov = this._healpixGrid.getMinFoV();
4034
4043
  const zoomStep = this.computeZoomStep(currentFov, event.deltaY);
4044
+ this._camera.cancelFlyTo();
4035
4045
  // Apply wheel zoom immediately and discard any queued inertia so reversing
4036
4046
  // direction feels responsive instead of "buffered".
4037
4047
  this.zoomInertia = 0;
@@ -4054,8 +4064,11 @@ class AstroSphere {
4054
4064
  }
4055
4065
  this.mouseHelper.update(mousePoint);
4056
4066
  this.updateLastMousePoint();
4057
- for (const cat of this.activeCatalogues) {
4058
- const pickResult = cat.getSourcesFromPointer(this.mouseHelper);
4067
+ for (const cat of this.getActivePointSets()) {
4068
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4069
+ if (!activeModelMatrix)
4070
+ continue;
4071
+ const pickResult = cat.getSourcesFromPointer(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4059
4072
  if (!pickResult?.sources.length)
4060
4073
  continue;
4061
4074
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-contextmenu", {
@@ -4070,7 +4083,7 @@ class AstroSphere {
4070
4083
  }));
4071
4084
  break;
4072
4085
  }
4073
- for (const fset of this.activeFootprintSets) {
4086
+ for (const fset of this.getActiveFootprintSets()) {
4074
4087
  const pickResult = fset.getFootprintsFromPointer(this.mouseHelper);
4075
4088
  if (!pickResult?.footprints.length)
4076
4089
  continue;
@@ -4160,19 +4173,22 @@ class AstroSphere {
4160
4173
  return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
4161
4174
  }
4162
4175
  activateHiPS(hipsDescriptor) {
4176
+ if (this._activeHiPSLayers.length > 0) {
4177
+ throw new Error("Cannot load a HiPS base layer while stacked layers are active.");
4178
+ }
4163
4179
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4164
- for (const hips of this._activeHiPSLayers) {
4165
- tileBuffer.removeHiPS(hips);
4180
+ if (this._activeHiPS) {
4181
+ tileBuffer.removeHiPS(this._activeHiPS);
4166
4182
  }
4167
- this._activeHiPSLayers = [];
4168
4183
  const hips = this.createHiPS(hipsDescriptor);
4169
- this._activeHiPSLayers.push(hips);
4170
4184
  this._activeHiPS = hips;
4185
+ this._activeDomain = "astronomy";
4171
4186
  this._activeBaseLayer = "hips";
4172
4187
  return hips;
4173
4188
  }
4174
4189
  setHiPSOpacity(hips, opacity) {
4175
- if (!this._activeHiPSLayers.includes(hips)) {
4190
+ const isBaseLayer = this._activeHiPSLayers.length === 0 && this._activeHiPS === hips;
4191
+ if (!isBaseLayer && !this._activeHiPSLayers.includes(hips)) {
4176
4192
  throw new Error("HiPS layer is not active in this AstroSphere.");
4177
4193
  }
4178
4194
  hips.setOpacity(opacity);
@@ -4180,6 +4196,13 @@ class AstroSphere {
4180
4196
  addHiPS(hipsDescriptor) {
4181
4197
  const normalizeURL = (url) => String(url).replace(/\/+$/, "");
4182
4198
  const descriptorURL = normalizeURL(hipsDescriptor.url);
4199
+ // Entering stack mode discards the standalone base HiPS. Base mode and
4200
+ // stack mode are intentionally mutually exclusive.
4201
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4202
+ const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4203
+ tileBuffer.removeHiPS(this._activeHiPS);
4204
+ this._activeHiPS = null;
4205
+ }
4183
4206
  const existing = this._activeHiPSLayers.find((hips) => normalizeURL(hips.baseURL) === descriptorURL);
4184
4207
  if (existing) {
4185
4208
  throw new Error(`HiPS already active: ${String(hipsDescriptor.url)}`);
@@ -4187,6 +4210,7 @@ class AstroSphere {
4187
4210
  const hips = this.createHiPS(hipsDescriptor);
4188
4211
  this._activeHiPSLayers.push(hips);
4189
4212
  this._activeHiPS = hips;
4213
+ this._activeDomain = "astronomy";
4190
4214
  this._activeBaseLayer = "hips";
4191
4215
  return hips;
4192
4216
  }
@@ -4211,8 +4235,13 @@ class AstroSphere {
4211
4235
  }
4212
4236
  removeAllHiPS() {
4213
4237
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4214
- for (const hips of this._activeHiPSLayers) {
4215
- tileBuffer.removeHiPS(hips);
4238
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4239
+ tileBuffer.removeHiPS(this._activeHiPS);
4240
+ }
4241
+ else {
4242
+ for (const hips of this._activeHiPSLayers) {
4243
+ tileBuffer.removeHiPS(hips);
4244
+ }
4216
4245
  }
4217
4246
  this._activeHiPSLayers = [];
4218
4247
  this._activeHiPS = null;
@@ -4225,39 +4254,169 @@ class AstroSphere {
4225
4254
  this._activeBaseLayer = "xyz";
4226
4255
  }
4227
4256
  activateXYZ2(config) {
4257
+ this._activeDomain = "earth";
4228
4258
  this._activeXYZ2 = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4229
4259
  this._activeBaseLayer = "xyz";
4230
4260
  }
4231
4261
  activateMeshHiPS(descriptor) {
4262
+ this._activeDomain = "mesh";
4232
4263
  this._activeMeshHiPS = new MeshHiPS_js_1.MeshHiPS(descriptor.meshRadius, [0.0, 0.0, 0.0], 0, 0, descriptor, this._webgl, this._healpixGrid);
4233
4264
  this._activeBaseLayer = "meships";
4234
4265
  }
4266
+ setMeshHiPSColor(color) {
4267
+ this._activeMeshHiPS?.setColor(color);
4268
+ }
4269
+ setMeshHiPSWireframe(wireframe) {
4270
+ this._activeMeshHiPS?.setWireframe(wireframe);
4271
+ }
4235
4272
  activateWMTS(config) {
4273
+ this._activeDomain = "earth";
4236
4274
  const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4237
4275
  const xyzConfig = adapter.toXYZLayerConfig();
4238
4276
  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);
4239
4277
  this._activeBaseLayer = "xyz";
4240
4278
  }
4241
- // Catalogue section
4279
+ addXYZRasterOverlay(config) {
4280
+ const map = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4281
+ return this.addEarthRasterOverlay(config.name, "xyz", map);
4282
+ }
4283
+ addWMTSRasterOverlay(config) {
4284
+ const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4285
+ const xyzConfig = adapter.toXYZLayerConfig();
4286
+ const name = config.layer ? `WMTS ${config.layer}` : "WMTS Earth Raster Overlay";
4287
+ 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);
4288
+ return this.addEarthRasterOverlay(name, "wmts", map);
4289
+ }
4290
+ getEarthRasterOverlays() {
4291
+ return this._earthRasterOverlays.map(({ map: _map, ...overlay }) => ({ ...overlay }));
4292
+ }
4293
+ setEarthRasterOverlayOpacity(id, opacity) {
4294
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4295
+ if (!overlay)
4296
+ return;
4297
+ overlay.opacity = Math.min(1, Math.max(0, opacity));
4298
+ overlay.map.setOpacity(overlay.opacity);
4299
+ }
4300
+ setEarthRasterOverlayVisible(id, visible) {
4301
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4302
+ if (overlay)
4303
+ overlay.visible = visible;
4304
+ }
4305
+ removeEarthRasterOverlay(id) {
4306
+ this._earthRasterOverlays = this._earthRasterOverlays.filter((entry) => entry.id !== id);
4307
+ }
4308
+ removeAllEarthRasterOverlays() {
4309
+ this._earthRasterOverlays = [];
4310
+ }
4311
+ addEarthRasterOverlay(name, sourceType, map) {
4312
+ const overlay = {
4313
+ id: `earth-raster-${++this._earthRasterOverlaySequence}`,
4314
+ name,
4315
+ sourceType,
4316
+ visible: true,
4317
+ opacity: 0.65,
4318
+ map,
4319
+ };
4320
+ map.setOpacity(overlay.opacity);
4321
+ this._earthRasterOverlays.push(overlay);
4322
+ const { map: _map, ...info } = overlay;
4323
+ return { ...info };
4324
+ }
4325
+ setActiveDomain(domain) {
4326
+ if (domain !== this._activeDomain) {
4327
+ this.clearGridLabelsForDomain(this._activeDomain);
4328
+ this.domainCameraStates[this._activeDomain] = this._camera.getViewState();
4329
+ const targetCameraState = this.domainCameraStates[domain];
4330
+ if (targetCameraState) {
4331
+ this._camera.restoreViewState(targetCameraState);
4332
+ }
4333
+ this.inertiaX = 0;
4334
+ this.inertiaY = 0;
4335
+ this.zoomInertia = 0;
4336
+ this._cameraStatusChanged = true;
4337
+ }
4338
+ this._activeDomain = domain;
4339
+ if (domain === "astronomy") {
4340
+ this._activeBaseLayer = this._activeHiPS ? "hips" : null;
4341
+ }
4342
+ else if (domain === "earth") {
4343
+ this._activeBaseLayer = this._activeXYZ2 ? "xyz" : null;
4344
+ }
4345
+ else {
4346
+ this._activeBaseLayer = this._activeMeshHiPS ? "meships" : null;
4347
+ }
4348
+ // A hover from the previous vertical must not leak into the new one.
4349
+ this.lastHoveredSource = null;
4350
+ this.lastHoveredCatalogue = null;
4351
+ }
4352
+ clearGridLabelsForDomain(domain) {
4353
+ if (domain === "astronomy") {
4354
+ this._healpixGrid?.clearLabels();
4355
+ this._equatorialGrid?.clearLabels();
4356
+ return;
4357
+ }
4358
+ if (domain === "earth") {
4359
+ this._activeXYZ2?.clearLonLatGridLabels?.();
4360
+ }
4361
+ }
4362
+ get activeDomain() {
4363
+ return this._activeDomain;
4364
+ }
4365
+ getActivePointSets() {
4366
+ if (this._activeDomain === "astronomy")
4367
+ return this.astronomyCatalogues;
4368
+ if (this._activeDomain === "earth")
4369
+ return this.earthPointSets;
4370
+ return [];
4371
+ }
4372
+ getActiveFootprintSets() {
4373
+ if (this._activeDomain === "astronomy")
4374
+ return this.astronomyFootprintSets;
4375
+ if (this._activeDomain === "earth")
4376
+ return this.earthFootprintSets;
4377
+ return [];
4378
+ }
4379
+ // Astronomy catalogue ownership
4242
4380
  async showCatalogue(cat) {
4243
- // console.log(cat)
4244
- if (cat)
4245
- this.activeCatalogues.push(cat);
4381
+ if (cat && !this.astronomyCatalogues.includes(cat)) {
4382
+ this.astronomyCatalogues.push(cat);
4383
+ }
4246
4384
  return cat;
4247
4385
  }
4248
4386
  deleteCatalogue(catalogue) {
4249
- this.activeCatalogues = this.activeCatalogues.filter((c) => c !== catalogue);
4387
+ this.astronomyCatalogues = this.astronomyCatalogues.filter((c) => c !== catalogue);
4250
4388
  }
4251
- // End Catalogue section
4252
- // Footprint section
4389
+ // Earth point-set ownership. TerraPointSetGL still extends CatalogueGL in
4390
+ // 3.12, but it no longer shares the Astronomy lifecycle collection.
4391
+ async showTerraPointSet(pointSet) {
4392
+ if (pointSet && !this.earthPointSets.includes(pointSet)) {
4393
+ this.earthPointSets.push(pointSet);
4394
+ }
4395
+ return pointSet;
4396
+ }
4397
+ deleteTerraPointSet(pointSet) {
4398
+ this.earthPointSets = this.earthPointSets.filter((set) => set !== pointSet);
4399
+ }
4400
+ // Astronomy footprint ownership
4253
4401
  async showFootprintSet(fset) {
4254
- // console.log(fset)
4255
- if (fset)
4256
- this.activeFootprintSets.push(fset);
4402
+ if (fset && !this.astronomyFootprintSets.includes(fset)) {
4403
+ this.astronomyFootprintSets.push(fset);
4404
+ }
4257
4405
  return fset;
4258
4406
  }
4259
4407
  deleteFootprintSet(footprintSet) {
4260
- this.activeFootprintSets = this.activeFootprintSets.filter((fst) => fst !== footprintSet);
4408
+ this.astronomyFootprintSets = this.astronomyFootprintSets.filter((fst) => fst !== footprintSet);
4409
+ }
4410
+ // Earth footprint ownership. TerraFootprintSetGL still reuses the common
4411
+ // FootprintSetGL rendering implementation, but has an independent lifecycle.
4412
+ async showTerraFootprintSet(footprintSet) {
4413
+ if (footprintSet && !this.earthFootprintSets.includes(footprintSet)) {
4414
+ this.earthFootprintSets.push(footprintSet);
4415
+ }
4416
+ return footprintSet;
4417
+ }
4418
+ deleteTerraFootprintSet(footprintSet) {
4419
+ this.earthFootprintSets = this.earthFootprintSets.filter((fst) => fst !== footprintSet);
4261
4420
  }
4262
4421
  async showPolylineSet(polylineSet) {
4263
4422
  if (polylineSet)
@@ -4287,8 +4446,8 @@ class AstroSphere {
4287
4446
  satelliteObject.dispose();
4288
4447
  }
4289
4448
  getHoveredFootprints() {
4290
- let footprintsHovered = [];
4291
- this.activeFootprintSets.forEach((fset) => {
4449
+ const footprintsHovered = [];
4450
+ this.getActiveFootprintSets().forEach((fset) => {
4292
4451
  footprintsHovered.push(fset.hoveredFootprints);
4293
4452
  });
4294
4453
  return footprintsHovered;
@@ -4297,6 +4456,9 @@ class AstroSphere {
4297
4456
  goTo(raDeg, decDeg) {
4298
4457
  this._camera.goTo(raDeg, decDeg);
4299
4458
  }
4459
+ flyTo(raDeg, decDeg, durationMs = 1200) {
4460
+ this._camera.flyTo(raDeg, decDeg, durationMs);
4461
+ }
4300
4462
  getActiveCoordinateMode() {
4301
4463
  if (this._activeBaseLayer === "xyz") {
4302
4464
  return "lonlat";
@@ -4517,8 +4679,6 @@ class AstroSphere {
4517
4679
  return;
4518
4680
  if (!this._webgl)
4519
4681
  return;
4520
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
4521
- return;
4522
4682
  if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
4523
4683
  return;
4524
4684
  if (this._healpixGrid.fovObj === undefined)
@@ -4532,6 +4692,16 @@ class AstroSphere {
4532
4692
  let PHI = 0;
4533
4693
  this._webgl.viewport(0, 0, this._webgl.drawingBufferWidth, this._webgl.drawingBufferHeight);
4534
4694
  this._webgl.clear(this._webgl.COLOR_BUFFER_BIT | this._webgl.DEPTH_BUFFER_BIT);
4695
+ const now = performance.now();
4696
+ const cameraFlying = this._camera.updateFlyTo(now);
4697
+ if (cameraFlying) {
4698
+ this.inertiaX = 0;
4699
+ this.inertiaY = 0;
4700
+ this.lastCameraMotionAt = now;
4701
+ this._cameraStatusChanged = true;
4702
+ this.updateCentralPoint();
4703
+ 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);
4704
+ }
4535
4705
  // Zoom inertia
4536
4706
  if (this.zoomInertia !== 0) {
4537
4707
  if (Math.abs(this.zoomInertia) > 0.0001) {
@@ -4620,8 +4790,10 @@ class AstroSphere {
4620
4790
  this._webgl.enable(this._webgl.CULL_FACE);
4621
4791
  this._webgl.cullFace(Global_js_1.default.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
4622
4792
  this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
4623
- if (this._activeBaseLayer === "hips" && this._activeHiPSLayers.length > 0) {
4624
- const maxHiPSOrder = Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder));
4793
+ if (this._activeBaseLayer === "hips" && this._activeHiPS) {
4794
+ const maxHiPSOrder = this._activeHiPSLayers.length > 0
4795
+ ? Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder))
4796
+ : this._activeHiPS.maxOrder;
4625
4797
  const visibleOrder = Math.min(this._healpixGrid.visibleorder, maxHiPSOrder);
4626
4798
  this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
4627
4799
  }
@@ -4656,12 +4828,19 @@ class AstroSphere {
4656
4828
  }
4657
4829
  if (this._activeBaseLayer === "xyz") {
4658
4830
  this._activeXYZ2?.draw(skyEntityDrawInput);
4831
+ for (const overlay of this._earthRasterOverlays) {
4832
+ if (overlay.visible) {
4833
+ overlay.map.draw(skyEntityDrawInput, true);
4834
+ }
4835
+ }
4659
4836
  }
4660
4837
  if (this._activeBaseLayer === "meships") {
4661
4838
  this._activeMeshHiPS?.draw(skyEntityDrawInput);
4662
4839
  }
4663
- this._healpixGrid.draw(skyEntityDrawInput);
4664
- this._equatorialGrid.draw(skyEntityDrawInput);
4840
+ if (this._activeDomain === "astronomy") {
4841
+ this._healpixGrid.draw(skyEntityDrawInput);
4842
+ this._equatorialGrid.draw(skyEntityDrawInput);
4843
+ }
4665
4844
  this._webgl.enable(this._webgl.DEPTH_TEST);
4666
4845
  this._webgl.disable(this._webgl.CULL_FACE);
4667
4846
  if (this.startup) {
@@ -4680,52 +4859,53 @@ class AstroSphere {
4680
4859
  decDMS,
4681
4860
  });
4682
4861
  }
4683
- this.activeCatalogues.forEach((cat) => {
4684
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4685
- this._activeXYZ2?.getModelMatrix() ??
4686
- this._activeMeshHiPS?.getModelMatrix();
4862
+ this.getActivePointSets().forEach((cat) => {
4863
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4687
4864
  if (activeModelMatrix) {
4688
4865
  cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4689
4866
  }
4690
4867
  });
4691
4868
  this.emitHoveredSourceIfChanged();
4692
- this.activeFootprintSets.forEach((fst) => {
4693
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4694
- this._activeXYZ2?.getModelMatrix() ??
4695
- this._activeMeshHiPS?.getModelMatrix();
4869
+ this.getActiveFootprintSets().forEach((fst) => {
4870
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4696
4871
  if (activeModelMatrix) {
4697
4872
  fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4698
4873
  }
4699
4874
  });
4700
- this.activePolylineSets.forEach((polylineSet) => {
4701
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4702
- this._activeXYZ2?.getModelMatrix() ??
4703
- this._activeMeshHiPS?.getModelMatrix();
4704
- if (activeModelMatrix) {
4705
- polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4706
- }
4707
- });
4708
- this.activeSensorCones.forEach((sensorCone) => {
4709
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4710
- this._activeXYZ2?.getModelMatrix() ??
4711
- this._activeMeshHiPS?.getModelMatrix();
4712
- if (activeModelMatrix) {
4713
- sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4714
- }
4715
- });
4716
- this.activeSatelliteObjects.forEach((satelliteObject) => {
4717
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4718
- this._activeXYZ2?.getModelMatrix() ??
4719
- this._activeMeshHiPS?.getModelMatrix();
4720
- if (activeModelMatrix) {
4721
- satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4722
- }
4723
- });
4875
+ if (this._activeDomain === "earth") {
4876
+ this.activePolylineSets.forEach((polylineSet) => {
4877
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4878
+ if (activeModelMatrix) {
4879
+ polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4880
+ }
4881
+ });
4882
+ this.activeSensorCones.forEach((sensorCone) => {
4883
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4884
+ if (activeModelMatrix) {
4885
+ sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4886
+ }
4887
+ });
4888
+ this.activeSatelliteObjects.forEach((satelliteObject) => {
4889
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4890
+ if (activeModelMatrix) {
4891
+ satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4892
+ }
4893
+ });
4894
+ }
4895
+ }
4896
+ getActiveDomainModelMatrix() {
4897
+ if (this._activeDomain === "astronomy") {
4898
+ return (this._activeHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4899
+ }
4900
+ if (this._activeDomain === "earth") {
4901
+ return (this._activeXYZ2?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4902
+ }
4903
+ return (this._activeMeshHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4724
4904
  }
4725
4905
  emitHoveredSourceIfChanged() {
4726
4906
  let nextHoveredSource = null;
4727
4907
  let nextHoveredCatalogue = null;
4728
- for (const cat of this.activeCatalogues) {
4908
+ for (const cat of this.getActivePointSets()) {
4729
4909
  const hovered = cat.getPrimaryHoveredSource();
4730
4910
  if (!hovered)
4731
4911
  continue;
@@ -4854,25 +5034,25 @@ class AstroViewer {
4854
5034
  return new TerraPointSetGL_js_1.TerraPointSetGL(pointSetName, pointSetDescription, providerUrl, metadataManager, this.webgl, this.astroSphere.healpixGrid.visibleTilesManager);
4855
5035
  }
4856
5036
  showTerraPointSet(pointSet) {
4857
- this.astroSphere.showCatalogue(pointSet);
5037
+ this.astroSphere.showTerraPointSet(pointSet);
4858
5038
  }
4859
5039
  hideTerraPointSet(pointSet, isVisible) {
4860
5040
  pointSet.setIsVisible(isVisible);
4861
5041
  }
4862
5042
  deleteTerraPointSet(pointSet) {
4863
- this.astroSphere.deleteCatalogue(pointSet);
5043
+ this.astroSphere.deleteTerraPointSet(pointSet);
4864
5044
  }
4865
5045
  createTerraFootprintSet(footprintSetName, footprintSetDescription, providerUrl, metadataManager) {
4866
5046
  return new TerraFootprintSetGL_js_1.TerraFootprintSetGL(footprintSetName, footprintSetDescription, providerUrl, metadataManager, this.webgl);
4867
5047
  }
4868
5048
  showTerraFootprintSet(footprintSet) {
4869
- this.astroSphere.showFootprintSet(footprintSet);
5049
+ this.astroSphere.showTerraFootprintSet(footprintSet);
4870
5050
  }
4871
5051
  hideTerraFootprintSet(footprintSet, isVisible) {
4872
5052
  footprintSet.setIsVisible(isVisible);
4873
5053
  }
4874
5054
  deleteTerraFootprintSet(footprintSet) {
4875
- this.astroSphere.deleteFootprintSet(footprintSet);
5055
+ this.astroSphere.deleteTerraFootprintSet(footprintSet);
4876
5056
  }
4877
5057
  createTerraPolylineSet(polylineSetName, polylineSetDescription, providerUrl, metadataManager) {
4878
5058
  return new TerraPolylineSetGL_js_1.TerraPolylineSetGL(polylineSetName, polylineSetDescription, providerUrl, metadataManager, this.webgl);
@@ -4925,6 +5105,12 @@ class AstroViewer {
4925
5105
  getDefaultHiPSURL() {
4926
5106
  return Config_js_1.bootSetup.defaultHipsUrl;
4927
5107
  }
5108
+ setActiveDomain(domain) {
5109
+ this.astroSphere.setActiveDomain(domain);
5110
+ }
5111
+ getActiveDomain() {
5112
+ return this.astroSphere.activeDomain;
5113
+ }
4928
5114
  activateHiPS(hipsDescriptor) {
4929
5115
  this.astroSphere.activateHiPS(hipsDescriptor);
4930
5116
  }
@@ -4938,9 +5124,37 @@ class AstroViewer {
4938
5124
  activateWMTS(config) {
4939
5125
  this.astroSphere.activateWMTS(config);
4940
5126
  }
5127
+ addXYZRasterOverlay(config) {
5128
+ 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);
5129
+ return this.astroSphere.addXYZRasterOverlay(descriptor);
5130
+ }
5131
+ addWMTSRasterOverlay(config) {
5132
+ return this.astroSphere.addWMTSRasterOverlay(config);
5133
+ }
5134
+ getEarthRasterOverlays() {
5135
+ return this.astroSphere.getEarthRasterOverlays();
5136
+ }
5137
+ setEarthRasterOverlayOpacity(id, opacity) {
5138
+ this.astroSphere.setEarthRasterOverlayOpacity(id, opacity);
5139
+ }
5140
+ setEarthRasterOverlayVisible(id, visible) {
5141
+ this.astroSphere.setEarthRasterOverlayVisible(id, visible);
5142
+ }
5143
+ removeEarthRasterOverlay(id) {
5144
+ this.astroSphere.removeEarthRasterOverlay(id);
5145
+ }
5146
+ removeAllEarthRasterOverlays() {
5147
+ this.astroSphere.removeAllEarthRasterOverlays();
5148
+ }
4941
5149
  activateMeshHiPS(config) {
4942
5150
  this.astroSphere.activateMeshHiPS(new MeshHiPSDescriptor_js_1.MeshHiPSDescriptor(config));
4943
5151
  }
5152
+ setMeshHiPSColor(color) {
5153
+ this.astroSphere.setMeshHiPSColor(color);
5154
+ }
5155
+ setMeshHiPSWireframe(wireframe) {
5156
+ this.astroSphere.setMeshHiPSWireframe(wireframe);
5157
+ }
4944
5158
  setXYZMaxConcurrentRequests(value) {
4945
5159
  XYZTileRequestScheduler_js_1.xyzTileRequestScheduler.setMaxConcurrent(value);
4946
5160
  }
@@ -5063,6 +5277,9 @@ class AstroViewer {
5063
5277
  // console.log(`AstroViewer.goTo goto(${raDeg}, ${decDeg})`)
5064
5278
  this.astroSphere.goTo(raDeg, decDeg);
5065
5279
  }
5280
+ flyTo(raDeg, decDeg, durationMs = 1200) {
5281
+ this.astroSphere.flyTo(raDeg, decDeg, durationMs);
5282
+ }
5066
5283
  getActiveCoordinateMode() {
5067
5284
  return this.astroSphere.getActiveCoordinateMode();
5068
5285
  }
@@ -5385,6 +5602,7 @@ class Camera {
5385
5602
  lockRotX = false;
5386
5603
  lockRotY = false;
5387
5604
  lockRotZ = false;
5605
+ flyToAnimation = null;
5388
5606
  constructor(in_position, in_sphere) {
5389
5607
  this.init(in_position, in_sphere);
5390
5608
  }
@@ -5394,7 +5612,11 @@ class Camera {
5394
5612
  this.vMatrix = gl_matrix_1.mat4.create();
5395
5613
  this.T = gl_matrix_1.mat4.create();
5396
5614
  this.R = gl_matrix_1.mat4.create();
5397
- gl_matrix_1.mat4.translate(this.T, this.T, [this.cam_pos[0], this.cam_pos[1], this.cam_pos[2]]);
5615
+ gl_matrix_1.mat4.translate(this.T, this.T, [
5616
+ this.cam_pos[0],
5617
+ this.cam_pos[1],
5618
+ this.cam_pos[2],
5619
+ ]);
5398
5620
  // reset helpers
5399
5621
  this.FoV = this.previousFoV = 180.0;
5400
5622
  this.move = gl_matrix_1.vec3.clone([0, 0, 0]);
@@ -5403,11 +5625,15 @@ class Camera {
5403
5625
  this.goTo(raDeg, decDeg);
5404
5626
  }
5405
5627
  goTo(raDeg, decDeg) {
5628
+ this.cancelFlyTo();
5406
5629
  this.goToPhiTheta((0, Utils_js_1.astroDegToSpherical)(raDeg, decDeg));
5407
5630
  }
5408
5631
  goToPhiTheta(ptDeg) {
5409
5632
  const xyz = (0, Utils_js_1.sphericalToCartesian)(ptDeg.phi, ptDeg.theta, this.cam_pos[2]);
5410
- const targetDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), gl_matrix_1.vec3.fromValues(xyz[0], xyz[1], xyz[2]));
5633
+ this.applyCameraPosition(gl_matrix_1.vec3.fromValues(xyz[0], xyz[1], xyz[2]));
5634
+ }
5635
+ applyCameraPosition(cameraPosition) {
5636
+ const targetDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), cameraPosition);
5411
5637
  const celestialNorth = gl_matrix_1.vec3.fromValues(0.0, 0.0, 1.0);
5412
5638
  const northProjection = gl_matrix_1.vec3.scale(gl_matrix_1.vec3.create(), targetDirection, gl_matrix_1.vec3.dot(celestialNorth, targetDirection));
5413
5639
  const cameraUp = gl_matrix_1.vec3.subtract(gl_matrix_1.vec3.create(), celestialNorth, northProjection);
@@ -5418,9 +5644,13 @@ class Camera {
5418
5644
  gl_matrix_1.vec3.normalize(cameraUp, cameraUp);
5419
5645
  }
5420
5646
  let cameraMatrix = gl_matrix_1.mat4.create();
5421
- cameraMatrix = gl_matrix_1.mat4.translate(cameraMatrix, cameraMatrix, gl_matrix_1.vec3.fromValues(xyz[0], xyz[1], xyz[2]));
5647
+ cameraMatrix = gl_matrix_1.mat4.translate(cameraMatrix, cameraMatrix, cameraPosition);
5422
5648
  const focusPoint = [0.0, 0.0, 0.0];
5423
- const cameraPos = [cameraMatrix[12], cameraMatrix[13], cameraMatrix[14]];
5649
+ const cameraPos = [
5650
+ cameraMatrix[12],
5651
+ cameraMatrix[13],
5652
+ cameraMatrix[14],
5653
+ ];
5424
5654
  cameraMatrix = gl_matrix_1.mat4.targetTo(cameraMatrix, cameraPos, focusPoint, cameraUp);
5425
5655
  this.R = gl_matrix_1.mat4.clone(cameraMatrix);
5426
5656
  this.R[12] = 0;
@@ -5432,6 +5662,81 @@ class Camera {
5432
5662
  }
5433
5663
  this.vMatrix = viewMatrix;
5434
5664
  }
5665
+ flyTo(raDeg, decDeg, durationMs = 1200) {
5666
+ const targetSpherical = (0, Utils_js_1.astroDegToSpherical)(raDeg, decDeg);
5667
+ const targetPositionValues = (0, Utils_js_1.sphericalToCartesian)(targetSpherical.phi, targetSpherical.theta, this.cam_pos[2]);
5668
+ const targetPosition = gl_matrix_1.vec3.fromValues(targetPositionValues[0], targetPositionValues[1], targetPositionValues[2]);
5669
+ const currentPosition = this.getCameraPosition();
5670
+ const startDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), gl_matrix_1.vec3.fromValues(currentPosition[0], currentPosition[1], currentPosition[2]));
5671
+ const targetDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), targetPosition);
5672
+ this.flyToAnimation = {
5673
+ startDirection,
5674
+ targetDirection,
5675
+ targetPosition,
5676
+ startTime: performance.now(),
5677
+ durationMs: Math.max(1, durationMs),
5678
+ };
5679
+ }
5680
+ updateFlyTo(now) {
5681
+ const animation = this.flyToAnimation;
5682
+ if (!animation) {
5683
+ return false;
5684
+ }
5685
+ const elapsed = now - animation.startTime;
5686
+ const progress = Math.min(1, elapsed / animation.durationMs);
5687
+ const easedProgress = progress < 0.5
5688
+ ? 4 * progress * progress * progress
5689
+ : 1 - Math.pow(-2 * progress + 2, 3) / 2;
5690
+ const direction = this.slerpDirection(animation.startDirection, animation.targetDirection, easedProgress);
5691
+ const radius = Math.abs(this.cam_pos[2]);
5692
+ const cameraPosition = gl_matrix_1.vec3.scale(gl_matrix_1.vec3.create(), direction, radius);
5693
+ this.applyCameraPosition(cameraPosition);
5694
+ if (progress >= 1) {
5695
+ this.applyCameraPosition(animation.targetPosition);
5696
+ this.flyToAnimation = null;
5697
+ }
5698
+ return true;
5699
+ }
5700
+ slerpDirection(start, end, t) {
5701
+ const dot = Math.max(-1, Math.min(1, gl_matrix_1.vec3.dot(start, end)));
5702
+ /*
5703
+ * For almost identical directions, normalized linear interpolation is
5704
+ * numerically more stable than the general SLERP formula.
5705
+ */
5706
+ if (dot > 0.999999) {
5707
+ const result = gl_matrix_1.vec3.lerp(gl_matrix_1.vec3.create(), start, end, t);
5708
+ return gl_matrix_1.vec3.normalize(result, result);
5709
+ }
5710
+ /*
5711
+ * The normal SLERP formula becomes singular for antipodal vectors because
5712
+ * sin(pi) = 0. In that case choose a stable vector orthogonal to start and
5713
+ * rotate through that great circle.
5714
+ */
5715
+ if (dot < -0.999999) {
5716
+ const referenceAxis = Math.abs(start[0]) < 0.9
5717
+ ? gl_matrix_1.vec3.fromValues(1, 0, 0)
5718
+ : gl_matrix_1.vec3.fromValues(0, 1, 0);
5719
+ const orthogonal = gl_matrix_1.vec3.cross(gl_matrix_1.vec3.create(), start, referenceAxis);
5720
+ gl_matrix_1.vec3.normalize(orthogonal, orthogonal);
5721
+ const angle = Math.PI * t;
5722
+ const result = gl_matrix_1.vec3.create();
5723
+ gl_matrix_1.vec3.scale(result, start, Math.cos(angle));
5724
+ gl_matrix_1.vec3.scaleAndAdd(result, result, orthogonal, Math.sin(angle));
5725
+ return gl_matrix_1.vec3.normalize(result, result);
5726
+ }
5727
+ const angle = Math.acos(dot);
5728
+ const sinAngle = Math.sin(angle);
5729
+ const startWeight = Math.sin((1 - t) * angle) / sinAngle;
5730
+ const endWeight = Math.sin(t * angle) / sinAngle;
5731
+ const result = gl_matrix_1.vec3.create();
5732
+ result[0] = start[0] * startWeight + end[0] * endWeight;
5733
+ result[1] = start[1] * startWeight + end[1] * endWeight;
5734
+ result[2] = start[2] * startWeight + end[2] * endWeight;
5735
+ return gl_matrix_1.vec3.normalize(result, result);
5736
+ }
5737
+ cancelFlyTo() {
5738
+ this.flyToAnimation = null;
5739
+ }
5435
5740
  toggleInsideSphere() {
5436
5741
  // if (inside !== global.insideSphere) {
5437
5742
  // global.insideSphere = inside;
@@ -5653,6 +5958,22 @@ class Camera {
5653
5958
  }
5654
5959
  return [inv[12], inv[13], inv[14]];
5655
5960
  }
5961
+ getViewState() {
5962
+ return {
5963
+ position: [this.cam_pos[0], this.cam_pos[1], this.cam_pos[2]],
5964
+ viewMatrix: gl_matrix_1.mat4.clone(this.vMatrix),
5965
+ translationMatrix: gl_matrix_1.mat4.clone(this.T),
5966
+ rotationMatrix: gl_matrix_1.mat4.clone(this.R),
5967
+ };
5968
+ }
5969
+ restoreViewState(state) {
5970
+ this.cancelFlyTo();
5971
+ this.cam_pos = gl_matrix_1.vec3.fromValues(...state.position);
5972
+ this.vMatrix = gl_matrix_1.mat4.clone(state.viewMatrix);
5973
+ this.T = gl_matrix_1.mat4.clone(state.translationMatrix);
5974
+ this.R = gl_matrix_1.mat4.clone(state.rotationMatrix);
5975
+ this.move = gl_matrix_1.vec3.create();
5976
+ }
5656
5977
  setCameraMatrix(viewMatrix) {
5657
5978
  this.vMatrix = viewMatrix;
5658
5979
  }
@@ -5689,9 +6010,15 @@ class Camera {
5689
6010
  clearRotationLock() {
5690
6011
  this.lockRotX = this.lockRotY = this.lockRotZ = false;
5691
6012
  }
5692
- isRotationLockedX() { return this.lockRotX; }
5693
- isRotationLockedY() { return this.lockRotY; }
5694
- isRotationLockedZ() { return this.lockRotZ; }
6013
+ isRotationLockedX() {
6014
+ return this.lockRotX;
6015
+ }
6016
+ isRotationLockedY() {
6017
+ return this.lockRotY;
6018
+ }
6019
+ isRotationLockedZ() {
6020
+ return this.lockRotZ;
6021
+ }
5695
6022
  }
5696
6023
  exports["default"] = Camera;
5697
6024
 
@@ -5864,7 +6191,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5864
6191
  return (mod && mod.__esModule) ? mod : { "default": mod };
5865
6192
  };
5866
6193
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5867
- 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;
6194
+ 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;
5868
6195
  var AstroViewer_js_1 = __webpack_require__(772);
5869
6196
  Object.defineProperty(exports, "AstroViewer", ({ enumerable: true, get: function () { return AstroViewer_js_1.AstroViewer; } }));
5870
6197
  var HiPSDescriptor_js_1 = __webpack_require__(5087);
@@ -5905,6 +6232,9 @@ Object.defineProperty(exports, "SphericalOpts", ({ enumerable: true, get: functi
5905
6232
  Object.defineProperty(exports, "AstroOpts", ({ enumerable: true, get: function () { return Point_js_1.AstroOpts; } }));
5906
6233
  Object.defineProperty(exports, "PointInitOpts", ({ enumerable: true, get: function () { return Point_js_1.PointInitOpts; } }));
5907
6234
  Object.defineProperty(exports, "CartesianOpts", ({ enumerable: true, get: function () { return Point_js_1.CartesianOpts; } }));
6235
+ var Utils_js_1 = __webpack_require__(7930);
6236
+ Object.defineProperty(exports, "raHMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.raHMSToDeg; } }));
6237
+ Object.defineProperty(exports, "decDMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.decDMSToDeg; } }));
5908
6238
  var FoVUtils_js_1 = __webpack_require__(8083);
5909
6239
  Object.defineProperty(exports, "FoVUtils", ({ enumerable: true, get: function () { return FoVUtils_js_1.FoVUtils; } }));
5910
6240
  var CoordsType_js_1 = __webpack_require__(8145);
@@ -7903,6 +8233,7 @@ class Source {
7903
8233
  _h_pix;
7904
8234
  _shapesize;
7905
8235
  _brightnessFactor;
8236
+ _mediaStyle;
7906
8237
  /**
7907
8238
  * @param in_point Point.js (Cartesian/RA-Dec wrapper)
7908
8239
  * @param in_details Optional array of key/value metadata
@@ -7911,7 +8242,7 @@ class Source {
7911
8242
  this._point = in_point;
7912
8243
  this._details = in_details;
7913
8244
  this._shapesize = 16.0;
7914
- this._brightnessFactor = -99;
8245
+ this._brightnessFactor = 3.0;
7915
8246
  this.computeHealpixPixel();
7916
8247
  }
7917
8248
  getDetailByindex(index) {
@@ -7955,6 +8286,12 @@ class Source {
7955
8286
  set brightnessFactor(factor) {
7956
8287
  this._brightnessFactor = factor;
7957
8288
  }
8289
+ get mediaStyle() {
8290
+ return this._mediaStyle;
8291
+ }
8292
+ set mediaStyle(style) {
8293
+ this._mediaStyle = style;
8294
+ }
7958
8295
  }
7959
8296
  exports.Source = Source;
7960
8297
 
@@ -8182,6 +8519,9 @@ exports.SphereFoV = SphereFoV;
8182
8519
  *
8183
8520
  * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
8184
8521
  */
8522
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8523
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8524
+ };
8185
8525
  Object.defineProperty(exports, "__esModule", ({ value: true }));
8186
8526
  exports.CatalogueGL = void 0;
8187
8527
  const Source_js_1 = __webpack_require__(146);
@@ -8190,11 +8530,14 @@ const CoordsType_js_1 = __webpack_require__(8145);
8190
8530
  const Utils_js_1 = __webpack_require__(7930);
8191
8531
  const CatalogueShaderProgram_js_1 = __webpack_require__(3559);
8192
8532
  const MetadataManager_js_1 = __webpack_require__(5403);
8533
+ const gl_matrix_1 = __webpack_require__(3890);
8534
+ const healpix_1 = __webpack_require__(4851);
8535
+ const Global_js_1 = __importDefault(__webpack_require__(4382));
8193
8536
  class CatalogueGL {
8194
8537
  _kind = "CatalogueGL";
8195
8538
  static ELEM_SIZE = 6;
8196
8539
  static BYTES_X_ELEM = new Float32Array().BYTES_PER_ELEMENT;
8197
- static STANDARD_SHAPE_SIZE = 10.0;
8540
+ static STANDARD_SHAPE_SIZE = 16.0;
8198
8541
  static STANDARD_SHAPE_HUE = 3.0;
8199
8542
  _ready;
8200
8543
  _name;
@@ -8221,6 +8564,9 @@ class CatalogueGL {
8221
8564
  _providerUrl;
8222
8565
  _catalogueShaderProgram;
8223
8566
  _visibleTilesManager;
8567
+ _mediaOverlayHost = null;
8568
+ _mediaElements = [];
8569
+ _svgMediaDataUrls = new Map();
8224
8570
  constructor(catalogueName, catalogueDescription, providerUrl, metadataManager, webgl, visibleTilesManager) {
8225
8571
  this._webgl = webgl;
8226
8572
  this._ready = false;
@@ -8391,9 +8737,10 @@ class CatalogueGL {
8391
8737
  * @param in_data Rows of TAP results
8392
8738
  * @param columnsmeta TapMetadataList (unused here because `CatalogueProps` already holds indices)
8393
8739
  */
8394
- addSources(in_data, columnsmeta) {
8740
+ addSources(in_data, columnsmeta, options = {}) {
8395
8741
  this._ready = false;
8396
8742
  this._sources = [];
8743
+ this.clearMediaOverlay();
8397
8744
  this._metadataManager = new MetadataManager_js_1.MetadataManager(columnsmeta);
8398
8745
  // const raDataIndex = (this.catalogueProps.raColumn as any).index ?? (this.catalogueProps.raColumn as any)._index;
8399
8746
  // const decDataIndex = (this.catalogueProps.decColumn as any).index ?? (this.catalogueProps.decColumn as any)._index;
@@ -8407,6 +8754,10 @@ class CatalogueGL {
8407
8754
  decDeg: in_data[j][decDataIndex],
8408
8755
  }, CoordsType_js_1.CoordsType.ASTRO);
8409
8756
  const source = new Source_js_1.Source(point, in_data[j]);
8757
+ const mediaStyle = this.extractMediaStyle(in_data[j], columnsmeta, options.mediaColumns);
8758
+ if (mediaStyle) {
8759
+ source.mediaStyle = mediaStyle;
8760
+ }
8410
8761
  // Ensure optional fields exist
8411
8762
  source.shapeSize = source.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
8412
8763
  source.brightnessFactor = 3;
@@ -8426,11 +8777,226 @@ class CatalogueGL {
8426
8777
  this._ready = true;
8427
8778
  this._bufferInitialised = false;
8428
8779
  }
8780
+ getColumnIndex(columnsmeta, columnName) {
8781
+ if (!columnName)
8782
+ return -1;
8783
+ return (columnsmeta.find((column) => column.name === columnName)?.index ?? -1);
8784
+ }
8785
+ readCell(row, columnsmeta, columnName) {
8786
+ const idx = this.getColumnIndex(columnsmeta, columnName);
8787
+ return idx >= 0 ? row[idx] : undefined;
8788
+ }
8789
+ extractMediaStyle(row, columnsmeta, mediaColumns) {
8790
+ if (!mediaColumns?.src)
8791
+ return undefined;
8792
+ const rawSrc = this.readCell(row, columnsmeta, mediaColumns.src);
8793
+ const src = String(rawSrc ?? "").trim();
8794
+ if (!src)
8795
+ return undefined;
8796
+ const rawKind = String(this.readCell(row, columnsmeta, mediaColumns.type) ?? "sprite")
8797
+ .trim()
8798
+ .toLowerCase();
8799
+ const kind = rawKind === "model"
8800
+ ? "model"
8801
+ : rawKind === "image"
8802
+ ? "image"
8803
+ : rawKind === "icon"
8804
+ ? "icon"
8805
+ : rawKind === "circle"
8806
+ ? "circle"
8807
+ : "sprite";
8808
+ if (kind === "circle" || kind === "model")
8809
+ return undefined;
8810
+ const scale = Number(this.readCell(row, columnsmeta, mediaColumns.scale));
8811
+ const rotationDeg = Number(this.readCell(row, columnsmeta, mediaColumns.rotation));
8812
+ const opacity = Number(this.readCell(row, columnsmeta, mediaColumns.opacity));
8813
+ return {
8814
+ kind,
8815
+ src,
8816
+ scale: Number.isFinite(scale) && scale > 0 ? scale : undefined,
8817
+ rotationDeg: Number.isFinite(rotationDeg) ? rotationDeg : undefined,
8818
+ opacity: Number.isFinite(opacity)
8819
+ ? Math.max(0, Math.min(1, opacity))
8820
+ : undefined,
8821
+ };
8822
+ }
8429
8823
  clearSources() {
8430
8824
  this._sources = [];
8431
8825
  this.hoveredIndexes = [];
8432
8826
  this._healpixDensityMap.clear();
8433
8827
  this.vertexCataloguePosition = new Float32Array(0);
8828
+ this.clearMediaOverlay();
8829
+ }
8830
+ clearMediaOverlay() {
8831
+ this._mediaElements.forEach((el) => el.remove());
8832
+ this._mediaElements = [];
8833
+ if (this._mediaOverlayHost) {
8834
+ this._mediaOverlayHost.remove();
8835
+ this._mediaOverlayHost = null;
8836
+ }
8837
+ }
8838
+ ensureMediaOverlayHost() {
8839
+ const canvas = this._webgl?.canvas;
8840
+ const parent = canvas?.parentElement;
8841
+ if (!canvas || !parent)
8842
+ return null;
8843
+ const parentStyle = window.getComputedStyle(parent);
8844
+ if (parentStyle.position === "static") {
8845
+ parent.style.position = "relative";
8846
+ }
8847
+ if (!this._mediaOverlayHost) {
8848
+ const host = document.createElement("div");
8849
+ host.dataset.astroCatalogueMedia = "true";
8850
+ host.style.position = "absolute";
8851
+ host.style.left = `${canvas.offsetLeft}px`;
8852
+ host.style.top = `${canvas.offsetTop}px`;
8853
+ host.style.width = `${canvas.clientWidth}px`;
8854
+ host.style.height = `${canvas.clientHeight}px`;
8855
+ host.style.pointerEvents = "none";
8856
+ host.style.overflow = "hidden";
8857
+ host.style.zIndex = "4";
8858
+ canvas.style.position = canvas.style.position || "relative";
8859
+ canvas.style.zIndex = canvas.style.zIndex || "0";
8860
+ parent.appendChild(host);
8861
+ this._mediaOverlayHost = host;
8862
+ }
8863
+ else {
8864
+ this._mediaOverlayHost.style.left = `${canvas.offsetLeft}px`;
8865
+ this._mediaOverlayHost.style.top = `${canvas.offsetTop}px`;
8866
+ this._mediaOverlayHost.style.width = `${canvas.clientWidth}px`;
8867
+ this._mediaOverlayHost.style.height = `${canvas.clientHeight}px`;
8868
+ }
8869
+ return this._mediaOverlayHost;
8870
+ }
8871
+ ensureMediaElement(index, style) {
8872
+ let img = this._mediaElements[index];
8873
+ if (!img) {
8874
+ img = document.createElement("img");
8875
+ img.loading = "lazy";
8876
+ img.decoding = "async";
8877
+ img.style.position = "absolute";
8878
+ img.style.objectFit = "contain";
8879
+ img.style.pointerEvents = "none";
8880
+ img.style.transformOrigin = "center center";
8881
+ img.style.filter = "drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55))";
8882
+ img.style.willChange = "transform, left, top";
8883
+ this._mediaElements[index] = img;
8884
+ }
8885
+ const src = String(style.src ?? "").trim();
8886
+ const resolvedSrc = src ? new URL(src, document.baseURI).href : "";
8887
+ if (resolvedSrc && img.getAttribute("src") !== resolvedSrc) {
8888
+ img.removeAttribute("data-astro-media-src-index");
8889
+ img.removeAttribute("data-astro-media-loaded");
8890
+ img.onerror = null;
8891
+ img.onload = null;
8892
+ img.setAttribute("src", resolvedSrc);
8893
+ if (/\.svg(?:[?#].*)?$/i.test(resolvedSrc)) {
8894
+ void this.loadSvgMediaAsDataUrl(img, resolvedSrc);
8895
+ }
8896
+ }
8897
+ return img;
8898
+ }
8899
+ async loadSvgMediaAsDataUrl(img, resolvedSrc) {
8900
+ try {
8901
+ let dataUrl = this._svgMediaDataUrls.get(resolvedSrc);
8902
+ if (!dataUrl) {
8903
+ const response = await fetch(resolvedSrc);
8904
+ if (!response.ok)
8905
+ return;
8906
+ const svg = await response.text();
8907
+ if (!/^\s*<svg[\s>]/i.test(svg))
8908
+ return;
8909
+ dataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
8910
+ this._svgMediaDataUrls.set(resolvedSrc, dataUrl);
8911
+ }
8912
+ if (img.getAttribute("src") === resolvedSrc) {
8913
+ img.setAttribute("src", dataUrl);
8914
+ }
8915
+ }
8916
+ catch {
8917
+ // Keep the direct image src; remote servers may already provide a valid MIME type.
8918
+ }
8919
+ }
8920
+ isSourceOnVisibleHemisphere(source, in_mMatrix, vMatrix) {
8921
+ if (Global_js_1.default.insideSphere)
8922
+ return true;
8923
+ const invView = gl_matrix_1.mat4.create();
8924
+ if (!gl_matrix_1.mat4.invert(invView, vMatrix))
8925
+ return true;
8926
+ const cameraPos = gl_matrix_1.vec3.fromValues(invView[12], invView[13], invView[14]);
8927
+ const worldPoint4 = gl_matrix_1.vec4.fromValues(source.point.x, source.point.y, source.point.z, 1);
8928
+ gl_matrix_1.vec4.transformMat4(worldPoint4, worldPoint4, in_mMatrix);
8929
+ const worldPoint = gl_matrix_1.vec3.fromValues(worldPoint4[0], worldPoint4[1], worldPoint4[2]);
8930
+ const normal = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), worldPoint);
8931
+ const toCamera = gl_matrix_1.vec3.subtract(gl_matrix_1.vec3.create(), cameraPos, worldPoint);
8932
+ return gl_matrix_1.vec3.dot(normal, toCamera) > 0;
8933
+ }
8934
+ updateMediaOverlay(in_mMatrix, vMatrix, pMatrix) {
8935
+ const sourcesWithMedia = this._sources.filter((source) => !!source.mediaStyle?.src);
8936
+ if (!sourcesWithMedia.length) {
8937
+ this.clearMediaOverlay();
8938
+ return;
8939
+ }
8940
+ const host = this.ensureMediaOverlayHost();
8941
+ const canvas = this._webgl.canvas;
8942
+ if (!host || !canvas.clientWidth || !canvas.clientHeight)
8943
+ return;
8944
+ const modelView = gl_matrix_1.mat4.create();
8945
+ const mvp = gl_matrix_1.mat4.create();
8946
+ gl_matrix_1.mat4.multiply(modelView, vMatrix, in_mMatrix);
8947
+ gl_matrix_1.mat4.multiply(mvp, pMatrix, modelView);
8948
+ const width = canvas.clientWidth;
8949
+ const height = canvas.clientHeight;
8950
+ for (let i = 0; i < this._sources.length; i++) {
8951
+ const source = this._sources[i];
8952
+ const style = source.mediaStyle;
8953
+ const img = this._mediaElements[i];
8954
+ if (!style?.src) {
8955
+ if (img)
8956
+ img.style.display = "none";
8957
+ continue;
8958
+ }
8959
+ if (!this.isSourceOnVisibleHemisphere(source, in_mMatrix, vMatrix)) {
8960
+ if (img)
8961
+ img.style.display = "none";
8962
+ continue;
8963
+ }
8964
+ const projected = gl_matrix_1.vec4.fromValues(source.point.x, source.point.y, source.point.z, 1);
8965
+ gl_matrix_1.vec4.transformMat4(projected, projected, mvp);
8966
+ if (!projected[3]) {
8967
+ if (img)
8968
+ img.style.display = "none";
8969
+ continue;
8970
+ }
8971
+ const ndcX = projected[0] / projected[3];
8972
+ const ndcY = projected[1] / projected[3];
8973
+ const ndcZ = projected[2] / projected[3];
8974
+ if (ndcX < -1.15 ||
8975
+ ndcX > 1.15 ||
8976
+ ndcY < -1.15 ||
8977
+ ndcY > 1.15 ||
8978
+ ndcZ < -1.15 ||
8979
+ ndcZ > 1.15) {
8980
+ if (img)
8981
+ img.style.display = "none";
8982
+ continue;
8983
+ }
8984
+ const mediaEl = this.ensureMediaElement(i, style);
8985
+ if (!mediaEl.parentElement)
8986
+ host.appendChild(mediaEl);
8987
+ const px = (ndcX * 0.5 + 0.5) * width;
8988
+ const py = (1 - (ndcY * 0.5 + 0.5)) * height;
8989
+ const baseSize = source.shapeSize || CatalogueGL.STANDARD_SHAPE_SIZE;
8990
+ const size = Math.max(8, baseSize * (style.scale ?? 2.4));
8991
+ const rotation = style.rotationDeg ?? 0;
8992
+ mediaEl.style.display = "block";
8993
+ mediaEl.style.left = `${px - size / 2}px`;
8994
+ mediaEl.style.top = `${py - size / 2}px`;
8995
+ mediaEl.style.width = `${size}px`;
8996
+ mediaEl.style.height = `${size}px`;
8997
+ mediaEl.style.opacity = `${style.opacity ?? 1}`;
8998
+ mediaEl.style.transform = `rotate(${rotation}deg)`;
8999
+ }
8434
9000
  }
8435
9001
  sourceMatches(left, right) {
8436
9002
  if (left === right)
@@ -8542,33 +9108,46 @@ class CatalogueGL {
8542
9108
  this._webgl.bufferData(this._webgl.ARRAY_BUFFER, this.vertexCataloguePosition, this._webgl.STATIC_DRAW);
8543
9109
  this._bufferInitialised = true;
8544
9110
  }
8545
- getSelectionRadius() {
8546
- const order = this._visibleTilesManager.getVisibleOrder();
8547
- switch (order) {
8548
- case 0:
8549
- case 1:
8550
- case 2:
8551
- // return 0.005;
8552
- return 0.01;
8553
- case 3:
8554
- // return 0.001;
8555
- case 4:
8556
- // return 0.0009;
8557
- case 5:
8558
- // return 0.0005;
8559
- return 0.005;
8560
- case 6:
8561
- // return 0.0001;
8562
- case 7:
8563
- // return 0.00009;
8564
- case 8:
8565
- // return 0.00005;
8566
- return 0.001;
8567
- case 9:
8568
- return 0.0005;
8569
- default:
8570
- return 0.0001;
9111
+ static MIN_HIT_RADIUS_PX = 6;
9112
+ static HIT_PADDING_PX = 3;
9113
+ static CANDIDATE_RADIUS_PX = 16;
9114
+ getCanvas() {
9115
+ return this._webgl.canvas;
9116
+ }
9117
+ getHitRadiusPx(source) {
9118
+ const markerSize = source.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
9119
+ return Math.max(CatalogueGL.MIN_HIT_RADIUS_PX, markerSize / 2 + CatalogueGL.HIT_PADDING_PX);
9120
+ }
9121
+ getCandidateSearchRadiusRad(pMatrix) {
9122
+ const canvas = this._webgl.canvas;
9123
+ const width = Math.max(1, canvas.clientWidth);
9124
+ const height = Math.max(1, canvas.clientHeight);
9125
+ // pMatrix[0] and pMatrix[5] are the perspective scale factors for X/Y.
9126
+ // Convert a small screen-space radius to a conservative angular radius.
9127
+ const fovXRad = 2 * Math.atan(1 / Math.abs(pMatrix[0]));
9128
+ const fovYRad = 2 * Math.atan(1 / Math.abs(pMatrix[5]));
9129
+ const radPerPixel = Math.max(fovXRad / width, fovYRad / height);
9130
+ return CatalogueGL.CANDIDATE_RADIUS_PX * radPerPixel;
9131
+ }
9132
+ projectPointToScreen(point, mvp, width, height) {
9133
+ const projected = gl_matrix_1.vec4.fromValues(point.x, point.y, point.z, 1);
9134
+ gl_matrix_1.vec4.transformMat4(projected, projected, mvp);
9135
+ if (projected[3] <= 0)
9136
+ return null;
9137
+ const ndcX = projected[0] / projected[3];
9138
+ const ndcY = projected[1] / projected[3];
9139
+ return [(ndcX * 0.5 + 0.5) * width, (1 - (ndcY * 0.5 + 0.5)) * height];
9140
+ }
9141
+ clearSelection() {
9142
+ for (const idx of this.selectedIndexes) {
9143
+ const base = idx * CatalogueGL.ELEM_SIZE;
9144
+ if (base + 4 >= this.vertexCataloguePosition.length)
9145
+ continue;
9146
+ this.vertexCataloguePosition[base + 3] = 0.0;
9147
+ this.vertexCataloguePosition[base + 4] =
9148
+ this._sources[idx]?.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
8571
9149
  }
9150
+ this.selectedIndexes = [];
8572
9151
  }
8573
9152
  setSelectedIndexes(selectedIndex) {
8574
9153
  selectedIndex.forEach((idx) => {
@@ -8595,8 +9174,8 @@ class CatalogueGL {
8595
9174
  * Run click-picking and update selection with the nearest candidate in current pixel.
8596
9175
  * Returns the selected source or null if no source was hit.
8597
9176
  */
8598
- getSourcesFromPointer(in_mouseHelper) {
8599
- const pickedIndexes = this.checkClicking(in_mouseHelper);
9177
+ getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9178
+ const pickedIndexes = this.checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8600
9179
  if (!pickedIndexes.length) {
8601
9180
  return {
8602
9181
  sources: [],
@@ -8615,8 +9194,8 @@ class CatalogueGL {
8615
9194
  * Run click-picking and update selection with the nearest candidate in current pixel.
8616
9195
  * Returns the selected source or null if no source was hit.
8617
9196
  */
8618
- selectPrimarySourceFromClick(in_mouseHelper) {
8619
- const picked = this.getSourcesFromPointer(in_mouseHelper);
9197
+ selectPrimarySourceFromClick(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9198
+ const picked = this.getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8620
9199
  const clickedIndexes = picked?.pickedIndexes ?? [];
8621
9200
  // if (!clickedIndexes.length) {
8622
9201
  // this.setSelectedIndexes([]);
@@ -8652,42 +9231,65 @@ class CatalogueGL {
8652
9231
  const idx = this.hoveredIndexes[0];
8653
9232
  return this._sources[idx] ?? null;
8654
9233
  }
8655
- findNearestSourceIndex(in_mouseHelper) {
8656
- if (in_mouseHelper.x == null ||
8657
- in_mouseHelper.y == null ||
8658
- in_mouseHelper.z == null) {
9234
+ findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9235
+ if (in_mouseHelper.xyz == null)
8659
9236
  return null;
9237
+ const healpix = Global_js_1.default.getHealpix(Global_js_1.default.selectionOrder);
9238
+ const mouseVec = new healpix_1.Vec3(...in_mouseHelper.xyz);
9239
+ const mousePointing = new healpix_1.Pointing(mouseVec, false);
9240
+ const searchRadiusRad = this.getCandidateSearchRadiusRad(pMatrix);
9241
+ const rangeSet = healpix.queryDiscInclusive(mousePointing, searchRadiusRad, 4);
9242
+ const pixels = rangeSet.r;
9243
+ const candidateIndexes = new Set();
9244
+ for (const pixel of pixels) {
9245
+ const indexes = this._healpixDensityMap.get(pixel);
9246
+ if (!indexes)
9247
+ continue;
9248
+ indexes.forEach((idx) => candidateIndexes.add(idx));
8660
9249
  }
8661
- const mousePix = in_mouseHelper.computeNpix();
8662
- if (mousePix == null)
9250
+ if (!candidateIndexes.size)
9251
+ return null;
9252
+ const canvas = this._webgl.canvas;
9253
+ const width = canvas.clientWidth;
9254
+ const height = canvas.clientHeight;
9255
+ if (!width || !height)
8663
9256
  return null;
8664
- const candidates = this._healpixDensityMap.get(mousePix);
8665
- if (!candidates?.length)
9257
+ const modelView = gl_matrix_1.mat4.create();
9258
+ const mvp = gl_matrix_1.mat4.create();
9259
+ gl_matrix_1.mat4.multiply(modelView, vMatrix, in_mMatrix);
9260
+ gl_matrix_1.mat4.multiply(mvp, pMatrix, modelView);
9261
+ const mousePoint = new Point_js_1.Point({
9262
+ x: in_mouseHelper.xyz[0],
9263
+ y: in_mouseHelper.xyz[1],
9264
+ z: in_mouseHelper.xyz[2],
9265
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
9266
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
9267
+ if (!mouseScreen)
8666
9268
  return null;
8667
- const selR = this.getSelectionRadius();
8668
9269
  let nearestIndex = null;
8669
- let nearestDistance = Number.POSITIVE_INFINITY;
8670
- for (const sourceIdx of candidates) {
9270
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
9271
+ for (const sourceIdx of candidateIndexes) {
8671
9272
  const source = this._sources[sourceIdx];
8672
9273
  if (!source)
8673
9274
  continue;
8674
- const dx = source.point.x - in_mouseHelper.x;
8675
- const dy = source.point.y - in_mouseHelper.y;
8676
- const dz = source.point.z - in_mouseHelper.z;
8677
- const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
8678
- if (dist <= selR && dist < nearestDistance) {
8679
- nearestDistance = dist;
9275
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
9276
+ if (!sourceScreen)
9277
+ continue;
9278
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
9279
+ if (distancePx <= this.getHitRadiusPx(source) &&
9280
+ distancePx < nearestDistancePx) {
9281
+ nearestDistancePx = distancePx;
8680
9282
  nearestIndex = sourceIdx;
8681
9283
  }
8682
9284
  }
8683
9285
  return nearestIndex;
8684
9286
  }
8685
- checkClicking(in_mouseHelper) {
8686
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9287
+ checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9288
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8687
9289
  return nearestIndex == null ? [] : [nearestIndex];
8688
9290
  }
8689
- checkHovering(in_mouseHelper) {
8690
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9291
+ checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9292
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8691
9293
  return nearestIndex == null ? [] : [nearestIndex];
8692
9294
  }
8693
9295
  /**
@@ -8723,6 +9325,13 @@ class CatalogueGL {
8723
9325
  if (this._catalogueShaderProgram.locations.color) {
8724
9326
  this._webgl.uniform4f(this._catalogueShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], 1.0);
8725
9327
  }
9328
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
9329
+ if (this._catalogueShaderProgram.locations.hoverColor) {
9330
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.hoverColor, ...interactionColors.hover, 1.0);
9331
+ }
9332
+ if (this._catalogueShaderProgram.locations.selectedColor) {
9333
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.selectedColor, ...interactionColors.selected, 1.0);
9334
+ }
8726
9335
  // selected flags
8727
9336
  for (let s = 0; s < this.selectedIndexes.length; s++) {
8728
9337
  const idx = this.selectedIndexes[s];
@@ -8733,7 +9342,8 @@ class CatalogueGL {
8733
9342
  // clear old hovered
8734
9343
  for (let k = 0; k < this.hoveredIndexes.length; k++) {
8735
9344
  const base = this.hoveredIndexes[k] * CatalogueGL.ELEM_SIZE;
8736
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9345
+ if (this.selectedIndexes.includes(this.hoveredIndexes[k]))
9346
+ continue;
8737
9347
  this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
8738
9348
  this.vertexCataloguePosition[base + 4] =
8739
9349
  this._sources[this.hoveredIndexes[k]].shapeSize; // size
@@ -8747,7 +9357,7 @@ class CatalogueGL {
8747
9357
  // this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
8748
9358
  // this.vertexCataloguePosition[base + 4] = this._sources[this.hoveredIndexes[k]].shapeSize; // size
8749
9359
  // }
8750
- this.hoveredIndexes = this.checkHovering(in_mouseHelper);
9360
+ this.hoveredIndexes = this.checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8751
9361
  // // new hovered
8752
9362
  // for (let i = 0; i < this.hoveredIndexes.length; i++) {
8753
9363
  // const idx = this.hoveredIndexes[i];
@@ -8761,7 +9371,8 @@ class CatalogueGL {
8761
9371
  for (let i = 0; i < this.hoveredIndexes.length; i++) {
8762
9372
  const idx = this.hoveredIndexes[i];
8763
9373
  const base = idx * CatalogueGL.ELEM_SIZE;
8764
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9374
+ if (this.selectedIndexes.includes(idx))
9375
+ continue;
8765
9376
  this.vertexCataloguePosition[base + 3] = 1.0; // hovered
8766
9377
  this.vertexCataloguePosition[base + 4] = this._sources[idx].shapeSize; // size
8767
9378
  }
@@ -8770,6 +9381,7 @@ class CatalogueGL {
8770
9381
  // draw
8771
9382
  const numItems = this.vertexCataloguePosition.length / CatalogueGL.ELEM_SIZE;
8772
9383
  this._webgl.drawArrays(this._webgl.POINTS, 0, numItems);
9384
+ this.updateMediaOverlay(in_mMatrix, vMatrix, pMatrix);
8773
9385
  this._oldMouseCoords = in_mouseHelper.xyz;
8774
9386
  }
8775
9387
  }
@@ -9168,6 +9780,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9168
9780
  _latLonGrid;
9169
9781
  _colorMapIdx = 0;
9170
9782
  _colorMap = ColorMaps_js_1.ColorMaps['native'];
9783
+ _opacity = 1;
9171
9784
  constructor(radius, position, xrad, yrad, descriptor, webgl, gridLabelContainers) {
9172
9785
  super(radius, position, xrad, yrad, descriptor.name, webgl, false);
9173
9786
  this._descriptor = descriptor;
@@ -9182,6 +9795,10 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9182
9795
  const fov = 180;
9183
9796
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9184
9797
  }
9798
+ setOpacity(opacity) {
9799
+ this._opacity = Math.min(1, Math.max(0, opacity));
9800
+ this._xyzShaderProgram.setOpacity(this._opacity);
9801
+ }
9185
9802
  changeColorMap(colorMap) {
9186
9803
  this._colorMap = colorMap;
9187
9804
  switch (colorMap.name) {
@@ -9229,6 +9846,9 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9229
9846
  initShaders() {
9230
9847
  this._xyzShaderProgram.enableProgram();
9231
9848
  }
9849
+ clearLonLatGridLabels() {
9850
+ this._latLonGrid.clearLabels();
9851
+ }
9232
9852
  isLonLatGridVisible() {
9233
9853
  return this._latLonGrid.isVisible();
9234
9854
  }
@@ -9243,7 +9863,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9243
9863
  // this._zoom = this.resolveVisibleZoom(fov)
9244
9864
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9245
9865
  }
9246
- draw(input) {
9866
+ draw(input, asOverlay = false) {
9247
9867
  const vMatrix = input.camera.getCameraMatrix();
9248
9868
  if (!vMatrix)
9249
9869
  return;
@@ -9253,6 +9873,18 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9253
9873
  this.refresh(input);
9254
9874
  const mMatrix = this.getModelMatrix();
9255
9875
  this._xyzShaderProgram.setRuntimeColorMap(this._colorMap);
9876
+ this._xyzShaderProgram.setOpacity(this._opacity);
9877
+ const depthTestWasEnabled = this._webgl.isEnabled(this._webgl.DEPTH_TEST);
9878
+ const blendWasEnabled = this._webgl.isEnabled(this._webgl.BLEND);
9879
+ const blendSrcRgb = this._webgl.getParameter(this._webgl.BLEND_SRC_RGB);
9880
+ const blendDstRgb = this._webgl.getParameter(this._webgl.BLEND_DST_RGB);
9881
+ const blendSrcAlpha = this._webgl.getParameter(this._webgl.BLEND_SRC_ALPHA);
9882
+ const blendDstAlpha = this._webgl.getParameter(this._webgl.BLEND_DST_ALPHA);
9883
+ if (asOverlay) {
9884
+ this._webgl.disable(this._webgl.DEPTH_TEST);
9885
+ this._webgl.enable(this._webgl.BLEND);
9886
+ this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
9887
+ }
9256
9888
  const tileSelection = this._visibleTilesManager.computeVisibleTiles(this._zoom, this, this._webgl, input.camera, input.pMatrix);
9257
9889
  const visibleTiles = tileSelection.visibleTiles;
9258
9890
  const ancestorsMap = tileSelection.ancestorsMap;
@@ -9270,7 +9902,20 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9270
9902
  const ancestorTile = this.findBestAvailableAncestor(tile.coord);
9271
9903
  ancestorTile?.draw(tileSelection.currentZoom, [tile.coord], ancestorsMap, pMatrix, vMatrix, mMatrix, this._colorMapIdx);
9272
9904
  }
9273
- this._latLonGrid.draw(input);
9905
+ if (asOverlay) {
9906
+ if (depthTestWasEnabled)
9907
+ this._webgl.enable(this._webgl.DEPTH_TEST);
9908
+ else
9909
+ this._webgl.disable(this._webgl.DEPTH_TEST);
9910
+ this._webgl.blendFuncSeparate(blendSrcRgb, blendDstRgb, blendSrcAlpha, blendDstAlpha);
9911
+ if (blendWasEnabled)
9912
+ this._webgl.enable(this._webgl.BLEND);
9913
+ else
9914
+ this._webgl.disable(this._webgl.BLEND);
9915
+ }
9916
+ else {
9917
+ this._latLonGrid.draw(input);
9918
+ }
9274
9919
  }
9275
9920
  createTile(coord) {
9276
9921
  return new XYZAnchestorTile_js_1.XYZAnchestorTile(coord, this.resolveTileUrl(coord), this._webgl, this._xyzShaderProgram, this._meshBuilder, this._descriptor.segmentsPerSide);
@@ -10564,6 +11209,10 @@ class FootprintSetGL {
10564
11209
  }
10565
11210
  return clickedIndexes;
10566
11211
  }
11212
+ clearSelection() {
11213
+ this.selectedIndexes = [];
11214
+ this.refreshSelectedFootprints();
11215
+ }
10567
11216
  setSelectedIndexes(selectedIndex) {
10568
11217
  selectedIndex.forEach((idx) => {
10569
11218
  if (idx < 0 || idx >= this.footprintPolygons.length)
@@ -10865,9 +11514,10 @@ class FootprintSetGL {
10865
11514
  if (in_mouseHelper != null && in_mouseHelper.xyz != this.oldMouseCoords) {
10866
11515
  this.checkSelection(in_mouseHelper);
10867
11516
  }
11517
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
10868
11518
  if (this._hoveredFootprints.length > 0) {
10869
11519
  // TODO POINT_SIZE doesn't have any effect on line thickness!! it only applies to points
10870
- const rgb = (0, Utils_js_1.colorHex2RGB)("#00FF00");
11520
+ const rgb = interactionColors.hover;
10871
11521
  const alpha = 1.0;
10872
11522
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
10873
11523
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -10882,7 +11532,7 @@ class FootprintSetGL {
10882
11532
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.hoveredVertexPosition.length / 3 + this.nHoveredPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
10883
11533
  }
10884
11534
  if (this._selectedFootprints.length > 0) {
10885
- const rgb = (0, Utils_js_1.colorHex2RGB)("#ECB462");
11535
+ const rgb = interactionColors.selected;
10886
11536
  const alpha = 1.0;
10887
11537
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
10888
11538
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -10895,6 +11545,11 @@ class FootprintSetGL {
10895
11545
  this._webgl.bufferData(this._webgl.ELEMENT_ARRAY_BUFFER, this.selectedElementIndexes, this._webgl.STATIC_DRAW);
10896
11546
  // this._gl.drawElements (this._gl.LINE_LOOP, this._selectedVertexPosition.length / 3 + this._nSlectedPrimitiveFlags,this._gl.UNSIGNED_SHORT, 0);
10897
11547
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11548
+ // Keep selection visually distinct from hover without relying on
11549
+ // non-portable line widths. Reuse the selected geometry as small
11550
+ // vertex markers; POINT_SIZE is supported for POINTS in WebGL2.
11551
+ this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 4.0);
11552
+ this._webgl.drawElements(this._webgl.POINTS, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
10898
11553
  }
10899
11554
  this._webgl.bindBuffer(this._webgl.ARRAY_BUFFER, this.vertexCataloguePositionBuffer);
10900
11555
  this._webgl.vertexAttribPointer(this._footprintShaderProgram.locations.position, FootprintSetGL.ELEM_SIZE, this._webgl.FLOAT, false, FootprintSetGL.BYTES_X_ELEM * FootprintSetGL.ELEM_SIZE, 0);
@@ -11128,6 +11783,9 @@ class EquatorialGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11128
11783
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
11129
11784
  }
11130
11785
  }
11786
+ clearLabels() {
11787
+ this.gridText.resetDivSets();
11788
+ }
11131
11789
  isVisible() {
11132
11790
  return this.showGrid;
11133
11791
  }
@@ -11724,6 +12382,9 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11724
12382
  this.gridText.resetDivSets();
11725
12383
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
11726
12384
  }
12385
+ clearLabels() {
12386
+ this.gridText.resetDivSets();
12387
+ }
11727
12388
  get visibleorder() {
11728
12389
  return this._visibleorder;
11729
12390
  }
@@ -11958,6 +12619,9 @@ class LatLonGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11958
12619
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
11959
12620
  }
11960
12621
  }
12622
+ clearLabels() {
12623
+ this.gridText.resetDivSets();
12624
+ }
11961
12625
  draw(input) {
11962
12626
  if (!this._showGrid) {
11963
12627
  this.gridText.resetDivSets();
@@ -14626,6 +15290,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14626
15290
  _currentOrder;
14627
15291
  _visibleTiles = [];
14628
15292
  _coverageTiles = [];
15293
+ _color;
15294
+ _wireframe;
14629
15295
  constructor(radius, position, xrad, yrad, _descriptor, webgl, _healpixGrid) {
14630
15296
  super(radius, position, xrad, yrad, _descriptor.name, webgl, false);
14631
15297
  this._descriptor = _descriptor;
@@ -14634,6 +15300,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14634
15300
  this._shaderProgram = new MeshHiPSShaderProgram_js_1.MeshHiPSShaderProgram(webgl);
14635
15301
  this._shaderProgram.enableProgram();
14636
15302
  this._currentOrder = _descriptor.selectedOrder;
15303
+ this._color = [..._descriptor.color];
15304
+ this._wireframe = _descriptor.wireframe;
14637
15305
  }
14638
15306
  get currentOrder() {
14639
15307
  return this._currentOrder;
@@ -14656,6 +15324,12 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14656
15324
  getProperty(key) {
14657
15325
  return this._descriptor.getProperty(key);
14658
15326
  }
15327
+ setColor(color) {
15328
+ this._color = [...color];
15329
+ }
15330
+ setWireframe(wireframe) {
15331
+ this._wireframe = wireframe;
15332
+ }
14659
15333
  refreshOrder(fovDeg) {
14660
15334
  if (this._descriptor.fixedOrder) {
14661
15335
  this._currentOrder = this._descriptor.selectedOrder;
@@ -14695,7 +15369,7 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14695
15369
  for (const coord of byOrder.get(order) ?? []) {
14696
15370
  this._tiles
14697
15371
  .get(this.tileKey(coord))
14698
- ?.draw(pMatrix, vMatrix, mMatrix, this._descriptor.color, this._descriptor.wireframe);
15372
+ ?.draw(pMatrix, vMatrix, mMatrix, this._color, this._wireframe);
14699
15373
  }
14700
15374
  }
14701
15375
  if (wasCullFace)
@@ -16486,6 +17160,7 @@ exports.TerraFootprintSetGL = TerraFootprintSetGL;
16486
17160
  Object.defineProperty(exports, "__esModule", ({ value: true }));
16487
17161
  exports.TerraPointSetGL = void 0;
16488
17162
  const CatalogueGL_js_1 = __webpack_require__(1232);
17163
+ const gl_matrix_1 = __webpack_require__(3890);
16489
17164
  const MetadataManager_js_1 = __webpack_require__(5403);
16490
17165
  const Point_js_1 = __webpack_require__(6553);
16491
17166
  const CoordsType_js_1 = __webpack_require__(8145);
@@ -16520,8 +17195,45 @@ class TerraPointSetGL extends CatalogueGL_js_1.CatalogueGL {
16520
17195
  this._ready = true;
16521
17196
  this._bufferInitialised = false;
16522
17197
  }
17198
+ findNearestSourceIndex(inMouseHelper, inMatrix, viewMatrix, projectionMatrix) {
17199
+ if (inMouseHelper.xyz == null)
17200
+ return null;
17201
+ const canvas = this.getCanvas();
17202
+ const width = canvas.clientWidth;
17203
+ const height = canvas.clientHeight;
17204
+ if (!width || !height)
17205
+ return null;
17206
+ const modelView = gl_matrix_1.mat4.create();
17207
+ const mvp = gl_matrix_1.mat4.create();
17208
+ gl_matrix_1.mat4.multiply(modelView, viewMatrix, inMatrix);
17209
+ gl_matrix_1.mat4.multiply(mvp, projectionMatrix, modelView);
17210
+ const mousePoint = new Point_js_1.Point({
17211
+ x: inMouseHelper.xyz[0],
17212
+ y: inMouseHelper.xyz[1],
17213
+ z: inMouseHelper.xyz[2],
17214
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
17215
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
17216
+ if (!mouseScreen)
17217
+ return null;
17218
+ let nearestIndex = null;
17219
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
17220
+ for (let index = 0; index < this._sources.length; index += 1) {
17221
+ const source = this._sources[index];
17222
+ if (!source)
17223
+ continue;
17224
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
17225
+ if (!sourceScreen)
17226
+ continue;
17227
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
17228
+ if (distancePx <= this.getHitRadiusPx(source) &&
17229
+ distancePx < nearestDistancePx) {
17230
+ nearestDistancePx = distancePx;
17231
+ nearestIndex = index;
17232
+ }
17233
+ }
17234
+ return nearestIndex;
17235
+ }
16523
17236
  draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix) {
16524
- this._oldMouseCoords = inMouseHelper?.xyz ?? null;
16525
17237
  super.draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix);
16526
17238
  }
16527
17239
  }
@@ -16753,6 +17465,8 @@ class CatalogueShaderProgram {
16753
17465
  this._webgl = webgl;
16754
17466
  this.gl_uniforms = {
16755
17467
  vertex_color: 'u_fragcolor',
17468
+ hover_color: 'u_hovercolor',
17469
+ selected_color: 'u_selectedcolor',
16756
17470
  m_perspective: 'uPMatrix',
16757
17471
  m_model_view: 'uMVMatrix'
16758
17472
  };
@@ -16766,6 +17480,8 @@ class CatalogueShaderProgram {
16766
17480
  pMatrix: null,
16767
17481
  mvMatrix: null,
16768
17482
  color: null,
17483
+ hoverColor: null,
17484
+ selectedColor: null,
16769
17485
  position: -1,
16770
17486
  hovered: -1,
16771
17487
  pointSize: -1,
@@ -16815,6 +17531,8 @@ class CatalogueShaderProgram {
16815
17531
  this.locations.pointSize = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_size);
16816
17532
  this.locations.brightness = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_hue);
16817
17533
  this.locations.color = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.vertex_color);
17534
+ this.locations.hoverColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.hover_color);
17535
+ this.locations.selectedColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.selected_color);
16818
17536
  }
16819
17537
  enableShaders(pMatrix, modelMatrix, viewMatrix) {
16820
17538
  // const gl = global.gl as GL
@@ -17505,6 +18223,8 @@ class ShaderManager {
17505
18223
  in float v_brightness;
17506
18224
 
17507
18225
  uniform vec4 u_fragcolor;
18226
+ uniform vec4 u_hovercolor;
18227
+ uniform vec4 u_selectedcolor;
17508
18228
 
17509
18229
  out vec4 fragColor;
17510
18230
 
@@ -17561,11 +18281,12 @@ class ShaderManager {
17561
18281
  #endif
17562
18282
 
17563
18283
  if (v_selected == 1.0){
17564
- // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
17565
- fragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
18284
+ fragColor = u_hovercolor * alpha;
17566
18285
  } else if (v_selected == 2.0){
17567
- // gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
17568
- fragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
18286
+ // Persistent selection: keep the dataset colour as an outer ring
18287
+ // and use the hover colour for the fill. This remains visible for
18288
+ // both light and dark user-selected base colours.
18289
+ fragColor = (r > 0.72 ? u_fragcolor : u_hovercolor) * alpha;
17569
18290
  }else{
17570
18291
  if (r < 0.4) {
17571
18292
  discard;
@@ -17973,6 +18694,7 @@ class XYZShaderProgram {
17973
18694
  _shaderProgram;
17974
18695
  _colorMapBlockIndex = null;
17975
18696
  _colorMapBuffer = null;
18697
+ _opacity = 1;
17976
18698
  _runtimeColorMap;
17977
18699
  _colorMapVariableInfo = {
17978
18700
  r_palette: { index: 0, offset: 0 },
@@ -17987,6 +18709,7 @@ class XYZShaderProgram {
17987
18709
  vMatrix: null,
17988
18710
  sampler: null,
17989
18711
  colorMapIdx: null,
18712
+ opacity: null,
17990
18713
  vertexPositionAttribute: -1,
17991
18714
  textureCoordAttribute: -1,
17992
18715
  };
@@ -18007,6 +18730,9 @@ class XYZShaderProgram {
18007
18730
  enableProgram() {
18008
18731
  this._webgl.useProgram(this.shaderProgram);
18009
18732
  }
18733
+ setOpacity(opacity) {
18734
+ this._opacity = Math.min(1, Math.max(0, opacity));
18735
+ }
18010
18736
  setRuntimeColorMap(colorMap) {
18011
18737
  this._runtimeColorMap = colorMap;
18012
18738
  }
@@ -18019,6 +18745,7 @@ class XYZShaderProgram {
18019
18745
  this.locations.vMatrix = gl.getUniformLocation(program, 'uVMatrix');
18020
18746
  this.locations.sampler = gl.getUniformLocation(program, 'uSampler');
18021
18747
  this.locations.colorMapIdx = gl.getUniformLocation(program, 'cmapIdx');
18748
+ this.locations.opacity = gl.getUniformLocation(program, 'uOpacity');
18022
18749
  this.locations.vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
18023
18750
  this.locations.textureCoordAttribute = gl.getAttribLocation(program, 'aTextureCoord');
18024
18751
  gl.uniformMatrix4fv(this.locations.pMatrix, false, pMatrix);
@@ -18026,6 +18753,7 @@ class XYZShaderProgram {
18026
18753
  gl.uniformMatrix4fv(this.locations.mMatrix, false, mMatrix);
18027
18754
  gl.uniform1i(this.locations.sampler, 0);
18028
18755
  gl.uniform1i(this.locations.colorMapIdx, colorMapIdx);
18756
+ gl.uniform1f(this.locations.opacity, this._opacity);
18029
18757
  if (colorMapIdx >= 2) {
18030
18758
  this.uploadColorMap(colorMapIdx);
18031
18759
  }
@@ -18048,6 +18776,7 @@ class XYZShaderProgram {
18048
18776
  in vec2 vTextureCoord;
18049
18777
  uniform sampler2D uSampler;
18050
18778
  uniform int cmapIdx;
18779
+ uniform float uOpacity;
18051
18780
 
18052
18781
  layout (std140) uniform colormap {
18053
18782
  float r_palette[256];
@@ -18062,7 +18791,7 @@ class XYZShaderProgram {
18062
18791
 
18063
18792
  if (cmapIdx == 1) {
18064
18793
  float gray = 0.21 * color.r + 0.71 * color.g + 0.07 * color.b;
18065
- outColor = vec4(vec3(gray), color.a);
18794
+ outColor = vec4(vec3(gray), color.a * uOpacity);
18066
18795
  return;
18067
18796
  }
18068
18797
 
@@ -18075,12 +18804,12 @@ class XYZShaderProgram {
18075
18804
  r_palette[rIndex] / 256.0,
18076
18805
  g_palette[gIndex] / 256.0,
18077
18806
  b_palette[bIndex] / 256.0,
18078
- color.a
18807
+ color.a * uOpacity
18079
18808
  );
18080
18809
  return;
18081
18810
  }
18082
18811
 
18083
- outColor = color;
18812
+ outColor = vec4(color.rgb, color.a * uOpacity);
18084
18813
  }`);
18085
18814
  gl.attachShader(this._shaderProgram, vertexShader);
18086
18815
  gl.attachShader(this._shaderProgram, fragmentShader);
@@ -19291,7 +20020,6 @@ exports["default"] = STCSParser;
19291
20020
  (__unused_webpack_module, exports, __webpack_require__) {
19292
20021
 
19293
20022
  "use strict";
19294
- var __webpack_unused_export__;
19295
20023
 
19296
20024
  /*
19297
20025
  * AstroViewer
@@ -19303,9 +20031,10 @@ var __webpack_unused_export__;
19303
20031
  *
19304
20032
  * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
19305
20033
  */
19306
- __webpack_unused_export__ = ({ value: true });
20034
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
19307
20035
  exports.cartesianToSpherical = cartesianToSpherical;
19308
20036
  exports.colorHex2RGB = colorHex2RGB;
20037
+ exports.interactionColorsFromHex = interactionColorsFromHex;
19309
20038
  exports.degToRad = degToRad;
19310
20039
  exports.radToDeg = radToDeg;
19311
20040
  exports.sphericalToAstroDeg = sphericalToAstroDeg;
@@ -19313,6 +20042,8 @@ exports.sphericalToCartesian = sphericalToCartesian;
19313
20042
  exports.astroDegToSpherical = astroDegToSpherical;
19314
20043
  exports.raDegToHMS = raDegToHMS;
19315
20044
  exports.decDegToDMS = decDegToDMS;
20045
+ exports.raHMSToDeg = raHMSToDeg;
20046
+ exports.decDMSToDeg = decDMSToDeg;
19316
20047
  /**
19317
20048
  * @author Fabrizio Giordano (Fab)
19318
20049
  */
@@ -19343,6 +20074,25 @@ function colorHex2RGB(hexColor) {
19343
20074
  const rgb3 = (dec3 / 255).toFixed(2);
19344
20075
  return [parseFloat(rgb1), parseFloat(rgb2), parseFloat(rgb3)];
19345
20076
  }
20077
+ /**
20078
+ * Derive interaction colours from the object's normal colour.
20079
+ * Dark colours move towards white; light colours move towards black.
20080
+ * Selection uses the stronger contrast so selected > hover > normal.
20081
+ */
20082
+ function interactionColorsFromHex(hexColor) {
20083
+ const base = colorHex2RGB(hexColor);
20084
+ const luminance = 0.2126 * base[0] + 0.7152 * base[1] + 0.0722 * base[2];
20085
+ const target = luminance < 0.55 ? 1.0 : 0.0;
20086
+ const mix = (amount) => [
20087
+ base[0] + (target - base[0]) * amount,
20088
+ base[1] + (target - base[1]) * amount,
20089
+ base[2] + (target - base[2]) * amount,
20090
+ ];
20091
+ return {
20092
+ hover: mix(0.45),
20093
+ selected: mix(0.45),
20094
+ };
20095
+ }
19346
20096
  function degToRad(degrees) {
19347
20097
  return (degrees / 180) * Math.PI;
19348
20098
  }
@@ -19389,6 +20139,13 @@ function decDegToDMS(decDeg) {
19389
20139
  d = d * sign;
19390
20140
  return { d, m, s };
19391
20141
  }
20142
+ function raHMSToDeg(hms) {
20143
+ return 15 * (hms.h + hms.m / 60 + hms.s / 3600);
20144
+ }
20145
+ function decDMSToDeg(dms) {
20146
+ const sign = dms.d < 0 || Object.is(dms.d, -0) ? -1 : 1;
20147
+ return sign * (Math.abs(dms.d) + dms.m / 60 + dms.s / 3600);
20148
+ }
19392
20149
 
19393
20150
 
19394
20151
  /***/ },