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
@@ -43,6 +43,7 @@ __webpack_require__.d(vec3_namespaceObject, {
43
43
  dot: () => (dot),
44
44
  fromValues: () => (vec3_fromValues),
45
45
  length: () => (vec3_length),
46
+ lerp: () => (lerp),
46
47
  normalize: () => (normalize),
47
48
  scale: () => (vec3_scale),
48
49
  scaleAndAdd: () => (scaleAndAdd),
@@ -3629,12 +3630,18 @@ class AstroSphere {
3629
3630
  _activeHiPS = null;
3630
3631
  _activeHiPSLayers = [];
3631
3632
  _activeXYZ2 = null;
3633
+ _earthRasterOverlays = [];
3634
+ _earthRasterOverlaySequence = 0;
3632
3635
  _activeMeshHiPS = null;
3633
3636
  _activeBaseLayer = null;
3637
+ _activeDomain = "astronomy";
3638
+ domainCameraStates = {};
3634
3639
  startup = true;
3635
3640
  fov;
3636
- activeCatalogues = [];
3637
- activeFootprintSets = [];
3641
+ astronomyCatalogues = [];
3642
+ earthPointSets = [];
3643
+ astronomyFootprintSets = [];
3644
+ earthFootprintSets = [];
3638
3645
  activePolylineSets = [];
3639
3646
  activeSensorCones = [];
3640
3647
  activeSatelliteObjects = [];
@@ -3867,9 +3874,7 @@ class AstroSphere {
3867
3874
  return true;
3868
3875
  }
3869
3876
  emitCameraChanged(reason) {
3870
- // avoid dispatch before scene is ready
3871
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
3872
- return;
3877
+ // Camera interaction remains meaningful even when no base layer is active.
3873
3878
  if (!this._healpixGrid?.fovObj)
3874
3879
  return;
3875
3880
  const detail = this.getCurrentStatus();
@@ -3895,6 +3900,7 @@ class AstroSphere {
3895
3900
  const handleMouseDown = (event) => {
3896
3901
  canvas.setPointerCapture(event.pointerId);
3897
3902
  this.mouseDown = true;
3903
+ this._camera.cancelFlyTo();
3898
3904
  const rect = canvas.getBoundingClientRect();
3899
3905
  this.lastMouseX = event.clientX - rect.left; // locale al canvas
3900
3906
  this.lastMouseY = event.clientY - rect.top; // locale al canvas
@@ -3933,8 +3939,11 @@ class AstroSphere {
3933
3939
  if (mousePoint && mousePoint.length > 0) {
3934
3940
  this.mouseHelper.update(mousePoint);
3935
3941
  this.updateLastMousePoint();
3936
- for (const cat of this.activeCatalogues) {
3937
- const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper);
3942
+ for (const cat of this.getActivePointSets()) {
3943
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
3944
+ if (!activeModelMatrix)
3945
+ continue;
3946
+ const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
3938
3947
  if (!clickResult?.sources.length)
3939
3948
  continue;
3940
3949
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-clicked", {
@@ -3947,7 +3956,7 @@ class AstroSphere {
3947
3956
  composed: true,
3948
3957
  }));
3949
3958
  }
3950
- for (const fset of this.activeFootprintSets) {
3959
+ for (const fset of this.getActiveFootprintSets()) {
3951
3960
  const clickResult = fset.selectPrimaryFootprintFromClick(this.mouseHelper);
3952
3961
  if (!clickResult?.footprints.length)
3953
3962
  continue;
@@ -4021,6 +4030,7 @@ class AstroSphere {
4021
4030
  const handleMouseWheel = (event) => {
4022
4031
  const currentFov = this._healpixGrid.getMinFoV();
4023
4032
  const zoomStep = this.computeZoomStep(currentFov, event.deltaY);
4033
+ this._camera.cancelFlyTo();
4024
4034
  // Apply wheel zoom immediately and discard any queued inertia so reversing
4025
4035
  // direction feels responsive instead of "buffered".
4026
4036
  this.zoomInertia = 0;
@@ -4043,8 +4053,11 @@ class AstroSphere {
4043
4053
  }
4044
4054
  this.mouseHelper.update(mousePoint);
4045
4055
  this.updateLastMousePoint();
4046
- for (const cat of this.activeCatalogues) {
4047
- const pickResult = cat.getSourcesFromPointer(this.mouseHelper);
4056
+ for (const cat of this.getActivePointSets()) {
4057
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4058
+ if (!activeModelMatrix)
4059
+ continue;
4060
+ const pickResult = cat.getSourcesFromPointer(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4048
4061
  if (!pickResult?.sources.length)
4049
4062
  continue;
4050
4063
  this._webgl.canvas.dispatchEvent(new CustomEvent("source-contextmenu", {
@@ -4059,7 +4072,7 @@ class AstroSphere {
4059
4072
  }));
4060
4073
  break;
4061
4074
  }
4062
- for (const fset of this.activeFootprintSets) {
4075
+ for (const fset of this.getActiveFootprintSets()) {
4063
4076
  const pickResult = fset.getFootprintsFromPointer(this.mouseHelper);
4064
4077
  if (!pickResult?.footprints.length)
4065
4078
  continue;
@@ -4149,19 +4162,22 @@ class AstroSphere {
4149
4162
  return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
4150
4163
  }
4151
4164
  activateHiPS(hipsDescriptor) {
4165
+ if (this._activeHiPSLayers.length > 0) {
4166
+ throw new Error("Cannot load a HiPS base layer while stacked layers are active.");
4167
+ }
4152
4168
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4153
- for (const hips of this._activeHiPSLayers) {
4154
- tileBuffer.removeHiPS(hips);
4169
+ if (this._activeHiPS) {
4170
+ tileBuffer.removeHiPS(this._activeHiPS);
4155
4171
  }
4156
- this._activeHiPSLayers = [];
4157
4172
  const hips = this.createHiPS(hipsDescriptor);
4158
- this._activeHiPSLayers.push(hips);
4159
4173
  this._activeHiPS = hips;
4174
+ this._activeDomain = "astronomy";
4160
4175
  this._activeBaseLayer = "hips";
4161
4176
  return hips;
4162
4177
  }
4163
4178
  setHiPSOpacity(hips, opacity) {
4164
- if (!this._activeHiPSLayers.includes(hips)) {
4179
+ const isBaseLayer = this._activeHiPSLayers.length === 0 && this._activeHiPS === hips;
4180
+ if (!isBaseLayer && !this._activeHiPSLayers.includes(hips)) {
4165
4181
  throw new Error("HiPS layer is not active in this AstroSphere.");
4166
4182
  }
4167
4183
  hips.setOpacity(opacity);
@@ -4169,6 +4185,13 @@ class AstroSphere {
4169
4185
  addHiPS(hipsDescriptor) {
4170
4186
  const normalizeURL = (url) => String(url).replace(/\/+$/, "");
4171
4187
  const descriptorURL = normalizeURL(hipsDescriptor.url);
4188
+ // Entering stack mode discards the standalone base HiPS. Base mode and
4189
+ // stack mode are intentionally mutually exclusive.
4190
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4191
+ const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4192
+ tileBuffer.removeHiPS(this._activeHiPS);
4193
+ this._activeHiPS = null;
4194
+ }
4172
4195
  const existing = this._activeHiPSLayers.find((hips) => normalizeURL(hips.baseURL) === descriptorURL);
4173
4196
  if (existing) {
4174
4197
  throw new Error(`HiPS already active: ${String(hipsDescriptor.url)}`);
@@ -4176,6 +4199,7 @@ class AstroSphere {
4176
4199
  const hips = this.createHiPS(hipsDescriptor);
4177
4200
  this._activeHiPSLayers.push(hips);
4178
4201
  this._activeHiPS = hips;
4202
+ this._activeDomain = "astronomy";
4179
4203
  this._activeBaseLayer = "hips";
4180
4204
  return hips;
4181
4205
  }
@@ -4200,8 +4224,13 @@ class AstroSphere {
4200
4224
  }
4201
4225
  removeAllHiPS() {
4202
4226
  const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4203
- for (const hips of this._activeHiPSLayers) {
4204
- tileBuffer.removeHiPS(hips);
4227
+ if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
4228
+ tileBuffer.removeHiPS(this._activeHiPS);
4229
+ }
4230
+ else {
4231
+ for (const hips of this._activeHiPSLayers) {
4232
+ tileBuffer.removeHiPS(hips);
4233
+ }
4205
4234
  }
4206
4235
  this._activeHiPSLayers = [];
4207
4236
  this._activeHiPS = null;
@@ -4214,39 +4243,169 @@ class AstroSphere {
4214
4243
  this._activeBaseLayer = "xyz";
4215
4244
  }
4216
4245
  activateXYZ2(config) {
4246
+ this._activeDomain = "earth";
4217
4247
  this._activeXYZ2 = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4218
4248
  this._activeBaseLayer = "xyz";
4219
4249
  }
4220
4250
  activateMeshHiPS(descriptor) {
4251
+ this._activeDomain = "mesh";
4221
4252
  this._activeMeshHiPS = new MeshHiPS_js_1.MeshHiPS(descriptor.meshRadius, [0.0, 0.0, 0.0], 0, 0, descriptor, this._webgl, this._healpixGrid);
4222
4253
  this._activeBaseLayer = "meships";
4223
4254
  }
4255
+ setMeshHiPSColor(color) {
4256
+ this._activeMeshHiPS?.setColor(color);
4257
+ }
4258
+ setMeshHiPSWireframe(wireframe) {
4259
+ this._activeMeshHiPS?.setWireframe(wireframe);
4260
+ }
4224
4261
  activateWMTS(config) {
4262
+ this._activeDomain = "earth";
4225
4263
  const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4226
4264
  const xyzConfig = adapter.toXYZLayerConfig();
4227
4265
  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);
4228
4266
  this._activeBaseLayer = "xyz";
4229
4267
  }
4230
- // Catalogue section
4268
+ addXYZRasterOverlay(config) {
4269
+ const map = new XYZMap_js_1.XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
4270
+ return this.addEarthRasterOverlay(config.name, "xyz", map);
4271
+ }
4272
+ addWMTSRasterOverlay(config) {
4273
+ const adapter = new WMTSAdapter_js_1.WMTSAdapter(config);
4274
+ const xyzConfig = adapter.toXYZLayerConfig();
4275
+ const name = config.layer ? `WMTS ${config.layer}` : "WMTS Earth Raster Overlay";
4276
+ 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);
4277
+ return this.addEarthRasterOverlay(name, "wmts", map);
4278
+ }
4279
+ getEarthRasterOverlays() {
4280
+ return this._earthRasterOverlays.map(({ map: _map, ...overlay }) => ({ ...overlay }));
4281
+ }
4282
+ setEarthRasterOverlayOpacity(id, opacity) {
4283
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4284
+ if (!overlay)
4285
+ return;
4286
+ overlay.opacity = Math.min(1, Math.max(0, opacity));
4287
+ overlay.map.setOpacity(overlay.opacity);
4288
+ }
4289
+ setEarthRasterOverlayVisible(id, visible) {
4290
+ const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
4291
+ if (overlay)
4292
+ overlay.visible = visible;
4293
+ }
4294
+ removeEarthRasterOverlay(id) {
4295
+ this._earthRasterOverlays = this._earthRasterOverlays.filter((entry) => entry.id !== id);
4296
+ }
4297
+ removeAllEarthRasterOverlays() {
4298
+ this._earthRasterOverlays = [];
4299
+ }
4300
+ addEarthRasterOverlay(name, sourceType, map) {
4301
+ const overlay = {
4302
+ id: `earth-raster-${++this._earthRasterOverlaySequence}`,
4303
+ name,
4304
+ sourceType,
4305
+ visible: true,
4306
+ opacity: 0.65,
4307
+ map,
4308
+ };
4309
+ map.setOpacity(overlay.opacity);
4310
+ this._earthRasterOverlays.push(overlay);
4311
+ const { map: _map, ...info } = overlay;
4312
+ return { ...info };
4313
+ }
4314
+ setActiveDomain(domain) {
4315
+ if (domain !== this._activeDomain) {
4316
+ this.clearGridLabelsForDomain(this._activeDomain);
4317
+ this.domainCameraStates[this._activeDomain] = this._camera.getViewState();
4318
+ const targetCameraState = this.domainCameraStates[domain];
4319
+ if (targetCameraState) {
4320
+ this._camera.restoreViewState(targetCameraState);
4321
+ }
4322
+ this.inertiaX = 0;
4323
+ this.inertiaY = 0;
4324
+ this.zoomInertia = 0;
4325
+ this._cameraStatusChanged = true;
4326
+ }
4327
+ this._activeDomain = domain;
4328
+ if (domain === "astronomy") {
4329
+ this._activeBaseLayer = this._activeHiPS ? "hips" : null;
4330
+ }
4331
+ else if (domain === "earth") {
4332
+ this._activeBaseLayer = this._activeXYZ2 ? "xyz" : null;
4333
+ }
4334
+ else {
4335
+ this._activeBaseLayer = this._activeMeshHiPS ? "meships" : null;
4336
+ }
4337
+ // A hover from the previous vertical must not leak into the new one.
4338
+ this.lastHoveredSource = null;
4339
+ this.lastHoveredCatalogue = null;
4340
+ }
4341
+ clearGridLabelsForDomain(domain) {
4342
+ if (domain === "astronomy") {
4343
+ this._healpixGrid?.clearLabels();
4344
+ this._equatorialGrid?.clearLabels();
4345
+ return;
4346
+ }
4347
+ if (domain === "earth") {
4348
+ this._activeXYZ2?.clearLonLatGridLabels?.();
4349
+ }
4350
+ }
4351
+ get activeDomain() {
4352
+ return this._activeDomain;
4353
+ }
4354
+ getActivePointSets() {
4355
+ if (this._activeDomain === "astronomy")
4356
+ return this.astronomyCatalogues;
4357
+ if (this._activeDomain === "earth")
4358
+ return this.earthPointSets;
4359
+ return [];
4360
+ }
4361
+ getActiveFootprintSets() {
4362
+ if (this._activeDomain === "astronomy")
4363
+ return this.astronomyFootprintSets;
4364
+ if (this._activeDomain === "earth")
4365
+ return this.earthFootprintSets;
4366
+ return [];
4367
+ }
4368
+ // Astronomy catalogue ownership
4231
4369
  async showCatalogue(cat) {
4232
- // console.log(cat)
4233
- if (cat)
4234
- this.activeCatalogues.push(cat);
4370
+ if (cat && !this.astronomyCatalogues.includes(cat)) {
4371
+ this.astronomyCatalogues.push(cat);
4372
+ }
4235
4373
  return cat;
4236
4374
  }
4237
4375
  deleteCatalogue(catalogue) {
4238
- this.activeCatalogues = this.activeCatalogues.filter((c) => c !== catalogue);
4376
+ this.astronomyCatalogues = this.astronomyCatalogues.filter((c) => c !== catalogue);
4239
4377
  }
4240
- // End Catalogue section
4241
- // Footprint section
4378
+ // Earth point-set ownership. TerraPointSetGL still extends CatalogueGL in
4379
+ // 3.12, but it no longer shares the Astronomy lifecycle collection.
4380
+ async showTerraPointSet(pointSet) {
4381
+ if (pointSet && !this.earthPointSets.includes(pointSet)) {
4382
+ this.earthPointSets.push(pointSet);
4383
+ }
4384
+ return pointSet;
4385
+ }
4386
+ deleteTerraPointSet(pointSet) {
4387
+ this.earthPointSets = this.earthPointSets.filter((set) => set !== pointSet);
4388
+ }
4389
+ // Astronomy footprint ownership
4242
4390
  async showFootprintSet(fset) {
4243
- // console.log(fset)
4244
- if (fset)
4245
- this.activeFootprintSets.push(fset);
4391
+ if (fset && !this.astronomyFootprintSets.includes(fset)) {
4392
+ this.astronomyFootprintSets.push(fset);
4393
+ }
4246
4394
  return fset;
4247
4395
  }
4248
4396
  deleteFootprintSet(footprintSet) {
4249
- this.activeFootprintSets = this.activeFootprintSets.filter((fst) => fst !== footprintSet);
4397
+ this.astronomyFootprintSets = this.astronomyFootprintSets.filter((fst) => fst !== footprintSet);
4398
+ }
4399
+ // Earth footprint ownership. TerraFootprintSetGL still reuses the common
4400
+ // FootprintSetGL rendering implementation, but has an independent lifecycle.
4401
+ async showTerraFootprintSet(footprintSet) {
4402
+ if (footprintSet && !this.earthFootprintSets.includes(footprintSet)) {
4403
+ this.earthFootprintSets.push(footprintSet);
4404
+ }
4405
+ return footprintSet;
4406
+ }
4407
+ deleteTerraFootprintSet(footprintSet) {
4408
+ this.earthFootprintSets = this.earthFootprintSets.filter((fst) => fst !== footprintSet);
4250
4409
  }
4251
4410
  async showPolylineSet(polylineSet) {
4252
4411
  if (polylineSet)
@@ -4276,8 +4435,8 @@ class AstroSphere {
4276
4435
  satelliteObject.dispose();
4277
4436
  }
4278
4437
  getHoveredFootprints() {
4279
- let footprintsHovered = [];
4280
- this.activeFootprintSets.forEach((fset) => {
4438
+ const footprintsHovered = [];
4439
+ this.getActiveFootprintSets().forEach((fset) => {
4281
4440
  footprintsHovered.push(fset.hoveredFootprints);
4282
4441
  });
4283
4442
  return footprintsHovered;
@@ -4286,6 +4445,9 @@ class AstroSphere {
4286
4445
  goTo(raDeg, decDeg) {
4287
4446
  this._camera.goTo(raDeg, decDeg);
4288
4447
  }
4448
+ flyTo(raDeg, decDeg, durationMs = 1200) {
4449
+ this._camera.flyTo(raDeg, decDeg, durationMs);
4450
+ }
4289
4451
  getActiveCoordinateMode() {
4290
4452
  if (this._activeBaseLayer === "xyz") {
4291
4453
  return "lonlat";
@@ -4506,8 +4668,6 @@ class AstroSphere {
4506
4668
  return;
4507
4669
  if (!this._webgl)
4508
4670
  return;
4509
- if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
4510
- return;
4511
4671
  if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
4512
4672
  return;
4513
4673
  if (this._healpixGrid.fovObj === undefined)
@@ -4521,6 +4681,16 @@ class AstroSphere {
4521
4681
  let PHI = 0;
4522
4682
  this._webgl.viewport(0, 0, this._webgl.drawingBufferWidth, this._webgl.drawingBufferHeight);
4523
4683
  this._webgl.clear(this._webgl.COLOR_BUFFER_BIT | this._webgl.DEPTH_BUFFER_BIT);
4684
+ const now = performance.now();
4685
+ const cameraFlying = this._camera.updateFlyTo(now);
4686
+ if (cameraFlying) {
4687
+ this.inertiaX = 0;
4688
+ this.inertiaY = 0;
4689
+ this.lastCameraMotionAt = now;
4690
+ this._cameraStatusChanged = true;
4691
+ this.updateCentralPoint();
4692
+ 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);
4693
+ }
4524
4694
  // Zoom inertia
4525
4695
  if (this.zoomInertia !== 0) {
4526
4696
  if (Math.abs(this.zoomInertia) > 0.0001) {
@@ -4609,8 +4779,10 @@ class AstroSphere {
4609
4779
  this._webgl.enable(this._webgl.CULL_FACE);
4610
4780
  this._webgl.cullFace(Global_js_1.default.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
4611
4781
  this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
4612
- if (this._activeBaseLayer === "hips" && this._activeHiPSLayers.length > 0) {
4613
- const maxHiPSOrder = Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder));
4782
+ if (this._activeBaseLayer === "hips" && this._activeHiPS) {
4783
+ const maxHiPSOrder = this._activeHiPSLayers.length > 0
4784
+ ? Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder))
4785
+ : this._activeHiPS.maxOrder;
4614
4786
  const visibleOrder = Math.min(this._healpixGrid.visibleorder, maxHiPSOrder);
4615
4787
  this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
4616
4788
  }
@@ -4645,12 +4817,19 @@ class AstroSphere {
4645
4817
  }
4646
4818
  if (this._activeBaseLayer === "xyz") {
4647
4819
  this._activeXYZ2?.draw(skyEntityDrawInput);
4820
+ for (const overlay of this._earthRasterOverlays) {
4821
+ if (overlay.visible) {
4822
+ overlay.map.draw(skyEntityDrawInput, true);
4823
+ }
4824
+ }
4648
4825
  }
4649
4826
  if (this._activeBaseLayer === "meships") {
4650
4827
  this._activeMeshHiPS?.draw(skyEntityDrawInput);
4651
4828
  }
4652
- this._healpixGrid.draw(skyEntityDrawInput);
4653
- this._equatorialGrid.draw(skyEntityDrawInput);
4829
+ if (this._activeDomain === "astronomy") {
4830
+ this._healpixGrid.draw(skyEntityDrawInput);
4831
+ this._equatorialGrid.draw(skyEntityDrawInput);
4832
+ }
4654
4833
  this._webgl.enable(this._webgl.DEPTH_TEST);
4655
4834
  this._webgl.disable(this._webgl.CULL_FACE);
4656
4835
  if (this.startup) {
@@ -4669,52 +4848,53 @@ class AstroSphere {
4669
4848
  decDMS,
4670
4849
  });
4671
4850
  }
4672
- this.activeCatalogues.forEach((cat) => {
4673
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4674
- this._activeXYZ2?.getModelMatrix() ??
4675
- this._activeMeshHiPS?.getModelMatrix();
4851
+ this.getActivePointSets().forEach((cat) => {
4852
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4676
4853
  if (activeModelMatrix) {
4677
4854
  cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4678
4855
  }
4679
4856
  });
4680
4857
  this.emitHoveredSourceIfChanged();
4681
- this.activeFootprintSets.forEach((fst) => {
4682
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4683
- this._activeXYZ2?.getModelMatrix() ??
4684
- this._activeMeshHiPS?.getModelMatrix();
4858
+ this.getActiveFootprintSets().forEach((fst) => {
4859
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4685
4860
  if (activeModelMatrix) {
4686
4861
  fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4687
4862
  }
4688
4863
  });
4689
- this.activePolylineSets.forEach((polylineSet) => {
4690
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4691
- this._activeXYZ2?.getModelMatrix() ??
4692
- this._activeMeshHiPS?.getModelMatrix();
4693
- if (activeModelMatrix) {
4694
- polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4695
- }
4696
- });
4697
- this.activeSensorCones.forEach((sensorCone) => {
4698
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4699
- this._activeXYZ2?.getModelMatrix() ??
4700
- this._activeMeshHiPS?.getModelMatrix();
4701
- if (activeModelMatrix) {
4702
- sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4703
- }
4704
- });
4705
- this.activeSatelliteObjects.forEach((satelliteObject) => {
4706
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
4707
- this._activeXYZ2?.getModelMatrix() ??
4708
- this._activeMeshHiPS?.getModelMatrix();
4709
- if (activeModelMatrix) {
4710
- satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4711
- }
4712
- });
4864
+ if (this._activeDomain === "earth") {
4865
+ this.activePolylineSets.forEach((polylineSet) => {
4866
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4867
+ if (activeModelMatrix) {
4868
+ polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
4869
+ }
4870
+ });
4871
+ this.activeSensorCones.forEach((sensorCone) => {
4872
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4873
+ if (activeModelMatrix) {
4874
+ sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4875
+ }
4876
+ });
4877
+ this.activeSatelliteObjects.forEach((satelliteObject) => {
4878
+ const activeModelMatrix = this.getActiveDomainModelMatrix();
4879
+ if (activeModelMatrix) {
4880
+ satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
4881
+ }
4882
+ });
4883
+ }
4884
+ }
4885
+ getActiveDomainModelMatrix() {
4886
+ if (this._activeDomain === "astronomy") {
4887
+ return (this._activeHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4888
+ }
4889
+ if (this._activeDomain === "earth") {
4890
+ return (this._activeXYZ2?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4891
+ }
4892
+ return (this._activeMeshHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
4713
4893
  }
4714
4894
  emitHoveredSourceIfChanged() {
4715
4895
  let nextHoveredSource = null;
4716
4896
  let nextHoveredCatalogue = null;
4717
- for (const cat of this.activeCatalogues) {
4897
+ for (const cat of this.getActivePointSets()) {
4718
4898
  const hovered = cat.getPrimaryHoveredSource();
4719
4899
  if (!hovered)
4720
4900
  continue;
@@ -4842,25 +5022,25 @@ class AstroViewer {
4842
5022
  return new TerraPointSetGL_js_1.TerraPointSetGL(pointSetName, pointSetDescription, providerUrl, metadataManager, this.webgl, this.astroSphere.healpixGrid.visibleTilesManager);
4843
5023
  }
4844
5024
  showTerraPointSet(pointSet) {
4845
- this.astroSphere.showCatalogue(pointSet);
5025
+ this.astroSphere.showTerraPointSet(pointSet);
4846
5026
  }
4847
5027
  hideTerraPointSet(pointSet, isVisible) {
4848
5028
  pointSet.setIsVisible(isVisible);
4849
5029
  }
4850
5030
  deleteTerraPointSet(pointSet) {
4851
- this.astroSphere.deleteCatalogue(pointSet);
5031
+ this.astroSphere.deleteTerraPointSet(pointSet);
4852
5032
  }
4853
5033
  createTerraFootprintSet(footprintSetName, footprintSetDescription, providerUrl, metadataManager) {
4854
5034
  return new TerraFootprintSetGL_js_1.TerraFootprintSetGL(footprintSetName, footprintSetDescription, providerUrl, metadataManager, this.webgl);
4855
5035
  }
4856
5036
  showTerraFootprintSet(footprintSet) {
4857
- this.astroSphere.showFootprintSet(footprintSet);
5037
+ this.astroSphere.showTerraFootprintSet(footprintSet);
4858
5038
  }
4859
5039
  hideTerraFootprintSet(footprintSet, isVisible) {
4860
5040
  footprintSet.setIsVisible(isVisible);
4861
5041
  }
4862
5042
  deleteTerraFootprintSet(footprintSet) {
4863
- this.astroSphere.deleteFootprintSet(footprintSet);
5043
+ this.astroSphere.deleteTerraFootprintSet(footprintSet);
4864
5044
  }
4865
5045
  createTerraPolylineSet(polylineSetName, polylineSetDescription, providerUrl, metadataManager) {
4866
5046
  return new TerraPolylineSetGL_js_1.TerraPolylineSetGL(polylineSetName, polylineSetDescription, providerUrl, metadataManager, this.webgl);
@@ -4913,6 +5093,12 @@ class AstroViewer {
4913
5093
  getDefaultHiPSURL() {
4914
5094
  return Config_js_1.bootSetup.defaultHipsUrl;
4915
5095
  }
5096
+ setActiveDomain(domain) {
5097
+ this.astroSphere.setActiveDomain(domain);
5098
+ }
5099
+ getActiveDomain() {
5100
+ return this.astroSphere.activeDomain;
5101
+ }
4916
5102
  activateHiPS(hipsDescriptor) {
4917
5103
  this.astroSphere.activateHiPS(hipsDescriptor);
4918
5104
  }
@@ -4926,9 +5112,37 @@ class AstroViewer {
4926
5112
  activateWMTS(config) {
4927
5113
  this.astroSphere.activateWMTS(config);
4928
5114
  }
5115
+ addXYZRasterOverlay(config) {
5116
+ 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);
5117
+ return this.astroSphere.addXYZRasterOverlay(descriptor);
5118
+ }
5119
+ addWMTSRasterOverlay(config) {
5120
+ return this.astroSphere.addWMTSRasterOverlay(config);
5121
+ }
5122
+ getEarthRasterOverlays() {
5123
+ return this.astroSphere.getEarthRasterOverlays();
5124
+ }
5125
+ setEarthRasterOverlayOpacity(id, opacity) {
5126
+ this.astroSphere.setEarthRasterOverlayOpacity(id, opacity);
5127
+ }
5128
+ setEarthRasterOverlayVisible(id, visible) {
5129
+ this.astroSphere.setEarthRasterOverlayVisible(id, visible);
5130
+ }
5131
+ removeEarthRasterOverlay(id) {
5132
+ this.astroSphere.removeEarthRasterOverlay(id);
5133
+ }
5134
+ removeAllEarthRasterOverlays() {
5135
+ this.astroSphere.removeAllEarthRasterOverlays();
5136
+ }
4929
5137
  activateMeshHiPS(config) {
4930
5138
  this.astroSphere.activateMeshHiPS(new MeshHiPSDescriptor_js_1.MeshHiPSDescriptor(config));
4931
5139
  }
5140
+ setMeshHiPSColor(color) {
5141
+ this.astroSphere.setMeshHiPSColor(color);
5142
+ }
5143
+ setMeshHiPSWireframe(wireframe) {
5144
+ this.astroSphere.setMeshHiPSWireframe(wireframe);
5145
+ }
4932
5146
  setXYZMaxConcurrentRequests(value) {
4933
5147
  XYZTileRequestScheduler_js_1.xyzTileRequestScheduler.setMaxConcurrent(value);
4934
5148
  }
@@ -5051,6 +5265,9 @@ class AstroViewer {
5051
5265
  // console.log(`AstroViewer.goTo goto(${raDeg}, ${decDeg})`)
5052
5266
  this.astroSphere.goTo(raDeg, decDeg);
5053
5267
  }
5268
+ flyTo(raDeg, decDeg, durationMs = 1200) {
5269
+ this.astroSphere.flyTo(raDeg, decDeg, durationMs);
5270
+ }
5054
5271
  getActiveCoordinateMode() {
5055
5272
  return this.astroSphere.getActiveCoordinateMode();
5056
5273
  }
@@ -5372,6 +5589,7 @@ class Camera {
5372
5589
  lockRotX = false;
5373
5590
  lockRotY = false;
5374
5591
  lockRotZ = false;
5592
+ flyToAnimation = null;
5375
5593
  constructor(in_position, in_sphere) {
5376
5594
  this.init(in_position, in_sphere);
5377
5595
  }
@@ -5381,7 +5599,11 @@ class Camera {
5381
5599
  this.vMatrix = gl_matrix_1.mat4.create();
5382
5600
  this.T = gl_matrix_1.mat4.create();
5383
5601
  this.R = gl_matrix_1.mat4.create();
5384
- gl_matrix_1.mat4.translate(this.T, this.T, [this.cam_pos[0], this.cam_pos[1], this.cam_pos[2]]);
5602
+ gl_matrix_1.mat4.translate(this.T, this.T, [
5603
+ this.cam_pos[0],
5604
+ this.cam_pos[1],
5605
+ this.cam_pos[2],
5606
+ ]);
5385
5607
  // reset helpers
5386
5608
  this.FoV = this.previousFoV = 180.0;
5387
5609
  this.move = gl_matrix_1.vec3.clone([0, 0, 0]);
@@ -5390,11 +5612,15 @@ class Camera {
5390
5612
  this.goTo(raDeg, decDeg);
5391
5613
  }
5392
5614
  goTo(raDeg, decDeg) {
5615
+ this.cancelFlyTo();
5393
5616
  this.goToPhiTheta((0, Utils_js_1.astroDegToSpherical)(raDeg, decDeg));
5394
5617
  }
5395
5618
  goToPhiTheta(ptDeg) {
5396
5619
  const xyz = (0, Utils_js_1.sphericalToCartesian)(ptDeg.phi, ptDeg.theta, this.cam_pos[2]);
5397
- const targetDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), gl_matrix_1.vec3.fromValues(xyz[0], xyz[1], xyz[2]));
5620
+ this.applyCameraPosition(gl_matrix_1.vec3.fromValues(xyz[0], xyz[1], xyz[2]));
5621
+ }
5622
+ applyCameraPosition(cameraPosition) {
5623
+ const targetDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), cameraPosition);
5398
5624
  const celestialNorth = gl_matrix_1.vec3.fromValues(0.0, 0.0, 1.0);
5399
5625
  const northProjection = gl_matrix_1.vec3.scale(gl_matrix_1.vec3.create(), targetDirection, gl_matrix_1.vec3.dot(celestialNorth, targetDirection));
5400
5626
  const cameraUp = gl_matrix_1.vec3.subtract(gl_matrix_1.vec3.create(), celestialNorth, northProjection);
@@ -5405,9 +5631,13 @@ class Camera {
5405
5631
  gl_matrix_1.vec3.normalize(cameraUp, cameraUp);
5406
5632
  }
5407
5633
  let cameraMatrix = gl_matrix_1.mat4.create();
5408
- cameraMatrix = gl_matrix_1.mat4.translate(cameraMatrix, cameraMatrix, gl_matrix_1.vec3.fromValues(xyz[0], xyz[1], xyz[2]));
5634
+ cameraMatrix = gl_matrix_1.mat4.translate(cameraMatrix, cameraMatrix, cameraPosition);
5409
5635
  const focusPoint = [0.0, 0.0, 0.0];
5410
- const cameraPos = [cameraMatrix[12], cameraMatrix[13], cameraMatrix[14]];
5636
+ const cameraPos = [
5637
+ cameraMatrix[12],
5638
+ cameraMatrix[13],
5639
+ cameraMatrix[14],
5640
+ ];
5411
5641
  cameraMatrix = gl_matrix_1.mat4.targetTo(cameraMatrix, cameraPos, focusPoint, cameraUp);
5412
5642
  this.R = gl_matrix_1.mat4.clone(cameraMatrix);
5413
5643
  this.R[12] = 0;
@@ -5419,6 +5649,81 @@ class Camera {
5419
5649
  }
5420
5650
  this.vMatrix = viewMatrix;
5421
5651
  }
5652
+ flyTo(raDeg, decDeg, durationMs = 1200) {
5653
+ const targetSpherical = (0, Utils_js_1.astroDegToSpherical)(raDeg, decDeg);
5654
+ const targetPositionValues = (0, Utils_js_1.sphericalToCartesian)(targetSpherical.phi, targetSpherical.theta, this.cam_pos[2]);
5655
+ const targetPosition = gl_matrix_1.vec3.fromValues(targetPositionValues[0], targetPositionValues[1], targetPositionValues[2]);
5656
+ const currentPosition = this.getCameraPosition();
5657
+ const startDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), gl_matrix_1.vec3.fromValues(currentPosition[0], currentPosition[1], currentPosition[2]));
5658
+ const targetDirection = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), targetPosition);
5659
+ this.flyToAnimation = {
5660
+ startDirection,
5661
+ targetDirection,
5662
+ targetPosition,
5663
+ startTime: performance.now(),
5664
+ durationMs: Math.max(1, durationMs),
5665
+ };
5666
+ }
5667
+ updateFlyTo(now) {
5668
+ const animation = this.flyToAnimation;
5669
+ if (!animation) {
5670
+ return false;
5671
+ }
5672
+ const elapsed = now - animation.startTime;
5673
+ const progress = Math.min(1, elapsed / animation.durationMs);
5674
+ const easedProgress = progress < 0.5
5675
+ ? 4 * progress * progress * progress
5676
+ : 1 - Math.pow(-2 * progress + 2, 3) / 2;
5677
+ const direction = this.slerpDirection(animation.startDirection, animation.targetDirection, easedProgress);
5678
+ const radius = Math.abs(this.cam_pos[2]);
5679
+ const cameraPosition = gl_matrix_1.vec3.scale(gl_matrix_1.vec3.create(), direction, radius);
5680
+ this.applyCameraPosition(cameraPosition);
5681
+ if (progress >= 1) {
5682
+ this.applyCameraPosition(animation.targetPosition);
5683
+ this.flyToAnimation = null;
5684
+ }
5685
+ return true;
5686
+ }
5687
+ slerpDirection(start, end, t) {
5688
+ const dot = Math.max(-1, Math.min(1, gl_matrix_1.vec3.dot(start, end)));
5689
+ /*
5690
+ * For almost identical directions, normalized linear interpolation is
5691
+ * numerically more stable than the general SLERP formula.
5692
+ */
5693
+ if (dot > 0.999999) {
5694
+ const result = gl_matrix_1.vec3.lerp(gl_matrix_1.vec3.create(), start, end, t);
5695
+ return gl_matrix_1.vec3.normalize(result, result);
5696
+ }
5697
+ /*
5698
+ * The normal SLERP formula becomes singular for antipodal vectors because
5699
+ * sin(pi) = 0. In that case choose a stable vector orthogonal to start and
5700
+ * rotate through that great circle.
5701
+ */
5702
+ if (dot < -0.999999) {
5703
+ const referenceAxis = Math.abs(start[0]) < 0.9
5704
+ ? gl_matrix_1.vec3.fromValues(1, 0, 0)
5705
+ : gl_matrix_1.vec3.fromValues(0, 1, 0);
5706
+ const orthogonal = gl_matrix_1.vec3.cross(gl_matrix_1.vec3.create(), start, referenceAxis);
5707
+ gl_matrix_1.vec3.normalize(orthogonal, orthogonal);
5708
+ const angle = Math.PI * t;
5709
+ const result = gl_matrix_1.vec3.create();
5710
+ gl_matrix_1.vec3.scale(result, start, Math.cos(angle));
5711
+ gl_matrix_1.vec3.scaleAndAdd(result, result, orthogonal, Math.sin(angle));
5712
+ return gl_matrix_1.vec3.normalize(result, result);
5713
+ }
5714
+ const angle = Math.acos(dot);
5715
+ const sinAngle = Math.sin(angle);
5716
+ const startWeight = Math.sin((1 - t) * angle) / sinAngle;
5717
+ const endWeight = Math.sin(t * angle) / sinAngle;
5718
+ const result = gl_matrix_1.vec3.create();
5719
+ result[0] = start[0] * startWeight + end[0] * endWeight;
5720
+ result[1] = start[1] * startWeight + end[1] * endWeight;
5721
+ result[2] = start[2] * startWeight + end[2] * endWeight;
5722
+ return gl_matrix_1.vec3.normalize(result, result);
5723
+ }
5724
+ cancelFlyTo() {
5725
+ this.flyToAnimation = null;
5726
+ }
5422
5727
  toggleInsideSphere() {
5423
5728
  // if (inside !== global.insideSphere) {
5424
5729
  // global.insideSphere = inside;
@@ -5640,6 +5945,22 @@ class Camera {
5640
5945
  }
5641
5946
  return [inv[12], inv[13], inv[14]];
5642
5947
  }
5948
+ getViewState() {
5949
+ return {
5950
+ position: [this.cam_pos[0], this.cam_pos[1], this.cam_pos[2]],
5951
+ viewMatrix: gl_matrix_1.mat4.clone(this.vMatrix),
5952
+ translationMatrix: gl_matrix_1.mat4.clone(this.T),
5953
+ rotationMatrix: gl_matrix_1.mat4.clone(this.R),
5954
+ };
5955
+ }
5956
+ restoreViewState(state) {
5957
+ this.cancelFlyTo();
5958
+ this.cam_pos = gl_matrix_1.vec3.fromValues(...state.position);
5959
+ this.vMatrix = gl_matrix_1.mat4.clone(state.viewMatrix);
5960
+ this.T = gl_matrix_1.mat4.clone(state.translationMatrix);
5961
+ this.R = gl_matrix_1.mat4.clone(state.rotationMatrix);
5962
+ this.move = gl_matrix_1.vec3.create();
5963
+ }
5643
5964
  setCameraMatrix(viewMatrix) {
5644
5965
  this.vMatrix = viewMatrix;
5645
5966
  }
@@ -5676,9 +5997,15 @@ class Camera {
5676
5997
  clearRotationLock() {
5677
5998
  this.lockRotX = this.lockRotY = this.lockRotZ = false;
5678
5999
  }
5679
- isRotationLockedX() { return this.lockRotX; }
5680
- isRotationLockedY() { return this.lockRotY; }
5681
- isRotationLockedZ() { return this.lockRotZ; }
6000
+ isRotationLockedX() {
6001
+ return this.lockRotX;
6002
+ }
6003
+ isRotationLockedY() {
6004
+ return this.lockRotY;
6005
+ }
6006
+ isRotationLockedZ() {
6007
+ return this.lockRotZ;
6008
+ }
5682
6009
  }
5683
6010
  exports["default"] = Camera;
5684
6011
 
@@ -5848,7 +6175,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5848
6175
  return (mod && mod.__esModule) ? mod : { "default": mod };
5849
6176
  };
5850
6177
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5851
- 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;
6178
+ 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;
5852
6179
  var AstroViewer_js_1 = __webpack_require__(772);
5853
6180
  Object.defineProperty(exports, "AstroViewer", ({ enumerable: true, get: function () { return AstroViewer_js_1.AstroViewer; } }));
5854
6181
  var HiPSDescriptor_js_1 = __webpack_require__(5087);
@@ -5889,6 +6216,9 @@ Object.defineProperty(exports, "SphericalOpts", ({ enumerable: true, get: functi
5889
6216
  Object.defineProperty(exports, "AstroOpts", ({ enumerable: true, get: function () { return Point_js_1.AstroOpts; } }));
5890
6217
  Object.defineProperty(exports, "PointInitOpts", ({ enumerable: true, get: function () { return Point_js_1.PointInitOpts; } }));
5891
6218
  Object.defineProperty(exports, "CartesianOpts", ({ enumerable: true, get: function () { return Point_js_1.CartesianOpts; } }));
6219
+ var Utils_js_1 = __webpack_require__(7930);
6220
+ Object.defineProperty(exports, "raHMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.raHMSToDeg; } }));
6221
+ Object.defineProperty(exports, "decDMSToDeg", ({ enumerable: true, get: function () { return Utils_js_1.decDMSToDeg; } }));
5892
6222
  var FoVUtils_js_1 = __webpack_require__(8083);
5893
6223
  Object.defineProperty(exports, "FoVUtils", ({ enumerable: true, get: function () { return FoVUtils_js_1.FoVUtils; } }));
5894
6224
  var CoordsType_js_1 = __webpack_require__(8145);
@@ -7880,6 +8210,7 @@ class Source {
7880
8210
  _h_pix;
7881
8211
  _shapesize;
7882
8212
  _brightnessFactor;
8213
+ _mediaStyle;
7883
8214
  /**
7884
8215
  * @param in_point Point.js (Cartesian/RA-Dec wrapper)
7885
8216
  * @param in_details Optional array of key/value metadata
@@ -7888,7 +8219,7 @@ class Source {
7888
8219
  this._point = in_point;
7889
8220
  this._details = in_details;
7890
8221
  this._shapesize = 16.0;
7891
- this._brightnessFactor = -99;
8222
+ this._brightnessFactor = 3.0;
7892
8223
  this.computeHealpixPixel();
7893
8224
  }
7894
8225
  getDetailByindex(index) {
@@ -7932,6 +8263,12 @@ class Source {
7932
8263
  set brightnessFactor(factor) {
7933
8264
  this._brightnessFactor = factor;
7934
8265
  }
8266
+ get mediaStyle() {
8267
+ return this._mediaStyle;
8268
+ }
8269
+ set mediaStyle(style) {
8270
+ this._mediaStyle = style;
8271
+ }
7935
8272
  }
7936
8273
  exports.Source = Source;
7937
8274
 
@@ -8157,6 +8494,9 @@ exports.SphereFoV = SphereFoV;
8157
8494
  *
8158
8495
  * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
8159
8496
  */
8497
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8498
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8499
+ };
8160
8500
  Object.defineProperty(exports, "__esModule", ({ value: true }));
8161
8501
  exports.CatalogueGL = void 0;
8162
8502
  const Source_js_1 = __webpack_require__(146);
@@ -8165,11 +8505,14 @@ const CoordsType_js_1 = __webpack_require__(8145);
8165
8505
  const Utils_js_1 = __webpack_require__(7930);
8166
8506
  const CatalogueShaderProgram_js_1 = __webpack_require__(3559);
8167
8507
  const MetadataManager_js_1 = __webpack_require__(5403);
8508
+ const gl_matrix_1 = __webpack_require__(3890);
8509
+ const healpix_1 = __webpack_require__(4851);
8510
+ const Global_js_1 = __importDefault(__webpack_require__(4382));
8168
8511
  class CatalogueGL {
8169
8512
  _kind = "CatalogueGL";
8170
8513
  static ELEM_SIZE = 6;
8171
8514
  static BYTES_X_ELEM = new Float32Array().BYTES_PER_ELEMENT;
8172
- static STANDARD_SHAPE_SIZE = 10.0;
8515
+ static STANDARD_SHAPE_SIZE = 16.0;
8173
8516
  static STANDARD_SHAPE_HUE = 3.0;
8174
8517
  _ready;
8175
8518
  _name;
@@ -8196,6 +8539,9 @@ class CatalogueGL {
8196
8539
  _providerUrl;
8197
8540
  _catalogueShaderProgram;
8198
8541
  _visibleTilesManager;
8542
+ _mediaOverlayHost = null;
8543
+ _mediaElements = [];
8544
+ _svgMediaDataUrls = new Map();
8199
8545
  constructor(catalogueName, catalogueDescription, providerUrl, metadataManager, webgl, visibleTilesManager) {
8200
8546
  this._webgl = webgl;
8201
8547
  this._ready = false;
@@ -8366,9 +8712,10 @@ class CatalogueGL {
8366
8712
  * @param in_data Rows of TAP results
8367
8713
  * @param columnsmeta TapMetadataList (unused here because `CatalogueProps` already holds indices)
8368
8714
  */
8369
- addSources(in_data, columnsmeta) {
8715
+ addSources(in_data, columnsmeta, options = {}) {
8370
8716
  this._ready = false;
8371
8717
  this._sources = [];
8718
+ this.clearMediaOverlay();
8372
8719
  this._metadataManager = new MetadataManager_js_1.MetadataManager(columnsmeta);
8373
8720
  // const raDataIndex = (this.catalogueProps.raColumn as any).index ?? (this.catalogueProps.raColumn as any)._index;
8374
8721
  // const decDataIndex = (this.catalogueProps.decColumn as any).index ?? (this.catalogueProps.decColumn as any)._index;
@@ -8382,6 +8729,10 @@ class CatalogueGL {
8382
8729
  decDeg: in_data[j][decDataIndex],
8383
8730
  }, CoordsType_js_1.CoordsType.ASTRO);
8384
8731
  const source = new Source_js_1.Source(point, in_data[j]);
8732
+ const mediaStyle = this.extractMediaStyle(in_data[j], columnsmeta, options.mediaColumns);
8733
+ if (mediaStyle) {
8734
+ source.mediaStyle = mediaStyle;
8735
+ }
8385
8736
  // Ensure optional fields exist
8386
8737
  source.shapeSize = source.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
8387
8738
  source.brightnessFactor = 3;
@@ -8401,11 +8752,226 @@ class CatalogueGL {
8401
8752
  this._ready = true;
8402
8753
  this._bufferInitialised = false;
8403
8754
  }
8755
+ getColumnIndex(columnsmeta, columnName) {
8756
+ if (!columnName)
8757
+ return -1;
8758
+ return (columnsmeta.find((column) => column.name === columnName)?.index ?? -1);
8759
+ }
8760
+ readCell(row, columnsmeta, columnName) {
8761
+ const idx = this.getColumnIndex(columnsmeta, columnName);
8762
+ return idx >= 0 ? row[idx] : undefined;
8763
+ }
8764
+ extractMediaStyle(row, columnsmeta, mediaColumns) {
8765
+ if (!mediaColumns?.src)
8766
+ return undefined;
8767
+ const rawSrc = this.readCell(row, columnsmeta, mediaColumns.src);
8768
+ const src = String(rawSrc ?? "").trim();
8769
+ if (!src)
8770
+ return undefined;
8771
+ const rawKind = String(this.readCell(row, columnsmeta, mediaColumns.type) ?? "sprite")
8772
+ .trim()
8773
+ .toLowerCase();
8774
+ const kind = rawKind === "model"
8775
+ ? "model"
8776
+ : rawKind === "image"
8777
+ ? "image"
8778
+ : rawKind === "icon"
8779
+ ? "icon"
8780
+ : rawKind === "circle"
8781
+ ? "circle"
8782
+ : "sprite";
8783
+ if (kind === "circle" || kind === "model")
8784
+ return undefined;
8785
+ const scale = Number(this.readCell(row, columnsmeta, mediaColumns.scale));
8786
+ const rotationDeg = Number(this.readCell(row, columnsmeta, mediaColumns.rotation));
8787
+ const opacity = Number(this.readCell(row, columnsmeta, mediaColumns.opacity));
8788
+ return {
8789
+ kind,
8790
+ src,
8791
+ scale: Number.isFinite(scale) && scale > 0 ? scale : undefined,
8792
+ rotationDeg: Number.isFinite(rotationDeg) ? rotationDeg : undefined,
8793
+ opacity: Number.isFinite(opacity)
8794
+ ? Math.max(0, Math.min(1, opacity))
8795
+ : undefined,
8796
+ };
8797
+ }
8404
8798
  clearSources() {
8405
8799
  this._sources = [];
8406
8800
  this.hoveredIndexes = [];
8407
8801
  this._healpixDensityMap.clear();
8408
8802
  this.vertexCataloguePosition = new Float32Array(0);
8803
+ this.clearMediaOverlay();
8804
+ }
8805
+ clearMediaOverlay() {
8806
+ this._mediaElements.forEach((el) => el.remove());
8807
+ this._mediaElements = [];
8808
+ if (this._mediaOverlayHost) {
8809
+ this._mediaOverlayHost.remove();
8810
+ this._mediaOverlayHost = null;
8811
+ }
8812
+ }
8813
+ ensureMediaOverlayHost() {
8814
+ const canvas = this._webgl?.canvas;
8815
+ const parent = canvas?.parentElement;
8816
+ if (!canvas || !parent)
8817
+ return null;
8818
+ const parentStyle = window.getComputedStyle(parent);
8819
+ if (parentStyle.position === "static") {
8820
+ parent.style.position = "relative";
8821
+ }
8822
+ if (!this._mediaOverlayHost) {
8823
+ const host = document.createElement("div");
8824
+ host.dataset.astroCatalogueMedia = "true";
8825
+ host.style.position = "absolute";
8826
+ host.style.left = `${canvas.offsetLeft}px`;
8827
+ host.style.top = `${canvas.offsetTop}px`;
8828
+ host.style.width = `${canvas.clientWidth}px`;
8829
+ host.style.height = `${canvas.clientHeight}px`;
8830
+ host.style.pointerEvents = "none";
8831
+ host.style.overflow = "hidden";
8832
+ host.style.zIndex = "4";
8833
+ canvas.style.position = canvas.style.position || "relative";
8834
+ canvas.style.zIndex = canvas.style.zIndex || "0";
8835
+ parent.appendChild(host);
8836
+ this._mediaOverlayHost = host;
8837
+ }
8838
+ else {
8839
+ this._mediaOverlayHost.style.left = `${canvas.offsetLeft}px`;
8840
+ this._mediaOverlayHost.style.top = `${canvas.offsetTop}px`;
8841
+ this._mediaOverlayHost.style.width = `${canvas.clientWidth}px`;
8842
+ this._mediaOverlayHost.style.height = `${canvas.clientHeight}px`;
8843
+ }
8844
+ return this._mediaOverlayHost;
8845
+ }
8846
+ ensureMediaElement(index, style) {
8847
+ let img = this._mediaElements[index];
8848
+ if (!img) {
8849
+ img = document.createElement("img");
8850
+ img.loading = "lazy";
8851
+ img.decoding = "async";
8852
+ img.style.position = "absolute";
8853
+ img.style.objectFit = "contain";
8854
+ img.style.pointerEvents = "none";
8855
+ img.style.transformOrigin = "center center";
8856
+ img.style.filter = "drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55))";
8857
+ img.style.willChange = "transform, left, top";
8858
+ this._mediaElements[index] = img;
8859
+ }
8860
+ const src = String(style.src ?? "").trim();
8861
+ const resolvedSrc = src ? new URL(src, document.baseURI).href : "";
8862
+ if (resolvedSrc && img.getAttribute("src") !== resolvedSrc) {
8863
+ img.removeAttribute("data-astro-media-src-index");
8864
+ img.removeAttribute("data-astro-media-loaded");
8865
+ img.onerror = null;
8866
+ img.onload = null;
8867
+ img.setAttribute("src", resolvedSrc);
8868
+ if (/\.svg(?:[?#].*)?$/i.test(resolvedSrc)) {
8869
+ void this.loadSvgMediaAsDataUrl(img, resolvedSrc);
8870
+ }
8871
+ }
8872
+ return img;
8873
+ }
8874
+ async loadSvgMediaAsDataUrl(img, resolvedSrc) {
8875
+ try {
8876
+ let dataUrl = this._svgMediaDataUrls.get(resolvedSrc);
8877
+ if (!dataUrl) {
8878
+ const response = await fetch(resolvedSrc);
8879
+ if (!response.ok)
8880
+ return;
8881
+ const svg = await response.text();
8882
+ if (!/^\s*<svg[\s>]/i.test(svg))
8883
+ return;
8884
+ dataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
8885
+ this._svgMediaDataUrls.set(resolvedSrc, dataUrl);
8886
+ }
8887
+ if (img.getAttribute("src") === resolvedSrc) {
8888
+ img.setAttribute("src", dataUrl);
8889
+ }
8890
+ }
8891
+ catch {
8892
+ // Keep the direct image src; remote servers may already provide a valid MIME type.
8893
+ }
8894
+ }
8895
+ isSourceOnVisibleHemisphere(source, in_mMatrix, vMatrix) {
8896
+ if (Global_js_1.default.insideSphere)
8897
+ return true;
8898
+ const invView = gl_matrix_1.mat4.create();
8899
+ if (!gl_matrix_1.mat4.invert(invView, vMatrix))
8900
+ return true;
8901
+ const cameraPos = gl_matrix_1.vec3.fromValues(invView[12], invView[13], invView[14]);
8902
+ const worldPoint4 = gl_matrix_1.vec4.fromValues(source.point.x, source.point.y, source.point.z, 1);
8903
+ gl_matrix_1.vec4.transformMat4(worldPoint4, worldPoint4, in_mMatrix);
8904
+ const worldPoint = gl_matrix_1.vec3.fromValues(worldPoint4[0], worldPoint4[1], worldPoint4[2]);
8905
+ const normal = gl_matrix_1.vec3.normalize(gl_matrix_1.vec3.create(), worldPoint);
8906
+ const toCamera = gl_matrix_1.vec3.subtract(gl_matrix_1.vec3.create(), cameraPos, worldPoint);
8907
+ return gl_matrix_1.vec3.dot(normal, toCamera) > 0;
8908
+ }
8909
+ updateMediaOverlay(in_mMatrix, vMatrix, pMatrix) {
8910
+ const sourcesWithMedia = this._sources.filter((source) => !!source.mediaStyle?.src);
8911
+ if (!sourcesWithMedia.length) {
8912
+ this.clearMediaOverlay();
8913
+ return;
8914
+ }
8915
+ const host = this.ensureMediaOverlayHost();
8916
+ const canvas = this._webgl.canvas;
8917
+ if (!host || !canvas.clientWidth || !canvas.clientHeight)
8918
+ return;
8919
+ const modelView = gl_matrix_1.mat4.create();
8920
+ const mvp = gl_matrix_1.mat4.create();
8921
+ gl_matrix_1.mat4.multiply(modelView, vMatrix, in_mMatrix);
8922
+ gl_matrix_1.mat4.multiply(mvp, pMatrix, modelView);
8923
+ const width = canvas.clientWidth;
8924
+ const height = canvas.clientHeight;
8925
+ for (let i = 0; i < this._sources.length; i++) {
8926
+ const source = this._sources[i];
8927
+ const style = source.mediaStyle;
8928
+ const img = this._mediaElements[i];
8929
+ if (!style?.src) {
8930
+ if (img)
8931
+ img.style.display = "none";
8932
+ continue;
8933
+ }
8934
+ if (!this.isSourceOnVisibleHemisphere(source, in_mMatrix, vMatrix)) {
8935
+ if (img)
8936
+ img.style.display = "none";
8937
+ continue;
8938
+ }
8939
+ const projected = gl_matrix_1.vec4.fromValues(source.point.x, source.point.y, source.point.z, 1);
8940
+ gl_matrix_1.vec4.transformMat4(projected, projected, mvp);
8941
+ if (!projected[3]) {
8942
+ if (img)
8943
+ img.style.display = "none";
8944
+ continue;
8945
+ }
8946
+ const ndcX = projected[0] / projected[3];
8947
+ const ndcY = projected[1] / projected[3];
8948
+ const ndcZ = projected[2] / projected[3];
8949
+ if (ndcX < -1.15 ||
8950
+ ndcX > 1.15 ||
8951
+ ndcY < -1.15 ||
8952
+ ndcY > 1.15 ||
8953
+ ndcZ < -1.15 ||
8954
+ ndcZ > 1.15) {
8955
+ if (img)
8956
+ img.style.display = "none";
8957
+ continue;
8958
+ }
8959
+ const mediaEl = this.ensureMediaElement(i, style);
8960
+ if (!mediaEl.parentElement)
8961
+ host.appendChild(mediaEl);
8962
+ const px = (ndcX * 0.5 + 0.5) * width;
8963
+ const py = (1 - (ndcY * 0.5 + 0.5)) * height;
8964
+ const baseSize = source.shapeSize || CatalogueGL.STANDARD_SHAPE_SIZE;
8965
+ const size = Math.max(8, baseSize * (style.scale ?? 2.4));
8966
+ const rotation = style.rotationDeg ?? 0;
8967
+ mediaEl.style.display = "block";
8968
+ mediaEl.style.left = `${px - size / 2}px`;
8969
+ mediaEl.style.top = `${py - size / 2}px`;
8970
+ mediaEl.style.width = `${size}px`;
8971
+ mediaEl.style.height = `${size}px`;
8972
+ mediaEl.style.opacity = `${style.opacity ?? 1}`;
8973
+ mediaEl.style.transform = `rotate(${rotation}deg)`;
8974
+ }
8409
8975
  }
8410
8976
  sourceMatches(left, right) {
8411
8977
  if (left === right)
@@ -8517,33 +9083,46 @@ class CatalogueGL {
8517
9083
  this._webgl.bufferData(this._webgl.ARRAY_BUFFER, this.vertexCataloguePosition, this._webgl.STATIC_DRAW);
8518
9084
  this._bufferInitialised = true;
8519
9085
  }
8520
- getSelectionRadius() {
8521
- const order = this._visibleTilesManager.getVisibleOrder();
8522
- switch (order) {
8523
- case 0:
8524
- case 1:
8525
- case 2:
8526
- // return 0.005;
8527
- return 0.01;
8528
- case 3:
8529
- // return 0.001;
8530
- case 4:
8531
- // return 0.0009;
8532
- case 5:
8533
- // return 0.0005;
8534
- return 0.005;
8535
- case 6:
8536
- // return 0.0001;
8537
- case 7:
8538
- // return 0.00009;
8539
- case 8:
8540
- // return 0.00005;
8541
- return 0.001;
8542
- case 9:
8543
- return 0.0005;
8544
- default:
8545
- return 0.0001;
9086
+ static MIN_HIT_RADIUS_PX = 6;
9087
+ static HIT_PADDING_PX = 3;
9088
+ static CANDIDATE_RADIUS_PX = 16;
9089
+ getCanvas() {
9090
+ return this._webgl.canvas;
9091
+ }
9092
+ getHitRadiusPx(source) {
9093
+ const markerSize = source.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
9094
+ return Math.max(CatalogueGL.MIN_HIT_RADIUS_PX, markerSize / 2 + CatalogueGL.HIT_PADDING_PX);
9095
+ }
9096
+ getCandidateSearchRadiusRad(pMatrix) {
9097
+ const canvas = this._webgl.canvas;
9098
+ const width = Math.max(1, canvas.clientWidth);
9099
+ const height = Math.max(1, canvas.clientHeight);
9100
+ // pMatrix[0] and pMatrix[5] are the perspective scale factors for X/Y.
9101
+ // Convert a small screen-space radius to a conservative angular radius.
9102
+ const fovXRad = 2 * Math.atan(1 / Math.abs(pMatrix[0]));
9103
+ const fovYRad = 2 * Math.atan(1 / Math.abs(pMatrix[5]));
9104
+ const radPerPixel = Math.max(fovXRad / width, fovYRad / height);
9105
+ return CatalogueGL.CANDIDATE_RADIUS_PX * radPerPixel;
9106
+ }
9107
+ projectPointToScreen(point, mvp, width, height) {
9108
+ const projected = gl_matrix_1.vec4.fromValues(point.x, point.y, point.z, 1);
9109
+ gl_matrix_1.vec4.transformMat4(projected, projected, mvp);
9110
+ if (projected[3] <= 0)
9111
+ return null;
9112
+ const ndcX = projected[0] / projected[3];
9113
+ const ndcY = projected[1] / projected[3];
9114
+ return [(ndcX * 0.5 + 0.5) * width, (1 - (ndcY * 0.5 + 0.5)) * height];
9115
+ }
9116
+ clearSelection() {
9117
+ for (const idx of this.selectedIndexes) {
9118
+ const base = idx * CatalogueGL.ELEM_SIZE;
9119
+ if (base + 4 >= this.vertexCataloguePosition.length)
9120
+ continue;
9121
+ this.vertexCataloguePosition[base + 3] = 0.0;
9122
+ this.vertexCataloguePosition[base + 4] =
9123
+ this._sources[idx]?.shapeSize ?? CatalogueGL.STANDARD_SHAPE_SIZE;
8546
9124
  }
9125
+ this.selectedIndexes = [];
8547
9126
  }
8548
9127
  setSelectedIndexes(selectedIndex) {
8549
9128
  selectedIndex.forEach((idx) => {
@@ -8570,8 +9149,8 @@ class CatalogueGL {
8570
9149
  * Run click-picking and update selection with the nearest candidate in current pixel.
8571
9150
  * Returns the selected source or null if no source was hit.
8572
9151
  */
8573
- getSourcesFromPointer(in_mouseHelper) {
8574
- const pickedIndexes = this.checkClicking(in_mouseHelper);
9152
+ getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9153
+ const pickedIndexes = this.checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8575
9154
  if (!pickedIndexes.length) {
8576
9155
  return {
8577
9156
  sources: [],
@@ -8590,8 +9169,8 @@ class CatalogueGL {
8590
9169
  * Run click-picking and update selection with the nearest candidate in current pixel.
8591
9170
  * Returns the selected source or null if no source was hit.
8592
9171
  */
8593
- selectPrimarySourceFromClick(in_mouseHelper) {
8594
- const picked = this.getSourcesFromPointer(in_mouseHelper);
9172
+ selectPrimarySourceFromClick(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9173
+ const picked = this.getSourcesFromPointer(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8595
9174
  const clickedIndexes = picked?.pickedIndexes ?? [];
8596
9175
  // if (!clickedIndexes.length) {
8597
9176
  // this.setSelectedIndexes([]);
@@ -8627,42 +9206,65 @@ class CatalogueGL {
8627
9206
  const idx = this.hoveredIndexes[0];
8628
9207
  return this._sources[idx] ?? null;
8629
9208
  }
8630
- findNearestSourceIndex(in_mouseHelper) {
8631
- if (in_mouseHelper.x == null ||
8632
- in_mouseHelper.y == null ||
8633
- in_mouseHelper.z == null) {
9209
+ findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9210
+ if (in_mouseHelper.xyz == null)
8634
9211
  return null;
9212
+ const healpix = Global_js_1.default.getHealpix(Global_js_1.default.selectionOrder);
9213
+ const mouseVec = new healpix_1.Vec3(...in_mouseHelper.xyz);
9214
+ const mousePointing = new healpix_1.Pointing(mouseVec, false);
9215
+ const searchRadiusRad = this.getCandidateSearchRadiusRad(pMatrix);
9216
+ const rangeSet = healpix.queryDiscInclusive(mousePointing, searchRadiusRad, 4);
9217
+ const pixels = rangeSet.r;
9218
+ const candidateIndexes = new Set();
9219
+ for (const pixel of pixels) {
9220
+ const indexes = this._healpixDensityMap.get(pixel);
9221
+ if (!indexes)
9222
+ continue;
9223
+ indexes.forEach((idx) => candidateIndexes.add(idx));
8635
9224
  }
8636
- const mousePix = in_mouseHelper.computeNpix();
8637
- if (mousePix == null)
9225
+ if (!candidateIndexes.size)
9226
+ return null;
9227
+ const canvas = this._webgl.canvas;
9228
+ const width = canvas.clientWidth;
9229
+ const height = canvas.clientHeight;
9230
+ if (!width || !height)
8638
9231
  return null;
8639
- const candidates = this._healpixDensityMap.get(mousePix);
8640
- if (!candidates?.length)
9232
+ const modelView = gl_matrix_1.mat4.create();
9233
+ const mvp = gl_matrix_1.mat4.create();
9234
+ gl_matrix_1.mat4.multiply(modelView, vMatrix, in_mMatrix);
9235
+ gl_matrix_1.mat4.multiply(mvp, pMatrix, modelView);
9236
+ const mousePoint = new Point_js_1.Point({
9237
+ x: in_mouseHelper.xyz[0],
9238
+ y: in_mouseHelper.xyz[1],
9239
+ z: in_mouseHelper.xyz[2],
9240
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
9241
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
9242
+ if (!mouseScreen)
8641
9243
  return null;
8642
- const selR = this.getSelectionRadius();
8643
9244
  let nearestIndex = null;
8644
- let nearestDistance = Number.POSITIVE_INFINITY;
8645
- for (const sourceIdx of candidates) {
9245
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
9246
+ for (const sourceIdx of candidateIndexes) {
8646
9247
  const source = this._sources[sourceIdx];
8647
9248
  if (!source)
8648
9249
  continue;
8649
- const dx = source.point.x - in_mouseHelper.x;
8650
- const dy = source.point.y - in_mouseHelper.y;
8651
- const dz = source.point.z - in_mouseHelper.z;
8652
- const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
8653
- if (dist <= selR && dist < nearestDistance) {
8654
- nearestDistance = dist;
9250
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
9251
+ if (!sourceScreen)
9252
+ continue;
9253
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
9254
+ if (distancePx <= this.getHitRadiusPx(source) &&
9255
+ distancePx < nearestDistancePx) {
9256
+ nearestDistancePx = distancePx;
8655
9257
  nearestIndex = sourceIdx;
8656
9258
  }
8657
9259
  }
8658
9260
  return nearestIndex;
8659
9261
  }
8660
- checkClicking(in_mouseHelper) {
8661
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9262
+ checkClicking(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9263
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8662
9264
  return nearestIndex == null ? [] : [nearestIndex];
8663
9265
  }
8664
- checkHovering(in_mouseHelper) {
8665
- const nearestIndex = this.findNearestSourceIndex(in_mouseHelper);
9266
+ checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix) {
9267
+ const nearestIndex = this.findNearestSourceIndex(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8666
9268
  return nearestIndex == null ? [] : [nearestIndex];
8667
9269
  }
8668
9270
  /**
@@ -8698,6 +9300,13 @@ class CatalogueGL {
8698
9300
  if (this._catalogueShaderProgram.locations.color) {
8699
9301
  this._webgl.uniform4f(this._catalogueShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], 1.0);
8700
9302
  }
9303
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
9304
+ if (this._catalogueShaderProgram.locations.hoverColor) {
9305
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.hoverColor, ...interactionColors.hover, 1.0);
9306
+ }
9307
+ if (this._catalogueShaderProgram.locations.selectedColor) {
9308
+ this._webgl.uniform4f(this._catalogueShaderProgram.locations.selectedColor, ...interactionColors.selected, 1.0);
9309
+ }
8701
9310
  // selected flags
8702
9311
  for (let s = 0; s < this.selectedIndexes.length; s++) {
8703
9312
  const idx = this.selectedIndexes[s];
@@ -8708,7 +9317,8 @@ class CatalogueGL {
8708
9317
  // clear old hovered
8709
9318
  for (let k = 0; k < this.hoveredIndexes.length; k++) {
8710
9319
  const base = this.hoveredIndexes[k] * CatalogueGL.ELEM_SIZE;
8711
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9320
+ if (this.selectedIndexes.includes(this.hoveredIndexes[k]))
9321
+ continue;
8712
9322
  this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
8713
9323
  this.vertexCataloguePosition[base + 4] =
8714
9324
  this._sources[this.hoveredIndexes[k]].shapeSize; // size
@@ -8722,7 +9332,7 @@ class CatalogueGL {
8722
9332
  // this.vertexCataloguePosition[base + 3] = 0.0; // not hovered
8723
9333
  // this.vertexCataloguePosition[base + 4] = this._sources[this.hoveredIndexes[k]].shapeSize; // size
8724
9334
  // }
8725
- this.hoveredIndexes = this.checkHovering(in_mouseHelper);
9335
+ this.hoveredIndexes = this.checkHovering(in_mouseHelper, in_mMatrix, vMatrix, pMatrix);
8726
9336
  // // new hovered
8727
9337
  // for (let i = 0; i < this.hoveredIndexes.length; i++) {
8728
9338
  // const idx = this.hoveredIndexes[i];
@@ -8736,7 +9346,8 @@ class CatalogueGL {
8736
9346
  for (let i = 0; i < this.hoveredIndexes.length; i++) {
8737
9347
  const idx = this.hoveredIndexes[i];
8738
9348
  const base = idx * CatalogueGL.ELEM_SIZE;
8739
- // if (this.vertexCataloguePosition[base + 3] == 2.0) continue; // selected, skip hover
9349
+ if (this.selectedIndexes.includes(idx))
9350
+ continue;
8740
9351
  this.vertexCataloguePosition[base + 3] = 1.0; // hovered
8741
9352
  this.vertexCataloguePosition[base + 4] = this._sources[idx].shapeSize; // size
8742
9353
  }
@@ -8745,6 +9356,7 @@ class CatalogueGL {
8745
9356
  // draw
8746
9357
  const numItems = this.vertexCataloguePosition.length / CatalogueGL.ELEM_SIZE;
8747
9358
  this._webgl.drawArrays(this._webgl.POINTS, 0, numItems);
9359
+ this.updateMediaOverlay(in_mMatrix, vMatrix, pMatrix);
8748
9360
  this._oldMouseCoords = in_mouseHelper.xyz;
8749
9361
  }
8750
9362
  }
@@ -9139,6 +9751,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9139
9751
  _latLonGrid;
9140
9752
  _colorMapIdx = 0;
9141
9753
  _colorMap = ColorMaps_js_1.ColorMaps['native'];
9754
+ _opacity = 1;
9142
9755
  constructor(radius, position, xrad, yrad, descriptor, webgl, gridLabelContainers) {
9143
9756
  super(radius, position, xrad, yrad, descriptor.name, webgl, false);
9144
9757
  this._descriptor = descriptor;
@@ -9153,6 +9766,10 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9153
9766
  const fov = 180;
9154
9767
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9155
9768
  }
9769
+ setOpacity(opacity) {
9770
+ this._opacity = Math.min(1, Math.max(0, opacity));
9771
+ this._xyzShaderProgram.setOpacity(this._opacity);
9772
+ }
9156
9773
  changeColorMap(colorMap) {
9157
9774
  this._colorMap = colorMap;
9158
9775
  switch (colorMap.name) {
@@ -9200,6 +9817,9 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9200
9817
  initShaders() {
9201
9818
  this._xyzShaderProgram.enableProgram();
9202
9819
  }
9820
+ clearLonLatGridLabels() {
9821
+ this._latLonGrid.clearLabels();
9822
+ }
9203
9823
  isLonLatGridVisible() {
9204
9824
  return this._latLonGrid.isVisible();
9205
9825
  }
@@ -9214,7 +9834,7 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9214
9834
  // this._zoom = this.resolveVisibleZoom(fov)
9215
9835
  this._zoom = XYZFoVHelper_js_1.xyzFovHelper.getZoom(fov);
9216
9836
  }
9217
- draw(input) {
9837
+ draw(input, asOverlay = false) {
9218
9838
  const vMatrix = input.camera.getCameraMatrix();
9219
9839
  if (!vMatrix)
9220
9840
  return;
@@ -9224,6 +9844,18 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9224
9844
  this.refresh(input);
9225
9845
  const mMatrix = this.getModelMatrix();
9226
9846
  this._xyzShaderProgram.setRuntimeColorMap(this._colorMap);
9847
+ this._xyzShaderProgram.setOpacity(this._opacity);
9848
+ const depthTestWasEnabled = this._webgl.isEnabled(this._webgl.DEPTH_TEST);
9849
+ const blendWasEnabled = this._webgl.isEnabled(this._webgl.BLEND);
9850
+ const blendSrcRgb = this._webgl.getParameter(this._webgl.BLEND_SRC_RGB);
9851
+ const blendDstRgb = this._webgl.getParameter(this._webgl.BLEND_DST_RGB);
9852
+ const blendSrcAlpha = this._webgl.getParameter(this._webgl.BLEND_SRC_ALPHA);
9853
+ const blendDstAlpha = this._webgl.getParameter(this._webgl.BLEND_DST_ALPHA);
9854
+ if (asOverlay) {
9855
+ this._webgl.disable(this._webgl.DEPTH_TEST);
9856
+ this._webgl.enable(this._webgl.BLEND);
9857
+ this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
9858
+ }
9227
9859
  const tileSelection = this._visibleTilesManager.computeVisibleTiles(this._zoom, this, this._webgl, input.camera, input.pMatrix);
9228
9860
  const visibleTiles = tileSelection.visibleTiles;
9229
9861
  const ancestorsMap = tileSelection.ancestorsMap;
@@ -9241,7 +9873,20 @@ class XYZMap extends AbstractSkyEntity_js_1.AbstractSkyEntity {
9241
9873
  const ancestorTile = this.findBestAvailableAncestor(tile.coord);
9242
9874
  ancestorTile?.draw(tileSelection.currentZoom, [tile.coord], ancestorsMap, pMatrix, vMatrix, mMatrix, this._colorMapIdx);
9243
9875
  }
9244
- this._latLonGrid.draw(input);
9876
+ if (asOverlay) {
9877
+ if (depthTestWasEnabled)
9878
+ this._webgl.enable(this._webgl.DEPTH_TEST);
9879
+ else
9880
+ this._webgl.disable(this._webgl.DEPTH_TEST);
9881
+ this._webgl.blendFuncSeparate(blendSrcRgb, blendDstRgb, blendSrcAlpha, blendDstAlpha);
9882
+ if (blendWasEnabled)
9883
+ this._webgl.enable(this._webgl.BLEND);
9884
+ else
9885
+ this._webgl.disable(this._webgl.BLEND);
9886
+ }
9887
+ else {
9888
+ this._latLonGrid.draw(input);
9889
+ }
9245
9890
  }
9246
9891
  createTile(coord) {
9247
9892
  return new XYZAnchestorTile_js_1.XYZAnchestorTile(coord, this.resolveTileUrl(coord), this._webgl, this._xyzShaderProgram, this._meshBuilder, this._descriptor.segmentsPerSide);
@@ -10527,6 +11172,10 @@ class FootprintSetGL {
10527
11172
  }
10528
11173
  return clickedIndexes;
10529
11174
  }
11175
+ clearSelection() {
11176
+ this.selectedIndexes = [];
11177
+ this.refreshSelectedFootprints();
11178
+ }
10530
11179
  setSelectedIndexes(selectedIndex) {
10531
11180
  selectedIndex.forEach((idx) => {
10532
11181
  if (idx < 0 || idx >= this.footprintPolygons.length)
@@ -10828,9 +11477,10 @@ class FootprintSetGL {
10828
11477
  if (in_mouseHelper != null && in_mouseHelper.xyz != this.oldMouseCoords) {
10829
11478
  this.checkSelection(in_mouseHelper);
10830
11479
  }
11480
+ const interactionColors = (0, Utils_js_1.interactionColorsFromHex)(this._shapeColor);
10831
11481
  if (this._hoveredFootprints.length > 0) {
10832
11482
  // TODO POINT_SIZE doesn't have any effect on line thickness!! it only applies to points
10833
- const rgb = (0, Utils_js_1.colorHex2RGB)("#00FF00");
11483
+ const rgb = interactionColors.hover;
10834
11484
  const alpha = 1.0;
10835
11485
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
10836
11486
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -10845,7 +11495,7 @@ class FootprintSetGL {
10845
11495
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.hoveredVertexPosition.length / 3 + this.nHoveredPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
10846
11496
  }
10847
11497
  if (this._selectedFootprints.length > 0) {
10848
- const rgb = (0, Utils_js_1.colorHex2RGB)("#ECB462");
11498
+ const rgb = interactionColors.selected;
10849
11499
  const alpha = 1.0;
10850
11500
  this._webgl.uniform4f(this._footprintShaderProgram.locations.color, rgb[0], rgb[1], rgb[2], alpha);
10851
11501
  this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 14.0); // <--- POINT_SIZE in LINE_LOOP is not applicable
@@ -10858,6 +11508,11 @@ class FootprintSetGL {
10858
11508
  this._webgl.bufferData(this._webgl.ELEMENT_ARRAY_BUFFER, this.selectedElementIndexes, this._webgl.STATIC_DRAW);
10859
11509
  // this._gl.drawElements (this._gl.LINE_LOOP, this._selectedVertexPosition.length / 3 + this._nSlectedPrimitiveFlags,this._gl.UNSIGNED_SHORT, 0);
10860
11510
  this._webgl.drawElements(this._webgl.LINE_LOOP, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
11511
+ // Keep selection visually distinct from hover without relying on
11512
+ // non-portable line widths. Reuse the selected geometry as small
11513
+ // vertex markers; POINT_SIZE is supported for POINTS in WebGL2.
11514
+ this._webgl.uniform1f(this._footprintShaderProgram.locations.pointSize, 4.0);
11515
+ this._webgl.drawElements(this._webgl.POINTS, this.selectedVertexPosition.length / 3 + this.nSlectedPrimitiveFlags, this._webgl.UNSIGNED_INT, 0);
10861
11516
  }
10862
11517
  this._webgl.bindBuffer(this._webgl.ARRAY_BUFFER, this.vertexCataloguePositionBuffer);
10863
11518
  this._webgl.vertexAttribPointer(this._footprintShaderProgram.locations.position, FootprintSetGL.ELEM_SIZE, this._webgl.FLOAT, false, FootprintSetGL.BYTES_X_ELEM * FootprintSetGL.ELEM_SIZE, 0);
@@ -11090,6 +11745,9 @@ class EquatorialGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11090
11745
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
11091
11746
  }
11092
11747
  }
11748
+ clearLabels() {
11749
+ this.gridText.resetDivSets();
11750
+ }
11093
11751
  isVisible() {
11094
11752
  return this.showGrid;
11095
11753
  }
@@ -11684,6 +12342,9 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11684
12342
  this.gridText.resetDivSets();
11685
12343
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
11686
12344
  }
12345
+ clearLabels() {
12346
+ this.gridText.resetDivSets();
12347
+ }
11687
12348
  get visibleorder() {
11688
12349
  return this._visibleorder;
11689
12350
  }
@@ -11917,6 +12578,9 @@ class LatLonGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11917
12578
  gl.uniform4f(uColor, rgb[0], rgb[1], rgb[2], 1.0);
11918
12579
  }
11919
12580
  }
12581
+ clearLabels() {
12582
+ this.gridText.resetDivSets();
12583
+ }
11920
12584
  draw(input) {
11921
12585
  if (!this._showGrid) {
11922
12586
  this.gridText.resetDivSets();
@@ -14574,6 +15238,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14574
15238
  _currentOrder;
14575
15239
  _visibleTiles = [];
14576
15240
  _coverageTiles = [];
15241
+ _color;
15242
+ _wireframe;
14577
15243
  constructor(radius, position, xrad, yrad, _descriptor, webgl, _healpixGrid) {
14578
15244
  super(radius, position, xrad, yrad, _descriptor.name, webgl, false);
14579
15245
  this._descriptor = _descriptor;
@@ -14582,6 +15248,8 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14582
15248
  this._shaderProgram = new MeshHiPSShaderProgram_js_1.MeshHiPSShaderProgram(webgl);
14583
15249
  this._shaderProgram.enableProgram();
14584
15250
  this._currentOrder = _descriptor.selectedOrder;
15251
+ this._color = [..._descriptor.color];
15252
+ this._wireframe = _descriptor.wireframe;
14585
15253
  }
14586
15254
  get currentOrder() {
14587
15255
  return this._currentOrder;
@@ -14604,6 +15272,12 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14604
15272
  getProperty(key) {
14605
15273
  return this._descriptor.getProperty(key);
14606
15274
  }
15275
+ setColor(color) {
15276
+ this._color = [...color];
15277
+ }
15278
+ setWireframe(wireframe) {
15279
+ this._wireframe = wireframe;
15280
+ }
14607
15281
  refreshOrder(fovDeg) {
14608
15282
  if (this._descriptor.fixedOrder) {
14609
15283
  this._currentOrder = this._descriptor.selectedOrder;
@@ -14643,7 +15317,7 @@ class MeshHiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
14643
15317
  for (const coord of byOrder.get(order) ?? []) {
14644
15318
  this._tiles
14645
15319
  .get(this.tileKey(coord))
14646
- ?.draw(pMatrix, vMatrix, mMatrix, this._descriptor.color, this._descriptor.wireframe);
15320
+ ?.draw(pMatrix, vMatrix, mMatrix, this._color, this._wireframe);
14647
15321
  }
14648
15322
  }
14649
15323
  if (wasCullFace)
@@ -16424,6 +17098,7 @@ exports.TerraFootprintSetGL = TerraFootprintSetGL;
16424
17098
  Object.defineProperty(exports, "__esModule", ({ value: true }));
16425
17099
  exports.TerraPointSetGL = void 0;
16426
17100
  const CatalogueGL_js_1 = __webpack_require__(1232);
17101
+ const gl_matrix_1 = __webpack_require__(3890);
16427
17102
  const MetadataManager_js_1 = __webpack_require__(5403);
16428
17103
  const Point_js_1 = __webpack_require__(6553);
16429
17104
  const CoordsType_js_1 = __webpack_require__(8145);
@@ -16458,8 +17133,45 @@ class TerraPointSetGL extends CatalogueGL_js_1.CatalogueGL {
16458
17133
  this._ready = true;
16459
17134
  this._bufferInitialised = false;
16460
17135
  }
17136
+ findNearestSourceIndex(inMouseHelper, inMatrix, viewMatrix, projectionMatrix) {
17137
+ if (inMouseHelper.xyz == null)
17138
+ return null;
17139
+ const canvas = this.getCanvas();
17140
+ const width = canvas.clientWidth;
17141
+ const height = canvas.clientHeight;
17142
+ if (!width || !height)
17143
+ return null;
17144
+ const modelView = gl_matrix_1.mat4.create();
17145
+ const mvp = gl_matrix_1.mat4.create();
17146
+ gl_matrix_1.mat4.multiply(modelView, viewMatrix, inMatrix);
17147
+ gl_matrix_1.mat4.multiply(mvp, projectionMatrix, modelView);
17148
+ const mousePoint = new Point_js_1.Point({
17149
+ x: inMouseHelper.xyz[0],
17150
+ y: inMouseHelper.xyz[1],
17151
+ z: inMouseHelper.xyz[2],
17152
+ }, CoordsType_js_1.CoordsType.CARTESIAN);
17153
+ const mouseScreen = this.projectPointToScreen(mousePoint, mvp, width, height);
17154
+ if (!mouseScreen)
17155
+ return null;
17156
+ let nearestIndex = null;
17157
+ let nearestDistancePx = Number.POSITIVE_INFINITY;
17158
+ for (let index = 0; index < this._sources.length; index += 1) {
17159
+ const source = this._sources[index];
17160
+ if (!source)
17161
+ continue;
17162
+ const sourceScreen = this.projectPointToScreen(source.point, mvp, width, height);
17163
+ if (!sourceScreen)
17164
+ continue;
17165
+ const distancePx = Math.hypot(sourceScreen[0] - mouseScreen[0], sourceScreen[1] - mouseScreen[1]);
17166
+ if (distancePx <= this.getHitRadiusPx(source) &&
17167
+ distancePx < nearestDistancePx) {
17168
+ nearestDistancePx = distancePx;
17169
+ nearestIndex = index;
17170
+ }
17171
+ }
17172
+ return nearestIndex;
17173
+ }
16461
17174
  draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix) {
16462
- this._oldMouseCoords = inMouseHelper?.xyz ?? null;
16463
17175
  super.draw(inMatrix, inMouseHelper, viewMatrix, projectionMatrix);
16464
17176
  }
16465
17177
  }
@@ -16689,6 +17401,8 @@ class CatalogueShaderProgram {
16689
17401
  this._webgl = webgl;
16690
17402
  this.gl_uniforms = {
16691
17403
  vertex_color: 'u_fragcolor',
17404
+ hover_color: 'u_hovercolor',
17405
+ selected_color: 'u_selectedcolor',
16692
17406
  m_perspective: 'uPMatrix',
16693
17407
  m_model_view: 'uMVMatrix'
16694
17408
  };
@@ -16702,6 +17416,8 @@ class CatalogueShaderProgram {
16702
17416
  pMatrix: null,
16703
17417
  mvMatrix: null,
16704
17418
  color: null,
17419
+ hoverColor: null,
17420
+ selectedColor: null,
16705
17421
  position: -1,
16706
17422
  hovered: -1,
16707
17423
  pointSize: -1,
@@ -16751,6 +17467,8 @@ class CatalogueShaderProgram {
16751
17467
  this.locations.pointSize = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_size);
16752
17468
  this.locations.brightness = gl.getAttribLocation(this.shaderProgram, this.gl_attributes.point_hue);
16753
17469
  this.locations.color = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.vertex_color);
17470
+ this.locations.hoverColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.hover_color);
17471
+ this.locations.selectedColor = gl.getUniformLocation(this.shaderProgram, this.gl_uniforms.selected_color);
16754
17472
  }
16755
17473
  enableShaders(pMatrix, modelMatrix, viewMatrix) {
16756
17474
  // const gl = global.gl as GL
@@ -17436,6 +18154,8 @@ class ShaderManager {
17436
18154
  in float v_brightness;
17437
18155
 
17438
18156
  uniform vec4 u_fragcolor;
18157
+ uniform vec4 u_hovercolor;
18158
+ uniform vec4 u_selectedcolor;
17439
18159
 
17440
18160
  out vec4 fragColor;
17441
18161
 
@@ -17492,11 +18212,12 @@ class ShaderManager {
17492
18212
  #endif
17493
18213
 
17494
18214
  if (v_selected == 1.0){
17495
- // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
17496
- fragColor = vec4(1.0, 0.0, 0.0, 1.0) * (alpha);
18215
+ fragColor = u_hovercolor * alpha;
17497
18216
  } else if (v_selected == 2.0){
17498
- // gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
17499
- fragColor = vec4(1.0, 1.0, 0.0, 1.0) * (alpha);
18217
+ // Persistent selection: keep the dataset colour as an outer ring
18218
+ // and use the hover colour for the fill. This remains visible for
18219
+ // both light and dark user-selected base colours.
18220
+ fragColor = (r > 0.72 ? u_fragcolor : u_hovercolor) * alpha;
17500
18221
  }else{
17501
18222
  if (r < 0.4) {
17502
18223
  discard;
@@ -17903,6 +18624,7 @@ class XYZShaderProgram {
17903
18624
  _shaderProgram;
17904
18625
  _colorMapBlockIndex = null;
17905
18626
  _colorMapBuffer = null;
18627
+ _opacity = 1;
17906
18628
  _runtimeColorMap;
17907
18629
  _colorMapVariableInfo = {
17908
18630
  r_palette: { index: 0, offset: 0 },
@@ -17917,6 +18639,7 @@ class XYZShaderProgram {
17917
18639
  vMatrix: null,
17918
18640
  sampler: null,
17919
18641
  colorMapIdx: null,
18642
+ opacity: null,
17920
18643
  vertexPositionAttribute: -1,
17921
18644
  textureCoordAttribute: -1,
17922
18645
  };
@@ -17937,6 +18660,9 @@ class XYZShaderProgram {
17937
18660
  enableProgram() {
17938
18661
  this._webgl.useProgram(this.shaderProgram);
17939
18662
  }
18663
+ setOpacity(opacity) {
18664
+ this._opacity = Math.min(1, Math.max(0, opacity));
18665
+ }
17940
18666
  setRuntimeColorMap(colorMap) {
17941
18667
  this._runtimeColorMap = colorMap;
17942
18668
  }
@@ -17949,6 +18675,7 @@ class XYZShaderProgram {
17949
18675
  this.locations.vMatrix = gl.getUniformLocation(program, 'uVMatrix');
17950
18676
  this.locations.sampler = gl.getUniformLocation(program, 'uSampler');
17951
18677
  this.locations.colorMapIdx = gl.getUniformLocation(program, 'cmapIdx');
18678
+ this.locations.opacity = gl.getUniformLocation(program, 'uOpacity');
17952
18679
  this.locations.vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
17953
18680
  this.locations.textureCoordAttribute = gl.getAttribLocation(program, 'aTextureCoord');
17954
18681
  gl.uniformMatrix4fv(this.locations.pMatrix, false, pMatrix);
@@ -17956,6 +18683,7 @@ class XYZShaderProgram {
17956
18683
  gl.uniformMatrix4fv(this.locations.mMatrix, false, mMatrix);
17957
18684
  gl.uniform1i(this.locations.sampler, 0);
17958
18685
  gl.uniform1i(this.locations.colorMapIdx, colorMapIdx);
18686
+ gl.uniform1f(this.locations.opacity, this._opacity);
17959
18687
  if (colorMapIdx >= 2) {
17960
18688
  this.uploadColorMap(colorMapIdx);
17961
18689
  }
@@ -17978,6 +18706,7 @@ class XYZShaderProgram {
17978
18706
  in vec2 vTextureCoord;
17979
18707
  uniform sampler2D uSampler;
17980
18708
  uniform int cmapIdx;
18709
+ uniform float uOpacity;
17981
18710
 
17982
18711
  layout (std140) uniform colormap {
17983
18712
  float r_palette[256];
@@ -17992,7 +18721,7 @@ class XYZShaderProgram {
17992
18721
 
17993
18722
  if (cmapIdx == 1) {
17994
18723
  float gray = 0.21 * color.r + 0.71 * color.g + 0.07 * color.b;
17995
- outColor = vec4(vec3(gray), color.a);
18724
+ outColor = vec4(vec3(gray), color.a * uOpacity);
17996
18725
  return;
17997
18726
  }
17998
18727
 
@@ -18005,12 +18734,12 @@ class XYZShaderProgram {
18005
18734
  r_palette[rIndex] / 256.0,
18006
18735
  g_palette[gIndex] / 256.0,
18007
18736
  b_palette[bIndex] / 256.0,
18008
- color.a
18737
+ color.a * uOpacity
18009
18738
  );
18010
18739
  return;
18011
18740
  }
18012
18741
 
18013
- outColor = color;
18742
+ outColor = vec4(color.rgb, color.a * uOpacity);
18014
18743
  }`);
18015
18744
  gl.attachShader(this._shaderProgram, vertexShader);
18016
18745
  gl.attachShader(this._shaderProgram, fragmentShader);
@@ -19212,7 +19941,6 @@ exports["default"] = STCSParser;
19212
19941
  /***/ 7930
19213
19942
  (__unused_webpack_module, exports, __webpack_require__) {
19214
19943
 
19215
- var __webpack_unused_export__;
19216
19944
 
19217
19945
  /*
19218
19946
  * AstroViewer
@@ -19224,9 +19952,10 @@ var __webpack_unused_export__;
19224
19952
  *
19225
19953
  * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
19226
19954
  */
19227
- __webpack_unused_export__ = ({ value: true });
19955
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
19228
19956
  exports.cartesianToSpherical = cartesianToSpherical;
19229
19957
  exports.colorHex2RGB = colorHex2RGB;
19958
+ exports.interactionColorsFromHex = interactionColorsFromHex;
19230
19959
  exports.degToRad = degToRad;
19231
19960
  exports.radToDeg = radToDeg;
19232
19961
  exports.sphericalToAstroDeg = sphericalToAstroDeg;
@@ -19234,6 +19963,8 @@ exports.sphericalToCartesian = sphericalToCartesian;
19234
19963
  exports.astroDegToSpherical = astroDegToSpherical;
19235
19964
  exports.raDegToHMS = raDegToHMS;
19236
19965
  exports.decDegToDMS = decDegToDMS;
19966
+ exports.raHMSToDeg = raHMSToDeg;
19967
+ exports.decDMSToDeg = decDMSToDeg;
19237
19968
  /**
19238
19969
  * @author Fabrizio Giordano (Fab)
19239
19970
  */
@@ -19264,6 +19995,25 @@ function colorHex2RGB(hexColor) {
19264
19995
  const rgb3 = (dec3 / 255).toFixed(2);
19265
19996
  return [parseFloat(rgb1), parseFloat(rgb2), parseFloat(rgb3)];
19266
19997
  }
19998
+ /**
19999
+ * Derive interaction colours from the object's normal colour.
20000
+ * Dark colours move towards white; light colours move towards black.
20001
+ * Selection uses the stronger contrast so selected > hover > normal.
20002
+ */
20003
+ function interactionColorsFromHex(hexColor) {
20004
+ const base = colorHex2RGB(hexColor);
20005
+ const luminance = 0.2126 * base[0] + 0.7152 * base[1] + 0.0722 * base[2];
20006
+ const target = luminance < 0.55 ? 1.0 : 0.0;
20007
+ const mix = (amount) => [
20008
+ base[0] + (target - base[0]) * amount,
20009
+ base[1] + (target - base[1]) * amount,
20010
+ base[2] + (target - base[2]) * amount,
20011
+ ];
20012
+ return {
20013
+ hover: mix(0.45),
20014
+ selected: mix(0.45),
20015
+ };
20016
+ }
19267
20017
  function degToRad(degrees) {
19268
20018
  return (degrees / 180) * Math.PI;
19269
20019
  }
@@ -19310,6 +20060,13 @@ function decDegToDMS(decDeg) {
19310
20060
  d = d * sign;
19311
20061
  return { d, m, s };
19312
20062
  }
20063
+ function raHMSToDeg(hms) {
20064
+ return 15 * (hms.h + hms.m / 60 + hms.s / 3600);
20065
+ }
20066
+ function decDMSToDeg(dms) {
20067
+ const sign = dms.d < 0 || Object.is(dms.d, -0) ? -1 : 1;
20068
+ return sign * (Math.abs(dms.d) + dms.m / 60 + dms.s / 3600);
20069
+ }
19313
20070
 
19314
20071
 
19315
20072
  /***/ },