refactor: Viewports state (#1218)

* Organize viewports reducers

* Organize viewports actions

* Add local state to store dom node and remove hack

* Comment usage of dom in vtk

* Fix set of enabledElements

* Fix warning in html viewport

* Update docs for state

* Add commandsmanager to commandsmodule
This commit is contained in:
Igor Octaviano 2019-12-02 13:29:04 -03:00 committed by Danny Brown
parent 0bb3eeb068
commit f396b30166
15 changed files with 505 additions and 444 deletions

View File

@ -2,6 +2,7 @@ import CornerstoneViewport from 'react-cornerstone-viewport';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import throttle from 'lodash.throttle'; import throttle from 'lodash.throttle';
import { setEnabledElement } from './state';
const { setViewportActive, setViewportSpecificData } = OHIF.redux.actions; const { setViewportActive, setViewportSpecificData } = OHIF.redux.actions;
const { const {
@ -80,11 +81,11 @@ const mapDispatchToProps = (dispatch, ownProps) => {
*/ */
onElementEnabled: event => { onElementEnabled: event => {
const enabledElement = event.detail.element; const enabledElement = event.detail.element;
setEnabledElement(viewportIndex, enabledElement);
dispatch( dispatch(
setViewportSpecificData(viewportIndex, { setViewportSpecificData(viewportIndex, {
// TODO: Hack to make sure our plugin info is available from the outset // TODO: Hack to make sure our plugin info is available from the outset
plugin: 'cornerstone', plugin: 'cornerstone',
dom: enabledElement,
}) })
); );
}, },

View File

@ -1,14 +1,12 @@
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools'; import cornerstoneTools from 'cornerstone-tools';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
import { getEnabledElement } from './state';
const scroll = cornerstoneTools.import('util/scroll'); const scroll = cornerstoneTools.import('util/scroll');
const actions = { const actions = {
rotateViewport: ({ viewports, rotation }) => { rotateViewport: ({ viewports, rotation }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
if (enabledElement) { if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement); let viewport = cornerstone.getViewport(enabledElement);
@ -17,10 +15,7 @@ const actions = {
} }
}, },
flipViewportHorizontal: ({ viewports }) => { flipViewportHorizontal: ({ viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
if (enabledElement) { if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement); let viewport = cornerstone.getViewport(enabledElement);
@ -29,10 +24,7 @@ const actions = {
} }
}, },
flipViewportVertical: ({ viewports }) => { flipViewportVertical: ({ viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
if (enabledElement) { if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement); let viewport = cornerstone.getViewport(enabledElement);
@ -40,11 +32,8 @@ const actions = {
cornerstone.setViewport(enabledElement, viewport); cornerstone.setViewport(enabledElement, viewport);
} }
}, },
scaleViewport: ({ viewports, direction }) => { scaleViewport: ({ direction, viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
const step = direction * 0.15; const step = direction * 0.15;
if (enabledElement) { if (enabledElement) {
@ -58,20 +47,14 @@ const actions = {
} }
}, },
resetViewport: ({ viewports }) => { resetViewport: ({ viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
if (enabledElement) { if (enabledElement) {
cornerstone.reset(enabledElement); cornerstone.reset(enabledElement);
} }
}, },
invertViewport: ({ viewports }) => { invertViewport: ({ viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
if (enabledElement) { if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement); let viewport = cornerstone.getViewport(enabledElement);
@ -92,10 +75,7 @@ const actions = {
console.warn('updateDisplaySet: ', direction); console.warn('updateDisplaySet: ', direction);
}, },
clearAnnotations: ({ viewports }) => { clearAnnotations: ({ viewports }) => {
const element = _getActiveViewportEnabledElement( const element = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
if (!element) { if (!element) {
return; return;
} }
@ -147,24 +127,25 @@ const actions = {
}); });
}, },
nextImage: ({ viewports }) => { nextImage: ({ viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
scroll(enabledElement, 1); scroll(enabledElement, 1);
}, },
previousImage: ({ viewports }) => { previousImage: ({ viewports }) => {
const enabledElement = _getActiveViewportEnabledElement( const enabledElement = getEnabledElement(viewports.activeViewportIndex);
viewports.viewportSpecificData,
viewports.activeViewportIndex
);
scroll(enabledElement, -1); scroll(enabledElement, -1);
}, },
getActiveViewportEnabledElement: ({ viewports }) => {
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
return enabledElement;
},
}; };
const definitions = { const definitions = {
getActiveViewportEnabledElement: {
commandFn: actions.getActiveViewportEnabledElement,
storeContexts: ['viewports'],
options: {},
},
rotateViewportCW: { rotateViewportCW: {
commandFn: actions.rotateViewport, commandFn: actions.rotateViewport,
storeContexts: ['viewports'], storeContexts: ['viewports'],
@ -245,15 +226,6 @@ const definitions = {
}, },
}; };
/**
* Grabs `dom` reference for the enabledElement of
* the active viewport
*/
function _getActiveViewportEnabledElement(viewports, activeIndex) {
const activeViewport = viewports[activeIndex] || {};
return activeViewport.dom;
}
export default { export default {
actions, actions,
definitions, definitions,

View File

@ -0,0 +1,20 @@
const state = {
enabledElements: {},
};
/**
* Sets the enabled element `dom` reference for an active viewport.
* @param {HTMLElement} dom Active viewport element.
* @return void
*/
const setEnabledElement = (viewportIndex, element) =>
(state.enabledElements[viewportIndex] = element);
/**
* Grabs the enabled element `dom` reference of an active viewport.
*
* @return {HTMLElement} Active viewport element.
*/
const getEnabledElement = viewportIndex => state.enabledElements[viewportIndex];
export { setEnabledElement, getEnabledElement };

View File

@ -6,7 +6,7 @@ const { setViewportActive } = OHIF.redux.actions;
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { viewportIndex, byteArray } = ownProps; const { viewportIndex, byteArray } = ownProps;
const activeViewportIndex = state.viewports; const { activeViewportIndex } = state.viewports;
return { return {
viewportIndex, viewportIndex,

View File

@ -238,7 +238,7 @@ class DicomHtmlViewport extends Component {
} = this.props; } = this.props;
if (viewportIndex !== activeViewportIndex) { if (viewportIndex !== activeViewportIndex) {
setViewportActive(); setViewportActive(viewportIndex);
} }
}; };

View File

@ -1,3 +1,4 @@
import throttle from 'lodash.throttle';
import { import {
vtkInteractorStyleMPRCrosshairs, vtkInteractorStyleMPRCrosshairs,
vtkInteractorStyleMPRWindowLevel, vtkInteractorStyleMPRWindowLevel,
@ -8,14 +9,14 @@ import {
import setMPRLayout from './utils/setMPRLayout.js'; import setMPRLayout from './utils/setMPRLayout.js';
import setViewportToVTK from './utils/setViewportToVTK.js'; import setViewportToVTK from './utils/setViewportToVTK.js';
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js'; import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
import throttle from 'lodash.throttle';
const { BlendMode } = Constants; const { BlendMode } = Constants;
// TODO: Put this somewhere else const commandsModule = ({ commandsManager }) => {
let apis = {}; // TODO: Put this somewhere else
let apis = {};
async function _getActiveViewportVTKApi(viewports) { async function _getActiveViewportVTKApi(viewports) {
const { const {
numRows, numRows,
numColumns, numColumns,
@ -51,18 +52,57 @@ async function _getActiveViewportVTKApi(viewports) {
} }
return api; return api;
} }
function _setView(api, sliceNormal, viewUp) { function _setView(api, sliceNormal, viewUp) {
const renderWindow = api.genericRenderWindow.getRenderWindow(); const renderWindow = api.genericRenderWindow.getRenderWindow();
const istyle = renderWindow.getInteractor().getInteractorStyle(); const istyle = renderWindow.getInteractor().getInteractorStyle();
istyle.setSliceNormal(...sliceNormal); istyle.setSliceNormal(...sliceNormal);
istyle.setViewUp(...viewUp); istyle.setViewUp(...viewUp);
renderWindow.render(); renderWindow.render();
} }
const actions = { function getVOIFromCornerstoneViewport() {
const dom = commandsManager.runCommand('getActiveViewportEnabledElement');
const cornerstoneElement = cornerstone.getEnabledElement(dom);
if (cornerstoneElement) {
const imageId = cornerstoneElement.image.imageId;
const { modality } = cornerstone.metaData.get(
'generalSeriesModule',
imageId
);
if (modality !== 'PT') {
const { windowWidth, windowCenter } = cornerstoneElement.viewport.voi;
return {
windowWidth,
windowCenter,
};
}
}
}
function setVOI(voi) {
const { windowWidth, windowCenter } = voi;
const lower = windowCenter - windowWidth / 2.0;
const upper = windowCenter + windowWidth / 2.0;
const rgbTransferFunction = apis[0].volumes[0]
.getProperty()
.getRGBTransferFunction(0);
rgbTransferFunction.setRange(lower, upper);
apis.forEach(api => {
api.updateVOI(windowWidth, windowCenter);
});
}
const actions = {
axial: async ({ viewports }) => { axial: async ({ viewports }) => {
const api = await _getActiveViewportVTKApi(viewports); const api = await _getActiveViewportVTKApi(viewports);
@ -183,7 +223,7 @@ const actions = {
viewports.viewportSpecificData[viewports.activeViewportIndex]; viewports.viewportSpecificData[viewports.activeViewportIndex];
// Get current VOI if cornerstone viewport. // Get current VOI if cornerstone viewport.
const cornerstoneVOI = getVOIFromCornerstoneViewport(displaySet); const cornerstoneVOI = getVOIFromCornerstoneViewport();
const viewportProps = [ const viewportProps = [
{ {
@ -234,11 +274,11 @@ const actions = {
}); });
}); });
}, },
}; };
window.vtkActions = actions; window.vtkActions = actions;
const definitions = { const definitions = {
axial: { axial: {
commandFn: actions.axial, commandFn: actions.axial,
storeContexts: ['viewports'], storeContexts: ['viewports'],
@ -315,47 +355,12 @@ const definitions = {
options: {}, options: {},
context: 'VIEWER', context: 'VIEWER',
}, },
}; };
export default {
definitions,
defaultContext: 'ACTIVE_VIEWPORT::VTK',
};
function getVOIFromCornerstoneViewport(displaySet) {
const cornerstoneElement = cornerstone.getEnabledElement(displaySet.dom);
if (cornerstoneElement) {
const imageId = cornerstoneElement.image.imageId;
const { modality } = cornerstone.metaData.get(
'generalSeriesModule',
imageId
);
if (modality !== 'PT') {
const { windowWidth, windowCenter } = cornerstoneElement.viewport.voi;
return { return {
windowWidth, definitions,
windowCenter, defaultContext: 'ACTIVE_VIEWPORT::VTK',
}; };
} };
}
}
function setVOI(voi) { export default commandsModule;
const { windowWidth, windowCenter } = voi;
const lower = windowCenter - windowWidth / 2.0;
const upper = windowCenter + windowWidth / 2.0;
const rgbTransferFunction = apis[0].volumes[0]
.getProperty()
.getRGBTransferFunction(0);
rgbTransferFunction.setRange(lower, upper);
apis.forEach(api => {
api.updateVOI(windowWidth, windowCenter);
});
}

View File

@ -20,8 +20,8 @@ const vtkExtension = {
getToolbarModule() { getToolbarModule() {
return toolbarModule; return toolbarModule;
}, },
getCommandsModule() { getCommandsModule({ commandsManager }) {
return commandsModule; return commandsModule({ commandsManager });
}, },
}; };

View File

@ -69,6 +69,7 @@ export default class ExtensionManager {
if (extension.preRegistration) { if (extension.preRegistration) {
extension.preRegistration({ extension.preRegistration({
servicesManager: this._servicesManager, servicesManager: this._servicesManager,
commandsManager: this._commandsManager,
configuration, configuration,
}); });
} }
@ -112,6 +113,7 @@ export default class ExtensionManager {
try { try {
const extensionModule = getModuleFn({ const extensionModule = getModuleFn({
servicesManager: this._servicesManager, servicesManager: this._servicesManager,
commandsManager: this._commandsManager,
}); });
if (!extensionModule) { if (!extensionModule) {

View File

@ -49,7 +49,7 @@ describe('ExtensionManager.js', () => {
expect(fakeExtension.preRegistration.mock.calls.length).toBe(1); expect(fakeExtension.preRegistration.mock.calls.length).toBe(1);
}); });
it('calls preRegistration() passing configuration and servicesManager instance for extension', () => { it('calls preRegistration() passing configuration along with servicesManager and commandsManager instances for extension', () => {
const configuration = { config: 'Some configuration' }; const configuration = { config: 'Some configuration' };
extensionManager._servicesManager = { services: { TestService: {} } }; extensionManager._servicesManager = { services: { TestService: {} } };
@ -60,6 +60,7 @@ describe('ExtensionManager.js', () => {
// Assert // Assert
expect(fakeExtension.preRegistration.mock.calls[0][0]).toEqual({ expect(fakeExtension.preRegistration.mock.calls[0][0]).toEqual({
servicesManager: extensionManager._servicesManager, servicesManager: extensionManager._servicesManager,
commandsManager: extensionManager._commandsManager,
configuration, configuration,
}); });
}); });
@ -134,7 +135,7 @@ describe('ExtensionManager.js', () => {
); );
}); });
it('successfully passes a servicesManager instance to each module', () => { it('successfully passes a servicesManager and commandsManager instances to each module', () => {
extensionManager._servicesManager = { services: { TestService: {} } }; extensionManager._servicesManager = { services: { TestService: {} } };
const extension = { const extension = {
@ -150,6 +151,7 @@ describe('ExtensionManager.js', () => {
expect(extension.getViewportModule.mock.calls[0][0]).toEqual({ expect(extension.getViewportModule.mock.calls[0][0]).toEqual({
servicesManager: extensionManager._servicesManager, servicesManager: extensionManager._servicesManager,
commandsManager: extensionManager._commandsManager,
}); });
}); });

View File

@ -12,13 +12,25 @@ import {
SET_VIEWPORT_LAYOUT_AND_DATA, SET_VIEWPORT_LAYOUT_AND_DATA,
} from './constants/ActionTypes.js'; } from './constants/ActionTypes.js';
/**
* The definition of a viewport layout.
*
* @typedef {Object} ViewportLayout
* @property {number} numRows -
* @property {number} numColumns -
* @property {array} viewports -
*/
/** /**
* VIEWPORT * VIEWPORT
*/ */
export const setViewportSpecificData = (viewportIndex, data) => ({ export const setViewportSpecificData = (
viewportIndex,
viewportSpecificData
) => ({
type: SET_VIEWPORT, type: SET_VIEWPORT,
viewportIndex, viewportIndex,
data, viewportSpecificData,
}); });
export const setViewportActive = viewportIndex => ({ export const setViewportActive = viewportIndex => ({
@ -27,10 +39,7 @@ export const setViewportActive = viewportIndex => ({
}); });
/** /**
* @param {object} layout * @param {ViewportLayout} layout
* @param {number} layout.numRows
* @param {number} layout.numColumns
* @param {array} layout.viewports
*/ */
export const setLayout = ({ numRows, numColumns, viewports }) => ({ export const setLayout = ({ numRows, numColumns, viewports }) => ({
type: SET_VIEWPORT_LAYOUT, type: SET_VIEWPORT_LAYOUT,
@ -40,10 +49,9 @@ export const setLayout = ({ numRows, numColumns, viewports }) => ({
}); });
/** /**
* @param {object} layout
* @param {number} layout.numRows * @param {number} layout.numRows
* @param {number} layout.numColumns * @param {number} layout.numColumns
* @param {array} layout.viewports * @param {array} viewports
*/ */
export const setViewportLayoutAndData = ( export const setViewportLayoutAndData = (
{ numRows, numColumns, viewports }, { numRows, numColumns, viewports },
@ -61,9 +69,9 @@ export const clearViewportSpecificData = viewportIndex => ({
viewportIndex, viewportIndex,
}); });
export const setActiveViewportSpecificData = data => ({ export const setActiveViewportSpecificData = viewportSpecificData => ({
type: SET_ACTIVE_SPECIFIC_DATA, type: SET_ACTIVE_SPECIFIC_DATA,
data, viewportSpecificData,
}); });
/** /**
@ -113,13 +121,18 @@ export const setServers = servers => ({
}); });
const actions = { const actions = {
// VIEWPORT /**
* VIEWPORT
*/
setViewportActive, setViewportActive,
setViewportSpecificData, setViewportSpecificData,
setViewportLayoutAndData, setViewportLayoutAndData,
setLayout, setLayout,
clearViewportSpecificData, clearViewportSpecificData,
setActiveViewportSpecificData, setActiveViewportSpecificData,
/**
* NOT-VIEWPORT
*/
setStudyLoadingProgress, setStudyLoadingProgress,
clearStudyLoadingProgress, clearStudyLoadingProgress,
setUserPreferences, setUserPreferences,

View File

@ -28,7 +28,7 @@ describe('actions', () => {
describe('viewport action creators', () => { describe('viewport action creators', () => {
it('should create an action to set the viewport specific data', () => { it('should create an action to set the viewport specific data', () => {
const data = { const viewportSpecificData = {
displaySetInstanceUid: 'ef859a23-4631-93ab-d26b-7940a822c699', displaySetInstanceUid: 'ef859a23-4631-93ab-d26b-7940a822c699',
seriesDate: '20151026', seriesDate: '20151026',
seriesTime: '082611.370000', seriesTime: '082611.370000',
@ -65,12 +65,12 @@ describe('actions', () => {
const expectedAction = { const expectedAction = {
type: types.SET_ACTIVE_SPECIFIC_DATA, type: types.SET_ACTIVE_SPECIFIC_DATA,
data, viewportSpecificData,
}; };
expect(actions.setActiveViewportSpecificData(data)).toEqual( expect(
expectedAction actions.setActiveViewportSpecificData(viewportSpecificData)
); ).toEqual(expectedAction);
}); });
it('should create an action to clear clearViewportSpecificData', () => { it('should create an action to clear clearViewportSpecificData', () => {

View File

@ -1,3 +1,6 @@
import cloneDeep from 'lodash.clonedeep';
import merge from 'lodash.merge';
import { import {
CLEAR_VIEWPORT, CLEAR_VIEWPORT,
SET_ACTIVE_SPECIFIC_DATA, SET_ACTIVE_SPECIFIC_DATA,
@ -8,10 +11,7 @@ import {
SET_VIEWPORT_LAYOUT_AND_DATA, SET_VIEWPORT_LAYOUT_AND_DATA,
} from './../constants/ActionTypes.js'; } from './../constants/ActionTypes.js';
import cloneDeep from 'lodash.clonedeep'; const DEFAULT_STATE = {
import merge from 'lodash.merge';
const defaultState = {
numRows: 1, numRows: 1,
numColumns: 1, numColumns: 1,
activeViewportIndex: 0, activeViewportIndex: 0,
@ -26,95 +26,138 @@ const defaultState = {
}; };
/** /**
* @param {Object} [state=defaultState] * The definition of a viewport action.
* @param {Object} action *
* @param {string} [action.type] * @typedef {Object} ViewportAction
* @param {number} [action.viewportIndex] * @property {string} type -
* @param {Object} [action.layout] * @property {Object} data -
* @param {Object} [action.viewportSpecificData] * @property {Object} layout -
* @property {number} viewportIndex -
* @property {Object} viewportSpecificData -
*/ */
const viewports = (state = defaultState, action) => {
let viewportSpecificData; /**
* @param {Object} [state=DEFAULT_STATE] The current viewport state.
* @param {ViewportAction} action A viewport action.
*/
const viewports = (state = DEFAULT_STATE, action) => {
let useActiveViewport = false; let useActiveViewport = false;
switch (action.type) { switch (action.type) {
case SET_VIEWPORT_ACTIVE: /**
return Object.assign({}, state, { * Sets the active viewport index.
activeViewportIndex: action.viewportIndex, *
}); * @return {Object} New state.
*/
case SET_VIEWPORT_ACTIVE: {
return { ...state, activeViewportIndex: action.viewportIndex };
}
/**
* Sets viewport layout.
*
* @return {Object} New state.
*/
case SET_VIEWPORT_LAYOUT: { case SET_VIEWPORT_LAYOUT: {
const { numRows, numColumns, viewports } = action; return {
const layout = { ...state,
viewports: [...viewports], numRows: action.numRows,
numColumns: action.numColumns,
layout: { viewports: [...action.viewports] },
}; };
return Object.assign({}, state, { numRows, numColumns, layout });
} }
/**
* Sets viewport layout and data.
*
* @return {Object} New state.
*/
case SET_VIEWPORT_LAYOUT_AND_DATA: { case SET_VIEWPORT_LAYOUT_AND_DATA: {
const { numRows, numColumns, viewports, viewportSpecificData } = action; return {
const layout = { ...state,
viewports: [...viewports], numRows: action.numRows,
numColumns: action.numColumns,
layout: { viewports: [...action.viewports] },
viewportSpecificData: cloneDeep(action.viewportSpecificData),
}; };
return Object.assign({}, state, {
numRows,
numColumns,
layout,
viewportSpecificData: cloneDeep(viewportSpecificData),
});
} }
/**
* Sets viewport specific data of active viewport.
*
* @return {Object} New state.
*/
case SET_VIEWPORT: { case SET_VIEWPORT: {
const layout = cloneDeep(state.layout); const layout = cloneDeep(state.layout);
const hasPlugin = action.data && action.data.plugin;
viewportSpecificData = cloneDeep(state.viewportSpecificData); let viewportSpecificData = cloneDeep(state.viewportSpecificData);
viewportSpecificData[action.viewportIndex] = merge( viewportSpecificData[action.viewportIndex] = merge(
{}, {},
viewportSpecificData[action.viewportIndex], viewportSpecificData[action.viewportIndex],
action.data action.viewportSpecificData
); );
if (hasPlugin) { if (action.viewportSpecificData && action.viewportSpecificData.plugin) {
layout.viewports[action.viewportIndex].plugin = action.data.plugin; layout.viewports[action.viewportIndex].plugin =
action.viewportSpecificData.plugin;
} }
return Object.assign({}, state, { layout, viewportSpecificData }); return { ...state, layout, viewportSpecificData };
} }
/**
* Sets viewport specific data of active/any viewport.
*
* @return {Object} New state.
*/
case SET_ACTIVE_SPECIFIC_DATA: case SET_ACTIVE_SPECIFIC_DATA:
useActiveViewport = true; useActiveViewport = true;
// Allow fall-through // Allow fall-through
// eslint-disable-next-line // eslint-disable-next-line
case SET_SPECIFIC_DATA: { case SET_SPECIFIC_DATA: {
const layout = cloneDeep(state.layout); const layout = cloneDeep(state.layout);
const hasPlugin = action.data && action.data.plugin;
const viewportIndex = useActiveViewport const viewportIndex = useActiveViewport
? state.activeViewportIndex ? state.activeViewportIndex
: action.viewportIndex; : action.viewportIndex;
const { dom } = state.viewportSpecificData[viewportIndex];
viewportSpecificData = cloneDeep(state.viewportSpecificData); let viewportSpecificData = cloneDeep(state.viewportSpecificData);
viewportSpecificData[viewportIndex] = { viewportSpecificData[viewportIndex] = {
dom, ...action.viewportSpecificData,
...action.data,
}; };
if (hasPlugin) { if (action.viewportSpecificData && action.viewportSpecificData.plugin) {
layout.viewports[viewportIndex].plugin = action.data.plugin; layout.viewports[viewportIndex].plugin =
action.viewportSpecificData.plugin;
} }
return Object.assign({}, state, { layout, viewportSpecificData }); return { ...state, layout, viewportSpecificData };
} }
case CLEAR_VIEWPORT:
viewportSpecificData = cloneDeep(state.viewportSpecificData); /**
* Clears viewport specific data of any viewport.
*
* @return {Object} New state.
*/
case CLEAR_VIEWPORT: {
let viewportSpecificData = cloneDeep(state.viewportSpecificData);
if (action.viewportIndex) { if (action.viewportIndex) {
viewportSpecificData[action.viewportIndex] = {}; viewportSpecificData[action.viewportIndex] = {};
return Object.assign({}, state, { viewportSpecificData }); return { ...state, viewportSpecificData };
} else { } else {
return defaultState; return DEFAULT_STATE;
}
} }
default: /**
* Returns the current application state.
*
* @return {Object} The current state.
*/
default: {
return state; return state;
} }
}
}; };
export default viewports; export default viewports;

View File

@ -60,7 +60,7 @@ describe('viewports reducer', () => {
const setViewportAction = { const setViewportAction = {
type: types.SET_VIEWPORT, type: types.SET_VIEWPORT,
viewportIndex: viewportToSet, viewportIndex: viewportToSet,
data: { viewportSpecificData: {
hello: 'this is that data for the viewport', hello: 'this is that data for the viewport',
world: 'that will be set for the viewportIndex', world: 'that will be set for the viewportIndex',
}, },
@ -69,7 +69,7 @@ describe('viewports reducer', () => {
const updatedState = reducer(undefined, setViewportAction); const updatedState = reducer(undefined, setViewportAction);
const updatedViewport = updatedState.viewportSpecificData[viewportToSet]; const updatedViewport = updatedState.viewportSpecificData[viewportToSet];
expect(updatedViewport).toEqual(setViewportAction.data); expect(updatedViewport).toEqual(setViewportAction.viewportSpecificData);
}); });
it('should handle CLEAR_VIEWPORT', () => { it('should handle CLEAR_VIEWPORT', () => {

View File

@ -2,6 +2,7 @@ import { connect } from 'react-redux';
import { CineDialog } from '@ohif/ui'; import { CineDialog } from '@ohif/ui';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
import csTools from 'cornerstone-tools'; import csTools from 'cornerstone-tools';
import { commandsManager } from './../App.js';
// Our target output kills the `as` and "import" throws a keyword error // Our target output kills the `as` and "import" throws a keyword error
// import { import as toolImport, getToolState } from 'cornerstone-tools'; // import { import as toolImport, getToolState } from 'cornerstone-tools';
import cloneDeep from 'lodash.clonedeep'; import cloneDeep from 'lodash.clonedeep';
@ -16,7 +17,8 @@ const { setViewportSpecificData } = OHIF.redux.actions;
const mapStateToProps = state => { const mapStateToProps = state => {
// Get activeViewport's `cine` and `stack` // Get activeViewport's `cine` and `stack`
const { viewportSpecificData, activeViewportIndex } = state.viewports; const { viewportSpecificData, activeViewportIndex } = state.viewports;
const { cine, dom } = viewportSpecificData[activeViewportIndex] || {}; const { cine } = viewportSpecificData[activeViewportIndex] || {};
const dom = commandsManager.runCommand('getActiveViewportEnabledElement');
const cineData = cine || { const cineData = cine || {
isPlaying: false, isPlaying: false,

View File

@ -3,15 +3,16 @@ import { ViewportDownloadForm } from '@ohif/ui';
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools'; import cornerstoneTools from 'cornerstone-tools';
import { commandsManager } from './../App.js';
const MINIMUM_SIZE = 100; const MINIMUM_SIZE = 100;
const DEFAULT_SIZE = 512; const DEFAULT_SIZE = 512;
const MAX_TEXTURE_SIZE = 10000; const MAX_TEXTURE_SIZE = 10000;
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { viewportSpecificData, activeViewportIndex } = state.viewports; const activeEnabledElement = commandsManager.runCommand(
const { dom: activeEnabledElement } = 'getActiveViewportEnabledElement'
viewportSpecificData[activeViewportIndex] || {}; );
return { return {
onClose: ownProps.hide, onClose: ownProps.hide,