@holoscript/radio-astronomy-plugin 2.0.2 → 2.0.3

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.
@@ -33,7 +33,9 @@ describe('FITS Viewer Pipeline', () => {
33
33
  });
34
34
 
35
35
  it('parses a synthetic 3D FITS spectral cube', () => {
36
- const nx = 4, ny = 3, nz = 5;
36
+ const nx = 4,
37
+ ny = 3,
38
+ nz = 5;
37
39
  const data: number[] = [];
38
40
  for (let k = 0; k < nz; k++) {
39
41
  for (let j = 0; j < ny; j++) {
@@ -107,9 +109,9 @@ describe('FITS Viewer Pipeline', () => {
107
109
 
108
110
  const fits = parseFITS(buffer);
109
111
  // Physical value = BSCALE * raw + BZERO
110
- expect(fits.data[0]).toBeCloseTo(50); // 0.1 * 0 + 50
111
- expect(fits.data[1]).toBeCloseTo(60); // 0.1 * 100 + 50
112
- expect(fits.data[3]).toBeCloseTo(80); // 0.1 * 300 + 50
112
+ expect(fits.data[0]).toBeCloseTo(50); // 0.1 * 0 + 50
113
+ expect(fits.data[1]).toBeCloseTo(60); // 0.1 * 100 + 50
114
+ expect(fits.data[3]).toBeCloseTo(80); // 0.1 * 300 + 50
113
115
  });
114
116
 
115
117
  it('2D image converts to nx×ny×1 grid', () => {
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "@holoscript/radio-astronomy-plugin",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "Radio Astrophysics plugin for HoloScript spatial environments. Adds primitives for interferometers, pulsar analysis, and synchrotron emissions.",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "vitest run",
10
+ "lint": "eslint src --ext .ts",
11
+ "clean": "rimraf dist",
12
+ "test:coverage": "vitest run --coverage"
13
+ },
7
14
  "keywords": [
8
15
  "holoscript",
9
16
  "plugin",
@@ -17,17 +24,10 @@
17
24
  "@holoscript/engine": "^6.1.3"
18
25
  },
19
26
  "peerDependencies": {
20
- "@holoscript/core": "8.0.6"
27
+ "@holoscript/core": ">=8.0.0"
21
28
  },
22
29
  "devDependencies": {
23
30
  "typescript": "^5.0.0",
24
31
  "vitest": "^4.1.5"
25
- },
26
- "scripts": {
27
- "build": "tsc",
28
- "test": "vitest run",
29
- "lint": "eslint src --ext .ts",
30
- "clean": "rimraf dist",
31
- "test:coverage": "vitest run --coverage"
32
32
  }
33
- }
33
+ }
@@ -121,7 +121,8 @@ export function SpectralCubeViewer({
121
121
 
122
122
  // Data range for colormap normalization
123
123
  const [min, max] = useMemo(() => {
124
- let lo = Infinity, hi = -Infinity;
124
+ let lo = Infinity,
125
+ hi = -Infinity;
125
126
  for (let i = 0; i < channelData.length; i++) {
126
127
  if (channelData[i] < lo) lo = channelData[i];
127
128
  if (channelData[i] > hi) hi = channelData[i];
@@ -131,7 +132,12 @@ export function SpectralCubeViewer({
131
132
 
132
133
  // Build geometry: flat plane with per-pixel intensity attribute
133
134
  const geometry = useMemo(() => {
134
- const geo = new THREE.PlaneGeometry(nx / Math.max(nx, ny), ny / Math.max(nx, ny), nx - 1, ny - 1);
135
+ const geo = new THREE.PlaneGeometry(
136
+ nx / Math.max(nx, ny),
137
+ ny / Math.max(nx, ny),
138
+ nx - 1,
139
+ ny - 1
140
+ );
135
141
  const intensities = new Float32Array(nx * ny);
136
142
  for (let j = 0; j < ny; j++) {
137
143
  for (let i = 0; i < nx; i++) {
@@ -155,10 +161,13 @@ export function SpectralCubeViewer({
155
161
 
156
162
  const fragmentShader = useMemo(() => makeFrag(colormap), [colormap]);
157
163
 
158
- const uniforms = useMemo(() => ({
159
- uMin: { value: min },
160
- uMax: { value: max },
161
- }), [min, max]);
164
+ const uniforms = useMemo(
165
+ () => ({
166
+ uMin: { value: min },
167
+ uMax: { value: max },
168
+ }),
169
+ [min, max]
170
+ );
162
171
 
163
172
  // Channel label from WCS
164
173
  const channelLabel = useMemo(() => {
@@ -215,15 +224,17 @@ export function FITSViewerPanel({ fitsBuffer, onClose }: FITSViewerPanelProps) {
215
224
  useEffect(() => {
216
225
  try {
217
226
  // Dynamic import to avoid bundling FITS parser unless needed
218
- import('../fits/FITSParser').then(({ parseFITS }) => {
219
- const fits = parseFITS(fitsBuffer);
220
- setFitsData({
221
- data: fits.data,
222
- shape: fits.shape,
223
- wcs: fits.wcs,
224
- object: fits.object || 'Unknown Object',
225
- });
226
- }).catch((e) => setError(String(e)));
227
+ import('../fits/FITSParser')
228
+ .then(({ parseFITS }) => {
229
+ const fits = parseFITS(fitsBuffer);
230
+ setFitsData({
231
+ data: fits.data,
232
+ shape: fits.shape,
233
+ wcs: fits.wcs,
234
+ object: fits.object || 'Unknown Object',
235
+ });
236
+ })
237
+ .catch((e) => setError(String(e)));
227
238
  } catch (e) {
228
239
  setError(String(e));
229
240
  }
@@ -249,20 +260,23 @@ export function FITSViewerPanel({ fitsBuffer, onClose }: FITSViewerPanelProps) {
249
260
  const is3D = fitsData.shape.length >= 3 && nz > 1;
250
261
 
251
262
  return (
252
- <div style={{
253
- display: 'flex', flexDirection: 'column', gap: 8,
254
- padding: 12, background: '#1a1a2e', borderRadius: 8,
255
- border: '1px solid #2a2a3e', color: '#e4e4e7',
256
- fontFamily: "'Space Mono', monospace",
257
- }}>
263
+ <div
264
+ style={{
265
+ display: 'flex',
266
+ flexDirection: 'column',
267
+ gap: 8,
268
+ padding: 12,
269
+ background: '#1a1a2e',
270
+ borderRadius: 8,
271
+ border: '1px solid #2a2a3e',
272
+ color: '#e4e4e7',
273
+ fontFamily: "'Space Mono', monospace",
274
+ }}
275
+ >
258
276
  {/* Header */}
259
277
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
260
- <span style={{ fontSize: 13, fontWeight: 700, color: '#a78bfa' }}>
261
- {fitsData.object}
262
- </span>
263
- <span style={{ fontSize: 10, color: '#71717a' }}>
264
- {fitsData.shape.join(' × ')} px
265
- </span>
278
+ <span style={{ fontSize: 13, fontWeight: 700, color: '#a78bfa' }}>{fitsData.object}</span>
279
+ <span style={{ fontSize: 10, color: '#71717a' }}>{fitsData.shape.join(' × ')} px</span>
266
280
  </div>
267
281
 
268
282
  {/* Channel Slider (only for 3D cubes) */}
@@ -272,8 +286,12 @@ export function FITSViewerPanel({ fitsBuffer, onClose }: FITSViewerPanelProps) {
272
286
  onClick={() => setPlaying(!playing)}
273
287
  style={{
274
288
  background: playing ? '#ef4444' : '#3b82f6',
275
- border: 'none', borderRadius: 4, padding: '4px 10px',
276
- color: 'white', fontSize: 11, cursor: 'pointer',
289
+ border: 'none',
290
+ borderRadius: 4,
291
+ padding: '4px 10px',
292
+ color: 'white',
293
+ fontSize: 11,
294
+ cursor: 'pointer',
277
295
  }}
278
296
  >
279
297
  {playing ? 'Stop' : 'Play'}
@@ -283,7 +301,10 @@ export function FITSViewerPanel({ fitsBuffer, onClose }: FITSViewerPanelProps) {
283
301
  min={0}
284
302
  max={nz - 1}
285
303
  value={channel}
286
- onChange={(e) => { setChannel(Number(e.target.value)); setPlaying(false); }}
304
+ onChange={(e) => {
305
+ setChannel(Number(e.target.value));
306
+ setPlaying(false);
307
+ }}
287
308
  style={{ flex: 1 }}
288
309
  />
289
310
  <span style={{ fontSize: 10, color: '#71717a', minWidth: 80, textAlign: 'right' }}>
@@ -294,14 +315,18 @@ export function FITSViewerPanel({ fitsBuffer, onClose }: FITSViewerPanelProps) {
294
315
 
295
316
  {/* Data range */}
296
317
  <div style={{ fontSize: 10, color: '#71717a' }}>
297
- Range: {dataRange(fitsData.data).map((v) => v.toExponential(2)).join(' ')}
318
+ Range:{' '}
319
+ {dataRange(fitsData.data)
320
+ .map((v) => v.toExponential(2))
321
+ .join(' → ')}
298
322
  </div>
299
323
  </div>
300
324
  );
301
325
  }
302
326
 
303
327
  function dataRange(data: Float32Array): [number, number] {
304
- let min = Infinity, max = -Infinity;
328
+ let min = Infinity,
329
+ max = -Infinity;
305
330
  for (let i = 0; i < data.length; i++) {
306
331
  if (data[i] < min) min = data[i];
307
332
  if (data[i] > max) max = data[i];
@@ -66,8 +66,7 @@ export function parseFITS(buffer: ArrayBuffer): FITSFile {
66
66
  const headers = new Map<string, string | number | boolean>();
67
67
  let headerEnd = 0;
68
68
 
69
- outer:
70
- for (let block = 0; block * BLOCK_SIZE < buffer.byteLength; block++) {
69
+ outer: for (let block = 0; block * BLOCK_SIZE < buffer.byteLength; block++) {
71
70
  for (let card = 0; card < CARDS_PER_BLOCK; card++) {
72
71
  const offset = block * BLOCK_SIZE + card * CARD_SIZE;
73
72
  if (offset + CARD_SIZE > buffer.byteLength) break outer;
@@ -111,7 +110,9 @@ export function parseFITS(buffer: ArrayBuffer): FITSFile {
111
110
  const dataOffset = headerEnd;
112
111
 
113
112
  if (dataOffset + totalPixels * bytesPerPixel > buffer.byteLength) {
114
- throw new Error(`FITS: Data section extends beyond buffer (need ${dataOffset + totalPixels * bytesPerPixel}, have ${buffer.byteLength})`);
113
+ throw new Error(
114
+ `FITS: Data section extends beyond buffer (need ${dataOffset + totalPixels * bytesPerPixel}, have ${buffer.byteLength})`
115
+ );
115
116
  }
116
117
 
117
118
  const data = new Float32Array(totalPixels);
@@ -148,7 +149,11 @@ export function parseFITS(buffer: ArrayBuffer): FITSFile {
148
149
  let wcs: WCSInfo | null = null;
149
150
  if (headers.has('CRVAL1')) {
150
151
  wcs = {
151
- crpix: [], crval: [], cdelt: [], ctype: [], cunit: [],
152
+ crpix: [],
153
+ crval: [],
154
+ cdelt: [],
155
+ ctype: [],
156
+ cunit: [],
152
157
  };
153
158
  for (let i = 1; i <= naxis; i++) {
154
159
  wcs.crpix.push(getNumOr(headers, `CRPIX${i}`, 1));
@@ -225,11 +230,21 @@ export function buildFITS(opts: {
225
230
  for (let i = 0; i < opts.data.length; i++) {
226
231
  const off = i * bytesPerPixel;
227
232
  switch (opts.bitpix) {
228
- case 8: new Uint8Array(dataBytes)[off] = opts.data[i]; break;
229
- case 16: dataView.setInt16(off, opts.data[i], false); break;
230
- case 32: dataView.setInt32(off, opts.data[i], false); break;
231
- case -32: dataView.setFloat32(off, opts.data[i], false); break;
232
- case -64: dataView.setFloat64(off, opts.data[i], false); break;
233
+ case 8:
234
+ new Uint8Array(dataBytes)[off] = opts.data[i];
235
+ break;
236
+ case 16:
237
+ dataView.setInt16(off, opts.data[i], false);
238
+ break;
239
+ case 32:
240
+ dataView.setInt32(off, opts.data[i], false);
241
+ break;
242
+ case -32:
243
+ dataView.setFloat32(off, opts.data[i], false);
244
+ break;
245
+ case -64:
246
+ dataView.setFloat64(off, opts.data[i], false);
247
+ break;
233
248
  }
234
249
  }
235
250
 
@@ -264,12 +279,20 @@ function getNum(headers: Map<string, string | number | boolean>, key: string): n
264
279
  return v;
265
280
  }
266
281
 
267
- function getNumOr(headers: Map<string, string | number | boolean>, key: string, def: number): number {
282
+ function getNumOr(
283
+ headers: Map<string, string | number | boolean>,
284
+ key: string,
285
+ def: number
286
+ ): number {
268
287
  const v = headers.get(key);
269
288
  return typeof v === 'number' ? v : def;
270
289
  }
271
290
 
272
- function getStrOr(headers: Map<string, string | number | boolean>, key: string, def: string): string {
291
+ function getStrOr(
292
+ headers: Map<string, string | number | boolean>,
293
+ key: string,
294
+ def: string
295
+ ): string {
273
296
  const v = headers.get(key);
274
297
  return typeof v === 'string' ? v : def;
275
298
  }
@@ -85,7 +85,8 @@ export function extractChannel(fits: FITSFile, channel: number): Float32Array {
85
85
  * Useful for colormap normalization.
86
86
  */
87
87
  export function fitsDataRange(fits: FITSFile): [number, number] {
88
- let min = Infinity, max = -Infinity;
88
+ let min = Infinity,
89
+ max = -Infinity;
89
90
  for (let i = 0; i < fits.data.length; i++) {
90
91
  const v = fits.data[i];
91
92
  if (v < min) min = v;
package/src/index.ts CHANGED
@@ -17,7 +17,12 @@ export { PythonAstropyBridge, type AstropyResult };
17
17
  // Export FITS parsing and visualization
18
18
  export { parseFITS, buildFITS, type FITSFile, type WCSInfo } from './fits/FITSParser';
19
19
  export { fitsToGrid3D, extractChannel, fitsDataRange } from './fits/FITSToGrid';
20
- export { SpectralCubeViewer, FITSViewerPanel, type SpectralCubeViewerProps, type FITSViewerPanelProps } from './components/SpectralCubeViewer';
20
+ export {
21
+ SpectralCubeViewer,
22
+ FITSViewerPanel,
23
+ type SpectralCubeViewerProps,
24
+ type FITSViewerPanelProps,
25
+ } from './components/SpectralCubeViewer';
21
26
 
22
27
  /**
23
28
  * Metadata exposing domain capabilities to the Studio / Schema Mapper.
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025-2026 HoloScript Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,24 +0,0 @@
1
- /**
2
- * Interface representing the result of a radio astronomy physics calculation.
3
- */
4
- export interface AstropyResult {
5
- wavelength_meters?: number;
6
- frequency_hz?: number;
7
- flux_density_jy?: number;
8
- error?: string;
9
- }
10
- /**
11
- * Runner that bridges HoloScript to the underlying astropy Python toolkit.
12
- */
13
- export declare class PythonAstropyBridge {
14
- private scriptPath;
15
- constructor();
16
- /**
17
- * Dispatches a calculation to the Python environment.
18
- * @param command The astronomical command to invoke (e.g. 'calc_synchrotron')
19
- * @param params JSON payload of parameters
20
- * @returns A promise resolving to the astronomical result payload
21
- */
22
- executeCommand(command: string, params: Record<string, any>): Promise<AstropyResult>;
23
- }
24
- //# sourceMappingURL=python-runner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"python-runner.d.ts","sourceRoot":"","sources":["../../src/bridge/python-runner.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,UAAU,CAAS;;IAM3B;;;;;OAKG;IACU,cAAc,CACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC1B,OAAO,CAAC,aAAa,CAAC;CA6B1B"}
@@ -1,43 +0,0 @@
1
- import { spawn } from 'child_process';
2
- import * as path from 'path';
3
- /**
4
- * Runner that bridges HoloScript to the underlying astropy Python toolkit.
5
- */
6
- export class PythonAstropyBridge {
7
- constructor() {
8
- this.scriptPath = path.resolve(__dirname, '../../python/astropy_bridge.py');
9
- }
10
- /**
11
- * Dispatches a calculation to the Python environment.
12
- * @param command The astronomical command to invoke (e.g. 'calc_synchrotron')
13
- * @param params JSON payload of parameters
14
- * @returns A promise resolving to the astronomical result payload
15
- */
16
- async executeCommand(command, params) {
17
- return new Promise((resolve, reject) => {
18
- const process = spawn('python', [this.scriptPath, command, JSON.stringify(params)]);
19
- let output = '';
20
- let errorOutput = '';
21
- process.stdout.on('data', (data) => {
22
- output += data.toString();
23
- });
24
- process.stderr.on('data', (data) => {
25
- errorOutput += data.toString();
26
- });
27
- process.on('close', (code) => {
28
- if (code !== 0) {
29
- return reject(new Error(`Astropy bridge exited with code ${code}: ${errorOutput}`));
30
- }
31
- try {
32
- // Attempt to parse JSON line output from python script
33
- const parsed = JSON.parse(output.trim());
34
- resolve(parsed);
35
- }
36
- catch (_e) {
37
- reject(new Error(`Failed to parse bridge output: ${output}`));
38
- }
39
- });
40
- });
41
- }
42
- }
43
- //# sourceMappingURL=python-runner.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"python-runner.js","sourceRoot":"","sources":["../../src/bridge/python-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAY7B;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAG9B;QACE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,cAAc,CACzB,OAAe,EACf,MAA2B;QAE3B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAEpF,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,WAAW,GAAG,EAAE,CAAC;YAErB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACjC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACjC,WAAW,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC3B,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,mCAAmC,IAAI,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC;gBACtF,CAAC;gBACD,IAAI,CAAC;oBACH,uDAAuD;oBACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;oBACzC,OAAO,CAAC,MAAuB,CAAC,CAAC;gBACnC,CAAC;gBAAC,OAAO,EAAE,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -1,45 +0,0 @@
1
- /**
2
- * SpectralCubeViewer — Interactive 3D viewer for FITS spectral cubes.
3
- *
4
- * Drop a FITS file → see the spectral cube in 3D → slide through
5
- * frequency channels → play as animation. Zero code required.
6
- *
7
- * Works with:
8
- * - 3D cubes (RA × Dec × Freq): full volumetric slice-by-slice
9
- * - 2D images (RA × Dec): single-channel colormap
10
- * - 1D spectra: displayed as a line in 3D space
11
- */
12
- export type ColormapName = 'jet' | 'viridis' | 'turbo' | 'inferno' | 'coolwarm';
13
- export interface SpectralCubeViewerProps {
14
- /** Parsed FITS data array (physical values after BSCALE/BZERO) */
15
- data: Float32Array;
16
- /** Axis dimensions [NAXIS1, NAXIS2] or [NAXIS1, NAXIS2, NAXIS3] */
17
- shape: number[];
18
- /** Colormap (default: 'viridis') */
19
- colormap?: ColormapName;
20
- /** Auto-play through channels (default: false) */
21
- autoPlay?: boolean;
22
- /** Playback speed in channels per second (default: 5) */
23
- playSpeed?: number;
24
- /** WCS metadata for axis labels */
25
- wcs?: {
26
- ctype?: string[];
27
- cunit?: string[];
28
- crval?: number[];
29
- cdelt?: number[];
30
- };
31
- /** Object name from FITS header */
32
- objectName?: string;
33
- }
34
- export declare function SpectralCubeViewer({ data, shape, colormap, autoPlay, playSpeed, wcs, objectName, }: SpectralCubeViewerProps): import("react/jsx-runtime").JSX.Element;
35
- export interface FITSViewerPanelProps {
36
- /** Raw FITS ArrayBuffer (from file drop) */
37
- fitsBuffer: ArrayBuffer;
38
- onClose?: () => void;
39
- }
40
- /**
41
- * Full FITS viewer panel: parses FITS → shows 3D → channel slider.
42
- * This is the entry point for drag-and-drop FITS files.
43
- */
44
- export declare function FITSViewerPanel({ fitsBuffer, onClose }: FITSViewerPanelProps): import("react/jsx-runtime").JSX.Element;
45
- //# sourceMappingURL=SpectralCubeViewer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SpectralCubeViewer.d.ts","sourceRoot":"","sources":["../../src/components/SpectralCubeViewer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAEhF,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,GAAG,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACjF,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4DD,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,KAAK,EACL,QAAoB,EACpB,QAAgB,EAChB,SAAa,EACb,GAAG,EACH,UAAU,GACX,EAAE,uBAAuB,2CAsFzB;AAID,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,UAAU,EAAE,WAAW,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,oBAAoB,2CAiG5E"}
@@ -1,196 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * SpectralCubeViewer — Interactive 3D viewer for FITS spectral cubes.
4
- *
5
- * Drop a FITS file → see the spectral cube in 3D → slide through
6
- * frequency channels → play as animation. Zero code required.
7
- *
8
- * Works with:
9
- * - 3D cubes (RA × Dec × Freq): full volumetric slice-by-slice
10
- * - 2D images (RA × Dec): single-channel colormap
11
- * - 1D spectra: displayed as a line in 3D space
12
- */
13
- import { useState, useMemo, useRef, useEffect } from 'react';
14
- import { useFrame } from '@react-three/fiber';
15
- import * as THREE from 'three';
16
- // ── Colormap GLSL ────────────────────────────────────────────────────────────
17
- const COLORMAPS = {
18
- viridis: `
19
- vec3 colormap(float t) {
20
- vec3 c0 = vec3(0.267, 0.004, 0.329);
21
- vec3 c4 = vec3(0.127, 0.566, 0.551);
22
- vec3 c8 = vec3(0.993, 0.906, 0.144);
23
- if (t < 0.5) return mix(c0, c4, t * 2.0);
24
- return mix(c4, c8, (t - 0.5) * 2.0);
25
- }
26
- `,
27
- turbo: `
28
- vec3 colormap(float t) {
29
- float r = 0.136 + t * (4.615 + t * (-42.66 + t * (132.13 + t * (-152.55 + t * 56.31))));
30
- float g = 0.091 + t * (2.264 + t * (-14.02 + t * (32.21 + t * (-29.27 + t * 10.16))));
31
- float b = 0.107 + t * (12.75 + t * (-60.58 + t * (132.75 + t * (-134.01 + t * 50.26))));
32
- return clamp(vec3(r, g, b), 0.0, 1.0);
33
- }
34
- `,
35
- inferno: `
36
- vec3 colormap(float t) {
37
- vec3 c0 = vec3(0.001, 0.0, 0.014);
38
- vec3 c3 = vec3(0.735, 0.216, 0.329);
39
- vec3 c6 = vec3(0.988, 0.999, 0.644);
40
- if (t < 0.5) return mix(c0, c3, t * 2.0);
41
- return mix(c3, c6, (t - 0.5) * 2.0);
42
- }
43
- `,
44
- };
45
- const VERT = /* glsl */ `
46
- attribute float aIntensity;
47
- uniform float uMin;
48
- uniform float uMax;
49
- varying float vNorm;
50
-
51
- void main() {
52
- float range = uMax - uMin;
53
- vNorm = range > 0.0 ? clamp((aIntensity - uMin) / range, 0.0, 1.0) : 0.5;
54
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
55
- }
56
- `;
57
- function makeFrag(cmName) {
58
- const cm = COLORMAPS[cmName] ?? COLORMAPS.viridis;
59
- return /* glsl */ `
60
- varying float vNorm;
61
- ${cm}
62
- void main() {
63
- vec3 c = colormap(vNorm);
64
- gl_FragColor = vec4(c, 1.0);
65
- }
66
- `;
67
- }
68
- // ── Component ────────────────────────────────────────────────────────────────
69
- export function SpectralCubeViewer({ data, shape, colormap = 'viridis', autoPlay = false, playSpeed = 5, wcs, objectName, }) {
70
- const nx = shape[0] ?? 1;
71
- const ny = shape[1] ?? 1;
72
- const nz = shape[2] ?? 1;
73
- const is3D = shape.length >= 3 && nz > 1;
74
- const [channel, setChannel] = useState(0);
75
- const [playing, setPlaying] = useState(autoPlay);
76
- const meshRef = useRef(null);
77
- const timeRef = useRef(0);
78
- // Extract channel slice
79
- const channelData = useMemo(() => {
80
- if (!is3D)
81
- return data; // 2D — use entire dataset
82
- const offset = channel * nx * ny;
83
- return data.slice(offset, offset + nx * ny);
84
- }, [data, channel, nx, ny, is3D]);
85
- // Data range for colormap normalization
86
- const [min, max] = useMemo(() => {
87
- let lo = Infinity, hi = -Infinity;
88
- for (let i = 0; i < channelData.length; i++) {
89
- if (channelData[i] < lo)
90
- lo = channelData[i];
91
- if (channelData[i] > hi)
92
- hi = channelData[i];
93
- }
94
- return [lo, hi];
95
- }, [channelData]);
96
- // Build geometry: flat plane with per-pixel intensity attribute
97
- const geometry = useMemo(() => {
98
- const geo = new THREE.PlaneGeometry(nx / Math.max(nx, ny), ny / Math.max(nx, ny), nx - 1, ny - 1);
99
- const intensities = new Float32Array(nx * ny);
100
- for (let j = 0; j < ny; j++) {
101
- for (let i = 0; i < nx; i++) {
102
- // PlaneGeometry vertex order: row by row, top to bottom
103
- intensities[j * nx + i] = channelData[j * nx + i] ?? 0;
104
- }
105
- }
106
- geo.setAttribute('aIntensity', new THREE.BufferAttribute(intensities, 1));
107
- return geo;
108
- }, [nx, ny, channelData]);
109
- // Auto-play animation
110
- useFrame((_, delta) => {
111
- if (!playing || !is3D)
112
- return;
113
- timeRef.current += delta * playSpeed;
114
- if (timeRef.current >= 1) {
115
- timeRef.current -= 1;
116
- setChannel((c) => (c + 1) % nz);
117
- }
118
- });
119
- const fragmentShader = useMemo(() => makeFrag(colormap), [colormap]);
120
- const uniforms = useMemo(() => ({
121
- uMin: { value: min },
122
- uMax: { value: max },
123
- }), [min, max]);
124
- // Channel label from WCS
125
- const channelLabel = useMemo(() => {
126
- if (!wcs?.crval || !wcs?.cdelt || !wcs?.ctype)
127
- return `Channel ${channel}/${nz}`;
128
- const freqIdx = wcs.ctype.findIndex((t) => t.includes('FREQ'));
129
- if (freqIdx < 0)
130
- return `Channel ${channel}/${nz}`;
131
- const freq = wcs.crval[freqIdx] + channel * wcs.cdelt[freqIdx];
132
- const unit = wcs.cunit?.[freqIdx] ?? 'Hz';
133
- if (freq >= 1e9)
134
- return `${(freq / 1e9).toFixed(3)} GHz`;
135
- if (freq >= 1e6)
136
- return `${(freq / 1e6).toFixed(3)} MHz`;
137
- return `${freq.toFixed(0)} ${unit}`;
138
- }, [channel, nz, wcs]);
139
- return (_jsx("group", { children: _jsx("mesh", { ref: meshRef, geometry: geometry, children: _jsx("shaderMaterial", { vertexShader: VERT, fragmentShader: fragmentShader, uniforms: uniforms, side: THREE.DoubleSide }) }) }));
140
- }
141
- /**
142
- * Full FITS viewer panel: parses FITS → shows 3D → channel slider.
143
- * This is the entry point for drag-and-drop FITS files.
144
- */
145
- export function FITSViewerPanel({ fitsBuffer, onClose }) {
146
- const [fitsData, setFitsData] = useState(null);
147
- const [channel, setChannel] = useState(0);
148
- const [playing, setPlaying] = useState(false);
149
- const [error, setError] = useState(null);
150
- useEffect(() => {
151
- try {
152
- // Dynamic import to avoid bundling FITS parser unless needed
153
- import('../fits/FITSParser').then(({ parseFITS }) => {
154
- const fits = parseFITS(fitsBuffer);
155
- setFitsData({
156
- data: fits.data,
157
- shape: fits.shape,
158
- wcs: fits.wcs,
159
- object: fits.object || 'Unknown Object',
160
- });
161
- }).catch((e) => setError(String(e)));
162
- }
163
- catch (e) {
164
- setError(String(e));
165
- }
166
- }, [fitsBuffer]);
167
- if (error) {
168
- return (_jsxs("div", { style: { padding: 20, color: '#ef4444', fontFamily: 'monospace' }, children: ["FITS Parse Error: ", error] }));
169
- }
170
- if (!fitsData) {
171
- return (_jsx("div", { style: { padding: 20, color: '#94a3b8', fontFamily: 'monospace' }, children: "Loading FITS data..." }));
172
- }
173
- const nz = fitsData.shape[2] ?? 1;
174
- const is3D = fitsData.shape.length >= 3 && nz > 1;
175
- return (_jsxs("div", { style: {
176
- display: 'flex', flexDirection: 'column', gap: 8,
177
- padding: 12, background: '#1a1a2e', borderRadius: 8,
178
- border: '1px solid #2a2a3e', color: '#e4e4e7',
179
- fontFamily: "'Space Mono', monospace",
180
- }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("span", { style: { fontSize: 13, fontWeight: 700, color: '#a78bfa' }, children: fitsData.object }), _jsxs("span", { style: { fontSize: 10, color: '#71717a' }, children: [fitsData.shape.join(' × '), " px"] })] }), is3D && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("button", { onClick: () => setPlaying(!playing), style: {
181
- background: playing ? '#ef4444' : '#3b82f6',
182
- border: 'none', borderRadius: 4, padding: '4px 10px',
183
- color: 'white', fontSize: 11, cursor: 'pointer',
184
- }, children: playing ? 'Stop' : 'Play' }), _jsx("input", { type: "range", min: 0, max: nz - 1, value: channel, onChange: (e) => { setChannel(Number(e.target.value)); setPlaying(false); }, style: { flex: 1 } }), _jsxs("span", { style: { fontSize: 10, color: '#71717a', minWidth: 80, textAlign: 'right' }, children: ["Ch ", channel, "/", nz - 1] })] })), _jsxs("div", { style: { fontSize: 10, color: '#71717a' }, children: ["Range: ", dataRange(fitsData.data).map((v) => v.toExponential(2)).join(' → ')] })] }));
185
- }
186
- function dataRange(data) {
187
- let min = Infinity, max = -Infinity;
188
- for (let i = 0; i < data.length; i++) {
189
- if (data[i] < min)
190
- min = data[i];
191
- if (data[i] > max)
192
- max = data[i];
193
- }
194
- return [min, max];
195
- }
196
- //# sourceMappingURL=SpectralCubeViewer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SpectralCubeViewer.js","sourceRoot":"","sources":["../../src/components/SpectralCubeViewer.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +0,0 @@
1
- /**
2
- * Specific traits representing properties for Radio Astrophysics.
3
- */
4
- export declare const RADIO_ASTRONOMY_TRAITS: readonly ["radio_emitter", "synchrotron", "interferometer", "em_wave", "pulsar_timing", "spectral_line"];
5
- export type RadioAstronomyTraitName = (typeof RADIO_ASTRONOMY_TRAITS)[number];
6
- //# sourceMappingURL=astronomy-traits.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"astronomy-traits.d.ts","sourceRoot":"","sources":["../../src/constants/astronomy-traits.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,sBAAsB,0GAOzB,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Specific traits representing properties for Radio Astrophysics.
3
- */
4
- export const RADIO_ASTRONOMY_TRAITS = [
5
- 'radio_emitter', // Signals a 3D construct as an origin point for specific radio wavelengths
6
- 'synchrotron', // Determines emission behavior via magnetic fields and relativistic electrons
7
- 'interferometer', // Marks a multi-nodal virtual sensor
8
- 'em_wave', // Represents an electromagnetic wave primitive
9
- 'pulsar_timing', // Represents pulsar timing array signals
10
- 'spectral_line', // Maps to particular spectral line signals (e.g., 21cm HI line)
11
- ];
12
- //# sourceMappingURL=astronomy-traits.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"astronomy-traits.js","sourceRoot":"","sources":["../../src/constants/astronomy-traits.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,eAAe,EAAE,2EAA2E;IAC5F,aAAa,EAAE,8EAA8E;IAC7F,gBAAgB,EAAE,qCAAqC;IACvD,SAAS,EAAE,+CAA+C;IAC1D,eAAe,EAAE,yCAAyC;IAC1D,eAAe,EAAE,gEAAgE;CACzE,CAAC"}
@@ -1,60 +0,0 @@
1
- /**
2
- * FITSParser — Pure JavaScript parser for FITS (Flexible Image Transport System) files.
3
- *
4
- * FITS is the standard data format in astronomy. Structure:
5
- * - Header: 2880-byte blocks of 80-character ASCII "cards" (key=value pairs)
6
- * - Data: big-endian binary arrays, padded to 2880-byte boundary
7
- * - Extensions: additional HDUs (Header Data Units) with same structure
8
- *
9
- * Supports: BITPIX 8 (uint8), 16 (int16), 32 (int32), -32 (float32), -64 (float64)
10
- * Handles: BSCALE/BZERO physical value scaling, WCS coordinate metadata
11
- *
12
- * @see https://fits.gsfc.nasa.gov/fits_standard.html
13
- */
14
- export interface WCSInfo {
15
- /** Reference pixel (1-indexed, per FITS convention) */
16
- crpix: number[];
17
- /** Reference value (world coordinate at reference pixel) */
18
- crval: number[];
19
- /** Pixel scale (coordinate increment per pixel) */
20
- cdelt: number[];
21
- /** Axis types (e.g., 'RA---TAN', 'DEC--TAN', 'FREQ') */
22
- ctype: string[];
23
- /** Axis units */
24
- cunit: string[];
25
- }
26
- export interface FITSFile {
27
- /** All header cards as key→value */
28
- headers: Map<string, string | number | boolean>;
29
- /** Data array (physical values after BSCALE/BZERO) */
30
- data: Float32Array;
31
- /** Axis dimensions [NAXIS1, NAXIS2, ...] */
32
- shape: number[];
33
- /** World Coordinate System info (if present) */
34
- wcs: WCSInfo | null;
35
- /** BITPIX from header */
36
- bitpix: number;
37
- /** Object name (OBJECT card) */
38
- object: string;
39
- /** Telescope name (TELESCOP card) */
40
- telescope: string;
41
- /** Observation date (DATE-OBS card) */
42
- dateObs: string;
43
- }
44
- /**
45
- * Parse a FITS file from an ArrayBuffer.
46
- * Returns the primary HDU (first header + data unit).
47
- */
48
- export declare function parseFITS(buffer: ArrayBuffer): FITSFile;
49
- /**
50
- * Build a minimal FITS file as ArrayBuffer (for testing).
51
- */
52
- export declare function buildFITS(opts: {
53
- bitpix: number;
54
- shape: number[];
55
- data: number[];
56
- bscale?: number;
57
- bzero?: number;
58
- headers?: Record<string, string | number>;
59
- }): ArrayBuffer;
60
- //# sourceMappingURL=FITSParser.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FITSParser.d.ts","sourceRoot":"","sources":["../../src/fits/FITSParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,OAAO;IACtB,uDAAuD;IACvD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mDAAmD;IACnD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wDAAwD;IACxD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,iBAAiB;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAChD,sDAAsD;IACtD,IAAI,EAAE,YAAY,CAAC;IACnB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACpB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,CA+GvD;AAID;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC3C,GAAG,WAAW,CAuDd"}
@@ -1,230 +0,0 @@
1
- /**
2
- * FITSParser — Pure JavaScript parser for FITS (Flexible Image Transport System) files.
3
- *
4
- * FITS is the standard data format in astronomy. Structure:
5
- * - Header: 2880-byte blocks of 80-character ASCII "cards" (key=value pairs)
6
- * - Data: big-endian binary arrays, padded to 2880-byte boundary
7
- * - Extensions: additional HDUs (Header Data Units) with same structure
8
- *
9
- * Supports: BITPIX 8 (uint8), 16 (int16), 32 (int32), -32 (float32), -64 (float64)
10
- * Handles: BSCALE/BZERO physical value scaling, WCS coordinate metadata
11
- *
12
- * @see https://fits.gsfc.nasa.gov/fits_standard.html
13
- */
14
- // ── Constants ────────────────────────────────────────────────────────────────
15
- const BLOCK_SIZE = 2880;
16
- const CARD_SIZE = 80;
17
- const CARDS_PER_BLOCK = BLOCK_SIZE / CARD_SIZE; // 36
18
- // ── Parser ───────────────────────────────────────────────────────────────────
19
- /**
20
- * Parse a FITS file from an ArrayBuffer.
21
- * Returns the primary HDU (first header + data unit).
22
- */
23
- export function parseFITS(buffer) {
24
- const bytes = new Uint8Array(buffer);
25
- const view = new DataView(buffer);
26
- // ── Parse Header ─────────────────────────────────────────────────
27
- const headers = new Map();
28
- let headerEnd = 0;
29
- outer: for (let block = 0; block * BLOCK_SIZE < buffer.byteLength; block++) {
30
- for (let card = 0; card < CARDS_PER_BLOCK; card++) {
31
- const offset = block * BLOCK_SIZE + card * CARD_SIZE;
32
- if (offset + CARD_SIZE > buffer.byteLength)
33
- break outer;
34
- const cardStr = decodeASCII(bytes, offset, CARD_SIZE);
35
- const keyword = cardStr.substring(0, 8).trim();
36
- if (keyword === 'END') {
37
- headerEnd = (block + 1) * BLOCK_SIZE; // data starts at next block boundary
38
- break outer;
39
- }
40
- if (cardStr[8] === '=' && cardStr[9] === ' ') {
41
- const valueStr = cardStr.substring(10).split('/')[0].trim();
42
- headers.set(keyword, parseCardValue(valueStr));
43
- }
44
- else if (keyword === 'COMMENT' || keyword === 'HISTORY') {
45
- // Skip comment/history cards
46
- }
47
- }
48
- }
49
- if (headerEnd === 0) {
50
- throw new Error('FITS: No END card found in header');
51
- }
52
- // ── Extract Critical Keywords ────────────────────────────────────
53
- const bitpix = getNum(headers, 'BITPIX');
54
- const naxis = getNum(headers, 'NAXIS');
55
- const shape = [];
56
- for (let i = 1; i <= naxis; i++) {
57
- shape.push(getNum(headers, `NAXIS${i}`));
58
- }
59
- const bscale = getNumOr(headers, 'BSCALE', 1.0);
60
- const bzero = getNumOr(headers, 'BZERO', 0.0);
61
- // ── Parse Data ───────────────────────────────────────────────────
62
- const totalPixels = shape.reduce((a, b) => a * b, 1);
63
- const bytesPerPixel = Math.abs(bitpix) / 8;
64
- const dataOffset = headerEnd;
65
- if (dataOffset + totalPixels * bytesPerPixel > buffer.byteLength) {
66
- throw new Error(`FITS: Data section extends beyond buffer (need ${dataOffset + totalPixels * bytesPerPixel}, have ${buffer.byteLength})`);
67
- }
68
- const data = new Float32Array(totalPixels);
69
- for (let i = 0; i < totalPixels; i++) {
70
- const off = dataOffset + i * bytesPerPixel;
71
- let raw;
72
- switch (bitpix) {
73
- case 8:
74
- raw = bytes[off];
75
- break;
76
- case 16:
77
- raw = view.getInt16(off, false); // big-endian
78
- break;
79
- case 32:
80
- raw = view.getInt32(off, false);
81
- break;
82
- case -32:
83
- raw = view.getFloat32(off, false);
84
- break;
85
- case -64:
86
- raw = view.getFloat64(off, false);
87
- break;
88
- default:
89
- throw new Error(`FITS: Unsupported BITPIX ${bitpix}`);
90
- }
91
- // Apply physical value transformation
92
- data[i] = bscale * raw + bzero;
93
- }
94
- // ── Extract WCS ──────────────────────────────────────────────────
95
- let wcs = null;
96
- if (headers.has('CRVAL1')) {
97
- wcs = {
98
- crpix: [], crval: [], cdelt: [], ctype: [], cunit: [],
99
- };
100
- for (let i = 1; i <= naxis; i++) {
101
- wcs.crpix.push(getNumOr(headers, `CRPIX${i}`, 1));
102
- wcs.crval.push(getNumOr(headers, `CRVAL${i}`, 0));
103
- wcs.cdelt.push(getNumOr(headers, `CDELT${i}`, 1));
104
- wcs.ctype.push(getStrOr(headers, `CTYPE${i}`, ''));
105
- wcs.cunit.push(getStrOr(headers, `CUNIT${i}`, ''));
106
- }
107
- }
108
- return {
109
- headers,
110
- data,
111
- shape,
112
- wcs,
113
- bitpix,
114
- object: getStrOr(headers, 'OBJECT', ''),
115
- telescope: getStrOr(headers, 'TELESCOP', ''),
116
- dateObs: getStrOr(headers, 'DATE-OBS', ''),
117
- };
118
- }
119
- // ── FITS Builder (for tests) ─────────────────────────────────────────────────
120
- /**
121
- * Build a minimal FITS file as ArrayBuffer (for testing).
122
- */
123
- export function buildFITS(opts) {
124
- const cards = [];
125
- cards.push(fmtCard('SIMPLE', true));
126
- cards.push(fmtCard('BITPIX', opts.bitpix));
127
- cards.push(fmtCard('NAXIS', opts.shape.length));
128
- for (let i = 0; i < opts.shape.length; i++) {
129
- cards.push(fmtCard(`NAXIS${i + 1}`, opts.shape[i]));
130
- }
131
- if (opts.bscale !== undefined)
132
- cards.push(fmtCard('BSCALE', opts.bscale));
133
- if (opts.bzero !== undefined)
134
- cards.push(fmtCard('BZERO', opts.bzero));
135
- if (opts.headers) {
136
- for (const [key, val] of Object.entries(opts.headers)) {
137
- cards.push(fmtCard(key, val));
138
- }
139
- }
140
- cards.push('END'.padEnd(CARD_SIZE));
141
- // Pad header to block boundary
142
- while (cards.length % CARDS_PER_BLOCK !== 0) {
143
- cards.push(' '.repeat(CARD_SIZE));
144
- }
145
- const headerBytes = new Uint8Array(cards.length * CARD_SIZE);
146
- for (let i = 0; i < cards.length; i++) {
147
- for (let j = 0; j < CARD_SIZE; j++) {
148
- headerBytes[i * CARD_SIZE + j] = cards[i].charCodeAt(j);
149
- }
150
- }
151
- // Write data
152
- const bytesPerPixel = Math.abs(opts.bitpix) / 8;
153
- const dataSize = opts.data.length * bytesPerPixel;
154
- const paddedDataSize = Math.ceil(dataSize / BLOCK_SIZE) * BLOCK_SIZE;
155
- const dataBytes = new ArrayBuffer(paddedDataSize);
156
- const dataView = new DataView(dataBytes);
157
- for (let i = 0; i < opts.data.length; i++) {
158
- const off = i * bytesPerPixel;
159
- switch (opts.bitpix) {
160
- case 8:
161
- new Uint8Array(dataBytes)[off] = opts.data[i];
162
- break;
163
- case 16:
164
- dataView.setInt16(off, opts.data[i], false);
165
- break;
166
- case 32:
167
- dataView.setInt32(off, opts.data[i], false);
168
- break;
169
- case -32:
170
- dataView.setFloat32(off, opts.data[i], false);
171
- break;
172
- case -64:
173
- dataView.setFloat64(off, opts.data[i], false);
174
- break;
175
- }
176
- }
177
- // Combine
178
- const result = new Uint8Array(headerBytes.length + paddedDataSize);
179
- result.set(headerBytes);
180
- result.set(new Uint8Array(dataBytes), headerBytes.length);
181
- return result.buffer;
182
- }
183
- // ── Helpers ──────────────────────────────────────────────────────────────────
184
- function decodeASCII(bytes, offset, length) {
185
- let s = '';
186
- for (let i = 0; i < length; i++) {
187
- s += String.fromCharCode(bytes[offset + i]);
188
- }
189
- return s;
190
- }
191
- function parseCardValue(s) {
192
- if (s === 'T')
193
- return true;
194
- if (s === 'F')
195
- return false;
196
- if (s.startsWith("'"))
197
- return s.replace(/^'|'$/g, '').trim();
198
- const n = Number(s);
199
- return Number.isNaN(n) ? s : n;
200
- }
201
- function getNum(headers, key) {
202
- const v = headers.get(key);
203
- if (typeof v !== 'number')
204
- throw new Error(`FITS: Missing or non-numeric header ${key}`);
205
- return v;
206
- }
207
- function getNumOr(headers, key, def) {
208
- const v = headers.get(key);
209
- return typeof v === 'number' ? v : def;
210
- }
211
- function getStrOr(headers, key, def) {
212
- const v = headers.get(key);
213
- return typeof v === 'string' ? v : def;
214
- }
215
- function fmtCard(keyword, value) {
216
- const kw = keyword.padEnd(8);
217
- let valStr;
218
- if (typeof value === 'boolean') {
219
- valStr = value ? 'T' : 'F';
220
- valStr = valStr.padStart(20);
221
- }
222
- else if (typeof value === 'number') {
223
- valStr = String(value).padStart(20);
224
- }
225
- else {
226
- valStr = `'${value}'`.padEnd(20);
227
- }
228
- return `${kw}= ${valStr}`.padEnd(CARD_SIZE);
229
- }
230
- //# sourceMappingURL=FITSParser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FITSParser.js","sourceRoot":"","sources":["../../src/fits/FITSParser.ts"],"names":[],"mappings":"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"}
@@ -1,27 +0,0 @@
1
- /**
2
- * FITSToGrid — Convert parsed FITS data to HoloScript grid structures.
3
- *
4
- * Maps FITS spectral cubes (RA × Dec × Freq) and 2D images to
5
- * RegularGrid3D for visualization via ScalarFieldOverlay or SimResultsMesh.
6
- */
7
- import { RegularGrid3D } from '@holoscript/engine/simulation';
8
- import type { FITSFile } from './FITSParser';
9
- /**
10
- * Convert a parsed FITS file to a RegularGrid3D.
11
- *
12
- * - 3D cubes (NAXIS=3): maps directly to grid
13
- * - 2D images (NAXIS=2): creates a 1-deep grid (nx × ny × 1)
14
- * - 1D spectra (NAXIS=1): creates a 1×1×n grid
15
- */
16
- export declare function fitsToGrid3D(fits: FITSFile): RegularGrid3D;
17
- /**
18
- * Extract a single frequency channel (z-slice) from a 3D FITS cube.
19
- * Returns a 2D Float32Array (nx × ny) for use as a ScalarFieldOverlay.
20
- */
21
- export declare function extractChannel(fits: FITSFile, channel: number): Float32Array;
22
- /**
23
- * Get data range (min/max) for the FITS data.
24
- * Useful for colormap normalization.
25
- */
26
- export declare function fitsDataRange(fits: FITSFile): [number, number];
27
- //# sourceMappingURL=FITSToGrid.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FITSToGrid.d.ts","sourceRoot":"","sources":["../../src/fits/FITSToGrid.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,aAAa,CAmC1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAsB5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ9D"}
@@ -1,85 +0,0 @@
1
- /**
2
- * FITSToGrid — Convert parsed FITS data to HoloScript grid structures.
3
- *
4
- * Maps FITS spectral cubes (RA × Dec × Freq) and 2D images to
5
- * RegularGrid3D for visualization via ScalarFieldOverlay or SimResultsMesh.
6
- */
7
- import { RegularGrid3D } from '@holoscript/engine/simulation';
8
- /**
9
- * Convert a parsed FITS file to a RegularGrid3D.
10
- *
11
- * - 3D cubes (NAXIS=3): maps directly to grid
12
- * - 2D images (NAXIS=2): creates a 1-deep grid (nx × ny × 1)
13
- * - 1D spectra (NAXIS=1): creates a 1×1×n grid
14
- */
15
- export function fitsToGrid3D(fits) {
16
- const shape = fits.shape;
17
- let nx, ny, nz;
18
- if (shape.length >= 3) {
19
- nx = shape[0];
20
- ny = shape[1];
21
- nz = shape[2];
22
- }
23
- else if (shape.length === 2) {
24
- nx = shape[0];
25
- ny = shape[1];
26
- nz = 1;
27
- }
28
- else if (shape.length === 1) {
29
- nx = shape[0];
30
- ny = 1;
31
- nz = 1;
32
- }
33
- else {
34
- throw new Error(`FITS: Cannot convert ${shape.length}D data to grid`);
35
- }
36
- const grid = new RegularGrid3D([nx, ny, nz], [nx, ny, nz]);
37
- // FITS stores data in FORTRAN order (column-major: NAXIS1 varies fastest)
38
- // RegularGrid3D uses row-major (x varies fastest in our convention)
39
- // Since NAXIS1 = x and it varies fastest in both, direct copy works
40
- const data = fits.data;
41
- const gridData = grid.data;
42
- const len = Math.min(data.length, gridData.length);
43
- for (let i = 0; i < len; i++) {
44
- gridData[i] = data[i];
45
- }
46
- return grid;
47
- }
48
- /**
49
- * Extract a single frequency channel (z-slice) from a 3D FITS cube.
50
- * Returns a 2D Float32Array (nx × ny) for use as a ScalarFieldOverlay.
51
- */
52
- export function extractChannel(fits, channel) {
53
- if (fits.shape.length < 3) {
54
- // 2D image — return the whole thing
55
- return new Float32Array(fits.data);
56
- }
57
- const nx = fits.shape[0];
58
- const ny = fits.shape[1];
59
- const nz = fits.shape[2];
60
- if (channel < 0 || channel >= nz) {
61
- throw new Error(`Channel ${channel} out of range [0, ${nz - 1}]`);
62
- }
63
- const slice = new Float32Array(nx * ny);
64
- const offset = channel * nx * ny;
65
- for (let i = 0; i < nx * ny; i++) {
66
- slice[i] = fits.data[offset + i];
67
- }
68
- return slice;
69
- }
70
- /**
71
- * Get data range (min/max) for the FITS data.
72
- * Useful for colormap normalization.
73
- */
74
- export function fitsDataRange(fits) {
75
- let min = Infinity, max = -Infinity;
76
- for (let i = 0; i < fits.data.length; i++) {
77
- const v = fits.data[i];
78
- if (v < min)
79
- min = v;
80
- if (v > max)
81
- max = v;
82
- }
83
- return [min, max];
84
- }
85
- //# sourceMappingURL=FITSToGrid.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FITSToGrid.js","sourceRoot":"","sources":["../../src/fits/FITSToGrid.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts DELETED
@@ -1,25 +0,0 @@
1
- import { RADIO_ASTRONOMY_TRAITS, RadioAstronomyTraitName } from './constants/astronomy-traits';
2
- import { PythonAstropyBridge, AstropyResult } from './bridge/python-runner';
3
- /**
4
- * @holoscript/radio-astronomy-plugin
5
- *
6
- * Domain plugin bridging Radio Astrophysics simulation concepts into the HoloScript Universal pipeline.
7
- * Extends standard traits without bloating core. Provides an astropy python bridge for logic evaluation.
8
- */
9
- export { RADIO_ASTRONOMY_TRAITS, type RadioAstronomyTraitName };
10
- export { PythonAstropyBridge, type AstropyResult };
11
- export { parseFITS, buildFITS, type FITSFile, type WCSInfo } from './fits/FITSParser';
12
- export { fitsToGrid3D, extractChannel, fitsDataRange } from './fits/FITSToGrid';
13
- export { SpectralCubeViewer, FITSViewerPanel, type SpectralCubeViewerProps, type FITSViewerPanelProps } from './components/SpectralCubeViewer';
14
- /**
15
- * Metadata exposing domain capabilities to the Studio / Schema Mapper.
16
- */
17
- export declare const DOMAIN_MANIFEST: {
18
- id: string;
19
- name: string;
20
- version: string;
21
- description: string;
22
- keywords: string[];
23
- traits: readonly ["radio_emitter", "synchrotron", "interferometer", "em_wave", "pulsar_timing", "spectral_line"];
24
- };
25
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5E;;;;;GAKG;AAGH,OAAO,EAAE,sBAAsB,EAAE,KAAK,uBAAuB,EAAE,CAAC;AAGhE,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,CAAC;AAGnD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,uBAAuB,EAAE,KAAK,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAE/I;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;CAO3B,CAAC"}
package/dist/index.js DELETED
@@ -1,28 +0,0 @@
1
- import { RADIO_ASTRONOMY_TRAITS } from './constants/astronomy-traits';
2
- import { PythonAstropyBridge } from './bridge/python-runner';
3
- /**
4
- * @holoscript/radio-astronomy-plugin
5
- *
6
- * Domain plugin bridging Radio Astrophysics simulation concepts into the HoloScript Universal pipeline.
7
- * Extends standard traits without bloating core. Provides an astropy python bridge for logic evaluation.
8
- */
9
- // Export vocabulary
10
- export { RADIO_ASTRONOMY_TRAITS };
11
- // Export Bridges
12
- export { PythonAstropyBridge };
13
- // Export FITS parsing and visualization
14
- export { parseFITS, buildFITS } from './fits/FITSParser';
15
- export { fitsToGrid3D, extractChannel, fitsDataRange } from './fits/FITSToGrid';
16
- export { SpectralCubeViewer, FITSViewerPanel } from './components/SpectralCubeViewer';
17
- /**
18
- * Metadata exposing domain capabilities to the Studio / Schema Mapper.
19
- */
20
- export const DOMAIN_MANIFEST = {
21
- id: 'domain.science.astronomy.radio',
22
- name: 'Radio Astronomy Plugin',
23
- version: '1.0.0',
24
- description: 'Extends HoloScript spatial environments with radio astrophysics primitives.',
25
- keywords: ['interferometer', 'radio emitting', 'synchrotron radiation', 'pulsar'],
26
- traits: RADIO_ASTRONOMY_TRAITS,
27
- };
28
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAA2B,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAiB,MAAM,wBAAwB,CAAC;AAE5E;;;;;GAKG;AAEH,oBAAoB;AACpB,OAAO,EAAE,sBAAsB,EAAgC,CAAC;AAEhE,iBAAiB;AACjB,OAAO,EAAE,mBAAmB,EAAsB,CAAC;AAEnD,wCAAwC;AACxC,OAAO,EAAE,SAAS,EAAE,SAAS,EAA+B,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAA2D,MAAM,iCAAiC,CAAC;AAE/I;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,EAAE,EAAE,gCAAgC;IACpC,IAAI,EAAE,wBAAwB;IAC9B,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,6EAA6E;IAC1F,QAAQ,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,QAAQ,CAAC;IACjF,MAAM,EAAE,sBAAsB;CAC/B,CAAC"}