@fideus-labs/fidnii 0.1.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/LICENSE.txt +9 -0
- package/README.md +180 -0
- package/dist/BufferManager.d.ts +86 -0
- package/dist/BufferManager.d.ts.map +1 -0
- package/dist/BufferManager.js +146 -0
- package/dist/BufferManager.js.map +1 -0
- package/dist/ClipPlanes.d.ts +180 -0
- package/dist/ClipPlanes.d.ts.map +1 -0
- package/dist/ClipPlanes.js +513 -0
- package/dist/ClipPlanes.js.map +1 -0
- package/dist/OMEZarrNVImage.d.ts +545 -0
- package/dist/OMEZarrNVImage.d.ts.map +1 -0
- package/dist/OMEZarrNVImage.js +1799 -0
- package/dist/OMEZarrNVImage.js.map +1 -0
- package/dist/RegionCoalescer.d.ts +75 -0
- package/dist/RegionCoalescer.d.ts.map +1 -0
- package/dist/RegionCoalescer.js +151 -0
- package/dist/RegionCoalescer.js.map +1 -0
- package/dist/ResolutionSelector.d.ts +88 -0
- package/dist/ResolutionSelector.d.ts.map +1 -0
- package/dist/ResolutionSelector.js +224 -0
- package/dist/ResolutionSelector.js.map +1 -0
- package/dist/ViewportBounds.d.ts +50 -0
- package/dist/ViewportBounds.d.ts.map +1 -0
- package/dist/ViewportBounds.js +325 -0
- package/dist/ViewportBounds.js.map +1 -0
- package/dist/events.d.ts +122 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +12 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +273 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +126 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/affine.d.ts +72 -0
- package/dist/utils/affine.d.ts.map +1 -0
- package/dist/utils/affine.js +173 -0
- package/dist/utils/affine.js.map +1 -0
- package/dist/utils/coordinates.d.ts +80 -0
- package/dist/utils/coordinates.d.ts.map +1 -0
- package/dist/utils/coordinates.js +207 -0
- package/dist/utils/coordinates.js.map +1 -0
- package/package.json +61 -0
- package/src/BufferManager.ts +176 -0
- package/src/ClipPlanes.ts +640 -0
- package/src/OMEZarrNVImage.ts +2286 -0
- package/src/RegionCoalescer.ts +217 -0
- package/src/ResolutionSelector.ts +325 -0
- package/src/ViewportBounds.ts +369 -0
- package/src/events.ts +146 -0
- package/src/index.ts +153 -0
- package/src/types.ts +429 -0
- package/src/utils/affine.ts +218 -0
- package/src/utils/coordinates.ts +271 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Fideus Labs LLC <info@fideus.io>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# @fideus-labs/fidnii
|
|
2
|
+
|
|
3
|
+
Render OME-Zarr images in NiiVue with progressive multi-resolution loading.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Progressive loading** - Quick preview from lowest resolution, then target
|
|
8
|
+
resolution
|
|
9
|
+
- **Automatic resolution selection** - Picks optimal resolution based on pixel
|
|
10
|
+
budget
|
|
11
|
+
- **Clip planes** - Up to 6 arbitrary clip planes for cropping/visualization
|
|
12
|
+
- **Dynamic buffer sizing** - Matches fetched data exactly (no upsampling)
|
|
13
|
+
- **Chunk caching** - LRU decoded-chunk cache avoids redundant decompression
|
|
14
|
+
- **Request coalescing** - Efficient chunk fetching
|
|
15
|
+
- **Event system** - Browser-native EventTarget API for loading states
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @fideus-labs/fidnii @fideus-labs/ngff-zarr @niivue/niivue
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Niivue } from "@niivue/niivue";
|
|
27
|
+
import { fromNgffZarr } from "@fideus-labs/ngff-zarr";
|
|
28
|
+
import { OMEZarrNVImage } from "@fideus-labs/fidnii";
|
|
29
|
+
|
|
30
|
+
const nv = new Niivue();
|
|
31
|
+
await nv.attachToCanvas(document.getElementById("canvas"));
|
|
32
|
+
nv.setSliceType(nv.sliceTypeRender);
|
|
33
|
+
|
|
34
|
+
const multiscales = await fromNgffZarr("/path/to/data.ome.zarr");
|
|
35
|
+
|
|
36
|
+
// Image is automatically added to NiiVue and loads progressively
|
|
37
|
+
await OMEZarrNVImage.create({ multiscales, niivue: nv });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Options
|
|
41
|
+
|
|
42
|
+
| Option | Type | Default | Description |
|
|
43
|
+
| --------------------- | ------------- | ------------ | ----------------------------------------------- |
|
|
44
|
+
| `multiscales` | `Multiscales` | required | OME-Zarr multiscales data from `fromNgffZarr()` |
|
|
45
|
+
| `niivue` | `Niivue` | required | NiiVue instance |
|
|
46
|
+
| `maxPixels` | `number` | `50_000_000` | Maximum pixels to load (controls resolution) |
|
|
47
|
+
| `autoLoad` | `boolean` | `true` | Auto-add to NiiVue and start loading |
|
|
48
|
+
| `clipPlaneDebounceMs` | `number` | `300` | Debounce delay for clip plane updates |
|
|
49
|
+
| `maxCacheEntries` | `number` | `200` | Max decoded-chunk cache entries (0 to disable) |
|
|
50
|
+
| `cache` | `ChunkCache` | — | Pre-built cache (overrides `maxCacheEntries`) |
|
|
51
|
+
|
|
52
|
+
## Events
|
|
53
|
+
|
|
54
|
+
Listen to loading events using the browser-native EventTarget API:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const image = await OMEZarrNVImage.create({ multiscales, niivue: nv });
|
|
58
|
+
|
|
59
|
+
image.addEventListener("loadingStart", (e) => {
|
|
60
|
+
console.log(`Loading level ${e.detail.levelIndex}...`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
image.addEventListener("populateComplete", (e) => {
|
|
64
|
+
console.log(`Loaded level ${e.detail.currentLevel}`);
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Available Events
|
|
69
|
+
|
|
70
|
+
| Event | Description |
|
|
71
|
+
| ------------------ | --------------------------------------------------- |
|
|
72
|
+
| `loadingStart` | Fired when loading starts for a resolution level |
|
|
73
|
+
| `loadingComplete` | Fired when loading completes for a resolution level |
|
|
74
|
+
| `resolutionChange` | Fired when resolution level changes |
|
|
75
|
+
| `populateComplete` | Fired when all loading is done |
|
|
76
|
+
| `clipPlanesChange` | Fired when clip planes are updated (after debounce) |
|
|
77
|
+
|
|
78
|
+
## Advanced Usage
|
|
79
|
+
|
|
80
|
+
For manual control over when loading starts, use `autoLoad: false`:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const image = await OMEZarrNVImage.create({
|
|
84
|
+
multiscales,
|
|
85
|
+
niivue: nv,
|
|
86
|
+
autoLoad: false,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Set up event listeners before loading starts
|
|
90
|
+
image.addEventListener("populateComplete", () => {
|
|
91
|
+
console.log("Loading complete!");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Manually add to NiiVue and start loading
|
|
95
|
+
nv.addVolume(image);
|
|
96
|
+
await image.populateVolume();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Clip Planes
|
|
100
|
+
|
|
101
|
+
Clip planes define visible regions of the volume. Up to 6 clip planes can be
|
|
102
|
+
active.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { createAxisAlignedClipPlane } from "@fideus-labs/fidnii";
|
|
106
|
+
|
|
107
|
+
const image = await OMEZarrNVImage.create({ multiscales, niivue: nv });
|
|
108
|
+
|
|
109
|
+
// Wait for initial load
|
|
110
|
+
image.addEventListener("populateComplete", () => {
|
|
111
|
+
const bounds = image.getVolumeBounds();
|
|
112
|
+
|
|
113
|
+
// Clip at X = midpoint, keeping +X side visible
|
|
114
|
+
const midX = (bounds.min[0] + bounds.max[0]) / 2;
|
|
115
|
+
const clipPlane = createAxisAlignedClipPlane("x", midX, "positive", bounds);
|
|
116
|
+
|
|
117
|
+
image.setClipPlanes([clipPlane]);
|
|
118
|
+
}, { once: true });
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Chunk Caching
|
|
122
|
+
|
|
123
|
+
Fidnii ships with an LRU decoded-chunk cache that avoids redundant
|
|
124
|
+
decompression when the same Zarr chunk is read more than once. This happens
|
|
125
|
+
frequently with overlapping clip-plane selections, repeated `populateVolume`
|
|
126
|
+
calls, and progressive loading where 2D slabs and 3D volumes share chunks.
|
|
127
|
+
|
|
128
|
+
Caching is **enabled by default** with a 200-entry limit. No extra code is
|
|
129
|
+
needed:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Default: 200-entry LRU cache created automatically
|
|
133
|
+
const image = await OMEZarrNVImage.create({ multiscales, niivue: nv });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Custom cache size
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const image = await OMEZarrNVImage.create({
|
|
140
|
+
multiscales,
|
|
141
|
+
niivue: nv,
|
|
142
|
+
maxCacheEntries: 500,
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Disabling caching
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const image = await OMEZarrNVImage.create({
|
|
150
|
+
multiscales,
|
|
151
|
+
niivue: nv,
|
|
152
|
+
maxCacheEntries: 0,
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Bring-your-own cache
|
|
157
|
+
|
|
158
|
+
Any object that satisfies the `ChunkCache` interface (`get` / `set` with
|
|
159
|
+
`string` keys and `ArrayBuffer` values) can be passed directly:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import type { ChunkCache } from "@fideus-labs/fidnii";
|
|
163
|
+
|
|
164
|
+
const myCache: ChunkCache = {
|
|
165
|
+
get(key: string) { /* ... */ },
|
|
166
|
+
set(key: string, value: ArrayBuffer) { /* ... */ },
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const image = await OMEZarrNVImage.create({
|
|
170
|
+
multiscales,
|
|
171
|
+
niivue: nv,
|
|
172
|
+
cache: myCache,
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
When `cache` is provided it takes precedence over `maxCacheEntries`.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { TypedArray, ZarrDtype } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Manages a dynamically-sized pixel buffer for volume data.
|
|
4
|
+
*
|
|
5
|
+
* The buffer is resized to match the fetched data dimensions exactly.
|
|
6
|
+
* Memory is reused when possible to avoid unnecessary allocations.
|
|
7
|
+
*
|
|
8
|
+
* Memory reuse strategy:
|
|
9
|
+
* - Reuse buffer if newSize <= currentCapacity
|
|
10
|
+
* - Reallocate if newSize > currentCapacity OR newSize < 25% of currentCapacity
|
|
11
|
+
*/
|
|
12
|
+
export declare class BufferManager {
|
|
13
|
+
private buffer;
|
|
14
|
+
private currentDimensions;
|
|
15
|
+
private readonly maxPixels;
|
|
16
|
+
private readonly TypedArrayCtor;
|
|
17
|
+
private readonly bytesPerPixel;
|
|
18
|
+
private readonly dtype;
|
|
19
|
+
/**
|
|
20
|
+
* Create a new BufferManager.
|
|
21
|
+
*
|
|
22
|
+
* @param maxPixels - Maximum number of pixels allowed (budget)
|
|
23
|
+
* @param dtype - Data type for the buffer
|
|
24
|
+
*/
|
|
25
|
+
constructor(maxPixels: number, dtype: ZarrDtype);
|
|
26
|
+
/**
|
|
27
|
+
* Resize the buffer to fit the given dimensions.
|
|
28
|
+
*
|
|
29
|
+
* Reuses existing buffer if large enough, otherwise allocates new buffer.
|
|
30
|
+
* Will also reallocate if the buffer is significantly oversized (< 25% utilization).
|
|
31
|
+
*
|
|
32
|
+
* If dimensions exceed maxPixels, a warning is logged but the buffer is still
|
|
33
|
+
* allocated. This handles the case where even the lowest resolution exceeds the
|
|
34
|
+
* pixel budget - we still want to load something rather than failing.
|
|
35
|
+
*
|
|
36
|
+
* @param dimensions - New dimensions [z, y, x]
|
|
37
|
+
* @returns TypedArray view over the (possibly new) buffer
|
|
38
|
+
*/
|
|
39
|
+
resize(dimensions: [number, number, number]): TypedArray;
|
|
40
|
+
/**
|
|
41
|
+
* Get the underlying ArrayBuffer.
|
|
42
|
+
*/
|
|
43
|
+
getBuffer(): ArrayBuffer;
|
|
44
|
+
/**
|
|
45
|
+
* Get a typed array view over the current buffer region.
|
|
46
|
+
*
|
|
47
|
+
* The view is sized to match currentDimensions, not the full buffer capacity.
|
|
48
|
+
*/
|
|
49
|
+
getTypedArray(): TypedArray;
|
|
50
|
+
/**
|
|
51
|
+
* Get the current buffer dimensions [z, y, x].
|
|
52
|
+
*/
|
|
53
|
+
getDimensions(): [number, number, number];
|
|
54
|
+
/**
|
|
55
|
+
* Get the total number of pixels in the current buffer region.
|
|
56
|
+
*/
|
|
57
|
+
getPixelCount(): number;
|
|
58
|
+
/**
|
|
59
|
+
* Get the buffer capacity in pixels.
|
|
60
|
+
*/
|
|
61
|
+
getCapacity(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get the bytes per pixel.
|
|
64
|
+
*/
|
|
65
|
+
getBytesPerPixel(): number;
|
|
66
|
+
/**
|
|
67
|
+
* Get the data type.
|
|
68
|
+
*/
|
|
69
|
+
getDtype(): ZarrDtype;
|
|
70
|
+
/**
|
|
71
|
+
* Get the maximum pixels budget.
|
|
72
|
+
*/
|
|
73
|
+
getMaxPixels(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Clear the current buffer region to zeros.
|
|
76
|
+
*/
|
|
77
|
+
clear(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Check if the buffer can accommodate the given dimensions without reallocation.
|
|
80
|
+
*
|
|
81
|
+
* @param dimensions - Dimensions to check [z, y, x]
|
|
82
|
+
* @returns True if current buffer can fit the dimensions
|
|
83
|
+
*/
|
|
84
|
+
canAccommodate(dimensions: [number, number, number]): boolean;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=BufferManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BufferManager.d.ts","sourceRoot":"","sources":["../src/BufferManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAC;AAG/E;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAElC;;;;;OAKG;gBACS,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS;IAW/C;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,UAAU;IA8BxD;;OAEG;IACH,SAAS,IAAI,WAAW;IAIxB;;;;OAIG;IACH,aAAa,IAAI,UAAU;IAO3B;;OAEG;IACH,aAAa,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAIzC;;OAEG;IACH,aAAa,IAAI,MAAM;IAQvB;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;OAEG;IACH,QAAQ,IAAI,SAAS;IAIrB;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;;;OAKG;IACH,cAAc,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO;CAK9D"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { getBytesPerPixel, getTypedArrayConstructor } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Manages a dynamically-sized pixel buffer for volume data.
|
|
6
|
+
*
|
|
7
|
+
* The buffer is resized to match the fetched data dimensions exactly.
|
|
8
|
+
* Memory is reused when possible to avoid unnecessary allocations.
|
|
9
|
+
*
|
|
10
|
+
* Memory reuse strategy:
|
|
11
|
+
* - Reuse buffer if newSize <= currentCapacity
|
|
12
|
+
* - Reallocate if newSize > currentCapacity OR newSize < 25% of currentCapacity
|
|
13
|
+
*/
|
|
14
|
+
export class BufferManager {
|
|
15
|
+
buffer;
|
|
16
|
+
currentDimensions;
|
|
17
|
+
maxPixels;
|
|
18
|
+
TypedArrayCtor;
|
|
19
|
+
bytesPerPixel;
|
|
20
|
+
dtype;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new BufferManager.
|
|
23
|
+
*
|
|
24
|
+
* @param maxPixels - Maximum number of pixels allowed (budget)
|
|
25
|
+
* @param dtype - Data type for the buffer
|
|
26
|
+
*/
|
|
27
|
+
constructor(maxPixels, dtype) {
|
|
28
|
+
this.maxPixels = maxPixels;
|
|
29
|
+
this.dtype = dtype;
|
|
30
|
+
this.TypedArrayCtor = getTypedArrayConstructor(dtype);
|
|
31
|
+
this.bytesPerPixel = getBytesPerPixel(dtype);
|
|
32
|
+
// Initialize with empty buffer - will be allocated on first resize
|
|
33
|
+
this.currentDimensions = [0, 0, 0];
|
|
34
|
+
this.buffer = new ArrayBuffer(0);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resize the buffer to fit the given dimensions.
|
|
38
|
+
*
|
|
39
|
+
* Reuses existing buffer if large enough, otherwise allocates new buffer.
|
|
40
|
+
* Will also reallocate if the buffer is significantly oversized (< 25% utilization).
|
|
41
|
+
*
|
|
42
|
+
* If dimensions exceed maxPixels, a warning is logged but the buffer is still
|
|
43
|
+
* allocated. This handles the case where even the lowest resolution exceeds the
|
|
44
|
+
* pixel budget - we still want to load something rather than failing.
|
|
45
|
+
*
|
|
46
|
+
* @param dimensions - New dimensions [z, y, x]
|
|
47
|
+
* @returns TypedArray view over the (possibly new) buffer
|
|
48
|
+
*/
|
|
49
|
+
resize(dimensions) {
|
|
50
|
+
const requiredPixels = dimensions[0] * dimensions[1] * dimensions[2];
|
|
51
|
+
if (requiredPixels > this.maxPixels) {
|
|
52
|
+
console.warn(`[fidnii] BufferManager: Requested dimensions [${dimensions.join(", ")}] = ${requiredPixels} pixels exceeds maxPixels (${this.maxPixels}). ` +
|
|
53
|
+
`Proceeding anyway (likely at lowest resolution).`);
|
|
54
|
+
}
|
|
55
|
+
const currentCapacityPixels = this.buffer.byteLength / this.bytesPerPixel;
|
|
56
|
+
const utilizationRatio = currentCapacityPixels > 0
|
|
57
|
+
? requiredPixels / currentCapacityPixels
|
|
58
|
+
: 0;
|
|
59
|
+
const needsReallocation = requiredPixels > currentCapacityPixels ||
|
|
60
|
+
utilizationRatio < 0.25;
|
|
61
|
+
if (needsReallocation) {
|
|
62
|
+
// Allocate new buffer
|
|
63
|
+
const newByteLength = requiredPixels * this.bytesPerPixel;
|
|
64
|
+
this.buffer = new ArrayBuffer(newByteLength);
|
|
65
|
+
}
|
|
66
|
+
this.currentDimensions = [...dimensions];
|
|
67
|
+
return this.getTypedArray();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the underlying ArrayBuffer.
|
|
71
|
+
*/
|
|
72
|
+
getBuffer() {
|
|
73
|
+
return this.buffer;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get a typed array view over the current buffer region.
|
|
77
|
+
*
|
|
78
|
+
* The view is sized to match currentDimensions, not the full buffer capacity.
|
|
79
|
+
*/
|
|
80
|
+
getTypedArray() {
|
|
81
|
+
const pixelCount = this.currentDimensions[0] *
|
|
82
|
+
this.currentDimensions[1] *
|
|
83
|
+
this.currentDimensions[2];
|
|
84
|
+
return new this.TypedArrayCtor(this.buffer, 0, pixelCount);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the current buffer dimensions [z, y, x].
|
|
88
|
+
*/
|
|
89
|
+
getDimensions() {
|
|
90
|
+
return [...this.currentDimensions];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the total number of pixels in the current buffer region.
|
|
94
|
+
*/
|
|
95
|
+
getPixelCount() {
|
|
96
|
+
return (this.currentDimensions[0] *
|
|
97
|
+
this.currentDimensions[1] *
|
|
98
|
+
this.currentDimensions[2]);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get the buffer capacity in pixels.
|
|
102
|
+
*/
|
|
103
|
+
getCapacity() {
|
|
104
|
+
return this.buffer.byteLength / this.bytesPerPixel;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get the bytes per pixel.
|
|
108
|
+
*/
|
|
109
|
+
getBytesPerPixel() {
|
|
110
|
+
return this.bytesPerPixel;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get the data type.
|
|
114
|
+
*/
|
|
115
|
+
getDtype() {
|
|
116
|
+
return this.dtype;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get the maximum pixels budget.
|
|
120
|
+
*/
|
|
121
|
+
getMaxPixels() {
|
|
122
|
+
return this.maxPixels;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Clear the current buffer region to zeros.
|
|
126
|
+
*/
|
|
127
|
+
clear() {
|
|
128
|
+
const pixelCount = this.getPixelCount();
|
|
129
|
+
if (pixelCount > 0) {
|
|
130
|
+
const view = new Uint8Array(this.buffer, 0, pixelCount * this.bytesPerPixel);
|
|
131
|
+
view.fill(0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if the buffer can accommodate the given dimensions without reallocation.
|
|
136
|
+
*
|
|
137
|
+
* @param dimensions - Dimensions to check [z, y, x]
|
|
138
|
+
* @returns True if current buffer can fit the dimensions
|
|
139
|
+
*/
|
|
140
|
+
canAccommodate(dimensions) {
|
|
141
|
+
const requiredPixels = dimensions[0] * dimensions[1] * dimensions[2];
|
|
142
|
+
const currentCapacityPixels = this.buffer.byteLength / this.bytesPerPixel;
|
|
143
|
+
return requiredPixels <= currentCapacityPixels;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=BufferManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BufferManager.js","sourceRoot":"","sources":["../src/BufferManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { Multiscales, NgffImage } from "@fideus-labs/ngff-zarr";
|
|
2
|
+
import type { ChunkAlignedRegion, ClipPlane, ClipPlanes, PixelRegion, VolumeBounds } from "./types.js";
|
|
3
|
+
/** Maximum number of clip planes supported by NiiVue */
|
|
4
|
+
export declare const MAX_CLIP_PLANES = 6;
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a 3D vector to unit length.
|
|
7
|
+
*
|
|
8
|
+
* @param v - Vector to normalize [x, y, z]
|
|
9
|
+
* @returns Normalized vector [x, y, z]
|
|
10
|
+
* @throws Error if vector has zero length
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeVector(v: [number, number, number]): [number, number, number];
|
|
13
|
+
/**
|
|
14
|
+
* Create a clip plane from a point and normal vector.
|
|
15
|
+
* The normal is automatically normalized to unit length.
|
|
16
|
+
*
|
|
17
|
+
* @param point - A point on the plane [x, y, z] in world coordinates
|
|
18
|
+
* @param normal - Normal vector pointing toward visible region [x, y, z]
|
|
19
|
+
* @returns ClipPlane with normalized normal
|
|
20
|
+
*/
|
|
21
|
+
export declare function createClipPlane(point: [number, number, number], normal: [number, number, number]): ClipPlane;
|
|
22
|
+
/**
|
|
23
|
+
* Create default clip planes (empty array = full volume visible).
|
|
24
|
+
*
|
|
25
|
+
* @param _multiscales - The OME-Zarr multiscales data (unused, kept for API consistency)
|
|
26
|
+
* @returns Empty ClipPlanes array
|
|
27
|
+
*/
|
|
28
|
+
export declare function createDefaultClipPlanes(_multiscales: Multiscales): ClipPlanes;
|
|
29
|
+
/**
|
|
30
|
+
* Get volume bounds from multiscales metadata.
|
|
31
|
+
*
|
|
32
|
+
* @param multiscales - The OME-Zarr multiscales data
|
|
33
|
+
* @returns Volume bounds in world space
|
|
34
|
+
*/
|
|
35
|
+
export declare function getVolumeBoundsFromMultiscales(multiscales: Multiscales): VolumeBounds;
|
|
36
|
+
/**
|
|
37
|
+
* Convert a normal vector to azimuth and elevation angles (for NiiVue).
|
|
38
|
+
*
|
|
39
|
+
* NiiVue convention:
|
|
40
|
+
* - Azimuth: 0 = posterior (+Y), 90 = right (+X), 180 = anterior (-Y), 270 = left (-X)
|
|
41
|
+
* - Elevation: 0 = horizontal, 90 = superior (+Z), -90 = inferior (-Z)
|
|
42
|
+
*
|
|
43
|
+
* @param normal - Unit normal vector [x, y, z]
|
|
44
|
+
* @returns Object with azimuth and elevation in degrees
|
|
45
|
+
*/
|
|
46
|
+
export declare function normalToAzimuthElevation(normal: [number, number, number]): {
|
|
47
|
+
azimuth: number;
|
|
48
|
+
elevation: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Convert azimuth and elevation angles to a unit normal vector.
|
|
52
|
+
*
|
|
53
|
+
* @param azimuth - Azimuth angle in degrees (0 = +Y, 90 = +X)
|
|
54
|
+
* @param elevation - Elevation angle in degrees (0 = horizontal, 90 = +Z)
|
|
55
|
+
* @returns Unit normal vector [x, y, z]
|
|
56
|
+
*/
|
|
57
|
+
export declare function azimuthElevationToNormal(azimuth: number, elevation: number): [number, number, number];
|
|
58
|
+
/**
|
|
59
|
+
* Calculate the NiiVue depth parameter for a clip plane.
|
|
60
|
+
*
|
|
61
|
+
* NiiVue's clip plane depth is in normalized texture coordinates where
|
|
62
|
+
* the volume center is at 0.5. Depth represents the signed distance from
|
|
63
|
+
* the center (0) to the plane, where -0.5 is at min boundary and +0.5 is
|
|
64
|
+
* at max boundary. Values beyond [-0.5, 0.5] place the plane outside the volume.
|
|
65
|
+
*
|
|
66
|
+
* @param plane - The clip plane
|
|
67
|
+
* @param volumeBounds - Volume bounds in world space
|
|
68
|
+
* @returns Depth value for NiiVue (typically in range [-0.5, 0.5] for planes within volume)
|
|
69
|
+
*/
|
|
70
|
+
export declare function calculateNiivueDepth(plane: ClipPlane, volumeBounds: VolumeBounds): number;
|
|
71
|
+
/**
|
|
72
|
+
* Convert a single clip plane to NiiVue format [depth, azimuth, elevation].
|
|
73
|
+
*
|
|
74
|
+
* NiiVue's shader convention:
|
|
75
|
+
* - The "back" side of the plane (sampleSide > 0) is VISIBLE
|
|
76
|
+
* - The "front" side of the plane (sampleSide < 0) is CLIPPED
|
|
77
|
+
* - sampleSide = dot(shaderNormal, p - 0.5) + depth
|
|
78
|
+
* - NiiVue internally adds 180° to azimuth, which flips the normal direction
|
|
79
|
+
*
|
|
80
|
+
* Our convention:
|
|
81
|
+
* - Normal points toward the VISIBLE region
|
|
82
|
+
*
|
|
83
|
+
* To reconcile these conventions:
|
|
84
|
+
* 1. We negate the normal before computing azimuth/elevation
|
|
85
|
+
* 2. After NiiVue's +180° flip, the shader sees our original normal direction
|
|
86
|
+
* 3. We also negate the depth to match the flipped normal
|
|
87
|
+
*
|
|
88
|
+
* @param plane - The clip plane
|
|
89
|
+
* @param volumeBounds - Volume bounds in world space
|
|
90
|
+
* @returns [depth, azimuth, elevation] for NiiVue
|
|
91
|
+
*/
|
|
92
|
+
export declare function clipPlaneToNiivue(plane: ClipPlane, volumeBounds: VolumeBounds): [number, number, number];
|
|
93
|
+
/**
|
|
94
|
+
* Convert clip planes to NiiVue format.
|
|
95
|
+
*
|
|
96
|
+
* @param clipPlanes - Array of clip planes
|
|
97
|
+
* @param volumeBounds - Volume bounds in world space
|
|
98
|
+
* @returns Array of [depth, azimuth, elevation] for NiiVue
|
|
99
|
+
*/
|
|
100
|
+
export declare function clipPlanesToNiivue(clipPlanes: ClipPlanes, volumeBounds: VolumeBounds): number[][];
|
|
101
|
+
/**
|
|
102
|
+
* Calculate the signed distance from a point to a plane.
|
|
103
|
+
*
|
|
104
|
+
* Positive = point is on the visible side (same side as normal)
|
|
105
|
+
* Negative = point is on the clipped side (opposite side from normal)
|
|
106
|
+
*
|
|
107
|
+
* @param testPoint - Point to test [x, y, z]
|
|
108
|
+
* @param plane - The clip plane
|
|
109
|
+
* @returns Signed distance
|
|
110
|
+
*/
|
|
111
|
+
export declare function pointToPlaneDistance(testPoint: [number, number, number], plane: ClipPlane): number;
|
|
112
|
+
/**
|
|
113
|
+
* Check if a point is inside all clip planes (on the visible side).
|
|
114
|
+
*
|
|
115
|
+
* @param worldCoord - World coordinate [x, y, z]
|
|
116
|
+
* @param clipPlanes - Array of clip planes
|
|
117
|
+
* @returns True if the point is inside all clip planes (or if there are no planes)
|
|
118
|
+
*/
|
|
119
|
+
export declare function isInsideClipPlanes(worldCoord: [number, number, number], clipPlanes: ClipPlanes): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Calculate the axis-aligned bounding box that contains the clipped region.
|
|
122
|
+
*
|
|
123
|
+
* For oblique clip planes, this finds the intersection of the clip planes
|
|
124
|
+
* with the volume bounds and returns the AABB of that intersection.
|
|
125
|
+
*
|
|
126
|
+
* This is used for data fetching (zarr is always axis-aligned).
|
|
127
|
+
*
|
|
128
|
+
* @param clipPlanes - Array of clip planes
|
|
129
|
+
* @param volumeBounds - Full volume bounds in world space
|
|
130
|
+
* @returns Bounding box of the clipped region
|
|
131
|
+
*/
|
|
132
|
+
export declare function clipPlanesToBoundingBox(clipPlanes: ClipPlanes, volumeBounds: VolumeBounds): VolumeBounds;
|
|
133
|
+
/**
|
|
134
|
+
* Convert clip planes to a pixel region for a specific NgffImage.
|
|
135
|
+
*
|
|
136
|
+
* This calculates the axis-aligned bounding box of the clipped region
|
|
137
|
+
* and converts it to pixel coordinates. When viewportBounds is provided,
|
|
138
|
+
* the result is further constrained to only include the visible viewport
|
|
139
|
+
* area (for viewport-aware resolution selection).
|
|
140
|
+
*
|
|
141
|
+
* @param clipPlanes - Array of clip planes
|
|
142
|
+
* @param volumeBounds - Full volume bounds in world space
|
|
143
|
+
* @param ngffImage - The NgffImage to convert to
|
|
144
|
+
* @param viewportBounds - Optional viewport bounds to intersect with
|
|
145
|
+
* @returns Pixel region [z, y, x] start and end indices
|
|
146
|
+
*/
|
|
147
|
+
export declare function clipPlanesToPixelRegion(clipPlanes: ClipPlanes, volumeBounds: VolumeBounds, ngffImage: NgffImage, viewportBounds?: VolumeBounds): PixelRegion;
|
|
148
|
+
/**
|
|
149
|
+
* Align a pixel region to chunk boundaries.
|
|
150
|
+
*
|
|
151
|
+
* This expands the region to include complete chunks, which is necessary
|
|
152
|
+
* for efficient zarr fetching.
|
|
153
|
+
*
|
|
154
|
+
* @param region - The pixel region to align
|
|
155
|
+
* @param ngffImage - The NgffImage (for chunk shape)
|
|
156
|
+
* @returns Chunk-aligned region with clipping information
|
|
157
|
+
*/
|
|
158
|
+
export declare function alignToChunks(region: PixelRegion, ngffImage: NgffImage): ChunkAlignedRegion;
|
|
159
|
+
/**
|
|
160
|
+
* Create an axis-aligned clip plane at a specific position.
|
|
161
|
+
*
|
|
162
|
+
* The normal points toward the VISIBLE region (the region to keep).
|
|
163
|
+
* NiiVue's shader convention is that the "back" side of the plane (where
|
|
164
|
+
* dot(n, p-center) + depth > 0) is visible.
|
|
165
|
+
*
|
|
166
|
+
* @param axis - The axis ('x', 'y', or 'z')
|
|
167
|
+
* @param position - Position along the axis in world coordinates
|
|
168
|
+
* @param direction - Which side to keep visible ('positive' or 'negative')
|
|
169
|
+
* @param volumeBounds - Volume bounds for centering the point
|
|
170
|
+
* @returns ClipPlane with point at center of volume projected to the plane
|
|
171
|
+
*/
|
|
172
|
+
export declare function createAxisAlignedClipPlane(axis: "x" | "y" | "z", position: number, direction: "positive" | "negative", volumeBounds: VolumeBounds): ClipPlane;
|
|
173
|
+
/**
|
|
174
|
+
* Validate clip planes array.
|
|
175
|
+
*
|
|
176
|
+
* @param clipPlanes - Array of clip planes to validate
|
|
177
|
+
* @throws Error if validation fails
|
|
178
|
+
*/
|
|
179
|
+
export declare function validateClipPlanes(clipPlanes: ClipPlanes): void;
|
|
180
|
+
//# sourceMappingURL=ClipPlanes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClipPlanes.d.ts","sourceRoot":"","sources":["../src/ClipPlanes.ts"],"names":[],"mappings":"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"}
|