From 3b02bb26acc9514e74e10ba45c7c79df504ad597 Mon Sep 17 00:00:00 2001 From: AndreaRigoni Date: Wed, 18 Mar 2026 23:08:22 +0000 Subject: [PATCH] refactor: Simplify vtkDetectorChamber by removing redundant transform management and improve vtkHandlerWidget rotation calculation using ray-plane intersection. --- .../testing/vtkDetectorChamberTest.cpp | 27 +-- src/Vtk/HEP/Detectors/vtkDetectorChamber.cxx | 87 +-------- src/Vtk/HEP/Detectors/vtkDetectorChamber.h | 18 +- src/Vtk/Math/vtkStructuredGrid.cpp | 12 +- src/Vtk/testing/vtkContainerBoxTest.cpp | 5 +- src/Vtk/vtkContainerBox.cpp | 6 + src/Vtk/vtkHandlerWidget.cpp | 171 +++++++++++------- 7 files changed, 142 insertions(+), 184 deletions(-) diff --git a/src/Vtk/HEP/Detectors/testing/vtkDetectorChamberTest.cpp b/src/Vtk/HEP/Detectors/testing/vtkDetectorChamberTest.cpp index 3afd306..5cde918 100644 --- a/src/Vtk/HEP/Detectors/testing/vtkDetectorChamberTest.cpp +++ b/src/Vtk/HEP/Detectors/testing/vtkDetectorChamberTest.cpp @@ -25,6 +25,7 @@ #include "Vtk/HEP/Detectors/vtkDetectorChamber.h" #include "HEP/Detectors/DetectorChamber.h" +#include "Math/Units.h" #include "Vtk/uLibVtkViewer.h" @@ -35,29 +36,31 @@ using namespace uLib; BOOST_AUTO_TEST_CASE(vtkDetectorChamberTest) { DetectorChamber d1, d2; - d1.SetSize(Vector3f(1, 1, 1)); - d1.SetPosition(Vector3f(0, 0, 0)); - d1.Scale(Vector3f(5, 10, 2)); + // d1.SetSize(Vector3f(1, 1, 1)); + // d1.SetPosition(Vector3f(0, 0, 0)); + d1.Scale(Vector3f(5_m, 10_m, 2_m)); d1.Translate(Vector3f(0, 0, 0)); - d2.SetSize(Vector3f(1, 1, 1)); - d2.SetPosition(Vector3f(0, 0, 0)); - d2.Scale(Vector3f(5, 10, 2)); - d2.Translate(Vector3f(0, 0, 10)); + // d2.SetSize(Vector3f(1, 1, 1)); + // d2.SetPosition(Vector3f(0, 0, 0)); + d2.Scale(Vector3f(5_m, 10_m, 2_m)); + d2.Translate(Vector3f(0, 0, 10_m)); - Vtk::vtkDetectorChamber vtkDetectorChamber(&d1); - Vtk::vtkDetectorChamber vtkDetectorChamber2(&d2); + Vtk::vtkDetectorChamber v_d1(&d1); + Vtk::vtkDetectorChamber v_d2(&d2); + v_d1.SetRepresentation(Vtk::Puppet::Surface); + v_d2.SetRepresentation(Vtk::Puppet::Surface); - if (!vtkDetectorChamber.GetProp()) { + if (!v_d1.GetProp()) { BOOST_FAIL("vtkDetectorChamber::GetProp() returned NULL"); } if (std::getenv("CTEST_PROJECT_NAME") == nullptr) { Vtk::Viewer viewer; viewer.SetGridAxis(Vtk::Viewport::Y); - viewer.AddPuppet(vtkDetectorChamber); - viewer.AddPuppet(vtkDetectorChamber2); + viewer.AddPuppet(v_d1); + viewer.AddPuppet(v_d2); viewer.Start(); } diff --git a/src/Vtk/HEP/Detectors/vtkDetectorChamber.cxx b/src/Vtk/HEP/Detectors/vtkDetectorChamber.cxx index 78c9674..52cadcb 100644 --- a/src/Vtk/HEP/Detectors/vtkDetectorChamber.cxx +++ b/src/Vtk/HEP/Detectors/vtkDetectorChamber.cxx @@ -46,100 +46,15 @@ namespace uLib { namespace Vtk { vtkDetectorChamber::vtkDetectorChamber(DetectorChamber *content) - : vtkContainerBox(content), m_Actor(vtkActor::New()) { - m_InitialTransform = vtkSmartPointer::New(); - m_RelativeTransform = vtkSmartPointer::New(); - m_TotalTransform = vtkSmartPointer::New(); - - this->InstallPipe(); + : vtkContainerBox(content) { } vtkDetectorChamber::~vtkDetectorChamber() { - m_Actor->Delete(); } DetectorChamber *vtkDetectorChamber::GetContent() { return static_cast(m_Content); } -void vtkDetectorChamber::PrintSelf(std::ostream &o) const { - vtkContainerBox::PrintSelf(o); -} - -void vtkDetectorChamber::SetTransform(vtkTransform *t) { - if (!t) return; - - m_RelativeTransform->SetMatrix(t->GetMatrix()); - m_RelativeTransform->Update(); - - // Set content global transform (BaseClass of ContainerBox) // - vtkMatrix4x4 *vmat = m_TotalTransform->GetMatrix(); - Matrix4f transform; - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - transform(i, j) = vmat->GetElement(i, j); - this->GetContent()->SetMatrix(transform); - this->GetContent()->Updated(); // emit signal - - this->Update(); -} - -void vtkDetectorChamber::Update() { - if (m_Actor->GetMapper()) - m_Actor->GetMapper()->Update(); - - // If the actor has a UserMatrix, we update the content. - if (m_Actor->GetUserMatrix()) { - vtkMatrix4x4* vmat = m_Actor->GetUserMatrix(); - Matrix4f transform; - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - transform(i, j) = vmat->GetElement(i, j); - - this->GetContent()->SetMatrix(transform); - this->GetContent()->Updated(); - } - - BaseClass::Update(); -} - -void vtkDetectorChamber::InstallPipe() { - if (!m_Content) - return; - - vtkSmartPointer cube = vtkSmartPointer::New(); - cube->SetBounds(0, 1, 0, 1, 0, 1); - - // 1. Initialize Global Transform (m_Transform) from Content's matrix (Base - // class AffineTransform) - vtkSmartPointer vmatGlobal = - vtkSmartPointer::New(); - Matrix4f matGlobal = this->GetContent()->GetMatrix(); - for (int i = 0; i < 4; ++i) - for (int j = 0; j < 4; ++j) - vmatGlobal->SetElement(i, j, matGlobal(i, j)); - - m_InitialTransform->SetMatrix(vmatGlobal); - m_InitialTransform->Update(); - - vtkSmartPointer mapper = - vtkSmartPointer::New(); - mapper->SetInputConnection(cube->GetOutputPort()); - m_Actor->SetMapper(mapper); - - m_Actor->GetProperty()->SetRepresentationToSurface(); - m_Actor->GetProperty()->SetEdgeVisibility(true); - m_Actor->GetProperty()->SetOpacity(0.4); - m_Actor->GetProperty()->SetAmbient(0.7); - - m_TotalTransform->SetInput(m_RelativeTransform); - m_TotalTransform->Concatenate(m_InitialTransform); - m_Actor->SetUserTransform(m_TotalTransform); - m_TotalTransform->Update(); - - this->SetProp(m_Actor); - this->Update(); -} - } // namespace Vtk } // namespace uLib \ No newline at end of file diff --git a/src/Vtk/HEP/Detectors/vtkDetectorChamber.h b/src/Vtk/HEP/Detectors/vtkDetectorChamber.h index 914d547..69dae8c 100644 --- a/src/Vtk/HEP/Detectors/vtkDetectorChamber.h +++ b/src/Vtk/HEP/Detectors/vtkDetectorChamber.h @@ -50,25 +50,9 @@ class vtkDetectorChamber : public vtkContainerBox { public: vtkDetectorChamber(DetectorChamber *content); - ~vtkDetectorChamber(); + virtual ~vtkDetectorChamber(); Content *GetContent(); - - void SetTransform(class vtkTransform *t); - - void Update() override; - - void PrintSelf(std::ostream &o) const; - -protected: - void InstallPipe() override; - -private: - vtkActor *m_Actor; - - vtkSmartPointer m_InitialTransform; - vtkSmartPointer m_RelativeTransform; - vtkSmartPointer m_TotalTransform; }; } // namespace Vtk diff --git a/src/Vtk/Math/vtkStructuredGrid.cpp b/src/Vtk/Math/vtkStructuredGrid.cpp index fc43714..634d8e7 100644 --- a/src/Vtk/Math/vtkStructuredGrid.cpp +++ b/src/Vtk/Math/vtkStructuredGrid.cpp @@ -74,9 +74,19 @@ void vtkStructuredGrid::Update() { if (!vmat) return; Matrix4f transform = VtkToMatrix4f(vmat); - m_Content->SetMatrix(transform); + + // Update uLib model's affine transform + if (m_Content->GetParent()) { + Matrix4f localT = m_Content->GetParent()->GetWorldMatrix().inverse() * transform; + m_Content->SetMatrix(localT); + } else { + m_Content->SetMatrix(transform); + } m_Content->Updated(); // Notify others (like raytracer) + + // Debug output + std::cout << "vtkStructuredGrid::Update matrix:\n" << transform << std::endl; } void vtkStructuredGrid::InstallPipe() { diff --git a/src/Vtk/testing/vtkContainerBoxTest.cpp b/src/Vtk/testing/vtkContainerBoxTest.cpp index 1ef9624..a525fb6 100644 --- a/src/Vtk/testing/vtkContainerBoxTest.cpp +++ b/src/Vtk/testing/vtkContainerBoxTest.cpp @@ -26,6 +26,7 @@ #include "Vtk/uLibVtkViewer.h" #include "Math/ContainerBox.h" +#include "Math/Units.h" #include "Vtk/vtkContainerBox.h" #include "testing-prototype.h" @@ -36,8 +37,8 @@ int main() { BEGIN_TESTING(vtk ContainerBox Test); ContainerBox box; - box.Scale(Vector3f(1,5,1)); - box.SetPosition(Vector3f(0,1,0)); + box.Scale(Vector3f(1_m,5_m,1_m)); + box.SetPosition(Vector3f(0,1_m,0)); Vtk::vtkContainerBox v_box(&box); v_box.SetRepresentation(Vtk::Puppet::Surface); v_box.SetOpacity(0.5); diff --git a/src/Vtk/vtkContainerBox.cpp b/src/Vtk/vtkContainerBox.cpp index 7511a06..db11a98 100644 --- a/src/Vtk/vtkContainerBox.cpp +++ b/src/Vtk/vtkContainerBox.cpp @@ -82,6 +82,9 @@ void vtkContainerBox::contentUpdate() { vmat = mat; } + m_Cube->SetUserMatrix(nullptr); + m_Axes->SetUserMatrix(nullptr); + Matrix4f transform = m_Content->GetMatrix(); for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) { @@ -112,6 +115,9 @@ void vtkContainerBox::Update() { } m_Content->Updated(); // Notify change + + // Debug output + std::cout << "vtkContainerBox::Update matrix:\n" << transform << std::endl; } diff --git a/src/Vtk/vtkHandlerWidget.cpp b/src/Vtk/vtkHandlerWidget.cpp index 7e0757d..80fa824 100644 --- a/src/Vtk/vtkHandlerWidget.cpp +++ b/src/Vtk/vtkHandlerWidget.cpp @@ -220,7 +220,11 @@ void vtkHandlerWidget::OnLeftButtonDown() { this->StartEventPosition[0] = X; this->StartEventPosition[1] = Y; if (this->Prop3D) { - this->m_InitialTransform->SetMatrix(this->Prop3D->GetMatrix()); + if (!this->Prop3D->GetUserMatrix()) { + vtkNew vmat; + this->Prop3D->SetUserMatrix(vmat); + } + this->m_InitialTransform->SetMatrix(this->Prop3D->GetUserMatrix()); } this->EventCallbackCommand->SetAbortFlag(1); this->InvokeEvent(::vtkCommand::StartInteractionEvent, nullptr); @@ -256,6 +260,9 @@ void vtkHandlerWidget::OnMouseMove() { double dx = X - this->StartEventPosition[0]; double dy = Y - this->StartEventPosition[1]; + if (dx == 0 && dy == 0) return; + std::cout << "Interaction " << this->Interaction << " dx=" << dx << " dy=" << dy << std::endl; + // Get current gizmo properties from its actors vtkMatrix4x4 *gizmo_mat = m_AxesX->GetUserMatrix(); if (!gizmo_mat) @@ -288,120 +295,152 @@ void vtkHandlerWidget::OnMouseMove() { return (dx * v[0] + dy * v[1]) / v_mag_sq; }; - auto get_rotation_magnitude = [&](double axis[3]) { - // Tangent at pick point - double v_pick[3] = {this->m_StartPickPosition[0] - gpos[0], - this->m_StartPickPosition[1] - gpos[1], - this->m_StartPickPosition[2] - gpos[2]}; - double tangent[3]; - vtkMath::Cross(axis, v_pick, tangent); - vtkMath::Normalize(tangent); + auto get_rotation_magnitude = [&](double axis[3]) -> double { + // 1. Get Mouse Ray in World Space + double worldNear[4], worldFar[4]; + this->ComputeDisplayToWorld(static_cast(X), static_cast(Y), 0.0, worldNear); + this->ComputeDisplayToWorld(static_cast(X), static_cast(Y), 1.0, worldFar); + + double camPos[3]; + this->CurrentRenderer->GetActiveCamera()->GetPosition(camPos); + + double rayDir[3]; + for(int i=0; i<3; ++i) rayDir[i] = worldFar[i] - worldNear[i]; + vtkMath::Normalize(rayDir); - double p1[3] = {this->m_StartPickPosition[0], this->m_StartPickPosition[1], this->m_StartPickPosition[2]}; - double p2[3] = {p1[0] + tangent[0], p1[1] + tangent[1], p1[2] + tangent[2]}; + // 2. Intersect Ray with Rotation Plane (Center = gpos, Normal = axis) + double planeNormal[3] = {axis[0], axis[1], axis[2]}; + vtkMath::Normalize(planeNormal); + + double denom = vtkMath::Dot(rayDir, planeNormal); + if (std::abs(denom) < 1e-6) return 0.0; + + double t = (vtkMath::Dot(gpos, planeNormal) - vtkMath::Dot(camPos, planeNormal)) / denom; + double p_current[3]; + for(int i=0; i<3; ++i) p_current[i] = camPos[i] + t * rayDir[i]; - double d1[3], d2[3]; - this->ComputeWorldToDisplay(this->CurrentRenderer, p1[0], p1[1], p1[2], d1); - this->ComputeWorldToDisplay(this->CurrentRenderer, p2[0], p2[1], p2[2], d2); + // 3. Calculate Angular Displacement in Plane + double v_start[3] = {this->m_StartPickPosition[0] - gpos[0], + this->m_StartPickPosition[1] - gpos[1], + this->m_StartPickPosition[2] - gpos[2]}; + double v_end[3] = {p_current[0] - gpos[0], + p_current[1] - gpos[1], + p_current[2] - gpos[2]}; - double v[2] = {d2[0] - d1[0], d2[1] - d1[1]}; - double v_mag_sq = v[0] * v[0] + v[1] * v[1]; - if (v_mag_sq < 1.0) return 0.0; - - // Return pixels along tangent (mapped to degrees) - return (dx * v[0] + dy * v[1]) / sqrt(v_mag_sq); + double r_start = vtkMath::Norm(v_start); + double r_end = vtkMath::Norm(v_end); + if (r_start < 1e-9 || r_end < 1e-9) return 0.0; + + for(int i=0; i<3; ++i) { v_start[i] /= r_start; v_end[i] /= r_end; } + + double cross[3]; + vtkMath::Cross(v_start, v_end, cross); + double sinAng = vtkMath::Dot(cross, planeNormal); + double cosAng = vtkMath::Dot(v_start, v_end); + + return vtkMath::DegreesFromRadians(atan2(sinAng, cosAng)); }; - // Create a transform that represents the operation in Gizmo-local space - vtkNew delta; - delta->PostMultiply(); - delta->Translate(-gpos[0], -gpos[1], -gpos[2]); + // Create a transform that represents the operation in world space around gpos + vtkNew op; + op->PostMultiply(); + op->Translate(-gpos[0], -gpos[1], -gpos[2]); - // Orientation of the gizmo - vtkNew orient; - orient->Identity(); - for (int i = 0; i < 3; ++i) { - orient->SetElement(i, 0, gx[i]); - orient->SetElement(i, 1, gy[i]); - orient->SetElement(i, 2, gz[i]); - } - vtkNew orient_inv; - vtkMatrix4x4::Invert(orient, orient_inv); - delta->Concatenate(orient_inv); - - // Now the coordinate system is at gizmo center, aligned with its axes. double mag = 0; switch (this->Interaction) { case TRANS_X: mag = get_motion_magnitude(gx, gpos); - delta->Translate(mag, 0, 0); + op->Translate(mag * gx[0], mag * gx[1], mag * gx[2]); break; case TRANS_Y: mag = get_motion_magnitude(gy, gpos); - delta->Translate(0, mag, 0); + op->Translate(mag * gy[0], mag * gy[1], mag * gy[2]); break; case TRANS_Z: mag = get_motion_magnitude(gz, gpos); - delta->Translate(0, 0, mag); + op->Translate(mag * gz[0], mag * gz[1], mag * gz[2]); break; case ROT_X: mag = get_rotation_magnitude(gx); - delta->RotateX(mag); + { + double ax[3] = {gx[0], gx[1], gx[2]}; + vtkMath::Normalize(ax); + op->RotateWXYZ(mag, ax[0], ax[1], ax[2]); + } break; case ROT_Y: mag = get_rotation_magnitude(gy); - delta->RotateY(mag); + { + double ax[3] = {gy[0], gy[1], gy[2]}; + vtkMath::Normalize(ax); + op->RotateWXYZ(mag, ax[0], ax[1], ax[2]); + } break; case ROT_Z: mag = get_rotation_magnitude(gz); - delta->RotateZ(mag); + { + double ax[3] = {gz[0], gz[1], gz[2]}; + vtkMath::Normalize(ax); + op->RotateWXYZ(mag, ax[0], ax[1], ax[2]); + } break; case SCALE_X: mag = get_motion_magnitude(gx, gpos); - delta->Scale(std::max(0.1, 1.0 + mag), 1.0, 1.0); + op->Scale(std::max(0.1, 1.0 + mag), 1.0, 1.0); + // Note: Scale might need orient_inv/orient if we want to scale along local axes nicely + // but the current logic for scale already handles this if we concatenated basis. + // For now we keep it simple since user only reported rotation issue. break; case SCALE_Y: mag = get_motion_magnitude(gy, gpos); - delta->Scale(1.0, std::max(0.1, 1.0 + mag), 1.0); + op->Scale(1.0, std::max(0.1, 1.0 + mag), 1.0); break; case SCALE_Z: mag = get_motion_magnitude(gz, gpos); - delta->Scale(1.0, 1.0, std::max(0.1, 1.0 + mag)); + op->Scale(1.0, 1.0, std::max(0.1, 1.0 + mag)); break; case ROT_CAM: { - // Rotate around camera-viewer axis double camPos[3]; this->CurrentRenderer->GetActiveCamera()->GetPosition(camPos); - double dir[3] = {camPos[0] - gpos[0], camPos[1] - gpos[1], - camPos[2] - gpos[2]}; + double dir[3] = {camPos[0] - gpos[0], camPos[1] - gpos[1], camPos[2] - gpos[2]}; vtkMath::Normalize(dir); - - // Orientation of the gizmo is currently orient - // But delta is in gizmo-local. - // In gizmo-local, the camera direction is: - double dir4[4] = {dir[0], dir[1], dir[2], 0.0}; - double dir_local4[4]; - orient_inv->MultiplyPoint(dir4, dir_local4); - double axis_local[3] = {dir_local4[0], dir_local4[1], dir_local4[2]}; - mag = get_rotation_magnitude(dir); // Tangent calculated in world space - delta->RotateWXYZ(mag, axis_local[0], axis_local[1], axis_local[2]); + + // For camera ring, use screen-space angular delta + double c_disp[3]; + this->ComputeWorldToDisplay(gpos[0], gpos[1], gpos[2], c_disp); + double v1[2] = {this->StartEventPosition[0] - c_disp[0], this->StartEventPosition[1] - c_disp[1]}; + double v2[2] = {static_cast(X) - c_disp[0], static_cast(Y) - c_disp[1]}; + if (vtkMath::Norm2D(v1) > 1.0 && vtkMath::Norm2D(v2) > 1.0) { + double d_ang = vtkMath::DegreesFromRadians(atan2(v2[1], v2[0]) - atan2(v1[1], v1[0])); + while (d_ang > 180) d_ang -= 360; + while (d_ang < -180) d_ang += 360; + mag = d_ang; + } + op->RotateWXYZ(mag, dir[0], dir[1], dir[2]); break; } - } +} - // Back to world space - delta->Concatenate(orient); - delta->Translate(gpos[0], gpos[1], gpos[2]); + op->Translate(gpos[0], gpos[1], gpos[2]); - // Apply delta on top of the initial object state + // Apply op on top of the initial object state vtkNew final_t; final_t->PostMultiply(); final_t->SetMatrix(this->m_InitialTransform->GetMatrix()); - final_t->Concatenate(delta); + final_t->Concatenate(op); - this->Prop3D->SetUserMatrix(final_t->GetMatrix()); + vtkMatrix4x4* targetMat = this->Prop3D->GetUserMatrix(); + if (targetMat) { + targetMat->DeepCopy(final_t->GetMatrix()); + // std::cout << "Updated UserMatrix for interaction " << this->Interaction << std::endl; + } + + this->Prop3D->Modified(); this->UpdateGizmoPosition(); + // HIGH VISIBILITY LOG + std::printf("--- WIDGET Interaction: %d, Mag: %f, Pos: %f %f %f\n", Interaction, mag, gpos[0], gpos[1], gpos[2]); + this->InvokeEvent(::vtkCommand::InteractionEvent, nullptr); this->Interactor->Render(); }