@aics/vole-core 4.0.0 → 4.2.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/es/ImageInfo.js +7 -2
- package/es/VolumeDrawable.js +3 -1
- package/es/VolumeMaker.js +35 -10
- package/es/loaders/JsonImageInfoLoader.js +1 -1
- package/es/loaders/OmeZarrLoader.js +10 -7
- package/es/loaders/OpenCellLoader.js +1 -1
- package/es/loaders/RawArrayLoader.js +31 -10
- package/es/loaders/TiffLoader.js +39 -36
- package/es/loaders/VolumeLoaderUtils.js +2 -1
- package/es/types/ImageInfo.d.ts +10 -11
- package/es/types/VolumeMaker.d.ts +12 -5
- package/es/types/index.d.ts +2 -2
- package/es/types/loaders/RawArrayLoader.d.ts +2 -2
- package/es/types/types.d.ts +12 -14
- package/es/types.js +0 -16
- package/package.json +1 -1
package/es/ImageInfo.js
CHANGED
|
@@ -6,7 +6,7 @@ export function defaultImageInfo() {
|
|
|
6
6
|
atlasTileDims: [1, 1],
|
|
7
7
|
subregionSize: [1, 1, 1],
|
|
8
8
|
subregionOffset: [0, 0, 0],
|
|
9
|
-
|
|
9
|
+
numChannelsPerSource: [1],
|
|
10
10
|
channelNames: ["0"],
|
|
11
11
|
channelColors: [[255, 255, 255]],
|
|
12
12
|
multiscaleLevel: 0,
|
|
@@ -34,7 +34,12 @@ export class CImageInfo {
|
|
|
34
34
|
|
|
35
35
|
/** Number of channels in the image */
|
|
36
36
|
get numChannels() {
|
|
37
|
-
return this.imageInfo.
|
|
37
|
+
return this.imageInfo.numChannelsPerSource.reduce((a, b) => a + b, 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Number of channels per source, ordered by source index */
|
|
41
|
+
get numChannelsPerSource() {
|
|
42
|
+
return this.imageInfo.numChannelsPerSource;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/** XYZ size of the *original* (not downsampled) volume, in pixels */
|
package/es/VolumeDrawable.js
CHANGED
|
@@ -45,7 +45,9 @@ export default class VolumeDrawable {
|
|
|
45
45
|
this.viewMode = Axis.NONE; // 3D mode
|
|
46
46
|
|
|
47
47
|
this.channelColors = this.volume.channelColorsDefault.slice();
|
|
48
|
-
this.channelOptions =
|
|
48
|
+
this.channelOptions = Array.from({
|
|
49
|
+
length: this.volume.imageInfo.numChannels
|
|
50
|
+
}, () => ({}));
|
|
49
51
|
this.fusion = this.channelColors.map((col, index) => {
|
|
50
52
|
let rgbColor;
|
|
51
53
|
// take copy of original channel color
|
package/es/VolumeMaker.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ARRAY_CONSTRUCTORS } from "./types.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Basic utility functions to create sample volume data
|
|
3
5
|
* @class
|
|
@@ -9,9 +11,11 @@ export default class VolumeMaker {
|
|
|
9
11
|
* @param {number} vy
|
|
10
12
|
* @param {number} vz
|
|
11
13
|
* @param {function} sdFunc A function f(x,y,z) that returns a distance. f < 0 will be the interior of the volume, and f>=0 will be outside.
|
|
14
|
+
* @param {NumberType} dtype The data type for the output array
|
|
12
15
|
*/
|
|
13
|
-
static createVolume(vx, vy, vz, sdFunc) {
|
|
14
|
-
const
|
|
16
|
+
static createVolume(vx, vy, vz, sdFunc, dtype = "uint8") {
|
|
17
|
+
const ctor = ARRAY_CONSTRUCTORS[dtype];
|
|
18
|
+
const data = new ctor(vx * vy * vz).fill(0);
|
|
15
19
|
const cx = vx / 2;
|
|
16
20
|
const cy = vy / 2;
|
|
17
21
|
const cz = vz / 2;
|
|
@@ -40,11 +44,12 @@ export default class VolumeMaker {
|
|
|
40
44
|
* @param {number} vy
|
|
41
45
|
* @param {number} vz
|
|
42
46
|
* @param {number} radius
|
|
47
|
+
* @param {NumberType} dtype The data type for the output array
|
|
43
48
|
*/
|
|
44
|
-
static createSphere(vx, vy, vz, radius) {
|
|
49
|
+
static createSphere(vx, vy, vz, radius, dtype = "uint8") {
|
|
45
50
|
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
46
51
|
return Math.sqrt(px * px + py * py + pz * pz) - radius;
|
|
47
|
-
});
|
|
52
|
+
}, dtype);
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/**
|
|
@@ -54,8 +59,9 @@ export default class VolumeMaker {
|
|
|
54
59
|
* @param {number} vz
|
|
55
60
|
* @param {number} hx width of cap (?)
|
|
56
61
|
* @param {number} hy depth of cap (?)
|
|
62
|
+
* @param {NumberType} dtype The data type for the output array
|
|
57
63
|
*/
|
|
58
|
-
static createCylinder(vx, vy, vz, hx, hy) {
|
|
64
|
+
static createCylinder(vx, vy, vz, hx, hy, dtype = "uint8") {
|
|
59
65
|
let dx, dy, mdx, mdy;
|
|
60
66
|
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
61
67
|
dx = Math.abs(Math.sqrt(px * px + pz * pz)) - hx;
|
|
@@ -63,7 +69,7 @@ export default class VolumeMaker {
|
|
|
63
69
|
mdx = Math.max(dx, 0.0);
|
|
64
70
|
mdy = Math.max(dy, 0.0);
|
|
65
71
|
return Math.min(Math.max(dx, dy), 0.0) + Math.sqrt(mdx * mdx + mdy * mdy);
|
|
66
|
-
});
|
|
72
|
+
}, dtype);
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
/**
|
|
@@ -73,14 +79,15 @@ export default class VolumeMaker {
|
|
|
73
79
|
* @param {number} vz
|
|
74
80
|
* @param {number} tx inner radius
|
|
75
81
|
* @param {number} ty outer radius
|
|
82
|
+
* @param {NumberType} dtype The data type for the output array
|
|
76
83
|
*/
|
|
77
|
-
static createTorus(vx, vy, vz, tx, ty) {
|
|
84
|
+
static createTorus(vx, vy, vz, tx, ty, dtype = "uint8") {
|
|
78
85
|
let qx, qy;
|
|
79
86
|
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
80
87
|
qx = Math.sqrt(px * px + pz * pz) - tx;
|
|
81
88
|
qy = py;
|
|
82
89
|
return Math.sqrt(qx * qx + qy * qy) - ty;
|
|
83
|
-
});
|
|
90
|
+
}, dtype);
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
/**
|
|
@@ -90,12 +97,30 @@ export default class VolumeMaker {
|
|
|
90
97
|
* @param {number} vz
|
|
91
98
|
* @param {number} cx base radius
|
|
92
99
|
* @param {number} cy height
|
|
100
|
+
* @param {NumberType} dtype The data type for the output array
|
|
93
101
|
*/
|
|
94
|
-
static createCone(vx, vy, vz, cx, cy) {
|
|
102
|
+
static createCone(vx, vy, vz, cx, cy, dtype = "uint8") {
|
|
95
103
|
let q;
|
|
96
104
|
return VolumeMaker.createVolume(vx, vy, vz, (px, py, pz) => {
|
|
97
105
|
q = Math.sqrt(px * px + py * py);
|
|
98
106
|
return cx * q + cy * pz;
|
|
99
|
-
});
|
|
107
|
+
}, dtype);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// take a list of TypedArrays and concatenate them into a single TypedArray of the same Type:
|
|
111
|
+
static concatenateArrays(arrays, dtype) {
|
|
112
|
+
if (arrays.length === 0) {
|
|
113
|
+
throw new Error("Cannot concatenate empty array list");
|
|
114
|
+
}
|
|
115
|
+
const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0);
|
|
116
|
+
|
|
117
|
+
// Create a new array of the same type as the input arrays
|
|
118
|
+
const result = new ARRAY_CONSTRUCTORS[dtype](totalLength);
|
|
119
|
+
let offset = 0;
|
|
120
|
+
for (const arr of arrays) {
|
|
121
|
+
result.set(arr, offset);
|
|
122
|
+
offset += arr.length;
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
100
125
|
}
|
|
101
126
|
}
|
|
@@ -29,7 +29,7 @@ const convertImageInfo = json => {
|
|
|
29
29
|
atlasTileDims: [json.cols, json.rows],
|
|
30
30
|
subregionSize: [json.tile_width, json.tile_height, json.tiles],
|
|
31
31
|
subregionOffset: [0, 0, 0],
|
|
32
|
-
|
|
32
|
+
numChannelsPerSource: json.images.map(image => image.channels.length),
|
|
33
33
|
channelNames: json.channel_names,
|
|
34
34
|
channelColors: json.channel_colors,
|
|
35
35
|
multiscaleLevel: 0,
|
|
@@ -247,12 +247,14 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
|
|
|
247
247
|
const levelToLoad = pickLevelToLoad(loadSpec, this.getLevelShapesZYX());
|
|
248
248
|
const shapeLv = source0.scaleLevels[levelToLoad].shape;
|
|
249
249
|
const [spatialUnit, timeUnit] = this.getUnitSymbols();
|
|
250
|
+
const numChannelsPerSource = [];
|
|
251
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
252
|
+
const source = this.sources[i];
|
|
253
|
+
const cIndex = source.axesTCZYX[1];
|
|
254
|
+
const sourceChannelCount = cIndex > -1 ? source.scaleLevels[levelToLoad].shape[cIndex] : 1;
|
|
255
|
+
numChannelsPerSource.push(sourceChannelCount);
|
|
256
|
+
}
|
|
250
257
|
|
|
251
|
-
// Now we care about other sources: # of channels is the `channelOffset` of the last source plus its # of channels
|
|
252
|
-
const sourceLast = this.sources[this.sources.length - 1];
|
|
253
|
-
const cLast = sourceLast.axesTCZYX[1];
|
|
254
|
-
const lastHasC = cLast > -1;
|
|
255
|
-
const numChannels = sourceLast.channelOffset + (lastHasC ? sourceLast.scaleLevels[levelToLoad].shape[cLast] : 1);
|
|
256
258
|
// we need to make sure that the corresponding matched shapes
|
|
257
259
|
// use the min size of T
|
|
258
260
|
let times = 1;
|
|
@@ -310,7 +312,7 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
|
|
|
310
312
|
atlasTileDims: [atlasTileDims.x, atlasTileDims.y],
|
|
311
313
|
subregionSize: [pxSizeLv.x, pxSizeLv.y, pxSizeLv.z],
|
|
312
314
|
subregionOffset: [0, 0, 0],
|
|
313
|
-
|
|
315
|
+
numChannelsPerSource,
|
|
314
316
|
channelNames,
|
|
315
317
|
multiscaleLevel: levelToLoad,
|
|
316
318
|
multiscaleLevelDims: alldims,
|
|
@@ -423,9 +425,10 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
|
|
|
423
425
|
const updatedImageInfo = this.updateImageInfoForLoad(imageInfo, loadSpec);
|
|
424
426
|
onUpdateMetadata(updatedImageInfo);
|
|
425
427
|
const {
|
|
426
|
-
|
|
428
|
+
numChannelsPerSource,
|
|
427
429
|
multiscaleLevel
|
|
428
430
|
} = updatedImageInfo;
|
|
431
|
+
const combinedNumChannels = numChannelsPerSource.reduce((a, b) => a + b, 0);
|
|
429
432
|
const channelIndexes = loadSpec.channels ?? Array.from({
|
|
430
433
|
length: combinedNumChannels
|
|
431
434
|
}, (_, i) => i);
|
|
@@ -24,7 +24,7 @@ class OpenCellLoader extends ThreadableVolumeLoader {
|
|
|
24
24
|
atlasTileDims: [27, 1],
|
|
25
25
|
subregionSize: [600, 600, 27],
|
|
26
26
|
subregionOffset: [0, 0, 0],
|
|
27
|
-
|
|
27
|
+
numChannelsPerSource: [numChannels],
|
|
28
28
|
channelNames: chnames,
|
|
29
29
|
multiscaleLevel: 0,
|
|
30
30
|
multiscaleLevelDims: [{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box3, Vector3 } from "three";
|
|
2
2
|
import { ThreadableVolumeLoader } from "./IVolumeLoader.js";
|
|
3
3
|
import { computePackedAtlasDims } from "./VolumeLoaderUtils.js";
|
|
4
|
+
import { ARRAY_CONSTRUCTORS } from "../types.js";
|
|
4
5
|
import { getDataRange } from "../utils/num_utils.js";
|
|
5
6
|
|
|
6
7
|
// this is the form in which a 4D numpy array arrives as converted
|
|
@@ -9,7 +10,25 @@ import { getDataRange } from "../utils/num_utils.js";
|
|
|
9
10
|
|
|
10
11
|
// minimal metadata for visualization
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
function getBytesPerPixel(dtype) {
|
|
14
|
+
switch (dtype) {
|
|
15
|
+
case "uint8":
|
|
16
|
+
case "int8":
|
|
17
|
+
return 1;
|
|
18
|
+
case "uint16":
|
|
19
|
+
case "int16":
|
|
20
|
+
return 2;
|
|
21
|
+
case "uint32":
|
|
22
|
+
case "int32":
|
|
23
|
+
case "float32":
|
|
24
|
+
return 4;
|
|
25
|
+
case "float64":
|
|
26
|
+
return 8;
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unsupported dtype: ${dtype}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const convertImageInfo = (json, dtype) => {
|
|
13
32
|
const atlasTileDims = computePackedAtlasDims(json.sizeZ, json.sizeX, json.sizeY);
|
|
14
33
|
return {
|
|
15
34
|
name: json.name,
|
|
@@ -18,7 +37,7 @@ const convertImageInfo = json => {
|
|
|
18
37
|
atlasTileDims: [atlasTileDims.x, atlasTileDims.y],
|
|
19
38
|
subregionSize: [json.sizeX, json.sizeY, json.sizeZ],
|
|
20
39
|
subregionOffset: [0, 0, 0],
|
|
21
|
-
|
|
40
|
+
numChannelsPerSource: [json.sizeC],
|
|
22
41
|
channelNames: json.channelNames,
|
|
23
42
|
channelColors: undefined,
|
|
24
43
|
multiscaleLevel: 0,
|
|
@@ -27,7 +46,7 @@ const convertImageInfo = json => {
|
|
|
27
46
|
spacing: [1, 1, json.physicalPixelSize[2], json.physicalPixelSize[1], json.physicalPixelSize[0]],
|
|
28
47
|
spaceUnit: json.spatialUnit || "μm",
|
|
29
48
|
timeUnit: "s",
|
|
30
|
-
dataType:
|
|
49
|
+
dataType: dtype
|
|
31
50
|
}],
|
|
32
51
|
transform: {
|
|
33
52
|
translation: [0, 0, 0],
|
|
@@ -53,14 +72,14 @@ class RawArrayLoader extends ThreadableVolumeLoader {
|
|
|
53
72
|
shape: [1, jsonInfo.sizeC, jsonInfo.sizeZ, jsonInfo.sizeY, jsonInfo.sizeX],
|
|
54
73
|
spacing: [1, 1, jsonInfo.physicalPixelSize[2], jsonInfo.physicalPixelSize[1], jsonInfo.physicalPixelSize[0]],
|
|
55
74
|
spaceUnit: jsonInfo.spatialUnit || "μm",
|
|
56
|
-
dataType:
|
|
75
|
+
dataType: this.data.dtype,
|
|
57
76
|
timeUnit: "s" // time unit not specified
|
|
58
77
|
};
|
|
59
78
|
return [d];
|
|
60
79
|
}
|
|
61
80
|
async createImageInfo(loadSpec) {
|
|
62
81
|
return {
|
|
63
|
-
imageInfo: convertImageInfo(this.jsonInfo),
|
|
82
|
+
imageInfo: convertImageInfo(this.jsonInfo, this.data.dtype),
|
|
64
83
|
loadSpec
|
|
65
84
|
};
|
|
66
85
|
}
|
|
@@ -73,15 +92,17 @@ class RawArrayLoader extends ThreadableVolumeLoader {
|
|
|
73
92
|
multiscaleLevel: 0
|
|
74
93
|
};
|
|
75
94
|
onUpdateMetadata(undefined, adjustedLoadSpec);
|
|
76
|
-
|
|
95
|
+
const totalChannels = imageInfo.numChannelsPerSource.reduce((a, b) => a + b, 0);
|
|
96
|
+
for (let chindex = 0; chindex < totalChannels; ++chindex) {
|
|
77
97
|
if (requestedChannels && requestedChannels.length > 0 && !requestedChannels.includes(chindex)) {
|
|
78
98
|
continue;
|
|
79
99
|
}
|
|
80
|
-
|
|
81
|
-
const
|
|
100
|
+
// x*y*z pixels
|
|
101
|
+
const volSizePixels = this.data.shape[3] * this.data.shape[2] * this.data.shape[1];
|
|
102
|
+
const ctor = ARRAY_CONSTRUCTORS[this.data.dtype];
|
|
103
|
+
const channelData = new ctor(this.data.buffer.buffer, chindex * volSizePixels * getBytesPerPixel(this.data.dtype), volSizePixels);
|
|
82
104
|
const range = getDataRange(channelData);
|
|
83
|
-
|
|
84
|
-
onData([chindex], ["uint8"], [channelData], [range]);
|
|
105
|
+
onData([chindex], [this.data.dtype], [channelData], [range]);
|
|
85
106
|
}
|
|
86
107
|
return Promise.resolve();
|
|
87
108
|
}
|
package/es/loaders/TiffLoader.js
CHANGED
|
@@ -157,13 +157,13 @@ class TiffLoader extends ThreadableVolumeLoader {
|
|
|
157
157
|
const tilesizey = Math.floor(targetSize / atlasDims.y);
|
|
158
158
|
|
|
159
159
|
// load tiff and check metadata
|
|
160
|
-
|
|
160
|
+
const numChannelsPerSource = this.url.length > 1 ? Array(this.url.length).fill(1) : [dims.sizec];
|
|
161
161
|
const imgdata = {
|
|
162
162
|
name: "TEST",
|
|
163
163
|
atlasTileDims: [atlasDims.x, atlasDims.y],
|
|
164
164
|
subregionSize: [tilesizex, tilesizey, dims.sizez],
|
|
165
165
|
subregionOffset: [0, 0, 0],
|
|
166
|
-
|
|
166
|
+
numChannelsPerSource,
|
|
167
167
|
channelNames: dims.channelnames,
|
|
168
168
|
multiscaleLevel: 0,
|
|
169
169
|
multiscaleLevelDims: [{
|
|
@@ -194,41 +194,44 @@ class TiffLoader extends ThreadableVolumeLoader {
|
|
|
194
194
|
const volumeSize = cimageinfo.volumeSize;
|
|
195
195
|
const channelProms = [];
|
|
196
196
|
// do each channel on a worker?
|
|
197
|
-
for (let
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
197
|
+
for (let source = 0; source < imageInfo.numChannelsPerSource.length; ++source) {
|
|
198
|
+
const numChannels = imageInfo.numChannelsPerSource[source];
|
|
199
|
+
for (let channel = 0; channel < numChannels; ++channel) {
|
|
200
|
+
const thisChannelProm = new Promise((resolve, reject) => {
|
|
201
|
+
const params = {
|
|
202
|
+
channel: channel,
|
|
203
|
+
// these are target xy sizes for the in-memory volume data
|
|
204
|
+
// they may or may not be the same size as original xy sizes
|
|
205
|
+
tilesizex: volumeSize.x,
|
|
206
|
+
tilesizey: volumeSize.y,
|
|
207
|
+
sizec: numChannels,
|
|
208
|
+
sizez: volumeSize.z,
|
|
209
|
+
dimensionOrder: dims.dimensionorder,
|
|
210
|
+
bytesPerSample: getBytesPerSample(dims.pixeltype),
|
|
211
|
+
url: this.url[source]
|
|
212
|
+
};
|
|
213
|
+
const worker = new Worker(new URL("../workers/FetchTiffWorker", import.meta.url), {
|
|
214
|
+
type: "module"
|
|
215
|
+
});
|
|
216
|
+
worker.onmessage = e => {
|
|
217
|
+
if (e.data.isError) {
|
|
218
|
+
reject(deserializeError(e.data.error));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const {
|
|
222
|
+
data,
|
|
223
|
+
dtype,
|
|
224
|
+
channel,
|
|
225
|
+
range
|
|
226
|
+
} = e.data;
|
|
227
|
+
onData([channel], [dtype], [data], [range]);
|
|
228
|
+
worker.terminate();
|
|
229
|
+
resolve();
|
|
230
|
+
};
|
|
231
|
+
worker.postMessage(params);
|
|
213
232
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
reject(deserializeError(e.data.error));
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
const {
|
|
220
|
-
data,
|
|
221
|
-
dtype,
|
|
222
|
-
channel,
|
|
223
|
-
range
|
|
224
|
-
} = e.data;
|
|
225
|
-
onData([channel], [dtype], [data], [range]);
|
|
226
|
-
worker.terminate();
|
|
227
|
-
resolve();
|
|
228
|
-
};
|
|
229
|
-
worker.postMessage(params);
|
|
230
|
-
});
|
|
231
|
-
channelProms.push(thisChannelProm);
|
|
233
|
+
channelProms.push(thisChannelProm);
|
|
234
|
+
}
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
// waiting for all channels to load allows errors to propagate to the caller via this promise
|
|
@@ -211,7 +211,8 @@ export function buildDefaultMetadata(rawImageInfo) {
|
|
|
211
211
|
};
|
|
212
212
|
metadata["Multiresolution levels"] = rawImageInfo.multiscaleLevelDims;
|
|
213
213
|
// TODO decide???? combined or not?
|
|
214
|
-
|
|
214
|
+
const totalChannels = imageInfo.numChannelsPerSource.reduce((a, b) => a + b, 0);
|
|
215
|
+
metadata["Channels"] = totalChannels;
|
|
215
216
|
metadata["Time series frames"] = imageInfo.times || 1;
|
|
216
217
|
// don't add User data if it's empty
|
|
217
218
|
if (rawImageInfo.userData && !isEmpty(rawImageInfo.userData)) {
|
package/es/types/ImageInfo.d.ts
CHANGED
|
@@ -3,20 +3,17 @@ import { Vector3, Vector2 } from "three";
|
|
|
3
3
|
export type ImageInfo = Readonly<{
|
|
4
4
|
name: string;
|
|
5
5
|
/**
|
|
6
|
-
* XY dimensions of the texture atlas used by `RayMarchedAtlasVolume` and
|
|
7
|
-
* tiles (not pixels). Chosen by the
|
|
6
|
+
* XY dimensions of the texture atlas used by `RayMarchedAtlasVolume` and
|
|
7
|
+
* `Atlas2DSlice`, in number of z-slice tiles (not pixels). Chosen by the
|
|
8
|
+
* loader to lay out the 3D volume in the squarest possible 2D texture atlas.
|
|
8
9
|
*/
|
|
9
10
|
atlasTileDims: [number, number];
|
|
10
11
|
/** Size of the currently loaded subregion, in pixels, in XYZ order */
|
|
11
12
|
subregionSize: [number, number, number];
|
|
12
13
|
/** Offset of the loaded subregion into the total volume, in pixels, in XYZ order */
|
|
13
14
|
subregionOffset: [number, number, number];
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
* that this could be different than the number of channels in the multiscaleLevelDims.
|
|
17
|
-
* NOTE Currently there is one ImageInfo per Volume, not per source.
|
|
18
|
-
*/
|
|
19
|
-
combinedNumChannels: number;
|
|
15
|
+
/** The number of channels in each source, in source order. */
|
|
16
|
+
numChannelsPerSource: number[];
|
|
20
17
|
/** The names of each channel */
|
|
21
18
|
channelNames: string[];
|
|
22
19
|
/** Optional overrides to default channel colors, in 0-255 range, RGB order */
|
|
@@ -26,9 +23,9 @@ export type ImageInfo = Readonly<{
|
|
|
26
23
|
/** The scale level from which this image was loaded, between `0` and `numMultiscaleLevels-1` */
|
|
27
24
|
multiscaleLevel: number;
|
|
28
25
|
/**
|
|
29
|
-
* An *optional* transform which may be supplied by image metadata. It is
|
|
30
|
-
* default, but may be read and fed to `View3d` methods:
|
|
31
|
-
* `setVolumeRotation`, `setVolumeScale`.
|
|
26
|
+
* An *optional* transform which may be supplied by image metadata. It is
|
|
27
|
+
* *not* applied by default, but may be read and fed to `View3d` methods:
|
|
28
|
+
* `setVolumeTransform`, `setVolumeRotation`, `setVolumeScale`.
|
|
32
29
|
*/
|
|
33
30
|
transform: {
|
|
34
31
|
/** Translation of the volume from the center of space, in volume voxels in XYZ order */
|
|
@@ -48,6 +45,8 @@ export declare class CImageInfo {
|
|
|
48
45
|
get currentLevelDims(): VolumeDims;
|
|
49
46
|
/** Number of channels in the image */
|
|
50
47
|
get numChannels(): number;
|
|
48
|
+
/** Number of channels per source, ordered by source index */
|
|
49
|
+
get numChannelsPerSource(): number[];
|
|
51
50
|
/** XYZ size of the *original* (not downsampled) volume, in pixels */
|
|
52
51
|
get originalSize(): Vector3;
|
|
53
52
|
/** Size of the volume, in pixels */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type NumberType, type TypedArray } from "./types.js";
|
|
1
2
|
/**
|
|
2
3
|
* Basic utility functions to create sample volume data
|
|
3
4
|
* @class
|
|
@@ -9,16 +10,18 @@ export default class VolumeMaker {
|
|
|
9
10
|
* @param {number} vy
|
|
10
11
|
* @param {number} vz
|
|
11
12
|
* @param {function} sdFunc A function f(x,y,z) that returns a distance. f < 0 will be the interior of the volume, and f>=0 will be outside.
|
|
13
|
+
* @param {NumberType} dtype The data type for the output array
|
|
12
14
|
*/
|
|
13
|
-
static createVolume(vx: number, vy: number, vz: number, sdFunc: (px: number, py: number, pz: number) => number):
|
|
15
|
+
static createVolume(vx: number, vy: number, vz: number, sdFunc: (px: number, py: number, pz: number) => number, dtype?: NumberType): TypedArray<NumberType>;
|
|
14
16
|
/**
|
|
15
17
|
* Create a volume filled with a sphere in the center
|
|
16
18
|
* @param {number} vx
|
|
17
19
|
* @param {number} vy
|
|
18
20
|
* @param {number} vz
|
|
19
21
|
* @param {number} radius
|
|
22
|
+
* @param {NumberType} dtype The data type for the output array
|
|
20
23
|
*/
|
|
21
|
-
static createSphere(vx: number, vy: number, vz: number, radius: number):
|
|
24
|
+
static createSphere(vx: number, vy: number, vz: number, radius: number, dtype?: NumberType): TypedArray<NumberType>;
|
|
22
25
|
/**
|
|
23
26
|
* Create a volume with a cylinder centered inside.
|
|
24
27
|
* @param {number} vx
|
|
@@ -26,8 +29,9 @@ export default class VolumeMaker {
|
|
|
26
29
|
* @param {number} vz
|
|
27
30
|
* @param {number} hx width of cap (?)
|
|
28
31
|
* @param {number} hy depth of cap (?)
|
|
32
|
+
* @param {NumberType} dtype The data type for the output array
|
|
29
33
|
*/
|
|
30
|
-
static createCylinder(vx: number, vy: number, vz: number, hx: number, hy: number):
|
|
34
|
+
static createCylinder(vx: number, vy: number, vz: number, hx: number, hy: number, dtype?: NumberType): TypedArray<NumberType>;
|
|
31
35
|
/**
|
|
32
36
|
* Create a volume with a torus centered inside
|
|
33
37
|
* @param {number} vx
|
|
@@ -35,8 +39,9 @@ export default class VolumeMaker {
|
|
|
35
39
|
* @param {number} vz
|
|
36
40
|
* @param {number} tx inner radius
|
|
37
41
|
* @param {number} ty outer radius
|
|
42
|
+
* @param {NumberType} dtype The data type for the output array
|
|
38
43
|
*/
|
|
39
|
-
static createTorus(vx: number, vy: number, vz: number, tx: number, ty: number):
|
|
44
|
+
static createTorus(vx: number, vy: number, vz: number, tx: number, ty: number, dtype?: NumberType): TypedArray<NumberType>;
|
|
40
45
|
/**
|
|
41
46
|
* Create a volume with a cone centered inside. cx, cy must be a 2d normalized pair...?
|
|
42
47
|
* @param {number} vx
|
|
@@ -44,6 +49,8 @@ export default class VolumeMaker {
|
|
|
44
49
|
* @param {number} vz
|
|
45
50
|
* @param {number} cx base radius
|
|
46
51
|
* @param {number} cy height
|
|
52
|
+
* @param {NumberType} dtype The data type for the output array
|
|
47
53
|
*/
|
|
48
|
-
static createCone(vx: number, vy: number, vz: number, cx: number, cy: number):
|
|
54
|
+
static createCone(vx: number, vy: number, vz: number, cx: number, cy: number, dtype?: NumberType): TypedArray<NumberType>;
|
|
55
|
+
static concatenateArrays(arrays: TypedArray<NumberType>[], dtype: NumberType): TypedArray<NumberType>;
|
|
49
56
|
}
|
package/es/types/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import RequestQueue from "./utils/RequestQueue.js";
|
|
|
8
8
|
import SubscribableRequestQueue from "./utils/SubscribableRequestQueue.js";
|
|
9
9
|
import Histogram from "./Histogram.js";
|
|
10
10
|
import { Lut, remapControlPoints } from "./Lut.js";
|
|
11
|
-
import { type ColorizeFeature, ViewportCorner } from "./types.js";
|
|
11
|
+
import { type ColorizeFeature, type NumberType, ViewportCorner } from "./types.js";
|
|
12
12
|
import { VolumeFileFormat, createVolumeLoader, PrefetchDirection } from "./loaders/index.js";
|
|
13
13
|
import { LoadSpec } from "./loaders/IVolumeLoader.js";
|
|
14
14
|
import { OMEZarrLoader } from "./loaders/OmeZarrLoader.js";
|
|
@@ -26,4 +26,4 @@ export type { CreateLoaderOptions } from "./loaders/index.js";
|
|
|
26
26
|
export type { IVolumeLoader, PerChannelCallback, ThreadableVolumeLoader } from "./loaders/IVolumeLoader.js";
|
|
27
27
|
export type { ZarrLoaderFetchOptions } from "./loaders/OmeZarrLoader.js";
|
|
28
28
|
export type { WorkerLoader } from "./workers/VolumeLoaderContext.js";
|
|
29
|
-
export { Histogram, Lut, Line3d, remapControlPoints, View3d, Volume, VolumeDrawable, LoadSpec, VolumeMaker, VolumeCache, RequestQueue, SubscribableRequestQueue, PrefetchDirection, OMEZarrLoader, JsonImageInfoLoader, RawArrayLoader, type RawArrayData, type RawArrayInfo, type RawArrayLoaderOptions, TiffLoader, VolumeLoaderContext, VolumeLoadError, VolumeLoadErrorType, VolumeFileFormat, createVolumeLoader, Channel, Light, ViewportCorner, AREA_LIGHT, RENDERMODE_PATHTRACE, RENDERMODE_RAYMARCH, SKY_LIGHT, type CameraState, type ColorizeFeature, };
|
|
29
|
+
export { Histogram, Lut, Line3d, remapControlPoints, View3d, Volume, VolumeDrawable, LoadSpec, VolumeMaker, VolumeCache, RequestQueue, SubscribableRequestQueue, PrefetchDirection, OMEZarrLoader, JsonImageInfoLoader, RawArrayLoader, type RawArrayData, type RawArrayInfo, type RawArrayLoaderOptions, TiffLoader, VolumeLoaderContext, VolumeLoadError, VolumeLoadErrorType, VolumeFileFormat, createVolumeLoader, Channel, Light, ViewportCorner, AREA_LIGHT, RENDERMODE_PATHTRACE, RENDERMODE_RAYMARCH, SKY_LIGHT, type CameraState, type ColorizeFeature, type NumberType, };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ThreadableVolumeLoader, type LoadSpec, type RawChannelDataCallback, type LoadedVolumeInfo } from "./IVolumeLoader.js";
|
|
2
2
|
import type { ImageInfo } from "../ImageInfo.js";
|
|
3
3
|
import type { VolumeDims } from "../VolumeDims.js";
|
|
4
|
-
import {
|
|
4
|
+
import { NumberType } from "../types.js";
|
|
5
5
|
export type RawArrayData = {
|
|
6
|
-
dtype:
|
|
6
|
+
dtype: NumberType;
|
|
7
7
|
shape: [number, number, number, number];
|
|
8
8
|
buffer: DataView;
|
|
9
9
|
};
|
package/es/types/types.d.ts
CHANGED
|
@@ -96,28 +96,26 @@ export interface FuseChannel {
|
|
|
96
96
|
}
|
|
97
97
|
/** If `FuseChannel.rgbColor` is this value, it is disabled from fusion. */
|
|
98
98
|
export declare const FUSE_DISABLED_RGB_COLOR = 0;
|
|
99
|
-
/**
|
|
100
|
-
* Provide options to control the visual appearance of a Volume
|
|
101
|
-
* @typedef {Object} VolumeChannelDisplayOptions
|
|
102
|
-
* @property {boolean} enabled array of boolean per channel
|
|
103
|
-
* @property {Array.<number>} color array of rgb per channel
|
|
104
|
-
* @property {Array.<number>} specularColor array of rgb per channel
|
|
105
|
-
* @property {Array.<number>} emissiveColor array of rgb per channel
|
|
106
|
-
* @property {number} glossiness array of float per channel
|
|
107
|
-
* @property {boolean} isosurfaceEnabled array of boolean per channel
|
|
108
|
-
* @property {number} isovalue array of number per channel
|
|
109
|
-
* @property {number} isosurfaceOpacity array of number per channel
|
|
110
|
-
* @example let options = {
|
|
111
|
-
};
|
|
112
|
-
*/
|
|
113
99
|
export interface VolumeChannelDisplayOptions {
|
|
100
|
+
/** Whether the channel's volume data should be rendered for this channel. */
|
|
114
101
|
enabled?: boolean;
|
|
102
|
+
/** RGB color array, with values in the range of [0, 255]. */
|
|
115
103
|
color?: [number, number, number];
|
|
104
|
+
/** RGB color array for specular (highlight) color, with values in the range of [0, 255]. */
|
|
116
105
|
specularColor?: [number, number, number];
|
|
106
|
+
/** RGB color array for emissive (glow) color, with values in the range of [0, 255]. */
|
|
117
107
|
emissiveColor?: [number, number, number];
|
|
108
|
+
/** Exponent factor controlling the glossiness ("shininess") of the material. 0 is default. */
|
|
118
109
|
glossiness?: number;
|
|
110
|
+
/** Whether the isosurface mesh should be rendered for this channel. */
|
|
119
111
|
isosurfaceEnabled?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Isovalue used to calculate the isosurface mesh, in a [0, 255] range.
|
|
114
|
+
* Isosurface is found at the set of all boundaries between voxels whose
|
|
115
|
+
* intensities span across this isovalue.
|
|
116
|
+
*/
|
|
120
117
|
isovalue?: number;
|
|
118
|
+
/** Opacity of the isosurface, in a [0, 1] range. */
|
|
121
119
|
isosurfaceOpacity?: number;
|
|
122
120
|
}
|
|
123
121
|
export declare enum RenderMode {
|
package/es/types.js
CHANGED
|
@@ -18,22 +18,6 @@ export function isFloatTypeArray(array) {
|
|
|
18
18
|
}
|
|
19
19
|
/** If `FuseChannel.rgbColor` is this value, it is disabled from fusion. */
|
|
20
20
|
export const FUSE_DISABLED_RGB_COLOR = 0;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Provide options to control the visual appearance of a Volume
|
|
24
|
-
* @typedef {Object} VolumeChannelDisplayOptions
|
|
25
|
-
* @property {boolean} enabled array of boolean per channel
|
|
26
|
-
* @property {Array.<number>} color array of rgb per channel
|
|
27
|
-
* @property {Array.<number>} specularColor array of rgb per channel
|
|
28
|
-
* @property {Array.<number>} emissiveColor array of rgb per channel
|
|
29
|
-
* @property {number} glossiness array of float per channel
|
|
30
|
-
* @property {boolean} isosurfaceEnabled array of boolean per channel
|
|
31
|
-
* @property {number} isovalue array of number per channel
|
|
32
|
-
* @property {number} isosurfaceOpacity array of number per channel
|
|
33
|
-
* @example let options = {
|
|
34
|
-
};
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
21
|
export let RenderMode = /*#__PURE__*/function (RenderMode) {
|
|
38
22
|
RenderMode[RenderMode["RAYMARCH"] = 0] = "RAYMARCH";
|
|
39
23
|
RenderMode[RenderMode["PATHTRACE"] = 1] = "PATHTRACE";
|