diff --git a/examples/src/omezarr/app.tsx b/examples/src/omezarr/app.tsx index c9a8c96..4a93ecb 100644 --- a/examples/src/omezarr/app.tsx +++ b/examples/src/omezarr/app.tsx @@ -4,7 +4,6 @@ import { SliceView } from './sliceview'; import { type OmeZarrDataset, loadOmeZarr } from '@alleninstitute/vis-omezarr'; const demo_versa = 'https://neuroglancer-vis-prototype.s3.amazonaws.com/VERSA/scratch/0500408166/'; - export function AppUi() { return ; } diff --git a/examples/src/omezarr/sliceview.tsx b/examples/src/omezarr/sliceview.tsx index d3132b0..de31c28 100644 --- a/examples/src/omezarr/sliceview.tsx +++ b/examples/src/omezarr/sliceview.tsx @@ -7,7 +7,7 @@ import { type RenderSettings, } from '@alleninstitute/vis-omezarr'; import type { RenderFrameFn } from '@alleninstitute/vis-scatterbrain'; -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { useContext, useEffect, useRef } from 'react'; import { renderServerContext } from '~/common/react/render-server-provider'; type Props = { @@ -21,7 +21,7 @@ const settings: RenderSettings = { B: { gamut: { min: 0, max: 100 }, index: 2 }, }, plane: 'xy', - planeIndex: 0, + planeIndex: 3, camera: { view: Box2D.create([0, 0], [250, 120]), screenSize: [500, 500], @@ -82,10 +82,22 @@ export function SliceView(props: Props) { ); } }, [server, renderer.current, cnvs.current, omezarr, view]); + const pan = useCallback( + (e: React.MouseEvent) => { + if (e.ctrlKey) { + const pos = Vec2.div([-e.movementX, -e.movementY], settings.camera.screenSize); + const scaledOffset = Vec2.mul(pos, Box2D.size(view)); + const v = Box2D.translate(view, scaledOffset); + setView(v); + } + }, + [view] + ); return ( { const scale = e.deltaY > 0 ? 1.1 : 0.9; const m = Box2D.midpoint(view); diff --git a/packages/omezarr/package.json b/packages/omezarr/package.json index 9e0aa8e..0c02023 100644 --- a/packages/omezarr/package.json +++ b/packages/omezarr/package.json @@ -1,6 +1,6 @@ { "name": "@alleninstitute/vis-omezarr", - "version": "0.0.1", + "version": "0.0.2", "contributors": [ { "name": "Lane Sawyer", diff --git a/packages/omezarr/src/sliceview/loader.test.ts b/packages/omezarr/src/sliceview/loader.test.ts new file mode 100644 index 0000000..bf152d4 --- /dev/null +++ b/packages/omezarr/src/sliceview/loader.test.ts @@ -0,0 +1,195 @@ +import type { ZarrDataset } from '../zarr-data'; +import { describe, expect, it } from 'vitest'; +import { Box2D, box2D } from '@alleninstitute/vis-geometry'; +import { getVisibleTiles } from './loader'; +const exampleOmeZarr: ZarrDataset = { + url: 'https://allen-genetic-tools.s3.us-west-2.amazonaws.com/tissuecyte/1263343692/ome-zarr/', + multiscales: [ + { + axes: [ + { + name: 'c', + type: 'channel', + unit: 'millimeter', + }, + { + name: 'z', + type: 'space', + unit: 'millimeter', + }, + { + name: 'y', + type: 'space', + unit: 'millimeter', + }, + { + name: 'x', + type: 'space', + unit: 'millimeter', + }, + ], + datasets: [ + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.00035, 0.00035], + type: 'scale', + }, + { + translation: [0, 0, 0, 0], + type: 'translation', + }, + ], + path: '0', + shape: [3, 142, 29998, 39998], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0007, 0.0007], + type: 'scale', + }, + { + translation: [0, 0, 0.00035, 0.00035], + type: 'translation', + }, + ], + path: '1', + shape: [3, 142, 14999, 19999], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0014, 0.0014], + type: 'scale', + }, + { + translation: [0, 0, 0.00105, 0.00105], + type: 'translation', + }, + ], + path: '2', + shape: [3, 142, 7499, 9999], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0028, 0.0028], + type: 'scale', + }, + { + translation: [0, 0, 0.00245, 0.00245], + type: 'translation', + }, + ], + path: '3', + shape: [3, 142, 3749, 4999], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0056, 0.0056], + type: 'scale', + }, + { + translation: [0, 0, 0.00525, 0.00525], + type: 'translation', + }, + ], + path: '4', + shape: [3, 142, 1874, 2499], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0112, 0.0112], + type: 'scale', + }, + { + translation: [0, 0, 0.01085, 0.01085], + type: 'translation', + }, + ], + path: '5', + shape: [3, 142, 937, 1249], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0224, 0.0224], + type: 'scale', + }, + { + translation: [0, 0, 0.02205, 0.02205], + type: 'translation', + }, + ], + path: '6', + shape: [3, 142, 468, 624], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0448, 0.0448], + type: 'scale', + }, + { + translation: [0, 0, 0.044449999999999996, 0.044449999999999996], + type: 'translation', + }, + ], + path: '7', + shape: [3, 142, 234, 312], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.0896, 0.0896], + type: 'scale', + }, + { + translation: [0, 0, 0.08925, 0.08925], + type: 'translation', + }, + ], + path: '8', + shape: [3, 142, 117, 156], + }, + { + coordinateTransformations: [ + { + scale: [1, 0.1, 0.1792, 0.1792], + type: 'scale', + }, + { + translation: [0, 0, 0.17885, 0.17885], + type: 'translation', + }, + ], + path: '9', + shape: [3, 142, 58, 78], + }, + ], + }, + ], +}; + +describe('omezarr basic tiled loading', () => { + describe('getVisibleTiles', () => { + it('visible tiles cannot extend beyond the bounds of their layer', () => { + const view: box2D = { + minCorner: [-7, -11], + maxCorner: [28, 35], + }; + const camera = { view, screenSize: [210, 210] as const }; + const visible = getVisibleTiles(camera, 'xy', 2, exampleOmeZarr, 256); + // this is a basic regression test: we had a bug which would result in + // tiles from the image being larger than the image itself (they would be the given tile size) + expect(visible.length).toBe(1); + const expectedLayer = exampleOmeZarr.multiscales[0].datasets[9]; + // we expect to be seeing the lowest resolution layer with our very zoomed out, low res camera + const [_c, _z, y, x] = expectedLayer.shape; + expect(visible[0].bounds).toEqual(Box2D.create([0, 0], [x, y])); + }); + }); +}); diff --git a/packages/omezarr/src/sliceview/loader.ts b/packages/omezarr/src/sliceview/loader.ts index fa86017..3112815 100644 --- a/packages/omezarr/src/sliceview/loader.ts +++ b/packages/omezarr/src/sliceview/loader.ts @@ -30,7 +30,7 @@ function visitTilesWithin(idealTilePx: vec2, size: vec2, bounds: box2D, visit: ( for (let y = Math.floor(boundsInTiles.minCorner[1]); y < Math.ceil(boundsInTiles.maxCorner[1]); y += 1) { // all tiles visited are always within both the bounds, and the image itself const lo = Vec2.mul([x, y], idealTilePx); - const hi = Vec2.add(lo, idealTilePx); + const hi = Vec2.min(size, Vec2.add(lo, idealTilePx)); visit(Box2D.create(lo, hi)); } }