astro-viewer 3.2.0 → 3.3.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 +775 -7
- package/dist/astroviewer.js +1 -1
- package/dist/astroviewer.min.js +1 -1
- package/lib-esm/AstroSphere.d.ts +7 -0
- package/lib-esm/AstroSphere.js +36 -6
- package/lib-esm/AstroViewer.d.ts +4 -0
- package/lib-esm/AstroViewer.js +27 -0
- package/lib-esm/index.d.ts +3 -0
- package/lib-esm/index.js +2 -0
- package/lib-esm/model/earth/XYZConfig.d.ts +1 -1
- package/lib-esm/model/hips/HiPSConfig.d.ts +1 -1
- package/lib-esm/model/meships/MeshHiPS.d.ts +30 -0
- package/lib-esm/model/meships/MeshHiPS.js +182 -0
- package/lib-esm/model/meships/MeshHiPSDescriptor.d.ts +34 -0
- package/lib-esm/model/meships/MeshHiPSDescriptor.js +132 -0
- package/lib-esm/model/meships/MeshHiPSTile.d.ts +29 -0
- package/lib-esm/model/meships/MeshHiPSTile.js +148 -0
- package/lib-esm/model/meships/MeshHiPSTypes.d.ts +41 -0
- package/lib-esm/model/meships/MeshHiPSTypes.js +6 -0
- package/lib-esm/model/meships/OBJMeshParser.d.ts +6 -0
- package/lib-esm/model/meships/OBJMeshParser.js +92 -0
- package/lib-esm/shader/MeshHiPSShaderProgram.d.ts +20 -0
- package/lib-esm/shader/MeshHiPSShaderProgram.js +98 -0
- package/package.json +5 -4
package/lib-esm/AstroSphere.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ import type { WMTSLayerConfig, XYZDebugStats, XYZLayerConfig } from "./model/ear
|
|
|
13
13
|
import type { HiPSDebugStats } from "./model/hips/HiPSConfig.js";
|
|
14
14
|
import { XYZMapDescriptor } from "./model/earth/XYZMapDescriptor.js";
|
|
15
15
|
import { XYZMap } from "./model/earth/XYZMap.js";
|
|
16
|
+
import { MeshHiPS } from "./model/meships/MeshHiPS.js";
|
|
17
|
+
import { MeshHiPSDescriptor } from "./model/meships/MeshHiPSDescriptor.js";
|
|
18
|
+
import type { MeshHiPSDebugStats } from "./model/meships/MeshHiPSTypes.js";
|
|
16
19
|
export type PointCoordinates = {
|
|
17
20
|
astroDeg: AstroCoords;
|
|
18
21
|
raHMS: HMS;
|
|
@@ -59,6 +62,7 @@ declare class AstroSphere {
|
|
|
59
62
|
private pointerDownAt;
|
|
60
63
|
private _activeHiPS;
|
|
61
64
|
private _activeXYZ2;
|
|
65
|
+
private _activeMeshHiPS;
|
|
62
66
|
private _activeBaseLayer;
|
|
63
67
|
private startup;
|
|
64
68
|
private fov;
|
|
@@ -103,6 +107,7 @@ declare class AstroSphere {
|
|
|
103
107
|
activateHiPS(hipsDescriptor: HiPSDescriptor): void;
|
|
104
108
|
activateXYZ(config: XYZLayerConfig): void;
|
|
105
109
|
activateXYZ2(config: XYZMapDescriptor): void;
|
|
110
|
+
activateMeshHiPS(descriptor: MeshHiPSDescriptor): void;
|
|
106
111
|
activateWMTS(config: WMTSLayerConfig): void;
|
|
107
112
|
showCatalogue(cat: CatalogueGL): Promise<CatalogueGL>;
|
|
108
113
|
deleteCatalogue(catalogue: CatalogueGL): void;
|
|
@@ -132,6 +137,7 @@ declare class AstroSphere {
|
|
|
132
137
|
private prevCentralDecDeg;
|
|
133
138
|
get activeHiPS(): HiPS | null;
|
|
134
139
|
get activeXYZ(): XYZMap | null;
|
|
140
|
+
get activeMeshHiPS(): MeshHiPS | null;
|
|
135
141
|
isLonLatGridVisible(): boolean;
|
|
136
142
|
toggleLonLatGrid(): boolean;
|
|
137
143
|
setEastWestRotationLocked(locked: boolean): void;
|
|
@@ -140,6 +146,7 @@ declare class AstroSphere {
|
|
|
140
146
|
isNorthSouthRotationLocked(): boolean;
|
|
141
147
|
getXYZDebugStats(): XYZDebugStats;
|
|
142
148
|
getHiPSDebugStats(): HiPSDebugStats | null;
|
|
149
|
+
getMeshHiPSDebugStats(): MeshHiPSDebugStats | null;
|
|
143
150
|
draw(canvas: HTMLCanvasElement): void;
|
|
144
151
|
private emitHoveredSourceIfChanged;
|
|
145
152
|
}
|
package/lib-esm/AstroSphere.js
CHANGED
|
@@ -28,6 +28,7 @@ import { xyzTileRequestScheduler } from "./model/earth/XYZTileRequestScheduler.j
|
|
|
28
28
|
import { WMTSAdapter } from "./model/earth/WMTSAdapter.js";
|
|
29
29
|
import { XYZMapDescriptor } from "./model/earth/XYZMapDescriptor.js";
|
|
30
30
|
import { XYZMap } from "./model/earth/XYZMap.js";
|
|
31
|
+
import { MeshHiPS } from "./model/meships/MeshHiPS.js";
|
|
31
32
|
import { mat4, vec3, vec4 } from "gl-matrix";
|
|
32
33
|
/**
|
|
33
34
|
* AstroSphere — main WebGL scene controller (TS port)
|
|
@@ -54,6 +55,7 @@ class AstroSphere {
|
|
|
54
55
|
pointerDownAt = 0;
|
|
55
56
|
_activeHiPS = null;
|
|
56
57
|
_activeXYZ2 = null;
|
|
58
|
+
_activeMeshHiPS = null;
|
|
57
59
|
_activeBaseLayer = null;
|
|
58
60
|
startup = true;
|
|
59
61
|
fov;
|
|
@@ -287,7 +289,7 @@ class AstroSphere {
|
|
|
287
289
|
}
|
|
288
290
|
emitCameraChanged(reason) {
|
|
289
291
|
// avoid dispatch before scene is ready
|
|
290
|
-
if (!this._activeHiPS)
|
|
292
|
+
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
291
293
|
return;
|
|
292
294
|
if (!this._healpixGrid?.fovObj)
|
|
293
295
|
return;
|
|
@@ -566,6 +568,10 @@ class AstroSphere {
|
|
|
566
568
|
this._activeXYZ2 = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl);
|
|
567
569
|
this._activeBaseLayer = "xyz";
|
|
568
570
|
}
|
|
571
|
+
activateMeshHiPS(descriptor) {
|
|
572
|
+
this._activeMeshHiPS = new MeshHiPS(descriptor.meshRadius, [0.0, 0.0, 0.0], 0, 0, descriptor, this._webgl, this._healpixGrid);
|
|
573
|
+
this._activeBaseLayer = "meships";
|
|
574
|
+
}
|
|
569
575
|
activateWMTS(config) {
|
|
570
576
|
const adapter = new WMTSAdapter(config);
|
|
571
577
|
const xyzConfig = adapter.toXYZLayerConfig();
|
|
@@ -718,6 +724,13 @@ class AstroSphere {
|
|
|
718
724
|
if (centralradeg == null || centraldecdeg == null) {
|
|
719
725
|
return null;
|
|
720
726
|
}
|
|
727
|
+
let fovPolygon = [];
|
|
728
|
+
try {
|
|
729
|
+
fovPolygon = this.getFoVPolygon();
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
console.warn("[AstroSphere] getCurrentStatus: FoV polygon is not available.", error);
|
|
733
|
+
}
|
|
721
734
|
// if (this._rotating && centraldecdeg && centralradeg) {
|
|
722
735
|
const detail = {
|
|
723
736
|
fovDeg: this.fov.minFoV,
|
|
@@ -732,14 +745,14 @@ class AstroSphere {
|
|
|
732
745
|
centralPoint: new Point({ raDeg: centralradeg, decDeg: centraldecdeg }, CoordsType.ASTRO),
|
|
733
746
|
mouseHoverPoint: this.mousePointCoords,
|
|
734
747
|
colorMap: this._selectedColorMap,
|
|
735
|
-
getFoVPolygon:
|
|
748
|
+
getFoVPolygon: fovPolygon,
|
|
736
749
|
};
|
|
737
750
|
return detail;
|
|
738
751
|
// }
|
|
739
752
|
// return null
|
|
740
753
|
}
|
|
741
754
|
changeColorMap(cm) {
|
|
742
|
-
if (!this._activeHiPS && !this._activeXYZ2)
|
|
755
|
+
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
743
756
|
return;
|
|
744
757
|
this._selectedColorMap = cm;
|
|
745
758
|
this._activeHiPS?.changeColorMap(cm);
|
|
@@ -754,6 +767,9 @@ class AstroSphere {
|
|
|
754
767
|
get activeXYZ() {
|
|
755
768
|
return this._activeXYZ2;
|
|
756
769
|
}
|
|
770
|
+
get activeMeshHiPS() {
|
|
771
|
+
return this._activeMeshHiPS;
|
|
772
|
+
}
|
|
757
773
|
isLonLatGridVisible() {
|
|
758
774
|
return this._activeXYZ2?.isLonLatGridVisible() ?? false;
|
|
759
775
|
}
|
|
@@ -794,12 +810,17 @@ class AstroSphere {
|
|
|
794
810
|
return null;
|
|
795
811
|
return this._activeHiPS.getDebugStats();
|
|
796
812
|
}
|
|
813
|
+
getMeshHiPSDebugStats() {
|
|
814
|
+
if (!this._activeMeshHiPS)
|
|
815
|
+
return null;
|
|
816
|
+
return this._activeMeshHiPS.getDebugStats();
|
|
817
|
+
}
|
|
797
818
|
draw(canvas) {
|
|
798
819
|
if (this._refreshingStatus)
|
|
799
820
|
return;
|
|
800
821
|
if (!this._webgl)
|
|
801
822
|
return;
|
|
802
|
-
if (!this._activeHiPS && !this._activeXYZ2)
|
|
823
|
+
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
803
824
|
return;
|
|
804
825
|
if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
|
|
805
826
|
return;
|
|
@@ -906,6 +927,10 @@ class AstroSphere {
|
|
|
906
927
|
const visibleOrder = Math.min(this._healpixGrid.visibleorder, this._activeHiPS.maxOrder);
|
|
907
928
|
this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
908
929
|
}
|
|
930
|
+
if (this._activeBaseLayer === "meships" && this._activeMeshHiPS) {
|
|
931
|
+
const visibleOrder = this._activeMeshHiPS.refreshOrder(this.fov?.minFoV ?? this._healpixGrid.getMinFoV());
|
|
932
|
+
this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
933
|
+
}
|
|
909
934
|
// DRAW HiPS
|
|
910
935
|
const stableFovDeg = this.fov?.minFoV ?? this._healpixGrid.getMinFoV();
|
|
911
936
|
const nowForGrid = performance.now();
|
|
@@ -937,6 +962,9 @@ class AstroSphere {
|
|
|
937
962
|
if (this._activeBaseLayer === "xyz") {
|
|
938
963
|
this._activeXYZ2?.draw(skyEntityDrawInput);
|
|
939
964
|
}
|
|
965
|
+
if (this._activeBaseLayer === "meships") {
|
|
966
|
+
this._activeMeshHiPS?.draw(skyEntityDrawInput);
|
|
967
|
+
}
|
|
940
968
|
this._healpixGrid.draw(skyEntityDrawInput);
|
|
941
969
|
this._equatorialGrid.draw(skyEntityDrawInput);
|
|
942
970
|
this._webgl.enable(this._webgl.DEPTH_TEST);
|
|
@@ -959,7 +987,8 @@ class AstroSphere {
|
|
|
959
987
|
}
|
|
960
988
|
this.activeCatalogues.forEach((cat) => {
|
|
961
989
|
const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
|
|
962
|
-
this._activeXYZ2?.getModelMatrix()
|
|
990
|
+
this._activeXYZ2?.getModelMatrix() ??
|
|
991
|
+
this._activeMeshHiPS?.getModelMatrix();
|
|
963
992
|
if (activeModelMatrix) {
|
|
964
993
|
cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
965
994
|
}
|
|
@@ -967,7 +996,8 @@ class AstroSphere {
|
|
|
967
996
|
this.emitHoveredSourceIfChanged();
|
|
968
997
|
this.activeFootprintSets.forEach((fst) => {
|
|
969
998
|
const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
|
|
970
|
-
this._activeXYZ2?.getModelMatrix()
|
|
999
|
+
this._activeXYZ2?.getModelMatrix() ??
|
|
1000
|
+
this._activeMeshHiPS?.getModelMatrix();
|
|
971
1001
|
if (activeModelMatrix) {
|
|
972
1002
|
fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
973
1003
|
}
|
package/lib-esm/AstroViewer.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { HiPSDebugStats } from './model/hips/HiPSConfig.js';
|
|
|
14
14
|
import { XYZMap } from './model/earth/XYZMap.js';
|
|
15
15
|
import { TerraPointSetGL } from './model/terra/TerraPointSetGL.js';
|
|
16
16
|
import { TerraFootprintSetGL } from './model/terra/TerraFootprintSetGL.js';
|
|
17
|
+
import type { MeshHiPSConfig, MeshHiPSDebugStats } from './model/meships/MeshHiPSTypes.js';
|
|
17
18
|
export declare class AstroViewer {
|
|
18
19
|
private astroSphere;
|
|
19
20
|
private canvas;
|
|
@@ -54,11 +55,14 @@ export declare class AstroViewer {
|
|
|
54
55
|
name?: string;
|
|
55
56
|
}): void;
|
|
56
57
|
activateWMTS(config: WMTSLayerConfig): void;
|
|
58
|
+
activateMeshHiPS(config: MeshHiPSConfig): void;
|
|
57
59
|
setXYZMaxConcurrentRequests(value: number): void;
|
|
58
60
|
getXYZMaxConcurrentRequests(): number;
|
|
59
61
|
getXYZDebugStats(): XYZDebugStats;
|
|
60
62
|
getHiPSDebugStats(): HiPSDebugStats | null;
|
|
63
|
+
getMeshHiPSDebugStats(): MeshHiPSDebugStats | null;
|
|
61
64
|
loadHiPS(baseUrl: string): Promise<string>;
|
|
65
|
+
loadMeshHiPS(baseUrl: string, config?: Omit<MeshHiPSConfig, 'baseUrl'>): Promise<string>;
|
|
62
66
|
changeColorMap(colorMapName: ColorMapName): void;
|
|
63
67
|
changeCustomColorMap(colorMap: ColorMap): void;
|
|
64
68
|
getActiveHiPS(): HiPS | null;
|
package/lib-esm/AstroViewer.js
CHANGED
|
@@ -21,6 +21,7 @@ import { xyzTileRequestScheduler } from './model/earth/XYZTileRequestScheduler.j
|
|
|
21
21
|
import { XYZMapDescriptor } from './model/earth/XYZMapDescriptor.js';
|
|
22
22
|
import { TerraPointSetGL } from './model/terra/TerraPointSetGL.js';
|
|
23
23
|
import { TerraFootprintSetGL } from './model/terra/TerraFootprintSetGL.js';
|
|
24
|
+
import { MeshHiPSDescriptor } from './model/meships/MeshHiPSDescriptor.js';
|
|
24
25
|
// & {
|
|
25
26
|
// viewportWidth: number
|
|
26
27
|
// viewportHeight: number
|
|
@@ -136,6 +137,9 @@ export class AstroViewer {
|
|
|
136
137
|
activateWMTS(config) {
|
|
137
138
|
this.astroSphere.activateWMTS(config);
|
|
138
139
|
}
|
|
140
|
+
activateMeshHiPS(config) {
|
|
141
|
+
this.astroSphere.activateMeshHiPS(new MeshHiPSDescriptor(config));
|
|
142
|
+
}
|
|
139
143
|
setXYZMaxConcurrentRequests(value) {
|
|
140
144
|
xyzTileRequestScheduler.setMaxConcurrent(value);
|
|
141
145
|
}
|
|
@@ -148,6 +152,9 @@ export class AstroViewer {
|
|
|
148
152
|
getHiPSDebugStats() {
|
|
149
153
|
return this.astroSphere.getHiPSDebugStats();
|
|
150
154
|
}
|
|
155
|
+
getMeshHiPSDebugStats() {
|
|
156
|
+
return this.astroSphere.getMeshHiPSDebugStats();
|
|
157
|
+
}
|
|
151
158
|
async loadHiPS(baseUrl) {
|
|
152
159
|
const hipsUrl = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/';
|
|
153
160
|
const resp = await fetch(hipsUrl + 'properties');
|
|
@@ -159,6 +166,26 @@ export class AstroViewer {
|
|
|
159
166
|
return desc.surveyName;
|
|
160
167
|
// this.activateHiPS(desc);
|
|
161
168
|
}
|
|
169
|
+
async loadMeshHiPS(baseUrl, config = {}) {
|
|
170
|
+
const meshHiPSUrl = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/';
|
|
171
|
+
let propsText = '';
|
|
172
|
+
const resp = await fetch(meshHiPSUrl + 'properties');
|
|
173
|
+
if (resp.ok) {
|
|
174
|
+
propsText = await resp.text();
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const jsonResp = await fetch(meshHiPSUrl + 'properties.json');
|
|
178
|
+
if (!jsonResp.ok)
|
|
179
|
+
throw new Error(`HTTP ${jsonResp.status} fetching MeshHiPS properties`);
|
|
180
|
+
const json = await jsonResp.json();
|
|
181
|
+
propsText = Object.entries(json)
|
|
182
|
+
.map(([key, value]) => `${key}=${String(value)}`)
|
|
183
|
+
.join('\n');
|
|
184
|
+
}
|
|
185
|
+
const desc = new MeshHiPSDescriptor({ ...config, baseUrl: meshHiPSUrl }, propsText);
|
|
186
|
+
this.astroSphere.activateMeshHiPS(desc);
|
|
187
|
+
return desc.name;
|
|
188
|
+
}
|
|
162
189
|
// changeColorMap(hips: HiPS, colorMapName: ColorMapName) {
|
|
163
190
|
changeColorMap(colorMapName) {
|
|
164
191
|
const colorMap = ColorMaps[colorMapName];
|
package/lib-esm/index.d.ts
CHANGED
|
@@ -19,7 +19,10 @@ export type { ColorMap, ColorMapName } from './model/ColorMaps.js';
|
|
|
19
19
|
export { HiPS } from './model/hips/HiPS.js';
|
|
20
20
|
export { XYZMap } from './model/earth/XYZMap.js';
|
|
21
21
|
export { WMTSAdapter } from './model/earth/WMTSAdapter.js';
|
|
22
|
+
export { MeshHiPS } from './model/meships/MeshHiPS.js';
|
|
23
|
+
export { MeshHiPSDescriptor } from './model/meships/MeshHiPSDescriptor.js';
|
|
22
24
|
export type { XYZLayerConfig, WMTSLayerConfig, WMTSRequestEncoding } from './model/earth/XYZConfig.js';
|
|
23
25
|
export type { XYZTileCoord, XYZTileMesh } from './model/earth/XYZTypes.js';
|
|
26
|
+
export type { MeshHiPSConfig, MeshHiPSDebugStats, MeshHiPSTileCoord } from './model/meships/MeshHiPSTypes.js';
|
|
24
27
|
export { Source } from './model/Source.js';
|
|
25
28
|
export { Footprint } from './model/footprints/Footprint.js';
|
package/lib-esm/index.js
CHANGED
|
@@ -28,6 +28,8 @@ export { ColorMaps, COLOR_MAP_SAMPLE_COUNT, createColorMapFromSamples } from './
|
|
|
28
28
|
export { HiPS } from './model/hips/HiPS.js';
|
|
29
29
|
export { XYZMap } from './model/earth/XYZMap.js';
|
|
30
30
|
export { WMTSAdapter } from './model/earth/WMTSAdapter.js';
|
|
31
|
+
export { MeshHiPS } from './model/meships/MeshHiPS.js';
|
|
32
|
+
export { MeshHiPSDescriptor } from './model/meships/MeshHiPSDescriptor.js';
|
|
31
33
|
export { Source } from './model/Source.js';
|
|
32
34
|
export { Footprint } from './model/footprints/Footprint.js';
|
|
33
35
|
console.log('astroviewer UMD loaded');
|
|
@@ -48,7 +48,7 @@ export type XYZRequestSchedulerDebugStats = {
|
|
|
48
48
|
hostsInBackoff: XYZRequestBackoffDebugEntry[];
|
|
49
49
|
};
|
|
50
50
|
export type XYZDebugStats = {
|
|
51
|
-
activeBaseLayer: 'hips' | 'xyz' | null;
|
|
51
|
+
activeBaseLayer: 'hips' | 'xyz' | 'meships' | null;
|
|
52
52
|
layer: XYZLayerDebugStats | null;
|
|
53
53
|
requests: XYZRequestSchedulerDebugStats;
|
|
54
54
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AbstractSkyEntity, SkyEntityDrawInput } from '../AbstractSkyEntity.js';
|
|
2
|
+
import { HealpixGrid } from '../grid/HealpixGrid.js';
|
|
3
|
+
import { MeshHiPSDescriptor } from './MeshHiPSDescriptor.js';
|
|
4
|
+
import type { MeshHiPSDebugStats } from './MeshHiPSTypes.js';
|
|
5
|
+
export declare class MeshHiPS extends AbstractSkyEntity {
|
|
6
|
+
private _descriptor;
|
|
7
|
+
private _healpixGrid;
|
|
8
|
+
private _shaderProgram;
|
|
9
|
+
private _tiles;
|
|
10
|
+
private _currentOrder;
|
|
11
|
+
private _visibleTiles;
|
|
12
|
+
private _coverageTiles;
|
|
13
|
+
constructor(radius: number, position: [number, number, number], xrad: number, yrad: number, _descriptor: MeshHiPSDescriptor, webgl: WebGL2RenderingContext, _healpixGrid: HealpixGrid);
|
|
14
|
+
get currentOrder(): number;
|
|
15
|
+
get maxOrder(): number;
|
|
16
|
+
get minOrder(): number;
|
|
17
|
+
get baseURL(): string;
|
|
18
|
+
get propertiesRawText(): string;
|
|
19
|
+
get properties(): ReadonlyMap<string, string>;
|
|
20
|
+
getProperty(key: string): string | undefined;
|
|
21
|
+
refreshOrder(fovDeg: number | undefined): number;
|
|
22
|
+
draw(input: SkyEntityDrawInput): void;
|
|
23
|
+
getDebugStats(): MeshHiPSDebugStats;
|
|
24
|
+
private resolveVisibleTiles;
|
|
25
|
+
private resolveCoverageTiles;
|
|
26
|
+
private groupTilesByOrder;
|
|
27
|
+
private ensureTiles;
|
|
28
|
+
private evictCached;
|
|
29
|
+
private tileKey;
|
|
30
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* AstroViewer
|
|
3
|
+
* Copyright (C) Fabrizio Giordano
|
|
4
|
+
* SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
|
|
5
|
+
*/
|
|
6
|
+
import { AbstractSkyEntity } from '../AbstractSkyEntity.js';
|
|
7
|
+
import { fovHelper } from '../hips/FoVHelper.js';
|
|
8
|
+
import { MeshHiPSShaderProgram } from '../../shader/MeshHiPSShaderProgram.js';
|
|
9
|
+
import { MeshHiPSTile } from './MeshHiPSTile.js';
|
|
10
|
+
export class MeshHiPS extends AbstractSkyEntity {
|
|
11
|
+
_descriptor;
|
|
12
|
+
_healpixGrid;
|
|
13
|
+
_shaderProgram;
|
|
14
|
+
_tiles = new Map();
|
|
15
|
+
_currentOrder;
|
|
16
|
+
_visibleTiles = [];
|
|
17
|
+
_coverageTiles = [];
|
|
18
|
+
constructor(radius, position, xrad, yrad, _descriptor, webgl, _healpixGrid) {
|
|
19
|
+
super(radius, position, xrad, yrad, _descriptor.name, webgl, false);
|
|
20
|
+
this._descriptor = _descriptor;
|
|
21
|
+
this._healpixGrid = _healpixGrid;
|
|
22
|
+
this.initGL(webgl);
|
|
23
|
+
this._shaderProgram = new MeshHiPSShaderProgram(webgl);
|
|
24
|
+
this._shaderProgram.enableProgram();
|
|
25
|
+
this._currentOrder = _descriptor.selectedOrder;
|
|
26
|
+
}
|
|
27
|
+
get currentOrder() {
|
|
28
|
+
return this._currentOrder;
|
|
29
|
+
}
|
|
30
|
+
get maxOrder() {
|
|
31
|
+
return this._descriptor.maxOrder;
|
|
32
|
+
}
|
|
33
|
+
get minOrder() {
|
|
34
|
+
return this._descriptor.minOrder;
|
|
35
|
+
}
|
|
36
|
+
get baseURL() {
|
|
37
|
+
return this._descriptor.baseUrl;
|
|
38
|
+
}
|
|
39
|
+
get propertiesRawText() {
|
|
40
|
+
return this._descriptor.propertiesRawText;
|
|
41
|
+
}
|
|
42
|
+
get properties() {
|
|
43
|
+
return this._descriptor.properties;
|
|
44
|
+
}
|
|
45
|
+
getProperty(key) {
|
|
46
|
+
return this._descriptor.getProperty(key);
|
|
47
|
+
}
|
|
48
|
+
refreshOrder(fovDeg) {
|
|
49
|
+
if (this._descriptor.fixedOrder) {
|
|
50
|
+
this._currentOrder = this._descriptor.selectedOrder;
|
|
51
|
+
return this._currentOrder;
|
|
52
|
+
}
|
|
53
|
+
const fov = Number.isFinite(fovDeg) && fovDeg > 0
|
|
54
|
+
? fovDeg
|
|
55
|
+
: this._healpixGrid.getMinFoV();
|
|
56
|
+
const safeFov = Number.isFinite(fov) && fov > 0 ? fov : 1e-6;
|
|
57
|
+
const order = fovHelper.getHiPSNorder(safeFov, this._currentOrder);
|
|
58
|
+
this._currentOrder = Math.max(this._descriptor.minOrder, Math.min(this._descriptor.maxOrder, order));
|
|
59
|
+
return this._currentOrder;
|
|
60
|
+
}
|
|
61
|
+
draw(input) {
|
|
62
|
+
const vMatrix = input.camera.getCameraMatrix();
|
|
63
|
+
if (!vMatrix || !input.pMatrix)
|
|
64
|
+
return;
|
|
65
|
+
this.refreshOrder(input.fovDeg);
|
|
66
|
+
this._visibleTiles = this.resolveVisibleTiles();
|
|
67
|
+
this._coverageTiles = this.resolveCoverageTiles(this._visibleTiles);
|
|
68
|
+
this.ensureTiles(this._coverageTiles);
|
|
69
|
+
this.evictCached();
|
|
70
|
+
const gl = this._webgl;
|
|
71
|
+
const pMatrix = input.pMatrix;
|
|
72
|
+
const mMatrix = this.getModelMatrix();
|
|
73
|
+
const wasCullFace = gl.isEnabled(gl.CULL_FACE);
|
|
74
|
+
const wasDepthTest = gl.isEnabled(gl.DEPTH_TEST);
|
|
75
|
+
const wasDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK);
|
|
76
|
+
const previousDepthFunc = gl.getParameter(gl.DEPTH_FUNC);
|
|
77
|
+
gl.enable(gl.DEPTH_TEST);
|
|
78
|
+
gl.depthMask(true);
|
|
79
|
+
gl.depthFunc(gl.LEQUAL);
|
|
80
|
+
gl.disable(gl.CULL_FACE);
|
|
81
|
+
const byOrder = this.groupTilesByOrder(this._coverageTiles);
|
|
82
|
+
const orders = Array.from(byOrder.keys()).sort((a, b) => a - b);
|
|
83
|
+
for (const order of orders) {
|
|
84
|
+
for (const coord of byOrder.get(order) ?? []) {
|
|
85
|
+
this._tiles.get(this.tileKey(coord))?.draw(pMatrix, vMatrix, mMatrix, this._descriptor.color, this._descriptor.wireframe);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (wasCullFace)
|
|
89
|
+
gl.enable(gl.CULL_FACE);
|
|
90
|
+
if (!wasDepthTest)
|
|
91
|
+
gl.disable(gl.DEPTH_TEST);
|
|
92
|
+
gl.depthFunc(previousDepthFunc);
|
|
93
|
+
gl.depthMask(wasDepthMask);
|
|
94
|
+
}
|
|
95
|
+
getDebugStats() {
|
|
96
|
+
const tiles = Array.from(this._tiles.values());
|
|
97
|
+
return {
|
|
98
|
+
activeBaseLayer: 'meships',
|
|
99
|
+
meshHiPSName: this._descriptor.name,
|
|
100
|
+
meshHiPSUrl: this._descriptor.baseUrl,
|
|
101
|
+
currentOrder: this._currentOrder,
|
|
102
|
+
visibleTileCount: this._visibleTiles.length,
|
|
103
|
+
coverageTileCount: this._coverageTiles.length,
|
|
104
|
+
cacheSize: this._tiles.size,
|
|
105
|
+
readyTileCount: tiles.filter((tile) => tile.ready).length,
|
|
106
|
+
loadingTileCount: tiles.filter((tile) => tile.loading).length,
|
|
107
|
+
failedTileCount: tiles.filter((tile) => tile.failed).length,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
resolveVisibleTiles() {
|
|
111
|
+
const manager = this._healpixGrid.visibleTilesManager;
|
|
112
|
+
const byOrder = manager?.visibleTilesByOrder;
|
|
113
|
+
const pixels = byOrder?.order === this._currentOrder && Array.isArray(byOrder.pixels)
|
|
114
|
+
? byOrder.pixels
|
|
115
|
+
: [];
|
|
116
|
+
if (pixels.length > 0) {
|
|
117
|
+
return pixels.map((ipix) => ({ order: this._currentOrder, ipix }));
|
|
118
|
+
}
|
|
119
|
+
const tileCount = 12 * 4 ** this._currentOrder;
|
|
120
|
+
return Array.from({ length: tileCount }, (_, ipix) => ({ order: this._currentOrder, ipix }));
|
|
121
|
+
}
|
|
122
|
+
resolveCoverageTiles(visibleTiles) {
|
|
123
|
+
const tiles = new Map();
|
|
124
|
+
const add = (coord) => {
|
|
125
|
+
if (coord.order < this._descriptor.minOrder || coord.order > this._descriptor.maxOrder)
|
|
126
|
+
return;
|
|
127
|
+
tiles.set(this.tileKey(coord), coord);
|
|
128
|
+
};
|
|
129
|
+
for (const coord of visibleTiles) {
|
|
130
|
+
add(coord);
|
|
131
|
+
for (let order = coord.order - 1; order >= this._descriptor.minOrder; order--) {
|
|
132
|
+
const shift = 2 * (coord.order - order);
|
|
133
|
+
add({ order, ipix: coord.ipix >> shift });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const manager = this._healpixGrid.visibleTilesManager;
|
|
137
|
+
const ancestorsMap = manager?.ancestorsMap;
|
|
138
|
+
if (ancestorsMap) {
|
|
139
|
+
for (const [order, ipixes] of ancestorsMap) {
|
|
140
|
+
if (order < this._descriptor.minOrder || order > this._descriptor.maxOrder)
|
|
141
|
+
continue;
|
|
142
|
+
for (const ipix of ipixes)
|
|
143
|
+
add({ order, ipix });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return Array.from(tiles.values());
|
|
147
|
+
}
|
|
148
|
+
groupTilesByOrder(coords) {
|
|
149
|
+
const byOrder = new Map();
|
|
150
|
+
for (const coord of coords) {
|
|
151
|
+
const list = byOrder.get(coord.order) ?? [];
|
|
152
|
+
list.push(coord);
|
|
153
|
+
byOrder.set(coord.order, list);
|
|
154
|
+
}
|
|
155
|
+
return byOrder;
|
|
156
|
+
}
|
|
157
|
+
ensureTiles(coords) {
|
|
158
|
+
for (const coord of coords) {
|
|
159
|
+
const key = this.tileKey(coord);
|
|
160
|
+
if (this._tiles.has(key))
|
|
161
|
+
continue;
|
|
162
|
+
this._tiles.set(key, new MeshHiPSTile(coord, this._descriptor.getTileUrl(coord.order, coord.ipix), this._webgl, this._shaderProgram));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
evictCached() {
|
|
166
|
+
const maxCached = this._descriptor.maxCachedTiles;
|
|
167
|
+
if (this._tiles.size <= maxCached)
|
|
168
|
+
return;
|
|
169
|
+
const visibleKeys = new Set(this._coverageTiles.map((coord) => this.tileKey(coord)));
|
|
170
|
+
const evictable = Array.from(this._tiles.entries())
|
|
171
|
+
.filter(([key]) => !visibleKeys.has(key))
|
|
172
|
+
.sort((a, b) => a[1].lastUsedAt - b[1].lastUsedAt);
|
|
173
|
+
while (this._tiles.size > maxCached && evictable.length > 0) {
|
|
174
|
+
const [key, tile] = evictable.shift();
|
|
175
|
+
tile.dispose();
|
|
176
|
+
this._tiles.delete(key);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
tileKey(coord) {
|
|
180
|
+
return `${coord.order}/${coord.ipix}`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MeshHiPSConfig } from './MeshHiPSTypes.js';
|
|
2
|
+
export declare class MeshHiPSDescriptor {
|
|
3
|
+
private _name;
|
|
4
|
+
private _baseUrl;
|
|
5
|
+
private _minOrder;
|
|
6
|
+
private _maxOrder;
|
|
7
|
+
private _selectedOrder;
|
|
8
|
+
private _fixedOrder;
|
|
9
|
+
private _maxCachedTiles;
|
|
10
|
+
private _color;
|
|
11
|
+
private _wireframe;
|
|
12
|
+
private _propertiesRawText;
|
|
13
|
+
private _propertiesMap;
|
|
14
|
+
private _meshRadius;
|
|
15
|
+
constructor(config: MeshHiPSConfig, propertiesText?: string);
|
|
16
|
+
get name(): string;
|
|
17
|
+
get baseUrl(): string;
|
|
18
|
+
get minOrder(): number;
|
|
19
|
+
get maxOrder(): number;
|
|
20
|
+
get selectedOrder(): number;
|
|
21
|
+
get fixedOrder(): boolean;
|
|
22
|
+
get maxCachedTiles(): number;
|
|
23
|
+
get color(): [number, number, number, number];
|
|
24
|
+
get wireframe(): boolean;
|
|
25
|
+
get propertiesRawText(): string;
|
|
26
|
+
get properties(): ReadonlyMap<string, string>;
|
|
27
|
+
get meshRadius(): number;
|
|
28
|
+
getProperty(key: string): string | undefined;
|
|
29
|
+
getTileUrl(order: number, ipix: number): string;
|
|
30
|
+
private parseProperties;
|
|
31
|
+
private readNumber;
|
|
32
|
+
private clampOrder;
|
|
33
|
+
private normalizeBaseUrl;
|
|
34
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* AstroViewer
|
|
3
|
+
* Copyright (C) Fabrizio Giordano
|
|
4
|
+
* SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
|
|
5
|
+
*/
|
|
6
|
+
export class MeshHiPSDescriptor {
|
|
7
|
+
_name = 'MeshHiPS';
|
|
8
|
+
_baseUrl;
|
|
9
|
+
_minOrder = 0;
|
|
10
|
+
_maxOrder = 0;
|
|
11
|
+
_selectedOrder;
|
|
12
|
+
_fixedOrder = false;
|
|
13
|
+
_maxCachedTiles = 384;
|
|
14
|
+
// default neutral color (contrasts with page background)
|
|
15
|
+
_color = [0.32, 0.34, 0.36, 1.0];
|
|
16
|
+
_wireframe = false;
|
|
17
|
+
_propertiesRawText = '';
|
|
18
|
+
_propertiesMap = new Map();
|
|
19
|
+
_meshRadius = null;
|
|
20
|
+
constructor(config, propertiesText = '') {
|
|
21
|
+
this._baseUrl = this.normalizeBaseUrl(config.baseUrl);
|
|
22
|
+
this._propertiesRawText = propertiesText;
|
|
23
|
+
this.parseProperties(propertiesText);
|
|
24
|
+
this._name = config.name ?? this._propertiesMap.get('obs_collection') ?? this._propertiesMap.get('label') ?? this._name;
|
|
25
|
+
this._minOrder = config.minOrder ?? this.readNumber('hips_order_min', this._minOrder);
|
|
26
|
+
this._maxOrder = config.maxOrder ?? this.readNumber('hips_order', this.readNumber('hips_order_max', this._maxOrder));
|
|
27
|
+
this._fixedOrder = config.order !== undefined;
|
|
28
|
+
this._selectedOrder = config.order ?? this._minOrder;
|
|
29
|
+
this._maxCachedTiles = config.maxCachedTiles ?? this._maxCachedTiles;
|
|
30
|
+
// color: prefer explicit config, then properties.mesh_color, then default
|
|
31
|
+
if (config.color) {
|
|
32
|
+
this._color = config.color;
|
|
33
|
+
}
|
|
34
|
+
else if (this._propertiesMap.has('mesh_color')) {
|
|
35
|
+
const raw = this._propertiesMap.get('mesh_color') || '';
|
|
36
|
+
const parts = raw.split(/[,\s]+/).map((v) => Number(v)).filter(Number.isFinite);
|
|
37
|
+
if (parts.length >= 3) {
|
|
38
|
+
let [r, g, b, a] = parts;
|
|
39
|
+
if (r > 1 || g > 1 || b > 1) {
|
|
40
|
+
// assume 0-255 range
|
|
41
|
+
r = r / 255;
|
|
42
|
+
g = g / 255;
|
|
43
|
+
b = b / 255;
|
|
44
|
+
}
|
|
45
|
+
if (!Number.isFinite(a))
|
|
46
|
+
a = 1;
|
|
47
|
+
this._color = [r, g, b, a];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this._color = this._color;
|
|
52
|
+
}
|
|
53
|
+
this._wireframe = config.wireframe ?? this._wireframe;
|
|
54
|
+
this._selectedOrder = this.clampOrder(this._selectedOrder);
|
|
55
|
+
// mesh radius: prefer explicit value from config, otherwise read mandatory property
|
|
56
|
+
if (Number.isFinite(config.meshRadius)) {
|
|
57
|
+
this._meshRadius = config.meshRadius;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const radiusFromProps = this.readNumber('mesh_radius', NaN);
|
|
61
|
+
if (!Number.isFinite(radiusFromProps)) {
|
|
62
|
+
throw new Error('Missing mandatory property "mesh_radius" in MeshHiPS properties');
|
|
63
|
+
}
|
|
64
|
+
this._meshRadius = radiusFromProps;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
get name() {
|
|
68
|
+
return this._name;
|
|
69
|
+
}
|
|
70
|
+
get baseUrl() {
|
|
71
|
+
return this._baseUrl;
|
|
72
|
+
}
|
|
73
|
+
get minOrder() {
|
|
74
|
+
return this._minOrder;
|
|
75
|
+
}
|
|
76
|
+
get maxOrder() {
|
|
77
|
+
return this._maxOrder;
|
|
78
|
+
}
|
|
79
|
+
get selectedOrder() {
|
|
80
|
+
return this._selectedOrder;
|
|
81
|
+
}
|
|
82
|
+
get fixedOrder() {
|
|
83
|
+
return this._fixedOrder;
|
|
84
|
+
}
|
|
85
|
+
get maxCachedTiles() {
|
|
86
|
+
return this._maxCachedTiles;
|
|
87
|
+
}
|
|
88
|
+
get color() {
|
|
89
|
+
return this._color;
|
|
90
|
+
}
|
|
91
|
+
get wireframe() {
|
|
92
|
+
return this._wireframe;
|
|
93
|
+
}
|
|
94
|
+
get propertiesRawText() {
|
|
95
|
+
return this._propertiesRawText;
|
|
96
|
+
}
|
|
97
|
+
get properties() {
|
|
98
|
+
return new Map(this._propertiesMap);
|
|
99
|
+
}
|
|
100
|
+
get meshRadius() {
|
|
101
|
+
return this._meshRadius;
|
|
102
|
+
}
|
|
103
|
+
getProperty(key) {
|
|
104
|
+
return this._propertiesMap.get(key);
|
|
105
|
+
}
|
|
106
|
+
getTileUrl(order, ipix) {
|
|
107
|
+
const dir = Math.floor(ipix / 10_000) * 10_000;
|
|
108
|
+
return `${this._baseUrl}Norder${order}/Dir${dir}/Npix${ipix}.obj`;
|
|
109
|
+
}
|
|
110
|
+
parseProperties(propertiesText) {
|
|
111
|
+
const lines = propertiesText.split(/\r\n|\n/);
|
|
112
|
+
for (const raw of lines) {
|
|
113
|
+
const line = raw.trim();
|
|
114
|
+
if (!line || line.startsWith('#'))
|
|
115
|
+
continue;
|
|
116
|
+
const idx = line.indexOf('=');
|
|
117
|
+
if (idx < 0)
|
|
118
|
+
continue;
|
|
119
|
+
this._propertiesMap.set(line.slice(0, idx).trim(), line.slice(idx + 1).trim());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
readNumber(key, fallback) {
|
|
123
|
+
const parsed = Number(this._propertiesMap.get(key));
|
|
124
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
125
|
+
}
|
|
126
|
+
clampOrder(order) {
|
|
127
|
+
return Math.max(this._minOrder, Math.min(this._maxOrder, order));
|
|
128
|
+
}
|
|
129
|
+
normalizeBaseUrl(baseUrl) {
|
|
130
|
+
return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
131
|
+
}
|
|
132
|
+
}
|