diff --git a/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js b/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js index 5335529254e..522005f5527 100644 --- a/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js +++ b/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js @@ -150,8 +150,29 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { const length = vtkMath.normalize(vector); axis.line.source.setDirection(vector); axis.line.source.setHeight(length); - axis.rotation1.source.setCenter(state.getRotationPoint1()); - axis.rotation2.source.setCenter(state.getRotationPoint2()); + + // Rotation handles + const pixelWorldHeight = publicAPI.getPixelWorldHeightAtCoord(center); + const { rendererPixelDims } = model.displayScaleParams; + const minDim = Math.min(rendererPixelDims[0], rendererPixelDims[1]); + const ratio = 0.5; + const distance = + (window.devicePixelRatio * (pixelWorldHeight * ratio * minDim)) / 2; + const rotationHandlePosition = []; + vtkMath.multiplyAccumulate( + center, + vector, + distance, + rotationHandlePosition + ); + axis.rotation1.source.setCenter(rotationHandlePosition); + vtkMath.multiplyAccumulate( + center, + vector, + -distance, + rotationHandlePosition + ); + axis.rotation2.source.setCenter(rotationHandlePosition); } /** @@ -247,7 +268,6 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { const axis2State = state[getAxis2](); let activeLineState = null; - let activeRotationPointName = ''; let methodName = ''; switch (prop) { @@ -261,22 +281,18 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { break; case model.pipelines.axes[0].rotation1.actor: activeLineState = axis1State; - activeRotationPointName = 'RotationPoint1'; methodName = InteractionMethodsName.RotateLine; break; case model.pipelines.axes[0].rotation2.actor: activeLineState = axis1State; - activeRotationPointName = 'RotationPoint2'; methodName = InteractionMethodsName.RotateLine; break; case model.pipelines.axes[1].rotation1.actor: activeLineState = axis2State; - activeRotationPointName = 'RotationPoint1'; methodName = InteractionMethodsName.RotateLine; break; case model.pipelines.axes[1].rotation2.actor: activeLineState = axis2State; - activeRotationPointName = 'RotationPoint2'; methodName = InteractionMethodsName.RotateLine; break; default: @@ -285,7 +301,6 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { } state.setActiveLineState(activeLineState); - state.setActiveRotationPointName(activeRotationPointName); state.setUpdateMethodName(methodName); return state; @@ -296,33 +311,39 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { // Object factory // ---------------------------------------------------------------------------- -const DEFAULT_VALUES = { - axis1Name: '', - axis2Name: '', - coincidentTopologyParameters: { - Point: { - factor: -1.0, - offset: -1.0, - }, - Line: { - factor: -1.5, - offset: -1.5, +function defaultValues(initialValues) { + return { + axis1Name: '', + axis2Name: '', + coincidentTopologyParameters: { + Point: { + factor: -1.0, + offset: -1.0, + }, + Line: { + factor: -1.5, + offset: -1.5, + }, + Polygon: { + factor: -2.0, + offset: -2.0, + }, }, - Polygon: { - factor: -2.0, - offset: -2.0, - }, - }, - rotationEnabled: true, - scaleInPixels: true, - viewType: null, -}; + rotationEnabled: true, + rotationHandlePosition: 0.5, + scaleInPixels: true, + viewType: null, + ...initialValues, + }; +} // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { + Object.assign(model, defaultValues(initialValues)); vtkWidgetRepresentation.extend(publicAPI, model, initialValues); - Object.assign(model, DEFAULT_VALUES, initialValues); + + macro.setGet(publicAPI, model, ['rotationHandlePosition']); // Object specific methods vtkResliceCursorContextRepresentation(publicAPI, model); diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md b/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md index fd323a2588b..c1035fab3c5 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md @@ -24,8 +24,6 @@ These sub states contain : - activeLineState: Used in the behavior.js file in order to get the attribute of the selected line -- activeRotationPointName: Used in the behavior.js file in order to get the selected rotation point - - image: vtkImage used to place the reslice cursor - activeViewType: Used in the behavior.js file in order to get the current view diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js index 1daeeeafe98..b2edaddd2a6 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js @@ -316,20 +316,19 @@ export default function widgetBehavior(publicAPI, model) { ); const center = model.widgetState.getCenter(); - const previousWorldPosition = activeLine[ - `get${model.widgetState.getActiveRotationPointName()}` - ](); - - const previousVectorToOrigin = [0, 0, 0]; - vtkMath.subtract(previousWorldPosition, center, previousVectorToOrigin); - vtkMath.normalize(previousVectorToOrigin); + const previousLineDirection = vtkMath.subtract( + activeLine.getPoint1(), + activeLine.getPoint2(), + [] + ); + vtkMath.normalize(previousLineDirection); const currentVectorToOrigin = [0, 0, 0]; vtkMath.subtract(worldCoords, center, currentVectorToOrigin); vtkMath.normalize(currentVectorToOrigin); const radianAngle = vtkMath.signedAngleBetweenVectors( - previousVectorToOrigin, + previousLineDirection, currentVectorToOrigin, planeNormal ); diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js index 797a854e9ad..a4bdf0093e1 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js @@ -7,6 +7,7 @@ import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; import vtkAnnotatedCubeActor from 'vtk.js/Sources/Rendering/Core/AnnotatedCubeActor'; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; +import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData'; import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper'; import vtkImageReslice from 'vtk.js/Sources/Imaging/Core/ImageReslice'; @@ -15,11 +16,7 @@ import vtkInteractorStyleImage from 'vtk.js/Sources/Interaction/Style/Interactor import vtkInteractorStyleTrackballCamera from 'vtk.js/Sources/Interaction/Style/InteractorStyleTrackballCamera'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; import vtkOutlineFilter from 'vtk.js/Sources/Filters/General/OutlineFilter'; -import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow'; import vtkOrientationMarkerWidget from 'vtk.js/Sources/Interaction/Widgets/OrientationMarkerWidget'; -import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer'; -import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow'; -import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor'; import vtkResliceCursorWidget from 'vtk.js/Sources/Widgets/Widgets3D/ResliceCursorWidget'; import vtkWidgetManager from 'vtk.js/Sources/Widgets/Core/WidgetManager'; @@ -59,25 +56,9 @@ const showDebugActors = true; // ---------------------------------------------------------------------------- const container = document.querySelector('body'); -const table = document.createElement('table'); -table.setAttribute('id', 'table'); -container.appendChild(table); - -// Define first line that will contains control panel -const trLine0 = document.createElement('tr'); -trLine0.setAttribute('id', 'line0'); -table.appendChild(trLine0); const controlContainer = document.createElement('div'); -trLine0.appendChild(controlContainer); controlContainer.innerHTML = controlPanel; - -const trLine1 = document.createElement('tr'); -trLine1.setAttribute('id', 'line1'); -table.appendChild(trLine1); - -const trLine2 = document.createElement('tr'); -trLine2.setAttribute('id', 'line2'); -table.appendChild(trLine2); +container.appendChild(controlContainer); // ---------------------------------------------------------------------------- // Setup rendering code @@ -125,25 +106,28 @@ function createRGBStringFromRGBValues(rgb) { } widgetState.setOpacity(0.6); +widgetState.setSphereRadius(10); const initialPlanesState = { ...widgetState.getPlanes() }; let view3D = null; for (let i = 0; i < 4; i++) { - const element = document.createElement('td'); - - if (i % 2 === 0) { - trLine2.appendChild(element); - } else { - trLine1.appendChild(element); - } - + const element = document.createElement('div'); + element.setAttribute('class', 'view'); + element.style.width = '50%'; + element.style.height = '300px'; + element.style.display = 'inline-block'; + container.appendChild(element); + + const grw = vtkGenericRenderWindow.newInstance(); + grw.setContainer(element); + grw.resize(); const obj = { - renderWindow: vtkRenderWindow.newInstance(), - renderer: vtkRenderer.newInstance(), - GLWindow: vtkOpenGLRenderWindow.newInstance(), - interactor: vtkRenderWindowInteractor.newInstance(), + renderWindow: grw.getRenderWindow(), + renderer: grw.getRenderer(), + GLWindow: grw.getOpenGLRenderWindow(), + interactor: grw.getInteractor(), widgetManager: vtkWidgetManager.newInstance(), }; @@ -152,7 +136,6 @@ for (let i = 0; i < 4; i++) { obj.renderWindow.addRenderer(obj.renderer); obj.renderWindow.addView(obj.GLWindow); obj.renderWindow.setInteractor(obj.interactor); - obj.GLWindow.setContainer(element); obj.interactor.setView(obj.GLWindow); obj.interactor.initialize(); obj.interactor.bindEvents(element); diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js index 3de1a453fbd..bab2250314d 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js @@ -132,7 +132,7 @@ export function rotateVector(vectorToBeRotated, axis, angle) { } // Update the extremities and the rotation point coordinate of the line -function updateLine(lineState, center, axis, lineLength, rotationLength) { +function updateLine(lineState, center, axis, lineLength) { const p1 = [ center[0] - lineLength * axis[0], center[1] - lineLength * axis[1], @@ -143,21 +143,9 @@ function updateLine(lineState, center, axis, lineLength, rotationLength) { center[1] + lineLength * axis[1], center[2] + lineLength * axis[2], ]; - const rotationP1 = [ - center[0] - rotationLength * axis[0], - center[1] - rotationLength * axis[1], - center[2] - rotationLength * axis[2], - ]; - const rotationP2 = [ - center[0] + rotationLength * axis[0], - center[1] + rotationLength * axis[1], - center[2] + rotationLength * axis[2], - ]; lineState.setPoint1(p1); lineState.setPoint2(p2); - lineState.setRotationPoint1(rotationP1); - lineState.setRotationPoint2(rotationP2); } // Update the reslice cursor state according to the three planes normals and the origin @@ -173,12 +161,6 @@ export function updateState(widgetState) { const bounds = widgetState.getImage().getBounds(); const center = widgetState.getCenter(); - // Factor used to define where the rotation point will be displayed - // according to the plane size where there will be visible - const factor = 0.5 * 0.85; - const xRotationLength = (bounds[1] - bounds[0]) * factor; - const yRotationLength = (bounds[3] - bounds[2]) * factor; - const zRotationLength = (bounds[5] - bounds[4]) * factor; // Length of the principal diagonal. const pdLength = 20 * 0.5 * vtkBoundingBox.getDiagonalLength(bounds); @@ -187,45 +169,39 @@ export function updateState(widgetState) { widgetState.getAxisXinY(), center, xyIntersectionLineAxis, - pdLength, - zRotationLength + pdLength ); updateLine( widgetState.getAxisYinX(), center, xyIntersectionLineAxis, - pdLength, - zRotationLength + pdLength ); updateLine( widgetState.getAxisYinZ(), center, yzIntersectionLineAxis, - pdLength, - xRotationLength + pdLength ); updateLine( widgetState.getAxisZinY(), center, yzIntersectionLineAxis, - pdLength, - xRotationLength + pdLength ); updateLine( widgetState.getAxisXinZ(), center, xzIntersectionLineAxis, - pdLength, - yRotationLength + pdLength ); updateLine( widgetState.getAxisZinX(), center, xzIntersectionLineAxis, - pdLength, - yRotationLength + pdLength ); } diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js index 698f3e6a0ef..8919f9382a5 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js @@ -3,7 +3,6 @@ import { ScrollingMethods } from 'vtk.js/Sources/Widgets/Widgets3D/ResliceCursor import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants'; const factor = 1; -const rotationFactor = 1; const axisXColor = [1, 0, 0]; const axisYColor = [0, 1, 0]; const axisZColor = [0, 0, 1]; @@ -13,8 +12,6 @@ const generateAxisXinY = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, 0, -factor] }) .addField({ name: 'point2', initialValue: [0, 0, factor] }) - .addField({ name: 'rotationPoint1', initialValue: [0, 0, -rotationFactor] }) - .addField({ name: 'rotationPoint2', initialValue: [0, 0, rotationFactor] }) .addField({ name: 'color', initialValue: axisXColor }) .addField({ name: 'name', initialValue: 'AxisXinY' }) .addField({ name: 'viewType', initialValue: ViewTypes.YZ_PLANE }) @@ -26,8 +23,6 @@ const generateAxisXinZ = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, -factor, 0] }) .addField({ name: 'point2', initialValue: [0, factor, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [0, -rotationFactor, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [0, rotationFactor, 0] }) .addField({ name: 'color', initialValue: axisXColor }) .addField({ name: 'name', initialValue: 'AxisXinZ' }) .addField({ name: 'viewType', initialValue: ViewTypes.YZ_PLANE }) @@ -39,8 +34,6 @@ const generateAxisYinX = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, 0, -factor] }) .addField({ name: 'point2', initialValue: [0, 0, factor] }) - .addField({ name: 'rotationPoint1', initialValue: [0, 0, -rotationFactor] }) - .addField({ name: 'rotationPoint2', initialValue: [0, 0, rotationFactor] }) .addField({ name: 'color', initialValue: axisYColor }) .addField({ name: 'name', initialValue: 'AxisYinX' }) .addField({ name: 'viewType', initialValue: ViewTypes.XZ_PLANE }) @@ -52,8 +45,6 @@ const generateAxisYinZ = () => .createBuilder() .addField({ name: 'point1', initialValue: [-factor, 0, 0] }) .addField({ name: 'point2', initialValue: [factor, 0, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [-rotationFactor, 0, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [rotationFactor, 0, 0] }) .addField({ name: 'color', initialValue: axisYColor }) .addField({ name: 'name', initialValue: 'AxisYinZ' }) .addField({ name: 'viewType', initialValue: ViewTypes.XZ_PLANE }) @@ -65,8 +56,6 @@ const generateAxisZinX = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, -factor, 0] }) .addField({ name: 'point2', initialValue: [0, factor, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [0, -rotationFactor, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [0, rotationFactor, 0] }) .addField({ name: 'color', initialValue: axisZColor }) .addField({ name: 'name', initialValue: 'AxisZinX' }) .addField({ name: 'viewType', initialValue: ViewTypes.XY_PLANE }) @@ -78,8 +67,6 @@ const generateAxisZinY = () => .createBuilder() .addField({ name: 'point1', initialValue: [-factor, 0, 0] }) .addField({ name: 'point2', initialValue: [factor, 0, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [-rotationFactor, 0, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [rotationFactor, 0, 0] }) .addField({ name: 'color', initialValue: axisZColor }) .addField({ name: 'name', initialValue: 'AxisZinY' }) .addField({ name: 'viewType', initialValue: ViewTypes.XY_PLANE }) @@ -122,7 +109,6 @@ export default function generateState() { .addField({ name: 'center', initialValue: [0, 0, 0] }) .addField({ name: 'opacity', initialValue: 1 }) .addField({ name: 'activeLineState', initialValue: null }) - .addField({ name: 'activeRotationPointName', initialValue: null }) .addField({ name: 'image', initialValue: null }) .addField({ name: 'activeViewType', initialValue: null }) .addField({ name: 'lineThickness', initialValue: 2 })