Skip to content

Commit

Permalink
feat: add button to zoom in selected roi (#115)
Browse files Browse the repository at this point in the history
* feat: add button to zoom in selected roi

* chore: update react-roi

* fix: fix roi zoom zone

* chore: add margin to roi zoom zone
  • Loading branch information
moonayyur authored May 31, 2024
1 parent da5782b commit 63047dd
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 90 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"react-kbs": "^2.1.1",
"react-map-interaction": "^2.1.0",
"react-plot": "^1.4.2",
"react-roi": "^1.4.0",
"react-roi": "^1.4.2",
"react-science": "^3.1.0",
"react-use": "^17.4.0",
"tiff": "^6.1.0"
Expand Down
70 changes: 15 additions & 55 deletions src/components/ImageViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@ import {
CSSProperties,
memo,
MutableRefObject,
useCallback,
useEffect,
useMemo,
} from 'react';
import { RoiContainer, RoiProvider, useTargetRef } from 'react-roi';
import { RoiContainer, useTargetRef } from 'react-roi';

import useImage from '../hooks/useImage';
import useView from '../hooks/useView';
import useViewDispatch from '../hooks/useViewDispatch';
import { SET_PAN_ZOOM } from '../state/view/ViewActionTypes';

import ROIAnnotations from './roi/ROIAnnotations';

Expand Down Expand Up @@ -45,66 +41,30 @@ function ImageViewer({
image,
annotable = false,
}: ImageViewerProps) {
const view = useView();
const viewDispatch = useViewDispatch();

const { original, pipelined } = useImage();

const imageToShow = useMemo(() => {
if (image !== undefined) return image;
return showOriginal ? original : pipelined;
}, [image, original, pipelined, showOriginal]);

const panZoom = useMemo(() => {
return (
view.imageViewerProps[identifier] || {
scale: 1,
translation: [0, 0],
}
);
}, [identifier, view.imageViewerProps]);

const setPanZoom = useCallback(
(panZoom) => {
viewDispatch({
type: SET_PAN_ZOOM,
payload: { identifier, panZoom },
});
},
[identifier, viewDispatch],
);

return (
<RoiProvider
initialConfig={{
zoom: {
initial: panZoom,
min: 0.1,
max: 30,
spaceAroundTarget: 0,
},
resizeStrategy: 'contain',
mode: 'select',
<RoiContainer
zoomWithoutModifierKey
target={<TargetCanvas imageToShow={imageToShow} />}
style={{
width: '100%',
height: '100%',
}}
onAfterZoomChange={setPanZoom}
>
<RoiContainer
zoomWithoutModifierKey
target={<TargetCanvas imageToShow={imageToShow} />}
style={{
width: '100%',
height: '100%',
}}
>
{annotable && (
<ROIAnnotations
width={imageToShow?.width}
height={imageToShow?.height}
identifier={identifier}
/>
)}
</RoiContainer>
</RoiProvider>
{annotable && (
<ROIAnnotations
width={imageToShow?.width}
height={imageToShow?.height}
identifier={identifier}
/>
)}
</RoiContainer>
);
}

Expand Down
103 changes: 75 additions & 28 deletions src/components/Pixelium.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import styled from '@emotion/styled';
import { WebSource } from 'filelist-utils';
import { memo, useMemo, useReducer, useRef } from 'react';
import { memo, useCallback, useMemo, useReducer, useRef } from 'react';
import { KbsProvider } from 'react-kbs';
import { RoiProvider } from 'react-roi';
import { RootLayout, SplitPane, Toolbar } from 'react-science/ui';

import useViewDispatch from '../hooks/useViewDispatch';
import {
dataReducer,
DataState,
Expand All @@ -14,6 +16,7 @@ import {
preferencesReducer,
PreferencesState,
} from '../state/preferences/PreferencesReducer';
import { SET_PAN_ZOOM } from '../state/view/ViewActionTypes';
import {
initialViewState,
viewReducer,
Expand Down Expand Up @@ -65,6 +68,7 @@ const PixeliumMainStyle = styled.div`
`;

function Pixelium({ data, preferences, view, webSource }: PixeliumProps) {
const viewDispatch = useViewDispatch();
// Refs
const rootRef = useRef<HTMLDivElement>(null);

Expand All @@ -83,6 +87,35 @@ function Pixelium({ data, preferences, view, webSource }: PixeliumProps) {
);
const [roiState, dispatchROI] = useReducer(ROIReducer, initialROIState);

const identifier = viewState.currentTab;

const panZoom = useMemo(() => {
if (
view !== undefined &&
identifier !== undefined &&
view.imageViewerProps[identifier]
) {
return view.imageViewerProps[identifier];
} else {
return {
scale: 1,
translation: [0, 0] as [number, number],
};
}
}, [identifier, view]);

const setPanZoom = useCallback(
(panZoom) => {
if (view !== undefined && identifier !== undefined) {
viewDispatch({
type: SET_PAN_ZOOM,
payload: { identifier, panZoom },
});
}
},
[identifier, view, viewDispatch],
);

const dispatchers = useMemo(() => {
return {
data: dispatchData,
Expand All @@ -103,33 +136,47 @@ function Pixelium({ data, preferences, view, webSource }: PixeliumProps) {
<DispatchProvider value={dispatchers}>
<PipelineProvider identifier={viewState.currentTab}>
<ROIProvider value={roiState}>
<AnnotationsProvider>
<AutoLoader webSource={webSource}>
<PixeliumStyle ref={rootRef}>
<Header />
<PixeliumMainStyle>
<Toolbar vertical>
<ImportTool />
<ExportTool />
<GreyTool />
<MaskTool />
<MorphologyTool />
<GeometryTool />
<ROITool />
<ModalContainer />
</Toolbar>
<SplitPane
direction="horizontal"
size="300px"
controlledSide="end"
>
<CenterPanel />
<Sidebar />
</SplitPane>
</PixeliumMainStyle>
</PixeliumStyle>
</AutoLoader>
</AnnotationsProvider>
<RoiProvider
initialConfig={{
zoom: {
initial: panZoom,
min: 0.1,
max: 30,
spaceAroundTarget: 0.1,
},
resizeStrategy: 'contain',
mode: 'select',
}}
onAfterZoomChange={setPanZoom}
>
<AnnotationsProvider>
<AutoLoader webSource={webSource}>
<PixeliumStyle ref={rootRef}>
<Header />
<PixeliumMainStyle>
<Toolbar vertical>
<ImportTool />
<ExportTool />
<GreyTool />
<MaskTool />
<MorphologyTool />
<GeometryTool />
<ROITool />
<ModalContainer />
</Toolbar>
<SplitPane
direction="horizontal"
size="400px"
controlledSide="end"
>
<CenterPanel />
<Sidebar />
</SplitPane>
</PixeliumMainStyle>
</PixeliumStyle>
</AutoLoader>
</AnnotationsProvider>
</RoiProvider>
</ROIProvider>
</PipelineProvider>
</DispatchProvider>
Expand Down
2 changes: 1 addition & 1 deletion src/components/roi/ROIAnnotations.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo, useEffect, useMemo, useRef } from 'react';
import { usePanZoomTransform } from 'react-roi/lib-esm/hooks/usePanZoom';
import { usePanZoomTransform } from 'react-roi';

import useAnnotationRef from '../../hooks/useAnnotationRef';
import useOriginalFilteredROIs from '../../hooks/useOriginalFilteredROIs';
Expand Down
22 changes: 21 additions & 1 deletion src/components/roi/ROITable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Button, Popover } from '@blueprintjs/core';
import styled from '@emotion/styled';
import startCase from 'lodash/startCase';
import { memo } from 'react';
import { MdFilterAlt } from 'react-icons/md';
import { MdFilterAlt, MdSearch } from 'react-icons/md';
import { useActions } from 'react-roi';
import { Table, ValueRenderers } from 'react-science/ui';

import useFilteredROIs from '../../hooks/useFilteredROIs';
Expand All @@ -24,6 +25,7 @@ interface ROITableProps {
}

function ROITable({ identifier }: ROITableProps) {
const { zoomIntoROI } = useActions();
const rois = useROIs(identifier);
const { filters } = useROIContext();
const filteredROIs = useFilteredROIs(identifier);
Expand All @@ -46,6 +48,7 @@ function ROITable({ identifier }: ROITableProps) {
>
<Table compact striped>
<Table.Header>
<ValueRenderers.Header />
{columns.map((column) => (
<ValueRenderers.Component
key={column}
Expand Down Expand Up @@ -73,6 +76,23 @@ function ROITable({ identifier }: ROITableProps) {
</Table.Header>
{filteredROIs.map((roi) => (
<Table.Row key={roi.id}>
<ValueRenderers.Component style={{ marginTop: -2 }}>
<Button
minimal
onClick={() => {
zoomIntoROI(
[
{ x: roi.column, y: roi.row },
{ x: roi.column + roi.width, y: roi.row + roi.height },
],
{ margin: 0.5 },
);
}}
style={{ padding: 0 }}
>
<MdSearch size={20} />
</Button>
</ValueRenderers.Component>
{columns.map((column) => (
<ValueRenderers.Number
key={column}
Expand Down

0 comments on commit 63047dd

Please sign in to comment.