|
1 |
| -import React from 'react'; |
| 1 | +import React, { useRef } from 'react'; |
2 | 2 | import { useRecoilState, useRecoilValue } from 'recoil';
|
3 | 3 | import DeckGL from 'deck.gl';
|
4 | 4 | import { OrthographicView } from '@deck.gl/core';
|
5 | 5 | import type { Layer } from '@deck.gl/core';
|
6 | 6 |
|
7 |
| -import { viewerViewState, layersSelector } from '../state'; |
8 |
| -import type { VivLayerProps, ZarrLoader } from '@hms-dbmi/viv'; |
| 7 | +import { viewerViewState, layersSelector, LayerState } from '../state'; |
| 8 | +import { isInterleaved, fitBounds } from '../utils'; |
9 | 9 |
|
10 |
| -function WrappedViewStateDeck({ layers }: { layers: Layer<VivLayerProps>[] }): JSX.Element { |
| 10 | +function getLayerSize(props: LayerState['layerProps']) { |
| 11 | + const { loader, rows, columns } = props; |
| 12 | + const [base, maxZoom] = Array.isArray(loader) ? [loader[0], loader.length] : [loader, 0]; |
| 13 | + const interleaved = isInterleaved(base.shape); |
| 14 | + let [height, width] = base.shape.slice(interleaved ? -3 : -2); |
| 15 | + if (rows && columns) { |
| 16 | + // TODO: Don't hardcode spacer size. Probably best to inspect the deck.gl Layers rather than |
| 17 | + // the Layer Props. |
| 18 | + const spacer = 5; |
| 19 | + height = (height + spacer) * rows; |
| 20 | + width = (width + spacer) * columns; |
| 21 | + } |
| 22 | + return { height, width, maxZoom }; |
| 23 | +} |
| 24 | + |
| 25 | +function WrappedViewStateDeck({ layers }: { layers: Layer<any, any>[] }): JSX.Element { |
11 | 26 | const [viewState, setViewState] = useRecoilState(viewerViewState);
|
| 27 | + const deckRef = useRef<DeckGL>(null); |
| 28 | + const views = [new OrthographicView({ id: 'ortho', controller: true })]; |
12 | 29 |
|
13 | 30 | // If viewState hasn't been updated, use the first loader to guess viewState
|
14 | 31 | // TODO: There is probably a better place / way to set the intital view and this is a hack.
|
15 |
| - if (viewState?.default && (layers[0]?.props as VivLayerProps)?.loader?.base) { |
16 |
| - const loader = (layers[0].props as VivLayerProps).loader as ZarrLoader; |
17 |
| - const [height, width] = loader.base.shape.slice(-2); |
18 |
| - const zoom = -loader.numLevels; |
19 |
| - const target = [width / 2, height / 2, 0]; |
| 32 | + if (deckRef.current && viewState?.default && layers[0]?.props?.loader) { |
| 33 | + const { deck } = deckRef.current; |
| 34 | + const { width, height, maxZoom } = getLayerSize(layers[0].props); |
| 35 | + const padding = deck.width < 400 ? 10 : deck.width < 600 ? 30 : 50; // Adjust depending on viewport width. |
| 36 | + const { zoom, target } = fitBounds([width, height], [deck.width, deck.height], maxZoom, padding); |
20 | 37 | setViewState({ zoom, target });
|
21 | 38 | }
|
22 | 39 |
|
23 |
| - const views = [new OrthographicView({ id: 'ortho', controller: true })]; |
24 |
| - |
25 | 40 | return (
|
26 |
| - <DeckGL layers={layers} viewState={viewState} onViewStateChange={(e) => setViewState(e.viewState)} views={views} /> |
| 41 | + <DeckGL |
| 42 | + ref={deckRef} |
| 43 | + layers={layers} |
| 44 | + viewState={viewState} |
| 45 | + onViewStateChange={(e) => setViewState(e.viewState)} |
| 46 | + views={views} |
| 47 | + /> |
27 | 48 | );
|
28 | 49 | }
|
29 | 50 |
|
|
0 commit comments