From d64efad081d97f4469886785de7f05ae7ba8276d Mon Sep 17 00:00:00 2001 From: Max Wilfinger Date: Thu, 29 May 2025 20:42:16 +0200 Subject: [PATCH] Advanced options for move to other object in the transform tool. Allows for masking of individual translation or rotation axes. --- src/Gui/TaskTransform.cpp | 49 +++++++- src/Gui/TaskTransform.ui | 215 +++++++++++++++++++++++++++++++- src/Gui/ViewProviderDragger.cpp | 109 +++++++++++++++- src/Gui/ViewProviderDragger.h | 24 +++- 4 files changed, 389 insertions(+), 8 deletions(-) diff --git a/src/Gui/TaskTransform.cpp b/src/Gui/TaskTransform.cpp index 0fd71933d5..d118725373 100644 --- a/src/Gui/TaskTransform.cpp +++ b/src/Gui/TaskTransform.cpp @@ -242,6 +242,21 @@ void TaskTransform::setupGui() &QPushButton::clicked, this, &TaskTransform::onAlignToOtherObject); + connect(ui->moveOptionsButton, + &QPushButton::toggled, + ui->frameMoveOptions, + &QWidget::setVisible); + connect(ui->translateCheckbox, &QCheckBox::toggled, this, [this](bool translateChecked) { + ui->matchXcheckbox->setEnabled(translateChecked); + ui->matchYcheckbox->setEnabled(translateChecked); + ui->matchZcheckbox->setEnabled(translateChecked); + }); + connect(ui->rotateCheckbox, &QCheckBox::toggled, this, [this](bool rotateChecked) { + ui->alignXcheckbox->setEnabled(rotateChecked); + ui->alignYcheckbox->setEnabled(rotateChecked); + ui->alignZcheckbox->setEnabled(rotateChecked); + }); + connect(ui->flipPartButton, &QPushButton::clicked, this, &TaskTransform::onFlip); connect(ui->alignRotationCheckBox, @@ -289,6 +304,7 @@ void TaskTransform::loadPreferences() ui->translationIncrementSpinBox->setValue(lastTranslationIncrement); ui->rotationIncrementSpinBox->setValue(lastRotationIncrement); + ui->moveOptionsButton->setIcon(Gui::BitmapFactory().pixmap("Std_DlgParameter")); } void TaskTransform::savePreferences() @@ -531,8 +547,39 @@ void TaskTransform::onAlignToOtherObject() void TaskTransform::moveObjectToDragger() { + // Check which dragger components should be considered + ViewProviderDragger::DraggerComponents components; + if (ui->matchXcheckbox->isChecked()) { + components |= ViewProviderDragger::DraggerComponent::XPos; + } + if (ui->matchYcheckbox->isChecked()) { + components |= ViewProviderDragger::DraggerComponent::YPos; + } + if (ui->matchZcheckbox->isChecked()) { + components |= ViewProviderDragger::DraggerComponent::ZPos; + } + if (ui->alignXcheckbox->isChecked()) { + components |= ViewProviderDragger::DraggerComponent::XRot; + } + if (ui->alignYcheckbox->isChecked()) { + components |= ViewProviderDragger::DraggerComponent::YRot; + } + if (ui->alignZcheckbox->isChecked()) { + components |= ViewProviderDragger::DraggerComponent::ZRot; + } + if (!ui->translateCheckbox->isChecked()) { + components &= ~ViewProviderDragger::DraggerComponent::XPos; + components &= ~ViewProviderDragger::DraggerComponent::YPos; + components &= ~ViewProviderDragger::DraggerComponent::ZPos; + } + if (!ui->rotateCheckbox->isChecked()) { + components &= ~ViewProviderDragger::DraggerComponent::XRot; + components &= ~ViewProviderDragger::DraggerComponent::YRot; + components &= ~ViewProviderDragger::DraggerComponent::ZRot; + } + vp->updateTransformFromDragger(); - vp->updatePlacementFromDragger(); + vp->updatePlacementFromDragger(components); resetReferenceRotation(); resetReferencePlacement(); diff --git a/src/Gui/TaskTransform.ui b/src/Gui/TaskTransform.ui index 029b5c51de..18d5e30cb3 100644 --- a/src/Gui/TaskTransform.ui +++ b/src/Gui/TaskTransform.ui @@ -194,10 +194,219 @@ - - - Move to other object + + + 6 + + + + + 0 + 0 + + + + Move to other object + + + + + + + + + + + + + true + + + false + + + + + + + + + false + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + 6 + + + + + Translate + + + true + + + + + + + Rotate + + + true + + + + + + + + + 0 + + + 6 + + + + + + 0 + 0 + + + + Match U/X + + + true + + + + + + + + 0 + 0 + + + + Match V/Y + + + true + + + + + + + + 0 + 0 + + + + Match W/Z + + + true + + + + + + + + 0 + 0 + + + + Align U/X + + + true + + + + + + + + 0 + 0 + + + + Align V/Y + + + true + + + + + + + + 0 + 0 + + + + Align W/Z + + + true + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 18 + 20 + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 18 + 20 + + + + + + + diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index 8dcc38712d..be2278f143 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -252,7 +252,7 @@ void ViewProviderDragger::dragMotionCallback(void* data, [[maybe_unused]] SoDrag vp->updateTransformFromDragger(); } -void ViewProviderDragger::updatePlacementFromDragger() +void ViewProviderDragger::updatePlacementFromDragger(DraggerComponents components) { const auto placement = getObject()->getPropertyByName("Placement"); @@ -260,7 +260,112 @@ void ViewProviderDragger::updatePlacementFromDragger() return; } - placement->setValue(getDraggerPlacement() * getTransformOrigin().inverse()); + // Get new target dragger placement + Base::Placement newDraggerPlacement = getDraggerPlacement(); + Base::Vector3d newDraggerPosition = newDraggerPlacement.getPosition(); + Base::Rotation newDraggerRotation = newDraggerPlacement.getRotation(); + + // Get old dragger placement before movement + const Base::Placement oldObjectPlacement = placement->getValue(); + const Base::Placement oldDraggerPlacement = oldObjectPlacement * getTransformOrigin(); + const Base::Vector3d oldDraggerPosition = oldDraggerPlacement.getPosition(); + const Base::Rotation oldDraggerRotation = oldDraggerPlacement.getRotation(); + + // --- Mask translation --- + const Base::Vector3d deltaPositionGlobal = newDraggerPosition - oldDraggerPosition; + const Base::Vector3d deltaPositionLocal = oldDraggerRotation.inverse().multVec(deltaPositionGlobal); + Base::Vector3d maskedDeltaPositionLocal = deltaPositionLocal; + + if (!components.testFlag(DraggerComponent::XPos)) { + maskedDeltaPositionLocal.x = 0.0; + } + if (!components.testFlag(DraggerComponent::YPos)) { + maskedDeltaPositionLocal.y = 0.0; + } + if (!components.testFlag(DraggerComponent::ZPos)) { + maskedDeltaPositionLocal.z = 0.0; + } + + const Base::Vector3d maskedDeltaPositionGlobal = oldDraggerRotation.multVec(maskedDeltaPositionLocal); + Base::Vector3d finalPosition = oldDraggerPosition + maskedDeltaPositionGlobal; + + // --- Mask rotation --- + Base::Vector3d oldX = oldDraggerRotation.multVec(Base::Vector3d::UnitX); + Base::Vector3d oldY = oldDraggerRotation.multVec(Base::Vector3d::UnitY); + Base::Vector3d oldZ = oldDraggerRotation.multVec(Base::Vector3d::UnitZ); + + Base::Vector3d newX = newDraggerRotation.multVec(Base::Vector3d::UnitX); + Base::Vector3d newY = newDraggerRotation.multVec(Base::Vector3d::UnitY); + Base::Vector3d newZ = newDraggerRotation.multVec(Base::Vector3d::UnitZ); + + // Choose which axes to align + Base::Vector3d x = components.testFlag(DraggerComponent::XRot) ? newX : oldX; + Base::Vector3d y = components.testFlag(DraggerComponent::YRot) ? newY : oldY; + Base::Vector3d z = components.testFlag(DraggerComponent::ZRot) ? newZ : oldZ; + + Base::Rotation finalRotation = orthonormalize(x, y, z, components); + + // Create new dragger placement, only if components are masked + Base::Placement finalDraggerPlacement(newDraggerPosition, newDraggerRotation); + if (!components.testFlag(DraggerComponent::All)){ + finalDraggerPlacement.setPosition(finalPosition); + finalDraggerPlacement.setRotation(finalRotation); + } + + placement->setValue((finalDraggerPlacement * getTransformOrigin().inverse())); +} + +Base::Rotation Gui::ViewProviderDragger::orthonormalize(Base::Vector3d x, + Base::Vector3d y, + Base::Vector3d z, + ViewProviderDragger::DraggerComponents components) +{ + // Orthonormalize (Gram–Schmidt process) to find perpendicular unit vector depending on masked axes + if (components.testFlag(Gui::ViewProviderDragger::DraggerComponent::XRot) + && components.testFlag(Gui::ViewProviderDragger::DraggerComponent::YRot)) { + x.Normalize(); + y = y - x * (x * y); + y.Normalize(); + z = x.Cross(y); + z.Normalize(); + } + else if (components.testFlag(Gui::ViewProviderDragger::DraggerComponent::XRot) && components.testFlag(Gui::ViewProviderDragger::DraggerComponent::ZRot)) { + x.Normalize(); + z = z - x * (x * z); + z.Normalize(); + y = z.Cross(x); + y.Normalize(); + } + else if (components.testFlag(Gui::ViewProviderDragger::DraggerComponent::YRot) && components.testFlag(Gui::ViewProviderDragger::DraggerComponent::ZRot)) { + y.Normalize(); + z = z - y * (y * z); + z.Normalize(); + x = y.Cross(z); + x.Normalize(); + } + else if (components.testFlag(Gui::ViewProviderDragger::DraggerComponent::XRot)) { + x.Normalize(); + y = y - x * (x * y); + y.Normalize(); + z = x.Cross(y); + z.Normalize(); + } + else if (components.testFlag(Gui::ViewProviderDragger::DraggerComponent::YRot)) { + y.Normalize(); + x = x - y * (x * y); + x.Normalize(); + z = x.Cross(y); + z.Normalize(); + } + else if (components.testFlag(Gui::ViewProviderDragger::DraggerComponent::ZRot)) { + z.Normalize(); + x = x - z * (x * z); + x.Normalize(); + y = z.Cross(x); + y.Normalize(); + } + + return Base::Rotation::makeRotationByAxes(x, y, z); } void ViewProviderDragger::updateTransformFromDragger() diff --git a/src/Gui/ViewProviderDragger.h b/src/Gui/ViewProviderDragger.h index cea63ed988..ef1d89654c 100644 --- a/src/Gui/ViewProviderDragger.h +++ b/src/Gui/ViewProviderDragger.h @@ -27,6 +27,7 @@ #include "ViewProviderDocumentObject.h" #include #include +#include class SoDragger; class SoTransform; @@ -82,8 +83,20 @@ public: /*! synchronize From FC placement to Coin placement*/ static void updateTransform(const Base::Placement &from, SoTransform *to); - /// updates placement of object based on dragger position - void updatePlacementFromDragger(); + enum class DraggerComponent + { + None = 0, + XPos = 1 << 0, + YPos = 1 << 1, + ZPos = 1 << 2, + XRot = 1 << 3, + YRot = 1 << 4, + ZRot = 1 << 5, + All = XPos | YPos | ZPos | XRot | YRot | ZRot + }; + using DraggerComponents = Base::Flags; + /// updates placement of object based on dragger position and chosen axes components + void updatePlacementFromDragger(DraggerComponents components = DraggerComponent::All); /// updates transform of object based on dragger position, can be used to preview movement void updateTransformFromDragger(); @@ -125,10 +138,17 @@ private: void updateDraggerPosition(); Base::Placement draggerPlacement { }; + + // Rotation by orthonormalizing depending on given axes components + Base::Rotation orthonormalize(Base::Vector3d x, + Base::Vector3d y, + Base::Vector3d z, + ViewProviderDragger::DraggerComponents components = DraggerComponent::All); }; } // namespace Gui +ENABLE_BITMASK_OPERATORS(Gui::ViewProviderDragger::DraggerComponent) #endif // GUI_VIEWPROVIDER_DRAGGER_H