@vitessce/spatial-utils 3.1.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{deflate-6fd0bca9.js → deflate-3240dc7e.js} +1 -1
- package/dist/{index-551fa504.js → index-ca4e31e3.js} +1007 -82
- package/dist/index.js +15 -2
- package/dist/{jpeg-d67d9d82.js → jpeg-76bff48f.js} +1 -1
- package/dist/{lerc-522345a3.js → lerc-1ad8fc7c.js} +1 -1
- package/dist/{lzw-c3783269.js → lzw-c5e8dff0.js} +1 -1
- package/dist/{packbits-209c03d7.js → packbits-ccf26149.js} +1 -1
- package/dist/{raw-8e22b1c4.js → raw-446b6beb.js} +1 -1
- package/dist/{webimage-a5d2769c.js → webimage-bf1c5425.js} +1 -1
- package/dist-tsc/constants.d.ts +2 -0
- package/dist-tsc/constants.d.ts.map +1 -0
- package/dist-tsc/constants.js +12 -0
- package/dist-tsc/index.d.ts +3 -1
- package/dist-tsc/index.js +3 -1
- package/dist-tsc/layer-controller.d.ts +10 -0
- package/dist-tsc/layer-controller.d.ts.map +1 -1
- package/dist-tsc/layer-controller.js +32 -5
- package/dist-tsc/layer-controller.test.d.ts +2 -0
- package/dist-tsc/layer-controller.test.d.ts.map +1 -0
- package/dist-tsc/layer-controller.test.js +22 -0
- package/dist-tsc/spatial.d.ts +25 -4
- package/dist-tsc/spatial.d.ts.map +1 -1
- package/dist-tsc/spatial.js +124 -3
- package/dist-tsc/spatial.test.js +156 -1
- package/package.json +3 -3
- package/src/constants.js +14 -0
- package/src/index.js +17 -0
- package/src/layer-controller.js +34 -5
- package/src/layer-controller.test.js +24 -0
- package/src/spatial.js +141 -3
- package/src/spatial.test.js +160 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D, e, a, b, c, d, G, n, l, m, h, i, f, j, k, s } from "./index-
|
|
1
|
+
import { D, e, a, b, c, d, C, G, z, r, n, A, y, w, l, m, x, v, h, q, o, i, f, j, k, t, p, s, u } from "./index-ca4e31e3.js";
|
|
2
2
|
import "react";
|
|
3
3
|
export {
|
|
4
4
|
D as DEFAULT_CELLS_LAYER,
|
|
@@ -7,14 +7,27 @@ export {
|
|
|
7
7
|
b as DEFAULT_NEIGHBORHOODS_LAYER,
|
|
8
8
|
c as DEFAULT_RASTER_DOMAIN_TYPE,
|
|
9
9
|
d as DEFAULT_RASTER_LAYER_PROPS,
|
|
10
|
+
C as DOMAINS,
|
|
10
11
|
G as GLOBAL_LABELS,
|
|
12
|
+
z as abbreviateNumber,
|
|
13
|
+
r as canLoadResolution,
|
|
11
14
|
n as coordinateTransformationsToMatrix,
|
|
15
|
+
A as filterSelection,
|
|
16
|
+
y as getBoundingCube,
|
|
17
|
+
w as getMultiSelectionStats,
|
|
12
18
|
l as getNgffAxes,
|
|
13
19
|
m as getNgffAxesForTiff,
|
|
20
|
+
x as getPhysicalSizeScalingMatrix,
|
|
21
|
+
v as getSingleSelectionStats,
|
|
14
22
|
h as getSourceFromLoader,
|
|
23
|
+
q as getStatsForResolution,
|
|
24
|
+
o as hexToRgb,
|
|
15
25
|
i as initializeLayerChannels,
|
|
16
26
|
f as initializeRasterLayersAndChannels,
|
|
17
27
|
j as isInterleaved,
|
|
18
28
|
k as isRgb,
|
|
19
|
-
|
|
29
|
+
t as normalizeCoordinateTransformations,
|
|
30
|
+
p as physicalSizeToMatrix,
|
|
31
|
+
s as square,
|
|
32
|
+
u as toRgbUIString
|
|
20
33
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
|
|
2
|
-
import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-
|
|
2
|
+
import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-ca4e31e3.js";
|
|
3
3
|
import "react";
|
|
4
4
|
const LercParameters = {
|
|
5
5
|
Version: 0,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.js"],"names":[],"mappings":"AAaA,yBAAoC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { viv } from '@vitessce/gl';
|
|
2
|
+
function getDomains() {
|
|
3
|
+
const domains = {};
|
|
4
|
+
const needMin = ['Int8', 'Int16', 'Int32'];
|
|
5
|
+
Object.keys(viv.DTYPE_VALUES).forEach((dtype) => {
|
|
6
|
+
const { max } = viv.DTYPE_VALUES[dtype];
|
|
7
|
+
const min = needMin.includes(dtype) ? -(max + 1) : 0;
|
|
8
|
+
domains[dtype] = [min, max];
|
|
9
|
+
});
|
|
10
|
+
return domains;
|
|
11
|
+
}
|
|
12
|
+
export const DOMAINS = getDomains();
|
package/dist-tsc/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { DOMAINS } from "./constants.js";
|
|
2
|
+
export { GLOBAL_LABELS, DEFAULT_CELLS_LAYER, DEFAULT_MOLECULES_LAYER, DEFAULT_NEIGHBORHOODS_LAYER, DEFAULT_RASTER_DOMAIN_TYPE, DEFAULT_RASTER_LAYER_PROPS, DEFAULT_LAYER_TYPE_ORDERING, square, initializeLayerChannels, initializeRasterLayersAndChannels, getSourceFromLoader, isInterleaved, isRgb, getNgffAxes, getNgffAxesForTiff, coordinateTransformationsToMatrix, physicalSizeToMatrix, hexToRgb, getStatsForResolution, canLoadResolution, normalizeCoordinateTransformations } from "./spatial.js";
|
|
3
|
+
export { toRgbUIString, getSingleSelectionStats, getMultiSelectionStats, getPhysicalSizeScalingMatrix, getBoundingCube, abbreviateNumber, filterSelection } from "./layer-controller.js";
|
|
2
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist-tsc/index.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export { GLOBAL_LABELS, DEFAULT_CELLS_LAYER, DEFAULT_MOLECULES_LAYER, DEFAULT_NEIGHBORHOODS_LAYER, DEFAULT_RASTER_DOMAIN_TYPE, DEFAULT_RASTER_LAYER_PROPS, DEFAULT_LAYER_TYPE_ORDERING, square, initializeLayerChannels, initializeRasterLayersAndChannels, getSourceFromLoader, isInterleaved, isRgb, getNgffAxes, getNgffAxesForTiff, coordinateTransformationsToMatrix, } from './spatial.js';
|
|
1
|
+
export { GLOBAL_LABELS, DEFAULT_CELLS_LAYER, DEFAULT_MOLECULES_LAYER, DEFAULT_NEIGHBORHOODS_LAYER, DEFAULT_RASTER_DOMAIN_TYPE, DEFAULT_RASTER_LAYER_PROPS, DEFAULT_LAYER_TYPE_ORDERING, square, initializeLayerChannels, initializeRasterLayersAndChannels, getSourceFromLoader, isInterleaved, isRgb, getNgffAxes, getNgffAxesForTiff, coordinateTransformationsToMatrix, physicalSizeToMatrix, hexToRgb, getStatsForResolution, canLoadResolution, normalizeCoordinateTransformations, } from './spatial.js';
|
|
2
|
+
export { toRgbUIString, getSingleSelectionStats, getMultiSelectionStats, getPhysicalSizeScalingMatrix, getBoundingCube, abbreviateNumber, filterSelection, } from './layer-controller.js';
|
|
3
|
+
export { DOMAINS, } from './constants.js';
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure that the channel selection object does not have
|
|
3
|
+
* extra dimension keys, as this will cause Viv functions
|
|
4
|
+
* to throw errors about not being able to access the data.
|
|
5
|
+
* @param {*} loader
|
|
6
|
+
* @param {object} selection Mapping from dimension label to slice index.
|
|
7
|
+
* @returns {object} The filtered selection.
|
|
8
|
+
*/
|
|
9
|
+
export function filterSelection(loader: any, selection: object): object;
|
|
1
10
|
/**
|
|
2
11
|
* Get physical size scaling Matrix4
|
|
3
12
|
* @param {Object} loader PixelSource
|
|
@@ -11,6 +20,7 @@ export function getPhysicalSizeScalingMatrix(loader: Object): Object;
|
|
|
11
20
|
*/
|
|
12
21
|
export function getBoundingCube(loader: Object): any[];
|
|
13
22
|
export function abbreviateNumber(value: any): any;
|
|
23
|
+
export function toRgbUIString(on: any, arr: any, theme: any): string;
|
|
14
24
|
export function getSingleSelectionStats({ loader, selection, use3d }: Object): Object;
|
|
15
25
|
export function getMultiSelectionStats({ loader, selections, use3d }: {
|
|
16
26
|
loader: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layer-controller.d.ts","sourceRoot":"","sources":["../src/layer-controller.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"layer-controller.d.ts","sourceRoot":"","sources":["../src/layer-controller.js"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,wDAHW,MAAM,GACJ,MAAM,CASlB;AAwED;;;;GAIG;AACH,qDAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;GAIG;AACH,wCAHW,MAAM,SAchB;AAED,kDAwBC;AAvJM,qEAKN;AAyEM,sEALI,MAAM,GAGJ,MAAM,CAOlB;AAEM;;;;;;;GAON"}
|
|
@@ -1,25 +1,52 @@
|
|
|
1
|
-
import { Matrix4 } from 'math.gl';
|
|
2
1
|
import { viv } from '@vitessce/gl';
|
|
2
|
+
import { fromEntries } from '@vitessce/utils';
|
|
3
|
+
import { Matrix4 } from 'math.gl';
|
|
4
|
+
// Returns an rgb string for display, and changes the color (arr)
|
|
5
|
+
// to use a grey for light theme + white color or if the colormap is on.
|
|
6
|
+
export const toRgbUIString = (on, arr, theme) => {
|
|
7
|
+
const color = on || (theme === 'light' && arr?.every(i => i === 255))
|
|
8
|
+
? [220, 220, 220]
|
|
9
|
+
: arr;
|
|
10
|
+
return `rgb(${color})`;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Ensure that the channel selection object does not have
|
|
14
|
+
* extra dimension keys, as this will cause Viv functions
|
|
15
|
+
* to throw errors about not being able to access the data.
|
|
16
|
+
* @param {*} loader
|
|
17
|
+
* @param {object} selection Mapping from dimension label to slice index.
|
|
18
|
+
* @returns {object} The filtered selection.
|
|
19
|
+
*/
|
|
20
|
+
export function filterSelection(loader, selection) {
|
|
21
|
+
if (!selection) {
|
|
22
|
+
return selection;
|
|
23
|
+
}
|
|
24
|
+
const data = Array.isArray(loader) ? loader[loader.length - 1] : loader;
|
|
25
|
+
const { labels } = data;
|
|
26
|
+
return fromEntries(Object.entries(selection).filter(([key]) => labels.includes(key)));
|
|
27
|
+
}
|
|
3
28
|
async function getSingleSelectionStats2D({ loader, selection }) {
|
|
4
29
|
const data = Array.isArray(loader) ? loader[loader.length - 1] : loader;
|
|
5
|
-
const
|
|
30
|
+
const filteredSelection = filterSelection(loader, selection);
|
|
31
|
+
const raster = await data.getRaster({ selection: filteredSelection });
|
|
6
32
|
const selectionStats = viv.getChannelStats(raster.data);
|
|
7
33
|
const { domain, contrastLimits: slider } = selectionStats;
|
|
8
34
|
return { domain, slider };
|
|
9
35
|
}
|
|
10
36
|
async function getSingleSelectionStats3D({ loader, selection }) {
|
|
11
37
|
const lowResSource = loader[loader.length - 1];
|
|
38
|
+
const filteredSelection = filterSelection(lowResSource, selection);
|
|
12
39
|
const { shape, labels } = lowResSource;
|
|
13
40
|
// eslint-disable-next-line no-bitwise
|
|
14
41
|
const sizeZ = shape[labels.indexOf('z')] >> (loader.length - 1);
|
|
15
42
|
const raster0 = await lowResSource.getRaster({
|
|
16
|
-
selection: { ...
|
|
43
|
+
selection: { ...filteredSelection, z: 0 },
|
|
17
44
|
});
|
|
18
45
|
const rasterMid = await lowResSource.getRaster({
|
|
19
|
-
selection: { ...
|
|
46
|
+
selection: { ...filteredSelection, z: Math.floor(sizeZ / 2) },
|
|
20
47
|
});
|
|
21
48
|
const rasterTop = await lowResSource.getRaster({
|
|
22
|
-
selection: { ...
|
|
49
|
+
selection: { ...filteredSelection, z: Math.max(0, sizeZ - 1) },
|
|
23
50
|
});
|
|
24
51
|
const stats0 = viv.getChannelStats(raster0.data);
|
|
25
52
|
const statsMid = viv.getChannelStats(rasterMid.data);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-controller.test.d.ts","sourceRoot":"","sources":["../src/layer-controller.test.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { toRgbUIString } from './layer-controller.js';
|
|
3
|
+
const GREY = [220, 220, 220];
|
|
4
|
+
describe('layer-controller/RasterChannelController.js', () => {
|
|
5
|
+
describe('toRgbUIString()', () => {
|
|
6
|
+
it('Maps color value to itself when colormap off and not white with dark theme', () => {
|
|
7
|
+
expect(toRgbUIString(false, [200, 200, 200], 'dark')).toEqual(`rgb(${[200, 200, 200]})`);
|
|
8
|
+
});
|
|
9
|
+
it('Maps color value to grey when colormap on with dark theme', () => {
|
|
10
|
+
expect(toRgbUIString(true, [200, 200, 200], 'dark')).toEqual(`rgb(${GREY})`);
|
|
11
|
+
});
|
|
12
|
+
it('Maps color value to grey when colormap on with light theme', () => {
|
|
13
|
+
expect(toRgbUIString(true, [200, 200, 200], 'light')).toEqual(`rgb(${GREY})`);
|
|
14
|
+
});
|
|
15
|
+
it('Maps color value to grey when colormap off and white with light theme', () => {
|
|
16
|
+
expect(toRgbUIString(false, [255, 255, 255], 'light')).toEqual(`rgb(${GREY})`);
|
|
17
|
+
});
|
|
18
|
+
it('Maps color value to grey when colormap off and white with light theme', () => {
|
|
19
|
+
expect(toRgbUIString(false, [200, 200, 200], 'light')).toEqual(`rgb(${[200, 200, 200]})`);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
package/dist-tsc/spatial.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export function getSourceFromLoader(loader: object, level?: number | undefined):
|
|
|
13
13
|
* @param {array|null} channels
|
|
14
14
|
*/
|
|
15
15
|
export function isRgb(loader: object, channels: array | null): boolean;
|
|
16
|
+
export function physicalSizeToMatrix(xSize: any, ySize: any, zSize: any, xUnit: any, yUnit: any, zUnit: any): Matrix4;
|
|
16
17
|
/**
|
|
17
18
|
* @param {Array.<number>} shape loader shape
|
|
18
19
|
*/
|
|
@@ -44,13 +45,25 @@ export function getNgffAxesForTiff(dimOrder: any): any;
|
|
|
44
45
|
/**
|
|
45
46
|
* Convert an array of coordinateTransformations objects to a 16-element
|
|
46
47
|
* plain JS array using Matrix4 linear algebra transformation functions.
|
|
47
|
-
* @param {object[]} coordinateTransformations List of objects matching the
|
|
48
|
+
* @param {object[]|undefined} coordinateTransformations List of objects matching the
|
|
48
49
|
* OME-NGFF v0.4 coordinateTransformations spec.
|
|
49
|
-
* @param {object[]} axes Axes in OME-NGFF v0.4 format, objects
|
|
50
|
+
* @param {object[]|undefined} axes Axes in OME-NGFF v0.4 format, objects
|
|
50
51
|
* with { type, name }.
|
|
51
|
-
* @returns {
|
|
52
|
+
* @returns {Matrix4} Array of 16 numbers representing the Matrix4.
|
|
52
53
|
*/
|
|
53
|
-
export function coordinateTransformationsToMatrix(coordinateTransformations: object[], axes: object[]):
|
|
54
|
+
export function coordinateTransformationsToMatrix(coordinateTransformations: object[] | undefined, axes: object[] | undefined): Matrix4;
|
|
55
|
+
export function hexToRgb(hex: any): number[];
|
|
56
|
+
/**
|
|
57
|
+
* Normalize coordinate transformations to the OME-NGFF v0.4 format,
|
|
58
|
+
* despite potentially being in the new format proposed in
|
|
59
|
+
* https://github.com/ome/ngff/pull/138 (As of 2023-09-02).
|
|
60
|
+
* @param {object[]|undefined} coordinateTransformations Value of
|
|
61
|
+
* multiscales[0].coordinateTransformations in either OME-NGFF v0.4 format
|
|
62
|
+
* or that proposed in https://github.com/ome/ngff/pull/138.
|
|
63
|
+
* @param {object[]} datasets Value of multiscales[0].datasets in OME-NGFF v0.4 format.
|
|
64
|
+
* @returns {object[]} Array of coordinateTransformations in OME-NGFF v0.4 format.
|
|
65
|
+
*/
|
|
66
|
+
export function normalizeCoordinateTransformations(coordinateTransformations: object[] | undefined, datasets: object[]): object[];
|
|
54
67
|
export const GLOBAL_LABELS: string[];
|
|
55
68
|
export const DEFAULT_RASTER_DOMAIN_TYPE: "Min/Max";
|
|
56
69
|
export namespace DEFAULT_RASTER_LAYER_PROPS {
|
|
@@ -83,5 +96,13 @@ export namespace DEFAULT_NEIGHBORHOODS_LAYER {
|
|
|
83
96
|
export { visible_3 as visible };
|
|
84
97
|
}
|
|
85
98
|
export const DEFAULT_LAYER_TYPE_ORDERING: string[];
|
|
99
|
+
export function getStatsForResolution(loader: any, resolution: any): {
|
|
100
|
+
height: any;
|
|
101
|
+
width: any;
|
|
102
|
+
depthDownsampled: number;
|
|
103
|
+
totalBytes: number;
|
|
104
|
+
};
|
|
105
|
+
export function canLoadResolution(loader: any, resolution: any): boolean;
|
|
106
|
+
import { Matrix4 } from ".pnpm/@math.gl+core@3.6.3/node_modules/@math.gl/core";
|
|
86
107
|
import { viv } from "@vitessce/gl";
|
|
87
108
|
//# sourceMappingURL=spatial.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spatial.d.ts","sourceRoot":"","sources":["../src/spatial.js"],"names":[],"mappings":"AAOA,wDAEC;AAiCD;;;;;;GAMG;AACH,4CAJW,MAAM,UACN,MAAM,eACJ,MAAM,CAMlB;
|
|
1
|
+
{"version":3,"file":"spatial.d.ts","sourceRoot":"","sources":["../src/spatial.js"],"names":[],"mappings":"AAOA,wDAEC;AAiCD;;;;;;GAMG;AACH,4CAJW,MAAM,UACN,MAAM,eACJ,MAAM,CAMlB;AAoCD;;;;GAIG;AACH,8BAHW,MAAM,YACN,QAAM,IAAI,WAiBpB;AAID,sHAkCC;AAiHD;;GAEG;AACH,qCAFW,MAAO,MAAM,CAAC,WAKxB;AAED;;;;;;GAMG;AACH,gDALW,MAAM,eAEJ,MAAM,EAAE,CAuCpB;AAED;;;;;;;;;GASG;AACH,gEAJW,MAAM,EAAE,sBAER,CAAC,MAAM,EAAE,GAAC,IAAI,CAAC,+CAuEzB;AASD;;;IAuBC;AAED,uDAQC;AAED;;;;;;;;GAQG;AACH,6EANW,MAAM,EAAE,GAAC,SAAS,QAElB,MAAM,EAAE,GAAC,SAAS,GAEhB,OAAO,CA4CnB;AAED,6CAOC;AAED;;;;;;;;;GASG;AACH,8EANW,MAAM,EAAE,GAAC,SAAS,YAGlB,MAAM,EAAE,GACN,MAAM,EAAE,CAiDpB;AAhiBD,qCAAwC;AAExC,mDAAoD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBpD,mDAKE;AAeK;;;;;EAYN;AAEM,yEAkBN"}
|
package/dist-tsc/spatial.js
CHANGED
|
@@ -45,6 +45,31 @@ export function getSourceFromLoader(loader, level) {
|
|
|
45
45
|
const source = Array.isArray(data) ? data[(level || data.length - 1)] : data;
|
|
46
46
|
return source;
|
|
47
47
|
}
|
|
48
|
+
export const getStatsForResolution = (loader, resolution) => {
|
|
49
|
+
const { shape, labels } = loader[resolution];
|
|
50
|
+
const height = shape[labels.indexOf('y')];
|
|
51
|
+
const width = shape[labels.indexOf('x')];
|
|
52
|
+
const depth = shape[labels.indexOf('z')];
|
|
53
|
+
// eslint-disable-next-line no-bitwise
|
|
54
|
+
const depthDownsampled = Math.max(1, depth >> resolution);
|
|
55
|
+
// Check memory allocation limits for Float32Array (used in XR3DLayer for rendering)
|
|
56
|
+
const totalBytes = 4 * height * width * depthDownsampled;
|
|
57
|
+
return {
|
|
58
|
+
height, width, depthDownsampled, totalBytes,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export const canLoadResolution = (loader, resolution) => {
|
|
62
|
+
const { totalBytes, height, width, depthDownsampled, } = getStatsForResolution(loader, resolution);
|
|
63
|
+
const maxHeapSize = window.performance?.memory
|
|
64
|
+
&& window.performance?.memory?.jsHeapSizeLimit / 2;
|
|
65
|
+
const maxSize = maxHeapSize || (2 ** 31) - 1;
|
|
66
|
+
// 2048 is a normal texture size limit although some browsers go larger.
|
|
67
|
+
return (totalBytes < maxSize
|
|
68
|
+
&& height <= 2048
|
|
69
|
+
&& depthDownsampled <= 2048
|
|
70
|
+
&& width <= 2048
|
|
71
|
+
&& depthDownsampled > 1);
|
|
72
|
+
};
|
|
48
73
|
/**
|
|
49
74
|
* Helper method to determine whether pixel data is interleaved and rgb or not.
|
|
50
75
|
* @param {object} loader
|
|
@@ -66,6 +91,39 @@ export function isRgb(loader, channels) {
|
|
|
66
91
|
return false;
|
|
67
92
|
}
|
|
68
93
|
// From spatial/utils.js
|
|
94
|
+
export function physicalSizeToMatrix(xSize, ySize, zSize, xUnit, yUnit, zUnit) {
|
|
95
|
+
const hasZPhyscialSize = Boolean(zSize) && Boolean(zUnit);
|
|
96
|
+
const hasYPhyscialSize = Boolean(ySize) && Boolean(yUnit);
|
|
97
|
+
const hasXPhyscialSize = Boolean(xSize) && Boolean(xUnit);
|
|
98
|
+
if (!hasXPhyscialSize || !hasYPhyscialSize) {
|
|
99
|
+
return (new Matrix4()).identity();
|
|
100
|
+
}
|
|
101
|
+
const sizes = [
|
|
102
|
+
unit(`${xSize} ${xUnit}`.replace('µ', 'u')),
|
|
103
|
+
unit(`${ySize} ${yUnit}`.replace('µ', 'u')),
|
|
104
|
+
];
|
|
105
|
+
if (hasZPhyscialSize) {
|
|
106
|
+
sizes.push(unit(`${zSize} ${zUnit}`.replace('µ', 'u')));
|
|
107
|
+
}
|
|
108
|
+
// Find the ratio of the sizes to get the scaling factor.
|
|
109
|
+
const scale = sizes.map(i => divide(i, unit('1 um')));
|
|
110
|
+
// TODO: is this still needed
|
|
111
|
+
// sizes are special objects with own equals method - see `unit` in declaration
|
|
112
|
+
if (!sizes[0].equals(sizes[1])) {
|
|
113
|
+
// Handle scaling in the Y direction for non-square pixels
|
|
114
|
+
scale[1] = divide(sizes[1], sizes[0]);
|
|
115
|
+
}
|
|
116
|
+
// END TODO: is this still needed
|
|
117
|
+
// Add in z dimension needed for Matrix4 scale API.
|
|
118
|
+
if (!scale[2]) {
|
|
119
|
+
scale[2] = 1;
|
|
120
|
+
}
|
|
121
|
+
// no need to store/use identity scaling
|
|
122
|
+
if (isEqual(scale, [1, 1, 1])) {
|
|
123
|
+
return (new Matrix4()).identity();
|
|
124
|
+
}
|
|
125
|
+
return new Matrix4().scale([...scale]);
|
|
126
|
+
}
|
|
69
127
|
function getMetaWithTransformMatrices(imageMeta, imageLoaders) {
|
|
70
128
|
// Do not fill in transformation matrices if any of the layers specify one.
|
|
71
129
|
const sources = imageLoaders.map(loader => getSourceFromLoader(loader));
|
|
@@ -328,11 +386,11 @@ export function getNgffAxesForTiff(dimOrder) {
|
|
|
328
386
|
/**
|
|
329
387
|
* Convert an array of coordinateTransformations objects to a 16-element
|
|
330
388
|
* plain JS array using Matrix4 linear algebra transformation functions.
|
|
331
|
-
* @param {object[]} coordinateTransformations List of objects matching the
|
|
389
|
+
* @param {object[]|undefined} coordinateTransformations List of objects matching the
|
|
332
390
|
* OME-NGFF v0.4 coordinateTransformations spec.
|
|
333
|
-
* @param {object[]} axes Axes in OME-NGFF v0.4 format, objects
|
|
391
|
+
* @param {object[]|undefined} axes Axes in OME-NGFF v0.4 format, objects
|
|
334
392
|
* with { type, name }.
|
|
335
|
-
* @returns {
|
|
393
|
+
* @returns {Matrix4} Array of 16 numbers representing the Matrix4.
|
|
336
394
|
*/
|
|
337
395
|
export function coordinateTransformationsToMatrix(coordinateTransformations, axes) {
|
|
338
396
|
let mat = (new Matrix4()).identity();
|
|
@@ -373,3 +431,66 @@ export function coordinateTransformationsToMatrix(coordinateTransformations, axe
|
|
|
373
431
|
}
|
|
374
432
|
return mat;
|
|
375
433
|
}
|
|
434
|
+
export function hexToRgb(hex) {
|
|
435
|
+
const result = /^#?([A-F\d]{2})([A-F\d]{2})([A-F\d]{2})$/i.exec(hex);
|
|
436
|
+
return [
|
|
437
|
+
parseInt(result[1].toLowerCase(), 16),
|
|
438
|
+
parseInt(result[2].toLowerCase(), 16),
|
|
439
|
+
parseInt(result[3].toLowerCase(), 16),
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Normalize coordinate transformations to the OME-NGFF v0.4 format,
|
|
444
|
+
* despite potentially being in the new format proposed in
|
|
445
|
+
* https://github.com/ome/ngff/pull/138 (As of 2023-09-02).
|
|
446
|
+
* @param {object[]|undefined} coordinateTransformations Value of
|
|
447
|
+
* multiscales[0].coordinateTransformations in either OME-NGFF v0.4 format
|
|
448
|
+
* or that proposed in https://github.com/ome/ngff/pull/138.
|
|
449
|
+
* @param {object[]} datasets Value of multiscales[0].datasets in OME-NGFF v0.4 format.
|
|
450
|
+
* @returns {object[]} Array of coordinateTransformations in OME-NGFF v0.4 format.
|
|
451
|
+
*/
|
|
452
|
+
export function normalizeCoordinateTransformations(coordinateTransformations, datasets) {
|
|
453
|
+
// "The transformations in the list are applied sequentially and in order."
|
|
454
|
+
// Reference: https://ngff.openmicroscopy.org/0.4/index.html#trafo-md
|
|
455
|
+
let result = [];
|
|
456
|
+
if (Array.isArray(coordinateTransformations)) {
|
|
457
|
+
result = coordinateTransformations.map((transform) => {
|
|
458
|
+
if (transform.input && transform.output) {
|
|
459
|
+
// This is a new-style coordinate transformation.
|
|
460
|
+
// (As proposed in https://github.com/ome/ngff/pull/138)
|
|
461
|
+
const { type } = transform;
|
|
462
|
+
if (type === 'translation') {
|
|
463
|
+
return {
|
|
464
|
+
type,
|
|
465
|
+
translation: transform.translation,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
if (type === 'scale') {
|
|
469
|
+
return {
|
|
470
|
+
type,
|
|
471
|
+
scale: transform.scale,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
if (type === 'identity') {
|
|
475
|
+
return { type };
|
|
476
|
+
}
|
|
477
|
+
console.warn(`Coordinate transformation type "${type}" is not supported.`);
|
|
478
|
+
}
|
|
479
|
+
// Assume it was already an old-style (NGFF v0.4) coordinate transformation.
|
|
480
|
+
return transform;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
if (Array.isArray(datasets?.[0]?.coordinateTransformations)) {
|
|
484
|
+
// "Datasets SHOULD define a transformation from array space
|
|
485
|
+
// to their "native physical space."
|
|
486
|
+
// This transformation SHOULD describe physical pixel spacing
|
|
487
|
+
// and origin only, and therefore SHOULD consist of
|
|
488
|
+
// `scale` and/or `translation` types only.""
|
|
489
|
+
// Reference: https://github.com/ome/ngff/blob/b92f540dc95440f2d6b7012185b09c2b862aa744/latest/transform-details.bs#L99
|
|
490
|
+
result = [
|
|
491
|
+
...datasets[0].coordinateTransformations,
|
|
492
|
+
...result,
|
|
493
|
+
];
|
|
494
|
+
}
|
|
495
|
+
return result;
|
|
496
|
+
}
|
package/dist-tsc/spatial.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { square, coordinateTransformationsToMatrix } from './spatial.js';
|
|
2
|
+
import { square, coordinateTransformationsToMatrix, normalizeCoordinateTransformations, } from './spatial.js';
|
|
3
3
|
const defaultAxes = [
|
|
4
4
|
{ type: 'time', name: 't' },
|
|
5
5
|
{ type: 'channel', name: 'c' },
|
|
@@ -41,4 +41,159 @@ describe('Spatial.js', () => {
|
|
|
41
41
|
]);
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
|
+
describe('normalizeCoordinateTransformations', () => {
|
|
45
|
+
it('does nothing for OME-NGFF v0.4 input', () => {
|
|
46
|
+
const datasets = [
|
|
47
|
+
{
|
|
48
|
+
coordinateTransformations: [
|
|
49
|
+
{
|
|
50
|
+
scale: [
|
|
51
|
+
1,
|
|
52
|
+
0.5002025531914894,
|
|
53
|
+
0.3603981534640209,
|
|
54
|
+
0.3603981534640209,
|
|
55
|
+
],
|
|
56
|
+
type: 'scale',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
path: '0',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
coordinateTransformations: [
|
|
63
|
+
{
|
|
64
|
+
scale: [
|
|
65
|
+
1,
|
|
66
|
+
0.5002025531914894,
|
|
67
|
+
0.7207963069280418,
|
|
68
|
+
0.7207963069280418,
|
|
69
|
+
],
|
|
70
|
+
type: 'scale',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
path: '1',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
coordinateTransformations: [
|
|
77
|
+
{
|
|
78
|
+
scale: [
|
|
79
|
+
1,
|
|
80
|
+
0.5002025531914894,
|
|
81
|
+
1.4415926138560835,
|
|
82
|
+
1.4415926138560835,
|
|
83
|
+
],
|
|
84
|
+
type: 'scale',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
path: '2',
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
expect(normalizeCoordinateTransformations(undefined, datasets)).toEqual([
|
|
91
|
+
// Here, we expect only the first dataset to be used.
|
|
92
|
+
// However, we should eventually support transforms
|
|
93
|
+
// specified for each dataset individually,
|
|
94
|
+
// since there could in theory be irregular ways of downsampling.
|
|
95
|
+
{
|
|
96
|
+
scale: [
|
|
97
|
+
1,
|
|
98
|
+
0.5002025531914894,
|
|
99
|
+
0.3603981534640209,
|
|
100
|
+
0.3603981534640209,
|
|
101
|
+
],
|
|
102
|
+
type: 'scale',
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
it('transforms SpatialData input', () => {
|
|
107
|
+
const newCoordinateTransformations = [
|
|
108
|
+
{
|
|
109
|
+
input: {
|
|
110
|
+
axes: [
|
|
111
|
+
{
|
|
112
|
+
name: 'c',
|
|
113
|
+
type: 'channel',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'y',
|
|
117
|
+
type: 'space',
|
|
118
|
+
unit: 'unit',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'x',
|
|
122
|
+
type: 'space',
|
|
123
|
+
unit: 'unit',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
name: 'cyx',
|
|
127
|
+
},
|
|
128
|
+
output: {
|
|
129
|
+
axes: [
|
|
130
|
+
{
|
|
131
|
+
name: 'c',
|
|
132
|
+
type: 'channel',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'y',
|
|
136
|
+
type: 'space',
|
|
137
|
+
unit: 'unit',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'x',
|
|
141
|
+
type: 'space',
|
|
142
|
+
unit: 'unit',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
name: 'ST8059048',
|
|
146
|
+
},
|
|
147
|
+
scale: [
|
|
148
|
+
1.0,
|
|
149
|
+
8.670500183814605,
|
|
150
|
+
8.670500183814605,
|
|
151
|
+
],
|
|
152
|
+
type: 'scale',
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
const datasets = [
|
|
156
|
+
{
|
|
157
|
+
coordinateTransformations: [
|
|
158
|
+
{
|
|
159
|
+
scale: [
|
|
160
|
+
1.0,
|
|
161
|
+
1.0,
|
|
162
|
+
1.0,
|
|
163
|
+
],
|
|
164
|
+
type: 'scale',
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
path: '0',
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
expect(normalizeCoordinateTransformations(newCoordinateTransformations, datasets)).toEqual([
|
|
171
|
+
// Here, we expect only the first dataset to be used.
|
|
172
|
+
// However, we should eventually support transforms
|
|
173
|
+
// specified for each dataset individually,
|
|
174
|
+
// since there could in theory be irregular ways of downsampling.
|
|
175
|
+
{
|
|
176
|
+
scale: [
|
|
177
|
+
1.0,
|
|
178
|
+
1.0,
|
|
179
|
+
1.0,
|
|
180
|
+
],
|
|
181
|
+
type: 'scale',
|
|
182
|
+
},
|
|
183
|
+
// We expect the dataset transform to be prepended
|
|
184
|
+
// to the newCoordinateTransformations items.
|
|
185
|
+
// We do not check the input/output coordinate systems, but we should eventually allow
|
|
186
|
+
// the user to specify this somehow and use that information to filter which transforms
|
|
187
|
+
// are included here.
|
|
188
|
+
{
|
|
189
|
+
scale: [
|
|
190
|
+
1.0,
|
|
191
|
+
8.670500183814605,
|
|
192
|
+
8.670500183814605,
|
|
193
|
+
],
|
|
194
|
+
type: 'scale',
|
|
195
|
+
},
|
|
196
|
+
]);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
44
199
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/spatial-utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"lodash-es": "^4.17.21",
|
|
20
20
|
"math.gl": "^3.5.6",
|
|
21
21
|
"mathjs": "^9.2.0",
|
|
22
|
-
"@vitessce/gl": "3.
|
|
23
|
-
"@vitessce/utils": "3.
|
|
22
|
+
"@vitessce/gl": "3.2.0",
|
|
23
|
+
"@vitessce/utils": "3.2.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { viv } from '@vitessce/gl';
|
|
2
|
+
|
|
3
|
+
function getDomains() {
|
|
4
|
+
const domains = {};
|
|
5
|
+
const needMin = ['Int8', 'Int16', 'Int32'];
|
|
6
|
+
Object.keys(viv.DTYPE_VALUES).forEach((dtype) => {
|
|
7
|
+
const { max } = viv.DTYPE_VALUES[dtype];
|
|
8
|
+
const min = needMin.includes(dtype) ? -(max + 1) : 0;
|
|
9
|
+
domains[dtype] = [min, max];
|
|
10
|
+
});
|
|
11
|
+
return domains;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const DOMAINS = getDomains();
|