Skip to content

Commit

Permalink
feat: volume api and TMTV mode (#2817)
Browse files Browse the repository at this point in the history
* feat: volumeAPI and TMTV mode

* feat: use cs3d cache service to obtain viewportData

* wip for volume api

* wip: fusion viewport

* feat: add blend mode option

* wip for image scrollbar

* fix drag and drop thumbnail

* wip for image scrollbar for voluems

* fix: element mismatch bug for scroll

* feat: Add image scrollbar to volumes

* feat: add syncGroups to volume api

* feat: add tmtv mode initial setup

* feat: add initial image options for the stack viewports

* feat: Add custom load strategy for volume viewports via HP

* apply review comments

fix: Jump presets cs3d (#2812)

* feat: Add JumpPreset to OHIF for Cornerstone3D

* fix: accessing viewport service from servicesManager

feat: volume API and TMTV mode (#2814)

* fix: do not display overlays on mip viewports

* feat: add optional disableCommands for toggle buttons

* fix: toggleCrossharis

* feat: config the crosshairs

* feat: add PetSUV Panel for changing metadata

* feat: initial work for the rectangleROIThreshold panel

* wip: measurement service

* roi threshold working

* feat: Add displayText to segmentations

* feat: add remove segmentation

* feat: add csv export

* feat: add RT export for annotations in tmtv mode

* fix: fusion to use pt in tmtv mode and measuremet mappings

* fix: various bugs

* apply review comments

* apply review comments

* feat: add fusion color maps

* add readme to tmtv mode

* Update README.md

* fix: try to fix build

* fix unit tests

* Update README.md

* feat: add about to the panel

* fix: changing strategy in roi panel

* apply review comments

* update package versions

* wip for stackPrefetch
  • Loading branch information
sedghi authored Jun 1, 2022
1 parent 78566b6 commit 6871110
Show file tree
Hide file tree
Showing 124 changed files with 16,749 additions and 1,069 deletions.
235 changes: 96 additions & 139 deletions extensions/cornerstone-3d/src/Viewport/OHIFCornerstone3DViewport.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import React, { useEffect, useRef, useCallback, useState } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import { useViewportGrid, ImageScrollbar } from '@ohif/ui';
import OHIF from '@ohif/core';
import PropTypes from 'prop-types';
import { useViewportGrid } from '@ohif/ui';
import * as cs3DTools from '@cornerstonejs/tools';
import { Enums, eventTarget } from '@cornerstonejs/core';

import PropTypes from 'prop-types';

import { setEnabledElement } from '../state';
import Cornerstone3DViewportService from '../services/ViewportService/Cornerstone3DViewportService';
import CornerstoneOverlay from './CornerstoneOverlay';
import ViewportLoadingIndicator from './ViewportLoadingIndicator';
import ViewportOrientationMarkers from './ViewportOrientationMarkers';
import Cornerstone3DCacheService from '../services/ViewportService/Cornerstone3DCacheService';

import './OHIFCornerstone3DViewport.css';

const { StackManager } = OHIF.utils;
import CornerstoneOverlays from './Overlays/CornerstoneOverlays';

const STACK = 'stack';

Expand All @@ -28,8 +22,6 @@ function areEqual(prevProps, nextProps) {
return false;
}

// Todo: handle fusion
// Todo: handle orientation
const prevDisplaySets = prevProps.displaySets[0];
const nextDisplaySets = nextProps.displaySets[0];

Expand Down Expand Up @@ -67,9 +59,8 @@ const OHIFCornerstoneViewport = React.memo(props => {
initialImageIdOrIndex,
} = props;

const [viewportData, setViewportData] = useState(null);
const [imageIndex, setImageIndex] = useState(0);
const [scrollbarHeight, setScrollbarHeight] = useState('100px');
const [viewportData, setViewportData] = useState(null);
const [_, viewportGridService] = useViewportGrid();

const elementRef = useRef();
Expand All @@ -79,6 +70,8 @@ const OHIFCornerstoneViewport = React.memo(props => {
DisplaySetService,
ToolBarService,
ToolGroupService,
SyncGroupService,
Cornerstone3DViewportService,
} = servicesManager.services;

// useCallback for scroll bar height calculation
Expand All @@ -103,7 +96,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
}

const { viewportId, element } = evt.detail;
const viewportInfo = Cornerstone3DViewportService.getViewportInfoById(
const viewportInfo = Cornerstone3DViewportService.getViewportInfo(
viewportId
);
const viewportIndex = viewportInfo.getViewportIndex();
Expand All @@ -112,12 +105,20 @@ const OHIFCornerstoneViewport = React.memo(props => {

const renderingEngineId = viewportInfo.getRenderingEngineId();
const toolGroupId = viewportInfo.getToolGroupId();
ToolGroupService.addToolGroupViewport(
const syncGroups = viewportInfo.getSyncGroups();

ToolGroupService.addViewportToToolGroup(
viewportId,
renderingEngineId,
toolGroupId
);

SyncGroupService.addViewportToSyncGroup(
viewportId,
renderingEngineId,
syncGroups
);

if (onElementEnabled) {
onElementEnabled(evt);
}
Expand All @@ -129,6 +130,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
useEffect(() => {
Cornerstone3DViewportService.enableElement(
viewportIndex,
viewportOptions,
elementRef.current
);

Expand All @@ -138,49 +140,96 @@ const OHIFCornerstoneViewport = React.memo(props => {
);

setImageScrollBarHeight();

return () => {
const viewportInfo = Cornerstone3DViewportService.getViewportInfoByIndex(
viewportIndex
);

const viewportId = viewportInfo.getViewportId();
const renderingEngineId = viewportInfo.getRenderingEngineId();
const syncGroups = viewportInfo.getSyncGroups();

ToolGroupService.disable(viewportId, renderingEngineId);
SyncGroupService.removeViewportFromSyncGroup(
viewportId,
renderingEngineId,
syncGroups
);

Cornerstone3DViewportService.disableElement(viewportIndex);

eventTarget.removeEventListener(
Enums.Events.ELEMENT_ENABLED,
elementEnabledHandler
);
};
}, []);

// subscribe to displaySet metadata invalidation (updates)
// Currently, if the metadata changes we need to re-render the display set
// for it to take effect in the viewport. As we deal with scaling in the loading,
// we need to remove the old volume from the cache, and let the
// viewport to re-add it which will use the new metadata. Otherwise, the
// viewport will use the cached volume and the new metadata will not be used.
// Note: this approach does not actually end of sending network requests
// and it uses the network cache
useEffect(() => {
const viewportData = _getViewportData(
dataSource,
displaySets,
viewportOptions.viewportType,
initialImageIdOrIndex
const { unsubscribe } = DisplaySetService.subscribe(
DisplaySetService.EVENTS.DISPLAY_SET_SERIES_METADATA_INVALIDATED,
async invalidatedDisplaySetInstanceUID => {
if (
viewportData.displaySetInstanceUIDs.includes(
invalidatedDisplaySetInstanceUID
)
) {
const newViewportData = await Cornerstone3DCacheService.invalidateViewportData(
viewportData,
invalidatedDisplaySetInstanceUID,
dataSource,
DisplaySetService
);

Cornerstone3DViewportService.updateViewport(
viewportIndex,
newViewportData
);

setViewportData(newViewportData);
}
}
);
return () => {
unsubscribe();
};
}, [viewportData, viewportIndex]);

setViewportData(viewportData);

Cornerstone3DViewportService.setViewportDisplaySets(
viewportIndex,
viewportData,
viewportOptions,
displaySetOptions
);
useEffect(() => {
// handle the default viewportType to be stack
if (!viewportOptions.viewportType) {
viewportOptions.viewportType = STACK;
}

const element = elementRef.current;
const loadViewportData = async () => {
const viewportData = await Cornerstone3DCacheService.getViewportData(
viewportIndex,
displaySets,
viewportOptions.viewportType,
dataSource,
initialImageIdOrIndex
);

const updateIndex = event => {
const { imageId } = event.detail;
// find the index of imageId in the imageIds
const index = viewportData.stack?.imageIds.indexOf(imageId);
Cornerstone3DViewportService.setViewportDisplaySets(
viewportIndex,
viewportData,
viewportOptions,
displaySetOptions
);

if (index !== -1) {
setImageIndex(index);
}
setViewportData(viewportData);
};

element.addEventListener(Enums.Events.STACK_NEW_IMAGE, updateIndex);

return () => {
element.removeEventListener(Enums.Events.STACK_NEW_IMAGE, updateIndex);
};
loadViewportData();
}, [viewportOptions, displaySets, dataSource]);

/**
Expand All @@ -194,10 +243,6 @@ const OHIFCornerstoneViewport = React.memo(props => {
* the cache for jumping to see if there is any jump queued, then we jump to the correct slice.
*/
useEffect(() => {
if (!viewportData) {
return;
}

const unsubscribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
Expand All @@ -219,34 +264,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
return () => {
unsubscribeFromJumpToMeasurementEvents();
};
}, [displaySets, elementRef, viewportIndex, viewportData]);

const onImageScrollbarChange = useCallback(
(imageIndex, viewportIndex) => {
const viewportInfo = Cornerstone3DViewportService.getViewportInfoByIndex(
viewportIndex
);

const viewportId = viewportInfo.getViewportId();
const viewport = Cornerstone3DViewportService.getCornerstone3DViewport(
viewportId
);

// if getCurrentImageId is not a method on viewport
if (!viewport.getCurrentImageId) {
throw new Error('cannot use scrollbar for non-stack viewports');
}

// Later scrollThroughStack should return two values the current index
// and the total number of indices (volume it is different)
viewport.setImageIdIndex(imageIndex).then(() => {
// Update scrollbar index
const currentIndex = viewport.getCurrentImageIdIndex();
setImageIndex(currentIndex);
});
},
[viewportIndex, viewportData]
);
}, [displaySets, elementRef, viewportIndex]);

return (
<div className="viewport-wrapper">
Expand All @@ -266,78 +284,17 @@ const OHIFCornerstoneViewport = React.memo(props => {
onMouseDown={e => e.preventDefault()}
ref={elementRef}
></div>
<ImageScrollbar
onChange={evt => onImageScrollbarChange(evt, viewportIndex)}
max={viewportData ? viewportData.stack?.imageIds?.length - 1 : 0}
height={scrollbarHeight}
value={imageIndex}
/>
<CornerstoneOverlay
viewportData={viewportData}
imageIndex={imageIndex}
<CornerstoneOverlays
viewportIndex={viewportIndex}
ToolBarService={ToolBarService}
element={elementRef.current}
scrollbarHeight={scrollbarHeight}
Cornerstone3DViewportService={Cornerstone3DViewportService}
/>
{viewportData && (
<>
<ViewportLoadingIndicator
viewportData={viewportData}
element={elementRef.current}
/>
<ViewportOrientationMarkers
viewportData={viewportData}
imageIndex={imageIndex}
viewportIndex={viewportIndex}
/>
</>
)}
</div>
);
}, areEqual);

function _getCornerstoneStack(displaySet, dataSource) {
// Get stack from Stack Manager
const storedStack = StackManager.findOrCreateStack(displaySet, dataSource);

// Clone the stack here so we don't mutate it
const stack = Object.assign({}, storedStack);

return stack;
}

function _getViewportData(
dataSource,
displaySets,
viewportType,
initialImageIdOrIndex
) {
viewportType = viewportType || STACK;
if (viewportType !== STACK) {
throw new Error('Only STACK viewport type is supported now');
}

// For Stack Viewport we don't have fusion currently
const displaySet = displaySets[0];

const stack = _getCornerstoneStack(displaySet, dataSource);

if (initialImageIdOrIndex !== undefined && stack?.imageIds) {
if (typeof initialImageIdOrIndex === 'number') {
stack.initialImageIdIndex = initialImageIdOrIndex;
} else {
stack.initialImageIdIndex = stack.imageIds.indexOf(initialImageIdOrIndex);
}
}

const viewportData = {
StudyInstanceUID: displaySet.StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
stack,
};

return viewportData;
}

function _subscribeToJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
Expand Down
Loading

0 comments on commit 6871110

Please sign in to comment.