From d2138273199a15224e14de73e6ab3bf2f8927e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Mon, 25 Mar 2024 01:33:43 +0100 Subject: [PATCH 01/13] Add function to check if a Transformed feature is a sub-feature of MultiTransform This will check the in-list instead of relying on an empty Originals property. --- src/Mod/PartDesign/App/FeatureTransformed.cpp | 22 +++++++++++++++++-- src/Mod/PartDesign/App/FeatureTransformed.h | 6 +++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index 7656e15d7d..c52de2c4b5 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -43,6 +43,7 @@ #include "FeatureTransformed.h" #include "Body.h" #include "FeatureAddSub.h" +#include "FeatureMultiTransform.h" #include "FeatureMirrored.h" #include "FeatureLinearPattern.h" #include "FeaturePolarPattern.h" @@ -138,6 +139,22 @@ void Transformed::Restore(Base::XMLReader &reader) PartDesign::Feature::Restore(reader); } +bool Transformed::isMultiTransformChild() const +{ + for (auto const* obj : getInList()) { + auto mt = Base::freecad_dynamic_cast(obj); + if (!mt) { + continue; + } + + auto const transfmt = mt->Transformations.getValues(); + if (std::find(transfmt.begin(), transfmt.end(), this) != transfmt.end()) { + return true; + } + } + return false; +} + void Transformed::handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop) { // The property 'Angle' of PolarPattern has changed from PropertyFloat @@ -165,9 +182,9 @@ short Transformed::mustExecute() const App::DocumentObjectExecReturn *Transformed::execute() { - std::vector originals = Originals.getValues(); - if (originals.empty()) // typically InsideMultiTransform + if (isMultiTransformChild()) { return App::DocumentObject::StdReturn; + } if(!this->BaseFeature.getValue()) { auto body = getFeatureBody(); @@ -178,6 +195,7 @@ App::DocumentObjectExecReturn *Transformed::execute() this->positionBySupport(); + std::vector originals = Originals.getValues(); // Remove suppressed features from the list so the transformations behave as if they are not there { auto eraseIter = std::remove_if(originals.begin(), originals.end(), [](App::DocumentObject const* obj) { diff --git a/src/Mod/PartDesign/App/FeatureTransformed.h b/src/Mod/PartDesign/App/FeatureTransformed.h index 3e8e50104a..d9279de598 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.h +++ b/src/Mod/PartDesign/App/FeatureTransformed.h @@ -45,8 +45,7 @@ public: Transformed(); /** The shapes to be transformed - * if Originals is empty the instance is just a container for storing transformation data - */ + */ App::PropertyLinkList Originals; App::PropertyBool Refine; @@ -90,6 +89,9 @@ public: protected: void Restore(Base::XMLReader &reader) override; void handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop) override; + + /// Return true if this feature is a child of a MultiTransform + bool isMultiTransformChild() const; virtual void positionBySupport(); TopoDS_Shape refineShapeIfActive(const TopoDS_Shape&) const; void divideTools(const std::vector &toolsIn, std::vector &individualsOut, From 5790795f030124e5dd351a5f7f7149d0e957619b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Mon, 25 Mar 2024 02:07:26 +0100 Subject: [PATCH 02/13] Add a TransformMode property to the pattern features This property controls wether the selected features tool shapes are transformed or the whole base feature's shape. --- .../PartDesign/App/FeatureMultiTransform.cpp | 25 ++- src/Mod/PartDesign/App/FeatureScaled.cpp | 24 ++- src/Mod/PartDesign/App/FeatureTransformed.cpp | 142 +++++++++++------- src/Mod/PartDesign/App/FeatureTransformed.h | 9 +- 4 files changed, 120 insertions(+), 80 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureMultiTransform.cpp b/src/Mod/PartDesign/App/FeatureMultiTransform.cpp index b4ac76d554..9163668f32 100644 --- a/src/Mod/PartDesign/App/FeatureMultiTransform.cpp +++ b/src/Mod/PartDesign/App/FeatureMultiTransform.cpp @@ -75,23 +75,20 @@ const std::list MultiTransform::getTransformations(const std::vector transFeatures = Transformations.getValues(); - // Find centre of gravity of first original - // FIXME: This method will NOT give the expected result for more than one original! - Part::Feature* originalFeature = static_cast(originals.front()); - TopoDS_Shape original; + gp_Pnt cog; + if (!originals.empty()) { + // Find centre of gravity of first original + // FIXME: This method will NOT give the expected result for more than one original! + if (auto addFeature = + Base::freecad_dynamic_cast(originals.front())) { + TopoDS_Shape original = addFeature->AddSubShape.getShape().getShape(); - if (originalFeature->isDerivedFrom()) { - PartDesign::FeatureAddSub* addFeature = static_cast(originalFeature); - //if (addFeature->getAddSubType() == FeatureAddSub::Additive) - // original = addFeature->AddSubShape.getShape().getShape(); - //else - original = addFeature->AddSubShape.getShape().getShape(); + GProp_GProps props; + BRepGProp::VolumeProperties(original, props); + cog = props.CentreOfMass(); + } } - GProp_GProps props; - BRepGProp::VolumeProperties(original,props); - gp_Pnt cog = props.CentreOfMass(); - std::list result; std::list cogs; std::vector::const_iterator f; diff --git a/src/Mod/PartDesign/App/FeatureScaled.cpp b/src/Mod/PartDesign/App/FeatureScaled.cpp index 7cc1717e89..b66f15d980 100644 --- a/src/Mod/PartDesign/App/FeatureScaled.cpp +++ b/src/Mod/PartDesign/App/FeatureScaled.cpp @@ -64,23 +64,19 @@ const std::list Scaled::getTransformations(const std::vector(originals.front()); - TopoDS_Shape original; + gp_Pnt cog; + if (!originals.empty()) { + // Find centre of gravity of first original + // FIXME: This method will NOT give the expected result for more than one original! + if (auto feature = Base::freecad_dynamic_cast(originals.front())) { + TopoDS_Shape original = feature->AddSubShape.getShape().getShape(); - if (originalFeature->isDerivedFrom()) { - PartDesign::FeatureAddSub* Feature = static_cast(originalFeature); - //if(Feature->getAddSubType() == FeatureAddSub::Additive) - // original = Feature->AddSubShape.getShape().getShape(); - //else - original = Feature->AddSubShape.getShape().getShape(); + GProp_GProps props; + BRepGProp::VolumeProperties(original, props); + cog = props.CentreOfMass(); + } } - GProp_GProps props; - BRepGProp::VolumeProperties(original,props); - gp_Pnt cog = props.CentreOfMass(); - // Note: The original feature is NOT included in the list of transformations! Therefore // we start with occurrence number 1, not number 0 std::list transformations; diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index c52de2c4b5..dfdf030407 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -33,6 +33,8 @@ # include #endif +#include + #include #include #include @@ -56,12 +58,19 @@ namespace PartDesign { PROPERTY_SOURCE(PartDesign::Transformed, PartDesign::Feature) +std::array transformModeEnums = {"Transform tool shapes", + "Transform body", + nullptr}; + Transformed::Transformed() { ADD_PROPERTY(Originals,(nullptr)); Originals.setSize(0); Placement.setStatus(App::Property::ReadOnly, true); + ADD_PROPERTY(TransformMode, (static_cast(Mode::TransformToolShapes))); + TransformMode.setEnums(transformModeEnums.data()); + ADD_PROPERTY_TYPE(Refine,(0),"Part Design",(App::PropertyType)(App::Prop_None),"Refine shape (clean up redundant edges) after adding/subtracting"); //init Refine property @@ -175,8 +184,9 @@ void Transformed::handleChangedPropertyType(Base::XMLReader &reader, const char short Transformed::mustExecute() const { - if (Originals.isTouched()) + if (Originals.isTouched() || TransformMode.isTouched()) { return 1; + } return PartDesign::Feature::mustExecute(); } @@ -186,6 +196,11 @@ App::DocumentObjectExecReturn *Transformed::execute() return App::DocumentObject::StdReturn; } + auto const mode = static_cast(TransformMode.getValue()); + if (mode == Mode::TransformBody) { + Originals.setValues({}); + } + if(!this->BaseFeature.getValue()) { auto body = getFeatureBody(); if(body) { @@ -205,6 +220,10 @@ App::DocumentObjectExecReturn *Transformed::execute() originals.erase(eraseIter, originals.end()); } + if (mode == Mode::TransformToolShapes && originals.empty()) { + return App::DocumentObject::StdReturn; + } + // get transformations from subclass by calling virtual method std::vector transformations; try { @@ -274,45 +293,82 @@ App::DocumentObjectExecReturn *Transformed::execute() return shapeTools; }; - // NOTE: It would be possible to build a compound from all original addShapes/subShapes and then - // transform the compounds as a whole. But we choose to apply the transformations to each - // Original separately. This way it is easier to discover what feature causes a fuse/cut - // to fail. The downside is that performance suffers when there are many originals. But it seems - // safe to assume that in most cases there are few originals and many transformations - for (auto original : originals) - { - // Extract the original shape and determine whether to cut or to fuse - Part::TopoShape fuseShape; - Part::TopoShape cutShape; + switch (mode) { + case Mode::TransformToolShapes : + // NOTE: It would be possible to build a compound from all original addShapes/subShapes and then + // transform the compounds as a whole. But we choose to apply the transformations to each + // Original separately. This way it is easier to discover what feature causes a fuse/cut + // to fail. The downside is that performance suffers when there are many originals. But it seems + // safe to assume that in most cases there are few originals and many transformations + for (auto original : originals) + { + // Extract the original shape and determine whether to cut or to fuse + Part::TopoShape fuseShape; + Part::TopoShape cutShape; - if (original->isDerivedFrom()) { - PartDesign::FeatureAddSub* feature = static_cast(original); - feature->getAddSubShape(fuseShape, cutShape); - if (fuseShape.isNull() && cutShape.isNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Shape of additive/subtractive feature is empty")); - gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv); + if (original->isDerivedFrom()) { + PartDesign::FeatureAddSub* feature = static_cast(original); + feature->getAddSubShape(fuseShape, cutShape); + if (fuseShape.isNull() && cutShape.isNull()) + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Shape of additive/subtractive feature is empty")); + gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv); #ifdef FC_USE_TNP_FIX - if (!fuseShape.isNull()) - fuseShape = fuseShape.makeElementTransform(trsf); - if (!cutShape.isNull()) - cutShape = cutShape.makeElementTransform(trsf); + if (!fuseShape.isNull()) + fuseShape = fuseShape.makeElementTransform(trsf); + if (!cutShape.isNull()) + cutShape = cutShape.makeElementTransform(trsf); #else - if (!fuseShape.isNull()) - fuseShape = fuseShape.makeTransform(trsf); - if (!cutShape.isNull()) - cutShape = cutShape.makeTransform(trsf); + if (!fuseShape.isNull()) + fuseShape = fuseShape.makeTransform(trsf); + if (!cutShape.isNull()) + cutShape = cutShape.makeTransform(trsf); #endif - } - else { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Only additive and subtractive features can be transformed")); - } + } + else { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Only additive and subtractive features can be transformed")); + } - TopoDS_Shape current = support; - if (!fuseShape.isNull()) { + TopoDS_Shape current = support; + if (!fuseShape.isNull()) { + TopTools_ListOfShape shapeArguments; + shapeArguments.Append(current); + TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape()); + if (!shapeTools.IsEmpty()) { + std::unique_ptr mkBool(new BRepAlgoAPI_Fuse()); + mkBool->SetArguments(shapeArguments); + mkBool->SetTools(shapeTools); + mkBool->Build(); + if (!mkBool->IsDone()) { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); + } + current = mkBool->Shape(); + } + } + if (!cutShape.isNull()) { + TopTools_ListOfShape shapeArguments; + shapeArguments.Append(current); + TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape()); + if (!shapeTools.IsEmpty()) { + std::unique_ptr mkBool(new BRepAlgoAPI_Cut()); + mkBool->SetArguments(shapeArguments); + mkBool->SetTools(shapeTools); + mkBool->Build(); + if (!mkBool->IsDone()) { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); + } + current = mkBool->Shape(); + } + } + + support = current; // Use result of this operation for fuse/cut of next original + } + break; + case Mode::TransformBody : + { TopTools_ListOfShape shapeArguments; - shapeArguments.Append(current); - TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape()); + shapeArguments.Append(support); + TopTools_ListOfShape shapeTools = getTransformedCompShape(support); if (!shapeTools.IsEmpty()) { std::unique_ptr mkBool(new BRepAlgoAPI_Fuse()); mkBool->SetArguments(shapeArguments); @@ -321,26 +377,10 @@ App::DocumentObjectExecReturn *Transformed::execute() if (!mkBool->IsDone()) { return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); } - current = mkBool->Shape(); + support = mkBool->Shape(); } + break; } - if (!cutShape.isNull()) { - TopTools_ListOfShape shapeArguments; - shapeArguments.Append(current); - TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape()); - if (!shapeTools.IsEmpty()) { - std::unique_ptr mkBool(new BRepAlgoAPI_Cut()); - mkBool->SetArguments(shapeArguments); - mkBool->SetTools(shapeTools); - mkBool->Build(); - if (!mkBool->IsDone()) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); - } - current = mkBool->Shape(); - } - } - - support = current; // Use result of this operation for fuse/cut of next original } support = refineShapeIfActive(support); diff --git a/src/Mod/PartDesign/App/FeatureTransformed.h b/src/Mod/PartDesign/App/FeatureTransformed.h index d9279de598..b68d2be759 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.h +++ b/src/Mod/PartDesign/App/FeatureTransformed.h @@ -42,12 +42,19 @@ class PartDesignExport Transformed : public PartDesign::Feature PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Transformed); public: + enum class Mode { + TransformToolShapes, + TransformBody + }; + Transformed(); - /** The shapes to be transformed + /** The features to be transformed */ App::PropertyLinkList Originals; + App::PropertyEnumeration TransformMode; + App::PropertyBool Refine; /** From 98d46c41be7375ac340e4ec35b0eed1ee45908d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Mon, 25 Mar 2024 02:07:55 +0100 Subject: [PATCH 03/13] Add a combo box to the transform featues UI for the TransformMode --- .../Gui/TaskTransformedParameters.cpp | 27 ++++- .../Gui/TaskTransformedParameters.h | 1 + .../Gui/TaskTransformedParameters.ui | 107 +++++++++++++----- 3 files changed, 107 insertions(+), 28 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp index d2045b6b57..298efb6c76 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp @@ -91,6 +91,10 @@ void TaskTransformedParameters::setupUI() ui->setupUi(proxy); QMetaObject::connectSlotsByName(this); + connect(ui->comboMode, + qOverload(&QComboBox::activated), + this, + &TaskTransformedParameters::onModeChanged); connect(ui->buttonAddFeature, &QToolButton::toggled, this, @@ -120,8 +124,13 @@ void TaskTransformedParameters::setupUI() // Get the feature data auto pcTransformed = static_cast(getObject()); - std::vector originals = pcTransformed->Originals.getValues(); + using Mode = PartDesign::Transformed::Mode; + auto const mode = static_cast(pcTransformed->TransformMode.getValue()); + ui->groupFeatureList->setEnabled(mode == Mode::TransformToolShapes); + ui->comboMode->setCurrentIndex(static_cast(mode)); + + std::vector originals = pcTransformed->Originals.getValues(); // Fill data into dialog elements for (auto obj : originals) { if (obj) { @@ -272,6 +281,22 @@ bool TaskTransformedParameters::isEnabledTransaction() const return enableTransaction; } +void TaskTransformedParameters::onModeChanged(int mode) +{ + using Mode = PartDesign::Transformed::Mode; + + auto pcTransformed = static_cast(getObject()); + pcTransformed->TransformMode.setValue(mode); + + auto const tmode = static_cast(mode); + ui->groupFeatureList->setEnabled(tmode == Mode::TransformToolShapes); + if (tmode == Mode::TransformBody) { + ui->listWidgetFeatures->clear(); + } + setupTransaction(); + recomputeFeature(); +} + void TaskTransformedParameters::onButtonAddFeature(bool checked) { if (checked) { diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h index a94c30add5..c0fc9826b1 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h @@ -228,6 +228,7 @@ private Q_SLOTS: void onButtonRemoveFeature(bool checked); void onFeatureDeleted(); void indexesMoved(); + void onModeChanged(int mode); private: /** Setup the parameter UI. diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui b/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui index 14bfe88d8a..1e30aa45d4 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui @@ -6,8 +6,8 @@ 0 0 - 262 - 207 + 297 + 248 @@ -15,43 +15,95 @@ - + + + 0 + - + - Add feature - - - true + Mode: - - - Remove feature - - - true + + + + 0 + 0 + + + + Transform tool shapes + + + + + Transform body + + - - - - 16777215 - 120 - - - - List can be reordered by dragging - - - QAbstractItemView::InternalMove - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Add feature + + + true + + + + + + + Remove feature + + + true + + + + + + + + + + 16777215 + 120 + + + + List can be reordered by dragging + + + QAbstractItemView::InternalMove + + + + @@ -70,6 +122,7 @@ + comboMode buttonAddFeature buttonRemoveFeature listWidgetFeatures From 75bba376c41a59959cdb684fb300da23bddd7d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Thu, 23 May 2024 12:23:28 +0200 Subject: [PATCH 04/13] Format code with clang-format --- .../PartDesign/App/FeatureMultiTransform.cpp | 101 ++++--- .../PartDesign/App/FeatureMultiTransform.h | 27 +- src/Mod/PartDesign/App/FeatureScaled.cpp | 30 +- src/Mod/PartDesign/App/FeatureScaled.h | 28 +- src/Mod/PartDesign/App/FeatureTransformed.cpp | 284 ++++++++++-------- src/Mod/PartDesign/App/FeatureTransformed.h | 48 +-- 6 files changed, 297 insertions(+), 221 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureMultiTransform.cpp b/src/Mod/PartDesign/App/FeatureMultiTransform.cpp index 9163668f32..f4f8e05b2b 100644 --- a/src/Mod/PartDesign/App/FeatureMultiTransform.cpp +++ b/src/Mod/PartDesign/App/FeatureMultiTransform.cpp @@ -23,9 +23,9 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include +#include +#include +#include #endif #include "FeatureMultiTransform.h" @@ -35,14 +35,15 @@ using namespace PartDesign; -namespace PartDesign { +namespace PartDesign +{ PROPERTY_SOURCE(PartDesign::MultiTransform, PartDesign::Transformed) MultiTransform::MultiTransform() { - ADD_PROPERTY(Transformations,(nullptr)); + ADD_PROPERTY(Transformations, (nullptr)); Transformations.setSize(0); } @@ -51,8 +52,9 @@ void MultiTransform::positionBySupport() PartDesign::Transformed::positionBySupport(); std::vector transFeatures = Transformations.getValues(); for (auto f : transFeatures) { - if (!(f->isDerivedFrom())) + if (!(f->isDerivedFrom())) { throw Base::TypeError("Transformation features must be subclasses of Transformed"); + } PartDesign::Transformed* transFeature = static_cast(f); transFeature->Placement.setValue(this->Placement.getValue()); @@ -66,27 +68,29 @@ void MultiTransform::positionBySupport() short MultiTransform::mustExecute() const { - if (Transformations.isTouched()) + if (Transformations.isTouched()) { return 1; + } return Transformed::mustExecute(); } -const std::list MultiTransform::getTransformations(const std::vector originals) +const std::list +MultiTransform::getTransformations(const std::vector originals) { std::vector transFeatures = Transformations.getValues(); gp_Pnt cog; if (!originals.empty()) { - // Find centre of gravity of first original - // FIXME: This method will NOT give the expected result for more than one original! - if (auto addFeature = - Base::freecad_dynamic_cast(originals.front())) { - TopoDS_Shape original = addFeature->AddSubShape.getShape().getShape(); + // Find centre of gravity of first original + // FIXME: This method will NOT give the expected result for more than one original! + if (auto addFeature = + Base::freecad_dynamic_cast(originals.front())) { + TopoDS_Shape original = addFeature->AddSubShape.getShape().getShape(); - GProp_GProps props; - BRepGProp::VolumeProperties(original, props); - cog = props.CentreOfMass(); - } + GProp_GProps props; + BRepGProp::VolumeProperties(original, props); + cog = props.CentreOfMass(); + } } std::list result; @@ -94,24 +98,28 @@ const std::list MultiTransform::getTransformations(const std::vector::const_iterator f; for (f = transFeatures.begin(); f != transFeatures.end(); ++f) { - if (!((*f)->isDerivedFrom())) + if (!((*f)->isDerivedFrom())) { throw Base::TypeError("Transformation features must be subclasses of Transformed"); + } PartDesign::Transformed* transFeature = static_cast(*f); std::list newTransformations = transFeature->getTransformations(originals); if (result.empty()) { // First transformation Feature result = newTransformations; - for (std::list::const_iterator nt = newTransformations.begin(); nt != newTransformations.end(); ++nt) { + for (std::list::const_iterator nt = newTransformations.begin(); + nt != newTransformations.end(); + ++nt) { cogs.push_back(cog.Transformed(*nt)); } - } else { + } + else { // Retain a copy of the first set of transformations for iterator ot // We can't iterate through result if we are also adding elements with push_back()! std::list oldTransformations; - result.swap(oldTransformations); // empty result to receive new transformations + result.swap(oldTransformations); // empty result to receive new transformations std::list oldCogs; - cogs.swap(oldCogs); // empty cogs to receive new cogs + cogs.swap(oldCogs); // empty cogs to receive new cogs if ((*f)->is()) { // Diagonal method @@ -123,25 +131,33 @@ const std::list MultiTransform::getTransformations(const std::vector::const_iterator ot = oldTransformations.begin(); std::list::const_iterator oc = oldCogs.begin(); - for (std::list::const_iterator nt = newTransformations.begin(); nt != newTransformations.end(); ++nt) { + for (std::list::const_iterator nt = newTransformations.begin(); + nt != newTransformations.end(); + ++nt) { for (unsigned s = 0; s < sliceLength; s++) { gp_Trsf trans; - double factor = nt->ScaleFactor(); // extract scale factor + double factor = nt->ScaleFactor(); // extract scale factor if (factor > Precision::Confusion()) { - trans.SetScale(*oc, factor); // recreate the scaled transformation to use the correct COG + trans.SetScale(*oc, factor); // recreate the scaled transformation to + // use the correct COG trans = trans * (*ot); - cogs.push_back(*oc); // Scaling does not affect the COG - } else { + cogs.push_back(*oc); // Scaling does not affect the COG + } + else { trans = (*nt) * (*ot); cogs.push_back(oc->Transformed(*nt)); } @@ -150,30 +166,35 @@ const std::list MultiTransform::getTransformations(const std::vector::const_iterator nt = newTransformations.begin(); nt != newTransformations.end(); ++nt) { + for (std::list::const_iterator nt = newTransformations.begin(); + nt != newTransformations.end(); + ++nt) { std::list::const_iterator oc = oldCogs.begin(); - for (std::list::const_iterator ot = oldTransformations.begin(); ot != oldTransformations.end(); ++ot) { + for (std::list::const_iterator ot = oldTransformations.begin(); + ot != oldTransformations.end(); + ++ot) { result.push_back((*nt) * (*ot)); cogs.push_back(oc->Transformed(*nt)); ++oc; } } } - // What about the Additive method: Take the last (set of) transformations and use them as - // "originals" for the next transformationFeature, so that something similar to a sweep - // for transformations could be put together? + // What about the Additive method: Take the last (set of) transformations and use them + // as "originals" for the next transformationFeature, so that something similar to a + // sweep for transformations could be put together? } } return result; } -} +} // namespace PartDesign diff --git a/src/Mod/PartDesign/App/FeatureMultiTransform.h b/src/Mod/PartDesign/App/FeatureMultiTransform.h index b2ceb85693..bfe1e0a658 100644 --- a/src/Mod/PartDesign/App/FeatureMultiTransform.h +++ b/src/Mod/PartDesign/App/FeatureMultiTransform.h @@ -30,7 +30,7 @@ namespace PartDesign { -class PartDesignExport MultiTransform : public PartDesign::Transformed +class PartDesignExport MultiTransform: public PartDesign::Transformed { PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::MultiTransform); @@ -39,30 +39,35 @@ public: App::PropertyLinkList Transformations; - /** @name methods override feature */ + /** @name methods override feature */ //@{ short mustExecute() const override; /// returns the type name of the view provider - const char* getViewProviderName() const override { + const char* getViewProviderName() const override + { return "PartDesignGui::ViewProviderMultiTransform"; } //@} - std::vector getOriginals() const { return Originals.getValues(); } + std::vector getOriginals() const + { + return Originals.getValues(); + } /** Create transformations - * Returns a list containing the product of all transformations of the subfeatures given - * by the Transformations property. Subfeatures can be Mirrored, LinearPattern, PolarPattern and - * Scaled. - */ - const std::list getTransformations(const std::vector originals) override; + * Returns a list containing the product of all transformations of the subfeatures given + * by the Transformations property. Subfeatures can be Mirrored, LinearPattern, PolarPattern and + * Scaled. + */ + const std::list + getTransformations(const std::vector originals) override; protected: void positionBySupport() override; }; -} //namespace PartDesign +} // namespace PartDesign -#endif // PARTDESIGN_FeatureMultiTransform_H +#endif // PARTDESIGN_FeatureMultiTransform_H diff --git a/src/Mod/PartDesign/App/FeatureScaled.cpp b/src/Mod/PartDesign/App/FeatureScaled.cpp index b66f15d980..7675eb12f0 100644 --- a/src/Mod/PartDesign/App/FeatureScaled.cpp +++ b/src/Mod/PartDesign/App/FeatureScaled.cpp @@ -23,9 +23,9 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include +#include +#include +#include #endif #include "FeatureScaled.h" @@ -34,33 +34,37 @@ using namespace PartDesign; -namespace PartDesign { +namespace PartDesign +{ PROPERTY_SOURCE(PartDesign::Scaled, PartDesign::Transformed) Scaled::Scaled() { - ADD_PROPERTY(Factor,(2.0)); - ADD_PROPERTY(Occurrences,(2)); + ADD_PROPERTY(Factor, (2.0)); + ADD_PROPERTY(Occurrences, (2)); } short Scaled::mustExecute() const { - if (Factor.isTouched() || - Occurrences.isTouched()) + if (Factor.isTouched() || Occurrences.isTouched()) { return 1; + } return Transformed::mustExecute(); } -const std::list Scaled::getTransformations(const std::vector originals) +const std::list +Scaled::getTransformations(const std::vector originals) { double factor = Factor.getValue(); - if (factor < Precision::Confusion()) + if (factor < Precision::Confusion()) { throw Base::ValueError("Scaling factor too small"); + } int occurrences = Occurrences.getValue(); - if (occurrences < 2) + if (occurrences < 2) { throw Base::ValueError("At least two occurrences required"); + } double f = (factor - 1.0) / double(occurrences - 1); @@ -81,7 +85,7 @@ const std::list Scaled::getTransformations(const std::vector transformations; gp_Trsf trans; - transformations.push_back(trans); // identity transformation + transformations.push_back(trans); // identity transformation for (int i = 1; i < occurrences; i++) { trans.SetScale(cog, 1.0 + double(i) * f); @@ -91,4 +95,4 @@ const std::list Scaled::getTransformations(const std::vector getTransformations(const std::vector originals) override; + const std::list + getTransformations(const std::vector originals) override; }; -} //namespace PartDesign +} // namespace PartDesign -#endif // PARTDESIGN_FeatureScaled_H +#endif // PARTDESIGN_FeatureScaled_H diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index dfdf030407..004dddb995 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -22,15 +22,15 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif #include @@ -54,7 +54,8 @@ using namespace PartDesign; -namespace PartDesign { +namespace PartDesign +{ PROPERTY_SOURCE(PartDesign::Transformed, PartDesign::Feature) @@ -64,46 +65,59 @@ std::array transformModeEnums = {"Transform tool shapes", Transformed::Transformed() { - ADD_PROPERTY(Originals,(nullptr)); + ADD_PROPERTY(Originals, (nullptr)); Originals.setSize(0); Placement.setStatus(App::Property::ReadOnly, true); ADD_PROPERTY(TransformMode, (static_cast(Mode::TransformToolShapes))); TransformMode.setEnums(transformModeEnums.data()); - ADD_PROPERTY_TYPE(Refine,(0),"Part Design",(App::PropertyType)(App::Prop_None),"Refine shape (clean up redundant edges) after adding/subtracting"); + ADD_PROPERTY_TYPE(Refine, + (0), + "Part Design", + (App::PropertyType)(App::Prop_None), + "Refine shape (clean up redundant edges) after adding/subtracting"); - //init Refine property - Base::Reference hGrp = App::GetApplication().GetUserParameter() - .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/PartDesign"); + // init Refine property + Base::Reference hGrp = App::GetApplication() + .GetUserParameter() + .GetGroup("BaseApp") + ->GetGroup("Preferences") + ->GetGroup("Mod/PartDesign"); this->Refine.setValue(hGrp->GetBool("RefineModel", false)); } void Transformed::positionBySupport() { // TODO May be here better to throw exception (silent=false) (2015-07-27, Fat-Zer) - Part::Feature *support = getBaseObject(/* silent =*/ true); - if (support) + Part::Feature* support = getBaseObject(/* silent =*/true); + if (support) { this->Placement.setValue(support->Placement.getValue()); + } } -Part::Feature* Transformed::getBaseObject(bool silent) const { - Part::Feature *rv = Feature::getBaseObject(/* silent = */ true); +Part::Feature* Transformed::getBaseObject(bool silent) const +{ + Part::Feature* rv = Feature::getBaseObject(/* silent = */ true); if (rv) { return rv; } const char* err = nullptr; - const std::vector & originals = Originals.getValues(); - // NOTE: may be here supposed to be last origin but in order to keep the old behaviour keep here first + const std::vector& originals = Originals.getValues(); + // NOTE: may be here supposed to be last origin but in order to keep the old behaviour keep here + // first App::DocumentObject* firstOriginal = originals.empty() ? nullptr : originals.front(); if (firstOriginal) { - if(firstOriginal->isDerivedFrom(Part::Feature::getClassTypeId())) { + if (firstOriginal->isDerivedFrom(Part::Feature::getClassTypeId())) { rv = static_cast(firstOriginal); - } else { - err = QT_TRANSLATE_NOOP("Exception", "Transformation feature Linked object is not a Part object"); } - } else { + else { + err = QT_TRANSLATE_NOOP("Exception", + "Transformation feature Linked object is not a Part object"); + } + } + else { err = QT_TRANSLATE_NOOP("Exception", "No originals linked to the transformed feature."); } @@ -143,7 +157,7 @@ App::DocumentObject* Transformed::getSketchObject() const } } -void Transformed::Restore(Base::XMLReader &reader) +void Transformed::Restore(Base::XMLReader& reader) { PartDesign::Feature::Restore(reader); } @@ -164,13 +178,15 @@ bool Transformed::isMultiTransformChild() const return false; } -void Transformed::handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop) +void Transformed::handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, + App::Property* prop) { // The property 'Angle' of PolarPattern has changed from PropertyFloat // to PropertyAngle and the property 'Length' has changed to PropertyLength. Base::Type inputType = Base::Type::fromName(TypeName); - if (prop->isDerivedFrom() && - inputType.isDerivedFrom(App::PropertyFloat::getClassTypeId())) { + if (prop->isDerivedFrom() + && inputType.isDerivedFrom(App::PropertyFloat::getClassTypeId())) { // Do not directly call the property's Restore method in case the implementation // has changed. So, create a temporary PropertyFloat object and assign the value. App::PropertyFloat floatProp; @@ -190,7 +206,7 @@ short Transformed::mustExecute() const return PartDesign::Feature::mustExecute(); } -App::DocumentObjectExecReturn *Transformed::execute() +App::DocumentObjectExecReturn* Transformed::execute() { if (isMultiTransformChild()) { return App::DocumentObject::StdReturn; @@ -201,9 +217,9 @@ App::DocumentObjectExecReturn *Transformed::execute() Originals.setValues({}); } - if(!this->BaseFeature.getValue()) { + if (!this->BaseFeature.getValue()) { auto body = getFeatureBody(); - if(body) { + if (body) { body->setBaseProperty(this); } } @@ -211,12 +227,14 @@ App::DocumentObjectExecReturn *Transformed::execute() this->positionBySupport(); std::vector originals = Originals.getValues(); - // Remove suppressed features from the list so the transformations behave as if they are not there + // Remove suppressed features from the list so the transformations behave as if they are not + // there { - auto eraseIter = std::remove_if(originals.begin(), originals.end(), [](App::DocumentObject const* obj) { - auto feature = Base::freecad_dynamic_cast(obj); - return feature != nullptr && feature->Suppressed.getValue(); - }); + auto eraseIter = + std::remove_if(originals.begin(), originals.end(), [](App::DocumentObject const* obj) { + auto feature = Base::freecad_dynamic_cast(obj); + return feature != nullptr && feature->Suppressed.getValue(); + }); originals.erase(eraseIter, originals.end()); } @@ -237,21 +255,25 @@ App::DocumentObjectExecReturn *Transformed::execute() return new App::DocumentObjectExecReturn(e.GetMessageString()); } - if (transformations.empty()) - return App::DocumentObject::StdReturn; // No transformations defined, exit silently + if (transformations.empty()) { + return App::DocumentObject::StdReturn; // No transformations defined, exit silently + } // Get the support Part::Feature* supportFeature; try { supportFeature = getBaseObject(); - } catch (Base::Exception& e) { + } + catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } const Part::TopoShape& supportTopShape = supportFeature->Shape.getShape(); - if (supportTopShape.getShape().IsNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Cannot transform invalid support shape")); + if (supportTopShape.getShape().IsNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Cannot transform invalid support shape")); + } // create an untransformed copy of the support shape Part::TopoShape supportShape(supportTopShape); @@ -261,8 +283,7 @@ App::DocumentObjectExecReturn *Transformed::execute() supportShape.setTransform(Base::Matrix4D()); TopoDS_Shape support = supportShape.getShape(); - auto getTransformedCompShape = [&](const auto& origShape) - { + auto getTransformedCompShape = [&](const auto& origShape) { TopTools_ListOfShape shapeTools; std::vector shapes; @@ -272,13 +293,13 @@ App::DocumentObjectExecReturn *Transformed::execute() ++transformIter; for (; transformIter != transformations.end(); ++transformIter) { - // Make an explicit copy of the shape because the "true" parameter to BRepBuilderAPI_Transform - // seems to be pretty broken + // Make an explicit copy of the shape because the "true" parameter to + // BRepBuilderAPI_Transform seems to be pretty broken BRepBuilderAPI_Copy copy(origShape); TopoDS_Shape shape = copy.Shape(); - BRepBuilderAPI_Transform mkTrf(shape, *transformIter, false); // No need to copy, now + BRepBuilderAPI_Transform mkTrf(shape, *transformIter, false); // No need to copy, now if (!mkTrf.IsDone()) { throw Base::CADKernelError(QT_TRANSLATE_NOOP("Exception", "Transformation failed")); } @@ -287,85 +308,98 @@ App::DocumentObjectExecReturn *Transformed::execute() shapes.emplace_back(shape); } - for (const auto& shape : shapes) + for (const auto& shape : shapes) { shapeTools.Append(shape); + } return shapeTools; }; switch (mode) { - case Mode::TransformToolShapes : - // NOTE: It would be possible to build a compound from all original addShapes/subShapes and then - // transform the compounds as a whole. But we choose to apply the transformations to each - // Original separately. This way it is easier to discover what feature causes a fuse/cut - // to fail. The downside is that performance suffers when there are many originals. But it seems - // safe to assume that in most cases there are few originals and many transformations - for (auto original : originals) - { - // Extract the original shape and determine whether to cut or to fuse - Part::TopoShape fuseShape; - Part::TopoShape cutShape; + case Mode::TransformToolShapes: + // NOTE: It would be possible to build a compound from all original addShapes/subShapes + // and then transform the compounds as a whole. But we choose to apply the + // transformations to each Original separately. This way it is easier to discover what + // feature causes a fuse/cut to fail. The downside is that performance suffers when + // there are many originals. But it seems safe to assume that in most cases there are + // few originals and many transformations + for (auto original : originals) { + // Extract the original shape and determine whether to cut or to fuse + Part::TopoShape fuseShape; + Part::TopoShape cutShape; - if (original->isDerivedFrom()) { - PartDesign::FeatureAddSub* feature = static_cast(original); - feature->getAddSubShape(fuseShape, cutShape); - if (fuseShape.isNull() && cutShape.isNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Shape of additive/subtractive feature is empty")); - gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv); + if (original->isDerivedFrom()) { + PartDesign::FeatureAddSub* feature = + static_cast(original); + feature->getAddSubShape(fuseShape, cutShape); + if (fuseShape.isNull() && cutShape.isNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", + "Shape of additive/subtractive feature is empty")); + } + gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv); #ifdef FC_USE_TNP_FIX - if (!fuseShape.isNull()) - fuseShape = fuseShape.makeElementTransform(trsf); - if (!cutShape.isNull()) - cutShape = cutShape.makeElementTransform(trsf); + if (!fuseShape.isNull()) { + fuseShape = fuseShape.makeElementTransform(trsf); + } + if (!cutShape.isNull()) { + cutShape = cutShape.makeElementTransform(trsf); + } #else - if (!fuseShape.isNull()) - fuseShape = fuseShape.makeTransform(trsf); - if (!cutShape.isNull()) - cutShape = cutShape.makeTransform(trsf); + if (!fuseShape.isNull()) { + fuseShape = fuseShape.makeTransform(trsf); + } + if (!cutShape.isNull()) { + cutShape = cutShape.makeTransform(trsf); + } #endif - } - else { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Only additive and subtractive features can be transformed")); - } - - TopoDS_Shape current = support; - if (!fuseShape.isNull()) { - TopTools_ListOfShape shapeArguments; - shapeArguments.Append(current); - TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape()); - if (!shapeTools.IsEmpty()) { - std::unique_ptr mkBool(new BRepAlgoAPI_Fuse()); - mkBool->SetArguments(shapeArguments); - mkBool->SetTools(shapeTools); - mkBool->Build(); - if (!mkBool->IsDone()) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); - } - current = mkBool->Shape(); } - } - if (!cutShape.isNull()) { - TopTools_ListOfShape shapeArguments; - shapeArguments.Append(current); - TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape()); - if (!shapeTools.IsEmpty()) { - std::unique_ptr mkBool(new BRepAlgoAPI_Cut()); - mkBool->SetArguments(shapeArguments); - mkBool->SetTools(shapeTools); - mkBool->Build(); - if (!mkBool->IsDone()) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); - } - current = mkBool->Shape(); + else { + return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( + "Exception", + "Only additive and subtractive features can be transformed")); } - } - support = current; // Use result of this operation for fuse/cut of next original - } - break; - case Mode::TransformBody : - { + TopoDS_Shape current = support; + if (!fuseShape.isNull()) { + TopTools_ListOfShape shapeArguments; + shapeArguments.Append(current); + TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape()); + if (!shapeTools.IsEmpty()) { + std::unique_ptr mkBool( + new BRepAlgoAPI_Fuse()); + mkBool->SetArguments(shapeArguments); + mkBool->SetTools(shapeTools); + mkBool->Build(); + if (!mkBool->IsDone()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); + } + current = mkBool->Shape(); + } + } + if (!cutShape.isNull()) { + TopTools_ListOfShape shapeArguments; + shapeArguments.Append(current); + TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape()); + if (!shapeTools.IsEmpty()) { + std::unique_ptr mkBool(new BRepAlgoAPI_Cut()); + mkBool->SetArguments(shapeArguments); + mkBool->SetTools(shapeTools); + mkBool->Build(); + if (!mkBool->IsDone()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); + } + current = mkBool->Shape(); + } + } + + support = current; // Use result of this operation for fuse/cut of next original + } + break; + case Mode::TransformBody: { TopTools_ListOfShape shapeArguments; shapeArguments.Append(support); TopTools_ListOfShape shapeTools = getTransformedCompShape(support); @@ -375,7 +409,8 @@ App::DocumentObjectExecReturn *Transformed::execute() mkBool->SetTools(shapeTools); mkBool->Build(); if (!mkBool->IsDone()) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); } support = mkBool->Shape(); } @@ -414,8 +449,9 @@ TopoDS_Shape Transformed::refineShapeIfActive(const TopoDS_Shape& oldShape) cons return oldShape; } -void Transformed::divideTools(const std::vector &toolsIn, std::vector &individualsOut, - TopoDS_Compound &compoundOut) const +void Transformed::divideTools(const std::vector& toolsIn, + std::vector& individualsOut, + TopoDS_Compound& compoundOut) const { using ShapeBoundPair = std::pair; using PairList = std::list; @@ -435,16 +471,16 @@ void Transformed::divideTools(const std::vector &toolsIn, std::vec BRep_Builder builder; builder.MakeCompound(compoundOut); - while(!pairList.empty()) { + while (!pairList.empty()) { PairVector currentGroup; currentGroup.push_back(pairList.front()); pairList.pop_front(); PairList::iterator it = pairList.begin(); - while(it != pairList.end()) { + while (it != pairList.end()) { PairVector::const_iterator groupIt; bool found(false); for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) { - if (!(*it).second.IsOut((*groupIt).second)) {//touching means is out. + if (!(*it).second.IsOut((*groupIt).second)) { // touching means is out. found = true; break; } @@ -452,7 +488,7 @@ void Transformed::divideTools(const std::vector &toolsIn, std::vec if (found) { currentGroup.push_back(*it); pairList.erase(it); - it=pairList.begin(); + it = pairList.begin(); continue; } ++it; @@ -463,8 +499,9 @@ void Transformed::divideTools(const std::vector &toolsIn, std::vec } else { PairVector::const_iterator groupIt; - for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) + for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) { individualsOut.push_back((*groupIt).first); + } } } } @@ -475,10 +512,11 @@ TopoDS_Shape Transformed::getRemainingSolids(const TopoDS_Shape& shape) TopoDS_Compound compShape; builder.MakeCompound(compShape); - if (shape.IsNull()) + if (shape.IsNull()) { Standard_Failure::Raise("Shape is null"); + } TopExp_Explorer xp; - xp.Init(shape,TopAbs_SOLID); + xp.Init(shape, TopAbs_SOLID); xp.Next(); // skip the first for (; xp.More(); xp.Next()) { @@ -488,4 +526,4 @@ TopoDS_Shape Transformed::getRemainingSolids(const TopoDS_Shape& shape) return TopoDS_Shape(std::move(compShape)); } -} +} // namespace PartDesign diff --git a/src/Mod/PartDesign/App/FeatureTransformed.h b/src/Mod/PartDesign/App/FeatureTransformed.h index b68d2be759..0374bb5d47 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.h +++ b/src/Mod/PartDesign/App/FeatureTransformed.h @@ -37,12 +37,13 @@ namespace PartDesign * Abstract superclass of all features that are created by transformation of another feature * Transformations are translation, rotation and mirroring */ -class PartDesignExport Transformed : public PartDesign::Feature +class PartDesignExport Transformed: public PartDesign::Feature { PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Transformed); public: - enum class Mode { + enum class Mode + { TransformToolShapes, TransformBody }; @@ -64,51 +65,56 @@ public: * silently return a nullptr, otherwise throw Base::Exception. * Default is false. */ - Part::Feature* getBaseObject(bool silent=false) const override; + Part::Feature* getBaseObject(bool silent = false) const override; /// Return the sketch of the first original App::DocumentObject* getSketchObject() const; /// Get the list of transformations describing the members of the pattern // Note: Only the Scaled feature requires the originals - virtual const std::list getTransformations(const std::vector /*originals*/) { - return std::list(); // Default method + virtual const std::list + getTransformations(const std::vector /*originals*/) + { + return std::list(); // Default method } - /** @name methods override feature */ + /** @name methods override feature */ //@{ /** Recalculate the feature - * Gets the transformations from the virtual getTransformations() method of the sub class - * and applies them to every member of Originals. The total number of copies including - * the untransformed Originals will be sizeof(Originals) times sizeof(getTransformations()) - * If Originals is empty, execute() returns immediately without doing anything as - * the actual processing will happen in the MultiTransform feature - */ - App::DocumentObjectExecReturn *execute() override; + * Gets the transformations from the virtual getTransformations() method of the sub class + * and applies them to every member of Originals. The total number of copies including + * the untransformed Originals will be sizeof(Originals) times sizeof(getTransformations()) + * If Originals is empty, execute() returns immediately without doing anything as + * the actual processing will happen in the MultiTransform feature + */ + App::DocumentObjectExecReturn* execute() override; short mustExecute() const override; //@} /** returns the compound of the shapes that were rejected during the last execute - * because they did not overlap with the support - */ + * because they did not overlap with the support + */ TopoDS_Shape rejected; protected: - void Restore(Base::XMLReader &reader) override; - void handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop) override; + void Restore(Base::XMLReader& reader) override; + void handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, + App::Property* prop) override; /// Return true if this feature is a child of a MultiTransform bool isMultiTransformChild() const; virtual void positionBySupport(); TopoDS_Shape refineShapeIfActive(const TopoDS_Shape&) const; - void divideTools(const std::vector &toolsIn, std::vector &individualsOut, - TopoDS_Compound &compoundOut) const; + void divideTools(const std::vector& toolsIn, + std::vector& individualsOut, + TopoDS_Compound& compoundOut) const; static TopoDS_Shape getRemainingSolids(const TopoDS_Shape&); private: }; -} //namespace PartDesign +} // namespace PartDesign -#endif // PARTDESIGN_FeatureTransformed_H +#endif // PARTDESIGN_FeatureTransformed_H From 5c0aaa40d9f5bbe75ca005a0a70a06a822d83a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Mon, 25 Mar 2024 11:21:02 +0100 Subject: [PATCH 05/13] Modernize code --- .../PartDesign/App/FeatureMultiTransform.cpp | 50 +++--- src/Mod/PartDesign/App/FeatureScaled.cpp | 4 +- src/Mod/PartDesign/App/FeatureTransformed.cpp | 149 ++++++++---------- 3 files changed, 91 insertions(+), 112 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureMultiTransform.cpp b/src/Mod/PartDesign/App/FeatureMultiTransform.cpp index f4f8e05b2b..14cc57177b 100644 --- a/src/Mod/PartDesign/App/FeatureMultiTransform.cpp +++ b/src/Mod/PartDesign/App/FeatureMultiTransform.cpp @@ -52,10 +52,11 @@ void MultiTransform::positionBySupport() PartDesign::Transformed::positionBySupport(); std::vector transFeatures = Transformations.getValues(); for (auto f : transFeatures) { - if (!(f->isDerivedFrom())) { + auto transFeature = Base::freecad_dynamic_cast(f); + if (!transFeature) { throw Base::TypeError("Transformation features must be subclasses of Transformed"); } - PartDesign::Transformed* transFeature = static_cast(f); + transFeature->Placement.setValue(this->Placement.getValue()); // To avoid that a linked transform feature stays touched after a recompute @@ -95,22 +96,19 @@ MultiTransform::getTransformations(const std::vector origi std::list result; std::list cogs; - std::vector::const_iterator f; - for (f = transFeatures.begin(); f != transFeatures.end(); ++f) { - if (!((*f)->isDerivedFrom())) { + for (auto const& f : transFeatures) { + auto transFeature = Base::freecad_dynamic_cast(f); + if (!transFeature) { throw Base::TypeError("Transformation features must be subclasses of Transformed"); } - PartDesign::Transformed* transFeature = static_cast(*f); - std::list newTransformations = transFeature->getTransformations(originals); + std::list newTransformations = transFeature->getTransformations(originals); if (result.empty()) { // First transformation Feature result = newTransformations; - for (std::list::const_iterator nt = newTransformations.begin(); - nt != newTransformations.end(); - ++nt) { - cogs.push_back(cog.Transformed(*nt)); + for (auto nt : newTransformations) { + cogs.push_back(cog.Transformed(nt)); } } else { @@ -121,7 +119,7 @@ MultiTransform::getTransformations(const std::vector origi std::list oldCogs; cogs.swap(oldCogs); // empty cogs to receive new cogs - if ((*f)->is()) { + if (transFeature->is()) { // Diagonal method // Multiply every element in the old transformations' slices with the corresponding // element in the newTransformations. Example: @@ -141,15 +139,13 @@ MultiTransform::getTransformations(const std::vector origi } unsigned sliceLength = oldTransformations.size() / newTransformations.size(); - std::list::const_iterator ot = oldTransformations.begin(); - std::list::const_iterator oc = oldCogs.begin(); + auto ot = oldTransformations.begin(); + auto oc = oldCogs.begin(); - for (std::list::const_iterator nt = newTransformations.begin(); - nt != newTransformations.end(); - ++nt) { + for (auto const& nt : newTransformations) { for (unsigned s = 0; s < sliceLength; s++) { gp_Trsf trans; - double factor = nt->ScaleFactor(); // extract scale factor + double factor = nt.ScaleFactor(); // extract scale factor if (factor > Precision::Confusion()) { trans.SetScale(*oc, factor); // recreate the scaled transformation to @@ -158,8 +154,8 @@ MultiTransform::getTransformations(const std::vector origi cogs.push_back(*oc); // Scaling does not affect the COG } else { - trans = (*nt) * (*ot); - cogs.push_back(oc->Transformed(*nt)); + trans = nt * (*ot); + cogs.push_back(oc->Transformed(nt)); } result.push_back(trans); ++ot; @@ -174,16 +170,12 @@ MultiTransform::getTransformations(const std::vector origi // a11 a12 b1 a11*b1 a12*b1 a11*b2 a12*b2 a11*b3 a12*b3 // a21 a22 mul b2 = a21*b1 a22*b1 a21*b2 a22*b2 a21*b3 a22*b3 // b3 - for (std::list::const_iterator nt = newTransformations.begin(); - nt != newTransformations.end(); - ++nt) { - std::list::const_iterator oc = oldCogs.begin(); + for (auto const& nt : newTransformations) { + auto oc = oldCogs.begin(); - for (std::list::const_iterator ot = oldTransformations.begin(); - ot != oldTransformations.end(); - ++ot) { - result.push_back((*nt) * (*ot)); - cogs.push_back(oc->Transformed(*nt)); + for (auto const& ot : oldTransformations) { + result.push_back(nt * ot); + cogs.push_back(oc->Transformed(nt)); ++oc; } } diff --git a/src/Mod/PartDesign/App/FeatureScaled.cpp b/src/Mod/PartDesign/App/FeatureScaled.cpp index 7675eb12f0..a6a0b8fd7d 100644 --- a/src/Mod/PartDesign/App/FeatureScaled.cpp +++ b/src/Mod/PartDesign/App/FeatureScaled.cpp @@ -57,11 +57,11 @@ short Scaled::mustExecute() const const std::list Scaled::getTransformations(const std::vector originals) { - double factor = Factor.getValue(); + double const factor = Factor.getValue(); if (factor < Precision::Confusion()) { throw Base::ValueError("Scaling factor too small"); } - int occurrences = Occurrences.getValue(); + int const occurrences = Occurrences.getValue(); if (occurrences < 2) { throw Base::ValueError("At least two occurrences required"); } diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index 004dddb995..619301fc5b 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -109,10 +109,8 @@ Part::Feature* Transformed::getBaseObject(bool silent) const // first App::DocumentObject* firstOriginal = originals.empty() ? nullptr : originals.front(); if (firstOriginal) { - if (firstOriginal->isDerivedFrom(Part::Feature::getClassTypeId())) { - rv = static_cast(firstOriginal); - } - else { + rv = Base::freecad_dynamic_cast(firstOriginal); + if (!rv) { err = QT_TRANSLATE_NOOP("Exception", "Transformation feature Linked object is not a Part object"); } @@ -131,30 +129,25 @@ Part::Feature* Transformed::getBaseObject(bool silent) const App::DocumentObject* Transformed::getSketchObject() const { std::vector originals = Originals.getValues(); - if (!originals.empty() && originals.front()->isDerivedFrom()) { - return (static_cast(originals.front()))->getVerifiedSketch(true); + DocumentObject const* firstOriginal = !originals.empty() ? originals.front() : nullptr; + + if (auto feature = Base::freecad_dynamic_cast(firstOriginal)) { + return feature->getVerifiedSketch(true); } - else if (!originals.empty() && originals.front()->isDerivedFrom()) { + if (Base::freecad_dynamic_cast(firstOriginal)) { return nullptr; } - else if (this->isDerivedFrom()) { - // if Originals is empty then try the linear pattern's Direction property - const LinearPattern* pattern = static_cast(this); + if (auto pattern = Base::freecad_dynamic_cast(this)) { return pattern->Direction.getValue(); } - else if (this->isDerivedFrom()) { - // if Originals is empty then try the polar pattern's Axis property - const PolarPattern* pattern = static_cast(this); + if (auto pattern = Base::freecad_dynamic_cast(this)) { return pattern->Axis.getValue(); } - else if (this->isDerivedFrom()) { - // if Originals is empty then try the mirror pattern's MirrorPlane property - const Mirrored* pattern = static_cast(this); + if (auto pattern = Base::freecad_dynamic_cast(this)) { return pattern->MirrorPlane.getValue(); } - else { - return nullptr; - } + + return nullptr; } void Transformed::Restore(Base::XMLReader& reader) @@ -185,13 +178,13 @@ void Transformed::handleChangedPropertyType(Base::XMLReader& reader, // The property 'Angle' of PolarPattern has changed from PropertyFloat // to PropertyAngle and the property 'Length' has changed to PropertyLength. Base::Type inputType = Base::Type::fromName(TypeName); - if (prop->isDerivedFrom() - && inputType.isDerivedFrom(App::PropertyFloat::getClassTypeId())) { + if (auto property = Base::freecad_dynamic_cast(prop); + property != nullptr && inputType.isDerivedFrom(App::PropertyFloat::getClassTypeId())) { // Do not directly call the property's Restore method in case the implementation // has changed. So, create a temporary PropertyFloat object and assign the value. App::PropertyFloat floatProp; floatProp.Restore(reader); - static_cast(prop)->setValue(floatProp.getValue()); + property->setValue(floatProp.getValue()); } else { PartDesign::Feature::handleChangedPropertyType(reader, TypeName, prop); @@ -260,7 +253,7 @@ App::DocumentObjectExecReturn* Transformed::execute() } // Get the support - Part::Feature* supportFeature; + Part::Feature* supportFeature = nullptr; try { supportFeature = getBaseObject(); @@ -287,7 +280,7 @@ App::DocumentObjectExecReturn* Transformed::execute() TopTools_ListOfShape shapeTools; std::vector shapes; - std::vector::const_iterator transformIter = transformations.begin(); + auto transformIter = transformations.cbegin(); // First transformation is skipped since it should not be part of the toolShape. ++transformIter; @@ -328,55 +321,52 @@ App::DocumentObjectExecReturn* Transformed::execute() Part::TopoShape fuseShape; Part::TopoShape cutShape; - if (original->isDerivedFrom()) { - PartDesign::FeatureAddSub* feature = - static_cast(original); - feature->getAddSubShape(fuseShape, cutShape); - if (fuseShape.isNull() && cutShape.isNull()) { - return new App::DocumentObjectExecReturn( - QT_TRANSLATE_NOOP("Exception", - "Shape of additive/subtractive feature is empty")); - } - gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv); -#ifdef FC_USE_TNP_FIX - if (!fuseShape.isNull()) { - fuseShape = fuseShape.makeElementTransform(trsf); - } - if (!cutShape.isNull()) { - cutShape = cutShape.makeElementTransform(trsf); - } -#else - if (!fuseShape.isNull()) { - fuseShape = fuseShape.makeTransform(trsf); - } - if (!cutShape.isNull()) { - cutShape = cutShape.makeTransform(trsf); - } - -#endif - } - else { + auto feature = Base::freecad_dynamic_cast(original); + if (!feature) { return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( "Exception", "Only additive and subtractive features can be transformed")); } + feature->getAddSubShape(fuseShape, cutShape); + if (fuseShape.isNull() && cutShape.isNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", + "Shape of additive/subtractive feature is empty")); + } + gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv); +#ifdef FC_USE_TNP_FIX + if (!fuseShape.isNull()) { + fuseShape = fuseShape.makeElementTransform(trsf); + } + if (!cutShape.isNull()) { + cutShape = cutShape.makeElementTransform(trsf); + } +#else + if (!fuseShape.isNull()) { + fuseShape = fuseShape.makeTransform(trsf); + } + if (!cutShape.isNull()) { + cutShape = cutShape.makeTransform(trsf); + } + +#endif + TopoDS_Shape current = support; if (!fuseShape.isNull()) { TopTools_ListOfShape shapeArguments; shapeArguments.Append(current); TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape()); if (!shapeTools.IsEmpty()) { - std::unique_ptr mkBool( - new BRepAlgoAPI_Fuse()); - mkBool->SetArguments(shapeArguments); - mkBool->SetTools(shapeTools); - mkBool->Build(); - if (!mkBool->IsDone()) { + BRepAlgoAPI_Fuse mkBool; + mkBool.SetArguments(shapeArguments); + mkBool.SetTools(shapeTools); + mkBool.Build(); + if (!mkBool.IsDone()) { return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); } - current = mkBool->Shape(); + current = mkBool.Shape(); } } if (!cutShape.isNull()) { @@ -384,15 +374,15 @@ App::DocumentObjectExecReturn* Transformed::execute() shapeArguments.Append(current); TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape()); if (!shapeTools.IsEmpty()) { - std::unique_ptr mkBool(new BRepAlgoAPI_Cut()); - mkBool->SetArguments(shapeArguments); - mkBool->SetTools(shapeTools); - mkBool->Build(); - if (!mkBool->IsDone()) { + BRepAlgoAPI_Cut mkBool; + mkBool.SetArguments(shapeArguments); + mkBool.SetTools(shapeTools); + mkBool.Build(); + if (!mkBool.IsDone()) { return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); } - current = mkBool->Shape(); + current = mkBool.Shape(); } } @@ -404,15 +394,15 @@ App::DocumentObjectExecReturn* Transformed::execute() shapeArguments.Append(support); TopTools_ListOfShape shapeTools = getTransformedCompShape(support); if (!shapeTools.IsEmpty()) { - std::unique_ptr mkBool(new BRepAlgoAPI_Fuse()); - mkBool->SetArguments(shapeArguments); - mkBool->SetTools(shapeTools); - mkBool->Build(); - if (!mkBool->IsDone()) { + BRepAlgoAPI_Fuse mkBool; + mkBool.SetArguments(shapeArguments); + mkBool.SetTools(shapeTools); + mkBool.Build(); + if (!mkBool.IsDone()) { return new App::DocumentObjectExecReturn( QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); } - support = mkBool->Shape(); + support = mkBool.Shape(); } break; } @@ -449,9 +439,9 @@ TopoDS_Shape Transformed::refineShapeIfActive(const TopoDS_Shape& oldShape) cons return oldShape; } -void Transformed::divideTools(const std::vector& toolsIn, +void divideTools(const std::vector& toolsIn, std::vector& individualsOut, - TopoDS_Compound& compoundOut) const + TopoDS_Compound& compoundOut) { using ShapeBoundPair = std::pair; using PairList = std::list; @@ -459,8 +449,7 @@ void Transformed::divideTools(const std::vector& toolsIn, PairList pairList; - std::vector::const_iterator it; - for (it = toolsIn.begin(); it != toolsIn.end(); ++it) { + for (auto it = toolsIn.begin(); it != toolsIn.end(); ++it) { Bnd_Box bound; BRepBndLib::Add(*it, bound); bound.SetGap(0.0); @@ -475,11 +464,10 @@ void Transformed::divideTools(const std::vector& toolsIn, PairVector currentGroup; currentGroup.push_back(pairList.front()); pairList.pop_front(); - PairList::iterator it = pairList.begin(); + auto it = pairList.begin(); while (it != pairList.end()) { - PairVector::const_iterator groupIt; bool found(false); - for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) { + for (auto groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) { if (!(*it).second.IsOut((*groupIt).second)) { // touching means is out. found = true; break; @@ -498,8 +486,7 @@ void Transformed::divideTools(const std::vector& toolsIn, builder.Add(compoundOut, currentGroup.front().first); } else { - PairVector::const_iterator groupIt; - for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) { + for (auto groupIt = currentGroup.cbegin(); groupIt != currentGroup.end(); ++groupIt) { individualsOut.push_back((*groupIt).first); } } @@ -523,7 +510,7 @@ TopoDS_Shape Transformed::getRemainingSolids(const TopoDS_Shape& shape) builder.Add(compShape, xp.Current()); } - return TopoDS_Shape(std::move(compShape)); + return {std::move(compShape)}; } } // namespace PartDesign From f2c3ade1f592eef518335bc4ad43dc02a5eafa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Mon, 25 Mar 2024 03:14:48 +0100 Subject: [PATCH 06/13] Remove unused function --- src/Mod/PartDesign/App/FeatureTransformed.cpp | 54 ------------------- src/Mod/PartDesign/App/FeatureTransformed.h | 3 -- 2 files changed, 57 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index 619301fc5b..78dc14fb94 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -439,60 +439,6 @@ TopoDS_Shape Transformed::refineShapeIfActive(const TopoDS_Shape& oldShape) cons return oldShape; } -void divideTools(const std::vector& toolsIn, - std::vector& individualsOut, - TopoDS_Compound& compoundOut) -{ - using ShapeBoundPair = std::pair; - using PairList = std::list; - using PairVector = std::vector; - - PairList pairList; - - for (auto it = toolsIn.begin(); it != toolsIn.end(); ++it) { - Bnd_Box bound; - BRepBndLib::Add(*it, bound); - bound.SetGap(0.0); - ShapeBoundPair temp = std::make_pair(*it, bound); - pairList.push_back(temp); - } - - BRep_Builder builder; - builder.MakeCompound(compoundOut); - - while (!pairList.empty()) { - PairVector currentGroup; - currentGroup.push_back(pairList.front()); - pairList.pop_front(); - auto it = pairList.begin(); - while (it != pairList.end()) { - bool found(false); - for (auto groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) { - if (!(*it).second.IsOut((*groupIt).second)) { // touching means is out. - found = true; - break; - } - } - if (found) { - currentGroup.push_back(*it); - pairList.erase(it); - it = pairList.begin(); - continue; - } - ++it; - } - - if (currentGroup.size() == 1) { - builder.Add(compoundOut, currentGroup.front().first); - } - else { - for (auto groupIt = currentGroup.cbegin(); groupIt != currentGroup.end(); ++groupIt) { - individualsOut.push_back((*groupIt).first); - } - } - } -} - TopoDS_Shape Transformed::getRemainingSolids(const TopoDS_Shape& shape) { BRep_Builder builder; diff --git a/src/Mod/PartDesign/App/FeatureTransformed.h b/src/Mod/PartDesign/App/FeatureTransformed.h index 0374bb5d47..a549b449bd 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.h +++ b/src/Mod/PartDesign/App/FeatureTransformed.h @@ -106,9 +106,6 @@ protected: bool isMultiTransformChild() const; virtual void positionBySupport(); TopoDS_Shape refineShapeIfActive(const TopoDS_Shape&) const; - void divideTools(const std::vector& toolsIn, - std::vector& individualsOut, - TopoDS_Compound& compoundOut) const; static TopoDS_Shape getRemainingSolids(const TopoDS_Shape&); private: From f021ea6c7fe88fd0247489d3a659961667372743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Mon, 25 Mar 2024 19:37:11 +0100 Subject: [PATCH 07/13] Fix ambiguous setValue() call --- src/Mod/PartDesign/App/FeatureTransformed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index 78dc14fb94..ea47fa01c8 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -69,7 +69,7 @@ Transformed::Transformed() Originals.setSize(0); Placement.setStatus(App::Property::ReadOnly, true); - ADD_PROPERTY(TransformMode, (static_cast(Mode::TransformToolShapes))); + ADD_PROPERTY(TransformMode, (static_cast(Mode::TransformToolShapes))); TransformMode.setEnums(transformModeEnums.data()); ADD_PROPERTY_TYPE(Refine, From f4aa5879cc2c9ce7b0d00428a386c7905431db58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Thu, 28 Mar 2024 11:43:32 +0100 Subject: [PATCH 08/13] Fix solid feature detection for "Transform Body" mode Because the Origins property is empty in "Transform Body" mode, the features are detected as not solid. This messes with the feature order on insertions and moves. This is fixed by calling the isMultitransformChild() method of the Transformed features instead of checking the Origins property in the Body code. --- src/Mod/PartDesign/App/Body.cpp | 37 +++++++-------------- src/Mod/PartDesign/App/Body.h | 3 -- src/Mod/PartDesign/App/FeatureTransformed.h | 5 +-- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp index ff92107892..95e101d549 100644 --- a/src/Mod/PartDesign/App/Body.cpp +++ b/src/Mod/PartDesign/App/Body.cpp @@ -183,35 +183,22 @@ bool Body::isAfterInsertPoint(App::DocumentObject* feature) { } } -bool Body::isMemberOfMultiTransform(const App::DocumentObject* obj) -{ - if (!obj) - return false; - - // ORIGINAL COMMENT: - // This can be recognized because the Originals property is empty (it is contained - // in the MultiTransform instead) - // COMMENT ON THE COMMENT: - // This is wrong because at the creation (addObject) and before assigning the originals, that - // is when this code is executed, the originals property is indeed empty. - // - // However, for the purpose of setting the base feature, the transform feature has been modified - // to auto set it when the originals are not null. See: - // App::DocumentObjectExecReturn *Transformed::execute(void) - // - return (obj->isDerivedFrom() && - static_cast(obj)->Originals.getValues().empty()); -} - bool Body::isSolidFeature(const App::DocumentObject *obj) { - if (!obj) + if (!obj) { return false; + } - if (obj->isDerivedFrom() && - !PartDesign::Feature::isDatum(obj)) { - // Transformed Features inside a MultiTransform are not solid features - return !isMemberOfMultiTransform(obj); + if (obj->isDerivedFrom()) { + if (PartDesign::Feature::isDatum(obj)) { + // Datum objects are not solid + return false; + } + if (auto transFeature = Base::freecad_dynamic_cast(obj)) { + // Transformed Features inside a MultiTransform are not solid features + return !transFeature->isMultiTransformChild(); + } + return true; } return false;//DeepSOIC: work-in-progress? } diff --git a/src/Mod/PartDesign/App/Body.h b/src/Mod/PartDesign/App/Body.h index e36f751960..b905fd2997 100644 --- a/src/Mod/PartDesign/App/Body.h +++ b/src/Mod/PartDesign/App/Body.h @@ -91,9 +91,6 @@ public: */ bool isAfterInsertPoint(App::DocumentObject* feature); - /// Return true if the given feature is member of a MultiTransform feature - static bool isMemberOfMultiTransform(const App::DocumentObject *obj); - /** * Return true if the given feature is a solid feature allowed in a Body. Currently this is only valid * for features derived from PartDesign::Feature diff --git a/src/Mod/PartDesign/App/FeatureTransformed.h b/src/Mod/PartDesign/App/FeatureTransformed.h index a549b449bd..18c93e8194 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.h +++ b/src/Mod/PartDesign/App/FeatureTransformed.h @@ -70,6 +70,9 @@ public: /// Return the sketch of the first original App::DocumentObject* getSketchObject() const; + /// Return true if this feature is a child of a MultiTransform + bool isMultiTransformChild() const; + /// Get the list of transformations describing the members of the pattern // Note: Only the Scaled feature requires the originals virtual const std::list @@ -102,8 +105,6 @@ protected: const char* TypeName, App::Property* prop) override; - /// Return true if this feature is a child of a MultiTransform - bool isMultiTransformChild() const; virtual void positionBySupport(); TopoDS_Shape refineShapeIfActive(const TopoDS_Shape&) const; static TopoDS_Shape getRemainingSolids(const TopoDS_Shape&); From ebad053c7026336e0fad7f7fefa9b7cb476a0634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Wed, 1 May 2024 15:58:48 +0200 Subject: [PATCH 09/13] Change MultiTransform child detection to the previous method isMultiTransformChild() tried to do a better job by checking for a parent MultiTransform in the dependency list, but this is unusable during initialization, when these dependencies are not established. The method is changed back to the previous one which only checks for default property values. This will give false results during initialization but not cause problems. --- src/Mod/PartDesign/App/FeatureTransformed.cpp | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index ea47fa01c8..fe68f30155 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -157,6 +157,9 @@ void Transformed::Restore(Base::XMLReader& reader) bool Transformed::isMultiTransformChild() const { + // Checking for a MultiTransform in the dependency list is not reliable on initialization + // because the dependencies are only established after creation. + /* for (auto const* obj : getInList()) { auto mt = Base::freecad_dynamic_cast(obj); if (!mt) { @@ -168,6 +171,14 @@ bool Transformed::isMultiTransformChild() const return true; } } + */ + + // instead check for default property values because these are invalid for a standalone transform feature. + // This will mislabel standalone features during the initialization phase. + if (TransformMode.getValue() == 0 && Originals.getValue().empty()) { + return true; + } + return false; } @@ -210,15 +221,6 @@ App::DocumentObjectExecReturn* Transformed::execute() Originals.setValues({}); } - if (!this->BaseFeature.getValue()) { - auto body = getFeatureBody(); - if (body) { - body->setBaseProperty(this); - } - } - - this->positionBySupport(); - std::vector originals = Originals.getValues(); // Remove suppressed features from the list so the transformations behave as if they are not // there @@ -235,6 +237,15 @@ App::DocumentObjectExecReturn* Transformed::execute() return App::DocumentObject::StdReturn; } + if (!this->BaseFeature.getValue()) { + auto body = getFeatureBody(); + if (body) { + body->setBaseProperty(this); + } + } + + this->positionBySupport(); + // get transformations from subclass by calling virtual method std::vector transformations; try { From 40a1487cb5b7535923bbd144a382f54c6f0add45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Sat, 18 May 2024 18:45:24 +0200 Subject: [PATCH 10/13] Set TransformMode to "Transform body" if no features are selected on creation of transform features --- src/Mod/PartDesign/Gui/Command.cpp | 67 +++++++++--------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 77ec3e6381..53b2715b6e 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -1877,14 +1877,6 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const { std::string FeatName = cmd->getUniqueObjectName(which.c_str(), pcActiveBody); - auto accepter = [=](std::vector features) -> bool { - - if (features.empty()) - return false; - - return true; - }; - auto worker = [=](std::vector features) { std::stringstream str; str << cmd->getObjectCmd(FeatName.c_str(), pcActiveBody->getDocument()) << ".Originals = ["; @@ -1904,6 +1896,10 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str()); + if (features.empty()) { + FCMD_OBJ_CMD(Feat,"TransformMode = \"Transform body\""); + } + // TODO What is this function supposed to do? (2015-08-05, Fat-Zer) func(Feat, features); @@ -1943,6 +1939,9 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const Gui::Control().closeDialog(); Gui::Selection().clearSelection(); + auto accepter = [](std::vector) -> bool { + return true; + }; Gui::Control().showDialog(new PartDesignGui::TaskDlgFeaturePick(features, status, accepter, worker, false)); return; } else if (features.empty()) { @@ -1999,24 +1998,17 @@ void CmdPartDesignMirrored::activated(int iMsg) return; Gui::Command* cmd = this; - auto worker = [cmd](App::DocumentObject *Feat, std::vector features) { - - if (features.empty()) - return; - + auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector features) { bool direction = false; - if (features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { - Part::Part2DObject *sketch = (static_cast(features.front()))->getVerifiedSketch(/* silent =*/ true); + if (!features.empty() && features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { + Part::Part2DObject* sketch = (static_cast(features.front()))->getVerifiedSketch(/* silent =*/ true); if (sketch) { FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<(Part::BodyBase::findBodyOf(features.front())); - if (body) { - FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<getOrigin()->getXY())<<", [''])"); - } + FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<getOrigin()->getXY())<<", [''])"); } finishTransformed(cmd, Feat); @@ -2061,13 +2053,9 @@ void CmdPartDesignLinearPattern::activated(int iMsg) return; Gui::Command* cmd = this; - auto worker = [cmd](App::DocumentObject *Feat, std::vector features) { - - if (!Feat || features.empty()) - return; - + auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector features) { bool direction = false; - if (features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { + if (!features.empty() && features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { Part::Part2DObject *sketch = (static_cast(features.front()))->getVerifiedSketch(/* silent =*/ true); if (sketch) { FCMD_OBJ_CMD(Feat,"Direction = ("<(Part::BodyBase::findBodyOf(features.front())); - if (body) { - FCMD_OBJ_CMD(Feat,"Direction = ("<getOrigin()->getX())<<",[''])"); - } + FCMD_OBJ_CMD(Feat,"Direction = ("<getOrigin()->getX())<<",[''])"); } FCMD_OBJ_CMD(Feat,"Length = 100"); FCMD_OBJ_CMD(Feat,"Occurrences = 2"); @@ -2125,13 +2110,10 @@ void CmdPartDesignPolarPattern::activated(int iMsg) return; Gui::Command* cmd = this; - auto worker = [cmd](App::DocumentObject *Feat, std::vector features) { - - if (!Feat || features.empty()) - return; + auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector features) { bool direction = false; - if (features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { + if (!features.empty() && features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { Part::Part2DObject *sketch = (static_cast(features.front()))->getVerifiedSketch(/* silent =*/ true); if (sketch) { FCMD_OBJ_CMD(Feat,"Axis = ("<(Part::BodyBase::findBodyOf(features.front())); - if (body) { - FCMD_OBJ_CMD(Feat,"Axis = ("<getOrigin()->getZ())<<",[''])"); - } + FCMD_OBJ_CMD(Feat,"Axis = ("<getOrigin()->getZ())<<",[''])"); } FCMD_OBJ_CMD(Feat,"Angle = 360"); @@ -2189,11 +2168,7 @@ void CmdPartDesignScaled::activated(int iMsg) return; Gui::Command* cmd = this; - auto worker = [cmd](App::DocumentObject *Feat, std::vector features) { - - if (!Feat || features.empty()) - return; - + auto worker = [cmd](App::DocumentObject *Feat, std::vector /*features*/) { FCMD_OBJ_CMD(Feat,"Factor = 2"); FCMD_OBJ_CMD(Feat,"Occurrences = 2"); @@ -2307,11 +2282,7 @@ void CmdPartDesignMultiTransform::activated(int iMsg) } else { Gui::Command* cmd = this; - auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector features) { - - if (!Feat || features.empty()) - return; - + auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector /*features*/) { // Make sure the user isn't presented with an empty screen because no transformations are defined yet... App::DocumentObject* prevSolid = pcActiveBody->Tip.getValue(); if (prevSolid) { From 099e33a77f1be1252f8fee8cddbe57f50b917343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Thu, 23 May 2024 12:40:41 +0200 Subject: [PATCH 11/13] Remove feature selection dialog for transformed features The dialog is no longer necessary because without a selection the transformed features default to "Transform body". --- src/Mod/PartDesign/Gui/Command.cpp | 58 +++++------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 53b2715b6e..42d56ff990 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -1878,13 +1878,6 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const std::string FeatName = cmd->getUniqueObjectName(which.c_str(), pcActiveBody); auto worker = [=](std::vector features) { - std::stringstream str; - str << cmd->getObjectCmd(FeatName.c_str(), pcActiveBody->getDocument()) << ".Originals = ["; - for (auto feature : features) { - str << cmd->getObjectCmd(feature) << ","; - } - str << "]"; - std::string msg("Make "); msg += which; Gui::Command::openCommand(msg.c_str()); @@ -1892,12 +1885,19 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const // FIXME: There seems to be kind of a race condition here, leading to sporadic errors like // Exception (Thu Sep 6 11:52:01 2012): 'App.Document' object has no attribute 'Mirrored' Gui::Command::updateActive(); // Helps to ensure that the object already exists when the next command comes up - Gui::Command::doCommand(Gui::Command::Doc, str.str().c_str()); auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str()); if (features.empty()) { - FCMD_OBJ_CMD(Feat,"TransformMode = \"Transform body\""); + FCMD_OBJ_CMD(Feat, "TransformMode = \"Transform body\""); + } else { + std::stringstream str; + str << "Originals = ["; + for (auto feature : features) { + str << cmd->getObjectCmd(feature) << ","; + } + str << "]"; + FCMD_OBJ_CMD(Feat, str.str().c_str()); } // TODO What is this function supposed to do? (2015-08-05, Fat-Zer) @@ -1909,47 +1909,7 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const }; // Get a valid original from the user - // First check selections std::vector features = cmd->getSelection().getObjectsOfType(PartDesign::Feature::getClassTypeId()); - // Next create a list of all eligible objects - if (features.empty()) { - features = cmd->getDocument()->getObjectsOfType(PartDesign::Feature::getClassTypeId()); - // If there is more than one selected or eligible object, show dialog and let user pick one - if (features.size() > 1) { - std::vector status; - for (unsigned i = 0; i < features.size(); i++) - status.push_back(PartDesignGui::TaskFeaturePick::validFeature); - - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); - PartDesignGui::TaskDlgFeaturePick* pickDlg = qobject_cast(dlg); - if (dlg && !pickDlg) { - QMessageBox msgBox; - msgBox.setText(QObject::tr("A dialog is already open in the task panel")); - msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) - Gui::Control().closeDialog(); - else - return; - } - - if (dlg) - Gui::Control().closeDialog(); - - Gui::Selection().clearSelection(); - auto accepter = [](std::vector) -> bool { - return true; - }; - Gui::Control().showDialog(new PartDesignGui::TaskDlgFeaturePick(features, status, accepter, worker, false)); - return; - } else if (features.empty()) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"), - QObject::tr("Please create a feature first.")); - return; - } - } PartDesign::Body* activeBody = PartDesignGui::getBody(true); for (auto feature : features) { From b42d56bb3bc8cccf6663568e279644897636751a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Tue, 28 May 2024 09:32:34 +0200 Subject: [PATCH 12/13] Replace combobox with radio buttons --- .../Gui/TaskTransformedParameters.cpp | 38 +++++++--- .../Gui/TaskTransformedParameters.h | 2 +- .../Gui/TaskTransformedParameters.ui | 71 +++++++++++-------- 3 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp index 298efb6c76..a6b5afa449 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp @@ -91,10 +91,6 @@ void TaskTransformedParameters::setupUI() ui->setupUi(proxy); QMetaObject::connectSlotsByName(this); - connect(ui->comboMode, - qOverload(&QComboBox::activated), - this, - &TaskTransformedParameters::onModeChanged); connect(ui->buttonAddFeature, &QToolButton::toggled, this, @@ -126,9 +122,25 @@ void TaskTransformedParameters::setupUI() auto pcTransformed = static_cast(getObject()); using Mode = PartDesign::Transformed::Mode; + + ui->buttonGroupMode->setId(ui->radioTransformBody, static_cast(Mode::TransformBody)); + ui->buttonGroupMode->setId(ui->radioTransformToolShapes, static_cast(Mode::TransformToolShapes)); + + connect(ui->buttonGroupMode, + qOverload(&QButtonGroup::idClicked), + this, + &TaskTransformedParameters::onModeChanged); + auto const mode = static_cast(pcTransformed->TransformMode.getValue()); ui->groupFeatureList->setEnabled(mode == Mode::TransformToolShapes); - ui->comboMode->setCurrentIndex(static_cast(mode)); + switch (mode) { + case Mode::TransformBody: + ui->radioTransformBody->setChecked(true); + break; + case Mode::TransformToolShapes: + ui->radioTransformToolShapes->setChecked(true); + break; + } std::vector originals = pcTransformed->Originals.getValues(); // Fill data into dialog elements @@ -281,16 +293,20 @@ bool TaskTransformedParameters::isEnabledTransaction() const return enableTransaction; } -void TaskTransformedParameters::onModeChanged(int mode) +void TaskTransformedParameters::onModeChanged(int mode_id) { - using Mode = PartDesign::Transformed::Mode; + if (mode_id < 0) { + return; + } auto pcTransformed = static_cast(getObject()); - pcTransformed->TransformMode.setValue(mode); + pcTransformed->TransformMode.setValue(mode_id); - auto const tmode = static_cast(mode); - ui->groupFeatureList->setEnabled(tmode == Mode::TransformToolShapes); - if (tmode == Mode::TransformBody) { + using Mode = PartDesign::Transformed::Mode; + Mode const mode = static_cast(mode_id); + + ui->groupFeatureList->setEnabled(mode == Mode::TransformToolShapes); + if (mode == Mode::TransformBody) { ui->listWidgetFeatures->clear(); } setupTransaction(); diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h index c0fc9826b1..98ebd66498 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.h +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.h @@ -228,7 +228,7 @@ private Q_SLOTS: void onButtonRemoveFeature(bool checked); void onFeatureDeleted(); void indexesMoved(); - void onModeChanged(int mode); + void onModeChanged(int mode_id); private: /** Setup the parameter UI. diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui b/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui index 1e30aa45d4..0535e4cc3f 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.ui @@ -15,38 +15,45 @@ - - - 0 - - - - - Mode: - - - - - - - - 0 - 0 - - - - - Transform tool shapes - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + Transform body - - - - + + true + + + buttonGroupMode + + + + + + + Transform tool shapes + + + buttonGroupMode + + + + + @@ -122,7 +129,8 @@ - comboMode + radioTransformBody + radioTransformToolShapes buttonAddFeature buttonRemoveFeature listWidgetFeatures @@ -130,4 +138,7 @@ + + + From cd3bda3845c2ae769b07fc22a89d14d34fcd6d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Tue, 28 May 2024 13:48:48 +0200 Subject: [PATCH 13/13] Fix signal for Qt < 5.15 --- src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp index a6b5afa449..e014e5d614 100644 --- a/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskTransformedParameters.cpp @@ -127,7 +127,11 @@ void TaskTransformedParameters::setupUI() ui->buttonGroupMode->setId(ui->radioTransformToolShapes, static_cast(Mode::TransformToolShapes)); connect(ui->buttonGroupMode, - qOverload(&QButtonGroup::idClicked), +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + qOverload(&QButtonGroup::buttonClicked), +#else + &QButtonGroup::idClicked, +#endif this, &TaskTransformedParameters::onModeChanged);