diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index e8b1163d86..fa0a7caa54 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1844,6 +1844,46 @@ public: CN, }; + /** Make a solid using shells or CompSolid + * + * @param shapes: input shapes of either shells or CompSolid. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The function produces a solid. The original content of this + * TopoShape is discarded and replaced with the new shape. The + * function returns the TopoShape itself as a self reference so + * that multiple operations can be carried out for the same shape + * in the same line of code. + */ + // TODO: This does not appear to be called, and the implementation seems impossible +// TopoShape &makeElementSolid(const std::vector &shapes, const char *op=nullptr); + /** Make a solid using shells or CompSolid + * + * @param shape: input shape of either a shell, a compound of shells, or a + * CompSolid. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The function produces a solid. The original content of this + * TopoShape is discarded and replaced with the new shape. The + * function returns the TopoShape itself as a self reference so + * that multiple operations can be carried out for the same shape + * in the same line of code. + */ + TopoShape &makeElementSolid(const TopoShape &shape, const char *op=nullptr); + /** Make a solid using this shape + * + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The function returns a new solid using the shell or CompSolid + * inside this shape. The shape itself is not modified. + */ + TopoShape makeElementSolid(const char *op=nullptr) const { + return TopoShape(0,Hasher).makeElementSolid(*this,op); + } + /** Generic shape making with mapped element name from shape history * diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 4b66da9988..3fadc5ecd9 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -2702,6 +2703,69 @@ struct MapperThruSections: MapperMaker } }; +// TODO: This method does not appear to ever be called in the codebase, and it is probably +// broken, because using TopoShape() with no parameters means the result will not have an +// element Map. If ever restored, code like this should be used to make a tag. +//TopoShape& TopoShape::makeElementSolid(const std::vector& shapes, const char* op) +//{ +// static std::random_device _RD; +// static std::mt19937 _RGEN(_RD()); +// static std::uniform_int_distribution<> _RDIST(1, 10000); +// long idx = _RDIST(_RGEN); +// return makeElementSolid(TopoShape(idx).makeElementCompound(shapes), op); +//} + +TopoShape& TopoShape::makeElementSolid(const TopoShape& shape, const char* op) +{ + if (!op) { + op = Part::OpCodes::Solid; + } + + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + + // first, if we were given a compsolid, try making a solid out of it + TopoDS_CompSolid compsolid; + int count = 0; + for (const auto& s : shape.getSubShapes(TopAbs_COMPSOLID)) { + ++count; + compsolid = TopoDS::CompSolid(s); + if (count > 1) { + break; + } + } + if (count == 0) { + // no compsolids. Get shells... + BRepBuilderAPI_MakeSolid mkSolid; + count = 0; + for (const auto& s : shape.getSubShapes(TopAbs_SHELL)) { + ++count; + mkSolid.Add(TopoDS::Shell(s)); + } + + if (count == 0) { // no shells? + FC_THROWM(Base::CADKernelError, "No shells or compsolids found in shape"); + } + + makeElementShape(mkSolid, shape, op); + + TopoDS_Solid solid = TopoDS::Solid(_Shape); + BRepLib::OrientClosedSolid(solid); + setShape(solid, false); + } + else if (count == 1) { + BRepBuilderAPI_MakeSolid mkSolid(compsolid); + makeElementShape(mkSolid, shape, op); + } + else { // if (count > 1) + FC_THROWM(Base::CADKernelError, + "Only one compsolid can be accepted. " + "Provided shape has more than one compsolid."); + } + return *this; +} + TopoShape& TopoShape::makeElementMirror(const TopoShape& shape, const gp_Ax2& ax2, const char* op) { if (!op) { diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 3ad7f17cd2..c8cbf4dd49 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2048,4 +2048,31 @@ TEST_F(TopoShapeExpansionTest, makeElementGTransformWithMap) // Not testing _makeElementTransform as it is a thin wrapper that calls the same places as the four // preceding tests. +TEST_F(TopoShapeExpansionTest, makeElementSolid) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto tr {gp_Trsf()}; + tr.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); + cube2.Move(TopLoc_Location(tr)); + TopoShape topoShape1 {cube1, 1L}; + TopoShape topoShape2 {cube2, 2L}; + // Act + TopExp_Explorer exp(topoShape1.getShape(), TopAbs_SHELL); + auto shell1 = exp.Current(); + exp.Init(topoShape2.getShape(), TopAbs_SHELL); + auto shell2 = exp.Current(); + TopoShape& topoShape3 = topoShape1.makeElementCompound({shell1, shell2}); + TopoShape& result = topoShape1.makeElementSolid(topoShape3); // Need the single parm form + auto elements = elementMap(result); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0.0, -0.5, 0.0, 1.5, 1.0, 1.0))); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 2); + // Assert elementMap is correct + EXPECT_EQ(elements.size(), 52); + EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); + EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;SLD;:H1:4,F")); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)