Merge pull request #17564 from kadet1090/new-transform-dialog

Gui: New transform dialog
This commit is contained in:
Chris Hennes
2025-01-13 11:18:26 -06:00
committed by GitHub
42 changed files with 2559 additions and 750 deletions

View File

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

View File

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

View File

@@ -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);

View File

@@ -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();

View File

@@ -61,12 +61,7 @@ void GeoFeature::transformPlacement(const Base::Placement& transform)
Base::Placement GeoFeature::globalPlacement() const
{
auto* group = GeoFeatureGroupExtension::getGroupOfObject(this);
if (group) {
auto ext = group->getExtensionByType<GeoFeatureGroupExtension>();
return ext->globalGroupPlacement() * Placement.getValue();
}
return Placement.getValue();
return GeoFeature::getGlobalPlacement(this);
}
const PropertyComplexGeoData* GeoFeature::getPropertyOfGeometry() const
@@ -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();
}

View File

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

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

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "Services.h"
std::optional<Base::Vector3d>
App::NullCenterOfMass::ofDocumentObject([[maybe_unused]] DocumentObject* object) const
{
return std::nullopt;
}

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

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

View File

@@ -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];

View File

@@ -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;

View File

@@ -26,8 +26,7 @@
#include "ServiceProvider.h"
Base::ServiceProvider& Base::ServiceProvider::get()
namespace Base
{
static Base::ServiceProvider instance;
return instance;
Base::ServiceProvider globalServiceProvider;
}

View File

@@ -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

View File

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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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.

View File

@@ -379,6 +379,8 @@ SoFrameLabel::SoFrameLabel()
SO_NODE_ADD_FIELD(name, ("Helvetica"));
SO_NODE_ADD_FIELD(size, (12));
SO_NODE_ADD_FIELD(frame, (true));
SO_NODE_ADD_FIELD(border, (true));
SO_NODE_ADD_FIELD(backgroundUseBaseColor, (false));
//SO_NODE_ADD_FIELD(image, (SbVec2s(0,0), 0, NULL));
}
@@ -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);
}

View File

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

View File

@@ -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 &degreesIn)
namespace
{
return degreesIn * (M_PI / 180.0);
}
TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDocumentObject* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) :
dragger(draggerIn)
void alignGridLayoutColumns(const std::list<QGridLayout*>& layouts, unsigned column = 0)
{
assert(vpObjectIn);
assert(draggerIn);
vpObject = vpObjectIn->getObject();
dragger->ref();
std::vector<int> widths;
setupGui();
}
auto getActualWidth = [&](const QGridLayout* layout) -> int {
if (auto const item = layout->itemAtPosition(0, column)) {
return item->geometry().width();
}
TaskCSysDragger::~TaskCSysDragger()
{
dragger->unref();
Gui::Application::Instance->commandManager().getCommandByName("Std_OrthographicCamera")->setEnabled(true);
Gui::Application::Instance->commandManager().getCommandByName("Std_PerspectiveCamera")->setEnabled(true);
}
return 0;
};
void TaskCSysDragger::dragStartCallback(void *, SoDragger *)
{
// This is called when a manipulator is about to manipulating
if(firstDrag)
{
Gui::Application::Instance->activeDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Transform"));
firstDrag=false;
for (const auto layout : layouts) {
widths.push_back(getActualWidth(layout));
}
const auto maxWidth = *std::max_element(widths.begin(), widths.end());
for (const auto layout : layouts) {
layout->setColumnMinimumWidth(column, maxWidth);
}
}
void TaskCSysDragger::setupGui()
} // namespace
TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp,
Gui::SoFCCSysDragger* dragger,
QWidget* parent,
App::SubObjectPlacementProvider* subObjectPlacemenProvider,
App::CenterOfMassProvider* centerOfMassProvider)
: TaskBox(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"), tr("Transform"), false, parent)
, vp(vp)
, subObjectPlacementProvider(subObjectPlacemenProvider)
, centerOfMassProvider(centerOfMassProvider)
, dragger(dragger)
, ui(new Ui_TaskCSysDragger)
{
auto incrementsBox = new Gui::TaskView::TaskBox(
Gui::BitmapFactory().pixmap("Std_TransformManip"),
tr("Transform"), true, nullptr);
blockSelection(true);
auto gridLayout = new QGridLayout();
gridLayout->setColumnStretch(1, 1);
dragger->addStartCallback(dragStartCallback, this);
dragger->addMotionCallback(dragMotionCallback, this);
auto tLabel = new QLabel(tr("Translation Increment:"), incrementsBox);
gridLayout->addWidget(tLabel, 0, 0, Qt::AlignRight);
vp->resetTransformOrigin();
QFontMetrics metrics(QApplication::font());
int spinBoxWidth = metrics.averageCharWidth() * 20;
tSpinBox = new QuantitySpinBox(incrementsBox);
tSpinBox->setMinimum(0.0);
tSpinBox->setMaximum(std::numeric_limits<double>::max());
tSpinBox->setUnit(Base::Unit::Length);
tSpinBox->setMinimumWidth(spinBoxWidth);
gridLayout->addWidget(tSpinBox, 0, 1, Qt::AlignLeft);
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"

View File

@@ -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
View 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>&lt;b&gt;Snapping&lt;/b&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="translationIncrementLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Translation</string>
</property>
<property name="buddy">
<cstring>translationIncrementSpinBox</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="Gui::QuantitySpinBox" name="rotationIncrementSpinBox">
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<widget class="QWidget" name="referencePickerWidget" native="true">
<layout class="QGridLayout" name="referencePickerLayout" columnstretch="0,1">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="referenceLabel">
<property name="minimumSize">
<size>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Reference</string>
</property>
<property name="buddy">
<cstring>referenceLineEdit</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="pickTransformOriginButton">
<property name="text">
<string>pick reference</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="referenceLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="1">
<widget class="Gui::QuantitySpinBox" name="translationIncrementSpinBox">
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2147483647.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mode</string>
</property>
<property name="buddy">
<cstring>placementComboBox</cstring>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="rotationIncrementLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Rotation</string>
</property>
<property name="buddy">
<cstring>rotationIncrementSpinBox</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<widget class="QGroupBox" name="rotationGroupBox">
<property name="title">
<string>Rotation</string>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="0" colspan="2">
<widget class="QWidget" name="rotationStackWidget" native="true">
<layout class="QGridLayout" name="absoluteRotationLayout" columnstretch="1,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="2" column="1">
<widget class="Gui::QuantitySpinBox" name="zRotationSpinBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="yRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Y</string>
</property>
<property name="buddy">
<cstring>yRotationSpinBox</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="zRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Z</string>
</property>
<property name="buddy">
<cstring>zRotationSpinBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::QuantitySpinBox" name="yRotationSpinBox"/>
</item>
<item row="0" column="1">
<widget class="Gui::QuantitySpinBox" name="xRotationSpinBox"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="xRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>X</string>
</property>
<property name="buddy">
<cstring>xRotationSpinBox</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QAbstractSpinBox</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>placementComboBox</tabstop>
<tabstop>referenceLineEdit</tabstop>
<tabstop>pickTransformOriginButton</tabstop>
<tabstop>translationIncrementSpinBox</tabstop>
<tabstop>rotationIncrementSpinBox</tabstop>
<tabstop>positionModeComboBox</tabstop>
<tabstop>alignRotationCheckBox</tabstop>
<tabstop>xPositionSpinBox</tabstop>
<tabstop>yPositionSpinBox</tabstop>
<tabstop>zPositionSpinBox</tabstop>
<tabstop>xRotationSpinBox</tabstop>
<tabstop>yRotationSpinBox</tabstop>
<tabstop>zRotationSpinBox</tabstop>
<tabstop>alignToOtherObjectButton</tabstop>
<tabstop>flipPartButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

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

View File

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

View File

@@ -23,16 +23,19 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <string>
# include <QAction>
# include <QMenu>
# include <Inventor/draggers/SoDragger.h>
# include <Inventor/nodes/SoPickStyle.h>
# include <Inventor/nodes/SoTransform.h>
#include <memory>
#include <string>
#include <QAction>
#include <QMenu>
#include <Inventor/draggers/SoDragger.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Inventor/nodes/SoTransform.h>
#endif
#include <App/GeoFeature.h>
#include <Base/Placement.h>
#include <Base/Vector3D.h>
#include <Base/Converter.h>
#include "Gui/ViewParams.h"
#include "Application.h"
@@ -44,20 +47,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);
}

View File

@@ -25,16 +25,20 @@
#define GUI_VIEWPROVIDER_DRAGGER_H
#include "ViewProviderDocumentObject.h"
#include "SoFCCSysDragger.h"
#include <Base/Placement.h>
#include <App/PropertyGeo.h>
class SoDragger;
class SoTransform;
namespace Base { class Placement;}
namespace Gui {
namespace TaskView {
class TaskDialog;
}
class View3DInventorViewer;
class SoFCCSysDragger;
/**
* The base class for all view providers modifying the placement
@@ -52,6 +56,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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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>

View File

@@ -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);
}

View File

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

View File

@@ -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);

View File

@@ -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);
};

View File

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

View File

@@ -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())

View File

@@ -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;

View 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 {};
}

View File

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

View File

@@ -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;

View File

@@ -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

View File

@@ -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>();

View File

@@ -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);