fix: remaining bugs for MPR and toolbar icon colors (#3026)
* feat: warn for overlapping segments * fix: typo * refactor mpr toggle and stack sync * fix: wip for more general mpr * fix: tool groups for mpr * fix: changing of viewport id * fix: viewport Id for the seg viewports * fix * update cornerstone version
This commit is contained in:
parent
6db4d7a917
commit
87eab39768
@ -36,6 +36,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolGroupService,
|
ToolGroupService,
|
||||||
SegmentationService,
|
SegmentationService,
|
||||||
|
UINotificationService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
|
|
||||||
const toolGroupId = `${SEG_TOOLGROUP_BASE_NAME}-${viewportIndex}`;
|
const toolGroupId = `${SEG_TOOLGROUP_BASE_NAME}-${viewportIndex}`;
|
||||||
@ -105,7 +106,6 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
} = referencedDisplaySetRef.current;
|
} = referencedDisplaySetRef.current;
|
||||||
|
|
||||||
// Todo: jump to the center of the first segment
|
// Todo: jump to the center of the first segment
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
@ -114,6 +114,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
viewportType: 'volume',
|
viewportType: 'volume',
|
||||||
toolGroupId: toolGroupId,
|
toolGroupId: toolGroupId,
|
||||||
orientation: viewportOptions.orientation,
|
orientation: viewportOptions.orientation,
|
||||||
|
viewportId: viewportOptions.viewportId,
|
||||||
}}
|
}}
|
||||||
onElementEnabled={onElementEnabled}
|
onElementEnabled={onElementEnabled}
|
||||||
onElementDisabled={onElementDisabled}
|
onElementDisabled={onElementDisabled}
|
||||||
@ -176,6 +177,15 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
) {
|
) {
|
||||||
setSegIsLoading(false);
|
setSegIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.overlappingSegments) {
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Overlapping Segments',
|
||||||
|
message:
|
||||||
|
'Overlapping segments detected which is not currently supported',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "7.16.3",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"@cornerstonejs/core": "^0.21.0",
|
"@cornerstonejs/core": "^0.21.4",
|
||||||
"@cornerstonejs/tools": "^0.29.2"
|
"@cornerstonejs/tools": "^0.29.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,9 +43,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.17.9",
|
"@babel/runtime": "7.17.9",
|
||||||
"@cornerstonejs/core": "^0.21.0",
|
"@cornerstonejs/core": "^0.21.4",
|
||||||
"@cornerstonejs/streaming-image-volume-loader": "^0.6.1",
|
"@cornerstonejs/streaming-image-volume-loader": "^0.6.5",
|
||||||
"@cornerstonejs/tools": "^0.29.2",
|
"@cornerstonejs/tools": "^0.29.6",
|
||||||
"@kitware/vtk.js": "25.9.0",
|
"@kitware/vtk.js": "25.9.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, useRef, useCallback, useState } from 'react';
|
import React, { useEffect, useRef, useCallback, useState } from 'react';
|
||||||
import ReactResizeDetector from 'react-resize-detector';
|
import ReactResizeDetector from 'react-resize-detector';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useViewportGrid } from '@ohif/ui';
|
|
||||||
import * as cs3DTools from '@cornerstonejs/tools';
|
import * as cs3DTools from '@cornerstonejs/tools';
|
||||||
import {
|
import {
|
||||||
Enums,
|
Enums,
|
||||||
@ -40,6 +39,13 @@ function areEqual(prevProps, nextProps) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
prevProps.viewportOptions.toolGroupId !==
|
||||||
|
nextProps.viewportOptions.toolGroupId
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prevProps.viewportOptions.viewportType !==
|
prevProps.viewportOptions.viewportType !==
|
||||||
nextProps.viewportOptions.viewportType
|
nextProps.viewportOptions.viewportType
|
||||||
@ -134,6 +140,28 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
}
|
}
|
||||||
}, [elementRef]);
|
}, [elementRef]);
|
||||||
|
|
||||||
|
const cleanUpServices = useCallback(() => {
|
||||||
|
const viewportInfo = CornerstoneViewportService.getViewportInfoByIndex(
|
||||||
|
viewportIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!viewportInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewportId = viewportInfo.getViewportId();
|
||||||
|
const renderingEngineId = viewportInfo.getRenderingEngineId();
|
||||||
|
const syncGroups = viewportInfo.getSyncGroups();
|
||||||
|
|
||||||
|
ToolGroupService.removeViewportFromToolGroup(viewportId, renderingEngineId);
|
||||||
|
|
||||||
|
SyncGroupService.removeViewportFromSyncGroup(
|
||||||
|
viewportId,
|
||||||
|
renderingEngineId,
|
||||||
|
syncGroups
|
||||||
|
);
|
||||||
|
}, [viewportIndex, viewportOptions.viewportId]);
|
||||||
|
|
||||||
const elementEnabledHandler = useCallback(
|
const elementEnabledHandler = useCallback(
|
||||||
evt => {
|
evt => {
|
||||||
// check this is this element reference and return early if doesn't match
|
// check this is this element reference and return early if doesn't match
|
||||||
@ -188,28 +216,15 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
setImageScrollBarHeight();
|
setImageScrollBarHeight();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const viewportInfo = CornerstoneViewportService.getViewportInfoByIndex(
|
cleanUpServices();
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
|
||||||
const renderingEngineId = viewportInfo.getRenderingEngineId();
|
|
||||||
const syncGroups = viewportInfo.getSyncGroups();
|
|
||||||
|
|
||||||
ToolGroupService.removeViewportFromToolGroup(
|
|
||||||
viewportId,
|
|
||||||
renderingEngineId
|
|
||||||
);
|
|
||||||
|
|
||||||
SyncGroupService.removeViewportFromSyncGroup(
|
|
||||||
viewportId,
|
|
||||||
renderingEngineId,
|
|
||||||
syncGroups
|
|
||||||
);
|
|
||||||
|
|
||||||
CornerstoneViewportService.disableElement(viewportIndex);
|
CornerstoneViewportService.disableElement(viewportIndex);
|
||||||
|
|
||||||
if (onElementDisabled) {
|
if (onElementDisabled) {
|
||||||
|
const viewportInfo = CornerstoneViewportService.getViewportInfoByIndex(
|
||||||
|
viewportIndex
|
||||||
|
);
|
||||||
|
|
||||||
onElementDisabled(viewportInfo);
|
onElementDisabled(viewportInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,8 +341,8 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
handleWidth
|
handleWidth
|
||||||
handleHeight
|
handleHeight
|
||||||
skipOnMount={true} // Todo: make these configurable
|
skipOnMount={true} // Todo: make these configurable
|
||||||
refreshMode={'throttle'}
|
refreshMode={'debounce'}
|
||||||
refreshRate={100}
|
refreshRate={200} // transition amount in side panel
|
||||||
onResize={onResize}
|
onResize={onResize}
|
||||||
targetRef={elementRef.current}
|
targetRef={elementRef.current}
|
||||||
/>
|
/>
|
||||||
@ -355,7 +370,7 @@ function _subscribeToJumpToMeasurementEvents(
|
|||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportGridService,
|
ViewportGridService,
|
||||||
CornerstoneViewportService
|
CornerstoneViewportService
|
||||||
) {
|
) {
|
||||||
const displaysUIDs = displaySets.map(
|
const displaysUIDs = displaySets.map(
|
||||||
|
|||||||
@ -7,34 +7,20 @@ import {
|
|||||||
ToolGroupManager,
|
ToolGroupManager,
|
||||||
Enums,
|
Enums,
|
||||||
utilities as cstUtils,
|
utilities as cstUtils,
|
||||||
segmentation as cstSegmentation,
|
|
||||||
ReferenceLinesTool,
|
ReferenceLinesTool,
|
||||||
synchronizers as cstSynchronizers,
|
|
||||||
} from '@cornerstonejs/tools';
|
} from '@cornerstonejs/tools';
|
||||||
|
|
||||||
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
|
||||||
|
|
||||||
import { getEnabledElement as OHIFgetEnabledElement } from './state';
|
import { getEnabledElement as OHIFgetEnabledElement } from './state';
|
||||||
|
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
||||||
import callInputDialog from './utils/callInputDialog';
|
import callInputDialog from './utils/callInputDialog';
|
||||||
import { setColormap } from './utils/colormap/transferFunctionHelpers';
|
import { setColormap } from './utils/colormap/transferFunctionHelpers';
|
||||||
import getProtocolViewportStructureFromGridViewports from './utils/getProtocolViewportStructureFromGridViewports';
|
import toggleMPRHangingProtocol from './utils/mpr/toggleMPRHangingProtocol';
|
||||||
import removeToolGroupSegmentationRepresentations from './utils/removeToolGroupSegmentationRepresentations';
|
import toggleStackImageSync from './utils/stackSync/toggleStackImageSync';
|
||||||
import calculateViewportRegistrations from './utils/calculateViewportRegistrations';
|
|
||||||
|
|
||||||
const MPR_TOOLGROUP_ID = 'mpr';
|
|
||||||
|
|
||||||
// [ {
|
|
||||||
// synchronizerId: string,
|
|
||||||
// viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
|
|
||||||
// ]}
|
|
||||||
let STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
|
||||||
|
|
||||||
const commandsModule = ({ servicesManager }) => {
|
const commandsModule = ({ servicesManager }) => {
|
||||||
const {
|
const {
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
ToolGroupService,
|
ToolGroupService,
|
||||||
DisplaySetService,
|
|
||||||
SyncGroupService,
|
|
||||||
CineService,
|
CineService,
|
||||||
ToolBarService,
|
ToolBarService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
@ -160,48 +146,51 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolGroup = _getToolGroup(toolGroupId);
|
|
||||||
|
|
||||||
if (!toolGroup) {
|
|
||||||
console.warn('No tool group found for toolGroupId:', toolGroupId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Todo: we need to check if the viewports of the toolGroup is actually
|
|
||||||
// parts of the ViewportGrid's viewports, if not we return
|
|
||||||
|
|
||||||
const { viewports } = ViewportGridService.getState() || {
|
const { viewports } = ViewportGridService.getState() || {
|
||||||
viewports: [],
|
viewports: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// iterate over all viewports and set the tool active for the
|
const toolGroup = _getToolGroup(toolGroupId);
|
||||||
// viewports that belong to the toolGroup
|
const toolGroupViewportIds = toolGroup.getViewportIds();
|
||||||
for (let index = 0; index < viewports.length; index++) {
|
|
||||||
const ohifEnabledElement = OHIFgetEnabledElement(index);
|
|
||||||
|
|
||||||
if (!ohifEnabledElement) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewport = getEnabledElement(ohifEnabledElement.element);
|
|
||||||
|
|
||||||
if (!viewport) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the current active tool and set it to be passive
|
|
||||||
const activeTool = toolGroup.getActivePrimaryMouseButtonTool();
|
|
||||||
|
|
||||||
if (activeTool) {
|
|
||||||
toolGroup.setToolPassive(activeTool);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new toolName to be active
|
|
||||||
toolGroup.setToolActive(toolName, {
|
|
||||||
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// if toolGroup has been destroyed, or its viewports have been removed
|
||||||
|
if (!toolGroupViewportIds || !toolGroupViewportIds.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filteredViewports = viewports.filter(viewport => {
|
||||||
|
if (!viewport.viewportOptions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolGroupViewportIds.includes(
|
||||||
|
viewport.viewportOptions.viewportId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!filteredViewports.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeToolName = toolGroup.getActivePrimaryMouseButtonTool();
|
||||||
|
|
||||||
|
if (activeToolName) {
|
||||||
|
// Todo: this is a hack to prevent the crosshairs to stick around
|
||||||
|
// after another tool is selected. We should find a better way to do this
|
||||||
|
if (activeToolName === 'Crosshairs') {
|
||||||
|
toolGroup.setToolDisabled(activeToolName);
|
||||||
|
} else {
|
||||||
|
toolGroup.setToolPassive(activeToolName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set the new toolName to be active
|
||||||
|
toolGroup.setToolActive(toolName, {
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
mouseButton: Enums.MouseBindings.Primary,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
showDownloadViewportModal: () => {
|
showDownloadViewportModal: () => {
|
||||||
const { activeViewportIndex } = ViewportGridService.getState();
|
const { activeViewportIndex } = ViewportGridService.getState();
|
||||||
@ -375,229 +364,17 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
HangingProtocolService.setProtocol(protocolId);
|
HangingProtocolService.setProtocol(protocolId);
|
||||||
},
|
},
|
||||||
toggleMPR: ({ toggledState }) => {
|
toggleMPR: ({ toggledState }) => {
|
||||||
const { activeViewportIndex, viewports } = ViewportGridService.getState();
|
toggleMPRHangingProtocol({
|
||||||
const viewportDisplaySetInstanceUIDs =
|
toggledState,
|
||||||
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
servicesManager,
|
||||||
|
getToolGroup: _getToolGroup,
|
||||||
const errorCallback = error => {
|
|
||||||
UINotificationService.show({
|
|
||||||
title: 'Multiplanar reconstruction (MPR) ',
|
|
||||||
message:
|
|
||||||
'Cannot create MPR for this DisplaySet since it is not reconstructable.',
|
|
||||||
type: 'info',
|
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const cacheId = 'beforeMPR';
|
|
||||||
if (toggledState) {
|
|
||||||
ViewportGridService.setCachedLayout({
|
|
||||||
cacheId,
|
|
||||||
cachedLayout: ViewportGridService.getState(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const matchDetails = {
|
|
||||||
displaySetInstanceUIDs: viewportDisplaySetInstanceUIDs,
|
|
||||||
};
|
|
||||||
|
|
||||||
HangingProtocolService.setProtocol(
|
|
||||||
MPR_TOOLGROUP_ID,
|
|
||||||
matchDetails,
|
|
||||||
errorCallback
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { cachedLayout } = ViewportGridService.getState();
|
|
||||||
|
|
||||||
if (!cachedLayout || !cachedLayout[cacheId]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { viewports: cachedViewports, numRows, numCols } = cachedLayout[
|
|
||||||
cacheId
|
|
||||||
];
|
|
||||||
|
|
||||||
// Todo: The following assumes that when turning off MPR we are applying the default
|
|
||||||
// protocol which might not be the one that was used before MPR was turned on
|
|
||||||
// In order to properly implement this logic, we should modify the hanging protocol
|
|
||||||
// upon layout change with layout selector, and cache and restore it when turning
|
|
||||||
// MPR on and off
|
|
||||||
const viewportStructure = getProtocolViewportStructureFromGridViewports({
|
|
||||||
viewports: cachedViewports,
|
|
||||||
numRows,
|
|
||||||
numCols,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewportSpecificMatch = cachedViewports.reduce(
|
|
||||||
(acc, viewport, index) => {
|
|
||||||
const {
|
|
||||||
displaySetInstanceUIDs,
|
|
||||||
viewportOptions,
|
|
||||||
displaySetOptions,
|
|
||||||
} = viewport;
|
|
||||||
|
|
||||||
acc[index] = {
|
|
||||||
displaySetInstanceUIDs,
|
|
||||||
viewportOptions,
|
|
||||||
displaySetOptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
const defaultProtocol = HangingProtocolService.getProtocolById('default');
|
|
||||||
|
|
||||||
// Todo: this assumes there is only one stage in the default protocol
|
|
||||||
const defaultProtocolStage = defaultProtocol.stages[0];
|
|
||||||
defaultProtocolStage.viewportStructure = viewportStructure;
|
|
||||||
|
|
||||||
const { primaryToolId } = ToolBarService.state;
|
|
||||||
const mprToolGroup = _getToolGroup(MPR_TOOLGROUP_ID);
|
|
||||||
// turn off crosshairs if it is on
|
|
||||||
if (
|
|
||||||
primaryToolId === 'Crosshairs' ||
|
|
||||||
mprToolGroup.getToolInstance('Crosshairs')?.mode ===
|
|
||||||
Enums.ToolModes.Active
|
|
||||||
) {
|
|
||||||
const toolGroup = _getToolGroup(MPR_TOOLGROUP_ID);
|
|
||||||
toolGroup.setToolDisabled('Crosshairs');
|
|
||||||
ToolBarService.recordInteraction({
|
|
||||||
groupId: 'WindowLevel',
|
|
||||||
itemId: 'WindowLevel',
|
|
||||||
interactionType: 'tool',
|
|
||||||
commands: [
|
|
||||||
{
|
|
||||||
commandName: 'setToolActive',
|
|
||||||
commandOptions: {
|
|
||||||
toolName: 'WindowLevel',
|
|
||||||
},
|
|
||||||
context: 'CORNERSTONE',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear segmentations if they exist
|
|
||||||
removeToolGroupSegmentationRepresentations(MPR_TOOLGROUP_ID);
|
|
||||||
|
|
||||||
HangingProtocolService.setProtocol(
|
|
||||||
'default',
|
|
||||||
viewportSpecificMatch,
|
|
||||||
error => {
|
|
||||||
UINotificationService.show({
|
|
||||||
title: 'Multiplanar reconstruction (MPR) ',
|
|
||||||
message:
|
|
||||||
'Something went wrong while trying to restore the previous layout.',
|
|
||||||
type: 'info',
|
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
toggleStackImageSync: ({ toggledState }) => {
|
toggleStackImageSync: ({ toggledState }) => {
|
||||||
if (!toggledState) {
|
toggleStackImageSync({
|
||||||
STACK_IMAGE_SYNC_GROUPS_INFO.forEach(syncGroupInfo => {
|
getEnabledElement,
|
||||||
const { viewports, synchronizerId } = syncGroupInfo;
|
servicesManager,
|
||||||
|
toggledState,
|
||||||
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
|
||||||
SyncGroupService.removeViewportFromSyncGroup(
|
|
||||||
viewportId,
|
|
||||||
renderingEngineId,
|
|
||||||
synchronizerId
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
|
||||||
|
|
||||||
// create synchronization groups and add viewports
|
|
||||||
let { viewports } = ViewportGridService.getState();
|
|
||||||
|
|
||||||
// filter empty viewports
|
|
||||||
viewports = viewports.filter(
|
|
||||||
viewport =>
|
|
||||||
viewport.displaySetInstanceUIDs &&
|
|
||||||
viewport.displaySetInstanceUIDs.length
|
|
||||||
);
|
|
||||||
|
|
||||||
// filter reconstructable viewports
|
|
||||||
viewports = viewports.filter(viewport => {
|
|
||||||
const { displaySetInstanceUIDs } = viewport;
|
|
||||||
|
|
||||||
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
|
||||||
const displaySet = DisplaySetService.getDisplaySetByUID(
|
|
||||||
displaySetInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
if (displaySet && displaySet.isReconstructable) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const viewportsByOrientation = viewports.reduce((acc, viewport) => {
|
|
||||||
const { viewportId, viewportType } = viewport.viewportOptions;
|
|
||||||
|
|
||||||
if (viewportType !== 'stack') {
|
|
||||||
console.warn('Viewport is not a stack, cannot sync images yet');
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { element } = CornerstoneViewportService.getViewportInfo(
|
|
||||||
viewportId
|
|
||||||
);
|
|
||||||
const { viewport: csViewport, renderingEngineId } = getEnabledElement(
|
|
||||||
element
|
|
||||||
);
|
|
||||||
const { viewPlaneNormal } = csViewport.getCamera();
|
|
||||||
|
|
||||||
// Should we round here? I guess so, but not sure how much precision we need
|
|
||||||
const orientation = viewPlaneNormal.map(v => Math.round(v)).join(',');
|
|
||||||
|
|
||||||
if (!acc[orientation]) {
|
|
||||||
acc[orientation] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
acc[orientation].push({ viewportId, renderingEngineId });
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
// create synchronizer for each group
|
|
||||||
Object.values(viewportsByOrientation).map(viewports => {
|
|
||||||
let synchronizerId = viewports
|
|
||||||
.map(({ viewportId }) => viewportId)
|
|
||||||
.join(',');
|
|
||||||
|
|
||||||
synchronizerId = `imageSync_${synchronizerId}`;
|
|
||||||
|
|
||||||
calculateViewportRegistrations(viewports);
|
|
||||||
|
|
||||||
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
|
||||||
SyncGroupService.addViewportToSyncGroup(
|
|
||||||
viewportId,
|
|
||||||
renderingEngineId,
|
|
||||||
{
|
|
||||||
type: 'stackimage',
|
|
||||||
id: synchronizerId,
|
|
||||||
source: true,
|
|
||||||
target: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
STACK_IMAGE_SYNC_GROUPS_INFO.push({
|
|
||||||
synchronizerId,
|
|
||||||
viewports,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
toggleReferenceLines: ({ toggledState }) => {
|
toggleReferenceLines: ({ toggledState }) => {
|
||||||
@ -635,11 +412,6 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
storeContexts: [],
|
storeContexts: [],
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
toggleCrosshairs: {
|
|
||||||
commandFn: actions.toggleCrosshairs,
|
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
rotateViewportCW: {
|
rotateViewportCW: {
|
||||||
commandFn: actions.rotateViewport,
|
commandFn: actions.rotateViewport,
|
||||||
storeContexts: [],
|
storeContexts: [],
|
||||||
|
|||||||
@ -362,13 +362,13 @@ export default async function init({
|
|||||||
const viewportId = `viewport-${viewportIndex}`;
|
const viewportId = `viewport-${viewportIndex}`;
|
||||||
const toolGroup = ToolGroupService.getToolGroupForViewport(viewportId);
|
const toolGroup = ToolGroupService.getToolGroupForViewport(viewportId);
|
||||||
|
|
||||||
if (!toolGroup) {
|
if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if reference lines are active
|
// check if reference lines are active
|
||||||
const referenceLinesEnabled =
|
const referenceLinesEnabled =
|
||||||
toolGroup._toolInstances?.['ReferenceLines'].mode ===
|
toolGroup._toolInstances['ReferenceLines'].mode ===
|
||||||
Enums.ToolModes.Enabled;
|
Enums.ToolModes.Enabled;
|
||||||
|
|
||||||
if (!referenceLinesEnabled) {
|
if (!referenceLinesEnabled) {
|
||||||
|
|||||||
@ -590,6 +590,8 @@ class SegmentationService {
|
|||||||
// Note: ideally we could use the TypedArray set method, but since each
|
// Note: ideally we could use the TypedArray set method, but since each
|
||||||
// slice can have multiple segments, we need to loop over each slice and
|
// slice can have multiple segments, we need to loop over each slice and
|
||||||
// set the segment value for each segment.
|
// set the segment value for each segment.
|
||||||
|
let overlappingSegments = false;
|
||||||
|
|
||||||
const _segmentInfoUpdate = (segmentInfo, segmentIndex) => {
|
const _segmentInfoUpdate = (segmentInfo, segmentIndex) => {
|
||||||
const { pixelData: segPixelData } = segmentInfo;
|
const { pixelData: segPixelData } = segmentInfo;
|
||||||
|
|
||||||
@ -636,6 +638,10 @@ class SegmentationService {
|
|||||||
i++, j++
|
i++, j++
|
||||||
) {
|
) {
|
||||||
if (functionGroupPixelData[j] !== 0) {
|
if (functionGroupPixelData[j] !== 0) {
|
||||||
|
if (derivedVolumeScalarData[i] !== 0) {
|
||||||
|
overlappingSegments = true;
|
||||||
|
}
|
||||||
|
|
||||||
derivedVolumeScalarData[i] = segmentIndex;
|
derivedVolumeScalarData[i] = segmentIndex;
|
||||||
|
|
||||||
// centroid calculations
|
// centroid calculations
|
||||||
@ -716,6 +722,7 @@ class SegmentationService {
|
|||||||
this._broadcastEvent(EVENTS.SEGMENTATION_PIXEL_DATA_CREATED, {
|
this._broadcastEvent(EVENTS.SEGMENTATION_PIXEL_DATA_CREATED, {
|
||||||
segmentationId,
|
segmentationId,
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
|
overlappingSegments,
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.addOrUpdateSegmentation(segmentationSchema, suppressEvents);
|
return this.addOrUpdateSegmentation(segmentationSchema, suppressEvents);
|
||||||
|
|||||||
@ -77,17 +77,14 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
viewportOptions: PublicViewportOptions,
|
viewportOptions: PublicViewportOptions,
|
||||||
elementRef: HTMLDivElement
|
elementRef: HTMLDivElement
|
||||||
) {
|
) {
|
||||||
const viewportId =
|
const viewportInfo = new ViewportInfo(
|
||||||
viewportOptions.viewportId || this.getViewportId(viewportIndex);
|
viewportIndex,
|
||||||
const viewportInfo = new ViewportInfo(viewportIndex, viewportId);
|
viewportOptions.viewportId
|
||||||
|
);
|
||||||
viewportInfo.setElement(elementRef);
|
viewportInfo.setElement(elementRef);
|
||||||
this.viewportsInfo.set(viewportIndex, viewportInfo);
|
this.viewportsInfo.set(viewportIndex, viewportInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getViewportId(viewportIndex: number): string {
|
|
||||||
return `viewport-${viewportIndex}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getViewportIds(): string[] {
|
public getViewportIds(): string[] {
|
||||||
const viewportIds = [];
|
const viewportIds = [];
|
||||||
|
|
||||||
@ -176,6 +173,22 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
): void {
|
): void {
|
||||||
const renderingEngine = this.getRenderingEngine();
|
const renderingEngine = this.getRenderingEngine();
|
||||||
const viewportInfo = this.viewportsInfo.get(viewportIndex);
|
const viewportInfo = this.viewportsInfo.get(viewportIndex);
|
||||||
|
let viewportId = viewportInfo.getViewportId();
|
||||||
|
|
||||||
|
// if currently there is a viewport with the viewportId, but it is not the same
|
||||||
|
// as the one we are trying to set, we need to disable the old one
|
||||||
|
// and enable the new one, we could ideally change the name of the viewportId
|
||||||
|
// but the viewportId is an integral part in renderers map, tools svg cache
|
||||||
|
// etc. which would require a lot of refactoring, for now we will just disable
|
||||||
|
// the old one and enable the new one at the end of this function
|
||||||
|
let newViewportId = null;
|
||||||
|
if (publicViewportOptions?.viewportId !== viewportId) {
|
||||||
|
newViewportId = publicViewportOptions.viewportId;
|
||||||
|
viewportInfo.setViewportId(newViewportId);
|
||||||
|
|
||||||
|
renderingEngine.disableElement(viewportId);
|
||||||
|
}
|
||||||
|
|
||||||
viewportInfo.setRenderingEngineId(renderingEngine.id);
|
viewportInfo.setRenderingEngineId(renderingEngine.id);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -196,7 +209,7 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
viewportIndex,
|
viewportIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
viewportId = viewportInfo.getViewportId();
|
||||||
const element = viewportInfo.getElement();
|
const element = viewportInfo.getElement();
|
||||||
const type = viewportInfo.getViewportType();
|
const type = viewportInfo.getViewportType();
|
||||||
const background = viewportInfo.getBackground();
|
const background = viewportInfo.getBackground();
|
||||||
@ -218,7 +231,8 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
// ENABLED_ELEMENT again and again, which will run onEnableElement callbacks
|
// ENABLED_ELEMENT again and again, which will run onEnableElement callbacks
|
||||||
renderingEngine.enableElement(viewportInput);
|
renderingEngine.enableElement(viewportInput);
|
||||||
|
|
||||||
this._setDisplaySets(viewportId, viewportData, viewportInfo);
|
const viewport = renderingEngine.getViewport(viewportId);
|
||||||
|
this._setDisplaySets(viewport, viewportData, viewportInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCornerstoneViewport(
|
public getCornerstoneViewport(
|
||||||
@ -616,12 +630,10 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_setDisplaySets(
|
_setDisplaySets(
|
||||||
viewportId: string,
|
viewport: StackViewport | VolumeViewport,
|
||||||
viewportData: StackViewportData | VolumeViewportData,
|
viewportData: StackViewportData | VolumeViewportData,
|
||||||
viewportInfo: ViewportInfo
|
viewportInfo: ViewportInfo
|
||||||
): void {
|
): void {
|
||||||
const viewport = this.getCornerstoneViewport(viewportId);
|
|
||||||
|
|
||||||
if (viewport instanceof StackViewport) {
|
if (viewport instanceof StackViewport) {
|
||||||
this._setStackViewport(
|
this._setStackViewport(
|
||||||
viewport,
|
viewport,
|
||||||
@ -687,10 +699,6 @@ class CornerstoneViewportService implements IViewportService {
|
|||||||
} {
|
} {
|
||||||
const viewportIndex = viewportInfo.getViewportIndex();
|
const viewportIndex = viewportInfo.getViewportIndex();
|
||||||
|
|
||||||
if (!publicViewportOptions.viewportId) {
|
|
||||||
publicViewportOptions.viewportId = this.getViewportId(viewportIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating a temporary viewportInfo to handle defaults
|
// Creating a temporary viewportInfo to handle defaults
|
||||||
const newViewportInfo = new ViewportInfo(
|
const newViewportInfo = new ViewportInfo(
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
|
|||||||
285
extensions/cornerstone/src/utils/mpr/toggleMPRHangingProtocol.ts
Normal file
285
extensions/cornerstone/src/utils/mpr/toggleMPRHangingProtocol.ts
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
import { Enums } from '@cornerstonejs/tools';
|
||||||
|
import removeToolGroupSegmentationRepresentations from '../removeToolGroupSegmentationRepresentations';
|
||||||
|
|
||||||
|
const MPR_TOOLGROUP_ID = 'mpr';
|
||||||
|
|
||||||
|
const cachedState = {
|
||||||
|
protocol: null,
|
||||||
|
stage: null,
|
||||||
|
viewportMatchDetails: null,
|
||||||
|
viewportStructure: null,
|
||||||
|
toolOptions: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCachedState = (
|
||||||
|
protocol,
|
||||||
|
stage,
|
||||||
|
viewportMatchDetails,
|
||||||
|
viewportStructure,
|
||||||
|
toolOptions
|
||||||
|
) => {
|
||||||
|
cachedState.protocol = protocol;
|
||||||
|
cachedState.stage = stage;
|
||||||
|
cachedState.viewportMatchDetails = viewportMatchDetails;
|
||||||
|
cachedState.viewportStructure = viewportStructure;
|
||||||
|
cachedState.toolOptions = JSON.parse(JSON.stringify(toolOptions));
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetCachedState = () => {
|
||||||
|
cachedState.protocol = null;
|
||||||
|
cachedState.stage = null;
|
||||||
|
cachedState.viewportMatchDetails = null;
|
||||||
|
cachedState.viewportStructure = null;
|
||||||
|
cachedState.toolOptions = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function toggleMPRHangingProtocol({
|
||||||
|
toggledState,
|
||||||
|
servicesManager,
|
||||||
|
getToolGroup,
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
HangingProtocolService,
|
||||||
|
ViewportGridService,
|
||||||
|
ToolBarService,
|
||||||
|
} = servicesManager.services;
|
||||||
|
|
||||||
|
const {
|
||||||
|
activeViewportIndex,
|
||||||
|
viewports,
|
||||||
|
numRows,
|
||||||
|
numCols,
|
||||||
|
} = ViewportGridService.getState();
|
||||||
|
const viewportDisplaySetInstanceUIDs =
|
||||||
|
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
||||||
|
|
||||||
|
const errorCallback = error => {
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Multiplanar reconstruction (MPR) ',
|
||||||
|
message:
|
||||||
|
'Cannot create MPR for this DisplaySet since it is not reconstructable.',
|
||||||
|
type: 'info',
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// What is the current active protocol and stage number to restore later
|
||||||
|
const { protocol, stage } = HangingProtocolService.getActiveProtocol();
|
||||||
|
|
||||||
|
if (toggledState) {
|
||||||
|
resetCachedState();
|
||||||
|
|
||||||
|
const {
|
||||||
|
viewportMatchDetails,
|
||||||
|
viewportStructure,
|
||||||
|
toolOptions,
|
||||||
|
} = _getViewportsInfo({
|
||||||
|
protocol,
|
||||||
|
stage,
|
||||||
|
viewports,
|
||||||
|
servicesManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
setCachedState(
|
||||||
|
protocol,
|
||||||
|
stage,
|
||||||
|
viewportMatchDetails,
|
||||||
|
viewportStructure,
|
||||||
|
toolOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
const matchDetails = {
|
||||||
|
displaySetInstanceUIDs: viewportDisplaySetInstanceUIDs,
|
||||||
|
};
|
||||||
|
|
||||||
|
_disableCrosshairs(
|
||||||
|
toolOptions.map(({ toolGroupId }) => toolGroupId),
|
||||||
|
getToolGroup
|
||||||
|
);
|
||||||
|
|
||||||
|
HangingProtocolService.setProtocol(
|
||||||
|
MPR_TOOLGROUP_ID,
|
||||||
|
matchDetails,
|
||||||
|
errorCallback
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const restoreErrorCallback = error => {
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Multiplanar reconstruction (MPR) ',
|
||||||
|
message:
|
||||||
|
'Something went wrong while trying to restore the previous layout.',
|
||||||
|
type: 'info',
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_disableCrosshairs([MPR_TOOLGROUP_ID], getToolGroup);
|
||||||
|
|
||||||
|
const { layoutType, properties } = cachedState.viewportStructure;
|
||||||
|
const { viewportMatchDetails } = cachedState;
|
||||||
|
|
||||||
|
// The reason we split the flow here is that we don't allow viewport grid
|
||||||
|
// change in the non default hanging protocol, so we can just apply the
|
||||||
|
// cached protocol and stage. However, for the default protocol, we need
|
||||||
|
// to also apply the layout type and properties.
|
||||||
|
if (cachedState.protocol.id !== 'default') {
|
||||||
|
HangingProtocolService.setProtocol(
|
||||||
|
cachedState.protocol.id,
|
||||||
|
viewportMatchDetails,
|
||||||
|
restoreErrorCallback
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HangingProtocolService.setProtocol(
|
||||||
|
'default',
|
||||||
|
viewportMatchDetails,
|
||||||
|
restoreErrorCallback
|
||||||
|
);
|
||||||
|
|
||||||
|
if (numRows !== properties.rows || numCols !== properties.columns) {
|
||||||
|
ViewportGridService.setLayout({
|
||||||
|
numRows: properties.rows,
|
||||||
|
numCols: properties.columns,
|
||||||
|
layoutType,
|
||||||
|
layoutOptions: properties.layoutOptions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const numViewports =
|
||||||
|
properties.layoutOptions.length || properties.rows * properties.columns;
|
||||||
|
|
||||||
|
// loop inside viewportMatchDetails map
|
||||||
|
// and set the viewportOptions for each viewport
|
||||||
|
[...Array(numViewports).keys()].forEach(viewportIndex => {
|
||||||
|
const viewportMatchDetailsForViewport = viewportMatchDetails.get(
|
||||||
|
viewportIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
if (viewportMatchDetailsForViewport) {
|
||||||
|
const {
|
||||||
|
viewportOptions,
|
||||||
|
displaySetsInfo,
|
||||||
|
} = viewportMatchDetailsForViewport;
|
||||||
|
ViewportGridService.setDisplaySetsForViewport({
|
||||||
|
viewportIndex,
|
||||||
|
displaySetInstanceUIDs: displaySetsInfo.map(
|
||||||
|
displaySetInfo => displaySetInfo.displaySetInstanceUID
|
||||||
|
),
|
||||||
|
viewportOptions,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ViewportGridService.setDisplaySetsForViewport({
|
||||||
|
viewportIndex,
|
||||||
|
displaySetInstanceUIDs: [],
|
||||||
|
viewportOptions: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ToolBarService.recordInteraction({
|
||||||
|
groupId: 'WindowLevel',
|
||||||
|
itemId: 'WindowLevel',
|
||||||
|
interactionType: 'tool',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
commandName: 'setToolActive',
|
||||||
|
commandOptions: {
|
||||||
|
toolName: 'WindowLevel',
|
||||||
|
},
|
||||||
|
context: 'CORNERSTONE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
//clear segmentations if they exist
|
||||||
|
removeToolGroupSegmentationRepresentations(MPR_TOOLGROUP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _disableCrosshairs(toolGroupIds, getToolGroup) {
|
||||||
|
toolGroupIds.forEach(toolGroupId => {
|
||||||
|
const toolGroup = getToolGroup(toolGroupId);
|
||||||
|
if (
|
||||||
|
toolGroup.getToolInstance('Crosshairs')?.mode === Enums.ToolModes.Active
|
||||||
|
) {
|
||||||
|
toolGroup.setToolDisabled('Crosshairs');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getViewportsInfo({ protocol, stage, viewports, servicesManager }) {
|
||||||
|
// here we need to use the viewports and try to map it into the
|
||||||
|
// viewportMatchDetails and displaySetMatch that HangingProtocolService
|
||||||
|
// expects
|
||||||
|
const {
|
||||||
|
ViewportGridService,
|
||||||
|
HangingProtocolService,
|
||||||
|
ToolGroupService,
|
||||||
|
} = servicesManager.services;
|
||||||
|
|
||||||
|
const { numRows, numCols } = ViewportGridService.getState();
|
||||||
|
|
||||||
|
let viewportMatchDetails = new Map();
|
||||||
|
|
||||||
|
const viewportStructure = {
|
||||||
|
layoutType: 'grid',
|
||||||
|
properties: {
|
||||||
|
rows: numRows,
|
||||||
|
columns: numCols,
|
||||||
|
layoutOptions: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
viewports.forEach((viewport, viewportIndex) => {
|
||||||
|
viewportStructure.properties.layoutOptions.push({
|
||||||
|
x: viewport.x,
|
||||||
|
y: viewport.y,
|
||||||
|
width: viewport.width,
|
||||||
|
height: viewport.height,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (protocol.id === 'default') {
|
||||||
|
viewports.forEach((viewport, viewportIndex) => {
|
||||||
|
if (viewport.displaySetInstanceUIDs) {
|
||||||
|
viewportMatchDetails.set(viewportIndex, {
|
||||||
|
displaySetsInfo: viewport.displaySetInstanceUIDs.map(
|
||||||
|
displaySetInstanceUID => {
|
||||||
|
return { displaySetInstanceUID };
|
||||||
|
}
|
||||||
|
),
|
||||||
|
viewportOptions: viewport.viewportOptions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
({ viewportMatchDetails } = HangingProtocolService.getMatchDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the toolGroup state for viewports
|
||||||
|
let toolOptions = [];
|
||||||
|
const viewportIds = viewports
|
||||||
|
.map(
|
||||||
|
viewport =>
|
||||||
|
viewport.displaySetInstanceUIDs &&
|
||||||
|
viewport.displaySetInstanceUIDs.length > 0 &&
|
||||||
|
viewport.viewportOptions?.viewportId
|
||||||
|
)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (viewportIds.length) {
|
||||||
|
toolOptions = viewportIds.map(viewportId => {
|
||||||
|
const toolGroup = ToolGroupService.getToolGroupForViewport(viewportId);
|
||||||
|
return {
|
||||||
|
toolGroupId: toolGroup.id,
|
||||||
|
toolOptions: toolGroup.toolOptions,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { viewportMatchDetails, viewportStructure, toolOptions };
|
||||||
|
}
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
import calculateViewportRegistrations from './calculateViewportRegistrations';
|
||||||
|
|
||||||
|
// [ {
|
||||||
|
// synchronizerId: string,
|
||||||
|
// viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
|
||||||
|
// ]}
|
||||||
|
let STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
||||||
|
|
||||||
|
export default function toggleStackImageSync({
|
||||||
|
toggledState,
|
||||||
|
servicesManager,
|
||||||
|
getEnabledElement,
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
SyncGroupService,
|
||||||
|
ViewportGridService,
|
||||||
|
DisplaySetService,
|
||||||
|
CornerstoneViewportService,
|
||||||
|
} = servicesManager.services;
|
||||||
|
|
||||||
|
if (!toggledState) {
|
||||||
|
STACK_IMAGE_SYNC_GROUPS_INFO.forEach(syncGroupInfo => {
|
||||||
|
const { viewports, synchronizerId } = syncGroupInfo;
|
||||||
|
|
||||||
|
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
||||||
|
SyncGroupService.removeViewportFromSyncGroup(
|
||||||
|
viewportId,
|
||||||
|
renderingEngineId,
|
||||||
|
synchronizerId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
||||||
|
|
||||||
|
// create synchronization groups and add viewports
|
||||||
|
let { viewports } = ViewportGridService.getState();
|
||||||
|
|
||||||
|
// filter empty viewports
|
||||||
|
viewports = viewports.filter(
|
||||||
|
viewport =>
|
||||||
|
viewport.displaySetInstanceUIDs && viewport.displaySetInstanceUIDs.length
|
||||||
|
);
|
||||||
|
|
||||||
|
// filter reconstructable viewports
|
||||||
|
viewports = viewports.filter(viewport => {
|
||||||
|
const { displaySetInstanceUIDs } = viewport;
|
||||||
|
|
||||||
|
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
||||||
|
const displaySet = DisplaySetService.getDisplaySetByUID(
|
||||||
|
displaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
if (displaySet && displaySet.isReconstructable) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewportsByOrientation = viewports.reduce((acc, viewport) => {
|
||||||
|
const { viewportId, viewportType } = viewport.viewportOptions;
|
||||||
|
|
||||||
|
if (viewportType !== 'stack') {
|
||||||
|
console.warn('Viewport is not a stack, cannot sync images yet');
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { element } = CornerstoneViewportService.getViewportInfo(viewportId);
|
||||||
|
const { viewport: csViewport, renderingEngineId } = getEnabledElement(
|
||||||
|
element
|
||||||
|
);
|
||||||
|
const { viewPlaneNormal } = csViewport.getCamera();
|
||||||
|
|
||||||
|
// Should we round here? I guess so, but not sure how much precision we need
|
||||||
|
const orientation = viewPlaneNormal.map(v => Math.round(v)).join(',');
|
||||||
|
|
||||||
|
if (!acc[orientation]) {
|
||||||
|
acc[orientation] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[orientation].push({ viewportId, renderingEngineId });
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// create synchronizer for each group
|
||||||
|
Object.values(viewportsByOrientation).map(viewports => {
|
||||||
|
let synchronizerId = viewports
|
||||||
|
.map(({ viewportId }) => viewportId)
|
||||||
|
.join(',');
|
||||||
|
|
||||||
|
synchronizerId = `imageSync_${synchronizerId}`;
|
||||||
|
|
||||||
|
calculateViewportRegistrations(viewports);
|
||||||
|
|
||||||
|
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
||||||
|
SyncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, {
|
||||||
|
type: 'stackimage',
|
||||||
|
id: synchronizerId,
|
||||||
|
source: true,
|
||||||
|
target: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
STACK_IMAGE_SYNC_GROUPS_INFO.push({
|
||||||
|
synchronizerId,
|
||||||
|
viewports,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -92,7 +92,7 @@ function createDicomWebApi(dicomWebConfig, UserAuthenticationService) {
|
|||||||
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
|
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
|
||||||
|
|
||||||
const StudyInstanceUIDs =
|
const StudyInstanceUIDs =
|
||||||
queryStudyInstanceUIDs.length && queryStudyInstanceUIDs ||
|
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
|
||||||
paramsStudyInstanceUIDs;
|
paramsStudyInstanceUIDs;
|
||||||
const StudyInstanceUIDsAsArray =
|
const StudyInstanceUIDsAsArray =
|
||||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||||
|
|||||||
@ -7,7 +7,7 @@ const commandsModule = ({ servicesManager, commandsManager }) => {
|
|||||||
HangingProtocolService,
|
HangingProtocolService,
|
||||||
UINotificationService,
|
UINotificationService,
|
||||||
ViewportGridService,
|
ViewportGridService,
|
||||||
DisplaySetService
|
DisplaySetService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
@ -30,16 +30,13 @@ const commandsModule = ({ servicesManager, commandsManager }) => {
|
|||||||
},
|
},
|
||||||
openDICOMTagViewer() {
|
openDICOMTagViewer() {
|
||||||
const { activeViewportIndex, viewports } = ViewportGridService.getState();
|
const { activeViewportIndex, viewports } = ViewportGridService.getState();
|
||||||
const activeViewportSpecificData =
|
const activeViewportSpecificData = viewports[activeViewportIndex];
|
||||||
viewports[activeViewportIndex];
|
const { displaySetInstanceUIDs } = activeViewportSpecificData;
|
||||||
const {
|
|
||||||
displaySetInstanceUIDs,
|
|
||||||
} = activeViewportSpecificData;
|
|
||||||
|
|
||||||
const displaySets = DisplaySetService.activeDisplaySets;
|
const displaySets = DisplaySetService.activeDisplaySets;
|
||||||
const { UIModalService } = servicesManager.services;
|
const { UIModalService } = servicesManager.services;
|
||||||
|
|
||||||
const displaySetInstanceUID = displaySetInstanceUIDs[0]
|
const displaySetInstanceUID = displaySetInstanceUIDs[0];
|
||||||
UIModalService.show({
|
UIModalService.show({
|
||||||
content: DicomTagBrowser,
|
content: DicomTagBrowser,
|
||||||
contentProps: {
|
contentProps: {
|
||||||
@ -47,9 +44,9 @@ const commandsModule = ({ servicesManager, commandsManager }) => {
|
|||||||
displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
onClose: UIModalService.hide,
|
onClose: UIModalService.hide,
|
||||||
},
|
},
|
||||||
title: 'DICOM Tag Browser'
|
title: 'DICOM Tag Browser',
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const definitions = {
|
const definitions = {
|
||||||
@ -75,7 +72,7 @@ const commandsModule = ({ servicesManager, commandsManager }) => {
|
|||||||
},
|
},
|
||||||
openDICOMTagViewer: {
|
openDICOMTagViewer: {
|
||||||
commandFn: actions.openDICOMTagViewer,
|
commandFn: actions.openDICOMTagViewer,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^3.0.0",
|
"@ohif/core": "^3.0.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"@cornerstonejs/core": "^0.21.0",
|
"@cornerstonejs/core": "^0.21.4",
|
||||||
"@cornerstonejs/tools": "^0.29.2",
|
"@cornerstonejs/tools": "^0.29.6",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||||
"dcmjs": "^0.28.3",
|
"dcmjs": "^0.28.3",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
|
|||||||
@ -29,6 +29,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
commandsManager,
|
commandsManager,
|
||||||
|
viewportOptions,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { t } = useTranslation('TrackedViewport');
|
const { t } = useTranslation('TrackedViewport');
|
||||||
@ -50,8 +51,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
const [element, setElement] = useState(null);
|
const [element, setElement] = useState(null);
|
||||||
|
|
||||||
const { trackedSeries } = trackedMeasurements.context;
|
const { trackedSeries } = trackedMeasurements.context;
|
||||||
|
const viewportId = viewportOptions.viewportId;
|
||||||
const viewportId = CornerstoneViewportService.getViewportId(viewportIndex);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
Modality,
|
Modality,
|
||||||
@ -86,8 +86,6 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
cineService.playClip(element, {
|
cineService.playClip(element, {
|
||||||
framesPerSecond: validFrameRate,
|
framesPerSecond: validFrameRate,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
cineService.stopClip(element);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -91,8 +91,6 @@ function modeFactory({ modeConfiguration }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Since we only have one viewport for the basic cs3d mode and it has
|
|
||||||
// only one hanging protocol, we can just use the first viewport
|
|
||||||
const { unsubscribe } = ToolGroupService.subscribe(
|
const { unsubscribe } = ToolGroupService.subscribe(
|
||||||
ToolGroupService.EVENTS.VIEWPORT_ADDED,
|
ToolGroupService.EVENTS.VIEWPORT_ADDED,
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@ -4,9 +4,10 @@ export const toolGroupIds = {
|
|||||||
Fusion: 'fusionToolGroup',
|
Fusion: 'fusionToolGroup',
|
||||||
MIP: 'mipToolGroup',
|
MIP: 'mipToolGroup',
|
||||||
default: 'default',
|
default: 'default',
|
||||||
|
// MPR: 'mpr',
|
||||||
};
|
};
|
||||||
|
|
||||||
function initToolGroups(toolNames, Enums, ToolGroupService, commandsManager) {
|
function _initToolGroups(toolNames, Enums, ToolGroupService, commandsManager) {
|
||||||
const tools = {
|
const tools = {
|
||||||
active: [
|
active: [
|
||||||
{
|
{
|
||||||
@ -43,7 +44,7 @@ function initToolGroups(toolNames, Enums, ToolGroupService, commandsManager) {
|
|||||||
[toolNames.Crosshairs]: {
|
[toolNames.Crosshairs]: {
|
||||||
viewportIndicators: false,
|
viewportIndicators: false,
|
||||||
autoPan: {
|
autoPan: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
panSize: 10,
|
panSize: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -122,4 +123,70 @@ function initToolGroups(toolNames, Enums, ToolGroupService, commandsManager) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initMPRToolGroup(toolNames, Enums, ToolGroupService, commandsManager) {
|
||||||
|
const tools = {
|
||||||
|
active: [
|
||||||
|
{
|
||||||
|
toolName: toolNames.WindowLevel,
|
||||||
|
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolName: toolNames.Pan,
|
||||||
|
bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolName: toolNames.Zoom,
|
||||||
|
bindings: [{ mouseButton: Enums.MouseBindings.Secondary }],
|
||||||
|
},
|
||||||
|
{ toolName: toolNames.StackScrollMouseWheel, bindings: [] },
|
||||||
|
],
|
||||||
|
passive: [
|
||||||
|
{ toolName: toolNames.Length },
|
||||||
|
{ toolName: toolNames.ArrowAnnotate },
|
||||||
|
{ toolName: toolNames.Bidirectional },
|
||||||
|
{ toolName: toolNames.DragProbe },
|
||||||
|
{ toolName: toolNames.EllipticalROI },
|
||||||
|
{ toolName: toolNames.RectangleROI },
|
||||||
|
{ toolName: toolNames.StackScroll },
|
||||||
|
{ toolName: toolNames.Angle },
|
||||||
|
{ toolName: toolNames.SegmentationDisplay },
|
||||||
|
],
|
||||||
|
disabled: [{ toolName: toolNames.Crosshairs }],
|
||||||
|
|
||||||
|
// enabled
|
||||||
|
// disabled
|
||||||
|
};
|
||||||
|
|
||||||
|
const toolsConfig = {
|
||||||
|
[toolNames.Crosshairs]: {
|
||||||
|
viewportIndicators: false,
|
||||||
|
autoPan: {
|
||||||
|
enabled: false,
|
||||||
|
panSize: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[toolNames.ArrowAnnotate]: {
|
||||||
|
getTextCallback: (callback, eventDetails) =>
|
||||||
|
commandsManager.runCommand('arrowTextCallback', {
|
||||||
|
callback,
|
||||||
|
eventDetails,
|
||||||
|
}),
|
||||||
|
|
||||||
|
changeTextCallback: (data, eventDetails, callback) =>
|
||||||
|
commandsManager.runCommand('arrowTextCallback', {
|
||||||
|
callback,
|
||||||
|
data,
|
||||||
|
eventDetails,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ToolGroupService.createToolGroupAndAddTools('mpr', tools, toolsConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initToolGroups(toolNames, Enums, ToolGroupService, commandsManager) {
|
||||||
|
_initToolGroups(toolNames, Enums, ToolGroupService, commandsManager);
|
||||||
|
// initMPRToolGroup(toolNames, Enums, ToolGroupService, commandsManager);
|
||||||
|
}
|
||||||
|
|
||||||
export default initToolGroups;
|
export default initToolGroups;
|
||||||
|
|||||||
@ -105,6 +105,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
'Length'
|
'Length'
|
||||||
@ -125,6 +126,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
'Length Tool'
|
'Length Tool'
|
||||||
@ -138,6 +140,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
'Bidirectional Tool'
|
'Bidirectional Tool'
|
||||||
@ -151,6 +154,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
'Arrow Annotate'
|
'Arrow Annotate'
|
||||||
@ -164,6 +168,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
'Ellipse Tool'
|
'Ellipse Tool'
|
||||||
@ -184,10 +189,27 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'MPR',
|
||||||
|
type: 'ohif.action',
|
||||||
|
props: {
|
||||||
|
type: 'toggle',
|
||||||
|
icon: 'icon-mpr',
|
||||||
|
label: 'MPR',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
commandName: 'toggleMPR',
|
||||||
|
commandOptions: {},
|
||||||
|
context: 'CORNERSTONE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
// Window Level + Presets...
|
// Window Level + Presets...
|
||||||
{
|
{
|
||||||
id: 'WindowLevel',
|
id: 'WindowLevel',
|
||||||
@ -203,6 +225,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
'Window Level'
|
'Window Level'
|
||||||
@ -236,6 +259,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -253,6 +277,7 @@ const toolbarButtons = [
|
|||||||
toolGroupIds.CT,
|
toolGroupIds.CT,
|
||||||
toolGroupIds.PT,
|
toolGroupIds.PT,
|
||||||
toolGroupIds.Fusion,
|
toolGroupIds.Fusion,
|
||||||
|
// toolGroupIds.MPR,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -122,6 +122,13 @@ class HangingProtocolService {
|
|||||||
this.displaySetMatchDetails = new Map();
|
this.displaySetMatchDetails = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getActiveProtocol(): {
|
||||||
|
protocol: HangingProtocol.Protocol;
|
||||||
|
stage: number;
|
||||||
|
} {
|
||||||
|
return { protocol: this.protocol, stage: this.stage };
|
||||||
|
}
|
||||||
|
|
||||||
public getDefaultProtocol(): HangingProtocol.Protocol {
|
public getDefaultProtocol(): HangingProtocol.Protocol {
|
||||||
return this.getProtocolById('default');
|
return this.getProtocolById('default');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,10 @@ export default function sortInstances(instances: Array<any>) {
|
|||||||
ImageOrientationPatient,
|
ImageOrientationPatient,
|
||||||
} = instances[Math.floor(instances.length / 2)]; // this prevents getting scout image as test image
|
} = instances[Math.floor(instances.length / 2)]; // this prevents getting scout image as test image
|
||||||
|
|
||||||
|
if (!referenceImagePositionPatient || !ImageOrientationPatient) {
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
const rowCosineVec = vec3.fromValues(
|
const rowCosineVec = vec3.fromValues(
|
||||||
ImageOrientationPatient[0],
|
ImageOrientationPatient[0],
|
||||||
ImageOrientationPatient[1],
|
ImageOrientationPatient[1],
|
||||||
|
|||||||
@ -52,5 +52,4 @@ const utilityModule = extensionManager.getModuleEntry(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { CornerstoneViewportService } = utilityModule.exports;
|
const { CornerstoneViewportService } = utilityModule.exports;
|
||||||
const viewportId = CornerstoneViewportService.getViewportId(viewportIndex);
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -33,27 +33,47 @@ const variantClasses = {
|
|||||||
},
|
},
|
||||||
outlined: {
|
outlined: {
|
||||||
default:
|
default:
|
||||||
'border bg-trasparent border-primary-light text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
'border border-primary-light text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
primary:
|
primary:
|
||||||
'border bg-transparent border-primary-main text-primary-main hover:opacity-80 active:opacity-100 focus:opacity-80',
|
'border border-primary-main text-primary-main hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
secondary:
|
secondary:
|
||||||
'border bg-transparent border-secondary-light text-secondary-light hover:opacity-80 active:opacity-100 focus:opacity-80',
|
'border border-secondary-light text-secondary-light hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
white:
|
white:
|
||||||
'border bg-transparent border-white text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
'border border-white text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
black:
|
black:
|
||||||
'border bg-black border-primary-main text-white hover:bg-primary-main focus:bg-primary-main hover:border-black focus:border-black',
|
'border border-primary-main text-white hover:bg-primary-main focus:bg-primary-main hover:border-black focus:border-black',
|
||||||
},
|
},
|
||||||
contained: {
|
contained: {
|
||||||
default:
|
default: 'text-black hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
'bg-primary-light text-black hover:opacity-80 active:opacity-100 focus:opacity-80',
|
primary: 'text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
primary:
|
|
||||||
'bg-primary-main text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
|
||||||
secondary:
|
secondary:
|
||||||
'bg-secondary-light text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
'text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
white:
|
white: 'text-black hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
'bg-white text-black hover:opacity-80 active:opacity-100 focus:opacity-80',
|
black: 'text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
||||||
black:
|
},
|
||||||
'bg-black text-white hover:opacity-80 active:opacity-100 focus:opacity-80',
|
};
|
||||||
|
|
||||||
|
const backgroundClasses = {
|
||||||
|
text: {
|
||||||
|
default: '',
|
||||||
|
primary: '',
|
||||||
|
secondary: '',
|
||||||
|
white: '',
|
||||||
|
black: '',
|
||||||
|
},
|
||||||
|
outlined: {
|
||||||
|
default: 'bg-transparent',
|
||||||
|
primary: 'bg-transparent',
|
||||||
|
secondary: 'bg-transparent',
|
||||||
|
white: 'bg-transparent',
|
||||||
|
black: 'bg-black',
|
||||||
|
},
|
||||||
|
contained: {
|
||||||
|
default: 'bg-primary-light',
|
||||||
|
primary: 'bg-primary-main',
|
||||||
|
secondary: 'bg-secondary-light',
|
||||||
|
white: 'bg-white',
|
||||||
|
black: 'bg-black',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,6 +110,7 @@ const IconButton = ({
|
|||||||
className,
|
className,
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
|
bgColor,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const buttonElement = useRef(null);
|
const buttonElement = useRef(null);
|
||||||
@ -98,6 +119,9 @@ const IconButton = ({
|
|||||||
buttonElement.current.blur();
|
buttonElement.current.blur();
|
||||||
onClick(e);
|
onClick(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const bgColorToUse = bgColor ? bgColor : backgroundClasses[variant][color];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={classnames(
|
className={classnames(
|
||||||
@ -107,6 +131,7 @@ const IconButton = ({
|
|||||||
sizeClasses[size],
|
sizeClasses[size],
|
||||||
fullWidthClasses[fullWidth],
|
fullWidthClasses[fullWidth],
|
||||||
disabledClasses[disabled],
|
disabledClasses[disabled],
|
||||||
|
bgColorToUse,
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import classnames from 'classnames';
|
|||||||
import { IconButton, Icon, Tooltip } from '../';
|
import { IconButton, Icon, Tooltip } from '../';
|
||||||
|
|
||||||
const ToolbarButton = ({
|
const ToolbarButton = ({
|
||||||
type,
|
type = 'tool',
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
label,
|
label,
|
||||||
@ -20,14 +20,19 @@ const ToolbarButton = ({
|
|||||||
const { primaryToolId } = bState;
|
const { primaryToolId } = bState;
|
||||||
const isActive = _isActive || (type === 'tool' && id === primaryToolId);
|
const isActive = _isActive || (type === 'tool' && id === primaryToolId);
|
||||||
const classes = {
|
const classes = {
|
||||||
type: {
|
tool: isActive
|
||||||
primary: isActive
|
? 'text-black'
|
||||||
? 'text-black'
|
: 'text-common-bright hover:bg-primary-dark hover:text-primary-light',
|
||||||
: 'text-common-bright hover:bg-primary-dark hover:text-primary-light',
|
toggle: isActive
|
||||||
secondary: isActive
|
? 'text-[#348CFD]'
|
||||||
? 'text-black'
|
: 'text-common-bright hover:bg-primary-dark hover:text-primary-light',
|
||||||
: 'text-white hover:bg-secondary-dark hover:text-white focus:bg-secondary-dark focus:text-white',
|
action: isActive
|
||||||
},
|
? 'text-black'
|
||||||
|
: 'text-common-bright hover:bg-primary-dark hover:text-primary-light',
|
||||||
|
};
|
||||||
|
|
||||||
|
const bgClasses = {
|
||||||
|
toggle: isActive && 'bg-transparent',
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeClass = isActive ? 'active' : '';
|
const activeClass = isActive ? 'active' : '';
|
||||||
@ -42,8 +47,9 @@ const ToolbarButton = ({
|
|||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
variant={isActive ? 'contained' : 'text'}
|
variant={isActive ? 'contained' : 'text'}
|
||||||
|
bgColor={bgClasses[type]}
|
||||||
size="toolbar"
|
size="toolbar"
|
||||||
className={classnames('mx-1', activeClass, classes.type[type])}
|
className={classnames('mx-1', activeClass, classes[type])}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onInteraction({
|
onInteraction({
|
||||||
itemId: id,
|
itemId: id,
|
||||||
|
|||||||
@ -64,6 +64,10 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
|
|
||||||
const viewports = state.viewports.slice();
|
const viewports = state.viewports.slice();
|
||||||
|
|
||||||
|
if (!viewportOptions.viewportId) {
|
||||||
|
viewportOptions.viewportId = `viewport-${viewportIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
// merge the displaySetOptions and viewportOptions and displaySetInstanceUIDs
|
// merge the displaySetOptions and viewportOptions and displaySetInstanceUIDs
|
||||||
// into the viewport object at the given index
|
// into the viewport object at the given index
|
||||||
viewports[viewportIndex] = {
|
viewports[viewportIndex] = {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ window.config = {
|
|||||||
extensions: [],
|
extensions: [],
|
||||||
modes: [],
|
modes: [],
|
||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
// maxNumberOfWebWorkers: 3,
|
maxNumberOfWebWorkers: 4,
|
||||||
// below flag is for performance reasons, but it might not work for all servers
|
// below flag is for performance reasons, but it might not work for all servers
|
||||||
omitQuotationForMultipartRequest: true,
|
omitQuotationForMultipartRequest: true,
|
||||||
showLoadingIndicator: true,
|
showLoadingIndicator: true,
|
||||||
|
|||||||
28
yarn.lock
28
yarn.lock
@ -2383,28 +2383,28 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjpeg/-/codec-openjpeg-0.1.1.tgz#5bd1c52a33a425299299e970312731fa0cc2711b"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjpeg/-/codec-openjpeg-0.1.1.tgz#5bd1c52a33a425299299e970312731fa0cc2711b"
|
||||||
integrity sha512-HOMMOLV6xy8O/agNGGvrl0a8DwShpBvWxAzEzv2pqq12d3r5z/3MyIgNA3Oj/8bIBVvvVXxh9RX7rMDRHJdowg==
|
integrity sha512-HOMMOLV6xy8O/agNGGvrl0a8DwShpBvWxAzEzv2pqq12d3r5z/3MyIgNA3Oj/8bIBVvvVXxh9RX7rMDRHJdowg==
|
||||||
|
|
||||||
"@cornerstonejs/core@^0.21.0":
|
"@cornerstonejs/core@^0.21.4":
|
||||||
version "0.21.0"
|
version "0.21.4"
|
||||||
resolved "https://registry.npmjs.org/@cornerstonejs/core/-/core-0.21.0.tgz#12f58f7552a5664d8c9085df2a8c12558c89d70e"
|
resolved "https://registry.npmjs.org/@cornerstonejs/core/-/core-0.21.4.tgz#d1dab2c985c382495a8ec91a28f2d62872cdb350"
|
||||||
integrity sha512-p1aRTJxu1ZWV5uM5EctA870g37Okjpat4xDGfcqTupI2DgwVbCRNvJmuf44gRmBjVeHImDCybMMXXi9nWsCmLw==
|
integrity sha512-8D8QyK5bjqxefSVr3vLzsp2MUprmABAbX4qpvpp1XRmC76i1dKKR8Yo9G9jdreR5xwKvJN7pY6/n1F2ZYSN48w==
|
||||||
dependencies:
|
dependencies:
|
||||||
detect-gpu "^4.0.45"
|
detect-gpu "^4.0.45"
|
||||||
lodash.clonedeep "4.5.0"
|
lodash.clonedeep "4.5.0"
|
||||||
|
|
||||||
"@cornerstonejs/streaming-image-volume-loader@^0.6.1":
|
"@cornerstonejs/streaming-image-volume-loader@^0.6.5":
|
||||||
version "0.6.1"
|
version "0.6.5"
|
||||||
resolved "https://registry.npmjs.org/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.6.1.tgz#a8240c38607648dbc9983b7998e4de2d0ba916fa"
|
resolved "https://registry.npmjs.org/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.6.5.tgz#f3f2ab362bf494e4072a146b45acedce0197fb36"
|
||||||
integrity sha512-QBuT22dojEH5A0UKgbuYleMEBjbVTJH7bunL6ePcgxI0DY82Lq87qVOMtODfFcHUomBvUlBjP08U/6uP7VJYtA==
|
integrity sha512-dowoZYz0tMVtuUQl2WIZjOC/2TC0zMgas8KtpVcxJcGEu9HWA9oatgPlY0WqtFniDU7Oxnyy7OqYawtEoSSwdw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cornerstonejs/core" "^0.21.0"
|
"@cornerstonejs/core" "^0.21.4"
|
||||||
cornerstone-wado-image-loader "^4.2.1"
|
cornerstone-wado-image-loader "^4.2.1"
|
||||||
|
|
||||||
"@cornerstonejs/tools@^0.29.2":
|
"@cornerstonejs/tools@^0.29.6":
|
||||||
version "0.29.2"
|
version "0.29.6"
|
||||||
resolved "https://registry.npmjs.org/@cornerstonejs/tools/-/tools-0.29.2.tgz#6655a91ab82681bd585ad7469ec3a2d372b45521"
|
resolved "https://registry.npmjs.org/@cornerstonejs/tools/-/tools-0.29.6.tgz#6cfcb6b56284c4d1265330993ecafa3f48c429b8"
|
||||||
integrity sha512-sy1pK6YG+vWhJvESDNkiVDnLLpEwgou3LG1VAXIYXvA5JBUVG98LVXBWeAFY8TqdseSrnc/nwW+VQPk899wG5g==
|
integrity sha512-Rmz4Nx6IfH+31C7fsGxLWwQXQ+w1nN5WmILb9/N7WB4M5mYUZvYiopXdWII4mzh91nHXcEfEiJimaihyuCVLJQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cornerstonejs/core" "^0.21.0"
|
"@cornerstonejs/core" "^0.21.4"
|
||||||
lodash.clonedeep "4.5.0"
|
lodash.clonedeep "4.5.0"
|
||||||
lodash.get "^4.4.2"
|
lodash.get "^4.4.2"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user