fix: Hanging protocol state fixes (#3242)
* fix: Some residual issues with hanging protocol after state sync fix: Some issues introduced by the state syncing changes * fix: PR fixes, mostly code cleanup * Improve a race condition in an automated test * Remove obsolete code * PR fixes
This commit is contained in:
1 parent
544bf55a4f
commit
ee4e8a4105
19 files changed
+268
-171
No files matched your search
@@ -8,7 +8,7 @@ import React, {
|
||||
import PropTypes from 'prop-types';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import viewportLabels from '../utils/viewportLabels';
|
||||
import getPresentationId from './getPresentationId';
|
||||
import getPresentationIds from './getPresentationIds';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
activeViewportIndex: 0,
|
||||
@@ -69,7 +69,7 @@ const reuseViewport = (idSet, viewport, stateViewports) => {
|
||||
// };
|
||||
}
|
||||
// Find a viewport instance number different from earlier viewports having
|
||||
// the same presentationId as this one would - will be less than 10k
|
||||
// the same presentationIds as this one would - will be less than 10k
|
||||
// viewports hopefully :-)
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
const viewportId = 'viewport-' + i;
|
||||
@@ -121,7 +121,10 @@ export function ViewportGridProvider({ children, service }) {
|
||||
displaySetOptions,
|
||||
viewportLabel: viewportLabels[viewportIndex],
|
||||
};
|
||||
viewportOptions.presentationId = getPresentationId(newView, viewports);
|
||||
viewportOptions.presentationIds = getPresentationIds(
|
||||
newView,
|
||||
viewports
|
||||
);
|
||||
|
||||
// Make sure we assign a viewport id
|
||||
newView = reuseViewport({}, newView, state.viewports);
|
||||
@@ -207,8 +210,8 @@ export function ViewportGridProvider({ children, service }) {
|
||||
viewports[viewportIndex],
|
||||
state.viewports
|
||||
);
|
||||
if (!viewport.viewportOptions.presentationId) {
|
||||
viewport.viewportOptions.presentationId = getPresentationId(
|
||||
if (!viewport.viewportOptions.presentationIds) {
|
||||
viewport.viewportOptions.presentationIds = getPresentationIds(
|
||||
viewport,
|
||||
viewports
|
||||
);
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Selects a presentation ID to use for this viewport.
|
||||
* This is done to allow the same display set to be displayed more than once
|
||||
* on screen, with different attributes such as window level and initial position.
|
||||
* Then, when redisplaying that, the nearest/most common attribute is re-used.
|
||||
*
|
||||
* For example, for display set <displaySetUID>, in a viewport of type volume,
|
||||
* the generated presentationID might be
|
||||
* `volume:axial:<displaySetUID>`. This can then be used to store and retrieve
|
||||
* presentation information in state sync service 'presentationSync' state.
|
||||
*
|
||||
* The generated value attempts to generate a unique value for every type
|
||||
* of viewport which should have it's own presentation information. Thus, the
|
||||
* following values are used for presentation ID:
|
||||
*
|
||||
* 1. viewportType - since the presentation information for a volume is different than for a stack
|
||||
* 2. orientation - since the camera is different for different orientations
|
||||
* 3. display set instance UID - since different display sets should get displayed differently
|
||||
* 4. instance count - since displaying the same series twice should allow applying different window level etc
|
||||
*
|
||||
* @param viewport requiring a presentation Id
|
||||
* @param viewports is the list of viewports being shown. Any presentation ID's
|
||||
* among them must not be re-used in order to have each viewport have it's own presentation ID.
|
||||
* @returns Presentation ID id, or undefined if nothing displayed
|
||||
*/
|
||||
const getPresentationId = (viewport, viewports): string => {
|
||||
if (!viewport) return;
|
||||
const { viewportOptions, displaySetInstanceUIDs } = viewport;
|
||||
if (!viewportOptions || !displaySetInstanceUIDs?.length) {
|
||||
console.log('No viewport type or display sets in', viewport);
|
||||
return;
|
||||
}
|
||||
|
||||
const viewportType = viewportOptions.viewportType || 'stack';
|
||||
const idArr = [viewportType, 0, ...displaySetInstanceUIDs];
|
||||
if (viewportOptions.orientation) {
|
||||
idArr.splice(2, 0, viewportOptions.orientation);
|
||||
}
|
||||
|
||||
// Allow setting a custom presentation prefix in the hanging protocol
|
||||
// This allows defining new
|
||||
// presentation groups to be set automatically when one knows that the
|
||||
// same display set will be displayed in different ways.
|
||||
// This is the recommended way to manage a hanging protocol which displays
|
||||
// multiple views of a single display set, eg to display brain, bone, soft
|
||||
// tissue views in different viewports.
|
||||
if (viewportOptions.presentationPrefix) {
|
||||
idArr.push(viewportOptions.presentationPrefix);
|
||||
}
|
||||
if (!viewports) {
|
||||
console.log('viewports not defined', idArr.join(','));
|
||||
return idArr.join('&');
|
||||
}
|
||||
|
||||
// This code finds the first unique index to add to the presentation id so that
|
||||
// two viewports containing the same display set in the same type of viewport
|
||||
// can have different presentation information. This allows comparison of
|
||||
// a single display set in two or more viewports, when the user has simply
|
||||
// dragged and dropped the view in twice. For example, it allows displaying
|
||||
// bone, brain and soft tissue views of a single display set, and to still
|
||||
// remember the specific changes to each viewport.
|
||||
for (let displayInstance = 0; displayInstance < 128; displayInstance++) {
|
||||
idArr[1] = displayInstance;
|
||||
const testId = idArr.join('&');
|
||||
if (!viewports.find(it => it.viewportOptions?.presentationId === testId)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const id = idArr.join('&');
|
||||
return id;
|
||||
};
|
||||
|
||||
export default getPresentationId;
|
||||
@@ -0,0 +1,120 @@
|
||||
const JOIN_STR = '&';
|
||||
|
||||
// The default lut presentation id if none defined
|
||||
const DEFAULT = 'default';
|
||||
|
||||
// This code finds the first unique index to add to the presentation id so that
|
||||
// two viewports containing the same display set in the same type of viewport
|
||||
// can have different presentation information. This allows comparison of
|
||||
// a single display set in two or more viewports, when the user has simply
|
||||
// dragged and dropped the view in twice. For example, it allows displaying
|
||||
// bone, brain and soft tissue views of a single display set, and to still
|
||||
// remember the specific changes to each viewport.
|
||||
const addUniqueIndex = (arr, key, viewports) => {
|
||||
arr.push(0);
|
||||
// The 128 is just a value that is larger than how many viewports we
|
||||
// display at once, used as an upper bound on how many unique presentation
|
||||
// ID's might exist for a single display set at once.
|
||||
for (let displayInstance = 0; displayInstance < 128; displayInstance++) {
|
||||
arr[arr.length - 1] = displayInstance;
|
||||
const testId = arr.join(JOIN_STR);
|
||||
if (
|
||||
!viewports.find(
|
||||
viewport => viewport.viewportOptions?.presentationIds?.[key] === testId
|
||||
)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getLutId = (ds): string => {
|
||||
if (!ds || !ds.options) return DEFAULT;
|
||||
if (ds.options.id) return ds.options.id;
|
||||
const arr = Object.entries(ds.options).map(([key, val]) => `${key}=${value}`);
|
||||
if (!arr.length) return DEFAULT;
|
||||
return arr.join(JOIN_STR);
|
||||
};
|
||||
|
||||
export type PresentationIds = {
|
||||
positionPresentationId?: string;
|
||||
lutPresentationId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a set of presentation IDs for a viewport. The presentation IDs are
|
||||
* used to remember the presentation state of the viewport when it is navigated
|
||||
* to different layouts.
|
||||
*
|
||||
* The design of this is setup to allow preserving the view information in the
|
||||
* following cases:
|
||||
*
|
||||
*
|
||||
* * If a set of display sets was previously displayed in the same initial
|
||||
* position as it is currently being asked to be displayed,
|
||||
* then remember the camera position as previously displayed
|
||||
*
|
||||
* * If a set of display sets was previously displayed with the same initial
|
||||
* LUT conditions, then remember the last LUT displayed for that display set
|
||||
* and re-apply it.
|
||||
*
|
||||
* * Otherwise, apply the initial hanging protocol specified LUT and camera
|
||||
* position to new display sets.
|
||||
*
|
||||
* This means generating two presentationId keys:
|
||||
*
|
||||
* `positionPresentationId`
|
||||
*
|
||||
* Used for getting the camera/initial position state sync values.
|
||||
* This is a combination of:
|
||||
* * `viewportOptions.id`
|
||||
* * `viewportOptions.orientation`
|
||||
* * display set UID's - as displayed for this viewport, excluding seg
|
||||
* * a unique index number if the previous key is already displayed
|
||||
*
|
||||
* `lutPresentationId`
|
||||
*
|
||||
* Used for getting the voi LUT information. Generated from:
|
||||
*
|
||||
* * `displaySetOption[0].options` - including the id if present
|
||||
* * displaySetUID's
|
||||
* * a unique index number if the previously generated key is already
|
||||
* displayed.
|
||||
*
|
||||
* @param viewport requiring a presentation Id
|
||||
* @param viewports is the list of viewports being shown. Any presentation ID's
|
||||
* among them must not be re-used in order to have each viewport have it's own presentation ID.
|
||||
* @returns PresentationIds
|
||||
*/
|
||||
const getPresentationIds = (viewport, viewports): PresentationIds => {
|
||||
if (!viewport) return;
|
||||
const {
|
||||
viewportOptions,
|
||||
displaySetInstanceUIDs,
|
||||
displaySetOptions,
|
||||
} = viewport;
|
||||
if (!viewportOptions || !displaySetInstanceUIDs?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { id, orientation } = viewportOptions;
|
||||
const lutId = getLutId(displaySetOptions[0]);
|
||||
const lutPresentationArr = [lutId];
|
||||
|
||||
const positionPresentationArr = [orientation || 'acquisition'];
|
||||
if (id) positionPresentationArr.push(id);
|
||||
|
||||
for (const uid of displaySetInstanceUIDs) {
|
||||
positionPresentationArr.push(uid);
|
||||
lutPresentationArr.push(uid);
|
||||
}
|
||||
|
||||
addUniqueIndex(positionPresentationArr, 'positionPresentationId', viewports);
|
||||
addUniqueIndex(lutPresentationArr, 'lutPresentationId', viewports);
|
||||
|
||||
const lutPresentationId = lutPresentationArr.join(JOIN_STR);
|
||||
const positionPresentationId = positionPresentationArr.join(JOIN_STR);
|
||||
return { lutPresentationId, positionPresentationId };
|
||||
};
|
||||
|
||||
export default getPresentationIds;
|
||||
@@ -1,5 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import ThumbnailType from './ThumbnailType';
|
||||
import { PresentationIds } from '../contextProviders/getPresentationIds';
|
||||
|
||||
// A few miscellaneous types declared inline here.
|
||||
|
||||
@@ -14,4 +15,4 @@ const StringNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
||||
*/
|
||||
const StringArray = PropTypes.oneOfType([PropTypes.string, PropTypes.array]);
|
||||
|
||||
export { StringNumber, StringArray, ThumbnailType };
|
||||
export { StringNumber, StringArray, ThumbnailType, PresentationIds };
|
||||
Reference in new issue
Block a user