Merge pull request #17564 from kadet1090/new-transform-dialog
Gui: New transform dialog
This commit is contained in:
@@ -63,6 +63,7 @@
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Base/BoundBoxPy.h>
|
||||
#include <Base/ConsoleObserver.h>
|
||||
#include <Base/ServiceProvider.h>
|
||||
#include <Base/CoordinateSystemPy.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/ExceptionFactory.h>
|
||||
@@ -89,6 +90,7 @@
|
||||
#include "Application.h"
|
||||
#include "CleanupProcess.h"
|
||||
#include "ComplexGeoData.h"
|
||||
#include "Services.h"
|
||||
#include "DocumentObjectFileIncluded.h"
|
||||
#include "DocumentObjectGroup.h"
|
||||
#include "DocumentObjectGroupPy.h"
|
||||
@@ -2217,6 +2219,8 @@ void Application::initTypes()
|
||||
new Base::ExceptionProducer<Base::CADKernelError>;
|
||||
new Base::ExceptionProducer<Base::RestoreError>;
|
||||
new Base::ExceptionProducer<Base::PropertyError>;
|
||||
|
||||
Base::registerServiceImplementation<CenterOfMassProvider>(new NullCenterOfMass);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -184,6 +184,17 @@ bool ComplexGeoData::getCenterOfGravity(Base::Vector3d& unused) const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<Base::Vector3d> ComplexGeoData::centerOfGravity() const
|
||||
{
|
||||
Base::Vector3d centerOfGravity;
|
||||
|
||||
if (getCenterOfGravity(centerOfGravity)) {
|
||||
return centerOfGravity;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string& ComplexGeoData::elementMapPrefix()
|
||||
{
|
||||
static std::string prefix(ELEMENT_MAP_PREFIX);
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#define APP_COMPLEX_GEO_DATA_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include <Base/Handle.h>
|
||||
#include <Base/Matrix.h>
|
||||
#include <Base/Persistence.h>
|
||||
@@ -200,6 +202,7 @@ public:
|
||||
* The default implementation only returns false.
|
||||
*/
|
||||
virtual bool getCenterOfGravity(Base::Vector3d& center) const;
|
||||
virtual std::optional<Base::Vector3d> centerOfGravity() const;
|
||||
//@}
|
||||
|
||||
static const std::string& elementMapPrefix();
|
||||
|
||||
@@ -61,12 +61,7 @@ void GeoFeature::transformPlacement(const Base::Placement& transform)
|
||||
|
||||
Base::Placement GeoFeature::globalPlacement() const
|
||||
{
|
||||
auto* group = GeoFeatureGroupExtension::getGroupOfObject(this);
|
||||
if (group) {
|
||||
auto ext = group->getExtensionByType<GeoFeatureGroupExtension>();
|
||||
return ext->globalGroupPlacement() * Placement.getValue();
|
||||
}
|
||||
return Placement.getValue();
|
||||
return GeoFeature::getGlobalPlacement(this);
|
||||
}
|
||||
|
||||
const PropertyComplexGeoData* GeoFeature::getPropertyOfGeometry() const
|
||||
@@ -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<App::PropertyPlacement>("Placement");
|
||||
|
||||
if (!placementProperty) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* group = GeoFeatureGroupExtension::getGroupOfObject(obj);
|
||||
if (group) {
|
||||
auto ext = group->getExtensionByType<GeoFeatureGroupExtension>();
|
||||
return ext->globalGroupPlacement() * placementProperty->getValue();
|
||||
}
|
||||
|
||||
return placementProperty->getValue();
|
||||
}
|
||||
|
||||
@@ -195,6 +195,7 @@ public:
|
||||
static Base::Placement
|
||||
getGlobalPlacement(DocumentObject* targetObj, DocumentObject* rootObj, const std::string& sub);
|
||||
static Base::Placement getGlobalPlacement(DocumentObject* targetObj, PropertyXLinkSub* prop);
|
||||
static Base::Placement getGlobalPlacement(const DocumentObject* obj);
|
||||
|
||||
protected:
|
||||
void onChanged(const Property* prop) override;
|
||||
|
||||
30
src/App/Services.cpp
Normal file
30
src/App/Services.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "Services.h"
|
||||
|
||||
std::optional<Base::Vector3d>
|
||||
App::NullCenterOfMass::ofDocumentObject([[maybe_unused]] DocumentObject* object) const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
74
src/App/Services.h
Normal file
74
src/App/Services.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef APP_SERVICES_H
|
||||
#define APP_SERVICES_H
|
||||
|
||||
#include "DocumentObject.h"
|
||||
|
||||
#include <optional>
|
||||
#include <Base/Placement.h>
|
||||
|
||||
namespace App
|
||||
{
|
||||
|
||||
/**
|
||||
* This service should provide placement of given sub object (like for example face).
|
||||
* This feature is not implemented in the core and so it must be provided by module.
|
||||
*/
|
||||
class SubObjectPlacementProvider
|
||||
{
|
||||
public:
|
||||
virtual ~SubObjectPlacementProvider() = default;
|
||||
|
||||
/**
|
||||
* Returns placement of sub object relative to the base placement.
|
||||
*/
|
||||
virtual Base::Placement calculate(SubObjectT object, Base::Placement basePlacement) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This service should provide center of mass calculation;
|
||||
*/
|
||||
class CenterOfMassProvider
|
||||
{
|
||||
public:
|
||||
virtual ~CenterOfMassProvider() = default;
|
||||
|
||||
virtual std::optional<Base::Vector3d> ofDocumentObject(DocumentObject* object) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default implementation for the center of mass contract
|
||||
* It always returns empty optional
|
||||
*/
|
||||
class NullCenterOfMass final : public CenterOfMassProvider
|
||||
{
|
||||
public:
|
||||
std::optional<Base::Vector3d> ofDocumentObject(DocumentObject* object) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // APP_SERVICES_H
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
|
||||
#include "ServiceProvider.h"
|
||||
|
||||
Base::ServiceProvider& Base::ServiceProvider::get()
|
||||
namespace Base
|
||||
{
|
||||
static Base::ServiceProvider instance;
|
||||
return instance;
|
||||
Base::ServiceProvider globalServiceProvider;
|
||||
}
|
||||
|
||||
@@ -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<App::SubObjectPlacementProvider>(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<App::SubObjectPlacementProvider>();
|
||||
* @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<typename T>
|
||||
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<const char*, std::deque<ServiceDescriptor>> _implementations;
|
||||
std::map<std::string, std::deque<ServiceDescriptor>> _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<typename T>
|
||||
T* provideImplementation()
|
||||
T* provideService()
|
||||
{
|
||||
return ServiceProvider::get().provide<T>();
|
||||
return globalServiceProvider.provide<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<typename T>
|
||||
std::list<T*> provideAllImplementations()
|
||||
std::list<T*> provideServiceImplementations()
|
||||
{
|
||||
return ServiceProvider::get().all<T>();
|
||||
return globalServiceProvider.all<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers implementation of service in the global service provider.
|
||||
*
|
||||
* @tparam T Service kind to obtain.
|
||||
* @return List of available service implementation.
|
||||
*/
|
||||
template<typename T>
|
||||
void implementContract(T* implementation)
|
||||
void registerServiceImplementation(T* implementation)
|
||||
{
|
||||
ServiceProvider::get().implement<T>(implementation);
|
||||
globalServiceProvider.registerImplementation<T>(implementation);
|
||||
}
|
||||
|
||||
} // namespace Base
|
||||
|
||||
@@ -402,6 +402,7 @@ SET(Gui_UIC_SRCS
|
||||
SceneInspector.ui
|
||||
InputVector.ui
|
||||
Placement.ui
|
||||
TaskCSysDragger.ui
|
||||
TextureMapping.ui
|
||||
TaskView/TaskAppearance.ui
|
||||
TaskView/TaskOrientation.ui
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -27,10 +27,8 @@
|
||||
#include <Inventor/SbRotation.h>
|
||||
#include <Inventor/actions/SoGLRenderAction.h>
|
||||
#include <Inventor/engines/SoComposeVec3f.h>
|
||||
#include <Inventor/elements/SoLazyElement.h>
|
||||
#include <Inventor/nodes/SoLightModel.h>
|
||||
#include <Inventor/nodes/SoDrawStyle.h>
|
||||
#include <Inventor/nodes/SoAnnotation.h>
|
||||
#include <Inventor/nodes/SoBaseColor.h>
|
||||
#include <Inventor/nodes/SoCone.h>
|
||||
#include <Inventor/nodes/SoCoordinate3.h>
|
||||
@@ -46,6 +44,9 @@
|
||||
#include <Inventor/nodes/SoSphere.h>
|
||||
#include <Inventor/nodes/SoSwitch.h>
|
||||
#include <Inventor/nodes/SoTranslation.h>
|
||||
#include <Inventor/nodes/SoText2.h>
|
||||
#include <Inventor/nodes/SoAnnotation.h>
|
||||
#include <Inventor/nodes/SoFontStyle.h>
|
||||
#endif
|
||||
|
||||
#include <Base/Quantity.h>
|
||||
@@ -56,6 +57,8 @@
|
||||
#include "MainWindow.h"
|
||||
#include "SoFCDB.h"
|
||||
|
||||
#include <SoTextLabel.h>
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
@@ -29,10 +29,12 @@
|
||||
#include <Inventor/fields/SoSFFloat.h>
|
||||
#include <Inventor/fields/SoSFInt32.h>
|
||||
#include <Inventor/fields/SoSFRotation.h>
|
||||
#include <Inventor/fields/SoSFString.h>
|
||||
#include <Inventor/projectors/SbLineProjector.h>
|
||||
#include <Inventor/projectors/SbPlaneProjector.h>
|
||||
#include <Inventor/sensors/SoFieldSensor.h>
|
||||
#include <Inventor/sensors/SoIdleSensor.h>
|
||||
#include <Inventor/nodes/SoBaseColor.h>
|
||||
#include <FCGlobal.h>
|
||||
|
||||
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.
|
||||
|
||||
@@ -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; i<num; i++) {
|
||||
@@ -432,7 +436,7 @@ void SoFrameLabel::drawImage()
|
||||
}
|
||||
|
||||
int padding = 5;
|
||||
|
||||
|
||||
bool drawIcon = false;
|
||||
QImage iconImg;
|
||||
int widthIcon = 0;
|
||||
@@ -457,18 +461,17 @@ void SoFrameLabel::drawImage()
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
SbBool drawFrame = frame.getValue();
|
||||
if (drawFrame) {
|
||||
painter.setPen(QPen(QColor(0,0,127), 2, Qt::SolidLine, Qt::RoundCap,
|
||||
Qt::RoundJoin));
|
||||
painter.setBrush(QBrush(brush, Qt::SolidPattern));
|
||||
SbBool drawBorder = border.getValue();
|
||||
if (drawFrame || drawBorder) {
|
||||
painter.setPen(drawBorder ? borderPen : QPen(Qt::transparent));
|
||||
painter.setBrush(QBrush(drawFrame ? backgroundBrush : Qt::transparent));
|
||||
|
||||
QRectF rectangle(0.0, 0.0, widthTotal, heightTotal);
|
||||
painter.drawRoundedRect(rectangle, 5, 5);
|
||||
}
|
||||
|
||||
|
||||
if (drawIcon) {
|
||||
painter.drawImage(QPoint(padding, paddingIconV), iconImg);
|
||||
|
||||
}
|
||||
|
||||
painter.setPen(front);
|
||||
@@ -495,6 +498,16 @@ void SoFrameLabel::drawImage()
|
||||
*/
|
||||
void SoFrameLabel::GLRender(SoGLRenderAction *action)
|
||||
{
|
||||
|
||||
if (backgroundUseBaseColor.getValue()) {
|
||||
SoState* state = action->getState();
|
||||
const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0);
|
||||
|
||||
if (diffuse != this->backgroundColor.getValue()) {
|
||||
this->backgroundColor.setValue(diffuse);
|
||||
}
|
||||
}
|
||||
|
||||
inherited::GLRender(action);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ public:
|
||||
SoSFName name;
|
||||
SoSFInt32 size;
|
||||
SoSFBool frame;
|
||||
SoSFBool border;
|
||||
SoSFBool backgroundUseBaseColor;
|
||||
//SoSFImage image;
|
||||
QPixmap iconPixmap;
|
||||
|
||||
|
||||
@@ -25,12 +25,19 @@
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <QApplication>
|
||||
#include <QGridLayout>
|
||||
#endif
|
||||
|
||||
#include <View3DInventorViewer.h>
|
||||
#include <Utilities.h>
|
||||
|
||||
#include <App/Document.h>
|
||||
#include "Document.h" // must be before TaskCSysDragger.h
|
||||
#include "TaskCSysDragger.h"
|
||||
#include <App/GeoFeature.h>
|
||||
#include <App/Services.h>
|
||||
#include <Base/Precision.h>
|
||||
#include <Base/ServiceProvider.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include "Document.h" // must be before TaskCSysDragger.h
|
||||
#include "Application.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "Command.h"
|
||||
@@ -39,139 +46,717 @@
|
||||
#include "ViewProviderDragger.h"
|
||||
#include "TaskView/TaskView.h"
|
||||
|
||||
#include "TaskCSysDragger.h"
|
||||
#include "ui_TaskCSysDragger.h"
|
||||
|
||||
#include <Inventor/nodes/SoPickStyle.h>
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
|
||||
static double degreesToRadians(const double °reesIn)
|
||||
namespace
|
||||
{
|
||||
return degreesIn * (M_PI / 180.0);
|
||||
}
|
||||
|
||||
|
||||
TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDocumentObject* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) :
|
||||
dragger(draggerIn)
|
||||
void alignGridLayoutColumns(const std::list<QGridLayout*>& layouts, unsigned column = 0)
|
||||
{
|
||||
assert(vpObjectIn);
|
||||
assert(draggerIn);
|
||||
vpObject = vpObjectIn->getObject();
|
||||
dragger->ref();
|
||||
std::vector<int> widths;
|
||||
|
||||
setupGui();
|
||||
}
|
||||
auto getActualWidth = [&](const QGridLayout* layout) -> int {
|
||||
if (auto const item = layout->itemAtPosition(0, column)) {
|
||||
return item->geometry().width();
|
||||
}
|
||||
|
||||
TaskCSysDragger::~TaskCSysDragger()
|
||||
{
|
||||
dragger->unref();
|
||||
Gui::Application::Instance->commandManager().getCommandByName("Std_OrthographicCamera")->setEnabled(true);
|
||||
Gui::Application::Instance->commandManager().getCommandByName("Std_PerspectiveCamera")->setEnabled(true);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
void TaskCSysDragger::dragStartCallback(void *, SoDragger *)
|
||||
{
|
||||
// This is called when a manipulator is about to manipulating
|
||||
if(firstDrag)
|
||||
{
|
||||
Gui::Application::Instance->activeDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Transform"));
|
||||
firstDrag=false;
|
||||
for (const auto layout : layouts) {
|
||||
widths.push_back(getActualWidth(layout));
|
||||
}
|
||||
|
||||
const auto maxWidth = *std::max_element(widths.begin(), widths.end());
|
||||
for (const auto layout : layouts) {
|
||||
layout->setColumnMinimumWidth(column, maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskCSysDragger::setupGui()
|
||||
} // namespace
|
||||
|
||||
TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp,
|
||||
Gui::SoFCCSysDragger* dragger,
|
||||
QWidget* parent,
|
||||
App::SubObjectPlacementProvider* subObjectPlacemenProvider,
|
||||
App::CenterOfMassProvider* centerOfMassProvider)
|
||||
: TaskBox(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"), tr("Transform"), false, parent)
|
||||
, vp(vp)
|
||||
, subObjectPlacementProvider(subObjectPlacemenProvider)
|
||||
, centerOfMassProvider(centerOfMassProvider)
|
||||
, dragger(dragger)
|
||||
, ui(new Ui_TaskCSysDragger)
|
||||
{
|
||||
auto incrementsBox = new Gui::TaskView::TaskBox(
|
||||
Gui::BitmapFactory().pixmap("Std_TransformManip"),
|
||||
tr("Transform"), true, nullptr);
|
||||
blockSelection(true);
|
||||
|
||||
auto gridLayout = new QGridLayout();
|
||||
gridLayout->setColumnStretch(1, 1);
|
||||
dragger->addStartCallback(dragStartCallback, this);
|
||||
dragger->addMotionCallback(dragMotionCallback, this);
|
||||
|
||||
auto tLabel = new QLabel(tr("Translation Increment:"), incrementsBox);
|
||||
gridLayout->addWidget(tLabel, 0, 0, Qt::AlignRight);
|
||||
vp->resetTransformOrigin();
|
||||
|
||||
QFontMetrics metrics(QApplication::font());
|
||||
int spinBoxWidth = metrics.averageCharWidth() * 20;
|
||||
tSpinBox = new QuantitySpinBox(incrementsBox);
|
||||
tSpinBox->setMinimum(0.0);
|
||||
tSpinBox->setMaximum(std::numeric_limits<double>::max());
|
||||
tSpinBox->setUnit(Base::Unit::Length);
|
||||
tSpinBox->setMinimumWidth(spinBoxWidth);
|
||||
gridLayout->addWidget(tSpinBox, 0, 1, Qt::AlignLeft);
|
||||
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<double>(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onTIncrementSlot);
|
||||
connect(rSpinBox, qOverload<double>(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onRIncrementSlot);
|
||||
setupGui();
|
||||
}
|
||||
|
||||
void TaskCSysDragger::onTIncrementSlot(double freshValue)
|
||||
TaskTransform::~TaskTransform()
|
||||
{
|
||||
dragger->translationIncrement.setValue(freshValue);
|
||||
Gui::Application::Instance->commandManager()
|
||||
.getCommandByName("Std_OrthographicCamera")
|
||||
->setEnabled(true);
|
||||
|
||||
Gui::Application::Instance->commandManager()
|
||||
.getCommandByName("Std_PerspectiveCamera")
|
||||
->setEnabled(true);
|
||||
|
||||
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<TaskTransform*>(data);
|
||||
|
||||
const auto currentRotation = task->vp->getOriginalDraggerPlacement().getRotation();
|
||||
const auto updatedRotation = task->vp->getDraggerPlacement().getRotation();
|
||||
|
||||
const auto rotationAxisHasChanged = [task](auto first, auto second) {
|
||||
double alpha, beta, gamma;
|
||||
|
||||
(first.inverse() * second).getEulerAngles(task->eulerSequence(), alpha, beta, gamma);
|
||||
|
||||
auto angles = {alpha, beta, gamma};
|
||||
const int changed = std::count_if(angles.begin(), angles.end(), [](double angle) {
|
||||
return std::fabs(angle) > tolerance;
|
||||
});
|
||||
|
||||
// if representation of both differs by more than one axis the axis of rotation must be
|
||||
// different
|
||||
return changed > 1;
|
||||
};
|
||||
|
||||
if (!updatedRotation.isSame(currentRotation, tolerance)) {
|
||||
task->resetReferencePlacement();
|
||||
|
||||
if (rotationAxisHasChanged(task->referenceRotation, updatedRotation)) {
|
||||
task->referenceRotation = currentRotation;
|
||||
}
|
||||
}
|
||||
|
||||
task->updatePositionAndRotationUi();
|
||||
}
|
||||
|
||||
void TaskTransform::loadPlacementModeItems() const
|
||||
{
|
||||
ui->placementComboBox->clear();
|
||||
|
||||
ui->placementComboBox->addItem(tr("Object origin"),
|
||||
QVariant::fromValue(PlacementMode::ObjectOrigin));
|
||||
|
||||
if (centerOfMassProvider->ofDocumentObject(vp->getObject()).has_value()) {
|
||||
ui->placementComboBox->addItem(tr("Center of mass / Centroid"),
|
||||
QVariant::fromValue(PlacementMode::Centroid));
|
||||
}
|
||||
|
||||
if (subObjectPlacementProvider) {
|
||||
ui->placementComboBox->addItem(tr("Custom"), QVariant::fromValue(PlacementMode::Custom));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskTransform::loadPositionModeItems() const
|
||||
{
|
||||
ui->positionModeComboBox->clear();
|
||||
ui->positionModeComboBox->addItem(tr("Local"), QVariant::fromValue(PositionMode::Local));
|
||||
ui->positionModeComboBox->addItem(tr("Global"), QVariant::fromValue(PositionMode::Global));
|
||||
}
|
||||
|
||||
void TaskTransform::setupGui()
|
||||
{
|
||||
auto proxy = new QWidget(this);
|
||||
ui->setupUi(proxy);
|
||||
this->groupLayout()->addWidget(proxy);
|
||||
|
||||
loadPlacementModeItems();
|
||||
loadPositionModeItems();
|
||||
|
||||
ui->referencePickerWidget->hide();
|
||||
ui->alignRotationCheckBox->hide();
|
||||
|
||||
for (auto positionSpinBox : {ui->translationIncrementSpinBox,
|
||||
ui->xPositionSpinBox,
|
||||
ui->yPositionSpinBox,
|
||||
ui->zPositionSpinBox}) {
|
||||
positionSpinBox->setUnit(Base::Unit::Length);
|
||||
}
|
||||
|
||||
for (auto rotationSpinBox : {ui->rotationIncrementSpinBox,
|
||||
ui->xRotationSpinBox,
|
||||
ui->yRotationSpinBox,
|
||||
ui->zRotationSpinBox}) {
|
||||
rotationSpinBox->setUnit(Base::Unit::Angle);
|
||||
}
|
||||
|
||||
connect(ui->translationIncrementSpinBox,
|
||||
qOverload<double>(&QuantitySpinBox::valueChanged),
|
||||
this,
|
||||
[this](double) {
|
||||
updateIncrements();
|
||||
});
|
||||
connect(ui->rotationIncrementSpinBox,
|
||||
qOverload<double>(&QuantitySpinBox::valueChanged),
|
||||
this,
|
||||
[this](double) {
|
||||
updateIncrements();
|
||||
});
|
||||
connect(ui->positionModeComboBox,
|
||||
qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this,
|
||||
&TaskTransform::onCoordinateSystemChange);
|
||||
connect(ui->placementComboBox,
|
||||
qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this,
|
||||
&TaskTransform::onPlacementModeChange);
|
||||
connect(ui->pickTransformOriginButton,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&TaskTransform::onPickTransformOrigin);
|
||||
connect(ui->alignToOtherObjectButton,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&TaskTransform::onAlignToOtherObject);
|
||||
connect(ui->flipPartButton, &QPushButton::clicked, this, &TaskTransform::onFlip);
|
||||
|
||||
connect(ui->alignRotationCheckBox,
|
||||
&QCheckBox::clicked,
|
||||
this,
|
||||
&TaskTransform::onAlignRotationChanged);
|
||||
|
||||
for (auto positionSpinBox :
|
||||
{ui->xPositionSpinBox, ui->yPositionSpinBox, ui->zPositionSpinBox}) {
|
||||
connect(positionSpinBox,
|
||||
qOverload<double>(&QuantitySpinBox::valueChanged),
|
||||
this,
|
||||
[this](double) {
|
||||
onPositionChange();
|
||||
});
|
||||
}
|
||||
|
||||
for (auto rotationSpinBox :
|
||||
{ui->xRotationSpinBox, ui->yRotationSpinBox, ui->zRotationSpinBox}) {
|
||||
connect(rotationSpinBox,
|
||||
qOverload<double>(&QuantitySpinBox::valueChanged),
|
||||
this,
|
||||
[=](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<PlacementMode>();
|
||||
|
||||
updateTransformOrigin();
|
||||
}
|
||||
|
||||
void TaskTransform::updateTransformOrigin()
|
||||
{
|
||||
auto getTransformOrigin = [this](const PlacementMode& mode) -> Base::Placement {
|
||||
switch (mode) {
|
||||
case PlacementMode::ObjectOrigin:
|
||||
return {};
|
||||
case PlacementMode::Centroid:
|
||||
if (const auto com = centerOfMassProvider->ofDocumentObject(vp->getObject())) {
|
||||
return {*com, {}};
|
||||
}
|
||||
return {};
|
||||
case PlacementMode::Custom:
|
||||
return customTransformOrigin.value_or(Base::Placement {});
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
ui->referencePickerWidget->setVisible(placementMode == PlacementMode::Custom);
|
||||
|
||||
if (placementMode == PlacementMode::Custom && !customTransformOrigin.has_value()) {
|
||||
setSelectionMode(SelectionMode::SelectTransformOrigin);
|
||||
return;
|
||||
}
|
||||
|
||||
auto transformOrigin = getTransformOrigin(placementMode);
|
||||
if (isDraggerAlignedToCoordinateSystem()) {
|
||||
transformOrigin.setRotation(
|
||||
(vp->getObjectPlacement().inverse() * globalCoordinateSystem().origin).getRotation());
|
||||
}
|
||||
|
||||
vp->setTransformOrigin(transformOrigin);
|
||||
|
||||
resetReferencePlacement();
|
||||
resetReferenceRotation();
|
||||
|
||||
updatePositionAndRotationUi();
|
||||
updateDraggerLabels();
|
||||
}
|
||||
|
||||
void TaskTransform::updateSpinBoxesReadOnlyStatus() const
|
||||
{
|
||||
const bool isReadOnly = selectionMode != SelectionMode::None;
|
||||
|
||||
const auto controls = {
|
||||
ui->xPositionSpinBox,
|
||||
ui->yPositionSpinBox,
|
||||
ui->zPositionSpinBox,
|
||||
ui->xRotationSpinBox,
|
||||
ui->yRotationSpinBox,
|
||||
ui->zRotationSpinBox,
|
||||
};
|
||||
|
||||
for (const auto& control : controls) {
|
||||
control->setReadOnly(isReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskTransform::resetReferencePlacement()
|
||||
{
|
||||
referencePlacement = vp->getDraggerPlacement();
|
||||
}
|
||||
|
||||
void TaskTransform::resetReferenceRotation()
|
||||
{
|
||||
referenceRotation = vp->getDraggerPlacement().getRotation();
|
||||
}
|
||||
|
||||
bool TaskTransform::isDraggerAlignedToCoordinateSystem() const
|
||||
{
|
||||
return positionMode == PositionMode::Global && ui->alignRotationCheckBox->isChecked();
|
||||
}
|
||||
|
||||
void TaskTransform::onTransformOriginReset()
|
||||
{
|
||||
vp->resetTransformOrigin();
|
||||
}
|
||||
|
||||
void TaskTransform::onCoordinateSystemChange([[maybe_unused]] int mode)
|
||||
{
|
||||
positionMode = ui->positionModeComboBox->currentData().value<PositionMode>();
|
||||
|
||||
ui->alignRotationCheckBox->setVisible(positionMode != PositionMode::Local);
|
||||
|
||||
updateInputLabels();
|
||||
updatePositionAndRotationUi();
|
||||
updateTransformOrigin();
|
||||
}
|
||||
|
||||
void TaskTransform::onPositionChange()
|
||||
{
|
||||
const auto uvwPosition = Base::Vector3d(ui->xPositionSpinBox->rawValue(),
|
||||
ui->yPositionSpinBox->rawValue(),
|
||||
ui->zPositionSpinBox->rawValue());
|
||||
|
||||
const auto xyzPosition = currentCoordinateSystem().origin.getPosition()
|
||||
+ currentCoordinateSystem().origin.getRotation().multVec(uvwPosition);
|
||||
|
||||
const auto placement = vp->getDraggerPlacement();
|
||||
|
||||
vp->setDraggerPlacement({xyzPosition, placement.getRotation()});
|
||||
|
||||
vp->updateTransformFromDragger();
|
||||
vp->updatePlacementFromDragger();
|
||||
}
|
||||
|
||||
void TaskTransform::onRotationChange(QuantitySpinBox* changed)
|
||||
{
|
||||
if (positionMode == PositionMode::Local) {
|
||||
for (auto rotationSpinBox : {ui->xRotationSpinBox,
|
||||
ui->yRotationSpinBox,
|
||||
ui->zRotationSpinBox}) {
|
||||
QSignalBlocker blocker(rotationSpinBox);
|
||||
|
||||
// if any other spinbox contains non-zero value we need to reset rotation reference first
|
||||
if (std::fabs(rotationSpinBox->rawValue()) > tolerance && rotationSpinBox != changed) {
|
||||
resetReferenceRotation();
|
||||
rotationSpinBox->setValue(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto uvwRotation = Base::Rotation::fromEulerAngles(eulerSequence(),
|
||||
ui->xRotationSpinBox->rawValue(),
|
||||
ui->yRotationSpinBox->rawValue(),
|
||||
ui->zRotationSpinBox->rawValue());
|
||||
|
||||
auto referenceRotation = positionMode == PositionMode::Local
|
||||
? this->referenceRotation
|
||||
: currentCoordinateSystem().origin.getRotation();
|
||||
|
||||
const auto xyzRotation = referenceRotation * uvwRotation;
|
||||
|
||||
const auto placement = vp->getDraggerPlacement();
|
||||
|
||||
vp->setDraggerPlacement({placement.getPosition(), xyzRotation});
|
||||
|
||||
vp->updateTransformFromDragger();
|
||||
vp->updatePlacementFromDragger();
|
||||
|
||||
resetReferencePlacement();
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
@@ -25,39 +25,152 @@
|
||||
#define TASKCSYSDRAGGER_H
|
||||
|
||||
#include "TaskView/TaskDialog.h"
|
||||
#include <App/DocumentObserver.h>
|
||||
#include "TaskView/TaskView.h"
|
||||
#include "ViewProviderDragger.h"
|
||||
|
||||
#include <Base/ServiceProvider.h>
|
||||
#include <App/Services.h>
|
||||
|
||||
class SoDragger;
|
||||
|
||||
namespace Gui
|
||||
namespace Attacher
|
||||
{
|
||||
class QuantitySpinBox;
|
||||
class SoFCCSysDragger;
|
||||
class ViewProviderDragger;
|
||||
|
||||
class TaskCSysDragger : public Gui::TaskView::TaskDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TaskCSysDragger(ViewProviderDocumentObject *vpObjectIn, SoFCCSysDragger *draggerIn);
|
||||
~TaskCSysDragger() override;
|
||||
QDialogButtonBox::StandardButtons getStandardButtons() const override
|
||||
{ return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;}
|
||||
void open() override;
|
||||
bool accept() override;
|
||||
bool reject() override;
|
||||
private Q_SLOTS:
|
||||
void onTIncrementSlot(double freshValue);
|
||||
void onRIncrementSlot(double freshValue);
|
||||
private:
|
||||
static inline bool firstDrag = true;
|
||||
static void dragStartCallback(void * data, SoDragger * d);
|
||||
void setupGui();
|
||||
App::DocumentObjectT vpObject;
|
||||
SoFCCSysDragger *dragger;
|
||||
QuantitySpinBox *tSpinBox;
|
||||
QuantitySpinBox *rSpinBox;
|
||||
};
|
||||
class AttachEngine;
|
||||
}
|
||||
|
||||
#endif // TASKCSYSDRAGGER_H
|
||||
namespace Gui
|
||||
{
|
||||
class QuantitySpinBox;
|
||||
class SoFCCSysDragger;
|
||||
class ViewProviderDragger;
|
||||
class Ui_TaskCSysDragger;
|
||||
|
||||
class TaskTransform : public Gui::TaskView::TaskBox, public Gui::SelectionObserver
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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<std::string, 3> labels;
|
||||
Base::Placement origin;
|
||||
};
|
||||
|
||||
Q_ENUM(SelectionMode)
|
||||
Q_ENUM(PlacementMode)
|
||||
Q_ENUM(PositionMode)
|
||||
|
||||
TaskTransform(Gui::ViewProviderDragger* vp,
|
||||
Gui::SoFCCSysDragger* dragger,
|
||||
QWidget* parent = nullptr,
|
||||
App::SubObjectPlacementProvider* subObjectPlacementProvider =
|
||||
Base::provideService<App::SubObjectPlacementProvider>(),
|
||||
App::CenterOfMassProvider* centerOfMassProvider =
|
||||
Base::provideService<App::CenterOfMassProvider>());
|
||||
~TaskTransform() override;
|
||||
|
||||
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<SoFCCSysDragger> dragger;
|
||||
|
||||
Ui_TaskCSysDragger *ui;
|
||||
|
||||
SelectionMode selectionMode { SelectionMode::None };
|
||||
PlacementMode placementMode { PlacementMode::ObjectOrigin };
|
||||
PositionMode positionMode { PositionMode::Local };
|
||||
|
||||
std::optional<Base::Placement> 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
|
||||
|
||||
532
src/Gui/TaskCSysDragger.ui
Normal file
532
src/Gui/TaskCSysDragger.ui
Normal file
@@ -0,0 +1,532 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Gui::TaskCSysDragger</class>
|
||||
<widget class="QWidget" name="Gui::TaskCSysDragger">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>450</width>
|
||||
<height>1012</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Placement</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Coordinate System</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>positionModeComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="positionModeComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Local Coordinate System</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Global Coordinate System</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="alignRotationCheckBox">
|
||||
<property name="text">
|
||||
<string>align dragger rotation with selected coordinate system</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<spacer name="vSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>41</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QGroupBox" name="positionGroupBox">
|
||||
<property name="title">
|
||||
<string>Translation</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" columnminimumwidth="100">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QWidget" name="positionStackWidget" native="true">
|
||||
<layout class="QGridLayout" name="absolutePositionLayout" columnstretch="1,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="4" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="zPositionSpinBox">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="xPositionSpinBox">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="xPositionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>xPositionSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="yPositionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>yPositionSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="zPositionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Z</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>zPositionSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="yPositionSpinBox">
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Utilities</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="alignToOtherObjectButton">
|
||||
<property name="text">
|
||||
<string>Move to other object</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="flipPartButton">
|
||||
<property name="text">
|
||||
<string>Flip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="transformOriginGroupBox">
|
||||
<property name="title">
|
||||
<string>Dragger</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="transformOriginLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="placementComboBox"/>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QLabel" name="snappingLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><b>Snapping</b></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="translationIncrementLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Translation</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>translationIncrementSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="rotationIncrementSpinBox">
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>360.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>5.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QWidget" name="referencePickerWidget" native="true">
|
||||
<layout class="QGridLayout" name="referencePickerLayout" columnstretch="0,1">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="referenceLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>68</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reference</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>referenceLineEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="pickTransformOriginButton">
|
||||
<property name="text">
|
||||
<string>pick reference</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="referenceLineEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="translationIncrementSpinBox">
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>2147483647.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>placementComboBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="rotationIncrementLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rotation</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>rotationIncrementSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QGroupBox" name="rotationGroupBox">
|
||||
<property name="title">
|
||||
<string>Rotation</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QWidget" name="rotationStackWidget" native="true">
|
||||
<layout class="QGridLayout" name="absoluteRotationLayout" columnstretch="1,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="zRotationSpinBox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="yRotationLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>yRotationSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="zRotationLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Z</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>zRotationSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="yRotationSpinBox"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="xRotationSpinBox"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="xRotationLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>xRotationSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QAbstractSpinBox</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>placementComboBox</tabstop>
|
||||
<tabstop>referenceLineEdit</tabstop>
|
||||
<tabstop>pickTransformOriginButton</tabstop>
|
||||
<tabstop>translationIncrementSpinBox</tabstop>
|
||||
<tabstop>rotationIncrementSpinBox</tabstop>
|
||||
<tabstop>positionModeComboBox</tabstop>
|
||||
<tabstop>alignRotationCheckBox</tabstop>
|
||||
<tabstop>xPositionSpinBox</tabstop>
|
||||
<tabstop>yPositionSpinBox</tabstop>
|
||||
<tabstop>zPositionSpinBox</tabstop>
|
||||
<tabstop>xRotationSpinBox</tabstop>
|
||||
<tabstop>yRotationSpinBox</tabstop>
|
||||
<tabstop>zRotationSpinBox</tabstop>
|
||||
<tabstop>alignToOtherObjectButton</tabstop>
|
||||
<tabstop>flipPartButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -104,6 +104,28 @@ struct vec_traits<App::Color> {
|
||||
private:
|
||||
const vec_type& v;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline SbMatrix convertTo<SbMatrix, Base::Matrix4D>(const Base::Matrix4D& vec2)
|
||||
{
|
||||
double dMtrx[16];
|
||||
vec2.getGLMatrix(dMtrx);
|
||||
return SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], // clazy:exclude=rule-of-two-soft
|
||||
dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7],
|
||||
dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11],
|
||||
dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Base::Matrix4D convertTo<Base::Matrix4D, SbMatrix>(const SbMatrix& vec2)
|
||||
{
|
||||
Base::Matrix4D mat;
|
||||
for(int i=0;i<4;++i) {
|
||||
for(int j=0;j<4;++j)
|
||||
mat[i][j] = vec2[j][i];
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
|
||||
namespace App{ class DocumentObject; }
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
#include "ViewProviderLink.h"
|
||||
#include "ViewProviderPy.h"
|
||||
|
||||
#include <Utilities.h>
|
||||
|
||||
|
||||
FC_LOG_LEVEL_INIT("ViewProvider", true, true)
|
||||
|
||||
@@ -345,13 +347,7 @@ QIcon ViewProvider::mergeColorfulOverlayIcons (const QIcon & orig) const
|
||||
|
||||
void ViewProvider::setTransformation(const Base::Matrix4D &rcMatrix)
|
||||
{
|
||||
double dMtrx[16];
|
||||
rcMatrix.getGLMatrix(dMtrx);
|
||||
|
||||
pcTransform->setMatrix(SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3],
|
||||
dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7],
|
||||
dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11],
|
||||
dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]));
|
||||
pcTransform->setMatrix(convert(rcMatrix));
|
||||
}
|
||||
|
||||
void ViewProvider::setTransformation(const SbMatrix &rcMatrix)
|
||||
@@ -361,24 +357,12 @@ void ViewProvider::setTransformation(const SbMatrix &rcMatrix)
|
||||
|
||||
SbMatrix ViewProvider::convert(const Base::Matrix4D &rcMatrix)
|
||||
{
|
||||
//NOLINTBEGIN
|
||||
double dMtrx[16];
|
||||
rcMatrix.getGLMatrix(dMtrx);
|
||||
return SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], // clazy:exclude=rule-of-two-soft
|
||||
dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7],
|
||||
dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11],
|
||||
dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]);
|
||||
//NOLINTEND
|
||||
return Base::convertTo<SbMatrix>(rcMatrix);
|
||||
}
|
||||
|
||||
Base::Matrix4D ViewProvider::convert(const SbMatrix &smat)
|
||||
{
|
||||
Base::Matrix4D mat;
|
||||
for(int i=0;i<4;++i) {
|
||||
for(int j=0;j<4;++j)
|
||||
mat[i][j] = smat[j][i];
|
||||
}
|
||||
return mat;
|
||||
return Base::convertTo<Base::Matrix4D>(smat);
|
||||
}
|
||||
|
||||
void ViewProvider::addDisplayMaskMode(SoNode *node, const char* type)
|
||||
|
||||
@@ -23,16 +23,19 @@
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <string>
|
||||
# include <QAction>
|
||||
# include <QMenu>
|
||||
# include <Inventor/draggers/SoDragger.h>
|
||||
# include <Inventor/nodes/SoPickStyle.h>
|
||||
# include <Inventor/nodes/SoTransform.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <Inventor/draggers/SoDragger.h>
|
||||
#include <Inventor/nodes/SoPickStyle.h>
|
||||
#include <Inventor/nodes/SoTransform.h>
|
||||
#endif
|
||||
|
||||
#include <App/GeoFeature.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/Converter.h>
|
||||
#include "Gui/ViewParams.h"
|
||||
|
||||
#include "Application.h"
|
||||
@@ -44,20 +47,27 @@
|
||||
#include "TaskCSysDragger.h"
|
||||
#include "View3DInventorViewer.h"
|
||||
#include "ViewProviderDragger.h"
|
||||
#include "Utilities.h"
|
||||
|
||||
#include <ViewProviderLink.h>
|
||||
#include <App/DocumentObjectGroup.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
PROPERTY_SOURCE(Gui::ViewProviderDragger, Gui::ViewProviderDocumentObject)
|
||||
|
||||
ViewProviderDragger::ViewProviderDragger() = default;
|
||||
ViewProviderDragger::ViewProviderDragger()
|
||||
{
|
||||
ADD_PROPERTY_TYPE(TransformOrigin, ({}), nullptr, App::Prop_Hidden, nullptr);
|
||||
};
|
||||
|
||||
ViewProviderDragger::~ViewProviderDragger() = default;
|
||||
|
||||
void ViewProviderDragger::updateData(const App::Property* prop)
|
||||
{
|
||||
if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId()) &&
|
||||
strcmp(prop->getName(), "Placement") == 0) {
|
||||
if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId())
|
||||
&& strcmp(prop->getName(), "Placement") == 0) {
|
||||
// Note: If R is the rotation, c the rotation center and t the translation
|
||||
// vector then Inventor applies the following transformation: R*(x-c)+c+t
|
||||
// In FreeCAD a placement only has a rotation and a translation part but
|
||||
@@ -73,6 +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<ViewProviderLink>()) {
|
||||
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<App::GeoFeature *>(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<SoGroup*>(viewer->getSceneGraph());
|
||||
selection->insertChild(rootPickStyle, 0);
|
||||
viewer->setSelectionEnabled(false);
|
||||
csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
|
||||
if (csysDragger && viewer) {
|
||||
csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
|
||||
|
||||
auto mat = viewer->getDocument()->getEditingTransform();
|
||||
viewer->getDocument()->setEditingTransform(mat);
|
||||
auto feat = dynamic_cast<App::GeoFeature *>(getObject());
|
||||
if(feat) {
|
||||
auto matInverse = feat->Placement.getValue().toMatrix();
|
||||
matInverse.inverse();
|
||||
mat *= matInverse;
|
||||
}
|
||||
viewer->setupEditingRoot(csysDragger,&mat);
|
||||
auto 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<SoGroup*>(viewer->getSceneGraph());
|
||||
SoNode *child = selection->getChild(0);
|
||||
if (child && child->isOfType(SoPickStyle::getClassTypeId())) {
|
||||
selection->removeChild(child);
|
||||
viewer->setSelectionEnabled(true);
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
void ViewProviderDragger::dragFinishCallback(void *data, SoDragger *d)
|
||||
void ViewProviderDragger::dragStartCallback(void* data, [[maybe_unused]] SoDragger* d)
|
||||
{
|
||||
// This is called when a manipulator has done manipulating
|
||||
auto vp = static_cast<ViewProviderDragger*>(data);
|
||||
|
||||
auto sudoThis = static_cast<ViewProviderDragger *>(data);
|
||||
auto dragger = static_cast<SoFCCSysDragger *>(d);
|
||||
updatePlacementFromDragger(sudoThis, dragger);
|
||||
|
||||
//Gui::Application::Instance->activeDocument()->commitCommand();
|
||||
vp->draggerPlacement = vp->getDraggerPlacement();
|
||||
vp->csysDragger->clearIncrementCounts();
|
||||
}
|
||||
|
||||
void ViewProviderDragger::updatePlacementFromDragger(ViewProviderDragger* sudoThis, SoFCCSysDragger* draggerIn)
|
||||
void ViewProviderDragger::dragFinishCallback(void* data, SoDragger* d)
|
||||
{
|
||||
App::DocumentObject *genericObject = sudoThis->getObject();
|
||||
if (!genericObject->isDerivedFrom(App::GeoFeature::getClassTypeId()))
|
||||
return;
|
||||
auto geoFeature = static_cast<App::GeoFeature *>(genericObject);
|
||||
Base::Placement originalPlacement = geoFeature->Placement.getValue();
|
||||
double pMatrix[16];
|
||||
originalPlacement.toMatrix().getMatrix(pMatrix);
|
||||
Base::Placement freshPlacement = originalPlacement;
|
||||
// This is called when a manipulator has done manipulating
|
||||
auto vp = static_cast<ViewProviderDragger*>(data);
|
||||
|
||||
//local cache for brevity.
|
||||
double translationIncrement = draggerIn->translationIncrement.getValue();
|
||||
double rotationIncrement = draggerIn->rotationIncrement.getValue();
|
||||
int tCountX = draggerIn->translationIncrementCountX.getValue();
|
||||
int tCountY = draggerIn->translationIncrementCountY.getValue();
|
||||
int tCountZ = draggerIn->translationIncrementCountZ.getValue();
|
||||
int rCountX = draggerIn->rotationIncrementCountX.getValue();
|
||||
int rCountY = draggerIn->rotationIncrementCountY.getValue();
|
||||
int rCountZ = draggerIn->rotationIncrementCountZ.getValue();
|
||||
vp->draggerPlacement = vp->getDraggerPlacement();
|
||||
vp->csysDragger->clearIncrementCounts();
|
||||
|
||||
//just as a little sanity check make sure only 1 or 2 fields has changed.
|
||||
int numberOfFieldChanged = 0;
|
||||
if (tCountX) numberOfFieldChanged++;
|
||||
if (tCountY) numberOfFieldChanged++;
|
||||
if (tCountZ) numberOfFieldChanged++;
|
||||
if (rCountX) numberOfFieldChanged++;
|
||||
if (rCountY) numberOfFieldChanged++;
|
||||
if (rCountZ) numberOfFieldChanged++;
|
||||
if (numberOfFieldChanged == 0)
|
||||
return;
|
||||
assert(numberOfFieldChanged == 1 || numberOfFieldChanged == 2);
|
||||
vp->updatePlacementFromDragger();
|
||||
}
|
||||
|
||||
//helper lambdas.
|
||||
auto getVectorX = [&pMatrix]() {return Base::Vector3d(pMatrix[0], pMatrix[4], pMatrix[8]);};
|
||||
auto getVectorY = [&pMatrix]() {return Base::Vector3d(pMatrix[1], pMatrix[5], pMatrix[9]);};
|
||||
auto getVectorZ = [&pMatrix]() {return Base::Vector3d(pMatrix[2], pMatrix[6], pMatrix[10]);};
|
||||
void ViewProviderDragger::dragMotionCallback(void* data, SoDragger* d)
|
||||
{
|
||||
auto vp = static_cast<ViewProviderDragger*>(data);
|
||||
|
||||
if (tCountX)
|
||||
{
|
||||
Base::Vector3d movementVector(getVectorX());
|
||||
movementVector *= (tCountX * translationIncrement);
|
||||
freshPlacement.move(movementVector);
|
||||
geoFeature->Placement.setValue(freshPlacement);
|
||||
}
|
||||
if (tCountY)
|
||||
{
|
||||
Base::Vector3d movementVector(getVectorY());
|
||||
movementVector *= (tCountY * translationIncrement);
|
||||
freshPlacement.move(movementVector);
|
||||
geoFeature->Placement.setValue(freshPlacement);
|
||||
}
|
||||
if (tCountZ)
|
||||
{
|
||||
Base::Vector3d movementVector(getVectorZ());
|
||||
movementVector *= (tCountZ * translationIncrement);
|
||||
freshPlacement.move(movementVector);
|
||||
geoFeature->Placement.setValue(freshPlacement);
|
||||
}
|
||||
if (rCountX)
|
||||
{
|
||||
Base::Vector3d rotationVector(getVectorX());
|
||||
Base::Rotation rotation(rotationVector, rCountX * rotationIncrement);
|
||||
freshPlacement.setRotation(rotation * freshPlacement.getRotation());
|
||||
geoFeature->Placement.setValue(freshPlacement);
|
||||
}
|
||||
if (rCountY)
|
||||
{
|
||||
Base::Vector3d rotationVector(getVectorY());
|
||||
Base::Rotation rotation(rotationVector, rCountY * rotationIncrement);
|
||||
freshPlacement.setRotation(rotation * freshPlacement.getRotation());
|
||||
geoFeature->Placement.setValue(freshPlacement);
|
||||
}
|
||||
if (rCountZ)
|
||||
{
|
||||
Base::Vector3d rotationVector(getVectorZ());
|
||||
Base::Rotation rotation(rotationVector, rCountZ * rotationIncrement);
|
||||
freshPlacement.setRotation(rotation * freshPlacement.getRotation());
|
||||
geoFeature->Placement.setValue(freshPlacement);
|
||||
}
|
||||
vp->updateTransformFromDragger();
|
||||
}
|
||||
|
||||
draggerIn->clearIncrementCounts();
|
||||
void ViewProviderDragger::updatePlacementFromDragger()
|
||||
{
|
||||
const auto placement = getObject()->getPropertyByName<App::PropertyPlacement>("Placement");
|
||||
|
||||
if (!placement) {
|
||||
return;
|
||||
}
|
||||
|
||||
placement->setValue(getDraggerPlacement() * getTransformOrigin().inverse());
|
||||
}
|
||||
|
||||
void ViewProviderDragger::updateTransformFromDragger()
|
||||
{
|
||||
const auto placement = getDraggerPlacement() * getTransformOrigin().inverse();
|
||||
|
||||
pcTransform->translation.setValue(Base::convertTo<SbVec3f>(placement.getPosition()));
|
||||
pcTransform->rotation.setValue(Base::convertTo<SbRotation>(placement.getRotation()));
|
||||
}
|
||||
|
||||
Base::Placement ViewProviderDragger::getObjectPlacement() const
|
||||
{
|
||||
if (auto placement = getObject()->getPropertyByName<App::PropertyPlacement>("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<SbVec3f>(placement.getPosition()));
|
||||
csysDragger->rotation.setValue(Base::convertTo<SbRotation>(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<SbRotation>(from.getRotation()));
|
||||
to->translation.setValue(Base::convertTo<SbVec3f>(from.getPosition()));
|
||||
to->center.setValue(0.0f, 0.0f, 0.0f);
|
||||
to->scaleFactor.setValue(1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
@@ -25,16 +25,20 @@
|
||||
#define GUI_VIEWPROVIDER_DRAGGER_H
|
||||
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include "SoFCCSysDragger.h"
|
||||
#include <Base/Placement.h>
|
||||
#include <App/PropertyGeo.h>
|
||||
|
||||
class SoDragger;
|
||||
class SoTransform;
|
||||
|
||||
namespace Base { class Placement;}
|
||||
|
||||
namespace Gui {
|
||||
|
||||
namespace TaskView {
|
||||
class TaskDialog;
|
||||
}
|
||||
|
||||
class View3DInventorViewer;
|
||||
class SoFCCSysDragger;
|
||||
|
||||
/**
|
||||
* The base class for all view providers modifying the placement
|
||||
@@ -52,6 +56,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<SoFCCSysDragger> 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
|
||||
|
||||
@@ -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<const App::PropertyPlacement*>(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<SoCenterballDragger>(new SoCenterballDragger);
|
||||
else
|
||||
pcDragger = CoinPtr<SoFCCSysDragger>(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<SoFCUnifiedSelection*>(
|
||||
viewer->getSceneGraph())->insertChild(rootPickStyle, 0);
|
||||
|
||||
if(useCenterballDragger) {
|
||||
auto dragger = static_cast<SoCenterballDragger*>(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<SoSurroundScale*>(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<SoFCCSysDragger*>(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<SoFCUnifiedSelection*>(viewer->getSceneGraph())->getChild(0);
|
||||
if (child && child->isOfType(SoPickStyle::getClassTypeId()))
|
||||
static_cast<SoFCUnifiedSelection*>(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<SoCenterballDragger*>(pcDragger.get());
|
||||
v = dragger->center.getValue();
|
||||
r = dragger->rotation.getValue();
|
||||
}
|
||||
else {
|
||||
auto dragger = static_cast<SoFCCSysDragger*>(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<SoCenterballDragger*>(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<SoFCCSysDragger*>(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<ViewProviderLink*>(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<ViewProviderLink*>(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<ViewProviderLink*>(data);
|
||||
me->callDraggerProxy("onDragMotion",true);
|
||||
me->callDraggerProxy("onDragMotion");
|
||||
}
|
||||
|
||||
void ViewProviderLink::updateLinks(ViewProvider *vp) {
|
||||
|
||||
@@ -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<std::string, App::Color> getElementColors(const char *subname=nullptr) const override;
|
||||
void setElementColors(const std::map<std::string, App::Color> &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<DraggerContext> dragCtx;
|
||||
CoinPtr<SoDragger> pcDragger;
|
||||
ViewProviderDocumentObject *childVp;
|
||||
LinkInfoPtr childVpLink;
|
||||
mutable qint64 overlayCacheKey;
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
</Documentation>
|
||||
<Parameter Name="DraggingPlacement" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="UseCenterballDragger">
|
||||
<Documentation>
|
||||
<UserDocu>Get/set dragger type</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="UseCenterballDragger" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="LinkView" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Get the associated LinkView object</UserDocu>
|
||||
|
||||
@@ -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<Base::PlacementPy*>(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);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <Base/ExceptionFactory.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Parameter.h>
|
||||
#include <Base/ServiceProvider.h>
|
||||
#include <Base/PrecisionPy.h>
|
||||
|
||||
#include "ArcOfCirclePy.h"
|
||||
@@ -187,8 +188,12 @@
|
||||
|
||||
#include <OCAF/ImportExportSettings.h>
|
||||
#include "MeasureClient.h"
|
||||
|
||||
#include <FuzzyHelper.h>
|
||||
|
||||
#include <App/Services.h>
|
||||
#include <Services.h>
|
||||
|
||||
namespace Part {
|
||||
extern PyObject* initModule();
|
||||
}
|
||||
@@ -572,7 +577,10 @@ PyMOD_INIT_FUNC(Part)
|
||||
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean");
|
||||
|
||||
Part::FuzzyHelper::setBooleanFuzzy(hGrp->GetFloat("BooleanFuzzy",10.0));
|
||||
|
||||
|
||||
Base::registerServiceImplementation<App::SubObjectPlacementProvider>(new AttacherSubObjectPlacement);
|
||||
Base::registerServiceImplementation<App::CenterOfMassProvider>(new PartCenterOfMass);
|
||||
|
||||
PyMOD_Return(partModule);
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
@@ -69,6 +69,8 @@
|
||||
#include "AttachExtension.h"
|
||||
#include "Tools.h"
|
||||
|
||||
#include <Geometry.h>
|
||||
|
||||
|
||||
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<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> shapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> shapeStorage;
|
||||
std::vector<eRefType> 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<App::DocumentObject*>(obj), subshape.c_str());
|
||||
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> 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<const TopoDS_Shape*> &shapes)
|
||||
GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector<const TopoShape*> &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<const TopoD
|
||||
*/
|
||||
void AttachEngine::readLinks(const std::vector<App::DocumentObject*>& objs,
|
||||
const std::vector<std::string> &subs,
|
||||
std::vector<const TopoDS_Shape*> &shapes,
|
||||
std::vector<TopoDS_Shape> &storage,
|
||||
std::vector<const TopoShape*> &shapes,
|
||||
std::vector<TopoShape> &storage,
|
||||
std::vector<eRefType> &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<App::GeoFeature*>(objs[i]);
|
||||
auto geof = extractGeoFeature(objs[i]);
|
||||
if (!geof) {
|
||||
// Accept App::Links to GeoFeatures
|
||||
geof = dynamic_cast<App::GeoFeature*>(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<App::GeoFeature*>(obj)) {
|
||||
return geof;
|
||||
}
|
||||
|
||||
auto linkedObject = obj->getLinkedObject();
|
||||
if (auto linkedGeof = dynamic_cast<App::GeoFeature*>(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<App::DocumentObjec
|
||||
throw ExceptionCancel(); // to be handled in positionBySupport, to not do anything if
|
||||
// disabled
|
||||
}
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(objs, subs, shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -1172,7 +1197,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects "
|
||||
"specified (need one vertex).");
|
||||
}
|
||||
const TopoDS_Shape& sh = *shapes[0];
|
||||
const TopoDS_Shape& sh = shapes[0]->getShape();
|
||||
if (sh.IsNull()) {
|
||||
throw Base::ValueError(
|
||||
"Null shape in AttachEngine3D::calculateAttachedPlacement()!");
|
||||
@@ -1207,7 +1232,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
|
||||
}
|
||||
else if (isShapeOfType(types[0], rtConic) > 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::vector<App::DocumentObjec
|
||||
|
||||
TopoDS_Vertex vertex;
|
||||
try {
|
||||
vertex = TopoDS::Vertex(*(shapes[1]));
|
||||
vertex = TopoDS::Vertex(shapes[1]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1330,7 +1355,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Pln plane;
|
||||
bool Reverse = false;
|
||||
try {
|
||||
face = TopoDS::Face(*(shapes[0]));
|
||||
face = TopoDS::Face(shapes[0]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1387,14 +1412,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
}
|
||||
|
||||
bool bThruVertex = false;
|
||||
if (shapes[0]->ShapeType() == 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::vector<App::DocumentObjec
|
||||
|
||||
TopoDS_Vertex vertex;
|
||||
try {
|
||||
vertex = TopoDS::Vertex(*(shapes[1]));
|
||||
vertex = TopoDS::Vertex(shapes[1]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1472,14 +1497,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
}
|
||||
|
||||
bool bThruVertex = false;
|
||||
if (shapes[0]->ShapeType() == 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<App::DocumentObjec
|
||||
if (shapes.size() >= 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<App::DocumentObjec
|
||||
std::vector<gp_Pnt> 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::vector<App::DocumentObjec
|
||||
gp_Lin lines[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
edges[i] = &TopoDS::Edge(*(shapes[i]));
|
||||
edges[i] = &TopoDS::Edge(shapes[i]->getShape());
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
@@ -1845,28 +1870,28 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
gp_Vec dirs[3];
|
||||
|
||||
// read out origin
|
||||
if (shapes[0]->IsNull()) {
|
||||
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::vector<App::DocumentObjec
|
||||
plm *= this->attachmentOffset;
|
||||
return plm;
|
||||
} break;
|
||||
case mmMidpoint: {
|
||||
Base::Placement placement;
|
||||
|
||||
// special case for planes
|
||||
if (auto plane = dynamic_cast<App::Plane*>(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<GeomPoint*>(geom.get())) {
|
||||
placement.setPosition(point->getPoint());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TopAbs_EDGE: {
|
||||
if (auto conic = dynamic_cast<GeomConic*>(geom.get())) {
|
||||
placement.setPosition(conic->getLocation());
|
||||
placement.setRotation(conic->getRotation().value_or(Base::Rotation {}));
|
||||
} else if (auto line = dynamic_cast<GeomCurve*>(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<GeomSurface*>(geom.get());
|
||||
|
||||
if (auto sphere = dynamic_cast<GeomSphere*>(geom.get())) {
|
||||
placement.setPosition(sphere->getLocation());
|
||||
} else if (auto cone = dynamic_cast<GeomCone*>(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<Base::Vector3d>(-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<App::DocumentObj
|
||||
|
||||
Base::Placement plm;
|
||||
if (!bReUsed) {
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(objs, subs, shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -2157,7 +2266,7 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
std::vector<gp_Pnt> 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::vector<App::DocumentObj
|
||||
} break;
|
||||
case mm1Asymptote1:
|
||||
case mm1Asymptote2: {
|
||||
if (shapes[0]->IsNull()) {
|
||||
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::vector<App::DocumentObj
|
||||
} break;
|
||||
case mm1Directrix1:
|
||||
case mm1Directrix2: {
|
||||
if (shapes[0]->IsNull()) {
|
||||
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::vector<App::DocumentObj
|
||||
"AttachEngineLine::calculateAttachedPlacement: Intersection mode requires "
|
||||
"two shapes; only one is supplied");
|
||||
}
|
||||
if (shapes[0]->IsNull() || 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::vector<App::DocumentObj
|
||||
"AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two "
|
||||
"shapes; only one is supplied");
|
||||
}
|
||||
if (shapes[0]->IsNull()) {
|
||||
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<App::DocumentOb
|
||||
|
||||
Base::Placement plm;
|
||||
if (!bReUsed) {
|
||||
std::vector<const TopoDS_Shape*> shapes;
|
||||
std::vector<TopoDS_Shape> copiedShapeStorage;
|
||||
std::vector<const TopoShape*> shapes;
|
||||
std::vector<TopoShape> copiedShapeStorage;
|
||||
std::vector<eRefType> types;
|
||||
readLinks(objs, subs, shapes, copiedShapeStorage, types);
|
||||
|
||||
@@ -2470,7 +2579,7 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector<App::DocumentOb
|
||||
std::vector<gp_Pnt> 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::vector<App::DocumentOb
|
||||
} break;
|
||||
case mm0Focus1:
|
||||
case mm0Focus2: {
|
||||
if (shapes[0]->IsNull()) {
|
||||
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::vector<App::DocumentOb
|
||||
"AttachEnginePoint::calculateAttachedPlacement: Proximity mode requires "
|
||||
"two shapes; only one is supplied");
|
||||
}
|
||||
if (shapes[0]->IsNull()) {
|
||||
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);
|
||||
|
||||
@@ -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<const TopoDS_Shape*> &shapes);
|
||||
static GProp_GProps getInertialPropsOfShape(const std::vector<const Part::TopoShape*> &shapes);
|
||||
|
||||
std::vector<App::DocumentObject*> getRefObjects() const;
|
||||
const std::vector<std::string> &getSubValues() const {return subnames;}
|
||||
@@ -430,11 +431,36 @@ protected:
|
||||
}
|
||||
static void readLinks(const std::vector<App::DocumentObject*> &objs,
|
||||
const std::vector<std::string> &subs,
|
||||
std::vector<const TopoDS_Shape*>& shapes, std::vector<TopoDS_Shape> &storage,
|
||||
|
||||
std::vector<const Part::TopoShape*>& shapes,
|
||||
std::vector<Part::TopoShape> &storage,
|
||||
std::vector<eRefType> &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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -553,6 +553,8 @@ SET(Part_SRCS
|
||||
PreCompiled.h
|
||||
ProgressIndicator.cpp
|
||||
ProgressIndicator.h
|
||||
Services.cpp
|
||||
Services.h
|
||||
TopoShape.cpp
|
||||
TopoShape.h
|
||||
TopoShapeCache.cpp
|
||||
|
||||
@@ -149,6 +149,8 @@
|
||||
#include "ToroidPy.h"
|
||||
#include "TopoShape.h"
|
||||
|
||||
#include <gp_Quaternion.hxx>
|
||||
|
||||
|
||||
#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<Base::Rotation> 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<GeomPlane*>(this->clone());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return dynamic_cast<GeomPlane*>(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<Base::Rotation> 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<Base::Rotation> 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<Base::Vector3d>(s->Apex());
|
||||
}
|
||||
|
||||
bool GeomCone::isSame(const Geometry &_other, double tol, double atol) const
|
||||
{
|
||||
if(_other.getTypeId() != getTypeId())
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
|
||||
@@ -409,6 +410,7 @@ public:
|
||||
*/
|
||||
Base::Vector3d getCenter() const;
|
||||
Base::Vector3d getLocation() const;
|
||||
std::optional<Base::Rotation> getRotation() const;
|
||||
void setLocation(const Base::Vector3d& Center);
|
||||
/*!
|
||||
* \deprecated use setLocation
|
||||
@@ -882,6 +884,8 @@ public:
|
||||
|
||||
GeomPlane *toPlane(bool clone=true, double tol=1e-7) const;
|
||||
|
||||
virtual std::optional<Base::Rotation> getRotation() const;
|
||||
|
||||
bool tangentU(double u, double v, gp_Dir& dirU) const;
|
||||
bool tangentV(double u, double v, gp_Dir& dirV) const;
|
||||
bool normal(double u, double v, gp_Dir& dir) const;
|
||||
@@ -961,6 +965,7 @@ public:
|
||||
~GeomElementarySurface() override;
|
||||
|
||||
Base::Vector3d getLocation() const;
|
||||
|
||||
Base::Vector3d getDir() const;
|
||||
Base::Vector3d getXDir() const;
|
||||
Base::Vector3d getYDir() const;
|
||||
@@ -1014,6 +1019,8 @@ public:
|
||||
double getRadius() const;
|
||||
double getSemiAngle() const;
|
||||
|
||||
Base::Vector3d getApex() const;
|
||||
|
||||
bool isSame(const Geometry &other, double tol, double atol) const override;
|
||||
|
||||
void setHandle(const Handle(Geom_ConicalSurface)&);
|
||||
@@ -1091,6 +1098,8 @@ public:
|
||||
~GeomPlane() override;
|
||||
Geometry *copy() const override;
|
||||
|
||||
std::optional<Base::Rotation> getRotation() const override;
|
||||
|
||||
// Persistence implementer ---------------------
|
||||
unsigned int getMemSize() const override;
|
||||
void Save(Base::Writer &/*writer*/) const override;
|
||||
|
||||
55
src/Mod/Part/App/Services.cpp
Normal file
55
src/Mod/Part/App/Services.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "Services.h"
|
||||
|
||||
AttacherSubObjectPlacement::AttacherSubObjectPlacement()
|
||||
: attacher(std::make_unique<Attacher::AttachEngine3D>())
|
||||
{
|
||||
attacher->setUp({}, Attacher::mmMidpoint);
|
||||
}
|
||||
|
||||
Base::Placement AttacherSubObjectPlacement::calculate(App::SubObjectT object,
|
||||
Base::Placement basePlacement) const
|
||||
{
|
||||
attacher->setReferences({object});
|
||||
|
||||
auto calculatedAttachment = attacher->calculateAttachedPlacement(basePlacement);
|
||||
|
||||
return basePlacement.inverse() * calculatedAttachment;
|
||||
}
|
||||
|
||||
std::optional<Base::Vector3d> PartCenterOfMass::ofDocumentObject(App::DocumentObject* object) const
|
||||
{
|
||||
if (const auto feature = dynamic_cast<Part::Feature*>(object)) {
|
||||
const auto shape = feature->Shape.getShape();
|
||||
|
||||
if (const auto cog = shape.centerOfGravity()) {
|
||||
const Base::Placement comPlacement { *cog, Base::Rotation { } };
|
||||
|
||||
return (feature->Placement.getValue().inverse() * comPlacement).getPosition();
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
47
src/Mod/Part/App/Services.h
Normal file
47
src/Mod/Part/App/Services.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef PART_SERVICES_H
|
||||
#define PART_SERVICES_H
|
||||
|
||||
#include <Attacher.h>
|
||||
#include <App/Services.h>
|
||||
|
||||
class AttacherSubObjectPlacement final: public App::SubObjectPlacementProvider
|
||||
{
|
||||
public:
|
||||
AttacherSubObjectPlacement();
|
||||
|
||||
Base::Placement calculate(App::SubObjectT object, Base::Placement basePlacement) const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Attacher::AttachEngine3D> attacher;
|
||||
};
|
||||
|
||||
class PartCenterOfMass final: public App::CenterOfMassProvider
|
||||
{
|
||||
public:
|
||||
std::optional<Base::Vector3d> ofDocumentObject(App::DocumentObject* object) const override;
|
||||
};
|
||||
|
||||
#endif // PART_SERVICES_H
|
||||
@@ -81,7 +81,16 @@ void ViewProvider::setupContextMenu(QMenu* menu, QObject* receiver, const char*
|
||||
|
||||
bool ViewProvider::setEdit(int ModNum)
|
||||
{
|
||||
if (ModNum == ViewProvider::Default ) {
|
||||
if (ModNum == ViewProvider::Transform) {
|
||||
if (forwardToLink()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is feature so we need to forward the transform to the body
|
||||
forwardedViewProvider = getBodyViewProvider();
|
||||
return forwardedViewProvider->startEditing(ModNum);
|
||||
}
|
||||
else if (ModNum == ViewProvider::Default) {
|
||||
// When double-clicking on the item for this feature the
|
||||
// object unsets and sets its edit mode without closing
|
||||
// the task panel
|
||||
@@ -194,6 +203,22 @@ void ViewProvider::onChanged(const App::Property* prop) {
|
||||
PartGui::ViewProviderPartExt::onChanged(prop);
|
||||
}
|
||||
|
||||
Gui::ViewProvider* ViewProvider::startEditing(int ModNum)
|
||||
{
|
||||
// in case of transform we forward the request to body
|
||||
if (ModNum == Transform) {
|
||||
forwardedViewProvider = nullptr;
|
||||
|
||||
if (!ViewProviderPart::startEditing(ModNum)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return forwardedViewProvider;
|
||||
}
|
||||
|
||||
return ViewProviderPart::startEditing(ModNum);
|
||||
}
|
||||
|
||||
void ViewProvider::setTipIcon(bool onoff) {
|
||||
isSetTipIcon = onoff;
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
void updateData(const App::Property*) override;
|
||||
void onChanged(const App::Property* prop) override;
|
||||
|
||||
Gui::ViewProvider* startEditing(int ModNum) override;
|
||||
|
||||
void setTipIcon(bool onoff);
|
||||
|
||||
//body mode means that the object is part of a body and that the body is used to set the
|
||||
|
||||
@@ -62,7 +62,7 @@ TEST(ServiceProvider, provideImplementation)
|
||||
// Arrange
|
||||
Base::ServiceProvider serviceProvider;
|
||||
|
||||
serviceProvider.implement<SimpleService>(new FirstServiceImplementation);
|
||||
serviceProvider.registerImplementation<SimpleService>(new FirstServiceImplementation);
|
||||
|
||||
// Act
|
||||
auto implementation = serviceProvider.provide<SimpleService>();
|
||||
@@ -77,8 +77,8 @@ TEST(ServiceProvider, provideLatestImplementation)
|
||||
// Arrange
|
||||
Base::ServiceProvider serviceProvider;
|
||||
|
||||
serviceProvider.implement<SimpleService>(new FirstServiceImplementation);
|
||||
serviceProvider.implement<SimpleService>(new SecondServiceImplementation);
|
||||
serviceProvider.registerImplementation<SimpleService>(new FirstServiceImplementation);
|
||||
serviceProvider.registerImplementation<SimpleService>(new SecondServiceImplementation);
|
||||
|
||||
// Act
|
||||
auto implementation = serviceProvider.provide<SimpleService>();
|
||||
@@ -93,8 +93,8 @@ TEST(ServiceProvider, provideAllImplementations)
|
||||
// Arrange
|
||||
Base::ServiceProvider serviceProvider;
|
||||
|
||||
serviceProvider.implement<SimpleService>(new FirstServiceImplementation);
|
||||
serviceProvider.implement<SimpleService>(new SecondServiceImplementation);
|
||||
serviceProvider.registerImplementation<SimpleService>(new FirstServiceImplementation);
|
||||
serviceProvider.registerImplementation<SimpleService>(new SecondServiceImplementation);
|
||||
|
||||
// Act
|
||||
auto implementations = serviceProvider.all<SimpleService>();
|
||||
|
||||
@@ -79,8 +79,8 @@ TEST_F(AttacherTest, TestGetShapeType)
|
||||
TEST_F(AttacherTest, TestGetInertialPropsOfShape)
|
||||
{
|
||||
auto& attacher = _boxes[1]->attacher();
|
||||
std::vector<const TopoDS_Shape*> result;
|
||||
auto faces = _boxes[1]->Shape.getShape().getSubShapes(TopAbs_FACE);
|
||||
std::vector<const TopoShape*> result;
|
||||
auto faces = _boxes[1]->Shape.getShape().getSubTopoShapes(TopAbs_FACE);
|
||||
result.emplace_back(&faces[0]);
|
||||
auto shapeType = attacher.getInertialPropsOfShape(result);
|
||||
EXPECT_EQ(result.size(), 1);
|
||||
|
||||
Reference in New Issue
Block a user