feat(advanced-roi-tools): new tools and icon updates and overlay bug fixes (#4014)
This commit is contained in:
1 parent
78fd8cce86
commit
cea27d438d
52 files changed
+662
-210
No files matched your search
@@ -80,9 +80,9 @@ function CustomizableViewportOverlay({
|
||||
'cornerstoneOverlayBottomRight'
|
||||
);
|
||||
|
||||
const instance = useMemo(() => {
|
||||
const instances = useMemo(() => {
|
||||
if (viewportData != null) {
|
||||
return _getViewportInstance(viewportData, imageIndex);
|
||||
return _getViewportInstances(viewportData);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -177,7 +177,7 @@ function CustomizableViewportOverlay({
|
||||
formatTime: formatDICOMTime,
|
||||
formatNumberPrecision,
|
||||
},
|
||||
instance,
|
||||
instance: instances ? instances[item?.instanceIndex] : null,
|
||||
voi,
|
||||
scale,
|
||||
instanceNumber,
|
||||
@@ -207,7 +207,7 @@ function CustomizableViewportOverlay({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
customizationService,
|
||||
instance,
|
||||
instances,
|
||||
voi,
|
||||
scale,
|
||||
instanceNumber,
|
||||
@@ -217,13 +217,15 @@ function CustomizableViewportOverlay({
|
||||
const getContent = useCallback(
|
||||
(customization, defaultItems, keyPrefix) => {
|
||||
const items = customization?.items ?? defaultItems;
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.map((item, index) => (
|
||||
<div key={`${keyPrefix}_${index}`}>
|
||||
{item?.condition
|
||||
? item.condition({ instance, formatters: { formatDate: formatDICOMDate } })
|
||||
? item.condition({
|
||||
instance: instances ? instances[item?.instanceIndex] : null,
|
||||
formatters: { formatDate: formatDICOMDate },
|
||||
})
|
||||
? _renderOverlayItem(item)
|
||||
: null
|
||||
: _renderOverlayItem(item)}
|
||||
@@ -235,35 +237,50 @@ function CustomizableViewportOverlay({
|
||||
[_renderOverlayItem]
|
||||
);
|
||||
|
||||
const studyDateItem = {
|
||||
id: 'StudyDate',
|
||||
customizationType: 'ohif.overlayItem',
|
||||
label: '',
|
||||
title: 'Study date',
|
||||
condition: ({ instance }) => instance && instance.StudyDate,
|
||||
contentF: ({ instance, formatters: { formatDate } }) => formatDate(instance.StudyDate),
|
||||
};
|
||||
|
||||
const seriesDescriptionItem = {
|
||||
id: 'SeriesDescription',
|
||||
customizationType: 'ohif.overlayItem',
|
||||
label: '',
|
||||
title: 'Series description',
|
||||
attribute: 'SeriesDescription',
|
||||
condition: ({ instance }) => {
|
||||
return instance && instance.SeriesDescription;
|
||||
},
|
||||
};
|
||||
|
||||
const topLeftItems = instances
|
||||
? instances
|
||||
.map((instance, index) => {
|
||||
return [
|
||||
{
|
||||
...studyDateItem,
|
||||
instanceIndex: index,
|
||||
},
|
||||
{
|
||||
...seriesDescriptionItem,
|
||||
instanceIndex: index,
|
||||
},
|
||||
];
|
||||
})
|
||||
.flat()
|
||||
: [];
|
||||
|
||||
return (
|
||||
<ViewportOverlay
|
||||
topLeft={
|
||||
/**
|
||||
* Inline default overlay items for a more standard expansion
|
||||
*/
|
||||
getContent(
|
||||
topLeftCustomization,
|
||||
[
|
||||
{
|
||||
id: 'StudyDate',
|
||||
customizationType: 'ohif.overlayItem',
|
||||
label: '',
|
||||
title: 'Study date',
|
||||
condition: ({ instance }) => instance && instance.StudyDate,
|
||||
contentF: ({ instance, formatters: { formatDate } }) =>
|
||||
formatDate(instance.StudyDate),
|
||||
},
|
||||
{
|
||||
id: 'SeriesDescription',
|
||||
customizationType: 'ohif.overlayItem',
|
||||
label: '',
|
||||
title: 'Series description',
|
||||
attribute: 'SeriesDescription',
|
||||
condition: ({ instance }) => instance && instance.SeriesDescription,
|
||||
},
|
||||
],
|
||||
'topLeftOverlayItem'
|
||||
)
|
||||
getContent(topLeftCustomization, [...topLeftItems], 'topLeftOverlayItem')
|
||||
}
|
||||
topRight={getContent(topRightCustomization, [], 'topRightOverlayItem')}
|
||||
bottomLeft={getContent(
|
||||
@@ -298,18 +315,26 @@ function CustomizableViewportOverlay({
|
||||
);
|
||||
}
|
||||
|
||||
function _getViewportInstance(viewportData, imageIndex) {
|
||||
let imageId = null;
|
||||
function _getViewportInstances(viewportData) {
|
||||
const imageIds = [];
|
||||
if (viewportData.viewportType === Enums.ViewportType.STACK) {
|
||||
imageId = viewportData.data.imageIds[imageIndex];
|
||||
imageIds.push(viewportData.data.imageIds[0]);
|
||||
} else if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC) {
|
||||
const volumes = viewportData.data;
|
||||
if (volumes && volumes.length == 1) {
|
||||
const volume = volumes[0];
|
||||
imageId = volume.imageIds[imageIndex];
|
||||
}
|
||||
volumes.forEach(volume => {
|
||||
if (!volume?.imageIds) {
|
||||
return;
|
||||
}
|
||||
imageIds.push(volume.imageIds[0]);
|
||||
});
|
||||
}
|
||||
return imageId ? metaData.get('instance', imageId) || {} : {};
|
||||
const instances = [];
|
||||
|
||||
imageIds.forEach(imageId => {
|
||||
const instance = metaData.get('instance', imageId) || {};
|
||||
instances.push(instance);
|
||||
});
|
||||
return instances;
|
||||
}
|
||||
|
||||
const getInstanceNumber = (viewportData, viewportId, imageIndex, cornerstoneViewportService) => {
|
||||
@@ -323,7 +348,8 @@ const getInstanceNumber = (viewportData, viewportId, imageIndex, cornerstoneView
|
||||
instanceNumber = _getInstanceNumberFromVolume(
|
||||
viewportData,
|
||||
viewportId,
|
||||
cornerstoneViewportService
|
||||
cornerstoneViewportService,
|
||||
imageIndex
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -354,15 +380,20 @@ function _getInstanceNumberFromStack(viewportData, imageIndex) {
|
||||
// Since volume viewports can be in any view direction, they can render
|
||||
// a reconstructed image which don't have imageIds; therefore, no instance and instanceNumber
|
||||
// Here we check if viewport is in the acquisition direction and if so, we get the instanceNumber
|
||||
function _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewportService) {
|
||||
const volumes = viewportData.volumes;
|
||||
function _getInstanceNumberFromVolume(
|
||||
viewportData,
|
||||
viewportId,
|
||||
cornerstoneViewportService,
|
||||
imageIndex
|
||||
) {
|
||||
const volumes = viewportData.data;
|
||||
|
||||
// Todo: support fusion of acquisition plane which has instanceNumber
|
||||
if (!volumes || volumes.length > 1) {
|
||||
if (!volumes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const volume = volumes[0];
|
||||
// Todo: support fusion of acquisition plane which has instanceNumber
|
||||
const { volume } = volumes[0];
|
||||
const { direction, imageIds } = volume;
|
||||
|
||||
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
@@ -445,11 +476,15 @@ function InstanceNumberOverlayItem({
|
||||
className="overlay-item flex flex-row"
|
||||
style={{ color: (customization && customization.color) || undefined }}
|
||||
>
|
||||
<span className="mr-1 shrink-0">I:</span>
|
||||
<span>
|
||||
{instanceNumber !== undefined && instanceNumber !== null
|
||||
? `${instanceNumber} (${imageIndex + 1}/${numberOfSlices})`
|
||||
: `${imageIndex + 1}/${numberOfSlices}`}
|
||||
{instanceNumber !== undefined && instanceNumber !== null ? (
|
||||
<>
|
||||
<span className="mr-1 shrink-0">I:</span>
|
||||
<span>{`${instanceNumber} (${imageIndex + 1}/${numberOfSlices})`}</span>
|
||||
</>
|
||||
) : (
|
||||
`${imageIndex + 1}/${numberOfSlices}`
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -182,6 +182,26 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'evaluate.isUS',
|
||||
evaluate: ({ viewportId, disabledText }) => {
|
||||
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
|
||||
if (!displaySetUIDs?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const displaySets = displaySetUIDs.map(displaySetService.getDisplaySetByUID);
|
||||
const isUS = displaySets.some(displaySet => displaySet.Modality === 'US');
|
||||
if (!isUS) {
|
||||
return {
|
||||
disabled: true,
|
||||
className: '!text-common-bright ohif-disabled',
|
||||
disabledText: disabledText ?? 'Not available on the current viewport',
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'evaluate.viewportProperties.toggle',
|
||||
evaluate: ({ viewportId, button }) => {
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
ProbeTool,
|
||||
AngleTool,
|
||||
CobbAngleTool,
|
||||
PlanarFreehandROITool,
|
||||
MagnifyTool,
|
||||
CrosshairsTool,
|
||||
SegmentationDisplayTool,
|
||||
@@ -28,6 +27,11 @@ import {
|
||||
CircleScissorsTool,
|
||||
RectangleScissorsTool,
|
||||
SphereScissorsTool,
|
||||
AdvancedMagnifyTool,
|
||||
UltrasoundDirectionalTool,
|
||||
PlanarFreehandROITool,
|
||||
SplineROITool,
|
||||
LivewireContourTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
|
||||
import CalibrationLineTool from './tools/CalibrationLineTool';
|
||||
@@ -55,7 +59,6 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(DragProbeTool);
|
||||
addTool(AngleTool);
|
||||
addTool(CobbAngleTool);
|
||||
addTool(PlanarFreehandROITool);
|
||||
addTool(MagnifyTool);
|
||||
addTool(CrosshairsTool);
|
||||
addTool(SegmentationDisplayTool);
|
||||
@@ -66,6 +69,11 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(RectangleScissorsTool);
|
||||
addTool(SphereScissorsTool);
|
||||
addTool(ImageOverlayViewerTool);
|
||||
addTool(AdvancedMagnifyTool);
|
||||
addTool(UltrasoundDirectionalTool);
|
||||
addTool(PlanarFreehandROITool);
|
||||
addTool(SplineROITool);
|
||||
addTool(LivewireContourTool);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
const annotationStyle = {
|
||||
@@ -100,7 +108,6 @@ const toolNames = {
|
||||
Bidirectional: BidirectionalTool.toolName,
|
||||
Angle: AngleTool.toolName,
|
||||
CobbAngle: CobbAngleTool.toolName,
|
||||
PlanarFreehandROI: PlanarFreehandROITool.toolName,
|
||||
Magnify: MagnifyTool.toolName,
|
||||
Crosshairs: CrosshairsTool.toolName,
|
||||
SegmentationDisplay: SegmentationDisplayTool.toolName,
|
||||
@@ -111,6 +118,11 @@ const toolNames = {
|
||||
RectangleScissors: RectangleScissorsTool.toolName,
|
||||
SphereScissors: SphereScissorsTool.toolName,
|
||||
ImageOverlayViewer: ImageOverlayViewerTool.toolName,
|
||||
AdvancedMagnify: AdvancedMagnifyTool.toolName,
|
||||
UltrasoundDirectional: UltrasoundDirectionalTool.toolName,
|
||||
SplineROI: SplineROITool.toolName,
|
||||
LivewireContour: LivewireContourTool.toolName,
|
||||
PlanarFreehandROI: PlanarFreehandROITool.toolName,
|
||||
};
|
||||
|
||||
export { toolNames };
|
||||
Reference in new issue
Block a user