fix transforms from handler

This commit is contained in:
AndreaRigoni
2026-03-24 11:36:46 +00:00
parent f13342ff30
commit b45cde0bad
11 changed files with 201 additions and 26 deletions

View File

@@ -14,6 +14,8 @@
ContextPanel::ContextPanel(QWidget* parent)
: QWidget(parent)
, m_context(nullptr) {
this->setObjectName("ContextPanel");
this->setAttribute(Qt::WA_StyledBackground);
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(0);
@@ -88,6 +90,5 @@ void ContextPanel::onSelectionChanged(const QItemSelection& selected, const QIte
}
emit objectSelected(target);
m_propertiesPanel->setObject(target);
}

View File

@@ -17,6 +17,8 @@
#include "StyleManager.h"
MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_mainVtkContext(nullptr) {
this->setObjectName("MainPanel");
this->setAttribute(Qt::WA_StyledBackground);
auto* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
@@ -158,7 +160,6 @@ void MainPanel::setContext(uLib::ObjectsContext* context) {
}
uLib::Object::connect(context, &uLib::Object::Updated, [viewport]() {
viewport->ZoomAuto();
viewport->Render();
});
viewport->ZoomAuto();

View File

@@ -7,6 +7,7 @@
PropertiesPanel::PropertiesPanel(QWidget* parent) : QWidget(parent) {
this->setObjectName("PropertiesPanel");
this->setAttribute(Qt::WA_StyledBackground);
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(0);

View File

@@ -11,8 +11,9 @@ QLabel#TitleLabel { font-weight: bold; margin-left: 2px; }
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #ccc; }
QToolButton#PaneCloseButton:hover { color: white; background: #c42b1c; }
/* Viewport & Properties Panels */
QWidget#DisplayPropertiesPanel, QWidget#PropertiesPanel { background-color: #252526; border-left: 1px solid #3e3e42; }
/* Global & Panel Backgrounds */
QMainWindow, QWidget#MainPanel { background-color: #1e1e1e; }
QWidget#DisplayPropertiesPanel, QWidget#PropertiesPanel, QWidget#ContextPanel { background-color: #252526; border-left: 1px solid #3e3e42; }
QPushButton#DisplayToggleBtn { background-color: #333337; border: 1px solid #3e3e42; border-radius: 2px; color: #f1f1f1; font-size: 11px; }
QPushButton#DisplayToggleBtn:checked { background-color: #0078d7; color: white; border-color: #005a9e; font-weight: bold; }
QPushButton#DisplayToggleBtn:hover { border-color: #0078d7; }
@@ -53,8 +54,9 @@ QLabel#TitleLabel { font-weight: bold; margin-left: 2px; }
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #666; }
QToolButton#PaneCloseButton:hover { color: white; background: #e81123; }
/* Viewport & Properties Panels */
QWidget#DisplayPropertiesPanel { background-color: #ffffff; border-left: 1px solid #cccccc; }
/* Global & Panel Backgrounds */
QMainWindow, QWidget#MainPanel { background-color: #f3f3f3; }
QWidget#DisplayPropertiesPanel, QWidget#PropertiesPanel, QWidget#ContextPanel { background-color: #ffffff; border-left: 1px solid #cccccc; }
QPushButton#DisplayToggleBtn { background-color: #ffffff; border: 1px solid #cccccc; border-radius: 2px; color: #333; font-size: 11px; }
QPushButton#DisplayToggleBtn:checked { background-color: #0078d7; color: white; border-color: #005a9e; font-weight: bold; }
QPushButton#DisplayToggleBtn:hover { border-color: #0078d7; }

View File

@@ -10,6 +10,8 @@ ObjectsContext::~ObjectsContext() {}
void ObjectsContext::AddObject(Object* obj) {
if (obj && std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) {
m_objects.push_back(obj);
// Connect child's update to context's update to trigger re-renders
Object::connect(obj, &Object::Updated, this, &Object::Updated);
ULIB_SIGNAL_EMIT(ObjectsContext::ObjectAdded, obj);
this->Updated(); // Signal that the context has been updated
}

View File

@@ -86,6 +86,8 @@ public:
if (*m_value != value) {
*m_value = value;
ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged);
this->Updated();
if (m_owner) m_owner->Updated();
}
}

View File

@@ -102,7 +102,7 @@ public:
Matrix3f GetRotation() const { return this->m_T.rotation(); }
void Translate(const Vector3f v) { this->m_T.pretranslate(v); }
void Translate(const Vector3f v) { this->m_T.translate(v); }
void Scale(const Vector3f v) { this->m_T.scale(v); }

View File

@@ -5,6 +5,7 @@ set(TESTS
vtkQuadMeshTest
vtkVoxImageTest
vtkVoxImageInteractiveTest
vtkContainerBoxTest2
)
set(LIBRARIES

View File

@@ -0,0 +1,45 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
// All rights reserved
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/uLibVtkViewer.h"
#include "Math/ContainerBox.h"
#include "Math/Units.h"
#include "Vtk/vtkContainerBox.h"
#include <iostream>
using namespace uLib;
int main() {
std::cout << "Creating ContainerBox..." << std::endl;
ContainerBox box(Vector3f(1.0, 1.0, 1.0)); // 1x1x1 unit box
box.SetInstanceName("MyTestBox");
std::cout << "Creating VTK representation..." << std::endl;
Vtk::vtkContainerBox v_box(&box);
v_box.SetRepresentation(Vtk::Puppet::Wireframe);
v_box.SetColor(1.0, 0.0, 0.0); // Red
// // 1. Initial Visualization setup (handled by Viewer)
// // 3. Apply scaling of half the size along x
// std::cout << "Applying scaling of 0.5 along X..." << std::endl;
// box.Scale(Vector3f(0.5f, 1.0f, 1.0f));
// box.Updated();
// // 2. Apply rotation along Y axes of 45 deg
// std::cout << "Applying 45 deg rotation along Y..." << std::endl;
// // box.Rotate(45.0_deg, Vector3f::UnitY());
// box.EulerYZYRotate(Vector3f(45.0_deg, 0, 0));
// box.Updated();
std::cout << "Starting viewer (close window to exit)..." << std::endl;
Vtk::Viewer viewer;
viewer.AddPuppet(v_box);
viewer.Start();
return 0;
}

View File

@@ -71,11 +71,31 @@ vtkHandlerWidget::vtkHandlerWidget() {
this->m_TranslationEnabled = true;
this->m_RotationEnabled = true;
this->m_ScalingEnabled = true;
this->m_BaseMatrix = vtkSmartPointer<::vtkMatrix4x4>::New();
this->m_BaseMatrix->Identity();
this->CreateGizmos();
}
vtkHandlerWidget::~vtkHandlerWidget() {}
void vtkHandlerWidget::SetProp3D(::vtkProp3D *prop) {
if (this->Prop3D == prop) {
return;
}
this->Prop3D = prop;
if (this->Prop3D) {
// Initialize m_BaseMatrix from the object's current matrix
if (this->Prop3D->GetUserMatrix()) {
this->m_BaseMatrix->DeepCopy(this->Prop3D->GetUserMatrix());
} else {
this->m_BaseMatrix->Identity();
}
this->m_TransformChain.clear(); // Clear any previous transform chain
this->UpdateGizmoPosition();
}
this->Modified();
}
void vtkHandlerWidget::SetEnabled(int enabling) {
if (enabling) {
if (this->Enabled)
@@ -109,6 +129,8 @@ void vtkHandlerWidget::SetEnabled(int enabling) {
this->Priority);
i->AddObserver(::vtkCommand::RenderEvent, this->EventCallbackCommand,
this->Priority);
i->AddObserver(::vtkCommand::KeyPressEvent, this->EventCallbackCommand,
this->Priority);
this->UpdateGizmoPosition();
@@ -181,6 +203,37 @@ void vtkHandlerWidget::ProcessEvents(::vtkObject *caller, unsigned long event,
case ::vtkCommand::RenderEvent:
self->UpdateGizmoPosition();
break;
case ::vtkCommand::KeyPressEvent:
self->OnKeyPress();
break;
}
}
void vtkHandlerWidget::OnKeyPress() {
std::string key = this->Interactor->GetKeySym();
bool ctrl = (this->Interactor->GetControlKey() != 0);
if (ctrl && key == "z") {
if (!this->m_TransformChain.empty()) {
std::cout << "Undoing last transform action..." << std::endl;
this->m_TransformChain.pop_back();
// Update object from chain
vtkNew<vtkTransform> total;
total->PostMultiply();
total->SetMatrix(this->m_BaseMatrix.GetPointer());
for (auto& t : m_TransformChain) {
total->Concatenate(t);
}
if (this->Prop3D && this->Prop3D->GetUserMatrix()) {
this->Prop3D->GetUserMatrix()->DeepCopy(total->GetMatrix());
this->Prop3D->Modified();
this->UpdateGizmoPosition();
this->InvokeEvent(::vtkCommand::InteractionEvent, nullptr);
this->Interactor->Render();
}
}
}
}
@@ -229,7 +282,17 @@ void vtkHandlerWidget::OnLeftButtonDown() {
vtkNew<vtkMatrix4x4> vmat;
this->Prop3D->SetUserMatrix(vmat);
}
this->m_InitialTransform->SetMatrix(this->Prop3D->GetUserMatrix());
// If the chain is empty, initialize base from current state?
// Actually, if we just started selecting this object, we should have initialized BaseMatrix.
// For now, let's keep m_InitialTransform as the state BEFORE this drag
vtkNew<vtkTransform> current;
current->PostMultiply();
current->SetMatrix(this->m_BaseMatrix.GetPointer());
for (auto& t : m_TransformChain) {
current->Concatenate(t);
}
this->m_InitialTransform->SetMatrix(current->GetMatrix());
}
this->EventCallbackCommand->SetAbortFlag(1);
this->InvokeEvent(::vtkCommand::StartInteractionEvent, nullptr);
@@ -241,6 +304,27 @@ void vtkHandlerWidget::OnLeftButtonUp() {
if (this->Interaction == IDLE)
return;
// Finalize the current interaction into the chain
int X = this->Interactor->GetEventPosition()[0];
int Y = this->Interactor->GetEventPosition()[1];
// We need to re-calculate the final 'op' to store it
// Actually, we could have stored it in OnMouseMove, but let's re-calculate or
// just capture the delta between m_InitialTransform and current UserMatrix.
if (this->Prop3D && this->Prop3D->GetUserMatrix()) {
vtkNew<vtkMatrix4x4> inv;
vtkMatrix4x4::Invert(this->m_InitialTransform->GetMatrix(), inv);
vtkNew<vtkMatrix4x4> final_op_mat;
vtkMatrix4x4::Multiply4x4(this->Prop3D->GetUserMatrix(), inv, final_op_mat);
vtkNew<vtkTransform> final_op;
final_op->SetMatrix(final_op_mat);
this->m_TransformChain.push_back(final_op);
std::cout << "Action finalized. Chain size: " << this->m_TransformChain.size() << std::endl;
}
this->Interaction = IDLE;
this->EventCallbackCommand->SetAbortFlag(1);
this->InvokeEvent(::vtkCommand::EndInteractionEvent, nullptr);
@@ -390,19 +474,46 @@ void vtkHandlerWidget::OnMouseMove() {
}
break;
case SCALE_X:
mag = get_motion_magnitude(gx, gpos);
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);
op->Scale(1.0, std::max(0.1, 1.0 + mag), 1.0);
break;
case SCALE_Z:
mag = get_motion_magnitude(gz, gpos);
op->Scale(1.0, 1.0, std::max(0.1, 1.0 + mag));
{
// 1. Calculate magnitude
if (this->Interaction == SCALE_X) mag = get_motion_magnitude(gx, gpos);
else if (this->Interaction == SCALE_Y) mag = get_motion_magnitude(gy, gpos);
else mag = get_motion_magnitude(gz, gpos);
double s = std::max(0.1, 1.0 + mag);
// 2. Build a strictly orthonormal basis from the gizmo axes
double X[3] = {gx[0], gx[1], gx[2]};
double Y[3] = {gy[0], gy[1], gy[2]};
double Z[3];
vtkMath::Normalize(X);
double dot = vtkMath::Dot(X, Y);
for(int i=0; i<3; ++i) Y[i] -= dot * X[i];
vtkMath::Normalize(Y);
vtkMath::Cross(X, Y, Z); // Z is now orthogonal to X and Y
vtkNew<vtkMatrix4x4> basis;
basis->Identity();
for(int i=0; i<3; ++i) {
basis->SetElement(i, 0, X[i]);
basis->SetElement(i, 1, Y[i]);
basis->SetElement(i, 2, Z[i]);
}
vtkNew<vtkMatrix4x4> basis_inv;
vtkMatrix4x4::Invert(basis, basis_inv);
// 3. Assemble oriented scale: T(gpos) * basis * S * basis_inv * T(-gpos)
// With PostMultiply: Result = T(gpos) * (basis * (S * (basis_inv * (T(-gpos) * Ident))))
op->Concatenate(basis_inv);
if (this->Interaction == SCALE_X) op->Scale(s, 1.0, 1.0);
else if (this->Interaction == SCALE_Y) op->Scale(1.0, s, 1.0);
else op->Scale(1.0, 1.0, s);
op->Concatenate(basis);
}
break;
case ROT_CAM: {
double camPos[3];
@@ -428,16 +539,15 @@ void vtkHandlerWidget::OnMouseMove() {
op->Translate(gpos[0], gpos[1], gpos[2]);
// Apply op on top of the initial object state
vtkNew<vtkTransform> final_t;
final_t->PostMultiply();
final_t->SetMatrix(this->m_InitialTransform->GetMatrix());
final_t->Concatenate(op);
// Total transform = Base * Chain * Interaction
vtkNew<vtkTransform> total;
total->PostMultiply();
total->SetMatrix(this->m_InitialTransform->GetMatrix()); // m_InitialTransform is already Base*Chain
total->Concatenate(op);
vtkMatrix4x4* targetMat = this->Prop3D->GetUserMatrix();
if (targetMat) {
targetMat->DeepCopy(final_t->GetMatrix());
// std::cout << "Updated UserMatrix for interaction " << this->Interaction << std::endl;
targetMat->DeepCopy(total->GetMatrix());
}
this->Prop3D->Modified();

View File

@@ -31,6 +31,8 @@
#include <vtk3DWidget.h>
#include <vtkSmartPointer.h>
#include <vtkMatrix4x4.h>
#include <vector>
// Forward declarations of VTK classes in global namespace
class vtkActor;
@@ -39,6 +41,7 @@ class vtkCellPicker;
class vtkTransform;
class vtkObject;
class vtkPlane;
class vtkProp3D;
class vtkRenderWindowInteractor;
namespace uLib {
@@ -136,6 +139,13 @@ protected:
double StartEventPosition[2];
double m_StartPickPosition[3];
vtkSmartPointer<::vtkTransform> m_InitialTransform;
std::vector<vtkSmartPointer<::vtkTransform>> m_TransformChain;
vtkSmartPointer<::vtkMatrix4x4> m_BaseMatrix;
public:
virtual void SetProp3D(::vtkProp3D *prop) override;
void OnKeyPress();
private:
vtkHandlerWidget(const vtkHandlerWidget &) = delete;