@biongff/vizarr 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +1 -0
  2. package/dist/biongff-vizarr.cjs.js +1 -0
  3. package/dist/biongff-vizarr.es.js +9 -0
  4. package/dist/blosc-CeItQ6qj.cjs +17 -0
  5. package/dist/blosc-DaK8KnI4.js +719 -0
  6. package/dist/chunk-INHXZS53-DiyuLb3Z.js +14 -0
  7. package/dist/chunk-INHXZS53-z3BpFH8p.cjs +1 -0
  8. package/dist/deflate-DzrO3E-1.cjs +1 -0
  9. package/dist/deflate-Q4iCMMBa.js +10 -0
  10. package/dist/index-3CiumG5V.js +53876 -0
  11. package/dist/index-Ckhcz4QQ.cjs +3815 -0
  12. package/dist/index.d.ts +119 -0
  13. package/dist/jpeg-BdEIe3QG.js +533 -0
  14. package/dist/jpeg-DFzBPcur.cjs +1 -0
  15. package/dist/jpeg2k-1tnzEScI.js +24 -0
  16. package/dist/jpeg2k-CcF6vCxI.cjs +1 -0
  17. package/dist/lerc-35FtXNGP.cjs +1 -0
  18. package/dist/lerc-B6qlOBE2.js +1039 -0
  19. package/dist/lz4-CssV0LoA.js +643 -0
  20. package/dist/lz4-PFaIsPAh.cjs +15 -0
  21. package/dist/lzw-CeXp5T9s.js +84 -0
  22. package/dist/lzw-DnHvgKbO.cjs +1 -0
  23. package/dist/packbits-CAZoMCQq.js +24 -0
  24. package/dist/packbits-DwdUAwAt.cjs +1 -0
  25. package/dist/pako.esm-Bx5X36Wo.js +1074 -0
  26. package/dist/pako.esm-DZC2QrbJ.cjs +1 -0
  27. package/dist/raw-BB450wdT.cjs +1 -0
  28. package/dist/raw-oTi55tWo.js +9 -0
  29. package/dist/ref-Bd0nPTvp.js +196 -0
  30. package/dist/ref-CIxavIn6.cjs +1 -0
  31. package/dist/webimage-66a9vFf5.cjs +1 -0
  32. package/dist/webimage-CKRNzqyG.js +19 -0
  33. package/dist/zstd-Cqadn9HA.js +610 -0
  34. package/dist/zstd-_xUhkGOV.cjs +15 -0
  35. package/package.json +35 -21
  36. package/.gitattributes +0 -1
  37. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
  38. package/.github/ISSUE_TEMPLATE/documentation_update.md +0 -16
  39. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  40. package/.github/ISSUE_TEMPLATE/maintenance.md +0 -19
  41. package/.github/ISSUE_TEMPLATE/optimisation_request.md +0 -23
  42. package/.github/ISSUE_TEMPLATE/support_request.md +0 -25
  43. package/.github/PULL_REQUEST_TEMPLATE.md +0 -21
  44. package/.github/dependabot.yml +0 -13
  45. package/.github/workflows/deploy.yml +0 -56
  46. package/.github/workflows/npm-publish-dev.yml +0 -49
  47. package/.github/workflows/npm-publish.yml +0 -42
  48. package/.github/workflows/release.yml +0 -37
  49. package/.github/workflows/test.yml +0 -21
  50. package/.releaserc.json +0 -29
  51. package/CHANGELOG.md +0 -70
  52. package/CITATION.cff +0 -56
  53. package/CONTRIBUTING.md +0 -82
  54. package/README.md +0 -85
  55. package/assets/favicon.ico +0 -0
  56. package/assets/favicon.png +0 -0
  57. package/assets/logo-wide.png +0 -0
  58. package/assets/logo-wide.svg +0 -60
  59. package/assets/logo.png +0 -0
  60. package/assets/logo.svg +0 -36
  61. package/assets/robots.txt +0 -3
  62. package/assets/screenshot.png +0 -0
  63. package/biome.json +0 -34
  64. package/index.html +0 -24
  65. package/main.ts +0 -52
  66. package/patches/@vivjs__loaders@0.17.2.patch +0 -13
  67. package/python/.python-version +0 -1
  68. package/python/README.md +0 -14
  69. package/python/Untitled.ipynb +0 -40
  70. package/python/deno.json +0 -22
  71. package/python/notebooks/IDR_example.ipynb +0 -86
  72. package/python/notebooks/README.md +0 -35
  73. package/python/notebooks/create_fixture.py +0 -72
  74. package/python/notebooks/getting_started.ipynb +0 -217
  75. package/python/notebooks/mandelbrot.ipynb +0 -211
  76. package/python/notebooks/spatial_transformations.ipynb +0 -139
  77. package/python/pyproject.toml +0 -18
  78. package/python/src/vizarr/__init__.py +0 -10
  79. package/python/src/vizarr/_widget.js +0 -113
  80. package/python/src/vizarr/_widget.py +0 -67
  81. package/python/uv.lock +0 -2495
  82. package/src/ZarrPixelSource.ts +0 -182
  83. package/src/codecs/jpeg2k.ts +0 -22
  84. package/src/codecs/register.ts +0 -2
  85. package/src/components/LayerController/AcquisitionController.tsx +0 -46
  86. package/src/components/LayerController/AddChannelButton.tsx +0 -122
  87. package/src/components/LayerController/AxisOptions.tsx +0 -93
  88. package/src/components/LayerController/AxisSlider.tsx +0 -86
  89. package/src/components/LayerController/AxisSliders.tsx +0 -29
  90. package/src/components/LayerController/ChannelController.tsx +0 -84
  91. package/src/components/LayerController/ChannelOptions.tsx +0 -183
  92. package/src/components/LayerController/ColorPalette.tsx +0 -40
  93. package/src/components/LayerController/Content.tsx +0 -71
  94. package/src/components/LayerController/Header.tsx +0 -48
  95. package/src/components/LayerController/Labels.tsx +0 -73
  96. package/src/components/LayerController/LayerFitToViewportButton.tsx +0 -43
  97. package/src/components/LayerController/LayerVisibilityButton.tsx +0 -32
  98. package/src/components/LayerController/OpacitySlider.tsx +0 -25
  99. package/src/components/LayerController/index.tsx +0 -42
  100. package/src/components/Menu.tsx +0 -54
  101. package/src/components/Viewer.tsx +0 -136
  102. package/src/hooks.ts +0 -29
  103. package/src/index.tsx +0 -138
  104. package/src/io.ts +0 -276
  105. package/src/layers/grid-layer.ts +0 -214
  106. package/src/layers/label-layer.ts +0 -446
  107. package/src/layers/viv-layers.ts +0 -55
  108. package/src/lru-store.ts +0 -57
  109. package/src/ome.ts +0 -388
  110. package/src/state.ts +0 -202
  111. package/src/theme.ts +0 -80
  112. package/src/types.ts +0 -112
  113. package/src/utils.ts +0 -663
  114. package/tsconfig.json +0 -14
  115. package/vite.config.js +0 -47
@@ -1,55 +0,0 @@
1
- /**
2
- * Type-complete interfaces to Viv's core layers
3
- *
4
- * @module
5
- */
6
- import { ImageLayer as BaseImageLayer, MultiscaleImageLayer as BaseMultiscaleImageLayer } from "@hms-dbmi/viv";
7
-
8
- import type { Layer } from "deck.gl";
9
- import type { Matrix4 } from "math.gl";
10
- import type { ZarrPixelSource } from "../ZarrPixelSource";
11
-
12
- export interface BaseLayerProps {
13
- id: string;
14
- contrastLimits: Array<[min: number, max: number]>;
15
- colors: [r: number, g: number, b: number][];
16
- channelsVisible: Array<boolean>;
17
- opacity: number;
18
- colormap: string; // TODO: more precise
19
- selections: number[][];
20
- modelMatrix: Matrix4;
21
- contrastLimitsRange: [min: number, max: number][];
22
- pickable?: boolean;
23
- onClick?: (e: Record<string, unknown>) => void;
24
- }
25
-
26
- export interface MultiscaleImageLayerProps extends BaseLayerProps {
27
- loader: Array<ZarrPixelSource>;
28
- }
29
-
30
- // @ts-expect-error - Viv does faithfully implement the Layer interface, just not captured in the types
31
- export class MultiscaleImageLayer
32
- extends BaseMultiscaleImageLayer<string[]>
33
- implements Layer<MultiscaleImageLayerProps>
34
- {
35
- static layerName = "VizarMultiscaleImageLayer";
36
- // biome-ignore lint/complexity/noUselessConstructor: Necessary for TypeScript to get the types
37
- constructor(props: MultiscaleImageLayerProps) {
38
- // @ts-expect-error - Viv's types are not correct
39
- super(props);
40
- }
41
- }
42
-
43
- export interface ImageLayerProps extends BaseLayerProps {
44
- loader: ZarrPixelSource;
45
- }
46
-
47
- // @ts-expect-error - Viv does faithfully implement the Layer interface, just not captured in the types
48
- export class ImageLayer extends BaseImageLayer<string[]> implements Layer<ImageLayerProps> {
49
- static layerName = "VizarrImageLayer";
50
- // biome-ignore lint/complexity/noUselessConstructor: Necessary for TypeScript to get the types
51
- constructor(props: ImageLayerProps) {
52
- // @ts-expect-error - Viv's types are not correct
53
- super(props);
54
- }
55
- }
package/src/lru-store.ts DELETED
@@ -1,57 +0,0 @@
1
- import QuickLRU from "quick-lru";
2
-
3
- import type * as zarr from "zarrita";
4
-
5
- type RangeQuery =
6
- | {
7
- offset: number;
8
- length: number;
9
- }
10
- | {
11
- suffixLength: number;
12
- };
13
-
14
- function normalizeKey(key: string, range?: RangeQuery) {
15
- if (!range) return key;
16
- if ("suffixLength" in range) return `${key}:-${range.suffixLength}`;
17
- return `${key}:${range.offset}:${range.offset + range.length - 1}`;
18
- }
19
-
20
- export function lru<S extends zarr.Readable>(store: S, maxSize = 100) {
21
- const cache = new QuickLRU<string, Promise<Uint8Array | undefined>>({ maxSize });
22
- let getRange = store.getRange ? store.getRange.bind(store) : undefined;
23
- function get(...args: Parameters<S["get"]>) {
24
- const [key, opts] = args;
25
- const cacheKey = normalizeKey(key);
26
- const cached = cache.get(cacheKey);
27
- if (cached) return cached;
28
- const result = Promise.resolve(store.get(key, opts)).catch((err) => {
29
- cache.delete(cacheKey);
30
- throw err;
31
- });
32
- cache.set(cacheKey, result);
33
- return result;
34
- }
35
- if (getRange) {
36
- const _getRange = getRange;
37
- getRange = (...args: Parameters<NonNullable<S["getRange"]>>) => {
38
- const [key, range, opts] = args;
39
- const cacheKey = normalizeKey(key, range);
40
- const cached = cache.get(cacheKey);
41
- if (cached) return cached;
42
- const result = Promise.resolve(_getRange?.(key, range, opts)).catch((err) => {
43
- cache.delete(cacheKey);
44
- throw err;
45
- });
46
- cache.set(cacheKey, result);
47
- return result;
48
- };
49
- }
50
- return new Proxy(store, {
51
- get(target, prop, receiver) {
52
- if (prop === "get") return get;
53
- if (prop === "getRange" && getRange) return getRange;
54
- return Reflect.get(target, prop, receiver);
55
- },
56
- });
57
- }
package/src/ome.ts DELETED
@@ -1,388 +0,0 @@
1
- import pMap from "p-map";
2
- import * as zarr from "zarrita";
3
- import type { ImageLabels, ImageLayerConfig, OnClickData, SourceData } from "./state";
4
-
5
- import { ZarrPixelSource } from "./ZarrPixelSource";
6
- import * as utils from "./utils";
7
-
8
- export async function loadWell(
9
- config: ImageLayerConfig,
10
- grp: zarr.Group<zarr.Readable>,
11
- wellAttrs: Ome.Well,
12
- ): Promise<SourceData> {
13
- // Can filter Well fields by URL query ?acquisition=ID
14
- const acquisitionId: number | undefined = config.acquisition ? Number.parseInt(config.acquisition) : undefined;
15
- let acquisitions: Ome.Acquisition[] = [];
16
-
17
- utils.assert(wellAttrs?.images, "Well .zattrs missing images");
18
- utils.assert(grp.path, "Cannot inspect zarr path to open well.");
19
-
20
- const [row, col] = grp.path.split("/").filter(Boolean).slice(-2);
21
-
22
- let { images } = wellAttrs;
23
-
24
- // Do we have more than 1 Acquisition?
25
- const acqIds = images.flatMap((img) => (img.acquisition ? [img.acquisition] : []));
26
-
27
- if (acqIds.length > 1) {
28
- // Need to get acquisitions metadata from parent Plate
29
- const platePath = grp.path.replace(`${row}/${col}`, "");
30
- const plate = await zarr.open(grp.resolve(platePath));
31
- const plateAttrs = utils.resolveAttrs(plate.attrs) as { plate: Ome.Plate };
32
- acquisitions = plateAttrs.plate.acquisitions ?? [];
33
- // filter imagePaths by acquisition
34
- if (acquisitionId && acqIds.includes(acquisitionId)) {
35
- images = images.filter((img) => img.acquisition === acquisitionId);
36
- }
37
- }
38
-
39
- const imgPaths = images.map((img) => img.path);
40
- const cols = Math.ceil(Math.sqrt(imgPaths.length));
41
- const rows = Math.ceil(imgPaths.length / cols);
42
-
43
- // Use first image for rendering settings, resolutions etc.
44
- const first = await zarr.open(grp.resolve(imgPaths[0]), { kind: "group" });
45
- const imgAttrs = utils.resolveAttrs(first.attrs);
46
-
47
- utils.assert(utils.isMultiscales(imgAttrs), "Path for image is not valid.");
48
- let resolution = imgAttrs.multiscales[0].datasets[0].path;
49
-
50
- // Create loader for every Image.
51
- const promises = imgPaths.map((p) => {
52
- const loc = grp.resolve(utils.join(p, resolution));
53
- // @ts-expect-error - ok flag to avoid loading unused attrs
54
- const arr: zarr.Array<zarr.DataType, zarr.Readable> = zarr.open(loc, { kind: "array", attrs: false });
55
- return arr;
56
- });
57
- const data = await Promise.all(promises);
58
- const axes = utils.getNgffAxes(imgAttrs.multiscales);
59
- const axis_labels = utils.getNgffAxisLabels(axes);
60
-
61
- const tileSize = utils.guessTileSize(data[0]);
62
- const loaders = utils.range(rows).flatMap((row) => {
63
- // filter to remove any empty row/col position
64
- return utils
65
- .range(cols)
66
- .filter((col) => col + row * cols < data.length)
67
- .map((col) => {
68
- const offset = col + row * cols;
69
- return {
70
- name: String(offset),
71
- row,
72
- col,
73
- loader: new ZarrPixelSource(data[offset], { labels: axis_labels, tileSize }),
74
- };
75
- });
76
- });
77
-
78
- let meta: Meta;
79
- if (utils.isOmeMultiscales(imgAttrs)) {
80
- meta = parseOmeroMeta(imgAttrs.omero, axes);
81
- } else {
82
- const lowres = loaders.at(-1);
83
- utils.assert(lowres, "Expected at least one resolution, found none.");
84
- meta = await defaultMeta(lowres.loader, axis_labels);
85
- }
86
-
87
- const sourceData: SourceData = {
88
- loaders,
89
- ...meta,
90
- axis_labels,
91
- loader: [loaders[0].loader],
92
- model_matrix: utils.parseMatrix(config.model_matrix),
93
- defaults: {
94
- selection: meta.defaultSelection,
95
- colormap: config.colormap ?? "",
96
- opacity: config.opacity ?? 1,
97
- },
98
- name: `Well ${row}${col}`,
99
- };
100
-
101
- if (acquisitions.length > 0) {
102
- // To show acquisition chooser in UI
103
- sourceData.acquisitions = acquisitions;
104
- sourceData.acquisitionId = acquisitionId || -1;
105
- }
106
-
107
- sourceData.rows = rows;
108
- sourceData.columns = cols;
109
- sourceData.onClick = (info: OnClickData) => {
110
- let gridCoord = info.gridCoord;
111
- if (!gridCoord) {
112
- return;
113
- }
114
- const { row, column } = gridCoord;
115
- let imgSource = undefined;
116
- if (typeof config.source === "string" && grp.path && !Number.isNaN(row) && !Number.isNaN(column)) {
117
- const field = row * cols + column;
118
- imgSource = utils.join(config.source, imgPaths[field]);
119
- }
120
- if (config.onClick) {
121
- info.layer = undefined;
122
- info.imageSource = imgSource;
123
- config.onClick(info);
124
- } else if (imgSource) {
125
- window.open(`${window.location.origin + window.location.pathname}?source=${imgSource}`);
126
- }
127
- };
128
-
129
- return sourceData;
130
- }
131
-
132
- export async function loadPlate(
133
- config: ImageLayerConfig,
134
- grp: zarr.Group<zarr.Readable>,
135
- plateAttrs: Ome.Plate,
136
- ): Promise<SourceData> {
137
- utils.assert(plateAttrs?.rows || plateAttrs?.columns, "Plate .zattrs missing rows, columns or wells");
138
-
139
- const rows = plateAttrs.rows.map((row) => row.name);
140
- const columns = plateAttrs.columns.map((row) => row.name);
141
-
142
- // Fields are by index and we assume at least 1 per Well
143
- const wellPaths = plateAttrs.wells.map((well) => well.path);
144
- const zarrVersion = await utils.guessZarrVersion(grp);
145
-
146
- // Use first image as proxy for others.
147
- const wellAttrs = await utils.getAttrsOnly<{ well: Ome.Well }>(grp, {
148
- path: wellPaths[0],
149
- zarrVersion,
150
- });
151
- utils.assert("well" in wellAttrs, "Path for image is not valid, not a well.");
152
-
153
- const imgPath = wellAttrs.well.images[0].path;
154
- const imgAttrs = await utils.getAttrsOnly<Ome.Attrs>(grp, {
155
- path: utils.join(wellPaths[0], imgPath),
156
- zarrVersion,
157
- });
158
- utils.assert("multiscales" in imgAttrs, "Path for image is not valid.");
159
-
160
- // Lowest resolution is the 'path' of the last 'dataset' from the first multiscales
161
- const { datasets } = imgAttrs.multiscales[0];
162
- const resolution = datasets[datasets.length - 1].path;
163
-
164
- async function getImgPath(wellPath: string) {
165
- const wellAttrs = await utils.getAttrsOnly<{ well: Ome.Well }>(grp, {
166
- path: wellPath,
167
- zarrVersion,
168
- });
169
- utils.assert("well" in wellAttrs, "Path for image is not valid, not a well.");
170
- return utils.join(wellPath, wellAttrs.well.images[0].path);
171
- }
172
- const wellImagePaths = await Promise.all(wellPaths.map(getImgPath));
173
-
174
- // Create loader for every Well. Some loaders may be undefined if Wells are missing.
175
- const mapper = async ([key, path]: string[]) => {
176
- // @ts-expect-error - we don't need the meta for these arrays
177
- let arr: zarr.Array<zarr.DataType, zarr.Readable> = await zarr.open(grp.resolve(path), {
178
- kind: "array",
179
- attrs: false,
180
- });
181
- return [key, arr] as const;
182
- };
183
-
184
- const promises = await pMap(
185
- wellImagePaths.map((p) => [p, utils.join(p, resolution)]),
186
- mapper,
187
- { concurrency: 10 },
188
- );
189
- const data = await Promise.all(promises);
190
- const axes = utils.getNgffAxes(imgAttrs.multiscales);
191
- const axis_labels = utils.getNgffAxisLabels(axes);
192
- const tileSize = utils.guessTileSize(data[0][1]);
193
- const loaders = data.map((d) => {
194
- const [row, col] = d[0].split("/");
195
- return {
196
- name: `${row}${col}`,
197
- row: rows.indexOf(row),
198
- col: columns.indexOf(col),
199
- loader: new ZarrPixelSource(d[1], { labels: axis_labels, tileSize }),
200
- };
201
- });
202
- let meta: Meta;
203
- if ("omero" in imgAttrs) {
204
- meta = parseOmeroMeta(imgAttrs.omero, axes);
205
- } else {
206
- const lowres = loaders.at(-1);
207
- utils.assert(lowres, "Expected at least one resolution, found none.");
208
- meta = await defaultMeta(lowres.loader, axis_labels);
209
- }
210
-
211
- // Load Image to use for channel names, rendering settings, sizeZ, sizeT etc.
212
- const sourceData: SourceData = {
213
- loaders,
214
- ...meta,
215
- axis_labels,
216
- loader: [loaders[0].loader],
217
- model_matrix: utils.parseMatrix(config.model_matrix),
218
- defaults: {
219
- selection: meta.defaultSelection,
220
- colormap: config.colormap ?? "",
221
- opacity: config.opacity ?? 1,
222
- },
223
- name: plateAttrs.name || "Plate",
224
- rows: rows.length,
225
- columns: columns.length,
226
- };
227
- // Us onClick from image config or Open Well in new window
228
- sourceData.onClick = (info: OnClickData) => {
229
- let gridCoord = info.gridCoord;
230
- if (!gridCoord) {
231
- return;
232
- }
233
- const { row, column } = gridCoord;
234
- let imgSource = undefined;
235
- if (typeof config.source === "string" && grp.path && !Number.isNaN(row) && !Number.isNaN(column)) {
236
- imgSource = utils.join(config.source, rows[row], columns[column]);
237
- }
238
- if (config.onClick) {
239
- info.layer = undefined;
240
- info.imageSource = imgSource;
241
- config.onClick(info);
242
- } else if (imgSource) {
243
- window.open(`${window.location.origin + window.location.pathname}?source=${imgSource}`);
244
- }
245
- };
246
- return sourceData;
247
- }
248
-
249
- export async function loadOmeMultiscales(
250
- config: ImageLayerConfig,
251
- grp: zarr.Group<zarr.Readable>,
252
- attrs: { multiscales: Ome.Multiscale[] },
253
- ): Promise<SourceData> {
254
- const { name, opacity = 1, colormap = "" } = config;
255
- const data = await utils.loadMultiscales(grp, attrs.multiscales);
256
- const axes = utils.getNgffAxes(attrs.multiscales);
257
- const axis_labels = utils.getNgffAxisLabels(axes);
258
- const tileSize = utils.guessTileSize(data[0]);
259
- let meta: Meta;
260
- if (utils.isOmeMultiscales(attrs)) {
261
- meta = parseOmeroMeta(attrs.omero, axes);
262
- } else {
263
- const lowresArray = data.at(-1);
264
- utils.assert(lowresArray, "Expected at least one resolution in multiscales, found none.");
265
- const lowresSource = new ZarrPixelSource(lowresArray, { labels: axis_labels, tileSize });
266
- meta = await defaultMeta(lowresSource, axis_labels);
267
- }
268
- const physicalSizes = utils.getPhysicalSizes(utils.resolveAttrs(attrs));
269
- const loader = data.map(
270
- (arr, i) =>
271
- new ZarrPixelSource(arr, {
272
- labels: axis_labels,
273
- tileSize,
274
- ...(i === 0 ? { meta: { physicalSizes } } : {}),
275
- }),
276
- );
277
- const labels = await resolveOmeLabelsFromMultiscales(grp);
278
- return {
279
- loader: loader,
280
- axis_labels,
281
- model_matrix: config.model_matrix
282
- ? utils.parseMatrix(config.model_matrix)
283
- : utils.coordinateTransformationsToMatrix(attrs.multiscales),
284
- defaults: {
285
- selection: meta.defaultSelection,
286
- colormap,
287
- opacity,
288
- },
289
- ...meta,
290
- name: meta.name ?? name,
291
- labels: await Promise.all(labels.map((name) => loadOmeImageLabel(grp.resolve("labels"), name))),
292
- };
293
- }
294
-
295
- async function loadOmeImageLabel(root: zarr.Location<zarr.Readable>, name: string): Promise<ImageLabels[number]> {
296
- const grp = await zarr.open(root.resolve(name), { kind: "group" });
297
- const attrs = utils.resolveAttrs(grp.attrs);
298
- utils.assert(utils.isOmeImageLabel(attrs), "No 'image-label' metadata.");
299
- const data = await utils.loadMultiscales(grp, attrs.multiscales);
300
- const baseResolution = data.at(0);
301
- utils.assert(baseResolution, "No base resolution found for multiscale labels.");
302
- const tileSize = utils.guessTileSize(baseResolution);
303
- const axes = utils.getNgffAxes(attrs.multiscales);
304
- const labels = utils.getNgffAxisLabels(axes);
305
- const colors = (attrs["image-label"].colors ?? []).map((d) => ({ labelValue: d["label-value"], rgba: d.rgba }));
306
- return {
307
- name,
308
- modelMatrix: utils.coordinateTransformationsToMatrix(attrs.multiscales),
309
- loader: data.map((arr) => new ZarrPixelSource(arr, { labels, tileSize })),
310
- colors: colors.length > 0 ? colors : undefined,
311
- };
312
- }
313
-
314
- async function resolveOmeLabelsFromMultiscales(grp: zarr.Group<zarr.Readable>): Promise<Array<string>> {
315
- return zarr
316
- .open(grp.resolve("labels"), { kind: "group" })
317
- .then(({ attrs }) => (utils.resolveAttrs(attrs).labels ?? []) as Array<string>)
318
- .catch((e) => {
319
- utils.rethrowUnless(e, zarr.NodeNotFoundError);
320
- return [];
321
- });
322
- }
323
-
324
- type Meta = {
325
- name: string | undefined;
326
- names: Array<string>;
327
- colors: Array<string>;
328
- contrast_limits: Array<[number, number] | undefined>;
329
- visibilities: Array<boolean>;
330
- channel_axis: number | null;
331
- defaultSelection: Array<number>;
332
- };
333
-
334
- async function defaultMeta(loader: ZarrPixelSource, axis_labels: string[]): Promise<Meta> {
335
- const channel_axis = axis_labels.indexOf("c");
336
- const channel_count = channel_axis === -1 ? 1 : loader.shape[channel_axis];
337
- const visibilities = utils.getDefaultVisibilities(channel_count);
338
- const contrast_limits = await utils.calcConstrastLimits(loader, channel_axis, visibilities);
339
- const colors = utils.getDefaultColors(channel_count, visibilities);
340
- return {
341
- name: "Image",
342
- names: utils.range(channel_count).map((i) => `channel_${i}`),
343
- colors,
344
- contrast_limits,
345
- visibilities,
346
- channel_axis: axis_labels.includes("c") ? axis_labels.indexOf("c") : null,
347
- defaultSelection: axis_labels.map(() => 0),
348
- };
349
- }
350
-
351
- function parseOmeroMeta({ rdefs, channels, name }: Ome.Omero, axes: Ome.Axis[]): Meta {
352
- const t = rdefs?.defaultT ?? 0;
353
- const z = rdefs?.defaultZ ?? 0;
354
- const greyscale = rdefs?.model === "greyscale";
355
-
356
- const colors: string[] = [];
357
- const contrast_limits: [min: number, max: number][] = [];
358
- const visibilities: boolean[] = [];
359
- const names: string[] = [];
360
-
361
- channels.forEach((c, index) => {
362
- colors.push(c.color);
363
- contrast_limits.push([c.window.start, c.window.end]);
364
- visibilities.push(c.active);
365
- names.push(c.label || `${index}`);
366
- });
367
-
368
- if (greyscale && colors.length === 1) {
369
- colors[0] = "FFFFFF";
370
- }
371
-
372
- const defaultSelection = axes.map((axis) => {
373
- if (axis.type === "time") return t;
374
- if (axis.name === "z") return z;
375
- return 0;
376
- });
377
- const channel_axis = axes.findIndex((axis) => axis.type === "channel");
378
-
379
- return {
380
- name,
381
- names,
382
- colors,
383
- contrast_limits,
384
- visibilities,
385
- channel_axis,
386
- defaultSelection,
387
- };
388
- }
package/src/state.ts DELETED
@@ -1,202 +0,0 @@
1
- import { type Atom, atom } from "jotai";
2
- import { atomFamily, splitAtom, waitForAll } from "jotai/utils";
3
- import { RedirectError, rethrowUnless } from "./utils";
4
-
5
- import type { Deck, Layer } from "deck.gl";
6
- import type { PrimitiveAtom } from "jotai";
7
- import type { AtomFamily } from "jotai/vanilla/utils/atomFamily";
8
- import type { Matrix4 } from "math.gl";
9
- import type * as zarr from "zarrita";
10
- import type { ZarrPixelSource } from "./ZarrPixelSource";
11
- import { initLayerStateFromSource } from "./io";
12
-
13
- import { GridLayer, type GridLayerProps, type GridLoader } from "./layers/grid-layer";
14
- import { LabelLayer, type LabelLayerProps, type OmeColor } from "./layers/label-layer";
15
- import {
16
- ImageLayer,
17
- type ImageLayerProps,
18
- MultiscaleImageLayer,
19
- type MultiscaleImageLayerProps,
20
- } from "./layers/viv-layers";
21
-
22
- export interface ViewState {
23
- zoom: number;
24
- target: [number, number];
25
- width?: number;
26
- height?: number;
27
- }
28
-
29
- interface BaseConfig {
30
- source: string | zarr.Readable;
31
- axis_labels?: string[];
32
- name?: string;
33
- colormap?: string;
34
- opacity?: number;
35
- acquisition?: string;
36
- model_matrix?: string | number[];
37
- onClick?: (e: unknown) => void;
38
- }
39
-
40
- export interface MultichannelConfig extends BaseConfig {
41
- colors?: string[];
42
- channel_axis?: number;
43
- contrast_limits?: [min: number, max: number][];
44
- names?: string[];
45
- visibilities?: boolean[];
46
- }
47
-
48
- export interface SingleChannelConfig extends BaseConfig {
49
- color?: string;
50
- contrast_limits?: [min: number, max: number];
51
- visibility?: boolean;
52
- }
53
-
54
- export type ImageLayerConfig = MultichannelConfig | SingleChannelConfig;
55
-
56
- export type OnClickData = Record<string, unknown> & {
57
- gridCoord?: { row: number; column: number };
58
- };
59
-
60
- export type ImageLabels = Array<{
61
- name: string;
62
- loader: ZarrPixelSource[];
63
- modelMatrix: Matrix4;
64
- colors?: ReadonlyArray<OmeColor>;
65
- }>;
66
-
67
- export type SourceData = {
68
- loader: ZarrPixelSource[];
69
- loaders?: GridLoader[]; // for OME plates
70
- rows?: number;
71
- columns?: number;
72
- acquisitions?: Ome.Acquisition[];
73
- acquisitionId?: number;
74
- name?: string;
75
- channel_axis: number | null;
76
- colors: string[];
77
- names: string[];
78
- contrast_limits: ([min: number, max: number] | undefined)[];
79
- visibilities: boolean[];
80
- defaults: {
81
- selection: number[];
82
- colormap: string;
83
- opacity: number;
84
- };
85
- model_matrix: Matrix4;
86
- axis_labels: string[];
87
- onClick?: (e: OnClickData) => void;
88
- labels?: ImageLabels;
89
- };
90
-
91
- type LayerType = "image" | "multiscale" | "grid";
92
- type LayerPropsMap = {
93
- image: ImageLayerProps;
94
- multiscale: MultiscaleImageLayerProps;
95
- grid: GridLayerProps;
96
- };
97
-
98
- export type LayerState<T extends LayerType = LayerType> = {
99
- kind: T;
100
- layerProps: LayerPropsMap[T];
101
- on: boolean;
102
- labels?: Array<{
103
- layerProps: Omit<LabelLayerProps, "selection">;
104
- on: boolean;
105
- transformSourceSelection: (sourceSelection: Array<number>) => Array<number>;
106
- }>;
107
- };
108
-
109
- type WithId<T> = T & { id: string };
110
-
111
- export const viewStateAtom = atom<ViewState | null>(null);
112
- export const sourceErrorAtom = atom<string | null>(null);
113
-
114
- export interface Redirect {
115
- url: string;
116
- message: string;
117
- }
118
- export const redirectObjAtom = atom<Redirect | null>(null);
119
-
120
- export const viewportAtom = atom<Deck | null>(null);
121
-
122
- export const sourceInfoAtom = atom<WithId<SourceData>[]>([]);
123
-
124
- export const addImageAtom = atom(null, async (get, set, config: ImageLayerConfig) => {
125
- const { createSourceData } = await import("./io");
126
- const id = Math.random().toString(36).slice(2);
127
-
128
- try {
129
- const sourceData = await createSourceData(config);
130
- const prevSourceInfo = get(sourceInfoAtom);
131
- if (!sourceData.name) {
132
- sourceData.name = `image_${Object.keys(prevSourceInfo).length}`;
133
- }
134
- set(sourceInfoAtom, [...prevSourceInfo, { id, ...sourceData }]);
135
- } catch (err) {
136
- rethrowUnless(err, Error);
137
- if (err instanceof RedirectError) {
138
- set(redirectObjAtom, { message: err.message, url: err.url });
139
- } else {
140
- set(sourceErrorAtom, err.message);
141
- }
142
- }
143
- });
144
-
145
- export const sourceInfoAtomAtoms = splitAtom(sourceInfoAtom);
146
-
147
- export const layerFamilyAtom: AtomFamily<WithId<SourceData>, PrimitiveAtom<WithId<LayerState>>> = atomFamily(
148
- (param: WithId<SourceData>) => atom({ ...initLayerStateFromSource(param), id: param.id }),
149
- (a, b) => a.id === b.id,
150
- );
151
-
152
- export type VizarrLayer =
153
- | Layer<MultiscaleImageLayerProps>
154
- | Layer<ImageLayerProps>
155
- | Layer<GridLayerProps>
156
- | Layer<LabelLayerProps>;
157
-
158
- const LayerConstructors = {
159
- image: ImageLayer,
160
- multiscale: MultiscaleImageLayer,
161
- grid: GridLayer,
162
- } as const;
163
-
164
- const layerInstanceFamily = atomFamily((a: Atom<LayerState>) =>
165
- atom((get) => {
166
- const { on, layerProps, kind } = get(a);
167
- if (!on) {
168
- return null;
169
- }
170
- const Layer = LayerConstructors[kind];
171
- // @ts-expect-error - TS can't resolve that Layer & layerProps bound together
172
- return new Layer({ ...layerProps, pickable: layerProps.pickable ?? false }) as VizarrLayer;
173
- }),
174
- );
175
-
176
- const imageLabelsIstanceFamily = atomFamily((a: Atom<LayerState>) =>
177
- atom((get) => {
178
- const { on, labels, layerProps } = get(a);
179
- if (!on || !labels) {
180
- return [];
181
- }
182
- return labels.map((label) =>
183
- label.on
184
- ? new LabelLayer({
185
- ...label.layerProps,
186
- selection: label.transformSourceSelection(layerProps.selections[0]),
187
- pickable: true,
188
- })
189
- : null,
190
- );
191
- }),
192
- );
193
-
194
- export const layerAtoms = atom((get) => {
195
- const layerAtoms = [];
196
- for (const sourceAtom of get(sourceInfoAtomAtoms)) {
197
- const layerStateAtom = layerFamilyAtom(get(sourceAtom));
198
- layerAtoms.push(layerInstanceFamily(layerStateAtom));
199
- layerAtoms.push(imageLabelsIstanceFamily(layerStateAtom));
200
- }
201
- return get(waitForAll(layerAtoms)).flat();
202
- });