diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 56ef0a38b2..9bd89679b8 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -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; new Base::ExceptionProducer; new Base::ExceptionProducer; + + Base::registerServiceImplementation(new NullCenterOfMass); } namespace { diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index 8512c365ea..a4ab592db6 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -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 ) diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index d87f405ceb..8d34144c8c 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -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(); - 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("Placement"); + + if (!placementProperty) { + return {}; + } + + auto* group = GeoFeatureGroupExtension::getGroupOfObject(obj); + if (group) { + auto ext = group->getExtensionByType(); + return ext->globalGroupPlacement() * placementProperty->getValue(); + } + + return placementProperty->getValue(); +} diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index cb4dcfbd66..abc36912c5 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -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; diff --git a/src/App/Services.cpp b/src/App/Services.cpp new file mode 100644 index 0000000000..ae70735441 --- /dev/null +++ b/src/App/Services.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 * + * . * + * * + ***************************************************************************/ + +#include "Services.h" + +std::optional +App::NullCenterOfMass::ofDocumentObject([[maybe_unused]] DocumentObject* object) const +{ + return std::nullopt; +} \ No newline at end of file diff --git a/src/App/Services.h b/src/App/Services.h new file mode 100644 index 0000000000..297819e0c6 --- /dev/null +++ b/src/App/Services.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 * + * . * + * * + ***************************************************************************/ + +#ifndef APP_SERVICES_H +#define APP_SERVICES_H + +#include "DocumentObject.h" + +#include +#include + +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 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 ofDocumentObject(DocumentObject* object) const override; +}; + +} + + +#endif // APP_SERVICES_H diff --git a/src/Base/Rotation.cpp b/src/Base/Rotation.cpp index 9947d0cd0b..623b41dd2d 100644 --- a/src/Base/Rotation.cpp +++ b/src/Base/Rotation.cpp @@ -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]; diff --git a/src/Base/Rotation.h b/src/Base/Rotation.h index 03b82d62ad..9f8e6b5c16 100644 --- a/src/Base/Rotation.h +++ b/src/Base/Rotation.h @@ -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; diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 9e71c9a4f7..ede57f85ba 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -402,6 +402,7 @@ SET(Gui_UIC_SRCS SceneInspector.ui InputVector.ui Placement.ui + TaskCSysDragger.ui TextureMapping.ui TaskView/TaskAppearance.ui TaskView/TaskOrientation.ui diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp index ed569c44de..a395b968b1 100644 --- a/src/Gui/SoFCCSysDragger.cpp +++ b/src/Gui/SoFCCSysDragger.cpp @@ -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; diff --git a/src/Gui/SoFCCSysDragger.h b/src/Gui/SoFCCSysDragger.h index bf33ec9a92..b759bcf70a 100644 --- a/src/Gui/SoFCCSysDragger.h +++ b/src/Gui/SoFCCSysDragger.h @@ -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. diff --git a/src/Gui/SoTextLabel.cpp b/src/Gui/SoTextLabel.cpp index feed088b84..1a4a29c0aa 100644 --- a/src/Gui/SoTextLabel.cpp +++ b/src/Gui/SoTextLabel.cpp @@ -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; igetState(); - 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); } diff --git a/src/Gui/SoTextLabel.h b/src/Gui/SoTextLabel.h index b91feb1980..56b9726711 100644 --- a/src/Gui/SoTextLabel.h +++ b/src/Gui/SoTextLabel.h @@ -118,6 +118,8 @@ public: SoSFName name; SoSFInt32 size; SoSFBool frame; + SoSFBool border; + SoSFBool backgroundUseBaseColor; //SoSFImage image; QPixmap iconPixmap; diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index ab23bc533a..a4e38bcce3 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -26,11 +26,20 @@ #include #include #include +#include #endif +#include +#include + #include -#include "Document.h" // must be before TaskCSysDragger.h -#include "TaskCSysDragger.h" +#include +#include +#include +#include +#include + +#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 using namespace Gui; - -static double degreesToRadians(const double °reesIn) +namespace { - return degreesIn * (M_PI / 180.0); -} - -TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDocumentObject* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) : - dragger(draggerIn) +void alignGridLayoutColumns(const std::list& layouts, unsigned column = 0) { - assert(vpObjectIn); - assert(draggerIn); - vpObject = vpObjectIn->getObject(); - dragger->ref(); + std::vector 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::max()); - tSpinBox->setUnit(Base::Unit::Length); - tSpinBox->setMinimumWidth(spinBoxWidth); - gridLayout->addWidget(tSpinBox, 0, 1, Qt::AlignLeft); + if (auto geoFeature = vp->getObject()) { + 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(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onTIncrementSlot); - connect(rSpinBox, qOverload(&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(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(&QuantitySpinBox::valueChanged), + this, + [this](double) { + updateIncrements(); + }); + connect(ui->rotationIncrementSpinBox, + qOverload(&QuantitySpinBox::valueChanged), + this, + [this](double) { + updateIncrements(); + }); + connect(ui->positionModeComboBox, + qOverload(&QComboBox::currentIndexChanged), + this, + &TaskTransform::onCoordinateSystemChange); + connect(ui->placementComboBox, + qOverload(&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(&QuantitySpinBox::valueChanged), + this, + [this](double) { + onPositionChange(); + }); + } + + for (auto rotationSpinBox : + {ui->xRotationSpinBox, ui->yRotationSpinBox, ui->zRotationSpinBox}) { + connect(rotationSpinBox, + qOverload(&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(); + + 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(); + + 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" diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index ba223e5d2c..aac80bf0b9 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -25,39 +25,138 @@ #define TASKCSYSDRAGGER_H #include "TaskView/TaskDialog.h" -#include +#include "TaskView/TaskView.h" +#include "ViewProviderDragger.h" + +#include +#include 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 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::CenterOfMassProvider* centerOfMassProvider = + Base::provideService()); + ~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 dragger; + + Ui_TaskCSysDragger *ui; + + SelectionMode selectionMode { SelectionMode::None }; + PlacementMode placementMode { PlacementMode::ObjectOrigin }; + PositionMode positionMode { PositionMode::Local }; + + std::optional 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 diff --git a/src/Gui/TaskCSysDragger.ui b/src/Gui/TaskCSysDragger.ui new file mode 100644 index 0000000000..a86539f01d --- /dev/null +++ b/src/Gui/TaskCSysDragger.ui @@ -0,0 +1,544 @@ + + + Gui::TaskCSysDragger + + + + 0 + 0 + 450 + 1012 + + + + Placement + + + + + + 0 + + + + + Coordinate System + + + positionModeComboBox + + + + + + + + Local Coordinate System + + + + + Global Coordinate System + + + + + + + + + + 0 + + + + + align dragger rotation with selected coordinate system + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 41 + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 16 + + + + + + + + Translation + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + mm + + + + + + + mm + + + + + + + + 0 + 0 + + + + X + + + xPositionSpinBox + + + + + + + + 0 + 0 + + + + Y + + + yPositionSpinBox + + + + + + + + 0 + 0 + + + + Z + + + zPositionSpinBox + + + + + + + mm + + + + + + + + + + + + + Utilities + + + + + + Move to other object + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + Flip + + + + + + + + + + Dragger + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + + + 0 + 0 + + + + QFrame::Shape::NoFrame + + + <b>Snapping</b> + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Translation + + + translationIncrementSpinBox + + + + + + + 0.000000000000000 + + + 360.000000000000000 + + + 5.000000000000000 + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 10 + + + + + + + + + QLayout::SizeConstraint::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 68 + 0 + + + + Reference + + + referenceLineEdit + + + + + + + pick reference + + + + + + + true + + + + + + + + + + mm + + + 0.000000000000000 + + + 2147483647.000000000000000 + + + 1.000000000000000 + + + + + + + Mode + + + placementComboBox + + + + + + + + 0 + 0 + + + + Rotation + + + rotationIncrementSpinBox + + + + + + + + + + Rotation + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + Y + + + yRotationSpinBox + + + + + + + + 0 + 0 + + + + Z + + + zRotationSpinBox + + + + + + + + + + + + + + 0 + 0 + + + + X + + + xRotationSpinBox + + + + + + + + + + + + + + + Gui::QuantitySpinBox + QAbstractSpinBox +
Gui/QuantitySpinBox.h
+
+
+ + placementComboBox + referenceLineEdit + pickTransformOriginButton + translationIncrementSpinBox + rotationIncrementSpinBox + positionModeComboBox + alignRotationCheckBox + xPositionSpinBox + yPositionSpinBox + zPositionSpinBox + xRotationSpinBox + yRotationSpinBox + zRotationSpinBox + alignToOtherObjectButton + flipPartButton + + + +
diff --git a/src/Gui/Utilities.h b/src/Gui/Utilities.h index b9e8019464..42079bf1e1 100644 --- a/src/Gui/Utilities.h +++ b/src/Gui/Utilities.h @@ -104,6 +104,28 @@ struct vec_traits { private: const vec_type& v; }; + +template <> +inline SbMatrix convertTo(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(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; } diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index 95085b677d..5fbaeb758e 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -56,6 +56,8 @@ #include "ViewProviderLink.h" #include "ViewProviderPy.h" +#include + 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(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(smat); } void ViewProvider::addDisplayMaskMode(SoNode *node, const char* type) diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index 2e20b3f52c..4e60eab9c1 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -23,16 +23,19 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include #endif #include #include +#include +#include #include "Gui/ViewParams.h" #include "Application.h" @@ -44,20 +47,25 @@ #include "TaskCSysDragger.h" #include "View3DInventorViewer.h" #include "ViewProviderDragger.h" +#include "Utilities.h" +#include 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(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(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(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()) { + mat *= geoFeature->Placement.getValue().inverse().toMatrix(); + } + + viewer->getDocument()->setEditingTransform(mat); + viewer->setupEditingRoot(csysDragger, &mat); } } void ViewProviderDragger::unsetEditViewer(Gui::View3DInventorViewer* viewer) -{ - auto selection = static_cast(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(data); - auto sudoThis = static_cast(data); - auto dragger = static_cast(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(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(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(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(); + + if (!geoFeature) { + return; + } + + geoFeature->Placement.setValue(getDraggerPlacement() * getTransformOrigin().inverse()); +} + +void ViewProviderDragger::updateTransformFromDragger() +{ + const auto placement = getDraggerPlacement() * getTransformOrigin().inverse(); + + pcTransform->translation.setValue(Base::convertTo(placement.getPosition())); + pcTransform->rotation.setValue(Base::convertTo(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(placement.getPosition())); + csysDragger->rotation.setValue(Base::convertTo(placement.getRotation())); + + draggerPlacement = placement; + csysDragger->clearIncrementCounts(); +} + +void ViewProviderDragger::updateDraggerPosition() +{ + if (!csysDragger) { + return; + } + + auto placement = getObject()->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(from.getRotation())); + to->translation.setValue(Base::convertTo(from.getPosition())); + to->center.setValue(0.0f, 0.0f, 0.0f); + to->scaleFactor.setValue(1.0f, 1.0f, 1.0f); } diff --git a/src/Gui/ViewProviderDragger.h b/src/Gui/ViewProviderDragger.h index 40502f016a..ce5a6da50c 100644 --- a/src/Gui/ViewProviderDragger.h +++ b/src/Gui/ViewProviderDragger.h @@ -25,16 +25,20 @@ #define GUI_VIEWPROVIDER_DRAGGER_H #include "ViewProviderDocumentObject.h" +#include "SoFCCSysDragger.h" +#include +#include 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 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 diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index a2cad7a67f..6e2fe24d0b 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -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); } } diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index c244d033a5..7668f6d9dd 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "ArcOfCirclePy.h" @@ -187,8 +188,12 @@ #include #include "MeasureClient.h" + #include +#include +#include + 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(new AttacherSubObjectPlacement); + Base::registerServiceImplementation(new PartCenterOfMass); + PyMOD_Return(partModule); } // clang-format on diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 71bedcac65..871d58c2df 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -553,6 +553,8 @@ SET(Part_SRCS PreCompiled.h ProgressIndicator.cpp ProgressIndicator.h + Services.cpp + Services.h TopoShape.cpp TopoShape.h TopoShapeCache.cpp diff --git a/src/Mod/Part/App/Services.cpp b/src/Mod/Part/App/Services.cpp new file mode 100644 index 0000000000..53eb46b947 --- /dev/null +++ b/src/Mod/Part/App/Services.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 * + * . * + * * + ***************************************************************************/ + +#include "Services.h" + +AttacherSubObjectPlacement::AttacherSubObjectPlacement() + : attacher(std::make_unique()) +{ + 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 PartCenterOfMass::ofDocumentObject(App::DocumentObject* object) const +{ + if (const auto feature = dynamic_cast(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 {}; +} \ No newline at end of file diff --git a/src/Mod/Part/App/Services.h b/src/Mod/Part/App/Services.h new file mode 100644 index 0000000000..217ff47e8d --- /dev/null +++ b/src/Mod/Part/App/Services.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 * + * . * + * * + ***************************************************************************/ + +#ifndef PART_SERVICES_H +#define PART_SERVICES_H + +#include +#include + +class AttacherSubObjectPlacement final: public App::SubObjectPlacementProvider +{ +public: + AttacherSubObjectPlacement(); + + Base::Placement calculate(App::SubObjectT object, Base::Placement basePlacement) const override; + +private: + std::unique_ptr attacher; +}; + +class PartCenterOfMass final: public App::CenterOfMassProvider +{ +public: + std::optional ofDocumentObject(App::DocumentObject* object) const override; +}; + +#endif // PART_SERVICES_H