import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; import vtkCompositeCameraManipulator from 'vtk.js/Sources/Interaction/Manipulators/CompositeCameraManipulator'; import vtkCompositeMouseManipulator from 'vtk.js/Sources/Interaction/Manipulators/CompositeMouseManipulator'; import vtkInteractorStyleConstants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; import vtkPointPicker from 'vtk.js/Sources/Rendering/Core/PointPicker'; import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource';
import { FieldAssociations } from 'vtk.js/Sources/Common/DataModel/DataSet/Constants'; import { mat4, vec3 } from 'gl-matrix'; import macro from 'vtk.js/Sources/macros'; import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
const { States } = vtkInteractorStyleConstants;
function vtkMouseCameraUnicamRotateManipulator(publicAPI, model) { model.classHierarchy.push('vtkMouseCameraUnicamRotateManipulator');
model.picker = vtkPointPicker.newInstance();
model.downPoint = [0, 0, 0]; model.isDot = false; model.state = States.IS_NONE;
const sphereSource = vtkSphereSource.newInstance(); sphereSource.setThetaResolution(6); sphereSource.setPhiResolution(6); const sphereMapper = vtkMapper.newInstance(); sphereMapper.setInputConnection(sphereSource.getOutputPort());
model.focusSphere = vtkActor.newInstance(); model.focusSphere.setMapper(sphereMapper); model.focusSphere.getProperty().setColor(0.89, 0.66, 0.41); model.focusSphere.getProperty().setAmbient(1); model.focusSphere.getProperty().setDiffuse(0); model.focusSphere.getProperty().setRepresentationToWireframe();
const updateAndRender = (interactor) => { if (!interactor) { return; }
if (model.useWorldUpVec) { const camera = interactor.findPokedRenderer().getActiveCamera(); if (!vtkMath.areEquals(model.worldUpVec, camera.getViewPlaneNormal())) { camera.setViewUp(model.worldUpVec); } } interactor.render(); };
const normalize = (position, interactor) => { const renderer = interactor.findPokedRenderer(); const [width, height] = interactor.getView().getViewportSize(renderer);
const nx = -1.0 + (2.0 * position.x) / width; const ny = -1.0 + (2.0 * position.y) / height;
return { x: nx, y: ny }; };
const rotateCamera = (camera, cx, cy, cz, ax, ay, az, angle) => { const cameraPosition = camera.getPosition(); const cameraFocalPoint = camera.getFocalPoint(); const cameraViewUp = camera.getViewUp();
cameraPosition[3] = 1.0; cameraFocalPoint[3] = 1.0; cameraViewUp[3] = 0.0;
const transform = mat4.identity(new Float64Array(16)); mat4.translate(transform, transform, [cx, cy, cz]); mat4.rotate(transform, transform, angle, [ax, ay, az]); mat4.translate(transform, transform, [-cx, -cy, -cz]); const newCameraPosition = []; const newCameraFocalPoint = []; vec3.transformMat4(newCameraPosition, cameraPosition, transform); vec3.transformMat4(newCameraFocalPoint, cameraFocalPoint, transform);
mat4.identity(transform); mat4.rotate(transform, transform, angle, [ax, ay, az]); const newCameraViewUp = []; vec3.transformMat4(newCameraViewUp, cameraViewUp, transform);
camera.setPosition(...newCameraPosition); camera.setFocalPoint(...newCameraFocalPoint); camera.setViewUp(...newCameraViewUp); };
const rotate = (interactor, position) => { const renderer = interactor.findPokedRenderer(); const normalizedPosition = normalize(position, interactor); const normalizedPreviousPosition = normalize( model.previousPosition, interactor );
const center = model.focusSphere.getPosition(); let normalizedCenter = interactor .getView() .worldToDisplay(...center, renderer); normalizedCenter = normalize({ x: center[0], y: center[1] }, interactor); normalizedCenter = [normalizedCenter.x, normalizedCenter.y, center[2]];
const radsq = (1.0 + Math.abs(normalizedCenter[0])) ** 2.0; const op = [normalizedPreviousPosition.x, 0, 0]; const oe = [normalizedPosition.x, 0, 0];
const opsq = op[0] ** 2; const oesq = oe[0] ** 2;
const lop = opsq > radsq ? 0 : Math.sqrt(radsq - opsq); const loe = oesq > radsq ? 0 : Math.sqrt(radsq - oesq);
const nop = [op[0], 0, lop]; vtkMath.normalize(nop); const noe = [oe[0], 0, loe]; vtkMath.normalize(noe);
const dot = vtkMath.dot(nop, noe); if (Math.abs(dot) > 0.0001) { const angle = -2 * Math.acos(vtkMath.clampValue(dot, -1.0, 1.0)) * Math.sign(normalizedPosition.x - normalizedPreviousPosition.x) * publicAPI.getRotationFactor();
const camera = renderer.getActiveCamera();
const upVec = model.useWorldUpVec ? model.worldUpVec : camera.getViewUp(); vtkMath.normalize(upVec);
rotateCamera(camera, ...center, ...upVec, angle);
const dVec = []; const cameraPosition = camera.getPosition(); vtkMath.subtract(cameraPosition, position, dVec);
let rDist = (normalizedPosition.y - normalizedPreviousPosition.y) * publicAPI.getRotationFactor(); vtkMath.normalize(dVec);
const atV = camera.getViewPlaneNormal(); const upV = camera.getViewUp(); const rightV = []; vtkMath.cross(upV, atV, rightV); vtkMath.normalize(rightV);
if (model.useWorldUpVec) { const OVER_THE_TOP_THRESHOLD = 0.99; if (vtkMath.dot(upVec, atV) > OVER_THE_TOP_THRESHOLD && rDist < 0) { rDist = 0; } if (vtkMath.dot(upVec, atV) < -OVER_THE_TOP_THRESHOLD && rDist > 0) { rDist = 0; } }
rotateCamera(camera, ...center, ...rightV, rDist);
if ( model.useWorldUpVec && !vtkMath.areEquals(upVec, camera.getViewPlaneNormal()) ) { camera.setViewUp(...upVec); }
model.previousPosition = position;
renderer.resetCameraClippingRange(); updateAndRender(interactor); } };
const placeFocusSphere = (interactor) => { const renderer = interactor.findPokedRenderer(); model.focusSphere.setPosition(...model.downPoint);
const camera = renderer.getActiveCamera(); const cameraPosition = camera.getPosition(); const cameraToPointVec = [];
vtkMath.subtract(model.downPoint, cameraPosition, cameraToPointVec); if (camera.getParallelProjection()) { vtkMath.multiplyScalar(cameraToPointVec, camera.getParallelScale()); }
const atV = camera.getDirectionOfProjection(); vtkMath.normalize(atV);
const scale = 0.02 * vtkMath.dot(atV, cameraToPointVec) * model.focusSphereRadiusFactor;
model.focusSphere.setScale(scale, scale, scale); };
const placeAndDisplayFocusSphere = (interactor) => { placeFocusSphere(interactor); interactor.findPokedRenderer().addActor(model.focusSphere); model.isDot = true; };
const hideFocusSphere = (interactor) => { interactor.findPokedRenderer().removeActor(model.focusSphere); model.isDot = false; };
const pickWithPointPicker = (interactor, position) => { const renderer = interactor.findPokedRenderer(); model.picker.pick([position.x, position.y, position.z], renderer); const pickedPositions = model.picker.getPickedPositions();
if (pickedPositions.length === 0) { return model.picker.getPickPosition(); }
const cameraPosition = renderer.getActiveCamera().getPosition();
pickedPositions.sort( (pointA, pointB) => vtkMath.distance2BetweenPoints(pointA, cameraPosition) - vtkMath.distance2BetweenPoints(pointB, cameraPosition) );
return pickedPositions[0]; };
const pickPoint = (interactor, position) => { const renderer = interactor.findPokedRenderer(); let selections = null; if (model.useHardwareSelector) { const selector = interactor.getView().getSelector(); selector.setCaptureZValues(true); selector.setFieldAssociation(FieldAssociations.FIELD_ASSOCIATION_POINTS); selector.attach(interactor.getView(), renderer);
selector.setArea(position.x, position.y, position.x, position.y); selections = selector.select(); }
if (selections && selections.length !== 0) { return Array.from(selections[0].getProperties().worldPosition); } return pickWithPointPicker(interactor, position); };
publicAPI.onButtonDown = (interactor, renderer, position) => { model.buttonPressed = true; model.startPosition = position; model.previousPosition = position;
const normalizedPosition = normalize(position, interactor); const borderRatio = 0.1; if ( Math.abs(normalizedPosition.x) > 1 - borderRatio || Math.abs(normalizedPosition.y) > 1 - borderRatio ) { model.state = States.IS_ROTATE; placeAndDisplayFocusSphere(interactor); return; }
model.downPoint = pickPoint(interactor, position);
if (model.isDot) { model.state = States.IS_ROTATE; } else { model.state = States.IS_NONE; if (model.displayFocusSphereOnButtonDown) { placeAndDisplayFocusSphere(interactor); } } };
publicAPI.onMouseMove = (interactor, renderer, position) => { if (!model.buttonPressed) { return; }
model.state = States.IS_ROTATE; rotate(interactor, position);
model.previousPosition = position; };
publicAPI.onButtonUp = (interactor) => { const renderer = interactor.findPokedRenderer(); model.buttonPressed = false;
if (model.state === States.IS_ROTATE && !model.isDot) { return; }
if (model.state === States.IS_ROTATE) { hideFocusSphere(interactor); } else if (model.state === States.IS_NONE) { placeAndDisplayFocusSphere(interactor); }
renderer.resetCameraClippingRange(); updateAndRender(interactor); };
publicAPI.getFocusSphereColor = () => { model.focusSphere.getProperty().getColor(); }; publicAPI.setFocusSphereColor = (r, g, b) => { model.focusSphere.getProperty().setColor(r, g, b); }; }
const DEFAULT_VALUES = { focusSphereRadiusFactor: 1, displayFocusSphereOnButtonDown: true, useHardwareSelector: true, useWorldUpVec: true, worldUpVec: [0, 0, 1], };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
macro.obj(publicAPI, model); vtkCompositeCameraManipulator.extend(publicAPI, model, initialValues); vtkCompositeMouseManipulator.extend(publicAPI, model, initialValues);
macro.setGet(publicAPI, model, [ 'focusSphereRadiusFactor', 'displayFocusSphereOnButtonDown', 'useHardwareSelector', 'useWorldUpVec', ]); macro.get(publicAPI, model, ['state']); macro.getArray(publicAPI, model, ['downPoint'], 3); macro.setGetArray(publicAPI, model, ['worldUpVec'], 3);
vtkMouseCameraUnicamRotateManipulator(publicAPI, model); }
export const newInstance = macro.newInstance( extend, 'vtkMouseCameraUnicamRotateManipulator' );
export default { newInstance, extend };
|