UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

182 lines (152 loc) 7.88 kB
import { vec3, vec4, quat, mat4 } from 'gl-matrix'; import vtkCompositeVRManipulator from '../CompositeVRManipulator.js'; import vtkPicker from '../../../Rendering/Core/Picker.js'; import { m as macro } from '../../../macros2.js'; import { States } from '../../../Rendering/Core/InteractorStyle/Constants.js'; // ---------------------------------------------------------------------------- // vtk3DControllerModelSelectorManipulator methods // ---------------------------------------------------------------------------- function vtk3DControllerModelSelectorManipulator(publicAPI, model) { model.classHierarchy.push('vtk3DControllerModelSelectorManipulator'); const picker = vtkPicker.newInstance(); // The current prop we are manipulating, if any. let pickedProp; // pre-allocate array buffers const physicalToWorldMatrix = new Float64Array(16); // arrays holding deltas from lastWorldPosition and lastOrientation const translation = new Float64Array(3); const rotation = new Float64Array(4); const lastOrientationConjugate = new Float64Array(4); const orientationAxis = new Float64Array(3); // arrays holding the transform const computedTransform = new Float64Array(16); const computedTransformRotation = new Float64Array(4); // arrays holding the current state of pickedProp. const transposedPropMatrix = new Float64Array(16); const propCurrentOrientation = new Float64Array(4); const propCurrentOrientationConjugate = new Float64Array(4); // arrays holding the new properties that must be assigned to pickedProp. const propNewTranslation = new Float64Array(3); const propNewScaling = new Float64Array(3); const propNewOrientation = new Float64Array(4); function applyPositionAndOrientationToProp(prop, worldPosition, orientation) { vec3.subtract(translation, worldPosition, model.lastWorldPosition); quat.conjugate(lastOrientationConjugate, model.lastOrientation); quat.multiply(rotation, orientation, lastOrientationConjugate); quat.normalize(rotation, rotation); const rotationAngle = quat.getAxisAngle(orientationAxis, rotation); // reset to identity mat4.identity(computedTransform); // compute transform mat4.translate(computedTransform, computedTransform, worldPosition); mat4.rotate(computedTransform, computedTransform, rotationAngle, orientationAxis); mat4.translate(computedTransform, computedTransform, vec3.negate(new Float64Array(3), worldPosition)); mat4.translate(computedTransform, computedTransform, translation); // lookup the prop internal matrix mat4.transpose(transposedPropMatrix, prop.getMatrix()); // apply the new computedTransform to the prop internal matrix mat4.multiply(computedTransform, computedTransform, transposedPropMatrix); // Multiply the computedTransform with the current prop orientation to get the delta // that must be applied to the prop mat4.getRotation(computedTransformRotation, computedTransform); prop.getOrientationQuaternion(propCurrentOrientation); quat.conjugate(propCurrentOrientationConjugate, propCurrentOrientation); quat.multiply(propNewOrientation, propCurrentOrientationConjugate, computedTransformRotation); quat.normalize(propNewOrientation, propNewOrientation); mat4.getTranslation(propNewTranslation, computedTransform); mat4.getScaling(propNewScaling, computedTransform); // Update the prop internal matrix prop.setPosition(...propNewTranslation); prop.setScale(...propNewScaling); prop.rotateQuaternion(propNewOrientation); } function releasePickedProp() { model.lastOrientation = null; model.lastWorldPosition = null; if (pickedProp) { pickedProp.setDragable(true); } pickedProp = null; } publicAPI.onButton3D = (interactorStyle, renderer, state, eventData) => { // If the button is not pressed, clear the state if (!eventData.pressed) { releasePickedProp(); return macro.VOID; } const camera = renderer.getActiveCamera(); camera.getPhysicalToWorldMatrix(physicalToWorldMatrix); const { targetPosition, targetOrientation } = eventData; // Since targetPosition is in physical coordinates, // transform it using the physicalToWorldMatrix to get it in world coordinates const targetRayWorldPosition = vec3.transformMat4([], [targetPosition.x, targetPosition.y, targetPosition.z], physicalToWorldMatrix); const targetRayWorldDirection = camera.physicalOrientationToWorldDirection([targetOrientation.x, targetOrientation.y, targetOrientation.z, targetOrientation.w]); const dist = renderer.getActiveCamera().getClippingRange()[1]; const rayPoint1 = [...targetRayWorldPosition, 1.0]; const rayPoint2 = [rayPoint1[0] - targetRayWorldDirection[0] * dist, rayPoint1[1] - targetRayWorldDirection[1] * dist, rayPoint1[2] - targetRayWorldDirection[2] * dist, 1.0]; // Perform picking on the given renderer picker.pick3DPoint(rayPoint1, rayPoint2, renderer); const props = picker.getActors(); // If we have picked props, store the first one. if (props.length > 0 && props[0].getNestedDragable()) { pickedProp = props[0]; // prevent the prop from being dragged somewhere else pickedProp.setDragable(false); } else { releasePickedProp(); } return macro.EVENT_ABORT; }; // pre-allocation to reduce gc in onMove3D const currentTargetRayWorldPosition = new Float64Array(3); const currentTargetRayOrientation = new Float64Array(4); publicAPI.onMove3D = (interactorStyle, renderer, state, eventData) => { // If we are not interacting with any prop, we have nothing to do. // Also check for dragable if (state !== States.IS_CAMERA_POSE || pickedProp == null) { return macro.VOID; } const camera = renderer.getActiveCamera(); camera.getPhysicalToWorldMatrix(physicalToWorldMatrix); const { targetPosition } = eventData; vec3.transformMat4(currentTargetRayWorldPosition, [targetPosition.x, targetPosition.y, targetPosition.z], physicalToWorldMatrix); // this is a unit quaternion vec4.set(currentTargetRayOrientation, eventData.targetOrientation.x, eventData.targetOrientation.y, eventData.targetOrientation.z, eventData.targetOrientation.w); if (model.lastWorldPosition && model.lastOrientation) { applyPositionAndOrientationToProp(pickedProp, currentTargetRayWorldPosition, currentTargetRayOrientation); } else { // allocate model.lastWorldPosition = new Float64Array(3); model.lastOrientation = new Float64Array(4); } vec3.copy(model.lastWorldPosition, currentTargetRayWorldPosition); vec4.copy(model.lastOrientation, currentTargetRayOrientation); return macro.EVENT_ABORT; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = {}; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); macro.get(publicAPI, model, ['lastWorldPosition', 'lastOrientation']); macro.obj(publicAPI, model); vtkCompositeVRManipulator.extend(publicAPI, model, initialValues); // Object specific methods vtk3DControllerModelSelectorManipulator(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtk3DControllerModelSelectorManipulator'); // ---------------------------------------------------------------------------- var vtk3DControllerModelSelectorManipulator$1 = { newInstance, extend }; export { vtk3DControllerModelSelectorManipulator$1 as default, extend, newInstance };