diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 1eb6ff50f8..967bf5bcb2 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/ComplexGeoData.cpp b/src/App/ComplexGeoData.cpp
index e8d50030a4..b8a1c67c6a 100644
--- a/src/App/ComplexGeoData.cpp
+++ b/src/App/ComplexGeoData.cpp
@@ -184,6 +184,17 @@ bool ComplexGeoData::getCenterOfGravity(Base::Vector3d& unused) const
return false;
}
+std::optional ComplexGeoData::centerOfGravity() const
+{
+ Base::Vector3d centerOfGravity;
+
+ if (getCenterOfGravity(centerOfGravity)) {
+ return centerOfGravity;
+ }
+
+ return {};
+}
+
const std::string& ComplexGeoData::elementMapPrefix()
{
static std::string prefix(ELEMENT_MAP_PREFIX);
diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h
index 995b797944..8c0f8dc59b 100644
--- a/src/App/ComplexGeoData.h
+++ b/src/App/ComplexGeoData.h
@@ -28,6 +28,8 @@
#define APP_COMPLEX_GEO_DATA_H
#include
+#include
+
#include
#include
#include
@@ -200,6 +202,7 @@ public:
* The default implementation only returns false.
*/
virtual bool getCenterOfGravity(Base::Vector3d& center) const;
+ virtual std::optional centerOfGravity() const;
//@}
static const std::string& elementMapPrefix();
diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp
index d87f405ceb..344aea1a58 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
@@ -316,6 +311,11 @@ Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj,
return plc;
}
+ if (rootObj->isLink()) {
+ // Update doc in case its an external link.
+ doc = rootObj->getLinkedObject()->getDocument();
+ }
+
for (auto& name : names) {
App::DocumentObject* obj = doc->getObject(name.c_str());
if (!obj) {
@@ -351,3 +351,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 e1c871e0f7..623b41dd2d 100644
--- a/src/Base/Rotation.cpp
+++ b/src/Base/Rotation.cpp
@@ -81,6 +81,19 @@ Rotation::Rotation(const Vector3d& rotateFrom, const Vector3d& rotateTo)
this->setValue(rotateFrom, rotateTo);
}
+Rotation Rotation::fromNormalVector(const Vector3d& normal)
+{
+ // We rotate Z axis to be aligned with the supplied normal vector
+ 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 f663c81510..9f8e6b5c16 100644
--- a/src/Base/Rotation.h
+++ b/src/Base/Rotation.h
@@ -49,26 +49,6 @@ public:
Rotation(const Rotation& rot) = default;
Rotation(Rotation&& rot) = default;
~Rotation() = default;
- //@}
-
- /** 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
{
@@ -112,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/Base/ServiceProvider.cpp b/src/Base/ServiceProvider.cpp
index 1f99a53100..7401c3e9cf 100644
--- a/src/Base/ServiceProvider.cpp
+++ b/src/Base/ServiceProvider.cpp
@@ -26,8 +26,7 @@
#include "ServiceProvider.h"
-Base::ServiceProvider& Base::ServiceProvider::get()
+namespace Base
{
- static Base::ServiceProvider instance;
- return instance;
+Base::ServiceProvider globalServiceProvider;
}
diff --git a/src/Base/ServiceProvider.h b/src/Base/ServiceProvider.h
index 738c56d172..8746de7baa 100644
--- a/src/Base/ServiceProvider.h
+++ b/src/Base/ServiceProvider.h
@@ -36,6 +36,49 @@
namespace Base
{
+/**
+ * Class that implements basic service container that can be used to obtain different implementation
+ * of various services.
+ *
+ * Primary use of such container is to provide ability to define global services that can be
+ * implemented within non-core modules. This for example allows to use code that is available only
+ * in Part module from Base with the only requirement being that Part implements specific interface
+ * and registers the service within service provider.
+ *
+ * For ease of use global service provider instance is provided with convenience functions:
+ * - Base::provideService
+ * - Base::provideServiceImplementations
+ * - Base::registerServiceImplementation
+ *
+ * As the example, we can define service that provides placement of sub objects in App:
+ * @code
+ * class SubObjectPlacementProvider
+ * {
+ * public:
+ * virtual Base::Placement calculate(SubObjectT object, Base::Placement basePlacement) const =
+ * 0;
+ * };
+ * @endcode
+ *
+ * App does not know how to implement this service, but it can be implemented within Part module:
+ * @code
+ * class AttacherSubObjectPlacement final: public App::SubObjectPlacementProvider { ... }
+ *
+ * // later in module initialization method
+ *
+ * Base::registerServiceImplementation(new
+ * AttacherSubObjectPlacement);
+ * @endcode
+ *
+ * This service can then be obtained inside other modules, without them being aware of the
+ * implementation - only the interface:
+ *
+ * @code
+ * auto subObjectPlacementProvider = Base::provideService();
+ * @endcode
+ *
+ * This function can (and should) be used as default for constructor injection of services.
+ */
class BaseExport ServiceProvider
{
struct ServiceDescriptor
@@ -106,35 +149,53 @@ public:
* @tparam T Service interface
*/
template
- void implement(T* contract)
+ void registerImplementation(T* contract)
{
ServiceDescriptor descriptor {typeid(T).name(), contract};
_implementations[typeid(T).name()].push_front(descriptor);
}
- static ServiceProvider& get();
-
private:
- std::map> _implementations;
+ std::map> _implementations;
};
+BaseExport extern ServiceProvider globalServiceProvider;
+
+/**
+ * Obtains primary implementation of requested service from the global service provider.
+ *
+ * @tparam T Service kind to obtain.
+ * @return Primary implementation of the service or nullptr if there is no implementation available.
+ */
template
-T* provideImplementation()
+T* provideService()
{
- return ServiceProvider::get().provide();
+ return globalServiceProvider.provide();
}
+/**
+ * Obtains all available implementations of requested service in the global service provider.
+ *
+ * @tparam T Service kind to obtain.
+ * @return List of available service implementation.
+ */
template
-std::list provideAllImplementations()
+std::list provideServiceImplementations()
{
- return ServiceProvider::get().all();
+ return globalServiceProvider.all();
}
+/**
+ * Registers implementation of service in the global service provider.
+ *
+ * @tparam T Service kind to obtain.
+ * @return List of available service implementation.
+ */
template
-void implementContract(T* implementation)
+void registerServiceImplementation(T* implementation)
{
- ServiceProvider::get().implement(implementation);
+ globalServiceProvider.registerImplementation(implementation);
}
} // namespace Base
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/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp
index 1ca7d684e2..1ab6a81e84 100644
--- a/src/Gui/QuantitySpinBox.cpp
+++ b/src/Gui/QuantitySpinBox.cpp
@@ -405,10 +405,18 @@ void QuantitySpinBox::resizeEvent(QResizeEvent * event)
resizeWidget();
}
-void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent *event)
+void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event)
{
- if (!handleKeyEvent(event->text()))
+ const auto isEnter = event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return;
+
+ if (isEnter && !isNormalized()) {
+ normalize();
+ return;
+ }
+
+ if (!handleKeyEvent(event->text())) {
QAbstractSpinBox::keyPressEvent(event);
+ }
}
void Gui::QuantitySpinBox::paintEvent(QPaintEvent*)
@@ -441,7 +449,7 @@ void QuantitySpinBox::updateEdit(const QString& text)
edit->setText(text);
- cursor = qBound(0, cursor, edit->displayText().size() - d->unitStr.size());
+ cursor = qBound(0, cursor, qMax(0, edit->displayText().size() - d->unitStr.size()));
if (selsize > 0) {
edit->setSelection(0, cursor);
}
@@ -477,6 +485,24 @@ double QuantitySpinBox::rawValue() const
return d->quantity.getValue();
}
+void QuantitySpinBox::normalize()
+{
+ // this does not really change the value, only the representation
+ QSignalBlocker blocker(this);
+
+ Q_D(const QuantitySpinBox);
+ return setValue(d->quantity);
+}
+
+bool QuantitySpinBox::isNormalized()
+{
+ static const QRegularExpression operators(QStringLiteral("[+\\-/*]"),
+ QRegularExpression::CaseInsensitiveOption);
+
+ Q_D(const QuantitySpinBox);
+ return !d->validStr.contains(operators);
+}
+
void QuantitySpinBox::setValue(const Base::Quantity& value)
{
Q_D(QuantitySpinBox);
@@ -884,6 +910,7 @@ void QuantitySpinBox::focusInEvent(QFocusEvent * event)
void QuantitySpinBox::focusOutEvent(QFocusEvent * event)
{
validateInput();
+ normalize();
QToolTip::hideText();
QAbstractSpinBox::focusOutEvent(event);
diff --git a/src/Gui/QuantitySpinBox.h b/src/Gui/QuantitySpinBox.h
index dadc28d906..780553718c 100644
--- a/src/Gui/QuantitySpinBox.h
+++ b/src/Gui/QuantitySpinBox.h
@@ -57,6 +57,9 @@ public:
/// Get the current quantity without unit
double rawValue() const;
+ void normalize();
+ bool isNormalized();
+
/// Gives the current state of the user input, gives true if it is a valid input with correct quantity
/// or returns false if the input is a unparsable string or has a wrong unit.
bool hasValidInput() const;
diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp
index c7e049e5c7..3d4ad6b650 100644
--- a/src/Gui/SoFCCSysDragger.cpp
+++ b/src/Gui/SoFCCSysDragger.cpp
@@ -27,10 +27,8 @@
#include
#include
#include
-#include
#include
#include
-#include
#include
#include
#include
@@ -46,6 +44,9 @@
#include
#include
#include
+#include
+#include
+#include
#endif
#include
@@ -56,6 +57,8 @@
#include "MainWindow.h"
#include "SoFCDB.h"
+#include
+
/*
GENERAL NOTE ON COIN3D CUSTOM DRAGGERS
@@ -100,14 +103,20 @@ TDragger::TDragger()
this->ref();
#endif
- SO_KIT_ADD_CATALOG_ENTRY(translatorSwitch, SoSwitch, TRUE, geomSeparator, "", TRUE);
- SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, translatorSwitch, "", TRUE);
- SO_KIT_ADD_CATALOG_ENTRY(translatorActive, SoSeparator, TRUE, translatorSwitch, "", TRUE);
+ SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, geomSeparator, "", TRUE);
+ SO_KIT_ADD_CATALOG_ENTRY(activeSwitch, SoSwitch, TRUE, translator, "", TRUE);
+ SO_KIT_ADD_CATALOG_ENTRY(activeColor, SoBaseColor, TRUE, activeSwitch, "", TRUE);
+ SO_KIT_ADD_CATALOG_ENTRY(coneSeparator, SoSeparator, TRUE, translator, "", TRUE);
+ SO_KIT_ADD_CATALOG_ENTRY(cylinderSeparator, SoSeparator, TRUE, translator, "", TRUE);
+ SO_KIT_ADD_CATALOG_ENTRY(labelSeparator, SoSeparator, TRUE, translator, "", TRUE);
if (SO_KIT_IS_FIRST_INSTANCE()) {
buildFirstInstance();
}
+ SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, geomSeparator, "", TRUE);
+
+ SO_KIT_ADD_FIELD(label, (""));
SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0));
SO_KIT_ADD_FIELD(translationIncrement, (1.0));
SO_KIT_ADD_FIELD(translationIncrementCount, (0));
@@ -118,11 +127,15 @@ TDragger::TDragger()
// initialize default parts.
// first is from 'SO_KIT_CATALOG_ENTRY_HEADER' macro
// second is unique name from buildFirstInstance().
- this->setPartAsDefault("translator", "CSysDynamics_TDragger_Translator");
- this->setPartAsDefault("translatorActive", "CSysDynamics_TDragger_TranslatorActive");
+ SoInteractionKit::setPartAsDefault("coneSeparator", "CSysDynamics_TDragger_Cone");
+ SoInteractionKit::setPartAsDefault("cylinderSeparator", "CSysDynamics_TDragger_Cylinder");
+ SoInteractionKit::setPartAsDefault("activeColor", "CSysDynamics_TDragger_ActiveColor");
+
+ SoInteractionKit::setPart("labelSeparator", buildLabelGeometry());
+
+ auto sw = SO_GET_ANY_PART(this, "activeSwitch", SoSwitch);
+ SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
- SoSwitch* sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch);
- SoInteractionKit::setSwitchValue(sw, 0);
this->addStartCallback(&TDragger::startCB);
this->addMotionCallback(&TDragger::motionCB);
@@ -150,34 +163,22 @@ TDragger::~TDragger()
void TDragger::buildFirstInstance()
{
- SoGroup* geometryGroup = buildGeometry();
+ auto cylinderSeparator = buildCylinderGeometry();
+ auto coneSeparator = buildConeGeometry();
+ auto activeColor = buildActiveColor();
- auto localTranslator = new SoSeparator();
- localTranslator->setName("CSysDynamics_TDragger_Translator");
- localTranslator->addChild(geometryGroup);
- SoFCDB::getStorage()->addChild(localTranslator);
+ cylinderSeparator->setName("CSysDynamics_TDragger_Cylinder");
+ coneSeparator->setName("CSysDynamics_TDragger_Cone");
+ activeColor->setName("CSysDynamics_TDragger_ActiveColor");
- auto localTranslatorActive = new SoSeparator();
- localTranslatorActive->setName("CSysDynamics_TDragger_TranslatorActive");
- auto colorActive = new SoBaseColor();
- colorActive->rgb.setValue(1.0, 1.0, 0.0);
- localTranslatorActive->addChild(colorActive);
- localTranslatorActive->addChild(geometryGroup);
- SoFCDB::getStorage()->addChild(localTranslatorActive);
+ SoFCDB::getStorage()->addChild(cylinderSeparator);
+ SoFCDB::getStorage()->addChild(coneSeparator);
+ SoFCDB::getStorage()->addChild(activeColor);
}
-SoGroup* TDragger::buildGeometry()
+SoSeparator* TDragger::buildCylinderGeometry() const
{
- // this builds one leg in the Y+ direction because of default done direction.
- // the location anchor for shapes is the center of shape.
-
- auto root = new SoGroup();
-
- // cylinder
- float cylinderHeight = 10.0;
- float cylinderRadius = 0.1f;
auto cylinderSeparator = new SoSeparator();
- root->addChild(cylinderSeparator);
auto cylinderLightModel = new SoLightModel();
cylinderLightModel->model = SoLightModel::BASE_COLOR;
@@ -192,14 +193,15 @@ SoGroup* TDragger::buildGeometry()
cylinder->height.setValue(cylinderHeight);
cylinderSeparator->addChild(cylinder);
- // cone
- float coneBottomRadius = 0.8F;
- float coneHeight = 2.5;
- auto coneSeparator = new SoSeparator();
- root->addChild(coneSeparator);
+ return cylinderSeparator;
+}
+SoSeparator* TDragger::buildConeGeometry() const
+{
auto coneLightModel = new SoLightModel();
coneLightModel->model = SoLightModel::BASE_COLOR;
+
+ auto coneSeparator = new SoSeparator();
coneSeparator->addChild(coneLightModel);
auto pickStyle = new SoPickStyle();
@@ -216,7 +218,35 @@ SoGroup* TDragger::buildGeometry()
cone->height.setValue(coneHeight);
coneSeparator->addChild(cone);
- return root;
+ return coneSeparator;
+}
+
+SoSeparator* TDragger::buildLabelGeometry()
+{
+ auto labelSeparator = new SoSeparator();
+
+ auto labelTranslation = new SoTranslation();
+ labelTranslation->translation.setValue(0.0, cylinderHeight + coneHeight * 1.5, 0.0);
+ labelSeparator->addChild(labelTranslation);
+
+ auto label = new SoFrameLabel();
+ label->string.connectFrom(&this->label);
+ label->textColor.setValue(1.0, 1.0, 1.0);
+ 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();
+ colorActive->rgb.setValue(1.0, 1.0, 0.0);
+
+ return colorActive;
}
void TDragger::startCB(void*, SoDragger* d)
@@ -274,8 +304,8 @@ void TDragger::valueChangedCB(void*, SoDragger* d)
void TDragger::dragStart()
{
SoSwitch* sw;
- sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch);
- SoInteractionKit::setSwitchValue(sw, 1);
+ sw = SO_GET_ANY_PART(this, "activeSwitch", SoSwitch);
+ SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
// do an initial projection to eliminate discrepancies
// in arrow head pick. we define the arrow in the y+ direction
@@ -334,8 +364,8 @@ void TDragger::drag()
void TDragger::dragFinish()
{
SoSwitch* sw;
- sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch);
- SoInteractionKit::setSwitchValue(sw, 0);
+ sw = SO_GET_ANY_PART(this, "activeSwitch", SoSwitch);
+ SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
SbBool TDragger::setUpConnections(SbBool onoff, SbBool doitalways)
@@ -1014,7 +1044,7 @@ SoFCCSysDragger::SoFCCSysDragger()
SO_KIT_ADD_CATALOG_ENTRY(annotation, So3DAnnotation, TRUE, geomSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(scaleNode, SoScale, TRUE, annotation, "", TRUE);
-
+ SO_KIT_ADD_CATALOG_ENTRY(pickStyle, SoPickStyle, TRUE, annotation, "", TRUE);
// Translator
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
@@ -1142,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));
@@ -1158,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
@@ -1165,21 +1198,22 @@ SoFCCSysDragger::SoFCCSysDragger()
SbColor(0, 1.0, 0).getPackedValue(0.0f),
SbColor(0, 0, 1.0).getPackedValue(0.0f));
- // Increments
-
// Translator
TDragger* tDragger;
tDragger = SO_GET_ANY_PART(this, "xTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
+ 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.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.connectFrom(&zAxisLabel);
translationIncrementCountZ.connectFrom(&tDragger->translationIncrementCount);
// Planar Translator
TPlanarDragger* tPlanarDragger;
@@ -1275,6 +1309,9 @@ SoFCCSysDragger::SoFCCSysDragger()
localScaleNode->scaleFactor.connectFrom(&scaleEngine->vector);
autoScaleResult.connectFrom(&draggerSize);
+ SoPickStyle* localPickStyle = SO_GET_ANY_PART(this, "pickStyle", SoPickStyle);
+ localPickStyle->style = SoPickStyle::SHAPE_ON_TOP;
+
addValueChangedCallback(&SoFCCSysDragger::valueChangedCB);
translationSensor.setFunction(&SoFCCSysDragger::translationSensorCB);
diff --git a/src/Gui/SoFCCSysDragger.h b/src/Gui/SoFCCSysDragger.h
index 20364506b1..99c7a77ac6 100644
--- a/src/Gui/SoFCCSysDragger.h
+++ b/src/Gui/SoFCCSysDragger.h
@@ -29,10 +29,12 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
class SoCamera;
@@ -50,12 +52,23 @@ namespace Gui
class TDragger : public SoDragger
{
SO_KIT_HEADER(TDragger);
- SO_KIT_CATALOG_ENTRY_HEADER(translatorSwitch);
+ SO_KIT_CATALOG_ENTRY_HEADER(activeSwitch);
+ SO_KIT_CATALOG_ENTRY_HEADER(activeColor);
SO_KIT_CATALOG_ENTRY_HEADER(translator);
- SO_KIT_CATALOG_ENTRY_HEADER(translatorActive);
+ SO_KIT_CATALOG_ENTRY_HEADER(cylinderSeparator);
+ SO_KIT_CATALOG_ENTRY_HEADER(coneSeparator);
+ SO_KIT_CATALOG_ENTRY_HEADER(labelSeparator);
+
+ static constexpr float coneBottomRadius { 0.8f };
+ static constexpr float coneHeight { 2.5f };
+
+ static constexpr float cylinderHeight { 10.0f };
+ static constexpr float cylinderRadius { 0.1f };
public:
static void initClass();
TDragger();
+
+ SoSFString label; //!< set from outside and used to label
SoSFVec3f translation; //!< set from outside and used from outside for single precision.
SoSFDouble translationIncrement; //!< set from outside and used for rounding.
SoSFInt32 translationIncrementCount; //!< number of steps. used from outside.
@@ -82,6 +95,12 @@ private:
void buildFirstInstance();
SbVec3f roundTranslation(const SbVec3f &vecIn, float incrementIn);
SoGroup* buildGeometry();
+
+ SoSeparator* buildCylinderGeometry() const;
+ SoSeparator* buildConeGeometry() const;
+ SoSeparator* buildLabelGeometry();
+ SoBaseColor* buildActiveColor();
+
using inherited = SoDragger;
};
@@ -198,6 +217,7 @@ class GuiExport SoFCCSysDragger : public SoDragger
SO_KIT_HEADER(SoFCCSysDragger);
SO_KIT_CATALOG_ENTRY_HEADER(annotation);
SO_KIT_CATALOG_ENTRY_HEADER(scaleNode);
+ SO_KIT_CATALOG_ENTRY_HEADER(pickStyle);
// Translator
SO_KIT_CATALOG_ENTRY_HEADER(xTranslatorSwitch);
SO_KIT_CATALOG_ENTRY_HEADER(yTranslatorSwitch);
@@ -263,6 +283,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 6b2f55d53c..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));
}
@@ -388,7 +390,6 @@ void SoFrameLabel::setIcon(const QPixmap &pixMap)
drawImage();
}
-
void SoFrameLabel::notify(SoNotList * list)
{
SoField *f = list->getLastField();
@@ -398,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);
}
@@ -418,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);
+
+ 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..c59a9f3f89 100644
--- a/src/Gui/TaskCSysDragger.cpp
+++ b/src/Gui/TaskCSysDragger.cpp
@@ -25,12 +25,19 @@
#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 +46,717 @@
#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);
+ referencePlacement = vp->getObjectPlacement();
+ referenceRotation = referencePlacement.getRotation();
- auto rLabel = new QLabel(tr("Rotation Increment:"), incrementsBox);
- gridLayout->addWidget(rLabel, 1, 0, Qt::AlignRight);
+ globalOrigin = vp->getObjectPlacement() * App::GeoFeature::getGlobalPlacement(vp->getObject()).inverse();
- 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);
+
+ savePreferences();
}
-void TaskCSysDragger::onRIncrementSlot(double freshValue)
+void TaskTransform::dragStartCallback(void* data, 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);
+
+ const auto currentRotation = task->vp->getOriginalDraggerPlacement().getRotation();
+ const auto updatedRotation = task->vp->getDraggerPlacement().getRotation();
+
+ const auto rotationAxisHasChanged = [task](auto first, auto second) {
+ double alpha, beta, gamma;
+
+ (first.inverse() * second).getEulerAngles(task->eulerSequence(), alpha, beta, gamma);
+
+ auto angles = {alpha, beta, gamma};
+ const int changed = std::count_if(angles.begin(), angles.end(), [](double angle) {
+ return std::fabs(angle) > tolerance;
+ });
+
+ // if representation of both differs by more than one axis the axis of rotation must be
+ // different
+ return changed > 1;
+ };
+
+ if (!updatedRotation.isSame(currentRotation, tolerance)) {
+ task->resetReferencePlacement();
+
+ if (rotationAxisHasChanged(task->referenceRotation, updatedRotation)) {
+ task->referenceRotation = currentRotation;
+ }
+ }
+
+ task->updatePositionAndRotationUi();
+}
+
+void TaskTransform::loadPlacementModeItems() const
+{
+ ui->placementComboBox->clear();
+
+ ui->placementComboBox->addItem(tr("Object origin"),
+ QVariant::fromValue(PlacementMode::ObjectOrigin));
+
+ if (centerOfMassProvider->ofDocumentObject(vp->getObject()).has_value()) {
+ ui->placementComboBox->addItem(tr("Center of mass / Centroid"),
+ QVariant::fromValue(PlacementMode::Centroid));
+ }
+
+ if (subObjectPlacementProvider) {
+ ui->placementComboBox->addItem(tr("Custom"), QVariant::fromValue(PlacementMode::Custom));
+ }
+}
+
+void TaskTransform::loadPositionModeItems() const
+{
+ ui->positionModeComboBox->clear();
+ ui->positionModeComboBox->addItem(tr("Local"), QVariant::fromValue(PositionMode::Local));
+ ui->positionModeComboBox->addItem(tr("Global"), QVariant::fromValue(PositionMode::Global));
+}
+
+void TaskTransform::setupGui()
+{
+ auto proxy = new QWidget(this);
+ ui->setupUi(proxy);
+ this->groupLayout()->addWidget(proxy);
+
+ loadPlacementModeItems();
+ loadPositionModeItems();
+
+ ui->referencePickerWidget->hide();
+ ui->alignRotationCheckBox->hide();
+
+ for (auto positionSpinBox : {ui->translationIncrementSpinBox,
+ ui->xPositionSpinBox,
+ ui->yPositionSpinBox,
+ ui->zPositionSpinBox}) {
+ positionSpinBox->setUnit(Base::Unit::Length);
+ }
+
+ for (auto rotationSpinBox : {ui->rotationIncrementSpinBox,
+ ui->xRotationSpinBox,
+ ui->yRotationSpinBox,
+ ui->zRotationSpinBox}) {
+ rotationSpinBox->setUnit(Base::Unit::Angle);
+ }
+
+ connect(ui->translationIncrementSpinBox,
+ qOverload(&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,
+ [=](double) {
+ onRotationChange(rotationSpinBox);
+ });
+ }
+
+ alignGridLayoutColumns({ui->absolutePositionLayout,
+ ui->absoluteRotationLayout,
+ ui->transformOriginLayout,
+ ui->referencePickerLayout});
+
+ loadPreferences();
+
+ updateInputLabels();
+ updateDraggerLabels();
+ updateIncrements();
+ updatePositionAndRotationUi();
+}
+
+void TaskTransform::loadPreferences()
+{
+ double lastTranslationIncrement = hGrp->GetFloat("LastTranslationIncrement", 1.0);
+ double lastRotationIncrement = hGrp->GetFloat("LastRotationIncrement", 5.0);
+
+ ui->translationIncrementSpinBox->setValue(lastTranslationIncrement);
+ ui->rotationIncrementSpinBox->setValue(lastRotationIncrement);
+}
+
+void TaskTransform::savePreferences()
+{
+ hGrp->SetFloat("LastTranslationIncrement", ui->translationIncrementSpinBox->rawValue());
+ hGrp->SetFloat("LastRotationIncrement", ui->rotationIncrementSpinBox->rawValue());
+}
+
+void TaskTransform::updatePositionAndRotationUi() const
+{
+ const auto referencePlacement = currentCoordinateSystem().origin;
+
+ const auto xyzPlacement = vp->getDraggerPlacement();
+ const auto uvwPlacement = referencePlacement.inverse() * xyzPlacement;
+
+ auto fixNegativeZero = [](const double value) {
+ return std::fabs(value) < Base::Precision::Confusion() ? 0.0 : value;
+ };
+
+ auto setPositionValues = [&](const Base::Vector3d& vec, auto* x, auto* y, auto* z) {
+ [[maybe_unused]]
+ auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)};
+
+ x->setValue(fixNegativeZero(vec.x));
+ y->setValue(fixNegativeZero(vec.y));
+ z->setValue(fixNegativeZero(vec.z));
+ };
+
+ auto setRotationValues = [&](const Base::Rotation& rot, auto* x, auto* y, auto* z) {
+ [[maybe_unused]]
+ auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)};
+
+ double alpha, beta, gamma;
+ rot.getEulerAngles(eulerSequence(), alpha, beta, gamma);
+
+ x->setValue(fixNegativeZero(alpha));
+ y->setValue(fixNegativeZero(beta));
+ z->setValue(fixNegativeZero(gamma));
+ };
+
+ setPositionValues(uvwPlacement.getPosition(),
+ ui->xPositionSpinBox,
+ ui->yPositionSpinBox,
+ ui->zPositionSpinBox);
+
+ setRotationValues(positionMode == PositionMode::Local
+ ? referenceRotation.inverse() * xyzPlacement.getRotation()
+ : uvwPlacement.getRotation(),
+ ui->xRotationSpinBox,
+ ui->yRotationSpinBox,
+ ui->zRotationSpinBox);
+}
+
+void TaskTransform::updateInputLabels() const
+{
+ auto [xLabel, yLabel, zLabel] = currentCoordinateSystem().labels;
+
+ ui->xPositionLabel->setText(QString::fromStdString(xLabel));
+ ui->yPositionLabel->setText(QString::fromStdString(yLabel));
+ ui->zPositionLabel->setText(QString::fromStdString(zLabel));
+
+ ui->xRotationLabel->setText(QString::fromStdString(xLabel));
+ ui->yRotationLabel->setText(QString::fromStdString(yLabel));
+ ui->zRotationLabel->setText(QString::fromStdString(zLabel));
+}
+
+void TaskTransform::updateDraggerLabels() const
+{
+ auto coordinateSystem =
+ isDraggerAlignedToCoordinateSystem() ? globalCoordinateSystem() : localCoordinateSystem();
+
+ auto [xLabel, yLabel, zLabel] = coordinateSystem.labels;
+
+ dragger->xAxisLabel.setValue(xLabel.c_str());
+ dragger->yAxisLabel.setValue(yLabel.c_str());
+ dragger->zAxisLabel.setValue(zLabel.c_str());
+}
+
+void TaskTransform::updateIncrements() const
+{
+ dragger->translationIncrement.setValue(
+ std::max(ui->translationIncrementSpinBox->rawValue(), 0.001));
+ dragger->rotationIncrement.setValue(
+ Base::toRadians(std::max(ui->rotationIncrementSpinBox->rawValue(), 0.01)));
+}
+
+void TaskTransform::setSelectionMode(SelectionMode mode)
+{
+ Gui::Selection().clearSelection();
+
+ SoPickStyle* draggerPickStyle = SO_GET_PART(dragger, "pickStyle", SoPickStyle);
+
+ ui->pickTransformOriginButton->setText(tr("Pick reference"));
+ ui->alignToOtherObjectButton->setText(tr("Move to other object"));
+
+ switch (mode) {
+ case SelectionMode::SelectTransformOrigin:
+ draggerPickStyle->style = SoPickStyle::UNPICKABLE;
+ draggerPickStyle->setOverride(true);
+ blockSelection(false);
+ ui->referenceLineEdit->setText(tr("Select face, edge or vertex..."));
+ ui->pickTransformOriginButton->setText(tr("Cancel"));
+ break;
+
+ case SelectionMode::SelectAlignTarget:
+ draggerPickStyle->style = SoPickStyle::UNPICKABLE;
+ draggerPickStyle->setOverride(true);
+ ui->alignToOtherObjectButton->setText(tr("Cancel"));
+ blockSelection(false);
+ break;
+
+ case SelectionMode::None:
+ draggerPickStyle->style = SoPickStyle::SHAPE_ON_TOP;
+ draggerPickStyle->setOverride(false);
+ blockSelection(true);
+
+ vp->setTransformOrigin(vp->getTransformOrigin());
+
+ break;
+ }
+
+ selectionMode = mode;
+
+ updateSpinBoxesReadOnlyStatus();
+}
+
+TaskTransform::SelectionMode TaskTransform::getSelectionMode() const
+{
+ return selectionMode;
+}
+
+TaskTransform::CoordinateSystem TaskTransform::localCoordinateSystem() const
+{
+ auto origin = referencePlacement;
+ origin.setRotation(vp->getDraggerPlacement().getRotation());
+
+ return {{"U", "V", "W"}, origin};
+}
+
+TaskTransform::CoordinateSystem TaskTransform::globalCoordinateSystem() const
+{
+ return {{"X", "Y", "Z"}, globalOrigin};
+}
+
+TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const
+{
+ return ui->positionModeComboBox->currentIndex() == 0 ? localCoordinateSystem()
+ : globalCoordinateSystem();
+}
+
+Base::Rotation::EulerSequence TaskTransform::eulerSequence() const
+{
+ return positionMode == PositionMode::Local ? Base::Rotation::Intrinsic_XYZ
+ : Base::Rotation::Extrinsic_XYZ;
+}
+
+void TaskTransform::onSelectionChanged(const SelectionChanges& msg)
+{
+ const auto isSupportedMessage =
+ msg.Type == SelectionChanges::AddSelection || msg.Type == SelectionChanges::SetPreselect;
+
+ if (!isSupportedMessage) {
+ return;
+ }
+
+ if (!subObjectPlacementProvider) {
+ return;
+ }
+
+ if (!msg.pOriginalMsg) {
+ // this should not happen! Original should contain unresolved message.
+ return;
+ }
+
+ auto doc = Application::Instance->getDocument(msg.pDocName);
+ auto obj = doc->getDocument()->getObject(msg.pObjectName);
+
+ auto orgDoc = Application::Instance->getDocument(msg.pOriginalMsg->pDocName);
+ auto orgObj = orgDoc->getDocument()->getObject(msg.pOriginalMsg->pObjectName);
+
+ auto globalPlacement = App::GeoFeature::getGlobalPlacement(obj, orgObj, msg.pOriginalMsg->pSubName);
+ auto localPlacement = App::GeoFeature::getPlacementFromProp(obj, "Placement");
+ auto rootPlacement = App::GeoFeature::getGlobalPlacement(vp->getObject());
+ auto attachedPlacement = subObjectPlacementProvider->calculate(msg.Object, localPlacement);
+
+ auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement * attachedPlacement;
+
+ auto label = QStringLiteral("%1#%2.%3")
+ .arg(QLatin1String(msg.pOriginalMsg->pObjectName),
+ QLatin1String(msg.pObjectName),
+ QLatin1String(msg.pSubName));
+
+ switch (selectionMode) {
+ case SelectionMode::SelectTransformOrigin: {
+ if (msg.Type == SelectionChanges::AddSelection) {
+ ui->referenceLineEdit->setText(label);
+ customTransformOrigin = selectedObjectPlacement;
+ updateTransformOrigin();
+ setSelectionMode(SelectionMode::None);
+ } else {
+ vp->setTransformOrigin(selectedObjectPlacement);
+ }
+
+ break;
+ }
+
+ case SelectionMode::SelectAlignTarget: {
+ vp->setDraggerPlacement(vp->getObjectPlacement() * selectedObjectPlacement);
+
+ if (msg.Type == SelectionChanges::AddSelection) {
+ moveObjectToDragger();
+
+ setSelectionMode(SelectionMode::None);
+ }
+
+ break;
+ }
+
+ default:
+ // no-op
+ break;
+ }
+}
+
+void TaskTransform::onAlignRotationChanged()
+{
+ updateDraggerLabels();
+ updateTransformOrigin();
+}
+
+void TaskTransform::onAlignToOtherObject()
+{
+ if (selectionMode == SelectionMode::SelectAlignTarget) {
+ setSelectionMode(SelectionMode::None);
+ return;
+ }
+
+ setSelectionMode(SelectionMode::SelectAlignTarget);
+}
+
+void TaskTransform::moveObjectToDragger()
+{
+ vp->updateTransformFromDragger();
+ vp->updatePlacementFromDragger();
+
+ resetReferenceRotation();
+ resetReferencePlacement();
+
+ updatePositionAndRotationUi();
+}
+
+void TaskTransform::onFlip()
+{
+ auto placement = vp->getDraggerPlacement();
+
+ placement.setRotation(placement.getRotation()
+ * Base::Rotation::fromNormalVector(Base::Vector3d(0, 0, -1)));
+
+ vp->setDraggerPlacement(placement);
+
+ moveObjectToDragger();
+}
+
+void TaskTransform::onPickTransformOrigin()
+{
+ setSelectionMode(selectionMode == SelectionMode::None ? SelectionMode::SelectTransformOrigin
+ : SelectionMode::None);
+}
+
+void TaskTransform::onPlacementModeChange(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 {*com, {}};
+ }
+ return {};
+ case PlacementMode::Custom:
+ return customTransformOrigin.value_or(Base::Placement {});
+ default:
+ return {};
+ }
+ };
+
+ ui->referencePickerWidget->setVisible(placementMode == PlacementMode::Custom);
+
+ if (placementMode == PlacementMode::Custom && !customTransformOrigin.has_value()) {
+ setSelectionMode(SelectionMode::SelectTransformOrigin);
+ return;
+ }
+
+ auto transformOrigin = getTransformOrigin(placementMode);
+ if (isDraggerAlignedToCoordinateSystem()) {
+ transformOrigin.setRotation(
+ (vp->getObjectPlacement().inverse() * globalCoordinateSystem().origin).getRotation());
+ }
+
+ vp->setTransformOrigin(transformOrigin);
+
+ resetReferencePlacement();
+ resetReferenceRotation();
+
+ updatePositionAndRotationUi();
+ updateDraggerLabels();
+}
+
+void TaskTransform::updateSpinBoxesReadOnlyStatus() const
+{
+ const bool isReadOnly = selectionMode != SelectionMode::None;
+
+ const auto controls = {
+ ui->xPositionSpinBox,
+ ui->yPositionSpinBox,
+ ui->zPositionSpinBox,
+ ui->xRotationSpinBox,
+ ui->yRotationSpinBox,
+ ui->zRotationSpinBox,
+ };
+
+ for (const auto& control : controls) {
+ control->setReadOnly(isReadOnly);
+ }
+}
+
+void TaskTransform::resetReferencePlacement()
+{
+ referencePlacement = vp->getDraggerPlacement();
+}
+
+void TaskTransform::resetReferenceRotation()
+{
+ referenceRotation = vp->getDraggerPlacement().getRotation();
+}
+
+bool TaskTransform::isDraggerAlignedToCoordinateSystem() const
+{
+ return positionMode == PositionMode::Global && ui->alignRotationCheckBox->isChecked();
+}
+
+void TaskTransform::onTransformOriginReset()
+{
+ vp->resetTransformOrigin();
+}
+
+void TaskTransform::onCoordinateSystemChange([[maybe_unused]] int mode)
+{
+ positionMode = ui->positionModeComboBox->currentData().value();
+
+ ui->alignRotationCheckBox->setVisible(positionMode != PositionMode::Local);
+
+ updateInputLabels();
+ updatePositionAndRotationUi();
+ updateTransformOrigin();
+}
+
+void TaskTransform::onPositionChange()
+{
+ const auto uvwPosition = Base::Vector3d(ui->xPositionSpinBox->rawValue(),
+ ui->yPositionSpinBox->rawValue(),
+ ui->zPositionSpinBox->rawValue());
+
+ const auto xyzPosition = currentCoordinateSystem().origin.getPosition()
+ + currentCoordinateSystem().origin.getRotation().multVec(uvwPosition);
+
+ const auto placement = vp->getDraggerPlacement();
+
+ vp->setDraggerPlacement({xyzPosition, placement.getRotation()});
+
+ vp->updateTransformFromDragger();
+ vp->updatePlacementFromDragger();
+}
+
+void TaskTransform::onRotationChange(QuantitySpinBox* changed)
+{
+ if (positionMode == PositionMode::Local) {
+ for (auto rotationSpinBox : {ui->xRotationSpinBox,
+ ui->yRotationSpinBox,
+ ui->zRotationSpinBox}) {
+ QSignalBlocker blocker(rotationSpinBox);
+
+ // if any other spinbox contains non-zero value we need to reset rotation reference first
+ if (std::fabs(rotationSpinBox->rawValue()) > tolerance && rotationSpinBox != changed) {
+ resetReferenceRotation();
+ rotationSpinBox->setValue(0.0);
+ }
+ }
+ }
+
+ const auto uvwRotation = Base::Rotation::fromEulerAngles(eulerSequence(),
+ ui->xRotationSpinBox->rawValue(),
+ ui->yRotationSpinBox->rawValue(),
+ ui->zRotationSpinBox->rawValue());
+
+ auto referenceRotation = positionMode == PositionMode::Local
+ ? this->referenceRotation
+ : currentCoordinateSystem().origin.getRotation();
+
+ const auto xyzRotation = referenceRotation * uvwRotation;
+
+ const auto placement = vp->getDraggerPlacement();
+
+ vp->setDraggerPlacement({placement.getPosition(), xyzRotation});
+
+ vp->updateTransformFromDragger();
+ vp->updatePlacementFromDragger();
+
+ resetReferencePlacement();
+}
+
+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..0f7fed2cca 100644
--- a/src/Gui/TaskCSysDragger.h
+++ b/src/Gui/TaskCSysDragger.h
@@ -25,39 +25,152 @@
#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
+
+ static constexpr double tolerance = 1e-7;
+
+public:
+ enum class SelectionMode { None, SelectTransformOrigin, SelectAlignTarget };
+ enum class PlacementMode { ObjectOrigin, Centroid, Custom };
+ enum class PositionMode { Local, Global };
+
+ 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;
+
+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(QuantitySpinBox* changed);
+
+private:
+ static inline bool firstDrag = true;
+ static void dragStartCallback(void* data, SoDragger* d);
+ static void dragMotionCallback(void* data, SoDragger* d);
+
+ void setSelectionMode(SelectionMode mode);
+ SelectionMode getSelectionMode() const;
+
+ CoordinateSystem globalCoordinateSystem() const;
+ CoordinateSystem localCoordinateSystem() const;
+ CoordinateSystem currentCoordinateSystem() const;
+
+ Base::Rotation::EulerSequence eulerSequence() const;
+
+ 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();
+ void updateSpinBoxesReadOnlyStatus() const;
+
+ void resetReferencePlacement();
+ void resetReferenceRotation();
+
+ void moveObjectToDragger();
+
+ 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 referencePlacement {};
+ Base::Placement globalOrigin {};
+ Base::Rotation referenceRotation {};
+
+ ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger");
+};
+
+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..0371c734a7
--- /dev/null
+++ b/src/Gui/TaskCSysDragger.ui
@@ -0,0 +1,532 @@
+
+
+ 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
+
+
-
+
+
+
+ -
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ X
+
+
+ xPositionSpinBox
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Y
+
+
+ yPositionSpinBox
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Z
+
+
+ zPositionSpinBox
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 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
+
+
+
+
+
+
+ -
+
+
+ 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
+
+
+
+
+ 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..2484ee197e 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,27 @@
#include "TaskCSysDragger.h"
#include "View3DInventorViewer.h"
#include "ViewProviderDragger.h"
+#include "Utilities.h"
+#include
+#include
+#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 +83,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 +115,228 @@ 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) {
- _linkDragger = nullptr;
+ViewProvider* ViewProviderDragger::startEditing(int mode)
+{
+ forwardedViewProvider = nullptr;
+
auto ret = ViewProviderDocumentObject::startEditing(mode);
- if(!ret)
+ if (!ret) {
return ret;
- return _linkDragger?_linkDragger:ret;
+ }
+
+ return forwardedViewProvider ? forwardedViewProvider : ret;
}
-bool ViewProviderDragger::checkLink() {
+bool ViewProviderDragger::forwardToLink()
+{
// 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 (vpParent == this) {
return false;
- auto vp = Application::Instance->getViewProvider(sobj);
- if(!vp)
+ }
+
+ if (!vpParent->isDerivedFrom()) {
return false;
- _linkDragger = vp->startEditing(ViewProvider::Transform);
- if(_linkDragger)
- return true;
- return false;
+ }
+
+ forwardedViewProvider = vpParent->startEditing(ViewProvider::Transform);
+
+ return forwardedViewProvider != 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 (forwardToLink()) {
+ 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 originPlacement = App::GeoFeature::getGlobalPlacement(getObject()) * getObjectPlacement().inverse();
+ auto mat = originPlacement.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 placement = getObject()->getPropertyByName("Placement");
+
+ if (!placement) {
+ return;
+ }
+
+ 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::getObjectPlacement() const
+{
+ if (auto placement = getObject()->getPropertyByName("Placement")) {
+ return placement->getValue();
+ }
+
+ return {};
+}
+
+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
+ );
+}
+
+Base::Placement ViewProviderDragger::getOriginalDraggerPlacement() const
+{
+ return draggerPlacement;
+}
+
+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 = getObjectPlacement() * 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..21808413a7 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,19 @@ public:
/// destructor.
~ViewProviderDragger() override;
+ /// Origin used when object is transformed. It temporarily changes the origin of object.
+ /// Dragger is normally placed at the transform origin, unless explicitly overridden via
+ /// ViewProviderDragger#setDraggerPlacement() method.
+ App::PropertyPlacement TransformOrigin;
+
+ /// Convenience method to obtain the transform origin
+ Base::Placement getTransformOrigin() const { return TransformOrigin.getValue(); }
+ /// Convenience method to set the transform origin
+ void setTransformOrigin(const Base::Placement& placement);
+ /// Resets transform origin to the object origin
+ void resetTransformOrigin();
+
+public:
/** @name Edit methods */
//@{
bool doubleClicked() override;
@@ -63,21 +80,48 @@ public:
/*! synchronize From FC placement to Coin placement*/
static void updateTransform(const Base::Placement &from, SoTransform *to);
+ /// updates placement of object based on dragger position
+ void updatePlacementFromDragger();
+ /// updates transform of object based on dragger position, can be used to preview movement
+ void updateTransformFromDragger();
+
+ /// Gets object placement relative to its coordinate system
+ Base::Placement getObjectPlacement() const;
+ /// Gets current dragger placement, including current dragger movement
+ Base::Placement getDraggerPlacement() const;
+ /// Gets original dragger placement, without current dragger movement
+ Base::Placement getOriginalDraggerPlacement() const;
+ /// Sets placement of dragger relative to objects origin
+ 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;
+
+ bool forwardToLink();
+
+ /**
+ * 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;
+ ViewProvider *forwardedViewProvider = 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);
- bool checkLink();
+ void updateDraggerPosition();
- ViewProvider *_linkDragger = nullptr;
+ Base::Placement draggerPlacement { };
};
} // namespace Gui
diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp
index a2cad7a67f..3e0d0b5244 100644
--- a/src/Gui/ViewProviderLink.cpp
+++ b/src/Gui/ViewProviderLink.cpp
@@ -1624,7 +1624,7 @@ static const char *_LinkElementIcon = "LinkElement";
ViewProviderLink::ViewProviderLink()
:linkType(LinkTypeNone),hasSubName(false),hasSubElement(false)
- ,useCenterballDragger(false),childVp(nullptr),overlayCacheKey(0)
+ ,childVp(nullptr),overlayCacheKey(0)
{
sPixmap = _LinkIcon;
@@ -1670,7 +1670,7 @@ ViewProviderLink::~ViewProviderLink()
}
bool ViewProviderLink::isSelectable() const {
- return !pcDragger && Selectable.getValue();
+ return Selectable.getValue();
}
void ViewProviderLink::attach(App::DocumentObject *pcObj) {
@@ -1851,7 +1851,7 @@ void ViewProviderLink::updateDataPrivate(App::LinkBaseExtension *ext, const App:
auto propLinkPlacement = ext->getLinkPlacementProperty();
if(!propLinkPlacement || propLinkPlacement == prop) {
const auto &pla = static_cast(prop)->getValue();
- ViewProviderGeometryObject::updateTransform(pla, pcTransform);
+ // ViewProviderGeometryObject::updateTransform(pla, pcTransform);
const auto &v = ext->getScaleVector();
if(canScale(v))
pcTransform->scaleFactor.setValue(v.x,v.y,v.z);
@@ -2764,25 +2764,28 @@ ViewProvider *ViewProviderLink::startEditing(int mode) {
}
static thread_local bool _pendingTransform;
- static thread_local Base::Matrix4D _editingTransform;
+ static thread_local Matrix4D _editingTransform;
auto doc = Application::Instance->editDocument();
- if(mode==ViewProvider::Transform) {
- if(_pendingTransform && doc)
+ if (mode == ViewProvider::Transform) {
+ if (_pendingTransform && doc) {
doc->setEditingTransform(_editingTransform);
+ }
- if(!initDraggingPlacement())
+ if (!initDraggingPlacement()) {
return nullptr;
- if(useCenterballDragger)
- pcDragger = CoinPtr(new SoCenterballDragger);
- else
- pcDragger = CoinPtr(new SoFCCSysDragger);
- updateDraggingPlacement(dragCtx->initialPlacement,true);
- pcDragger->addStartCallback(dragStartCallback, this);
- pcDragger->addFinishCallback(dragFinishCallback, this);
- pcDragger->addMotionCallback(dragMotionCallback, this);
- return inherited::startEditing(mode);
+ }
+
+ if (auto result = inherited::startEditing(mode)) {
+ csysDragger->addStartCallback(dragStartCallback, this);
+ csysDragger->addFinishCallback(dragFinishCallback, this);
+ csysDragger->addMotionCallback(dragMotionCallback, this);
+
+ setDraggerPlacement(dragCtx->initialPlacement);
+
+ return result;
+ }
}
if(!linkEdit()) {
@@ -2839,6 +2842,7 @@ bool ViewProviderLink::setEdit(int ModNum)
Selection().clearSelection();
return true;
}
+
return inherited::setEdit(ModNum);
}
@@ -2849,125 +2853,21 @@ void ViewProviderLink::setEditViewer(Gui::View3DInventorViewer* viewer, int ModN
return;
}
- if (pcDragger && viewer)
- {
- auto rootPickStyle = new SoPickStyle();
- rootPickStyle->style = SoPickStyle::UNPICKABLE;
- static_cast(
- viewer->getSceneGraph())->insertChild(rootPickStyle, 0);
-
- if(useCenterballDragger) {
- auto dragger = static_cast(pcDragger.get());
- auto group = new SoAnnotation;
- auto pickStyle = new SoPickStyle;
- pickStyle->setOverride(true);
- group->addChild(pickStyle);
- group->addChild(pcDragger);
-
- // Because the dragger is not grouped with the actual geometry,
- // we use an invisible cube sized by the bounding box obtained from
- // initDraggingPlacement() to scale the centerball dragger properly
-
- auto * ss = static_cast(dragger->getPart("surroundScale", TRUE));
- ss->numNodesUpToContainer = 3;
- ss->numNodesUpToReset = 2;
-
- auto *geoGroup = new SoGroup;
- group->addChild(geoGroup);
- auto *style = new SoDrawStyle;
- style->style.setValue(SoDrawStyle::INVISIBLE);
- style->setOverride(TRUE);
- geoGroup->addChild(style);
- auto *cube = new SoCube;
- geoGroup->addChild(cube);
- auto length = std::max(std::max(dragCtx->bbox.LengthX(),
- dragCtx->bbox.LengthY()), dragCtx->bbox.LengthZ());
- cube->width = length;
- cube->height = length;
- cube->depth = length;
-
- viewer->setupEditingRoot(group,&dragCtx->preTransform);
- } else {
- auto dragger = static_cast(pcDragger.get());
- dragger->draggerSize.setValue(ViewParams::instance()->getDraggerScale());
- dragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
- viewer->setupEditingRoot(pcDragger,&dragCtx->preTransform);
-
- auto task = new TaskCSysDragger(this, dragger);
- Gui::Control().showDialog(task);
- }
- }
+ ViewProviderDragger::setEditViewer(viewer, ModNum);
}
void ViewProviderLink::unsetEditViewer(Gui::View3DInventorViewer* viewer)
{
- SoNode *child = static_cast(viewer->getSceneGraph())->getChild(0);
- if (child && child->isOfType(SoPickStyle::getClassTypeId()))
- static_cast(viewer->getSceneGraph())->removeChild(child);
- pcDragger.reset();
dragCtx.reset();
- Gui::Control().closeDialog();
+
+ inherited::unsetEditViewer(viewer);
}
-Base::Placement ViewProviderLink::currentDraggingPlacement() const
-{
- // if there isn't an active dragger return a default placement
- if (!pcDragger)
- return Base::Placement();
-
- SbVec3f v;
- SbRotation r;
- if (useCenterballDragger) {
- auto dragger = static_cast(pcDragger.get());
- v = dragger->center.getValue();
- r = dragger->rotation.getValue();
- }
- else {
- auto dragger = static_cast(pcDragger.get());
- v = dragger->translation.getValue();
- r = dragger->rotation.getValue();
- }
-
- float q1,q2,q3,q4;
- r.getValue(q1,q2,q3,q4);
- return Base::Placement(Base::Vector3d(v[0],v[1],v[2]),Base::Rotation(q1,q2,q3,q4));
-}
-
-void ViewProviderLink::enableCenterballDragger(bool enable) {
- if(enable == useCenterballDragger)
- return;
- if(pcDragger)
- LINK_THROW(Base::RuntimeError,"Cannot change dragger during dragging");
- useCenterballDragger = enable;
-}
-
-void ViewProviderLink::updateDraggingPlacement(const Base::Placement &pla, bool force) {
- if(pcDragger && (force || currentDraggingPlacement()!=pla)) {
- const auto &pos = pla.getPosition();
- const auto &rot = pla.getRotation();
- FC_LOG("updating dragger placement (" << pos.x << ", " << pos.y << ", " << pos.z << ')');
- if(useCenterballDragger) {
- auto dragger = static_cast(pcDragger.get());
- SbBool wasenabled = dragger->enableValueChangedCallbacks(FALSE);
- SbMatrix matrix;
- matrix = convert(pla.toMatrix());
- dragger->center.setValue(SbVec3f(0,0,0));
- dragger->setMotionMatrix(matrix);
- if (wasenabled) {
- dragger->enableValueChangedCallbacks(TRUE);
- dragger->valueChanged();
- }
- }else{
- auto dragger = static_cast(pcDragger.get());
- dragger->translation.setValue(SbVec3f(pos.x,pos.y,pos.z));
- dragger->rotation.setValue(rot[0],rot[1],rot[2],rot[3]);
- }
- }
-}
-
-bool ViewProviderLink::callDraggerProxy(const char *fname, bool update) {
- if(!pcDragger)
+bool ViewProviderLink::callDraggerProxy(const char* fname) {
+ if (!csysDragger) {
return false;
+ }
+
Base::PyGILStateLocker lock;
try {
auto* proxy = getPropertyByName("Proxy");
@@ -2986,48 +2886,32 @@ bool ViewProviderLink::callDraggerProxy(const char *fname, bool update) {
return true;
}
- if(update) {
- auto ext = getLinkExtension();
- if(ext) {
- const auto &pla = currentDraggingPlacement();
- auto prop = ext->getLinkPlacementProperty();
- if(!prop)
- prop = ext->getPlacementProperty();
- if(prop) {
- auto plaNew = pla * Base::Placement(dragCtx->mat);
- if(prop->getValue()!=plaNew)
- prop->setValue(plaNew);
- }
- updateDraggingPlacement(pla);
- }
- }
return false;
}
void ViewProviderLink::dragStartCallback(void *data, SoDragger *) {
auto me = static_cast(data);
- me->dragCtx->initialPlacement = me->currentDraggingPlacement();
- if(!me->callDraggerProxy("onDragStart",false)) {
- me->dragCtx->cmdPending = true;
- me->getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Link Transform"));
- }else
- me->dragCtx->cmdPending = false;
+
+ me->dragCtx->initialPlacement = me->getDraggerPlacement();
+ me->callDraggerProxy("onDragStart");
}
void ViewProviderLink::dragFinishCallback(void *data, SoDragger *) {
auto me = static_cast(data);
- me->callDraggerProxy("onDragEnd",true);
- if(me->dragCtx->cmdPending) {
- if(me->currentDraggingPlacement() == me->dragCtx->initialPlacement)
+ me->callDraggerProxy("onDragEnd");
+
+ if (me->dragCtx->cmdPending) {
+ if (me->getDraggerPlacement() == me->dragCtx->initialPlacement) {
me->getDocument()->abortCommand();
- else
+ } else {
me->getDocument()->commitCommand();
+ }
}
}
void ViewProviderLink::dragMotionCallback(void *data, SoDragger *) {
auto me = static_cast(data);
- me->callDraggerProxy("onDragMotion",true);
+ me->callDraggerProxy("onDragMotion");
}
void ViewProviderLink::updateLinks(ViewProvider *vp) {
diff --git a/src/Gui/ViewProviderLink.h b/src/Gui/ViewProviderLink.h
index fc296f2561..79ead6d8cf 100644
--- a/src/Gui/ViewProviderLink.h
+++ b/src/Gui/ViewProviderLink.h
@@ -184,10 +184,10 @@ protected:
Py::Object PythonObject;
};
-class GuiExport ViewProviderLink : public ViewProviderDocumentObject
+class GuiExport ViewProviderLink : public ViewProviderDragger
{
PROPERTY_HEADER_WITH_OVERRIDE(Gui::ViewProviderLink);
- using inherited = ViewProviderDocumentObject;
+ using inherited = ViewProviderDragger;
public:
App::PropertyBool OverrideMaterial;
@@ -248,11 +248,6 @@ public:
static void updateLinks(ViewProvider *vp);
- void updateDraggingPlacement(const Base::Placement &pla, bool force=false);
- Base::Placement currentDraggingPlacement() const;
- void enableCenterballDragger(bool enable);
- bool isUsingCenterballDragger() const { return useCenterballDragger; }
-
std::map getElementColors(const char *subname=nullptr) const override;
void setElementColors(const std::map &colors) override;
@@ -311,7 +306,7 @@ protected:
ViewProvider *getLinkedView(bool real,const App::LinkBaseExtension *ext=nullptr) const;
bool initDraggingPlacement();
- bool callDraggerProxy(const char *fname, bool update);
+ bool callDraggerProxy(const char* fname);
private:
static void dragStartCallback(void * data, SoDragger * d);
@@ -323,7 +318,6 @@ protected:
LinkType linkType;
bool hasSubName;
bool hasSubElement;
- bool useCenterballDragger;
struct DraggerContext{
Base::Matrix4D preTransform;
@@ -333,7 +327,6 @@ protected:
bool cmdPending;
};
std::unique_ptr dragCtx;
- CoinPtr pcDragger;
ViewProviderDocumentObject *childVp;
LinkInfoPtr childVpLink;
mutable qint64 overlayCacheKey;
diff --git a/src/Gui/ViewProviderLinkPy.xml b/src/Gui/ViewProviderLinkPy.xml
index 88aaa0747f..09641c2cf8 100644
--- a/src/Gui/ViewProviderLinkPy.xml
+++ b/src/Gui/ViewProviderLinkPy.xml
@@ -19,12 +19,6 @@
-
-
- Get/set dragger type
-
-
-
Get the associated LinkView object
diff --git a/src/Gui/ViewProviderLinkPyImp.cpp b/src/Gui/ViewProviderLinkPyImp.cpp
index c1f3588e71..066c87f2e9 100644
--- a/src/Gui/ViewProviderLinkPyImp.cpp
+++ b/src/Gui/ViewProviderLinkPyImp.cpp
@@ -46,29 +46,16 @@ std::string ViewProviderLinkPy::representation() const
Py::Object ViewProviderLinkPy::getDraggingPlacement() const {
return Py::asObject(new Base::PlacementPy(new Base::Placement(
- getViewProviderLinkPtr()->currentDraggingPlacement())));
+ getViewProviderLinkPtr()->getDraggerPlacement())));
}
void ViewProviderLinkPy::setDraggingPlacement(Py::Object arg) {
if(!PyObject_TypeCheck(arg.ptr(),&Base::PlacementPy::Type))
throw Py::TypeError("expects a placement");
- getViewProviderLinkPtr()->updateDraggingPlacement(
+ getViewProviderLinkPtr()->setDraggerPlacement(
*static_cast(arg.ptr())->getPlacementPtr());
}
-Py::Boolean ViewProviderLinkPy::getUseCenterballDragger() const {
- return {getViewProviderLinkPtr()->isUsingCenterballDragger()};
-}
-
-void ViewProviderLinkPy::setUseCenterballDragger(Py::Boolean arg) {
- try {
- getViewProviderLinkPtr()->enableCenterballDragger(arg);
- }catch(const Base::Exception &e){
- e.setPyException();
- throw Py::Exception();
- }
-}
-
Py::Object ViewProviderLinkPy::getLinkView() const {
return Py::Object(getViewProviderLinkPtr()->getPyLinkView(),true);
}
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/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp
index f3800133e6..5009ce260b 100644
--- a/src/Mod/Part/App/Attacher.cpp
+++ b/src/Mod/Part/App/Attacher.cpp
@@ -69,6 +69,8 @@
#include "AttachExtension.h"
#include "Tools.h"
+#include
+
using namespace Part;
using namespace Attacher;
@@ -136,6 +138,7 @@ const char* AttachEngine::eMapModeStrings[]= {
"OYX",
"ParallelPlane",
+ "MidPoint",
nullptr};
@@ -388,8 +391,8 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const
result.message = SuggestResult::srLinkBroken;
result.bestFitMode = mmDeactivated;
- std::vector shapes;
- std::vector shapeStorage;
+ std::vector shapes;
+ std::vector shapeStorage;
std::vector typeStr;
try{
readLinks(getRefObjects(),subnames, shapes, shapeStorage, typeStr);
@@ -578,8 +581,8 @@ eRefType AttachEngine::getShapeType(const App::DocumentObject *obj, const std::s
//const_cast is worth here, to keep obj argument const. We are not going to write anything to obj through this temporary link.
tmpLink.setValue(const_cast(obj), subshape.c_str());
- std::vector shapes;
- std::vector copiedShapeStorage;
+ std::vector shapes;
+ std::vector copiedShapeStorage;
std::vector types;
readLinks(tmpLink.getValues(), tmpLink.getSubValues(), shapes, copiedShapeStorage, types);
@@ -740,13 +743,14 @@ eRefType AttachEngine::getRefTypeByName(const std::string& typeName)
throw AttachEngineException(errmsg.str());
}
-GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &shapes)
+GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &shapes)
{
//explode compounds
TopTools_HSequenceOfShape totalSeq;
- for (const TopoDS_Shape* pSh : shapes) {
+ for (auto tSh : shapes) {
+ auto pSh = tSh->getShape();
ShapeExtend_Explorer xp;
- totalSeq.Append( xp.SeqFromCompound(*pSh, /*recursive=*/true));
+ totalSeq.Append( xp.SeqFromCompound(pSh, /*recursive=*/true));
}
if (totalSeq.Length() == 0)
throw AttachEngineException("AttachEngine::getInertialPropsOfShape: no geometry provided");
@@ -820,75 +824,96 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector& objs,
const std::vector &subs,
- std::vector &shapes,
- std::vector &storage,
+ std::vector &shapes,
+ std::vector &storage,
std::vector &types)
{
storage.reserve(objs.size());
shapes.resize(objs.size());
types.resize(objs.size());
+
for (std::size_t i = 0; i < objs.size(); i++) {
- auto* geof = dynamic_cast(objs[i]);
+ auto geof = extractGeoFeature(objs[i]);
if (!geof) {
- // Accept App::Links to GeoFeatures
- geof = dynamic_cast(objs[i]->getLinkedObject());
- if (!geof) {
- FC_THROWM(AttachEngineException,
- "AttachEngine3D: attached to a non App::GeoFeature '" << objs[i]->getNameInDocument() << "'");
- }
- }
- TopoDS_Shape myShape;
-
- try {
- // getTopoShape support fully qualified subnames and should return shape with correct
- // global placement.
- Part::TopoShape shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true);
- for (;;) {
- if (shape.isNull()) {
- FC_THROWM(AttachEngineException,
- "AttachEngine3D: subshape not found "
- << objs[i]->getNameInDocument() << '.' << subs[i]);
- }
- if (shape.shapeType() != TopAbs_COMPOUND
- || shape.countSubShapes(TopAbs_SHAPE) != 1) {
- break;
- }
- // auto extract the single sub-shape from a compound
- shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
- }
-
- myShape = shape.getShape();
- }
- catch (Standard_Failure& e) {
FC_THROWM(AttachEngineException,
- "AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
- << '.' << subs[i] << std::endl
- << e.GetMessageString());
- }
- catch (Base::CADKernelError& e) {
- FC_THROWM(AttachEngineException,
- "AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
- << '.' << subs[i] << std::endl
- << e.what());
- }
- if (myShape.IsNull()) {
- FC_THROWM(AttachEngineException,
- "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
- << subs[i]);
+ "AttachEngine3D: attached to a non App::GeoFeature '"
+ << objs[i]->getNameInDocument() << "'");
}
- storage.emplace_back(myShape);
+ auto shape = extractSubShape(objs[i], subs[i]);
+ if (shape.isNull()) {
+ FC_THROWM(AttachEngineException,
+ "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
+ << subs[i]);
+ }
+
+ storage.emplace_back(shape);
shapes[i] = &(storage.back());
// FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be
// considered later, when the need arises.
- types[i] = getShapeType(*(shapes[i]));
+ types[i] = getShapeType(shapes[i]->getShape());
+
if (subs[i].length() == 0) {
types[i] = eRefType(types[i] | rtFlagHasPlacement);
}
}
}
+App::GeoFeature* AttachEngine::extractGeoFeature(App::DocumentObject *obj)
+{
+ if (auto geof = dynamic_cast(obj)) {
+ return geof;
+ }
+
+ auto linkedObject = obj->getLinkedObject();
+ if (auto linkedGeof = dynamic_cast(linkedObject)) {
+ return linkedGeof;
+ }
+
+ return nullptr;
+}
+
+TopoShape AttachEngine::extractSubShape(App::DocumentObject* obj, const std::string& subname)
+{
+ TopoShape shape;
+
+ try {
+ // getTopoShape support fully qualified subnames and should return shape with correct
+ // global placement.
+ shape = Feature::getTopoShape(obj, subname.c_str(), true);
+
+ for (;;) {
+ if (shape.isNull()) {
+ FC_THROWM(AttachEngineException,
+ "AttachEngine3D: subshape not found " << obj->getNameInDocument() << '.'
+ << subname);
+ }
+
+ if (shape.shapeType() != TopAbs_COMPOUND || shape.countSubShapes(TopAbs_SHAPE) != 1) {
+ break;
+ }
+
+ // auto extract the single sub-shape from a compound
+ shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
+ }
+ }
+ catch (Standard_Failure& e) {
+ FC_THROWM(AttachEngineException,
+ "AttachEngine3D: subshape not found " << obj->getNameInDocument() << '.'
+ << subname << std::endl
+ << e.GetMessageString());
+ }
+ catch (Base::CADKernelError& e) {
+ FC_THROWM(AttachEngineException,
+ "AttachEngine3D: subshape not found " << obj->getNameInDocument() << '.'
+ << subname << std::endl
+ << e.what());
+ }
+
+ return shape;
+}
+
void AttachEngine::throwWrongMode(eMapMode mmode)
{
std::stringstream errmsg;
@@ -1140,8 +1165,8 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector shapes;
- std::vector copiedShapeStorage;
+ std::vector shapes;
+ std::vector copiedShapeStorage;
std::vector types;
readLinks(objs, subs, shapes, copiedShapeStorage, types);
@@ -1172,7 +1197,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape();
if (sh.IsNull()) {
throw Base::ValueError(
"Null shape in AttachEngine3D::calculateAttachedPlacement()!");
@@ -1207,7 +1232,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector 0) {
- const TopoDS_Edge& e = TopoDS::Edge(*shapes[0]);
+ const TopoDS_Edge& e = TopoDS::Edge(shapes[0]->getShape());
BRepAdaptor_Curve adapt(e);
gp_Ax3 pos;
switch (adapt.GetType()) {
@@ -1259,7 +1284,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape());
}
catch (...) {
}
@@ -1330,7 +1355,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape());
}
catch (...) {
}
@@ -1387,14 +1412,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorShapeType() == TopAbs_VERTEX) {
+ if (shapes[0]->shapeType() == TopAbs_VERTEX) {
std::swap(shapes[0], shapes[1]);
bThruVertex = true;
}
TopoDS_Face face;
try {
- face = TopoDS::Face(*(shapes[0]));
+ face = TopoDS::Face(shapes[0]->getShape());
}
catch (...) {
}
@@ -1405,7 +1430,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape());
}
catch (...) {
}
@@ -1472,14 +1497,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorShapeType() == TopAbs_VERTEX && shapes.size() >= 2) {
+ if (shapes[0]->shapeType() == TopAbs_VERTEX && shapes.size() >= 2) {
std::swap(shapes[0], shapes[1]);
bThruVertex = true;
}
TopoDS_Edge path;
try {
- path = TopoDS::Edge(*(shapes[0]));
+ path = TopoDS::Edge(shapes[0]->getShape());
}
catch (...) {
}
@@ -1506,7 +1531,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector= 2) {
TopoDS_Vertex vertex;
try {
- vertex = TopoDS::Vertex(*(shapes[1]));
+ vertex = TopoDS::Vertex(shapes[1]->getShape());
}
catch (...) {
}
@@ -1633,7 +1658,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector points;
for (const auto & shape : shapes) {
- const TopoDS_Shape &sh = *shape;
+ const TopoDS_Shape &sh = shape->getShape();
if (sh.IsNull()) {
throw Base::ValueError(
"Null shape in AttachEngine3D::calculateAttachedPlacement()!");
@@ -1725,7 +1750,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape());
}
catch (...) {
}
@@ -1845,28 +1870,28 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorIsNull()) {
+ if (shapes[0]->isNull()) {
THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!")
}
- if (shapes[0]->ShapeType() != TopAbs_VERTEX) {
+ if (shapes[0]->shapeType() != TopAbs_VERTEX) {
THROWM(Base::TypeError,
"AttachEngine3D::calculateAttachedPlacement: first reference must be a "
"vertex, it's not")
}
- SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[0])));
+ SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(shapes[0]->getShape()));
// read out axes directions
for (size_t i = 1; i < 3 && i < shapes.size(); ++i) {
- if (shapes[i]->IsNull()) {
+ if (shapes[i]->isNull()) {
THROWM(Base::TypeError,
"AttachEngine3D::calculateAttachedPlacement: null shape!")
}
- if (shapes[i]->ShapeType() == TopAbs_VERTEX) {
- gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[i])));
+ if (shapes[i]->shapeType() == TopAbs_VERTEX) {
+ gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(shapes[i]->getShape()));
dirs[order[i - 1]] = gp_Vec(SketchBasePoint, p);
}
- else if (shapes[i]->ShapeType() == TopAbs_EDGE) {
- const TopoDS_Edge& e = TopoDS::Edge(*(shapes[i]));
+ else if (shapes[i]->shapeType() == TopAbs_EDGE) {
+ const TopoDS_Edge& e = TopoDS::Edge(shapes[i]->getShape());
BRepAdaptor_Curve crv(e);
double u1 = crv.FirstParameter();
double u2 = crv.LastParameter();
@@ -1896,6 +1921,90 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorattachmentOffset;
return plm;
} break;
+ case mmMidpoint: {
+ Base::Placement placement;
+
+ // special case for planes
+ if (auto plane = dynamic_cast(objs[0])) {
+ return plane->Placement.getValue() * attachmentOffset;
+ }
+
+ auto shape = shapes.front();
+ auto geom = Geometry::fromShape(shape->getShape());
+
+ switch (shape->shapeType()) {
+ case TopAbs_VERTEX: {
+ if (auto point = dynamic_cast(geom.get())) {
+ placement.setPosition(point->getPoint());
+ }
+ }
+ break;
+
+ case TopAbs_EDGE: {
+ if (auto conic = dynamic_cast(geom.get())) {
+ placement.setPosition(conic->getLocation());
+ placement.setRotation(conic->getRotation().value_or(Base::Rotation {}));
+ } else if (auto line = dynamic_cast(geom.get())) {
+ auto u1 = line->getFirstParameter();
+ auto u2 = line->getLastParameter();
+
+ auto middle = (u1 + u2) / 2;
+
+ placement.setPosition(line->pointAtParameter(middle));
+
+ Base::Vector3d direction;
+ if (!line->normalAt(middle, direction)) {
+ line->tangent(middle, direction);
+ }
+
+ placement.setRotation(Base::Rotation::fromNormalVector(direction));
+ }
+ }
+ break;
+
+ case TopAbs_FACE: {
+ auto surface = dynamic_cast(geom.get());
+
+ if (auto sphere = dynamic_cast(geom.get())) {
+ placement.setPosition(sphere->getLocation());
+ } else if (auto cone = dynamic_cast(geom.get())) {
+ placement.setPosition(cone->getApex());
+ } else if (auto com = shape->centerOfGravity()) {
+ placement.setPosition(*com);
+ } else {
+ placement.setPosition(shape->getBoundBox().GetCenter());
+ }
+
+ if (auto rotation = surface->getRotation()) {
+ placement.setRotation(*rotation);
+ } else {
+ auto adaptorSurface = BRepAdaptor_Surface(TopoDS::Face(shape->getShape()), true);
+
+ auto u1 = adaptorSurface.FirstUParameter();
+ auto u2 = adaptorSurface.LastUParameter();
+ auto v1 = adaptorSurface.FirstVParameter();
+ auto v2 = adaptorSurface.LastVParameter();
+
+ // calculate the normal at midpoint of the surface and use it as Z axis
+ gp_Dir dir;
+ surface->normal((u1 + u2) / 2, (v1 + v2) / 2, dir);
+
+ placement.setRotation(Base::Rotation::fromNormalVector(Base::convertTo(-dir)));
+ }
+ }
+ break;
+
+ default:
+ THROWM(Base::TypeError,
+ "AttachEngine3D::calculateAttachedPlacement: Unsupported shape type, "
+ "must be one of: Vertex, Edge, Face");
+ break;
+ }
+
+ return placement * attachmentOffset;
+
+ break;
+ }
default:
throwWrongMode(mmode);
} // switch (MapMode)
@@ -2083,8 +2192,8 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector shapes;
- std::vector copiedShapeStorage;
+ std::vector shapes;
+ std::vector copiedShapeStorage;
std::vector types;
readLinks(objs, subs, shapes, copiedShapeStorage, types);
@@ -2157,7 +2266,7 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector points;
for (const auto & shape : shapes) {
- const TopoDS_Shape &sh = *shape;
+ const TopoDS_Shape &sh = shape->getShape();
if (sh.IsNull()) {
throw Base::ValueError(
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
@@ -2197,13 +2306,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull()) {
+ if (shapes[0]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
}
TopoDS_Edge e;
try {
- e = TopoDS::Edge(*(shapes[0]));
+ e = TopoDS::Edge(shapes[0]->getShape());
}
catch (...) {
}
@@ -2228,13 +2337,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull()) {
+ if (shapes[0]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
}
TopoDS_Edge e;
try {
- e = TopoDS::Edge(*(shapes[0]));
+ e = TopoDS::Edge(shapes[0]->getShape());
}
catch (...) {
}
@@ -2283,13 +2392,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull() || shapes[1]->IsNull()) {
+ if (shapes[0]->isNull() || shapes[1]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
}
- const TopoDS_Face& face1 = TopoDS::Face(*(shapes[0]));
- const TopoDS_Face& face2 = TopoDS::Face(*(shapes[1]));
+ const TopoDS_Face& face1 = TopoDS::Face(shapes[0]->getShape());
+ const TopoDS_Face& face2 = TopoDS::Face(shapes[1]->getShape());
Handle(Geom_Surface) hSurf1 = BRep_Tool::Surface(face1);
Handle(Geom_Surface) hSurf2 = BRep_Tool::Surface(face2);
@@ -2324,15 +2433,15 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull()) {
+ if (shapes[0]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
}
- if (shapes[1]->IsNull()) {
+ if (shapes[1]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEngineLine::calculateAttachedPlacement()!");
}
- BRepExtrema_DistShapeShape distancer(*(shapes[0]), *(shapes[1]));
+ BRepExtrema_DistShapeShape distancer(shapes[0]->getShape(), shapes[1]->getShape());
if (!distancer.IsDone()) {
throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: "
"proximity calculation failed.");
@@ -2450,8 +2559,8 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector shapes;
- std::vector copiedShapeStorage;
+ std::vector shapes;
+ std::vector copiedShapeStorage;
std::vector types;
readLinks(objs, subs, shapes, copiedShapeStorage, types);
@@ -2470,7 +2579,7 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector points;
assert(!shapes.empty());
- const TopoDS_Shape& sh = *shapes[0];
+ const TopoDS_Shape& sh = shapes[0]->getShape();
if (sh.IsNull()) {
throw Base::ValueError(
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
@@ -2492,13 +2601,13 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vectorIsNull()) {
+ if (shapes[0]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
}
TopoDS_Edge e;
try {
- e = TopoDS::Edge(*(shapes[0]));
+ e = TopoDS::Edge(shapes[0]->getShape());
}
catch (...) {
}
@@ -2546,16 +2655,16 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vectorIsNull()) {
+ if (shapes[0]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
}
- if (shapes[1]->IsNull()) {
+ if (shapes[1]->isNull()) {
throw Base::ValueError(
"Null shape in AttachEnginePoint::calculateAttachedPlacement()!");
}
- BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1]));
+ BasePoint = getProximityPoint(mmode, shapes[0]->getShape(), shapes[1]->getShape());
} break;
case mm0CenterOfMass: {
GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes);
diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h
index dd0c8ba569..a990069f0b 100644
--- a/src/Mod/Part/App/Attacher.h
+++ b/src/Mod/Part/App/Attacher.h
@@ -108,6 +108,7 @@ enum eMapMode {
mmOYX,
mmParallelPlane,
+ mmMidpoint,
mmDummy_NumberOfModes//a value useful to check the validity of mode value
};//see also eMapModeStrings[] definition in .cpp
@@ -363,7 +364,7 @@ public://helper functions that may be useful outside of the class
static eRefType getRefTypeByName(const std::string &typeName);
- static GProp_GProps getInertialPropsOfShape(const std::vector &shapes);
+ static GProp_GProps getInertialPropsOfShape(const std::vector &shapes);
std::vector getRefObjects() const;
const std::vector &getSubValues() const {return subnames;}
@@ -430,11 +431,36 @@ protected:
}
static void readLinks(const std::vector &objs,
const std::vector &subs,
- std::vector& shapes, std::vector &storage,
+
+ std::vector& shapes,
+ std::vector &storage,
std::vector &types);
static void throwWrongMode(eMapMode mmode);
+ /**
+ * Extracts GeoFeature instance from given DocumentObject.
+ *
+ * In case of object itself being GeoFeature it returns itself, in other cases (like links)
+ * the method should return pointer to associated GeoFeature or nullptr if none is available.
+ *
+ * @param obj The document object to extract the GeoFeature.
+ *
+ * @return App::GeoFeature pointer associated with this document object
+ */
+ static App::GeoFeature* extractGeoFeature(App::DocumentObject* obj);
+
+ /**
+ * Tries to extract sub shape from document object with given subname.
+ *
+ * @param obj DocumentObject containing the sub shape
+ * @param subname Name of the sub shape to extract
+ *
+ * @return Extracted sub shape. Can be null.
+ *
+ * @throws AttachEngineException If given sub shape does not exist or is impossible to obtain.
+ */
+ static Part::TopoShape extractSubShape(App::DocumentObject* obj, const std::string& subname);
};
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/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp
index 9a47464e2b..f786c75c68 100644
--- a/src/Mod/Part/App/Geometry.cpp
+++ b/src/Mod/Part/App/Geometry.cpp
@@ -149,6 +149,8 @@
#include "ToroidPy.h"
#include "TopoShape.h"
+#include
+
#if OCC_VERSION_HEX >= 0x070600
using GeomAdaptor_HCurve = GeomAdaptor_Curve;
@@ -2127,10 +2129,25 @@ void GeomConic::setLocation(const Base::Vector3d& Center)
Base::Vector3d GeomConic::getCenter() const
{
- Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(handle());
+ Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(handle());
gp_Ax1 axis = conic->Axis();
const gp_Pnt& loc = axis.Location();
- return Base::Vector3d(loc.X(),loc.Y(),loc.Z());
+ return Base::Vector3d(loc.X(), loc.Y(), loc.Z());
+}
+
+std::optional GeomConic::getRotation() const
+{
+ Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(handle());
+
+ if (!conic) {
+ return {};
+ }
+
+ gp_Trsf trsf;
+ trsf.SetTransformation(conic->Position(), gp_Ax3());
+
+ auto q = trsf.GetRotation();
+ return Base::Rotation(q.X(), q.Y(), q.Z(), q.W());
}
void GeomConic::setCenter(const Base::Vector3d& Center)
@@ -2142,7 +2159,6 @@ void GeomConic::setCenter(const Base::Vector3d& Center)
conic->SetLocation(p1);
}
catch (Standard_Failure& e) {
-
THROWM(Base::CADKernelError,e.GetMessageString())
}
}
@@ -4803,28 +4819,36 @@ GeomPlane* GeomSurface::toPlane(bool clone, double tol) const
if (isDerivedFrom(GeomPlane::getClassTypeId())) {
if (clone) {
return dynamic_cast(this->clone());
- } else {
+ }
+ else {
return dynamic_cast(this->copy());
}
}
gp_Pln pln;
- if (!isPlanar(&pln, tol))
+ if (!isPlanar(&pln, tol)) {
return nullptr;
+ }
auto res = new GeomPlane(pln);
res->copyNonTag(this);
- if (clone)
+ if (clone) {
res->tag = this->tag;
+ }
return res;
}
+std::optional GeomSurface::getRotation() const
+{
+ return {};
+}
+
TopoDS_Shape GeomSurface::toShape() const
{
Handle(Geom_Surface) s = Handle(Geom_Surface)::DownCast(handle());
- Standard_Real u1,u2,v1,v2;
- s->Bounds(u1,u2,v1,v2);
- BRepBuilderAPI_MakeFace mkBuilder(s, u1, u2, v1, v2, Precision::Confusion() );
+ Standard_Real u1, u2, v1, v2;
+ s->Bounds(u1, u2, v1, v2);
+ BRepBuilderAPI_MakeFace mkBuilder(s, u1, u2, v1, v2, Precision::Confusion());
return mkBuilder.Shape();
}
@@ -5180,9 +5204,24 @@ GeomElementarySurface::~GeomElementarySurface()
Base::Vector3d GeomElementarySurface::getLocation(void) const
{
- Handle(Geom_ElementarySurface) surf = Handle(Geom_ElementarySurface)::DownCast(handle());
+ Handle(Geom_ElementarySurface) surf = Handle(Geom_ElementarySurface)::DownCast(handle());
gp_Pnt loc = surf->Location();
- return Base::Vector3d(loc.X(),loc.Y(),loc.Z());
+ return Base::Vector3d(loc.X(), loc.Y(), loc.Z());
+}
+
+std::optional GeomPlane::getRotation() const
+{
+ Handle(Geom_ElementarySurface) s = Handle(Geom_ElementarySurface)::DownCast(handle());
+
+ if (!s) {
+ return {};
+ }
+
+ gp_Trsf trsf;
+ trsf.SetTransformation(s->Position().Ax2(),gp_Ax3());
+ auto q = trsf.GetRotation();
+
+ return Base::Rotation(q.X(),q.Y(),q.Z(),q.W());
}
Base::Vector3d GeomElementarySurface::getDir(void) const
@@ -5411,6 +5450,12 @@ double GeomCone::getSemiAngle() const
return mySurface->SemiAngle();
}
+Base::Vector3d GeomCone::getApex() const
+{
+ Handle(Geom_ConicalSurface) s = Handle(Geom_ConicalSurface)::DownCast(handle());
+ return Base::convertTo(s->Apex());
+}
+
bool GeomCone::isSame(const Geometry &_other, double tol, double atol) const
{
if(_other.getTypeId() != getTypeId())
diff --git a/src/Mod/Part/App/Geometry.h b/src/Mod/Part/App/Geometry.h
index 4af1809128..376b16b99a 100644
--- a/src/Mod/Part/App/Geometry.h
+++ b/src/Mod/Part/App/Geometry.h
@@ -58,6 +58,7 @@
#include
#include
#include
+#include
#include