764 lines
25 KiB
C++
764 lines
25 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2015 Thomas Anderson <blobfish[at]gmx.com> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
#include <cassert>
|
|
#include <limits>
|
|
#include <QApplication>
|
|
#endif
|
|
|
|
#include <View3DInventorViewer.h>
|
|
#include <Utilities.h>
|
|
|
|
#include <App/Document.h>
|
|
#include <App/GeoFeature.h>
|
|
#include <App/Services.h>
|
|
#include <Base/Precision.h>
|
|
#include <Base/ServiceProvider.h>
|
|
#include <Base/Tools.h>
|
|
|
|
#include "Document.h" // must be before TaskTransform.h
|
|
#include "Application.h"
|
|
#include "BitmapFactory.h"
|
|
#include "Command.h"
|
|
#include "Inventor/Draggers/SoTransformDragger.h"
|
|
#include "QuantitySpinBox.h"
|
|
#include "ViewProviderDragger.h"
|
|
#include "TaskView/TaskView.h"
|
|
|
|
#include "TaskTransform.h"
|
|
#include "ui_TaskTransform.h"
|
|
|
|
#include <Inventor/nodes/SoPickStyle.h>
|
|
|
|
using namespace Gui;
|
|
|
|
namespace
|
|
{
|
|
|
|
void alignGridLayoutColumns(const std::list<QGridLayout*>& layouts, unsigned column = 0)
|
|
{
|
|
std::vector<int> widths;
|
|
|
|
auto getActualWidth = [&](const QGridLayout* layout) -> int {
|
|
if (auto const item = layout->itemAtPosition(0, column)) {
|
|
return item->geometry().width();
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
for (const auto layout : layouts) {
|
|
widths.push_back(getActualWidth(layout));
|
|
}
|
|
|
|
const auto maxWidth = *std::max_element(widths.begin(), widths.end());
|
|
for (const auto layout : layouts) {
|
|
layout->setColumnMinimumWidth(column, maxWidth);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp,
|
|
Gui::SoTransformDragger* dragger,
|
|
QWidget* parent,
|
|
App::SubObjectPlacementProvider* subObjectPlacemenProvider,
|
|
App::CenterOfMassProvider* centerOfMassProvider)
|
|
: TaskBox(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"), tr("Transform"), false, parent)
|
|
, vp(vp)
|
|
, subObjectPlacementProvider(subObjectPlacemenProvider)
|
|
, centerOfMassProvider(centerOfMassProvider)
|
|
, dragger(dragger)
|
|
, ui(new Ui_TaskTransformDialog)
|
|
{
|
|
blockSelection(true);
|
|
|
|
dragger->addStartCallback(dragStartCallback, this);
|
|
dragger->addMotionCallback(dragMotionCallback, this);
|
|
|
|
vp->resetTransformOrigin();
|
|
|
|
referencePlacement = vp->getObjectPlacement();
|
|
referenceRotation = referencePlacement.getRotation();
|
|
|
|
globalOrigin = vp->getObjectPlacement() * App::GeoFeature::getGlobalPlacement(vp->getObject()).inverse();
|
|
|
|
setupGui();
|
|
}
|
|
|
|
TaskTransform::~TaskTransform()
|
|
{
|
|
Gui::Application::Instance->commandManager()
|
|
.getCommandByName("Std_OrthographicCamera")
|
|
->setEnabled(true);
|
|
|
|
Gui::Application::Instance->commandManager()
|
|
.getCommandByName("Std_PerspectiveCamera")
|
|
->setEnabled(true);
|
|
|
|
savePreferences();
|
|
}
|
|
|
|
void TaskTransform::dragStartCallback([[maybe_unused]] void* data,
|
|
[[maybe_unused]] SoDragger* dragger)
|
|
{
|
|
// This is called when a manipulator is about to manipulating
|
|
if (firstDrag) {
|
|
Gui::Application::Instance->activeDocument()->openCommand(
|
|
QT_TRANSLATE_NOOP("Command", "Transform"));
|
|
firstDrag = false;
|
|
}
|
|
}
|
|
|
|
void TaskTransform::dragMotionCallback(void* data, [[maybe_unused]] SoDragger* dragger)
|
|
{
|
|
auto task = static_cast<TaskTransform*>(data);
|
|
|
|
const auto currentRotation = task->vp->getOriginalDraggerPlacement().getRotation();
|
|
const auto updatedRotation = task->vp->getDraggerPlacement().getRotation();
|
|
|
|
const auto rotationAxisHasChanged = [task](auto first, auto second) {
|
|
double alpha, beta, gamma;
|
|
|
|
(first.inverse() * second).getEulerAngles(task->eulerSequence(), alpha, beta, gamma);
|
|
|
|
auto angles = {alpha, beta, gamma};
|
|
const int changed = std::count_if(angles.begin(), angles.end(), [](double angle) {
|
|
return std::fabs(angle) > tolerance;
|
|
});
|
|
|
|
// if representation of both differs by more than one axis the axis of rotation must be
|
|
// different
|
|
return changed > 1;
|
|
};
|
|
|
|
if (!updatedRotation.isSame(currentRotation, tolerance)) {
|
|
task->resetReferencePlacement();
|
|
|
|
if (rotationAxisHasChanged(task->referenceRotation, updatedRotation)) {
|
|
task->referenceRotation = currentRotation;
|
|
}
|
|
}
|
|
|
|
task->updatePositionAndRotationUi();
|
|
}
|
|
|
|
void TaskTransform::loadPlacementModeItems() const
|
|
{
|
|
ui->placementComboBox->clear();
|
|
|
|
ui->placementComboBox->addItem(tr("Object origin"),
|
|
QVariant::fromValue(PlacementMode::ObjectOrigin));
|
|
|
|
if (centerOfMassProvider->ofDocumentObject(vp->getObject()).has_value()) {
|
|
ui->placementComboBox->addItem(tr("Center of mass / Centroid"),
|
|
QVariant::fromValue(PlacementMode::Centroid));
|
|
}
|
|
|
|
if (subObjectPlacementProvider) {
|
|
ui->placementComboBox->addItem(tr("Custom"), QVariant::fromValue(PlacementMode::Custom));
|
|
}
|
|
}
|
|
|
|
void TaskTransform::loadPositionModeItems() const
|
|
{
|
|
ui->positionModeComboBox->clear();
|
|
ui->positionModeComboBox->addItem(tr("Local"), QVariant::fromValue(PositionMode::Local));
|
|
ui->positionModeComboBox->addItem(tr("Global"), QVariant::fromValue(PositionMode::Global));
|
|
}
|
|
|
|
void TaskTransform::setupGui()
|
|
{
|
|
auto proxy = new QWidget(this);
|
|
ui->setupUi(proxy);
|
|
this->groupLayout()->addWidget(proxy);
|
|
|
|
loadPlacementModeItems();
|
|
loadPositionModeItems();
|
|
|
|
ui->referencePickerWidget->hide();
|
|
ui->alignRotationCheckBox->hide();
|
|
|
|
for (auto positionSpinBox : {ui->translationIncrementSpinBox,
|
|
ui->xPositionSpinBox,
|
|
ui->yPositionSpinBox,
|
|
ui->zPositionSpinBox}) {
|
|
positionSpinBox->setUnit(Base::Unit::Length);
|
|
}
|
|
|
|
for (auto rotationSpinBox : {ui->rotationIncrementSpinBox,
|
|
ui->xRotationSpinBox,
|
|
ui->yRotationSpinBox,
|
|
ui->zRotationSpinBox}) {
|
|
rotationSpinBox->setUnit(Base::Unit::Angle);
|
|
}
|
|
|
|
connect(ui->translationIncrementSpinBox,
|
|
qOverload<double>(&QuantitySpinBox::valueChanged),
|
|
this,
|
|
[this](double) {
|
|
updateIncrements();
|
|
});
|
|
connect(ui->rotationIncrementSpinBox,
|
|
qOverload<double>(&QuantitySpinBox::valueChanged),
|
|
this,
|
|
[this](double) {
|
|
updateIncrements();
|
|
});
|
|
connect(ui->positionModeComboBox,
|
|
qOverload<int>(&QComboBox::currentIndexChanged),
|
|
this,
|
|
&TaskTransform::onCoordinateSystemChange);
|
|
connect(ui->placementComboBox,
|
|
qOverload<int>(&QComboBox::currentIndexChanged),
|
|
this,
|
|
&TaskTransform::onPlacementModeChange);
|
|
connect(ui->pickTransformOriginButton,
|
|
&QPushButton::clicked,
|
|
this,
|
|
&TaskTransform::onPickTransformOrigin);
|
|
connect(ui->alignToOtherObjectButton,
|
|
&QPushButton::clicked,
|
|
this,
|
|
&TaskTransform::onAlignToOtherObject);
|
|
connect(ui->flipPartButton, &QPushButton::clicked, this, &TaskTransform::onFlip);
|
|
|
|
connect(ui->alignRotationCheckBox,
|
|
&QCheckBox::clicked,
|
|
this,
|
|
&TaskTransform::onAlignRotationChanged);
|
|
|
|
for (auto positionSpinBox :
|
|
{ui->xPositionSpinBox, ui->yPositionSpinBox, ui->zPositionSpinBox}) {
|
|
connect(positionSpinBox,
|
|
qOverload<double>(&QuantitySpinBox::valueChanged),
|
|
this,
|
|
[this](double) {
|
|
onPositionChange();
|
|
});
|
|
}
|
|
|
|
for (auto rotationSpinBox :
|
|
{ui->xRotationSpinBox, ui->yRotationSpinBox, ui->zRotationSpinBox}) {
|
|
connect(rotationSpinBox,
|
|
qOverload<double>(&QuantitySpinBox::valueChanged),
|
|
this,
|
|
[this,rotationSpinBox](double) {
|
|
onRotationChange(rotationSpinBox);
|
|
});
|
|
}
|
|
|
|
alignGridLayoutColumns({ui->absolutePositionLayout,
|
|
ui->absoluteRotationLayout,
|
|
ui->transformOriginLayout,
|
|
ui->referencePickerLayout});
|
|
|
|
loadPreferences();
|
|
|
|
updateInputLabels();
|
|
updateDraggerLabels();
|
|
updateIncrements();
|
|
updatePositionAndRotationUi();
|
|
}
|
|
|
|
void TaskTransform::loadPreferences()
|
|
{
|
|
double lastTranslationIncrement = hGrp->GetFloat("LastTranslationIncrement", 1.0);
|
|
double lastRotationIncrement = hGrp->GetFloat("LastRotationIncrement", 5.0);
|
|
|
|
ui->translationIncrementSpinBox->setValue(lastTranslationIncrement);
|
|
ui->rotationIncrementSpinBox->setValue(lastRotationIncrement);
|
|
}
|
|
|
|
void TaskTransform::savePreferences()
|
|
{
|
|
hGrp->SetFloat("LastTranslationIncrement", ui->translationIncrementSpinBox->rawValue());
|
|
hGrp->SetFloat("LastRotationIncrement", ui->rotationIncrementSpinBox->rawValue());
|
|
}
|
|
|
|
void TaskTransform::updatePositionAndRotationUi() const
|
|
{
|
|
const auto referencePlacement = currentCoordinateSystem().origin;
|
|
|
|
const auto xyzPlacement = vp->getDraggerPlacement();
|
|
const auto uvwPlacement = referencePlacement.inverse() * xyzPlacement;
|
|
|
|
auto fixNegativeZero = [](const double value) {
|
|
return std::fabs(value) < Base::Precision::Confusion() ? 0.0 : value;
|
|
};
|
|
|
|
auto setPositionValues = [&](const Base::Vector3d& vec, auto* x, auto* y, auto* z) {
|
|
[[maybe_unused]]
|
|
auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)};
|
|
|
|
x->setValue(fixNegativeZero(vec.x));
|
|
y->setValue(fixNegativeZero(vec.y));
|
|
z->setValue(fixNegativeZero(vec.z));
|
|
};
|
|
|
|
auto setRotationValues = [&](const Base::Rotation& rot, auto* x, auto* y, auto* z) {
|
|
[[maybe_unused]]
|
|
auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)};
|
|
|
|
double alpha, beta, gamma;
|
|
rot.getEulerAngles(eulerSequence(), alpha, beta, gamma);
|
|
|
|
x->setValue(fixNegativeZero(alpha));
|
|
y->setValue(fixNegativeZero(beta));
|
|
z->setValue(fixNegativeZero(gamma));
|
|
};
|
|
|
|
setPositionValues(uvwPlacement.getPosition(),
|
|
ui->xPositionSpinBox,
|
|
ui->yPositionSpinBox,
|
|
ui->zPositionSpinBox);
|
|
|
|
setRotationValues(positionMode == PositionMode::Local
|
|
? referenceRotation.inverse() * xyzPlacement.getRotation()
|
|
: uvwPlacement.getRotation(),
|
|
ui->xRotationSpinBox,
|
|
ui->yRotationSpinBox,
|
|
ui->zRotationSpinBox);
|
|
}
|
|
|
|
void TaskTransform::updateInputLabels() const
|
|
{
|
|
auto [xLabel, yLabel, zLabel] = currentCoordinateSystem().labels;
|
|
|
|
ui->xPositionLabel->setText(QString::fromStdString(xLabel));
|
|
ui->yPositionLabel->setText(QString::fromStdString(yLabel));
|
|
ui->zPositionLabel->setText(QString::fromStdString(zLabel));
|
|
|
|
ui->xRotationLabel->setText(QString::fromStdString(xLabel));
|
|
ui->yRotationLabel->setText(QString::fromStdString(yLabel));
|
|
ui->zRotationLabel->setText(QString::fromStdString(zLabel));
|
|
}
|
|
|
|
void TaskTransform::updateDraggerLabels() const
|
|
{
|
|
auto coordinateSystem =
|
|
isDraggerAlignedToCoordinateSystem() ? globalCoordinateSystem() : localCoordinateSystem();
|
|
|
|
auto [xLabel, yLabel, zLabel] = coordinateSystem.labels;
|
|
|
|
dragger->xAxisLabel.setValue(xLabel.c_str());
|
|
dragger->yAxisLabel.setValue(yLabel.c_str());
|
|
dragger->zAxisLabel.setValue(zLabel.c_str());
|
|
}
|
|
|
|
void TaskTransform::updateIncrements() const
|
|
{
|
|
dragger->translationIncrement.setValue(
|
|
std::max(ui->translationIncrementSpinBox->rawValue(), 0.001));
|
|
dragger->rotationIncrement.setValue(
|
|
Base::toRadians(std::max(ui->rotationIncrementSpinBox->rawValue(), 0.01)));
|
|
}
|
|
|
|
void TaskTransform::setSelectionMode(SelectionMode mode)
|
|
{
|
|
Gui::Selection().clearSelection();
|
|
|
|
SoPickStyle* draggerPickStyle = SO_GET_PART(dragger, "pickStyle", SoPickStyle);
|
|
|
|
ui->pickTransformOriginButton->setText(tr("Pick reference"));
|
|
ui->alignToOtherObjectButton->setText(tr("Move to other object"));
|
|
|
|
switch (mode) {
|
|
case SelectionMode::SelectTransformOrigin:
|
|
draggerPickStyle->style = SoPickStyle::UNPICKABLE;
|
|
draggerPickStyle->setOverride(true);
|
|
blockSelection(false);
|
|
ui->referenceLineEdit->setText(tr("Select face, edge or vertex..."));
|
|
ui->pickTransformOriginButton->setText(tr("Cancel"));
|
|
break;
|
|
|
|
case SelectionMode::SelectAlignTarget:
|
|
draggerPickStyle->style = SoPickStyle::UNPICKABLE;
|
|
draggerPickStyle->setOverride(true);
|
|
ui->alignToOtherObjectButton->setText(tr("Cancel"));
|
|
blockSelection(false);
|
|
break;
|
|
|
|
case SelectionMode::None:
|
|
draggerPickStyle->style = SoPickStyle::SHAPE_ON_TOP;
|
|
draggerPickStyle->setOverride(false);
|
|
blockSelection(true);
|
|
|
|
vp->setTransformOrigin(vp->getTransformOrigin());
|
|
|
|
break;
|
|
}
|
|
|
|
selectionMode = mode;
|
|
|
|
updateSpinBoxesReadOnlyStatus();
|
|
}
|
|
|
|
TaskTransform::SelectionMode TaskTransform::getSelectionMode() const
|
|
{
|
|
return selectionMode;
|
|
}
|
|
|
|
TaskTransform::CoordinateSystem TaskTransform::localCoordinateSystem() const
|
|
{
|
|
auto origin = referencePlacement;
|
|
origin.setRotation(vp->getDraggerPlacement().getRotation());
|
|
|
|
return {{"U", "V", "W"}, origin};
|
|
}
|
|
|
|
TaskTransform::CoordinateSystem TaskTransform::globalCoordinateSystem() const
|
|
{
|
|
return {{"X", "Y", "Z"}, globalOrigin};
|
|
}
|
|
|
|
TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const
|
|
{
|
|
return ui->positionModeComboBox->currentIndex() == 0 ? localCoordinateSystem()
|
|
: globalCoordinateSystem();
|
|
}
|
|
|
|
Base::Rotation::EulerSequence TaskTransform::eulerSequence() const
|
|
{
|
|
return positionMode == PositionMode::Local ? Base::Rotation::Intrinsic_XYZ
|
|
: Base::Rotation::Extrinsic_XYZ;
|
|
}
|
|
|
|
void TaskTransform::onSelectionChanged(const SelectionChanges& msg)
|
|
{
|
|
const auto isSupportedMessage =
|
|
msg.Type == SelectionChanges::AddSelection || msg.Type == SelectionChanges::SetPreselect;
|
|
|
|
if (!isSupportedMessage) {
|
|
return;
|
|
}
|
|
|
|
if (!subObjectPlacementProvider) {
|
|
return;
|
|
}
|
|
|
|
if (!msg.pOriginalMsg) {
|
|
// this should not happen! Original should contain unresolved message.
|
|
return;
|
|
}
|
|
|
|
auto doc = Application::Instance->getDocument(msg.pDocName);
|
|
auto obj = doc->getDocument()->getObject(msg.pObjectName);
|
|
|
|
auto orgDoc = Application::Instance->getDocument(msg.pOriginalMsg->pDocName);
|
|
auto orgObj = orgDoc->getDocument()->getObject(msg.pOriginalMsg->pObjectName);
|
|
|
|
auto globalPlacement = App::GeoFeature::getGlobalPlacement(obj, orgObj, msg.pOriginalMsg->pSubName);
|
|
auto localPlacement = App::GeoFeature::getPlacementFromProp(obj, "Placement");
|
|
auto rootPlacement = App::GeoFeature::getGlobalPlacement(vp->getObject());
|
|
auto attachedPlacement = subObjectPlacementProvider->calculate(msg.Object, localPlacement);
|
|
|
|
auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement * attachedPlacement;
|
|
|
|
auto label = QStringLiteral("%1#%2.%3")
|
|
.arg(QLatin1String(msg.pOriginalMsg->pObjectName),
|
|
QLatin1String(msg.pObjectName),
|
|
QLatin1String(msg.pSubName));
|
|
|
|
switch (selectionMode) {
|
|
case SelectionMode::SelectTransformOrigin: {
|
|
if (msg.Type == SelectionChanges::AddSelection) {
|
|
ui->referenceLineEdit->setText(label);
|
|
customTransformOrigin = selectedObjectPlacement;
|
|
updateTransformOrigin();
|
|
setSelectionMode(SelectionMode::None);
|
|
} else {
|
|
vp->setTransformOrigin(selectedObjectPlacement);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SelectionMode::SelectAlignTarget: {
|
|
vp->setDraggerPlacement(vp->getObjectPlacement() * selectedObjectPlacement);
|
|
|
|
if (msg.Type == SelectionChanges::AddSelection) {
|
|
moveObjectToDragger();
|
|
|
|
setSelectionMode(SelectionMode::None);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// no-op
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TaskTransform::onAlignRotationChanged()
|
|
{
|
|
updateDraggerLabels();
|
|
updateTransformOrigin();
|
|
}
|
|
|
|
void TaskTransform::onAlignToOtherObject()
|
|
{
|
|
if (selectionMode == SelectionMode::SelectAlignTarget) {
|
|
setSelectionMode(SelectionMode::None);
|
|
return;
|
|
}
|
|
|
|
setSelectionMode(SelectionMode::SelectAlignTarget);
|
|
}
|
|
|
|
void TaskTransform::moveObjectToDragger()
|
|
{
|
|
vp->updateTransformFromDragger();
|
|
vp->updatePlacementFromDragger();
|
|
|
|
resetReferenceRotation();
|
|
resetReferencePlacement();
|
|
|
|
updatePositionAndRotationUi();
|
|
}
|
|
|
|
void TaskTransform::onFlip()
|
|
{
|
|
auto placement = vp->getDraggerPlacement();
|
|
|
|
placement.setRotation(placement.getRotation()
|
|
* Base::Rotation::fromNormalVector(Base::Vector3d(0, 0, -1)));
|
|
|
|
vp->setDraggerPlacement(placement);
|
|
|
|
moveObjectToDragger();
|
|
}
|
|
|
|
void TaskTransform::onPickTransformOrigin()
|
|
{
|
|
setSelectionMode(selectionMode == SelectionMode::None ? SelectionMode::SelectTransformOrigin
|
|
: SelectionMode::None);
|
|
}
|
|
|
|
void TaskTransform::onPlacementModeChange([[maybe_unused]] int index)
|
|
{
|
|
placementMode = ui->placementComboBox->currentData().value<PlacementMode>();
|
|
|
|
updateTransformOrigin();
|
|
}
|
|
|
|
void TaskTransform::updateTransformOrigin()
|
|
{
|
|
auto getTransformOrigin = [this](const PlacementMode& mode) -> Base::Placement {
|
|
switch (mode) {
|
|
case PlacementMode::ObjectOrigin:
|
|
return {};
|
|
case PlacementMode::Centroid:
|
|
if (const auto com = centerOfMassProvider->ofDocumentObject(vp->getObject())) {
|
|
return {*com, {}};
|
|
}
|
|
return {};
|
|
case PlacementMode::Custom:
|
|
return customTransformOrigin.value_or(Base::Placement {});
|
|
default:
|
|
return {};
|
|
}
|
|
};
|
|
|
|
ui->referencePickerWidget->setVisible(placementMode == PlacementMode::Custom);
|
|
|
|
if (placementMode == PlacementMode::Custom && !customTransformOrigin.has_value()) {
|
|
setSelectionMode(SelectionMode::SelectTransformOrigin);
|
|
return;
|
|
}
|
|
|
|
auto transformOrigin = getTransformOrigin(placementMode);
|
|
if (isDraggerAlignedToCoordinateSystem()) {
|
|
transformOrigin.setRotation(
|
|
(vp->getObjectPlacement().inverse() * globalCoordinateSystem().origin).getRotation());
|
|
}
|
|
|
|
vp->setTransformOrigin(transformOrigin);
|
|
|
|
resetReferencePlacement();
|
|
resetReferenceRotation();
|
|
|
|
updatePositionAndRotationUi();
|
|
updateDraggerLabels();
|
|
}
|
|
|
|
void TaskTransform::updateSpinBoxesReadOnlyStatus() const
|
|
{
|
|
const bool isReadOnly = selectionMode != SelectionMode::None;
|
|
|
|
const auto controls = {
|
|
ui->xPositionSpinBox,
|
|
ui->yPositionSpinBox,
|
|
ui->zPositionSpinBox,
|
|
ui->xRotationSpinBox,
|
|
ui->yRotationSpinBox,
|
|
ui->zRotationSpinBox,
|
|
};
|
|
|
|
for (const auto& control : controls) {
|
|
control->setReadOnly(isReadOnly);
|
|
}
|
|
}
|
|
|
|
void TaskTransform::resetReferencePlacement()
|
|
{
|
|
referencePlacement = vp->getDraggerPlacement();
|
|
}
|
|
|
|
void TaskTransform::resetReferenceRotation()
|
|
{
|
|
referenceRotation = vp->getDraggerPlacement().getRotation();
|
|
}
|
|
|
|
bool TaskTransform::isDraggerAlignedToCoordinateSystem() const
|
|
{
|
|
return positionMode == PositionMode::Global && ui->alignRotationCheckBox->isChecked();
|
|
}
|
|
|
|
void TaskTransform::onTransformOriginReset()
|
|
{
|
|
vp->resetTransformOrigin();
|
|
}
|
|
|
|
void TaskTransform::onCoordinateSystemChange([[maybe_unused]] int mode)
|
|
{
|
|
positionMode = ui->positionModeComboBox->currentData().value<PositionMode>();
|
|
|
|
ui->alignRotationCheckBox->setVisible(positionMode != PositionMode::Local);
|
|
|
|
updateInputLabels();
|
|
updatePositionAndRotationUi();
|
|
updateTransformOrigin();
|
|
}
|
|
|
|
void TaskTransform::onPositionChange()
|
|
{
|
|
const auto uvwPosition = Base::Vector3d(ui->xPositionSpinBox->rawValue(),
|
|
ui->yPositionSpinBox->rawValue(),
|
|
ui->zPositionSpinBox->rawValue());
|
|
|
|
const auto xyzPosition = currentCoordinateSystem().origin.getPosition()
|
|
+ currentCoordinateSystem().origin.getRotation().multVec(uvwPosition);
|
|
|
|
const auto placement = vp->getDraggerPlacement();
|
|
|
|
vp->setDraggerPlacement({xyzPosition, placement.getRotation()});
|
|
|
|
vp->updateTransformFromDragger();
|
|
vp->updatePlacementFromDragger();
|
|
}
|
|
|
|
void TaskTransform::onRotationChange(QuantitySpinBox* changed)
|
|
{
|
|
if (positionMode == PositionMode::Local) {
|
|
for (auto rotationSpinBox : {ui->xRotationSpinBox,
|
|
ui->yRotationSpinBox,
|
|
ui->zRotationSpinBox}) {
|
|
QSignalBlocker blocker(rotationSpinBox);
|
|
|
|
// if any other spinbox contains non-zero value we need to reset rotation reference first
|
|
if (std::fabs(rotationSpinBox->rawValue()) > tolerance && rotationSpinBox != changed) {
|
|
resetReferenceRotation();
|
|
rotationSpinBox->setValue(0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto uvwRotation = Base::Rotation::fromEulerAngles(eulerSequence(),
|
|
ui->xRotationSpinBox->rawValue(),
|
|
ui->yRotationSpinBox->rawValue(),
|
|
ui->zRotationSpinBox->rawValue());
|
|
|
|
auto referenceRotation = positionMode == PositionMode::Local
|
|
? this->referenceRotation
|
|
: currentCoordinateSystem().origin.getRotation();
|
|
|
|
const auto xyzRotation = referenceRotation * uvwRotation;
|
|
|
|
const auto placement = vp->getDraggerPlacement();
|
|
|
|
vp->setDraggerPlacement({placement.getPosition(), xyzRotation});
|
|
|
|
vp->updateTransformFromDragger();
|
|
vp->updatePlacementFromDragger();
|
|
|
|
resetReferencePlacement();
|
|
}
|
|
|
|
TaskTransformDialog::TaskTransformDialog(ViewProviderDragger* vp, SoTransformDragger* dragger)
|
|
: vp(vp)
|
|
{
|
|
transform = new TaskTransform(vp, dragger);
|
|
Content.push_back(transform);
|
|
}
|
|
|
|
void TaskTransformDialog::open()
|
|
{
|
|
// we can't have user switching camera types while dragger is shown.
|
|
Gui::Application::Instance->commandManager()
|
|
.getCommandByName("Std_OrthographicCamera")
|
|
->setEnabled(false);
|
|
|
|
Gui::Application::Instance->commandManager()
|
|
.getCommandByName("Std_PerspectiveCamera")
|
|
->setEnabled(false);
|
|
|
|
Gui::TaskView::TaskDialog::open();
|
|
|
|
Gui::Application::Instance->activeDocument()->openCommand(
|
|
QT_TRANSLATE_NOOP("Command", "Transform"));
|
|
}
|
|
|
|
bool TaskTransformDialog::accept()
|
|
{
|
|
if (auto documentObject = vp->getObject()) {
|
|
Gui::Document* document =
|
|
Gui::Application::Instance->getDocument(documentObject->getDocument());
|
|
assert(document);
|
|
document->commitCommand();
|
|
document->resetEdit();
|
|
document->getDocument()->recompute();
|
|
}
|
|
|
|
return Gui::TaskView::TaskDialog::accept();
|
|
}
|
|
|
|
bool TaskTransformDialog::reject()
|
|
{
|
|
if (auto documentObject = vp->getObject()) {
|
|
Gui::Document* document =
|
|
Gui::Application::Instance->getDocument(documentObject->getDocument());
|
|
assert(document);
|
|
document->abortCommand();
|
|
document->resetEdit();
|
|
document->getDocument()->recompute();
|
|
}
|
|
|
|
return Gui::TaskView::TaskDialog::reject();
|
|
}
|
|
|
|
#include "moc_TaskTransform.cpp"
|