fix(viewports): The display of linked viewports during drag and drop has a race (#3286)

* fix: The display of linked viewports during drag and drop has a race

* PR review comments

* fix: Segmentation display two up

* Removing console logs

* Fix the blank viewport can have stuff added to it

* Fix the null name on HP module

* Fix the navigate to initial image

* Fix the nth interleave loader

* Fix the unit tests

* PR comments - docs mostly

* fix: Exception thrown on change displayset after double click
This commit is contained in:
Bill Wallace 2023-03-31 11:58:48 -04:00 committed by GitHub
parent ca3b83b2b6
commit 5e42a42b5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 215 additions and 195 deletions

View File

@ -31,8 +31,6 @@ async function _hydrateSEGDisplaySet({
displaySetInstanceUID displaySetInstanceUID
); );
viewportGridService.setDisplaySetsForViewports(updatedViewports);
// Todo: fix this after we have a better way for stack viewport segmentations // Todo: fix this after we have a better way for stack viewport segmentations
// check every viewport in the viewports to see if the displaySetInstanceUID // check every viewport in the viewports to see if the displaySetInstanceUID
@ -50,7 +48,7 @@ async function _hydrateSEGDisplaySet({
); );
if (shouldDisplaySeg) { if (shouldDisplaySeg) {
viewportGridService.setDisplaySetsForViewport({ updatedViewports.push({
viewportIndex: index, viewportIndex: index,
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs, displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
viewportOptions: { viewportOptions: {
@ -62,6 +60,9 @@ async function _hydrateSEGDisplaySet({
} }
}); });
// Do the entire update at once
viewportGridService.setDisplaySetsForViewports(updatedViewports);
return true; return true;
} }

View File

@ -10,7 +10,6 @@ import {
utilities as csUtils, utilities as csUtils,
CONSTANTS, CONSTANTS,
} from '@cornerstonejs/core'; } from '@cornerstonejs/core';
import { Services } from '@ohif/core';
import { setEnabledElement } from '../state'; import { setEnabledElement } from '../state';
@ -23,9 +22,6 @@ import {
import getSOPInstanceAttributes from '../utils/measurementServiceMappings/utils/getSOPInstanceAttributes'; import getSOPInstanceAttributes from '../utils/measurementServiceMappings/utils/getSOPInstanceAttributes';
import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui'; import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui';
import { CornerstoneViewportService } from '../services/ViewportService/CornerstoneViewportService';
import Presentation from '../types/Presentation';
const STACK = 'stack'; const STACK = 'stack';
function areEqual(prevProps, nextProps) { function areEqual(prevProps, nextProps) {
@ -407,7 +403,6 @@ const OHIFCornerstoneViewport = React.memo(props => {
lutPresentation: lutPresentation:
lutPresentationStore[presentationIds?.lutPresentationId], lutPresentationStore[presentationIds?.lutPresentationId],
}; };
console.log('Using presentations', presentations);
cornerstoneViewportService.setViewportData( cornerstoneViewportService.setViewportData(
viewportIndex, viewportIndex,

View File

@ -11,7 +11,7 @@ const mpr: Types.HangingProtocol.Protocol = {
// Unknown number of priors referenced - so just match any study // Unknown number of priors referenced - so just match any study
numberOfPriorsReferenced: 0, numberOfPriorsReferenced: 0,
protocolMatchingRules: [], protocolMatchingRules: [],
imageLoadStrategy: 'interleaveTopToBottom', imageLoadStrategy: 'nth',
callbacks: { callbacks: {
// Switches out of MPR mode when the layout change button is used // Switches out of MPR mode when the layout change button is used
onLayoutChange: [ onLayoutChange: [
@ -303,11 +303,11 @@ const mprAnd3DVolumeViewport = {
function getHangingProtocolModule() { function getHangingProtocolModule() {
return [ return [
{ {
id: 'mpr', name: 'mpr',
protocol: mpr, protocol: mpr,
}, },
{ {
id: mprAnd3DVolumeViewport.id, name: mprAnd3DVolumeViewport.id,
protocol: mprAnd3DVolumeViewport, protocol: mprAnd3DVolumeViewport,
}, },
]; ];

View File

@ -155,9 +155,14 @@ class CornerstoneViewportService extends PubSubService
/** /**
* 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.
*
* This is called when the element goes away entirely - with new viewportId's
* created for every new viewport, this will be called whenever the set of
* viewports is changed, but NOT when the viewport position changes only.
*
* @param viewportIndex * @param viewportIndex
*/ */
public disableElement(viewportIndex: number) { public disableElement(viewportIndex: number): void {
const viewportInfo = this.viewportsInfo.get(viewportIndex); const viewportInfo = this.viewportsInfo.get(viewportIndex);
if (!viewportInfo) { if (!viewportInfo) {
return; return;
@ -254,12 +259,6 @@ class CornerstoneViewportService extends PubSubService
viewportInfo.setDisplaySetOptions(displaySetOptions); viewportInfo.setDisplaySetOptions(displaySetOptions);
viewportInfo.setViewportData(viewportData); viewportInfo.setViewportData(viewportData);
this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
viewportData,
viewportIndex,
viewportId,
});
const element = viewportInfo.getElement(); const element = viewportInfo.getElement();
const type = viewportInfo.getViewportType(); const type = viewportInfo.getViewportType();
const background = viewportInfo.getBackground(); const background = viewportInfo.getBackground();
@ -283,6 +282,15 @@ class CornerstoneViewportService extends PubSubService
const viewport = renderingEngine.getViewport(viewportId); const viewport = renderingEngine.getViewport(viewportId);
this._setDisplaySets(viewport, viewportData, viewportInfo, presentations); this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
// The broadcast event here ensures that listeners have a valid, up to date
// viewport to access. Doing it too early can result in exceptions or
// invalid data.
this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
viewportData,
viewportIndex,
viewportId,
});
} }
public getCornerstoneViewport( public getCornerstoneViewport(
@ -386,11 +394,7 @@ class CornerstoneViewportService extends PubSubService
} }
} }
// There is a bug in CS3D that the setStack does not viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
// navigate to the desired image.
viewport.setStack(imageIds, 0).then(() => {
// The scroll, however, works fine in CS3D
viewport.scroll(initialImageIndexToUse);
viewport.setProperties(properties); viewport.setProperties(properties);
const camera = presentations.positionPresentation?.camera; const camera = presentations.positionPresentation?.camera;
if (camera) viewport.setCamera(camera); if (camera) viewport.setCamera(camera);
@ -621,6 +625,10 @@ class CornerstoneViewportService extends PubSubService
const viewportInfo = this.getViewportInfo(viewport.id); const viewportInfo = this.getViewportInfo(viewport.id);
if (!viewportInfo) {
console.warn('Viewport info not defined for', viewport.id);
}
const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id); const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id);
csToolsUtils.segmentation.triggerSegmentationRender(toolGroup.id); csToolsUtils.segmentation.triggerSegmentationRender(toolGroup.id);

View File

@ -18,7 +18,6 @@ const viewportIdVolumeInputArrayMap = new Map<string, unknown[]>();
export default function interleaveNthLoader({ export default function interleaveNthLoader({
data: { viewportId, volumeInputArray }, data: { viewportId, volumeInputArray },
displaySetsMatchDetails, displaySetsMatchDetails,
viewportMatchDetails: matchDetails,
}) { }) {
viewportIdVolumeInputArrayMap.set(viewportId, volumeInputArray); viewportIdVolumeInputArrayMap.set(viewportId, volumeInputArray);
@ -29,6 +28,7 @@ export default function interleaveNthLoader({
const volume = cache.getVolume(volumeId); const volume = cache.getVolume(volumeId);
if (!volume) { if (!volume) {
console.log("interleaveNthLoader::No volume, can't load it");
return; return;
} }
@ -39,33 +39,6 @@ export default function interleaveNthLoader({
} }
} }
/**
* The following is checking if all the viewports that were matched in the HP has been
* successfully created their cornerstone viewport or not. Todo: This can be
* improved by not checking it, and as soon as the matched DisplaySets have their
* volume loaded, we start the loading, but that comes at the cost of viewports
* not being created yet (e.g., in a 10 viewport ptCT fusion, when one ct viewport and one
* pt viewport are created we have a guarantee that the volumes are created in the cache
* but the rest of the viewports (fusion, mip etc.) are not created yet. So
* we can't initiate setting the volumes for those viewports. One solution can be
* to add an event when a viewport is created (not enabled element event) and then
* listen to it and as the other viewports are created we can set the volumes for them
* since volumes are already started loading.
*/
if (matchDetails.size !== viewportIdVolumeInputArrayMap.size) {
return;
}
// Check if all the matched volumes are loaded
for (const [_, details] of displaySetsMatchDetails.entries()) {
const { SeriesInstanceUID } = details;
// HangingProtocol has matched, but don't have all the volumes created yet, so return
if (!Array.from(volumeIdMapsToLoad.values()).includes(SeriesInstanceUID)) {
return;
}
}
const volumeIds = Array.from(volumeIdMapsToLoad.keys()).slice(); const volumeIds = Array.from(volumeIdMapsToLoad.keys()).slice();
// get volumes from cache // get volumes from cache
const volumes = volumeIds.map(volumeId => { const volumes = volumeIds.map(volumeId => {

View File

@ -290,7 +290,7 @@ const defaultProtocol = {
function getHangingProtocolModule() { function getHangingProtocolModule() {
return [ return [
{ {
id: defaultProtocol.id, name: defaultProtocol.id,
protocol: defaultProtocol, protocol: defaultProtocol,
}, },
]; ];

View File

@ -2,7 +2,7 @@ import hpMN from './hpMN';
const hangingProtocols = [ const hangingProtocols = [
{ {
id: '@ohif/hp-extension.mn', name: '@ohif/hp-extension.mn',
protocol: hpMN, protocol: hpMN,
}, },
]; ];

View File

@ -323,7 +323,7 @@ const ptCT = {
function getHangingProtocolModule() { function getHangingProtocolModule() {
return [ return [
{ {
id: ptCT.id, name: ptCT.id,
protocol: ptCT, protocol: ptCT,
}, },
]; ];

View File

@ -206,40 +206,40 @@ describe('ExtensionManager.ts', () => {
const extension = { const extension = {
id: 'hello-world', id: 'hello-world',
getViewportModule: () => { getViewportModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getSopClassHandlerModule: () => { getSopClassHandlerModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getPanelModule: () => { getPanelModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getToolbarModule: () => { getToolbarModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getCommandsModule: () => { getCommandsModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getLayoutTemplateModule: () => { getLayoutTemplateModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getDataSourcesModule: () => { getDataSourcesModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getHangingProtocolModule: () => { getHangingProtocolModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getContextModule: () => { getContextModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getUtilityModule: () => { getUtilityModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getCustomizationModule: () => { getCustomizationModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
getStateSyncModule: () => { getStateSyncModule: () => {
return [{}]; return [{ name: 'test' }];
}, },
}; };

View File

@ -283,6 +283,11 @@ export default class ExtensionManager {
// Default for most extension points, // Default for most extension points,
// Just adds each entry ready for consumption by mode. // Just adds each entry ready for consumption by mode.
extensionModule.forEach(element => { extensionModule.forEach(element => {
if (!element.name) {
throw new Error(
`Extension ID ${extensionId} module ${moduleType} element has no name`
);
}
const id = `${extensionId}.${moduleType}.${element.name}`; const id = `${extensionId}.${moduleType}.${element.name}`;
element.id = id; element.id = id;
this.modulesMap[id] = element; this.modulesMap[id] = element;
@ -366,10 +371,10 @@ export default class ExtensionManager {
_initHangingProtocolsModule = (extensionModule, extensionId) => { _initHangingProtocolsModule = (extensionModule, extensionId) => {
const { hangingProtocolService } = this._servicesManager.services; const { hangingProtocolService } = this._servicesManager.services;
extensionModule.forEach(({ id, protocol }) => { extensionModule.forEach(({ name, protocol }) => {
if (protocol) { if (protocol) {
// Only auto-register if protocol specified, otherwise let mode register // Only auto-register if protocol specified, otherwise let mode register
hangingProtocolService.addProtocol(id, protocol); hangingProtocolService.addProtocol(name, protocol);
} }
}); });
}; };

View File

@ -26,7 +26,6 @@ class ViewportGridService extends PubSubService {
public setServiceImplementation({ public setServiceImplementation({
getState: getStateImplementation, getState: getStateImplementation,
setActiveViewportIndex: setActiveViewportIndexImplementation, setActiveViewportIndex: setActiveViewportIndexImplementation,
setDisplaySetsForViewport: setDisplaySetsForViewportImplementation,
setDisplaySetsForViewports: setDisplaySetsForViewportsImplementation, setDisplaySetsForViewports: setDisplaySetsForViewportsImplementation,
setLayout: setLayoutImplementation, setLayout: setLayoutImplementation,
reset: resetImplementation, reset: resetImplementation,
@ -40,9 +39,6 @@ class ViewportGridService extends PubSubService {
if (setActiveViewportIndexImplementation) { if (setActiveViewportIndexImplementation) {
this.serviceImplementation._setActiveViewportIndex = setActiveViewportIndexImplementation; this.serviceImplementation._setActiveViewportIndex = setActiveViewportIndexImplementation;
} }
if (setDisplaySetsForViewportImplementation) {
this.serviceImplementation._setDisplaySetsForViewport = setDisplaySetsForViewportImplementation;
}
if (setDisplaySetsForViewportsImplementation) { if (setDisplaySetsForViewportsImplementation) {
this.serviceImplementation._setDisplaySetsForViewports = setDisplaySetsForViewportsImplementation; this.serviceImplementation._setDisplaySetsForViewports = setDisplaySetsForViewportsImplementation;
} }
@ -77,22 +73,13 @@ class ViewportGridService extends PubSubService {
return this.serviceImplementation._getState(); return this.serviceImplementation._getState();
} }
public setDisplaySetsForViewport({ public setDisplaySetsForViewport(props) {
viewportIndex, // Just update a single viewport, but use the multi-viewport update for it.
displaySetInstanceUIDs, this.serviceImplementation._setDisplaySetsForViewports([props]);
viewportOptions,
displaySetOptions,
}) {
this.serviceImplementation._setDisplaySetsForViewport({
viewportIndex,
displaySetInstanceUIDs,
viewportOptions,
displaySetOptions,
});
} }
public setDisplaySetsForViewports(viewports) { public setDisplaySetsForViewports(props) {
this.serviceImplementation._setDisplaySetsForViewports(viewports); this.serviceImplementation._setDisplaySetsForViewports(props);
} }
/** /**

View File

@ -34,54 +34,85 @@ const DEFAULT_STATE = {
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 viewport to re-use, and then set the viewportId * 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 idSet * @param viewportIdSet
* @param viewport * @param viewport
* @param stateViewports * @param stateViewports
* @returns * @returns
*/ */
const reuseViewport = (idSet, viewport, stateViewports) => { const reuseViewportId = (viewportIdSet: Set, viewport, stateViewports) => {
const oldIds = {};
for (const oldViewport of stateViewports) { for (const oldViewport of stateViewports) {
const { viewportId: oldId } = oldViewport; const { viewportId: oldId } = oldViewport;
oldIds[oldId] = true; if (!oldId) {
if (!oldId || idSet[oldId]) continue; // This occurs on startup, so skip re-using it
if (
!isEqual(
oldViewport.displaySetInstanceUIDs,
viewport.displaySetInstanceUIDs
)
) {
continue; continue;
} }
idSet[oldId] = true; if (viewportIdSet.has(oldId)) {
// TODO re-use viewports once the flickering/wrong size redraw is fixed // oldId is already used - we can't reuse it
// return { continue;
// ...oldViewport, }
// ...viewport, if (isReuseableViewport(oldViewport, viewport)) {
// viewportOptions: { viewportIdSet.add(oldId);
// ...oldViewport.viewportOptions, // 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,
},
};
}
}
// viewportId: oldViewport.viewportId, // 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);
// Find a viewport instance number different from earlier viewports having // Loop over viewport counters in case of a really long lived display
// the same presentationIds as this one would - will be less than 10k viewportCounter = (viewportCounter + 1) % 100000;
// viewports hopefully :-) // viewportOptions is already a copy, so can just update direct
for (let i = 0; i < 10000; i++) { viewport.viewportOptions.viewportId = viewportId;
const viewportId = 'viewport-' + i;
if (idSet[viewportId] || oldIds[viewportId]) continue; return {
idSet[viewportId] = true; ...viewport,
return { id: viewportId,
...viewport, viewportId,
viewportId, };
viewportOptions: { ...viewport.viewportOptions, viewportId },
};
}
throw new Error('No ID found');
}; };
export function ViewportGridProvider({ children, service }) { export function ViewportGridProvider({ children, service }) {
@ -90,53 +121,72 @@ export function ViewportGridProvider({ children, service }) {
case 'SET_ACTIVE_VIEWPORT_INDEX': { case 'SET_ACTIVE_VIEWPORT_INDEX': {
return { ...state, ...{ activeViewportIndex: action.payload } }; return { ...state, ...{ activeViewportIndex: action.payload } };
} }
case 'SET_DISPLAYSET_FOR_VIEWPORT': {
const payload = action.payload;
const { viewportIndex, displaySetInstanceUIDs } = payload;
// Note: there should be no inheritance happening at this level,
// we can't assume the new displaySet can inherit the previous
// displaySet's or viewportOptions at all. For instance, dragging
// and dropping a SEG/RT displaySet without any viewportOptions
// or displaySetOptions should not inherit the previous displaySet's
// which might have been a PDF Viewport. The viewport itself
// will deal with inheritance if required. Here is just a simple
// provider.
const viewport = state.viewports[viewportIndex] || {};
const viewportOptions = { ...payload.viewportOptions };
const displaySetOptions = payload.displaySetOptions || [];
if (displaySetOptions.length === 0) {
// Only copy index 0, as that is all that is currently supported by this
// method call.
displaySetOptions.push({ ...viewport.displaySetOptions?.[0] });
}
/**
* Sets the display sets for multiple viewports.
* This is a replacement for the older set display set for viewport (single)
* because the old one had race conditions wherein the viewports could
* render partially in various ways causing exceptions.
*/
case 'SET_DISPLAYSETS_FOR_VIEWPORTS': {
const { payload } = action;
const viewports = state.viewports.slice(); const viewports = state.viewports.slice();
let newView = { // Have the initial id set contain all viewports not updated here
...viewport, const viewportIdSet = new Set();
displaySetInstanceUIDs, viewports.forEach((viewport, index) => {
viewportOptions, if (!viewport.viewportId) return;
displaySetOptions, const isUpdated = payload.find(
viewportLabel: viewportLabels[viewportIndex], newViewport => newViewport.viewportIndex === index
}; );
viewportOptions.presentationIds = getPresentationIds( if (isUpdated) {
newView, return;
viewports }
); viewportIdSet.add(viewport.viewportId);
});
// Make sure we assign a viewport id for (const updatedViewport of payload) {
newView = reuseViewport({}, newView, state.viewports); // Use the newly provide viewportOptions and display set options
console.log( // when provided, and otherwise fall back to the previous ones.
'Creating new viewport', // That allows for easy updates of just the display set.
viewportIndex, const { viewportIndex, displaySetInstanceUIDs } = updatedViewport;
newView.viewportOptions.viewportId, const previousViewport = viewports[viewportIndex] || {};
displaySetInstanceUIDs, const viewportOptions = {
displaySetOptions ...(updatedViewport.viewportOptions ||
); previousViewport.viewportOptions),
};
viewports[viewportIndex] = newView; const displaySetOptions = updatedViewport.displaySetOptions || [];
if (!displaySetOptions.length) {
// Copy all the display set options, assuming a full set of displa
// set UID's is provided.
displaySetOptions.push(...previousViewport.displaySetOptions);
if (!displaySetOptions.length) {
displaySetOptions.push({});
}
}
let newViewport = {
...previousViewport,
displaySetInstanceUIDs,
viewportOptions,
displaySetOptions,
viewportLabel: viewportLabels[viewportIndex],
};
viewportOptions.presentationIds = getPresentationIds(
newViewport,
viewports
);
newViewport = reuseViewportId(
viewportIdSet,
newViewport,
state.viewports
);
newViewport.viewportIndex = previousViewport.viewportIndex;
viewports[viewportIndex] = newViewport;
}
return { ...state, viewports }; return { ...state, viewports };
} }
@ -203,13 +253,13 @@ export function ViewportGridProvider({ children, service }) {
activeViewportIndexToSet = activeViewportIndexToSet ?? 0; activeViewportIndexToSet = activeViewportIndexToSet ?? 0;
const viewportIdSet = {}; const viewportIdSet = new Set();
for ( for (
let viewportIndex = 0; let viewportIndex = 0;
viewportIndex < viewports.length; viewportIndex < viewports.length;
viewportIndex++ viewportIndex++
) { ) {
const viewport = reuseViewport( const viewport = reuseViewportId(
viewportIdSet, viewportIdSet,
viewports[viewportIndex], viewports[viewportIndex],
state.viewports state.viewports
@ -268,36 +318,15 @@ export function ViewportGridProvider({ children, service }) {
[dispatch] [dispatch]
); );
const setDisplaySetsForViewport = useCallback( const setDisplaySetsForViewports = useCallback(
({ viewports =>
viewportIndex,
displaySetInstanceUIDs,
viewportOptions,
displaySetSelectors,
displaySetOptions,
}) =>
dispatch({ dispatch({
type: 'SET_DISPLAYSET_FOR_VIEWPORT', type: 'SET_DISPLAYSETS_FOR_VIEWPORTS',
payload: { payload: viewports,
viewportIndex,
displaySetInstanceUIDs,
viewportOptions,
displaySetSelectors,
displaySetOptions,
},
}), }),
[dispatch] [dispatch]
); );
const setDisplaySetsForViewports = useCallback(
viewports => {
viewports.forEach(data => {
setDisplaySetsForViewport(data);
});
},
[setDisplaySetsForViewport]
);
const setLayout = useCallback( const setLayout = useCallback(
({ ({
layoutType, layoutType,
@ -355,7 +384,6 @@ export function ViewportGridProvider({ children, service }) {
service.setServiceImplementation({ service.setServiceImplementation({
getState, getState,
setActiveViewportIndex, setActiveViewportIndex,
setDisplaySetsForViewport,
setDisplaySetsForViewports, setDisplaySetsForViewports,
setLayout, setLayout,
reset, reset,
@ -368,7 +396,6 @@ export function ViewportGridProvider({ children, service }) {
getState, getState,
service, service,
setActiveViewportIndex, setActiveViewportIndex,
setDisplaySetsForViewport,
setDisplaySetsForViewports, setDisplaySetsForViewports,
setLayout, setLayout,
reset, reset,
@ -379,7 +406,6 @@ export function ViewportGridProvider({ children, service }) {
const api = { const api = {
getState, getState,
setActiveViewportIndex: index => service.setActiveViewportIndex(index), // run it through the service itself since we want to publish events setActiveViewportIndex: index => service.setActiveViewportIndex(index), // run it through the service itself since we want to publish events
setDisplaySetsForViewport,
setDisplaySetsForViewports, setDisplaySetsForViewports,
setLayout: layout => service.setLayout(layout), // run it through the service itself since we want to publish events setLayout: layout => service.setLayout(layout), // run it through the service itself since we want to publish events
reset, reset,

View File

@ -42,6 +42,31 @@ window.config = {
singlepart: 'bulkdata,video,pdf', singlepart: 'bulkdata,video,pdf',
}, },
}, },
{
friendlyName: 'dcmjs DICOMWeb Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'ohif',
configuration: {
name: 'aws',
// old server
// wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
// qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
// wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
// new server
wadoUriRoot: 'https://domvja9iplmyu.cloudfront.net/dicomweb',
qidoRoot: 'https://domvja9iplmyu.cloudfront.net/dicomweb',
wadoRoot: 'https://domvja9iplmyu.cloudfront.net/dicomweb',
qidoSupportsIncludeField: false,
supportsReject: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
enableStudyLazyLoad: true,
supportsFuzzyMatching: false,
supportsWildcard: true,
staticWado: true,
singlepart: 'bulkdata,video,pdf',
},
},
{ {
friendlyName: 'AWS S3 OHIF', friendlyName: 'AWS S3 OHIF',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',