astro-viewer 3.2.1 → 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 +767 -6
- 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 +28 -5
- 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 +4 -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();
|
|
@@ -746,7 +752,7 @@ class AstroSphere {
|
|
|
746
752
|
// return null
|
|
747
753
|
}
|
|
748
754
|
changeColorMap(cm) {
|
|
749
|
-
if (!this._activeHiPS && !this._activeXYZ2)
|
|
755
|
+
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
750
756
|
return;
|
|
751
757
|
this._selectedColorMap = cm;
|
|
752
758
|
this._activeHiPS?.changeColorMap(cm);
|
|
@@ -761,6 +767,9 @@ class AstroSphere {
|
|
|
761
767
|
get activeXYZ() {
|
|
762
768
|
return this._activeXYZ2;
|
|
763
769
|
}
|
|
770
|
+
get activeMeshHiPS() {
|
|
771
|
+
return this._activeMeshHiPS;
|
|
772
|
+
}
|
|
764
773
|
isLonLatGridVisible() {
|
|
765
774
|
return this._activeXYZ2?.isLonLatGridVisible() ?? false;
|
|
766
775
|
}
|
|
@@ -801,12 +810,17 @@ class AstroSphere {
|
|
|
801
810
|
return null;
|
|
802
811
|
return this._activeHiPS.getDebugStats();
|
|
803
812
|
}
|
|
813
|
+
getMeshHiPSDebugStats() {
|
|
814
|
+
if (!this._activeMeshHiPS)
|
|
815
|
+
return null;
|
|
816
|
+
return this._activeMeshHiPS.getDebugStats();
|
|
817
|
+
}
|
|
804
818
|
draw(canvas) {
|
|
805
819
|
if (this._refreshingStatus)
|
|
806
820
|
return;
|
|
807
821
|
if (!this._webgl)
|
|
808
822
|
return;
|
|
809
|
-
if (!this._activeHiPS && !this._activeXYZ2)
|
|
823
|
+
if (!this._activeHiPS && !this._activeXYZ2 && !this._activeMeshHiPS)
|
|
810
824
|
return;
|
|
811
825
|
if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
|
|
812
826
|
return;
|
|
@@ -913,6 +927,10 @@ class AstroSphere {
|
|
|
913
927
|
const visibleOrder = Math.min(this._healpixGrid.visibleorder, this._activeHiPS.maxOrder);
|
|
914
928
|
this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
|
|
915
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
|
+
}
|
|
916
934
|
// DRAW HiPS
|
|
917
935
|
const stableFovDeg = this.fov?.minFoV ?? this._healpixGrid.getMinFoV();
|
|
918
936
|
const nowForGrid = performance.now();
|
|
@@ -944,6 +962,9 @@ class AstroSphere {
|
|
|
944
962
|
if (this._activeBaseLayer === "xyz") {
|
|
945
963
|
this._activeXYZ2?.draw(skyEntityDrawInput);
|
|
946
964
|
}
|
|
965
|
+
if (this._activeBaseLayer === "meships") {
|
|
966
|
+
this._activeMeshHiPS?.draw(skyEntityDrawInput);
|
|
967
|
+
}
|
|
947
968
|
this._healpixGrid.draw(skyEntityDrawInput);
|
|
948
969
|
this._equatorialGrid.draw(skyEntityDrawInput);
|
|
949
970
|
this._webgl.enable(this._webgl.DEPTH_TEST);
|
|
@@ -966,7 +987,8 @@ class AstroSphere {
|
|
|
966
987
|
}
|
|
967
988
|
this.activeCatalogues.forEach((cat) => {
|
|
968
989
|
const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
|
|
969
|
-
this._activeXYZ2?.getModelMatrix()
|
|
990
|
+
this._activeXYZ2?.getModelMatrix() ??
|
|
991
|
+
this._activeMeshHiPS?.getModelMatrix();
|
|
970
992
|
if (activeModelMatrix) {
|
|
971
993
|
cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
972
994
|
}
|
|
@@ -974,7 +996,8 @@ class AstroSphere {
|
|
|
974
996
|
this.emitHoveredSourceIfChanged();
|
|
975
997
|
this.activeFootprintSets.forEach((fst) => {
|
|
976
998
|
const activeModelMatrix = this._activeHiPS?.getModelMatrix() ??
|
|
977
|
-
this._activeXYZ2?.getModelMatrix()
|
|
999
|
+
this._activeXYZ2?.getModelMatrix() ??
|
|
1000
|
+
this._activeMeshHiPS?.getModelMatrix();
|
|
978
1001
|
if (activeModelMatrix) {
|
|
979
1002
|
fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
|
|
980
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
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MeshHiPSShaderProgram } from '../../shader/MeshHiPSShaderProgram.js';
|
|
2
|
+
import type { MeshHiPSTileCoord } from './MeshHiPSTypes.js';
|
|
3
|
+
type Mat4 = Float32Array;
|
|
4
|
+
export declare class MeshHiPSTile {
|
|
5
|
+
readonly coord: MeshHiPSTileCoord;
|
|
6
|
+
private _url;
|
|
7
|
+
private _webgl;
|
|
8
|
+
private _shaderProgram;
|
|
9
|
+
private _gpuMesh;
|
|
10
|
+
private _ready;
|
|
11
|
+
private _loading;
|
|
12
|
+
private _failed;
|
|
13
|
+
private _lastUsedAt;
|
|
14
|
+
private _createdAt;
|
|
15
|
+
constructor(coord: MeshHiPSTileCoord, _url: string, _webgl: WebGL2RenderingContext, _shaderProgram: MeshHiPSShaderProgram);
|
|
16
|
+
get ready(): boolean;
|
|
17
|
+
get loading(): boolean;
|
|
18
|
+
get failed(): boolean;
|
|
19
|
+
get lastUsedAt(): number;
|
|
20
|
+
get createdAt(): number;
|
|
21
|
+
touch(): void;
|
|
22
|
+
draw(pMatrix: Mat4, vMatrix: Mat4, mMatrix: Mat4, color: [number, number, number, number], wireframe: boolean): boolean;
|
|
23
|
+
dispose(): void;
|
|
24
|
+
private load;
|
|
25
|
+
private uploadMesh;
|
|
26
|
+
private buildLineIndices;
|
|
27
|
+
private get key();
|
|
28
|
+
}
|
|
29
|
+
export {};
|