astro-viewer 3.11.0 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/astroviewer.cjs +837 -173
- package/dist/astroviewer.cjs.map +1 -1
- package/dist/astroviewer.js +837 -173
- package/dist/astroviewer.js.map +1 -1
- package/dist/astroviewer.min.js +1 -1
- package/dist/astroviewer.min.js.map +1 -1
- package/lib-esm/AstroSphere.d.ts +57 -2
- package/lib-esm/AstroSphere.d.ts.map +1 -1
- package/lib-esm/AstroSphere.js +370 -83
- package/lib-esm/AstroSphere.js.map +1 -1
- package/lib-esm/AstroViewer.d.ts +21 -1
- package/lib-esm/AstroViewer.d.ts.map +1 -1
- package/lib-esm/AstroViewer.js +56 -7
- package/lib-esm/AstroViewer.js.map +1 -1
- package/lib-esm/Camera.d.ts +22 -0
- package/lib-esm/Camera.d.ts.map +1 -1
- package/lib-esm/Camera.js +71 -0
- package/lib-esm/Camera.js.map +1 -1
- package/lib-esm/index.d.ts +1 -0
- package/lib-esm/index.d.ts.map +1 -1
- package/lib-esm/index.js +1 -0
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/model/Source.js +1 -1
- package/lib-esm/model/Source.js.map +1 -1
- package/lib-esm/model/catalogues/CatalogueGL.d.ts +13 -4
- package/lib-esm/model/catalogues/CatalogueGL.d.ts.map +1 -1
- package/lib-esm/model/catalogues/CatalogueGL.js +109 -59
- package/lib-esm/model/catalogues/CatalogueGL.js.map +1 -1
- package/lib-esm/model/earth/XYZMap.d.ts +4 -1
- package/lib-esm/model/earth/XYZMap.d.ts.map +1 -1
- package/lib-esm/model/earth/XYZMap.js +35 -2
- package/lib-esm/model/earth/XYZMap.js.map +1 -1
- package/lib-esm/model/footprints/FootprintSetGL.d.ts +1 -0
- package/lib-esm/model/footprints/FootprintSetGL.d.ts.map +1 -1
- package/lib-esm/model/footprints/FootprintSetGL.js +13 -3
- package/lib-esm/model/footprints/FootprintSetGL.js.map +1 -1
- package/lib-esm/model/grid/EquatorialGrid.d.ts +1 -0
- package/lib-esm/model/grid/EquatorialGrid.d.ts.map +1 -1
- package/lib-esm/model/grid/EquatorialGrid.js +3 -0
- package/lib-esm/model/grid/EquatorialGrid.js.map +1 -1
- package/lib-esm/model/grid/HealpixGrid.d.ts +1 -0
- package/lib-esm/model/grid/HealpixGrid.d.ts.map +1 -1
- package/lib-esm/model/grid/HealpixGrid.js +3 -0
- package/lib-esm/model/grid/HealpixGrid.js.map +1 -1
- package/lib-esm/model/grid/LonLatGrid.d.ts +1 -0
- package/lib-esm/model/grid/LonLatGrid.d.ts.map +1 -1
- package/lib-esm/model/grid/LonLatGrid.js +3 -0
- package/lib-esm/model/grid/LonLatGrid.js.map +1 -1
- package/lib-esm/model/hips/AllSky.d.ts.map +1 -1
- package/lib-esm/model/hips/AllSky.js +4 -2
- package/lib-esm/model/hips/AllSky.js.map +1 -1
- package/lib-esm/model/hips/AncestorTile.d.ts.map +1 -1
- package/lib-esm/model/hips/AncestorTile.js +4 -2
- package/lib-esm/model/hips/AncestorTile.js.map +1 -1
- package/lib-esm/model/hips/Tile.d.ts.map +1 -1
- package/lib-esm/model/hips/Tile.js +4 -2
- package/lib-esm/model/hips/Tile.js.map +1 -1
- package/lib-esm/model/hips/TileBuffer.d.ts +11 -0
- package/lib-esm/model/hips/TileBuffer.d.ts.map +1 -1
- package/lib-esm/model/hips/TileBuffer.js +47 -0
- package/lib-esm/model/hips/TileBuffer.js.map +1 -1
- package/lib-esm/model/hips/VisibleTilesManager.d.ts.map +1 -1
- package/lib-esm/model/hips/VisibleTilesManager.js +8 -2
- package/lib-esm/model/hips/VisibleTilesManager.js.map +1 -1
- package/lib-esm/model/meships/MeshHiPS.d.ts +4 -0
- package/lib-esm/model/meships/MeshHiPS.d.ts.map +1 -1
- package/lib-esm/model/meships/MeshHiPS.js +11 -1
- package/lib-esm/model/meships/MeshHiPS.js.map +1 -1
- package/lib-esm/model/terra/TerraPointSetGL.d.ts +1 -0
- package/lib-esm/model/terra/TerraPointSetGL.d.ts.map +1 -1
- package/lib-esm/model/terra/TerraPointSetGL.js +39 -1
- package/lib-esm/model/terra/TerraPointSetGL.js.map +1 -1
- package/lib-esm/shader/CatalogueShaderProgram.d.ts +4 -0
- package/lib-esm/shader/CatalogueShaderProgram.d.ts.map +1 -1
- package/lib-esm/shader/CatalogueShaderProgram.js +6 -0
- package/lib-esm/shader/CatalogueShaderProgram.js.map +1 -1
- package/lib-esm/shader/ShaderManager.d.ts.map +1 -1
- package/lib-esm/shader/ShaderManager.js +7 -4
- package/lib-esm/shader/ShaderManager.js.map +1 -1
- package/lib-esm/shader/XYZShaderProgram.d.ts +3 -0
- package/lib-esm/shader/XYZShaderProgram.d.ts.map +1 -1
- package/lib-esm/shader/XYZShaderProgram.js +11 -3
- package/lib-esm/shader/XYZShaderProgram.js.map +1 -1
- package/lib-esm/utils/Utils.d.ts +12 -0
- package/lib-esm/utils/Utils.d.ts.map +1 -1
- package/lib-esm/utils/Utils.js +26 -0
- package/lib-esm/utils/Utils.js.map +1 -1
- package/package.json +2 -2
package/lib-esm/AstroSphere.js
CHANGED
|
@@ -54,12 +54,18 @@ class AstroSphere {
|
|
|
54
54
|
_activeHiPS = null;
|
|
55
55
|
_activeHiPSLayers = [];
|
|
56
56
|
_activeXYZ2 = null;
|
|
57
|
+
_earthRasterOverlays = [];
|
|
58
|
+
_earthRasterOverlaySequence = 0;
|
|
57
59
|
_activeMeshHiPS = null;
|
|
58
60
|
_activeBaseLayer = null;
|
|
61
|
+
_activeDomain = "astronomy";
|
|
62
|
+
domainCameraStates = {};
|
|
59
63
|
startup = true;
|
|
60
64
|
fov;
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
astronomyCatalogues = [];
|
|
66
|
+
earthPointSets = [];
|
|
67
|
+
astronomyFootprintSets = [];
|
|
68
|
+
earthFootprintSets = [];
|
|
63
69
|
activePolylineSets = [];
|
|
64
70
|
activeSensorCones = [];
|
|
65
71
|
activeSatelliteObjects = [];
|
|
@@ -75,6 +81,7 @@ class AstroSphere {
|
|
|
75
81
|
lockedNorthSouthDecDeg = null;
|
|
76
82
|
keepCameraNorthUp = true;
|
|
77
83
|
gridLabelContainers;
|
|
84
|
+
fovAnimationResolve = null;
|
|
78
85
|
constructor(canvas, webgl, options = {}) {
|
|
79
86
|
console.log("[AstroSphere] new instance for canvas", canvas.id);
|
|
80
87
|
// Keep global GL context (as in original JS)
|
|
@@ -292,9 +299,7 @@ class AstroSphere {
|
|
|
292
299
|
return true;
|
|
293
300
|
}
|
|
294
301
|
emitCameraChanged(reason) {
|
|
295
|
-
//
|
|
296
|
-
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
297
|
-
return;
|
|
302
|
+
// Camera interaction remains meaningful even when no base layer is active.
|
|
298
303
|
if (!this._healpixGrid?.fovObj)
|
|
299
304
|
return;
|
|
300
305
|
const detail = this.getCurrentStatus();
|
|
@@ -321,6 +326,7 @@ class AstroSphere {
|
|
|
321
326
|
canvas.setPointerCapture(event.pointerId);
|
|
322
327
|
this.mouseDown = true;
|
|
323
328
|
this._camera.cancelFlyTo();
|
|
329
|
+
this.cancelFoVAnimation();
|
|
324
330
|
const rect = canvas.getBoundingClientRect();
|
|
325
331
|
this.lastMouseX = event.clientX - rect.left; // locale al canvas
|
|
326
332
|
this.lastMouseY = event.clientY - rect.top; // locale al canvas
|
|
@@ -359,8 +365,11 @@ class AstroSphere {
|
|
|
359
365
|
if (mousePoint && mousePoint.length > 0) {
|
|
360
366
|
this.mouseHelper.update(mousePoint);
|
|
361
367
|
this.updateLastMousePoint();
|
|
362
|
-
for (const cat of this.
|
|
363
|
-
const
|
|
368
|
+
for (const cat of this.getActivePointSets()) {
|
|
369
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
370
|
+
if (!activeModelMatrix)
|
|
371
|
+
continue;
|
|
372
|
+
const clickResult = cat.selectPrimarySourceFromClick(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
364
373
|
if (!clickResult?.sources.length)
|
|
365
374
|
continue;
|
|
366
375
|
this._webgl.canvas.dispatchEvent(new CustomEvent("source-clicked", {
|
|
@@ -373,7 +382,7 @@ class AstroSphere {
|
|
|
373
382
|
composed: true,
|
|
374
383
|
}));
|
|
375
384
|
}
|
|
376
|
-
for (const fset of this.
|
|
385
|
+
for (const fset of this.getActiveFootprintSets()) {
|
|
377
386
|
const clickResult = fset.selectPrimaryFootprintFromClick(this.mouseHelper);
|
|
378
387
|
if (!clickResult?.footprints.length)
|
|
379
388
|
continue;
|
|
@@ -448,6 +457,7 @@ class AstroSphere {
|
|
|
448
457
|
const currentFov = this._healpixGrid.getMinFoV();
|
|
449
458
|
const zoomStep = this.computeZoomStep(currentFov, event.deltaY);
|
|
450
459
|
this._camera.cancelFlyTo();
|
|
460
|
+
this.cancelFoVAnimation();
|
|
451
461
|
// Apply wheel zoom immediately and discard any queued inertia so reversing
|
|
452
462
|
// direction feels responsive instead of "buffered".
|
|
453
463
|
this.zoomInertia = 0;
|
|
@@ -470,8 +480,11 @@ class AstroSphere {
|
|
|
470
480
|
}
|
|
471
481
|
this.mouseHelper.update(mousePoint);
|
|
472
482
|
this.updateLastMousePoint();
|
|
473
|
-
for (const cat of this.
|
|
474
|
-
const
|
|
483
|
+
for (const cat of this.getActivePointSets()) {
|
|
484
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
485
|
+
if (!activeModelMatrix)
|
|
486
|
+
continue;
|
|
487
|
+
const pickResult = cat.getSourcesFromPointer(this.mouseHelper, activeModelMatrix, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
475
488
|
if (!pickResult?.sources.length)
|
|
476
489
|
continue;
|
|
477
490
|
this._webgl.canvas.dispatchEvent(new CustomEvent("source-contextmenu", {
|
|
@@ -486,7 +499,7 @@ class AstroSphere {
|
|
|
486
499
|
}));
|
|
487
500
|
break;
|
|
488
501
|
}
|
|
489
|
-
for (const fset of this.
|
|
502
|
+
for (const fset of this.getActiveFootprintSets()) {
|
|
490
503
|
const pickResult = fset.getFootprintsFromPointer(this.mouseHelper);
|
|
491
504
|
if (!pickResult?.footprints.length)
|
|
492
505
|
continue;
|
|
@@ -576,19 +589,22 @@ class AstroSphere {
|
|
|
576
589
|
return new HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
|
|
577
590
|
}
|
|
578
591
|
activateHiPS(hipsDescriptor) {
|
|
592
|
+
if (this._activeHiPSLayers.length > 0) {
|
|
593
|
+
throw new Error("Cannot load a HiPS base layer while stacked layers are active.");
|
|
594
|
+
}
|
|
579
595
|
const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
|
|
580
|
-
|
|
581
|
-
tileBuffer.removeHiPS(
|
|
596
|
+
if (this._activeHiPS) {
|
|
597
|
+
tileBuffer.removeHiPS(this._activeHiPS);
|
|
582
598
|
}
|
|
583
|
-
this._activeHiPSLayers = [];
|
|
584
599
|
const hips = this.createHiPS(hipsDescriptor);
|
|
585
|
-
this._activeHiPSLayers.push(hips);
|
|
586
600
|
this._activeHiPS = hips;
|
|
601
|
+
this._activeDomain = "astronomy";
|
|
587
602
|
this._activeBaseLayer = "hips";
|
|
588
603
|
return hips;
|
|
589
604
|
}
|
|
590
605
|
setHiPSOpacity(hips, opacity) {
|
|
591
|
-
|
|
606
|
+
const isBaseLayer = this._activeHiPSLayers.length === 0 && this._activeHiPS === hips;
|
|
607
|
+
if (!isBaseLayer && !this._activeHiPSLayers.includes(hips)) {
|
|
592
608
|
throw new Error("HiPS layer is not active in this AstroSphere.");
|
|
593
609
|
}
|
|
594
610
|
hips.setOpacity(opacity);
|
|
@@ -596,6 +612,13 @@ class AstroSphere {
|
|
|
596
612
|
addHiPS(hipsDescriptor) {
|
|
597
613
|
const normalizeURL = (url) => String(url).replace(/\/+$/, "");
|
|
598
614
|
const descriptorURL = normalizeURL(hipsDescriptor.url);
|
|
615
|
+
// Entering stack mode discards the standalone base HiPS. Base mode and
|
|
616
|
+
// stack mode are intentionally mutually exclusive.
|
|
617
|
+
if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
|
|
618
|
+
const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
|
|
619
|
+
tileBuffer.removeHiPS(this._activeHiPS);
|
|
620
|
+
this._activeHiPS = null;
|
|
621
|
+
}
|
|
599
622
|
const existing = this._activeHiPSLayers.find((hips) => normalizeURL(hips.baseURL) === descriptorURL);
|
|
600
623
|
if (existing) {
|
|
601
624
|
throw new Error(`HiPS already active: ${String(hipsDescriptor.url)}`);
|
|
@@ -603,6 +626,7 @@ class AstroSphere {
|
|
|
603
626
|
const hips = this.createHiPS(hipsDescriptor);
|
|
604
627
|
this._activeHiPSLayers.push(hips);
|
|
605
628
|
this._activeHiPS = hips;
|
|
629
|
+
this._activeDomain = "astronomy";
|
|
606
630
|
this._activeBaseLayer = "hips";
|
|
607
631
|
return hips;
|
|
608
632
|
}
|
|
@@ -627,8 +651,13 @@ class AstroSphere {
|
|
|
627
651
|
}
|
|
628
652
|
removeAllHiPS() {
|
|
629
653
|
const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
|
|
630
|
-
|
|
631
|
-
tileBuffer.removeHiPS(
|
|
654
|
+
if (this._activeHiPSLayers.length === 0 && this._activeHiPS) {
|
|
655
|
+
tileBuffer.removeHiPS(this._activeHiPS);
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
for (const hips of this._activeHiPSLayers) {
|
|
659
|
+
tileBuffer.removeHiPS(hips);
|
|
660
|
+
}
|
|
632
661
|
}
|
|
633
662
|
this._activeHiPSLayers = [];
|
|
634
663
|
this._activeHiPS = null;
|
|
@@ -641,39 +670,169 @@ class AstroSphere {
|
|
|
641
670
|
this._activeBaseLayer = "xyz";
|
|
642
671
|
}
|
|
643
672
|
activateXYZ2(config) {
|
|
673
|
+
this._activeDomain = "earth";
|
|
644
674
|
this._activeXYZ2 = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
|
|
645
675
|
this._activeBaseLayer = "xyz";
|
|
646
676
|
}
|
|
647
677
|
activateMeshHiPS(descriptor) {
|
|
678
|
+
this._activeDomain = "mesh";
|
|
648
679
|
this._activeMeshHiPS = new MeshHiPS(descriptor.meshRadius, [0.0, 0.0, 0.0], 0, 0, descriptor, this._webgl, this._healpixGrid);
|
|
649
680
|
this._activeBaseLayer = "meships";
|
|
650
681
|
}
|
|
682
|
+
setMeshHiPSColor(color) {
|
|
683
|
+
this._activeMeshHiPS?.setColor(color);
|
|
684
|
+
}
|
|
685
|
+
setMeshHiPSWireframe(wireframe) {
|
|
686
|
+
this._activeMeshHiPS?.setWireframe(wireframe);
|
|
687
|
+
}
|
|
651
688
|
activateWMTS(config) {
|
|
689
|
+
this._activeDomain = "earth";
|
|
652
690
|
const adapter = new WMTSAdapter(config);
|
|
653
691
|
const xyzConfig = adapter.toXYZLayerConfig();
|
|
654
692
|
this._activeXYZ2 = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, new 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);
|
|
655
693
|
this._activeBaseLayer = "xyz";
|
|
656
694
|
}
|
|
657
|
-
|
|
695
|
+
addXYZRasterOverlay(config) {
|
|
696
|
+
const map = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl, this.gridLabelContainers);
|
|
697
|
+
return this.addEarthRasterOverlay(config.name, "xyz", map);
|
|
698
|
+
}
|
|
699
|
+
addWMTSRasterOverlay(config) {
|
|
700
|
+
const adapter = new WMTSAdapter(config);
|
|
701
|
+
const xyzConfig = adapter.toXYZLayerConfig();
|
|
702
|
+
const name = config.layer ? `WMTS ${config.layer}` : "WMTS Earth Raster Overlay";
|
|
703
|
+
const map = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, new XYZMapDescriptor(name, xyzConfig.urlTemplate, xyzConfig.minZoom ?? 0, xyzConfig.maxZoom ?? 8, xyzConfig.segmentsPerSide ?? 48, xyzConfig.maxCachedTiles ?? 384, 8, xyzConfig.urlResolver), this._webgl, this.gridLabelContainers);
|
|
704
|
+
return this.addEarthRasterOverlay(name, "wmts", map);
|
|
705
|
+
}
|
|
706
|
+
getEarthRasterOverlays() {
|
|
707
|
+
return this._earthRasterOverlays.map(({ map: _map, ...overlay }) => ({ ...overlay }));
|
|
708
|
+
}
|
|
709
|
+
setEarthRasterOverlayOpacity(id, opacity) {
|
|
710
|
+
const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
|
|
711
|
+
if (!overlay)
|
|
712
|
+
return;
|
|
713
|
+
overlay.opacity = Math.min(1, Math.max(0, opacity));
|
|
714
|
+
overlay.map.setOpacity(overlay.opacity);
|
|
715
|
+
}
|
|
716
|
+
setEarthRasterOverlayVisible(id, visible) {
|
|
717
|
+
const overlay = this._earthRasterOverlays.find((entry) => entry.id === id);
|
|
718
|
+
if (overlay)
|
|
719
|
+
overlay.visible = visible;
|
|
720
|
+
}
|
|
721
|
+
removeEarthRasterOverlay(id) {
|
|
722
|
+
this._earthRasterOverlays = this._earthRasterOverlays.filter((entry) => entry.id !== id);
|
|
723
|
+
}
|
|
724
|
+
removeAllEarthRasterOverlays() {
|
|
725
|
+
this._earthRasterOverlays = [];
|
|
726
|
+
}
|
|
727
|
+
addEarthRasterOverlay(name, sourceType, map) {
|
|
728
|
+
const overlay = {
|
|
729
|
+
id: `earth-raster-${++this._earthRasterOverlaySequence}`,
|
|
730
|
+
name,
|
|
731
|
+
sourceType,
|
|
732
|
+
visible: true,
|
|
733
|
+
opacity: 0.65,
|
|
734
|
+
map,
|
|
735
|
+
};
|
|
736
|
+
map.setOpacity(overlay.opacity);
|
|
737
|
+
this._earthRasterOverlays.push(overlay);
|
|
738
|
+
const { map: _map, ...info } = overlay;
|
|
739
|
+
return { ...info };
|
|
740
|
+
}
|
|
741
|
+
setActiveDomain(domain) {
|
|
742
|
+
if (domain !== this._activeDomain) {
|
|
743
|
+
this.clearGridLabelsForDomain(this._activeDomain);
|
|
744
|
+
this.domainCameraStates[this._activeDomain] = this._camera.getViewState();
|
|
745
|
+
const targetCameraState = this.domainCameraStates[domain];
|
|
746
|
+
if (targetCameraState) {
|
|
747
|
+
this._camera.restoreViewState(targetCameraState);
|
|
748
|
+
}
|
|
749
|
+
this.inertiaX = 0;
|
|
750
|
+
this.inertiaY = 0;
|
|
751
|
+
this.zoomInertia = 0;
|
|
752
|
+
this._cameraStatusChanged = true;
|
|
753
|
+
}
|
|
754
|
+
this._activeDomain = domain;
|
|
755
|
+
if (domain === "astronomy") {
|
|
756
|
+
this._activeBaseLayer = this._activeHiPS ? "hips" : null;
|
|
757
|
+
}
|
|
758
|
+
else if (domain === "earth") {
|
|
759
|
+
this._activeBaseLayer = this._activeXYZ2 ? "xyz" : null;
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
this._activeBaseLayer = this._activeMeshHiPS ? "meships" : null;
|
|
763
|
+
}
|
|
764
|
+
// A hover from the previous vertical must not leak into the new one.
|
|
765
|
+
this.lastHoveredSource = null;
|
|
766
|
+
this.lastHoveredCatalogue = null;
|
|
767
|
+
}
|
|
768
|
+
clearGridLabelsForDomain(domain) {
|
|
769
|
+
if (domain === "astronomy") {
|
|
770
|
+
this._healpixGrid?.clearLabels();
|
|
771
|
+
this._equatorialGrid?.clearLabels();
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (domain === "earth") {
|
|
775
|
+
this._activeXYZ2?.clearLonLatGridLabels?.();
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
get activeDomain() {
|
|
779
|
+
return this._activeDomain;
|
|
780
|
+
}
|
|
781
|
+
getActivePointSets() {
|
|
782
|
+
if (this._activeDomain === "astronomy")
|
|
783
|
+
return this.astronomyCatalogues;
|
|
784
|
+
if (this._activeDomain === "earth")
|
|
785
|
+
return this.earthPointSets;
|
|
786
|
+
return [];
|
|
787
|
+
}
|
|
788
|
+
getActiveFootprintSets() {
|
|
789
|
+
if (this._activeDomain === "astronomy")
|
|
790
|
+
return this.astronomyFootprintSets;
|
|
791
|
+
if (this._activeDomain === "earth")
|
|
792
|
+
return this.earthFootprintSets;
|
|
793
|
+
return [];
|
|
794
|
+
}
|
|
795
|
+
// Astronomy catalogue ownership
|
|
658
796
|
async showCatalogue(cat) {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
797
|
+
if (cat && !this.astronomyCatalogues.includes(cat)) {
|
|
798
|
+
this.astronomyCatalogues.push(cat);
|
|
799
|
+
}
|
|
662
800
|
return cat;
|
|
663
801
|
}
|
|
664
802
|
deleteCatalogue(catalogue) {
|
|
665
|
-
this.
|
|
803
|
+
this.astronomyCatalogues = this.astronomyCatalogues.filter((c) => c !== catalogue);
|
|
666
804
|
}
|
|
667
|
-
//
|
|
668
|
-
//
|
|
805
|
+
// Earth point-set ownership. TerraPointSetGL still extends CatalogueGL in
|
|
806
|
+
// 3.12, but it no longer shares the Astronomy lifecycle collection.
|
|
807
|
+
async showTerraPointSet(pointSet) {
|
|
808
|
+
if (pointSet && !this.earthPointSets.includes(pointSet)) {
|
|
809
|
+
this.earthPointSets.push(pointSet);
|
|
810
|
+
}
|
|
811
|
+
return pointSet;
|
|
812
|
+
}
|
|
813
|
+
deleteTerraPointSet(pointSet) {
|
|
814
|
+
this.earthPointSets = this.earthPointSets.filter((set) => set !== pointSet);
|
|
815
|
+
}
|
|
816
|
+
// Astronomy footprint ownership
|
|
669
817
|
async showFootprintSet(fset) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
818
|
+
if (fset && !this.astronomyFootprintSets.includes(fset)) {
|
|
819
|
+
this.astronomyFootprintSets.push(fset);
|
|
820
|
+
}
|
|
673
821
|
return fset;
|
|
674
822
|
}
|
|
675
823
|
deleteFootprintSet(footprintSet) {
|
|
676
|
-
this.
|
|
824
|
+
this.astronomyFootprintSets = this.astronomyFootprintSets.filter((fst) => fst !== footprintSet);
|
|
825
|
+
}
|
|
826
|
+
// Earth footprint ownership. TerraFootprintSetGL still reuses the common
|
|
827
|
+
// FootprintSetGL rendering implementation, but has an independent lifecycle.
|
|
828
|
+
async showTerraFootprintSet(footprintSet) {
|
|
829
|
+
if (footprintSet && !this.earthFootprintSets.includes(footprintSet)) {
|
|
830
|
+
this.earthFootprintSets.push(footprintSet);
|
|
831
|
+
}
|
|
832
|
+
return footprintSet;
|
|
833
|
+
}
|
|
834
|
+
deleteTerraFootprintSet(footprintSet) {
|
|
835
|
+
this.earthFootprintSets = this.earthFootprintSets.filter((fst) => fst !== footprintSet);
|
|
677
836
|
}
|
|
678
837
|
async showPolylineSet(polylineSet) {
|
|
679
838
|
if (polylineSet)
|
|
@@ -703,8 +862,8 @@ class AstroSphere {
|
|
|
703
862
|
satelliteObject.dispose();
|
|
704
863
|
}
|
|
705
864
|
getHoveredFootprints() {
|
|
706
|
-
|
|
707
|
-
this.
|
|
865
|
+
const footprintsHovered = [];
|
|
866
|
+
this.getActiveFootprintSets().forEach((fset) => {
|
|
708
867
|
footprintsHovered.push(fset.hoveredFootprints);
|
|
709
868
|
});
|
|
710
869
|
return footprintsHovered;
|
|
@@ -756,25 +915,128 @@ class AstroSphere {
|
|
|
756
915
|
throw new Error(`healpixGrid is ${this.healpixGrid}`);
|
|
757
916
|
return FoVUtils.getFoVPolygon(this._camera, this.canvas, this._healpixGrid, this._healpixGrid, this._webgl, this._perspectiveMatrixManager.pMatrix);
|
|
758
917
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
918
|
+
validateFoV(deg) {
|
|
919
|
+
if (!Number.isFinite(deg) || deg <= 0 || deg >= 180) {
|
|
920
|
+
throw new RangeError(`FoV must be > 0 and < 180 degrees. Received ${deg}.`);
|
|
921
|
+
}
|
|
922
|
+
if (global.insideSphere) {
|
|
923
|
+
throw new Error("FoV navigation is currently supported only outside the sphere.");
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Resolve the radial camera distance that produces the requested minimum FoV.
|
|
928
|
+
*
|
|
929
|
+
* The search temporarily samples radial distances, then restores the original
|
|
930
|
+
* camera distance before returning. Callers can therefore either apply the
|
|
931
|
+
* result immediately or animate toward it.
|
|
932
|
+
*/
|
|
933
|
+
findRadialDistanceForFoV(deg) {
|
|
934
|
+
this.validateFoV(deg);
|
|
935
|
+
const originalDistance = this._camera.getRadialDistance();
|
|
936
|
+
const minDistance = 1.000001;
|
|
937
|
+
const maxDistance = 4.0;
|
|
938
|
+
const tolerance = Math.max(1e-5, deg * 0.001);
|
|
939
|
+
const maxIterations = 40;
|
|
940
|
+
let low = minDistance;
|
|
941
|
+
let high = maxDistance;
|
|
942
|
+
let bestDistance = originalDistance;
|
|
943
|
+
let bestError = Number.POSITIVE_INFINITY;
|
|
944
|
+
for (let iteration = 0; iteration < maxIterations; iteration += 1) {
|
|
945
|
+
const candidateDistance = (low + high) / 2;
|
|
946
|
+
this._camera.setRadialDistance(candidateDistance);
|
|
947
|
+
this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
|
|
948
|
+
const candidateFoV = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
949
|
+
const measuredFoV = candidateFoV.minFoV;
|
|
950
|
+
const error = Math.abs(measuredFoV - deg);
|
|
951
|
+
if (error < bestError) {
|
|
952
|
+
bestError = error;
|
|
953
|
+
bestDistance = candidateDistance;
|
|
954
|
+
}
|
|
955
|
+
if (error <= tolerance) {
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
if (measuredFoV >= deg || measuredFoV >= 179.999) {
|
|
959
|
+
high = candidateDistance;
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
low = candidateDistance;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
this._camera.setRadialDistance(originalDistance);
|
|
966
|
+
this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
|
|
967
|
+
this.fov = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
968
|
+
this._camera.refreshFoV(this.fov.minFoV);
|
|
969
|
+
return bestDistance;
|
|
970
|
+
}
|
|
971
|
+
finishFoVAnimation() {
|
|
972
|
+
const resolve = this.fovAnimationResolve;
|
|
973
|
+
this.fovAnimationResolve = null;
|
|
974
|
+
resolve?.();
|
|
975
|
+
}
|
|
976
|
+
cancelFoVAnimation() {
|
|
977
|
+
this._camera.cancelRadialAnimation();
|
|
978
|
+
this.finishFoVAnimation();
|
|
979
|
+
}
|
|
980
|
+
/** Set the minimum angular field of view immediately, in degrees. */
|
|
981
|
+
setFoV(deg) {
|
|
982
|
+
this.validateFoV(deg);
|
|
983
|
+
this.cancelFoVAnimation();
|
|
984
|
+
this._camera.cancelFlyTo();
|
|
985
|
+
this.zoomInertia = 0;
|
|
986
|
+
const targetDistance = this.findRadialDistanceForFoV(deg);
|
|
987
|
+
this._camera.setRadialDistance(targetDistance);
|
|
988
|
+
this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
|
|
762
989
|
this.fov = this._healpixGrid.refreshFoV(this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
763
990
|
this._camera.refreshFoV(this.fov.minFoV);
|
|
991
|
+
this.lastCameraMotionAt = performance.now();
|
|
992
|
+
this._cameraStatusChanged = true;
|
|
993
|
+
this.emitCameraChanged("set-fov");
|
|
994
|
+
}
|
|
995
|
+
async flyToFoV(deg, durationMs = 1200) {
|
|
996
|
+
this.validateFoV(deg);
|
|
997
|
+
if (!Number.isFinite(durationMs) || durationMs < 0) {
|
|
998
|
+
throw new RangeError(`FoV animation duration must be >= 0 ms. Received ${durationMs}.`);
|
|
999
|
+
}
|
|
1000
|
+
if (durationMs === 0) {
|
|
1001
|
+
this.setFoV(deg);
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
this.cancelFoVAnimation();
|
|
1005
|
+
this.zoomInertia = 0;
|
|
1006
|
+
const targetDistance = this.findRadialDistanceForFoV(deg);
|
|
1007
|
+
this._camera.flyToRadialDistance(targetDistance, durationMs);
|
|
1008
|
+
this.lastCameraMotionAt = performance.now();
|
|
1009
|
+
this._cameraStatusChanged = true;
|
|
1010
|
+
return new Promise((resolve) => {
|
|
1011
|
+
this.fovAnimationResolve = resolve;
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
async zoomIn(steps = 1, durationMs = 500) {
|
|
1015
|
+
this.validateZoomSteps(steps);
|
|
1016
|
+
const targetFoV = this.getFoV().minFoV / Math.pow(2, steps);
|
|
1017
|
+
return this.flyToFoV(targetFoV, durationMs);
|
|
1018
|
+
}
|
|
1019
|
+
async zoomOut(steps = 1, durationMs = 500) {
|
|
1020
|
+
this.validateZoomSteps(steps);
|
|
1021
|
+
const targetFoV = this.getFoV().minFoV * Math.pow(2, steps);
|
|
1022
|
+
return this.flyToFoV(targetFoV, durationMs);
|
|
764
1023
|
}
|
|
1024
|
+
validateZoomSteps(steps) {
|
|
1025
|
+
if (!Number.isInteger(steps) || steps < 1) {
|
|
1026
|
+
throw new RangeError(`Zoom steps must be a positive integer. Received ${steps}.`);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/** @deprecated Use setFoV(). */
|
|
1030
|
+
changeFoV(deg) {
|
|
1031
|
+
this.setFoV(deg);
|
|
1032
|
+
}
|
|
1033
|
+
/** @deprecated Use setFoV(). */
|
|
765
1034
|
changeFoV2(deg) {
|
|
766
|
-
|
|
767
|
-
.getFoV()
|
|
768
|
-
.computeCameraPositionForFoV(deg);
|
|
769
|
-
this._camera.setCameraPosition(newCameraPos);
|
|
1035
|
+
this.setFoV(deg);
|
|
770
1036
|
}
|
|
1037
|
+
/** @deprecated Use setFoV(). */
|
|
771
1038
|
changeFoV3(deg) {
|
|
772
|
-
|
|
773
|
-
.getFoV()
|
|
774
|
-
.computeCameraPositionForAngularDiameter(deg);
|
|
775
|
-
this._camera.setCameraPosition(newPos);
|
|
776
|
-
// Recompute projection after moving the camera
|
|
777
|
-
this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, false);
|
|
1039
|
+
this.setFoV(deg);
|
|
778
1040
|
}
|
|
779
1041
|
getInsideSphere() {
|
|
780
1042
|
return global.insideSphere;
|
|
@@ -784,6 +1046,7 @@ class AstroSphere {
|
|
|
784
1046
|
this.inertiaX = 0;
|
|
785
1047
|
this.inertiaY = 0;
|
|
786
1048
|
this.zoomInertia = 0;
|
|
1049
|
+
this.cancelFoVAnimation();
|
|
787
1050
|
global.insideSphere = !global.insideSphere;
|
|
788
1051
|
// console.log(global.insideSphere)
|
|
789
1052
|
this._camera.toggleInsideSphere();
|
|
@@ -936,8 +1199,6 @@ class AstroSphere {
|
|
|
936
1199
|
return;
|
|
937
1200
|
if (!this._webgl)
|
|
938
1201
|
return;
|
|
939
|
-
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
940
|
-
return;
|
|
941
1202
|
if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
|
|
942
1203
|
return;
|
|
943
1204
|
if (this._healpixGrid.fovObj === undefined)
|
|
@@ -961,6 +1222,14 @@ class AstroSphere {
|
|
|
961
1222
|
this.updateCentralPoint();
|
|
962
1223
|
this._perspectiveMatrixManager.computePerspectiveMatrix(canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
|
|
963
1224
|
}
|
|
1225
|
+
const fovFlying = this._camera.updateRadialAnimation(now);
|
|
1226
|
+
const fovAnimationFinished = fovFlying && !this._camera.isRadialAnimationActive();
|
|
1227
|
+
if (fovFlying) {
|
|
1228
|
+
this.zoomInertia = 0;
|
|
1229
|
+
this.lastCameraMotionAt = now;
|
|
1230
|
+
this._cameraStatusChanged = true;
|
|
1231
|
+
this._perspectiveMatrixManager.computePerspectiveMatrix(canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
|
|
1232
|
+
}
|
|
964
1233
|
// Zoom inertia
|
|
965
1234
|
if (this.zoomInertia !== 0) {
|
|
966
1235
|
if (Math.abs(this.zoomInertia) > 0.0001) {
|
|
@@ -1009,6 +1278,9 @@ class AstroSphere {
|
|
|
1009
1278
|
this._camera.refreshFoV(this.fov.minFoV);
|
|
1010
1279
|
this.prevFov = this.fov.minFoV;
|
|
1011
1280
|
}
|
|
1281
|
+
if (fovAnimationFinished) {
|
|
1282
|
+
this.finishFoVAnimation();
|
|
1283
|
+
}
|
|
1012
1284
|
// Se la camera è ruotata (anche solo per inerzia), aggiorna punto centrale + emetti cameraChanged
|
|
1013
1285
|
if (cameraRotated) {
|
|
1014
1286
|
// Ricalcola il punto centrale
|
|
@@ -1049,8 +1321,14 @@ class AstroSphere {
|
|
|
1049
1321
|
this._webgl.enable(this._webgl.CULL_FACE);
|
|
1050
1322
|
this._webgl.cullFace(global.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
|
|
1051
1323
|
this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
|
|
1052
|
-
if (this._activeBaseLayer === "hips" && this.
|
|
1053
|
-
|
|
1324
|
+
if (this._activeBaseLayer === "hips" && this._activeHiPS) {
|
|
1325
|
+
// During directional flyTo navigation, keep visibility current but render
|
|
1326
|
+
// only HiPS tiles that are already available. Final-view refinement
|
|
1327
|
+
// resumes automatically on the first frame after the flight.
|
|
1328
|
+
this._healpixGrid.visibleTilesManager.tileBuffer.setTileAcquisitionEnabled(!cameraFlying);
|
|
1329
|
+
const maxHiPSOrder = this._activeHiPSLayers.length > 0
|
|
1330
|
+
? Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder))
|
|
1331
|
+
: this._activeHiPS.maxOrder;
|
|
1054
1332
|
const visibleOrder = Math.min(this._healpixGrid.visibleorder, maxHiPSOrder);
|
|
1055
1333
|
this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
1056
1334
|
}
|
|
@@ -1065,6 +1343,7 @@ class AstroSphere {
|
|
|
1065
1343
|
Math.abs(this.zoomInertia) > 0.0001 ||
|
|
1066
1344
|
Math.abs(this.inertiaX) > 0.02 ||
|
|
1067
1345
|
Math.abs(this.inertiaY) > 0.02 ||
|
|
1346
|
+
this._camera.isRadialAnimationActive() ||
|
|
1068
1347
|
nowForGrid - this.lastCameraMotionAt < 220;
|
|
1069
1348
|
const skyEntityDrawInput = {
|
|
1070
1349
|
fovDeg: stableFovDeg,
|
|
@@ -1085,12 +1364,19 @@ class AstroSphere {
|
|
|
1085
1364
|
}
|
|
1086
1365
|
if (this._activeBaseLayer === "xyz") {
|
|
1087
1366
|
this._activeXYZ2?.draw(skyEntityDrawInput);
|
|
1367
|
+
for (const overlay of this._earthRasterOverlays) {
|
|
1368
|
+
if (overlay.visible) {
|
|
1369
|
+
overlay.map.draw(skyEntityDrawInput, true);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1088
1372
|
}
|
|
1089
1373
|
if (this._activeBaseLayer === "meships") {
|
|
1090
1374
|
this._activeMeshHiPS?.draw(skyEntityDrawInput);
|
|
1091
1375
|
}
|
|
1092
|
-
this.
|
|
1093
|
-
|
|
1376
|
+
if (this._activeDomain === "astronomy") {
|
|
1377
|
+
this._healpixGrid.draw(skyEntityDrawInput);
|
|
1378
|
+
this._equatorialGrid.draw(skyEntityDrawInput);
|
|
1379
|
+
}
|
|
1094
1380
|
this._webgl.enable(this._webgl.DEPTH_TEST);
|
|
1095
1381
|
this._webgl.disable(this._webgl.CULL_FACE);
|
|
1096
1382
|
if (this.startup) {
|
|
@@ -1109,52 +1395,53 @@ class AstroSphere {
|
|
|
1109
1395
|
decDMS,
|
|
1110
1396
|
});
|
|
1111
1397
|
}
|
|
1112
|
-
this.
|
|
1113
|
-
const activeModelMatrix = this.
|
|
1114
|
-
this._activeXYZ2?.getModelMatrix() ??
|
|
1115
|
-
this._activeMeshHiPS?.getModelMatrix();
|
|
1398
|
+
this.getActivePointSets().forEach((cat) => {
|
|
1399
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
1116
1400
|
if (activeModelMatrix) {
|
|
1117
1401
|
cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
1118
1402
|
}
|
|
1119
1403
|
});
|
|
1120
1404
|
this.emitHoveredSourceIfChanged();
|
|
1121
|
-
this.
|
|
1122
|
-
const activeModelMatrix = this.
|
|
1123
|
-
this._activeXYZ2?.getModelMatrix() ??
|
|
1124
|
-
this._activeMeshHiPS?.getModelMatrix();
|
|
1405
|
+
this.getActiveFootprintSets().forEach((fst) => {
|
|
1406
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
1125
1407
|
if (activeModelMatrix) {
|
|
1126
1408
|
fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
1127
1409
|
}
|
|
1128
1410
|
});
|
|
1129
|
-
this.
|
|
1130
|
-
|
|
1131
|
-
this.
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1411
|
+
if (this._activeDomain === "earth") {
|
|
1412
|
+
this.activePolylineSets.forEach((polylineSet) => {
|
|
1413
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
1414
|
+
if (activeModelMatrix) {
|
|
1415
|
+
polylineSet.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
this.activeSensorCones.forEach((sensorCone) => {
|
|
1419
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
1420
|
+
if (activeModelMatrix) {
|
|
1421
|
+
sensorCone.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
|
|
1422
|
+
}
|
|
1423
|
+
});
|
|
1424
|
+
this.activeSatelliteObjects.forEach((satelliteObject) => {
|
|
1425
|
+
const activeModelMatrix = this.getActiveDomainModelMatrix();
|
|
1426
|
+
if (activeModelMatrix) {
|
|
1427
|
+
satelliteObject.draw(this._perspectiveMatrixManager.pMatrix, this._camera.getCameraMatrix(), activeModelMatrix);
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
getActiveDomainModelMatrix() {
|
|
1433
|
+
if (this._activeDomain === "astronomy") {
|
|
1434
|
+
return (this._activeHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
|
|
1435
|
+
}
|
|
1436
|
+
if (this._activeDomain === "earth") {
|
|
1437
|
+
return (this._activeXYZ2?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
|
|
1438
|
+
}
|
|
1439
|
+
return (this._activeMeshHiPS?.getModelMatrix() ?? this._healpixGrid.getModelMatrix());
|
|
1153
1440
|
}
|
|
1154
1441
|
emitHoveredSourceIfChanged() {
|
|
1155
1442
|
let nextHoveredSource = null;
|
|
1156
1443
|
let nextHoveredCatalogue = null;
|
|
1157
|
-
for (const cat of this.
|
|
1444
|
+
for (const cat of this.getActivePointSets()) {
|
|
1158
1445
|
const hovered = cat.getPrimaryHoveredSource();
|
|
1159
1446
|
if (!hovered)
|
|
1160
1447
|
continue;
|