From 3161d34e7ab6045f206b2fb38e8a153f07d5776c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 21 Dec 2023 15:39:35 -0500 Subject: [PATCH] BUG: Fix Computation of Interaction Transform Used in PositionProp This commit fixes the incorrect and unnecessary conversion from axis-angle to quaternion inadvertently introduced in 503ef1d ("ENH: Add VR interactor style class for transforming objects in MRML", 2018-08-31). The additional conversion, introduced in the mentioned commit, was redundant since the event orientations (internally set using `vtkVRRenderWindowInteractor::ConvertPoseToWorldCoordinates`, itself introduced in kitware/VTK@803d3a327d, "ENH: Use pose matrices instead of camera parameters for VR controllers", 2018-09-25) are already in quaternion format. The redundant conversion was leading to an invalid transformation of the selected object. Reported-by: Lucas Gandel Co-authored-by: David Allemang --- .../vtkVirtualRealityViewInteractorStyle.cxx | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx b/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx index 725e8a2..771064c 100644 --- a/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx +++ b/VirtualReality/MRML/vtkVirtualRealityViewInteractorStyle.cxx @@ -281,36 +281,50 @@ void vtkVirtualRealityViewInteractorStyle::PositionProp(vtkEventData* ed, double return; } - // Get positions and orientations vtkRenderWindowInteractor3D* rwi = static_cast(this->Interactor); + + // Retrieve the last and current event positions in world coordinates double worldPos[3] = {0.0}; edd->GetWorldPosition(worldPos); double* lastWorldPos = rwi->GetLastWorldEventPosition(rwi->GetPointerIndex()); + + // Retrieve the last and current event orientation quaternions double* worldOrientation = rwi->GetWorldEventOrientation(rwi->GetPointerIndex()); double* lastWorldOrientation = rwi->GetLastWorldEventOrientation(rwi->GetPointerIndex()); - // Calculate transform - vtkNew interactionTransform; - interactionTransform->PreMultiply(); - + // Compute the translation vector double translation[3] = {0.0}; for (int i = 0; i < 3; i++) { translation[i] = worldPos[i] - lastWorldPos[i]; } - vtkQuaternion q1; - q1.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees( - lastWorldOrientation[0]), lastWorldOrientation[1], lastWorldOrientation[2], lastWorldOrientation[3]); - vtkQuaternion q2; - q2.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees( - worldOrientation[0]), worldOrientation[1], worldOrientation[2], worldOrientation[3]); + + // Calculate the relative quaternion rotation between the last and + // current orientations (q2 * q1') + vtkQuaternion q1(lastWorldOrientation); + vtkQuaternion q2(worldOrientation); q1.Conjugate(); q2 = q2*q1; + + // Convert from quaternion to angle-axis representation double axis[4] = {0.0}; axis[0] = vtkMath::DegreesFromRadians(q2.GetRotationAngleAndAxis(axis+1)); + + // Apply the calculated relative orientation and translation vector to the + // interaction transform + vtkNew interactionTransform; + interactionTransform->PreMultiply(); + + // Step 1: Translate to the current world position interactionTransform->Translate(worldPos[0], worldPos[1], worldPos[2]); + + // Step 2: Apply relative rotation interactionTransform->RotateWXYZ(axis[0], axis[1], axis[2], axis[3]); + + // Step 3: Translate back to the origin interactionTransform->Translate(-worldPos[0], -worldPos[1], -worldPos[2]); + + // Step 4: Final translation based on the computed translation vector interactionTransform->Translate(translation[0], translation[1], translation[2]); // Make sure that the topmost parent transform is the VR interaction transform