feat(grid): remove viewportIndex and only rely on viewportId (#3591)
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
This commit is contained in:
parent
5dafac7c92
commit
4c6ff873e8
@ -121,7 +121,7 @@ const getSopClassHandlerModule = (/* ... */) => {
|
|||||||
|
|
||||||
const getToolbarModule = () => {};
|
const getToolbarModule = () => {};
|
||||||
|
|
||||||
// displaySet, viewportIndex, dataSource
|
// displaySet, dataSource
|
||||||
const getViewportModule = () => {
|
const getViewportModule = () => {
|
||||||
const wrappedViewport = props => {
|
const wrappedViewport = props => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
async function _hydrateRTDisplaySet({
|
async function _hydrateRTDisplaySet({
|
||||||
rtDisplaySet,
|
rtDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
@ -27,7 +27,7 @@ async function _hydrateRTDisplaySet({
|
|||||||
const { viewports } = viewportGridService.getState();
|
const { viewports } = viewportGridService.getState();
|
||||||
|
|
||||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ async function _hydrateRTDisplaySet({
|
|||||||
// is being displayed, if so we need to update the viewport to use volume viewport
|
// is being displayed, if so we need to update the viewport to use volume viewport
|
||||||
// (if already is not using it) since Cornerstone3D currently only supports
|
// (if already is not using it) since Cornerstone3D currently only supports
|
||||||
// volume viewport for segmentation
|
// volume viewport for segmentation
|
||||||
viewports.forEach((viewport, index) => {
|
viewports.forEach(viewport => {
|
||||||
if (index === viewportIndex) {
|
if (viewport.viewportId === viewportId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,9 +51,16 @@ async function _hydrateRTDisplaySet({
|
|||||||
|
|
||||||
if (shouldDisplaySeg) {
|
if (shouldDisplaySeg) {
|
||||||
updatedViewports.push({
|
updatedViewports.push({
|
||||||
viewportIndex: index,
|
viewportId: viewport.viewportId,
|
||||||
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
|
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
|
||||||
|
// Used for segmentation hydration right now, since the logic to decide whether
|
||||||
|
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
|
||||||
|
// so we need to re-render (force update via change of the needsRerendering) so that React
|
||||||
|
// does the diffing and decides we should render this again (although the id and element has not changed)
|
||||||
|
// so that the CornerstoneViewportService can decide whether to render the segmentation or not.
|
||||||
|
needsRerendering: true,
|
||||||
initialImageOptions: {
|
initialImageOptions: {
|
||||||
preset: 'middle',
|
preset: 'middle',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,17 +10,14 @@ const RESPONSE = {
|
|||||||
function promptHydrateRT({
|
function promptHydrateRT({
|
||||||
servicesManager,
|
servicesManager,
|
||||||
rtDisplaySet,
|
rtDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
toolGroupId = 'default',
|
toolGroupId = 'default',
|
||||||
preHydrateCallbacks,
|
preHydrateCallbacks,
|
||||||
}) {
|
}) {
|
||||||
const { uiViewportDialogService } = servicesManager.services;
|
const { uiViewportDialogService } = servicesManager.services;
|
||||||
|
|
||||||
return new Promise(async function(resolve, reject) {
|
return new Promise(async function(resolve, reject) {
|
||||||
const promptResult = await _askHydrate(
|
const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
|
||||||
uiViewportDialogService,
|
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (promptResult === RESPONSE.HYDRATE_SEG) {
|
if (promptResult === RESPONSE.HYDRATE_SEG) {
|
||||||
preHydrateCallbacks?.forEach(callback => {
|
preHydrateCallbacks?.forEach(callback => {
|
||||||
@ -29,7 +26,7 @@ function promptHydrateRT({
|
|||||||
|
|
||||||
const isHydrated = await hydrateRTDisplaySet({
|
const isHydrated = await hydrateRTDisplaySet({
|
||||||
rtDisplaySet,
|
rtDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
toolGroupId,
|
toolGroupId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
});
|
});
|
||||||
@ -39,7 +36,7 @@ function promptHydrateRT({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askHydrate(uiViewportDialogService, viewportIndex) {
|
function _askHydrate(uiViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message = 'Do you want to open this Segmentation?';
|
const message = 'Do you want to open this Segmentation?';
|
||||||
const actions = [
|
const actions = [
|
||||||
@ -60,7 +57,7 @@ function _askHydrate(uiViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uiViewportDialogService.show({
|
uiViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
|
|||||||
@ -21,7 +21,6 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
children,
|
children,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
viewportIndex,
|
|
||||||
viewportLabel,
|
viewportLabel,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
@ -36,7 +35,9 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
customizationService,
|
customizationService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
|
|
||||||
const toolGroupId = `${RT_TOOLGROUP_BASE_NAME}-${viewportIndex}`;
|
const viewportId = viewportOptions.viewportId;
|
||||||
|
|
||||||
|
const toolGroupId = `${RT_TOOLGROUP_BASE_NAME}-${viewportId}`;
|
||||||
|
|
||||||
// RT viewport will always have a single display set
|
// RT viewport will always have a single display set
|
||||||
if (displaySets.length > 1) {
|
if (displaySets.length > 1) {
|
||||||
@ -67,7 +68,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
// refs
|
// refs
|
||||||
const referencedDisplaySetRef = useRef(null);
|
const referencedDisplaySetRef = useRef(null);
|
||||||
|
|
||||||
const { viewports, activeViewportIndex } = viewportGrid;
|
const { viewports, activeViewportId } = viewportGrid;
|
||||||
|
|
||||||
const referencedDisplaySet = rtDisplaySet.getReferenceDisplaySet();
|
const referencedDisplaySet = rtDisplaySet.getReferenceDisplaySet();
|
||||||
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
|
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
|
||||||
@ -93,9 +94,9 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const storePresentationState = useCallback(() => {
|
const storePresentationState = useCallback(() => {
|
||||||
viewportGrid?.viewports.forEach(({ viewportIndex }) => {
|
viewportGrid?.viewports.forEach(({ viewportId }) => {
|
||||||
commandsManager.runCommand('storePresentation', {
|
commandsManager.runCommand('storePresentation', {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [viewportGrid]);
|
}, [viewportGrid]);
|
||||||
@ -124,7 +125,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
onElementDisabled={onElementDisabled}
|
onElementDisabled={onElementDisabled}
|
||||||
></Component>
|
></Component>
|
||||||
);
|
);
|
||||||
}, [viewportIndex, rtDisplaySet, toolGroupId]);
|
}, [viewportId, rtDisplaySet, toolGroupId]);
|
||||||
|
|
||||||
const onSegmentChange = useCallback(
|
const onSegmentChange = useCallback(
|
||||||
direction => {
|
direction => {
|
||||||
@ -162,7 +163,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
|
|
||||||
promptHydrateRT({
|
promptHydrateRT({
|
||||||
servicesManager,
|
servicesManager,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
rtDisplaySet,
|
rtDisplaySet,
|
||||||
preHydrateCallbacks: [storePresentationState],
|
preHydrateCallbacks: [storePresentationState],
|
||||||
}).then(isHydrated => {
|
}).then(isHydrated => {
|
||||||
@ -170,7 +171,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
setIsHydrated(true);
|
setIsHydrated(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [servicesManager, viewportIndex, rtDisplaySet, rtIsLoading]);
|
}, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { unsubscribe } = segmentationService.subscribe(
|
const { unsubscribe } = segmentationService.subscribe(
|
||||||
@ -222,12 +223,12 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
||||||
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
||||||
({ displaySetInstanceUIDs }) => {
|
({ displaySetInstanceUIDs }) => {
|
||||||
const activeViewport = viewports[activeViewportIndex];
|
const activeViewport = viewports.get(activeViewportId);
|
||||||
if (
|
if (
|
||||||
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
|
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
|
||||||
) {
|
) {
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
viewportIndex: activeViewportIndex,
|
viewportId: activeViewportId,
|
||||||
displaySetInstanceUIDs: [],
|
displaySetInstanceUIDs: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -292,7 +293,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
return (
|
return (
|
||||||
child &&
|
child &&
|
||||||
React.cloneElement(child, {
|
React.cloneElement(child, {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
key: index,
|
key: index,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -322,7 +323,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
storePresentationState();
|
storePresentationState();
|
||||||
const isHydrated = await _hydrateRTDisplaySet({
|
const isHydrated = await _hydrateRTDisplaySet({
|
||||||
rtDisplaySet,
|
rtDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -384,7 +385,7 @@ function OHIFCornerstoneRTViewport(props) {
|
|||||||
|
|
||||||
OHIFCornerstoneRTViewport.propTypes = {
|
OHIFCornerstoneRTViewport.propTypes = {
|
||||||
displaySets: PropTypes.arrayOf(PropTypes.object),
|
displaySets: PropTypes.arrayOf(PropTypes.object),
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
viewportId: PropTypes.string.isRequired,
|
||||||
dataSource: PropTypes.object,
|
dataSource: PropTypes.object,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
customProps: PropTypes.object,
|
customProps: PropTypes.object,
|
||||||
|
|||||||
@ -271,7 +271,6 @@ PanelSegmentation.propTypes = {
|
|||||||
commandsManager: PropTypes.shape({
|
commandsManager: PropTypes.shape({
|
||||||
runCommand: PropTypes.func.isRequired,
|
runCommand: PropTypes.func.isRequired,
|
||||||
}),
|
}),
|
||||||
appConfig: PropTypes.object.isRequired,
|
|
||||||
servicesManager: PropTypes.shape({
|
servicesManager: PropTypes.shape({
|
||||||
services: PropTypes.shape({
|
services: PropTypes.shape({
|
||||||
segmentationService: PropTypes.shape({
|
segmentationService: PropTypes.shape({
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
async function _hydrateSEGDisplaySet({
|
async function _hydrateSEGDisplaySet({
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
viewportIndex,
|
viewportId: targetViewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
@ -27,7 +27,7 @@ async function _hydrateSEGDisplaySet({
|
|||||||
const { viewports } = viewportGridService.getState();
|
const { viewports } = viewportGridService.getState();
|
||||||
|
|
||||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||||
viewportIndex,
|
targetViewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -37,8 +37,8 @@ async function _hydrateSEGDisplaySet({
|
|||||||
// is being displayed, if so we need to update the viewport to use volume viewport
|
// is being displayed, if so we need to update the viewport to use volume viewport
|
||||||
// (if already is not using it) since Cornerstone3D currently only supports
|
// (if already is not using it) since Cornerstone3D currently only supports
|
||||||
// volume viewport for segmentation
|
// volume viewport for segmentation
|
||||||
viewports.forEach((viewport, index) => {
|
viewports.forEach((viewport, viewportId) => {
|
||||||
if (index === viewportIndex) {
|
if (targetViewportId === viewportId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +49,16 @@ async function _hydrateSEGDisplaySet({
|
|||||||
|
|
||||||
if (shouldDisplaySeg) {
|
if (shouldDisplaySeg) {
|
||||||
updatedViewports.push({
|
updatedViewports.push({
|
||||||
viewportIndex: index,
|
viewportId,
|
||||||
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
|
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
|
||||||
|
// Used for segmentation hydration right now, since the logic to decide whether
|
||||||
|
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
|
||||||
|
// so we need to re-render (force update via change of the needsRerendering) so that React
|
||||||
|
// does the diffing and decides we should render this again (although the id and element has not changed)
|
||||||
|
// so that the CornerstoneViewportService can decide whether to render the segmentation or not.
|
||||||
|
needsRerendering: true,
|
||||||
initialImageOptions: {
|
initialImageOptions: {
|
||||||
preset: 'middle',
|
preset: 'middle',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,16 +10,13 @@ const RESPONSE = {
|
|||||||
function promptHydrateSEG({
|
function promptHydrateSEG({
|
||||||
servicesManager,
|
servicesManager,
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
preHydrateCallbacks,
|
preHydrateCallbacks,
|
||||||
}) {
|
}) {
|
||||||
const { uiViewportDialogService } = servicesManager.services;
|
const { uiViewportDialogService } = servicesManager.services;
|
||||||
|
|
||||||
return new Promise(async function(resolve, reject) {
|
return new Promise(async function(resolve, reject) {
|
||||||
const promptResult = await _askHydrate(
|
const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
|
||||||
uiViewportDialogService,
|
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (promptResult === RESPONSE.HYDRATE_SEG) {
|
if (promptResult === RESPONSE.HYDRATE_SEG) {
|
||||||
preHydrateCallbacks?.forEach(callback => {
|
preHydrateCallbacks?.forEach(callback => {
|
||||||
@ -28,7 +25,7 @@ function promptHydrateSEG({
|
|||||||
|
|
||||||
const isHydrated = await hydrateSEGDisplaySet({
|
const isHydrated = await hydrateSEGDisplaySet({
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -37,7 +34,7 @@ function promptHydrateSEG({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askHydrate(uiViewportDialogService, viewportIndex) {
|
function _askHydrate(uiViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message = 'Do you want to open this Segmentation?';
|
const message = 'Do you want to open this Segmentation?';
|
||||||
const actions = [
|
const actions = [
|
||||||
@ -58,7 +55,7 @@ function _askHydrate(uiViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uiViewportDialogService.show({
|
uiViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
|
|||||||
@ -20,7 +20,6 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
children,
|
children,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
viewportIndex,
|
|
||||||
viewportLabel,
|
viewportLabel,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
@ -28,6 +27,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { t } = useTranslation('SEGViewport');
|
const { t } = useTranslation('SEGViewport');
|
||||||
|
const viewportId = viewportOptions.viewportId;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
displaySetService,
|
displaySetService,
|
||||||
@ -37,7 +37,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
customizationService,
|
customizationService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
|
|
||||||
const toolGroupId = `${SEG_TOOLGROUP_BASE_NAME}-${viewportIndex}`;
|
const toolGroupId = `${SEG_TOOLGROUP_BASE_NAME}-${viewportId}`;
|
||||||
|
|
||||||
// SEG viewport will always have a single display set
|
// SEG viewport will always have a single display set
|
||||||
if (displaySets.length > 1) {
|
if (displaySets.length > 1) {
|
||||||
@ -67,7 +67,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
// refs
|
// refs
|
||||||
const referencedDisplaySetRef = useRef(null);
|
const referencedDisplaySetRef = useRef(null);
|
||||||
|
|
||||||
const { viewports, activeViewportIndex } = viewportGrid;
|
const { viewports, activeViewportId } = viewportGrid;
|
||||||
|
|
||||||
const referencedDisplaySet = segDisplaySet.getReferenceDisplaySet();
|
const referencedDisplaySet = segDisplaySet.getReferenceDisplaySet();
|
||||||
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
|
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
|
||||||
@ -94,9 +94,9 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const storePresentationState = useCallback(() => {
|
const storePresentationState = useCallback(() => {
|
||||||
viewportGrid?.viewports.forEach(({ viewportIndex }) => {
|
viewportGrid?.viewports.forEach(({ viewportId }) => {
|
||||||
commandsManager.runCommand('storePresentation', {
|
commandsManager.runCommand('storePresentation', {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [viewportGrid]);
|
}, [viewportGrid]);
|
||||||
@ -126,7 +126,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
// initialImageIndex={initialImageIndex}
|
// initialImageIndex={initialImageIndex}
|
||||||
></Component>
|
></Component>
|
||||||
);
|
);
|
||||||
}, [viewportIndex, segDisplaySet, toolGroupId]);
|
}, [viewportId, segDisplaySet, toolGroupId]);
|
||||||
|
|
||||||
const onSegmentChange = useCallback(
|
const onSegmentChange = useCallback(
|
||||||
direction => {
|
direction => {
|
||||||
@ -165,7 +165,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
|
|
||||||
promptHydrateSEG({
|
promptHydrateSEG({
|
||||||
servicesManager,
|
servicesManager,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
preHydrateCallbacks: [storePresentationState],
|
preHydrateCallbacks: [storePresentationState],
|
||||||
}).then(isHydrated => {
|
}).then(isHydrated => {
|
||||||
@ -173,7 +173,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
setIsHydrated(true);
|
setIsHydrated(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [servicesManager, viewportIndex, segDisplaySet, segIsLoading]);
|
}, [servicesManager, viewportId, segDisplaySet, segIsLoading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { unsubscribe } = segmentationService.subscribe(
|
const { unsubscribe } = segmentationService.subscribe(
|
||||||
@ -225,12 +225,12 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
||||||
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
||||||
({ displaySetInstanceUIDs }) => {
|
({ displaySetInstanceUIDs }) => {
|
||||||
const activeViewport = viewports[activeViewportIndex];
|
const activeViewport = viewports.get(activeViewportId);
|
||||||
if (
|
if (
|
||||||
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
|
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
|
||||||
) {
|
) {
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
viewportIndex: activeViewportIndex,
|
viewportId: activeViewportId,
|
||||||
displaySetInstanceUIDs: [],
|
displaySetInstanceUIDs: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -296,7 +296,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
return (
|
return (
|
||||||
child &&
|
child &&
|
||||||
React.cloneElement(child, {
|
React.cloneElement(child, {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
key: index,
|
key: index,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -325,7 +325,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
storePresentationState();
|
storePresentationState();
|
||||||
const isHydrated = await hydrateSEGDisplaySet({
|
const isHydrated = await hydrateSEGDisplaySet({
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
|
|
||||||
OHIFCornerstoneSEGViewport.propTypes = {
|
OHIFCornerstoneSEGViewport.propTypes = {
|
||||||
displaySets: PropTypes.arrayOf(PropTypes.object),
|
displaySets: PropTypes.arrayOf(PropTypes.object),
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
viewportId: PropTypes.string.isRequired,
|
||||||
dataSource: PropTypes.object,
|
dataSource: PropTypes.object,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
customProps: PropTypes.object,
|
customProps: PropTypes.object,
|
||||||
|
|||||||
@ -21,7 +21,6 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
children,
|
children,
|
||||||
dataSource,
|
dataSource,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportIndex,
|
|
||||||
viewportLabel,
|
viewportLabel,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
@ -36,6 +35,8 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
measurementService,
|
measurementService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
|
|
||||||
|
const viewportId = viewportOptions.viewportId;
|
||||||
|
|
||||||
// SR viewport will always have a single display set
|
// SR viewport will always have a single display set
|
||||||
if (displaySets.length > 1) {
|
if (displaySets.length > 1) {
|
||||||
throw new Error('SR viewport should only have a single display set');
|
throw new Error('SR viewport should only have a single display set');
|
||||||
@ -54,7 +55,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
setReferencedDisplaySetMetadata,
|
setReferencedDisplaySetMetadata,
|
||||||
] = useState(null);
|
] = useState(null);
|
||||||
const [element, setElement] = useState(null);
|
const [element, setElement] = useState(null);
|
||||||
const { viewports, activeViewportIndex } = viewportGrid;
|
const { viewports, activeViewportId } = viewportGrid;
|
||||||
|
|
||||||
// Optional hook into tracking extension, if present.
|
// Optional hook into tracking extension, if present.
|
||||||
let trackedMeasurements;
|
let trackedMeasurements;
|
||||||
@ -88,7 +89,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
if (displaySets.length) {
|
if (displaySets.length) {
|
||||||
viewportGridService.setDisplaySetsForViewports([
|
viewportGridService.setDisplaySetsForViewports([
|
||||||
{
|
{
|
||||||
viewportIndex: activeViewportIndex,
|
viewportId: activeViewportId,
|
||||||
displaySetInstanceUIDs: [displaySets[0].displaySetInstanceUID],
|
displaySetInstanceUIDs: [displaySets[0].displaySetInstanceUID],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@ -163,12 +164,8 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
// imageIdIndex will handle it by updating the viewport, but if they
|
// imageIdIndex will handle it by updating the viewport, but if they
|
||||||
// are the same we just need to use measurementService to jump to the
|
// are the same we just need to use measurementService to jump to the
|
||||||
// new measurement
|
// new measurement
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
const csViewport = cornerstoneViewportService.getCornerstoneViewport(
|
const csViewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportInfo.getViewportId()
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
const imageIds = csViewport.getImageIds();
|
const imageIds = csViewport.getImageIds();
|
||||||
@ -183,7 +180,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[dataSource, srDisplaySet, activeImageDisplaySetData, viewportIndex]
|
[dataSource, srDisplaySet, activeImageDisplaySetData, viewportId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getCornerstoneViewport = useCallback(() => {
|
const getCornerstoneViewport = useCallback(() => {
|
||||||
@ -231,7 +228,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
isJumpToMeasurementDisabled={true}
|
isJumpToMeasurementDisabled={true}
|
||||||
></Component>
|
></Component>
|
||||||
);
|
);
|
||||||
}, [activeImageDisplaySetData, viewportIndex, measurementSelected]);
|
}, [activeImageDisplaySetData, viewportId, measurementSelected]);
|
||||||
|
|
||||||
const onMeasurementChange = useCallback(
|
const onMeasurementChange = useCallback(
|
||||||
direction => {
|
direction => {
|
||||||
@ -269,12 +266,12 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
||||||
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
||||||
({ displaySetInstanceUIDs }) => {
|
({ displaySetInstanceUIDs }) => {
|
||||||
const activeViewport = viewports[activeViewportIndex];
|
const activeViewport = viewports[activeViewportId];
|
||||||
if (
|
if (
|
||||||
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
|
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
|
||||||
) {
|
) {
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
viewportIndex: activeViewportIndex,
|
viewportId: activeViewportId,
|
||||||
displaySetInstanceUIDs: [],
|
displaySetInstanceUIDs: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -341,7 +338,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
return (
|
return (
|
||||||
child &&
|
child &&
|
||||||
React.cloneElement(child, {
|
React.cloneElement(child, {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
key: index,
|
key: index,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -373,7 +370,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
getStatusComponent={() =>
|
getStatusComponent={() =>
|
||||||
_getStatusComponent({
|
_getStatusComponent({
|
||||||
srDisplaySet,
|
srDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
isTracked: false,
|
isTracked: false,
|
||||||
isRehydratable: srDisplaySet.isRehydratable,
|
isRehydratable: srDisplaySet.isRehydratable,
|
||||||
isLocked,
|
isLocked,
|
||||||
@ -413,7 +410,7 @@ function OHIFCornerstoneSRViewport(props) {
|
|||||||
|
|
||||||
OHIFCornerstoneSRViewport.propTypes = {
|
OHIFCornerstoneSRViewport.propTypes = {
|
||||||
displaySets: PropTypes.arrayOf(PropTypes.object),
|
displaySets: PropTypes.arrayOf(PropTypes.object),
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
viewportId: PropTypes.string.isRequired,
|
||||||
dataSource: PropTypes.object,
|
dataSource: PropTypes.object,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
viewportLabel: PropTypes.string,
|
viewportLabel: PropTypes.string,
|
||||||
@ -462,7 +459,7 @@ async function _getViewportReferencedDisplaySetData(
|
|||||||
|
|
||||||
function _getStatusComponent({
|
function _getStatusComponent({
|
||||||
srDisplaySet,
|
srDisplaySet,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
isRehydratable,
|
isRehydratable,
|
||||||
isLocked,
|
isLocked,
|
||||||
sendTrackedMeasurementsEvent,
|
sendTrackedMeasurementsEvent,
|
||||||
@ -470,7 +467,7 @@ function _getStatusComponent({
|
|||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
sendTrackedMeasurementsEvent('HYDRATE_SR', {
|
sendTrackedMeasurementsEvent('HYDRATE_SR', {
|
||||||
displaySetInstanceUID: srDisplaySet.displaySetInstanceUID,
|
displaySetInstanceUID: srDisplaySet.displaySetInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -63,6 +63,10 @@ function areEqual(prevProps, nextProps) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nextProps.viewportOptions.needsRerendering) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const prevDisplaySets = prevProps.displaySets;
|
const prevDisplaySets = prevProps.displaySets;
|
||||||
const nextDisplaySets = nextProps.displaySets;
|
const nextDisplaySets = nextProps.displaySets;
|
||||||
|
|
||||||
@ -107,7 +111,6 @@ function areEqual(prevProps, nextProps) {
|
|||||||
// Then we don't need to worry about the re-renders if the props change.
|
// Then we don't need to worry about the re-renders if the props change.
|
||||||
const OHIFCornerstoneViewport = React.memo(props => {
|
const OHIFCornerstoneViewport = React.memo(props => {
|
||||||
const {
|
const {
|
||||||
viewportIndex,
|
|
||||||
displaySets,
|
displaySets,
|
||||||
dataSource,
|
dataSource,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
@ -123,6 +126,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
initialImageIndex,
|
initialImageIndex,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const viewportId = viewportOptions.viewportId;
|
||||||
const [scrollbarHeight, setScrollbarHeight] = useState('100px');
|
const [scrollbarHeight, setScrollbarHeight] = useState('100px');
|
||||||
const [enabledVPElement, setEnabledVPElement] = useState(null);
|
const [enabledVPElement, setEnabledVPElement] = useState(null);
|
||||||
const elementRef = useRef();
|
const elementRef = useRef();
|
||||||
@ -154,27 +158,24 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
}
|
}
|
||||||
}, [elementRef]);
|
}, [elementRef]);
|
||||||
|
|
||||||
const cleanUpServices = useCallback(() => {
|
const cleanUpServices = useCallback(
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
viewportInfo => {
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!viewportInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
|
||||||
const renderingEngineId = viewportInfo.getRenderingEngineId();
|
const renderingEngineId = viewportInfo.getRenderingEngineId();
|
||||||
const syncGroups = viewportInfo.getSyncGroups();
|
const syncGroups = viewportInfo.getSyncGroups();
|
||||||
|
|
||||||
toolGroupService.removeViewportFromToolGroup(viewportId, renderingEngineId);
|
toolGroupService.removeViewportFromToolGroup(
|
||||||
|
viewportId,
|
||||||
|
renderingEngineId
|
||||||
|
);
|
||||||
|
|
||||||
syncGroupService.removeViewportFromSyncGroup(
|
syncGroupService.removeViewportFromSyncGroup(
|
||||||
viewportId,
|
viewportId,
|
||||||
renderingEngineId,
|
renderingEngineId,
|
||||||
syncGroups
|
syncGroups
|
||||||
);
|
);
|
||||||
}, [viewportIndex, viewportOptions.viewportId]);
|
},
|
||||||
|
[viewportId]
|
||||||
|
);
|
||||||
|
|
||||||
const elementEnabledHandler = useCallback(
|
const elementEnabledHandler = useCallback(
|
||||||
evt => {
|
evt => {
|
||||||
@ -187,9 +188,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
||||||
viewportId
|
viewportId
|
||||||
);
|
);
|
||||||
const viewportIndex = viewportInfo.getViewportIndex();
|
setEnabledElement(viewportId, element);
|
||||||
|
|
||||||
setEnabledElement(viewportIndex, element);
|
|
||||||
setEnabledVPElement(element);
|
setEnabledVPElement(element);
|
||||||
|
|
||||||
const renderingEngineId = viewportInfo.getRenderingEngineId();
|
const renderingEngineId = viewportInfo.getRenderingEngineId();
|
||||||
@ -212,16 +211,12 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
onElementEnabled(evt);
|
onElementEnabled(evt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[viewportIndex, onElementEnabled, toolGroupService]
|
[viewportId, onElementEnabled, toolGroupService]
|
||||||
);
|
);
|
||||||
|
|
||||||
// disable the element upon unmounting
|
// disable the element upon unmounting
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
cornerstoneViewportService.enableViewport(
|
cornerstoneViewportService.enableViewport(viewportId, elementRef.current);
|
||||||
viewportIndex,
|
|
||||||
viewportOptions,
|
|
||||||
elementRef.current
|
|
||||||
);
|
|
||||||
|
|
||||||
eventTarget.addEventListener(
|
eventTarget.addEventListener(
|
||||||
Enums.Events.ELEMENT_ENABLED,
|
Enums.Events.ELEMENT_ENABLED,
|
||||||
@ -231,17 +226,16 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
setImageScrollBarHeight();
|
setImageScrollBarHeight();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
commandsManager.runCommand('storePresentation', {
|
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
||||||
viewportIndex,
|
viewportId
|
||||||
});
|
|
||||||
|
|
||||||
cleanUpServices();
|
|
||||||
|
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
|
||||||
viewportIndex
|
|
||||||
);
|
);
|
||||||
|
|
||||||
cornerstoneViewportService.disableElement(viewportIndex);
|
if (!viewportInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpServices(viewportInfo);
|
||||||
|
cornerstoneViewportService.storePresentation({ viewportId });
|
||||||
|
|
||||||
if (onElementDisabled) {
|
if (onElementDisabled) {
|
||||||
onElementDisabled(viewportInfo);
|
onElementDisabled(viewportInfo);
|
||||||
@ -273,8 +267,8 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (viewportInfo.hasDisplaySet(invalidatedDisplaySetInstanceUID)) {
|
if (viewportInfo.hasDisplaySet(invalidatedDisplaySetInstanceUID)) {
|
||||||
@ -288,7 +282,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
|
|
||||||
const keepCamera = true;
|
const keepCamera = true;
|
||||||
cornerstoneViewportService.updateViewport(
|
cornerstoneViewportService.updateViewport(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
newViewportData,
|
newViewportData,
|
||||||
keepCamera
|
keepCamera
|
||||||
);
|
);
|
||||||
@ -298,7 +292,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [viewportIndex]);
|
}, [viewportId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// handle the default viewportType to be stack
|
// handle the default viewportType to be stack
|
||||||
@ -329,15 +323,25 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
lutPresentationStore[presentationIds?.lutPresentationId],
|
lutPresentationStore[presentationIds?.lutPresentationId],
|
||||||
};
|
};
|
||||||
let measurement;
|
let measurement;
|
||||||
if (cacheJumpToMeasurementEvent?.viewportIndex === viewportIndex) {
|
if (cacheJumpToMeasurementEvent?.viewportId === viewportId) {
|
||||||
measurement = cacheJumpToMeasurementEvent.measurement;
|
measurement = cacheJumpToMeasurementEvent.measurement;
|
||||||
// Delete the position presentation so that viewport navigates direct
|
// Delete the position presentation so that viewport navigates direct
|
||||||
presentations.positionPresentation = null;
|
presentations.positionPresentation = null;
|
||||||
cacheJumpToMeasurementEvent = null;
|
cacheJumpToMeasurementEvent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
|
||||||
|
// Used for segmentation hydration right now, since the logic to decide whether
|
||||||
|
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
|
||||||
|
// so we need to re-render (force update via change of the needsRerendering) so that React
|
||||||
|
// does the diffing and decides we should render this again (although the id and element has not changed)
|
||||||
|
// so that the CornerstoneViewportService can decide whether to render the segmentation or not. Not that we reached here we can turn it off.
|
||||||
|
if (viewportOptions.needsRerendering) {
|
||||||
|
viewportOptions.needsRerendering = false;
|
||||||
|
}
|
||||||
|
|
||||||
cornerstoneViewportService.setViewportData(
|
cornerstoneViewportService.setViewportData(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
viewportData,
|
viewportData,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
displaySetOptions,
|
displaySetOptions,
|
||||||
@ -370,7 +374,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
@ -380,7 +384,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
@ -389,7 +393,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribeFromJumpToMeasurementEvents();
|
unsubscribeFromJumpToMeasurementEvents();
|
||||||
};
|
};
|
||||||
}, [displaySets, elementRef, viewportIndex]);
|
}, [displaySets, elementRef, viewportId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@ -408,7 +412,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
ref={elementRef}
|
ref={elementRef}
|
||||||
></div>
|
></div>
|
||||||
<CornerstoneOverlays
|
<CornerstoneOverlays
|
||||||
viewportIndex={viewportIndex}
|
viewportId={viewportId}
|
||||||
toolBarService={toolbarService}
|
toolBarService={toolbarService}
|
||||||
element={elementRef.current}
|
element={elementRef.current}
|
||||||
scrollbarHeight={scrollbarHeight}
|
scrollbarHeight={scrollbarHeight}
|
||||||
@ -416,12 +420,12 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
|||||||
/>
|
/>
|
||||||
<CinePlayer
|
<CinePlayer
|
||||||
enabledVPElement={enabledVPElement}
|
enabledVPElement={enabledVPElement}
|
||||||
viewportIndex={viewportIndex}
|
viewportId={viewportId}
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute w-full">
|
<div className="absolute w-full">
|
||||||
{viewportDialogState.viewportIndex === viewportIndex && (
|
{viewportDialogState.viewportId === viewportId && (
|
||||||
<Notification
|
<Notification
|
||||||
id="viewport-notification"
|
id="viewport-notification"
|
||||||
message={viewportDialogState.message}
|
message={viewportDialogState.message}
|
||||||
@ -440,37 +444,34 @@ function _subscribeToJumpToMeasurementEvents(
|
|||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
) {
|
) {
|
||||||
const displaysUIDs = displaySets.map(
|
|
||||||
displaySet => displaySet.displaySetInstanceUID
|
|
||||||
);
|
|
||||||
const { unsubscribe } = measurementService.subscribe(
|
const { unsubscribe } = measurementService.subscribe(
|
||||||
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT,
|
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT,
|
||||||
props => {
|
props => {
|
||||||
cacheJumpToMeasurementEvent = props;
|
cacheJumpToMeasurementEvent = props;
|
||||||
const { viewportIndex: jumpIndex, measurement, isConsumed } = props;
|
const { viewportId: jumpId, measurement, isConsumed } = props;
|
||||||
if (!measurement || isConsumed) {
|
if (!measurement || isConsumed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cacheJumpToMeasurementEvent.cornerstoneViewport === undefined) {
|
if (cacheJumpToMeasurementEvent.cornerstoneViewport === undefined) {
|
||||||
// Decide on which viewport should handle this
|
// Decide on which viewport should handle this
|
||||||
cacheJumpToMeasurementEvent.cornerstoneViewport = cornerstoneViewportService.getViewportIndexToJump(
|
cacheJumpToMeasurementEvent.cornerstoneViewport = cornerstoneViewportService.getViewportIdToJump(
|
||||||
jumpIndex,
|
jumpId,
|
||||||
measurement.displaySetInstanceUID,
|
measurement.displaySetInstanceUID,
|
||||||
{ referencedImageId: measurement.referencedImageId }
|
{ referencedImageId: measurement.referencedImageId }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportIndex) {
|
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_jumpToMeasurement(
|
_jumpToMeasurement(
|
||||||
measurement,
|
measurement,
|
||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
@ -487,7 +488,7 @@ function _checkForCachedJumpToMeasurementEvents(
|
|||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
@ -513,7 +514,7 @@ function _checkForCachedJumpToMeasurementEvents(
|
|||||||
_jumpToMeasurement(
|
_jumpToMeasurement(
|
||||||
measurement,
|
measurement,
|
||||||
elementRef,
|
elementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
@ -526,7 +527,7 @@ function _checkForCachedJumpToMeasurementEvents(
|
|||||||
function _jumpToMeasurement(
|
function _jumpToMeasurement(
|
||||||
measurement,
|
measurement,
|
||||||
targetElementRef,
|
targetElementRef,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
@ -548,13 +549,10 @@ function _jumpToMeasurement(
|
|||||||
// to set it properly
|
// to set it properly
|
||||||
// setCornerstoneMeasurementActive(measurement);
|
// setCornerstoneMeasurementActive(measurement);
|
||||||
|
|
||||||
viewportGridService.setActiveViewportIndex(viewportIndex);
|
viewportGridService.setActiveViewportId(viewportId);
|
||||||
|
|
||||||
const enabledElement = getEnabledElement(targetElement);
|
const enabledElement = getEnabledElement(targetElement);
|
||||||
|
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
if (enabledElement) {
|
if (enabledElement) {
|
||||||
// See how the jumpToSlice() of Cornerstone3D deals with imageIdx param.
|
// See how the jumpToSlice() of Cornerstone3D deals with imageIdx param.
|
||||||
const viewport = enabledElement.viewport as
|
const viewport = enabledElement.viewport as
|
||||||
@ -621,7 +619,6 @@ OHIFCornerstoneViewport.defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
OHIFCornerstoneViewport.propTypes = {
|
OHIFCornerstoneViewport.propTypes = {
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
|
||||||
displaySets: PropTypes.array.isRequired,
|
displaySets: PropTypes.array.isRequired,
|
||||||
dataSource: PropTypes.object.isRequired,
|
dataSource: PropTypes.object.isRequired,
|
||||||
viewportOptions: PropTypes.object,
|
viewportOptions: PropTypes.object,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import ViewportOrientationMarkers from './ViewportOrientationMarkers';
|
|||||||
import ViewportImageSliceLoadingIndicator from './ViewportImageSliceLoadingIndicator';
|
import ViewportImageSliceLoadingIndicator from './ViewportImageSliceLoadingIndicator';
|
||||||
|
|
||||||
function CornerstoneOverlays(props) {
|
function CornerstoneOverlays(props) {
|
||||||
const { viewportIndex, element, scrollbarHeight, servicesManager } = props;
|
const { viewportId, element, scrollbarHeight, servicesManager } = props;
|
||||||
const { cornerstoneViewportService } = servicesManager.services;
|
const { cornerstoneViewportService } = servicesManager.services;
|
||||||
const [imageSliceData, setImageSliceData] = useState({
|
const [imageSliceData, setImageSliceData] = useState({
|
||||||
imageIndex: 0,
|
imageIndex: 0,
|
||||||
@ -18,7 +18,7 @@ function CornerstoneOverlays(props) {
|
|||||||
const { unsubscribe } = cornerstoneViewportService.subscribe(
|
const { unsubscribe } = cornerstoneViewportService.subscribe(
|
||||||
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
||||||
props => {
|
props => {
|
||||||
if (props.viewportIndex !== viewportIndex) {
|
if (props.viewportId !== viewportId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,16 +29,14 @@ function CornerstoneOverlays(props) {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [viewportIndex]);
|
}, [viewportId]);
|
||||||
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewportData) {
|
if (viewportData) {
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (viewportInfo?.viewportOptions?.customViewportProps?.hideOverlays) {
|
if (viewportInfo?.viewportOptions?.customViewportProps?.hideOverlays) {
|
||||||
return null;
|
return null;
|
||||||
@ -48,7 +46,7 @@ function CornerstoneOverlays(props) {
|
|||||||
return (
|
return (
|
||||||
<div className="noselect">
|
<div className="noselect">
|
||||||
<ViewportImageScrollbar
|
<ViewportImageScrollbar
|
||||||
viewportIndex={viewportIndex}
|
viewportId={viewportId}
|
||||||
viewportData={viewportData}
|
viewportData={viewportData}
|
||||||
element={element}
|
element={element}
|
||||||
imageSliceData={imageSliceData}
|
imageSliceData={imageSliceData}
|
||||||
@ -60,7 +58,7 @@ function CornerstoneOverlays(props) {
|
|||||||
<CustomizableViewportOverlay
|
<CustomizableViewportOverlay
|
||||||
imageSliceData={imageSliceData}
|
imageSliceData={imageSliceData}
|
||||||
viewportData={viewportData}
|
viewportData={viewportData}
|
||||||
viewportIndex={viewportIndex}
|
viewportId={viewportId}
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
element={element}
|
element={element}
|
||||||
/>
|
/>
|
||||||
@ -75,7 +73,7 @@ function CornerstoneOverlays(props) {
|
|||||||
element={element}
|
element={element}
|
||||||
viewportData={viewportData}
|
viewportData={viewportData}
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
viewportIndex={viewportIndex}
|
viewportId={viewportId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -21,7 +21,6 @@ interface OverlayItemProps {
|
|||||||
element: any;
|
element: any;
|
||||||
viewportData: any;
|
viewportData: any;
|
||||||
imageSliceData: ImageSliceData;
|
imageSliceData: ImageSliceData;
|
||||||
viewportIndex: number | null;
|
|
||||||
servicesManager: ServicesManager;
|
servicesManager: ServicesManager;
|
||||||
instance: InstanceMetadata;
|
instance: InstanceMetadata;
|
||||||
customization: any;
|
customization: any;
|
||||||
@ -114,7 +113,7 @@ function CustomizableViewportOverlay({
|
|||||||
element,
|
element,
|
||||||
viewportData,
|
viewportData,
|
||||||
imageSliceData,
|
imageSliceData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
@ -152,13 +151,13 @@ function CustomizableViewportOverlay({
|
|||||||
if (viewportData != null) {
|
if (viewportData != null) {
|
||||||
return _getInstanceNumber(
|
return _getInstanceNumber(
|
||||||
viewportData,
|
viewportData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
imageIndex,
|
imageIndex,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [viewportData, viewportIndex, imageIndex, cornerstoneViewportService]);
|
}, [viewportData, viewportId, imageIndex, cornerstoneViewportService]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial toolbar state
|
* Initial toolbar state
|
||||||
@ -192,7 +191,7 @@ function CustomizableViewportOverlay({
|
|||||||
return () => {
|
return () => {
|
||||||
element.removeEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
|
element.removeEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
|
||||||
};
|
};
|
||||||
}, [viewportIndex, viewportData, voi, element]);
|
}, [viewportId, viewportData, voi, element]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updating the scale when the viewport changes its zoom
|
* Updating the scale when the viewport changes its zoom
|
||||||
@ -205,8 +204,8 @@ function CustomizableViewportOverlay({
|
|||||||
previousCamera.parallelScale !== camera.parallelScale ||
|
previousCamera.parallelScale !== camera.parallelScale ||
|
||||||
previousCamera.scale !== camera.scale
|
previousCamera.scale !== camera.scale
|
||||||
) {
|
) {
|
||||||
const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!viewport) {
|
if (!viewport) {
|
||||||
@ -237,7 +236,7 @@ function CustomizableViewportOverlay({
|
|||||||
return () => {
|
return () => {
|
||||||
element.removeEventListener(Enums.Events.CAMERA_MODIFIED, updateScale);
|
element.removeEventListener(Enums.Events.CAMERA_MODIFIED, updateScale);
|
||||||
};
|
};
|
||||||
}, [viewportIndex, viewportData, cornerstoneViewportService, element]);
|
}, [viewportId, viewportData, cornerstoneViewportService, element]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updating the active tools when the toolbar changes
|
* Updating the active tools when the toolbar changes
|
||||||
@ -262,7 +261,7 @@ function CustomizableViewportOverlay({
|
|||||||
element,
|
element,
|
||||||
viewportData,
|
viewportData,
|
||||||
imageSliceData,
|
imageSliceData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
customization: item,
|
customization: item,
|
||||||
formatters: {
|
formatters: {
|
||||||
@ -296,7 +295,7 @@ function CustomizableViewportOverlay({
|
|||||||
element,
|
element,
|
||||||
viewportData,
|
viewportData,
|
||||||
imageSliceData,
|
imageSliceData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
customizationService,
|
customizationService,
|
||||||
instance,
|
instance,
|
||||||
@ -390,7 +389,7 @@ function _getViewportInstance(viewportData, imageIndex) {
|
|||||||
|
|
||||||
function _getInstanceNumber(
|
function _getInstanceNumber(
|
||||||
viewportData,
|
viewportData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
imageIndex,
|
imageIndex,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
) {
|
) {
|
||||||
@ -406,7 +405,7 @@ function _getInstanceNumber(
|
|||||||
instanceNumber = _getInstanceNumberFromVolume(
|
instanceNumber = _getInstanceNumberFromVolume(
|
||||||
viewportData,
|
viewportData,
|
||||||
imageIndex,
|
imageIndex,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -438,8 +437,7 @@ function _getInstanceNumberFromStack(viewportData, imageIndex) {
|
|||||||
// Here we check if viewport is in the acquisition direction and if so, we get the instanceNumber
|
// Here we check if viewport is in the acquisition direction and if so, we get the instanceNumber
|
||||||
function _getInstanceNumberFromVolume(
|
function _getInstanceNumberFromVolume(
|
||||||
viewportData,
|
viewportData,
|
||||||
imageIndex,
|
viewportId,
|
||||||
viewportIndex,
|
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
) {
|
) {
|
||||||
const volumes = viewportData.volumes;
|
const volumes = viewportData.volumes;
|
||||||
@ -452,8 +450,8 @@ function _getInstanceNumberFromVolume(
|
|||||||
const volume = volumes[0];
|
const volume = volumes[0];
|
||||||
const { direction, imageIds } = volume;
|
const { direction, imageIds } = volume;
|
||||||
|
|
||||||
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!cornerstoneViewport) {
|
if (!cornerstoneViewport) {
|
||||||
@ -486,7 +484,7 @@ function _getInstanceNumberFromVolume(
|
|||||||
CustomizableViewportOverlay.propTypes = {
|
CustomizableViewportOverlay.propTypes = {
|
||||||
viewportData: PropTypes.object,
|
viewportData: PropTypes.object,
|
||||||
imageIndex: PropTypes.number,
|
imageIndex: PropTypes.number,
|
||||||
viewportIndex: PropTypes.number,
|
viewportId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomizableViewportOverlay;
|
export default CustomizableViewportOverlay;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { ServicesManger } from '@ohif/core';
|
|||||||
|
|
||||||
function CornerstoneImageScrollbar({
|
function CornerstoneImageScrollbar({
|
||||||
viewportData,
|
viewportData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
element,
|
element,
|
||||||
imageSliceData,
|
imageSliceData,
|
||||||
setImageSliceData,
|
setImageSliceData,
|
||||||
@ -19,12 +19,7 @@ function CornerstoneImageScrollbar({
|
|||||||
cornerstoneViewportService,
|
cornerstoneViewportService,
|
||||||
} = (servicesManager as ServicesManger).services;
|
} = (servicesManager as ServicesManger).services;
|
||||||
|
|
||||||
const onImageScrollbarChange = (imageIndex, viewportIndex) => {
|
const onImageScrollbarChange = (imageIndex, viewportId) => {
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
|
||||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportId
|
viewportId
|
||||||
);
|
);
|
||||||
@ -34,7 +29,7 @@ function CornerstoneImageScrollbar({
|
|||||||
if (isCineEnabled) {
|
if (isCineEnabled) {
|
||||||
// on image scrollbar change, stop the CINE if it is playing
|
// on image scrollbar change, stop the CINE if it is playing
|
||||||
cineService.stopClip(element);
|
cineService.stopClip(element);
|
||||||
cineService.setCine({ id: viewportIndex, isPlaying: false });
|
cineService.setCine({ id: viewportId, isPlaying: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
csToolsUtils.jumpToSlice(viewport.element, {
|
csToolsUtils.jumpToSlice(viewport.element, {
|
||||||
@ -48,8 +43,8 @@ function CornerstoneImageScrollbar({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!viewport) {
|
if (!viewport) {
|
||||||
@ -79,7 +74,7 @@ function CornerstoneImageScrollbar({
|
|||||||
const { imageIndex, numberOfSlices } = sliceData;
|
const { imageIndex, numberOfSlices } = sliceData;
|
||||||
setImageSliceData({ imageIndex, numberOfSlices });
|
setImageSliceData({ imageIndex, numberOfSlices });
|
||||||
}
|
}
|
||||||
}, [viewportIndex, viewportData]);
|
}, [viewportId, viewportData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (viewportData?.viewportType !== Enums.ViewportType.STACK) {
|
if (viewportData?.viewportType !== Enums.ViewportType.STACK) {
|
||||||
@ -131,7 +126,7 @@ function CornerstoneImageScrollbar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ImageScrollbar
|
<ImageScrollbar
|
||||||
onChange={evt => onImageScrollbarChange(evt, viewportIndex)}
|
onChange={evt => onImageScrollbarChange(evt, viewportId)}
|
||||||
max={
|
max={
|
||||||
imageSliceData.numberOfSlices ? imageSliceData.numberOfSlices - 1 : 0
|
imageSliceData.numberOfSlices ? imageSliceData.numberOfSlices - 1 : 0
|
||||||
}
|
}
|
||||||
@ -143,7 +138,7 @@ function CornerstoneImageScrollbar({
|
|||||||
|
|
||||||
CornerstoneImageScrollbar.propTypes = {
|
CornerstoneImageScrollbar.propTypes = {
|
||||||
viewportData: PropTypes.object,
|
viewportData: PropTypes.object,
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
viewportId: PropTypes.string.isRequired,
|
||||||
element: PropTypes.instanceOf(Element),
|
element: PropTypes.instanceOf(Element),
|
||||||
scrollbarHeight: PropTypes.string,
|
scrollbarHeight: PropTypes.string,
|
||||||
imageSliceData: PropTypes.object.isRequired,
|
imageSliceData: PropTypes.object.isRequired,
|
||||||
|
|||||||
@ -22,7 +22,7 @@ function ViewportOrientationMarkers({
|
|||||||
element,
|
element,
|
||||||
viewportData,
|
viewportData,
|
||||||
imageSliceData,
|
imageSliceData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
orientationMarkers = ['top', 'left'],
|
orientationMarkers = ['top', 'left'],
|
||||||
}) {
|
}) {
|
||||||
@ -114,9 +114,7 @@ function ViewportOrientationMarkers({
|
|||||||
flipHorizontal
|
flipHorizontal
|
||||||
);
|
);
|
||||||
|
|
||||||
const ohifViewport = cornerstoneViewportService.getViewportInfoByIndex(
|
const ohifViewport = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!ohifViewport) {
|
if (!ohifViewport) {
|
||||||
console.log('ViewportOrientationMarkers::No viewport');
|
console.log('ViewportOrientationMarkers::No viewport');
|
||||||
|
|||||||
@ -11,7 +11,7 @@ function CornerstoneViewportOverlay({
|
|||||||
element,
|
element,
|
||||||
viewportData,
|
viewportData,
|
||||||
imageSliceData,
|
imageSliceData,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
@ -73,7 +73,7 @@ function CornerstoneViewportOverlay({
|
|||||||
return () => {
|
return () => {
|
||||||
element.removeEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
|
element.removeEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
|
||||||
};
|
};
|
||||||
}, [viewportIndex, viewportData, voi, element]);
|
}, [viewportId, viewportData, voi, element]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updating the scale when the viewport changes its zoom
|
* Updating the scale when the viewport changes its zoom
|
||||||
@ -86,8 +86,8 @@ function CornerstoneViewportOverlay({
|
|||||||
previousCamera.parallelScale !== camera.parallelScale ||
|
previousCamera.parallelScale !== camera.parallelScale ||
|
||||||
previousCamera.scale !== camera.scale
|
previousCamera.scale !== camera.scale
|
||||||
) {
|
) {
|
||||||
const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!viewport) {
|
if (!viewport) {
|
||||||
@ -118,7 +118,7 @@ function CornerstoneViewportOverlay({
|
|||||||
return () => {
|
return () => {
|
||||||
element.removeEventListener(Enums.Events.CAMERA_MODIFIED, updateScale);
|
element.removeEventListener(Enums.Events.CAMERA_MODIFIED, updateScale);
|
||||||
};
|
};
|
||||||
}, [viewportIndex, viewportData]);
|
}, [viewportId, viewportData]);
|
||||||
|
|
||||||
const getTopLeftContent = useCallback(() => {
|
const getTopLeftContent = useCallback(() => {
|
||||||
const { windowWidth, windowCenter } = voi;
|
const { windowWidth, windowCenter } = voi;
|
||||||
@ -168,7 +168,7 @@ function CornerstoneViewportOverlay({
|
|||||||
instanceNumber = _getInstanceNumberFromVolume(
|
instanceNumber = _getInstanceNumberFromVolume(
|
||||||
viewportData,
|
viewportData,
|
||||||
imageIndex,
|
imageIndex,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -183,15 +183,13 @@ function CornerstoneViewportOverlay({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [imageSliceData, viewportData, viewportIndex]);
|
}, [imageSliceData, viewportData, viewportId]);
|
||||||
|
|
||||||
if (!viewportData) {
|
if (!viewportData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ohifViewport = cornerstoneViewportService.getViewportInfoByIndex(
|
const ohifViewport = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!ohifViewport) {
|
if (!ohifViewport) {
|
||||||
return null;
|
return null;
|
||||||
@ -240,7 +238,7 @@ function _getInstanceNumberFromStack(viewportData, imageIndex) {
|
|||||||
function _getInstanceNumberFromVolume(
|
function _getInstanceNumberFromVolume(
|
||||||
viewportData,
|
viewportData,
|
||||||
imageIndex,
|
imageIndex,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
cornerstoneViewportService
|
cornerstoneViewportService
|
||||||
) {
|
) {
|
||||||
const volumes = viewportData.volumes;
|
const volumes = viewportData.volumes;
|
||||||
@ -253,8 +251,8 @@ function _getInstanceNumberFromVolume(
|
|||||||
const volume = volumes[0];
|
const volume = volumes[0];
|
||||||
const { direction, imageIds } = volume;
|
const { direction, imageIds } = volume;
|
||||||
|
|
||||||
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!cornerstoneViewport) {
|
if (!cornerstoneViewport) {
|
||||||
@ -287,7 +285,7 @@ function _getInstanceNumberFromVolume(
|
|||||||
CornerstoneViewportOverlay.propTypes = {
|
CornerstoneViewportOverlay.propTypes = {
|
||||||
viewportData: PropTypes.object,
|
viewportData: PropTypes.object,
|
||||||
imageIndex: PropTypes.number,
|
imageIndex: PropTypes.number,
|
||||||
viewportIndex: PropTypes.number,
|
viewportId: PropTypes.string,
|
||||||
servicesManager: PropTypes.instanceOf(ServicesManager),
|
servicesManager: PropTypes.instanceOf(ServicesManager),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -239,8 +239,7 @@ function commandsModule({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportIndex = viewportInfo.getViewportIndex();
|
viewportGridService.setActiveViewportId(viewportId);
|
||||||
viewportGridService.setActiveViewportIndex(viewportIndex);
|
|
||||||
},
|
},
|
||||||
arrowTextCallback: ({ callback, data }) => {
|
arrowTextCallback: ({ callback, data }) => {
|
||||||
callInputDialog(uiDialogService, data, callback);
|
callInputDialog(uiDialogService, data, callback);
|
||||||
@ -310,9 +309,11 @@ function commandsModule({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { viewports } = viewportGridService.getState() || {
|
const { viewports } = viewportGridService.getState();
|
||||||
viewports: [],
|
|
||||||
};
|
if (!viewports.size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||||
const toolGroupViewportIds = toolGroup?.getViewportIds?.();
|
const toolGroupViewportIds = toolGroup?.getViewportIds?.();
|
||||||
@ -322,15 +323,11 @@ function commandsModule({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredViewports = viewports.filter(viewport => {
|
const filteredViewports = Array.from(viewports.values()).filter(
|
||||||
if (!viewport.viewportOptions) {
|
viewport => {
|
||||||
return false;
|
return toolGroupViewportIds.includes(viewport.viewportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return toolGroupViewportIds.includes(
|
|
||||||
viewport.viewportOptions.viewportId
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (!filteredViewports.length) {
|
if (!filteredViewports.length) {
|
||||||
return;
|
return;
|
||||||
@ -368,12 +365,10 @@ function commandsModule({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
showDownloadViewportModal: () => {
|
showDownloadViewportModal: () => {
|
||||||
const { activeViewportIndex } = viewportGridService.getState();
|
const { activeViewportId } = viewportGridService.getState();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!cornerstoneViewportService.getCornerstoneViewportByIndex(
|
!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)
|
||||||
activeViewportIndex
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
// Cannot download a non-cornerstone viewport (image).
|
// Cannot download a non-cornerstone viewport (image).
|
||||||
uiNotificationService.show({
|
uiNotificationService.show({
|
||||||
@ -391,7 +386,7 @@ function commandsModule({
|
|||||||
content: CornerstoneViewportDownloadForm,
|
content: CornerstoneViewportDownloadForm,
|
||||||
title: 'Download High Quality Image',
|
title: 'Download High Quality Image',
|
||||||
contentProps: {
|
contentProps: {
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
onClose: uiModalService.hide,
|
onClose: uiModalService.hide,
|
||||||
cornerstoneViewportService,
|
cornerstoneViewportService,
|
||||||
},
|
},
|
||||||
@ -554,13 +549,13 @@ function commandsModule({
|
|||||||
cstUtils.scroll(viewport, options);
|
cstUtils.scroll(viewport, options);
|
||||||
},
|
},
|
||||||
setViewportColormap: ({
|
setViewportColormap: ({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
colormap,
|
colormap,
|
||||||
immediate = false,
|
immediate = false,
|
||||||
}) => {
|
}) => {
|
||||||
const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
const actorEntries = viewport.getActors();
|
const actorEntries = viewport.getActors();
|
||||||
@ -577,17 +572,17 @@ function commandsModule({
|
|||||||
viewport.render();
|
viewport.render();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
incrementActiveViewport: () => {
|
changeActiveViewport: ({ direction = 1 }) => {
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||||
const nextViewportIndex = (activeViewportIndex + 1) % viewports.length;
|
const viewportIds = Array.from(viewports.keys());
|
||||||
viewportGridService.setActiveViewportIndex(nextViewportIndex);
|
const currentIndex = viewportIds.indexOf(activeViewportId);
|
||||||
},
|
|
||||||
decrementActiveViewport: () => {
|
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
|
||||||
const nextViewportIndex =
|
const nextViewportIndex =
|
||||||
(activeViewportIndex - 1 + viewports.length) % viewports.length;
|
(currentIndex + direction + viewportIds.length) % viewportIds.length;
|
||||||
viewportGridService.setActiveViewportIndex(nextViewportIndex);
|
viewportGridService.setActiveViewportId(
|
||||||
|
viewportIds[nextViewportIndex] as string
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleStackImageSync: ({ toggledState }) => {
|
toggleStackImageSync: ({ toggledState }) => {
|
||||||
toggleStackImageSync({
|
toggleStackImageSync({
|
||||||
getEnabledElement,
|
getEnabledElement,
|
||||||
@ -596,9 +591,9 @@ function commandsModule({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
toggleReferenceLines: ({ toggledState }) => {
|
toggleReferenceLines: ({ toggledState }) => {
|
||||||
const { activeViewportIndex } = viewportGridService.getState();
|
const { activeViewportId } = viewportGridService.getState();
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
|
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
||||||
activeViewportIndex
|
activeViewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
const viewportId = viewportInfo.getViewportId();
|
||||||
@ -617,34 +612,8 @@ function commandsModule({
|
|||||||
);
|
);
|
||||||
toolGroup.setToolEnabled(ReferenceLinesTool.toolName);
|
toolGroup.setToolEnabled(ReferenceLinesTool.toolName);
|
||||||
},
|
},
|
||||||
storePresentation: ({ viewportIndex }) => {
|
storePresentation: ({ viewportId }) => {
|
||||||
const presentation = cornerstoneViewportService.getPresentation(
|
cornerstoneViewportService.storePresentation({ viewportId });
|
||||||
viewportIndex
|
|
||||||
);
|
|
||||||
if (!presentation || !presentation.presentationIds) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
lutPresentationStore,
|
|
||||||
positionPresentationStore,
|
|
||||||
} = stateSyncService.getState();
|
|
||||||
const { presentationIds } = presentation;
|
|
||||||
const { lutPresentationId, positionPresentationId } =
|
|
||||||
presentationIds || {};
|
|
||||||
const storeState = {};
|
|
||||||
if (lutPresentationId) {
|
|
||||||
storeState.lutPresentationStore = {
|
|
||||||
...lutPresentationStore,
|
|
||||||
[lutPresentationId]: presentation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (positionPresentationId) {
|
|
||||||
storeState.positionPresentationStore = {
|
|
||||||
...positionPresentationStore,
|
|
||||||
[positionPresentationId]: presentation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
stateSyncService.store(storeState);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -701,10 +670,11 @@ function commandsModule({
|
|||||||
options: { rotation: -90 },
|
options: { rotation: -90 },
|
||||||
},
|
},
|
||||||
incrementActiveViewport: {
|
incrementActiveViewport: {
|
||||||
commandFn: actions.incrementActiveViewport,
|
commandFn: actions.changeActiveViewport,
|
||||||
},
|
},
|
||||||
decrementActiveViewport: {
|
decrementActiveViewport: {
|
||||||
commandFn: actions.decrementActiveViewport,
|
commandFn: actions.changeActiveViewport,
|
||||||
|
options: { direction: -1 },
|
||||||
},
|
},
|
||||||
flipViewportHorizontal: {
|
flipViewportHorizontal: {
|
||||||
commandFn: actions.flipViewportHorizontal,
|
commandFn: actions.flipViewportHorizontal,
|
||||||
|
|||||||
@ -2,14 +2,10 @@ import React, { useEffect } from 'react';
|
|||||||
import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui';
|
import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui';
|
||||||
import { Enums, eventTarget } from '@cornerstonejs/core';
|
import { Enums, eventTarget } from '@cornerstonejs/core';
|
||||||
|
|
||||||
function WrappedCinePlayer({
|
function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
|
||||||
enabledVPElement,
|
|
||||||
viewportIndex,
|
|
||||||
servicesManager,
|
|
||||||
}) {
|
|
||||||
const { toolbarService, customizationService } = servicesManager.services;
|
const { toolbarService, customizationService } = servicesManager.services;
|
||||||
const [{ isCineEnabled, cines }, cineService] = useCine();
|
const [{ isCineEnabled, cines }, cineService] = useCine();
|
||||||
const [{ activeViewportIndex }] = useViewportGrid();
|
const [{ activeViewportId }] = useViewportGrid();
|
||||||
|
|
||||||
const { component: CinePlayerComponent = CinePlayer } =
|
const { component: CinePlayerComponent = CinePlayer } =
|
||||||
customizationService.get('cinePlayer') ?? {};
|
customizationService.get('cinePlayer') ?? {};
|
||||||
@ -30,11 +26,11 @@ function WrappedCinePlayer({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cineHandler = () => {
|
const cineHandler = () => {
|
||||||
if (!cines || !cines[viewportIndex] || !enabledVPElement) {
|
if (!cines || !cines[viewportId] || !enabledVPElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cine = cines[viewportIndex];
|
const cine = cines[viewportId];
|
||||||
const isPlaying = cine.isPlaying || false;
|
const isPlaying = cine.isPlaying || false;
|
||||||
const frameRate = cine.frameRate || 24;
|
const frameRate = cine.frameRate || 24;
|
||||||
|
|
||||||
@ -56,7 +52,7 @@ function WrappedCinePlayer({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cineService.setCine({ id: viewportIndex, isPlaying: false });
|
cineService.setCine({ id: viewportId, isPlaying: false });
|
||||||
eventTarget.removeEventListener(
|
eventTarget.removeEventListener(
|
||||||
Enums.Events.STACK_VIEWPORT_NEW_STACK,
|
Enums.Events.STACK_VIEWPORT_NEW_STACK,
|
||||||
cineHandler
|
cineHandler
|
||||||
@ -65,20 +61,20 @@ function WrappedCinePlayer({
|
|||||||
}, [enabledVPElement]);
|
}, [enabledVPElement]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!cines || !cines[viewportIndex] || !enabledVPElement) {
|
if (!cines || !cines[viewportId] || !enabledVPElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cineHandler();
|
cineHandler();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (enabledVPElement && cines?.[viewportIndex]?.isPlaying) {
|
if (enabledVPElement && cines?.[viewportId]?.isPlaying) {
|
||||||
cineService.stopClip(enabledVPElement);
|
cineService.stopClip(enabledVPElement);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [cines, viewportIndex, cineService, enabledVPElement, cineHandler]);
|
}, [cines, viewportId, cineService, enabledVPElement, cineHandler]);
|
||||||
|
|
||||||
const cine = cines[viewportIndex];
|
const cine = cines[viewportId];
|
||||||
const isPlaying = (cine && cine.isPlaying) || false;
|
const isPlaying = (cine && cine.isPlaying) || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -89,13 +85,13 @@ function WrappedCinePlayer({
|
|||||||
onClose={handleCineClose}
|
onClose={handleCineClose}
|
||||||
onPlayPauseChange={isPlaying =>
|
onPlayPauseChange={isPlaying =>
|
||||||
cineService.setCine({
|
cineService.setCine({
|
||||||
id: activeViewportIndex,
|
id: activeViewportId,
|
||||||
isPlaying,
|
isPlaying,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onFrameRateChange={frameRate =>
|
onFrameRateChange={frameRate =>
|
||||||
cineService.setCine({
|
cineService.setCine({
|
||||||
id: activeViewportIndex,
|
id: activeViewportId,
|
||||||
frameRate,
|
frameRate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,6 +88,7 @@ const mpr: Types.HangingProtocol.Protocol = {
|
|||||||
viewports: [
|
viewports: [
|
||||||
{
|
{
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
|
viewportId: 'mpr-axial',
|
||||||
toolGroupId: 'mpr',
|
toolGroupId: 'mpr',
|
||||||
viewportType: 'volume',
|
viewportType: 'volume',
|
||||||
orientation: 'axial',
|
orientation: 'axial',
|
||||||
@ -111,6 +112,7 @@ const mpr: Types.HangingProtocol.Protocol = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
|
viewportId: 'mpr-sagittal',
|
||||||
toolGroupId: 'mpr',
|
toolGroupId: 'mpr',
|
||||||
viewportType: 'volume',
|
viewportType: 'volume',
|
||||||
orientation: 'sagittal',
|
orientation: 'sagittal',
|
||||||
@ -134,6 +136,7 @@ const mpr: Types.HangingProtocol.Protocol = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
|
viewportId: 'mpr-coronal',
|
||||||
toolGroupId: 'mpr',
|
toolGroupId: 'mpr',
|
||||||
viewportType: 'volume',
|
viewportType: 'volume',
|
||||||
orientation: 'coronal',
|
orientation: 'coronal',
|
||||||
|
|||||||
@ -282,9 +282,8 @@ export default async function init({
|
|||||||
);
|
);
|
||||||
|
|
||||||
viewportGridService.subscribe(
|
viewportGridService.subscribe(
|
||||||
viewportGridService.EVENTS.ACTIVE_VIEWPORT_INDEX_CHANGED,
|
viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED,
|
||||||
({ viewportIndex, viewportId }) => {
|
({ viewportId }) => {
|
||||||
viewportId = viewportId || `viewport-${viewportIndex}`;
|
|
||||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||||
|
|
||||||
if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
|
if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
|
||||||
|
|||||||
@ -63,7 +63,6 @@ function initContextMenu({
|
|||||||
customizationService.get('cornerstoneViewportClickCommands') ||
|
customizationService.get('cornerstoneViewportClickCommands') ||
|
||||||
DEFAULT_CONTEXT_MENU_CLICKS;
|
DEFAULT_CONTEXT_MENU_CLICKS;
|
||||||
const toRun = customizations[name];
|
const toRun = customizations[name];
|
||||||
console.log('initContextMenu::cornerstoneViewportHandleEvent', name, toRun);
|
|
||||||
const options = {
|
const options = {
|
||||||
nearbyToolData: findNearbyToolData(commandsManager, evt),
|
nearbyToolData: findNearbyToolData(commandsManager, evt),
|
||||||
event: evt,
|
event: evt,
|
||||||
@ -82,9 +81,8 @@ function initContextMenu({
|
|||||||
if (!viewportInfo) {
|
if (!viewportInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const viewportIndex = viewportInfo.getViewportIndex();
|
|
||||||
// TODO check update upstream
|
// TODO check update upstream
|
||||||
setEnabledElement(viewportIndex, element);
|
setEnabledElement(viewportId, element);
|
||||||
|
|
||||||
element.addEventListener(
|
element.addEventListener(
|
||||||
cs3DToolsEvents.MOUSE_CLICK,
|
cs3DToolsEvents.MOUSE_CLICK,
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import {
|
|||||||
VolumeViewport,
|
VolumeViewport,
|
||||||
VolumeViewport3D,
|
VolumeViewport3D,
|
||||||
cache,
|
cache,
|
||||||
utilities,
|
|
||||||
CONSTANTS,
|
|
||||||
Enums as csEnums,
|
Enums as csEnums,
|
||||||
} from '@cornerstonejs/core';
|
} from '@cornerstonejs/core';
|
||||||
|
|
||||||
@ -55,7 +53,6 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderingEngine: Types.IRenderingEngine | null;
|
renderingEngine: Types.IRenderingEngine | null;
|
||||||
viewportsInfo: Map<number, ViewportInfo> = new Map();
|
|
||||||
viewportsById: Map<string, ViewportInfo> = new Map();
|
viewportsById: Map<string, ViewportInfo> = new Map();
|
||||||
viewportGridResizeObserver: ResizeObserver | null;
|
viewportGridResizeObserver: ResizeObserver | null;
|
||||||
viewportsDisplaySets: Map<string, string[]> = new Map();
|
viewportsDisplaySets: Map<string, string[]> = new Map();
|
||||||
@ -75,45 +72,17 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the HTML element to the viewportService
|
* Adds the HTML element to the viewportService
|
||||||
* @param {*} viewportIndex
|
* @param {*} viewportId
|
||||||
* @param {*} elementRef
|
* @param {*} elementRef
|
||||||
*/
|
*/
|
||||||
public enableViewport(
|
public enableViewport(viewportId: string, elementRef: HTMLDivElement): void {
|
||||||
viewportIndex: number,
|
const viewportInfo = new ViewportInfo(viewportId);
|
||||||
viewportOptions: PublicViewportOptions,
|
|
||||||
elementRef: HTMLDivElement
|
|
||||||
): void {
|
|
||||||
// Use the provided viewportId
|
|
||||||
// Not providing a viewportId is frowned upon because it does weird things
|
|
||||||
// on moving them around, but it does mostly work.
|
|
||||||
if (!viewportOptions.viewportId) {
|
|
||||||
console.warn('Should provide viewport id externally', viewportOptions);
|
|
||||||
viewportOptions.viewportId =
|
|
||||||
this.getViewportId(viewportIndex) || `viewport-${viewportIndex}`;
|
|
||||||
}
|
|
||||||
const { viewportId } = viewportOptions;
|
|
||||||
const viewportInfo = new ViewportInfo(viewportIndex, viewportId);
|
|
||||||
if (!viewportInfo.viewportId) {
|
|
||||||
throw new Error('Should have viewport ID afterwards');
|
|
||||||
}
|
|
||||||
|
|
||||||
viewportInfo.setElement(elementRef);
|
viewportInfo.setElement(elementRef);
|
||||||
this.viewportsInfo.set(viewportIndex, viewportInfo);
|
|
||||||
this.viewportsById.set(viewportId, viewportInfo);
|
this.viewportsById.set(viewportId, viewportInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getViewportIds(): string[] {
|
public getViewportIds(): string[] {
|
||||||
const viewportIds = [];
|
return Array.from(this.viewportsById.keys());
|
||||||
|
|
||||||
this.viewportsInfo.forEach(viewportInfo => {
|
|
||||||
viewportIds.push(viewportInfo.getViewportId());
|
|
||||||
});
|
|
||||||
|
|
||||||
return viewportIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getViewportId(viewportIndex: number): string {
|
|
||||||
return this.viewportsInfo[viewportIndex]?.viewportId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,21 +140,14 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
* created for every new viewport, this will be called whenever the set of
|
* created for every new viewport, this will be called whenever the set of
|
||||||
* viewports is changed, but NOT when the viewport position changes only.
|
* viewports is changed, but NOT when the viewport position changes only.
|
||||||
*
|
*
|
||||||
* @param viewportIndex
|
* @param viewportId - The viewportId to disable
|
||||||
*/
|
*/
|
||||||
public disableElement(viewportIndex: number): void {
|
public disableElement(viewportId: string): void {
|
||||||
const viewportInfo = this.viewportsInfo.get(viewportIndex);
|
this.renderingEngine?.disableElement(viewportId);
|
||||||
if (!viewportInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
// clean up
|
||||||
|
|
||||||
this.renderingEngine && this.renderingEngine.disableElement(viewportId);
|
|
||||||
|
|
||||||
this.viewportsInfo.get(viewportIndex).destroy();
|
|
||||||
this.viewportsInfo.delete(viewportIndex);
|
|
||||||
this.viewportsById.delete(viewportId);
|
this.viewportsById.delete(viewportId);
|
||||||
|
this.viewportsDisplaySets.delete(viewportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPresentations(viewport, presentations?: Presentations): void {
|
public setPresentations(viewport, presentations?: Presentations): void {
|
||||||
@ -199,14 +161,14 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPresentation(viewportIndex: number): Presentation {
|
public getPresentation(viewportId: string): Presentation {
|
||||||
const viewportInfo = this.viewportsInfo.get(viewportIndex);
|
const viewportInfo = this.viewportsById.get(viewportId);
|
||||||
if (!viewportInfo) {
|
if (!viewportInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { viewportType, presentationIds } = viewportInfo.getViewportOptions();
|
const { viewportType, presentationIds } = viewportInfo.getViewportOptions();
|
||||||
|
|
||||||
const csViewport = this.getCornerstoneViewportByIndex(viewportIndex);
|
const csViewport = this.getCornerstoneViewport(viewportId);
|
||||||
if (!csViewport) {
|
if (!csViewport) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -228,55 +190,80 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public storePresentation({ viewportId }) {
|
||||||
|
const stateSyncService = this.servicesManager.services.stateSyncService;
|
||||||
|
let presentation;
|
||||||
|
try {
|
||||||
|
presentation = this.getPresentation(viewportId);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!presentation || !presentation.presentationIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
lutPresentationStore,
|
||||||
|
positionPresentationStore,
|
||||||
|
} = stateSyncService.getState();
|
||||||
|
const { presentationIds } = presentation;
|
||||||
|
const { lutPresentationId, positionPresentationId } = presentationIds || {};
|
||||||
|
const storeState = {};
|
||||||
|
if (lutPresentationId) {
|
||||||
|
storeState.lutPresentationStore = {
|
||||||
|
...lutPresentationStore,
|
||||||
|
[lutPresentationId]: presentation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (positionPresentationId) {
|
||||||
|
storeState.positionPresentationStore = {
|
||||||
|
...positionPresentationStore,
|
||||||
|
[positionPresentationId]: presentation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
stateSyncService.store(storeState);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the renderingEngine to enable the element for the given viewport index
|
* Sets the viewport data for a viewport.
|
||||||
* and sets the displaySet data to the viewport
|
* @param viewportId - The ID of the viewport to set the data for.
|
||||||
* @param {*} viewportIndex
|
* @param viewportData - The viewport data to set.
|
||||||
* @param {*} displaySet
|
* @param publicViewportOptions - The public viewport options.
|
||||||
* @param {*} dataSource
|
* @param publicDisplaySetOptions - The public display set options.
|
||||||
* @returns
|
* @param presentations - The presentations to set.
|
||||||
*/
|
*/
|
||||||
public setViewportData(
|
public setViewportData(
|
||||||
viewportIndex: number,
|
viewportId: string,
|
||||||
viewportData: StackViewportData | VolumeViewportData,
|
viewportData: StackViewportData | VolumeViewportData,
|
||||||
publicViewportOptions: PublicViewportOptions,
|
publicViewportOptions: PublicViewportOptions,
|
||||||
publicDisplaySetOptions: DisplaySetOptions[],
|
publicDisplaySetOptions: DisplaySetOptions[],
|
||||||
presentations?: Presentations
|
presentations?: Presentations
|
||||||
): void {
|
): void {
|
||||||
const renderingEngine = this.getRenderingEngine();
|
const renderingEngine = this.getRenderingEngine();
|
||||||
const viewportId =
|
|
||||||
publicViewportOptions.viewportId || this.getViewportId(viewportIndex);
|
|
||||||
if (!viewportId) {
|
|
||||||
throw new Error('Must define viewportId externally');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// This is the old viewportInfo, which may have old options but we might be
|
||||||
|
// using its viewport (same viewportId as the new viewportInfo)
|
||||||
const viewportInfo = this.viewportsById.get(viewportId);
|
const viewportInfo = this.viewportsById.get(viewportId);
|
||||||
|
|
||||||
|
// We should store the presentation for the current viewport since we can't only
|
||||||
|
// rely to store it WHEN the viewport is disabled since we might keep around the
|
||||||
|
// same viewport/element and just change the viewportData for it (drag and drop etc.)
|
||||||
|
// the disableElement storePresentation handle would not be called in this case
|
||||||
|
// and we would lose the presentation.
|
||||||
|
this.storePresentation({ viewportId: viewportInfo.getViewportId() });
|
||||||
|
|
||||||
if (!viewportInfo) {
|
if (!viewportInfo) {
|
||||||
throw new Error('Viewport info not defined');
|
throw new Error('element is not enabled for the given viewportId');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the viewport has moved index, then record the new index
|
// override the viewportOptions and displaySetOptions with the public ones
|
||||||
if (viewportInfo.viewportIndex !== viewportIndex) {
|
// since those are the newly set ones, we set them here so that it handles defaults
|
||||||
this.viewportsInfo.delete(viewportInfo.viewportIndex);
|
const displaySetOptions = viewportInfo.setPublicDisplaySetOptions(
|
||||||
this.viewportsInfo.set(viewportIndex, viewportInfo);
|
publicDisplaySetOptions
|
||||||
viewportInfo.viewportIndex = viewportIndex;
|
);
|
||||||
}
|
const viewportOptions = viewportInfo.setPublicViewportOptions(
|
||||||
|
publicViewportOptions
|
||||||
viewportInfo.setRenderingEngineId(renderingEngine.id);
|
|
||||||
|
|
||||||
const {
|
|
||||||
viewportOptions,
|
|
||||||
displaySetOptions,
|
|
||||||
} = this._getViewportAndDisplaySetOptions(
|
|
||||||
publicViewportOptions,
|
|
||||||
publicDisplaySetOptions,
|
|
||||||
viewportInfo
|
|
||||||
);
|
);
|
||||||
|
|
||||||
viewportInfo.setViewportOptions(viewportOptions);
|
|
||||||
viewportInfo.setDisplaySetOptions(displaySetOptions);
|
|
||||||
viewportInfo.setViewportData(viewportData);
|
|
||||||
|
|
||||||
const element = viewportInfo.getElement();
|
const element = viewportInfo.getElement();
|
||||||
const type = viewportInfo.getViewportType();
|
const type = viewportInfo.getViewportType();
|
||||||
@ -293,12 +280,26 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Rendering Engine Id set should happen before enabling the element
|
||||||
|
// since there are callbacks that depend on the renderingEngine id
|
||||||
|
// Todo: however, this is a limitation which means that we can't change
|
||||||
|
// the rendering engine id for a given viewport which might be a super edge
|
||||||
|
// case
|
||||||
|
viewportInfo.setRenderingEngineId(renderingEngine.id);
|
||||||
|
|
||||||
// Todo: this is not optimal at all, we are re-enabling the already enabled
|
// Todo: this is not optimal at all, we are re-enabling the already enabled
|
||||||
// element which is not what we want. But enabledElement as part of the
|
// element which is not what we want. But enabledElement as part of the
|
||||||
// renderingEngine is designed to be used like this. This will trigger
|
// renderingEngine is designed to be used like this. This will trigger
|
||||||
// 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);
|
||||||
|
|
||||||
|
viewportInfo.setViewportOptions(viewportOptions);
|
||||||
|
viewportInfo.setDisplaySetOptions(displaySetOptions);
|
||||||
|
viewportInfo.setViewportData(viewportData);
|
||||||
|
viewportInfo.setViewportId(viewportId);
|
||||||
|
|
||||||
|
this.viewportsById.set(viewportId, viewportInfo);
|
||||||
|
|
||||||
const viewport = renderingEngine.getViewport(viewportId);
|
const viewport = renderingEngine.getViewport(viewportId);
|
||||||
this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
|
this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
|
||||||
|
|
||||||
@ -307,7 +308,6 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
// invalid data.
|
// invalid data.
|
||||||
this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
|
this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
|
||||||
viewportData,
|
viewportData,
|
||||||
viewportIndex,
|
|
||||||
viewportId,
|
viewportId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -330,43 +330,8 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
return viewport;
|
return viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCornerstoneViewportByIndex(
|
|
||||||
viewportIndex: number
|
|
||||||
): Types.IStackViewport | Types.IVolumeViewport | null {
|
|
||||||
const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!viewportInfo ||
|
|
||||||
!this.renderingEngine ||
|
|
||||||
this.renderingEngine.hasBeenDestroyed
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewport = this.renderingEngine.getViewport(
|
|
||||||
viewportInfo.getViewportId()
|
|
||||||
);
|
|
||||||
|
|
||||||
return viewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the viewportIndex for the provided viewportId
|
|
||||||
* @param {string} viewportId - the viewportId
|
|
||||||
* @returns {number} - the viewportIndex
|
|
||||||
*/
|
|
||||||
public getViewportInfoByIndex(viewportIndex: number): ViewportInfo {
|
|
||||||
return this.viewportsInfo.get(viewportIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getViewportInfo(viewportId: string): ViewportInfo {
|
public getViewportInfo(viewportId: string): ViewportInfo {
|
||||||
// @ts-ignore
|
return this.viewportsById.get(viewportId);
|
||||||
for (const [index, viewport] of this.viewportsInfo.entries()) {
|
|
||||||
if (viewport.getViewportId() === viewportId) {
|
|
||||||
return viewport;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setStackViewport(
|
_setStackViewport(
|
||||||
@ -414,7 +379,7 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
|
viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
|
||||||
viewport.setProperties(properties);
|
viewport.setProperties({ ...properties });
|
||||||
const camera = presentations.positionPresentation?.camera;
|
const camera = presentations.positionPresentation?.camera;
|
||||||
if (camera) {
|
if (camera) {
|
||||||
viewport.setCamera(camera);
|
viewport.setCamera(camera);
|
||||||
@ -541,10 +506,7 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
|
|
||||||
this.viewportsDisplaySets.set(viewport.id, displaySetInstanceUIDs);
|
this.viewportsDisplaySets.set(viewport.id, displaySetInstanceUIDs);
|
||||||
|
|
||||||
if (
|
if (hangingProtocolService.getShouldPerformCustomImageLoad()) {
|
||||||
hangingProtocolService.hasCustomImageLoadStrategy() &&
|
|
||||||
!hangingProtocolService.customImageLoadPerformed
|
|
||||||
) {
|
|
||||||
// delegate the volume loading to the hanging protocol service if it has a custom image load strategy
|
// delegate the volume loading to the hanging protocol service if it has a custom image load strategy
|
||||||
return hangingProtocolService.runImageLoadStrategy({
|
return hangingProtocolService.runImageLoadStrategy({
|
||||||
viewportId: viewport.id,
|
viewportId: viewport.id,
|
||||||
@ -744,14 +706,8 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
|
|
||||||
// Todo: keepCamera is an interim solution until we have a better solution for
|
// Todo: keepCamera is an interim solution until we have a better solution for
|
||||||
// keeping the camera position when the viewport data is changed
|
// keeping the camera position when the viewport data is changed
|
||||||
public updateViewport(
|
public updateViewport(viewportId: string, viewportData, keepCamera = false) {
|
||||||
viewportIndex: number,
|
const viewportInfo = this.getViewportInfo(viewportId);
|
||||||
viewportData,
|
|
||||||
keepCamera = false
|
|
||||||
) {
|
|
||||||
const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
|
|
||||||
|
|
||||||
const viewportId = viewportInfo.getViewportId();
|
|
||||||
const viewport = this.getCornerstoneViewport(viewportId);
|
const viewport = this.getCornerstoneViewport(viewportId);
|
||||||
const viewportCamera = viewport.getCamera();
|
const viewportCamera = viewport.getCamera();
|
||||||
|
|
||||||
@ -841,36 +797,6 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getViewportAndDisplaySetOptions(
|
|
||||||
publicViewportOptions: PublicViewportOptions,
|
|
||||||
publicDisplaySetOptions: DisplaySetOptions[],
|
|
||||||
viewportInfo: ViewportInfo
|
|
||||||
): {
|
|
||||||
viewportOptions: ViewportOptions;
|
|
||||||
displaySetOptions: DisplaySetOptions[];
|
|
||||||
} {
|
|
||||||
const viewportIndex = viewportInfo.getViewportIndex();
|
|
||||||
|
|
||||||
// Creating a temporary viewportInfo to handle defaults
|
|
||||||
const newViewportInfo = new ViewportInfo(
|
|
||||||
viewportIndex,
|
|
||||||
viewportInfo.getViewportId()
|
|
||||||
);
|
|
||||||
|
|
||||||
// To handle setting the default values if missing for the viewportOptions and
|
|
||||||
// displaySetOptions
|
|
||||||
newViewportInfo.setPublicViewportOptions(publicViewportOptions);
|
|
||||||
newViewportInfo.setPublicDisplaySetOptions(publicDisplaySetOptions);
|
|
||||||
|
|
||||||
const newViewportOptions = newViewportInfo.getViewportOptions();
|
|
||||||
const newDisplaySetOptions = newViewportInfo.getDisplaySetOptions();
|
|
||||||
|
|
||||||
return {
|
|
||||||
viewportOptions: newViewportOptions,
|
|
||||||
displaySetOptions: newDisplaySetOptions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_getFrameOfReferenceUID(displaySetInstanceUID) {
|
_getFrameOfReferenceUID(displaySetInstanceUID) {
|
||||||
const { displaySetService } = this.servicesManager.services;
|
const { displaySetService } = this.servicesManager.services;
|
||||||
const displaySet = displaySetService.getDisplaySetByUID(
|
const displaySet = displaySetService.getDisplaySetByUID(
|
||||||
@ -907,25 +833,25 @@ class CornerstoneViewportService extends PubSubService
|
|||||||
*
|
*
|
||||||
* @param measurement
|
* @param measurement
|
||||||
* The measurement that is desired to view.
|
* The measurement that is desired to view.
|
||||||
* @param activeViewportIndex - the index that was active at the time the jump
|
* @param activeViewportId - the index that was active at the time the jump
|
||||||
* was initiated.
|
* was initiated.
|
||||||
* @return the viewportIndex to display the given measurement
|
* @return the viewportId that the measurement should be displayed in.
|
||||||
*/
|
*/
|
||||||
public getViewportIndexToJump(
|
public getViewportIdToJump(
|
||||||
activeViewportIndex: number,
|
activeViewportId: string,
|
||||||
displaySetInstanceUID: string,
|
displaySetInstanceUID: string,
|
||||||
cameraProps: unknown
|
cameraProps: unknown
|
||||||
): number {
|
): string {
|
||||||
const viewportInfo = this.viewportsInfo.get(activeViewportIndex);
|
const viewportInfo = this.getViewportInfo(activeViewportId);
|
||||||
const { referencedImageId } = cameraProps;
|
const { referencedImageId } = cameraProps;
|
||||||
if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
|
if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
|
||||||
return activeViewportIndex;
|
return activeViewportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
[...this.viewportsById.values()].find(viewportInfo =>
|
[...this.viewportsById.values()].find(viewportInfo =>
|
||||||
viewportInfo.contains(displaySetInstanceUID, referencedImageId)
|
viewportInfo.contains(displaySetInstanceUID, referencedImageId)
|
||||||
)?.viewportIndex ?? -1
|
)?.viewportId ?? null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,6 @@ export interface IViewportService {
|
|||||||
viewportGridResizeObserver: unknown;
|
viewportGridResizeObserver: unknown;
|
||||||
viewportsInfo: unknown;
|
viewportsInfo: unknown;
|
||||||
sceneVolumeInputs: unknown;
|
sceneVolumeInputs: unknown;
|
||||||
viewportIndexUIDMap: unknown;
|
|
||||||
viewportDivElements: unknown;
|
viewportDivElements: unknown;
|
||||||
ViewportPropertiesMap: unknown;
|
ViewportPropertiesMap: unknown;
|
||||||
volumeUIDs: unknown;
|
volumeUIDs: unknown;
|
||||||
@ -28,14 +27,9 @@ export interface IViewportService {
|
|||||||
_broadcastEvent: unknown;
|
_broadcastEvent: unknown;
|
||||||
/**
|
/**
|
||||||
* Adds the HTML element to the viewportService
|
* Adds the HTML element to the viewportService
|
||||||
* @param {*} viewportIndex
|
|
||||||
* @param {*} elementRef
|
* @param {*} elementRef
|
||||||
*/
|
*/
|
||||||
enableViewport(
|
enableViewport(viewportId: string, elementRef: HTMLDivElement): void;
|
||||||
viewportIndex: number,
|
|
||||||
viewportOptions: ViewportOptions,
|
|
||||||
elementRef: HTMLDivElement
|
|
||||||
): void;
|
|
||||||
/**
|
/**
|
||||||
* It retrieves the renderingEngine if it does exist, or creates one otherwise
|
* It retrieves the renderingEngine if it does exist, or creates one otherwise
|
||||||
* @returns {RenderingEngine} rendering engine
|
* @returns {RenderingEngine} rendering engine
|
||||||
@ -54,19 +48,17 @@ export interface IViewportService {
|
|||||||
/**
|
/**
|
||||||
* Disables the viewport inside the renderingEngine, if no viewport is left
|
* Disables the viewport inside the renderingEngine, if no viewport is left
|
||||||
* it destroys the renderingEngine.
|
* it destroys the renderingEngine.
|
||||||
* @param viewportIndex
|
* @param viewportId
|
||||||
*/
|
*/
|
||||||
disableElement(viewportIndex: number): void;
|
disableElement(viewportId: string): void;
|
||||||
/**
|
/**
|
||||||
* Uses the renderingEngine to enable the element for the given viewport index
|
* Uses the renderingEngine to enable the element for the given viewport index
|
||||||
* and sets the displaySet data to the viewport
|
* and sets the displaySet data to the viewport
|
||||||
* @param {*} viewportIndex
|
|
||||||
* @param {*} displaySet
|
* @param {*} displaySet
|
||||||
* @param {*} dataSource
|
* @param {*} dataSource
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
setViewportData(
|
setViewportData(
|
||||||
viewportIndex: number,
|
|
||||||
viewportData: StackData | VolumeData,
|
viewportData: StackData | VolumeData,
|
||||||
publicViewportOptions: PublicViewportOptions,
|
publicViewportOptions: PublicViewportOptions,
|
||||||
publicDisplaySetOptions: DisplaySetOptions[]
|
publicDisplaySetOptions: DisplaySetOptions[]
|
||||||
|
|||||||
@ -106,15 +106,13 @@ const dataContains = (
|
|||||||
|
|
||||||
class ViewportInfo {
|
class ViewportInfo {
|
||||||
private viewportId = '';
|
private viewportId = '';
|
||||||
private viewportIndex: number;
|
|
||||||
private element: HTMLDivElement;
|
private element: HTMLDivElement;
|
||||||
private viewportOptions: ViewportOptions;
|
private viewportOptions: ViewportOptions;
|
||||||
private displaySetOptions: Array<DisplaySetOptions>;
|
private displaySetOptions: Array<DisplaySetOptions>;
|
||||||
private viewportData: StackViewportData | VolumeViewportData;
|
private viewportData: StackViewportData | VolumeViewportData;
|
||||||
private renderingEngineId: string;
|
private renderingEngineId: string;
|
||||||
|
|
||||||
constructor(viewportIndex: number, viewportId: string) {
|
constructor(viewportId: string) {
|
||||||
this.viewportIndex = viewportIndex;
|
|
||||||
this.viewportId = viewportId;
|
this.viewportId = viewportId;
|
||||||
this.setPublicViewportOptions({});
|
this.setPublicViewportOptions({});
|
||||||
this.setPublicDisplaySetOptions([{}]);
|
this.setPublicDisplaySetOptions([{}]);
|
||||||
@ -155,9 +153,6 @@ class ViewportInfo {
|
|||||||
public setViewportId(viewportId: string): void {
|
public setViewportId(viewportId: string): void {
|
||||||
this.viewportId = viewportId;
|
this.viewportId = viewportId;
|
||||||
}
|
}
|
||||||
public setViewportIndex(viewportIndex: number): void {
|
|
||||||
this.viewportIndex = viewportIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setElement(element: HTMLDivElement): void {
|
public setElement(element: HTMLDivElement): void {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
@ -173,9 +168,6 @@ class ViewportInfo {
|
|||||||
return this.viewportData;
|
return this.viewportData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getViewportIndex(): number {
|
|
||||||
return this.viewportIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getElement(): HTMLDivElement {
|
public getElement(): HTMLDivElement {
|
||||||
return this.element;
|
return this.element;
|
||||||
@ -187,13 +179,15 @@ class ViewportInfo {
|
|||||||
|
|
||||||
public setPublicDisplaySetOptions(
|
public setPublicDisplaySetOptions(
|
||||||
publicDisplaySetOptions: PublicDisplaySetOptions[] | DisplaySetSelector[]
|
publicDisplaySetOptions: PublicDisplaySetOptions[] | DisplaySetSelector[]
|
||||||
): void {
|
): Array<DisplaySetOptions> {
|
||||||
// map the displaySetOptions and check if they are undefined then set them to default values
|
// map the displaySetOptions and check if they are undefined then set them to default values
|
||||||
const displaySetOptions = this.mapDisplaySetOptions(
|
const displaySetOptions = this.mapDisplaySetOptions(
|
||||||
publicDisplaySetOptions
|
publicDisplaySetOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
this.setDisplaySetOptions(displaySetOptions);
|
this.setDisplaySetOptions(displaySetOptions);
|
||||||
|
|
||||||
|
return this.displaySetOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasDisplaySet(displaySetInstanceUID: string): boolean {
|
public hasDisplaySet(displaySetInstanceUID: string): boolean {
|
||||||
@ -217,7 +211,7 @@ class ViewportInfo {
|
|||||||
|
|
||||||
public setPublicViewportOptions(
|
public setPublicViewportOptions(
|
||||||
viewportOptionsEntry: PublicViewportOptions
|
viewportOptionsEntry: PublicViewportOptions
|
||||||
): void {
|
): ViewportOptions {
|
||||||
let viewportType = viewportOptionsEntry.viewportType;
|
let viewportType = viewportOptionsEntry.viewportType;
|
||||||
const {
|
const {
|
||||||
toolGroupId = DEFAULT_TOOLGROUP_ID,
|
toolGroupId = DEFAULT_TOOLGROUP_ID,
|
||||||
@ -250,6 +244,8 @@ class ViewportInfo {
|
|||||||
toolGroupId,
|
toolGroupId,
|
||||||
presentationIds,
|
presentationIds,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this.viewportOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setViewportOptions(viewportOptions: ViewportOptions): void {
|
public setViewportOptions(viewportOptions: ViewportOptions): void {
|
||||||
|
|||||||
@ -10,13 +10,13 @@ const state = {
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
const setEnabledElement = (
|
const setEnabledElement = (
|
||||||
viewportIndex: number,
|
viewportId: string,
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
context?: string
|
context?: string
|
||||||
): void => {
|
): void => {
|
||||||
const targetContext = context || state.DEFAULT_CONTEXT;
|
const targetContext = context || state.DEFAULT_CONTEXT;
|
||||||
|
|
||||||
state.enabledElements[viewportIndex] = {
|
state.enabledElements[viewportId] = {
|
||||||
element,
|
element,
|
||||||
context: targetContext,
|
context: targetContext,
|
||||||
};
|
};
|
||||||
@ -27,8 +27,8 @@ const setEnabledElement = (
|
|||||||
*
|
*
|
||||||
* @return {HTMLElement} Active viewport element.
|
* @return {HTMLElement} Active viewport element.
|
||||||
*/
|
*/
|
||||||
const getEnabledElement = viewportIndex => {
|
const getEnabledElement = viewportId => {
|
||||||
return state.enabledElements[viewportIndex];
|
return state.enabledElements[viewportId];
|
||||||
};
|
};
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
|
|||||||
@ -20,10 +20,10 @@ const VIEWPORT_ID = 'cornerstone-viewport-download-form';
|
|||||||
|
|
||||||
const CornerstoneViewportDownloadForm = ({
|
const CornerstoneViewportDownloadForm = ({
|
||||||
onClose,
|
onClose,
|
||||||
activeViewportIndex,
|
activeViewportId: activeViewportIdProp,
|
||||||
cornerstoneViewportService,
|
cornerstoneViewportService,
|
||||||
}) => {
|
}) => {
|
||||||
const enabledElement = OHIFgetEnabledElement(activeViewportIndex);
|
const enabledElement = OHIFgetEnabledElement(activeViewportIdProp);
|
||||||
const activeViewportElement = enabledElement?.element;
|
const activeViewportElement = enabledElement?.element;
|
||||||
const activeViewportEnabledElement = getEnabledElement(activeViewportElement);
|
const activeViewportEnabledElement = getEnabledElement(activeViewportElement);
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
|
|
||||||
CornerstoneViewportDownloadForm.propTypes = {
|
CornerstoneViewportDownloadForm.propTypes = {
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
activeViewportIndex: PropTypes.number.isRequired,
|
activeViewportId: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CornerstoneViewportDownloadForm;
|
export default CornerstoneViewportDownloadForm;
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import { getEnabledElement as OHIFgetEnabledElement } from '../state';
|
|||||||
export default function getActiveViewportEnabledElement(
|
export default function getActiveViewportEnabledElement(
|
||||||
viewportGridService
|
viewportGridService
|
||||||
): IEnabledElement {
|
): IEnabledElement {
|
||||||
const { activeViewportIndex } = viewportGridService.getState();
|
const { activeViewportId } = viewportGridService.getState();
|
||||||
const { element } = OHIFgetEnabledElement(activeViewportIndex) || {};
|
const { element } = OHIFgetEnabledElement(activeViewportId) || {};
|
||||||
const enabledElement = getEnabledElement(element);
|
const enabledElement = getEnabledElement(element);
|
||||||
return enabledElement;
|
return enabledElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import calculateViewportRegistrations from './calculateViewportRegistrations';
|
|||||||
|
|
||||||
// [ {
|
// [ {
|
||||||
// synchronizerId: string,
|
// synchronizerId: string,
|
||||||
// viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
|
// viewports: [ { viewportId: string, renderingEngineId: string, index: number } , ...]
|
||||||
// ]}
|
// ]}
|
||||||
let STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
let STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
||||||
|
|
||||||
@ -37,16 +37,13 @@ export default function toggleStackImageSync({
|
|||||||
STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
||||||
|
|
||||||
// create synchronization groups and add viewports
|
// create synchronization groups and add viewports
|
||||||
let { viewports } = viewportGridService.getState();
|
const { viewports } = viewportGridService.getState();
|
||||||
|
|
||||||
// filter empty viewports
|
// filter empty viewports
|
||||||
viewports = viewports.filter(
|
const viewportsArray = Array.from(viewports.values())
|
||||||
viewport =>
|
.filter(viewport => viewport.displaySetInstanceUIDs?.length)
|
||||||
viewport.displaySetInstanceUIDs && viewport.displaySetInstanceUIDs.length
|
|
||||||
);
|
|
||||||
|
|
||||||
// filter reconstructable viewports
|
// filter reconstructable viewports
|
||||||
viewports = viewports.filter(viewport => {
|
.filter(viewport => {
|
||||||
const { displaySetInstanceUIDs } = viewport;
|
const { displaySetInstanceUIDs } = viewport;
|
||||||
|
|
||||||
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
||||||
@ -54,15 +51,11 @@ export default function toggleStackImageSync({
|
|||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
if (displaySet && displaySet.isReconstructable) {
|
return !!displaySet?.isReconstructable;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewportsByOrientation = viewports.reduce((acc, viewport) => {
|
const viewportsByOrientation = viewportsArray.reduce((acc, viewport) => {
|
||||||
const { viewportId, viewportType } = viewport.viewportOptions;
|
const { viewportId, viewportType } = viewport.viewportOptions;
|
||||||
|
|
||||||
if (viewportType !== 'stack') {
|
if (viewportType !== 'stack') {
|
||||||
@ -71,9 +64,8 @@ export default function toggleStackImageSync({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { element } = cornerstoneViewportService.getViewportInfo(viewportId);
|
const { element } = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||||
const { viewport: csViewport, renderingEngineId } = getEnabledElement(
|
const { viewport: csViewport, renderingEngineId } =
|
||||||
element
|
getEnabledElement(element);
|
||||||
);
|
|
||||||
const { viewPlaneNormal } = csViewport.getCamera();
|
const { viewPlaneNormal } = csViewport.getCamera();
|
||||||
|
|
||||||
// Should we round here? I guess so, but not sure how much precision we need
|
// Should we round here? I guess so, but not sure how much precision we need
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export default function PanelMeasurementTable({
|
|||||||
extensionManager,
|
extensionManager,
|
||||||
}): React.FunctionComponent {
|
}): React.FunctionComponent {
|
||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
const { activeViewportIndex, viewports } = viewportGrid;
|
const { activeViewportId, viewports } = viewportGrid;
|
||||||
const {
|
const {
|
||||||
measurementService,
|
measurementService,
|
||||||
uiDialogService,
|
uiDialogService,
|
||||||
@ -80,7 +80,7 @@ export default function PanelMeasurementTable({
|
|||||||
|
|
||||||
async function createReport(): Promise<any> {
|
async function createReport(): Promise<any> {
|
||||||
// filter measurements that are added to the active study
|
// filter measurements that are added to the active study
|
||||||
const activeViewport = viewports[activeViewportIndex];
|
const activeViewport = viewports.get(activeViewportId);
|
||||||
const measurements = measurementService.getMeasurements();
|
const measurements = measurementService.getMeasurements();
|
||||||
const displaySet = displaySetService.getDisplaySetByUID(
|
const displaySet = displaySetService.getDisplaySetByUID(
|
||||||
activeViewport.displaySetInstanceUIDs[0]
|
activeViewport.displaySetInstanceUIDs[0]
|
||||||
@ -133,7 +133,7 @@ export default function PanelMeasurementTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const jumpToImage = ({ uid, isActive }) => {
|
const jumpToImage = ({ uid, isActive }) => {
|
||||||
measurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, uid);
|
measurementService.jumpToMeasurement(viewportGrid.activeViewportId, uid);
|
||||||
|
|
||||||
onMeasurementItemClickHandler({ uid, isActive });
|
onMeasurementItemClickHandler({ uid, isActive });
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,7 +29,7 @@ function PanelStudyBrowser({
|
|||||||
// Tabs --> Studies --> DisplaySets --> Thumbnails
|
// Tabs --> Studies --> DisplaySets --> Thumbnails
|
||||||
const { StudyInstanceUIDs } = useImageViewer();
|
const { StudyInstanceUIDs } = useImageViewer();
|
||||||
const [
|
const [
|
||||||
{ activeViewportIndex, viewports },
|
{ activeViewportId, viewports },
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
] = useViewportGrid();
|
] = useViewportGrid();
|
||||||
const [activeTabName, setActiveTabName] = useState('primary');
|
const [activeTabName, setActiveTabName] = useState('primary');
|
||||||
@ -43,10 +43,10 @@ function PanelStudyBrowser({
|
|||||||
|
|
||||||
const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
|
const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
|
||||||
let updatedViewports = [];
|
let updatedViewports = [];
|
||||||
const viewportIndex = activeViewportIndex;
|
const viewportId = activeViewportId;
|
||||||
try {
|
try {
|
||||||
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -262,8 +262,8 @@ function PanelStudyBrowser({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeDisplaySetInstanceUIDs =
|
const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)
|
||||||
viewports[activeViewportIndex]?.displaySetInstanceUIDs;
|
?.displaySetInstanceUIDs;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StudyBrowser
|
<StudyBrowser
|
||||||
|
|||||||
@ -261,17 +261,17 @@ const commandsModule = ({
|
|||||||
];
|
];
|
||||||
stateSyncService.store(stateSyncReduce);
|
stateSyncService.store(stateSyncReduce);
|
||||||
// This is a default action applied
|
// This is a default action applied
|
||||||
actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
|
const { protocol } = hangingProtocolService.getActiveProtocol();
|
||||||
|
actions.toggleHpTools(protocol);
|
||||||
// Send the notification about updating the state
|
// Send the notification about updating the state
|
||||||
if (protocolId !== hpInfo.protocolId) {
|
if (protocolId !== hpInfo.protocolId) {
|
||||||
const { protocol } = hangingProtocolService.getActiveProtocol();
|
|
||||||
// The old protocol callbacks are used for turning off things
|
// The old protocol callbacks are used for turning off things
|
||||||
// like crosshairs when moving to the new HP
|
// like crosshairs when moving to the new HP
|
||||||
commandsManager.run(oldProtocol.callbacks?.onProtocolExit);
|
commandsManager.run(oldProtocol.callbacks?.onProtocolExit);
|
||||||
// The new protocol callback is used for things like
|
// The new protocol callback is used for things like
|
||||||
// activating modes etc.
|
// activating modes etc.
|
||||||
commandsManager.run(protocol.callbacks?.onProtocolEnter);
|
|
||||||
}
|
}
|
||||||
|
commandsManager.run(protocol.callbacks?.onProtocolEnter);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
|
actions.toggleHpTools(hangingProtocolService.getActiveProtocol());
|
||||||
@ -394,12 +394,12 @@ const commandsModule = ({
|
|||||||
|
|
||||||
toggleOneUp() {
|
toggleOneUp() {
|
||||||
const viewportGridState = viewportGridService.getState();
|
const viewportGridState = viewportGridService.getState();
|
||||||
const { activeViewportIndex, viewports, layout } = viewportGridState;
|
const { activeViewportId, viewports, layout } = viewportGridState;
|
||||||
const {
|
const {
|
||||||
displaySetInstanceUIDs,
|
displaySetInstanceUIDs,
|
||||||
displaySetOptions,
|
displaySetOptions,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
} = viewports[activeViewportIndex];
|
} = viewports.get(activeViewportId);
|
||||||
|
|
||||||
if (layout.numCols === 1 && layout.numRows === 1) {
|
if (layout.numCols === 1 && layout.numRows === 1) {
|
||||||
// The viewer is in one-up. Check if there is a state to restore/toggle back to.
|
// The viewer is in one-up. Check if there is a state to restore/toggle back to.
|
||||||
@ -410,20 +410,16 @@ const commandsModule = ({
|
|||||||
}
|
}
|
||||||
// There is a state to toggle back to. The viewport that was
|
// There is a state to toggle back to. The viewport that was
|
||||||
// originally toggled to one up was the former active viewport.
|
// originally toggled to one up was the former active viewport.
|
||||||
const viewportIndexToUpdate =
|
const viewportIdToUpdate =
|
||||||
toggleOneUpViewportGridStore.activeViewportIndex;
|
toggleOneUpViewportGridStore.activeViewportId;
|
||||||
|
|
||||||
// Determine which viewports need to be updated. This is particularly
|
const updatedViewportsViaHP =
|
||||||
// important when MPR is toggled to one up and a different reconstructable
|
|
||||||
// is swapped in. Note that currently HangingProtocolService.getViewportsRequireUpdate
|
|
||||||
// does not support viewport with multiple display sets.
|
|
||||||
const updatedViewports =
|
|
||||||
displaySetInstanceUIDs.length > 1
|
displaySetInstanceUIDs.length > 1
|
||||||
? []
|
? []
|
||||||
: displaySetInstanceUIDs
|
: displaySetInstanceUIDs
|
||||||
.map(displaySetInstanceUID =>
|
.map(displaySetInstanceUID =>
|
||||||
hangingProtocolService.getViewportsRequireUpdate(
|
hangingProtocolService.getViewportsRequireUpdate(
|
||||||
viewportIndexToUpdate,
|
viewportIdToUpdate,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -432,14 +428,37 @@ const commandsModule = ({
|
|||||||
// This findOrCreateViewport returns either one of the updatedViewports
|
// This findOrCreateViewport returns either one of the updatedViewports
|
||||||
// returned from the HP service OR if there is not one from the HP service then
|
// returned from the HP service OR if there is not one from the HP service then
|
||||||
// simply returns what was in the previous state.
|
// simply returns what was in the previous state.
|
||||||
const findOrCreateViewport = (viewportIndex: number) => {
|
const findOrCreateViewport = (position: number) => {
|
||||||
const viewport = updatedViewports.find(
|
// get the viewportId in the current state (since we are in the one-up layout)
|
||||||
viewport => viewport.viewportIndex === viewportIndex
|
const currentOneUpViewport = Array.from(viewports.values())[0];
|
||||||
);
|
|
||||||
|
|
||||||
return viewport
|
// we should restore the previous layout but take into the account the fact that
|
||||||
? { viewportOptions, displaySetOptions, ...viewport }
|
// the current one up viewport might have a new displaySet dragged and dropped on it
|
||||||
: toggleOneUpViewportGridStore.viewports[viewportIndex];
|
// so we should prioritize the current one in the old grid store layout viewports
|
||||||
|
|
||||||
|
const newViewports = Array.from(
|
||||||
|
toggleOneUpViewportGridStore.viewports.values()
|
||||||
|
).map(viewport => {
|
||||||
|
if (viewport.viewportId === currentOneUpViewport.viewportId) {
|
||||||
|
return {
|
||||||
|
...currentOneUpViewport,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewport;
|
||||||
|
});
|
||||||
|
|
||||||
|
// However, we also need to take into account that the current one up viewport
|
||||||
|
// might have been part of a bigger hanging protocol layout, so going back
|
||||||
|
// from one up we should apply those viewports as well.
|
||||||
|
return updatedViewportsViaHP.length > 1 &&
|
||||||
|
updatedViewportsViaHP[position]
|
||||||
|
? {
|
||||||
|
viewportOptions,
|
||||||
|
displaySetOptions,
|
||||||
|
...updatedViewportsViaHP[position],
|
||||||
|
}
|
||||||
|
: newViewports[position];
|
||||||
};
|
};
|
||||||
|
|
||||||
const layoutOptions = viewportGridService.getLayoutOptionsFromState(
|
const layoutOptions = viewportGridService.getLayoutOptionsFromState(
|
||||||
@ -450,7 +469,7 @@ const commandsModule = ({
|
|||||||
viewportGridService.setLayout({
|
viewportGridService.setLayout({
|
||||||
numRows: toggleOneUpViewportGridStore.layout.numRows,
|
numRows: toggleOneUpViewportGridStore.layout.numRows,
|
||||||
numCols: toggleOneUpViewportGridStore.layout.numCols,
|
numCols: toggleOneUpViewportGridStore.layout.numCols,
|
||||||
activeViewportIndex: viewportIndexToUpdate,
|
activeViewportId: viewportIdToUpdate,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
findOrCreateViewport,
|
findOrCreateViewport,
|
||||||
});
|
});
|
||||||
@ -520,8 +539,8 @@ const commandsModule = ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
openDICOMTagViewer() {
|
openDICOMTagViewer() {
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||||
const activeViewportSpecificData = viewports[activeViewportIndex];
|
const activeViewportSpecificData = viewports.get(activeViewportId);
|
||||||
const { displaySetInstanceUIDs } = activeViewportSpecificData;
|
const { displaySetInstanceUIDs } = activeViewportSpecificData;
|
||||||
|
|
||||||
const displaySets = displaySetService.activeDisplaySets;
|
const displaySets = displaySetService.activeDisplaySets;
|
||||||
@ -552,17 +571,9 @@ const commandsModule = ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
scrollActiveThumbnailIntoView: () => {
|
scrollActiveThumbnailIntoView: () => {
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||||
|
|
||||||
if (
|
const activeViewport = viewports.get(activeViewportId);
|
||||||
!viewports ||
|
|
||||||
activeViewportIndex < 0 ||
|
|
||||||
activeViewportIndex > viewports.length - 1
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeViewport = viewports[activeViewportIndex];
|
|
||||||
const activeDisplaySetInstanceUID =
|
const activeDisplaySetInstanceUID =
|
||||||
activeViewport.displaySetInstanceUIDs[0];
|
activeViewport.displaySetInstanceUIDs[0];
|
||||||
|
|
||||||
@ -615,9 +626,9 @@ const commandsModule = ({
|
|||||||
|
|
||||||
currentDisplaySets.sort(dsSortFn);
|
currentDisplaySets.sort(dsSortFn);
|
||||||
|
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||||
|
|
||||||
const { displaySetInstanceUIDs } = viewports[activeViewportIndex];
|
const { displaySetInstanceUIDs } = viewports.get(activeViewportId);
|
||||||
|
|
||||||
const activeDisplaySetIndex = currentDisplaySets.findIndex(displaySet =>
|
const activeDisplaySetIndex = currentDisplaySets.findIndex(displaySet =>
|
||||||
displaySetInstanceUIDs.includes(displaySet.displaySetInstanceUID)
|
displaySetInstanceUIDs.includes(displaySet.displaySetInstanceUID)
|
||||||
@ -656,7 +667,7 @@ const commandsModule = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { StateSyncService, Types } from '@ohif/core';
|
import { StateSyncService } from '@ohif/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This find or create viewport is paired with the reduce results from
|
* This find or create viewport is paired with the reduce results from
|
||||||
@ -7,7 +7,7 @@ import { StateSyncService, Types } from '@ohif/core';
|
|||||||
* then one can be re-used from the display set if it isn't going to be displayed.
|
* then one can be re-used from the display set if it isn't going to be displayed.
|
||||||
* @param hangingProtocolService - bound parameter supplied before using this
|
* @param hangingProtocolService - bound parameter supplied before using this
|
||||||
* @param viewportsByPosition - bound parameter supplied before using this
|
* @param viewportsByPosition - bound parameter supplied before using this
|
||||||
* @param viewportIndex - the index to retrieve
|
* @param position - the position in the grid to retrieve
|
||||||
* @param positionId - the current position on screen to retrieve
|
* @param positionId - the current position on screen to retrieve
|
||||||
* @param options - the set of options used, so that subsequent calls can
|
* @param options - the set of options used, so that subsequent calls can
|
||||||
* store state that is reset by the setLayout.
|
* store state that is reset by the setLayout.
|
||||||
@ -17,7 +17,7 @@ import { StateSyncService, Types } from '@ohif/core';
|
|||||||
export const findOrCreateViewport = (
|
export const findOrCreateViewport = (
|
||||||
hangingProtocolService,
|
hangingProtocolService,
|
||||||
viewportsByPosition,
|
viewportsByPosition,
|
||||||
viewportIndex: number,
|
position: number,
|
||||||
positionId: string,
|
positionId: string,
|
||||||
options: Record<string, unknown>
|
options: Record<string, unknown>
|
||||||
) => {
|
) => {
|
||||||
@ -74,24 +74,19 @@ const findViewportsByPosition = (
|
|||||||
const viewportsByPosition = { ...syncState.viewportsByPosition };
|
const viewportsByPosition = { ...syncState.viewportsByPosition };
|
||||||
const initialInDisplay = [];
|
const initialInDisplay = [];
|
||||||
|
|
||||||
for (const viewport of viewports) {
|
viewports.forEach(viewport => {
|
||||||
if (viewport.positionId) {
|
if (viewport.positionId) {
|
||||||
const storedViewport = {
|
const storedViewport = {
|
||||||
...viewport,
|
...viewport,
|
||||||
viewportOptions: { ...viewport.viewportOptions },
|
viewportOptions: { ...viewport.viewportOptions },
|
||||||
};
|
};
|
||||||
viewportsByPosition[viewport.positionId] = storedViewport;
|
viewportsByPosition[viewport.positionId] = storedViewport;
|
||||||
// The cache doesn't store the viewport options - it is only useful
|
|
||||||
// for remembering the type of viewport and UIDs
|
|
||||||
delete storedViewport.viewportId;
|
|
||||||
delete storedViewport.viewportOptions.viewportId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for (let row = 0; row < numRows; row++) {
|
for (let row = 0; row < numRows; row++) {
|
||||||
for (let col = 0; col < numCols; col++) {
|
for (let col = 0; col < numCols; col++) {
|
||||||
const pos = col + row * numCols;
|
const positionId = `${col}-${row}`;
|
||||||
const positionId = viewports?.[pos]?.positionId || `${col}-${row}`;
|
|
||||||
const viewport = viewportsByPosition[positionId];
|
const viewport = viewportsByPosition[positionId];
|
||||||
if (viewport?.displaySetInstanceUIDs) {
|
if (viewport?.displaySetInstanceUIDs) {
|
||||||
initialInDisplay.push(...viewport.displaySetInstanceUIDs);
|
initialInDisplay.push(...viewport.displaySetInstanceUIDs);
|
||||||
|
|||||||
@ -73,6 +73,7 @@ const defaultProtocol = {
|
|||||||
{
|
{
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
viewportType: 'stack',
|
viewportType: 'stack',
|
||||||
|
viewportId: 'default',
|
||||||
toolGroupId: 'default',
|
toolGroupId: 'default',
|
||||||
// This will specify the initial image options index if it matches in the URL
|
// This will specify the initial image options index if it matches in the URL
|
||||||
// and will otherwise not specify anything.
|
// and will otherwise not specify anything.
|
||||||
|
|||||||
@ -19,22 +19,24 @@ const reuseCachedLayout = (
|
|||||||
hangingProtocolService: HangingProtocolService,
|
hangingProtocolService: HangingProtocolService,
|
||||||
syncService: StateSyncService
|
syncService: StateSyncService
|
||||||
): ReturnType => {
|
): ReturnType => {
|
||||||
const { activeViewportIndex, viewports, layout } = state;
|
const { activeViewportId } = state;
|
||||||
|
const { protocol } = hangingProtocolService.getActiveProtocol();
|
||||||
const hpInfo = hangingProtocolService.getState();
|
const hpInfo = hangingProtocolService.getState();
|
||||||
const { protocolId, stageIndex, activeStudyUID } = hpInfo;
|
const { protocolId, stageIndex, activeStudyUID } = hpInfo;
|
||||||
const { protocol } = hangingProtocolService.getActiveProtocol();
|
|
||||||
|
const syncState = syncService.getState();
|
||||||
|
const viewportGridStore = { ...syncState.viewportGridStore };
|
||||||
|
const displaySetSelectorMap = { ...syncState.displaySetSelectorMap };
|
||||||
|
|
||||||
const stage = protocol.stages[stageIndex];
|
const stage = protocol.stages[stageIndex];
|
||||||
const storeId = `${activeStudyUID}:${protocolId}:${stageIndex}`;
|
const storeId = `${activeStudyUID}:${protocolId}:${stageIndex}`;
|
||||||
const syncState = syncService.getState();
|
|
||||||
const cacheId = `${activeStudyUID}:${protocolId}`;
|
const cacheId = `${activeStudyUID}:${protocolId}`;
|
||||||
const viewportGridStore = { ...syncState.viewportGridStore };
|
|
||||||
const hangingProtocolStageIndexMap = {
|
const hangingProtocolStageIndexMap = {
|
||||||
...syncState.hangingProtocolStageIndexMap,
|
...syncState.hangingProtocolStageIndexMap,
|
||||||
};
|
};
|
||||||
const displaySetSelectorMap = { ...syncState.displaySetSelectorMap };
|
|
||||||
const { rows, columns } = stage.viewportStructure.properties;
|
const { rows, columns } = stage.viewportStructure.properties;
|
||||||
const custom =
|
const custom =
|
||||||
stage.viewports.length !== state.viewports.length ||
|
stage.viewports.length !== state.viewports.size ||
|
||||||
state.layout.numRows !== rows ||
|
state.layout.numRows !== rows ||
|
||||||
state.layout.numCols !== columns;
|
state.layout.numCols !== columns;
|
||||||
|
|
||||||
@ -44,18 +46,17 @@ const reuseCachedLayout = (
|
|||||||
viewportGridStore[storeId] = { ...state };
|
viewportGridStore[storeId] = { ...state };
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let idx = 0; idx < state.viewports.length; idx++) {
|
state.viewports.forEach((viewport, viewportId) => {
|
||||||
const viewport = state.viewports[idx];
|
|
||||||
const { displaySetOptions, displaySetInstanceUIDs } = viewport;
|
const { displaySetOptions, displaySetInstanceUIDs } = viewport;
|
||||||
if (!displaySetOptions) {
|
if (!displaySetOptions) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < displaySetOptions.length; i++) {
|
for (let i = 0; i < displaySetOptions.length; i++) {
|
||||||
const displaySetUID = displaySetInstanceUIDs[i];
|
const displaySetUID = displaySetInstanceUIDs[i];
|
||||||
if (!displaySetUID) {
|
if (!displaySetUID) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (idx === activeViewportIndex && i === 0) {
|
if (viewportId === activeViewportId && i === 0) {
|
||||||
displaySetSelectorMap[
|
displaySetSelectorMap[
|
||||||
`${activeStudyUID}:activeDisplaySet:0`
|
`${activeStudyUID}:activeDisplaySet:0`
|
||||||
] = displaySetUID;
|
] = displaySetUID;
|
||||||
@ -67,7 +68,7 @@ const reuseCachedLayout = (
|
|||||||
] = displaySetUID;
|
] = displaySetUID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hangingProtocolStageIndexMap,
|
hangingProtocolStageIndexMap,
|
||||||
|
|||||||
@ -49,12 +49,12 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
viewportData: PropTypes.object,
|
viewportData: PropTypes.object,
|
||||||
activeViewportIndex: PropTypes.number,
|
activeViewportId: PropTypes.string,
|
||||||
setViewportActive: PropTypes.func,
|
setViewportActive: PropTypes.func,
|
||||||
|
|
||||||
// props from OHIF Viewport Grid
|
// props from OHIF Viewport Grid
|
||||||
displaySets: PropTypes.array,
|
displaySets: PropTypes.array,
|
||||||
viewportIndex: PropTypes.number,
|
viewportId: PropTypes.string,
|
||||||
viewportLabel: PropTypes.string,
|
viewportLabel: PropTypes.string,
|
||||||
dataSource: PropTypes.object,
|
dataSource: PropTypes.object,
|
||||||
viewportOptions: PropTypes.object,
|
viewportOptions: PropTypes.object,
|
||||||
@ -230,7 +230,7 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
|
|
||||||
this.managedViewer = this.microscopyService.addViewer(
|
this.managedViewer = this.microscopyService.addViewer(
|
||||||
this.viewer,
|
this.viewer,
|
||||||
this.props.viewportIndex,
|
this.props.viewportId,
|
||||||
container,
|
container,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID
|
SeriesInstanceUID
|
||||||
@ -259,8 +259,10 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { displaySets, viewportIndex } = this.props;
|
const { displaySets, viewportOptions } = this.props;
|
||||||
const displaySet = displaySets[viewportIndex];
|
const { viewportId } = viewportOptions;
|
||||||
|
// Todo-rename: this is always getting the 0
|
||||||
|
const displaySet = displaySets[0];
|
||||||
this.installOpenLayersRenderer(this.container.current, displaySet).then(
|
this.installOpenLayersRenderer(this.container.current, displaySet).then(
|
||||||
() => {
|
() => {
|
||||||
this.setState({ isLoaded: true });
|
this.setState({ isLoaded: true });
|
||||||
@ -295,14 +297,10 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setViewportActiveHandler = () => {
|
setViewportActiveHandler = () => {
|
||||||
const {
|
const { setViewportActive, viewportId, activeViewportId } = this.props;
|
||||||
setViewportActive,
|
|
||||||
viewportIndex,
|
|
||||||
activeViewportIndex,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (viewportIndex !== activeViewportIndex) {
|
if (viewportId !== activeViewportId) {
|
||||||
setViewportActive(viewportIndex);
|
setViewportActive(viewportId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,7 @@ const formatLength = (length, unit) => {
|
|||||||
|
|
||||||
interface IMicroscopyPanelProps extends WithTranslation {
|
interface IMicroscopyPanelProps extends WithTranslation {
|
||||||
viewports: PropTypes.array;
|
viewports: PropTypes.array;
|
||||||
activeViewportIndex: PropTypes.number;
|
activeViewportId: PropTypes.string;
|
||||||
|
|
||||||
//
|
//
|
||||||
onSaveComplete?: PropTypes.func; // callback when successfully saved annotations
|
onSaveComplete?: PropTypes.func; // callback when successfully saved annotations
|
||||||
@ -83,7 +83,7 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
|
|||||||
const { uiDialogService, displaySetService } = servicesManager.services;
|
const { uiDialogService, displaySetService } = servicesManager.services;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const viewport = props.viewports[props.activeViewportIndex];
|
const viewport = props.viewports[props.activeViewportId];
|
||||||
if (viewport.displaySetInstanceUIDs[0]) {
|
if (viewport.displaySetInstanceUIDs[0]) {
|
||||||
const displaySet = displaySetService.getDisplaySetByUID(
|
const displaySet = displaySetService.getDisplaySetByUID(
|
||||||
viewport.displaySetInstanceUIDs[0]
|
viewport.displaySetInstanceUIDs[0]
|
||||||
@ -92,7 +92,7 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
|
|||||||
setStudyInstanceUID(displaySet.StudyInstanceUID);
|
setStudyInstanceUID(displaySet.StudyInstanceUID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.viewports, props.activeViewportIndex]);
|
}, [props.viewports, props.activeViewportId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onAnnotationUpdated = () => {
|
const onAnnotationUpdated = () => {
|
||||||
@ -265,7 +265,7 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
|
|||||||
*/
|
*/
|
||||||
const onDeleteCurrentSRHandler = async () => {
|
const onDeleteCurrentSRHandler = async () => {
|
||||||
try {
|
try {
|
||||||
const activeViewport = props.viewports[props.activeViewportIndex];
|
const activeViewport = props.viewports[props.activeViewportId];
|
||||||
const { StudyInstanceUID } = activeViewport;
|
const { StudyInstanceUID } = activeViewport;
|
||||||
|
|
||||||
// TODO: studies?
|
// TODO: studies?
|
||||||
@ -307,7 +307,7 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
|
|||||||
const onMeasurementItemClickHandler = ({ uid }: { uid: string }) => {
|
const onMeasurementItemClickHandler = ({ uid }: { uid: string }) => {
|
||||||
const roiAnnotation = microscopyService.getAnnotation(uid);
|
const roiAnnotation = microscopyService.getAnnotation(uid);
|
||||||
microscopyService.selectAnnotation(roiAnnotation);
|
microscopyService.selectAnnotation(roiAnnotation);
|
||||||
microscopyService.focusAnnotation(roiAnnotation, props.activeViewportIndex);
|
microscopyService.focusAnnotation(roiAnnotation, props.activeViewportId);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -122,19 +122,6 @@ export default function getCommandsModule({
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
incrementActiveViewport: () => {
|
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
|
||||||
const nextViewportIndex = (activeViewportIndex + 1) % viewports.length;
|
|
||||||
viewportGridService.setActiveViewportIndex(nextViewportIndex);
|
|
||||||
},
|
|
||||||
decrementActiveViewport: () => {
|
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
|
||||||
const nextViewportIndex =
|
|
||||||
(activeViewportIndex - 1 + viewports.length) % viewports.length;
|
|
||||||
viewportGridService.setActiveViewportIndex(nextViewportIndex);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleOverlays: () => {
|
toggleOverlays: () => {
|
||||||
// overlay
|
// overlay
|
||||||
const overlays = document.getElementsByClassName(
|
const overlays = document.getElementsByClassName(
|
||||||
@ -149,8 +136,8 @@ export default function getCommandsModule({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// overview
|
// overview
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||||
microscopyService.toggleOverviewMap(activeViewportIndex);
|
microscopyService.toggleOverviewMap(activeViewportId);
|
||||||
},
|
},
|
||||||
toggleAnnotations: () => {
|
toggleAnnotations: () => {
|
||||||
microscopyService.toggleROIsVisibility();
|
microscopyService.toggleROIsVisibility();
|
||||||
@ -173,14 +160,6 @@ export default function getCommandsModule({
|
|||||||
storeContexts: [] as any[],
|
storeContexts: [] as any[],
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
incrementActiveViewport: {
|
|
||||||
commandFn: actions.incrementActiveViewport,
|
|
||||||
storeContexts: [] as any[],
|
|
||||||
},
|
|
||||||
decrementActiveViewport: {
|
|
||||||
commandFn: actions.decrementActiveViewport,
|
|
||||||
storeContexts: [] as any[],
|
|
||||||
},
|
|
||||||
toggleOverlays: {
|
toggleOverlays: {
|
||||||
commandFn: actions.toggleOverlays,
|
commandFn: actions.toggleOverlays,
|
||||||
storeContexts: [] as any[],
|
storeContexts: [] as any[],
|
||||||
|
|||||||
@ -18,15 +18,12 @@ export default function getPanelModule({
|
|||||||
extensionManager: ExtensionManager;
|
extensionManager: ExtensionManager;
|
||||||
}) {
|
}) {
|
||||||
const wrappedMeasurementPanel = () => {
|
const wrappedMeasurementPanel = () => {
|
||||||
const [
|
const [{ activeViewportId, viewports }] = useViewportGrid();
|
||||||
{ activeViewportIndex, viewports },
|
|
||||||
viewportGridService,
|
|
||||||
] = useViewportGrid();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MicroscopyPanel
|
<MicroscopyPanel
|
||||||
viewports={viewports}
|
viewports={viewports}
|
||||||
activeViewportIndex={activeViewportIndex}
|
activeViewportId={activeViewportId}
|
||||||
onSaveComplete={() => {}}
|
onSaveComplete={() => {}}
|
||||||
onRejectComplete={() => {}}
|
onRejectComplete={() => {}}
|
||||||
commandsManager={commandsManager}
|
commandsManager={commandsManager}
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export default {
|
|||||||
*
|
*
|
||||||
* @param props {*}
|
* @param props {*}
|
||||||
* @param props.displaySets
|
* @param props.displaySets
|
||||||
* @param props.viewportIndex
|
* @param props.viewportId
|
||||||
* @param props.viewportLabel
|
* @param props.viewportLabel
|
||||||
* @param props.dataSource
|
* @param props.dataSource
|
||||||
* @param props.viewportOptions
|
* @param props.viewportOptions
|
||||||
@ -63,16 +63,16 @@ export default {
|
|||||||
const { viewportOptions } = props;
|
const { viewportOptions } = props;
|
||||||
|
|
||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
const { viewports, activeViewportIndex } = viewportGrid;
|
const { activeViewportId } = viewportGrid;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MicroscopyViewport
|
<MicroscopyViewport
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
extensionManager={extensionManager}
|
extensionManager={extensionManager}
|
||||||
commandsManager={commandsManager}
|
commandsManager={commandsManager}
|
||||||
activeViewportIndex={activeViewportIndex}
|
activeViewportId={activeViewportId}
|
||||||
setViewportActive={(viewportIndex: number) => {
|
setViewportActive={(viewportId: string) => {
|
||||||
viewportGridService.setActiveViewportIndex(viewportIndex);
|
viewportGridService.setActiveViewportId(viewportId);
|
||||||
}}
|
}}
|
||||||
viewportData={viewportOptions}
|
viewportData={viewportOptions}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -286,31 +286,31 @@ export default class MicroscopyService extends PubSubService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a managed viewer instance for the given thrid-party API's viewer.
|
* Creates a managed viewer instance for the given third-party API's viewer.
|
||||||
* Restores existing annotations for the given study/series.
|
* Restores existing annotations for the given study/series.
|
||||||
* Adds event subscriptions for the viewer being added.
|
* Adds event subscriptions for the viewer being added.
|
||||||
* Focuses the selected annotation when the viewer is being loaded into the
|
* Focuses the selected annotation when the viewer is being loaded into the
|
||||||
* active viewport.
|
* active viewport.
|
||||||
*
|
*
|
||||||
* @param {Object} viewer Third-party viewer API's object to be managed
|
* @param viewer - Third-party viewer API's object to be managed
|
||||||
* @param {Number} viewportIndex The index of the viewport to load the viewer
|
* @param viewportId - The viewport Id where the viewer will be loaded
|
||||||
* @param {HTMLElement} container The DOM element where it will be renderd
|
* @param container - The DOM element where it will be rendered
|
||||||
* @param {String} studyInstanceUID The study UID of the loaded image
|
* @param studyInstanceUID - The study UID of the loaded image
|
||||||
* @param {String} seriesInstanceUID The series UID of the loaded image
|
* @param seriesInstanceUID - The series UID of the loaded image
|
||||||
* @param {Array} displaySets All displaySets related to the same StudyInstanceUID
|
* @param displaySets - All displaySets related to the same StudyInstanceUID
|
||||||
*
|
*
|
||||||
* @returns {ViewerManager} managed viewer
|
* @returns {ViewerManager} managed viewer
|
||||||
*/
|
*/
|
||||||
addViewer(
|
addViewer(
|
||||||
viewer,
|
viewer,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
container,
|
container,
|
||||||
studyInstanceUID,
|
studyInstanceUID,
|
||||||
seriesInstanceUID
|
seriesInstanceUID
|
||||||
) {
|
) {
|
||||||
const managedViewer = new ViewerManager(
|
const managedViewer = new ViewerManager(
|
||||||
viewer,
|
viewer,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
container,
|
container,
|
||||||
studyInstanceUID,
|
studyInstanceUID,
|
||||||
seriesInstanceUID
|
seriesInstanceUID
|
||||||
@ -325,7 +325,7 @@ export default class MicroscopyService extends PubSubService {
|
|||||||
|
|
||||||
if (this.pendingFocus) {
|
if (this.pendingFocus) {
|
||||||
this.pendingFocus = false;
|
this.pendingFocus = false;
|
||||||
this.focusAnnotation(this.selectedAnnotation, viewportIndex);
|
this.focusAnnotation(this.selectedAnnotation, viewportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return managedViewer;
|
return managedViewer;
|
||||||
@ -502,13 +502,13 @@ export default class MicroscopyService extends PubSubService {
|
|||||||
/**
|
/**
|
||||||
* Toggles overview map
|
* Toggles overview map
|
||||||
*
|
*
|
||||||
* @param viewportIndex The active viewport index
|
* @param viewportId The active viewport index
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
toggleOverviewMap(viewportIndex) {
|
toggleOverviewMap(viewportId) {
|
||||||
const managedViewers = Array.from(this.managedViewers);
|
const managedViewers = Array.from(this.managedViewers);
|
||||||
const managedViewer = managedViewers.find(
|
const managedViewer = managedViewers.find(
|
||||||
mv => mv.viewportIndex === viewportIndex
|
mv => mv.viewportId === viewportId
|
||||||
);
|
);
|
||||||
if (managedViewer) {
|
if (managedViewer) {
|
||||||
managedViewer.toggleOverviewMap();
|
managedViewer.toggleOverviewMap();
|
||||||
@ -550,10 +550,10 @@ export default class MicroscopyService extends PubSubService {
|
|||||||
* the managed viewer instance is created.
|
* the managed viewer instance is created.
|
||||||
*
|
*
|
||||||
* @param {RoiAnnotation} roiAnnotation RoiAnnotation instance to be focused
|
* @param {RoiAnnotation} roiAnnotation RoiAnnotation instance to be focused
|
||||||
* @param {Number} viewportIndex Index of the viewport to focus
|
* @param {string} viewportId Index of the viewport to focus
|
||||||
*/
|
*/
|
||||||
focusAnnotation(roiAnnotation, viewportIndex) {
|
focusAnnotation(roiAnnotation, viewportId) {
|
||||||
const filter = mv => mv.viewportIndex === viewportIndex;
|
const filter = mv => mv.viewportId === viewportId;
|
||||||
const managedViewer = Array.from(this.managedViewers).find(filter);
|
const managedViewer = Array.from(this.managedViewers).find(filter);
|
||||||
if (managedViewer) {
|
if (managedViewer) {
|
||||||
managedViewer.setViewStateByExtent(roiAnnotation);
|
managedViewer.setViewStateByExtent(roiAnnotation);
|
||||||
|
|||||||
@ -48,14 +48,14 @@ const EVENTS = {
|
|||||||
class ViewerManager extends PubSubService {
|
class ViewerManager extends PubSubService {
|
||||||
constructor(
|
constructor(
|
||||||
viewer,
|
viewer,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
container,
|
container,
|
||||||
studyInstanceUID,
|
studyInstanceUID,
|
||||||
seriesInstanceUID
|
seriesInstanceUID
|
||||||
) {
|
) {
|
||||||
super(EVENTS);
|
super(EVENTS);
|
||||||
this.viewer = viewer;
|
this.viewer = viewer;
|
||||||
this.viewportIndex = viewportIndex;
|
this.viewportId = viewportId;
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.studyInstanceUID = studyInstanceUID;
|
this.studyInstanceUID = studyInstanceUID;
|
||||||
this.seriesInstanceUID = seriesInstanceUID;
|
this.seriesInstanceUID = seriesInstanceUID;
|
||||||
|
|||||||
@ -33,7 +33,7 @@ function TrackedMeasurementsContextProvider(
|
|||||||
const [appConfig] = useAppConfig();
|
const [appConfig] = useAppConfig();
|
||||||
|
|
||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
const { activeViewportIndex, viewports } = viewportGrid;
|
const { activeViewportId, viewports } = viewportGrid;
|
||||||
const { measurementService, displaySetService } = servicesManager.services;
|
const { measurementService, displaySetService } = servicesManager.services;
|
||||||
|
|
||||||
const machineOptions = Object.assign({}, defaultOptions);
|
const machineOptions = Object.assign({}, defaultOptions);
|
||||||
@ -49,7 +49,7 @@ function TrackedMeasurementsContextProvider(
|
|||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'jumping to measurement reset viewport',
|
'jumping to measurement reset viewport',
|
||||||
viewportGrid.activeViewportIndex,
|
viewportGrid.activeViewportId,
|
||||||
trackedMeasurements[0]
|
trackedMeasurements[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ function TrackedMeasurementsContextProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
viewportIndex: viewportGrid.activeViewportIndex,
|
viewportId: viewportGrid.activeViewportId,
|
||||||
displaySetInstanceUIDs: [referencedDisplaySetUID],
|
displaySetInstanceUIDs: [referencedDisplaySetUID],
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
initialImageOptions: {
|
initialImageOptions: {
|
||||||
@ -99,7 +99,7 @@ function TrackedMeasurementsContextProvider(
|
|||||||
evt.data.createdDisplaySetInstanceUIDs[0].displaySetInstanceUID;
|
evt.data.createdDisplaySetInstanceUIDs[0].displaySetInstanceUID;
|
||||||
|
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
viewportIndex: evt.data.viewportIndex,
|
viewportId: evt.data.viewportId,
|
||||||
displaySetInstanceUIDs: [StructuredReportDisplaySetInstanceUID],
|
displaySetInstanceUIDs: [StructuredReportDisplaySetInstanceUID],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -172,16 +172,14 @@ function TrackedMeasurementsContextProvider(
|
|||||||
machineOptions
|
machineOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
const [
|
const [trackedMeasurements, sendTrackedMeasurementsEvent] = useMachine(
|
||||||
trackedMeasurements,
|
measurementTrackingMachine
|
||||||
sendTrackedMeasurementsEvent,
|
);
|
||||||
trackedMeasurementsService,
|
|
||||||
] = useMachine(measurementTrackingMachine);
|
|
||||||
|
|
||||||
// ~~ Listen for changes to ViewportGrid for potential SRs hung in panes when idle
|
// ~~ Listen for changes to ViewportGrid for potential SRs hung in panes when idle
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (viewports.length > 0) {
|
if (viewports.size > 0) {
|
||||||
const activeViewport = viewports[activeViewportIndex];
|
const activeViewport = viewports.get(activeViewportId);
|
||||||
|
|
||||||
if (!activeViewport || !activeViewport?.displaySetInstanceUIDs?.length) {
|
if (!activeViewport || !activeViewport?.displaySetInstanceUIDs?.length) {
|
||||||
return;
|
return;
|
||||||
@ -229,12 +227,12 @@ function TrackedMeasurementsContextProvider(
|
|||||||
sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', {
|
sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', {
|
||||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||||
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||||
viewportIndex: activeViewportIndex,
|
viewportId: activeViewportId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
sendTrackedMeasurementsEvent,
|
sendTrackedMeasurementsEvent,
|
||||||
servicesManager.services,
|
servicesManager.services,
|
||||||
viewports,
|
viewports,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ function hydrateStructuredReport(
|
|||||||
evt
|
evt
|
||||||
) {
|
) {
|
||||||
const { displaySetService } = servicesManager.services;
|
const { displaySetService } = servicesManager.services;
|
||||||
const { viewportIndex, displaySetInstanceUID } = evt;
|
const { viewportId, displaySetInstanceUID } = evt;
|
||||||
const srDisplaySet = displaySetService.getDisplaySetByUID(
|
const srDisplaySet = displaySetService.getDisplaySetByUID(
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
@ -23,7 +23,7 @@ function hydrateStructuredReport(
|
|||||||
resolve({
|
resolve({
|
||||||
displaySetInstanceUID: evt.displaySetInstanceUID,
|
displaySetInstanceUID: evt.displaySetInstanceUID,
|
||||||
srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
|
srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUIDs,
|
SeriesInstanceUIDs,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,24 +10,24 @@ const RESPONSE = {
|
|||||||
|
|
||||||
function promptBeginTracking({ servicesManager, extensionManager }, ctx, evt) {
|
function promptBeginTracking({ servicesManager, extensionManager }, ctx, evt) {
|
||||||
const { uiViewportDialogService } = servicesManager.services;
|
const { uiViewportDialogService } = servicesManager.services;
|
||||||
const { viewportIndex, StudyInstanceUID, SeriesInstanceUID } = evt;
|
const { viewportId, StudyInstanceUID, SeriesInstanceUID } = evt;
|
||||||
|
|
||||||
return new Promise(async function(resolve, reject) {
|
return new Promise(async function(resolve, reject) {
|
||||||
let promptResult = await _askTrackMeasurements(
|
let promptResult = await _askTrackMeasurements(
|
||||||
uiViewportDialogService,
|
uiViewportDialogService,
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
userResponse: promptResult,
|
userResponse: promptResult,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
|
function _askTrackMeasurements(uiViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message = 'Track measurements for this series?';
|
const message = 'Track measurements for this series?';
|
||||||
const actions = [
|
const actions = [
|
||||||
@ -56,7 +56,7 @@ function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uiViewportDialogService.show({
|
uiViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
id: 'measurement-tracking-prompt-begin-tracking',
|
id: 'measurement-tracking-prompt-begin-tracking',
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message,
|
message,
|
||||||
|
|||||||
@ -20,15 +20,15 @@ function promptHydrateStructuredReport(
|
|||||||
uiViewportDialogService,
|
uiViewportDialogService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
const { viewportIndex, displaySetInstanceUID } = evt;
|
const { viewportId, displaySetInstanceUID } = evt;
|
||||||
const srDisplaySet = displaySetService.getDisplaySetByUID(
|
const srDisplaySet = displaySetService.getDisplaySetByUID(
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Promise(async function (resolve, reject) {
|
return new Promise(async function(resolve, reject) {
|
||||||
const promptResult = await _askTrackMeasurements(
|
const promptResult = await _askTrackMeasurements(
|
||||||
uiViewportDialogService,
|
uiViewportDialogService,
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
// Need to do action here... So we can set state...
|
// Need to do action here... So we can set state...
|
||||||
@ -49,15 +49,15 @@ function promptHydrateStructuredReport(
|
|||||||
userResponse: promptResult,
|
userResponse: promptResult,
|
||||||
displaySetInstanceUID: evt.displaySetInstanceUID,
|
displaySetInstanceUID: evt.displaySetInstanceUID,
|
||||||
srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
|
srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUIDs,
|
SeriesInstanceUIDs,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
|
function _askTrackMeasurements(uiViewportDialogService, viewportId) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message =
|
const message =
|
||||||
'Do you want to continue tracking measurements for this study?';
|
'Do you want to continue tracking measurements for this study?';
|
||||||
const actions = [
|
const actions = [
|
||||||
@ -78,7 +78,7 @@ function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uiViewportDialogService.show({
|
uiViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
|
|||||||
@ -13,10 +13,8 @@ function promptSaveReport(
|
|||||||
measurementService,
|
measurementService,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
} = servicesManager.services;
|
} = servicesManager.services;
|
||||||
const viewportIndex =
|
const viewportId =
|
||||||
evt.viewportIndex === undefined
|
evt.viewportId === undefined ? evt.data.viewportId : evt.viewportId;
|
||||||
? evt.data.viewportIndex
|
|
||||||
: evt.viewportIndex;
|
|
||||||
const isBackupSave =
|
const isBackupSave =
|
||||||
evt.isBackupSave === undefined ? evt.data.isBackupSave : evt.isBackupSave;
|
evt.isBackupSave === undefined ? evt.data.isBackupSave : evt.isBackupSave;
|
||||||
const StudyInstanceUID = evt?.data?.StudyInstanceUID;
|
const StudyInstanceUID = evt?.data?.StudyInstanceUID;
|
||||||
@ -66,7 +64,7 @@ function promptSaveReport(
|
|||||||
createdDisplaySetInstanceUIDs: displaySetInstanceUIDs,
|
createdDisplaySetInstanceUIDs: displaySetInstanceUIDs,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
isBackupSave,
|
isBackupSave,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,17 +11,17 @@ const RESPONSE = {
|
|||||||
|
|
||||||
function promptTrackNewSeries({ servicesManager, extensionManager }, ctx, evt) {
|
function promptTrackNewSeries({ servicesManager, extensionManager }, ctx, evt) {
|
||||||
const { UIViewportDialogService } = servicesManager.services;
|
const { UIViewportDialogService } = servicesManager.services;
|
||||||
const { viewportIndex, StudyInstanceUID, SeriesInstanceUID } = evt;
|
const { viewportId, StudyInstanceUID, SeriesInstanceUID } = evt;
|
||||||
|
|
||||||
return new Promise(async function(resolve, reject) {
|
return new Promise(async function(resolve, reject) {
|
||||||
let promptResult = await _askShouldAddMeasurements(
|
let promptResult = await _askShouldAddMeasurements(
|
||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (promptResult === RESPONSE.CREATE_REPORT) {
|
if (promptResult === RESPONSE.CREATE_REPORT) {
|
||||||
promptResult = ctx.isDirty
|
promptResult = ctx.isDirty
|
||||||
? await _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex)
|
? await _askSaveDiscardOrCancel(UIViewportDialogService, viewportId)
|
||||||
: RESPONSE.SET_STUDY_AND_SERIES;
|
: RESPONSE.SET_STUDY_AND_SERIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,13 +29,13 @@ function promptTrackNewSeries({ servicesManager, extensionManager }, ctx, evt) {
|
|||||||
userResponse: promptResult,
|
userResponse: promptResult,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
isBackupSave: false,
|
isBackupSave: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askShouldAddMeasurements(uiViewportDialogService, viewportIndex) {
|
function _askShouldAddMeasurements(uiViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message =
|
const message =
|
||||||
'Do you want to add this measurement to the existing report?';
|
'Do you want to add this measurement to the existing report?';
|
||||||
@ -62,7 +62,7 @@ function _askShouldAddMeasurements(uiViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uiViewportDialogService.show({
|
uiViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
@ -75,7 +75,7 @@ function _askShouldAddMeasurements(uiViewportDialogService, viewportIndex) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) {
|
function _askSaveDiscardOrCancel(UIViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message =
|
const message =
|
||||||
'You have existing tracked measurements. What would you like to do with your existing tracked measurements?';
|
'You have existing tracked measurements. What would you like to do with your existing tracked measurements?';
|
||||||
@ -98,7 +98,7 @@ function _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
UIViewportDialogService.show({
|
UIViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
|
|||||||
@ -9,17 +9,17 @@ const RESPONSE = {
|
|||||||
|
|
||||||
function promptTrackNewStudy({ servicesManager, extensionManager }, ctx, evt) {
|
function promptTrackNewStudy({ servicesManager, extensionManager }, ctx, evt) {
|
||||||
const { UIViewportDialogService } = servicesManager.services;
|
const { UIViewportDialogService } = servicesManager.services;
|
||||||
const { viewportIndex, StudyInstanceUID, SeriesInstanceUID } = evt;
|
const { viewportId, StudyInstanceUID, SeriesInstanceUID } = evt;
|
||||||
|
|
||||||
return new Promise(async function(resolve, reject) {
|
return new Promise(async function(resolve, reject) {
|
||||||
let promptResult = await _askTrackMeasurements(
|
let promptResult = await _askTrackMeasurements(
|
||||||
UIViewportDialogService,
|
UIViewportDialogService,
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (promptResult === RESPONSE.SET_STUDY_AND_SERIES) {
|
if (promptResult === RESPONSE.SET_STUDY_AND_SERIES) {
|
||||||
promptResult = ctx.isDirty
|
promptResult = ctx.isDirty
|
||||||
? await _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex)
|
? await _askSaveDiscardOrCancel(UIViewportDialogService, viewportId)
|
||||||
: RESPONSE.SET_STUDY_AND_SERIES;
|
: RESPONSE.SET_STUDY_AND_SERIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,13 +27,13 @@ function promptTrackNewStudy({ servicesManager, extensionManager }, ctx, evt) {
|
|||||||
userResponse: promptResult,
|
userResponse: promptResult,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
isBackupSave: false,
|
isBackupSave: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askTrackMeasurements(UIViewportDialogService, viewportIndex) {
|
function _askTrackMeasurements(UIViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message = 'Track measurements for this series?';
|
const message = 'Track measurements for this series?';
|
||||||
const actions = [
|
const actions = [
|
||||||
@ -55,7 +55,7 @@ function _askTrackMeasurements(UIViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
UIViewportDialogService.show({
|
UIViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
@ -68,7 +68,7 @@ function _askTrackMeasurements(UIViewportDialogService, viewportIndex) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) {
|
function _askSaveDiscardOrCancel(UIViewportDialogService, viewportId) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
const message =
|
const message =
|
||||||
'Measurements cannot span across multiple studies. Do you want to save your tracked measurements?';
|
'Measurements cannot span across multiple studies. Do you want to save your tracked measurements?';
|
||||||
@ -91,7 +91,7 @@ function _askSaveDiscardOrCancel(UIViewportDialogService, viewportIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
UIViewportDialogService.show({
|
UIViewportDialogService.show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message,
|
message,
|
||||||
actions,
|
actions,
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const DISPLAY_STUDY_SUMMARY_INITIAL_VALUE = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid] = useViewportGrid();
|
||||||
const [measurementChangeTimestamp, setMeasurementsUpdated] = useState(
|
const [measurementChangeTimestamp, setMeasurementsUpdated] = useState(
|
||||||
Date.now().toString()
|
Date.now().toString()
|
||||||
);
|
);
|
||||||
@ -157,7 +157,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const jumpToImage = ({ uid, isActive }) => {
|
const jumpToImage = ({ uid, isActive }) => {
|
||||||
measurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, uid);
|
measurementService.jumpToMeasurement(viewportGrid.activeViewportId, uid);
|
||||||
|
|
||||||
onMeasurementItemClickHandler({ uid, isActive });
|
onMeasurementItemClickHandler({ uid, isActive });
|
||||||
};
|
};
|
||||||
@ -280,7 +280,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
|||||||
onExportClick={exportReport}
|
onExportClick={exportReport}
|
||||||
onCreateReportClick={() => {
|
onCreateReportClick={() => {
|
||||||
sendTrackedMeasurementsEvent('SAVE_REPORT', {
|
sendTrackedMeasurementsEvent('SAVE_REPORT', {
|
||||||
viewportIndex: viewportGrid.activeViewportIndex,
|
viewportId: viewportGrid.activeViewportId,
|
||||||
isBackupSave: true,
|
isBackupSave: true,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ function PanelStudyBrowserTracking({
|
|||||||
// Tabs --> Studies --> DisplaySets --> Thumbnails
|
// Tabs --> Studies --> DisplaySets --> Thumbnails
|
||||||
const { StudyInstanceUIDs } = useImageViewer();
|
const { StudyInstanceUIDs } = useImageViewer();
|
||||||
const [
|
const [
|
||||||
{ activeViewportIndex, viewports },
|
{ activeViewportId, viewports },
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
] = useViewportGrid();
|
] = useViewportGrid();
|
||||||
const [
|
const [
|
||||||
@ -58,10 +58,10 @@ function PanelStudyBrowserTracking({
|
|||||||
|
|
||||||
const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
|
const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
|
||||||
let updatedViewports = [];
|
let updatedViewports = [];
|
||||||
const viewportIndex = activeViewportIndex;
|
const viewportId = activeViewportId;
|
||||||
try {
|
try {
|
||||||
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -78,8 +78,8 @@ function PanelStudyBrowserTracking({
|
|||||||
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeViewportDisplaySetInstanceUIDs =
|
const activeViewportDisplaySetInstanceUIDs = viewports.get(activeViewportId)
|
||||||
viewports[activeViewportIndex]?.displaySetInstanceUIDs;
|
?.displaySetInstanceUIDs;
|
||||||
|
|
||||||
const { trackedSeries } = trackedMeasurements.context;
|
const { trackedSeries } = trackedMeasurements.context;
|
||||||
|
|
||||||
@ -447,20 +447,19 @@ function _mapDisplaySets(
|
|||||||
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
||||||
const componentType = _getComponentType(ds);
|
const componentType = _getComponentType(ds);
|
||||||
const numPanes = viewportGridService.getNumViewportPanes();
|
const numPanes = viewportGridService.getNumViewportPanes();
|
||||||
const viewportIdentificator =
|
const viewportIdentificator = [];
|
||||||
numPanes === 1
|
|
||||||
? []
|
if (numPanes !== 1) {
|
||||||
: viewports.reduce((acc, viewportData, index) => {
|
viewports.forEach(viewportData => {
|
||||||
if (
|
if (
|
||||||
index < numPanes &&
|
|
||||||
viewportData?.displaySetInstanceUIDs?.includes(
|
viewportData?.displaySetInstanceUIDs?.includes(
|
||||||
ds.displaySetInstanceUID
|
ds.displaySetInstanceUID
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
acc.push(viewportData.viewportLabel);
|
viewportIdentificator.push(viewportData.viewportLabel);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const array =
|
const array =
|
||||||
componentType === 'thumbnailTracked'
|
componentType === 'thumbnailTracked'
|
||||||
|
|||||||
@ -15,11 +15,10 @@ const { formatDate } = utils;
|
|||||||
function TrackedCornerstoneViewport(props) {
|
function TrackedCornerstoneViewport(props) {
|
||||||
const {
|
const {
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
viewportLabel,
|
viewportLabel,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
viewportOptions,
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { t } = useTranslation('Common');
|
const { t } = useTranslation('Common');
|
||||||
@ -43,7 +42,6 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
const [viewportElem, setViewportElem] = useState(null);
|
const [viewportElem, setViewportElem] = useState(null);
|
||||||
|
|
||||||
const { trackedSeries } = trackedMeasurements.context;
|
const { trackedSeries } = trackedMeasurements.context;
|
||||||
const viewportId = viewportOptions.viewportId;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SeriesDate,
|
SeriesDate,
|
||||||
@ -64,8 +62,8 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
} = displaySet.images[0];
|
} = displaySet.images[0];
|
||||||
|
|
||||||
const updateIsTracked = useCallback(() => {
|
const updateIsTracked = useCallback(() => {
|
||||||
const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(
|
||||||
viewportIndex
|
viewportId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (viewport instanceof BaseVolumeViewport) {
|
if (viewport instanceof BaseVolumeViewport) {
|
||||||
@ -84,7 +82,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
if (trackedSeries.includes(SeriesInstanceUID) !== isTracked) {
|
if (trackedSeries.includes(SeriesInstanceUID) !== isTracked) {
|
||||||
setIsTracked(!isTracked);
|
setIsTracked(!isTracked);
|
||||||
}
|
}
|
||||||
}, [isTracked, trackedMeasurements, viewportIndex, SeriesInstanceUID]);
|
}, [isTracked, trackedMeasurements, viewportId, SeriesInstanceUID]);
|
||||||
|
|
||||||
const onElementEnabled = useCallback(
|
const onElementEnabled = useCallback(
|
||||||
evt => {
|
evt => {
|
||||||
@ -113,7 +111,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
const { unsubscribe } = cornerstoneViewportService.subscribe(
|
const { unsubscribe } = cornerstoneViewportService.subscribe(
|
||||||
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
||||||
props => {
|
props => {
|
||||||
if (props.viewportIndex !== viewportIndex) {
|
if (props.viewportId !== viewportId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +122,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [updateIsTracked, viewportIndex]);
|
}, [updateIsTracked, viewportId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isTracked) {
|
if (isTracked) {
|
||||||
@ -169,12 +167,12 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
[added, addedRaw].forEach(evt => {
|
[added, addedRaw].forEach(evt => {
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
measurementService.subscribe(evt, ({ source, measurement }) => {
|
measurementService.subscribe(evt, ({ source, measurement }) => {
|
||||||
const { activeViewportIndex } = viewportGridService.getState();
|
const { activeViewportId } = viewportGridService.getState();
|
||||||
|
|
||||||
// Each TrackedCornerstoneViewport receives the MeasurementService's events.
|
// Each TrackedCornerstoneViewport receives the MeasurementService's events.
|
||||||
// Only send the tracked measurements event for the active viewport to avoid
|
// Only send the tracked measurements event for the active viewport to avoid
|
||||||
// sending it more than once.
|
// sending it more than once.
|
||||||
if (viewportIndex === activeViewportIndex) {
|
if (viewportId === activeViewportId) {
|
||||||
const {
|
const {
|
||||||
referenceStudyUID: StudyInstanceUID,
|
referenceStudyUID: StudyInstanceUID,
|
||||||
referenceSeriesUID: SeriesInstanceUID,
|
referenceSeriesUID: SeriesInstanceUID,
|
||||||
@ -182,7 +180,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
|
|
||||||
sendTrackedMeasurementsEvent('SET_DIRTY', { SeriesInstanceUID });
|
sendTrackedMeasurementsEvent('SET_DIRTY', { SeriesInstanceUID });
|
||||||
sendTrackedMeasurementsEvent('TRACK_SERIES', {
|
sendTrackedMeasurementsEvent('TRACK_SERIES', {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
});
|
});
|
||||||
@ -199,7 +197,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
}, [
|
}, [
|
||||||
measurementService,
|
measurementService,
|
||||||
sendTrackedMeasurementsEvent,
|
sendTrackedMeasurementsEvent,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -217,10 +215,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
|
|
||||||
setTrackedMeasurementUID(newTrackedMeasurementUID);
|
setTrackedMeasurementUID(newTrackedMeasurementUID);
|
||||||
|
|
||||||
measurementService.jumpToMeasurement(
|
measurementService.jumpToMeasurement(viewportId, newTrackedMeasurementUID);
|
||||||
viewportIndex,
|
|
||||||
newTrackedMeasurementUID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCornerstoneViewport = () => {
|
const getCornerstoneViewport = () => {
|
||||||
@ -280,7 +275,7 @@ function TrackedCornerstoneViewport(props) {
|
|||||||
|
|
||||||
TrackedCornerstoneViewport.propTypes = {
|
TrackedCornerstoneViewport.propTypes = {
|
||||||
displaySets: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
|
displaySets: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
viewportId: PropTypes.string.isRequired,
|
||||||
dataSource: PropTypes.object,
|
dataSource: PropTypes.object,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
customProps: PropTypes.object,
|
customProps: PropTypes.object,
|
||||||
@ -299,10 +294,10 @@ function _getNextMeasurementUID(
|
|||||||
const { measurementService, viewportGridService } = servicesManager.services;
|
const { measurementService, viewportGridService } = servicesManager.services;
|
||||||
const measurements = measurementService.getMeasurements();
|
const measurements = measurementService.getMeasurements();
|
||||||
|
|
||||||
const { activeViewportIndex, viewports } = viewportGridService.getState();
|
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||||
const {
|
const {
|
||||||
displaySetInstanceUIDs: activeViewportDisplaySetInstanceUIDs,
|
displaySetInstanceUIDs: activeViewportDisplaySetInstanceUIDs,
|
||||||
} = viewports[activeViewportIndex];
|
} = viewports.get(activeViewportId);
|
||||||
|
|
||||||
const { trackedSeries } = trackedMeasurements.context;
|
const { trackedSeries } = trackedMeasurements.context;
|
||||||
|
|
||||||
|
|||||||
246
extensions/test-extension/src/hpTestSwitch.ts
Normal file
246
extensions/test-extension/src/hpTestSwitch.ts
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
import { Types } from '@ohif/core';
|
||||||
|
|
||||||
|
const viewport0a = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportA',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport1b = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportB',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 1,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport2c = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportC',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 2,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport3d = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportD',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 3,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport4e = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportE',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 4,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport5f = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportF',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 5,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport3a = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportA',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 3,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport2b = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportB',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 2,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewport1c = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportC',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 1,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const viewport0d = {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportId: 'viewportD',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
matchedDisplaySetsIndex: 0,
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewportStructure = {
|
||||||
|
layoutType: 'grid',
|
||||||
|
properties: {
|
||||||
|
rows: 2,
|
||||||
|
columns: 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewportStructure32 = {
|
||||||
|
layoutType: 'grid',
|
||||||
|
properties: {
|
||||||
|
rows: 2,
|
||||||
|
columns: 3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This hanging protocol is a test hanging protocol used to apply various
|
||||||
|
* layouts in different positions for display, re-using earlier names in
|
||||||
|
* various orders.
|
||||||
|
*/
|
||||||
|
const hpTestSwitch: Types.HangingProtocol.Protocol = {
|
||||||
|
hasUpdatedPriorsInformation: false,
|
||||||
|
id: '@ohif/mnTestSwitch',
|
||||||
|
description: 'Has various hanging protocol grid layouts',
|
||||||
|
name: 'Test Switch',
|
||||||
|
protocolMatchingRules: [
|
||||||
|
{
|
||||||
|
id: 'OneOrMoreSeries',
|
||||||
|
weight: 25,
|
||||||
|
attribute: 'numberOfDisplaySetsWithImages',
|
||||||
|
constraint: {
|
||||||
|
greaterThan: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toolGroupIds: ['default'],
|
||||||
|
displaySetSelectors: {
|
||||||
|
defaultDisplaySetId: {
|
||||||
|
seriesMatchingRules: [
|
||||||
|
{
|
||||||
|
attribute: 'numImageFrames',
|
||||||
|
constraint: {
|
||||||
|
greaterThan: { value: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// This display set will select the specified items by preference
|
||||||
|
// It has no affect if nothing is specified in the URL.
|
||||||
|
{
|
||||||
|
attribute: 'isDisplaySetFromUrl',
|
||||||
|
weight: 10,
|
||||||
|
constraint: {
|
||||||
|
equals: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultViewport: {
|
||||||
|
viewportOptions: {
|
||||||
|
viewportType: 'stack',
|
||||||
|
toolGroupId: 'default',
|
||||||
|
allowUnmatchedView: true,
|
||||||
|
},
|
||||||
|
displaySets: [
|
||||||
|
{
|
||||||
|
id: 'defaultDisplaySetId',
|
||||||
|
matchedDisplaySetsIndex: -1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
stages: [
|
||||||
|
{
|
||||||
|
name: '2x2 0a1b2c3d',
|
||||||
|
viewportStructure,
|
||||||
|
viewports: [viewport0a, viewport1b, viewport2c, viewport3d],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '3x2 0a1b4e2c3d5f',
|
||||||
|
viewportStructure: viewportStructure32,
|
||||||
|
// Note the following structure simply preserves the viewportId for
|
||||||
|
// a given screen position
|
||||||
|
viewports: [
|
||||||
|
viewport0a,
|
||||||
|
viewport1b,
|
||||||
|
viewport4e,
|
||||||
|
viewport2c,
|
||||||
|
viewport3d,
|
||||||
|
viewport5f,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '2x2 1c0d3a2b',
|
||||||
|
viewportStructure,
|
||||||
|
viewports: [viewport1c, viewport0d, viewport3a, viewport2b],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '2x2 3a2b1c0d',
|
||||||
|
viewportStructure,
|
||||||
|
viewports: [viewport3a, viewport2b, viewport1c, viewport0d],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
numberOfPriorsReferenced: -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default hpTestSwitch;
|
||||||
@ -2,6 +2,8 @@ import { Types } from '@ohif/core';
|
|||||||
|
|
||||||
import { id } from './id';
|
import { id } from './id';
|
||||||
|
|
||||||
|
import hpTestSwitch from './hpTestSwitch';
|
||||||
|
|
||||||
import getCustomizationModule from './getCustomizationModule';
|
import getCustomizationModule from './getCustomizationModule';
|
||||||
// import {setViewportZoomPan, storeViewportZoomPan } from './custom-viewport/setViewportZoomPan';
|
// import {setViewportZoomPan, storeViewportZoomPan } from './custom-viewport/setViewportZoomPan';
|
||||||
import sameAs from './custom-attribute/sameAs';
|
import sameAs from './custom-attribute/sameAs';
|
||||||
@ -47,6 +49,16 @@ const testExtension: Types.Extensions.Extension = {
|
|||||||
|
|
||||||
/** Registers some customizations */
|
/** Registers some customizations */
|
||||||
getCustomizationModule,
|
getCustomizationModule,
|
||||||
|
|
||||||
|
getHangingProtocolModule: () => {
|
||||||
|
return [
|
||||||
|
// Create a MxN hanging protocol available by default
|
||||||
|
{
|
||||||
|
name: hpTestSwitch.id,
|
||||||
|
protocol: hpTestSwitch,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default testExtension;
|
export default testExtension;
|
||||||
|
|||||||
@ -36,8 +36,8 @@ const commandsModule = ({
|
|||||||
const { getEnabledElement } = utilityModule.exports;
|
const { getEnabledElement } = utilityModule.exports;
|
||||||
|
|
||||||
function _getActiveViewportsEnabledElement() {
|
function _getActiveViewportsEnabledElement() {
|
||||||
const { activeViewportIndex } = viewportGridService.getState();
|
const { activeViewportId } = viewportGridService.getState();
|
||||||
const { element } = getEnabledElement(activeViewportIndex) || {};
|
const { element } = getEnabledElement(activeViewportId) || {};
|
||||||
const enabledElement = cs.getEnabledElement(element);
|
const enabledElement = cs.getEnabledElement(element);
|
||||||
return enabledElement;
|
return enabledElement;
|
||||||
}
|
}
|
||||||
@ -45,8 +45,8 @@ const commandsModule = ({
|
|||||||
function _getMatchedViewportsToolGroupIds() {
|
function _getMatchedViewportsToolGroupIds() {
|
||||||
const { viewportMatchDetails } = hangingProtocolService.getMatchDetails();
|
const { viewportMatchDetails } = hangingProtocolService.getMatchDetails();
|
||||||
const toolGroupIds = [];
|
const toolGroupIds = [];
|
||||||
viewportMatchDetails.forEach((value, key) => {
|
viewportMatchDetails.forEach(viewport => {
|
||||||
const { viewportOptions } = value;
|
const { viewportOptions } = viewport;
|
||||||
const { toolGroupId } = viewportOptions;
|
const { toolGroupId } = viewportOptions;
|
||||||
if (toolGroupIds.indexOf(toolGroupId) === -1) {
|
if (toolGroupIds.indexOf(toolGroupId) === -1) {
|
||||||
toolGroupIds.push(toolGroupId);
|
toolGroupIds.push(toolGroupId);
|
||||||
@ -64,7 +64,7 @@ const commandsModule = ({
|
|||||||
// corrected PT vs the non-attenuation correct PT)
|
// corrected PT vs the non-attenuation correct PT)
|
||||||
|
|
||||||
let ptDisplaySet = null;
|
let ptDisplaySet = null;
|
||||||
for (const [viewportIndex, viewportDetails] of viewportMatchDetails) {
|
for (const [viewportId, viewportDetails] of viewportMatchDetails) {
|
||||||
const { displaySetsInfo } = viewportDetails;
|
const { displaySetsInfo } = viewportDetails;
|
||||||
const displaySets = displaySetsInfo.map(({ displaySetInstanceUID }) =>
|
const displaySets = displaySetsInfo.map(({ displaySetInstanceUID }) =>
|
||||||
displaySetService.getDisplaySetByUID(displaySetInstanceUID)
|
displaySetService.getDisplaySetByUID(displaySetInstanceUID)
|
||||||
@ -137,7 +137,6 @@ const commandsModule = ({
|
|||||||
|
|
||||||
// Add Segmentation to all toolGroupIds in the viewer
|
// Add Segmentation to all toolGroupIds in the viewer
|
||||||
const toolGroupIds = _getMatchedViewportsToolGroupIds();
|
const toolGroupIds = _getMatchedViewportsToolGroupIds();
|
||||||
|
|
||||||
const representationType = LABELMAP;
|
const representationType = LABELMAP;
|
||||||
|
|
||||||
for (const toolGroupId of toolGroupIds) {
|
for (const toolGroupId of toolGroupIds) {
|
||||||
@ -545,13 +544,8 @@ const commandsModule = ({
|
|||||||
|
|
||||||
let viewports = [];
|
let viewports = [];
|
||||||
fusionViewportIds.forEach(viewportId => {
|
fusionViewportIds.forEach(viewportId => {
|
||||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
|
||||||
viewportId
|
|
||||||
);
|
|
||||||
|
|
||||||
const viewportIndex = viewportInfo.getViewportIndex();
|
|
||||||
commandsManager.runCommand('setViewportColormap', {
|
commandsManager.runCommand('setViewportColormap', {
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID: ptDisplaySet.displaySetInstanceUID,
|
displaySetInstanceUID: ptDisplaySet.displaySetInstanceUID,
|
||||||
colormap: {
|
colormap: {
|
||||||
name: colormap,
|
name: colormap,
|
||||||
|
|||||||
@ -114,6 +114,9 @@ const ptAXIAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: true,
|
source: true,
|
||||||
target: false,
|
target: false,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -155,6 +158,9 @@ const ptSAGITTAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: true,
|
source: true,
|
||||||
target: false,
|
target: false,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -196,6 +202,9 @@ const ptCORONAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: true,
|
source: true,
|
||||||
target: false,
|
target: false,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -246,6 +255,9 @@ const fusionAXIAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: false,
|
source: false,
|
||||||
target: true,
|
target: true,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -306,6 +318,9 @@ const fusionSAGITTAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: false,
|
source: false,
|
||||||
target: true,
|
target: true,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -366,6 +381,9 @@ const fusionCORONAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: false,
|
source: false,
|
||||||
target: true,
|
target: true,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -411,6 +429,9 @@ const mipSAGITTAL = {
|
|||||||
id: 'ptFusionWLSync',
|
id: 'ptFusionWLSync',
|
||||||
source: true,
|
source: true,
|
||||||
target: false,
|
target: false,
|
||||||
|
options: {
|
||||||
|
syncInvertState: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@ -120,7 +120,7 @@
|
|||||||
"postcss-import": "^14.0.2",
|
"postcss-import": "^14.0.2",
|
||||||
"postcss-loader": "^6.1.1",
|
"postcss-loader": "^6.1.1",
|
||||||
"postcss-preset-env": "^7.4.3",
|
"postcss-preset-env": "^7.4.3",
|
||||||
"prettier": "^1.18.2",
|
"prettier": "^3.0.3",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
"semver": "^7.5.1",
|
"semver": "^7.5.1",
|
||||||
"serve": "^14.2.0",
|
"serve": "^14.2.0",
|
||||||
|
|||||||
@ -16,12 +16,18 @@ describe('OHIF Double Click', () => {
|
|||||||
.should('be.eq', numExpectedViewports);
|
.should('be.eq', numExpectedViewports);
|
||||||
|
|
||||||
for (let i = 0; i < numExpectedViewports; i += 1) {
|
for (let i = 0; i < numExpectedViewports; i += 1) {
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
// For whatever reason, with Cypress tests, we have to activate the
|
// For whatever reason, with Cypress tests, we have to activate the
|
||||||
// viewport we are double clicking first.
|
// viewport we are double clicking first.
|
||||||
cy.get('[data-cy="viewport-pane"]')
|
cy.get('[data-cy="viewport-pane"]')
|
||||||
.eq(i)
|
.eq(i)
|
||||||
.trigger('mousedown', 'center', { force: true })
|
.trigger('mousedown', 'center', {
|
||||||
.trigger('mouseup', 'center', { force: true });
|
force: true,
|
||||||
|
})
|
||||||
|
.trigger('mouseup', 'center', {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Wait for the viewport to be 'active'.
|
// Wait for the viewport to be 'active'.
|
||||||
// TODO Is there a better way to do this?
|
// TODO Is there a better way to do this?
|
||||||
@ -41,12 +47,16 @@ describe('OHIF Double Click', () => {
|
|||||||
.should('be.eq', 1);
|
.should('be.eq', 1);
|
||||||
|
|
||||||
cy.get('[data-cy="viewport-pane"]')
|
cy.get('[data-cy="viewport-pane"]')
|
||||||
.eq(0)
|
.trigger('mousedown', 'center', {
|
||||||
.trigger('dblclick', 'center');
|
force: true,
|
||||||
|
})
|
||||||
|
.trigger('mouseup', 'center', {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
|
||||||
cy.get('[data-cy="viewport-pane"]')
|
cy.get('[data-cy="viewport-pane"]')
|
||||||
.its('length')
|
.eq(0)
|
||||||
.should('be.eq', numExpectedViewports);
|
.trigger('dblclick', 'center');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -66,9 +66,10 @@ describe('OHIF MPR', () => {
|
|||||||
.then(cornerstone => {
|
.then(cornerstone => {
|
||||||
const viewports = cornerstone.getRenderingEngines()[0].getViewports();
|
const viewports = cornerstone.getRenderingEngines()[0].getViewports();
|
||||||
|
|
||||||
const imageData1 = viewports[0].getImageData();
|
// The stack viewport still exists after the changes to viewportId and inde
|
||||||
const imageData2 = viewports[1].getImageData();
|
const imageData1 = viewports[1].getImageData();
|
||||||
const imageData3 = viewports[2].getImageData();
|
const imageData2 = viewports[2].getImageData();
|
||||||
|
const imageData3 = viewports[3].getImageData();
|
||||||
|
|
||||||
// for some reason map doesn't work here
|
// for some reason map doesn't work here
|
||||||
cy.wrap(imageData1).should('not.be', undefined);
|
cy.wrap(imageData1).should('not.be', undefined);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ function ViewerViewportGrid(props) {
|
|||||||
const { servicesManager, viewportComponents, dataSource } = props;
|
const { servicesManager, viewportComponents, dataSource } = props;
|
||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
|
|
||||||
const { layout, activeViewportIndex, viewports } = viewportGrid;
|
const { layout, activeViewportId, viewports } = viewportGrid;
|
||||||
const { numCols, numRows } = layout;
|
const { numCols, numRows } = layout;
|
||||||
|
|
||||||
// TODO -> Need some way of selecting which displaySets hit the viewports.
|
// TODO -> Need some way of selecting which displaySets hit the viewports.
|
||||||
@ -51,10 +51,11 @@ function ViewerViewportGrid(props) {
|
|||||||
* specify the viewport match details, which specifies the size and
|
* specify the viewport match details, which specifies the size and
|
||||||
* setup of the various viewports.
|
* setup of the various viewports.
|
||||||
*/
|
*/
|
||||||
const findOrCreateViewport = viewportIndex => {
|
const findOrCreateViewport = pos => {
|
||||||
const details = viewportMatchDetails.get(viewportIndex);
|
const viewportId = Array.from(viewportMatchDetails.keys())[pos];
|
||||||
|
const details = viewportMatchDetails.get(viewportId);
|
||||||
if (!details) {
|
if (!details) {
|
||||||
console.log('No match details for viewport', viewportIndex);
|
console.log('No match details for viewport', viewportId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,11 +100,11 @@ function ViewerViewportGrid(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const _getUpdatedViewports = useCallback(
|
const _getUpdatedViewports = useCallback(
|
||||||
(viewportIndex, displaySetInstanceUID) => {
|
(viewportId, displaySetInstanceUID) => {
|
||||||
let updatedViewports = [];
|
let updatedViewports = [];
|
||||||
try {
|
try {
|
||||||
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -144,7 +145,7 @@ function ViewerViewportGrid(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { unsubscribe } = measurementService.subscribe(
|
const { unsubscribe } = measurementService.subscribe(
|
||||||
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_LAYOUT,
|
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_LAYOUT,
|
||||||
({ viewportIndex, measurement, isConsumed }) => {
|
({ viewportId, measurement, isConsumed }) => {
|
||||||
if (isConsumed) {
|
if (isConsumed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -155,7 +156,7 @@ function ViewerViewportGrid(props) {
|
|||||||
measurement;
|
measurement;
|
||||||
|
|
||||||
const updatedViewports = _getUpdatedViewports(
|
const updatedViewports = _getUpdatedViewports(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
referencedDisplaySetInstanceUID
|
referencedDisplaySetInstanceUID
|
||||||
);
|
);
|
||||||
// Arbitrarily assign the viewport to element 0
|
// Arbitrarily assign the viewport to element 0
|
||||||
@ -197,7 +198,7 @@ function ViewerViewportGrid(props) {
|
|||||||
}, [viewports]);
|
}, [viewports]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
const onDoubleClick = viewportIndex => {
|
const onDoubleClick = viewportId => {
|
||||||
// TODO -> Disabled for now.
|
// TODO -> Disabled for now.
|
||||||
// onNewImage on a cornerstone viewport is firing setDisplaySetsForViewport.
|
// onNewImage on a cornerstone viewport is firing setDisplaySetsForViewport.
|
||||||
// Which it really really shouldn't. We need a larger fix for jump to
|
// Which it really really shouldn't. We need a larger fix for jump to
|
||||||
@ -206,7 +207,7 @@ function ViewerViewportGrid(props) {
|
|||||||
viewportGridService.set({
|
viewportGridService.set({
|
||||||
numCols: cachedLayout.numCols,
|
numCols: cachedLayout.numCols,
|
||||||
numRows: cachedLayout.numRows,
|
numRows: cachedLayout.numRows,
|
||||||
activeViewportIndex: cachedLayout.activeViewportIndex,
|
activeViewportId: cachedLayout.activeViewportId,
|
||||||
viewports: cachedLayout.viewports,
|
viewports: cachedLayout.viewports,
|
||||||
cachedLayout: null,
|
cachedLayout: null,
|
||||||
});
|
});
|
||||||
@ -223,10 +224,10 @@ function ViewerViewportGrid(props) {
|
|||||||
viewportGridService.set({
|
viewportGridService.set({
|
||||||
numCols: 1,
|
numCols: 1,
|
||||||
numRows: 1,
|
numRows: 1,
|
||||||
activeViewportIndex: 0,
|
activeViewportId: 0,
|
||||||
viewports: [
|
viewports: [
|
||||||
{
|
{
|
||||||
displaySetInstanceUID: viewports[viewportIndex].displaySetInstanceUID,
|
displaySetInstanceUID: viewports[viewportId].displaySetInstanceUID,
|
||||||
imageIndex: undefined,
|
imageIndex: undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -234,15 +235,15 @@ function ViewerViewportGrid(props) {
|
|||||||
numCols,
|
numCols,
|
||||||
numRows,
|
numRows,
|
||||||
viewports: cachedViewports,
|
viewports: cachedViewports,
|
||||||
activeViewportIndex: viewportIndex,
|
activeViewportId: viewportId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const onDropHandler = (viewportIndex, { displaySetInstanceUID }) => {
|
const onDropHandler = (viewportId, { displaySetInstanceUID }) => {
|
||||||
const updatedViewports = _getUpdatedViewports(
|
const updatedViewports = _getUpdatedViewports(
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
||||||
@ -253,13 +254,7 @@ function ViewerViewportGrid(props) {
|
|||||||
|
|
||||||
const numViewportPanes = viewportGridService.getNumViewportPanes();
|
const numViewportPanes = viewportGridService.getNumViewportPanes();
|
||||||
for (let i = 0; i < numViewportPanes; i++) {
|
for (let i = 0; i < numViewportPanes; i++) {
|
||||||
const viewportIndex = i;
|
const paneMetadata = Array.from(viewports.values())[i] || {};
|
||||||
const isActive = activeViewportIndex === viewportIndex;
|
|
||||||
const paneMetadata = viewports[i] || {};
|
|
||||||
const viewportId = paneMetadata.viewportId || `viewport-${i}`;
|
|
||||||
if (!paneMetadata.viewportId) {
|
|
||||||
paneMetadata.viewportId = viewportId;
|
|
||||||
}
|
|
||||||
const {
|
const {
|
||||||
displaySetInstanceUIDs,
|
displaySetInstanceUIDs,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
@ -271,9 +266,13 @@ function ViewerViewportGrid(props) {
|
|||||||
viewportLabel,
|
viewportLabel,
|
||||||
} = paneMetadata;
|
} = paneMetadata;
|
||||||
|
|
||||||
|
const viewportId = viewportOptions.viewportId
|
||||||
|
const isActive = activeViewportId === viewportId;
|
||||||
|
|
||||||
|
|
||||||
const displaySetInstanceUIDsToUse = displaySetInstanceUIDs || [];
|
const displaySetInstanceUIDsToUse = displaySetInstanceUIDs || [];
|
||||||
|
|
||||||
// This is causing the viewport components re-render when the activeViewportIndex changes
|
// This is causing the viewport components re-render when the activeViewportId changes
|
||||||
const displaySets = displaySetInstanceUIDsToUse.map(
|
const displaySets = displaySetInstanceUIDsToUse.map(
|
||||||
displaySetInstanceUID => {
|
displaySetInstanceUID => {
|
||||||
return (
|
return (
|
||||||
@ -307,17 +306,23 @@ function ViewerViewportGrid(props) {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
viewportGridService.setActiveViewportIndex(viewportIndex);
|
viewportGridService.setActiveViewportId(viewportId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEMP -> Double click disabled for now
|
|
||||||
// onDoubleClick={() => onDoubleClick(viewportIndex)}
|
|
||||||
|
|
||||||
viewportPanes[i] = (
|
viewportPanes[i] = (
|
||||||
<ViewportPane
|
<ViewportPane
|
||||||
|
// Note: It is highly important that the key is the viewportId here,
|
||||||
|
// since it is used to determine if the component should be re-rendered
|
||||||
|
// by React, and also in the hanging protocol and stage changes if the
|
||||||
|
// same viewportId is used, React, by default, will only move (not re-render)
|
||||||
|
// those components. For instance, if we have a 2x3 layout, and we move
|
||||||
|
// from 2x3 to 1x1 (second viewport), if the key is the viewportIndex,
|
||||||
|
// React will RE-RENDER the resulting viewport as the key will be different.
|
||||||
|
// however, if the key is the viewportId, React will only move the component
|
||||||
|
// and not re-render it.
|
||||||
key={viewportId}
|
key={viewportId}
|
||||||
acceptDropsFor="displayset"
|
acceptDropsFor="displayset"
|
||||||
onDrop={onDropHandler.bind(null, viewportIndex)}
|
onDrop={onDropHandler.bind(null, viewportId)}
|
||||||
onInteraction={onInteractionHandler}
|
onInteraction={onInteractionHandler}
|
||||||
customStyle={{
|
customStyle={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -336,8 +341,8 @@ function ViewerViewportGrid(props) {
|
|||||||
>
|
>
|
||||||
<ViewportComponent
|
<ViewportComponent
|
||||||
displaySets={displaySets}
|
displaySets={displaySets}
|
||||||
viewportIndex={viewportIndex}
|
viewportLabel={viewports.size > 1 ? viewportLabel : ''}
|
||||||
viewportLabel={viewports.length > 1 ? viewportLabel : ''}
|
viewportId={viewportId}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
viewportOptions={viewportOptions}
|
viewportOptions={viewportOptions}
|
||||||
displaySetOptions={displaySetOptions}
|
displaySetOptions={displaySetOptions}
|
||||||
@ -349,7 +354,7 @@ function ViewerViewportGrid(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return viewportPanes;
|
return viewportPanes;
|
||||||
}, [viewports, activeViewportIndex, viewportComponents, dataSource]);
|
}, [viewports, activeViewportId, viewportComponents, dataSource]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading indicator until numCols and numRows are gotten from the HangingProtocolService
|
* Loading indicator until numCols and numRows are gotten from the HangingProtocolService
|
||||||
|
|||||||
@ -74,13 +74,14 @@ const match = (
|
|||||||
errorMessages = ['Something went wrong during validation.', e];
|
errorMessages = ['Something went wrong during validation.', e];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
// TODO: move to a logger
|
||||||
'Test',
|
// console.log(
|
||||||
`${from}.${attribute}`,
|
// 'Test',
|
||||||
readValues[attribute],
|
// `${from}.${attribute}`,
|
||||||
JSON.stringify(rule.constraint),
|
// readValues[attribute],
|
||||||
!errorMessages
|
// JSON.stringify(rule.constraint),
|
||||||
);
|
// !errorMessages
|
||||||
|
// );
|
||||||
|
|
||||||
if (!errorMessages) {
|
if (!errorMessages) {
|
||||||
// If no errorMessages were returned, then validation passed.
|
// If no errorMessages were returned, then validation passed.
|
||||||
|
|||||||
@ -118,7 +118,7 @@ function checkHpsBestMatch(hps) {
|
|||||||
hps.run({ studies: [studyMatch], displaySets: studyMatchDisplaySets });
|
hps.run({ studies: [studyMatch], displaySets: studyMatchDisplaySets });
|
||||||
const { viewportMatchDetails } = hps.getMatchDetails();
|
const { viewportMatchDetails } = hps.getMatchDetails();
|
||||||
expect(viewportMatchDetails.size).toBe(1);
|
expect(viewportMatchDetails.size).toBe(1);
|
||||||
expect(viewportMatchDetails.get(0)).toMatchObject({
|
expect(viewportMatchDetails.get('ctAXIAL')).toMatchObject({
|
||||||
viewportOptions: {
|
viewportOptions: {
|
||||||
viewportId: 'ctAXIAL',
|
viewportId: 'ctAXIAL',
|
||||||
viewportType: 'volume',
|
viewportType: 'volume',
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
} from './custom-attribute/isDisplaySetFromUrl';
|
} from './custom-attribute/isDisplaySetFromUrl';
|
||||||
import numberOfDisplaySetsWithImages from './custom-attribute/numberOfDisplaySetsWithImages';
|
import numberOfDisplaySetsWithImages from './custom-attribute/numberOfDisplaySetsWithImages';
|
||||||
import seriesDescriptionsFromDisplaySets from './custom-attribute/seriesDescriptionsFromDisplaySets';
|
import seriesDescriptionsFromDisplaySets from './custom-attribute/seriesDescriptionsFromDisplaySets';
|
||||||
|
import uuidv4 from '../../utils/uuidv4';
|
||||||
|
|
||||||
type Protocol = HangingProtocol.Protocol | HangingProtocol.ProtocolGenerator;
|
type Protocol = HangingProtocol.Protocol | HangingProtocol.ProtocolGenerator;
|
||||||
|
|
||||||
@ -132,11 +133,11 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
> = new Map();
|
> = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array that contains for each viewport (viewportIndex) specified in the
|
* An array that contains for each viewport (viewportId) specified in the
|
||||||
* hanging protocol, an object of the form
|
* hanging protocol, an object of the form
|
||||||
*/
|
*/
|
||||||
viewportMatchDetails: Map<
|
viewportMatchDetails: Map<
|
||||||
number, // viewportIndex
|
string, // viewportId
|
||||||
HangingProtocol.ViewportMatchDetails
|
HangingProtocol.ViewportMatchDetails
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
|
||||||
@ -185,7 +186,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
stage: HangingProtocol.ProtocolStage;
|
stage: HangingProtocol.ProtocolStage;
|
||||||
stageIndex: number;
|
stageIndex: number;
|
||||||
activeStudy?: StudyMetadata;
|
activeStudy?: StudyMetadata;
|
||||||
viewportMatchDetails: Map<number, HangingProtocol.ViewportMatchDetails>;
|
viewportMatchDetails: Map<string, HangingProtocol.ViewportMatchDetails>;
|
||||||
displaySetMatchDetails: Map<string, HangingProtocol.DisplaySetMatchDetails>;
|
displaySetMatchDetails: Map<string, HangingProtocol.DisplaySetMatchDetails>;
|
||||||
activeImageLoadStrategyName: string;
|
activeImageLoadStrategyName: string;
|
||||||
} {
|
} {
|
||||||
@ -404,7 +405,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
/**
|
/**
|
||||||
* Returns true, if the hangingProtocol has a custom loading strategy for the images
|
* Returns true, if the hangingProtocol has a custom loading strategy for the images
|
||||||
* and its callback has been added to the HangingProtocolService
|
* and its callback has been added to the HangingProtocolService
|
||||||
* @returns {boolean} true
|
* @returns A boolean indicating whether a custom image load strategy has been added or not.
|
||||||
*/
|
*/
|
||||||
public hasCustomImageLoadStrategy(): boolean {
|
public hasCustomImageLoadStrategy(): boolean {
|
||||||
return (
|
return (
|
||||||
@ -415,10 +416,31 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether a custom image load has been performed or not.
|
||||||
|
* A custom image load is performed when a custom image load strategy is used to load images.
|
||||||
|
* This method is used internally by the HangingProtocolService to determine whether to perform
|
||||||
|
* a custom image load or not.
|
||||||
|
*
|
||||||
|
* @returns A boolean indicating whether a custom image load has been performed or not.
|
||||||
|
*/
|
||||||
public getCustomImageLoadPerformed(): boolean {
|
public getCustomImageLoadPerformed(): boolean {
|
||||||
return this.customImageLoadPerformed;
|
return this.customImageLoadPerformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether a custom image load should be performed or not.
|
||||||
|
* A custom image load should be performed if a custom image load strategy has been added to the HangingProtocolService
|
||||||
|
* and it has not been performed yet.
|
||||||
|
*
|
||||||
|
* @returns A boolean indicating whether a custom image load should be performed or not.
|
||||||
|
*/
|
||||||
|
public getShouldPerformCustomImageLoad(): boolean {
|
||||||
|
return (
|
||||||
|
this.hasCustomImageLoadStrategy() && !this.getCustomImageLoadPerformed()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the strategy callback for loading images to the HangingProtocolService
|
* Set the strategy callback for loading images to the HangingProtocolService
|
||||||
* @param {string} name strategy name
|
* @param {string} name strategy name
|
||||||
@ -519,21 +541,28 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
|
|
||||||
for (let i = 0; i < rows * columns; i++) {
|
for (let i = 0; i < rows * columns; i++) {
|
||||||
stage.viewports.push({
|
stage.viewports.push({
|
||||||
viewportOptions: defaultViewportOptions,
|
viewportOptions: {
|
||||||
|
...defaultViewportOptions,
|
||||||
|
viewportId: uuidv4(),
|
||||||
|
},
|
||||||
displaySets: [],
|
displaySets: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Clone each viewport to ensure independent objects
|
||||||
|
stage.viewports = stage.viewports.map(viewport => ({
|
||||||
|
...viewport,
|
||||||
|
viewportOptions: {
|
||||||
|
...(viewport.viewportOptions || defaultViewportOptions),
|
||||||
|
viewportId: viewport.viewportOptions?.viewportId || uuidv4(),
|
||||||
|
},
|
||||||
|
displaySets: viewport.displaySets || [],
|
||||||
|
}));
|
||||||
|
|
||||||
stage.viewports.forEach(viewport => {
|
stage.viewports.forEach(viewport => {
|
||||||
viewport.viewportOptions =
|
|
||||||
viewport.viewportOptions || defaultViewportOptions;
|
|
||||||
if (!viewport.displaySets) {
|
|
||||||
viewport.displaySets = [];
|
|
||||||
} else {
|
|
||||||
viewport.displaySets.forEach(displaySet => {
|
viewport.displaySets.forEach(displaySet => {
|
||||||
displaySet.options = displaySet.options || {};
|
displaySet.options = displaySet.options || {};
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -558,7 +587,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewportsRequireUpdate(viewportIndex, displaySetInstanceUID) {
|
getViewportsRequireUpdate(viewportId, displaySetInstanceUID) {
|
||||||
const { displaySetService } = this._servicesManager.services;
|
const { displaySetService } = this._servicesManager.services;
|
||||||
const displaySet = displaySetService.getDisplaySetByUID(
|
const displaySet = displaySetService.getDisplaySetByUID(
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
@ -570,15 +599,22 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
const protocol = this.protocol;
|
const protocol = this.protocol;
|
||||||
const protocolStage = protocol.stages[this.stageIndex];
|
const protocolStage = protocol.stages[this.stageIndex];
|
||||||
const protocolViewports = protocolStage.viewports;
|
const protocolViewports = protocolStage.viewports;
|
||||||
const protocolViewport = protocolViewports[viewportIndex];
|
|
||||||
|
|
||||||
const defaultReturn = [
|
const defaultReturn = [
|
||||||
{
|
{
|
||||||
viewportIndex,
|
viewportId,
|
||||||
displaySetInstanceUIDs: [newDisplaySetInstanceUID],
|
displaySetInstanceUIDs: [newDisplaySetInstanceUID],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!protocolViewports) {
|
||||||
|
return defaultReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
const protocolViewport = protocolViewports.find(
|
||||||
|
pv => pv.viewportOptions.viewportId === viewportId
|
||||||
|
);
|
||||||
|
|
||||||
// if no viewport, then we can assume there is no predefined set of
|
// if no viewport, then we can assume there is no predefined set of
|
||||||
// rules that should be applied to this viewport while matching
|
// rules that should be applied to this viewport while matching
|
||||||
if (!protocolViewport) {
|
if (!protocolViewport) {
|
||||||
@ -644,13 +680,13 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we reach here, it means that the displaySetInstanceUIDs to be dropped
|
// if we reach here, it means that the displaySetInstanceUIDs to be dropped
|
||||||
// in the viewportIndex are valid, and we can proceed with the update. However
|
// for the desired viewportId are valid, and we can proceed with the update. However
|
||||||
// we need to check if the displaySets that the viewport were showing
|
// we need to check if the displaySets that the viewport were showing
|
||||||
// was also referenced by other viewports, and if so, we need to update those
|
// was also referenced by other viewports, and if so, we need to update those
|
||||||
// viewports as well
|
// viewports as well
|
||||||
|
|
||||||
// check if displaySetSelectors are used by other viewports, and
|
// check if displaySetSelectors are used by other viewports, and
|
||||||
// store the viewportIndex and displaySetInstanceUIDs that need to be updated
|
// store the viewportId and displaySetInstanceUIDs that need to be updated
|
||||||
const viewportsToUpdate = [];
|
const viewportsToUpdate = [];
|
||||||
protocolViewports.forEach((viewport, index) => {
|
protocolViewports.forEach((viewport, index) => {
|
||||||
const viewportNeedsUpdate = viewport.displaySets.some(
|
const viewportNeedsUpdate = viewport.displaySets.some(
|
||||||
@ -661,7 +697,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
|
|
||||||
if (viewportNeedsUpdate) {
|
if (viewportNeedsUpdate) {
|
||||||
// Try to recompute the viewport options based on the current
|
// Try to recompute the viewport options based on the current
|
||||||
// viewportIndex that needs update but from its old/original un-computed
|
// viewportId that needs update but from its old/original un-computed
|
||||||
// viewport & displaySet options
|
// viewport & displaySet options
|
||||||
if (originalProtocolStage) {
|
if (originalProtocolStage) {
|
||||||
const originalViewport = originalProtocolStage.viewports[index];
|
const originalViewport = originalProtocolStage.viewports[index];
|
||||||
@ -692,7 +728,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
viewportsToUpdate.push({
|
viewportsToUpdate.push({
|
||||||
viewportIndex: index,
|
viewportId: viewport.viewportOptions.viewportId,
|
||||||
displaySetInstanceUIDs,
|
displaySetInstanceUIDs,
|
||||||
viewportOptions: viewport.viewportOptions,
|
viewportOptions: viewport.viewportOptions,
|
||||||
displaySetOptions,
|
displaySetOptions,
|
||||||
@ -799,7 +835,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
* @param protocolId - name of the registered protocol to be set
|
* @param protocolId - name of the registered protocol to be set
|
||||||
* @param options - options to be passed to the protocol, this is either an array
|
* @param options - options to be passed to the protocol, this is either an array
|
||||||
* of the displaySetInstanceUIDs to be set on ALL VIEWPORTS OF THE PROTOCOL or an object
|
* of the displaySetInstanceUIDs to be set on ALL VIEWPORTS OF THE PROTOCOL or an object
|
||||||
* that contains viewportIndex as the key and displaySetInstanceUIDs as the value
|
* that contains viewportId as the key and displaySetInstanceUIDs as the value
|
||||||
* for each viewport that needs to be set.
|
* for each viewport that needs to be set.
|
||||||
* @param errorCallback - callback to be called if there is an error
|
* @param errorCallback - callback to be called if there is an error
|
||||||
* during the protocol application
|
* during the protocol application
|
||||||
@ -1098,7 +1134,6 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
// matching applied
|
// matching applied
|
||||||
this.viewportMatchDetails = new Map();
|
this.viewportMatchDetails = new Map();
|
||||||
this.displaySetMatchDetails = new Map();
|
this.displaySetMatchDetails = new Map();
|
||||||
this.customImageLoadPerformed = false;
|
|
||||||
|
|
||||||
// Retrieve the current stage
|
// Retrieve the current stage
|
||||||
const stageModel = this._getCurrentStageModel();
|
const stageModel = this._getCurrentStageModel();
|
||||||
@ -1148,7 +1183,11 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
displaySetMatchDetails: Map<string, HangingProtocol.DisplaySetMatchDetails>;
|
displaySetMatchDetails: Map<string, HangingProtocol.DisplaySetMatchDetails>;
|
||||||
} {
|
} {
|
||||||
let matchedViewports = 0;
|
let matchedViewports = 0;
|
||||||
stageModel.viewports.forEach((viewport, viewportIndex) => {
|
stageModel.viewports.forEach(viewport => {
|
||||||
|
// Todo: we should probably assign a random viewportId if not defined
|
||||||
|
// below, but it feels odd since viewportGrid should handle this kind
|
||||||
|
// of thing
|
||||||
|
const viewportId = viewport.viewportOptions.viewportId;
|
||||||
const matchDetails = this._matchViewport(
|
const matchDetails = this._matchViewport(
|
||||||
viewport,
|
viewport,
|
||||||
options,
|
options,
|
||||||
@ -1169,7 +1208,7 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
displaySetOptions: it,
|
displaySetOptions: it,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
viewportMatchDetails.set(viewportIndex, matchDetails);
|
viewportMatchDetails.set(viewportId, matchDetails);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -649,10 +649,7 @@ class MeasurementService extends PubSubService {
|
|||||||
* merely causes it to fire the event with the isConsumed set to true.
|
* merely causes it to fire the event with the isConsumed set to true.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public jumpToMeasurement(
|
public jumpToMeasurement(viewportId: string, measurementUID: string): void {
|
||||||
viewportIndex: number,
|
|
||||||
measurementUID: string
|
|
||||||
): void {
|
|
||||||
const measurement = this.measurements.get(measurementUID);
|
const measurement = this.measurements.get(measurementUID);
|
||||||
|
|
||||||
if (!measurement) {
|
if (!measurement) {
|
||||||
@ -660,7 +657,7 @@ class MeasurementService extends PubSubService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const consumableEvent = this.createConsumableEvent({
|
const consumableEvent = this.createConsumableEvent({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
measurement,
|
measurement,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
* @typedef {Object} ViewportDialogProps
|
* @typedef {Object} ViewportDialogProps
|
||||||
* @property {ReactElement|HTMLElement} [content=null] Modal content.
|
* @property {ReactElement|HTMLElement} [content=null] Modal content.
|
||||||
* @property {Object} [contentProps=null] Modal content props.
|
* @property {Object} [contentProps=null] Modal content props.
|
||||||
* @property {boolean} [viewportIndex=false] Modal is dismissible via the esc key.
|
* @property {boolean} [viewportId=false] Modal is dismissible via the esc key.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const name = 'uiViewportDialogService';
|
const name = 'uiViewportDialogService';
|
||||||
@ -22,12 +22,12 @@ const serviceImplementation = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a new UI viewport dialog on the specified viewportIndex;
|
* Show a new UI viewport dialog on the specified viewportId;
|
||||||
*
|
*
|
||||||
* @param {ViewportDialogProps} props { content, contentProps, viewportIndex }
|
* @param {ViewportDialogProps} props { content, contentProps, viewportId }
|
||||||
*/
|
*/
|
||||||
function _show({
|
function _show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
message,
|
message,
|
||||||
@ -36,7 +36,7 @@ function _show({
|
|||||||
onOutsideClick,
|
onOutsideClick,
|
||||||
}) {
|
}) {
|
||||||
return serviceImplementation._show({
|
return serviceImplementation._show({
|
||||||
viewportIndex,
|
viewportId,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
message,
|
message,
|
||||||
@ -59,7 +59,7 @@ function _hide() {
|
|||||||
* @param {*} {
|
* @param {*} {
|
||||||
* hide: hideImplementation,
|
* hide: hideImplementation,
|
||||||
* show: showImplementation,
|
* show: showImplementation,
|
||||||
* viewportIndex,
|
* viewportId,
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
function setServiceImplementation({
|
function setServiceImplementation({
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { getPresentationIds, PresentationIds } from './getPresentationIds';
|
|||||||
|
|
||||||
class ViewportGridService extends PubSubService {
|
class ViewportGridService extends PubSubService {
|
||||||
public static readonly EVENTS = {
|
public static readonly EVENTS = {
|
||||||
ACTIVE_VIEWPORT_INDEX_CHANGED: 'event::activeviewportindexchanged',
|
ACTIVE_VIEWPORT_ID_CHANGED: 'event::activeviewportidchanged',
|
||||||
LAYOUT_CHANGED: 'event::layoutChanged',
|
LAYOUT_CHANGED: 'event::layoutChanged',
|
||||||
GRID_STATE_CHANGED: 'event::gridStateChanged',
|
GRID_STATE_CHANGED: 'event::gridStateChanged',
|
||||||
};
|
};
|
||||||
@ -27,7 +27,7 @@ class ViewportGridService extends PubSubService {
|
|||||||
|
|
||||||
public setServiceImplementation({
|
public setServiceImplementation({
|
||||||
getState: getStateImplementation,
|
getState: getStateImplementation,
|
||||||
setActiveViewportIndex: setActiveViewportIndexImplementation,
|
setActiveViewportId: setActiveViewportIdImplementation,
|
||||||
setDisplaySetsForViewports: setDisplaySetsForViewportsImplementation,
|
setDisplaySetsForViewports: setDisplaySetsForViewportsImplementation,
|
||||||
setLayout: setLayoutImplementation,
|
setLayout: setLayoutImplementation,
|
||||||
reset: resetImplementation,
|
reset: resetImplementation,
|
||||||
@ -38,9 +38,9 @@ class ViewportGridService extends PubSubService {
|
|||||||
if (getStateImplementation) {
|
if (getStateImplementation) {
|
||||||
this.serviceImplementation._getState = getStateImplementation;
|
this.serviceImplementation._getState = getStateImplementation;
|
||||||
}
|
}
|
||||||
if (setActiveViewportIndexImplementation) {
|
if (setActiveViewportIdImplementation) {
|
||||||
this.serviceImplementation._setActiveViewportIndex =
|
this.serviceImplementation._setActiveViewport =
|
||||||
setActiveViewportIndexImplementation;
|
setActiveViewportIdImplementation;
|
||||||
}
|
}
|
||||||
if (setDisplaySetsForViewportsImplementation) {
|
if (setDisplaySetsForViewportsImplementation) {
|
||||||
this.serviceImplementation._setDisplaySetsForViewports =
|
this.serviceImplementation._setDisplaySetsForViewports =
|
||||||
@ -64,13 +64,11 @@ class ViewportGridService extends PubSubService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActiveViewportIndex(index) {
|
public setActiveViewportId(id: string) {
|
||||||
this.serviceImplementation._setActiveViewportIndex(index);
|
this.serviceImplementation._setActiveViewport(id);
|
||||||
const state = this.getState();
|
const state = this.getState();
|
||||||
const viewportId = state.viewports[index]?.viewportOptions?.viewportId;
|
this._broadcastEvent(this.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED, {
|
||||||
this._broadcastEvent(this.EVENTS.ACTIVE_VIEWPORT_INDEX_CHANGED, {
|
viewportId: id,
|
||||||
viewportIndex: index,
|
|
||||||
viewportId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +87,7 @@ class ViewportGridService extends PubSubService {
|
|||||||
const viewports = [];
|
const viewports = [];
|
||||||
|
|
||||||
for (const viewport of props) {
|
for (const viewport of props) {
|
||||||
const updatedViewport = state.viewports[viewport.viewportIndex];
|
const updatedViewport = state.viewports.get(viewport.viewportId);
|
||||||
if (updatedViewport) {
|
if (updatedViewport) {
|
||||||
viewports.push(updatedViewport);
|
viewports.push(updatedViewport);
|
||||||
} else {
|
} else {
|
||||||
@ -118,7 +116,7 @@ class ViewportGridService extends PubSubService {
|
|||||||
numRows,
|
numRows,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
layoutType = 'grid',
|
layoutType = 'grid',
|
||||||
activeViewportIndex = undefined,
|
activeViewportId = undefined,
|
||||||
findOrCreateViewport = undefined,
|
findOrCreateViewport = undefined,
|
||||||
}) {
|
}) {
|
||||||
this.serviceImplementation._setLayout({
|
this.serviceImplementation._setLayout({
|
||||||
@ -126,7 +124,7 @@ class ViewportGridService extends PubSubService {
|
|||||||
numRows,
|
numRows,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
layoutType,
|
layoutType,
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
findOrCreateViewport,
|
findOrCreateViewport,
|
||||||
});
|
});
|
||||||
this._broadcastEvent(this.EVENTS.LAYOUT_CHANGED, {
|
this._broadcastEvent(this.EVENTS.LAYOUT_CHANGED, {
|
||||||
@ -160,8 +158,8 @@ class ViewportGridService extends PubSubService {
|
|||||||
return this.serviceImplementation._getNumViewportPanes();
|
return this.serviceImplementation._getNumViewportPanes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLayoutOptionsFromState(state) {
|
public getLayoutOptionsFromState(state: any): { x: number; y: number; width: number; height: number }[] {
|
||||||
return state.viewports.map(viewport => {
|
return Array.from(state.viewports.entries()).map(([_, viewport]) => {
|
||||||
return {
|
return {
|
||||||
x: viewport.x,
|
x: viewport.x,
|
||||||
y: viewport.y,
|
y: viewport.y,
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const addUniqueIndex = (arr, key, viewports, isUpdatingSameViewport) => {
|
|||||||
arr[arr.length - 1] = displayInstance;
|
arr[arr.length - 1] = displayInstance;
|
||||||
const testId = arr.join(JOIN_STR);
|
const testId = arr.join(JOIN_STR);
|
||||||
if (
|
if (
|
||||||
!viewports.find(
|
!Array.from(viewports.values()).find(
|
||||||
viewport => viewport.viewportOptions?.presentationIds?.[key] === testId
|
viewport => viewport.viewportOptions?.presentationIds?.[key] === testId
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@ -126,11 +126,11 @@ const getPresentationIds = (viewport, viewports): PresentationIds => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only add unique index if the viewport is getting inserted and not updated
|
// only add unique index if the viewport is getting inserted and not updated
|
||||||
const isUpdatingSameViewport = viewports.some(v => {
|
const isUpdatingSameViewport = Array.from(viewports.values()).some(v => {
|
||||||
return (
|
return (
|
||||||
v.displaySetInstanceUIDs.toString() ===
|
v.displaySetInstanceUIDs.toString() ===
|
||||||
viewport.displaySetInstanceUIDs.toString() &&
|
viewport.displaySetInstanceUIDs.toString() &&
|
||||||
v.viewportIndex === viewport.viewportIndex
|
v.viewportId === viewport.viewportId
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export type SetProtocolOptions = {
|
|||||||
|
|
||||||
export type HangingProtocolMatchDetails = {
|
export type HangingProtocolMatchDetails = {
|
||||||
displaySetMatchDetails: Map<string, DisplaySetMatchDetails>;
|
displaySetMatchDetails: Map<string, DisplaySetMatchDetails>;
|
||||||
viewportMatchDetails: Map<number, ViewportMatchDetails>;
|
viewportMatchDetails: Map<string, ViewportMatchDetails>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConstraintValue =
|
export type ConstraintValue =
|
||||||
@ -263,6 +263,8 @@ export type ProtocolNotifications = {
|
|||||||
export type Protocol = {
|
export type Protocol = {
|
||||||
// Mandatory
|
// Mandatory
|
||||||
id: string;
|
id: string;
|
||||||
|
/** A description of this protocol. Used as a tool tip for the user. */
|
||||||
|
description?: string;
|
||||||
/** Maps ids to display set selectors to choose display sets */
|
/** Maps ids to display set selectors to choose display sets */
|
||||||
displaySetSelectors: Record<string, DisplaySetSelector>;
|
displaySetSelectors: Record<string, DisplaySetSelector>;
|
||||||
/** A default viewport to use for any stage to select new viewport layouts. */
|
/** A default viewport to use for any stage to select new viewport layouts. */
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import ObjectPath from './objectPath';
|
import ObjectPath from './objectPath';
|
||||||
import absoluteUrl from './absoluteUrl';
|
import absoluteUrl from './absoluteUrl';
|
||||||
import guid from './guid';
|
import guid from './guid';
|
||||||
|
import uuidv4 from './uuidv4';
|
||||||
import sortBy from './sortBy.js';
|
import sortBy from './sortBy.js';
|
||||||
import writeScript from './writeScript.js';
|
import writeScript from './writeScript.js';
|
||||||
import b64toBlob from './b64toBlob.js';
|
import b64toBlob from './b64toBlob.js';
|
||||||
@ -41,6 +42,7 @@ import { splitComma, getSplitParam } from './splitComma';
|
|||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
guid,
|
guid,
|
||||||
|
uuidv4,
|
||||||
ObjectPath,
|
ObjectPath,
|
||||||
absoluteUrl,
|
absoluteUrl,
|
||||||
sortBy,
|
sortBy,
|
||||||
|
|||||||
@ -39,6 +39,7 @@ describe('Top level exports', () => {
|
|||||||
'hierarchicalListUtils',
|
'hierarchicalListUtils',
|
||||||
'progressTrackingUtils',
|
'progressTrackingUtils',
|
||||||
'subscribeToNextViewportGridChange',
|
'subscribeToNextViewportGridChange',
|
||||||
|
'uuidv4',
|
||||||
].sort();
|
].sort();
|
||||||
|
|
||||||
const exports = Object.keys(utils.default).sort();
|
const exports = Object.keys(utils.default).sort();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ViewportGridService } from '../services';
|
import { ViewportGridService } from '../services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to the very next LAYOUT_CHANGED or GRID_STATE_CHANGED event that
|
* Subscribes to the very next LAYOUT_CHANGED event that
|
||||||
* is not currently on the event queue. The subscriptions are made on a 'zero'
|
* is not currently on the event queue. The subscriptions are made on a 'zero'
|
||||||
* timeout so as to avoid responding to any of those events currently on the event queue.
|
* timeout so as to avoid responding to any of those events currently on the event queue.
|
||||||
* The subscription persists only for a single invocation of either event.
|
* The subscription persists only for a single invocation of either event.
|
||||||
@ -24,10 +24,6 @@ function subscribeToNextViewportGridChange(
|
|||||||
viewportGridService.EVENTS.LAYOUT_CHANGED,
|
viewportGridService.EVENTS.LAYOUT_CHANGED,
|
||||||
callback
|
callback
|
||||||
),
|
),
|
||||||
viewportGridService.subscribe(
|
|
||||||
viewportGridService.EVENTS.GRID_STATE_CHANGED,
|
|
||||||
callback
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -43,20 +43,13 @@ const getViewportModule = () => {
|
|||||||
A simplified version of the tracked `OHIFCornerstoneViewport` is shown below, which
|
A simplified version of the tracked `OHIFCornerstoneViewport` is shown below, which
|
||||||
creates a cornerstone viewport:
|
creates a cornerstone viewport:
|
||||||
|
|
||||||
:::note Tip
|
|
||||||
|
|
||||||
Not in OHIF version 3.1 we use `displaySets` in the props which is new compared to
|
|
||||||
the previous version (3.0) which uses `displaySet`. This is due to the fact that
|
|
||||||
we are moving to a new data model that can render fused images in a single viewport.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function TrackedCornerstoneViewport({
|
function TrackedCornerstoneViewport({
|
||||||
children,
|
children,
|
||||||
dataSource,
|
dataSource,
|
||||||
displaySets,
|
displaySets,
|
||||||
viewportIndex,
|
viewportId,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
commandsManager,
|
commandsManager,
|
||||||
@ -87,6 +80,45 @@ function TrackedCornerstoneViewport({
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Viewport re-rendering optimizations
|
||||||
|
|
||||||
|
We make use of the React memoization pattern to prevent unnecessary re-renders
|
||||||
|
for the viewport unless certain aspects of the Viewport props change. You can take
|
||||||
|
a look into the `areEqual` function in the `OHIFCornerstoneViewport` component to
|
||||||
|
see how this is done.
|
||||||
|
|
||||||
|
```js
|
||||||
|
function areEqual(prevProps, nextProps) {
|
||||||
|
if (prevProps.displaySets.length !== nextProps.displaySets.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
prevProps.viewportOptions.orientation !==
|
||||||
|
nextProps.viewportOptions.orientation
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rest of the code
|
||||||
|
```
|
||||||
|
|
||||||
|
as you see, we check if the `needsRerendering` prop is true, and if so, we will
|
||||||
|
re-render the viewport if the `displaySets` prop changes or the orientation
|
||||||
|
changes.
|
||||||
|
|
||||||
|
|
||||||
|
We use viewportId to identify a viewport and we use it as a key in React
|
||||||
|
rendering. This is important because it allows us to keep track of the viewport
|
||||||
|
and its state, and also let React optimize and move the viewport around in the
|
||||||
|
grid without re-rendering it. However, there are some cases where we need to
|
||||||
|
force re-render the viewport, for example, when the viewport is hydrated
|
||||||
|
with a new Segmentation. For these cases, we use the `needsRerendering` prop
|
||||||
|
to force re-render the viewport. You can add it to the `viewportOptions`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### `@ohif/app`
|
### `@ohif/app`
|
||||||
|
|
||||||
|
|||||||
@ -68,7 +68,7 @@ There are seven events that get publish in `MeasurementService`:
|
|||||||
- `toMeasurementSchema`: A function to get the `data` into the same shape as
|
- `toMeasurementSchema`: A function to get the `data` into the same shape as
|
||||||
the source definition.
|
the source definition.
|
||||||
|
|
||||||
- `jumpToMeasurement(viewportIndex, id)`: calls the listeners who have
|
- `jumpToMeasurement(viewportId, id)`: calls the listeners who have
|
||||||
subscribed to `JUMP_TO_MEASUREMENT`.
|
subscribed to `JUMP_TO_MEASUREMENT`.
|
||||||
|
|
||||||
## Source / Mappers
|
## Source / Mappers
|
||||||
|
|||||||
@ -48,7 +48,7 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const DEFAULT_STATE = {
|
const DEFAULT_STATE = {
|
||||||
viewportIndex: null,
|
viewportId: null,
|
||||||
message: undefined,
|
message: undefined,
|
||||||
type: 'info', // "error" | "warning" | "info" | "success"
|
type: 'info', // "error" | "warning" | "info" | "success"
|
||||||
actions: undefined, // array of { type, text, value }
|
actions: undefined, // array of { type, text, value }
|
||||||
|
|||||||
@ -15,7 +15,7 @@ There are seven events that get publish in `ViewportGridService `:
|
|||||||
|
|
||||||
| Event | Description |
|
| Event | Description |
|
||||||
| ----------------------------- | --------------------------------------------------|
|
| ----------------------------- | --------------------------------------------------|
|
||||||
| ACTIVE_VIEWPORT_INDEX_CHANGED | Fires the index of the active viewport is changed |
|
| ACTIVE_VIEWPORT_ID_CHANGED | Fires the Id of the active viewport is changed |
|
||||||
| LAYOUT_CHANGED | Fires the layout is changed |
|
| LAYOUT_CHANGED | Fires the layout is changed |
|
||||||
| GRID_STATE_CHANGED | Fires when the entire grid state is changed |
|
| GRID_STATE_CHANGED | Fires when the entire grid state is changed |
|
||||||
## Interface
|
## Interface
|
||||||
@ -25,9 +25,9 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
|
|||||||
|
|
||||||
| API Member | Description |
|
| API Member | Description |
|
||||||
| --------------------------------------------------------------------- | --------------------------------------------------- |
|
| --------------------------------------------------------------------- | --------------------------------------------------- |
|
||||||
| `setActiveViewportIndex(index)` | Sets the active viewport index in the app |
|
| `setActiveViewportId(viewportId)` | Sets the active viewport Id in the app |
|
||||||
| `getState()` | Gets the states of the viewport (see below) |
|
| `getState()` | Gets the states of the viewport (see below) |
|
||||||
| `setDisplaySetsForViewport({ viewportIndex, displaySetInstanceUID })` | Sets displaySet for viewport based on displaySet Id |
|
| `setDisplaySetsForViewport({ viewportId, displaySetInstanceUID })` | Sets displaySet for viewport based on displaySet Id |
|
||||||
| `setLayout({numCols, numRows, keepExtraViewports})` | Sets rows and columns. When the total number of viewports decreases, optionally keep the extra/offscreen viewports. |
|
| `setLayout({numCols, numRows, keepExtraViewports})` | Sets rows and columns. When the total number of viewports decreases, optionally keep the extra/offscreen viewports. |
|
||||||
| `reset()` | Resets the default states |
|
| `reset()` | Resets the default states |
|
||||||
| `getNumViewportPanes()` | Gets the number of visible viewport panes |
|
| `getNumViewportPanes()` | Gets the number of visible viewport panes |
|
||||||
@ -56,6 +56,6 @@ const DEFAULT_STATE = {
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
],
|
],
|
||||||
activeViewportIndex: 0,
|
activeViewportId: null,
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|||||||
@ -25,7 +25,7 @@ Each `ViewportComponent` will receive the following props:
|
|||||||
```html
|
```html
|
||||||
<viewportComponent
|
<viewportComponent
|
||||||
viewportData="{viewportData}"
|
viewportData="{viewportData}"
|
||||||
viewportIndex="{viewportIndex}"
|
viewportId="{viewportId}"
|
||||||
children="{[children]}"
|
children="{[children]}"
|
||||||
/>
|
/>
|
||||||
```
|
```
|
||||||
@ -34,7 +34,7 @@ Each `ViewportComponent` will receive the following props:
|
|||||||
| --------------- | --------------- | --------------------------------- |
|
| --------------- | --------------- | --------------------------------- |
|
||||||
| `children` | React.element[] | |
|
| `children` | React.element[] | |
|
||||||
| `viewportData` | object | `viewportSpecificData` (probably) |
|
| `viewportData` | object | `viewportSpecificData` (probably) |
|
||||||
| `viewportIndex` | number | |
|
| `viewportId` | string | |
|
||||||
|
|
||||||
### `@ohif/viewer`
|
### `@ohif/viewer`
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const Footer = ({ actions, className, onSubmit, value }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(flex, padding, className)}>
|
<div className={classNames(flex, padding, className)}>
|
||||||
{actions.map((action, index) => {
|
{actions?.map((action, index) => {
|
||||||
const isFirst = index === 0;
|
const isFirst = index === 0;
|
||||||
|
|
||||||
const onClickHandler = event => onSubmit({ action, value, event });
|
const onClickHandler = event => onSubmit({ action, value, event });
|
||||||
|
|||||||
@ -73,9 +73,7 @@ const Notification = ({
|
|||||||
<span className="ml-2 text-[13px] text-black">{message}</span>
|
<span className="ml-2 text-[13px] text-black">{message}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2 justify-end mt-2">
|
<div className="flex flex-wrap gap-2 justify-end mt-2">
|
||||||
{actions.map((action, index) => {
|
{actions?.map((action, index) => {
|
||||||
const isFirst = index === 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
name={action.id}
|
name={action.id}
|
||||||
|
|||||||
@ -1,18 +1,40 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import LegacyViewportActionBar from '../LegacyViewportActionBar';
|
import LegacyViewportActionBar from '../LegacyViewportActionBar';
|
||||||
import Notification from '../Notification';
|
import Notification from '../Notification';
|
||||||
|
|
||||||
const Viewport = ({
|
interface StudyData {
|
||||||
|
label: string;
|
||||||
|
isTracked: boolean;
|
||||||
|
isLocked: boolean;
|
||||||
|
isRehydratable: boolean;
|
||||||
|
studyDate: string;
|
||||||
|
currentSeries: number;
|
||||||
|
seriesDescription: string;
|
||||||
|
modality: string;
|
||||||
|
patientInformation: {
|
||||||
|
patientName: string;
|
||||||
|
patientSex: string;
|
||||||
|
patientAge: string;
|
||||||
|
MRN: string;
|
||||||
|
thickness: string;
|
||||||
|
spacing: string;
|
||||||
|
scanner: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ViewportProps {
|
||||||
|
viewportId: string;
|
||||||
|
onArrowsClick: () => void;
|
||||||
|
studyData: StudyData;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Viewport: React.FC<ViewportProps> = ({
|
||||||
viewportId,
|
viewportId,
|
||||||
viewportIndex,
|
|
||||||
onArrowsClick,
|
onArrowsClick,
|
||||||
studyData,
|
studyData,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
if (!viewportId) {
|
|
||||||
viewportId = `viewport-${viewportIndex}`;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-col h-full">
|
<div className="relative flex flex-col h-full">
|
||||||
<div className="absolute top-0 left-0 w-full">
|
<div className="absolute top-0 left-0 w-full">
|
||||||
@ -58,30 +80,5 @@ const Viewport = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Viewport.propTypes = {
|
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
|
||||||
onArrowsClick: PropTypes.func.isRequired,
|
|
||||||
studyData: PropTypes.shape({
|
|
||||||
label: PropTypes.string.isRequired,
|
|
||||||
isTracked: PropTypes.bool.isRequired,
|
|
||||||
/* Specific to SR Viewports only... */
|
|
||||||
isLocked: PropTypes.bool.isRequired,
|
|
||||||
isRehydratable: PropTypes.bool.isRequired,
|
|
||||||
studyDate: PropTypes.string.isRequired,
|
|
||||||
currentSeries: PropTypes.number.isRequired,
|
|
||||||
seriesDescription: PropTypes.string.isRequired,
|
|
||||||
modality: PropTypes.string.isRequired,
|
|
||||||
patientInformation: PropTypes.shape({
|
|
||||||
patientName: PropTypes.string.isRequired,
|
|
||||||
patientSex: PropTypes.string.isRequired,
|
|
||||||
patientAge: PropTypes.string.isRequired,
|
|
||||||
MRN: PropTypes.string.isRequired,
|
|
||||||
thickness: PropTypes.string.isRequired,
|
|
||||||
spacing: PropTypes.string.isRequired,
|
|
||||||
scanner: PropTypes.string.isRequired,
|
|
||||||
}),
|
|
||||||
}).isRequired,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Viewport;
|
export default Viewport;
|
||||||
|
export type { ViewportProps, StudyData };
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
import Viewport from './Viewport';
|
|
||||||
export default Viewport;
|
|
||||||
5
platform/ui/src/components/Viewport/index.ts
Normal file
5
platform/ui/src/components/Viewport/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Viewport from './Viewport';
|
||||||
|
import type { StudyData, ViewportProps } from './Viewport';
|
||||||
|
|
||||||
|
export default Viewport;
|
||||||
|
export type { StudyData, ViewportProps };
|
||||||
@ -27,7 +27,6 @@ const ViewportActionBar = ({
|
|||||||
getStatusComponent,
|
getStatusComponent,
|
||||||
}: ViewportActionBarProps): JSX.Element => {
|
}: ViewportActionBarProps): JSX.Element => {
|
||||||
const { label, studyDate, seriesDescription, patientInformation } = studyData;
|
const { label, studyDate, seriesDescription, patientInformation } = studyData;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
patientName,
|
patientName,
|
||||||
patientSex,
|
patientSex,
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import React, {
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const DEFAULT_STATE = {
|
const DEFAULT_STATE = {
|
||||||
viewportIndex: null,
|
|
||||||
message: undefined,
|
message: undefined,
|
||||||
type: 'info', // "error" | "warning" | "info" | "success"
|
type: 'info', // "error" | "warning" | "info" | "success"
|
||||||
actions: undefined, // array of { type, text, value }
|
actions: undefined, // array of { type, text, value }
|
||||||
|
|||||||
@ -5,22 +5,55 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useReducer,
|
useReducer,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import merge from 'lodash.merge';
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import isEqual from 'lodash.isequal';
|
import { ViewportGridService, utils } from '@ohif/core';
|
||||||
import { ViewportGridService } from '@ohif/core';
|
|
||||||
import viewportLabels from '../utils/viewportLabels';
|
import viewportLabels from '../utils/viewportLabels';
|
||||||
|
|
||||||
const DEFAULT_STATE = {
|
interface Viewport {
|
||||||
activeViewportIndex: 0,
|
viewportId: string;
|
||||||
|
displaySetInstanceUIDs: string[];
|
||||||
|
viewportOptions: any;
|
||||||
|
displaySetSelectors: any[];
|
||||||
|
displaySetOptions: any[];
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
viewportLabel: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Layout {
|
||||||
|
numRows: number;
|
||||||
|
numCols: number;
|
||||||
|
layoutType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DefaultState {
|
||||||
|
activeViewportId: string | null;
|
||||||
|
layout: Layout;
|
||||||
|
viewports: Map<string, Viewport>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_STATE: DefaultState = {
|
||||||
|
activeViewportId: null,
|
||||||
layout: {
|
layout: {
|
||||||
numRows: 0,
|
numRows: 0,
|
||||||
numCols: 0,
|
numCols: 0,
|
||||||
layoutType: 'grid',
|
layoutType: 'grid',
|
||||||
},
|
},
|
||||||
viewports: [
|
// Viewports structure has been changed to Map (previously it was
|
||||||
{
|
// tied to the viewportIndex which caused multiple issues. Now we have
|
||||||
|
// moved completely to viewportId which is unique for each viewport.
|
||||||
|
viewports: new Map(
|
||||||
|
Object.entries({
|
||||||
|
default: {
|
||||||
|
viewportId: 'default',
|
||||||
displaySetInstanceUIDs: [],
|
displaySetInstanceUIDs: [],
|
||||||
viewportOptions: {},
|
viewportOptions: {
|
||||||
|
viewportId: 'default',
|
||||||
|
},
|
||||||
displaySetSelectors: [],
|
displaySetSelectors: [],
|
||||||
displaySetOptions: [{}],
|
displaySetOptions: [{}],
|
||||||
x: 0, // left
|
x: 0, // left
|
||||||
@ -29,97 +62,73 @@ const DEFAULT_STATE = {
|
|||||||
height: 100,
|
height: 100,
|
||||||
viewportLabel: null,
|
viewportLabel: null,
|
||||||
},
|
},
|
||||||
],
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
const getViewportLabel = (viewports, viewportId) => {
|
||||||
|
const viewportIds = Array.from(viewports.keys());
|
||||||
|
return viewportLabels[viewportIds.indexOf(viewportId)];
|
||||||
|
};
|
||||||
|
|
||||||
|
const determineActiveViewportId = (state: DefaultState, newViewports: Map) => {
|
||||||
|
const { activeViewportId } = state;
|
||||||
|
const currentActiveViewport = state.viewports.get(activeViewportId);
|
||||||
|
|
||||||
|
if (!currentActiveViewport) {
|
||||||
|
return null; // Return an appropriate value if the current active viewport is not found
|
||||||
|
}
|
||||||
|
|
||||||
|
// for the new viewports, we should rank them by the displaySetInstanceUIDs
|
||||||
|
// they are displaying and the orientation then we can find the active viewport
|
||||||
|
const currentActiveDisplaySetInstanceUIDs =
|
||||||
|
currentActiveViewport.displaySetInstanceUIDs;
|
||||||
|
|
||||||
|
// This doesn't take into account where stack viewport is converting to volumeViewport
|
||||||
|
// since in stack viewport we don't have a concept of "orientation" as a string
|
||||||
|
// maybe we should calculate the orientation based on the active imageId
|
||||||
|
// so that we can compare it with the new viewports (which might be volume viewports)
|
||||||
|
// and find the best match
|
||||||
|
const currentOrientation = currentActiveViewport.viewportOptions.orientation;
|
||||||
|
|
||||||
|
const sortedViewports = Array.from(newViewports.values()).sort((a, b) => {
|
||||||
|
// Compare orientations
|
||||||
|
const aOrientationMatch =
|
||||||
|
a.viewportOptions.orientation === currentOrientation;
|
||||||
|
const bOrientationMatch =
|
||||||
|
b.viewportOptions.orientation === currentOrientation;
|
||||||
|
if (aOrientationMatch !== bOrientationMatch) {
|
||||||
|
return bOrientationMatch - aOrientationMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare displaySetInstanceUIDs
|
||||||
|
const aMatch = a.displaySetInstanceUIDs.some(uid =>
|
||||||
|
currentActiveDisplaySetInstanceUIDs.includes(uid)
|
||||||
|
);
|
||||||
|
const bMatch = b.displaySetInstanceUIDs.some(uid =>
|
||||||
|
currentActiveDisplaySetInstanceUIDs.includes(uid)
|
||||||
|
);
|
||||||
|
if (aMatch !== bMatch) {
|
||||||
|
return bMatch - aMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Return 0 if no differences found
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!sortedViewports?.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedViewports[0].viewportId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewportGridContext = createContext(DEFAULT_STATE);
|
export const ViewportGridContext = createContext(DEFAULT_STATE);
|
||||||
|
|
||||||
/** A viewport is reuseable if it is the same size as the old
|
|
||||||
* one and has the same display sets, in the same position.
|
|
||||||
* It SHOULD be possible to re-use them at different positions, but
|
|
||||||
* this causes problems with segmentation.
|
|
||||||
*/
|
|
||||||
const isReuseableViewport = (oldViewport, newViewport) => {
|
|
||||||
const sameDiplaySets = isEqual(
|
|
||||||
oldViewport.displaySetInstanceUIDs,
|
|
||||||
newViewport.displaySetInstanceUIDs
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
oldViewport.viewportIndex === newViewport.viewportIndex &&
|
|
||||||
sameDiplaySets &&
|
|
||||||
oldViewport.height === newViewport.height &&
|
|
||||||
oldViewport.width === newViewport.width
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Holds a global viewport counter - used to assign new id's to viewports
|
|
||||||
// Starts at a value above zero so that any of the old viewport id's are
|
|
||||||
// immediately obvious and if we get any index generated viewports, they are
|
|
||||||
// definitely distinct from these ones.
|
|
||||||
let viewportCounter = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a viewportId to re-use if possible, preserving the existing
|
|
||||||
* viewport information, OR create a new one if the viewport given isn't
|
|
||||||
* compatible with what was there before.
|
|
||||||
*
|
|
||||||
* @param viewportIdSet
|
|
||||||
* @param viewport
|
|
||||||
* @param stateViewports
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const reuseViewportId = (viewportIdSet: Set, viewport, stateViewports) => {
|
|
||||||
for (const oldViewport of stateViewports) {
|
|
||||||
const { viewportId: oldId } = oldViewport;
|
|
||||||
if (!oldId) {
|
|
||||||
// This occurs on startup, so skip re-using it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (viewportIdSet.has(oldId)) {
|
|
||||||
// oldId is already used - we can't reuse it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isReuseableViewport(oldViewport, viewport)) {
|
|
||||||
viewportIdSet.add(oldId);
|
|
||||||
// This means the old and the new viewport are compatible, and
|
|
||||||
// since we have gotten here, the viewport ID isn't used, so we
|
|
||||||
// are good to reuse it.
|
|
||||||
// This will remember the old viewport options, assuming they are unchanging.
|
|
||||||
return {
|
|
||||||
...oldViewport,
|
|
||||||
...viewport,
|
|
||||||
id: oldId,
|
|
||||||
viewportId: oldId,
|
|
||||||
viewportOptions: {
|
|
||||||
// Update any viewport options from new
|
|
||||||
...viewport.viewportOptions,
|
|
||||||
viewportId: oldId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There wasn't an old id found to be reused, so create a new one
|
|
||||||
// Find a viewport instance number different from earlier viewports
|
|
||||||
const viewportId = 'viewport-' + viewportCounter;
|
|
||||||
viewportIdSet.add(viewportId);
|
|
||||||
// Loop over viewport counters in case of a really long lived display
|
|
||||||
viewportCounter = (viewportCounter + 1) % 100000;
|
|
||||||
// viewportOptions is already a copy, so can just update direct
|
|
||||||
viewport.viewportOptions.viewportId = viewportId;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...viewport,
|
|
||||||
id: viewportId,
|
|
||||||
viewportId,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ViewportGridProvider({ children, service }) {
|
export function ViewportGridProvider({ children, service }) {
|
||||||
const viewportGridReducer = (state, action) => {
|
const viewportGridReducer = (state: DefaultState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'SET_ACTIVE_VIEWPORT_INDEX': {
|
case 'SET_ACTIVE_VIEWPORT_ID': {
|
||||||
return { ...state, ...{ activeViewportIndex: action.payload } };
|
return { ...state, ...{ activeViewportId: action.payload } };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,65 +139,49 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
*/
|
*/
|
||||||
case 'SET_DISPLAYSETS_FOR_VIEWPORTS': {
|
case 'SET_DISPLAYSETS_FOR_VIEWPORTS': {
|
||||||
const { payload } = action;
|
const { payload } = action;
|
||||||
const viewports = state.viewports.slice();
|
const viewports = new Map(state.viewports);
|
||||||
|
|
||||||
// Have the initial id set contain all viewports not updated here
|
payload.forEach(updatedViewport => {
|
||||||
const viewportIdSet = new Set();
|
const { viewportId, displaySetInstanceUIDs } = updatedViewport;
|
||||||
viewports.forEach((viewport, index) => {
|
|
||||||
if (!viewport.viewportId) {
|
const previousViewport = viewports.get(viewportId);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isUpdated = payload.find(
|
|
||||||
newViewport => newViewport.viewportIndex === index
|
|
||||||
);
|
|
||||||
if (isUpdated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
viewportIdSet.add(viewport.viewportId);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const updatedViewport of payload) {
|
|
||||||
// Use the newly provide viewportOptions and display set options
|
// Use the newly provide viewportOptions and display set options
|
||||||
// when provided, and otherwise fall back to the previous ones.
|
// when provided, and otherwise fall back to the previous ones.
|
||||||
// That allows for easy updates of just the display set.
|
// That allows for easy updates of just the display set.
|
||||||
const { viewportIndex, displaySetInstanceUIDs } = updatedViewport;
|
const viewportOptions = merge(
|
||||||
const previousViewport = viewports[viewportIndex] || {};
|
{},
|
||||||
const viewportOptions = {
|
updatedViewport?.viewportOptions,
|
||||||
...(updatedViewport.viewportOptions ||
|
previousViewport?.viewportOptions
|
||||||
previousViewport.viewportOptions),
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const displaySetOptions = updatedViewport.displaySetOptions || [];
|
const displaySetOptions = updatedViewport.displaySetOptions || [];
|
||||||
if (!displaySetOptions.length) {
|
if (!displaySetOptions.length) {
|
||||||
// Copy all the display set options, assuming a full set of displa
|
// Copy all the display set options, assuming a full set of displaySet UID's is provided.
|
||||||
// set UID's is provided.
|
|
||||||
displaySetOptions.push(...previousViewport.displaySetOptions);
|
displaySetOptions.push(...previousViewport.displaySetOptions);
|
||||||
if (!displaySetOptions.length) {
|
if (!displaySetOptions.length) {
|
||||||
displaySetOptions.push({});
|
displaySetOptions.push({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let newViewport = {
|
const newViewport = {
|
||||||
...previousViewport,
|
...previousViewport,
|
||||||
displaySetInstanceUIDs,
|
displaySetInstanceUIDs,
|
||||||
viewportOptions,
|
viewportOptions,
|
||||||
displaySetOptions,
|
displaySetOptions,
|
||||||
viewportLabel: viewportLabels[viewportIndex],
|
viewportLabel: getViewportLabel(viewports, viewportId),
|
||||||
};
|
};
|
||||||
|
|
||||||
viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
|
viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
|
||||||
newViewport,
|
newViewport,
|
||||||
viewports
|
viewports
|
||||||
);
|
);
|
||||||
|
|
||||||
newViewport = reuseViewportId(
|
viewports.set(viewportId, {
|
||||||
viewportIdSet,
|
...viewports.get(viewportId),
|
||||||
newViewport,
|
...newViewport,
|
||||||
state.viewports
|
});
|
||||||
);
|
});
|
||||||
newViewport.viewportIndex = previousViewport.viewportIndex;
|
|
||||||
|
|
||||||
viewports[viewportIndex] = newViewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...state, viewports };
|
return { ...state, viewports };
|
||||||
}
|
}
|
||||||
@ -198,13 +191,13 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
numRows,
|
numRows,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
layoutType = 'grid',
|
layoutType = 'grid',
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
findOrCreateViewport,
|
findOrCreateViewport,
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
|
|
||||||
// If empty viewportOptions, we use numRow and numCols to calculate number of viewports
|
// If empty viewportOptions, we use numRow and numCols to calculate number of viewports
|
||||||
const hasOptions = layoutOptions?.length;
|
const hasOptions = layoutOptions?.length;
|
||||||
const viewports = [];
|
const viewports = new Map<string, Viewport>();
|
||||||
|
|
||||||
// Options is a temporary state store which can be used by the
|
// Options is a temporary state store which can be used by the
|
||||||
// findOrCreate to store state about already found viewports. Typically,
|
// findOrCreate to store state about already found viewports. Typically,
|
||||||
@ -213,34 +206,46 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
// haven't been viewed yet, and add them in the appropriate order.
|
// haven't been viewed yet, and add them in the appropriate order.
|
||||||
const options = {};
|
const options = {};
|
||||||
|
|
||||||
let activeViewportIndexToSet = activeViewportIndex;
|
let activeViewportIdToSet = activeViewportId;
|
||||||
for (let row = 0; row < numRows; row++) {
|
for (let row = 0; row < numRows; row++) {
|
||||||
for (let col = 0; col < numCols; col++) {
|
for (let col = 0; col < numCols; col++) {
|
||||||
const pos = col + row * numCols;
|
const position = col + row * numCols;
|
||||||
const layoutOption = layoutOptions[pos];
|
const layoutOption = layoutOptions[position];
|
||||||
const positionId = layoutOption?.positionId || `${col}-${row}`;
|
const positionId = layoutOption?.positionId || `${col}-${row}`;
|
||||||
if (hasOptions && pos >= layoutOptions.length) {
|
|
||||||
|
if (hasOptions && position >= layoutOptions.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
activeViewportIndexToSet == null &&
|
const viewport = findOrCreateViewport(
|
||||||
state.viewports[state.activeViewportIndex]?.positionId ===
|
position,
|
||||||
positionId
|
positionId,
|
||||||
) {
|
options
|
||||||
activeViewportIndexToSet = pos;
|
);
|
||||||
}
|
|
||||||
const viewport = findOrCreateViewport(pos, positionId, options);
|
|
||||||
if (!viewport) {
|
if (!viewport) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewport.positionId = positionId;
|
viewport.positionId = positionId;
|
||||||
|
|
||||||
|
// If the viewport doesn't have a viewportId, we create one
|
||||||
|
if (!viewport.viewportOptions?.viewportId) {
|
||||||
|
const randomUID = utils.uuidv4().substring(0, 8);
|
||||||
|
viewport.viewportOptions.viewportId = `viewport-${randomUID}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.viewportId = viewport.viewportOptions.viewportId;
|
||||||
|
|
||||||
// Create a new viewport object as it is getting updated here
|
// Create a new viewport object as it is getting updated here
|
||||||
// and it is part of the read only state
|
// and it is part of the read only state
|
||||||
viewports.push(viewport);
|
viewports.set(viewport.viewportId, viewport);
|
||||||
let xPos, yPos, w, h;
|
|
||||||
|
|
||||||
if (layoutOptions && layoutOptions[pos]) {
|
let xPos, yPos, w, h;
|
||||||
({ x: xPos, y: yPos, width: w, height: h } = layoutOptions[pos]);
|
if (layoutOptions && layoutOptions[position]) {
|
||||||
|
({ x: xPos, y: yPos, width: w, height: h } = layoutOptions[
|
||||||
|
position
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
w = 1 / numCols;
|
w = 1 / numCols;
|
||||||
h = 1 / numRows;
|
h = 1 / numRows;
|
||||||
@ -248,40 +253,35 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
yPos = row * h;
|
yPos = row * h;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewport.width = w;
|
Object.assign(viewport, {
|
||||||
viewport.height = h;
|
width: w,
|
||||||
viewport.x = xPos;
|
height: h,
|
||||||
viewport.y = yPos;
|
x: xPos,
|
||||||
}
|
y: yPos,
|
||||||
}
|
});
|
||||||
|
|
||||||
activeViewportIndexToSet = activeViewportIndexToSet ?? 0;
|
viewport.viewportLabel = getViewportLabel(
|
||||||
|
viewports,
|
||||||
const viewportIdSet = new Set();
|
viewport.viewportId
|
||||||
for (
|
|
||||||
let viewportIndex = 0;
|
|
||||||
viewportIndex < viewports.length;
|
|
||||||
viewportIndex++
|
|
||||||
) {
|
|
||||||
const viewport = reuseViewportId(
|
|
||||||
viewportIdSet,
|
|
||||||
viewports[viewportIndex],
|
|
||||||
state.viewports
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!viewport.viewportOptions.presentationIds) {
|
if (!viewport.viewportOptions.presentationIds) {
|
||||||
viewport.viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
|
viewport.viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
|
||||||
viewport,
|
viewport,
|
||||||
viewports
|
viewports
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
viewport.viewportIndex = viewportIndex;
|
|
||||||
viewport.viewportLabel = viewportLabels[viewportIndex];
|
|
||||||
viewports[viewportIndex] = viewport;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeViewportIdToSet =
|
||||||
|
activeViewportIdToSet ??
|
||||||
|
determineActiveViewportId(state, viewports) ??
|
||||||
|
'default';
|
||||||
|
|
||||||
const ret = {
|
const ret = {
|
||||||
...state,
|
...state,
|
||||||
activeViewportIndex: activeViewportIndexToSet,
|
activeViewportId: activeViewportIdToSet,
|
||||||
layout: {
|
layout: {
|
||||||
...state.layout,
|
...state.layout,
|
||||||
numCols,
|
numCols,
|
||||||
@ -317,8 +317,8 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
return viewportGridState;
|
return viewportGridState;
|
||||||
}, [viewportGridState]);
|
}, [viewportGridState]);
|
||||||
|
|
||||||
const setActiveViewportIndex = useCallback(
|
const setActiveViewportId = useCallback(
|
||||||
index => dispatch({ type: 'SET_ACTIVE_VIEWPORT_INDEX', payload: index }),
|
index => dispatch({ type: 'SET_ACTIVE_VIEWPORT_ID', payload: index }),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
numRows,
|
numRows,
|
||||||
numCols,
|
numCols,
|
||||||
layoutOptions = [],
|
layoutOptions = [],
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
findOrCreateViewport,
|
findOrCreateViewport,
|
||||||
}) =>
|
}) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -347,7 +347,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
numRows,
|
numRows,
|
||||||
numCols,
|
numCols,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
activeViewportIndex,
|
activeViewportId,
|
||||||
findOrCreateViewport,
|
findOrCreateViewport,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -375,7 +375,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
const getNumViewportPanes = useCallback(() => {
|
const getNumViewportPanes = useCallback(() => {
|
||||||
const { layout, viewports } = viewportGridState;
|
const { layout, viewports } = viewportGridState;
|
||||||
const { numRows, numCols } = layout;
|
const { numRows, numCols } = layout;
|
||||||
return Math.min(viewports.length, numCols * numRows);
|
return Math.min(viewports.size, numCols * numRows);
|
||||||
}, [viewportGridState]);
|
}, [viewportGridState]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -387,7 +387,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
if (service) {
|
if (service) {
|
||||||
service.setServiceImplementation({
|
service.setServiceImplementation({
|
||||||
getState,
|
getState,
|
||||||
setActiveViewportIndex,
|
setActiveViewportId,
|
||||||
setDisplaySetsForViewports,
|
setDisplaySetsForViewports,
|
||||||
setLayout,
|
setLayout,
|
||||||
reset,
|
reset,
|
||||||
@ -399,7 +399,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
}, [
|
}, [
|
||||||
getState,
|
getState,
|
||||||
service,
|
service,
|
||||||
setActiveViewportIndex,
|
setActiveViewportId,
|
||||||
setDisplaySetsForViewports,
|
setDisplaySetsForViewports,
|
||||||
setLayout,
|
setLayout,
|
||||||
reset,
|
reset,
|
||||||
@ -410,7 +410,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
// run many of the calls through the service itself since we want to publish events
|
// run many of the calls through the service itself since we want to publish events
|
||||||
const api = {
|
const api = {
|
||||||
getState,
|
getState,
|
||||||
setActiveViewportIndex: index => service.setActiveViewportIndex(index),
|
setActiveViewportId: index => service.setActiveViewportId(index),
|
||||||
setDisplaySetsForViewport: props =>
|
setDisplaySetsForViewport: props =>
|
||||||
service.setDisplaySetsForViewports([props]),
|
service.setDisplaySetsForViewports([props]),
|
||||||
setDisplaySetsForViewports: props =>
|
setDisplaySetsForViewports: props =>
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@ -16451,16 +16451,16 @@ prettier-linter-helpers@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-diff "^1.1.2"
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
prettier@^1.18.2:
|
|
||||||
version "1.19.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
|
||||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
|
||||||
|
|
||||||
prettier@^2.8.0:
|
prettier@^2.8.0:
|
||||||
version "2.8.8"
|
version "2.8.8"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
||||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||||
|
|
||||||
|
prettier@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643"
|
||||||
|
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
|
||||||
|
|
||||||
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0:
|
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0:
|
||||||
version "5.6.0"
|
version "5.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user