fix: Set VTK viewport as active by interaction (#1139)
* fix: Set VTK viewport as active by interaction * feat: listen for vtkscrollevent in wrapper component * fix: definitions can skip empty storeContexts key * hoc to set/pass in commandsManager * Bump minimum react-vtkjs-viewport version to leverage new event * Simplify to use onScroll event instead of passing down commandsManager to base component * fix: make sure we include @JamesAPetts bug fix Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
This commit is contained in:
parent
c22756051b
commit
686d12da5c
@ -49,7 +49,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"react-vtkjs-viewport": "^0.3.9"
|
"react-vtkjs-viewport": "^0.8.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^2.3.6",
|
"@ohif/core": "^2.3.6",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import OHIF from '@ohif/core';
|
import OHIF from '@ohif/core';
|
||||||
import { View2D } from 'react-vtkjs-viewport';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import VTKViewport from './VTKViewport';
|
||||||
|
|
||||||
const { setViewportActive, setViewportSpecificData } = OHIF.redux.actions;
|
const { setViewportActive, setViewportSpecificData } = OHIF.redux.actions;
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
const pluginDetails = viewportLayout.vtk || {};
|
const pluginDetails = viewportLayout.vtk || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
activeViewportIndex: state.viewports.activeViewportIndex,
|
||||||
layout: state.viewports.layout,
|
layout: state.viewports.layout,
|
||||||
isActive,
|
isActive,
|
||||||
...pluginDetails,
|
...pluginDetails,
|
||||||
@ -75,6 +76,6 @@ const ConnectedVTKViewport = connect(
|
|||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
mergeProps
|
mergeProps
|
||||||
)(View2D);
|
)(VTKViewport);
|
||||||
|
|
||||||
export default ConnectedVTKViewport;
|
export default ConnectedVTKViewport;
|
||||||
|
|||||||
@ -77,6 +77,11 @@ class OHIFVTKViewport extends Component {
|
|||||||
}),
|
}),
|
||||||
viewportIndex: PropTypes.number,
|
viewportIndex: PropTypes.number,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
onScroll: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
onScroll: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
static id = 'OHIFVTKViewport';
|
static id = 'OHIFVTKViewport';
|
||||||
@ -386,6 +391,7 @@ class OHIFVTKViewport extends Component {
|
|||||||
this.state.paintFilterBackgroundImageData
|
this.state.paintFilterBackgroundImageData
|
||||||
}
|
}
|
||||||
viewportIndex={this.props.viewportIndex}
|
viewportIndex={this.props.viewportIndex}
|
||||||
|
onScroll={this.props.onScroll}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
3
extensions/vtk/src/VTKViewport.css
Normal file
3
extensions/vtk/src/VTKViewport.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.vtk-viewport-handler svg {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
58
extensions/vtk/src/VTKViewport.js
Normal file
58
extensions/vtk/src/VTKViewport.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import React, { useEffect, useCallback } from 'react';
|
||||||
|
import { View2D } from 'react-vtkjs-viewport';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import './VTKViewport.css';
|
||||||
|
|
||||||
|
const VTKViewport = props => {
|
||||||
|
const style = { width: '100%', height: '100%', position: 'relative' };
|
||||||
|
|
||||||
|
const setViewportActiveHandler = useCallback(() => {
|
||||||
|
const { setViewportActive, viewportIndex, activeViewportIndex } = props;
|
||||||
|
|
||||||
|
if (viewportIndex !== activeViewportIndex) {
|
||||||
|
// set in Connected
|
||||||
|
setViewportActive();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScrollEvent = evt => {
|
||||||
|
const vtkViewportApiReference = props.onScroll(props.viewportIndex) || {};
|
||||||
|
const viewportUid = vtkViewportApiReference.uid;
|
||||||
|
const viewportWasScrolled = viewportUid === evt.detail.uid;
|
||||||
|
|
||||||
|
if (viewportWasScrolled) {
|
||||||
|
setViewportActiveHandler();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('vtkscrollevent', handleScrollEvent);
|
||||||
|
return () =>
|
||||||
|
window.removeEventListener('vtkscrollevent', handleScrollEvent);
|
||||||
|
}, [props, props.onScroll, props.viewportIndex, setViewportActiveHandler]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="vtk-viewport-handler"
|
||||||
|
style={style}
|
||||||
|
onClick={setViewportActiveHandler}
|
||||||
|
>
|
||||||
|
<View2D {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
VTKViewport.propTypes = {
|
||||||
|
setViewportActive: PropTypes.func.isRequired,
|
||||||
|
viewportIndex: PropTypes.number.isRequired,
|
||||||
|
activeViewportIndex: PropTypes.number.isRequired,
|
||||||
|
/* Receives viewportIndex */
|
||||||
|
onScroll: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
VTKViewport.defaultProps = {
|
||||||
|
onScroll: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VTKViewport;
|
||||||
@ -103,6 +103,9 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
|
getVtkApis: ({ index }) => {
|
||||||
|
return apis[index];
|
||||||
|
},
|
||||||
axial: async ({ viewports }) => {
|
axial: async ({ viewports }) => {
|
||||||
const api = await _getActiveViewportVTKApi(viewports);
|
const api = await _getActiveViewportVTKApi(viewports);
|
||||||
|
|
||||||
@ -266,11 +269,12 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
'crosshairsWidget'
|
'crosshairsWidget'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const uid = api.uid;
|
||||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||||
|
|
||||||
api.setInteractorStyle({
|
api.setInteractorStyle({
|
||||||
istyle,
|
istyle,
|
||||||
configuration: { apis, apiIndex },
|
configuration: { apis, apiIndex, uid },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -296,55 +300,45 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
},
|
},
|
||||||
enableRotateTool: {
|
enableRotateTool: {
|
||||||
commandFn: actions.enableRotateTool,
|
commandFn: actions.enableRotateTool,
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
enableCrosshairsTool: {
|
enableCrosshairsTool: {
|
||||||
commandFn: actions.enableCrosshairsTool,
|
commandFn: actions.enableCrosshairsTool,
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
enableLevelTool: {
|
enableLevelTool: {
|
||||||
commandFn: actions.enableLevelTool,
|
commandFn: actions.enableLevelTool,
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
setBlendModeToComposite: {
|
setBlendModeToComposite: {
|
||||||
commandFn: actions.setBlendModeToComposite,
|
commandFn: actions.setBlendModeToComposite,
|
||||||
storeContexts: [],
|
|
||||||
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
||||||
},
|
},
|
||||||
setBlendModeToMaximumIntensity: {
|
setBlendModeToMaximumIntensity: {
|
||||||
commandFn: actions.setBlendModeToMaximumIntensity,
|
commandFn: actions.setBlendModeToMaximumIntensity,
|
||||||
storeContexts: [],
|
|
||||||
options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND },
|
options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND },
|
||||||
},
|
},
|
||||||
setBlendModeToMinimumIntensity: {
|
setBlendModeToMinimumIntensity: {
|
||||||
commandFn: actions.setBlendMode,
|
commandFn: actions.setBlendMode,
|
||||||
storeContexts: [],
|
|
||||||
options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND },
|
options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND },
|
||||||
},
|
},
|
||||||
setBlendModeToAverageIntensity: {
|
setBlendModeToAverageIntensity: {
|
||||||
commandFn: actions.setBlendMode,
|
commandFn: actions.setBlendMode,
|
||||||
storeContexts: [],
|
|
||||||
options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND },
|
options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND },
|
||||||
},
|
},
|
||||||
setSlabThickness: {
|
setSlabThickness: {
|
||||||
// TODO: How do we pass in a function argument?
|
// TODO: How do we pass in a function argument?
|
||||||
commandFn: actions.setSlabThickness,
|
commandFn: actions.setSlabThickness,
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
increaseSlabThickness: {
|
increaseSlabThickness: {
|
||||||
commandFn: actions.changeSlabThickness,
|
commandFn: actions.changeSlabThickness,
|
||||||
storeContexts: [],
|
|
||||||
options: {
|
options: {
|
||||||
change: 3,
|
change: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
decreaseSlabThickness: {
|
decreaseSlabThickness: {
|
||||||
commandFn: actions.changeSlabThickness,
|
commandFn: actions.changeSlabThickness,
|
||||||
storeContexts: [],
|
|
||||||
options: {
|
options: {
|
||||||
change: -3,
|
change: -3,
|
||||||
},
|
},
|
||||||
@ -355,6 +349,10 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
options: {},
|
options: {},
|
||||||
context: 'VIEWER',
|
context: 'VIEWER',
|
||||||
},
|
},
|
||||||
|
getVtkApiForViewportIndex: {
|
||||||
|
commandFn: actions.getVtkApis,
|
||||||
|
context: 'VIEWER',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import asyncComponent from './asyncComponent.js';
|
import asyncComponent from './asyncComponent.js';
|
||||||
import commandsModule from './commandsModule.js';
|
import commandsModule from './commandsModule.js';
|
||||||
import toolbarModule from './toolbarModule.js';
|
import toolbarModule from './toolbarModule.js';
|
||||||
|
import withCommandsManager from './withCommandsManager.js';
|
||||||
// This feels weird
|
// This feels weird
|
||||||
// import loadLocales from './loadLocales';
|
// import loadLocales from './loadLocales';
|
||||||
|
|
||||||
@ -14,8 +15,8 @@ const vtkExtension = {
|
|||||||
*/
|
*/
|
||||||
id: 'vtk',
|
id: 'vtk',
|
||||||
|
|
||||||
getViewportModule() {
|
getViewportModule({ commandsManager }) {
|
||||||
return OHIFVTKViewport;
|
return withCommandsManager(OHIFVTKViewport, commandsManager);
|
||||||
},
|
},
|
||||||
getToolbarModule() {
|
getToolbarModule() {
|
||||||
return toolbarModule;
|
return toolbarModule;
|
||||||
|
|||||||
18
extensions/vtk/src/withCommandsManager.js
Normal file
18
extensions/vtk/src/withCommandsManager.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function withCommandsManager(Component, commandsManager = {}) {
|
||||||
|
return class WithCommandsManager extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Component
|
||||||
|
{...this.props}
|
||||||
|
onScroll={viewportIndex =>
|
||||||
|
commandsManager.runCommand('getVtkApiForViewportIndex', {
|
||||||
|
index: viewportIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -161,7 +161,7 @@ export class CommandsManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { commandFn, storeContexts } = definition;
|
const { commandFn, storeContexts = [] } = definition;
|
||||||
const definitionOptions = definition.options;
|
const definitionOptions = definition.options;
|
||||||
|
|
||||||
let commandParams = {};
|
let commandParams = {};
|
||||||
|
|||||||
36
yarn.lock
36
yarn.lock
@ -1100,13 +1100,34 @@
|
|||||||
pirates "^4.0.0"
|
pirates "^4.0.0"
|
||||||
source-map-support "^0.5.9"
|
source-map-support "^0.5.9"
|
||||||
|
|
||||||
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3":
|
"@babel/runtime@7.1.2":
|
||||||
|
version "7.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3"
|
||||||
|
integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.12.0"
|
||||||
|
|
||||||
|
"@babel/runtime@7.6.0":
|
||||||
|
version "7.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
|
||||||
|
integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
|
||||||
version "7.5.5"
|
version "7.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
||||||
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.2"
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3":
|
||||||
|
version "7.8.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1"
|
||||||
|
integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
|
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
|
||||||
version "7.6.0"
|
version "7.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
|
||||||
@ -15660,10 +15681,10 @@ react-transition-group@^4.1.1:
|
|||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
react-vtkjs-viewport@^0.3.9:
|
react-vtkjs-viewport@^0.8.2:
|
||||||
version "0.3.9"
|
version "0.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.3.9.tgz#dc3dd5323c4cbaf5978cd64af2794b6b4ed61642"
|
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.8.2.tgz#cdccf156cb089d32b935ebc3dc5f31213ba17bef"
|
||||||
integrity sha512-Sc3dRRz8tRSdUrdgL8tuYwVEl0CTjaZfDLqP5ufMXH2KjwBt3T5bjpk4xvzFyPc9CG6ywEX5MSa6eQ+NNjaUkw==
|
integrity sha512-2OPbU5y4ay+E6qfZw4sW19uqfBnNw9kAF1PaZ/Sk5XMEEKnebplEof9ZZtE6nhPImFqMCfu4TXzYVHIAPmwFYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
date-fns "^2.2.1"
|
date-fns "^2.2.1"
|
||||||
gl-matrix "^3.1.0"
|
gl-matrix "^3.1.0"
|
||||||
@ -16038,6 +16059,11 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
|
|||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||||
|
|
||||||
|
regenerator-runtime@^0.12.0:
|
||||||
|
version "0.12.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
||||||
|
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
||||||
|
|
||||||
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
|
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
|
||||||
version "0.13.3"
|
version "0.13.3"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user