|
| 1 | +import macro from 'vtk.js/Sources/macros'; |
| 2 | +import vtkMouseCameraTrackballPanManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulator'; |
| 3 | +import { mat4, vec3 } from 'gl-matrix'; |
| 4 | + |
| 5 | +// ---------------------------------------------------------------------------- |
| 6 | +// Helper functions for center of rotation adjustment |
| 7 | +// ---------------------------------------------------------------------------- |
| 8 | + |
| 9 | +/** |
| 10 | + * Transforms a vector by the transformation delta between two matrices. |
| 11 | + * |
| 12 | + * @param {Object} tempObjects - Temporary matrices/vectors for computation |
| 13 | + * @param {mat4} beforeMatrix - Matrix before transformation |
| 14 | + * @param {mat4} afterMatrix - Matrix after transformation |
| 15 | + * @param {Array} vector - Vector to transform [x, y, z] |
| 16 | + * @returns {Array} Transformed vector [x, y, z] |
| 17 | + */ |
| 18 | +function transformVectorByTransformation( |
| 19 | + tempObjects, |
| 20 | + beforeMatrix, |
| 21 | + afterMatrix, |
| 22 | + vector |
| 23 | +) { |
| 24 | + const { matrixA, matrixB, newCenter } = tempObjects; |
| 25 | + |
| 26 | + // The view matrix from vtk.js is row-major, but gl-matrix expects column-major. |
| 27 | + // We need to transpose them before use. |
| 28 | + mat4.transpose(matrixA, beforeMatrix); |
| 29 | + |
| 30 | + mat4.transpose(matrixB, afterMatrix); |
| 31 | + mat4.invert(matrixB, matrixB); |
| 32 | + |
| 33 | + // Compute delta transformation matrix |
| 34 | + mat4.multiply(matrixA, matrixB, matrixA); |
| 35 | + |
| 36 | + vec3.transformMat4(newCenter, vector, matrixA); |
| 37 | + return newCenter; |
| 38 | +} |
| 39 | + |
| 40 | +/** |
| 41 | + * Computes the new center of rotation based on camera movement. |
| 42 | + * When the camera moves (pan), the center of rotation should move |
| 43 | + * by the same transformation to maintain consistent rotation behavior. |
| 44 | + * |
| 45 | + * @param {Object} tempObjects - Temporary matrices/vectors for computation |
| 46 | + * @param {Object} renderer - VTK renderer |
| 47 | + * @param {mat4} beforeCameraMatrix - Camera view matrix before movement |
| 48 | + * @param {Array} oldCenterOfRotation - Previous center of rotation [x, y, z] |
| 49 | + * @returns {Array} New center of rotation [x, y, z] |
| 50 | + */ |
| 51 | +function computeNewCenterOfRotation( |
| 52 | + tempObjects, |
| 53 | + renderer, |
| 54 | + beforeCameraMatrix, |
| 55 | + oldCenterOfRotation |
| 56 | +) { |
| 57 | + const cam = renderer.getActiveCamera(); |
| 58 | + if (!cam || !beforeCameraMatrix) { |
| 59 | + return oldCenterOfRotation; |
| 60 | + } |
| 61 | + const afterMatrixRowMajor = cam.getViewMatrix(); |
| 62 | + |
| 63 | + return transformVectorByTransformation( |
| 64 | + tempObjects, |
| 65 | + beforeCameraMatrix, |
| 66 | + afterMatrixRowMajor, |
| 67 | + oldCenterOfRotation |
| 68 | + ); |
| 69 | +} |
| 70 | + |
| 71 | +function getCameraMatrix(renderer, tempMatrix) { |
| 72 | + const cam = renderer.getActiveCamera(); |
| 73 | + if (cam) { |
| 74 | + mat4.copy(tempMatrix, cam.getViewMatrix()); |
| 75 | + return tempMatrix; |
| 76 | + } |
| 77 | + return null; |
| 78 | +} |
| 79 | + |
| 80 | +// ---------------------------------------------------------------------------- |
| 81 | +// vtkMouseCameraTrackballPanManipulatorAutoCenter methods |
| 82 | +// ---------------------------------------------------------------------------- |
| 83 | + |
| 84 | +function vtkMouseCameraTrackballPanManipulatorAutoCenter(publicAPI, model) { |
| 85 | + // Set our className |
| 86 | + model.classHierarchy.push('vtkMouseCameraTrackballPanManipulatorAutoCenter'); |
| 87 | + |
| 88 | + // Initialize temporary objects to reduce garbage collection |
| 89 | + const tempCameraMatrix = mat4.create(); |
| 90 | + const tempComputeObjects = { |
| 91 | + matrixA: mat4.create(), |
| 92 | + matrixB: mat4.create(), |
| 93 | + newCenter: vec3.create(), |
| 94 | + }; |
| 95 | + |
| 96 | + // Store the original onMouseMove method |
| 97 | + const superOnMouseMove = publicAPI.onMouseMove; |
| 98 | + |
| 99 | + // Override onMouseMove to add center updating behavior |
| 100 | + publicAPI.onMouseMove = (interactor, renderer, position) => { |
| 101 | + if (!position) { |
| 102 | + return; |
| 103 | + } |
| 104 | + |
| 105 | + // Capture camera matrix before pan |
| 106 | + const beforeCameraMatrix = getCameraMatrix(renderer, tempCameraMatrix); |
| 107 | + |
| 108 | + // Call the parent's onMouseMove method |
| 109 | + superOnMouseMove(interactor, renderer, position); |
| 110 | + |
| 111 | + // Update center of rotation after pan |
| 112 | + if (beforeCameraMatrix && model.center) { |
| 113 | + const newCenter = computeNewCenterOfRotation( |
| 114 | + tempComputeObjects, |
| 115 | + renderer, |
| 116 | + beforeCameraMatrix, |
| 117 | + model.center |
| 118 | + ); |
| 119 | + publicAPI.setCenter(newCenter); |
| 120 | + |
| 121 | + // Also update the interactor style's center of rotation |
| 122 | + const style = interactor.getInteractorStyle(); |
| 123 | + if (style && style.setCenterOfRotation) { |
| 124 | + style.setCenterOfRotation(newCenter); |
| 125 | + } |
| 126 | + } |
| 127 | + }; |
| 128 | +} |
| 129 | + |
| 130 | +// ---------------------------------------------------------------------------- |
| 131 | +// Object factory |
| 132 | +// ---------------------------------------------------------------------------- |
| 133 | + |
| 134 | +const DEFAULT_VALUES = {}; |
| 135 | + |
| 136 | +// ---------------------------------------------------------------------------- |
| 137 | + |
| 138 | +export function extend(publicAPI, model, initialValues = {}) { |
| 139 | + Object.assign(model, DEFAULT_VALUES, initialValues); |
| 140 | + |
| 141 | + // Inheritance |
| 142 | + vtkMouseCameraTrackballPanManipulator.extend(publicAPI, model, initialValues); |
| 143 | + |
| 144 | + // Object specific methods |
| 145 | + vtkMouseCameraTrackballPanManipulatorAutoCenter(publicAPI, model); |
| 146 | +} |
| 147 | + |
| 148 | +// ---------------------------------------------------------------------------- |
| 149 | + |
| 150 | +export const newInstance = macro.newInstance( |
| 151 | + extend, |
| 152 | + 'vtkMouseCameraTrackballPanManipulatorAutoCenter' |
| 153 | +); |
| 154 | + |
| 155 | +// ---------------------------------------------------------------------------- |
| 156 | + |
| 157 | +export default { newInstance, extend }; |
0 commit comments