// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkInteractorStyleMultiTouchCamera.h" #include "vtkCallbackCommand.h" #include "vtkCamera.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkTransform.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkInteractorStyleMultiTouchCamera); //------------------------------------------------------------------------------ vtkInteractorStyleMultiTouchCamera::vtkInteractorStyleMultiTouchCamera() = default; //------------------------------------------------------------------------------ vtkInteractorStyleMultiTouchCamera::~vtkInteractorStyleMultiTouchCamera() = default; //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnStartRotate() { this->StartGesture(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnRotate() { if (this->State != VTKIS_GESTURE) { return; } int pointer = this->Interactor->GetPointerIndex(); this->FindPokedRenderer(this->Interactor->GetEventPositions(pointer)[0], this->Interactor->GetEventPositions(pointer)[1]); if (this->CurrentRenderer == nullptr) { return; } vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); int* pinchPositionDisplay = this->Interactor->GetEventPositions(pointer); // Calculate the focal depth since we'll be using it a lot double viewFocus[4]; camera->GetFocalPoint(viewFocus); this->ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); double focalDepth = viewFocus[2]; double oldPickPoint[4] = { 0, 0, 0, 0 }; vtkInteractorObserver::ComputeDisplayToWorld(this->CurrentRenderer, pinchPositionDisplay[0], pinchPositionDisplay[1], focalDepth, oldPickPoint); camera->Roll(this->Interactor->GetRotation() - this->Interactor->GetLastRotation()); camera->GetFocalPoint(viewFocus); this->ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); focalDepth = viewFocus[2]; double newPickPoint[4] = { 0, 0, 0, 0 }; vtkInteractorObserver::ComputeDisplayToWorld(this->CurrentRenderer, pinchPositionDisplay[0], pinchPositionDisplay[1], focalDepth, newPickPoint); double motionVector[3] = { 0, 0, 0 }; vtkMath::Subtract(oldPickPoint, newPickPoint, motionVector); vtkNew cameraTransform; cameraTransform->Identity(); cameraTransform->Translate(motionVector); camera->ApplyTransform(cameraTransform); camera->OrthogonalizeViewUp(); this->Interactor->Render(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnEndRotate() { this->EndGesture(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnStartPinch() { this->StartGesture(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnPinch() { if (this->State != VTKIS_GESTURE) { return; } int pointer = this->Interactor->GetPointerIndex(); this->FindPokedRenderer(this->Interactor->GetEventPositions(pointer)[0], this->Interactor->GetEventPositions(pointer)[1]); if (this->CurrentRenderer == nullptr) { return; } vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); int* pinchPositionDisplay = this->Interactor->GetEventPositions(pointer); // Calculate the focal depth since we'll be using it a lot double viewFocus[4], focalDepth; camera->GetFocalPoint(viewFocus); this->ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); focalDepth = viewFocus[2]; // Remember the position of the center of the pinch in world coordinates // This position should stay in the same location on the screen after the dolly has been performed double oldPickPoint[4] = { 0, 0, 0, 0 }; this->ComputeDisplayToWorld( pinchPositionDisplay[0], pinchPositionDisplay[1], focalDepth, oldPickPoint); double dyf = this->Interactor->GetScale() / this->Interactor->GetLastScale(); if (camera->GetParallelProjection()) { camera->SetParallelScale(camera->GetParallelScale() / dyf); } else { camera->Dolly(dyf); if (this->AutoAdjustCameraClippingRange) { this->CurrentRenderer->ResetCameraClippingRange(); } } camera->GetFocalPoint(viewFocus); this->ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); focalDepth = viewFocus[2]; // New position at the center of the pinch gesture double newPickPoint[4] = { 0, 0, 0, 0 }; this->ComputeDisplayToWorld( pinchPositionDisplay[0], pinchPositionDisplay[1], focalDepth, newPickPoint); // Determine how far the pinch center has been shifted from it's original location double motionVector[4] = { 0, 0, 0 }; vtkMath::Subtract(oldPickPoint, newPickPoint, motionVector); // Translate the camera to compensate for the shift of the pinch center vtkNew cameraTransform; cameraTransform->Identity(); cameraTransform->Translate(motionVector); camera->ApplyTransform(cameraTransform); if (this->Interactor->GetLightFollowCamera()) { this->CurrentRenderer->UpdateLightsGeometryToFollowCamera(); } this->Interactor->Render(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnEndPinch() { this->EndGesture(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnStartPan() { this->StartGesture(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnPan() { if (this->State != VTKIS_GESTURE) { return; } vtkRenderWindowInteractor* rwi = this->Interactor; int pointer = rwi->GetPointerIndex(); int* eventPos = rwi->GetEventPositions(pointer); this->FindPokedRenderer(eventPos[0], eventPos[1]); if (this->CurrentRenderer == nullptr) { return; } vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); // handle panning - 2 DOF double viewFocus[4], focalDepth, viewPoint[3]; double newPickPoint[4], oldPickPoint[4], motionVector[3]; // Calculate the focal depth since we'll be using it a lot camera->GetFocalPoint(viewFocus); this->ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); focalDepth = viewFocus[2]; this->ComputeDisplayToWorld(eventPos[0], eventPos[1], focalDepth, newPickPoint); // Has to recalc old mouse point since the viewport has moved, // so can't move it outside the loop int* lastEventPos = rwi->GetLastEventPositions(pointer); this->ComputeDisplayToWorld(lastEventPos[0], lastEventPos[1], focalDepth, oldPickPoint); // Camera motion is reversed motionVector[0] = oldPickPoint[0] - newPickPoint[0]; motionVector[1] = oldPickPoint[1] - newPickPoint[1]; motionVector[2] = oldPickPoint[2] - newPickPoint[2]; camera->GetFocalPoint(viewFocus); camera->GetPosition(viewPoint); camera->SetFocalPoint( motionVector[0] + viewFocus[0], motionVector[1] + viewFocus[1], motionVector[2] + viewFocus[2]); camera->SetPosition( motionVector[0] + viewPoint[0], motionVector[1] + viewPoint[1], motionVector[2] + viewPoint[2]); // clean up if (this->Interactor->GetLightFollowCamera()) { this->CurrentRenderer->UpdateLightsGeometryToFollowCamera(); } rwi->Render(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::OnEndPan() { this->EndGesture(); } //------------------------------------------------------------------------------ void vtkInteractorStyleMultiTouchCamera::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } VTK_ABI_NAMESPACE_END