From f8cca40b149400815956a83f6a81a60119758619 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Sat, 24 Feb 2024 16:19:25 -0500 Subject: [PATCH 1/2] Toposhape/Part: Transfer in replacEShape and removEShape --- src/Mod/Part/App/TopoShape.h | 42 +++++++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 37 +++++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index b5840144f3..b972b6a5d7 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1041,6 +1041,48 @@ public: double tol3d = 0.0, double tolBound = 0.0, double tolAngluar = 0.0); + /* Make a shape with some subshapes replaced + * + * @param source: the source shape + * @param s: replacement mapping the existing sub shape of source to new shapes + * + * @return 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 &replacEShape(const TopoShape &source, const std::vector > &s); + /* Make a new shape using this shape with some subshapes replaced by others + * + * @param s: replacement mapping the existing sub shape of source to new shapes + * + * @return Return the new shape. The TopoShape itself is not modified. + */ + TopoShape replacEShape(const std::vector > &s) const { + return TopoShape(0,Hasher).replacEShape(*this,s); + } + + /* Make a shape with some subshapes removed + * + * @param source: the source shape + * @param s: the subshapes to be removed + * + * @return 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 &removEShape(const TopoShape &source, const std::vector& s); + /* Make a new shape using this shape with some subshapes removed + * + * @param s: the subshapes to be removed + * + * @return Return the new shape. The TopoShape itself is not modified. + */ + TopoShape removEShape(const std::vector& s) const { + return TopoShape(0,Hasher).removEShape(*this,s); + } + /** Make shape using generalized fusion and return the modified sub shapes * diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index b6829b3260..e60c7780b2 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -3059,7 +3059,7 @@ TopoShape& TopoShape::makeElementSolid(const TopoShape& shape, const char* op) } return *this; } - + TopoShape& TopoShape::makeElementMirror(const TopoShape& shape, const gp_Ax2& ax2, const char* op) { if (!op) { @@ -3108,6 +3108,41 @@ TopoShape& TopoShape::makeElementSlices(const TopoShape& shape, return makeElementCompound(wires, op, SingleShapeCompoundCreationPolicy::returnShape); } +TopoShape &TopoShape::replacEShape(const TopoShape &shape, + const std::vector > &s) +{ + if(shape.isNull()) + HANDLE_NULL_SHAPE; + BRepTools_ReShape reshape; + std::vector shapes; + shapes.reserve(s.size()+1); + for (auto &v : s) { + if(v.first.isNull() || v.second.isNull()) + HANDLE_NULL_INPUT; + reshape.Replace(v.first.getShape(), v.second.getShape()); + shapes.push_back(v.second); + } + shapes.push_back(shape); + setShape(reshape.Apply(shape.getShape(),TopAbs_SHAPE)); + mapSubElement(shapes); + return *this; +} + +TopoShape &TopoShape::removEShape(const TopoShape &shape, const std::vector& s) +{ + if(shape.isNull()) + HANDLE_NULL_SHAPE; + BRepTools_ReShape reshape; + for(auto &sh : s) { + if(sh.isNull()) + HANDLE_NULL_INPUT; + reshape.Remove(sh.getShape()); + } + setShape(reshape.Apply(shape.getShape(), TopAbs_SHAPE)); + mapSubElement(shape); + return *this; +} + TopoShape& TopoShape::makeElementFillet(const TopoShape& shape, const std::vector& edges, double radius1, From 698f306c185f8591eb5c24e57c6e5f68e4662535 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sat, 24 Feb 2024 16:59:45 -0500 Subject: [PATCH 2/2] Toposhape/Part: Clean and test replacEShape and removEShape --- src/Mod/Part/App/TopoShape.h | 21 ++-- src/Mod/Part/App/TopoShapeExpansion.cpp | 37 ++++--- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 98 +++++++++++++++---- 3 files changed, 115 insertions(+), 41 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index b972b6a5d7..bc60bb4b57 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -1040,8 +1040,9 @@ public: const char* op = nullptr, double tol3d = 0.0, double tolBound = 0.0, - double tolAngluar = 0.0); - /* Make a shape with some subshapes replaced + double tolAngular = 0.0); + + /* Make a shape with some subshapes replaced. * * @param source: the source shape * @param s: replacement mapping the existing sub shape of source to new shapes @@ -1051,15 +1052,17 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &replacEShape(const TopoShape &source, const std::vector > &s); + TopoShape& replaceElementShape(const TopoShape& source, + const std::vector>& s); /* Make a new shape using this shape with some subshapes replaced by others * * @param s: replacement mapping the existing sub shape of source to new shapes * * @return Return the new shape. The TopoShape itself is not modified. */ - TopoShape replacEShape(const std::vector > &s) const { - return TopoShape(0,Hasher).replacEShape(*this,s); + TopoShape replaceElementShape(const std::vector>& s) const + { + return TopoShape(0, Hasher).replaceElementShape(*this, s); } /* Make a shape with some subshapes removed @@ -1072,18 +1075,18 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - TopoShape &removEShape(const TopoShape &source, const std::vector& s); + TopoShape& removeElementShape(const TopoShape& source, const std::vector& s); /* Make a new shape using this shape with some subshapes removed * * @param s: the subshapes to be removed * * @return Return the new shape. The TopoShape itself is not modified. */ - TopoShape removEShape(const std::vector& s) const { - return TopoShape(0,Hasher).removEShape(*this,s); + TopoShape removeElementShape(const std::vector& s) const + { + return TopoShape(0, Hasher).removeElementShape(*this, s); } - /** Make shape using generalized fusion and return the modified sub shapes * * @param sources: the source shapes diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index e60c7780b2..2495395751 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -3108,34 +3108,41 @@ TopoShape& TopoShape::makeElementSlices(const TopoShape& shape, return makeElementCompound(wires, op, SingleShapeCompoundCreationPolicy::returnShape); } -TopoShape &TopoShape::replacEShape(const TopoShape &shape, - const std::vector > &s) +TopoShape& TopoShape::replaceElementShape(const TopoShape& shape, + const std::vector>& s) { - if(shape.isNull()) - HANDLE_NULL_SHAPE; + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } BRepTools_ReShape reshape; std::vector shapes; - shapes.reserve(s.size()+1); - for (auto &v : s) { - if(v.first.isNull() || v.second.isNull()) - HANDLE_NULL_INPUT; + shapes.reserve(s.size() + 1); + for (auto& v : s) { + if (v.first.isNull() || v.second.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } reshape.Replace(v.first.getShape(), v.second.getShape()); shapes.push_back(v.second); } + // TODO: This does not work when replacing a shape in a compound. Should we replace with + // something else? + // Note that remove works with a compound. shapes.push_back(shape); - setShape(reshape.Apply(shape.getShape(),TopAbs_SHAPE)); + setShape(reshape.Apply(shape.getShape(), TopAbs_SHAPE)); mapSubElement(shapes); return *this; } -TopoShape &TopoShape::removEShape(const TopoShape &shape, const std::vector& s) +TopoShape& TopoShape::removeElementShape(const TopoShape& shape, const std::vector& s) { - if(shape.isNull()) - HANDLE_NULL_SHAPE; + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } BRepTools_ReShape reshape; - for(auto &sh : s) { - if(sh.isNull()) - HANDLE_NULL_INPUT; + for (auto& sh : s) { + if (sh.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } reshape.Remove(sh.getShape()); } setShape(reshape.Apply(shape.getShape(), TopAbs_SHAPE)); diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index e12642891a..6ffdd86d69 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -172,24 +172,24 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) EXPECT_TRUE( allElementsMatch(topoShape, { - "Edge1;:H1,E;:H7,E", "Edge2;:H1,E;:H7,E", "Edge3;:H1,E;:H7,E", - "Edge4;:H1,E;:H7,E", "Edge1;:H2,E;:H7,E", "Edge2;:H2,E;:H7,E", - "Edge3;:H2,E;:H7,E", "Edge4;:H2,E;:H7,E", "Edge1;:H3,E;:H7,E", - "Edge2;:H3,E;:H7,E", "Edge3;:H3,E;:H7,E", "Edge4;:H3,E;:H7,E", - "Edge1;:H8,E;:He,E", "Edge2;:H8,E;:He,E", "Edge3;:H8,E;:He,E", - "Edge4;:H8,E;:He,E", "Edge1;:H9,E;:He,E", "Edge2;:H9,E;:He,E", - "Edge3;:H9,E;:He,E", "Edge4;:H9,E;:He,E", "Edge1;:Ha,E;:He,E", - "Edge2;:Ha,E;:He,E", "Edge3;:Ha,E;:He,E", "Edge4;:Ha,E;:He,E", - "Vertex1;:H8,V;:He,V", "Vertex2;:H8,V;:He,V", "Vertex3;:H8,V;:He,V", - "Vertex4;:H8,V;:He,V", "Vertex1;:H9,V;:He,V", "Vertex2;:H9,V;:He,V", - "Vertex3;:H9,V;:He,V", "Vertex4;:H9,V;:He,V", "Face1;:H1,F;:H7,F", - "Face1;:H2,F;:H7,F", "Face1;:H3,F;:H7,F", "Face1;:H4,F;:H7,F", - "Face1;:H5,F;:H7,F", "Face1;:H6,F;:H7,F", "Face1;:H8,F;:He,F", + "Vertex1;:H1,V;:H7,V", "Vertex2;:H1,V;:H7,V", "Vertex3;:H1,V;:H7,V", + "Vertex4;:H1,V;:H7,V", "Vertex1;:H2,V;:H7,V", "Vertex2;:H2,V;:H7,V", + "Vertex3;:H2,V;:H7,V", "Vertex4;:H2,V;:H7,V", "Face1;:H8,F;:He,F", "Face1;:H9,F;:He,F", "Face1;:Ha,F;:He,F", "Face1;:Hb,F;:He,F", - "Face1;:Hc,F;:He,F", "Face1;:Hd,F;:He,F", "Vertex1;:H1,V;:H7,V", - "Vertex2;:H1,V;:H7,V", "Vertex3;:H1,V;:H7,V", "Vertex4;:H1,V;:H7,V", - "Vertex1;:H2,V;:H7,V", "Vertex2;:H2,V;:H7,V", "Vertex3;:H2,V;:H7,V", - "Vertex4;:H2,V;:H7,V", + "Face1;:Hc,F;:He,F", "Face1;:Hd,F;:He,F", "Edge1;:H8,E;:He,E", + "Edge2;:H8,E;:He,E", "Edge3;:H8,E;:He,E", "Edge4;:H8,E;:He,E", + "Edge1;:H9,E;:He,E", "Edge2;:H9,E;:He,E", "Edge3;:H9,E;:He,E", + "Edge4;:H9,E;:He,E", "Edge1;:Ha,E;:He,E", "Edge2;:Ha,E;:He,E", + "Edge3;:Ha,E;:He,E", "Edge4;:Ha,E;:He,E", "Vertex1;:H8,V;:He,V", + "Vertex2;:H8,V;:He,V", "Vertex3;:H8,V;:He,V", "Vertex4;:H8,V;:He,V", + "Vertex1;:H9,V;:He,V", "Vertex2;:H9,V;:He,V", "Vertex3;:H9,V;:He,V", + "Vertex4;:H9,V;:He,V", "Edge1;:H1,E;:H7,E", "Edge2;:H1,E;:H7,E", + "Edge3;:H1,E;:H7,E", "Edge4;:H1,E;:H7,E", "Edge1;:H2,E;:H7,E", + "Edge2;:H2,E;:H7,E", "Edge3;:H2,E;:H7,E", "Edge4;:H2,E;:H7,E", + "Edge1;:H3,E;:H7,E", "Edge2;:H3,E;:H7,E", "Edge3;:H3,E;:H7,E", + "Edge4;:H3,E;:H7,E", "Face1;:H1,F;:H7,F", "Face1;:H2,F;:H7,F", + "Face1;:H3,F;:H7,F", "Face1;:H4,F;:H7,F", "Face1;:H5,F;:H7,F", + "Face1;:H6,F;:H7,F", })); } @@ -2282,4 +2282,68 @@ TEST_F(TopoShapeExpansionTest, makeElementBSplineFace) })); } +TEST_F(TopoShapeExpansionTest, replaceElementShape) +{ + // Arrange + auto [cube1, cube2] = CreateTwoTopoShapeCubes(); + // We can't use a compound in replaceElementShape, so we'll make a replacement wire and a shell + auto wire {BRepBuilderAPI_MakeWire( + BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)), + BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)), + BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0))) + .Wire()}; + auto shell = cube1.makeElementShell(); + auto wires = shell.getSubTopoShapes(TopAbs_WIRE); + // Act + TopoShape& result = shell.replaceElementShape(shell, {{wires[0], wire}}); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); + EXPECT_FLOAT_EQ(getArea(result.getShape()), 5); + EXPECT_EQ(result.countSubElements("Wire"), 6); + // Assert that we're creating a correct element map + EXPECT_TRUE(result.getMappedChildElements().empty()); + EXPECT_TRUE(elementsMatch( + result, + { + "Edge1", "Edge1;:H1,E", "Edge1;:H2,E", "Edge1;:H3,E", "Edge2", + "Edge2;:H1,E", "Edge2;:H2,E", "Edge2;:H3,E", "Edge3", "Edge3;:H1,E", + "Edge3;:H2,E", "Edge3;:H3,E", "Edge4;:H1,E", "Edge4;:H2,E", "Edge4;:H3,E", + "Face1;:H2,F", "Face1;:H3,F", "Face1;:H4,F", "Face1;:H5,F", "Face1;:H6,F", + "Vertex1", "Vertex1;:H1,V", "Vertex1;:H2,V", "Vertex2", "Vertex2;:H1,V", + "Vertex2;:H2,V", "Vertex3", "Vertex3;:H1,V", "Vertex3;:H2,V", "Vertex4;:H1,V", + "Vertex4;:H2,V", + })); +} + +TEST_F(TopoShapeExpansionTest, removeElementShape) +{ + // Arrange + auto [cube1, cube2] = CreateTwoTopoShapeCubes(); + auto faces = cube1.getSubTopoShapes(TopAbs_FACE); + // Act + TopoShape result = cube1.removeElementShape({faces[0]}); + Base::BoundBox3d bb = result.getBoundBox(); + // Assert shape is correct + EXPECT_TRUE(PartTestHelpers::boxesMatch(bb, Base::BoundBox3d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0))); + EXPECT_FLOAT_EQ(getArea(result.getShape()), 5); + EXPECT_EQ(result.countSubShapes("Compound"), 1); + EXPECT_EQ(result.countSubShapes("Face"), 5); + // Assert that we're creating a correct element map + EXPECT_TRUE(result.getMappedChildElements().empty()); + EXPECT_TRUE( + elementsMatch(result, + { + "Edge1;:H1,E;:H7,E", "Edge1;:H2,E;:H7,E", "Edge1;:H3,E;:H7,E", + "Edge2;:H1,E;:H7,E", "Edge2;:H2,E;:H7,E", "Edge2;:H3,E;:H7,E", + "Edge3;:H1,E;:H7,E", "Edge3;:H2,E;:H7,E", "Edge3;:H3,E;:H7,E", + "Edge4;:H1,E;:H7,E", "Edge4;:H2,E;:H7,E", "Edge4;:H3,E;:H7,E", + "Face1;:H2,F;:H7,F", "Face1;:H3,F;:H7,F", "Face1;:H4,F;:H7,F", + "Face1;:H5,F;:H7,F", "Face1;:H6,F;:H7,F", "Vertex1;:H1,V;:H7,V", + "Vertex1;:H2,V;:H7,V", "Vertex2;:H1,V;:H7,V", "Vertex2;:H2,V;:H7,V", + "Vertex3;:H1,V;:H7,V", "Vertex3;:H2,V;:H7,V", "Vertex4;:H1,V;:H7,V", + "Vertex4;:H2,V;:H7,V", + })); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)