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

@@ -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();