Skip to content

Commit

Permalink
fix(avivator/ome-zarr): handle missiing selectable dimension (#713)
Browse files Browse the repository at this point in the history
  • Loading branch information
manzt authored Nov 14, 2023
1 parent 50ea1ef commit b56f4ad
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added

### Changed
- Fix Avivator demo for OME-Zarr with only spatial axes

## 0.14.1

Expand Down
2 changes: 1 addition & 1 deletion sites/avivator/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export const COLOR_PALLETE = [
[255, 255, 255],
[255, 0, 0]
];
export const GLOBAL_SLIDER_DIMENSION_FIELDS = ['z', 't'];
export const GLOBAL_SLIDER_DIMENSION_FIELDS = /** @type {const} */ (['z', 't']);
export const INITIAL_SLIDER_VALUE = [1500, 20000];
export const FILL_PIXEL_VALUE = '----';
2 changes: 1 addition & 1 deletion sites/avivator/src/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export const useImage = (source, history) => {
? [[255, 255, 255]]
: newDomains.map(
(_, i) =>
(Channels[i].Color && Channels[i].Color.slice(0, -1)) ??
(Channels[i]?.Color && Channels[i].Color.slice(0, -1)) ??
COLOR_PALLETE[i]
);
useViewerStore.setState({
Expand Down
82 changes: 59 additions & 23 deletions sites/avivator/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,24 +246,33 @@ export function getNameFromUrl(url) {
/**
* Return the midpoint of the global dimensions as a default selection.
*
* @param { import('../../src/types').PixelSource<['t', 'z', 'c']> } pixelSource
* @param {{ name: string, size: number }[]} dimensions
* @returns {{ [Key in typeof GLOBAL_SLIDER_DIMENSION_FIELDS[number]]?: number }
*/
function getDefaultGlobalSelection({ labels, shape }) {
const dims = labels
.map((name, i) => [name, i])
.filter(d => GLOBAL_SLIDER_DIMENSION_FIELDS.includes(d[0]));

/**
* @type { { t: number, z: number, c: number } }
*/
function getDefaultGlobalSelection(dimensions) {
const globalSelectableDimensions = dimensions.filter(d =>
GLOBAL_SLIDER_DIMENSION_FIELDS.includes(d.name.toLowerCase())
);

/** @type {{ [Key in typeof GLOBAL_SLIDER_DIMENSION_FIELDS[number]]?: number } */
const selection = {};
dims.forEach(([name, index]) => {
selection[name] = Math.floor((shape[index] || 0) / 2);
});
for (const dim of globalSelectableDimensions) {
selection[dim.name] = Math.floor(dim.size / 2);
}

return selection;
}

function isGlobalOrXYDimension(name) {
// normalize name to lowercase
name = name.toLowerCase();
return (
name === 'x' ||
name === 'y' ||
GLOBAL_SLIDER_DIMENSION_FIELDS.includes(name)
);
}

/**
* @param {Array.<number>} shape loader shape
*/
Expand All @@ -272,31 +281,58 @@ export function isInterleaved(shape) {
return lastDimSize === 3 || lastDimSize === 4;
}

/**
* @template A
* @template B
* @param {Array<A>} a
* @param {Array<B>} b
* @returns {Array<[A, B]>}
*/
function zip(a, b) {
if (a.length !== b.length) {
throw new Error('Array lengths must be equal');
}
return a.map((val, i) => [val, b[i]]);
}

// Create a default selection using the midpoint of the available global dimensions,
// and then the first four available selections from the first selectable channel.
/**
*
* @param { import('../../src/types').PixelSource<['t', 'z', 'c']> } pixelSource
* @param {{ labels: string[], shape: number[] }} pixelSource
*/
export function buildDefaultSelection(pixelSource) {
export function buildDefaultSelection({ labels, shape }) {
let selection = [];
const globalSelection = getDefaultGlobalSelection(pixelSource);

const dimensions = zip(labels, shape).map(([name, size]) => ({ name, size }));

const globalSelection = getDefaultGlobalSelection(dimensions);

// First non-global dimension with some sort of selectable values.
const firstNonGlobalSelectableDimension = dimensions.find(
dim => !isGlobalOrXYDimension(dim.name)
);

const firstNonGlobalDimension = pixelSource.labels
.map((name, i) => ({ name, size: pixelSource.shape[i] }))
.find(d => !GLOBAL_SLIDER_DIMENSION_FIELDS.includes(d.name) && d.size);
// If there are no additional selectable dimensions, return the global selection.
if (!firstNonGlobalSelectableDimension) {
return [globalSelection];
}

for (let i = 0; i < Math.min(4, firstNonGlobalDimension.size); i += 1) {
for (
let i = 0;
i < Math.min(4, firstNonGlobalSelectableDimension.size);
i += 1
) {
selection.push({
[firstNonGlobalDimension.name]: i,
[firstNonGlobalSelectableDimension.name]: i,
...globalSelection
});
}

selection = isInterleaved(pixelSource.shape)
? [{ ...selection[0], c: 0 }]
: selection;
if (isInterleaved(shape)) {
return [{ ...selection[0], c: 0 }];
}

return selection;
}

Expand Down

0 comments on commit b56f4ad

Please sign in to comment.