diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index 690944cd8b..19f3091c4e 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -102,12 +102,15 @@ App::DocumentObjectExecReturn *MultiFuse::execute() TopoShape compoundOfArguments; // if only one source shape, and it is a compound - fuse children of the compound - if (shapes.size() == 1) { + const int maxIterations = 1'000'000; // will trigger "not enough shape objects linked" error below if ever reached + for (int i = 0; shapes.size() == 1 && i < maxIterations; ++i) { compoundOfArguments = shapes[0]; if (compoundOfArguments.getShape().ShapeType() == TopAbs_COMPOUND) { shapes.clear(); shapes = compoundOfArguments.getSubTopoShapes(); argumentsAreInCompound = true; + } else { + break; } } diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index 9b3bd9ad37..e25c72649b 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -368,23 +368,7 @@ void CmdPartCommon::activated(int iMsg) std::vector Sel = getSelection().getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), Gui::ResolveMode::FollowLink); - //test if selected object is a compound, and if it is, look how many children it has... - std::size_t numShapes = 0; - if (Sel.size() == 1){ - numShapes = 1; //to be updated later in code, if - Gui::SelectionObject selobj = Sel[0]; - TopoDS_Shape sh = Part::Feature::getShape(selobj.getObject()); - if (sh.ShapeType() == TopAbs_COMPOUND) { - numShapes = 0; - TopoDS_Iterator it(sh); - for (; it.More(); it.Next()) { - ++numShapes; - } - } - } else { - numShapes = Sel.size(); - } - if (numShapes < 2) { + if (Sel.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Please select two shapes or more. Or, select one compound containing two or more shapes to compute the intersection between.")); return; @@ -450,12 +434,15 @@ void CmdPartFuse::activated(int iMsg) numShapes = 1; //to be updated later in code Gui::SelectionObject selobj = Sel[0]; TopoDS_Shape sh = Part::Feature::getShape(selobj.getObject()); - if (sh.ShapeType() == TopAbs_COMPOUND) { + while (numShapes==1 && sh.ShapeType() == TopAbs_COMPOUND) { numShapes = 0; TopoDS_Iterator it(sh); + TopoDS_Shape last; for (; it.More(); it.Next()) { ++numShapes; + last = it.Value(); } + sh = last; } } else { numShapes = Sel.size(); diff --git a/tests/src/Mod/Part/App/FeaturePartFuse.cpp b/tests/src/Mod/Part/App/FeaturePartFuse.cpp index 9fd70ebec3..cf7e53a395 100644 --- a/tests/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/tests/src/Mod/Part/App/FeaturePartFuse.cpp @@ -4,6 +4,7 @@ #include "Mod/Part/App/FeaturePartFuse.h" #include +#include "Mod/Part/App/FeatureCompound.h" #include "PartTestHelpers.h" @@ -20,12 +21,14 @@ protected: { createTestDoc(); _fuse = dynamic_cast(_doc->addObject("Part::Fuse")); + _multiFuse = dynamic_cast(_doc->addObject("Part::MultiFuse")); } void TearDown() override {} - Part::Fuse* _fuse = nullptr; // NOLINT Can't be private in a test framework + Part::Fuse* _fuse = nullptr; // NOLINT Can't be private in a test framework + Part::MultiFuse* _multiFuse = nullptr; // NOLINT Can't be private in a test framework }; TEST_F(FeaturePartFuseTest, testIntersecting) @@ -51,6 +54,64 @@ TEST_F(FeaturePartFuseTest, testIntersecting) EXPECT_DOUBLE_EQ(bb.MaxZ, 3.0); } +TEST_F(FeaturePartFuseTest, testCompound) +{ + // Arrange + Part::Compound* _compound = nullptr; + _compound = dynamic_cast(_doc->addObject("Part::Compound")); + _compound->Links.setValues({_boxes[0], _boxes[1]}); + _multiFuse->Shapes.setValues({_compound}); + + // Act + _compound->execute(); + _multiFuse->execute(); + Part::TopoShape ts = _multiFuse->Shape.getValue(); + double volume = PartTestHelpers::getVolume(ts.getShape()); + Base::BoundBox3d bb = ts.getBoundBox(); + + // Assert + EXPECT_DOUBLE_EQ(volume, 9.0); + // double check using bounds: + EXPECT_DOUBLE_EQ(bb.MinX, 0.0); + EXPECT_DOUBLE_EQ(bb.MinY, 0.0); + EXPECT_DOUBLE_EQ(bb.MinZ, 0.0); + EXPECT_DOUBLE_EQ(bb.MaxX, 1.0); + EXPECT_DOUBLE_EQ(bb.MaxY, 3.0); + EXPECT_DOUBLE_EQ(bb.MaxZ, 3.0); +} +TEST_F(FeaturePartFuseTest, testRecursiveCompound) +{ + // Arrange + Part::Compound* _compound[3] = {nullptr}; + int t; + for (t = 0; t < 3; t++) { + _compound[t] = dynamic_cast(_doc->addObject("Part::Compound")); + } + _compound[0]->Links.setValues({_boxes[0], _boxes[1]}); + _compound[1]->Links.setValues({_compound[0]}); + _compound[2]->Links.setValues({_compound[1]}); + _multiFuse->Shapes.setValues({_compound[2]}); + + // Act + for (t = 0; t < 3; t++) { + _compound[t]->execute(); + } + _multiFuse->execute(); + Part::TopoShape ts = _multiFuse->Shape.getValue(); + double volume = PartTestHelpers::getVolume(ts.getShape()); + Base::BoundBox3d bb = ts.getBoundBox(); + + // Assert + EXPECT_DOUBLE_EQ(volume, 9.0); + // double check using bounds: + EXPECT_DOUBLE_EQ(bb.MinX, 0.0); + EXPECT_DOUBLE_EQ(bb.MinY, 0.0); + EXPECT_DOUBLE_EQ(bb.MinZ, 0.0); + EXPECT_DOUBLE_EQ(bb.MaxX, 1.0); + EXPECT_DOUBLE_EQ(bb.MaxY, 3.0); + EXPECT_DOUBLE_EQ(bb.MaxZ, 3.0); +} + TEST_F(FeaturePartFuseTest, testNonIntersecting) { // Arrange