diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 9c67e97c23..1ec4890584 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -636,6 +636,11 @@ public: bool force = true); + enum class ConnectionPolicy { + REQUIRE_SHARED_VERTEX, + MERGE_WITH_TOLERANCE + }; + /** Make a compound of wires by connecting input edges * * @param shapes: input shapes. Can be any type of shape. Edges will be @@ -664,6 +669,7 @@ public: bool shared = false, TopoShapeMap* output = nullptr); + /** Make a compound of wires by connecting input edges * * @param shape: input shape. Can be any type of shape. Edges will be @@ -673,8 +679,8 @@ public: * @param keepOrder: whether to respect the order of the input edges * @param tol: tolerance for checking the distance of two vertex to decide * if two edges are connected - * @param shared: if true, then only connect edges if they shared the same - * vertex, or else use \c tol to check for connection. + * @param policy: if REQUIRE_SHARED_VERTEX, then only connect edges if they shared the same + * vertex. If MERGE_WITH_TOLERANCE use \c tol to check for connection. * @param output: optional output mapping from wire edges to input edge. * Note that edges may be modified after adding to the wire, * so the output edges may not be the same as the input @@ -689,7 +695,7 @@ public: TopoShape& makeElementWires(const TopoShape& shape, const char* op = nullptr, double tol = 0.0, - bool shared = false, + ConnectionPolicy policy = ConnectionPolicy::MERGE_WITH_TOLERANCE, TopoShapeMap* output = nullptr); /** Make a compound of wires by connecting input edges in the given order @@ -888,6 +894,7 @@ private: */ static std::deque sortEdges(std::list& edges, bool keepOrder = false, double tol = 0.0); + static TopoShape reverseEdge (const TopoShape& edge); }; /// Shape hasher that ignore orientation diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index cc0c3a3ffb..9e9bfa4e64 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -695,6 +695,40 @@ struct EdgePoints } }; +TopoShape TopoShape::reverseEdge (const TopoShape& edge) { + Standard_Real first = NAN; + Standard_Real last = NAN; + const Handle(Geom_Curve)& curve = + BRep_Tool::Curve(TopoDS::Edge(edge.getShape()), first, last); + first = curve->ReversedParameter(first); + last = curve->ReversedParameter(last); + TopoShape res(BRepBuilderAPI_MakeEdge(curve->Reversed(), last, first)); + auto edgeName = Data::IndexedName::fromConst("Edge", 1); + if (auto mapped = edge.getMappedName(edgeName)) { + res.elementMap()->setElementName(edgeName, mapped, res.Tag); + } + auto v1Name = Data::IndexedName::fromConst("Vertex", 1); + auto v2Name = Data::IndexedName::fromConst("Vertex", 2); + auto v1 = edge.getMappedName(v1Name); + auto v2 = edge.getMappedName(v2Name); + if (v1 && v2) { + res.elementMap()->setElementName(v1Name, v2, res.Tag); + res.elementMap()->setElementName(v2Name, v1, res.Tag); + } + else if (v1 && edge.countSubShapes(TopAbs_EDGE) == 1) { + // It's possible an edge has only one vertex, so no need to reverse + // the name + res.elementMap()->setElementName(v1Name, v1, res.Tag); + } + else if (v1) { + res.elementMap()->setElementName(v2Name, v1, res.Tag); + } + else if (v2) { + res.elementMap()->setElementName(v1Name, v2, res.Tag); + } + return res; +}; + std::deque TopoShape::sortEdges(std::list& edges, bool keepOrder, double tol) { if (tol < Precision::Confusion()) { @@ -702,67 +736,33 @@ std::deque TopoShape::sortEdges(std::list& edges, bool kee } double tol3d = tol * tol; - std::list edge_points; + std::list edgePoints; for (auto it = edges.begin(); it != edges.end(); ++it) { - edge_points.emplace_back(it, tol3d); + edgePoints.emplace_back(it, tol3d); } std::deque sorted; - if (edge_points.empty()) { + if (edgePoints.empty()) { return sorted; } gp_Pnt first; gp_Pnt last; - first = edge_points.front().v1; - last = edge_points.front().v2; + first = edgePoints.front().v1; + last = edgePoints.front().v2; - sorted.push_back(*edge_points.front().edge); - edges.erase(edge_points.front().it); - if (edge_points.front().closed) { + sorted.push_back(*edgePoints.front().edge); + edges.erase(edgePoints.front().it); + if (edgePoints.front().closed) { return sorted; } - edge_points.erase(edge_points.begin()); + edgePoints.erase(edgePoints.begin()); - auto reverseEdge = [](const TopoShape& edge) { - Standard_Real first = NAN; - Standard_Real last = NAN; - const Handle(Geom_Curve)& curve = - BRep_Tool::Curve(TopoDS::Edge(edge.getShape()), first, last); - first = curve->ReversedParameter(first); - last = curve->ReversedParameter(last); - TopoShape res(BRepBuilderAPI_MakeEdge(curve->Reversed(), last, first)); - auto edgeName = Data::IndexedName::fromConst("Edge", 1); - if (auto mapped = edge.getMappedName(edgeName)) { - res.elementMap()->setElementName(edgeName, mapped, res.Tag); - } - auto v1Name = Data::IndexedName::fromConst("Vertex", 1); - auto v2Name = Data::IndexedName::fromConst("Vertex", 2); - auto v1 = edge.getMappedName(v1Name); - auto v2 = edge.getMappedName(v2Name); - if (v1 && v2) { - res.elementMap()->setElementName(v1Name, v2, res.Tag); - res.elementMap()->setElementName(v2Name, v1, res.Tag); - } - else if (v1 && edge.countSubShapes(TopAbs_EDGE) == 1) { - // It's possible an edge has only one vertex, so no need to reverse - // the name - res.elementMap()->setElementName(v1Name, v1, res.Tag); - } - else if (v1) { - res.elementMap()->setElementName(v2Name, v1, res.Tag); - } - else if (v2) { - res.elementMap()->setElementName(v1Name, v2, res.Tag); - } - return res; - }; - - while (!edge_points.empty()) { + while (!edgePoints.empty()) { // search for adjacent edge std::list::iterator pEI; - for (pEI = edge_points.begin(); pEI != edge_points.end(); ++pEI) { + for (pEI = edgePoints.begin(); pEI != edgePoints.end(); ++pEI) { if (pEI->closed) { continue; } @@ -779,37 +779,37 @@ std::deque TopoShape::sortEdges(std::list& edges, bool kee last = pEI->v2; sorted.push_back(*pEI->edge); edges.erase(pEI->it); - edge_points.erase(pEI); - pEI = edge_points.begin(); + edgePoints.erase(pEI); + pEI = edgePoints.begin(); break; } if (pEI->v2.SquareDistance(first) <= tol3d) { sorted.push_front(*pEI->edge); first = pEI->v1; edges.erase(pEI->it); - edge_points.erase(pEI); - pEI = edge_points.begin(); + edgePoints.erase(pEI); + pEI = edgePoints.begin(); break; } if (pEI->v2.SquareDistance(last) <= tol3d) { last = pEI->v1; sorted.push_back(reverseEdge(*pEI->edge)); edges.erase(pEI->it); - edge_points.erase(pEI); - pEI = edge_points.begin(); + edgePoints.erase(pEI); + pEI = edgePoints.begin(); break; } if (pEI->v1.SquareDistance(first) <= tol3d) { first = pEI->v2; sorted.push_front(reverseEdge(*pEI->edge)); edges.erase(pEI->it); - edge_points.erase(pEI); - pEI = edge_points.begin(); + edgePoints.erase(pEI); + pEI = edgePoints.begin(); break; } } - if ((pEI == edge_points.end()) || (last.SquareDistance(first) <= tol3d)) { + if ((pEI == edgePoints.end()) || (last.SquareDistance(first) <= tol3d)) { // no adjacent edge found or polyline is closed return sorted; } diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 964f346f88..b07e50c04a 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -161,4 +161,20 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) // 26 subshapes each } +TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesAdjacent) +{ + // 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}; + + // Act + topoShape.makeElementWires(shapes); + + // Assert + auto elementMap = topoShape.getElementMap(); + EXPECT_EQ(6, elementMap.size()); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)