Advanced options for move to other object in the transform tool.
Allows for masking of individual translation or rotation axes.
This commit is contained in:
committed by
Kacper Donat
parent
2933eaf819
commit
d64efad081
@@ -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();
|
||||
|
||||
@@ -194,10 +194,219 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="alignToOtherObjectButton">
|
||||
<property name="text">
|
||||
<string>Move to other object</string>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="alignToOtherObjectButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move to other object</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="moveOptionsButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="preferences-other"/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frameMoveOptions">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="translateCheckbox">
|
||||
<property name="text">
|
||||
<string>Translate</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="rotateCheckbox">
|
||||
<property name="text">
|
||||
<string>Rotate</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="matchXcheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Match U/X</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="matchYcheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Match V/Y</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="matchZcheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Match W/Z</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="alignXcheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Align U/X</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="alignYcheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Align V/Y</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QCheckBox" name="alignZcheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Align W/Z</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -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<App::PropertyPlacement>("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()
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include <Base/Placement.h>
|
||||
#include <App/PropertyGeo.h>
|
||||
#include <Base/Bitmask.h>
|
||||
|
||||
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<DraggerComponent>;
|
||||
/// 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user