diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index fd05813763..672b4cbd3e 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -34,6 +34,8 @@ #include "TopoShapeOpCode.h" #include "modelRefine.h" +#include + using namespace Part; @@ -45,7 +47,6 @@ extern bool getRefineModelParameter(); PROPERTY_SOURCE(Part::Common, Part::Boolean) - Common::Common() = default; const char* Common::opCode() const @@ -63,6 +64,7 @@ BRepAlgoAPI_BooleanOperation* Common::makeOperation(const TopoDS_Shape& base, co PROPERTY_SOURCE(Part::MultiCommon, Part::Feature) +const char* MultiCommon::BehaviorEnums[] = {"CommonOfAllShapes", "CommonOfFirstAndRest", nullptr}; MultiCommon::MultiCommon() { @@ -85,17 +87,38 @@ MultiCommon::MultiCommon() "Refine shape (clean up redundant edges) after this boolean operation" ); + ADD_PROPERTY_TYPE( + Behavior, + (CommonOfAllShapes), + "Compatibility", + App::Prop_Hidden, + "Determines how the common operation is computed: either as the intersection of all " + "shapes, or the intersection of the first shape with all remaining shapes (for " + "compatibility with FreeCAD 1.0)." + ); + Behavior.setEnums(BehaviorEnums); + this->Refine.setValue(getRefineModelParameter()); } short MultiCommon::mustExecute() const { - if (Shapes.isTouched()) { + if (Shapes.isTouched() || Behavior.isTouched()) { return 1; } return 0; } +void MultiCommon::Restore(Base::XMLReader& reader) +{ + Feature::Restore(reader); + + // For 1.0 and 1.0 only the order was common of first and the rest due to a bug + if (Base::getVersion(reader.ProgramVersion) == Base::Version::v1_0) { + Behavior.setValue(CommonOfFirstAndRest); + } +} + App::DocumentObjectExecReturn* MultiCommon::execute() { std::vector shapes; @@ -107,8 +130,31 @@ App::DocumentObjectExecReturn* MultiCommon::execute() shapes.push_back(sh); } - TopoShape res {0}; - res.makeElementBoolean(Part::OpCodes::Common, shapes); + TopoShape res; + + if (Behavior.getValue() == CommonOfAllShapes) { + // special case - if there is only one argument, and it is compound - expand it + if (shapes.size() == 1) { + TopoShape shape = shapes.front(); + + if (shape.shapeType() == TopAbs_COMPOUND) { + shapes.clear(); + std::ranges::copy(shape.getSubTopoShapes(), std::back_inserter(shapes)); + } + } + + res = shapes.front(); + + // to achieve common of all shapes, we need to do it one shape at a time + for (const auto& tool : shapes) { + res = res.makeElementBoolean(OpCodes::Common, {res, tool}); + } + } + else { + res = TopoShape(0); + res.makeElementBoolean(OpCodes::Common, shapes); + } + if (res.isNull()) { throw Base::RuntimeError("Resulting shape is null"); } diff --git a/src/Mod/Part/App/FeaturePartCommon.h b/src/Mod/Part/App/FeaturePartCommon.h index 2f4fd501da..a92342cad0 100644 --- a/src/Mod/Part/App/FeaturePartCommon.h +++ b/src/Mod/Part/App/FeaturePartCommon.h @@ -49,6 +49,12 @@ protected: //@} }; +enum CommonBehavior +{ + CommonOfAllShapes, + CommonOfFirstAndRest, +}; + class PartExport MultiCommon: public Part::Feature { PROPERTY_HEADER_WITH_OVERRIDE(Part::MultiCommon); @@ -59,6 +65,7 @@ public: App::PropertyLinkList Shapes; PropertyShapeHistory History; App::PropertyBool Refine; + App::PropertyEnumeration Behavior; /** @name methods override feature */ //@{ @@ -66,11 +73,17 @@ public: App::DocumentObjectExecReturn* execute() override; short mustExecute() const override; //@} + + void Restore(Base::XMLReader& reader) override; + /// returns the type name of the ViewProvider const char* getViewProviderName() const override { return "PartGui::ViewProviderMultiCommon"; } + +private: + static const char* BehaviorEnums[]; }; } // namespace Part