From 10d95b9ce913bd872fef3c6a1a6cb5a8c8dbbbbb Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 22 Jan 2024 19:58:54 -0500 Subject: [PATCH 1/2] Toponaming/Part: Transfer makeElementBoolean in --- src/Mod/Part/App/TopoShape.h | 55 ++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 256 +++++++++++++++++- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 155 +++++------ 3 files changed, 388 insertions(+), 78 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 5ba19be764ac..0aaa5d9fe717 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -911,6 +911,61 @@ class PartExport TopoShape: public Data::ComplexGeoData return TopoShape(Tag, Hasher).makeElementCopy(*this, op, copyGeom, copyMesh); } + + /** Generalized shape making with mapped element name from shape history + * + * @param maker: op code from OpCodes + * @param sources: list of source shapes. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param tol: tolerance option available to some shape making algorithm + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementBoolean(const char* maker, + const std::vector& sources, + const char* op = nullptr, + double tol = 0.0); + /** Generalized shape making with mapped element name from shape history + * + * @param maker: op code from TopoShapeOpCodes + * @param source: source shape. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param tol: tolerance option available to some shape making algorithm + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementBoolean(const char* maker, + const TopoShape& source, + const char* op = nullptr, + double tol = 0.0); + + /** Generalized shape making with mapped element name from shape history + * + * @param maker: op code from TopoShapeOpCodes + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param tol: tolerance option available to some shape making algorithm + * + * @return Returns the new shape with mappend element name generated from + * shape history using this shape as the source. The shape itself + * is not modified. + */ + TopoShape + makeElementBoolean(const char* maker, const char* op = nullptr, double tol = 0.0) const + { + return TopoShape(0, Hasher).makeElementBoolean(maker, *this, op, tol); + } + /* Make a shell using this shape * @param silent: whether to throw exception on failure * @param op: optional string to be encoded into topo naming for indicating diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 0aa0353e7f73..53ec8381d009 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -34,11 +34,26 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include + #include #include -#include #include #include @@ -46,6 +61,7 @@ #include #include #include + #endif #include "TopoShape.h" @@ -56,12 +72,27 @@ #include "TopoShapeOpCode.h" #include #include +#include FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT namespace Part { +static void expandCompound(const TopoShape& shape, std::vector& res) +{ + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (shape.getShape().ShapeType() != TopAbs_COMPOUND) { + res.push_back(shape); + return; + } + for (auto& s : shape.getSubTopoShapes()) { + expandCompound(s, res); + } +} + void TopoShape::initCache(int reset) const { if (reset > 0 || !_cache || _cache->isTouched(_Shape)) { @@ -2362,4 +2393,227 @@ bool TopoShape::fixSolidOrientation() return false; } +TopoShape& +TopoShape::makeElementBoolean(const char* maker, const TopoShape& shape, const char* op, double tolerance) +{ + return makeElementBoolean(maker, std::vector(1, shape), op, tolerance); +} + + +TopoShape& TopoShape::makeElementBoolean(const char* maker, + const std::vector& shapes, + const char* op, + double tolerance) +{ +#if OCC_VERSION_HEX <= 0x060800 + if (tolerance > 0.0) { + Standard_Failure::Raise("Fuzzy Booleans are not supported in this version of OCCT"); + } +#endif + + if (!maker) { + FC_THROWM(Base::CADKernelError, "no maker"); + } + + if (!op) { + op = maker; + } + + if (shapes.empty()) { + FC_THROWM(NullShapeException, "Null shape"); + } + + if (strcmp(maker, Part::OpCodes::Compound) == 0) { + return makeElementCompound(shapes, op, SingleShapeCompoundCreationPolicy::returnShape); + } + else if (boost::starts_with(maker, Part::OpCodes::Face)) { + std::string prefix(Part::OpCodes::Face); + prefix += '.'; + const char* face_maker = 0; + if (boost::starts_with(maker, prefix)) { + face_maker = maker + prefix.size(); + } + return makeElementFace(shapes, op, face_maker); + } + else if (strcmp(maker, Part::OpCodes::Wire) == 0) { + return makeElementWires(shapes, op); + } + else if (strcmp(maker, Part::OpCodes::Compsolid) == 0) { + BRep_Builder builder; + TopoDS_CompSolid Comp; + builder.MakeCompSolid(Comp); + for (auto& s : shapes) { + if (!s.isNull()) { + builder.Add(Comp, s.getShape()); + } + } + setShape(Comp); + mapSubElement(shapes, op); + return *this; + } + + if (strcmp(maker, Part::OpCodes::Pipe) == 0) { + if (shapes.size() != 2) { + FC_THROWM(Base::CADKernelError, "Not enough input shapes"); + } + if (shapes[0].isNull() || shapes[1].isNull()) { + FC_THROWM(Base::CADKernelError, "Cannot sweep along empty spine"); + } + if (shapes[0].getShape().ShapeType() != TopAbs_WIRE) { + FC_THROWM(Base::CADKernelError, "Spine shape is not a wire"); + } + BRepOffsetAPI_MakePipe mkPipe(TopoDS::Wire(shapes[0].getShape()), shapes[1].getShape()); + return makeElementShape(mkPipe, shapes, op); + } + + if (strcmp(maker, Part::OpCodes::Shell) == 0) { + BRep_Builder builder; + TopoDS_Shell shell; + builder.MakeShell(shell); + for (auto& s : shapes) { + builder.Add(shell, s.getShape()); + } + setShape(shell); + mapSubElement(shapes, op); + BRepCheck_Analyzer check(shell); + if (!check.IsValid()) { + ShapeUpgrade_ShellSewing sewShell; + setShape(sewShell.ApplySewing(shell), false); + // TODO confirm the above won't change OCCT topological naming + } + return *this; + } + + bool buildShell = true; + + std::vector _shapes; + if (strcmp(maker, Part::OpCodes::Fuse) == 0) { + for (auto it = shapes.begin(); it != shapes.end(); ++it) { + auto& s = *it; + if (s.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (s.shapeType() == TopAbs_COMPOUND) { + if (_shapes.empty()) { + _shapes.insert(_shapes.end(), shapes.begin(), it); + } + expandCompound(s, _shapes); + } + else if (_shapes.size()) { + _shapes.push_back(s); + } + } + } + else if (strcmp(maker, Part::OpCodes::Cut) == 0) { + for (unsigned i = 1; i < shapes.size(); ++i) { + auto& s = shapes[i]; + if (s.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (s.shapeType() == TopAbs_COMPOUND) { + if (_shapes.empty()) { + _shapes.insert(_shapes.end(), shapes.begin(), shapes.begin() + i); + } + expandCompound(s, _shapes); + } + else if (_shapes.size()) { + _shapes.push_back(s); + } + } + } + + if (tolerance > 0.0 && _shapes.empty()) { + _shapes = shapes; + } + + const auto& inputs = _shapes.size() ? _shapes : shapes; + if (inputs.empty()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (inputs.size() == 1) { + *this = inputs[0]; + if (shapes.size() == 1) { + // _shapes has fewer items than shapes due to compound expansion. + // Only warn if the caller paseses one shape. + FC_WARN("Boolean operation with only one shape input"); + } + return *this; + } + + std::unique_ptr mk; + if (strcmp(maker, Part::OpCodes::Fuse) == 0) { + mk.reset(new BRepAlgoAPI_Fuse); + } + else if (strcmp(maker, Part::OpCodes::Cut) == 0) { + mk.reset(new BRepAlgoAPI_Cut); + } + else if (strcmp(maker, Part::OpCodes::Common) == 0) { + mk.reset(new BRepAlgoAPI_Common); + } + else if (strcmp(maker, Part::OpCodes::Section) == 0) { + mk.reset(new BRepAlgoAPI_Section); + buildShell = false; + } + else { + FC_THROWM(Base::CADKernelError, "Unknown maker"); + } + + TopTools_ListOfShape shapeArguments, shapeTools; + + int i = -1; + for (const auto& shape : inputs) { + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null input shape"); + } + if (++i == 0) { + shapeArguments.Append(shape.getShape()); + } + else if (tolerance > 0.0) { + auto& s = _shapes[i]; + // workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520 + s.setShape(BRepBuilderAPI_Copy(s.getShape()).Shape(), false); + shapeTools.Append(s.getShape()); + } + else { + shapeTools.Append(shape.getShape()); + } + } + +#if OCC_VERSION_HEX >= 0x070500 +// Can't find this threshold value anywhere. Go ahead assuming it is true. +// if (PartParams::getParallelRunThreshold() > 0) { + mk->SetRunParallel(Standard_True); + OSD_Parallel::SetUseOcctThreads(Standard_True); +// } +#else + // Only run parallel + if (shapeArguments.Size() + shapeTools.Size() > 2) { + mk->SetRunParallel(true); + } + else if (PartParams::getParallelRunThreshold() > 0) { + int total = 0; + for (const auto& shape : inputs) { + total += shape.countSubShapes(TopAbs_FACE); + if (total > PartParams::getParallelRunThreshold()) { + mk->SetRunParallel(true); + break; + } + } + } +#endif + + mk->SetArguments(shapeArguments); + mk->SetTools(shapeTools); + if (tolerance > 0.0) { + mk->SetFuzzyValue(tolerance); + } + mk->Build(); + makeElementShape(*mk, inputs, op); + + if (buildShell) { + makeElementShell(); + } + return *this; +} + } // namespace Part diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index f92da3d9c0b9..341c3192eb18 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -19,6 +19,7 @@ // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) +using namespace Part; using namespace PartTestHelpers; class TopoShapeExpansionTest: public ::testing::Test @@ -53,13 +54,13 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundOneShapeReturnsShape) { // Arrange auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); - Part::TopoShape topoShape {edge}; - std::vector shapes {topoShape}; + TopoShape topoShape {edge}; + std::vector shapes {topoShape}; // Act topoShape.makeElementCompound(shapes, "C", - Part::TopoShape::SingleShapeCompoundCreationPolicy:: + TopoShape::SingleShapeCompoundCreationPolicy:: returnShape /*Don't force the creation*/); // Assert @@ -70,14 +71,14 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundOneShapeForceReturnsCompound) { // Arrange auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); - Part::TopoShape topoShape {edge}; - std::vector shapes {topoShape}; + TopoShape topoShape {edge}; + std::vector shapes {topoShape}; // Act topoShape.makeElementCompound( shapes, "C", - Part::TopoShape::SingleShapeCompoundCreationPolicy::forceCompound /*Force the creation*/); + TopoShape::SingleShapeCompoundCreationPolicy::forceCompound /*Force the creation*/); // Assert EXPECT_NE(edge.ShapeType(), topoShape.getShape().ShapeType()); // No longer the same thing @@ -88,8 +89,8 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoShapesReturnsCompound) // Arrange auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(2.0, 0.0, 0.0)).Edge(); - Part::TopoShape topoShape {edge1}; - std::vector shapes {edge1, edge2}; + TopoShape topoShape {edge1}; + std::vector shapes {edge1, edge2}; // Act topoShape.makeElementCompound(shapes); @@ -102,8 +103,8 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundEmptyShapesReturnsEmptyCompoun { // Arrange auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); - Part::TopoShape topoShape {edge}; - std::vector shapes; + TopoShape topoShape {edge}; + std::vector shapes; // Act topoShape.makeElementCompound(shapes); @@ -121,8 +122,8 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoShapesGeneratesMap) // Arrange auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(2.0, 0.0, 0.0)).Edge(); - Part::TopoShape topoShape {edge1}; - std::vector shapes {edge1, edge2}; + TopoShape topoShape {edge1}; + std::vector shapes {edge1, edge2}; // Act topoShape.makeElementCompound(shapes); @@ -135,13 +136,13 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; + TopoShape cube1TS {cube1}; cube1TS.Tag = 1; - Part::TopoShape cube2TS {cube2}; + TopoShape cube2TS {cube2}; cube2TS.Tag = 2; // Act - Part::TopoShape topoShape; + TopoShape topoShape; topoShape.makeElementCompound({cube1TS, cube2TS}); // Assert @@ -261,8 +262,8 @@ TEST_F(TopoShapeExpansionTest, MapperMakerGenerated) // // Arrange // auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); // auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(2.0, 0.0, 0.0)).Edge(); -// Part::TopoShape topoShape; -// std::vector shapes {edge1, edge2}; +// TopoShape topoShape; +// std::vector shapes {edge1, edge2}; // // Act // topoShape.makeElementWires(shapes); @@ -281,11 +282,11 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceNull) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {face1}; + TopoShape topoShape {face1}; double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - Part::TopoShape newFace = topoShape.makeElementFace(nullptr); + TopoShape newFace = topoShape.makeElementFace(nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); // Assert @@ -304,11 +305,11 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {face1}; + TopoShape topoShape {face1}; double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - Part::TopoShape newFace = topoShape.makeElementFace(wire1); + TopoShape newFace = topoShape.makeElementFace(wire1); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); // Assert @@ -328,11 +329,11 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceParams) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {face1, 1L}; + TopoShape topoShape {face1, 1L}; double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - Part::TopoShape newFace = + TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", "Part::FaceMakerBullseye", nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); @@ -353,11 +354,11 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {face1, 1L}; + TopoShape topoShape {face1, 1L}; double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - Part::TopoShape newFace = + TopoShape newFace = topoShape.makeElementFace(face1, "Cut", "Part::FaceMakerBullseye", nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); @@ -379,11 +380,11 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {wire1, 1L}; + TopoShape topoShape {wire1, 1L}; double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - Part::TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", nullptr, nullptr); + TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", nullptr, nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); // Assert @@ -404,11 +405,11 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {wire2, 1L}; + TopoShape topoShape {wire2, 1L}; double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - Part::TopoShape newFace = + TopoShape newFace = topoShape.makeElementFace(wire2, "Cut", "Part::FaceMakerBullseye", nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); @@ -432,7 +433,7 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) TEST_F(TopoShapeExpansionTest, setElementComboNameNothing) { // Arrange - Part::TopoShape topoShape(1L); + TopoShape topoShape(1L); // Act Data::MappedName result = topoShape.setElementComboName(Data::IndexedName(), {}); // ASSERT @@ -444,7 +445,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameSimple) { // Arrange auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); - Part::TopoShape topoShape(edge1, 1L); + TopoShape topoShape(edge1, 1L); topoShape.setElementMap({}); // Initialize the map to avoid a segfault. // Also, maybe the end of TopoShape::mapSubElementTypeForShape should enforce that elementMap() // isn't nullptr to eliminate the segfault. @@ -460,7 +461,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameSimple) TEST_F(TopoShapeExpansionTest, setElementComboName) { // Arrange - Part::TopoShape topoShape(2L); + TopoShape topoShape(2L); topoShape.setElementMap({}); Data::MappedName edgeName = topoShape.getMappedName(Data::IndexedName::fromConst("Edge", 1), true); @@ -472,7 +473,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboName) // Act Data::MappedName result = topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1), {edgeName, faceName, faceName2}, - Part::OpCodes::Common, + OpCodes::Common, op); // Assert EXPECT_STREQ(result.toString().c_str(), "Edge1;CMN(Face7|Face8);Copy"); @@ -485,7 +486,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); auto wire1 = BRepBuilderAPI_MakeWire({edge1}).Wire(); auto wire2 = BRepBuilderAPI_MakeWire({edge1}).Wire(); - Part::TopoShape topoShape(2L); + TopoShape topoShape(2L); topoShape.makeElementCompound({wire1, wire2}); // Quality of shape doesn't matter Data::MappedName edgeName = topoShape.getMappedName(Data::IndexedName::fromConst("Edge", 1), true); @@ -497,7 +498,7 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) // Act Data::MappedName result = topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1), {edgeName, faceName, faceName2}, - Part::OpCodes::Common, + OpCodes::Common, op); // ASSERT EXPECT_STREQ(result.toString().c_str(), "Edge1;:H,E;CMN(Face7|Face8);Copy"); @@ -511,18 +512,18 @@ TEST_F(TopoShapeExpansionTest, splitWires) const float Wid = 2; const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); - Part::TopoShape topoShape {face1, 1L}; - std::vector inner; + TopoShape topoShape {face1, 1L}; + std::vector inner; // Act EXPECT_EQ(topoShape.getShape().Orientation(), TopAbs_FORWARD); - Part::TopoShape wire = - topoShape.splitWires(&inner, Part::TopoShape::SplitWireReorient::ReorientReversed); + TopoShape wire = + topoShape.splitWires(&inner, TopoShape::SplitWireReorient::ReorientReversed); // Assert EXPECT_EQ(inner.size(), 1); EXPECT_FLOAT_EQ(getLength(wire.getShape()), 2 + 2 + 3 + 3); EXPECT_FLOAT_EQ(getLength(inner.front().getShape()), M_PI * Rad * 2); EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED); - for (Part::TopoShape& shape : inner) { + for (TopoShape& shape : inner) { EXPECT_EQ(shape.getShape().Orientation(), TopAbs_FORWARD); } } @@ -536,28 +537,28 @@ TEST_F(TopoShapeExpansionTest, mapSubElementInvalidParm) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; + TopoShape cube1TS {cube1}; cube1TS.Tag = 1; // Act - std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); - Part::TopoShape face1 = subShapes.front(); + std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); + TopoShape face1 = subShapes.front(); face1.Tag = 2; // Assert - EXPECT_THROW(cube1TS.mapSubElement(face1), Part::NullShapeException); // No subshapes + EXPECT_THROW(cube1TS.mapSubElement(face1), NullShapeException); // No subshapes } TEST_F(TopoShapeExpansionTest, mapSubElementFindShapeByNames) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; - Part::TopoShape cube2TS {cube2}; + TopoShape cube1TS {cube1}; + TopoShape cube2TS {cube2}; cube1TS.Tag = 1; cube2TS.Tag = 2; - Part::TopoShape topoShape; - Part::TopoShape topoShape1; + TopoShape topoShape; + TopoShape topoShape1; // Act int fs1 = topoShape1.findShape(cube1); @@ -585,11 +586,11 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindShapeByType) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; - Part::TopoShape cube2TS {cube2}; + TopoShape cube1TS {cube1}; + TopoShape cube2TS {cube2}; cube1TS.Tag = 1; cube2TS.Tag = 2; - Part::TopoShape topoShape; + TopoShape topoShape; topoShape.makeElementCompound({cube1TS, cube2TS}); topoShape.mapSubElement(cube2TS, "Name", false); @@ -607,11 +608,11 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestor) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; - Part::TopoShape cube2TS {cube2}; + TopoShape cube1TS {cube1}; + TopoShape cube2TS {cube2}; cube1TS.Tag = 1; cube2TS.Tag = 2; - Part::TopoShape topoShape; + TopoShape topoShape; topoShape.makeElementCompound({cube1TS, cube2TS}); topoShape.mapSubElement(cube2TS, "Name", false); @@ -634,21 +635,21 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors) tr.SetTranslation(gp_Vec(gp_XYZ(0, 1, 0))); cube3.Move(TopLoc_Location(tr)); cube4.Move(TopLoc_Location(tr)); - Part::TopoShape cube1TS {cube1}; - Part::TopoShape cube2TS {cube2}; - Part::TopoShape cube3TS {cube3}; - Part::TopoShape cube4TS {cube4}; + TopoShape cube1TS {cube1}; + TopoShape cube2TS {cube2}; + TopoShape cube3TS {cube3}; + TopoShape cube4TS {cube4}; cube1TS.Tag = 1; cube2TS.Tag = 2; cube3TS.Tag = 3; cube4TS.Tag = 4; - Part::TopoShape topoShape; - Part::TopoShape topoShape1; - Part::TopoShape topoShape2; - Part::TopoShape topoShape3; - Part::TopoShape topoShape4; - Part::TopoShape topoShape5; - Part::TopoShape topoShape6; + TopoShape topoShape; + TopoShape topoShape1; + TopoShape topoShape2; + TopoShape topoShape3; + TopoShape topoShape4; + TopoShape topoShape5; + TopoShape topoShape6; topoShape.makeElementCompound({cube1TS, cube2TS}); topoShape1.makeElementCompound({cube3TS, cube4TS}); topoShape2.makeElementCompound({cube1TS, cube3TS}); @@ -688,7 +689,7 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors) TEST_F(TopoShapeExpansionTest, makeElementShellInvalid) { // Arrange - Part::TopoShape topoShape {1L}; + TopoShape topoShape {1L}; // Act / Assert EXPECT_THROW(topoShape.makeElementShell(false, nullptr), Base::CADKernelError); } @@ -699,9 +700,9 @@ TEST_F(TopoShapeExpansionTest, makeElementShellSingle) const float Len = 3; const float Wid = 2; auto [face1, wire1, edge1, edge2, edge3, _] = CreateRectFace(Len, Wid); - Part::TopoShape topoShape {face1, 1L}; + TopoShape topoShape {face1, 1L}; // Act - Part::TopoShape result = topoShape.makeElementShell(false, nullptr); + TopoShape result = topoShape.makeElementShell(false, nullptr); // Assert #if OCC_VERSION_HEX >= 0x070400 EXPECT_EQ(result.getShape().NbChildren(), 1); @@ -727,9 +728,9 @@ TEST_F(TopoShapeExpansionTest, makeElementShellOpen) builder.MakeCompound(compound1); builder.Add(compound1, face1); builder.Add(compound1, face2); - Part::TopoShape topoShape {compound1, 1L}; + TopoShape topoShape {compound1, 1L}; // Act - Part::TopoShape result = topoShape.makeElementShell(true, nullptr); + TopoShape result = topoShape.makeElementShell(true, nullptr); // Assert #if OCC_VERSION_HEX >= 0x070400 EXPECT_EQ(result.getShape().NbChildren(), 2); @@ -744,16 +745,16 @@ TEST_F(TopoShapeExpansionTest, makeElementShellClosed) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape topoShape {cube1}; - std::vector shapes; + TopoShape topoShape {cube1}; + std::vector shapes; for (const auto& face : topoShape.getSubShapes(TopAbs_FACE)) { shapes.emplace_back(face); } // Act - Part::TopoShape topoShape1 {1L}; + TopoShape topoShape1 {1L}; topoShape1.makeElementCompound(shapes, "D"); // Assert - Part::TopoShape result = topoShape1.makeElementShell(false, "SH1"); + TopoShape result = topoShape1.makeElementShell(false, "SH1"); #if OCC_VERSION_HEX >= 0x070400 EXPECT_EQ(result.getShape().NbChildren(), 6); #endif @@ -770,8 +771,8 @@ TEST_F(TopoShapeExpansionTest, makeElementShellIntersecting) auto transform {gp_Trsf()}; transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.5, 0.5, 0.0)); cube2.Move(TopLoc_Location(transform)); - Part::TopoShape topoShape {cube1}; - std::vector shapes; + TopoShape topoShape {cube1}; + std::vector shapes; for (const auto& face : topoShape.getSubShapes(TopAbs_FACE)) { shapes.emplace_back(face); } @@ -780,7 +781,7 @@ TEST_F(TopoShapeExpansionTest, makeElementShellIntersecting) shapes.emplace_back(face); } // Act - Part::TopoShape topoShape1 {1L}; + TopoShape topoShape1 {1L}; topoShape1.makeElementCompound(shapes, "D"); // Assert EXPECT_THROW(topoShape1.makeElementShell(false, nullptr), Base::CADKernelError); From 7ef0b6ec6121ff125009b93cc13c64e789f46a39 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Mon, 22 Jan 2024 20:00:33 -0500 Subject: [PATCH 2/2] Toponaming/Part: Add tests, dependencies, and cleanup --- src/Mod/Part/App/OpenCascadeAll.h | 1 + src/Mod/Part/App/TopoShapeExpansion.cpp | 50 +++----- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 109 +++++++++++++++--- 3 files changed, 108 insertions(+), 52 deletions(-) diff --git a/src/Mod/Part/App/OpenCascadeAll.h b/src/Mod/Part/App/OpenCascadeAll.h index 627d7fb58774..130c89b49d4b 100644 --- a/src/Mod/Part/App/OpenCascadeAll.h +++ b/src/Mod/Part/App/OpenCascadeAll.h @@ -112,6 +112,7 @@ #include #include +#include #include #include # include diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 53ec8381d009..2eb404b18c7e 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -42,37 +42,33 @@ #include #include #include -#include -#include #include #include #include #include #include #include -#include #include #include #include #include -#include -#include -#include -#include #endif +#if OCC_VERSION_HEX >= 0x070500 +# include +#endif + #include "TopoShape.h" +#include "TopoShapeOpCode.h" #include "TopoShapeCache.h" #include "TopoShapeMapper.h" #include "FaceMaker.h" -#include "TopoShapeOpCode.h" #include #include -#include FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -2400,17 +2396,12 @@ TopoShape::makeElementBoolean(const char* maker, const TopoShape& shape, const c } +// TODO: Refactor this so that each OpCode type is a separate method to reduce size TopoShape& TopoShape::makeElementBoolean(const char* maker, const std::vector& shapes, const char* op, double tolerance) { -#if OCC_VERSION_HEX <= 0x060800 - if (tolerance > 0.0) { - Standard_Failure::Raise("Fuzzy Booleans are not supported in this version of OCCT"); - } -#endif - if (!maker) { FC_THROWM(Base::CADKernelError, "no maker"); } @@ -2454,10 +2445,10 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, if (strcmp(maker, Part::OpCodes::Pipe) == 0) { if (shapes.size() != 2) { - FC_THROWM(Base::CADKernelError, "Not enough input shapes"); + FC_THROWM(Base::CADKernelError, "Sweep needs a spine and a shape"); } if (shapes[0].isNull() || shapes[1].isNull()) { - FC_THROWM(Base::CADKernelError, "Cannot sweep along empty spine"); + FC_THROWM(Base::CADKernelError, "Cannot sweep with empty spine or empty shape"); } if (shapes[0].getShape().ShapeType() != TopAbs_WIRE) { FC_THROWM(Base::CADKernelError, "Spine shape is not a wire"); @@ -2479,7 +2470,7 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, if (!check.IsValid()) { ShapeUpgrade_ShellSewing sewShell; setShape(sewShell.ApplySewing(shell), false); - // TODO confirm the above won't change OCCT topological naming + // TODO: confirm the above won't change OCCT topological naming } return *this; } @@ -2580,26 +2571,15 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker, } #if OCC_VERSION_HEX >= 0x070500 -// Can't find this threshold value anywhere. Go ahead assuming it is true. -// if (PartParams::getParallelRunThreshold() > 0) { + // -1/22/2024 Removing the parameter. + // if (PartParams::getParallelRunThreshold() > 0) { mk->SetRunParallel(Standard_True); OSD_Parallel::SetUseOcctThreads(Standard_True); -// } + // } #else - // Only run parallel - if (shapeArguments.Size() + shapeTools.Size() > 2) { - mk->SetRunParallel(true); - } - else if (PartParams::getParallelRunThreshold() > 0) { - int total = 0; - for (const auto& shape : inputs) { - total += shape.countSubShapes(TopAbs_FACE); - if (total > PartParams::getParallelRunThreshold()) { - mk->SetRunParallel(true); - break; - } - } - } + // 01/22/2024 This will be an extremely rare case, since we don't + // build against OCCT versions this old. Removing the parameter. + mk->SetRunParallel(true); #endif mk->SetArguments(shapeArguments); diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 341c3192eb18..b047f62eb006 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -60,8 +60,7 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundOneShapeReturnsShape) // Act topoShape.makeElementCompound(shapes, "C", - TopoShape::SingleShapeCompoundCreationPolicy:: - returnShape /*Don't force the creation*/); + TopoShape::SingleShapeCompoundCreationPolicy::returnShape); // Assert EXPECT_EQ(edge.ShapeType(), topoShape.getShape().ShapeType()); // NOT a Compound @@ -75,10 +74,9 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundOneShapeForceReturnsCompound) std::vector shapes {topoShape}; // Act - topoShape.makeElementCompound( - shapes, - "C", - TopoShape::SingleShapeCompoundCreationPolicy::forceCompound /*Force the creation*/); + topoShape.makeElementCompound(shapes, + "C", + TopoShape::SingleShapeCompoundCreationPolicy::forceCompound); // Assert EXPECT_NE(edge.ShapeType(), topoShape.getShape().ShapeType()); // No longer the same thing @@ -278,9 +276,7 @@ TEST_F(TopoShapeExpansionTest, MapperMakerGenerated) TEST_F(TopoShapeExpansionTest, makeElementFaceNull) { // Arrange - const float Len = 3; - const float Wid = 2; - const float Rad = 1; + const double Len = 3, Wid = 2, Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); TopoShape topoShape {face1}; double area = getArea(face1); @@ -333,8 +329,7 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceParams) double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - TopoShape newFace = - topoShape.makeElementFace(wire1, "Cut", "Part::FaceMakerBullseye", nullptr); + TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", "Part::FaceMakerBullseye", nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); // Assert @@ -358,8 +353,7 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - TopoShape newFace = - topoShape.makeElementFace(face1, "Cut", "Part::FaceMakerBullseye", nullptr); + TopoShape newFace = topoShape.makeElementFace(face1, "Cut", "Part::FaceMakerBullseye", nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); // Assert @@ -409,8 +403,7 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) double area = getArea(face1); double area1 = getArea(topoShape.getShape()); // Act - TopoShape newFace = - topoShape.makeElementFace(wire2, "Cut", "Part::FaceMakerBullseye", nullptr); + TopoShape newFace = topoShape.makeElementFace(wire2, "Cut", "Part::FaceMakerBullseye", nullptr); double area2 = getArea(newFace.getShape()); double area3 = getArea(topoShape.getShape()); // Assert @@ -516,8 +509,7 @@ TEST_F(TopoShapeExpansionTest, splitWires) std::vector inner; // Act EXPECT_EQ(topoShape.getShape().Orientation(), TopAbs_FORWARD); - TopoShape wire = - topoShape.splitWires(&inner, TopoShape::SplitWireReorient::ReorientReversed); + TopoShape wire = topoShape.splitWires(&inner, TopoShape::SplitWireReorient::ReorientReversed); // Assert EXPECT_EQ(inner.size(), 1); EXPECT_FLOAT_EQ(getLength(wire.getShape()), 2 + 2 + 3 + 3); @@ -791,5 +783,88 @@ TEST_F(TopoShapeExpansionTest, makeElementShellIntersecting) // { // // Arrange // } +TEST_F(TopoShapeExpansionTest, makeElementBooleanImpossibleCommon) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + TopoShape topoShape1 {cube1, 1L}; + TopoShape topoShape2 {cube2, 2L}; + // Act + TopoShape& result = + topoShape1.makeElementBoolean(Part::OpCodes::Common, {topoShape1, topoShape2}); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 0); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 0); +} + +TEST_F(TopoShapeExpansionTest, makeElementBooleanCommon) +{ + // 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 + TopoShape& result = + topoShape1.makeElementBoolean(Part::OpCodes::Common, {topoShape1, topoShape2}); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 26); + EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); + EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face3;:M;CMN;:H1:7,F")); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 0.25); +} + +TEST_F(TopoShapeExpansionTest, makeElementBooleanCut) +{ + // 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 + TopoShape& result = topoShape1.makeElementBoolean(Part::OpCodes::Cut, {topoShape1, topoShape2}); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 38); + EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); + EXPECT_EQ( + elements[IndexedName("Face", 1)], + MappedName( + "Face3;:M;CUT;:H1:7,F;:U;CUT;:H1:7,E;:L(Face5;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E|Face5;:M;" + "CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;CUT;:H1:7,V;:L(Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;" + "CUT;:H1:7,V);CUT;:H1:3c,E|Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E);CUT;:H1:cb,F")); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 0.75); +} + +TEST_F(TopoShapeExpansionTest, makeElementBooleanFuse) +{ + // 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 + TopoShape& result = + topoShape1.makeElementBoolean(Part::OpCodes::Fuse, {topoShape1, topoShape2}); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 66); + EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1); + EXPECT_EQ( + elements[IndexedName("Face", 1)], + MappedName( + "Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;" + "FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;" + "FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F")); + EXPECT_FLOAT_EQ(getVolume(result.getShape()), 1.75); +} // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)