diff --git a/examples/VTKLoadImageDataExample.js b/examples/VTKLoadImageDataExample.js index ed33766e..e5372cdb 100644 --- a/examples/VTKLoadImageDataExample.js +++ b/examples/VTKLoadImageDataExample.js @@ -141,8 +141,7 @@ class VTKLoadImageDataExample extends Component { const { slicePlaneNormal, sliceViewUp } = ORIENTATION[orientation]; - istyle.setSliceNormal(...slicePlaneNormal); - istyle.setViewUp(...sliceViewUp); + istyle.setSliceOrientation(slicePlaneNormal, sliceViewUp); this.imageDataObject.insertPixelDataPromises.forEach(promise => { promise.then(() => renderWindow.render()); diff --git a/package.json b/package.json index 15e0fa23..f4a83fed 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "style-loader": "^0.23.1", "stylelint": "^10.1.0", "stylelint-config-recommended": "^2.2.0", - "vtk.js": "^11.7.2", + "vtk.js": "^11.11.1", "webpack": "4.34.0", "webpack-cli": "^3.3.4", "webpack-dev-server": "^3.8.0", diff --git a/src/Custom/VTKMPRViewport.js b/src/Custom/VTKMPRViewport.js index 64d4a248..dcea08b9 100644 --- a/src/Custom/VTKMPRViewport.js +++ b/src/Custom/VTKMPRViewport.js @@ -134,7 +134,7 @@ export default class VtkMpr extends Component { // must be added AFTER the data volume is added so that this can be rendered in front this.renderer.addVolume(this.labelPipeline.actor); - istyle.setVolumeMapper(this.pipeline.mapper); + istyle.setVolumeActor(this.pipeline.actor); istyle.setSliceNormal([0, 0, 1]); const range = istyle.getSliceRange(); istyle.setSlice((range[0] + range[1]) / 2); diff --git a/src/VTKViewport/Manipulators/vtkMouseRangeRotateManipulator.js b/src/VTKViewport/Manipulators/vtkMouseRangeRotateManipulator.js index 0337713c..467a6b2d 100644 --- a/src/VTKViewport/Manipulators/vtkMouseRangeRotateManipulator.js +++ b/src/VTKViewport/Manipulators/vtkMouseRangeRotateManipulator.js @@ -20,7 +20,7 @@ function vtkMouseRangeRotateManipulator(publicAPI, model) { dThetaY => { let thetaY = dThetaY % 360; - model.viewportData.rotate(0, thetaY); + model.viewportData.rotateRelative(0, thetaY); // onInteractiveRotationChanged(); } diff --git a/src/VTKViewport/View2D.js b/src/VTKViewport/View2D.js index cd01f093..a66dcf5b 100644 --- a/src/VTKViewport/View2D.js +++ b/src/VTKViewport/View2D.js @@ -23,7 +23,6 @@ export default class View2D extends Component { onPaint: PropTypes.func, onPaintStart: PropTypes.func, onPaintEnd: PropTypes.func, - interactorStyleVolumeMapper: PropTypes.object, dataDetails: PropTypes.object, onCreated: PropTypes.func, onDestroyed: PropTypes.func, @@ -172,16 +171,11 @@ export default class View2D extends Component { renderer.getActiveCamera().setClippingRange(...r); });*/ - const istyleVolumeMapper = - this.props.interactorStyleVolumeMapper || - this.props.volumes[0].getMapper(); - // Set orientation based on props if (this.props.orientation) { const { orientation } = this.props; - istyle.setSliceNormal(...orientation.sliceNormal); - istyle.setViewUp(...orientation.viewUp); + istyle.setSliceOrientation(orientation.sliceNormal, orientation.viewUp); } else { istyle.setSliceNormal(0, 0, 1); } @@ -191,7 +185,7 @@ export default class View2D extends Component { camera.setParallelProjection(true); this.renderer.resetCamera(); - istyle.setVolumeMapper(istyleVolumeMapper); + istyle.setVolumeActor(this.props.volumes[0]); const range = istyle.getSliceRange(); istyle.setSlice((range[0] + range[1]) / 2); @@ -315,14 +309,12 @@ export default class View2D extends Component { istyle.setViewport(currentViewport); } - istyle.getVolumeMapper(); - - if (istyle.getVolumeMapper() !== volumes[0]) { + if (istyle.getVolumeActor() !== volumes[0]) { if (slabThickness && istyle.setSlabThickness) { istyle.setSlabThickness(slabThickness); } - istyle.setVolumeMapper(volumes[0]); + istyle.setVolumeActor(volumes[0]); } // Add appropriate callbacks diff --git a/src/VTKViewport/ViewportData.js b/src/VTKViewport/ViewportData.js index d91dab18..978a5f4e 100644 --- a/src/VTKViewport/ViewportData.js +++ b/src/VTKViewport/ViewportData.js @@ -1,4 +1,4 @@ -import { vec3, mat4 } from 'gl-matrix'; +import { vec3, mat4, quat } from 'gl-matrix'; import { degrees2radians } from '../lib/math/angles.js'; import EVENTS from '../events.js'; @@ -32,47 +32,66 @@ export default class { } else { this._state = JSON.parse(JSON.stringify(state)); } + + // copy the state to a cache, so we can modify internally, + // but remember the original values for absolute rotations + this._state.cache = { + ...this._state, + }; } getEventWindow = () => { return this.eventWindow; }; - rotate = (dThetaX, dThetaY) => { + _rotate = (viewUp, sliceNormal, dThetaX, dThetaY, dThetaZ = 0) => { validateNumber(dThetaX); validateNumber(dThetaY); + validateNumber(dThetaZ); let xAxis = []; - vec3.cross(xAxis, this._state.viewUp, this._state.sliceNormal); + vec3.cross(xAxis, viewUp, sliceNormal); vec3.normalize(xAxis, xAxis); - let yAxis = this._state.viewUp; + let yAxis = viewUp; // rotate around the vector of the cross product of the // plane and viewup as the X component - const sliceNormal = []; - const sliceViewUp = []; + const nSliceNormal = []; + const nViewUp = []; const planeMat = mat4.create(); - //Rotate around the vertical (slice-up) vector + // Rotate around the vertical (slice-up) vector mat4.rotate(planeMat, planeMat, degrees2radians(dThetaY), yAxis); - //Rotate around the horizontal (screen-x) vector + // Rotate around the horizontal (screen-x) vector mat4.rotate(planeMat, planeMat, degrees2radians(dThetaX), xAxis); - vec3.transformMat4(sliceNormal, this._state.sliceNormal, planeMat); - vec3.transformMat4(sliceViewUp, this._state.viewUp, planeMat); + vec3.transformMat4(nSliceNormal, sliceNormal, planeMat); + vec3.transformMat4(nViewUp, viewUp, planeMat); + + if (dThetaZ !== 0) { + // Rotate the viewUp in 90 degree increments + const zRotQuat = quat.create(); + // Use negative degrees clockwise rotation since the axis should really be the direction of projection, which is the negative of the plane normal + quat.setAxisAngle(zRotQuat, nSliceNormal, degrees2radians(-dThetaZ)); + quat.normalize(zRotQuat, zRotQuat); + + // rotate the ViewUp with the z rotation + vec3.transformQuat(nViewUp, nViewUp, zRotQuat); + } - this._state.sliceNormal = sliceNormal; - this._state.viewUp = sliceViewUp; + this._state.cache.sliceNormal = nSliceNormal; + this._state.cache.viewUp = nViewUp; var event = new CustomEvent(EVENTS.VIEWPORT_ROTATED, { detail: { - sliceNormal, - sliceViewUp, + sliceNormal: nSliceNormal, + sliceViewUp: nViewUp, dThetaX, dThetaY, + dThetaZ, }, bubbles: true, cancelable: true, @@ -81,9 +100,30 @@ export default class { this.eventWindow.dispatchEvent(event); }; + rotateAbsolute = (dThetaX, dThetaY, dThetaZ = 0) => { + this._rotate( + this._state.viewUp, + this._state.sliceNormal, + dThetaX, + dThetaY, + dThetaZ + ); + }; + rotateRelative = (dThetaX, dThetaY, dThetaZ = 0) => { + this._rotate( + this._state.cache.viewUp, + this._state.cache.sliceNormal, + dThetaX, + dThetaY, + dThetaZ + ); + }; + setOrientation = (sliceNormal, viewUp = [0, 1, 0]) => { - this._state.sliceNormal = sliceNormal; - this._state.viewUp = viewUp; + this._state.sliceNormal = [...sliceNormal]; + this._state.viewUp = [...viewUp]; + this._state.cache.sliceNormal = [...sliceNormal]; + this._state.cache.viewUp = [...viewUp]; }; getViewUp = () => { @@ -94,6 +134,14 @@ export default class { return this._state.sliceNormal; }; + getCurrentViewUp = () => { + return this._state.cache.viewUp; + }; + + getCurrentSliceNormal = () => { + return this._state.cache.sliceNormal; + }; + getReadOnlyViewPort = () => { const readOnlyState = JSON.parse(JSON.stringify(this._state)); diff --git a/src/VTKViewport/vtkInteractorStyleMPRCrosshairs.js b/src/VTKViewport/vtkInteractorStyleMPRCrosshairs.js index d8167ee3..1e834cf1 100644 --- a/src/VTKViewport/vtkInteractorStyleMPRCrosshairs.js +++ b/src/VTKViewport/vtkInteractorStyleMPRCrosshairs.js @@ -1,11 +1,5 @@ import macro from 'vtk.js/Sources/macro'; -import vtkMath from 'vtk.js/Sources/Common/Core/Math'; import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; -import vtkInteractorStyleManipulator from 'vtk.js/Sources/Interaction/Style/InteractorStyleManipulator'; -import vtkMouseCameraTrackballRotateManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballRotateManipulator'; -import vtkMouseCameraTrackballPanManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulator'; -import vtkMouseCameraTrackballZoomManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballZoomManipulator'; -import vtkMouseRangeManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseRangeManipulator'; import vtkInteractorStyleMPRSlice from './vtkInteractorStyleMPRSlice.js'; import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants'; import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; @@ -24,44 +18,6 @@ function vtkInteractorStyleMPRCrosshairs(publicAPI, model) { // Set our className model.classHierarchy.push('vtkInteractorStyleMPRCrosshairs'); - model.trackballManipulator = vtkMouseCameraTrackballRotateManipulator.newInstance( - { - button: 1, - } - ); - model.panManipulator = vtkMouseCameraTrackballPanManipulator.newInstance({ - button: 1, - shift: true, - }); - model.zoomManipulator = vtkMouseCameraTrackballZoomManipulator.newInstance({ - button: 3, - }); - model.scrollManipulator = vtkMouseRangeManipulator.newInstance({ - scrollEnabled: true, - dragEnabled: false, - }); - - function updateScrollManipulator() { - const range = publicAPI.getSliceRange(); - model.scrollManipulator.removeScrollListener(); - model.scrollManipulator.setScrollListener( - range[0], - range[1], - 1, - publicAPI.getSlice, - publicAPI.setSlice - ); - } - - function setManipulators() { - publicAPI.removeAllMouseManipulators(); - publicAPI.addMouseManipulator(model.trackballManipulator); - publicAPI.addMouseManipulator(model.panManipulator); - publicAPI.addMouseManipulator(model.zoomManipulator); - publicAPI.addMouseManipulator(model.scrollManipulator); - updateScrollManipulator(); - } - function moveCrosshairs(callData) { const { apis, apiIndex } = model; const pos = [callData.position.x, callData.position.y]; @@ -148,7 +104,7 @@ function vtkInteractorStyleMPRCrosshairs(publicAPI, model) { const superHandleLeftButtonPress = publicAPI.handleLeftButtonPress; publicAPI.handleLeftButtonPress = callData => { if (!callData.shiftKey && !callData.controlKey) { - if (model.volumeMapper) { + if (model.volumeActor) { moveCrosshairs(callData); publicAPI.startWindowLevel(); } @@ -157,24 +113,6 @@ function vtkInteractorStyleMPRCrosshairs(publicAPI, model) { } }; - const superSetVolumeMapper = publicAPI.setVolumeMapper; - publicAPI.setVolumeMapper = mapper => { - if (superSetVolumeMapper(mapper)) { - const renderer = model.interactor.getCurrentRenderer(); - const camera = renderer.getActiveCamera(); - if (mapper) { - // prevent zoom manipulator from messing with our focal point - camera.setFreezeFocalPoint(true); - updateScrollManipulator(); - // NOTE: Disabling this because it makes it more difficult to switch - // interactor styles. Need to find a better way to do this! - //publicAPI.setSliceNormal(...publicAPI.getSliceNormal()); - } else { - camera.setFreezeFocalPoint(false); - } - } - }; - publicAPI.superHandleLeftButtonRelease = publicAPI.handleLeftButtonRelease; publicAPI.handleLeftButtonRelease = () => { switch (model.state) { @@ -194,8 +132,6 @@ function vtkInteractorStyleMPRCrosshairs(publicAPI, model) { publicAPI.setApiIndex = apiIndex => { model.apiIndex = apiIndex; }; - - setManipulators(); } // ---------------------------------------------------------------------------- @@ -212,7 +148,7 @@ export function extend(publicAPI, model, initialValues = {}) { // Inheritance vtkInteractorStyleMPRSlice.extend(publicAPI, model, initialValues); - macro.setGet(publicAPI, model, ['volumeMapper', 'callback']); + macro.setGet(publicAPI, model, ['callback']); // Object specific methods vtkInteractorStyleMPRCrosshairs(publicAPI, model); diff --git a/src/VTKViewport/vtkInteractorStyleMPRRotate.js b/src/VTKViewport/vtkInteractorStyleMPRRotate.js index a913df0a..3ec12855 100644 --- a/src/VTKViewport/vtkInteractorStyleMPRRotate.js +++ b/src/VTKViewport/vtkInteractorStyleMPRRotate.js @@ -1,12 +1,8 @@ import macro from 'vtk.js/Sources/macro'; import vtkInteractorStyleMPRSlice from './vtkInteractorStyleMPRSlice.js'; import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants'; -import { vec3, mat4 } from 'gl-matrix'; -import { degrees2radians } from '../lib/math/angles.js'; -import ViewportData from './ViewportData.js'; const { States } = Constants; -const MAX_SAFE_INTEGER = 2147483647; // ---------------------------------------------------------------------------- // Global methods @@ -48,7 +44,7 @@ function vtkInteractorStyleMPRRotate(publicAPI, model) { const dThetaY = -((pos[0] - model.rotateStartPos[0]) * xSensitivity); const viewport = publicAPI.getViewport(); - viewport.rotate(dThetaX, dThetaY); + viewport.rotateRelative(dThetaX, dThetaY); model.rotateStartPos[0] = Math.round(pos[0]); model.rotateStartPos[1] = Math.round(pos[1]); @@ -59,7 +55,7 @@ function vtkInteractorStyleMPRRotate(publicAPI, model) { model.rotateStartPos[0] = Math.round(callData.position.x); model.rotateStartPos[1] = Math.round(callData.position.y); if (!callData.shiftKey && !callData.controlKey) { - const property = model.volumeMapper.getProperty(); + const property = model.volumeActor.getProperty(); if (property) { publicAPI.startRotate(); } @@ -68,20 +64,6 @@ function vtkInteractorStyleMPRRotate(publicAPI, model) { } }; - const superSetVolumeMapper = publicAPI.setVolumeMapper; - publicAPI.setVolumeMapper = mapper => { - if (superSetVolumeMapper(mapper)) { - const renderer = model.interactor.getCurrentRenderer(); - const camera = renderer.getActiveCamera(); - if (mapper) { - // prevent zoom manipulator from messing with our focal point - camera.setFreezeFocalPoint(true); - } else { - camera.setFreezeFocalPoint(false); - } - } - }; - publicAPI.superHandleLeftButtonRelease = publicAPI.handleLeftButtonRelease; publicAPI.handleLeftButtonRelease = () => { switch (model.state) { @@ -114,10 +96,7 @@ export function extend(publicAPI, model, initialValues = {}) { // Inheritance vtkInteractorStyleMPRSlice.extend(publicAPI, model, initialValues); - macro.setGet(publicAPI, model, [ - 'volumeMapper', - 'onInteractiveRotateChanged', - ]); + macro.setGet(publicAPI, model, ['onInteractiveRotateChanged']); // Object specific methods vtkInteractorStyleMPRRotate(publicAPI, model); diff --git a/src/VTKViewport/vtkInteractorStyleMPRSlice.js b/src/VTKViewport/vtkInteractorStyleMPRSlice.js index 907ed1ef..674c3aa6 100644 --- a/src/VTKViewport/vtkInteractorStyleMPRSlice.js +++ b/src/VTKViewport/vtkInteractorStyleMPRSlice.js @@ -79,12 +79,14 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { function updateScrollManipulator() { const range = publicAPI.getSliceRange(); model.scrollManipulator.removeScrollListener(); + // The Scroll listener has min, max, step, and getValue setValue as params. + // Internally, it checks that the result of the GET has changed, and only calls SET if it is new. model.scrollManipulator.setScrollListener( range[0], range[1], 1, publicAPI.getSlice, - publicAPI.setSlice + publicAPI.scrollToSlice ); } @@ -105,24 +107,15 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { ); } - function onRotateChanged(args) { - setSliceNormalInternal(args.detail.sliceNormal); - setViewUpInternal(args.detail.sliceViewUp); + function onRotateChanged(event) { + setSliceNormalInternal(event.detail.sliceNormal); + setViewUpInternal(event.detail.sliceViewUp); } function setViewUpInternal(viewUp) { const renderer = model.interactor.getCurrentRenderer(); const camera = renderer.getActiveCamera(); - const _viewUp = [...viewUp]; - - if (model.volumeMapper) { - let volumeCoordinateSpace = vec9toMat3([1, 0, 0, 0, 1, 0, 0, 0, 1]); - // Transpose the volume's coordinate space to create a transformation matrix - vtkMath.transpose3x3(volumeCoordinateSpace, volumeCoordinateSpace); - - vtkMath.multiply3x3_vect3(volumeCoordinateSpace, _viewUp, _viewUp); - camera.setViewUp(..._viewUp); - } + camera.setViewUp(...viewUp); } // in world space @@ -133,20 +126,15 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { //copy arguments for internal editing so we don't cause sideeffects const _normal = [...normal]; - if (model.volumeMapper) { + if (model.volumeActor) { vtkMath.normalize(_normal); - let volumeCoordinateSpace = vec9toMat3([1, 0, 0, 0, 1, 0, 0, 0, 1]); - // Transpose the volume's coordinate space to create a transformation matrix - vtkMath.transpose3x3(volumeCoordinateSpace, volumeCoordinateSpace); - // Convert the provided normal into the volume's space - vtkMath.multiply3x3_vect3(volumeCoordinateSpace, _normal, _normal); let center = camera.getFocalPoint(); let dist = camera.getDistance(); let angle = camera.getViewAngle(); if (!isCameraViewInitialized(camera)) { - const bounds = model.volumeMapper.getBounds(); + const bounds = model.volumeActor.getMapper().getBounds(); // diagonal will be used as "width" of camera scene const diagonal = Math.sqrt( vtkMath.distance2BetweenPoints( @@ -174,26 +162,12 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { center[2] - _normal[2] * dist, ]; - // set viewUp based on DOP rotation - // const oldDop = camera.getDirectionOfProjection(); - // const transform = vtkMatrixBuilder - // .buildFromDegree() - // .identity() - // .rotateFromDirections(oldDop, _normal); - - // transform.apply(_viewUp); - - const { slabThickness } = model; - camera.setPosition(...cameraPos); camera.setDistance(dist); // should be set after pos and distance camera.setDirectionOfProjection(..._normal); camera.setViewAngle(angle); - camera.setClippingRange( - dist - slabThickness / 2, - dist + slabThickness / 2 - ); + camera.setThicknessFromFocalPoint(model.slabThickness); publicAPI.setCenterOfRotation(center); } @@ -209,12 +183,13 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { model.viewportData = viewportData; if (model.scrollManipulator.setViewportData) { + // scroll manipulator is the custom MouseRangeRotate manipulator model.scrollManipulator.setViewportData(viewportData); } if (viewportData) { - setSliceNormalInternal(viewportData.getSliceNormal()); - setViewUpInternal(viewportData.getViewUp()); + setSliceNormalInternal(viewportData.getCurrentSliceNormal()); + setViewUpInternal(viewportData.getCurrentViewUp()); viewportData .getEventWindow() @@ -245,20 +220,12 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { const camera = renderer.getActiveCamera(); cameraSub = camera.onModified(() => { - // TODO: check why this is here? - // It overwrites inhirited functions updateScrollManipulator(); publicAPI.modified(); }); interactorSub = interactor.onAnimation(() => { - const { slabThickness } = model; - - const dist = camera.getDistance(); - const near = dist - slabThickness / 2; - const far = dist + slabThickness / 2; - - camera.setClippingRange(near, far); + camera.setThicknessFromFocalPoint(model.slabThickness); }); const eventWindow = model.interactor.getContainer(); @@ -271,38 +238,31 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { publicAPI.handleMouseMove = macro.chain(publicAPI.handleMouseMove, () => { const renderer = model.interactor.getCurrentRenderer(); - const { slabThickness } = model; const camera = renderer.getActiveCamera(); - const dist = camera.getDistance(); - const near = dist - slabThickness / 2; - const far = dist + slabThickness / 2; - - camera.setClippingRange(near, far); + camera.setThicknessFromFocalPoint(model.slabThickness); }); - const superSetVolumeMapper = publicAPI.setVolumeMapper; - publicAPI.setVolumeMapper = mapper => { - if (superSetVolumeMapper(mapper)) { - const renderer = model.interactor.getCurrentRenderer(); - const camera = renderer.getActiveCamera(); - if (mapper) { - // prevent zoom manipulator from messing with our focal point - camera.setFreezeFocalPoint(true); - - const viewportData = publicAPI.getViewport(); + publicAPI.setVolumeActor = actor => { + model.volumeActor = actor; + const renderer = model.interactor.getCurrentRenderer(); + const camera = renderer.getActiveCamera(); + if (actor) { + // prevent zoom manipulator from messing with our focal point + camera.setFreezeFocalPoint(true); - if (viewportData) { - setSliceNormalInternal(viewportData.getSliceNormal()); - setViewUpInternal(viewportData.getViewUp()); - } + const viewportData = publicAPI.getViewport(); - updateScrollManipulator(); - // NOTE: Disabling this because it makes it more difficult to switch - // interactor styles. Need to find a better way to do this! - //publicAPI.setSliceNormal(...publicAPI.getSliceNormal()); - } else { - camera.setFreezeFocalPoint(false); + if (viewportData) { + setSliceNormalInternal(viewportData.getCurrentSliceNormal()); + setViewUpInternal(viewportData.getCurrentViewUp()); } + + updateScrollManipulator(); + // NOTE: Disabling this because it makes it more difficult to switch + // interactor styles. Need to find a better way to do this! + //publicAPI.setSliceNormal(...publicAPI.getSliceNormal()); + } else { + camera.setFreezeFocalPoint(false); } }; @@ -322,13 +282,22 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { return fp[0]; }; + // Only run the onScroll callback if called from scrolling, + // preventing manual setSlice calls from triggering the CB. + publicAPI.scrollToSlice = slice => { + const slicePoint = publicAPI.setSlice(slice); + // run Callback + const onScroll = publicAPI.getOnScroll(); + if (onScroll) onScroll(slicePoint); + }; + publicAPI.setSlice = slice => { const renderer = model.interactor.getCurrentRenderer(); const camera = renderer.getActiveCamera(); - if (model.volumeMapper) { + if (model.volumeActor) { const range = publicAPI.getSliceRange(); - const bounds = model.volumeMapper.getBounds(); + const bounds = model.volumeActor.getMapper().getBounds(); const clampedSlice = clamp(slice, ...range); @@ -362,15 +331,12 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { camera.setPosition(...cameraPos); camera.setFocalPoint(...slicePoint); - - // run Callback - const onScroll = publicAPI.getOnScroll; - if (onScroll) onScroll(slicePoint); + return slicePoint; } }; publicAPI.getSliceRange = () => { - if (model.volumeMapper) { + if (model.volumeActor) { const sliceNormal = publicAPI.getSliceNormal(); if ( @@ -381,7 +347,7 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { return cache.sliceRange; } - const bounds = model.volumeMapper.getBounds(); + const bounds = model.volumeActor.getMapper().getBounds(); const points = boundsToCorners(bounds); // Get rotation matrix from normal to +X (since bounds is aligned to XYZ) @@ -414,7 +380,7 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { // Slice normal is just camera DOP publicAPI.getSliceNormal = () => { - if (model.volumeMapper && model.interactor) { + if (model.volumeActor && model.interactor) { const renderer = model.interactor.getCurrentRenderer(); const camera = renderer.getActiveCamera(); return camera.getDirectionOfProjection(); @@ -426,14 +392,14 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { const viewportData = publicAPI.getViewport(); if (viewportData) { - viewportData.setOrientation(normal, viewportData.getViewUp()); + viewportData.setOrientation(normal, viewportData.getCurrentViewUp()); } setSliceNormalInternal(normal); }; publicAPI.getViewUp = () => { - if (model.volumeMapper && model.interactor) { + if (model.volumeActor && model.interactor) { const renderer = model.interactor.getCurrentRenderer(); const camera = renderer.getActiveCamera(); @@ -453,6 +419,17 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { setViewUpInternal(viewUp); }; + publicAPI.setSliceOrientation = (normal, viewUp) => { + const viewportData = publicAPI.getViewport(); + + if (viewportData) { + viewportData.setOrientation(normal, viewUp); + } + + setSliceNormalInternal(normal); + setViewUpInternal(viewUp); + }; + publicAPI.setSlabThickness = slabThickness => { model.slabThickness = slabThickness; @@ -460,11 +437,7 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { // thickness property is changed const renderer = model.interactor.getCurrentRenderer(); const camera = renderer.getActiveCamera(); - const dist = camera.getDistance(); - const near = dist - slabThickness / 2; - const far = dist + slabThickness / 2; - - camera.setClippingRange(near, far); + camera.setThicknessFromFocalPoint(slabThickness); }; setManipulators(); @@ -486,8 +459,8 @@ export function extend(publicAPI, model, initialValues = {}) { // Inheritance vtkInteractorStyleManipulator.extend(publicAPI, model, initialValues); - macro.setGet(publicAPI, model, ['volumeMapper']); - macro.get(publicAPI, model, ['slabThickness']); + macro.setGet(publicAPI, model, ['onScroll']); + macro.get(publicAPI, model, ['slabThickness', 'volumeActor']); // Object specific methods vtkInteractorStyleMPRSlice(publicAPI, model); diff --git a/src/VTKViewport/vtkInteractorStyleMPRWindowLevel.js b/src/VTKViewport/vtkInteractorStyleMPRWindowLevel.js index 019c691d..64801df6 100644 --- a/src/VTKViewport/vtkInteractorStyleMPRWindowLevel.js +++ b/src/VTKViewport/vtkInteractorStyleMPRWindowLevel.js @@ -1,8 +1,4 @@ import macro from 'vtk.js/Sources/macro'; -import vtkMouseCameraTrackballRotateManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballRotateManipulator'; -import vtkMouseCameraTrackballPanManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulator'; -import vtkMouseCameraTrackballZoomManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballZoomManipulator'; -import vtkMouseRangeManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseRangeManipulator'; import vtkInteractorStyleMPRSlice from './vtkInteractorStyleMPRSlice.js'; import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants'; import { @@ -23,57 +19,6 @@ const { States } = Constants; function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { // Set our className model.classHierarchy.push('vtkInteractorStyleMPRWindowLevel'); - model.wlStartPos = [0, 0]; - - model.trackballManipulator = vtkMouseCameraTrackballRotateManipulator.newInstance( - { - button: 1, - } - ); - model.panManipulator = vtkMouseCameraTrackballPanManipulator.newInstance({ - button: 1, - shift: true, - }); - - // TODO: The inherited zoom manipulator does not appear to be working? - model.zoomManipulator = vtkMouseCameraTrackballZoomManipulator.newInstance({ - button: 3, - }); - model.scrollManipulator = vtkMouseRangeManipulator.newInstance({ - scrollEnabled: true, - dragEnabled: false, - }); - - function updateScrollManipulator() { - const range = publicAPI.getSliceRange(); - model.scrollManipulator.removeScrollListener(); - model.scrollManipulator.setScrollListener( - range[0], - range[1], - 1, - publicAPI.getSlice, - publicAPI.setSlice - ); - } - - function setManipulators() { - publicAPI.removeAllMouseManipulators(); - publicAPI.addMouseManipulator(model.trackballManipulator); - publicAPI.addMouseManipulator(model.panManipulator); - publicAPI.addMouseManipulator(model.zoomManipulator); - publicAPI.addMouseManipulator(model.scrollManipulator); - updateScrollManipulator(); - } - - publicAPI.setVolumeMapper = mapper => { - model.volumeMapper = mapper; - - updateScrollManipulator(); - }; - - publicAPI.getVolumeMapper = () => { - return model.volumeMapper; - }; const superHandleMouseMove = publicAPI.handleMouseMove; publicAPI.handleMouseMove = callData => { @@ -89,26 +34,8 @@ function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { } }; - const superSetVolumeMapper = publicAPI.setVolumeMapper; - publicAPI.setVolumeMapper = mapper => { - if (superSetVolumeMapper(mapper)) { - const renderer = model.interactor.getCurrentRenderer(); - const camera = renderer.getActiveCamera(); - if (mapper) { - // prevent zoom manipulator from messing with our focal point - camera.setFreezeFocalPoint(true); - - // NOTE: Disabling this because it makes it more difficult to switch - // interactor styles. Need to find a better way to do this! - //publicAPI.setSliceNormal(...publicAPI.getSliceNormal()); - } else { - camera.setFreezeFocalPoint(false); - } - } - }; - publicAPI.windowLevelFromMouse = pos => { - const range = model.volumeMapper + const range = model.volumeActor .getMapper() .getInputData() .getPointData() @@ -144,7 +71,7 @@ function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { }; publicAPI.getWindowLevel = () => { - const range = model.volumeMapper + const range = model.volumeActor .getProperty() .getRGBTransferFunction(0) .getMappingRange() @@ -158,7 +85,7 @@ function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { model.levels.windowWidth = windowWidth; model.levels.windowCenter = windowCenter; - model.volumeMapper + model.volumeActor .getProperty() .getRGBTransferFunction(0) .setMappingRange(lowHigh.lower, lowHigh.upper); @@ -169,7 +96,7 @@ function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { model.wlStartPos[0] = callData.position.x; model.wlStartPos[1] = callData.position.y; if (!callData.shiftKey && !callData.controlKey) { - const property = model.volumeMapper.getProperty(); + const property = model.volumeActor.getProperty(); if (property) { model.initialMRange = property .getRGBTransferFunction(0) @@ -200,8 +127,6 @@ function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { break; } }; - - setManipulators(); } // ---------------------------------------------------------------------------- @@ -209,7 +134,7 @@ function vtkInteractorStyleMPRWindowLevel(publicAPI, model) { // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { - wlStartPos: [], + wlStartPos: [0, 0], levelScale: 1, };