/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkInteractorStyleJoystickActor.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkInteractorStyleJoystickActor.h" #include "vtkCamera.h" #include "vtkCommand.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkRenderWindowInteractor.h" #include "vtkProp3D.h" #include "vtkRenderer.h" #include "vtkCellPicker.h" #include "vtkTransform.h" #include "vtkMatrix4x4.h" vtkCxxRevisionMacro(vtkInteractorStyleJoystickActor, "$Revision: 1.31 $"); vtkStandardNewMacro(vtkInteractorStyleJoystickActor); //---------------------------------------------------------------------------- vtkInteractorStyleJoystickActor::vtkInteractorStyleJoystickActor() { this->MotionFactor = 10.0; this->InteractionProp = NULL; this->InteractionPicker = vtkCellPicker::New(); this->InteractionPicker->SetTolerance(0.001); // Use timers to handle continous interaction this->UseTimers = 1; } //---------------------------------------------------------------------------- vtkInteractorStyleJoystickActor::~vtkInteractorStyleJoystickActor() { this->InteractionPicker->Delete(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnMouseMove() { int x = this->Interactor->GetEventPosition()[0]; int y = this->Interactor->GetEventPosition()[1]; switch (this->State) { case VTKIS_ROTATE: case VTKIS_PAN: case VTKIS_DOLLY: case VTKIS_SPIN: case VTKIS_USCALE: this->FindPokedRenderer(x, y); this->InvokeEvent(vtkCommand::InteractionEvent, NULL); break; } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnLeftButtonDown() { int x = this->Interactor->GetEventPosition()[0]; int y = this->Interactor->GetEventPosition()[1]; this->FindPokedRenderer(x, y); this->FindPickedActor(x, y); if (this->CurrentRenderer == NULL || this->InteractionProp == NULL) { return; } if (this->Interactor->GetShiftKey()) { this->StartPan(); } else if (this->Interactor->GetControlKey()) { this->StartSpin(); } else { this->StartRotate(); } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnLeftButtonUp() { switch (this->State) { case VTKIS_PAN: this->EndPan(); break; case VTKIS_SPIN: this->EndSpin(); break; case VTKIS_ROTATE: this->EndRotate(); break; } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnMiddleButtonDown() { int x = this->Interactor->GetEventPosition()[0]; int y = this->Interactor->GetEventPosition()[1]; this->FindPokedRenderer(x, y); this->FindPickedActor(x, y); if (this->CurrentRenderer == NULL || this->InteractionProp == NULL) { return; } if (this->Interactor->GetControlKey()) { this->StartDolly(); } else { this->StartPan(); } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnMiddleButtonUp() { switch (this->State) { case VTKIS_DOLLY: this->EndDolly(); break; case VTKIS_PAN: this->EndPan(); break; } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnRightButtonDown() { int x = this->Interactor->GetEventPosition()[0]; int y = this->Interactor->GetEventPosition()[1]; this->FindPokedRenderer(x, y); this->FindPickedActor(x, y); if (this->CurrentRenderer == NULL || this->InteractionProp == NULL) { return; } this->StartUniformScale(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::OnRightButtonUp() { switch (this->State) { case VTKIS_USCALE: this->EndUniformScale(); break; } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::Rotate() { if ( this->CurrentRenderer == NULL || this->InteractionProp == NULL ) { return; } vtkRenderWindowInteractor *rwi = this->Interactor; vtkCamera *cam = this->CurrentRenderer->GetActiveCamera(); // First get the origin of the assembly double *obj_center = this->InteractionProp->GetCenter(); // GetLength gets the length of the diagonal of the bounding box double boundRadius = this->InteractionProp->GetLength() * 0.5; // Get the view up and view right vectors double view_up[3], view_look[3], view_right[3]; cam->OrthogonalizeViewUp(); cam->ComputeViewPlaneNormal(); cam->GetViewUp(view_up); vtkMath::Normalize(view_up); cam->GetViewPlaneNormal(view_look); vtkMath::Cross(view_up, view_look, view_right); vtkMath::Normalize(view_right); // Get the furtherest point from object bounding box center double outsidept[3]; outsidept[0] = obj_center[0] + view_right[0] * boundRadius; outsidept[1] = obj_center[1] + view_right[1] * boundRadius; outsidept[2] = obj_center[2] + view_right[2] * boundRadius; // Convert to display coord double disp_obj_center[3]; this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center); this->ComputeWorldToDisplay(outsidept[0], outsidept[1], outsidept[2], outsidept); double radius = sqrt(vtkMath::Distance2BetweenPoints(disp_obj_center, outsidept)); double nxf = ((double)rwi->GetEventPosition()[0] - (double)disp_obj_center[0]) / radius; double nyf = ((double)rwi->GetEventPosition()[1] - (double)disp_obj_center[1]) / radius; if (nxf > 1.0) { nxf = 1.0; } else if (nxf < -1.0) { nxf = -1.0; } if (nyf > 1.0) { nyf = 1.0; } else if (nyf < -1.0) { nyf = -1.0; } double newXAngle = asin(nxf) * vtkMath::RadiansToDegrees() / this->MotionFactor; double newYAngle = asin(nyf) * vtkMath::RadiansToDegrees() / this->MotionFactor; double scale[3]; scale[0] = scale[1] = scale[2] = 1.0; double **rotate = new double*[2]; rotate[0] = new double[4]; rotate[1] = new double[4]; rotate[0][0] = newXAngle; rotate[0][1] = view_up[0]; rotate[0][2] = view_up[1]; rotate[0][3] = view_up[2]; rotate[1][0] = -newYAngle; rotate[1][1] = view_right[0]; rotate[1][2] = view_right[1]; rotate[1][3] = view_right[2]; this->Prop3DTransform(this->InteractionProp, obj_center, 2, rotate, scale); delete [] rotate[0]; delete [] rotate[1]; delete [] rotate; if (this->AutoAdjustCameraClippingRange) { this->CurrentRenderer->ResetCameraClippingRange(); } rwi->Render(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::Spin() { if ( this->CurrentRenderer == NULL || this->InteractionProp == NULL ) { return; } vtkRenderWindowInteractor *rwi = this->Interactor; vtkCamera *cam = this->CurrentRenderer->GetActiveCamera(); // Get the axis to rotate around = vector from eye to origin double *obj_center = this->InteractionProp->GetCenter(); double motion_vector[3]; double view_point[3]; if (cam->GetParallelProjection()) { // If parallel projection, want to get the view plane normal... cam->ComputeViewPlaneNormal(); cam->GetViewPlaneNormal(motion_vector); } else { // Perspective projection, get vector from eye to center of actor cam->GetPosition(view_point); motion_vector[0] = view_point[0] - obj_center[0]; motion_vector[1] = view_point[1] - obj_center[1]; motion_vector[2] = view_point[2] - obj_center[2]; vtkMath::Normalize(motion_vector); } double disp_obj_center[3]; this->ComputeWorldToDisplay(obj_center[0], obj_center[1],obj_center[2], disp_obj_center); double *center = this->CurrentRenderer->GetCenter(); double yf = ((double)rwi->GetEventPosition()[1] - (double)disp_obj_center[1]) / (double)center[1]; if (yf > 1.0) { yf = 1.0; } else if (yf < -1.0) { yf = -1.0; } double newAngle = asin(yf) * vtkMath::RadiansToDegrees() / this->MotionFactor; double scale[3]; scale[0] = scale[1] = scale[2] = 1.0; double **rotate = new double*[1]; rotate[0] = new double[4]; rotate[0][0] = newAngle; rotate[0][1] = motion_vector[0]; rotate[0][2] = motion_vector[1]; rotate[0][3] = motion_vector[2]; this->Prop3DTransform(this->InteractionProp, obj_center, 1, rotate, scale); delete [] rotate[0]; delete [] rotate; if (this->AutoAdjustCameraClippingRange) { this->CurrentRenderer->ResetCameraClippingRange(); } rwi->Render(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::Pan() { if (this->CurrentRenderer == NULL || this->InteractionProp == NULL) { return; } vtkRenderWindowInteractor *rwi = this->Interactor; // Use initial center as the origin from which to pan double *obj_center = this->InteractionProp->GetCenter(); double disp_obj_center[3], new_pick_point[4], motion_vector[3]; this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center); this->ComputeDisplayToWorld((double)rwi->GetEventPosition()[0], (double)rwi->GetEventPosition()[1], disp_obj_center[2], new_pick_point); // Compute a translation vector, moving everything 1/10 // the distance to the cursor. (Arbitrary scale factor) motion_vector[0] = (new_pick_point[0] - obj_center[0]) / this->MotionFactor; motion_vector[1] = (new_pick_point[1] - obj_center[1]) / this->MotionFactor; motion_vector[2] = (new_pick_point[2] - obj_center[2]) / this->MotionFactor; if (this->InteractionProp->GetUserMatrix() != NULL) { vtkTransform *t = vtkTransform::New(); t->PostMultiply(); t->SetMatrix(this->InteractionProp->GetUserMatrix()); t->Translate(motion_vector[0], motion_vector[1], motion_vector[2]); this->InteractionProp->GetUserMatrix()->DeepCopy(t->GetMatrix()); t->Delete(); } else { this->InteractionProp->AddPosition(motion_vector[0], motion_vector[1], motion_vector[2]); } rwi->Render(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::Dolly() { if (this->CurrentRenderer == NULL || this->InteractionProp == NULL) { return; } vtkRenderWindowInteractor *rwi = this->Interactor; vtkCamera *cam = this->CurrentRenderer->GetActiveCamera(); // Dolly is based on distance from center of screen, // and the upper half is positive, lower half is negative double view_point[3], view_focus[3]; double motion_vector[3]; cam->GetPosition(view_point); cam->GetFocalPoint(view_focus); // Use initial center as the origin from which to pan double *obj_center = this->InteractionProp->GetCenter(); double disp_obj_center[3]; this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center); double *center = this->CurrentRenderer->GetCenter(); double yf = ((double)rwi->GetEventPosition()[1] - (double)disp_obj_center[1]) / (double)center[1]; double dollyFactor = pow((double)1.1, yf); dollyFactor -= 1.0; motion_vector[0] = (view_point[0] - view_focus[0]) * dollyFactor; motion_vector[1] = (view_point[1] - view_focus[1]) * dollyFactor; motion_vector[2] = (view_point[2] - view_focus[2]) * dollyFactor; if (this->InteractionProp->GetUserMatrix() != NULL) { vtkTransform *t = vtkTransform::New(); t->PostMultiply(); t->SetMatrix(this->InteractionProp->GetUserMatrix()); t->Translate(motion_vector[0], motion_vector[1], motion_vector[2]); this->InteractionProp->GetUserMatrix()->DeepCopy(t->GetMatrix()); t->Delete(); } else { this->InteractionProp->AddPosition(motion_vector[0], motion_vector[1], motion_vector[2]); } if (this->AutoAdjustCameraClippingRange) { this->CurrentRenderer->ResetCameraClippingRange(); } rwi->Render(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::UniformScale() { if (this->CurrentRenderer == NULL || this->InteractionProp == NULL) { return; } vtkRenderWindowInteractor *rwi = this->Interactor; // Uniform scale is based on distance from center of screen, // and the upper half is positive, lower half is negative // use bounding box center as the origin from which to pan double *obj_center = this->InteractionProp->GetCenter(); double disp_obj_center[3]; this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2], disp_obj_center); double *center = this->CurrentRenderer->GetCenter(); double yf = ((double)rwi->GetEventPosition()[1] - (double)disp_obj_center[1]) / (double)center[1]; double scaleFactor = pow((double)1.1, yf); double **rotate = NULL; double scale[3]; scale[0] = scale[1] = scale[2] = scaleFactor; this->Prop3DTransform(this->InteractionProp, obj_center, 0, rotate, scale); if (this->AutoAdjustCameraClippingRange) { this->CurrentRenderer->ResetCameraClippingRange(); } rwi->Render(); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::FindPickedActor(int x, int y) { this->InteractionPicker->Pick(x, y, 0.0, this->CurrentRenderer); vtkProp *prop = this->InteractionPicker->GetViewProp(); if (prop != NULL) { this->InteractionProp = vtkProp3D::SafeDownCast(prop); } else { this->InteractionProp = NULL; } } //---------------------------------------------------------------------------- void vtkInteractorStyleJoystickActor::Prop3DTransform(vtkProp3D *prop3D, double *boxCenter, int numRotation, double **rotate, double *scale) { vtkMatrix4x4 *oldMatrix = vtkMatrix4x4::New(); prop3D->GetMatrix(oldMatrix); double orig[3]; prop3D->GetOrigin(orig); vtkTransform *newTransform = vtkTransform::New(); newTransform->PostMultiply(); if (prop3D->GetUserMatrix() != NULL) { newTransform->SetMatrix(prop3D->GetUserMatrix()); } else { newTransform->SetMatrix(oldMatrix); } newTransform->Translate(-(boxCenter[0]), -(boxCenter[1]), -(boxCenter[2])); for (int i = 0; i < numRotation; i++) { newTransform->RotateWXYZ(rotate[i][0], rotate[i][1], rotate[i][2], rotate[i][3]); } if ((scale[0] * scale[1] * scale[2]) != 0.0) { newTransform->Scale(scale[0], scale[1], scale[2]); } newTransform->Translate(boxCenter[0], boxCenter[1], boxCenter[2]); // now try to get the composit of translate, rotate, and scale newTransform->Translate(-(orig[0]), -(orig[1]), -(orig[2])); newTransform->PreMultiply(); newTransform->Translate(orig[0], orig[1], orig[2]); if (prop3D->GetUserMatrix() != NULL) { newTransform->GetMatrix(prop3D->GetUserMatrix()); } else { prop3D->SetPosition(newTransform->GetPosition()); prop3D->SetScale(newTransform->GetScale()); prop3D->SetOrientation(newTransform->GetOrientation()); } oldMatrix->Delete(); newTransform->Delete(); }