fix(ViewportGrid) fill blank viewports with display sets not yet in grid. (#3154)
* fix(ViewportGrid): In ViewportGrid, fill blank viewports with display sets not yet in grid. ViewportGridService now allows off-screen viewports to remain so that so as to maintain continuity if they were filled by the UI/user. * PR feedback: moved getNumViewportPanes into the ViewportGridService API. * PR feedback: - renamed some variables - proper import/export of IDisplaySet - added some comments for clarification - fixed broken e2e tests * Some missed rename of Services.
This commit is contained in:
parent
5e66faa45d
commit
f791a4cafb
@ -59,6 +59,13 @@ export default function toggleMPRHangingProtocol({
|
|||||||
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
||||||
|
|
||||||
const errorCallback = error => {
|
const errorCallback = error => {
|
||||||
|
// Unable to create MPR, so be sure to return to the cached/original protocol.
|
||||||
|
hangingProtocolService.setProtocol(
|
||||||
|
cachedState.protocol.id,
|
||||||
|
viewportMatchDetails,
|
||||||
|
restoreErrorCallback
|
||||||
|
);
|
||||||
|
|
||||||
uiNotificationService.show({
|
uiNotificationService.show({
|
||||||
title: 'Multiplanar reconstruction (MPR) ',
|
title: 'Multiplanar reconstruction (MPR) ',
|
||||||
message:
|
message:
|
||||||
@ -276,13 +283,17 @@ function _getViewportsInfo({ protocol, stage, viewports, servicesManager }) {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
if (viewportIds.length) {
|
if (viewportIds.length) {
|
||||||
toolOptions = viewportIds.map(viewportId => {
|
toolOptions = viewportIds
|
||||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
.map(viewportId => {
|
||||||
return {
|
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||||
toolGroupId: toolGroup.id,
|
return toolGroup
|
||||||
toolOptions: toolGroup.toolOptions,
|
? {
|
||||||
};
|
toolGroupId: toolGroup.id,
|
||||||
});
|
toolOptions: toolGroup.toolOptions,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { viewportMatchDetails, viewportStructure, toolOptions };
|
return { viewportMatchDetails, viewportStructure, toolOptions };
|
||||||
|
|||||||
@ -79,7 +79,15 @@ function LayoutSelector({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
viewportGridService.setLayout({ numRows, numCols });
|
|
||||||
|
// When a new layout is selected, keep any extra/offscreen viewports
|
||||||
|
// so that if any of those viewports were populated via the UI then they
|
||||||
|
// will be maintained in case those viewports are redisplayed later.
|
||||||
|
viewportGridService.setLayout({
|
||||||
|
numRows,
|
||||||
|
numCols,
|
||||||
|
keepExtraViewports: true,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -182,7 +182,7 @@ function PanelStudyBrowserTracking({
|
|||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
trackedSeries,
|
trackedSeries,
|
||||||
viewports,
|
viewports,
|
||||||
isSingleViewport,
|
viewportGridService,
|
||||||
dataSource,
|
dataSource,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
uiDialogService,
|
uiDialogService,
|
||||||
@ -245,7 +245,7 @@ function PanelStudyBrowserTracking({
|
|||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
trackedSeries,
|
trackedSeries,
|
||||||
viewports,
|
viewports,
|
||||||
isSingleViewport,
|
viewportGridService,
|
||||||
dataSource,
|
dataSource,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
uiDialogService,
|
uiDialogService,
|
||||||
@ -412,7 +412,7 @@ function _mapDisplaySets(
|
|||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
trackedSeriesInstanceUIDs,
|
trackedSeriesInstanceUIDs,
|
||||||
viewports, // TODO: make array of `displaySetInstanceUIDs`?
|
viewports, // TODO: make array of `displaySetInstanceUIDs`?
|
||||||
isSingleViewport,
|
viewportGridService,
|
||||||
dataSource,
|
dataSource,
|
||||||
displaySetService,
|
displaySetService,
|
||||||
uiDialogService,
|
uiDialogService,
|
||||||
@ -423,18 +423,21 @@ function _mapDisplaySets(
|
|||||||
displaySets.forEach(ds => {
|
displaySets.forEach(ds => {
|
||||||
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
||||||
const componentType = _getComponentType(ds.Modality);
|
const componentType = _getComponentType(ds.Modality);
|
||||||
const viewportIdentificator = isSingleViewport
|
const numPanes = viewportGridService.getNumViewportPanes();
|
||||||
? []
|
const viewportIdentificator =
|
||||||
: viewports.reduce((acc, viewportData, index) => {
|
numPanes === 1
|
||||||
if (
|
? []
|
||||||
viewportData?.displaySetInstanceUIDs?.includes(
|
: viewports.reduce((acc, viewportData, index) => {
|
||||||
ds.displaySetInstanceUID
|
if (
|
||||||
)
|
index < numPanes &&
|
||||||
) {
|
viewportData?.displaySetInstanceUIDs?.includes(
|
||||||
acc.push(viewportData.viewportLabel);
|
ds.displaySetInstanceUID
|
||||||
}
|
)
|
||||||
return acc;
|
) {
|
||||||
}, []);
|
acc.push(viewportData.viewportLabel);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const array =
|
const array =
|
||||||
componentType === 'thumbnailTracked'
|
componentType === 'thumbnailTracked'
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class ViewportGridService extends PubSubService {
|
|||||||
reset: resetImplementation,
|
reset: resetImplementation,
|
||||||
onModeExit: onModeExitImplementation,
|
onModeExit: onModeExitImplementation,
|
||||||
set: setImplementation,
|
set: setImplementation,
|
||||||
|
getNumViewportPanes: getNumViewportPanesImplementation,
|
||||||
}): void {
|
}): void {
|
||||||
if (getStateImplementation) {
|
if (getStateImplementation) {
|
||||||
this.serviceImplementation._getState = getStateImplementation;
|
this.serviceImplementation._getState = getStateImplementation;
|
||||||
@ -62,6 +63,9 @@ class ViewportGridService extends PubSubService {
|
|||||||
if (setImplementation) {
|
if (setImplementation) {
|
||||||
this.serviceImplementation._set = setImplementation;
|
this.serviceImplementation._set = setImplementation;
|
||||||
}
|
}
|
||||||
|
if (getNumViewportPanesImplementation) {
|
||||||
|
this.serviceImplementation._getNumViewportPanes = getNumViewportPanesImplementation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActiveViewportIndex(index) {
|
public setActiveViewportIndex(index) {
|
||||||
@ -122,6 +126,10 @@ class ViewportGridService extends PubSubService {
|
|||||||
public set(state) {
|
public set(state) {
|
||||||
this.serviceImplementation._set(state);
|
this.serviceImplementation._set(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getNumViewportPanes() {
|
||||||
|
return this.serviceImplementation._getNumViewportPanes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ViewportGridService;
|
export default ViewportGridService;
|
||||||
|
|||||||
@ -19,8 +19,9 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
|
|||||||
| `setActiveViewportIndex(index)` | Sets the active viewport index in the app |
|
| `setActiveViewportIndex(index)` | Sets the active viewport index 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({ viewportIndex, displaySetInstanceUID })` | Sets displaySet for viewport based on displaySet Id |
|
||||||
| `setLayout({numCols, numRows})` | Sets rows and columns |
|
| `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 |
|
||||||
|
|
||||||
## Implementations
|
## Implementations
|
||||||
|
|
||||||
|
|||||||
@ -19,8 +19,9 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
|
|||||||
| `setActiveViewportIndex(index)` | Sets the active viewport index in the app |
|
| `setActiveViewportIndex(index)` | Sets the active viewport index 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({ viewportIndex, displaySetInstanceUID })` | Sets displaySet for viewport based on displaySet Id |
|
||||||
| `setLayout({numCols, numRows})` | Sets rows and columns |
|
| `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 |
|
||||||
|
|
||||||
## Implementations
|
## Implementations
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
numRows,
|
numRows,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
layoutType = 'grid',
|
layoutType = 'grid',
|
||||||
|
keepExtraViewports = false,
|
||||||
} = 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
|
||||||
@ -97,8 +98,14 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
while (viewports.length < numPanes) {
|
while (viewports.length < numPanes) {
|
||||||
viewports.push({});
|
viewports.push({});
|
||||||
}
|
}
|
||||||
while (viewports.length > numPanes) {
|
|
||||||
viewports.pop();
|
// Extra viewports are kept when the grid layout is changed in the UI
|
||||||
|
// because the user populated those viewports and if the viewports were to
|
||||||
|
// return on screen their contents should be maintained.
|
||||||
|
if (!keepExtraViewports) {
|
||||||
|
while (viewports.length > numPanes) {
|
||||||
|
viewports.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < numPanes; i++) {
|
for (let i = 0; i < numPanes; i++) {
|
||||||
@ -238,7 +245,13 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const setLayout = useCallback(
|
const setLayout = useCallback(
|
||||||
({ layoutType, numRows, numCols, layoutOptions = [] }) =>
|
({
|
||||||
|
layoutType,
|
||||||
|
numRows,
|
||||||
|
numCols,
|
||||||
|
layoutOptions = [],
|
||||||
|
keepExtraViewports = false,
|
||||||
|
}) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_LAYOUT',
|
type: 'SET_LAYOUT',
|
||||||
payload: {
|
payload: {
|
||||||
@ -246,6 +259,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
numRows,
|
numRows,
|
||||||
numCols,
|
numCols,
|
||||||
layoutOptions,
|
layoutOptions,
|
||||||
|
keepExtraViewports,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
@ -288,6 +302,11 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getNumViewportPanes = useCallback(() => {
|
||||||
|
const { numCols, numRows, viewports } = viewportGridState;
|
||||||
|
return Math.min(viewports.length, numCols * numRows);
|
||||||
|
}, [viewportGridState]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the implementation of ViewportGridService that can be used by extensions.
|
* Sets the implementation of ViewportGridService that can be used by extensions.
|
||||||
*
|
*
|
||||||
@ -306,6 +325,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
setCachedLayout,
|
setCachedLayout,
|
||||||
restoreCachedLayout,
|
restoreCachedLayout,
|
||||||
set,
|
set,
|
||||||
|
getNumViewportPanes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@ -319,6 +339,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
setCachedLayout,
|
setCachedLayout,
|
||||||
restoreCachedLayout,
|
restoreCachedLayout,
|
||||||
set,
|
set,
|
||||||
|
getNumViewportPanes,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
@ -331,6 +352,7 @@ export function ViewportGridProvider({ children, service }) {
|
|||||||
restoreCachedLayout,
|
restoreCachedLayout,
|
||||||
reset,
|
reset,
|
||||||
set,
|
set,
|
||||||
|
getNumViewportPanes,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -54,20 +54,35 @@ function ViewerViewportGrid(props) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match each viewport individually
|
const gridDisplaySetUIDs = [];
|
||||||
const numViewports = viewportGrid.numRows * viewportGrid.numCols;
|
const blankViewportIndices = [];
|
||||||
|
|
||||||
|
// Match each viewport individually.
|
||||||
|
const numViewports = viewportGridService.getNumViewportPanes();
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let viewportIndex = 0;
|
let viewportIndex = 0;
|
||||||
viewportIndex < numViewports;
|
viewportIndex < numViewports;
|
||||||
viewportIndex++
|
viewportIndex++
|
||||||
) {
|
) {
|
||||||
|
const viewportDisplaySetUIDs =
|
||||||
|
viewports[viewportIndex]?.displaySetInstanceUIDs ?? [];
|
||||||
|
|
||||||
if (hpAlreadyApplied.get(viewportIndex)) {
|
if (hpAlreadyApplied.get(viewportIndex)) {
|
||||||
|
gridDisplaySetUIDs.push(...viewportDisplaySetUIDs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if current viewport doesn't have a match
|
// if current viewport doesn't have a match
|
||||||
if (viewportMatchDetails.get(viewportIndex) === undefined) {
|
if (viewportMatchDetails.get(viewportIndex) === undefined) {
|
||||||
return;
|
// if the current viewport is empty/blank
|
||||||
|
if (viewportDisplaySetUIDs.length === 0) {
|
||||||
|
blankViewportIndices.push(viewportIndex);
|
||||||
|
} else {
|
||||||
|
gridDisplaySetUIDs.push(...viewportDisplaySetUIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { displaySetsInfo, viewportOptions } = viewportMatchDetails.get(
|
const { displaySetsInfo, viewportOptions } = viewportMatchDetails.get(
|
||||||
@ -87,6 +102,8 @@ function ViewerViewportGrid(props) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gridDisplaySetUIDs.push(...displaySetUIDsToHang);
|
||||||
|
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
viewportIndex: viewportIndex,
|
viewportIndex: viewportIndex,
|
||||||
displaySetInstanceUIDs: displaySetUIDsToHang,
|
displaySetInstanceUIDs: displaySetUIDsToHang,
|
||||||
@ -111,6 +128,25 @@ function ViewerViewportGrid(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blankViewportIndices.forEach((blankVPIndex: number) => {
|
||||||
|
// try to fill the empty viewport with a display set not already in the grid
|
||||||
|
const displaySetsNotInGrid = availableDisplaySets.filter(
|
||||||
|
displaySet =>
|
||||||
|
gridDisplaySetUIDs.indexOf(displaySet.displaySetInstanceUID) === -1
|
||||||
|
);
|
||||||
|
|
||||||
|
if (displaySetsNotInGrid.length > 0) {
|
||||||
|
const displaySetUIDToAdd =
|
||||||
|
displaySetsNotInGrid[0].displaySetInstanceUID;
|
||||||
|
gridDisplaySetUIDs.push(displaySetUIDToAdd);
|
||||||
|
|
||||||
|
viewportGridService.setDisplaySetsForViewport({
|
||||||
|
viewportIndex: blankVPIndex,
|
||||||
|
displaySetInstanceUIDs: [displaySetUIDToAdd],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[viewportGrid, numRows, numCols]
|
[viewportGrid, numRows, numCols]
|
||||||
);
|
);
|
||||||
@ -178,6 +214,20 @@ function ViewerViewportGrid(props) {
|
|||||||
};
|
};
|
||||||
}, [viewports]);
|
}, [viewports]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { unsubscribe } = hangingProtocolService.subscribe(
|
||||||
|
hangingProtocolService.EVENTS.STAGE_CHANGE,
|
||||||
|
() => {
|
||||||
|
const displaySets = DisplaySetService.getActiveDisplaySets();
|
||||||
|
updateDisplaySetsForViewports(displaySets);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
}, [viewports]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { unsubscribe } = measurementService.subscribe(
|
const { unsubscribe } = measurementService.subscribe(
|
||||||
measurementService.EVENTS.JUMP_TO_MEASUREMENT,
|
measurementService.EVENTS.JUMP_TO_MEASUREMENT,
|
||||||
@ -314,7 +364,8 @@ function ViewerViewportGrid(props) {
|
|||||||
const getViewportPanes = useCallback(() => {
|
const getViewportPanes = useCallback(() => {
|
||||||
const viewportPanes = [];
|
const viewportPanes = [];
|
||||||
|
|
||||||
for (let i = 0; i < viewports.length; i++) {
|
const numViewports = viewportGridService.getNumViewportPanes();
|
||||||
|
for (let i = 0; i < numViewports; i++) {
|
||||||
const viewportIndex = i;
|
const viewportIndex = i;
|
||||||
const isActive = activeViewportIndex === viewportIndex;
|
const isActive = activeViewportIndex === viewportIndex;
|
||||||
const paneMetadata = viewports[i] || {};
|
const paneMetadata = viewports[i] || {};
|
||||||
@ -388,7 +439,7 @@ function ViewerViewportGrid(props) {
|
|||||||
<ViewportComponent
|
<ViewportComponent
|
||||||
displaySets={displaySets}
|
displaySets={displaySets}
|
||||||
viewportIndex={viewportIndex}
|
viewportIndex={viewportIndex}
|
||||||
viewportLabel={viewports.length > 1 ? viewportLabel : ''}
|
viewportLabel={numViewports > 1 ? viewportLabel : ''}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
viewportOptions={viewportOptions}
|
viewportOptions={viewportOptions}
|
||||||
displaySetOptions={displaySetOptions}
|
displaySetOptions={displaySetOptions}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user