@alleninstitute/vis-omezarr 0.0.17
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.md +11 -0
- package/dist/main.js +1365 -0
- package/dist/main.js.map +1 -0
- package/dist/module.js +969 -0
- package/dist/module.js.map +1 -0
- package/dist/types.d.ts +418 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +58 -0
package/dist/main.js
ADDED
|
@@ -0,0 +1,1365 @@
|
|
|
1
|
+
import {intervalToVec2 as $7soKB$intervalToVec2, Box2D as $7soKB$Box2D, Vec2 as $7soKB$Vec2, limit as $7soKB$limit} from "@alleninstitute/vis-geometry";
|
|
2
|
+
import {logger as $7soKB$logger, buildAsyncRenderer as $7soKB$buildAsyncRenderer, getResourceUrl as $7soKB$getResourceUrl, VisError as $7soKB$VisError, makeRGBAColorVector as $7soKB$makeRGBAColorVector, PriorityCache as $7soKB$PriorityCache, WorkerPool as $7soKB$WorkerPool, HEARTBEAT_RATE_MS as $7soKB$HEARTBEAT_RATE_MS} from "@alleninstitute/vis-core";
|
|
3
|
+
import {FetchStore as $7soKB$FetchStore, open as $7soKB$open, root as $7soKB$root, slice as $7soKB$slice, get as $7soKB$get} from "zarrita";
|
|
4
|
+
import $7soKB$zod, {ZodError as $7soKB$ZodError, z as $7soKB$z} from "zod";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class $8af220877f5aad67$export$96bea966bad52d02 extends (0, $7soKB$VisError) {
|
|
13
|
+
}
|
|
14
|
+
class $8af220877f5aad67$export$e819e674f1e05236 extends $8af220877f5aad67$export$96bea966bad52d02 {
|
|
15
|
+
}
|
|
16
|
+
class $8af220877f5aad67$export$d7f857358a0dbda6 extends $8af220877f5aad67$export$96bea966bad52d02 {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// these dimension indices are given for a 4-element shape array
|
|
24
|
+
const $0ff6a7cfdd9baaf1$var$SHAPE_Z_DIM_INDEX = 1;
|
|
25
|
+
const $0ff6a7cfdd9baaf1$var$SHAPE_Y_DIM_INDEX = 2;
|
|
26
|
+
const $0ff6a7cfdd9baaf1$var$SHAPE_X_DIM_INDEX = 3;
|
|
27
|
+
const $0ff6a7cfdd9baaf1$export$9989519d74cf4fd0 = (0, $7soKB$z).object({
|
|
28
|
+
name: (0, $7soKB$z).string().toLowerCase(),
|
|
29
|
+
type: (0, $7soKB$z).string(),
|
|
30
|
+
scale: (0, $7soKB$z).number().optional(),
|
|
31
|
+
unit: (0, $7soKB$z).string().optional()
|
|
32
|
+
});
|
|
33
|
+
const $0ff6a7cfdd9baaf1$export$13526d3947c5c7c2 = (0, $7soKB$z).object({
|
|
34
|
+
translation: (0, $7soKB$z).number().array().min(4).max(5),
|
|
35
|
+
type: (0, $7soKB$z).literal('translation')
|
|
36
|
+
});
|
|
37
|
+
const $0ff6a7cfdd9baaf1$export$f0f448c9214bb1a1 = (0, $7soKB$z).object({
|
|
38
|
+
scale: (0, $7soKB$z).number().array().min(4).max(5),
|
|
39
|
+
type: (0, $7soKB$z).literal('scale')
|
|
40
|
+
});
|
|
41
|
+
const $0ff6a7cfdd9baaf1$export$2e1c7be8e43a67e3 = (0, $7soKB$z).discriminatedUnion('type', [
|
|
42
|
+
$0ff6a7cfdd9baaf1$export$13526d3947c5c7c2,
|
|
43
|
+
$0ff6a7cfdd9baaf1$export$f0f448c9214bb1a1
|
|
44
|
+
]);
|
|
45
|
+
const $0ff6a7cfdd9baaf1$export$a535e1d8aa90b3c9 = (0, $7soKB$z).object({
|
|
46
|
+
coordinateTransformations: $0ff6a7cfdd9baaf1$export$2e1c7be8e43a67e3.array().nonempty(),
|
|
47
|
+
path: (0, $7soKB$z).string()
|
|
48
|
+
});
|
|
49
|
+
const $0ff6a7cfdd9baaf1$export$d7d6a65692bb1dc0 = (0, $7soKB$z).object({
|
|
50
|
+
name: (0, $7soKB$z).string(),
|
|
51
|
+
version: (0, $7soKB$z).string().optional(),
|
|
52
|
+
type: (0, $7soKB$z).string().optional(),
|
|
53
|
+
axes: $0ff6a7cfdd9baaf1$export$9989519d74cf4fd0.array().nonempty(),
|
|
54
|
+
datasets: $0ff6a7cfdd9baaf1$export$a535e1d8aa90b3c9.array().nonempty()
|
|
55
|
+
});
|
|
56
|
+
const $0ff6a7cfdd9baaf1$export$90729eda236ba57 = (0, $7soKB$z).object({
|
|
57
|
+
min: (0, $7soKB$z).number(),
|
|
58
|
+
start: (0, $7soKB$z).number(),
|
|
59
|
+
end: (0, $7soKB$z).number(),
|
|
60
|
+
max: (0, $7soKB$z).number()
|
|
61
|
+
});
|
|
62
|
+
const $0ff6a7cfdd9baaf1$export$bb4e42bb52e6d21d = (0, $7soKB$z).object({
|
|
63
|
+
active: (0, $7soKB$z).boolean().optional(),
|
|
64
|
+
color: (0, $7soKB$z).string(),
|
|
65
|
+
label: (0, $7soKB$z).string().optional(),
|
|
66
|
+
window: $0ff6a7cfdd9baaf1$export$90729eda236ba57
|
|
67
|
+
});
|
|
68
|
+
const $0ff6a7cfdd9baaf1$export$51e803dddccc5479 = (0, $7soKB$z).object({
|
|
69
|
+
channels: $0ff6a7cfdd9baaf1$export$bb4e42bb52e6d21d.array().nonempty()
|
|
70
|
+
});
|
|
71
|
+
const $0ff6a7cfdd9baaf1$export$fd8824855129d20 = (0, $7soKB$z).object({
|
|
72
|
+
multiscales: $0ff6a7cfdd9baaf1$export$d7d6a65692bb1dc0.array().nonempty(),
|
|
73
|
+
omero: $0ff6a7cfdd9baaf1$export$51e803dddccc5479.optional()
|
|
74
|
+
});
|
|
75
|
+
const $0ff6a7cfdd9baaf1$export$abb89b3cbbc08888 = $0ff6a7cfdd9baaf1$export$fd8824855129d20;
|
|
76
|
+
const $0ff6a7cfdd9baaf1$export$4a10f5b26a59ff84 = (0, $7soKB$z).object({
|
|
77
|
+
ome: $0ff6a7cfdd9baaf1$export$fd8824855129d20
|
|
78
|
+
});
|
|
79
|
+
const $0ff6a7cfdd9baaf1$export$a3b48a0a4f3d252 = (0, $7soKB$z).union([
|
|
80
|
+
$0ff6a7cfdd9baaf1$export$abb89b3cbbc08888,
|
|
81
|
+
$0ff6a7cfdd9baaf1$export$4a10f5b26a59ff84
|
|
82
|
+
]).transform((v)=>{
|
|
83
|
+
if ('ome' in v) return {
|
|
84
|
+
zarrVersion: 3,
|
|
85
|
+
...v.ome
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
zarrVersion: 2,
|
|
89
|
+
...v
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
function $0ff6a7cfdd9baaf1$export$ac32486ec3a6add8(omero) {
|
|
93
|
+
return omero.channels.map($0ff6a7cfdd9baaf1$export$9b3c6a55077c31a1);
|
|
94
|
+
}
|
|
95
|
+
function $0ff6a7cfdd9baaf1$export$9b3c6a55077c31a1(omeroChannel) {
|
|
96
|
+
const active = omeroChannel.active;
|
|
97
|
+
const label = omeroChannel.label;
|
|
98
|
+
const rgba = (0, $7soKB$makeRGBAColorVector)(omeroChannel.color);
|
|
99
|
+
const rgb = [
|
|
100
|
+
rgba[0],
|
|
101
|
+
rgba[1],
|
|
102
|
+
rgba[2]
|
|
103
|
+
];
|
|
104
|
+
const { min: winMin, max: winMax } = omeroChannel.window;
|
|
105
|
+
const { start: ranMin, end: ranMax } = omeroChannel.window;
|
|
106
|
+
const window = {
|
|
107
|
+
min: winMin,
|
|
108
|
+
max: winMax
|
|
109
|
+
};
|
|
110
|
+
const range = {
|
|
111
|
+
min: ranMin,
|
|
112
|
+
max: ranMax
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
rgb: rgb,
|
|
116
|
+
rgba: rgba,
|
|
117
|
+
window: window,
|
|
118
|
+
range: range,
|
|
119
|
+
active: active,
|
|
120
|
+
label: label
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
class $0ff6a7cfdd9baaf1$export$c7c266bed5c41e68 {
|
|
124
|
+
#url;
|
|
125
|
+
#attrs;
|
|
126
|
+
#arrays;
|
|
127
|
+
#zarrVersion;
|
|
128
|
+
constructor(url, attrs, arrays, zarrVersion){
|
|
129
|
+
this.#url = url;
|
|
130
|
+
this.#attrs = attrs;
|
|
131
|
+
this.#arrays = arrays;
|
|
132
|
+
this.#zarrVersion = zarrVersion;
|
|
133
|
+
}
|
|
134
|
+
get url() {
|
|
135
|
+
return this.#url;
|
|
136
|
+
}
|
|
137
|
+
get attrs() {
|
|
138
|
+
return this.#attrs;
|
|
139
|
+
}
|
|
140
|
+
get arrays() {
|
|
141
|
+
return this.#arrays;
|
|
142
|
+
}
|
|
143
|
+
get zarrVersion() {
|
|
144
|
+
return this.#zarrVersion;
|
|
145
|
+
}
|
|
146
|
+
toJSON() {
|
|
147
|
+
return {
|
|
148
|
+
url: this.url,
|
|
149
|
+
attrs: this.attrs,
|
|
150
|
+
arrays: this.arrays,
|
|
151
|
+
zarrVersion: this.zarrVersion,
|
|
152
|
+
colorChannels: this.colorChannels,
|
|
153
|
+
redChannel: this.redChannel,
|
|
154
|
+
blueChannel: this.blueChannel,
|
|
155
|
+
greenChannel: this.greenChannel
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
#getMultiscaleIndex(multiscale) {
|
|
159
|
+
if (multiscale !== undefined) {
|
|
160
|
+
if (typeof multiscale === 'number') {
|
|
161
|
+
if (multiscale < 0) return -1;
|
|
162
|
+
return multiscale;
|
|
163
|
+
}
|
|
164
|
+
return this.#attrs.multiscales.findIndex((m)=>m.name === multiscale);
|
|
165
|
+
}
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
#getValidMultiscaleIndex(multiscale) {
|
|
169
|
+
const multiscaleIndex = this.#getMultiscaleIndex(multiscale);
|
|
170
|
+
if (multiscaleIndex < 0) {
|
|
171
|
+
const message = `invalid multiscale requested: identifier [${multiscale}]`;
|
|
172
|
+
(0, $7soKB$logger).error(message);
|
|
173
|
+
throw new (0, $8af220877f5aad67$export$d7f857358a0dbda6)(message);
|
|
174
|
+
}
|
|
175
|
+
return multiscaleIndex;
|
|
176
|
+
}
|
|
177
|
+
#getDatasetIndex(dataset, multiscaleIndex) {
|
|
178
|
+
const datasets = this.#attrs.multiscales[multiscaleIndex]?.datasets ?? null;
|
|
179
|
+
if (!datasets) return -1;
|
|
180
|
+
if (typeof dataset === 'number') {
|
|
181
|
+
if (dataset < 0 || dataset >= datasets.length) return -1;
|
|
182
|
+
return dataset;
|
|
183
|
+
}
|
|
184
|
+
return datasets.findIndex((d)=>d.path === dataset);
|
|
185
|
+
}
|
|
186
|
+
#getValidDatasetIndex(dataset, multiscaleIndex) {
|
|
187
|
+
const datasetIndex = this.#getDatasetIndex(dataset, multiscaleIndex);
|
|
188
|
+
if (datasetIndex < 0) {
|
|
189
|
+
const message = `invalid dataset requested: identifier [${dataset}]`;
|
|
190
|
+
(0, $7soKB$logger).error(message);
|
|
191
|
+
throw new (0, $8af220877f5aad67$export$d7f857358a0dbda6)(message);
|
|
192
|
+
}
|
|
193
|
+
return datasetIndex;
|
|
194
|
+
}
|
|
195
|
+
/** Private function that retrieves the X value from the `shape` of a given array, within a
|
|
196
|
+
* specific multiscale representation of the data.
|
|
197
|
+
*/ #getShapeX(array, multiscaleIndex) {
|
|
198
|
+
const shape = array.shape;
|
|
199
|
+
if (!shape || shape.length < 4) {
|
|
200
|
+
const message = `invalid dataset: .zarray formatting invalid, found array without valid shape; path [${multiscaleIndex}/${array.path}]`;
|
|
201
|
+
(0, $7soKB$logger).error(message);
|
|
202
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
203
|
+
}
|
|
204
|
+
const shapeIndex = shape.length === 5 ? $0ff6a7cfdd9baaf1$var$SHAPE_X_DIM_INDEX + 1 : $0ff6a7cfdd9baaf1$var$SHAPE_X_DIM_INDEX;
|
|
205
|
+
return shape[shapeIndex];
|
|
206
|
+
}
|
|
207
|
+
/** Private function that retrieves the Y value from the `shape` of a given array, within a
|
|
208
|
+
* specific multiscale representation of the data.
|
|
209
|
+
*/ #getShapeY(array, multiscaleIndex) {
|
|
210
|
+
const shape = array.shape;
|
|
211
|
+
if (!shape || shape.length < 4) {
|
|
212
|
+
const message = `invalid dataset: .zarray formatting invalid, found array without valid shape; path [${multiscaleIndex}/${array.path}]`;
|
|
213
|
+
(0, $7soKB$logger).error(message);
|
|
214
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
215
|
+
}
|
|
216
|
+
const shapeIndex = shape.length === 5 ? $0ff6a7cfdd9baaf1$var$SHAPE_Y_DIM_INDEX + 1 : $0ff6a7cfdd9baaf1$var$SHAPE_Y_DIM_INDEX;
|
|
217
|
+
return shape[shapeIndex];
|
|
218
|
+
}
|
|
219
|
+
/** Private function that retrieves the Z value from the `shape` of a given array, within a
|
|
220
|
+
* specific multiscale representation of the data.
|
|
221
|
+
*/ #getShapeZ(array, multiscaleIndex) {
|
|
222
|
+
const shape = array.shape;
|
|
223
|
+
if (!shape || shape.length < 4) {
|
|
224
|
+
const message = `invalid dataset: .zarray formatting invalid, found array without valid shape; path [${multiscaleIndex}/${array.path}]`;
|
|
225
|
+
(0, $7soKB$logger).error(message);
|
|
226
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
227
|
+
}
|
|
228
|
+
// This checks to see if the shape provided has all 5 official OME-Zarr dimensions (t, c, z, y, x),
|
|
229
|
+
// or just the 4 that we typically have in our data files (c, z, y, x)
|
|
230
|
+
const shapeIndex = shape.length === 5 ? $0ff6a7cfdd9baaf1$var$SHAPE_Z_DIM_INDEX + 1 : $0ff6a7cfdd9baaf1$var$SHAPE_Z_DIM_INDEX;
|
|
231
|
+
return shape[shapeIndex];
|
|
232
|
+
}
|
|
233
|
+
/** Private function to retrieve the maximum value for a given shape element, e.g.
|
|
234
|
+
* the maximum value of one of the dimensions (t, c, z, y, x). It compares across all
|
|
235
|
+
* the values of that dimension for all zarrays/datasets within a given multiscale
|
|
236
|
+
* representation of the data.
|
|
237
|
+
*
|
|
238
|
+
* Note: Typically, we only receive the last 4 elements in the `shape` of a zarray.
|
|
239
|
+
*
|
|
240
|
+
* @param getShapeElement a function that retrieves one element from the `shape` of
|
|
241
|
+
* a zarray
|
|
242
|
+
* @returns the maxium value of that element across all arrays within the given
|
|
243
|
+
* multiscale representation
|
|
244
|
+
*/ #getShapeElementMax(getShapeElement, multiscale) {
|
|
245
|
+
const multiscaleIndex = this.#getValidMultiscaleIndex(multiscale);
|
|
246
|
+
return this.#attrs.multiscales[multiscaleIndex].datasets.map((dataset)=>{
|
|
247
|
+
const array = this.#arrays.find((a)=>a.path === dataset.path);
|
|
248
|
+
if (!array) {
|
|
249
|
+
const message = `invalid dataset: .zarray missing for dataset [${multiscaleIndex}/${dataset.path}]`;
|
|
250
|
+
(0, $7soKB$logger).error(message);
|
|
251
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
252
|
+
}
|
|
253
|
+
return getShapeElement(array, multiscaleIndex);
|
|
254
|
+
}).reduce((prev, curr)=>Math.max(prev, curr));
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Given a specific @param multiscale representation of the Zarr data, finds the
|
|
258
|
+
* largest X shape component among the shapes of the different dataset arrays.
|
|
259
|
+
* @param multiscale the index or path of a specific multiscale representation (defaults to 0)
|
|
260
|
+
* @returns the largest Z scale for the specified multiscale representation
|
|
261
|
+
*/ maxX(multiscale = 0) {
|
|
262
|
+
return this.#getShapeElementMax(this.#getShapeX, multiscale);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Given a specific @param multiscale representation of the Zarr data, finds the
|
|
266
|
+
* largest Y shape component among the shapes of the different dataset arrays.
|
|
267
|
+
* @param multiscale the index or path of a specific multiscale representation (defaults to 0)
|
|
268
|
+
* @returns the largest Z scale for the specified multiscale representation
|
|
269
|
+
*/ maxY(multiscale = 0) {
|
|
270
|
+
return this.#getShapeElementMax(this.#getShapeY, multiscale);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Given a specific @param multiscale representation of the Zarr data, finds the
|
|
274
|
+
* largest Z shape component among the shapes of the different dataset arrays.
|
|
275
|
+
* @param multiscale the index or path of a specific multiscale representation (defaults to 0)
|
|
276
|
+
* @returns the largest Z scale for the specified multiscale representation
|
|
277
|
+
*/ maxZ(multiscale = 0) {
|
|
278
|
+
return this.#getShapeElementMax(this.#getShapeZ, multiscale);
|
|
279
|
+
}
|
|
280
|
+
maxOrthogonal(plane, multiscale = 0) {
|
|
281
|
+
if (plane.ortho === 'x') return this.maxX(multiscale);
|
|
282
|
+
if (plane.ortho === 'y') return this.maxY(multiscale);
|
|
283
|
+
if (plane.ortho === 'z') return this.maxZ(multiscale);
|
|
284
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(`invalid plane: ortho set to '${plane.ortho}'`);
|
|
285
|
+
}
|
|
286
|
+
#makeShapedDataset(dataset, multiscaleIndex, datasetIndex) {
|
|
287
|
+
const array = this.#arrays.find((a)=>a.path === dataset.path);
|
|
288
|
+
if (!array) {
|
|
289
|
+
const message = `invalid dataset: .zarray missing for dataset [${multiscaleIndex}][${datasetIndex}]`;
|
|
290
|
+
(0, $7soKB$logger).error(message);
|
|
291
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
...dataset,
|
|
295
|
+
shape: array.shape,
|
|
296
|
+
multiscaleIndex: multiscaleIndex,
|
|
297
|
+
datasetIndex: datasetIndex
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
getShapedDataset(indexOrPath, multiscale = 0) {
|
|
301
|
+
try {
|
|
302
|
+
const multiscaleIndex = this.#getValidMultiscaleIndex(multiscale);
|
|
303
|
+
const datasetIndex = this.#getValidDatasetIndex(indexOrPath, multiscaleIndex);
|
|
304
|
+
const dataset = this.#attrs.multiscales[multiscaleIndex].datasets[datasetIndex];
|
|
305
|
+
return this.#makeShapedDataset(dataset, multiscaleIndex, datasetIndex);
|
|
306
|
+
} catch (e) {
|
|
307
|
+
if (e instanceof (0, $8af220877f5aad67$export$d7f857358a0dbda6)) {
|
|
308
|
+
(0, $7soKB$logger).debug('encountered index error when retrieving shaped dataset; returning undefined');
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
throw e;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
getFirstShapedDataset(multiscale = 0) {
|
|
315
|
+
let multiscaleIndex;
|
|
316
|
+
try {
|
|
317
|
+
multiscaleIndex = this.#getValidMultiscaleIndex(multiscale);
|
|
318
|
+
const dataset = this.#attrs.multiscales[multiscaleIndex].datasets[0];
|
|
319
|
+
return this.#makeShapedDataset(dataset, multiscaleIndex, 0);
|
|
320
|
+
} catch (e) {
|
|
321
|
+
if (e instanceof (0, $8af220877f5aad67$export$d7f857358a0dbda6)) {
|
|
322
|
+
(0, $7soKB$logger).debug('encountered index error when retrieving shaped dataset; returning undefined');
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
throw e;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
getLastShapedDataset(multiscale = 0) {
|
|
329
|
+
let multiscaleIndex;
|
|
330
|
+
try {
|
|
331
|
+
multiscaleIndex = this.#getValidMultiscaleIndex(multiscale);
|
|
332
|
+
const datasets = this.#attrs.multiscales[multiscaleIndex].datasets;
|
|
333
|
+
const dataset = datasets[datasets.length - 1];
|
|
334
|
+
return this.#makeShapedDataset(dataset, multiscaleIndex, 0);
|
|
335
|
+
} catch (e) {
|
|
336
|
+
if (e instanceof (0, $8af220877f5aad67$export$d7f857358a0dbda6)) {
|
|
337
|
+
(0, $7soKB$logger).debug('encountered index error when retrieving shaped dataset; returning undefined');
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
throw e;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
getNumLayers(multiscale = 0) {
|
|
344
|
+
const multiscaleIndex = this.#getValidMultiscaleIndex(multiscale);
|
|
345
|
+
return this.#attrs.multiscales[multiscaleIndex].datasets.length;
|
|
346
|
+
}
|
|
347
|
+
getAllShapedDatasets(multiscale = 0) {
|
|
348
|
+
const multiscaleIndex = this.#getValidMultiscaleIndex(multiscale);
|
|
349
|
+
const datasets = this.#attrs.multiscales[multiscaleIndex].datasets;
|
|
350
|
+
return datasets.map((dataset, i)=>this.#makeShapedDataset(dataset, multiscaleIndex, i));
|
|
351
|
+
}
|
|
352
|
+
dehydrate() {
|
|
353
|
+
return {
|
|
354
|
+
url: this.#url,
|
|
355
|
+
attrs: this.#attrs,
|
|
356
|
+
arrays: [
|
|
357
|
+
...this.#arrays
|
|
358
|
+
],
|
|
359
|
+
zarrVersion: this.#zarrVersion
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
static async rehydrate(dehydrated) {
|
|
363
|
+
const { url: url, attrs: attrs, arrays: arrays, zarrVersion: zarrVersion } = dehydrated;
|
|
364
|
+
return new $0ff6a7cfdd9baaf1$export$c7c266bed5c41e68(url, attrs, arrays, zarrVersion);
|
|
365
|
+
}
|
|
366
|
+
#getChannelByMask(colorMask) {
|
|
367
|
+
if (!this.#attrs.omero || !this.#attrs.omero.channels) {
|
|
368
|
+
(0, $7soKB$logger).debug(`no omero data found for color mask ${colorMask}, returning undefined`);
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
const omeroChannel = this.#attrs.omero.channels.find((ch)=>ch.color === colorMask);
|
|
372
|
+
if (!omeroChannel) {
|
|
373
|
+
(0, $7soKB$logger).debug(`no matching omero channel found for color mask ${colorMask}, returning undefined`);
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
return $0ff6a7cfdd9baaf1$export$9b3c6a55077c31a1(omeroChannel);
|
|
377
|
+
}
|
|
378
|
+
get colorChannels() {
|
|
379
|
+
return this.#attrs.omero ? $0ff6a7cfdd9baaf1$export$ac32486ec3a6add8(this.#attrs.omero) : [];
|
|
380
|
+
}
|
|
381
|
+
get redChannel() {
|
|
382
|
+
return this.#getChannelByMask('#FF0000');
|
|
383
|
+
}
|
|
384
|
+
get greenChannel() {
|
|
385
|
+
return this.#getChannelByMask('#00FF00');
|
|
386
|
+
}
|
|
387
|
+
get blueChannel() {
|
|
388
|
+
return this.#getChannelByMask('#0000FF');
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
async function $62574ebf548584b6$export$78b8536ed7d8a60d(res) {
|
|
396
|
+
const url = (0, $7soKB$getResourceUrl)(res);
|
|
397
|
+
const store = new $7soKB$FetchStore(url);
|
|
398
|
+
return $62574ebf548584b6$var$loadZarrAttrsFileFromStore(store);
|
|
399
|
+
}
|
|
400
|
+
async function $62574ebf548584b6$var$loadZarrAttrsFileFromStore(store) {
|
|
401
|
+
const group = await $7soKB$open(store, {
|
|
402
|
+
kind: 'group'
|
|
403
|
+
});
|
|
404
|
+
try {
|
|
405
|
+
return (0, $0ff6a7cfdd9baaf1$export$a3b48a0a4f3d252).parse(group.attrs);
|
|
406
|
+
} catch (e) {
|
|
407
|
+
if (e instanceof (0, $7soKB$ZodError)) (0, $7soKB$logger).error('could not load Zarr file: parsing failed');
|
|
408
|
+
throw e;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function $62574ebf548584b6$export$7f36aeab32ab33f7(res, path, version = 2, loadV2Attrs = true) {
|
|
412
|
+
const url = (0, $7soKB$getResourceUrl)(res);
|
|
413
|
+
const store = new $7soKB$FetchStore(url);
|
|
414
|
+
const result = await $62574ebf548584b6$export$ab574f5010f64a15(store, path, version, loadV2Attrs);
|
|
415
|
+
return result.metadata;
|
|
416
|
+
}
|
|
417
|
+
async function $62574ebf548584b6$export$ab574f5010f64a15(store, path, version = 2, loadV2Attrs = true) {
|
|
418
|
+
const root = $7soKB$root(store);
|
|
419
|
+
let array;
|
|
420
|
+
if (version === 3) array = await $7soKB$open.v3(root.resolve(path), {
|
|
421
|
+
kind: 'array'
|
|
422
|
+
});
|
|
423
|
+
else if (version === 2) array = await $7soKB$open.v2(root.resolve(path), {
|
|
424
|
+
kind: 'array',
|
|
425
|
+
attrs: loadV2Attrs
|
|
426
|
+
});
|
|
427
|
+
else {
|
|
428
|
+
const message = `unsupported Zarr format version specified: ${version}`;
|
|
429
|
+
(0, $7soKB$logger).error(message);
|
|
430
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
431
|
+
}
|
|
432
|
+
const { shape: shape, attrs: attrs } = array;
|
|
433
|
+
try {
|
|
434
|
+
return {
|
|
435
|
+
metadata: {
|
|
436
|
+
path: path,
|
|
437
|
+
shape: shape,
|
|
438
|
+
attrs: attrs
|
|
439
|
+
},
|
|
440
|
+
raw: array
|
|
441
|
+
};
|
|
442
|
+
} catch (e) {
|
|
443
|
+
if (e instanceof (0, $7soKB$ZodError)) (0, $7soKB$logger).error('could not load Zarr file: parsing failed');
|
|
444
|
+
throw e;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async function $62574ebf548584b6$export$7999b0bb51b3dcb8(res, loadV2ArrayAttrs = true) {
|
|
448
|
+
const url = (0, $7soKB$getResourceUrl)(res);
|
|
449
|
+
const store = new $7soKB$FetchStore(url);
|
|
450
|
+
const attrs = await $62574ebf548584b6$var$loadZarrAttrsFileFromStore(store);
|
|
451
|
+
const version = attrs.zarrVersion;
|
|
452
|
+
const arrays = await Promise.all(attrs.multiscales.map((multiscale)=>{
|
|
453
|
+
return multiscale.datasets?.map(async (dataset)=>{
|
|
454
|
+
return (await $62574ebf548584b6$export$ab574f5010f64a15(store, dataset.path, version, loadV2ArrayAttrs)).metadata;
|
|
455
|
+
}) ?? [];
|
|
456
|
+
}).reduce((prev, curr)=>prev.concat(curr)).filter((v)=>v !== undefined));
|
|
457
|
+
return new (0, $0ff6a7cfdd9baaf1$export$c7c266bed5c41e68)(url, attrs, arrays, version);
|
|
458
|
+
}
|
|
459
|
+
function $62574ebf548584b6$export$8efeb0367ee93033(zarr, plane, relativeView, displayResolution) {
|
|
460
|
+
const datasets = zarr.getAllShapedDatasets(0);
|
|
461
|
+
const axes = zarr.attrs.multiscales[0].axes;
|
|
462
|
+
const firstDataset = datasets[0];
|
|
463
|
+
if (!firstDataset) {
|
|
464
|
+
const message = 'invalid Zarr data: no datasets found';
|
|
465
|
+
(0, $7soKB$logger).error(message);
|
|
466
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
467
|
+
}
|
|
468
|
+
const realSize = $62574ebf548584b6$export$c5890dae76bd0ad7(plane, axes, firstDataset);
|
|
469
|
+
if (!realSize) {
|
|
470
|
+
const message = 'invalid Zarr data: could not determine the size of the plane in the given units';
|
|
471
|
+
(0, $7soKB$logger).error(message);
|
|
472
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
473
|
+
}
|
|
474
|
+
const vxlPitch = (size)=>(0, $7soKB$Vec2).div(realSize, size);
|
|
475
|
+
// size, in dataspace, of a pixel 1/res
|
|
476
|
+
const pxPitch = (0, $7soKB$Vec2).div((0, $7soKB$Box2D).size(relativeView), displayResolution);
|
|
477
|
+
const dstToDesired = (a, goal)=>{
|
|
478
|
+
const diff = (0, $7soKB$Vec2).sub(a, goal);
|
|
479
|
+
if (diff[0] * diff[1] > 0) // the res (a) is higher than our goal -
|
|
480
|
+
// weight this heavily to prefer smaller than the goal
|
|
481
|
+
return 1000 * (0, $7soKB$Vec2).length((0, $7soKB$Vec2).sub(a, goal));
|
|
482
|
+
return (0, $7soKB$Vec2).length((0, $7soKB$Vec2).sub(a, goal));
|
|
483
|
+
};
|
|
484
|
+
// we assume the datasets are ordered... hmmm TODO
|
|
485
|
+
const choice = datasets.reduce((bestSoFar, cur)=>{
|
|
486
|
+
const planeSizeBest = $62574ebf548584b6$export$ba17f86721d4d0ce(plane, axes, bestSoFar);
|
|
487
|
+
const planeSizeCur = $62574ebf548584b6$export$ba17f86721d4d0ce(plane, axes, cur);
|
|
488
|
+
if (!planeSizeBest || !planeSizeCur) return bestSoFar;
|
|
489
|
+
return dstToDesired(vxlPitch(planeSizeBest), pxPitch) > dstToDesired(vxlPitch(planeSizeCur), pxPitch) ? cur : bestSoFar;
|
|
490
|
+
}, datasets[0]);
|
|
491
|
+
return choice ?? datasets[datasets.length - 1];
|
|
492
|
+
}
|
|
493
|
+
// TODO this is a duplicate of indexOfDimension... delete one of them!
|
|
494
|
+
function $62574ebf548584b6$var$indexFor(dim, axes) {
|
|
495
|
+
return axes.findIndex((axis)=>axis.name === dim);
|
|
496
|
+
}
|
|
497
|
+
function $62574ebf548584b6$export$81eb4b4b765d519(layer, axes, parameter, dim) {
|
|
498
|
+
const dimIndex = $62574ebf548584b6$var$indexFor(dim, axes);
|
|
499
|
+
return Math.floor(layer.shape[dimIndex] * Math.max(0, Math.min(1, parameter)));
|
|
500
|
+
}
|
|
501
|
+
function $62574ebf548584b6$export$ca023b1dd6252668(zarr, plane, relativeView, displayResolution) {
|
|
502
|
+
// figure out what layer we'd be viewing
|
|
503
|
+
const layer = $62574ebf548584b6$export$8efeb0367ee93033(zarr, plane, relativeView, displayResolution);
|
|
504
|
+
const axes = zarr.attrs.multiscales[0].axes;
|
|
505
|
+
const slices = $62574ebf548584b6$export$fcb1a44ca126ee5c(plane.ortho, axes, layer);
|
|
506
|
+
return slices === undefined ? undefined : 1 / slices;
|
|
507
|
+
}
|
|
508
|
+
function $62574ebf548584b6$export$c5890dae76bd0ad7(plane, axes, dataset) {
|
|
509
|
+
const vxls = $62574ebf548584b6$export$ba17f86721d4d0ce(plane, axes, dataset);
|
|
510
|
+
if (vxls === undefined) return undefined;
|
|
511
|
+
let size = vxls;
|
|
512
|
+
// now, just apply the correct transforms, if they exist...
|
|
513
|
+
for (const trn of dataset.coordinateTransformations)if (trn.type === 'scale') {
|
|
514
|
+
// try to apply it!
|
|
515
|
+
const uIndex = $62574ebf548584b6$var$indexFor(plane.u, axes);
|
|
516
|
+
const vIndex = $62574ebf548584b6$var$indexFor(plane.v, axes);
|
|
517
|
+
size = (0, $7soKB$Vec2).mul(size, [
|
|
518
|
+
trn.scale[uIndex],
|
|
519
|
+
trn.scale[vIndex]
|
|
520
|
+
]);
|
|
521
|
+
}
|
|
522
|
+
return size;
|
|
523
|
+
}
|
|
524
|
+
function $62574ebf548584b6$export$fcb1a44ca126ee5c(dim, axes, dataset) {
|
|
525
|
+
const uI = $62574ebf548584b6$var$indexFor(dim, axes);
|
|
526
|
+
if (uI === -1) return undefined;
|
|
527
|
+
return dataset.shape[uI];
|
|
528
|
+
}
|
|
529
|
+
function $62574ebf548584b6$export$ba17f86721d4d0ce(plane, axes, dataset) {
|
|
530
|
+
// first - u&v must not refer to the same dimension,
|
|
531
|
+
// and both should exist in the axes...
|
|
532
|
+
if (!plane.isValid()) return undefined;
|
|
533
|
+
const uI = $62574ebf548584b6$var$indexFor(plane.u, axes);
|
|
534
|
+
const vI = $62574ebf548584b6$var$indexFor(plane.v, axes);
|
|
535
|
+
if (uI === -1 || vI === -1) return undefined;
|
|
536
|
+
return [
|
|
537
|
+
dataset.shape[uI],
|
|
538
|
+
dataset.shape[vI]
|
|
539
|
+
];
|
|
540
|
+
}
|
|
541
|
+
function $62574ebf548584b6$export$d7b58f10109feb02(r, axes, shape) {
|
|
542
|
+
const ordered = axes.map((a)=>r[a.name]);
|
|
543
|
+
// if any are undefined, throw up
|
|
544
|
+
if (ordered.some((a)=>a === undefined)) throw new (0, $8af220877f5aad67$export$e819e674f1e05236)('request does not match expected dimensions of OME-Zarr dataset');
|
|
545
|
+
return ordered.map((d, i)=>{
|
|
546
|
+
const bounds = {
|
|
547
|
+
min: 0,
|
|
548
|
+
max: shape[i]
|
|
549
|
+
};
|
|
550
|
+
if (d === null) return d;
|
|
551
|
+
if (typeof d === 'number') return (0, $7soKB$limit)(bounds, d);
|
|
552
|
+
return $7soKB$slice((0, $7soKB$limit)(bounds, d.min), (0, $7soKB$limit)(bounds, d.max));
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
async function $62574ebf548584b6$export$151d38946e654182(z) {
|
|
556
|
+
(0, $7soKB$logger).dir(z);
|
|
557
|
+
}
|
|
558
|
+
async function $62574ebf548584b6$export$831e2eeffaa681(metadata, r, level, signal) {
|
|
559
|
+
// put the request in native order
|
|
560
|
+
const store = new $7soKB$FetchStore(metadata.url);
|
|
561
|
+
const scene = metadata.attrs.multiscales[0];
|
|
562
|
+
const { axes: axes } = scene;
|
|
563
|
+
if (!level) {
|
|
564
|
+
const message = 'invalid Zarr data: no datasets found';
|
|
565
|
+
(0, $7soKB$logger).error(message);
|
|
566
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
567
|
+
}
|
|
568
|
+
const arr = metadata.arrays.find((a)=>a.path === level.path);
|
|
569
|
+
if (!arr) {
|
|
570
|
+
const message = `cannot load slice: no array found for path [${level.path}]`;
|
|
571
|
+
(0, $7soKB$logger).error(message);
|
|
572
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
573
|
+
}
|
|
574
|
+
const { raw: raw } = await $62574ebf548584b6$export$ab574f5010f64a15(store, arr.path, metadata.zarrVersion, false);
|
|
575
|
+
const result = await $7soKB$get(raw, $62574ebf548584b6$export$d7b58f10109feb02(r, axes, level.shape), {
|
|
576
|
+
opts: {
|
|
577
|
+
signal: signal ?? null
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
if (typeof result === 'number') throw new Error('oh noes, slice came back all weird');
|
|
581
|
+
return {
|
|
582
|
+
shape: result.shape,
|
|
583
|
+
buffer: result
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* given a image with @param size pixels, break it into tiles, each @param idealTilePx.
|
|
590
|
+
* for all such tiles which intersect the given bounds, call the visitor
|
|
591
|
+
* @param idealTilePx the size of a tile, in pixels
|
|
592
|
+
* @param size the size of the image at this level of detail
|
|
593
|
+
* @param bounds visit only the tiles that are within the given bounds (in pixels)
|
|
594
|
+
*/ function $4a90fbb98bec80dd$var$visitTilesWithin(idealTilePx, size, bounds, visit) {
|
|
595
|
+
const withinBoth = (0, $7soKB$Box2D).intersection(bounds, (0, $7soKB$Box2D).create([
|
|
596
|
+
0,
|
|
597
|
+
0
|
|
598
|
+
], size));
|
|
599
|
+
if (!withinBoth) return;
|
|
600
|
+
// convert the image into tile indexes:
|
|
601
|
+
const boundsInTiles = (0, $7soKB$Box2D).map(withinBoth, (corner)=>(0, $7soKB$Vec2).div(corner, idealTilePx));
|
|
602
|
+
for(let x = Math.floor(boundsInTiles.minCorner[0]); x < Math.ceil(boundsInTiles.maxCorner[0]); x += 1)for(let y = Math.floor(boundsInTiles.minCorner[1]); y < Math.ceil(boundsInTiles.maxCorner[1]); y += 1){
|
|
603
|
+
// all tiles visited are always within both the bounds, and the image itself
|
|
604
|
+
const lo = (0, $7soKB$Vec2).mul([
|
|
605
|
+
x,
|
|
606
|
+
y
|
|
607
|
+
], idealTilePx);
|
|
608
|
+
const hi = (0, $7soKB$Vec2).min(size, (0, $7soKB$Vec2).add(lo, idealTilePx));
|
|
609
|
+
visit((0, $7soKB$Box2D).create(lo, hi));
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function $4a90fbb98bec80dd$var$getVisibleTilesInLayer(camera, plane, orthoVal, dataset, tileSize, level) {
|
|
613
|
+
const size = (0, $62574ebf548584b6$export$ba17f86721d4d0ce)(plane, dataset.attrs.multiscales[0].axes, level);
|
|
614
|
+
const realSize = (0, $62574ebf548584b6$export$c5890dae76bd0ad7)(plane, dataset.attrs.multiscales[0].axes, level);
|
|
615
|
+
if (!size || !realSize) return [];
|
|
616
|
+
const scale = (0, $7soKB$Vec2).div(realSize, size);
|
|
617
|
+
const vxlToReal = (vxl)=>(0, $7soKB$Box2D).scale(vxl, scale);
|
|
618
|
+
const realToVxl = (real)=>(0, $7soKB$Box2D).scale(real, (0, $7soKB$Vec2).div([
|
|
619
|
+
1,
|
|
620
|
+
1
|
|
621
|
+
], scale));
|
|
622
|
+
const visibleTiles = [];
|
|
623
|
+
$4a90fbb98bec80dd$var$visitTilesWithin([
|
|
624
|
+
tileSize,
|
|
625
|
+
tileSize
|
|
626
|
+
], size, realToVxl(camera.view), (uv)=>{
|
|
627
|
+
visibleTiles.push({
|
|
628
|
+
plane: plane.axes,
|
|
629
|
+
realBounds: vxlToReal(uv),
|
|
630
|
+
bounds: uv,
|
|
631
|
+
orthoVal: orthoVal,
|
|
632
|
+
level: level
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
return visibleTiles;
|
|
636
|
+
}
|
|
637
|
+
function $4a90fbb98bec80dd$export$2957f72eb8eb81d4(camera, plane, planeLocation, metadata, tileSize) {
|
|
638
|
+
// TODO (someday) open the array, look at its chunks, use that size for the size of the tiles I request!
|
|
639
|
+
const layer = (0, $62574ebf548584b6$export$8efeb0367ee93033)(metadata, plane, camera.view, camera.screenSize);
|
|
640
|
+
// figure out the index of the slice
|
|
641
|
+
const sliceIndex = (0, $62574ebf548584b6$export$81eb4b4b765d519)(layer, metadata.attrs.multiscales[0].axes, planeLocation, plane.ortho);
|
|
642
|
+
return $4a90fbb98bec80dd$var$getVisibleTilesInLayer(camera, plane, sliceIndex, metadata, tileSize, layer);
|
|
643
|
+
}
|
|
644
|
+
const $4a90fbb98bec80dd$export$e83f4de492e8b929 = (metadata, r, level, signal)=>{
|
|
645
|
+
return (0, $62574ebf548584b6$export$831e2eeffaa681)(metadata, r, level, signal).then((result)=>{
|
|
646
|
+
const { shape: shape, buffer: buffer } = result;
|
|
647
|
+
return {
|
|
648
|
+
shape: shape,
|
|
649
|
+
data: new Float32Array(buffer.data)
|
|
650
|
+
};
|
|
651
|
+
});
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
// render a slice of an ome-zarr file as a 2D image
|
|
656
|
+
// note that the ome-zarr data must have exactly 3 channels
|
|
657
|
+
// the channels may be mapped to color-channels (RGB) with a basic 2-post gamut control
|
|
658
|
+
/* ================ TILE RENDERING VERTEX SHADER ================ */ const $3e6603f747b5a8a4$var$tileVert = /*glsl*/ `
|
|
659
|
+
precision highp float;
|
|
660
|
+
attribute vec2 pos;
|
|
661
|
+
|
|
662
|
+
uniform vec4 view;
|
|
663
|
+
uniform vec4 tile;
|
|
664
|
+
uniform float depth;
|
|
665
|
+
varying vec2 texCoord;
|
|
666
|
+
uniform float rot;
|
|
667
|
+
|
|
668
|
+
void main(){
|
|
669
|
+
vec2 tileSize = tile.zw-tile.xy;
|
|
670
|
+
texCoord = pos;
|
|
671
|
+
vec2 obj = (pos.xy*tileSize+tile.xy);
|
|
672
|
+
|
|
673
|
+
vec2 p = (obj-view.xy)/(view.zw-view.xy);
|
|
674
|
+
// now, to clip space
|
|
675
|
+
p = (p*2.0)-1.0;
|
|
676
|
+
gl_Position = vec4(p.x,p.y,depth,1.0);
|
|
677
|
+
}
|
|
678
|
+
`;
|
|
679
|
+
/* -------------------------------------------------------------- */ /* ============= RGB TILE RENDERING FRAGMENT SHADER ============= */ const $3e6603f747b5a8a4$var$rgbFrag = /*glsl*/ `
|
|
680
|
+
precision highp float;
|
|
681
|
+
uniform sampler2D R;
|
|
682
|
+
uniform sampler2D G;
|
|
683
|
+
uniform sampler2D B;
|
|
684
|
+
// for reasons which are pretty annoying
|
|
685
|
+
// its more direct to do 3 separate channels...
|
|
686
|
+
uniform vec2 Rgamut;
|
|
687
|
+
uniform vec2 Ggamut;
|
|
688
|
+
uniform vec2 Bgamut;
|
|
689
|
+
varying vec2 texCoord;
|
|
690
|
+
|
|
691
|
+
void main(){
|
|
692
|
+
vec3 mins = vec3(Rgamut.x, Ggamut.x, Bgamut.x);
|
|
693
|
+
vec3 maxs = vec3(Rgamut.y, Ggamut.y, Bgamut.y);
|
|
694
|
+
vec3 span = maxs - mins;
|
|
695
|
+
vec3 color = (vec3(
|
|
696
|
+
texture2D(R, texCoord).r,
|
|
697
|
+
texture2D(G, texCoord).r,
|
|
698
|
+
texture2D(B, texCoord).r
|
|
699
|
+
) - mins) / span;
|
|
700
|
+
|
|
701
|
+
gl_FragColor = vec4(color, 1.0);
|
|
702
|
+
}
|
|
703
|
+
`;
|
|
704
|
+
function $3e6603f747b5a8a4$export$e1c4d8365a017448(regl) {
|
|
705
|
+
const cmd = regl({
|
|
706
|
+
vert: $3e6603f747b5a8a4$var$tileVert,
|
|
707
|
+
frag: $3e6603f747b5a8a4$var$rgbFrag,
|
|
708
|
+
framebuffer: regl.prop('target'),
|
|
709
|
+
attributes: {
|
|
710
|
+
pos: [
|
|
711
|
+
0,
|
|
712
|
+
0,
|
|
713
|
+
1,
|
|
714
|
+
0,
|
|
715
|
+
1,
|
|
716
|
+
1,
|
|
717
|
+
0,
|
|
718
|
+
1
|
|
719
|
+
]
|
|
720
|
+
},
|
|
721
|
+
uniforms: {
|
|
722
|
+
tile: regl.prop('tile'),
|
|
723
|
+
view: regl.prop('view'),
|
|
724
|
+
depth: regl.prop('depth'),
|
|
725
|
+
R: regl.prop('R'),
|
|
726
|
+
G: regl.prop('G'),
|
|
727
|
+
B: regl.prop('B'),
|
|
728
|
+
Rgamut: regl.prop('Rgamut'),
|
|
729
|
+
Ggamut: regl.prop('Ggamut'),
|
|
730
|
+
Bgamut: regl.prop('Bgamut')
|
|
731
|
+
},
|
|
732
|
+
depth: {
|
|
733
|
+
enable: true
|
|
734
|
+
},
|
|
735
|
+
count: 4,
|
|
736
|
+
primitive: 'triangle fan'
|
|
737
|
+
});
|
|
738
|
+
return (p)=>cmd(p);
|
|
739
|
+
}
|
|
740
|
+
function $3e6603f747b5a8a4$export$640f994018c81008(regl, numChannels) {
|
|
741
|
+
const reglChannelUniforms = [];
|
|
742
|
+
const fragmentChannelUniformDefs = [];
|
|
743
|
+
const colorMerges = [];
|
|
744
|
+
for(let i = 0; i < numChannels; i++){
|
|
745
|
+
reglChannelUniforms.push({
|
|
746
|
+
[`gamut${i}`]: (_context, props)=>props.channels[i].gamut,
|
|
747
|
+
[`color${i}`]: (_context, props)=>props.channels[i].rgb,
|
|
748
|
+
[`tex${i}`]: (_context, props)=>props.channels[i].tex
|
|
749
|
+
});
|
|
750
|
+
fragmentChannelUniformDefs.push(`uniform vec2 gamut${i};`);
|
|
751
|
+
fragmentChannelUniformDefs.push(`uniform vec3 color${i};`);
|
|
752
|
+
fragmentChannelUniformDefs.push(`uniform sampler2D tex${i};`);
|
|
753
|
+
colorMerges.push(`
|
|
754
|
+
float ch${i}Val = texture2D(tex${i}, texCoord).r;
|
|
755
|
+
ch${i}Val = (ch${i}Val - gamut${i}.x) / (gamut${i}.y - gamut${i}.x);
|
|
756
|
+
color += (color${i} * ch${i}Val);
|
|
757
|
+
`);
|
|
758
|
+
}
|
|
759
|
+
const staticReglUniforms = {
|
|
760
|
+
tile: regl.prop('tile'),
|
|
761
|
+
view: regl.prop('view'),
|
|
762
|
+
depth: regl.prop('depth')
|
|
763
|
+
};
|
|
764
|
+
const uniforms = reglChannelUniforms.reduce((acc, curr)=>{
|
|
765
|
+
for(const key in curr)acc[key] = curr[key];
|
|
766
|
+
return acc;
|
|
767
|
+
}, staticReglUniforms);
|
|
768
|
+
const frag = `
|
|
769
|
+
precision highp float;
|
|
770
|
+
${fragmentChannelUniformDefs.join('\n')}
|
|
771
|
+
varying vec2 texCoord;
|
|
772
|
+
|
|
773
|
+
void main() {
|
|
774
|
+
vec3 color = vec3(0.0, 0.0, 0.0);
|
|
775
|
+
${colorMerges.join('\n')}
|
|
776
|
+
color = clamp(color, 0.0, 1.0);
|
|
777
|
+
gl_FragColor = vec4(color, 1.0);
|
|
778
|
+
}`;
|
|
779
|
+
// biome-ignore lint/suspicious/noExplicitAny: type of uniforms cannot be given explicitly due to dynamic nature of uniforms in these shaders
|
|
780
|
+
const cmd = regl({
|
|
781
|
+
vert: $3e6603f747b5a8a4$var$tileVert,
|
|
782
|
+
frag: frag,
|
|
783
|
+
framebuffer: regl.prop('target'),
|
|
784
|
+
attributes: {
|
|
785
|
+
pos: [
|
|
786
|
+
0,
|
|
787
|
+
0,
|
|
788
|
+
1,
|
|
789
|
+
0,
|
|
790
|
+
1,
|
|
791
|
+
1,
|
|
792
|
+
0,
|
|
793
|
+
1
|
|
794
|
+
]
|
|
795
|
+
},
|
|
796
|
+
uniforms: uniforms,
|
|
797
|
+
depth: {
|
|
798
|
+
enable: true
|
|
799
|
+
},
|
|
800
|
+
count: 4,
|
|
801
|
+
primitive: 'triangle fan'
|
|
802
|
+
});
|
|
803
|
+
return (p)=>cmd(p);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
function $6f9a458a1fcc6b82$var$toZarrRequest(tile, channel) {
|
|
808
|
+
const { plane: plane, orthoVal: orthoVal, bounds: bounds } = tile;
|
|
809
|
+
const { minCorner: min, maxCorner: max } = bounds;
|
|
810
|
+
const u = {
|
|
811
|
+
min: min[0],
|
|
812
|
+
max: max[0]
|
|
813
|
+
};
|
|
814
|
+
const v = {
|
|
815
|
+
min: min[1],
|
|
816
|
+
max: max[1]
|
|
817
|
+
};
|
|
818
|
+
switch(plane){
|
|
819
|
+
case 'xy':
|
|
820
|
+
return {
|
|
821
|
+
x: u,
|
|
822
|
+
y: v,
|
|
823
|
+
t: 0,
|
|
824
|
+
c: channel,
|
|
825
|
+
z: orthoVal
|
|
826
|
+
};
|
|
827
|
+
case 'xz':
|
|
828
|
+
return {
|
|
829
|
+
x: u,
|
|
830
|
+
z: v,
|
|
831
|
+
t: 0,
|
|
832
|
+
c: channel,
|
|
833
|
+
y: orthoVal
|
|
834
|
+
};
|
|
835
|
+
case 'yz':
|
|
836
|
+
return {
|
|
837
|
+
y: u,
|
|
838
|
+
z: v,
|
|
839
|
+
t: 0,
|
|
840
|
+
c: channel,
|
|
841
|
+
x: orthoVal
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
function $6f9a458a1fcc6b82$var$isPrepared(cacheData) {
|
|
846
|
+
if (!cacheData) return false;
|
|
847
|
+
const keys = Object.keys(cacheData);
|
|
848
|
+
if (keys.length < 1) return false;
|
|
849
|
+
return keys.every((key)=>cacheData[key]?.type === 'texture');
|
|
850
|
+
}
|
|
851
|
+
const $6f9a458a1fcc6b82$var$DEFAULT_NUM_CHANNELS = 3;
|
|
852
|
+
function $6f9a458a1fcc6b82$export$c97bf2bbe9bf6d5e(regl, decoder, options) {
|
|
853
|
+
const numChannels = options?.numChannels ?? $6f9a458a1fcc6b82$var$DEFAULT_NUM_CHANNELS;
|
|
854
|
+
function sliceAsTexture(slice) {
|
|
855
|
+
const { data: data, shape: shape } = slice;
|
|
856
|
+
return {
|
|
857
|
+
bytes: data.byteLength,
|
|
858
|
+
texture: regl.texture({
|
|
859
|
+
data: data,
|
|
860
|
+
width: shape[1],
|
|
861
|
+
height: shape[0],
|
|
862
|
+
format: 'luminance'
|
|
863
|
+
}),
|
|
864
|
+
type: 'texture'
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
const cmd = (0, $3e6603f747b5a8a4$export$640f994018c81008)(regl, numChannels);
|
|
868
|
+
return {
|
|
869
|
+
cacheKey: (item, requestKey, dataset, settings)=>{
|
|
870
|
+
const channelKeys = Object.keys(settings.channels);
|
|
871
|
+
if (!channelKeys.includes(requestKey)) {
|
|
872
|
+
const message = `cannot retrieve cache key: unrecognized requestKey [${requestKey}]`;
|
|
873
|
+
(0, $7soKB$logger).error(message);
|
|
874
|
+
throw new Error(message);
|
|
875
|
+
}
|
|
876
|
+
return `${dataset.url}_${JSON.stringify(item)}_ch=${requestKey}`;
|
|
877
|
+
},
|
|
878
|
+
destroy: ()=>{},
|
|
879
|
+
getVisibleItems: (dataset, settings)=>{
|
|
880
|
+
const { camera: camera, plane: plane, planeLocation: planeLocation, tileSize: tileSize } = settings;
|
|
881
|
+
return (0, $4a90fbb98bec80dd$export$2957f72eb8eb81d4)(camera, plane, planeLocation, dataset, tileSize);
|
|
882
|
+
},
|
|
883
|
+
fetchItemContent: (item, dataset, settings)=>{
|
|
884
|
+
const contents = {};
|
|
885
|
+
for(const key in settings.channels)contents[key] = (signal)=>decoder(dataset, $6f9a458a1fcc6b82$var$toZarrRequest(item, settings.channels[key].index), item.level, signal).then(sliceAsTexture);
|
|
886
|
+
return contents;
|
|
887
|
+
},
|
|
888
|
+
isPrepared: $6f9a458a1fcc6b82$var$isPrepared,
|
|
889
|
+
renderItem: (target, item, dataset, settings, gpuData)=>{
|
|
890
|
+
const channels = Object.keys(gpuData).map((key)=>({
|
|
891
|
+
tex: gpuData[key].texture,
|
|
892
|
+
gamut: (0, $7soKB$intervalToVec2)(settings.channels[key].gamut),
|
|
893
|
+
rgb: settings.channels[key].rgb
|
|
894
|
+
}));
|
|
895
|
+
const layers = dataset.getNumLayers();
|
|
896
|
+
// per the spec, the highest resolution layer should be first
|
|
897
|
+
// we want that layer most in front, so:
|
|
898
|
+
const depth = item.level.datasetIndex / layers;
|
|
899
|
+
const { camera: camera } = settings;
|
|
900
|
+
cmd({
|
|
901
|
+
channels: channels,
|
|
902
|
+
target: target,
|
|
903
|
+
depth: depth,
|
|
904
|
+
tile: (0, $7soKB$Box2D).toFlatArray(item.realBounds),
|
|
905
|
+
view: (0, $7soKB$Box2D).toFlatArray(camera.view)
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function $6f9a458a1fcc6b82$export$79b7536e23e20727(regl, decoder, options) {
|
|
911
|
+
return (0, $7soKB$buildAsyncRenderer)($6f9a458a1fcc6b82$export$c97bf2bbe9bf6d5e(regl, decoder, options), options?.queueOptions);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
function $e7ae61881db7ab65$var$isSliceRequest(payload) {
|
|
924
|
+
return typeof payload === 'object' && payload !== null && 'type' in payload && payload.type === 'ZarrSliceRequest';
|
|
925
|
+
}
|
|
926
|
+
function $e7ae61881db7ab65$var$isCancellationRequest(payload) {
|
|
927
|
+
return typeof payload === 'object' && payload !== null && 'type' in payload && payload.type === 'cancel';
|
|
928
|
+
}
|
|
929
|
+
function $e7ae61881db7ab65$export$79bc3a420a6742ac(ctx) {
|
|
930
|
+
const cancelers = {};
|
|
931
|
+
ctx.onmessage = (msg)=>{
|
|
932
|
+
const { data: data } = msg;
|
|
933
|
+
try {
|
|
934
|
+
if ($e7ae61881db7ab65$var$isSliceRequest(data)) {
|
|
935
|
+
const { metadata: dehydratedMetadata, req: req, level: level, id: id } = data;
|
|
936
|
+
const abort = new AbortController();
|
|
937
|
+
cancelers[id] = abort;
|
|
938
|
+
(0, $0ff6a7cfdd9baaf1$export$c7c266bed5c41e68).rehydrate(dehydratedMetadata).then((metadata)=>{
|
|
939
|
+
(0, $62574ebf548584b6$export$831e2eeffaa681)(metadata, req, level, abort.signal).then((result)=>{
|
|
940
|
+
const { shape: shape, buffer: buffer } = result;
|
|
941
|
+
const data = new Float32Array(buffer.data);
|
|
942
|
+
ctx.postMessage({
|
|
943
|
+
type: 'slice',
|
|
944
|
+
id: id,
|
|
945
|
+
shape: shape,
|
|
946
|
+
data: data
|
|
947
|
+
}, {
|
|
948
|
+
transfer: [
|
|
949
|
+
data.buffer
|
|
950
|
+
]
|
|
951
|
+
});
|
|
952
|
+
}).catch((err)=>{
|
|
953
|
+
if (!(err === 'cancelled' || typeof err === 'object' && ('name' in err && err.name === 'AbortError' || 'code' in err && err.code === 20))) (0, $7soKB$logger).error('error in slice fetch worker: ', err);
|
|
954
|
+
// else ignore it
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
} else if ($e7ae61881db7ab65$var$isCancellationRequest(data)) {
|
|
958
|
+
const { id: id } = data;
|
|
959
|
+
cancelers[id]?.abort('cancelled');
|
|
960
|
+
} else (0, $7soKB$logger).error('web-worker slice-fetcher recieved incomprehensible message: ', msg);
|
|
961
|
+
} catch (err) {
|
|
962
|
+
(0, $7soKB$logger).error('OME-Zarr fetch onmessage error', err);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
const $5b408c6c3c009876$export$c73be2def9fc3425 = 'fetch';
|
|
976
|
+
const $5b408c6c3c009876$export$d7c94d771e5d9aa7 = 'fetch-response';
|
|
977
|
+
const $5b408c6c3c009876$export$75e5bf1b92806f35 = 'cancel';
|
|
978
|
+
const $5b408c6c3c009876$var$FetchMessagePayloadSchema = (0, $7soKB$zod).object({
|
|
979
|
+
rootUrl: (0, $7soKB$zod).string().nonempty(),
|
|
980
|
+
path: (0, $7soKB$zod).string().nonempty().startsWith('/'),
|
|
981
|
+
range: (0, $7soKB$zod).union([
|
|
982
|
+
(0, $7soKB$zod).object({
|
|
983
|
+
offset: (0, $7soKB$zod).number(),
|
|
984
|
+
length: (0, $7soKB$zod).number()
|
|
985
|
+
}),
|
|
986
|
+
(0, $7soKB$zod).object({
|
|
987
|
+
suffixLength: (0, $7soKB$zod).number()
|
|
988
|
+
})
|
|
989
|
+
]).optional(),
|
|
990
|
+
options: (0, $7soKB$zod).unknown().optional()
|
|
991
|
+
});
|
|
992
|
+
const $5b408c6c3c009876$var$FetchMessageSchema = (0, $7soKB$zod).object({
|
|
993
|
+
type: (0, $7soKB$zod).literal($5b408c6c3c009876$export$c73be2def9fc3425),
|
|
994
|
+
id: (0, $7soKB$zod).string().nonempty(),
|
|
995
|
+
payload: $5b408c6c3c009876$var$FetchMessagePayloadSchema
|
|
996
|
+
});
|
|
997
|
+
const $5b408c6c3c009876$var$FetchResponseMessageSchema = (0, $7soKB$zod).object({
|
|
998
|
+
type: (0, $7soKB$zod).literal($5b408c6c3c009876$export$d7c94d771e5d9aa7),
|
|
999
|
+
id: (0, $7soKB$zod).string().nonempty(),
|
|
1000
|
+
payload: (0, $7soKB$zod).unknown().optional()
|
|
1001
|
+
});
|
|
1002
|
+
const $5b408c6c3c009876$var$CancelMessageSchema = (0, $7soKB$zod).object({
|
|
1003
|
+
type: (0, $7soKB$zod).literal($5b408c6c3c009876$export$75e5bf1b92806f35),
|
|
1004
|
+
id: (0, $7soKB$zod).string().nonempty()
|
|
1005
|
+
});
|
|
1006
|
+
function $5b408c6c3c009876$export$ced8a9318e8c4d3d(val) {
|
|
1007
|
+
return $5b408c6c3c009876$var$FetchMessageSchema.safeParse(val).success;
|
|
1008
|
+
}
|
|
1009
|
+
function $5b408c6c3c009876$export$87cac8e8f93fed1d(val) {
|
|
1010
|
+
return $5b408c6c3c009876$var$FetchResponseMessageSchema.safeParse(val).success;
|
|
1011
|
+
}
|
|
1012
|
+
function $5b408c6c3c009876$export$a77d75797e75745f(val) {
|
|
1013
|
+
return $5b408c6c3c009876$var$CancelMessageSchema.safeParse(val).success;
|
|
1014
|
+
}
|
|
1015
|
+
function $5b408c6c3c009876$export$2b3d06107d1323d4(err) {
|
|
1016
|
+
return err === 'cancelled' || typeof err === 'object' && err !== null && ('name' in err && err.name === 'AbortError' || 'code' in err && err.code === 20);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
const $81959a80f9a6789a$var$DEFAULT_NUM_WORKERS = 6;
|
|
1021
|
+
const $81959a80f9a6789a$var$DEFAULT_MAX_DATA_CACHE_BYTES = 262144; // 256 MB -- aribtrarily chosen at this point
|
|
1022
|
+
// @TODO implement a much more context-aware cache size limiting mechanism
|
|
1023
|
+
const $81959a80f9a6789a$var$getDataCacheSizeLimit = ()=>{
|
|
1024
|
+
return $81959a80f9a6789a$var$DEFAULT_MAX_DATA_CACHE_BYTES;
|
|
1025
|
+
};
|
|
1026
|
+
const $81959a80f9a6789a$export$d09252bd498a9707 = (key, range)=>{
|
|
1027
|
+
const keyStr = JSON.stringify(key);
|
|
1028
|
+
const rangeStr = range ? JSON.stringify(range) : 'no-range';
|
|
1029
|
+
return `${keyStr} ${rangeStr}`;
|
|
1030
|
+
};
|
|
1031
|
+
class $81959a80f9a6789a$var$CacheableByteArray {
|
|
1032
|
+
#arr;
|
|
1033
|
+
constructor(arr){
|
|
1034
|
+
this.#arr = arr;
|
|
1035
|
+
}
|
|
1036
|
+
destroy() {}
|
|
1037
|
+
sizeInBytes() {
|
|
1038
|
+
return this.#arr.byteLength;
|
|
1039
|
+
}
|
|
1040
|
+
get array() {
|
|
1041
|
+
return this.#arr;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
const $81959a80f9a6789a$var$copyToTransferableHeaders = (headers)=>{
|
|
1045
|
+
if (Array.isArray(headers)) {
|
|
1046
|
+
const result = {};
|
|
1047
|
+
headers.forEach(([key, val])=>{
|
|
1048
|
+
// TODO is key, val the correct order here?
|
|
1049
|
+
result[key] = val;
|
|
1050
|
+
});
|
|
1051
|
+
return result;
|
|
1052
|
+
}
|
|
1053
|
+
if (headers instanceof Headers) {
|
|
1054
|
+
const result = {};
|
|
1055
|
+
headers.forEach((val, key)=>{
|
|
1056
|
+
result[key] = val;
|
|
1057
|
+
});
|
|
1058
|
+
return result;
|
|
1059
|
+
}
|
|
1060
|
+
return headers;
|
|
1061
|
+
};
|
|
1062
|
+
const $81959a80f9a6789a$var$copyToTransferableRequestInit = (req)=>{
|
|
1063
|
+
if (req === undefined) return {};
|
|
1064
|
+
const updReq = {
|
|
1065
|
+
...req
|
|
1066
|
+
};
|
|
1067
|
+
delete updReq.signal;
|
|
1068
|
+
delete updReq.window;
|
|
1069
|
+
return {
|
|
1070
|
+
...updReq,
|
|
1071
|
+
body: req.body?.toString(),
|
|
1072
|
+
headers: $81959a80f9a6789a$var$copyToTransferableHeaders(req.headers)
|
|
1073
|
+
};
|
|
1074
|
+
};
|
|
1075
|
+
class $81959a80f9a6789a$export$927188fe483fb171 extends $7soKB$FetchStore {
|
|
1076
|
+
/**
|
|
1077
|
+
* Maintains a pool of available worker threads.
|
|
1078
|
+
*
|
|
1079
|
+
* TODO: Enable end-to-end Message-based type constraints for these that
|
|
1080
|
+
* enable us to restrict what types of messages can be sent to workers
|
|
1081
|
+
* for a given store instance.
|
|
1082
|
+
*/ // biome-ignore lint/suspicious/noExplicitAny: the type system for these parameters is a future feature
|
|
1083
|
+
#workerPool;
|
|
1084
|
+
/**
|
|
1085
|
+
* Stores the current set of cached data that has been successfully
|
|
1086
|
+
* fetched. This data is stored in raw byte array form so that it
|
|
1087
|
+
* integrates properly with the Zarrita framework.
|
|
1088
|
+
*/ #dataCache;
|
|
1089
|
+
/**
|
|
1090
|
+
* Maps cache keys to numeric times; the higher the time, the higher the priority.
|
|
1091
|
+
*
|
|
1092
|
+
* This effectively means that more frequently-requested items will be kept longer.
|
|
1093
|
+
*/ #priorityByTimestamp;
|
|
1094
|
+
/**
|
|
1095
|
+
* Stores in-progress requests that have not yet resolved.
|
|
1096
|
+
*/ #pendingRequests;
|
|
1097
|
+
/**
|
|
1098
|
+
* Stores one instance of a cache key for each time that cache key was requested,
|
|
1099
|
+
* removing them all once that particular request is fulfilled. This allows us to
|
|
1100
|
+
* keep track of whether or not it is safe to abort a pending request: as long as
|
|
1101
|
+
* there are at least 2 instances of the same cache key in this array, then that
|
|
1102
|
+
* means multiple requestors are waiting on a particular piece of data, and it is
|
|
1103
|
+
* not safe to abort that request.
|
|
1104
|
+
*/ #pendingRequestKeyCounts;
|
|
1105
|
+
/**
|
|
1106
|
+
* A callback form of the `score` function.
|
|
1107
|
+
*/ #scoreFn;
|
|
1108
|
+
// biome-ignore lint/suspicious/noExplicitAny: the type system for these parameters is a future feature
|
|
1109
|
+
constructor(url, handler, options){
|
|
1110
|
+
super(url, options?.fetchStoreOptions);
|
|
1111
|
+
this.#scoreFn = (h)=>this.score(h);
|
|
1112
|
+
this.#dataCache = new (0, $7soKB$PriorityCache)(new Map(), this.#scoreFn, options?.maxBytes ?? $81959a80f9a6789a$var$getDataCacheSizeLimit());
|
|
1113
|
+
this.#priorityByTimestamp = new Map();
|
|
1114
|
+
this.#workerPool = handler;
|
|
1115
|
+
this.#pendingRequests = new Map();
|
|
1116
|
+
this.#pendingRequestKeyCounts = new Map();
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Warning - nothing in this class should be considered useable after
|
|
1120
|
+
* calling this method - any/all methods called should be expected to be
|
|
1121
|
+
* completely unreliable. dont call me unless you're about to dispose of all references to this object
|
|
1122
|
+
*/ destroy() {
|
|
1123
|
+
// release all the web-workers!
|
|
1124
|
+
this.#workerPool.destroy();
|
|
1125
|
+
// todo: reject all promises
|
|
1126
|
+
this.#pendingRequests.forEach((p)=>{
|
|
1127
|
+
p.reject('cancelled');
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
score(key) {
|
|
1131
|
+
return this.#priorityByTimestamp.get(key) ?? 0;
|
|
1132
|
+
}
|
|
1133
|
+
#fromCache(cacheKey) {
|
|
1134
|
+
const cached = this.#dataCache.get(cacheKey);
|
|
1135
|
+
if (cached === undefined) return undefined;
|
|
1136
|
+
this.#priorityByTimestamp.set(cacheKey, Date.now());
|
|
1137
|
+
return cached.array;
|
|
1138
|
+
}
|
|
1139
|
+
#incrementKeyCount(cacheKey) {
|
|
1140
|
+
const count = this.#pendingRequestKeyCounts.get(cacheKey);
|
|
1141
|
+
const newCount = count !== undefined ? count + 1 : 1;
|
|
1142
|
+
this.#pendingRequestKeyCounts.set(cacheKey, newCount);
|
|
1143
|
+
return newCount;
|
|
1144
|
+
}
|
|
1145
|
+
#decrementKeyCount(cacheKey) {
|
|
1146
|
+
const count = this.#pendingRequestKeyCounts.get(cacheKey);
|
|
1147
|
+
if (count === undefined) {
|
|
1148
|
+
(0, $7soKB$logger).warn('attempted to decrement a non-existent request key');
|
|
1149
|
+
return 0;
|
|
1150
|
+
}
|
|
1151
|
+
if (count <= 1) {
|
|
1152
|
+
this.#pendingRequestKeyCounts.delete(cacheKey);
|
|
1153
|
+
return 0;
|
|
1154
|
+
}
|
|
1155
|
+
const newCount = count - 1;
|
|
1156
|
+
this.#pendingRequestKeyCounts.set(cacheKey, newCount);
|
|
1157
|
+
return newCount;
|
|
1158
|
+
}
|
|
1159
|
+
async #doFetch(key, range, options, abort) {
|
|
1160
|
+
const cacheKey = $81959a80f9a6789a$export$d09252bd498a9707(key, range);
|
|
1161
|
+
this.#priorityByTimestamp.set(cacheKey, Date.now());
|
|
1162
|
+
this.#dataCache.reprioritize(this.#scoreFn);
|
|
1163
|
+
this.#incrementKeyCount(cacheKey);
|
|
1164
|
+
const pending = this.#pendingRequests.get(cacheKey);
|
|
1165
|
+
if (pending !== undefined) return pending.promise;
|
|
1166
|
+
const { promise: promise, resolve: resolve, reject: reject } = Promise.withResolvers();
|
|
1167
|
+
this.#pendingRequests.set(cacheKey, {
|
|
1168
|
+
promise: promise,
|
|
1169
|
+
resolve: resolve,
|
|
1170
|
+
reject: reject
|
|
1171
|
+
});
|
|
1172
|
+
const chain = new AbortController();
|
|
1173
|
+
if (abort) abort.addEventListener('abort', ()=>{
|
|
1174
|
+
const count = this.#decrementKeyCount(cacheKey);
|
|
1175
|
+
if (count === 0) {
|
|
1176
|
+
this.#priorityByTimestamp.set(cacheKey, 0);
|
|
1177
|
+
this.#dataCache.reprioritize(this.#scoreFn);
|
|
1178
|
+
chain.abort();
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
const request = this.#workerPool.submitRequest({
|
|
1182
|
+
type: (0, $5b408c6c3c009876$export$c73be2def9fc3425),
|
|
1183
|
+
payload: {
|
|
1184
|
+
rootUrl: this.url,
|
|
1185
|
+
path: key,
|
|
1186
|
+
range: range,
|
|
1187
|
+
options: options
|
|
1188
|
+
}
|
|
1189
|
+
}, (0, $5b408c6c3c009876$export$87cac8e8f93fed1d), [], chain.signal);
|
|
1190
|
+
request.then((response)=>{
|
|
1191
|
+
const payload = response.payload;
|
|
1192
|
+
if (payload === undefined) {
|
|
1193
|
+
resolve(undefined);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
const arr = new Uint8Array(payload);
|
|
1197
|
+
this.#dataCache.put(cacheKey, new $81959a80f9a6789a$var$CacheableByteArray(arr));
|
|
1198
|
+
resolve(arr);
|
|
1199
|
+
}).catch((e)=>{
|
|
1200
|
+
reject(e);
|
|
1201
|
+
}).finally(()=>{
|
|
1202
|
+
this.#pendingRequests.delete(cacheKey);
|
|
1203
|
+
this.#pendingRequestKeyCounts.delete(cacheKey);
|
|
1204
|
+
});
|
|
1205
|
+
return promise;
|
|
1206
|
+
}
|
|
1207
|
+
async get(key, options) {
|
|
1208
|
+
const cacheKey = $81959a80f9a6789a$export$d09252bd498a9707(key);
|
|
1209
|
+
const cached = this.#fromCache(cacheKey);
|
|
1210
|
+
if (cached !== undefined) return cached;
|
|
1211
|
+
const workerOptions = $81959a80f9a6789a$var$copyToTransferableRequestInit(options);
|
|
1212
|
+
const abort = options?.signal ?? undefined;
|
|
1213
|
+
return this.#doFetch(key, undefined, workerOptions, abort);
|
|
1214
|
+
}
|
|
1215
|
+
async getRange(key, range, options) {
|
|
1216
|
+
const cacheKey = $81959a80f9a6789a$export$d09252bd498a9707(key, range);
|
|
1217
|
+
const cached = this.#fromCache(cacheKey);
|
|
1218
|
+
if (cached !== undefined) return cached;
|
|
1219
|
+
const workerOptions = $81959a80f9a6789a$var$copyToTransferableRequestInit(options);
|
|
1220
|
+
const abort = options?.signal ?? undefined;
|
|
1221
|
+
return this.#doFetch(key, range, workerOptions, abort);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
class $81959a80f9a6789a$export$80a50be84de8c993 extends $81959a80f9a6789a$export$927188fe483fb171 {
|
|
1225
|
+
constructor(url, workerModule, options){
|
|
1226
|
+
super(url, new (0, $7soKB$WorkerPool)(options?.numWorkers ?? $81959a80f9a6789a$var$DEFAULT_NUM_WORKERS, workerModule), options);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
|
|
1231
|
+
function $845a7aab26aa4fe6$export$fc561c682218655c(url, workerModule, options) {
|
|
1232
|
+
const store = new (0, $81959a80f9a6789a$export$80a50be84de8c993)(url, workerModule, options);
|
|
1233
|
+
const getSlice = async (metadata, req, level, signal)=>{
|
|
1234
|
+
if (metadata.url !== url) throw new Error('trying to use a decoder from a different store - we cant do that yet, although we could build a map of url->stores here if we wanted later - TODO');
|
|
1235
|
+
const scene = metadata.attrs.multiscales[0];
|
|
1236
|
+
const { axes: axes } = scene;
|
|
1237
|
+
if (!level) {
|
|
1238
|
+
const message = 'invalid Zarr data: no datasets found';
|
|
1239
|
+
(0, $7soKB$logger).error(message);
|
|
1240
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
1241
|
+
}
|
|
1242
|
+
const arr = metadata.arrays.find((a)=>a.path === level.path);
|
|
1243
|
+
if (!arr) {
|
|
1244
|
+
const message = `cannot load slice: no array found for path [${level.path}]`;
|
|
1245
|
+
(0, $7soKB$logger).error(message);
|
|
1246
|
+
throw new (0, $8af220877f5aad67$export$e819e674f1e05236)(message);
|
|
1247
|
+
}
|
|
1248
|
+
const { raw: raw } = await (0, $62574ebf548584b6$export$ab574f5010f64a15)(store, arr.path, metadata.zarrVersion, false);
|
|
1249
|
+
const result = await $7soKB$get(raw, (0, $62574ebf548584b6$export$d7b58f10109feb02)(req, axes, level.shape), {
|
|
1250
|
+
opts: {
|
|
1251
|
+
signal: signal ?? null
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
if (typeof result === 'number') throw new Error('oh noes, slice came back all weird');
|
|
1255
|
+
const { shape: shape, data: data } = result;
|
|
1256
|
+
if (typeof data !== 'object' || !('buffer' in data)) throw new Error('slice was malformed, array-buffer response required');
|
|
1257
|
+
// biome-ignore lint/suspicious/noExplicitAny: <hard to prove - but the typeof check above is sufficient for this to be safe>
|
|
1258
|
+
return {
|
|
1259
|
+
shape: shape,
|
|
1260
|
+
data: new Float32Array(data)
|
|
1261
|
+
};
|
|
1262
|
+
};
|
|
1263
|
+
return {
|
|
1264
|
+
decoder: getSlice,
|
|
1265
|
+
destroy: ()=>{
|
|
1266
|
+
store.destroy();
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
// a web-worker which fetches slices of data, decodes them, and returns the result as a flat float32 array, using transferables
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
const $b2fe35b4140f0787$var$NUM_RETRIES = 2;
|
|
1277
|
+
const $b2fe35b4140f0787$var$RETRY_DELAY_MS = 500;
|
|
1278
|
+
const $b2fe35b4140f0787$var$fetchFile = async (rootUrl, path, options, abortController)=>{
|
|
1279
|
+
const store = new (0, $7soKB$FetchStore)(rootUrl);
|
|
1280
|
+
return store.get(path, {
|
|
1281
|
+
...options || {},
|
|
1282
|
+
signal: abortController?.signal ?? null
|
|
1283
|
+
});
|
|
1284
|
+
};
|
|
1285
|
+
const $b2fe35b4140f0787$var$fetchSlice = async (rootUrl, path, range, options, abortController)=>{
|
|
1286
|
+
const store = new (0, $7soKB$FetchStore)(rootUrl);
|
|
1287
|
+
const wait = async (ms)=>new Promise((resolve)=>{
|
|
1288
|
+
setTimeout(resolve, ms);
|
|
1289
|
+
});
|
|
1290
|
+
for(let i = 0; i < $b2fe35b4140f0787$var$NUM_RETRIES; i++)try {
|
|
1291
|
+
return await store.getRange(path, range, {
|
|
1292
|
+
...options || {},
|
|
1293
|
+
signal: abortController?.signal ?? null
|
|
1294
|
+
});
|
|
1295
|
+
} catch (e) {
|
|
1296
|
+
(0, $7soKB$logger).error('getRange request failed:', e);
|
|
1297
|
+
const hasRetries = i < $b2fe35b4140f0787$var$NUM_RETRIES - 1;
|
|
1298
|
+
const message = `getRange request ${i < $b2fe35b4140f0787$var$NUM_RETRIES - 1 ? `will retry in ${$b2fe35b4140f0787$var$RETRY_DELAY_MS}ms` : 'has no retries left'}`;
|
|
1299
|
+
(0, $7soKB$logger).warn(message);
|
|
1300
|
+
if (hasRetries) await wait($b2fe35b4140f0787$var$RETRY_DELAY_MS);
|
|
1301
|
+
}
|
|
1302
|
+
return undefined;
|
|
1303
|
+
};
|
|
1304
|
+
const $b2fe35b4140f0787$var$handleFetch = (message, abortControllers)=>{
|
|
1305
|
+
const { id: id, payload: payload } = message;
|
|
1306
|
+
const { rootUrl: rootUrl, path: path, range: range, options: options } = payload;
|
|
1307
|
+
if (id in abortControllers) {
|
|
1308
|
+
(0, $7soKB$logger).error('cannot send message: request ID already in use');
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
const abort = new AbortController();
|
|
1312
|
+
abortControllers[id] = abort;
|
|
1313
|
+
const fetchFn = range !== undefined ? ()=>$b2fe35b4140f0787$var$fetchSlice(rootUrl, path, range, options, abort) : ()=>$b2fe35b4140f0787$var$fetchFile(rootUrl, path, options, abort);
|
|
1314
|
+
fetchFn().then((result)=>{
|
|
1315
|
+
const buffer = result?.buffer;
|
|
1316
|
+
const options = buffer !== undefined ? {
|
|
1317
|
+
transfer: [
|
|
1318
|
+
buffer
|
|
1319
|
+
]
|
|
1320
|
+
} : {};
|
|
1321
|
+
self.postMessage({
|
|
1322
|
+
type: (0, $5b408c6c3c009876$export$d7c94d771e5d9aa7),
|
|
1323
|
+
id: id,
|
|
1324
|
+
payload: result?.buffer
|
|
1325
|
+
}, {
|
|
1326
|
+
...options
|
|
1327
|
+
});
|
|
1328
|
+
}).catch((e)=>{
|
|
1329
|
+
if (!(0, $5b408c6c3c009876$export$2b3d06107d1323d4)(e)) (0, $7soKB$logger).error('error in slice fetch worker: ', e);
|
|
1330
|
+
// can ignore if it is a cancellation error
|
|
1331
|
+
});
|
|
1332
|
+
};
|
|
1333
|
+
const $b2fe35b4140f0787$var$handleCancel = (message, abortControllers)=>{
|
|
1334
|
+
const { id: id } = message;
|
|
1335
|
+
const abortController = abortControllers[id];
|
|
1336
|
+
if (!abortController) (0, $7soKB$logger).warn('attempted to cancel a non-existent request');
|
|
1337
|
+
else abortController.abort('cancelled');
|
|
1338
|
+
};
|
|
1339
|
+
const $b2fe35b4140f0787$var$startHeartbeat = ()=>setInterval(()=>{
|
|
1340
|
+
self.postMessage({
|
|
1341
|
+
type: 'heartbeat'
|
|
1342
|
+
});
|
|
1343
|
+
}, (0, $7soKB$HEARTBEAT_RATE_MS));
|
|
1344
|
+
const $b2fe35b4140f0787$var$setupOnMessage = ()=>{
|
|
1345
|
+
const abortControllers = {};
|
|
1346
|
+
const onmessage = async (e)=>{
|
|
1347
|
+
const { data: message } = e;
|
|
1348
|
+
if ((0, $5b408c6c3c009876$export$ced8a9318e8c4d3d)(message)) $b2fe35b4140f0787$var$handleFetch(message, abortControllers);
|
|
1349
|
+
else if ((0, $5b408c6c3c009876$export$a77d75797e75745f)(message)) $b2fe35b4140f0787$var$handleCancel(message, abortControllers);
|
|
1350
|
+
};
|
|
1351
|
+
return onmessage;
|
|
1352
|
+
};
|
|
1353
|
+
const $b2fe35b4140f0787$export$4908e147e219e01a = (ctx)=>{
|
|
1354
|
+
ctx.onmessage = $b2fe35b4140f0787$var$setupOnMessage();
|
|
1355
|
+
return {
|
|
1356
|
+
startHeartbeat: $b2fe35b4140f0787$var$startHeartbeat
|
|
1357
|
+
};
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
export {$6f9a458a1fcc6b82$export$c97bf2bbe9bf6d5e as buildOmeZarrSliceRenderer, $6f9a458a1fcc6b82$export$79b7536e23e20727 as buildAsyncOmezarrRenderer, $8af220877f5aad67$export$96bea966bad52d02 as VisZarrError, $8af220877f5aad67$export$e819e674f1e05236 as VisZarrDataError, $8af220877f5aad67$export$d7f857358a0dbda6 as VisZarrIndexError, $4a90fbb98bec80dd$export$e83f4de492e8b929 as defaultDecoder, $4a90fbb98bec80dd$export$2957f72eb8eb81d4 as getVisibleTiles, $3e6603f747b5a8a4$export$640f994018c81008 as buildTileRenderCommand, $3e6603f747b5a8a4$export$e1c4d8365a017448 as buildRGBTileRenderCommand, $0ff6a7cfdd9baaf1$export$9989519d74cf4fd0 as OmeZarrAxisSchema, $0ff6a7cfdd9baaf1$export$13526d3947c5c7c2 as OmeZarrCoordinateTranslationSchema, $0ff6a7cfdd9baaf1$export$f0f448c9214bb1a1 as OmeZarrCoordinateScaleSchema, $0ff6a7cfdd9baaf1$export$2e1c7be8e43a67e3 as OmeZarrCoordinateTransformSchema, $0ff6a7cfdd9baaf1$export$a535e1d8aa90b3c9 as OmeZarrDatasetSchema, $0ff6a7cfdd9baaf1$export$d7d6a65692bb1dc0 as OmeZarrMultiscaleSchema, $0ff6a7cfdd9baaf1$export$90729eda236ba57 as OmeZarrOmeroChannelWindowSchema, $0ff6a7cfdd9baaf1$export$bb4e42bb52e6d21d as OmeZarrOmeroChannelSchema, $0ff6a7cfdd9baaf1$export$51e803dddccc5479 as OmeZarrOmeroSchema, $0ff6a7cfdd9baaf1$export$a3b48a0a4f3d252 as OmeZarrAttrsSchema, $0ff6a7cfdd9baaf1$export$c7c266bed5c41e68 as OmeZarrMetadata, $62574ebf548584b6$export$7999b0bb51b3dcb8 as loadMetadata, $62574ebf548584b6$export$7f36aeab32ab33f7 as loadZarrArrayFile, $62574ebf548584b6$export$78b8536ed7d8a60d as loadZarrAttrsFile, $62574ebf548584b6$export$8efeb0367ee93033 as pickBestScale, $62574ebf548584b6$export$831e2eeffaa681 as loadSlice, $62574ebf548584b6$export$c5890dae76bd0ad7 as sizeInUnits, $62574ebf548584b6$export$fcb1a44ca126ee5c as sizeInVoxels, $62574ebf548584b6$export$ca023b1dd6252668 as nextSliceStep, $62574ebf548584b6$export$ba17f86721d4d0ce as planeSizeInVoxels, $e7ae61881db7ab65$export$79bc3a420a6742ac as makeOmeZarrSliceLoaderWorker, $845a7aab26aa4fe6$export$fc561c682218655c as decoderFactory, $b2fe35b4140f0787$export$4908e147e219e01a as setupFetchDataWorker, $5b408c6c3c009876$export$ced8a9318e8c4d3d as isFetchMessage, $5b408c6c3c009876$export$87cac8e8f93fed1d as isFetchResponseMessage, $5b408c6c3c009876$export$a77d75797e75745f as isCancelMessage, $5b408c6c3c009876$export$2b3d06107d1323d4 as isCancellationError};
|
|
1365
|
+
//# sourceMappingURL=main.js.map
|