Gui: Rework TaskCSysDragger into new Transform Dialog

This commit refactor ViewProviderDragger and TaskCSysDragger to be more
modern and to support selecting TransformOrigin.
This commit is contained in:
Kacper Donat
2024-10-27 18:39:51 +01:00
parent b470f74398
commit 1df3b5be6c
25 changed files with 1785 additions and 389 deletions

View File

@@ -63,6 +63,7 @@
#include <Base/BaseClass.h>
#include <Base/BoundBoxPy.h>
#include <Base/ConsoleObserver.h>
#include <Base/ServiceProvider.h>
#include <Base/CoordinateSystemPy.h>
#include <Base/Exception.h>
#include <Base/ExceptionFactory.h>
@@ -89,6 +90,7 @@
#include "Application.h"
#include "CleanupProcess.h"
#include "ComplexGeoData.h"
#include "Services.h"
#include "DocumentObjectFileIncluded.h"
#include "DocumentObjectGroup.h"
#include "DocumentObjectGroupPy.h"
@@ -2217,6 +2219,8 @@ void Application::initTypes()
new Base::ExceptionProducer<Base::CADKernelError>;
new Base::ExceptionProducer<Base::RestoreError>;
new Base::ExceptionProducer<Base::PropertyError>;
Base::registerServiceImplementation<CenterOfMassProvider>(new NullCenterOfMass);
}
namespace {

View File

@@ -289,6 +289,7 @@ SET(FreeCADApp_CPP_SRCS
MetadataPyImp.cpp
ElementNamingUtils.cpp
SafeMode.cpp
Services.cpp
StringHasher.cpp
StringHasherPyImp.cpp
StringIDPyImp.cpp
@@ -313,6 +314,7 @@ SET(FreeCADApp_HPP_SRCS
MeasureManager.h
Metadata.h
ElementNamingUtils.h
Services.h
StringHasher.h
)

View File

@@ -61,12 +61,7 @@ void GeoFeature::transformPlacement(const Base::Placement& transform)
Base::Placement GeoFeature::globalPlacement() const
{
auto* group = GeoFeatureGroupExtension::getGroupOfObject(this);
if (group) {
auto ext = group->getExtensionByType<GeoFeatureGroupExtension>();
return ext->globalGroupPlacement() * Placement.getValue();
}
return Placement.getValue();
return GeoFeature::getGlobalPlacement(this);
}
const PropertyComplexGeoData* GeoFeature::getPropertyOfGeometry() const
@@ -351,3 +346,20 @@ Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj,
return getGlobalPlacement(targetObj, prop->getValue(), subs[0]);
}
Base::Placement GeoFeature::getGlobalPlacement(const DocumentObject* obj)
{
auto placementProperty = obj->getPropertyByName<App::PropertyPlacement>("Placement");
if (!placementProperty) {
return {};
}
auto* group = GeoFeatureGroupExtension::getGroupOfObject(obj);
if (group) {
auto ext = group->getExtensionByType<GeoFeatureGroupExtension>();
return ext->globalGroupPlacement() * placementProperty->getValue();
}
return placementProperty->getValue();
}

View File

@@ -195,6 +195,7 @@ public:
static Base::Placement
getGlobalPlacement(DocumentObject* targetObj, DocumentObject* rootObj, const std::string& sub);
static Base::Placement getGlobalPlacement(DocumentObject* targetObj, PropertyXLinkSub* prop);
static Base::Placement getGlobalPlacement(const DocumentObject* obj);
protected:
void onChanged(const Property* prop) override;

30
src/App/Services.cpp Normal file
View File

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "Services.h"
std::optional<Base::Vector3d>
App::NullCenterOfMass::ofDocumentObject([[maybe_unused]] DocumentObject* object) const
{
return std::nullopt;
}

74
src/App/Services.h Normal file
View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef APP_SERVICES_H
#define APP_SERVICES_H
#include "DocumentObject.h"
#include <optional>
#include <Base/Placement.h>
namespace App
{
/**
* This service should provide placement of given sub object (like for example face).
* This feature is not implemented in the core and so it must be provided by module.
*/
class SubObjectPlacementProvider
{
public:
virtual ~SubObjectPlacementProvider() = default;
/**
* Returns placement of sub object relative to the base placement.
*/
virtual Base::Placement calculate(SubObjectT object, Base::Placement basePlacement) const = 0;
};
/**
* This service should provide center of mass calculation;
*/
class CenterOfMassProvider
{
public:
virtual ~CenterOfMassProvider() = default;
virtual std::optional<Base::Vector3d> ofDocumentObject(DocumentObject* object) const = 0;
};
/**
* Default implementation for the center of mass contract
* It always returns empty optional
*/
class NullCenterOfMass final : public CenterOfMassProvider
{
public:
std::optional<Base::Vector3d> ofDocumentObject(DocumentObject* object) const override;
};
}
#endif // APP_SERVICES_H

View File

@@ -87,6 +87,13 @@ Rotation Rotation::fromNormalVector(const Vector3d& normal)
return Rotation(Vector3d(0, 0, 1), normal);
}
Rotation Rotation::fromEulerAngles(EulerSequence theOrder, double alpha, double beta, double gamma)
{
Rotation rotation;
rotation.setEulerAngles(theOrder, alpha, beta, gamma);
return rotation;
}
const double* Rotation::getValue() const
{
return &this->quat[0];

View File

@@ -50,29 +50,6 @@ public:
Rotation(Rotation&& rot) = default;
~Rotation() = default;
/// Utility function to create Rotation based on direction / normal vector
static Rotation fromNormalVector(const Vector3d& normal);
//@}
/** Methods to get or set rotations. */
//@{
const double* getValue() const;
void getValue(double& q0, double& q1, double& q2, double& q3) const;
void setValue(double q0, double q1, double q2, double q3);
/// If not a null quaternion then \a axis will be normalized
void getValue(Vector3d& axis, double& rfAngle) const;
/// Does the same as the method above unless normalizing the axis.
void getRawValue(Vector3d& axis, double& rfAngle) const;
void getValue(Matrix4D& matrix) const;
void setValue(const double q[4]);
void setValue(const Matrix4D& matrix);
void setValue(const Vector3d& axis, double fAngle);
void setValue(const Vector3d& rotateFrom, const Vector3d& rotateTo);
/// Euler angles in yaw,pitch,roll notation
void setYawPitchRoll(double y, double p, double r);
/// Euler angles in yaw,pitch,roll notation
void getYawPitchRoll(double& y, double& p, double& r) const;
enum EulerSequence
{
Invalid,
@@ -115,6 +92,34 @@ public:
EulerSequenceLast,
};
/// Utility function to create Rotation based on direction / normal vector
/// Z base vector is assumed to represent the normal vector
static Rotation fromNormalVector(const Vector3d& normal);
/// Utility function to create Rotation based on euler angles
static Rotation
fromEulerAngles(EulerSequence theOrder, double alpha, double beta, double gamma);
//@}
/** Methods to get or set rotations. */
//@{
const double* getValue() const;
void getValue(double& q0, double& q1, double& q2, double& q3) const;
void setValue(double q0, double q1, double q2, double q3);
/// If not a null quaternion then \a axis will be normalized
void getValue(Vector3d& axis, double& rfAngle) const;
/// Does the same as the method above unless normalizing the axis.
void getRawValue(Vector3d& axis, double& rfAngle) const;
void getValue(Matrix4D& matrix) const;
void setValue(const double q[4]);
void setValue(const Matrix4D& matrix);
void setValue(const Vector3d& axis, double fAngle);
void setValue(const Vector3d& rotateFrom, const Vector3d& rotateTo);
/// Euler angles in yaw,pitch,roll notation
void setYawPitchRoll(double y, double p, double r);
/// Euler angles in yaw,pitch,roll notation
void getYawPitchRoll(double& y, double& p, double& r) const;
static const char* eulerSequenceName(EulerSequence seq);
static EulerSequence eulerSequenceFromName(const char* name);
void getEulerAngles(EulerSequence theOrder, double& alpha, double& beta, double& gamma) const;

View File

@@ -402,6 +402,7 @@ SET(Gui_UIC_SRCS
SceneInspector.ui
InputVector.ui
Placement.ui
TaskCSysDragger.ui
TextureMapping.ui
TaskView/TaskAppearance.ui
TaskView/TaskOrientation.ui

View File

@@ -230,15 +230,17 @@ SoSeparator* TDragger::buildLabelGeometry()
labelSeparator->addChild(labelTranslation);
auto label = new SoFrameLabel();
label->justification = SoFrameLabel::CENTER;
label->string.connectFrom(&this->label);
label->textColor.setValue(1.0, 1.0, 1.0);
label->horAlignment.setValue(SoImage::CENTER);
label->vertAlignment.setValue(SoImage::HALF);
label->horAlignment = SoImage::CENTER;
label->vertAlignment = SoImage::HALF;
label->border = false;
label->backgroundUseBaseColor = true;
labelSeparator->addChild(label);
return labelSeparator;
}
SoBaseColor* TDragger::buildActiveColor()
{
auto colorActive = new SoBaseColor();
@@ -1170,7 +1172,6 @@ SoFCCSysDragger::SoFCCSysDragger()
SO_KIT_ADD_CATALOG_ENTRY(zRotatorDragger, RDragger, TRUE, zRotatorSeparator, "", TRUE);
// Other
SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0));
SO_KIT_ADD_FIELD(translationIncrement, (1.0));
SO_KIT_ADD_FIELD(translationIncrementCountX, (0));
@@ -1186,6 +1187,10 @@ SoFCCSysDragger::SoFCCSysDragger()
SO_KIT_ADD_FIELD(draggerSize, (1.0));
SO_KIT_ADD_FIELD(autoScaleResult, (1.0));
SO_KIT_ADD_FIELD(xAxisLabel, ("X"));
SO_KIT_ADD_FIELD(yAxisLabel, ("Y"));
SO_KIT_ADD_FIELD(zAxisLabel, ("Z"));
SO_KIT_INIT_INSTANCE();
// Colors
@@ -1198,17 +1203,17 @@ SoFCCSysDragger::SoFCCSysDragger()
tDragger = SO_GET_ANY_PART(this, "xTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
tDragger->label.setValue("U");
tDragger->label.connectFrom(&xAxisLabel);
translationIncrementCountX.connectFrom(&tDragger->translationIncrementCount);
tDragger = SO_GET_ANY_PART(this, "yTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
tDragger->label.setValue("V");
tDragger->label.connectFrom(&yAxisLabel);
translationIncrementCountY.connectFrom(&tDragger->translationIncrementCount);
tDragger = SO_GET_ANY_PART(this, "zTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
tDragger->label.setValue("W");
tDragger->label.connectFrom(&zAxisLabel);
translationIncrementCountZ.connectFrom(&tDragger->translationIncrementCount);
// Planar Translator
TPlanarDragger* tPlanarDragger;

View File

@@ -282,6 +282,10 @@ public:
SoSFInt32 rotationIncrementCountY; //!< used from outside for rotation y steps.
SoSFInt32 rotationIncrementCountZ; //!< used from outside for rotation z steps.
SoSFString xAxisLabel; //!< label for X axis
SoSFString yAxisLabel; //!< label for Y axis
SoSFString zAxisLabel; //!< label for Z axis
void clearIncrementCounts(); //!< used to reset after drag update.
/*! @brief Overall scale of dragger node.

View File

@@ -379,6 +379,8 @@ SoFrameLabel::SoFrameLabel()
SO_NODE_ADD_FIELD(name, ("Helvetica"));
SO_NODE_ADD_FIELD(size, (12));
SO_NODE_ADD_FIELD(frame, (true));
SO_NODE_ADD_FIELD(border, (true));
SO_NODE_ADD_FIELD(backgroundUseBaseColor, (false));
//SO_NODE_ADD_FIELD(image, (SbVec2s(0,0), 0, NULL));
}
@@ -397,9 +399,11 @@ void SoFrameLabel::notify(SoNotList * list)
f == &this->justification ||
f == &this->name ||
f == &this->size ||
f == &this->frame) {
f == &this->frame ||
f == &this->border) {
drawImage();
}
inherited::notify(list);
}
@@ -417,11 +421,12 @@ void SoFrameLabel::drawImage()
int w = 0;
int h = fm.height() * num;
const SbColor& b = backgroundColor.getValue();
QColor brush;
brush.setRgbF(b[0],b[1],b[2]);
QColor backgroundBrush;
backgroundBrush.setRgbF(b[0],b[1],b[2]);
const SbColor& t = textColor.getValue();
QColor front;
front.setRgbF(t[0],t[1],t[2]);
const QPen borderPen(QColor(0,0,127), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
QStringList lines;
for (int i=0; i<num; i++) {
@@ -431,7 +436,7 @@ void SoFrameLabel::drawImage()
}
int padding = 5;
bool drawIcon = false;
QImage iconImg;
int widthIcon = 0;
@@ -456,15 +461,15 @@ void SoFrameLabel::drawImage()
painter.setRenderHint(QPainter::Antialiasing);
SbBool drawFrame = frame.getValue();
if (drawFrame) {
painter.setPen(QPen(QColor(0,0,127), 2, Qt::SolidLine, Qt::RoundCap,
Qt::RoundJoin));
painter.setBrush(QBrush(brush, Qt::SolidPattern));
SbBool drawBorder = border.getValue();
if (drawFrame || drawBorder) {
painter.setPen(drawBorder ? borderPen : QPen(Qt::transparent));
painter.setBrush(QBrush(drawFrame ? backgroundBrush : Qt::transparent));
QRectF rectangle(0.0, 0.0, widthTotal, heightTotal);
painter.drawRoundedRect(rectangle, 5, 5);
}
if (drawIcon) {
painter.drawImage(QPoint(padding, paddingIconV), iconImg);
}
@@ -493,10 +498,15 @@ void SoFrameLabel::drawImage()
*/
void SoFrameLabel::GLRender(SoGLRenderAction *action)
{
SoState * state = action->getState();
const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0);
this->backgroundColor.setValue(diffuse);
if (backgroundUseBaseColor.getValue()) {
SoState* state = action->getState();
const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0);
if (diffuse != this->backgroundColor.getValue()) {
this->backgroundColor.setValue(diffuse);
}
}
inherited::GLRender(action);
}

View File

@@ -118,6 +118,8 @@ public:
SoSFName name;
SoSFInt32 size;
SoSFBool frame;
SoSFBool border;
SoSFBool backgroundUseBaseColor;
//SoSFImage image;
QPixmap iconPixmap;

View File

@@ -26,11 +26,20 @@
#include <limits>
#include <QApplication>
#include <QGridLayout>
#include <QPushButton>
#endif
#include <View3DInventorViewer.h>
#include <Utilities.h>
#include <App/Document.h>
#include "Document.h" // must be before TaskCSysDragger.h
#include "TaskCSysDragger.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 TaskCSysDragger.h
#include "Application.h"
#include "BitmapFactory.h"
#include "Command.h"
@@ -39,139 +48,582 @@
#include "ViewProviderDragger.h"
#include "TaskView/TaskView.h"
#include "TaskCSysDragger.h"
#include "ui_TaskCSysDragger.h"
#include <Inventor/nodes/SoPickStyle.h>
using namespace Gui;
static double degreesToRadians(const double &degreesIn)
namespace
{
return degreesIn * (M_PI / 180.0);
}
TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDocumentObject* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) :
dragger(draggerIn)
void alignGridLayoutColumns(const std::list<QGridLayout*>& layouts, unsigned column = 0)
{
assert(vpObjectIn);
assert(draggerIn);
vpObject = vpObjectIn->getObject();
dragger->ref();
std::vector<int> widths;
setupGui();
}
auto getActualWidth = [&](const QGridLayout* layout) -> int {
if (auto const item = layout->itemAtPosition(0, column)) {
return item->geometry().width();
}
TaskCSysDragger::~TaskCSysDragger()
{
dragger->unref();
Gui::Application::Instance->commandManager().getCommandByName("Std_OrthographicCamera")->setEnabled(true);
Gui::Application::Instance->commandManager().getCommandByName("Std_PerspectiveCamera")->setEnabled(true);
}
return 0;
};
void TaskCSysDragger::dragStartCallback(void *, SoDragger *)
{
// This is called when a manipulator is about to manipulating
if(firstDrag)
{
Gui::Application::Instance->activeDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Transform"));
firstDrag=false;
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);
}
}
void TaskCSysDragger::setupGui()
} // namespace
TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp,
Gui::SoFCCSysDragger* 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_TaskCSysDragger)
{
auto incrementsBox = new Gui::TaskView::TaskBox(
Gui::BitmapFactory().pixmap("Std_TransformManip"),
tr("Transform"), true, nullptr);
blockSelection(true);
auto gridLayout = new QGridLayout();
gridLayout->setColumnStretch(1, 1);
dragger->addStartCallback(dragStartCallback, this);
dragger->addMotionCallback(dragMotionCallback, this);
auto tLabel = new QLabel(tr("Translation Increment:"), incrementsBox);
gridLayout->addWidget(tLabel, 0, 0, Qt::AlignRight);
vp->resetTransformOrigin();
QFontMetrics metrics(QApplication::font());
int spinBoxWidth = metrics.averageCharWidth() * 20;
tSpinBox = new QuantitySpinBox(incrementsBox);
tSpinBox->setMinimum(0.0);
tSpinBox->setMaximum(std::numeric_limits<double>::max());
tSpinBox->setUnit(Base::Unit::Length);
tSpinBox->setMinimumWidth(spinBoxWidth);
gridLayout->addWidget(tSpinBox, 0, 1, Qt::AlignLeft);
if (auto geoFeature = vp->getObject<App::GeoFeature>()) {
originalPlacement = geoFeature->Placement.getValue();
}
auto rLabel = new QLabel(tr("Rotation Increment:"), incrementsBox);
gridLayout->addWidget(rLabel, 1, 0, Qt::AlignRight);
rSpinBox = new QuantitySpinBox(incrementsBox);
rSpinBox->setMinimum(0.0);
rSpinBox->setMaximum(180.0);
rSpinBox->setUnit(Base::Unit::Angle);
rSpinBox->setMinimumWidth(spinBoxWidth);
gridLayout->addWidget(rSpinBox, 1, 1, Qt::AlignLeft);
incrementsBox->groupLayout()->addLayout(gridLayout);
Content.push_back(incrementsBox);
connect(tSpinBox, qOverload<double>(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onTIncrementSlot);
connect(rSpinBox, qOverload<double>(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onRIncrementSlot);
setupGui();
}
void TaskCSysDragger::onTIncrementSlot(double freshValue)
TaskTransform::~TaskTransform()
{
dragger->translationIncrement.setValue(freshValue);
Gui::Application::Instance->commandManager()
.getCommandByName("Std_OrthographicCamera")
->setEnabled(true);
Gui::Application::Instance->commandManager()
.getCommandByName("Std_PerspectiveCamera")
->setEnabled(true);
}
void TaskCSysDragger::onRIncrementSlot(double freshValue)
void TaskTransform::dragStartCallback(void*, SoDragger*)
{
dragger->rotationIncrement.setValue(degreesToRadians(freshValue));
// 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, SoDragger* dragger)
{
auto task = static_cast<TaskTransform*>(data);
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("Absolute"), QVariant::fromValue(PositionMode::Absolute));
}
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](double) {
onRotationChange();
});
}
alignGridLayoutColumns({ui->absolutePositionLayout,
ui->absoluteRotationLayout,
ui->transformOriginLayout,
ui->referencePickerLayout});
updateInputLabels();
updateDraggerLabels();
updateIncrements();
updatePositionAndRotationUi();
}
void TaskTransform::updatePositionAndRotationUi() const
{
const auto xyzPlacement = vp->getDraggerPlacement();
const auto uvwPlacement = currentCoordinateSystem().origin.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(Base::Rotation::Intrinsic_XYZ, alpha, beta, gamma);
x->setValue(fixNegativeZero(alpha));
y->setValue(fixNegativeZero(beta));
z->setValue(fixNegativeZero(gamma));
};
auto setValues = [&](const Base::Placement& placement,
auto* px,
auto* py,
auto* pz,
auto* rx,
auto* ry,
auto* rz) {
setPositionValues(placement.getPosition(), px, py, pz);
setRotationValues(placement.getRotation(), rx, ry, rz);
};
setValues(uvwPlacement,
ui->xPositionSpinBox,
ui->yPositionSpinBox,
ui->zPositionSpinBox,
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() ? absoluteCoordinateSystem() : 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();
ui->pickTransformOriginButton->setText(tr("Pick reference"));
ui->alignToOtherObjectButton->setText(tr("Move to other object"));
switch (mode) {
case SelectionMode::SelectTransformOrigin:
blockSelection(false);
ui->referenceLineEdit->setText(tr("Select face, edge or vertex..."));
ui->pickTransformOriginButton->setText(tr("Cancel"));
break;
case SelectionMode::SelectAlignTarget:
ui->alignToOtherObjectButton->setText(tr("Cancel"));
blockSelection(false);
break;
case SelectionMode::None:
blockSelection(true);
break;
}
selectionMode = mode;
}
TaskTransform::SelectionMode TaskTransform::getSelectionMode() const
{
return selectionMode;
}
TaskTransform::CoordinateSystem TaskTransform::localCoordinateSystem() const
{
auto origin = originalPlacement * vp->getTransformOrigin();
origin.setRotation(vp->getDraggerPlacement().getRotation());
return {{"U", "V", "W"}, origin};
}
TaskTransform::CoordinateSystem TaskTransform::absoluteCoordinateSystem() const
{
return {
{"X", "Y", "Z"},
Base::Placement {},
};
}
TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const
{
return ui->positionModeComboBox->currentIndex() == 0 ? localCoordinateSystem()
: absoluteCoordinateSystem();
}
void TaskTransform::onSelectionChanged(const SelectionChanges& msg)
{
if (msg.Type != SelectionChanges::AddSelection) {
return;
}
if (!subObjectPlacementProvider) {
return;
}
auto doc = Application::Instance->getDocument(msg.pDocName);
auto obj = doc->getDocument()->getObject(msg.pObjectName);
auto globalPlacement = App::GeoFeature::getGlobalPlacement(obj);
auto localPlacement = App::GeoFeature::getPlacementFromProp(obj, "Placement");
auto rootPlacement = App::GeoFeature::getGlobalPlacement(vp->getObject());
auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement
* subObjectPlacementProvider->calculate(msg.Object, localPlacement);
switch (selectionMode) {
case SelectionMode::SelectTransformOrigin: {auto label = msg.pOriginalMsg
? QStringLiteral("%1#%2.%3")
.arg(QLatin1String(msg.pOriginalMsg->pObjectName),
QLatin1String(msg.pObjectName),
QLatin1String(msg.pSubName))
: QStringLiteral("%1.%2").arg(QLatin1String(msg.pObjectName), QLatin1String(msg.pSubName));
ui->referenceLineEdit->setText(label);
customTransformOrigin = selectedObjectPlacement;
updateTransformOrigin();
break;
}
case SelectionMode::SelectAlignTarget: {
vp->setDraggerPlacement(vp->getObjectPlacement() * selectedObjectPlacement);
vp->updateTransformFromDragger();
vp->updatePlacementFromDragger();
break;
}
default:
// no-op
break;
}
setSelectionMode(SelectionMode::None);
}
void TaskTransform::onAlignRotationChanged()
{
updateDraggerLabels();
updateTransformOrigin();
}
void TaskTransform::onAlignToOtherObject()
{
setSelectionMode(SelectionMode::SelectAlignTarget);
}
void TaskTransform::onFlip()
{
auto placement = vp->getDraggerPlacement();
placement.setRotation(placement.getRotation()
* Base::Rotation::fromNormalVector(Base::Vector3d(0, 0, -1)));
vp->setDraggerPlacement(placement);
vp->updateTransformFromDragger();
vp->updatePlacementFromDragger();
}
void TaskTransform::onPickTransformOrigin()
{
setSelectionMode(selectionMode == SelectionMode::None ? SelectionMode::SelectTransformOrigin
: SelectionMode::None);
}
void TaskTransform::onPlacementModeChange(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 Base::Placement {*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()) {
onPickTransformOrigin();
}
auto transformOrigin = getTransformOrigin(placementMode);
if (isDraggerAlignedToCoordinateSystem()) {
transformOrigin.setRotation(
(vp->getObjectPlacement().inverse() * absoluteCoordinateSystem().origin).getRotation());
}
vp->setTransformOrigin(transformOrigin);
updatePositionAndRotationUi();
updateDraggerLabels();
}
bool TaskTransform::isDraggerAlignedToCoordinateSystem() const
{
return positionMode == PositionMode::Absolute && 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()
{
const auto uvwRotation = Base::Rotation::fromEulerAngles(Base::Rotation::Intrinsic_XYZ,
ui->xRotationSpinBox->rawValue(),
ui->yRotationSpinBox->rawValue(),
ui->zRotationSpinBox->rawValue());
const auto xyzRotation = currentCoordinateSystem().origin.getRotation() * uvwRotation;
const auto placement = vp->getDraggerPlacement();
vp->setDraggerPlacement({placement.getPosition(), xyzRotation});
vp->updateTransformFromDragger();
vp->updatePlacementFromDragger();
}
TaskCSysDragger::TaskCSysDragger(ViewProviderDragger* vp, SoFCCSysDragger* dragger)
: vp(vp)
{
transform = new TaskTransform(vp, dragger);
Content.push_back(transform);
}
void TaskCSysDragger::open()
{
dragger->addStartCallback(dragStartCallback, this);
//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);
// dragger->translationIncrement.setValue(lastTranslationIncrement);
// dragger->rotationIncrement.setValue(lastRotationIncrement);
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger");
double lastTranslationIncrement = hGrp->GetFloat("LastTranslationIncrement", 1.0);
double lastRotationIncrement = hGrp->GetFloat("LastRotationIncrement", 15.0);
tSpinBox->setValue(lastTranslationIncrement);
rSpinBox->setValue(lastRotationIncrement);
// we can't have user switching camera types while dragger is shown.
Gui::Application::Instance->commandManager()
.getCommandByName("Std_OrthographicCamera")
->setEnabled(false);
Gui::TaskView::TaskDialog::open();
Gui::Application::Instance->commandManager()
.getCommandByName("Std_PerspectiveCamera")
->setEnabled(false);
Gui::TaskView::TaskDialog::open();
Gui::Application::Instance->activeDocument()->openCommand(
QT_TRANSLATE_NOOP("Command", "Transform"));
}
bool TaskCSysDragger::accept()
{
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger");
hGrp->SetFloat("LastTranslationIncrement", tSpinBox->rawValue());
hGrp->SetFloat("LastRotationIncrement", rSpinBox->rawValue());
if (auto documentObject = vp->getObject()) {
Gui::Document* document =
Gui::Application::Instance->getDocument(documentObject->getDocument());
assert(document);
document->commitCommand();
document->resetEdit();
document->getDocument()->recompute();
}
App::DocumentObject* dObject = vpObject.getObject();
if (dObject) {
Gui::Document* document = Gui::Application::Instance->getDocument(dObject->getDocument());
assert(document);
firstDrag = true;
document->commitCommand();
document->resetEdit();
document->getDocument()->recompute();
}
return Gui::TaskView::TaskDialog::accept();
return Gui::TaskView::TaskDialog::accept();
}
bool TaskCSysDragger::reject()
{
App::DocumentObject* dObject = vpObject.getObject();
if (dObject) {
Gui::Document* document = Gui::Application::Instance->getDocument(dObject->getDocument());
assert(document);
firstDrag = true;
document->abortCommand();
document->resetEdit();
document->getDocument()->recompute();
}
return Gui::TaskView::TaskDialog::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_TaskCSysDragger.cpp"

View File

@@ -25,39 +25,138 @@
#define TASKCSYSDRAGGER_H
#include "TaskView/TaskDialog.h"
#include <App/DocumentObserver.h>
#include "TaskView/TaskView.h"
#include "ViewProviderDragger.h"
#include <Base/ServiceProvider.h>
#include <App/Services.h>
class SoDragger;
namespace Gui
namespace Attacher
{
class QuantitySpinBox;
class SoFCCSysDragger;
class ViewProviderDragger;
class TaskCSysDragger : public Gui::TaskView::TaskDialog
{
Q_OBJECT
public:
TaskCSysDragger(ViewProviderDocumentObject *vpObjectIn, SoFCCSysDragger *draggerIn);
~TaskCSysDragger() override;
QDialogButtonBox::StandardButtons getStandardButtons() const override
{ return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;}
void open() override;
bool accept() override;
bool reject() override;
private Q_SLOTS:
void onTIncrementSlot(double freshValue);
void onRIncrementSlot(double freshValue);
private:
static inline bool firstDrag = true;
static void dragStartCallback(void * data, SoDragger * d);
void setupGui();
App::DocumentObjectT vpObject;
SoFCCSysDragger *dragger;
QuantitySpinBox *tSpinBox;
QuantitySpinBox *rSpinBox;
};
class AttachEngine;
}
#endif // TASKCSYSDRAGGER_H
namespace Gui
{
class QuantitySpinBox;
class SoFCCSysDragger;
class ViewProviderDragger;
class Ui_TaskCSysDragger;
class TaskTransform : public Gui::TaskView::TaskBox, public Gui::SelectionObserver
{
Q_OBJECT
public:
enum class SelectionMode { None, SelectTransformOrigin, SelectAlignTarget };
enum class PlacementMode { ObjectOrigin, Centroid, Custom };
enum class PositionMode { Local, Absolute };
struct CoordinateSystem
{
std::array<std::string, 3> labels;
Base::Placement origin;
};
Q_ENUM(SelectionMode)
Q_ENUM(PlacementMode)
Q_ENUM(PositionMode)
TaskTransform(Gui::ViewProviderDragger* vp,
Gui::SoFCCSysDragger* dragger,
QWidget* parent = nullptr,
App::SubObjectPlacementProvider* subObjectPlacementProvider =
Base::provideService<App::SubObjectPlacementProvider>(),
App::CenterOfMassProvider* centerOfMassProvider =
Base::provideService<App::CenterOfMassProvider>());
~TaskTransform() override;
void setSelectionMode(SelectionMode mode);
SelectionMode getSelectionMode() const;
CoordinateSystem absoluteCoordinateSystem() const;
CoordinateSystem localCoordinateSystem() const;
CoordinateSystem currentCoordinateSystem() const;
private:
void onSelectionChanged(const SelectionChanges& msg) override;
private Q_SLOTS:
void onPlacementModeChange(int index);
void onPickTransformOrigin();
void onTransformOriginReset();
void onAlignRotationChanged();
void onAlignToOtherObject();
void onFlip();
void onCoordinateSystemChange(int mode);
void onPositionChange();
void onRotationChange();
private:
static inline bool firstDrag = true;
static void dragStartCallback(void* data, SoDragger* d);
static void dragMotionCallback(void* data, SoDragger* d);
void setupGui();
void loadPreferences();
void savePreferences();
void loadPositionModeItems() const;
void loadPlacementModeItems() const;
void updatePositionAndRotationUi() const;
void updateDraggerLabels() const;
void updateInputLabels() const;
void updateIncrements() const;
void updateTransformOrigin();
bool isDraggerAlignedToCoordinateSystem() const;
ViewProviderDragger* vp;
const App::SubObjectPlacementProvider* subObjectPlacementProvider;
const App::CenterOfMassProvider *centerOfMassProvider;
CoinPtr<SoFCCSysDragger> dragger;
Ui_TaskCSysDragger *ui;
SelectionMode selectionMode { SelectionMode::None };
PlacementMode placementMode { PlacementMode::ObjectOrigin };
PositionMode positionMode { PositionMode::Local };
std::optional<Base::Placement> customTransformOrigin {};
Base::Placement originalPlacement {};
};
class TaskCSysDragger: public Gui::TaskView::TaskDialog
{
Q_OBJECT
public:
TaskCSysDragger(ViewProviderDragger* vp, SoFCCSysDragger* dragger);
~TaskCSysDragger() override = default;
QDialogButtonBox::StandardButtons getStandardButtons() const override
{
return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
}
void open() override;
bool accept() override;
bool reject() override;
private:
ViewProviderDragger* vp;
TaskTransform* transform;
};
} // namespace Gui
#endif // TASKCSYSDRAGGER_H

544
src/Gui/TaskCSysDragger.ui Normal file
View File

@@ -0,0 +1,544 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::TaskCSysDragger</class>
<widget class="QWidget" name="Gui::TaskCSysDragger">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>1012</height>
</rect>
</property>
<property name="windowTitle">
<string>Placement</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Coordinate System</string>
</property>
<property name="buddy">
<cstring>positionModeComboBox</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="positionModeComboBox">
<item>
<property name="text">
<string>Local Coordinate System</string>
</property>
</item>
<item>
<property name="text">
<string>Global Coordinate System</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="alignRotationCheckBox">
<property name="text">
<string>align dragger rotation with selected coordinate system</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="0">
<spacer name="vSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>41</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="positionGroupBox">
<property name="title">
<string>Translation</string>
</property>
<layout class="QGridLayout" columnminimumwidth="100">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QWidget" name="positionStackWidget" native="true">
<layout class="QGridLayout" name="absolutePositionLayout" columnstretch="1,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="4" column="1">
<widget class="Gui::QuantitySpinBox" name="zPositionSpinBox">
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::QuantitySpinBox" name="xPositionSpinBox">
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="xPositionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>X</string>
</property>
<property name="buddy">
<cstring>xPositionSpinBox</cstring>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="yPositionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Y</string>
</property>
<property name="buddy">
<cstring>yPositionSpinBox</cstring>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="zPositionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Z</string>
</property>
<property name="buddy">
<cstring>zPositionSpinBox</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::QuantitySpinBox" name="yPositionSpinBox">
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Utilities</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="alignToOtherObjectButton">
<property name="text">
<string>Move to other object</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="flipPartButton">
<property name="text">
<string>Flip</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="transformOriginGroupBox">
<property name="title">
<string>Dragger</string>
</property>
<layout class="QGridLayout" name="transformOriginLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="placementComboBox"/>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="snappingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="text">
<string>&lt;b&gt;Snapping&lt;/b&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="translationIncrementLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Translation</string>
</property>
<property name="buddy">
<cstring>translationIncrementSpinBox</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="Gui::QuantitySpinBox" name="rotationIncrementSpinBox">
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<widget class="QWidget" name="referencePickerWidget" native="true">
<layout class="QGridLayout" name="referencePickerLayout" columnstretch="0,1">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="referenceLabel">
<property name="minimumSize">
<size>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Reference</string>
</property>
<property name="buddy">
<cstring>referenceLineEdit</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="pickTransformOriginButton">
<property name="text">
<string>pick reference</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="referenceLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="1">
<widget class="Gui::QuantitySpinBox" name="translationIncrementSpinBox">
<property name="unit" stdset="0">
<string>mm</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2147483647.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mode</string>
</property>
<property name="buddy">
<cstring>placementComboBox</cstring>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="rotationIncrementLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Rotation</string>
</property>
<property name="buddy">
<cstring>rotationIncrementSpinBox</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<widget class="QGroupBox" name="rotationGroupBox">
<property name="title">
<string>Rotation</string>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="0" colspan="2">
<widget class="QWidget" name="rotationStackWidget" native="true">
<layout class="QGridLayout" name="absoluteRotationLayout" columnstretch="1,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="2" column="1">
<widget class="Gui::QuantitySpinBox" name="zRotationSpinBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="yRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Y</string>
</property>
<property name="buddy">
<cstring>yRotationSpinBox</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="zRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Z</string>
</property>
<property name="buddy">
<cstring>zRotationSpinBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::QuantitySpinBox" name="yRotationSpinBox"/>
</item>
<item row="0" column="1">
<widget class="Gui::QuantitySpinBox" name="xRotationSpinBox"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="xRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>X</string>
</property>
<property name="buddy">
<cstring>xRotationSpinBox</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QAbstractSpinBox</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>placementComboBox</tabstop>
<tabstop>referenceLineEdit</tabstop>
<tabstop>pickTransformOriginButton</tabstop>
<tabstop>translationIncrementSpinBox</tabstop>
<tabstop>rotationIncrementSpinBox</tabstop>
<tabstop>positionModeComboBox</tabstop>
<tabstop>alignRotationCheckBox</tabstop>
<tabstop>xPositionSpinBox</tabstop>
<tabstop>yPositionSpinBox</tabstop>
<tabstop>zPositionSpinBox</tabstop>
<tabstop>xRotationSpinBox</tabstop>
<tabstop>yRotationSpinBox</tabstop>
<tabstop>zRotationSpinBox</tabstop>
<tabstop>alignToOtherObjectButton</tabstop>
<tabstop>flipPartButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -104,6 +104,28 @@ struct vec_traits<App::Color> {
private:
const vec_type& v;
};
template <>
inline SbMatrix convertTo<SbMatrix, Base::Matrix4D>(const Base::Matrix4D& vec2)
{
double dMtrx[16];
vec2.getGLMatrix(dMtrx);
return SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], // clazy:exclude=rule-of-two-soft
dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7],
dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11],
dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]);
}
template <>
inline Base::Matrix4D convertTo<Base::Matrix4D, SbMatrix>(const SbMatrix& vec2)
{
Base::Matrix4D mat;
for(int i=0;i<4;++i) {
for(int j=0;j<4;++j)
mat[i][j] = vec2[j][i];
}
return mat;
}
}
namespace App{ class DocumentObject; }

View File

@@ -56,6 +56,8 @@
#include "ViewProviderLink.h"
#include "ViewProviderPy.h"
#include <Utilities.h>
FC_LOG_LEVEL_INIT("ViewProvider", true, true)
@@ -345,13 +347,7 @@ QIcon ViewProvider::mergeColorfulOverlayIcons (const QIcon & orig) const
void ViewProvider::setTransformation(const Base::Matrix4D &rcMatrix)
{
double dMtrx[16];
rcMatrix.getGLMatrix(dMtrx);
pcTransform->setMatrix(SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3],
dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7],
dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11],
dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]));
pcTransform->setMatrix(convert(rcMatrix));
}
void ViewProvider::setTransformation(const SbMatrix &rcMatrix)
@@ -361,24 +357,12 @@ void ViewProvider::setTransformation(const SbMatrix &rcMatrix)
SbMatrix ViewProvider::convert(const Base::Matrix4D &rcMatrix)
{
//NOLINTBEGIN
double dMtrx[16];
rcMatrix.getGLMatrix(dMtrx);
return SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], // clazy:exclude=rule-of-two-soft
dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7],
dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11],
dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]);
//NOLINTEND
return Base::convertTo<SbMatrix>(rcMatrix);
}
Base::Matrix4D ViewProvider::convert(const SbMatrix &smat)
{
Base::Matrix4D mat;
for(int i=0;i<4;++i) {
for(int j=0;j<4;++j)
mat[i][j] = smat[j][i];
}
return mat;
return Base::convertTo<Base::Matrix4D>(smat);
}
void ViewProvider::addDisplayMaskMode(SoNode *node, const char* type)

View File

@@ -23,16 +23,19 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <string>
# include <QAction>
# include <QMenu>
# include <Inventor/draggers/SoDragger.h>
# include <Inventor/nodes/SoPickStyle.h>
# include <Inventor/nodes/SoTransform.h>
#include <memory>
#include <string>
#include <QAction>
#include <QMenu>
#include <Inventor/draggers/SoDragger.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Inventor/nodes/SoTransform.h>
#endif
#include <App/GeoFeature.h>
#include <Base/Placement.h>
#include <Base/Vector3D.h>
#include <Base/Converter.h>
#include "Gui/ViewParams.h"
#include "Application.h"
@@ -44,20 +47,25 @@
#include "TaskCSysDragger.h"
#include "View3DInventorViewer.h"
#include "ViewProviderDragger.h"
#include "Utilities.h"
#include <Base/Tools.h>
using namespace Gui;
PROPERTY_SOURCE(Gui::ViewProviderDragger, Gui::ViewProviderDocumentObject)
ViewProviderDragger::ViewProviderDragger() = default;
ViewProviderDragger::ViewProviderDragger()
{
ADD_PROPERTY_TYPE(TransformOrigin, ({}), nullptr, App::Prop_Hidden, nullptr);
};
ViewProviderDragger::~ViewProviderDragger() = default;
void ViewProviderDragger::updateData(const App::Property* prop)
{
if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId()) &&
strcmp(prop->getName(), "Placement") == 0) {
if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId())
&& strcmp(prop->getName(), "Placement") == 0) {
// Note: If R is the rotation, c the rotation center and t the translation
// vector then Inventor applies the following transformation: R*(x-c)+c+t
// In FreeCAD a placement only has a rotation and a translation part but
@@ -73,6 +81,30 @@ void ViewProviderDragger::updateData(const App::Property* prop)
ViewProviderDocumentObject::updateData(prop);
}
void ViewProviderDragger::setTransformOrigin(const Base::Placement& placement)
{
TransformOrigin.setValue(placement);
}
void ViewProviderDragger::resetTransformOrigin()
{
setTransformOrigin({});
}
void ViewProviderDragger::onChanged(const App::Property* property)
{
if (property == &TransformOrigin) {
updateDraggerPosition();
}
ViewProviderDocumentObject::onChanged(property);
}
TaskView::TaskDialog* ViewProviderDragger::getTransformDialog()
{
return new TaskCSysDragger(this, csysDragger);
}
bool ViewProviderDragger::doubleClicked()
{
Gui::Application::Instance->activeDocument()->setEdit(this, (int)ViewProvider::Default);
@@ -81,249 +113,216 @@ bool ViewProviderDragger::doubleClicked()
void ViewProviderDragger::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
{
QIcon iconObject = mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"));
QIcon iconObject =
mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"));
QAction* act = menu->addAction(iconObject, QObject::tr("Transform"), receiver, member);
act->setData(QVariant((int)ViewProvider::Transform));
ViewProviderDocumentObject::setupContextMenu(menu, receiver, member);
}
ViewProvider *ViewProviderDragger::startEditing(int mode) {
ViewProvider* ViewProviderDragger::startEditing(int mode)
{
_linkDragger = nullptr;
auto ret = ViewProviderDocumentObject::startEditing(mode);
if(!ret)
if (!ret) {
return ret;
return _linkDragger?_linkDragger:ret;
}
return _linkDragger ? _linkDragger : ret;
}
bool ViewProviderDragger::checkLink() {
bool ViewProviderDragger::checkLink()
{
// Trying to detect if the editing request is forwarded by a link object,
// usually by doubleClicked(). If so, we route the request back. There shall
// be no risk of infinite recursion, as ViewProviderLink handles
// ViewProvider::Transform request by itself.
ViewProviderDocumentObject *vpParent = nullptr;
ViewProviderDocumentObject* vpParent = nullptr;
std::string subname;
auto doc = Application::Instance->editDocument();
if(!doc)
if (!doc) {
return false;
doc->getInEdit(&vpParent,&subname);
if(!vpParent)
}
doc->getInEdit(&vpParent, &subname);
if (!vpParent) {
return false;
}
auto sobj = vpParent->getObject()->getSubObject(subname.c_str());
if(!sobj || sobj==getObject() || sobj->getLinkedObject(true)!=getObject())
if (!sobj || sobj == getObject() || sobj->getLinkedObject(true) != getObject()) {
return false;
}
auto vp = Application::Instance->getViewProvider(sobj);
if(!vp)
if (!vp) {
return false;
}
_linkDragger = vp->startEditing(ViewProvider::Transform);
if(_linkDragger)
return true;
return false;
return _linkDragger != nullptr;
}
bool ViewProviderDragger::setEdit(int ModNum)
{
Q_UNUSED(ModNum);
Q_UNUSED(ModNum);
if (checkLink()) {
return true;
}
App::DocumentObject *genericObject = this->getObject();
if (genericObject->isDerivedFrom(App::GeoFeature::getClassTypeId())) {
auto geoFeature = static_cast<App::GeoFeature *>(genericObject);
const Base::Placement &placement = geoFeature->Placement.getValue();
auto tempTransform = new SoTransform();
tempTransform->ref();
updateTransform(placement, tempTransform);
if (checkLink()) {
return true;
}
assert(!csysDragger);
csysDragger = new SoFCCSysDragger();
csysDragger->setAxisColors(
Gui::ViewParams::instance()->getAxisXColor(),
Gui::ViewParams::instance()->getAxisYColor(),
Gui::ViewParams::instance()->getAxisZColor()
);
csysDragger->setAxisColors(Gui::ViewParams::instance()->getAxisXColor(),
Gui::ViewParams::instance()->getAxisYColor(),
Gui::ViewParams::instance()->getAxisZColor());
csysDragger->draggerSize.setValue(ViewParams::instance()->getDraggerScale());
csysDragger->translation.setValue(tempTransform->translation.getValue());
csysDragger->rotation.setValue(tempTransform->rotation.getValue());
tempTransform->unref();
pcTransform->translation.connectFrom(&csysDragger->translation);
pcTransform->rotation.connectFrom(&csysDragger->rotation);
csysDragger->addStartCallback(dragStartCallback, this);
csysDragger->addFinishCallback(dragFinishCallback, this);
csysDragger->addMotionCallback(dragMotionCallback, this);
// dragger node is added to viewer's editing root in setEditViewer
// pcRoot->insertChild(csysDragger, 0);
csysDragger->ref();
Gui::Control().showDialog(getTransformDialog());
auto task = new TaskCSysDragger(this, csysDragger);
Gui::Control().showDialog(task);
}
updateDraggerPosition();
return true;
return true;
}
void ViewProviderDragger::unsetEdit(int ModNum)
{
Q_UNUSED(ModNum);
Q_UNUSED(ModNum);
if(csysDragger)
{
pcTransform->translation.disconnect(&csysDragger->translation);
pcTransform->rotation.disconnect(&csysDragger->rotation);
csysDragger.reset();
// dragger node is added to viewer's editing root in setEditViewer
// pcRoot->removeChild(csysDragger); //should delete csysDragger
csysDragger->unref();
csysDragger = nullptr;
}
Gui::Control().closeDialog();
Gui::Control().closeDialog();
}
void ViewProviderDragger::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum)
{
Q_UNUSED(ModNum);
if (csysDragger && viewer)
{
auto rootPickStyle = new SoPickStyle();
rootPickStyle->style = SoPickStyle::UNPICKABLE;
auto selection = static_cast<SoGroup*>(viewer->getSceneGraph());
selection->insertChild(rootPickStyle, 0);
viewer->setSelectionEnabled(false);
csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
if (csysDragger && viewer) {
csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
auto mat = viewer->getDocument()->getEditingTransform();
viewer->getDocument()->setEditingTransform(mat);
auto feat = dynamic_cast<App::GeoFeature *>(getObject());
if(feat) {
auto matInverse = feat->Placement.getValue().toMatrix();
matInverse.inverse();
mat *= matInverse;
}
viewer->setupEditingRoot(csysDragger,&mat);
auto mat = viewer->getDocument()->getEditingTransform();
if (auto geoFeature = getObject<App::GeoFeature>()) {
mat *= geoFeature->Placement.getValue().inverse().toMatrix();
}
viewer->getDocument()->setEditingTransform(mat);
viewer->setupEditingRoot(csysDragger, &mat);
}
}
void ViewProviderDragger::unsetEditViewer(Gui::View3DInventorViewer* viewer)
{
auto selection = static_cast<SoGroup*>(viewer->getSceneGraph());
SoNode *child = selection->getChild(0);
if (child && child->isOfType(SoPickStyle::getClassTypeId())) {
selection->removeChild(child);
viewer->setSelectionEnabled(true);
}
}
{}
void ViewProviderDragger::dragFinishCallback(void *data, SoDragger *d)
void ViewProviderDragger::dragStartCallback(void* data, [[maybe_unused]] SoDragger* d)
{
// This is called when a manipulator has done manipulating
auto vp = static_cast<ViewProviderDragger*>(data);
auto sudoThis = static_cast<ViewProviderDragger *>(data);
auto dragger = static_cast<SoFCCSysDragger *>(d);
updatePlacementFromDragger(sudoThis, dragger);
//Gui::Application::Instance->activeDocument()->commitCommand();
vp->draggerPlacement = vp->getDraggerPlacement();
vp->csysDragger->clearIncrementCounts();
}
void ViewProviderDragger::updatePlacementFromDragger(ViewProviderDragger* sudoThis, SoFCCSysDragger* draggerIn)
void ViewProviderDragger::dragFinishCallback(void* data, SoDragger* d)
{
App::DocumentObject *genericObject = sudoThis->getObject();
if (!genericObject->isDerivedFrom(App::GeoFeature::getClassTypeId()))
return;
auto geoFeature = static_cast<App::GeoFeature *>(genericObject);
Base::Placement originalPlacement = geoFeature->Placement.getValue();
double pMatrix[16];
originalPlacement.toMatrix().getMatrix(pMatrix);
Base::Placement freshPlacement = originalPlacement;
// This is called when a manipulator has done manipulating
auto vp = static_cast<ViewProviderDragger*>(data);
//local cache for brevity.
double translationIncrement = draggerIn->translationIncrement.getValue();
double rotationIncrement = draggerIn->rotationIncrement.getValue();
int tCountX = draggerIn->translationIncrementCountX.getValue();
int tCountY = draggerIn->translationIncrementCountY.getValue();
int tCountZ = draggerIn->translationIncrementCountZ.getValue();
int rCountX = draggerIn->rotationIncrementCountX.getValue();
int rCountY = draggerIn->rotationIncrementCountY.getValue();
int rCountZ = draggerIn->rotationIncrementCountZ.getValue();
vp->draggerPlacement = vp->getDraggerPlacement();
vp->csysDragger->clearIncrementCounts();
//just as a little sanity check make sure only 1 or 2 fields has changed.
int numberOfFieldChanged = 0;
if (tCountX) numberOfFieldChanged++;
if (tCountY) numberOfFieldChanged++;
if (tCountZ) numberOfFieldChanged++;
if (rCountX) numberOfFieldChanged++;
if (rCountY) numberOfFieldChanged++;
if (rCountZ) numberOfFieldChanged++;
if (numberOfFieldChanged == 0)
return;
assert(numberOfFieldChanged == 1 || numberOfFieldChanged == 2);
vp->updatePlacementFromDragger();
}
//helper lambdas.
auto getVectorX = [&pMatrix]() {return Base::Vector3d(pMatrix[0], pMatrix[4], pMatrix[8]);};
auto getVectorY = [&pMatrix]() {return Base::Vector3d(pMatrix[1], pMatrix[5], pMatrix[9]);};
auto getVectorZ = [&pMatrix]() {return Base::Vector3d(pMatrix[2], pMatrix[6], pMatrix[10]);};
void ViewProviderDragger::dragMotionCallback(void* data, SoDragger* d)
{
auto vp = static_cast<ViewProviderDragger*>(data);
if (tCountX)
{
Base::Vector3d movementVector(getVectorX());
movementVector *= (tCountX * translationIncrement);
freshPlacement.move(movementVector);
geoFeature->Placement.setValue(freshPlacement);
}
if (tCountY)
{
Base::Vector3d movementVector(getVectorY());
movementVector *= (tCountY * translationIncrement);
freshPlacement.move(movementVector);
geoFeature->Placement.setValue(freshPlacement);
}
if (tCountZ)
{
Base::Vector3d movementVector(getVectorZ());
movementVector *= (tCountZ * translationIncrement);
freshPlacement.move(movementVector);
geoFeature->Placement.setValue(freshPlacement);
}
if (rCountX)
{
Base::Vector3d rotationVector(getVectorX());
Base::Rotation rotation(rotationVector, rCountX * rotationIncrement);
freshPlacement.setRotation(rotation * freshPlacement.getRotation());
geoFeature->Placement.setValue(freshPlacement);
}
if (rCountY)
{
Base::Vector3d rotationVector(getVectorY());
Base::Rotation rotation(rotationVector, rCountY * rotationIncrement);
freshPlacement.setRotation(rotation * freshPlacement.getRotation());
geoFeature->Placement.setValue(freshPlacement);
}
if (rCountZ)
{
Base::Vector3d rotationVector(getVectorZ());
Base::Rotation rotation(rotationVector, rCountZ * rotationIncrement);
freshPlacement.setRotation(rotation * freshPlacement.getRotation());
geoFeature->Placement.setValue(freshPlacement);
}
vp->updateTransformFromDragger();
}
draggerIn->clearIncrementCounts();
void ViewProviderDragger::updatePlacementFromDragger()
{
const auto geoFeature = getObject<App::GeoFeature>();
if (!geoFeature) {
return;
}
geoFeature->Placement.setValue(getDraggerPlacement() * getTransformOrigin().inverse());
}
void ViewProviderDragger::updateTransformFromDragger()
{
const auto placement = getDraggerPlacement() * getTransformOrigin().inverse();
pcTransform->translation.setValue(Base::convertTo<SbVec3f>(placement.getPosition()));
pcTransform->rotation.setValue(Base::convertTo<SbRotation>(placement.getRotation()));
}
Base::Placement ViewProviderDragger::getDraggerPlacement() const
{
const double translationStep = csysDragger->translationIncrement.getValue();
const int xSteps = csysDragger->translationIncrementCountX.getValue();
const int ySteps = csysDragger->translationIncrementCountY.getValue();
const int zSteps = csysDragger->translationIncrementCountZ.getValue();
const auto rotation = draggerPlacement.getRotation();
const auto xBase = rotation.multVec(Base::Vector3d(1, 0, 0));
const auto yBase = rotation.multVec(Base::Vector3d(0, 1, 0));
const auto zBase = rotation.multVec(Base::Vector3d(0, 0, 1));
const auto positionIncrement =
xBase * (translationStep * xSteps) +
yBase * (translationStep * ySteps) +
zBase * (translationStep * zSteps);
const double rotationStep = csysDragger->rotationIncrement.getValue();
const int xRotationSteps = csysDragger->rotationIncrementCountX.getValue();
const int yRotationSteps = csysDragger->rotationIncrementCountY.getValue();
const int zRotationSteps = csysDragger->rotationIncrementCountZ.getValue();
auto newRotation = rotation;
newRotation = newRotation * Base::Rotation(Base::Vector3d(1, 0, 0), xRotationSteps * rotationStep);
newRotation = newRotation * Base::Rotation(Base::Vector3d(0, 1, 0), yRotationSteps * rotationStep);
newRotation = newRotation * Base::Rotation(Base::Vector3d(0, 0, 1), zRotationSteps * rotationStep);
return Base::Placement(
draggerPlacement.getPosition() + positionIncrement,
newRotation
);
}
void ViewProviderDragger::setDraggerPlacement(const Base::Placement& placement)
{
csysDragger->translation.setValue(Base::convertTo<SbVec3f>(placement.getPosition()));
csysDragger->rotation.setValue(Base::convertTo<SbRotation>(placement.getRotation()));
draggerPlacement = placement;
csysDragger->clearIncrementCounts();
}
void ViewProviderDragger::updateDraggerPosition()
{
if (!csysDragger) {
return;
}
auto placement = getObject<App::GeoFeature>()->Placement.getValue() * getTransformOrigin();
setDraggerPlacement(placement);
}
void ViewProviderDragger::updateTransform(const Base::Placement& from, SoTransform* to)
{
auto q0 = (float)from.getRotation().getValue()[0];
auto q1 = (float)from.getRotation().getValue()[1];
auto q2 = (float)from.getRotation().getValue()[2];
auto q3 = (float)from.getRotation().getValue()[3];
auto px = (float)from.getPosition().x;
auto py = (float)from.getPosition().y;
auto pz = (float)from.getPosition().z;
to->rotation.setValue(q0,q1,q2,q3);
to->translation.setValue(px,py,pz);
to->center.setValue(0.0f,0.0f,0.0f);
to->scaleFactor.setValue(1.0f,1.0f,1.0f);
to->rotation.setValue(Base::convertTo<SbRotation>(from.getRotation()));
to->translation.setValue(Base::convertTo<SbVec3f>(from.getPosition()));
to->center.setValue(0.0f, 0.0f, 0.0f);
to->scaleFactor.setValue(1.0f, 1.0f, 1.0f);
}

View File

@@ -25,16 +25,20 @@
#define GUI_VIEWPROVIDER_DRAGGER_H
#include "ViewProviderDocumentObject.h"
#include "SoFCCSysDragger.h"
#include <Base/Placement.h>
#include <App/PropertyGeo.h>
class SoDragger;
class SoTransform;
namespace Base { class Placement;}
namespace Gui {
namespace TaskView {
class TaskDialog;
}
class View3DInventorViewer;
class SoFCCSysDragger;
/**
* The base class for all view providers modifying the placement
@@ -52,6 +56,13 @@ public:
/// destructor.
~ViewProviderDragger() override;
App::PropertyPlacement TransformOrigin;
Base::Placement getTransformOrigin() const { return TransformOrigin.getValue(); }
void setTransformOrigin(const Base::Placement& placement);
void resetTransformOrigin();
public:
/** @name Edit methods */
//@{
bool doubleClicked() override;
@@ -63,21 +74,40 @@ public:
/*! synchronize From FC placement to Coin placement*/
static void updateTransform(const Base::Placement &from, SoTransform *to);
void updatePlacementFromDragger();
void updateTransformFromDragger();
Base::Placement getDraggerPlacement() const;
void setDraggerPlacement(const Base::Placement& placement);
protected:
bool setEdit(int ModNum) override;
void unsetEdit(int ModNum) override;
void setEditViewer(View3DInventorViewer*, int ModNum) override;
void unsetEditViewer(View3DInventorViewer*) override;
//@}
SoFCCSysDragger *csysDragger = nullptr;
void onChanged(const App::Property* prop) override;
/**
* Returns a newly create dialog for the part to be placed in the task view
* Must be reimplemented in subclasses.
*/
virtual TaskView::TaskDialog* getTransformDialog();
CoinPtr<SoFCCSysDragger> csysDragger = nullptr;
private:
static void dragFinishCallback(void * data, SoDragger * d);
static void updatePlacementFromDragger(ViewProviderDragger *sudoThis, SoFCCSysDragger *draggerIn);
static void dragStartCallback(void *data, SoDragger *d);
static void dragFinishCallback(void *data, SoDragger *d);
static void dragMotionCallback(void *data, SoDragger *d);
void updateDraggerPosition();
bool checkLink();
ViewProvider *_linkDragger = nullptr;
Base::Placement draggerPlacement { };
};
} // namespace Gui

View File

@@ -2893,7 +2893,7 @@ void ViewProviderLink::setEditViewer(Gui::View3DInventorViewer* viewer, int ModN
dragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
viewer->setupEditingRoot(pcDragger,&dragCtx->preTransform);
auto task = new TaskCSysDragger(this, dragger);
auto task = new TaskCSysDragger(nullptr, dragger);
Gui::Control().showDialog(task);
}
}

View File

@@ -34,6 +34,7 @@
#include <Base/ExceptionFactory.h>
#include <Base/Interpreter.h>
#include <Base/Parameter.h>
#include <Base/ServiceProvider.h>
#include <Base/PrecisionPy.h>
#include "ArcOfCirclePy.h"
@@ -187,8 +188,12 @@
#include <OCAF/ImportExportSettings.h>
#include "MeasureClient.h"
#include <FuzzyHelper.h>
#include <App/Services.h>
#include <Services.h>
namespace Part {
extern PyObject* initModule();
}
@@ -572,7 +577,10 @@ PyMOD_INIT_FUNC(Part)
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean");
Part::FuzzyHelper::setBooleanFuzzy(hGrp->GetFloat("BooleanFuzzy",10.0));
Base::registerServiceImplementation<App::SubObjectPlacementProvider>(new AttacherSubObjectPlacement);
Base::registerServiceImplementation<App::CenterOfMassProvider>(new PartCenterOfMass);
PyMOD_Return(partModule);
}
// clang-format on

View File

@@ -553,6 +553,8 @@ SET(Part_SRCS
PreCompiled.h
ProgressIndicator.cpp
ProgressIndicator.h
Services.cpp
Services.h
TopoShape.cpp
TopoShape.h
TopoShapeCache.cpp

View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "Services.h"
AttacherSubObjectPlacement::AttacherSubObjectPlacement()
: attacher(std::make_unique<Attacher::AttachEngine3D>())
{
attacher->setUp({}, Attacher::mmMidpoint);
}
Base::Placement AttacherSubObjectPlacement::calculate(App::SubObjectT object,
Base::Placement basePlacement) const
{
attacher->setReferences({object});
return basePlacement.inverse() * attacher->calculateAttachedPlacement(basePlacement);
}
std::optional<Base::Vector3d> PartCenterOfMass::ofDocumentObject(App::DocumentObject* object) const
{
if (const auto feature = dynamic_cast<Part::Feature*>(object)) {
const auto shape = feature->Shape.getShape();
if (const auto cog = shape.centerOfGravity()) {
const Base::Placement comPlacement { *cog, Base::Rotation { } };
return (feature->Placement.getValue().inverse() * comPlacement).getPosition();
}
}
return {};
}

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef PART_SERVICES_H
#define PART_SERVICES_H
#include <Attacher.h>
#include <App/Services.h>
class AttacherSubObjectPlacement final: public App::SubObjectPlacementProvider
{
public:
AttacherSubObjectPlacement();
Base::Placement calculate(App::SubObjectT object, Base::Placement basePlacement) const override;
private:
std::unique_ptr<Attacher::AttachEngine3D> attacher;
};
class PartCenterOfMass final: public App::CenterOfMassProvider
{
public:
std::optional<Base::Vector3d> ofDocumentObject(App::DocumentObject* object) const override;
};
#endif // PART_SERVICES_H