From c8120bca9b722bb89609e8bd5a41eda0a0b65751 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 21 Feb 2024 15:31:02 +0100 Subject: [PATCH 01/16] Part/Toponaming: Transfer WireJoiner --- src/Mod/Part/App/CMakeLists.txt | 2 + src/Mod/Part/App/WireJoiner.cpp | 2436 +++++++++++++++++++++++++++++++ src/Mod/Part/App/WireJoiner.h | 67 + 3 files changed, 2505 insertions(+) create mode 100644 src/Mod/Part/App/WireJoiner.cpp create mode 100644 src/Mod/Part/App/WireJoiner.h diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index ab4cd149a9..dd95907b49 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -554,6 +554,8 @@ SET(Part_SRCS FaceMakerCheese.h FaceMakerBullseye.cpp FaceMakerBullseye.h + WireJoiner.cpp + WireJoiner.h ) if(FREECAD_USE_PCH) diff --git a/src/Mod/Part/App/WireJoiner.cpp b/src/Mod/Part/App/WireJoiner.cpp new file mode 100644 index 0000000000..80baa650b3 --- /dev/null +++ b/src/Mod/Part/App/WireJoiner.cpp @@ -0,0 +1,2436 @@ +/**************************************************************************** + * Copyright (c) 2022 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#include "PreCompiled.h" +#include +#include + +#ifndef _PreComp_ +# 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 +#endif + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "WireJoiner.h" + +#include "Geometry.h" +#include "PartFeature.h" +#include "TopoShapeOpCode.h" +#include "TopoShapeMapper.h" + +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +using RParameters = bgi::linear<16>; + +BOOST_GEOMETRY_REGISTER_POINT_3D_GET_SET( + gp_Pnt,double,bg::cs::cartesian,X,Y,Z,SetX,SetY,SetZ) + +FC_LOG_LEVEL_INIT("WireJoiner",true, true) + +using namespace Part; + +static inline void getEndPoints(const TopoDS_Edge &e, gp_Pnt &p1, gp_Pnt &p2) { + p1 = BRep_Tool::Pnt(TopExp::FirstVertex(e)); + p2 = BRep_Tool::Pnt(TopExp::LastVertex(e)); +} + +static inline void getEndPoints(const TopoDS_Wire &wire, gp_Pnt &p1, gp_Pnt &p2) { + BRepTools_WireExplorer xp(wire); + p1 = BRep_Tool::Pnt(TopoDS::Vertex(xp.CurrentVertex())); + for(;xp.More();xp.Next()); + p2 = BRep_Tool::Pnt(TopoDS::Vertex(xp.CurrentVertex())); +} + +static void _assertCheck(int line, bool cond, const char *msg) +{ + if (!cond) { + _FC_ERR(__FILE__, line, "Assert failed: " << msg); + throw Base::RuntimeError("Assertion failed"); + } +} + +#define assertCheck(cond) _assertCheck(__LINE__, cond, #cond) + +class WireJoiner::WireJoinerP { +public: + double myTol = Precision::Confusion(); + double myTol2 = myTol * myTol; + double myAngularTol = Precision::Angular(); + bool doSplitEdge = true; + bool doMergeEdge = true; + bool doOutline = false; + bool doTightBound = true; + + std::string catchObject; + int catchIteration; + int iteration = 0; + + typedef bg::model::box Box; + + bool checkBBox(const Bnd_Box &box) + { + if (box.IsVoid()) + return false; + Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + box.Get(xMin, yMin, zMin, xMax, yMax, zMax); + return zMax - zMin <= myTol; + } + + WireJoinerP() + { + auto hParam = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/WireJoiner"); + catchObject = hParam->GetASCII("ObjectName"); + catchIteration = hParam->GetInt("Iteration", 0); + } + + bool getBBox(const TopoDS_Shape &e, Bnd_Box &bound) { + BRepBndLib::AddOptimal(e,bound,Standard_False); + if (bound.IsVoid()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("failed to get bound of edge"); + return false; + } + if (!checkBBox(bound)) + showShape(e, "invalid"); + if (bound.SquareExtent() < myTol2) + return false; + bound.Enlarge(myTol); + return true; + } + + bool getBBox(const TopoDS_Shape &e, Box &box) { + Bnd_Box bound; + if (!getBBox(e, bound)) + return false; + Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + bound.Get(xMin, yMin, zMin, xMax, yMax, zMax); + box = Box(gp_Pnt(xMin,yMin,zMin), gp_Pnt(xMax,yMax,zMax)); + return true; + } + + struct WireInfo; + struct EdgeSet; + + struct EdgeInfo { + TopoDS_Edge edge; + TopoDS_Wire superEdge; + mutable TopoDS_Shape edgeReversed; + mutable TopoDS_Shape superEdgeReversed; + gp_Pnt p1; + gp_Pnt p2; + gp_Pnt mid; + Box box; + int iStart[2]; // adjacent list index start for p1 and p2 + int iEnd[2]; // adjacent list index end + int iteration; + int iteration2; + bool queryBBox; + std::shared_ptr wireInfo; + std::shared_ptr wireInfo2; // an edge can be shared by at most two tight bound wires. + std::unique_ptr geo; + Standard_Real firstParam; + Standard_Real lastParam; + Handle_Geom_Curve curve; + GeomAbs_CurveType type; + bool isLinear; + + EdgeInfo(const TopoDS_Edge &e, + const gp_Pnt &pt1, + const gp_Pnt &pt2, + const Box &bound, + bool bbox, + bool isLinear) + :edge(e),p1(pt1),p2(pt2),box(bound),queryBBox(bbox),isLinear(isLinear) + { + curve = BRep_Tool::Curve(e, firstParam, lastParam); + type = GeomAdaptor_Curve(curve).GetType(); + assertCheck(!curve.IsNull()); + GeomLProp_CLProps prop(curve,(firstParam+lastParam)*0.5,0,Precision::Confusion()); + mid = prop.Value(); + + iteration = 0; + reset(); + } + Geometry *geometry() { + if (!geo) + geo = Geometry::fromShape(edge, /*silent*/true); + return geo.get(); + } + void reset() { + wireInfo.reset(); + wireInfo2.reset(); + if (iteration >= 0) + iteration = 0; + iteration2 = 0; + iStart[0] = iStart[1] = iEnd[0] = iEnd[1] = -1; + } + const TopoDS_Shape &shape(bool forward=true) const + { + if (superEdge.IsNull()) { + if (forward) + return edge; + if (edgeReversed.IsNull()) + edgeReversed = edge.Reversed(); + return edgeReversed; + } + if (forward) + return superEdge; + if (superEdgeReversed.IsNull()) + superEdgeReversed = superEdge.Reversed(); + return superEdgeReversed; + } + TopoDS_Wire wire() const + { + auto s = shape(); + if (s.ShapeType() == TopAbs_WIRE) + return TopoDS::Wire(s); + return BRepBuilderAPI_MakeWire(TopoDS::Edge(s)).Wire(); + } + }; + + template + struct VectorSet { + void sort() + { + if (!sorted) { + sorted = true; + std::sort(data.begin(), data.end()); + } + } + bool contains(const T &v) + { + if (!sorted) { + if (data.size() < 30) + return std::find(data.begin(), data.end(), v) != data.end(); + sort(); + } + auto it = std::lower_bound(data.begin(), data.end(), v); + return it!=data.end() && *it == v; + } + bool intersects(const VectorSet &other) + { + if (other.size() < size()) + return other.intersects(*this); + else if (!sorted) { + for (const auto &v : data) { + if (other.contains(v)) + return true; + } + } else { + other.sort(); + auto it = other.data.begin(); + for (const auto &v : data) { + it = std::lower_bound(it, other.data.end(), v); + if (it == other.data.end()) + return false; + if (*it == v) + return true; + } + } + return false; + } + void insert(const T &v) + { + if (sorted) + data.insert(std::upper_bound(data.begin(), data.end(), v), v); + else + data.push_back(v); + } + bool insertUnique(const T &v) + { + if (sorted) { + auto it = std::lower_bound(data.begin(), data.end(), v); + if (it != data.end() && *it == v) + return false; + data.insert(it, v); + return true; + } + + if (contains(v)) + return false; + data.push_back(v); + return true; + } + void erase(const T &v) + { + if (!sorted) + data.erase(std::remove(data.begin(), data.end(), v), data.end()); + else { + auto it = std::lower_bound(data.begin(), data.end(), v); + auto itEnd = it; + while (itEnd!=data.end() && *itEnd==v) + ++itEnd; + data.erase(it, itEnd); + } + if (data.size() < 20) + sorted = false; + } + void clear() + { + data.clear(); + sorted = false; + } + std::size_t size() const + { + return data.size(); + } + bool empty() const + { + return data.empty(); + } + private: + bool sorted = false; + std::vector data; + }; + + Handle(BRepTools_History) aHistory = new BRepTools_History; + + typedef std::list Edges; + Edges edges; + + std::map edgesTable; + + struct VertexInfo { + Edges::iterator it; + bool start; + VertexInfo() + {} + VertexInfo(Edges::iterator it, bool start) + :it(it),start(start) + {} + VertexInfo reversed() const { + return VertexInfo(it, !start); + } + bool operator==(const VertexInfo &other) const { + return it==other.it && start==other.start; + } + bool operator<(const VertexInfo &other) const { + auto a = edgeInfo(); + auto b = other.edgeInfo(); + if (a < b) + return true; + if (a > b) + return false; + return start < other.start; + } + const gp_Pnt &pt() const { + return start?it->p1:it->p2; + } + gp_Pnt &pt() { + return start?it->p1:it->p2; + } + const gp_Pnt &ptOther() const { + return start?it->p2:it->p1; + } + gp_Pnt &ptOther() { + return start?it->p2:it->p1; + } + TopoDS_Vertex vertex() const { + return start ? TopExp::FirstVertex(edge()) : TopExp::LastVertex(edge()); + } + TopoDS_Vertex otherVertex() const { + return start ? TopExp::LastVertex(edge()) : TopExp::FirstVertex(edge()); + } + EdgeInfo *edgeInfo() const { + return &(*it); + } + const TopoDS_Edge &edge() const { + return it->edge; + } + }; + + struct StackInfo { + size_t iStart; + size_t iEnd; + size_t iCurrent; + StackInfo(size_t idx=0):iStart(idx),iEnd(idx),iCurrent(idx){} + }; + + std::vector stack; + std::vector vertexStack; + std::vector tmpVertices; + std::vector adjacentList; + + struct WireInfo { + std::vector vertices; + mutable std::vector sorted; + TopoDS_Wire wire; + TopoDS_Face face; + mutable Bnd_Box box; + bool done = false; + bool purge = false; + + void sort() const + { + if (sorted.size() == vertices.size()) + return; + assertCheck(sorted.size() < vertices.size()); + sorted.reserve(vertices.size()); + for (int i=(int)sorted.size(); i<(int)vertices.size(); ++i) + sorted.push_back(i); + std::sort(sorted.begin(), sorted.end(), [&](int a, int b) { + return vertices[a] < vertices[b]; + }); + } + int find(const VertexInfo &info) const + { + if (vertices.size() < 20) { + auto it = std::find(vertices.begin(), vertices.end(), info); + if (it == vertices.end()) + return 0; + return it - vertices.begin() + 1; + } + sort(); + auto it = std::lower_bound(sorted.begin(), sorted.end(), info, + [&](int idx, const VertexInfo &v) {return vertices[idx]edgeInfo() == info) + return it - vertices.begin() + 1; + } + return 0; + } + sort(); + auto it = std::lower_bound(sorted.begin(), sorted.end(), info, + [&](int idx, const EdgeInfo *v) {return vertices[idx].edgeInfo() { + }; + EdgeSet edgeSet; + + struct WireSet: VectorSet { + }; + WireSet wireSet; + + const Bnd_Box &getWireBound(const WireInfo &wireInfo) + { + if (wireInfo.box.IsVoid()) { + for (auto &v : wireInfo.vertices) + BRepBndLib::Add(v.it->shape(),wireInfo.box); + wireInfo.box.Enlarge(myTol); + } + return wireInfo.box; + } + + bool initWireInfo(WireInfo &wireInfo) + { + if (!wireInfo.face.IsNull()) + return true; + getWireBound(wireInfo); + if (wireInfo.wire.IsNull()) { + wireData->Clear(); + for (auto &v : wireInfo.vertices) + wireData->Add(v.it->shape(v.start)); + wireInfo.wire = makeCleanWire(); + } + + if (!BRep_Tool::IsClosed(wireInfo.wire)) { + showShape(wireInfo.wire, "FailedToClose"); + FC_ERR("Wire not closed"); + for (auto &v : wireInfo.vertices) { + showShape(v.edgeInfo(), v.start ? "failed" : "failed_r", iteration); + } + return false; + } + + BRepBuilderAPI_MakeFace mkFace(wireInfo.wire); + if (!mkFace.IsDone()) { + FC_ERR("Failed to create face for wire"); + showShape(wireInfo.wire, "FailedFace"); + return false; + } + wireInfo.face = mkFace.Face(); + return true; + } + + bool isInside(const WireInfo &wireInfo, gp_Pnt &pt) + { + if (getWireBound(wireInfo).IsOut(pt)) + return false; + BRepClass_FaceClassifier fc(wireInfo.face, pt, myTol); + return fc.State() == TopAbs_IN; + } + + bool isOutside(const WireInfo &wireInfo, gp_Pnt &pt) + { + if (getWireBound(wireInfo).IsOut(pt)) + return false; + BRepClass_FaceClassifier fc(wireInfo.face, pt, myTol); + return fc.State() == TopAbs_OUT; + } + + struct PntGetter + { + typedef const gp_Pnt& result_type; + result_type operator()(const VertexInfo &v) const { + return v.pt(); + } + }; + + bgi::rtree vmap; + + struct BoxGetter + { + typedef const Box& result_type; + result_type operator()(Edges::iterator it) const { + return it->box; + } + }; + bgi::rtree boxMap; + + BRep_Builder builder; + TopoDS_Compound compound; + + std::unordered_set sourceEdges; + std::vector sourceEdgeArray; + TopoDS_Compound openWireCompound; + + Handle(ShapeExtend_WireData) wireData = new ShapeExtend_WireData(); + + void clear() + { + aHistory->Clear(); + iteration = 0; + boxMap.clear(); + vmap.clear(); + edges.clear(); + edgeSet.clear(); + wireSet.clear(); + adjacentList.clear(); + stack.clear(); + tmpVertices.clear(); + vertexStack.clear(); + builder.MakeCompound(compound); + openWireCompound.Nullify(); + } + + Edges::iterator remove(Edges::iterator it) + { + if (it->queryBBox) + boxMap.remove(it); + vmap.remove(VertexInfo(it,true)); + vmap.remove(VertexInfo(it,false)); + return edges.erase(it); + } + + void remove(EdgeInfo *info) + { + if (edgesTable.empty()) { + for (auto it=edges.begin(); it!=edges.end(); ++it) + edgesTable[&(*it)] = it; + } + auto it = edgesTable.find(info); + if (it != edgesTable.end()) { + remove(it->second); + edgesTable.erase(it); + } + } + + void add(Edges::iterator it) + { + vmap.insert(VertexInfo(it,true)); + vmap.insert(VertexInfo(it,false)); + if (it->queryBBox) + boxMap.insert(it); + showShape(it->edge, "add"); + } + + int add(const TopoDS_Edge &e, bool queryBBox=false) + { + auto it = edges.begin(); + return add(e, queryBBox, it); + } + + int add(const TopoDS_Edge &e, bool queryBBox, Edges::iterator &it) + { + Box bbox; + if (!getBBox(e, bbox)) { + showShape(e, "small"); + aHistory->Remove(e); + return 0; + } + return add(e, queryBBox, bbox, it) ? 1 : -1; + } + + bool add(const TopoDS_Edge &e, bool queryBBox, const Box &bbox, Edges::iterator &it) + { + gp_Pnt p1,p2; + getEndPoints(e,p1,p2); + TopoDS_Vertex v1, v2; + TopoDS_Edge ev1, ev2; + double tol = myTol2; + // search for duplicate edges + showShape(e, "addcheck"); + bool isLinear = TopoShape(e).isLinearEdge(); + std::unique_ptr geo; + + for (auto vit=vmap.qbegin(bgi::nearest(p1,INT_MAX));vit!=vmap.qend();++vit) { + auto &vinfo = *vit; + if (canShowShape()) + FC_MSG("addcheck " << vinfo.edge().HashCode(INT_MAX)); + double d1 = vinfo.pt().SquareDistance(p1); + if (d1 >= tol) + break; + if (v1.IsNull()) { + ev1 = vinfo.edge(); + v1 = vinfo.vertex(); + } + double d2 = vinfo.ptOther().SquareDistance(p2); + if (d2 < tol) { + if (v2.IsNull()) { + ev2 = vinfo.edge(); + v2 = vinfo.otherVertex(); + } + if (isLinear && vinfo.edgeInfo()->isLinear) { + showShape(e, "duplicate"); + aHistory->Remove(e); + return false; + } + else if (auto geoEdge = vinfo.edgeInfo()->geometry()) { + if (!geo) + geo = Geometry::fromShape(e, /*silent*/true); + if (geo && geo->isSame(*geoEdge, myTol, myAngularTol)) { + showShape(e, "duplicate"); + aHistory->Remove(e); + return false; + } + } + } + } + if (v2.IsNull()) { + for (auto vit=vmap.qbegin(bgi::nearest(p2,1));vit!=vmap.qend();++vit) { + auto &vinfo = *vit; + double d1 = vinfo.pt().SquareDistance(p2); + if (d1 < tol) { + v2 = vit->vertex(); + ev2 = vit->edge(); + } + } + } + + // Make sure coincident vertices are actually the same TopoDS_Vertex, + // which is crucial for the OCC internal shape hierarchy structure. We + // achieve this by making a temp wire and let OCC do the hard work of + // replacing the vertex. + auto connectEdge = [&](TopoDS_Edge &e, + const TopoDS_Vertex &v, + const TopoDS_Edge &eOther, + const TopoDS_Vertex &vOther) + { + if (vOther.IsNull()) + return; + if (v.IsSame(vOther)) + return; + double tol = std::max(BRep_Tool::Pnt(v).Distance(BRep_Tool::Pnt(vOther)), + BRep_Tool::Tolerance(vOther)); + if (tol >= BRep_Tool::Tolerance(v)) { + ShapeFix_ShapeTolerance fix; + fix.SetTolerance(v, std::max(tol*0.5, myTol), TopAbs_VERTEX); + } + BRepBuilderAPI_MakeWire mkWire(eOther); + mkWire.Add(e); + auto newEdge = mkWire.Edge(); + TopoDS_Vertex vFirst = TopExp::FirstVertex(newEdge); + TopoDS_Vertex vLast = TopExp::LastVertex(newEdge); + assertCheck(vLast.IsSame(vOther) || vFirst.IsSame(vOther)); + e = newEdge; + }; + + TopoDS_Edge edge = e; + TopoDS_Vertex vFirst = TopExp::FirstVertex(e); + TopoDS_Vertex vLast = TopExp::LastVertex(e); + connectEdge(edge, vFirst, ev1, v1); + connectEdge(edge, vLast, ev2, v2); + if (!edge.IsSame(e)) { + auto itSource = sourceEdges.find(e); + if (itSource != sourceEdges.end()) { + TopoShape newEdge = *itSource; + newEdge.setShape(edge, false); + sourceEdges.erase(itSource); + sourceEdges.insert(newEdge); + } + getEndPoints(edge,p1,p2); + // Shall we also update bbox? + } + it = edges.emplace(it,edge,p1,p2,bbox,queryBBox,isLinear); + add(it); + return true; + } + + void add(const TopoDS_Shape &shape, bool queryBBox=false) + { + for (TopExp_Explorer xp(shape,TopAbs_EDGE); xp.More(); xp.Next()) + add(TopoDS::Edge(xp.Current()),queryBBox); + } + + //This algorithm tries to join connected edges into wires + // + //tol*tol>Precision::SquareConfusion() can be used to join points that are + //close but do not coincide with a line segment. The close points may be + //the results of rounding issue. + // + void join() + { + double tol = myTol2; + while (edges.size()) { + auto it = edges.begin(); + BRepBuilderAPI_MakeWire mkWire; + mkWire.Add(it->edge); + gp_Pnt pstart(it->p1),pend(it->p2); + remove(it); + + bool done = false; + for (int idx=0;!done&&idx<2;++idx) { + while (edges.size()) { + std::vector ret; + ret.reserve(1); + const gp_Pnt &pt = idx==0?pstart:pend; + vmap.query(bgi::nearest(pt,1),std::back_inserter(ret)); + assertCheck(ret.size()==1); + double d = ret[0].pt().SquareDistance(pt); + if (d > tol) break; + + const auto &info = *ret[0].it; + bool start = ret[0].start; + if (d > Precision::SquareConfusion()) { + // insert a filling edge to solve the tolerance problem + const gp_Pnt &pt = ret[idx].pt(); + if (idx) + mkWire.Add(BRepBuilderAPI_MakeEdge(pend,pt).Edge()); + else + mkWire.Add(BRepBuilderAPI_MakeEdge(pt,pstart).Edge()); + } + + if (idx==1 && start) { + pend = info.p2; + mkWire.Add(info.edge); + }else if (idx==0 && !start) { + pstart = info.p1; + mkWire.Add(info.edge); + }else if (idx==0 && start) { + pstart = info.p2; + mkWire.Add(TopoDS::Edge(info.edge.Reversed())); + }else { + pend = info.p1; + mkWire.Add(TopoDS::Edge(info.edge.Reversed())); + } + remove(ret[0].it); + if (pstart.SquareDistance(pend)<=Precision::SquareConfusion()){ + done = true; + break; + } + } + } + builder.Add(compound,mkWire.Wire()); + } + } + + struct IntersectInfo { + double param; + TopoDS_Shape intersectShape; + gp_Pnt point; + IntersectInfo(double p, const gp_Pnt &pt, const TopoDS_Shape &s) + :param(p), intersectShape(s), point(pt) + {} + bool operator<(const IntersectInfo &other) const { + return param < other.param; + } + }; + + void checkSelfIntersection(const EdgeInfo &info, std::set ¶ms) + { + // Early return if checking for self intersection (only for non linear spline curves) + if (info.type <= GeomAbs_Parabola || info.isLinear) + return; + IntRes2d_SequenceOfIntersectionPoint points2d; + TColgp_SequenceOfPnt points3d; + TColStd_SequenceOfReal errors; + TopoDS_Wire wire; + BRepBuilderAPI_MakeWire mkWire(info.edge); + if (!mkWire.IsDone()) + return; + if (!BRep_Tool::IsClosed(mkWire.Wire())) { + BRepBuilderAPI_MakeEdge mkEdge(info.p1, info.p2); + if (!mkEdge.IsDone()) + return; + mkWire.Add(mkEdge.Edge()); + } + wire = mkWire.Wire(); + BRepBuilderAPI_MakeFace mkFace(wire); + if (!mkFace.IsDone()) + return; + TopoDS_Face face = mkFace.Face(); + ShapeAnalysis_Wire analysis(wire, face, myTol); + analysis.CheckSelfIntersectingEdge(1, points2d, points3d); + assertCheck(points2d.Length() == points3d.Length()); + for (int i=1; i<=points2d.Length(); ++i) { + params.emplace(points2d(i).ParamOnFirst(), points3d(i), info.edge); + params.emplace(points2d(i).ParamOnSecond(), points3d(i), info.edge); + } + } + + void checkIntersection(const EdgeInfo &info, + const EdgeInfo &other, + std::set ¶ms1, + std::set ¶ms2) + { + gp_Pln pln; + bool planar = TopoShape(info.edge).findPlane(pln); + if (!planar) { + TopoDS_Compound comp; + builder.MakeCompound(comp); + builder.Add(comp, info.edge); + builder.Add(comp, other.edge); + planar = TopoShape(comp).findPlane(pln); + if (!planar) { + BRepExtrema_DistShapeShape extss(info.edge, other.edge); + extss.Perform(); + if (extss.IsDone() && extss.NbSolution() > 0) + if (!extss.IsDone() || extss.NbSolution()<=0 || extss.Value()>=myTol) + return; + for (int i=1; i<=extss.NbSolution(); ++i) { + Standard_Real p; + auto s1 = extss.SupportOnShape1(i); + auto s2 = extss.SupportOnShape2(i); + if (s1.ShapeType() == TopAbs_EDGE) { + extss.ParOnEdgeS1(i,p); + pushIntersection(params1, p, extss.PointOnShape1(i), other.edge); + } + if (s2.ShapeType() == TopAbs_EDGE) { + extss.ParOnEdgeS2(i,p); + pushIntersection(params2, p, extss.PointOnShape2(i), info.edge); + } + } + return; + } + } + // BRepExtrema_DistShapeShape has trouble finding all solutions for a + // spline curve. ShapeAnalysis_Wire is better. Besides, it can check + // for self intersection. It's slightly more troublesome to use, as it + // requires to build a face for the wire, so we only use it for planar + // cases. + + IntRes2d_SequenceOfIntersectionPoint points2d; + TColgp_SequenceOfPnt points3d; + TColStd_SequenceOfReal errors; + TopoDS_Wire wire; + int idx; + BRepBuilderAPI_MakeWire mkWire(info.edge); + mkWire.Add(other.edge); + if (mkWire.IsDone()) + idx = 2; + else if (mkWire.Error() == BRepBuilderAPI_DisconnectedWire) { + idx = 3; + BRepBuilderAPI_MakeEdge mkEdge(info.p1, other.p1); + if (!mkEdge.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Failed to build edge for checking intersection"); + return; + } + mkWire.Add(mkEdge.Edge()); + mkWire.Add(other.edge); + } + + if (!mkWire.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Failed to build wire for checking intersection"); + return; + } + wire = mkWire.Wire(); + if (!BRep_Tool::IsClosed(wire)) { + gp_Pnt p1, p2; + getEndPoints(wire, p1, p2); + BRepBuilderAPI_MakeEdge mkEdge(p1, p2); + if (!mkEdge.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Failed to build edge for checking intersection"); + return; + } + mkWire.Add(mkEdge.Edge()); + } + + BRepBuilderAPI_MakeFace mkFace(wire); + if (!mkFace.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Failed to build face for checking intersection"); + return; + } + TopoDS_Face face = mkFace.Face(); + ShapeAnalysis_Wire analysis(wire, face, myTol); + analysis.CheckIntersectingEdges(1, idx, points2d, points3d, errors); + assertCheck(points2d.Length() == points3d.Length()); + for (int i=1; i<=points2d.Length(); ++i) { + pushIntersection(params1, points2d(i).ParamOnFirst(), points3d(i), other.edge); + pushIntersection(params2, points2d(i).ParamOnSecond(), points3d(i), info.edge); + } + } + + void pushIntersection(std::set ¶ms, double param, const gp_Pnt &pt, const TopoDS_Shape &shape) + { + IntersectInfo info(param, pt, shape); + auto it = params.upper_bound(info); + if (it != params.end()) { + if (it->point.SquareDistance(pt) < myTol2) + return; + } + if (it != params.begin()) { + auto itPrev = it; + --itPrev; + if (itPrev->point.SquareDistance(pt) < myTol2) + return; + } + params.insert(it, info); + return; + } + + struct SplitInfo { + TopoDS_Edge edge; + TopoDS_Shape intersectShape; + Box bbox; + }; + + // Try splitting any edges that intersects other edge + void splitEdges() + { + std::unordered_map> intersects; + + int i=0; + for (auto &info : edges) + info.iteration = ++i; + + std::unique_ptr seq( + new Base::SequencerLauncher("Splitting edges", edges.size())); + + i = 0; + for (auto it=edges.begin(); it!=edges.end();++it) { + seq->next(true); + ++i; + auto &info = *it; + auto ¶ms = intersects[&info]; + checkSelfIntersection(info, params); + + for (auto vit=boxMap.qbegin(bgi::intersects(info.box)); vit!=boxMap.qend(); ++vit) { + const auto &other = *(*vit); + if (other.iteration <= i) { + // means the edge is before us, and we've already checked intersection + continue; + } + checkIntersection(info, other, params, intersects[&other]); + } + } + + i=0; + std::vector splitted; + for (auto it=edges.begin(); it!=edges.end(); ) { + ++i; + auto iter = intersects.find(&(*it)); + if (iter == intersects.end()) { + ++it; + continue; + } + auto &info = *it; + auto ¶ms = iter->second; + if (params.empty()) { + ++it; + continue; + } + + auto itParam = params.begin(); + if (itParam->point.SquareDistance(info.p1) < myTol2) + params.erase(itParam); + params.emplace(info.firstParam, info.p1, TopoDS_Shape()); + itParam = params.end(); + --itParam; + if (itParam->point.SquareDistance(info.p2) < myTol2) + params.erase(itParam); + params.emplace(info.lastParam, info.p2, TopoDS_Shape()); + + if (params.size() <= 2) { + ++it; + continue; + } + + splitted.clear(); + itParam = params.begin(); + for (auto itPrevParam=itParam++; itParam!=params.end(); ++itParam) { + const auto &intersectShape = itParam->intersectShape.IsNull() + ? itPrevParam->intersectShape : itParam->intersectShape; + if (intersectShape.IsNull()) + break; + + // Using points cause MakeEdge failure for some reason. Using + // parameters is better. + // + const gp_Pnt &p1 = itPrevParam->point; + const gp_Pnt &p2 = itParam->point; + const Standard_Real ¶m1 = itPrevParam->param; + const Standard_Real ¶m2 = itParam->param; + + BRepBuilderAPI_MakeEdge mkEdge(info.curve, param1, param2); + if (mkEdge.IsDone()) { + splitted.emplace_back(); + auto &entry = splitted.back(); + entry.edge = mkEdge.Edge(); + entry.intersectShape = intersectShape; + if (getBBox(entry.edge, entry.bbox)) + itPrevParam = itParam; + else + splitted.pop_back(); + } + else if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("edge split failed " + << std::setprecision(16) + << FC_XYZ(p1) << FC_XYZ(p2) + << ": " << mkEdge.Error()); + } + } + if (splitted.size() <= 1) { + ++it; + continue; + } + + showShape(info.edge, "remove"); + auto removedEdge = info.edge; + it = remove(it); + for (const auto &v : splitted) { + if (!add(v.edge, false, v.bbox, it)) + continue; + auto &newInfo = *it++; + aHistory->AddModified(v.intersectShape, newInfo.edge); + // if (v.intersectShape != removedEdge) + // aHistory->AddModified(removedEdge, newInfo.edge); + showShape(newInfo.edge, "split"); + } + } + } + + void findSuperEdges() + { + std::unique_ptr seq( + new Base::SequencerLauncher("Combining edges", edges.size())); + + std::deque vertices; + + ++iteration; + + // Join edges (let's call it super edge) that are connected to only one + // other edges (count == 2 counts this and the other edge) on one of + // its vertices to save traverse time. + for (auto it=edges.begin(); it!=edges.end(); ++it) { + seq->next(true); + auto &info = *it; + if (info.iteration == iteration || info.iteration < 0) + continue; + info.iteration = iteration; + // showShape(&info, "scheck"); + + vertices.clear(); + vertices.emplace_back(it, true); + edgeSet.clear(); + + bool done = false; + for (int k=0; k<2; ++k) { // search in both direction + auto begin = k==1 ? vertices.back().reversed() : vertices.front(); + while (true) { + auto currentVertex = k==1 ? vertices.front() : vertices.back(); + auto current = currentVertex.edgeInfo(); + // showShape(current, "siter", k); + int idx = (currentVertex.start?1:0)^k; + EdgeInfo *found = nullptr; + for (int i=current->iStart[idx]; iiEnd[idx]; ++i) { + const auto &v = adjacentList[i]; + auto next = v.edgeInfo(); + if (next->iteration < 0 // skipped + || next == current) // skip self (see how adjacent list is built) + continue; + if (v == begin) { + // closed + done = true; + break; + } + if (found // more than one branch + || edgeSet.contains(next)) // or, self intersect + { + // if (found) { + // showShape(found, "branch_a", k); + // showShape(next, "branch_b", k); + // } else { + // showShape(next, "insect", k); + // } + found = nullptr; + break; + } + found = next; + currentVertex = v; + } + if (done || !found) + break; + // showShape(found, "snext", k); + if (k==1) { + edgeSet.insert(current); + vertices.push_front(currentVertex.reversed()); + } else { + edgeSet.insert(found); + vertices.push_back(currentVertex); + } + } + if (done) + break; + } + + if (vertices.size() <= 1) + continue; + + wireData->Clear(); + Bnd_Box bbox; + for (const auto &v : vertices) { + auto current = v.edgeInfo(); + bbox.Add(current->box.min_corner()); + bbox.Add(current->box.max_corner()); + wireData->Add(current->shape(v.start)); + showShape(current, "edge"); + current->iteration = -1; + } + auto first = vertices.front().edgeInfo(); + first->superEdge = makeCleanWire(false); + first->superEdgeReversed.Nullify(); + if (BRep_Tool::IsClosed(first->superEdge)) { + first->iteration = -2; + showShape(first, "super_done"); + } else { + first->iteration = iteration; + showShape(first, "super"); + auto &vFirst = vertices.front(); + auto &vLast = vertices.back(); + auto last = vLast.edgeInfo(); + vFirst.ptOther() = vLast.ptOther(); + int idx = vFirst.start ? 1 : 0; + first->iStart[idx] = last->iStart[vLast.start?1:0]; + first->iEnd[idx] = last->iEnd[vLast.start?1:0]; + + for (int i=first->iStart[idx];iiEnd[idx];++i) { + auto &v = adjacentList[i]; + if (v.it == vLast.it) { + v.it = vFirst.it; + v.start = !vFirst.start; + } + } + bbox.Enlarge(myTol); + first->box = Box(bbox.CornerMin(), bbox.CornerMax()); + } + } + } + + void buildAdjacentList() + { + builder.MakeCompound(compound); + + for (auto &info : edges) + info.reset(); + + adjacentList.clear(); + + // populate adjacent list + for (auto &info : edges) { + if (info.iteration == -2) { +#if OCC_VERSION_HEX >= 0x070000 + assertCheck(BRep_Tool::IsClosed(info.shape())); +#endif + showShape(&info,"closed"); + if (!doTightBound) + builder.Add(compound,info.wire()); + continue; + } else if (info.iteration < 0) + continue; + + if (info.p1.SquareDistance(info.p2)<=myTol2) { + if (!doTightBound) + builder.Add(compound,info.wire()); + info.iteration = -2; + continue; + } + + gp_Pnt pt[2]; + pt[0] = info.p1; + pt[1] = info.p2; + for (int i=0;i<2;++i) { + if (info.iStart[i]>=0) + continue; + info.iEnd[i] = info.iStart[i] = (int)adjacentList.size(); + + for (auto vit=vmap.qbegin(bgi::nearest(pt[i],INT_MAX));vit!=vmap.qend();++vit) { + auto &vinfo = *vit; + if (vinfo.pt().SquareDistance(pt[i]) > myTol2) + break; + + // We must push ourself too, because the adjacency + // information is shared among all connected edges. + // + // if (&(*vinfo.it) == &info) + // continue; + + if (vinfo.it->iteration < 0) + continue; + + adjacentList.push_back(vinfo); + ++info.iEnd[i]; + } + + // copy the adjacent indices to all connected edges + for (int j=info.iStart[i];jiteration >= 0 && other != &info) + break; + } + if (i == info.iEnd[k]) { + // If merge or tight bound, then repeat until no edges + // can be skipped. + done = !doMergeEdge & !doTightBound; + info.iteration = -3; + showShape(&info, "skip"); + break; + } + } + } + } + } + + // This algorithm tries to find a set of closed wires that includes as many + // edges (added by calling add() ) as possible. One edge may be included + // in more than one closed wires if it connects to more than one edges. + void findClosedWires(bool tightBound=false) + { + std::unique_ptr seq( + new Base::SequencerLauncher("Finding wires", edges.size())); + + for (auto &info : edges) { + info.wireInfo.reset(); + info.wireInfo2.reset(); + } + + for (auto it=edges.begin(); it!=edges.end(); ++it) { + VertexInfo beginVertex(it, true); + auto &beginInfo = *it; + seq->next(true); + ++iteration; + if (beginInfo.iteration < 0 || beginInfo.wireInfo) + continue; + + VertexInfo currentVertex(it, true); + EdgeInfo *currentInfo = &beginInfo; + showShape(currentInfo, "begin"); + stack.clear(); + vertexStack.clear(); + edgeSet.clear(); + + TopoDS_Wire wire = _findClosedWires(beginVertex, currentVertex); + if (wire.IsNull()) + continue; + + if (tightBound) { + assert(!beginInfo.wireInfo); + beginInfo.wireInfo.reset(new WireInfo); + beginInfo.wireInfo->vertices.emplace_back(it, true); + beginInfo.wireInfo->wire = wire; + } + for (auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + auto &info = *v.it; + if (tightBound) + beginInfo.wireInfo->vertices.push_back(v); + if (!info.wireInfo) { + info.wireInfo = beginInfo.wireInfo; + // showShape(&info, "visited"); + } + } + showShape(wire,"joined"); + if (!tightBound) + builder.Add(compound, wire); + } + } + + void checkStack() + { +#if 0 + if (stack.size() <= 1) + return; + std::vector edges; + auto &r = stack[stack.size()-2]; + for (int i=r.iStart;ivertices.front().edgeInfo()->wireInfo.get() == w); + } + } + + TopoDS_Wire _findClosedWires(VertexInfo beginVertex, + VertexInfo currentVertex, + std::shared_ptr wireInfo = std::shared_ptr(), + int *idxVertex = nullptr, + int *stackPos = nullptr) + { + Base::SequencerBase::Instance().checkAbort(); + EdgeInfo &beginInfo = *beginVertex.it; + + EdgeInfo *currentInfo = currentVertex.edgeInfo(); + int currentIdx = currentVertex.start ? 1 : 0; + currentInfo->iteration = iteration; + + gp_Pnt pstart = beginVertex.pt(); + gp_Pnt pend = currentVertex.ptOther(); + + auto stackEnd = stack.size(); + checkStack(); + + // pstart and pend is the start and end vertex of the current wire + while (true) { + // push a new stack entry + stack.emplace_back(vertexStack.size()); + auto &r = stack.back(); + showShape(currentInfo, "check", iteration); + + bool proceed = true; + + // The loop below is to find all edges connected to pend, and save them into stack.back() + auto size = vertexStack.size(); + for (int i=currentInfo->iStart[currentIdx];iiEnd[currentIdx];++i) { + auto &vinfo = adjacentList[i]; + auto &info = *vinfo.it; + if (info.iteration < 0 || currentInfo == &info) + continue; + + bool abort = false; + if (!wireSet.empty() && wireSet.contains(info.wireInfo.get())) { + showShape(&info, "wired", iteration); + if (wireInfo) + wireInfo->purge = true; + abort = true; + } + + if (edgeSet.contains(&info)) { + showShape(&info, "intersect", iteration); + // This means the current edge connects to an + // existing edge in the middle of the stack. + // skip the current edge. + r.iEnd = r.iStart; + vertexStack.resize(size); + break; + } + + if (abort || currentInfo->wireInfo2) { + if (wireInfo) + wireInfo->purge = true; + continue; + } + + if (info.iteration == iteration) + continue; + info.iteration = iteration; + + if (wireInfo) { + // We may be called by findTightBound() with an existing wire + // to try to find a new wire by splitting the current one. So + // check if we've iterated to some edge in the existing wire. + if (int idx = wireInfo->find(vinfo)) { + vertexStack.push_back(adjacentList[i]); + r.iCurrent = r.iEnd++; + --idx; + proceed = false; + if (idxVertex) + *idxVertex = idx; + if (stackPos) + *stackPos = (int)stack.size()-2; + + auto info = wireInfo->vertices[idx].edgeInfo(); + showShape(info, "merge", iteration); + + if (info != &beginInfo) { + while (true) { + if (++idx == (int)wireInfo->vertices.size()) + idx = 0; + info = wireInfo->vertices[idx].edgeInfo(); + if (info == &beginInfo) + break; + stack.emplace_back(vertexStack.size()); + vertexStack.push_back(wireInfo->vertices[idx]); + ++stack.back().iEnd; + checkStack(); + } + } + break; + } + + if (wireInfo->find(VertexInfo(vinfo.it, !vinfo.start))) { + showShape(&info, "rintersect", iteration); + // Only used when exhausting tight bound. + wireInfo->purge = true; + continue; + } + + if (isOutside(*wireInfo, info.mid)) { + showShape(&info, "outside", iteration); + continue; + } + } + vertexStack.push_back(adjacentList[i]); + ++r.iEnd; + } + checkStack(); + + if (proceed) { + while (true) { + auto &r = stack.back(); + if (r.iCurrentwireInfo.get()); + break; + } + vertexStack.erase(vertexStack.begin()+r.iStart,vertexStack.end()); + + stack.pop_back(); + if (stack.size() == stackEnd) { + // If stack reaches the end, it means this wire is open. + return TopoDS_Wire(); + } + + auto &lastInfo = *vertexStack[stack.back().iCurrent].it; + edgeSet.erase(&lastInfo); + wireSet.erase(lastInfo.wireInfo.get()); + showShape(&lastInfo, "pop", iteration); + ++stack.back().iCurrent; + } + + if (pstart.SquareDistance(pend) > myTol2) { + // if the wire is not closed yet, continue search for the + // next connected edge + continue; + } + if (wireInfo) { + if (idxVertex) + *idxVertex = (int)wireInfo->vertices.size(); + if (stackPos) + *stackPos = (int)stack.size()-1; + } + } + + wireData->Clear(); + wireData->Add(beginInfo.shape(beginVertex.start)); + for (auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + auto &info = *v.it; + wireData->Add(info.shape(v.start)); + } + TopoDS_Wire wire = makeCleanWire(); + if (!BRep_Tool::IsClosed(wire)) { + FC_WARN("failed to close some wire in iteration " << iteration); + showShape(wire,"_FailedToClose", iteration); + showShape(beginInfo.shape(beginVertex.start), "failed", iteration); + for (auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + auto &info = *v.it; + showShape(info.shape(v.start), v.start ? "failed" : "failed_r", iteration); + } + assertCheck(false); + continue; + } + return wire; + } + } + + void findTightBound() + { + // Assumption: all edges lies on a common manifold surface + // + // Definition of 'Tight Bound': a wire that cannot be splitted into + // smaller wires by any intersecting edges internal to the wire. + // + // The idea of the searching algorithm is simple. The initial condition + // here is that we've found a closed wire for each edge. To find the + // tight bound, for each wire, check wire edge branches (using the + // adjacent list built earlier), and split the wire whenever possible. + + std::unique_ptr seq( + new Base::SequencerLauncher("Finding tight bound", edges.size())); + + int iteration2 = iteration; + for (auto &info : edges) { + ++iteration; + seq->next(true); + if (info.iteration < 0 || !info.wireInfo) + continue; + + ++iteration2; + while(!info.wireInfo->done) { + auto wireInfo = info.wireInfo; + checkWireInfo(*wireInfo); + const auto &wireVertices = wireInfo->vertices; + auto beginVertex = wireVertices.front(); + auto &beginInfo = *beginVertex.it; + initWireInfo(*wireInfo); + showShape(wireInfo->wire, "iwire", iteration); + for (auto &v : wireVertices) + v.it->iteration2 = iteration2; + + stack.clear(); + vertexStack.clear(); + edgeSet.clear(); + + std::shared_ptr newWire; + gp_Pnt pstart = beginVertex.pt(); + + int idxV = 0; + while (true) { + int idx = wireVertices[idxV].start ? 1 : 0; + auto current = wireVertices[idxV].edgeInfo(); + showShape(current, "current", iteration); + + for (int n=current->iStart[idx]; niEnd[idx]; ++n) { + const auto ¤tVertex = adjacentList[n]; + auto next = currentVertex.edgeInfo(); + if (next == current || next->iteration2 == iteration2 || next->iteration<0) + continue; + + showShape(next, "tcheck", iteration); + + if (!isInside(*wireInfo, next->mid)) { + showShape(next, "ninside", iteration); + next->iteration2 = iteration2; + continue; + } + + edgeSet.insert(next); + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(currentVertex); + checkStack(); + + int idxEnd = (int)wireVertices.size(); + int stackStart = (int)stack.size()-1; + int stackPos = (int)stack.size()-1; + + TopoDS_Wire wire; + if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { + wire = _findClosedWires(beginVertex, currentVertex, beginInfo.wireInfo, &idxEnd, &stackPos); + if (wire.IsNull()) { + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + continue; + } + } + + newWire.reset(new WireInfo); + auto &newWireVertices = newWire->vertices; + newWireVertices.push_back(beginVertex); + for (auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + newWireVertices.push_back(v); + } + if (!wire.IsNull()) + newWire->wire = wire; + else if (!initWireInfo(*newWire)) { + newWire.reset(); + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + continue; + } + for (auto &v : newWire->vertices) { + if (v.edgeInfo()->wireInfo == wireInfo) + v.edgeInfo()->wireInfo = newWire; + } + beginInfo.wireInfo = newWire; + showShape(*newWire, "nwire", iteration); + + std::shared_ptr splitWire; + if (idxEnd == 0) + idxEnd = (int)wireVertices.size(); + ++idxV; + assertCheck(idxV<=idxEnd); + int idxStart = idxV; + for (int idx=idxV; idx!=idxEnd; ++idx) { + auto info = wireVertices[idx].edgeInfo(); + if (info == &beginInfo) { + showShape(*wireInfo, "exception", iteration, true); + showShape(info, "exception", iteration, true); + assertCheck(info != &beginInfo); + } + if (info->wireInfo == wireInfo) { + if (!splitWire) { + idxStart = idx; + splitWire.reset(new WireInfo); + } + info->wireInfo = splitWire; + } + } + if (splitWire) { + auto &splitEdges = splitWire->vertices; + gp_Pnt pstart, pt; + bool first = true; + for (int idx=idxStart; idx!=idxEnd; ++idx) { + auto &v = wireVertices[idx]; + if (first) { + first = false; + pstart = v.pt(); + } else + assertCheck(pt.SquareDistance(v.pt()) < myTol2); + pt = v.ptOther(); + splitEdges.push_back(v); + } + for (int i=stackPos; i>=stackStart; --i) { + const auto &v = vertexStack[stack[i].iCurrent]; + assertCheck(pt.SquareDistance(v.ptOther()) < myTol2); + pt = v.pt(); + // The edges in the stack are the ones to slice + // the wire in half. We construct a new wire + // that includes the original beginning edge in + // the loop above. And this loop contains the + // other half. Note that the slicing edges + // should run in the oppsite direction, hence reversed + splitEdges.push_back(v.reversed()); + } + for (int idx=idxV; idx!=idxStart; ++idx) { + auto &v = wireVertices[idx]; + assertCheck(pt.SquareDistance(v.pt()) < myTol2); + pt = v.ptOther(); + splitEdges.push_back(v); + } + assertCheck(pt.SquareDistance(pstart) < myTol2); + showShape(*splitWire, "swire", iteration); + } + + checkWireInfo(*newWire); + break; + } + if (newWire) { + ++iteration; + break; + } + + if (++idxV == (int)wireVertices.size()) + break; + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(wireVertices[idxV]); + edgeSet.insert(wireVertices[idxV].edgeInfo()); + checkStack(); + } + + if (!newWire) { + showShape(*beginInfo.wireInfo, "done", iteration); + beginInfo.wireInfo->done = true; + // If a wire is done, make sure all edges of this wire is + // marked as done. This can also prevent duplicated wires. + for (auto &v : beginInfo.wireInfo->vertices) { + auto info = v.edgeInfo(); + if (!info->wireInfo) { + info->wireInfo = beginInfo.wireInfo; + continue; + } + else if (info->wireInfo->done) + continue; + auto otherWire = info->wireInfo; + auto &otherWireVertices = info->wireInfo->vertices; + if (info == otherWireVertices.front().edgeInfo()) { + // About to change the first edge of the other wireInfo. + // Try to find a new first edge for it. + tmpVertices.clear(); + auto it = otherWireVertices.begin(); + tmpVertices.push_back(*it); + for (++it;it!=otherWireVertices.end();++it) { + if (it->edgeInfo()->wireInfo == otherWire) + break; + tmpVertices.push_back(*it); + } + if (tmpVertices.size() != otherWireVertices.size()) { + otherWireVertices.erase(otherWireVertices.begin(), it); + otherWireVertices.insert(otherWireVertices.end(), + tmpVertices.begin(), tmpVertices.end()); + } + } + assertCheck(info != &beginInfo); + info->wireInfo = beginInfo.wireInfo; + checkWireInfo(*otherWire); + } + checkWireInfo(*beginInfo.wireInfo); + } + } + } + } + + void exhaustTightBound() + { + // findTightBound() function will find a tight bound wire for each + // edge. Now we try to find all possible tight bound wires, relying on + // the important fact that an edge can be shared by at most two tight + // bound wires. + + std::unique_ptr seq( + new Base::SequencerLauncher("Exhaust tight bound", edges.size())); + + for (auto &info : edges) { + if (info.iteration < 0 || !info.wireInfo || !info.wireInfo->done) + continue; + for (auto &v : info.wireInfo->vertices) { + auto edgeInfo = v.edgeInfo(); + if (edgeInfo->wireInfo != info.wireInfo) + edgeInfo->wireInfo2 = info.wireInfo; + } + } + + int iteration2 = iteration; + for (auto &info : edges) { + ++iteration; + seq->next(true); + if (info.iteration < 0 + || !info.wireInfo + || !info.wireInfo->done) + { + if (info.wireInfo) + showShape(*info.wireInfo, "iskip"); + else + showShape(&info, "iskip"); + continue; + } + + if (info.wireInfo2 && info.wireInfo2->done) { + showShape(*info.wireInfo, "idone"); + continue; + } + + showShape(*info.wireInfo, "iwire2", iteration); + showShape(&info, "begin2", iteration); + + int idx = info.wireInfo->find(&info); + assertCheck(idx > 0); + const auto &vertices = info.wireInfo->vertices; + --idx; + int nextIdx = idx == (int)vertices.size()-1 ? 0 : idx + 1; + int prevIdx = idx == 0 ? (int)vertices.size()-1 : idx - 1; + int count = prevIdx == nextIdx ? 1 : 2; + for (int n=0; niteration<0 + || !next->wireInfo + || !next->wireInfo->done + || next->wireInfo2) + continue; + + showShape(next, "n2", iteration); + + stack.clear(); + stack.emplace_back(); + ++stack.back().iEnd; + vertexStack.clear(); + vertexStack.push_back(currentVertex); + + edgeSet.clear(); + edgeSet.insert(next); + wireSet.clear(); + wireSet.insert(next->wireInfo.get()); + + TopoDS_Wire wire; + if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { + wire = _findClosedWires(beginVertex, currentVertex); + if (wire.IsNull()) + continue; + } + + std::shared_ptr wireInfo(new WireInfo); + wireInfo->vertices.push_back(beginVertex); + for (auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + wireInfo->vertices.push_back(v); + } + if (!wire.IsNull()) + wireInfo->wire = wire; + else if (!initWireInfo(*wireInfo)) + continue; + + showShape(*wireInfo, "nw2", iteration); + + ++iteration; + ++iteration2; + + while (wireInfo && !wireInfo->done) { + showShape(next, "next2", iteration); + + vertexStack.resize(1); + stack.resize(1); + edgeSet.clear(); + edgeSet.insert(next); + wireSet.clear(); + wireSet.insert(next->wireInfo.get()); + + const auto &wireVertices = wireInfo->vertices; + initWireInfo(*wireInfo); + for (auto &v : wireVertices) + v.it->iteration2 = iteration2; + + std::shared_ptr newWire; + + int idxV = 1; + while (true) { + int idx = wireVertices[idxV].start ? 1 : 0; + auto current = wireVertices[idxV].edgeInfo(); + + for (int n=current->iStart[idx]; niEnd[idx]; ++n) { + const auto ¤tVertex = adjacentList[n]; + auto next = currentVertex.edgeInfo(); + if (next == current || next->iteration2 == iteration2 || next->iteration<0) + continue; + + showShape(next, "check2", iteration); + + if (!isInside(*wireInfo, next->mid)) { + showShape(next, "ninside2", iteration); + next->iteration2 = iteration2; + continue; + } + + edgeSet.insert(next); + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(currentVertex); + checkStack(); + + TopoDS_Wire wire; + if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { + wire = _findClosedWires(beginVertex, currentVertex, wireInfo); + if (wire.IsNull()) { + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + wireSet.erase(next->wireInfo.get()); + continue; + } + } + + newWire.reset(new WireInfo); + auto &newWireVertices = newWire->vertices; + newWireVertices.push_back(beginVertex); + for (auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + newWireVertices.push_back(v); + } + if (!wire.IsNull()) + newWire->wire = wire; + else if (!initWireInfo(*newWire)) { + newWire.reset(); + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + wireSet.erase(next->wireInfo.get()); + continue; + } + for (auto &v : newWire->vertices) { + if (v.edgeInfo()->wireInfo == wireInfo) + v.edgeInfo()->wireInfo = newWire; + } + showShape(*newWire, "nwire2", iteration); + checkWireInfo(*newWire); + break; + } + + if (newWire) { + ++iteration; + wireInfo = newWire; + break; + } + + if (++idxV == (int)wireVertices.size()) { + if (wireInfo->purge) { + showShape(*wireInfo, "discard2", iteration); + wireInfo.reset(); + } else { + wireInfo->done = true; + showShape(*wireInfo, "done2", iteration); + } + break; + } + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(wireVertices[idxV]); + edgeSet.insert(wireVertices[idxV].edgeInfo()); + checkStack(); + } + } + + if (wireInfo && wireInfo->done) { + for (auto &v : wireInfo->vertices) { + auto edgeInfo = v.edgeInfo(); + assertCheck(edgeInfo->wireInfo != nullptr); + if (edgeInfo->wireInfo->isSame(*wireInfo)) { + wireInfo = edgeInfo->wireInfo; + break; + } + } + for (auto &v : wireInfo->vertices) { + auto edgeInfo = v.edgeInfo(); + if (!edgeInfo->wireInfo2 && edgeInfo->wireInfo != wireInfo) + edgeInfo->wireInfo2 = wireInfo; + } + assertCheck(info.wireInfo2 == wireInfo); + assertCheck(info.wireInfo2 != info.wireInfo); + showShape(*wireInfo, "exhaust"); + break; + } + } + } + } + wireSet.clear(); + } + + TopoDS_Wire makeCleanWire(bool fixGap=true) + { + // Make a clean wire with sorted, oriented, connected, etc edges + TopoDS_Wire result; + std::vector inputEdges; + + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) { + for (int i=1; i<=wireData->NbEdges(); ++i) + inputEdges.emplace_back(wireData->Edge(i)); + } + + ShapeFix_Wire fixer; + fixer.SetContext(new ShapeBuild_ReShape); + fixer.Load(wireData); + fixer.SetMaxTolerance(myTol); + fixer.ClosedWireMode() = Standard_True; + fixer.Perform(); + // fixer.FixReorder(); + // fixer.FixConnected(); + + if (fixGap) { + // Gap fixing may change vertex, but we need all concident vertexes + // to be the same one. + // + // fixer.FixGap3d(1, Standard_True); + } + + fixer.FixClosed(); + + result = fixer.Wire(); + auto newHistory = fixer.Context()->History(); + + if (FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE+1) { + FC_MSG("init:"); + for (const auto &s : sourceEdges) + FC_MSG(s.getShape().TShape().get() << ", " << s.getShape().HashCode(INT_MAX)); + printHistory(aHistory, sourceEdges); + printHistory(newHistory, inputEdges); + } + + aHistory->Merge(newHistory); + + if (FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE+1) { + printHistory(aHistory, sourceEdges); + FC_MSG("final:"); + for (int i=1; i<=wireData->NbEdges(); ++i) { + auto s = wireData->Edge(i); + FC_MSG(s.TShape().get() << ", " << s.HashCode(INT_MAX)); + } + } + return result; + } + + template + void printHistory(Handle(BRepTools_History) hist, const T &input) + { + FC_MSG("\nHistory:\n"); + for (const auto &s : input) { + for(TopTools_ListIteratorOfListOfShape it(hist->Modified(s.getShape())); it.More(); it.Next()) { + FC_MSG(s.getShape().TShape().get() << ", " << s.getShape().HashCode(INT_MAX) + << " -> " << it.Value().TShape().get() << ", " << it.Value().HashCode(INT_MAX)); + } + } + } + + bool canShowShape(int idx=-1, bool forced=false) + { + if (idx < 0 || catchIteration == 0 || catchIteration > idx) { + if (!forced && FC_LOG_INSTANCE.level()<=FC_LOGLEVEL_TRACE) + return false; + } + return true; + } + + void showShape(const EdgeInfo *info, const char *name, int idx=-1, bool forced=false) + { + if (!canShowShape(idx, forced)) + return; + showShape(info->shape(), name, idx, forced); + } + + void showShape(WireInfo &wireInfo, const char *name, int idx=-1, bool forced=false) + { + if (!canShowShape(idx, forced)) + return; + if (wireInfo.wire.IsNull()) + initWireInfo(wireInfo); + showShape(wireInfo.wire, name, idx, forced); + } + + void showShape(const TopoDS_Shape &s, const char *name, int idx=-1, bool forced=false) + { + if (!canShowShape(idx, forced)) + return; + std::string _name; + if (idx >= 0) { + _name = name; + _name += "_"; + _name += std::to_string(idx); + _name += "_"; + name = _name.c_str(); + } + auto obj = Feature::create(s, name); + FC_MSG(obj->getNameInDocument() << " " << ShapeHasher()(s)); + if (catchObject == obj->getNameInDocument()) + FC_MSG("found"); + return; + } + + void build() + { + clear(); + sourceEdges.clear(); + sourceEdges.insert(sourceEdgeArray.begin(), sourceEdgeArray.end()); + for (const auto &e : sourceEdgeArray) + add(TopoDS::Edge(e.getShape()), true); + + if (doTightBound || doSplitEdge) + splitEdges(); + + buildAdjacentList(); + + if (!doTightBound && !doOutline) + findClosedWires(); + else { + findClosedWires(true); + findTightBound(); + exhaustTightBound(); + bool done = !doOutline; + while(!done) { + ++iteration; + done = true; + std::unordered_map counter; + std::unordered_set wires; + for (auto &info : edges) { + if (info.iteration == -2) + continue; + if (info.iteration < 0 || !info.wireInfo || !info.wireInfo->done) { + if (info.iteration >= 0) { + info.iteration = -1; + done = false; + showShape(&info, "removed", iteration); + aHistory->Remove(info.edge); + } + continue; + } + if (info.wireInfo2 && wires.insert(info.wireInfo2.get()).second) { + for (auto &v : info.wireInfo2->vertices) { + if (++counter[v.edgeInfo()] == 2) { + v.edgeInfo()->iteration = -1; + done = false; + showShape(v.edgeInfo(), "removed2", iteration); + aHistory->Remove(info.edge); + } + } + } + if (!wires.insert(info.wireInfo.get()).second) + continue; + for (auto &v : info.wireInfo->vertices) { + if (++counter[v.edgeInfo()] == 2) { + v.edgeInfo()->iteration = -1; + done = false; + showShape(v.edgeInfo(), "removed1", iteration); + aHistory->Remove(info.edge); + } + } + } + findClosedWires(true); + findTightBound(); + } + + builder.MakeCompound(compound); + wireSet.clear(); + for (auto &info : edges) { + if (info.iteration == -2) { + if (!info.wireInfo) { + builder.Add(compound, info.wire()); + continue; + } + addWire(info.wireInfo); + addWire(info.wireInfo2); + } + else if (info.iteration >= 0) { + addWire(info.wireInfo2); + addWire(info.wireInfo); + } + } + wireSet.clear(); + } + + // TODO: We choose to put open wires in a separated shape from the final + // result shape, so the history may contains some entries that are not + // presented in the final result, which will cause warning message when + // generating topo naming in TopoShape::makESHAPE(). We've lowered log + // message level to suppress the warning for the moment. The right way + // to solve the problem is to reconstruct the history and filter out + // those entries. + + bool hasOpenEdge = false; + for (const auto &info : edges) { + if (info.iteration == -3 || (!info.wireInfo && info.iteration>=0)) { + if (!hasOpenEdge) { + hasOpenEdge = true; + builder.MakeCompound(openWireCompound); + } + builder.Add(openWireCompound, info.wire()); + } + } + } + + void addWire(std::shared_ptr &wireInfo) + { + if (!wireInfo || !wireInfo->done || !wireSet.insertUnique(wireInfo.get())) + return; + initWireInfo(*wireInfo); + builder.Add(compound, wireInfo->wire); + } + + bool getOpenWires(TopoShape &shape, const char *op, bool noOriginal) { + if (openWireCompound.IsNull()) { + shape.setShape(TopoShape()); + return false; + } + auto comp = openWireCompound; + if (noOriginal) { + TopoShape source(-1); + source.makeElementCompound(sourceEdgeArray); + auto wires = TopoShape(openWireCompound, -1).getSubTopoShapes(TopAbs_WIRE); + bool touched = false; + for (auto it=wires.begin(); it!=wires.end();) { + bool purge = true; + for (const auto &e : it->getSubShapes(TopAbs_EDGE)) { + if (source.findSubShapesWithSharedVertex(TopoShape(e, -1)).empty()) { + purge = false; + break; + } + } + if (purge) { + it = wires.erase(it); + touched = true; + } else + ++it; + } + if (touched) { + if (wires.empty()) { + shape.setShape(TopoShape()); + return false; + } + comp = TopoDS::Compound(TopoShape(-1).makeElementCompound(wires).getShape()); + } + } + shape.makeShapeWithElementMap(comp, + MapperHistory(aHistory), + {sourceEdges.begin(), sourceEdges.end()}, + op); + return true; + } + + bool getResultWires(TopoShape &shape, const char *op) { + if (compound.IsNull()) { + shape.setShape(TopoShape()); + return false; + } + shape.makeShapeWithElementMap(compound, + MapperHistory(aHistory), + {sourceEdges.begin(), sourceEdges.end()}, + op); + return true; + } +}; + + +WireJoiner::WireJoiner() + :pimpl(new WireJoinerP) +{ +} + +WireJoiner::~WireJoiner() +{ +} + +void WireJoiner::addShape(const TopoShape &shape) +{ + NotDone(); + for (auto &e : shape.getSubTopoShapes(TopAbs_EDGE)) + pimpl->sourceEdgeArray.push_back(e); +} + +void WireJoiner::addShape(const std::vector &shapes) +{ + NotDone(); + for (const auto &shape : shapes) { + for (auto &e : shape.getSubTopoShapes(TopAbs_EDGE)) + pimpl->sourceEdgeArray.push_back(e); + } +} + +void WireJoiner::addShape(const std::vector &shapes) +{ + NotDone(); + for (const auto &shape : shapes) { + for (TopExp_Explorer xp(shape,TopAbs_EDGE); xp.More(); xp.Next()) + pimpl->sourceEdgeArray.emplace_back(TopoDS::Edge(xp.Current()), -1); + } +} + +void WireJoiner::setOutline(bool enable) +{ + if (enable != pimpl->doOutline) { + NotDone(); + pimpl->doOutline = enable; + } +} + +void WireJoiner::setTightBound(bool enable) +{ + if (enable != pimpl->doTightBound) { + NotDone(); + pimpl->doTightBound = enable; + } +} + +void WireJoiner::setSplitEdges(bool enable) +{ + if (enable != pimpl->doSplitEdge) { + NotDone(); + pimpl->doSplitEdge = enable; + } +} + +void WireJoiner::setMergeEdges(bool enable) +{ + if (enable != pimpl->doSplitEdge) { + NotDone(); + pimpl->doMergeEdge = enable; + } +} + +void WireJoiner::setTolerance(double tol, double atol) +{ + if (tol >= 0 && tol != pimpl->myTol) { + NotDone(); + pimpl->myTol = tol; + pimpl->myTol2 = tol * tol; + } + if (atol >= 0 && atol != pimpl->myAngularTol) { + NotDone(); + pimpl->myAngularTol = atol; + } +} + +#if OCC_VERSION_HEX < 0x070600 +void WireJoiner::Build() +#else +void WireJoiner::Build(const Message_ProgressRange&) +#endif +{ + if (IsDone()) + return; + pimpl->build(); + if (TopoShape(pimpl->compound).countSubShapes(TopAbs_SHAPE) > 0) + myShape = pimpl->compound; + else + myShape.Nullify(); + Done(); +} + +bool WireJoiner::getOpenWires(TopoShape &shape, const char *op, bool noOriginal) +{ + Build(); + return pimpl->getOpenWires(shape, op, noOriginal); +} + +bool WireJoiner::getResultWires(TopoShape &shape, const char *op) +{ + Build(); + return pimpl->getResultWires(shape, op); +} + +const TopTools_ListOfShape& WireJoiner::Generated (const TopoDS_Shape& S) +{ + Build(); + return pimpl->aHistory->Generated(S); +} + +const TopTools_ListOfShape& WireJoiner::Modified (const TopoDS_Shape& S) +{ + Build(); + return pimpl->aHistory->Modified(S); +} + +Standard_Boolean WireJoiner::IsDeleted (const TopoDS_Shape& S) +{ + Build(); + return pimpl->aHistory->IsRemoved(S); +} diff --git a/src/Mod/Part/App/WireJoiner.h b/src/Mod/Part/App/WireJoiner.h new file mode 100644 index 0000000000..948af08d0f --- /dev/null +++ b/src/Mod/Part/App/WireJoiner.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * Copyright (c) 2022 Zheng Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#ifndef PART_WIRE_JOINER_H +#define PART_WIRE_JOINER_H + +#include +#include +#include +#include "TopoShape.h" + +namespace Part{ + +class PartExport WireJoiner: public BRepBuilderAPI_MakeShape { +public: + WireJoiner(); + virtual ~WireJoiner(); + + void addShape(const TopoShape &shape); + void addShape(const std::vector &shapes); + void addShape(const std::vector &shapes); + + void setOutline(bool enable=true); + void setTightBound(bool enable=true); + void setSplitEdges(bool enable=true); + void setMergeEdges(bool enable=true); + void setTolerance(double tolerance, double angularTol=0.0); + + bool getOpenWires(TopoShape &shape, const char *op="", bool noOriginal=true); + bool getResultWires(TopoShape &shape, const char *op=""); + +#if OCC_VERSION_HEX < 0x070600 + void Build() override; +#else + void Build(const Message_ProgressRange& theRange = Message_ProgressRange()) override; +#endif + const TopTools_ListOfShape& Modified (const TopoDS_Shape& S) override; + const TopTools_ListOfShape& Generated (const TopoDS_Shape& S) override; + Standard_Boolean IsDeleted (const TopoDS_Shape& S) override; + +private: + class WireJoinerP; + std::unique_ptr pimpl; +}; + +} // namespace Part + +#endif // PART_WIRE_JOINER_H From 0f3362575b1dec10224711a05346e23d9ae9f62d Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Mon, 4 Mar 2024 12:06:50 +0100 Subject: [PATCH 02/16] Part/Toponaming: Transfer WireJoiner * Created dedicated tests file for WireJoiner * Added test for WireJoiner::addShape() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/CMakeLists.txt | 1 + tests/src/Mod/Part/App/WireJoiner.cpp | 128 ++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 tests/src/Mod/Part/App/WireJoiner.cpp diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index ac2d513352..bfd77aea3b 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -28,4 +28,5 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMakeShapeWithElementMap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMakeShape.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/WireJoiner.cpp ) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp new file mode 100644 index 0000000000..a319b94036 --- /dev/null +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "gtest/gtest.h" +#include "src/App/InitApplication.h" +#include "Mod/Part/App/WireJoiner.h" + +#include "PartTestHelpers.h" + +#include + +// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) + +using namespace Part; +using namespace PartTestHelpers; + +class WireJoinerTest: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + _docName = App::GetApplication().getUniqueDocumentName("test"); + App::GetApplication().newDocument(_docName.c_str(), "testUser"); + _hasher = Base::Reference(new App::StringHasher); + ASSERT_EQ(_hasher.getRefCount(), 1); + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + + +private: + std::string _docName; + Data::ElementIDRefs _sid; + App::StringHasherRef _hasher; +}; + +TEST_F(WireJoinerTest, addShape) +{ + // Arrange + + // Create various edges that will be used to define the arguments of the various + // WireJoiner::addShape() calls + + 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(1.0, 1.0, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 1.0, 0.0)).Edge()}; + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + + // A vector of TopoDS_Shape used as argument for wjvTDS.addShape() + std::vector edges = {edge1, edge2, edge3, edge4}; + + // Create various TopoShapes used as arguments for wjvTS.addShape() + + auto edge1TS {TopoShape(edge1, 1)}; + auto edge2TS {TopoShape(edge2, 2)}; + + // A wire TopoShape used as argument for wjTS.addShape() + auto wireTS {TopoShape(BRepBuilderAPI_MakeWire(edge1, edge2, edge3).Wire(), 3)}; + // An empty TopoShape that will contain the shapes added by wjvTS.addShape() + auto wirevTS {TopoShape(4)}; + // An empty TopoShape that will contain the shapes added by wjvTDS.addShape() + auto wirevTDS {TopoShape(5)}; + + // Create 3 WireJoiner objects, one for every definition of WireJoiner::addShape() + + // A WireJoiner object where it will be added a TopoShape without calling WireJoiner::Build() + // afterwards + auto wjTSNotDone {WireJoiner()}; + // A WireJoiner object where it will be added a TopoShape + auto wjTS {WireJoiner()}; + // A WireJoiner object where it will be added a vector of TopoShapes + auto wjvTS {WireJoiner()}; + // A WireJoiner object where it will be added a vector of TopoDS_Shapes + auto wjvTDS {WireJoiner()}; + + // Act + + // Calling only WireJoiner::addShape(). Expected result is that wjTSNotDone.Done() is false + wjTSNotDone.addShape(wireTS); + + // Calling WireJoiner::addShape() and then WireJoiner::Build(). + // If we don't call WireJoiner::Build() we can't see the effect of WireJoiner::addShape() as it + // adds the shapes in the private member WireJoinerP::sourceEdgeArray + + wjTS.addShape(wireTS); + wjTS.Build(); + // The wire in wjTS is open, therefor to see the effect of wjTS.addShape() we must call + // wjTS.getOpenWires() and put the result in wireTS + wjTS.getOpenWires(wireTS, nullptr, false); + + wjvTS.addShape({edge1TS, edge2TS}); + wjvTS.Build(); + // The wire in wjvTS is open, therefor to see the effect of wjvTS.addShape() we must call + // wjvTS.getOpenWires() and put the result in wirevTS + wjvTS.getOpenWires(wirevTS, nullptr, false); + + wjvTDS.addShape(edges); + wjvTDS.Build(); + // The wire in wjvTDS is closed, therefor to see the effect of wjvTDS.addShape() we can smply + // call wjvTDS.Shape() to replace the shape in wirevTDS + wirevTDS.setShape(wjvTDS.Shape()); + + // Assert + + // Check the output of WireJoiner::IsDone(). + // It should be true for all the objects that executed also WireJoiner::Build() + // (All except wjTSNotDone) + EXPECT_FALSE(wjTSNotDone.IsDone()); + EXPECT_TRUE(wjTS.IsDone()); + EXPECT_TRUE(wjvTS.IsDone()); + EXPECT_TRUE(wjvTDS.IsDone()); + + // wireTS is build with 3 edges. The same quantity should be in the shape built + EXPECT_EQ(wireTS.getSubTopoShapes(TopAbs_EDGE).size(), 3); + // wirevTS is build with 2 edges. The same quantity should be in the shape built + EXPECT_EQ(wirevTS.getSubTopoShapes(TopAbs_EDGE).size(), 2); + // wirevTDS is build with 4 edges. The same quantity should be in the shape built + EXPECT_EQ(wirevTDS.getSubTopoShapes(TopAbs_EDGE).size(), 4); +} + +// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From bbe2d8664bf98ae66bea733698c23cb595593ff4 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Mon, 4 Mar 2024 20:26:33 +0100 Subject: [PATCH 03/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::setOutline() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 112 ++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index a319b94036..57bc195fe0 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -125,4 +125,116 @@ TEST_F(WireJoinerTest, addShape) EXPECT_EQ(wirevTDS.getSubTopoShapes(TopAbs_EDGE).size(), 4); } +TEST_F(WireJoinerTest, setOutline) +{ + // Arrange + + // Create various edges that will be used to create wires for the WireJoiner objects testes + + 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(1.0, 1.0, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, -0.1, 0.0), gp_Pnt(-0.1, 1.1, 0.0)).Edge()}; + auto edge5 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 1.1, 0.0), gp_Pnt(1.1, 1.1, 0.0)).Edge()}; + auto edge6 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; + + // A vector of edges used as argument for wjNoOutline.addShape() + std::vector edgesNoOutline = {edge1, edge2, edge3, edge4, edge5, edge6}; + // A vector of edges used as argument for wjOutline.addShape() + std::vector edgesOutline = {edge1, edge2, edge3, edge4, edge5, edge6}; + + // To see the effect of setOutline() it is necessary to set the user parameter "Iteration" + // to a value, in this case, less than zero before the WireObjects are initializaed. + // To see the correct value for this parameter refer to method WireJoinerP::canShowShape() + + auto hParam {App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/WireJoiner")}; + auto catchIteration {hParam->GetInt("Iteration", 0)}; + hParam->SetInt("Iteration", -1); + + // A document where there will WireJoiner.Build() will be called having setOutline() set to + // false + auto docNoOutline {App::GetApplication().getActiveDocument()}; + + auto _docNameOutline {App::GetApplication().getUniqueDocumentName("docOutline")}; + App::GetApplication().newDocument(_docNameOutline.c_str(), "docOutlineUser"); + // A document where there will WireJoiner.Build() will be called having setOutline() set to true + auto docOutline {App::GetApplication().getActiveDocument()}; + + // A WireJoiner object where the value of setOutline() will be changed but no shapes will be + // built + auto wjNoBuild {WireJoiner()}; + // A WireJoiner object where setOutline() will be set to false + auto wjNoOutline {WireJoiner()}; + // A WireJoiner object where setOutline() will be set to true + auto wjOutline {WireJoiner()}; + + // Reset the parameter to its previous value. + hParam->SetInt("Iteration", catchIteration); + + // Act + + // Changing only the value of setOutline(). This should set wjNoBuild.IsDone() to false + wjNoBuild.setOutline(true); + + // We can see the effect of setOutline by searching, among all the DocumentObjects created by + // the logic, if there are any with a label containing "removed" + + // Testing first the Document where WireJoiner::Build() will be executed with setOutline set to + // false + + App::GetApplication().setActiveDocument(docNoOutline); + + wjNoOutline.addShape(edgesNoOutline); + wjNoOutline.setOutline(false); + wjNoOutline.Build(); + + auto objsInDoc {docNoOutline->getObjects()}; + + auto foundRemovedNoOutline {false}; + for (const auto& obj : objsInDoc) { + foundRemovedNoOutline = + (std::string(obj->Label.getValue()).find("removed") != std::string::npos); + if (foundRemovedNoOutline) { + break; + } + } + + // Then testing the Document where WireJoiner::Build() will be executed with setOutline set to + // true + + App::GetApplication().setActiveDocument(docOutline); + + wjOutline.addShape(wjNoOutline.Shape()); + // same as wjOutline.setOutline(true); + wjOutline.setOutline(); + wjOutline.Build(); + + objsInDoc = docOutline->getObjects(); + + auto foundRemovedOutline {false}; + for (const auto& obj : objsInDoc) { + foundRemovedOutline = + (std::string(obj->Label.getValue()).find("removed") != std::string::npos); + if (foundRemovedOutline) { + break; + } + } + + // Assert + + // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we + // only changed the value of setOutline() + EXPECT_FALSE(wjNoBuild.IsDone()); + + // In a document where WireJoiner::Build() is executed with setOutline() set to false there + // shouldn't be DocumentObjects with "removed" in their label + EXPECT_FALSE(foundRemovedNoOutline); + + // In a document where WireJoiner::Build() is executed with setOutline() set to true there + // should be at least a DocumentObject with "removed" in its label + EXPECT_TRUE(foundRemovedOutline); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From dabccfbc1c7f9fa01d22f8d23e48b6be95e79ba3 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Fri, 8 Mar 2024 13:40:31 +0100 Subject: [PATCH 04/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::setTightBound() * Minor modifications and typo Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 82 +++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 57bc195fe0..22bdec1a4b 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -54,7 +54,7 @@ TEST_F(WireJoinerTest, addShape) auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; // A vector of TopoDS_Shape used as argument for wjvTDS.addShape() - std::vector edges = {edge1, edge2, edge3, edge4}; + std::vector edges {edge1, edge2, edge3, edge4}; // Create various TopoShapes used as arguments for wjvTS.addShape() @@ -129,7 +129,7 @@ TEST_F(WireJoinerTest, setOutline) { // Arrange - // Create various edges that will be used to create wires for the WireJoiner objects testes + // Create various edges that will be used for the WireJoiner objects tests 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(1.0, 1.0, 0.0)).Edge()}; @@ -140,12 +140,12 @@ TEST_F(WireJoinerTest, setOutline) auto edge6 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; // A vector of edges used as argument for wjNoOutline.addShape() - std::vector edgesNoOutline = {edge1, edge2, edge3, edge4, edge5, edge6}; + std::vector edgesNoOutline {edge1, edge2, edge3, edge4, edge5, edge6}; // A vector of edges used as argument for wjOutline.addShape() - std::vector edgesOutline = {edge1, edge2, edge3, edge4, edge5, edge6}; + std::vector edgesOutline {edge1, edge2, edge3, edge4, edge5, edge6}; // To see the effect of setOutline() it is necessary to set the user parameter "Iteration" - // to a value, in this case, less than zero before the WireObjects are initializaed. + // to a value, in this case, less than zero before the WireObjects are initialized. // To see the correct value for this parameter refer to method WireJoinerP::canShowShape() auto hParam {App::GetApplication().GetParameterGroupByPath( @@ -206,7 +206,7 @@ TEST_F(WireJoinerTest, setOutline) App::GetApplication().setActiveDocument(docOutline); - wjOutline.addShape(wjNoOutline.Shape()); + wjOutline.addShape(edgesOutline); // same as wjOutline.setOutline(true); wjOutline.setOutline(); wjOutline.Build(); @@ -237,4 +237,74 @@ TEST_F(WireJoinerTest, setOutline) EXPECT_TRUE(foundRemovedOutline); } +TEST_F(WireJoinerTest, setTightBound) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + 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(1.0, 1.0, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; + + // A vector of edges used as argument for wjNoTightBound.addShape() + std::vector edgesNoTightBound {edge1, edge2, edge3, edge4}; + // A vector of edges used as argument for wjTightBound.addShape() + std::vector edgesTightBound {edge1, edge2, edge3, edge4}; + + // A WireJoiner object where the value of setTightBound() will be changed but no shapes will be + // built + auto wjNoBuild {WireJoiner()}; + // A WireJoiner object where setTightBound() will be set to false + auto wjNoTightBound {WireJoiner()}; + // A WireJoiner object where setTightBound() will be set to true + auto wjTightBound {WireJoiner()}; + + // An empty TopoShape that will contain the shapes returned by wjNoTightBound.getOpenWires() + auto wireNoTightBound {TopoShape(1)}; + // An empty TopoShape that will contain the shapes returned by wjTightBound.getOpenWires() + auto wireTightBound {TopoShape(2)}; + + // Act + + // Changing only the value of setTightBound(). This should set wjNoBuild.IsDone() to false + wjNoBuild.setTightBound(false); + + // To see the effect of setTightBound() we call WireJoiner::Build() and then + // WireJoiner::getOpenWires() + + wjNoTightBound.addShape(edgesNoTightBound); + wjNoTightBound.setTightBound(false); + wjNoTightBound.Build(); + wjNoTightBound.getOpenWires(wireNoTightBound, nullptr, false); + + wjTightBound.addShape(edgesTightBound); + // same as wjTightBound.setTightBound(true); + wjTightBound.setTightBound(); + wjTightBound.Build(); + wjTightBound.getOpenWires(wireTightBound, nullptr, false); + + // Assert + + // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we + // only changed the value of setTightBound() + EXPECT_FALSE(wjNoBuild.IsDone()); + + // Calling wjNoTightBound.Build() will put all the edges inside the private object + // WireJoiner::WireJoinerP::openWireCompound. + // In this case the number of edges is equal to 6 because: + // edge1 and edge2 aren't modified => 2 edges + // edge3 and edge4 are both split in 2 edges => 4 edges + // The split is made at the intersection point (0.5, 0.5, 0.0) + EXPECT_EQ(wireNoTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 6); + + // Calling wjTightBound.Build() will put inside the private object + // WireJoiner::WireJoinerP::openWireCompound only the edges that don't contribute to the + // creation of any closed wire. + // In this case the number of those edges is equal to 1 and that edge is the one with vertexes + // at the coordinates (0.5, 0.5, 0.0) - (0.0, 1.0, 0.0) + EXPECT_EQ(wireTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 1); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 8e5bdcfff0b1971c8cc59e1635e8cd5b7058ec30 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Fri, 8 Mar 2024 18:57:57 +0100 Subject: [PATCH 05/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::setSplitEdges() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 22bdec1a4b..f826c2915a 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -307,4 +307,76 @@ TEST_F(WireJoinerTest, setTightBound) EXPECT_EQ(wireTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 1); } +TEST_F(WireJoinerTest, setSplitEdges) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()}; + + // A vector of edges used as argument for wjNoSplitEdges.addShape() + std::vector edgesNoSplitEdges {edge1, edge2}; + // A vector of edges used as argument for wjSplitEdges.addShape() + std::vector edgesSplitEdges {edge1, edge2}; + + // A WireJoiner object where the value of setSplitEdges() will be changed but no shapes will be + // built + auto wjNoBuild {WireJoiner()}; + // A WireJoiner object where setSplitEdges() will be set to false + // To see it's effect it's necessary also to call setTightBound(false) otherwise + // WireJoiner::WireJoinerP::splitEdges() will be called in any case + auto wjNoSplitEdges {WireJoiner()}; + wjNoSplitEdges.setTightBound(false); + // A WireJoiner object where setSplitEdges() will be set to true + // To see it's effect it's necessary also to call setTightBound(false) otherwise + // WireJoiner::WireJoinerP::splitEdges() will be called in any case + auto wjSplitEdges {WireJoiner()}; + wjSplitEdges.setTightBound(false); + + // An empty TopoShape that will contain the shapes returned by wjNoSplitEdges.getOpenWires() + auto wireNoSplitEdges {TopoShape(1)}; + // An empty TopoShape that will contain the shapes returned by wjSplitEdges.getOpenWires() + auto wireSplitEdges {TopoShape(2)}; + + // Act + + // Changing only the value of setSplitEdges(). This should set wjNoBuild.IsDone() to false + wjNoBuild.setSplitEdges(false); + + // To see the effect of setSplitEdges() we call WireJoiner::Build() and then + // WireJoiner::getOpenWires() + + wjNoSplitEdges.addShape(edgesNoSplitEdges); + wjNoSplitEdges.setSplitEdges(false); + wjNoSplitEdges.Build(); + wjNoSplitEdges.getOpenWires(wireNoSplitEdges, nullptr, false); + + wjSplitEdges.addShape(edgesSplitEdges); + // same as wjSplitEdges.setSplitEdges(true); + wjSplitEdges.setSplitEdges(); + wjSplitEdges.Build(); + wjSplitEdges.getOpenWires(wireSplitEdges, nullptr, false); + + // Assert + + // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we + // only changed the value of setSplitEdges() + EXPECT_FALSE(wjNoBuild.IsDone()); + + // Calling wjNoSplitEdges.Build() will put all the edges that don't contribute to the creation + // of a closed wire inside the private object WireJoiner::WireJoinerP::openWireCompound. + // In this case the number of edges is equal to the number of edges added with + // wjNoSplitEdges.addShape() because none of them is used for the creation of a closed wire. + EXPECT_EQ(wireNoSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 2); + + // Calling wjSplitEdges.Build() will put inside the private object + // WireJoiner::WireJoinerP::openWireCompound all the edges processed by + // WireJoiner::WireJoinerP::splitEdges(). + // In this case the number of those edges is equal to 4 because both the edges added with + // wjSplitEdges.addShape() have been split at the intersection point (0.5, 0.5, 0.0) + EXPECT_EQ(wireSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 4); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 5337869ca67ab5e8cbd33606a89a31a320640680 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Sun, 10 Mar 2024 09:11:34 +0100 Subject: [PATCH 06/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::setMergeEdges() * Changed position of some comments Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 109 ++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 14 deletions(-) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index f826c2915a..60ce519385 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -276,12 +276,17 @@ TEST_F(WireJoinerTest, setTightBound) wjNoTightBound.addShape(edgesNoTightBound); wjNoTightBound.setTightBound(false); + // Calling wjNoTightBound.Build() will put all the edges inside the private object + // WireJoiner::WireJoinerP::openWireCompound. wjNoTightBound.Build(); wjNoTightBound.getOpenWires(wireNoTightBound, nullptr, false); wjTightBound.addShape(edgesTightBound); // same as wjTightBound.setTightBound(true); wjTightBound.setTightBound(); + // Calling wjTightBound.Build() will put inside the private object + // WireJoiner::WireJoinerP::openWireCompound only the edges that don't contribute to the + // creation of any closed wire. wjTightBound.Build(); wjTightBound.getOpenWires(wireTightBound, nullptr, false); @@ -291,17 +296,12 @@ TEST_F(WireJoinerTest, setTightBound) // only changed the value of setTightBound() EXPECT_FALSE(wjNoBuild.IsDone()); - // Calling wjNoTightBound.Build() will put all the edges inside the private object - // WireJoiner::WireJoinerP::openWireCompound. // In this case the number of edges is equal to 6 because: // edge1 and edge2 aren't modified => 2 edges // edge3 and edge4 are both split in 2 edges => 4 edges // The split is made at the intersection point (0.5, 0.5, 0.0) EXPECT_EQ(wireNoTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 6); - // Calling wjTightBound.Build() will put inside the private object - // WireJoiner::WireJoinerP::openWireCompound only the edges that don't contribute to the - // creation of any closed wire. // In this case the number of those edges is equal to 1 and that edge is the one with vertexes // at the coordinates (0.5, 0.5, 0.0) - (0.0, 1.0, 0.0) EXPECT_EQ(wireTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 1); @@ -324,15 +324,17 @@ TEST_F(WireJoinerTest, setSplitEdges) // A WireJoiner object where the value of setSplitEdges() will be changed but no shapes will be // built auto wjNoBuild {WireJoiner()}; + // A WireJoiner object where setSplitEdges() will be set to false - // To see it's effect it's necessary also to call setTightBound(false) otherwise - // WireJoiner::WireJoinerP::splitEdges() will be called in any case auto wjNoSplitEdges {WireJoiner()}; - wjNoSplitEdges.setTightBound(false); - // A WireJoiner object where setSplitEdges() will be set to true // To see it's effect it's necessary also to call setTightBound(false) otherwise // WireJoiner::WireJoinerP::splitEdges() will be called in any case + wjNoSplitEdges.setTightBound(false); + + // A WireJoiner object where setSplitEdges() will be set to true auto wjSplitEdges {WireJoiner()}; + // To see it's effect it's necessary also to call setTightBound(false) otherwise + // WireJoiner::WireJoinerP::splitEdges() will be called in any case wjSplitEdges.setTightBound(false); // An empty TopoShape that will contain the shapes returned by wjNoSplitEdges.getOpenWires() @@ -350,12 +352,17 @@ TEST_F(WireJoinerTest, setSplitEdges) wjNoSplitEdges.addShape(edgesNoSplitEdges); wjNoSplitEdges.setSplitEdges(false); + // Calling wjNoSplitEdges.Build() will put all the edges that don't contribute to the creation + // of a closed wire inside the private object WireJoiner::WireJoinerP::openWireCompound. wjNoSplitEdges.Build(); wjNoSplitEdges.getOpenWires(wireNoSplitEdges, nullptr, false); wjSplitEdges.addShape(edgesSplitEdges); // same as wjSplitEdges.setSplitEdges(true); wjSplitEdges.setSplitEdges(); + // Calling wjSplitEdges.Build() will put inside the private object + // WireJoiner::WireJoinerP::openWireCompound all the edges processed by + // WireJoiner::WireJoinerP::splitEdges(). wjSplitEdges.Build(); wjSplitEdges.getOpenWires(wireSplitEdges, nullptr, false); @@ -365,18 +372,92 @@ TEST_F(WireJoinerTest, setSplitEdges) // only changed the value of setSplitEdges() EXPECT_FALSE(wjNoBuild.IsDone()); - // Calling wjNoSplitEdges.Build() will put all the edges that don't contribute to the creation - // of a closed wire inside the private object WireJoiner::WireJoinerP::openWireCompound. // In this case the number of edges is equal to the number of edges added with // wjNoSplitEdges.addShape() because none of them is used for the creation of a closed wire. EXPECT_EQ(wireNoSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 2); - // Calling wjSplitEdges.Build() will put inside the private object - // WireJoiner::WireJoinerP::openWireCompound all the edges processed by - // WireJoiner::WireJoinerP::splitEdges(). // In this case the number of those edges is equal to 4 because both the edges added with // wjSplitEdges.addShape() have been split at the intersection point (0.5, 0.5, 0.0) EXPECT_EQ(wireSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 4); } +TEST_F(WireJoinerTest, setMergeEdges) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; + auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; + + // A vector of edges used as argument for wjNoMergeEdges.addShape() + std::vector edgesNoMergeEdges {edge1, edge2, edge3}; + // A vector of edges used as argument for wjMergeEdges.addShape() + std::vector edgesMergeEdges {edge1, edge2, edge3}; + + // A WireJoiner object where the value of setMergeEdges() will be changed but no shapes will be + // built + auto wjNoBuild {WireJoiner()}; + + // A WireJoiner object where setMergeEdges() will be set to false + auto wjNoMergeEdges {WireJoiner()}; + // To see it's effect it's necessary also to call setTightBound(false) otherwise + // WireJoiner::WireJoinerP::MergeEdges() will be called in any case + wjNoMergeEdges.setTightBound(false); + + // A WireJoiner object where setMergeEdges() will be set to true + auto wjMergeEdges {WireJoiner()}; + // To see it's effect it's necessary also to call setTightBound(false) otherwise + // WireJoiner::WireJoinerP::MergeEdges() will be called in any case + wjMergeEdges.setTightBound(false); + + // An empty TopoShape that will contain the shapes returned by wjNoMergeEdges.getOpenWires() + auto wireNoMergeEdges {TopoShape(1)}; + // An empty TopoShape that will contain the shapes returned by wjMergeEdges.getOpenWires() + auto wireMergeEdges {TopoShape(2)}; + + // Act + + // Changing only the value of setMergeEdges(). This should set wjNoBuild.IsDone() to false + wjNoBuild.setMergeEdges(false); + + // To see the effect of setMergeEdges() we call WireJoiner::Build() and then + // WireJoiner::getOpenWires() + + wjNoMergeEdges.addShape(edgesNoMergeEdges); + wjNoMergeEdges.setMergeEdges(false); + // Calling wjNoMergeEdges.Build() will put all the edges produced by + // WireJoiner::WireJoinerP::splitEdges() in the private object + // WireJoiner::WireJoinerP::openWireCompound. + wjNoMergeEdges.Build(); + wjNoMergeEdges.getOpenWires(wireNoMergeEdges, nullptr, false); + + wjMergeEdges.addShape(edgesMergeEdges); + // same as wjMergeEdges.setMergeEdges(true); + wjMergeEdges.setMergeEdges(); + // Calling wjMergeEdges.Build() will put, among the edges produced by + // WireJoiner::WireJoinerP::splitEdges(), only the edges that are connected to only one of the + // others by a single vertex in the private object WireJoiner::WireJoinerP::openWireCompound. + // In the code those are called SuperEdges and are + // processed by WireJoiner::WireJoinerP::findSuperEdges(). + wjMergeEdges.Build(); + wjMergeEdges.getOpenWires(wireMergeEdges, nullptr, false); + + // Assert + + // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we + // only changed the value of setMergeEdges() + EXPECT_FALSE(wjNoBuild.IsDone()); + + // In this case the number of edges is equal to 9 because all the 3 edges intersect the other 2 + // and are therefor split in 3 edges each. + EXPECT_EQ(wireNoMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 9); + + // In this case the number of edges is equal to 6 because, among the 9 produced by + // WireJoiner::WireJoinerP::splitEdges(), 3 of them are connected to more than one other edge + // and therefor aren't added by WireJoiner::WireJoinerP::findSuperEdges() + EXPECT_EQ(wireMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 6); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From f4e5af74479cbeff090b3faad9498db26fcb5ced Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Mon, 11 Mar 2024 09:44:00 +0100 Subject: [PATCH 07/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::setTolerance() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 128 ++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 60ce519385..c43b041597 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -460,4 +460,132 @@ TEST_F(WireJoinerTest, setMergeEdges) EXPECT_EQ(wireMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 6); } +TEST_F(WireJoinerTest, setTolerance) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + auto pi {acos(-1)}; + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.1, 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(1.0, 1.0, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.9, 0.0, 0.0)).Edge()}; + auto edge5 { + BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), + gp_Pnt(0.9 * std::cos(pi / 18), 0.9 * std::sin(pi / 18), 0.0)) + .Edge()}; + + // A vector of edges used as argument for wjNegtol.addShape() + std::vector edgesNegtol {edge1, edge2, edge3}; + // A vector of edges used as argument for wjtol.addShape() + std::vector edgestol {edge1, edge2, edge3}; + // A vector of edges used as argument for wjNegatol.addShape() + std::vector edgesNegatol {edge2, edge3, edge4, edge5}; + // A vector of edges used as argument for wjatol.addShape() + std::vector edgesatol {edge2, edge3, edge4, edge5}; + + // A WireJoiner object where the value of setTolerance() will be changed but no shapes will be + // built + auto wjNoBuild {WireJoiner()}; + + // A WireJoiner object where setTolerance() will be called passing to the argument tol a + // negative value. + auto wjNegtol {WireJoiner()}; + // A WireJoiner object where setTolerance() will be called passing to the argument tol a + // value both positive and not equal to WireJoiner::WireJoinerP::myTol + auto wjtol {WireJoiner()}; + + // A WireJoiner object where setTolerance() will be called passing to the argument atol a + // negative value. + auto wjNegatol {WireJoiner()}; + // A WireJoiner object where setTolerance() will be called passing to the argument atol a + // value both positive and not equal to WireJoiner::WireJoinerP::myAngularTol + auto wjatol {WireJoiner()}; + + // An empty TopoShape that will contain the shapes returned by wjNegtol.getOpenWires() + auto wireNegtol {TopoShape(1)}; + // An empty TopoShape that will contain the shapes returned by wjtol.getOpenWires() + auto wiretol {TopoShape(2)}; + + // An empty TopoShape that will contain the shapes returned by wjNegatol.getOpenWires() + auto wireNegatol {TopoShape(3)}; + // An empty TopoShape that will contain the shapes returned by wjatol.getOpenWires() + auto wireatol {TopoShape(4)}; + + // Act + + // Changing only the value of setTolerance(). This should set wjNoBuild.IsDone() to false + wjNoBuild.setTolerance(0.1); + + // To see the effect of setTolerance() we call WireJoiner::Build() and then + // WireJoiner::getOpenWires() to get the edges, if any, that aren't used to create a closed wire + + wjNegtol.addShape(edgesNegtol); + // Setting tol to a negative value won't have effect and therefor wjNegtol.pimpl->myTol will + // keep the default value. + // It's better also to give a negative value for the argument atol otherwise setTolerance() + // will set it to 0.0 + wjNegtol.setTolerance(-0.1, -pi); + wjNegtol.Build(); + wjNegtol.getOpenWires(wireNegtol, nullptr, false); + + wjtol.addShape(edgestol); + // Setting tol to a value that will change wjNegtol.pimpl->myTol. + // It's better also to give a negative value for the argument atol otherwise setTolerance() + // will set it to 0.0 + wjtol.setTolerance(0.2, -pi); + wjtol.Build(); + wjtol.getOpenWires(wiretol, nullptr, false); + + wjNegatol.addShape(edgesNegatol); + // Setting atol to a negative value won't have effect and therefor wjNegatol.pimpl->myAngularTol + // will keep the default value. + // The tol value must be given in any case. + wjNegatol.setTolerance(-0.1, -pi); + wjNegatol.Build(); + wjNegatol.getOpenWires(wireNegatol, nullptr, false); + + wjatol.addShape(edgesatol); + // Setting atol to a negative value won't have effect and therefor wjNegatol.pimpl->myAngularTol + // will keep the default value. + // We give also the tol value so that a closed wire can be created. + wjatol.setTolerance(0.2, pi / 9); + wjatol.Build(); + wjatol.getOpenWires(wireatol, nullptr, false); + + // Assert + + // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we + // only changed the value of setTolerance() + EXPECT_FALSE(wjNoBuild.IsDone()); + + // In this case, as there's a gap between edge1 and edge3, no closed wires are created. + EXPECT_TRUE(wjNegtol.Shape().IsNull()); + // All the edges added with wjNegtol.addShape() can be extracted with wjNegtol.getOpenWires() + EXPECT_EQ(wireNegtol.getSubTopoShapes(TopAbs_EDGE).size(), 3); + + // In this case, as the gap between edge1 and edge3 is smaller than tol, a closed wire can be + // created and it contains all the edges added with wjtol.addShape(). + EXPECT_EQ(TopoShape(wjtol.Shape()).getSubTopoShapes(TopAbs_EDGE).size(), 3); + // There are no open wires and therefor no edges that create them + EXPECT_EQ(wiretol.getSubTopoShapes(TopAbs_EDGE).size(), 0); + + // In this case, as there's a gap between edge2, edge4 and edge5, no closed wires are created. + EXPECT_TRUE(wjNegatol.Shape().IsNull()); + // All the edges added with wjNegtol.addShape() can be extracted with wjNegatol.getOpenWires() + EXPECT_EQ(wireNegatol.getSubTopoShapes(TopAbs_EDGE).size(), 4); + + // In this case, as the gap between edge2, edge4 and edge5 is smaller than tol, a closed wire + // can be created. + // Because of atol, edge4 and edge5 are considerated as duplicates and therefor one of them is + // removed by WireJoiner::WireJoinerP::add(). + // The closed wire is then created using all the edges added with wjatol.addShape() except the + // removed one + EXPECT_EQ(TopoShape(wjatol.Shape()).getSubTopoShapes(TopAbs_EDGE).size(), 3); + // There are no open wires and therefor no edges that create them + EXPECT_EQ(wireatol.getSubTopoShapes(TopAbs_EDGE).size(), 0); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 0d4502c177d0acf67911dcc0626394f2f7090a3a Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Tue, 12 Mar 2024 19:11:59 +0100 Subject: [PATCH 08/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::getOpenWires() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 105 ++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index c43b041597..7f5c6ae8eb 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -588,4 +588,109 @@ TEST_F(WireJoinerTest, setTolerance) EXPECT_EQ(wireatol.getSubTopoShapes(TopAbs_EDGE).size(), 0); } +TEST_F(WireJoinerTest, getOpenWires) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + 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(1.0, 1.0, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.5, 0.5, 0.0), gp_Pnt(1.5, 1.5, 0.0)).Edge()}; + + // A vector of edges used as argument for wjNoOpenWires.addShape() + std::vector edgesNoOpenWires {edge1, edge2, edge3}; + // A vector of edges used as argument for wjOriginal.addShape() + std::vector edgesOriginal {edge1, edge2, edge4}; + // A vector of edges used as argument for wjNoOriginal.addShape() + std::vector edgesNoOriginal {edge1, edge2, edge4}; + // A vector of TopoShape edges used as argument for wjNoOp.addShape(). A Tag is needed for every + // TopoShape, otherwise no element map will be created and no op can be found + std::vector edgesNoOp {TopoShape(edge2, 6), TopoShape(edge4, 7)}; + // A vector of TopoShape edges used as argument for wjOp.addShape(). A Tag is needed for every + // TopoShape, otherwise no element map will be created and no op can be found + std::vector edgesOp {TopoShape(edge2, 8), TopoShape(edge4, 9)}; + + // A WireJoiner object that will create a closed wire and no open wires + auto wjNoOpenWires {WireJoiner()}; + + // A WireJoiner object where the argument noOriginal will be set to false and that will create + // an open wire + auto wjOriginal {WireJoiner()}; + // A WireJoiner object where the argument noOriginal will be set to true and that will create an + // open wire + auto wjNoOriginal {WireJoiner()}; + + // A WireJoiner object where the argument op won't be set and that will create an open wire + auto wjNoOp {WireJoiner()}; + // A WireJoiner object where the argument op will be set and that will create an open wire + auto wjOp {WireJoiner()}; + + // An empty TopoShape that will contain the shapes returned by wjNoOpenWires.getOpenWires() + auto wireNoOpenWires {TopoShape(1)}; + + // An empty TopoShape that will contain the shapes returned by wjOriginal.getOpenWires() + auto wireOriginal {TopoShape(2)}; + // An empty TopoShape that will contain the shapes returned by wjNoOriginal.getOpenWires() + auto wireNoOriginal {TopoShape(3)}; + + // An empty TopoShape that will contain the shapes returned by wjNoOp.getOpenWires() + auto wireNoOp {TopoShape(4)}; + // An empty TopoShape that will contain the shapes returned by wjOp.getOpenWires() + auto wireOp {TopoShape(5)}; + + // Act + + wjNoOpenWires.addShape(edgesNoOpenWires); + // wjNoOpenWires.Build() is called by wjNoOpenWires.getOpenWires() + wjNoOpenWires.getOpenWires(wireNoOpenWires); + + wjOriginal.addShape(edgesOriginal); + // wjOriginal.Build() is called by wjOriginal.getOpenWires() + wjOriginal.getOpenWires(wireOriginal, nullptr, false); + + wjNoOriginal.addShape(edgesNoOriginal); + // wjNoOriginal.Build() is called by wjNoOriginal.getOpenWires() + wjNoOriginal.getOpenWires(wireNoOriginal); + + wjNoOp.addShape(edgesNoOp); + // wjNoOp.Build() is called by wjNoOp.getOpenWires() + wjNoOp.getOpenWires(wireNoOp, nullptr, false); + + wjOp.addShape(edgesOp); + // wjOp.Build() is called by wjOp.getOpenWires() + wjOp.getOpenWires(wireOp, "getOpenWires", false); + + // Assert + + // All the edges added with wjNoOpenWires.addShape() are used to create a closed wire, therefor + // wireNoOpenWires should be null + EXPECT_TRUE(wireNoOpenWires.isNull()); + + // In this case wireOriginal should contain all the edges added with wjOriginal.addShape(), + // except those ones that are split, and all the edges generated by splitting an edge with + // another one. + // edge1 and edge2 are left untouched, while edge4 is split in two at the intersection point + // (1.0, 1.0, 0.0), therefor 4 edges. + EXPECT_EQ(wireOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 4); + + // In this case wireNoOriginal should contain only the edges generated by splitting one of them + // with another one. + // As edge1 and edge2 are left untouched, the only edges we should find are the ones generated + // by splitting edge4 in two at the intersection point (1.0, 1.0, 0.0) + EXPECT_EQ(wireNoOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 2); + + // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getOpenWires() will + // call TopoShape::makeShapeWithElementMap() which, without a value for op, will use "MAK" as + // value for the various element maps + EXPECT_NE(wireNoOp.getElementMap()[0].name.find("MAK"), -1); + + // In this case WireJoiner::WireJoinerP::getOpenWires() will call + // TopoShape::makeShapeWithElementMap() giving "getOpenWires" as value for the op argument. + // That value should be found in the various element maps instead of "MAK" + EXPECT_EQ(wireOp.getElementMap()[0].name.find("MAK"), -1); + EXPECT_NE(wireOp.getElementMap()[0].name.find("getOpenWires"), -1); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 0834709375269ff144c08acbf8d1308df5cd4f82 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Wed, 13 Mar 2024 13:58:36 +0100 Subject: [PATCH 09/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::getResultWires() * Replaced references in test for WireJoiner::getOpenWires() with more correct references * Added a comment in WireJoiner::WireJoinerP::getResultWires() to better explain how it works Signed-off-by: CalligaroV --- src/Mod/Part/App/WireJoiner.cpp | 4 ++ tests/src/Mod/Part/App/WireJoiner.cpp | 88 +++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/Mod/Part/App/WireJoiner.cpp b/src/Mod/Part/App/WireJoiner.cpp index 80baa650b3..583d6fcb49 100644 --- a/src/Mod/Part/App/WireJoiner.cpp +++ b/src/Mod/Part/App/WireJoiner.cpp @@ -2297,6 +2297,10 @@ public: } bool getResultWires(TopoShape &shape, const char *op) { + // As compound is created by various calls to builder.MakeCompound() it looks that the + // following condition is always false. + // Probably it may be needed to add something like compound.Nullify() as done for + // openWireCompound in WireJoiner::WireJoinerP::clear() if (compound.IsNull()) { shape.setShape(TopoShape()); return false; diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 7f5c6ae8eb..d03c23ec5c 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "src/App/InitApplication.h" #include "Mod/Part/App/WireJoiner.h" +#include #include "PartTestHelpers.h" @@ -682,15 +683,92 @@ TEST_F(WireJoinerTest, getOpenWires) EXPECT_EQ(wireNoOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 2); // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getOpenWires() will - // call TopoShape::makeShapeWithElementMap() which, without a value for op, will use "MAK" as - // value for the various element maps - EXPECT_NE(wireNoOp.getElementMap()[0].name.find("MAK"), -1); + // call TopoShape::makeShapeWithElementMap() which, without a value for op, will use + // Part::OpCodes::Maker as value for the various element maps + EXPECT_NE(wireNoOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); // In this case WireJoiner::WireJoinerP::getOpenWires() will call // TopoShape::makeShapeWithElementMap() giving "getOpenWires" as value for the op argument. - // That value should be found in the various element maps instead of "MAK" - EXPECT_EQ(wireOp.getElementMap()[0].name.find("MAK"), -1); + // That value should be found in the various element maps instead of Part::OpCodes::Maker + EXPECT_EQ(wireOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); EXPECT_NE(wireOp.getElementMap()[0].name.find("getOpenWires"), -1); } +TEST_F(WireJoinerTest, getResultWires) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + // Unlike calling WireJoiner::Build(), WireJoiner::getResultWires() returns a shape with edges + // only if those given as inputs crosses each others + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; + auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(0.1, 0.1, 0.0)).Edge()}; + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; + + // A vector of edges used as argument for wjNoResultWires.addShape() + std::vector edgesNoResultWires {edge1, edge2, edge3}; + // A vector of TopoShape edges used as argument for wjNoOp.addShape(). A Tag is needed for every + // TopoShape, otherwise no element map will be created and no op can be found + std::vector edgesNoOp {TopoShape(edge1, 4), + TopoShape(edge2, 5), + TopoShape(edge4, 6)}; + // A vector of TopoShape edges used as argument for wjOp.addShape(). A Tag is needed for every + // TopoShape, otherwise no element map will be created and no op can be found + std::vector edgesOp {TopoShape(edge1, 7), TopoShape(edge2, 8), TopoShape(edge4, 9)}; + + // A WireJoiner object that will create no closed wires + auto wjNoResultWires {WireJoiner()}; + + // A WireJoiner object where the argument op won't be set and that will create a closed wire + auto wjNoOp {WireJoiner()}; + // A WireJoiner object where the argument op will be set and that will create a closed wire + auto wjOp {WireJoiner()}; + + // An empty TopoShape that will contain the shapes returned by + // wireNoResultWires.getResultWires() + auto wireNoResultWires {TopoShape(1)}; + + // An empty TopoShape that will contain the shapes returned by wjNoOp.getResultWires() + auto wireNoOp {TopoShape(2)}; + // An empty TopoShape that will contain the shapes returned by wjOp.getResultWires() + auto wireOp {TopoShape(3)}; + + + // Act + + wjNoResultWires.addShape(edgesNoResultWires); + // wjNoOpenWires.Build() is called by wjNoResultWires.getResultWires() + wjNoResultWires.getResultWires(wireNoResultWires); + + wjNoOp.addShape(edgesNoOp); + // wjNoOp.Build() is called by wjNoOp.getResultWires() + wjNoOp.getResultWires(wireNoOp, nullptr); + + wjOp.addShape(edgesOp); + // wjOp.Build() is called by wjOp.getResultWires() + wjOp.getResultWires(wireOp, "getResultWires"); + + // Arrange + + // All the edges added with wjNoResultWires.addShape() can't create a closed wire, therefor + // wireNoResultWires shouldn't have any edges + // It's not possible to get an useful result from wireNoResultWires.isNull() because + // WireJoiner::WireJoinerP::compound is always created by + // WireJoiner::WireJoinerP::builder.MakeCompound(), which doesn't create a null compound + EXPECT_EQ(wireNoResultWires.getSubTopoShapes(TopAbs_EDGE).size(), 0); + + // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getResultWires() + // will call TopoShape::makeShapeWithElementMap() which, without a value for op, will use + // Part::OpCodes::Maker as value for the various element maps + EXPECT_NE(wireNoOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); + + // In this case WireJoiner::WireJoinerP::getResultWires() will call + // TopoShape::makeShapeWithElementMap() giving "getResultWires" as value for the op argument. + // That value should be found in the various element maps instead of Part::OpCodes::Maker + EXPECT_EQ(wireOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1); + EXPECT_NE(wireOp.getElementMap()[0].name.find("getResultWires"), -1); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 66c08664a5f40050be1e5f253933216cf893d1d9 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Wed, 13 Mar 2024 20:39:26 +0100 Subject: [PATCH 10/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::Build() * Fixed typo in test for WireJoiner::getResultWires() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 40 ++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index d03c23ec5c..2dc546f4dc 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -750,7 +750,7 @@ TEST_F(WireJoinerTest, getResultWires) // wjOp.Build() is called by wjOp.getResultWires() wjOp.getResultWires(wireOp, "getResultWires"); - // Arrange + // Assert // All the edges added with wjNoResultWires.addShape() can't create a closed wire, therefor // wireNoResultWires shouldn't have any edges @@ -771,4 +771,42 @@ TEST_F(WireJoinerTest, getResultWires) EXPECT_NE(wireOp.getElementMap()[0].name.find("getResultWires"), -1); } +#if OCC_VERSION_HEX >= 0x070600 +// WireJoiner::Build() has already been tested indirectly in the other tests. +// Here we check only the difference with OCCT versions >= 7.6.0 that add the parameter theRange +TEST_F(WireJoinerTest, Build) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; + auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; + + // A vector of edges used as argument for wjtheRange.addShape() + std::vector edgestheRange {edge1, edge2, edge3}; + + // A WireJoiner object that will create no closed wires + auto wjtheRange {WireJoiner()}; + + // A Message_ProgressRange object that will be used as argument for wjtheRange.Build() + auto mpr {Message_ProgressRange()}; + + // Act + + wjtheRange.addShape(edgestheRange); + wjtheRange.Build(mpr); + + // Assert + + // theRange isn't used in WireJoiner::Build() and therefor not attached to any indicator. + // For more reference see + // https://dev.opencascade.org/doc/occt-7.6.0/refman/html/class_message___progress_range.html + // and + // https://dev.opencascade.org/doc/occt-7.6.0/refman/html/class_message___progress_indicator.html + EXPECT_FALSE(mpr.IsActive()); +} +#endif + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From c70f34b1f41c604de51b26c1ca2af46e60c92008 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Thu, 14 Mar 2024 11:27:41 +0100 Subject: [PATCH 11/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::Modified() * Fixed typo in test for WireJoiner::getResultWires() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 46 ++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 2dc546f4dc..6fa98445cb 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -739,7 +739,7 @@ TEST_F(WireJoinerTest, getResultWires) // Act wjNoResultWires.addShape(edgesNoResultWires); - // wjNoOpenWires.Build() is called by wjNoResultWires.getResultWires() + // wjNoResultWires.Build() is called by wjNoResultWires.getResultWires() wjNoResultWires.getResultWires(wireNoResultWires); wjNoOp.addShape(edgesNoOp); @@ -809,4 +809,48 @@ TEST_F(WireJoinerTest, Build) } #endif +TEST_F(WireJoinerTest, Modified) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; + auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; + + // A vector of edges used as argument for wjModified.addShape() + std::vector edges {edge1, edge2, edge3}; + + // A WireJoiner object that will create no closed wires + auto wjModified {WireJoiner()}; + + // Act + + wjModified.addShape(edges); + wjModified.Build(); + + // Assert + + // In this case, every edge added with wjModified.addShape() modifies other shapes 3 times + + // edge1 modifies the edges with vertexes: + // (0.0, 0.0, 0.0) - (1.0, 1.0, 0.0) + // (-0.1, -0.1, 0.0) - (0.0, 0.0, 0.0) + // (1.0, -0.1, 0.0) - (1.0, 0.0, 0.0) + EXPECT_EQ(wjModified.Modified(edge1).Size(), 3); + + // edge2 modifies the edges with vertexes: + // (1.0, 1.0, 0.0) - (1.1, 1.1, 0.0) + // (0.0, 0.0, 0.0) - (1.0, 0.0, 0.0) + // (1.0, 0.0, 0.0) - (1.1, 0.0, 0.0) + EXPECT_EQ(wjModified.Modified(edge2).Size(), 3); + + // edge3 modifies the edges with vertexes: + // (1.0, 0.0, 0.0) - (1.0, 1.0, 0.0) + // (1.0, 1.0, 0.0) - (1.0, 1.1, 0.0) + // (-0.1, 0.0, 0.0) - (0.0, 0.0, 0.0) + EXPECT_EQ(wjModified.Modified(edge3).Size(), 3); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From c6d3ada29d626f3ced5fdcb5bbca800f2aaa74d8 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Thu, 14 Mar 2024 16:01:14 +0100 Subject: [PATCH 12/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::Generated() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 6fa98445cb..0020178d22 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -853,4 +853,40 @@ TEST_F(WireJoinerTest, Modified) EXPECT_EQ(wjModified.Modified(edge3).Size(), 3); } +TEST_F(WireJoinerTest, Generated) +{ + // Arrange + + // Create various edges that will be used for the WireJoiner objects tests + + auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()}; + auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()}; + + // A vector of edges used as argument for wjGenerated.addShape() + std::vector edges {edge1, edge2, edge3}; + + // A WireJoiner object that will create no closed wires + auto wjGenerated {WireJoiner()}; + + // Act + + wjGenerated.addShape(edges); + wjGenerated.Build(); + + // Assert + + // There aren't calls to WireJoiner::WireJoinerP::aHistory->AddGenerated() or similar methods in + // WireJoiner::WireJoinerP, therefor nothing is returned by calling + // WireJoiner::WireJoinerP::aHistory->Generated(). + // There's a call to WireJoiner::WireJoinerP::aHistory->Merge() that uses the history produced + // by a ShapeFix_Wire object in WireJoiner::WireJoinerP::makeCleanWire() that however looks + // always empty as no methods in ShapeFix_Wire call AddGenerated() or similar methods + // (checked in OCCT 7.3.0 source + // https://git.dev.opencascade.org/gitweb/?p=occt.git;a=snapshot;h=42da0d5115bff683c6b596e66cdeaff957f81e7d;sf=tgz) + EXPECT_EQ(wjGenerated.Generated(edge1).Size(), 0); + EXPECT_EQ(wjGenerated.Generated(edge2).Size(), 0); + EXPECT_EQ(wjGenerated.Generated(edge3).Size(), 0); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 2d66dfbf401bf2049076f2141b999d51c0c5c252 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Thu, 14 Mar 2024 16:59:47 +0100 Subject: [PATCH 13/16] Part/Toponaming: Transfer WireJoiner * Added test for WireJoiner::IsDeleted() * Fixed a typo in test for WireJoiner::Modified() * Fixed a typo in test for WireJoiner::Generated() Signed-off-by: CalligaroV --- tests/src/Mod/Part/App/WireJoiner.cpp | 43 +++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/src/Mod/Part/App/WireJoiner.cpp b/tests/src/Mod/Part/App/WireJoiner.cpp index 0020178d22..717b8a4b3c 100644 --- a/tests/src/Mod/Part/App/WireJoiner.cpp +++ b/tests/src/Mod/Part/App/WireJoiner.cpp @@ -822,7 +822,7 @@ TEST_F(WireJoinerTest, Modified) // A vector of edges used as argument for wjModified.addShape() std::vector edges {edge1, edge2, edge3}; - // A WireJoiner object that will create no closed wires + // A WireJoiner object that will have shapes modified by the added edges auto wjModified {WireJoiner()}; // Act @@ -866,7 +866,7 @@ TEST_F(WireJoinerTest, Generated) // A vector of edges used as argument for wjGenerated.addShape() std::vector edges {edge1, edge2, edge3}; - // A WireJoiner object that will create no closed wires + // A WireJoiner object that will have shapes generated by the added edges auto wjGenerated {WireJoiner()}; // Act @@ -889,4 +889,43 @@ TEST_F(WireJoinerTest, Generated) EXPECT_EQ(wjGenerated.Generated(edge3).Size(), 0); } +TEST_F(WireJoinerTest, IsDeleted) +{ + + // Create various edges that will be used for the WireJoiner objects tests + + 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(1.0, 1.0, 0.0)).Edge()}; + auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()}; + auto edge5 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.49, 0.0, 0.0), gp_Pnt(0.51, 0.0, 0.0)).Edge()}; + + // A vector of edges used as argument for wjIsDeleted.addShape() + std::vector edges {edge1, edge2, edge3, edge4, edge5}; + + // A WireJoiner object that will have shapes deleted by the added edges + auto wjIsDeleted {WireJoiner()}; + // To get all the deleted shapes in this case we also need to set the tolerance value + wjIsDeleted.setTolerance(0.03); + + // Act + + wjIsDeleted.addShape(edges); + wjIsDeleted.Build(); + + // Assert + + // In this case, edge1, edge2 and edge3 don't meet the conditions for deletion + EXPECT_FALSE(wjIsDeleted.IsDeleted(edge1)); + EXPECT_FALSE(wjIsDeleted.IsDeleted(edge2)); + EXPECT_FALSE(wjIsDeleted.IsDeleted(edge3)); + + // edge4 is a duplicate of edge3 and therefor deleted + EXPECT_TRUE(wjIsDeleted.IsDeleted(edge4)); + + // edge5 is smaller that the smallest shape that can be considered with the given value of + // tolerance and therefor deleted + EXPECT_TRUE(wjIsDeleted.IsDeleted(edge5)); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 9da39d8ac99b6bcea50048b6af300231aa5c31d7 Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Fri, 15 Mar 2024 17:29:16 +0100 Subject: [PATCH 14/16] Part/Toponaming: Transfer WireJoiner * Applied modifications to reduce the number of Lint warnings (round 1) Signed-off-by: CalligaroV --- src/Mod/Part/App/WireJoiner.cpp | 484 +++++++++++++++++++------------- 1 file changed, 284 insertions(+), 200 deletions(-) diff --git a/src/Mod/Part/App/WireJoiner.cpp b/src/Mod/Part/App/WireJoiner.cpp index 583d6fcb49..de8b9dc213 100644 --- a/src/Mod/Part/App/WireJoiner.cpp +++ b/src/Mod/Part/App/WireJoiner.cpp @@ -86,15 +86,15 @@ FC_LOG_LEVEL_INIT("WireJoiner",true, true) using namespace Part; -static inline void getEndPoints(const TopoDS_Edge &e, gp_Pnt &p1, gp_Pnt &p2) { - p1 = BRep_Tool::Pnt(TopExp::FirstVertex(e)); - p2 = BRep_Tool::Pnt(TopExp::LastVertex(e)); +static inline void getEndPoints(const TopoDS_Edge &eForEndPoints, gp_Pnt &p1, gp_Pnt &p2) { + p1 = BRep_Tool::Pnt(TopExp::FirstVertex(eForEndPoints)); + p2 = BRep_Tool::Pnt(TopExp::LastVertex(eForEndPoints)); } static inline void getEndPoints(const TopoDS_Wire &wire, gp_Pnt &p1, gp_Pnt &p2) { BRepTools_WireExplorer xp(wire); p1 = BRep_Tool::Pnt(TopoDS::Vertex(xp.CurrentVertex())); - for(;xp.More();xp.Next()); + for(;xp.More();xp.Next()) {}; p2 = BRep_Tool::Pnt(TopoDS::Vertex(xp.CurrentVertex())); } @@ -122,12 +122,13 @@ public: int catchIteration; int iteration = 0; - typedef bg::model::box Box; - - bool checkBBox(const Bnd_Box &box) + using Box = bg::model::box; + + bool checkBBox(const Bnd_Box &box) const { - if (box.IsVoid()) + if (box.IsVoid()) { return false; + } Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; box.Get(xMin, yMin, zMin, xMax, yMax, zMax); return zMax - zMin <= myTol; @@ -140,25 +141,29 @@ public: catchIteration = hParam->GetInt("Iteration", 0); } - bool getBBox(const TopoDS_Shape &e, Bnd_Box &bound) { - BRepBndLib::AddOptimal(e,bound,Standard_False); + bool getBBox(const TopoDS_Shape &eForBBox, Bnd_Box &bound) { + BRepBndLib::AddOptimal(eForBBox,bound,Standard_False); if (bound.IsVoid()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("failed to get bound of edge"); + } return false; } - if (!checkBBox(bound)) - showShape(e, "invalid"); - if (bound.SquareExtent() < myTol2) + if (!checkBBox(bound)) { + showShape(eForBBox, "invalid"); + } + if (bound.SquareExtent() < myTol2) { return false; + } bound.Enlarge(myTol); return true; } - bool getBBox(const TopoDS_Shape &e, Box &box) { + bool getBBox(const TopoDS_Shape &eForBBox, Box &box) { Bnd_Box bound; - if (!getBBox(e, bound)) + if (!getBBox(eForBBox, bound)) { return false; + } Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; bound.Get(xMin, yMin, zMin, xMax, yMax, zMax); box = Box(gp_Pnt(xMin,yMin,zMin), gp_Pnt(xMax,yMax,zMax)); @@ -191,15 +196,15 @@ public: GeomAbs_CurveType type; bool isLinear; - EdgeInfo(const TopoDS_Edge &e, + EdgeInfo(const TopoDS_Edge &eForInfo, const gp_Pnt &pt1, const gp_Pnt &pt2, const Box &bound, bool bbox, bool isLinear) - :edge(e),p1(pt1),p2(pt2),box(bound),queryBBox(bbox),isLinear(isLinear) + :edge(eForInfo),p1(pt1),p2(pt2),box(bound),queryBBox(bbox),isLinear(isLinear) { - curve = BRep_Tool::Curve(e, firstParam, lastParam); + curve = BRep_Tool::Curve(eForInfo, firstParam, lastParam); type = GeomAdaptor_Curve(curve).GetType(); assertCheck(!curve.IsNull()); GeomLProp_CLProps prop(curve,(firstParam+lastParam)*0.5,0,Precision::Confusion()); @@ -216,32 +221,38 @@ public: void reset() { wireInfo.reset(); wireInfo2.reset(); - if (iteration >= 0) + if (iteration >= 0) { iteration = 0; + } iteration2 = 0; iStart[0] = iStart[1] = iEnd[0] = iEnd[1] = -1; } const TopoDS_Shape &shape(bool forward=true) const { if (superEdge.IsNull()) { - if (forward) + if (forward) { return edge; - if (edgeReversed.IsNull()) + } + if (edgeReversed.IsNull()) { edgeReversed = edge.Reversed(); + } return edgeReversed; } - if (forward) + if (forward) { return superEdge; - if (superEdgeReversed.IsNull()) + } + if (superEdgeReversed.IsNull()) { superEdgeReversed = superEdge.Reversed(); + } return superEdgeReversed; } TopoDS_Wire wire() const { - auto s = shape(); - if (s.ShapeType() == TopAbs_WIRE) - return TopoDS::Wire(s); - return BRepBuilderAPI_MakeWire(TopoDS::Edge(s)).Wire(); + auto sForWire = shape(); + if (sForWire.ShapeType() == TopAbs_WIRE) { + return TopoDS::Wire(sForWire); + } + return BRepBuilderAPI_MakeWire(TopoDS::Edge(sForWire)).Wire(); } }; @@ -254,20 +265,21 @@ public: std::sort(data.begin(), data.end()); } } - bool contains(const T &v) + bool contains(const T &vForContains) { if (!sorted) { if (data.size() < 30) - return std::find(data.begin(), data.end(), v) != data.end(); + return std::find(data.begin(), data.end(), vForContains) != data.end(); sort(); } - auto it = std::lower_bound(data.begin(), data.end(), v); - return it!=data.end() && *it == v; + auto it = std::lower_bound(data.begin(), data.end(), vForContains); + return it!=data.end() && *it == vForContains; } bool intersects(const VectorSet &other) { - if (other.size() < size()) + if (other.size() < size()) { return other.intersects(*this); + } else if (!sorted) { for (const auto &v : data) { if (other.contains(v)) @@ -286,41 +298,46 @@ public: } return false; } - void insert(const T &v) + void insert(const T &vToInsert) { if (sorted) - data.insert(std::upper_bound(data.begin(), data.end(), v), v); + data.insert(std::upper_bound(data.begin(), data.end(), vToInsert), vToInsert); else - data.push_back(v); + data.push_back(vToInsert); } - bool insertUnique(const T &v) + bool insertUnique(const T &vToInsertUnique) { if (sorted) { - auto it = std::lower_bound(data.begin(), data.end(), v); - if (it != data.end() && *it == v) + auto it = std::lower_bound(data.begin(), data.end(), vToInsertUnique); + if (it != data.end() && *it == vToInsertUnique) { return false; - data.insert(it, v); + } + data.insert(it, vToInsertUnique); return true; } - - if (contains(v)) + + if (contains(vToInsertUnique)) { return false; - data.push_back(v); + } + data.push_back(vToInsertUnique); return true; } - void erase(const T &v) + void erase(const T &vToErase) { - if (!sorted) - data.erase(std::remove(data.begin(), data.end(), v), data.end()); + if (!sorted) { + data.erase(std::remove(data.begin(), data.end(), vToErase), data.end()); + } else { - auto it = std::lower_bound(data.begin(), data.end(), v); + auto it = std::lower_bound(data.begin(), data.end(), vToErase); auto itEnd = it; - while (itEnd!=data.end() && *itEnd==v) + while (itEnd != data.end() && *itEnd == vToErase) { ++itEnd; + } data.erase(it, itEnd); } - if (data.size() < 20) + if (data.size() < 20) { sorted = false; + } } void clear() { @@ -342,7 +359,7 @@ public: Handle(BRepTools_History) aHistory = new BRepTools_History; - typedef std::list Edges; + using Edges = std::list; Edges edges; std::map edgesTable; @@ -362,12 +379,14 @@ public: return it==other.it && start==other.start; } bool operator<(const VertexInfo &other) const { - auto a = edgeInfo(); - auto b = other.edgeInfo(); - if (a < b) + auto thisInfo = edgeInfo(); + auto otherInfo = other.edgeInfo(); + if (thisInfo < otherInfo) { return true; - if (a > b) + } + if (thisInfo > otherInfo) { return false; + } return start < other.start; } const gp_Pnt &pt() const { @@ -400,7 +419,11 @@ public: size_t iStart; size_t iEnd; size_t iCurrent; - StackInfo(size_t idx=0):iStart(idx),iEnd(idx),iCurrent(idx){} + explicit StackInfo(size_t idx = 0) + : iStart(idx) + , iEnd(idx) + , iCurrent(idx) + {} }; std::vector stack; @@ -419,8 +442,9 @@ public: void sort() const { - if (sorted.size() == vertices.size()) + if (sorted.size() == vertices.size()) { return; + } assertCheck(sorted.size() < vertices.size()); sorted.reserve(vertices.size()); for (int i=(int)sorted.size(); i<(int)vertices.size(); ++i) @@ -433,16 +457,18 @@ public: { if (vertices.size() < 20) { auto it = std::find(vertices.begin(), vertices.end(), info); - if (it == vertices.end()) + if (it == vertices.end()) { return 0; + } return it - vertices.begin() + 1; } sort(); auto it = std::lower_bound(sorted.begin(), sorted.end(), info, [&](int idx, const VertexInfo &v) {return vertices[idx]Clear(); @@ -532,27 +564,29 @@ public: return true; } - bool isInside(const WireInfo &wireInfo, gp_Pnt &pt) + bool isInside(const WireInfo &wireInfo, gp_Pnt &pt) const { - if (getWireBound(wireInfo).IsOut(pt)) + if (getWireBound(wireInfo).IsOut(pt)) { return false; + } BRepClass_FaceClassifier fc(wireInfo.face, pt, myTol); return fc.State() == TopAbs_IN; } - bool isOutside(const WireInfo &wireInfo, gp_Pnt &pt) + bool isOutside(const WireInfo &wireInfo, gp_Pnt &pt) const { - if (getWireBound(wireInfo).IsOut(pt)) + if (getWireBound(wireInfo).IsOut(pt)) { return false; + } BRepClass_FaceClassifier fc(wireInfo.face, pt, myTol); return fc.State() == TopAbs_OUT; } struct PntGetter { - typedef const gp_Pnt& result_type; - result_type operator()(const VertexInfo &v) const { - return v.pt(); + using result_type = const gp_Pnt&; + result_type operator()(const VertexInfo &vInfo) const { + return vInfo.pt(); } }; @@ -560,7 +594,7 @@ public: struct BoxGetter { - typedef const Box& result_type; + using result_type = const Box&; result_type operator()(Edges::iterator it) const { return it->box; } @@ -624,33 +658,33 @@ public: showShape(it->edge, "add"); } - int add(const TopoDS_Edge &e, bool queryBBox=false) + int add(const TopoDS_Edge &eToAdd, bool queryBBox=false) { auto it = edges.begin(); - return add(e, queryBBox, it); + return add(eToAdd, queryBBox, it); } - int add(const TopoDS_Edge &e, bool queryBBox, Edges::iterator &it) + int add(const TopoDS_Edge &eToAdd, bool queryBBox, Edges::iterator &it) { Box bbox; - if (!getBBox(e, bbox)) { - showShape(e, "small"); - aHistory->Remove(e); + if (!getBBox(eToAdd, bbox)) { + showShape(eToAdd, "small"); + aHistory->Remove(eToAdd); return 0; } - return add(e, queryBBox, bbox, it) ? 1 : -1; + return add(eToAdd, queryBBox, bbox, it) ? 1 : -1; } - bool add(const TopoDS_Edge &e, bool queryBBox, const Box &bbox, Edges::iterator &it) + bool add(const TopoDS_Edge &eToAdd, bool queryBBox, const Box &bbox, Edges::iterator &it) { gp_Pnt p1,p2; - getEndPoints(e,p1,p2); + getEndPoints(eToAdd,p1,p2); TopoDS_Vertex v1, v2; TopoDS_Edge ev1, ev2; double tol = myTol2; // search for duplicate edges - showShape(e, "addcheck"); - bool isLinear = TopoShape(e).isLinearEdge(); + showShape(eToAdd, "addcheck"); + bool isLinear = TopoShape(eToAdd).isLinearEdge(); std::unique_ptr geo; for (auto vit=vmap.qbegin(bgi::nearest(p1,INT_MAX));vit!=vmap.qend();++vit) { @@ -671,16 +705,16 @@ public: v2 = vinfo.otherVertex(); } if (isLinear && vinfo.edgeInfo()->isLinear) { - showShape(e, "duplicate"); - aHistory->Remove(e); + showShape(eToAdd, "duplicate"); + aHistory->Remove(eToAdd); return false; } else if (auto geoEdge = vinfo.edgeInfo()->geometry()) { if (!geo) - geo = Geometry::fromShape(e, /*silent*/true); + geo = Geometry::fromShape(eToAdd, /*silent*/true); if (geo && geo->isSame(*geoEdge, myTol, myAngularTol)) { - showShape(e, "duplicate"); - aHistory->Remove(e); + showShape(eToAdd, "duplicate"); + aHistory->Remove(eToAdd); return false; } } @@ -701,37 +735,38 @@ public: // which is crucial for the OCC internal shape hierarchy structure. We // achieve this by making a temp wire and let OCC do the hard work of // replacing the vertex. - auto connectEdge = [&](TopoDS_Edge &e, - const TopoDS_Vertex &v, - const TopoDS_Edge &eOther, - const TopoDS_Vertex &vOther) - { - if (vOther.IsNull()) + auto connectEdge = [&](TopoDS_Edge& eCurrent, + const TopoDS_Vertex& vCurrent, + const TopoDS_Edge& eOther, + const TopoDS_Vertex& vOther) { + if (vOther.IsNull()) { return; - if (v.IsSame(vOther)) + } + if (vCurrent.IsSame(vOther)) { return; - double tol = std::max(BRep_Tool::Pnt(v).Distance(BRep_Tool::Pnt(vOther)), + } + double tol = std::max(BRep_Tool::Pnt(vCurrent).Distance(BRep_Tool::Pnt(vOther)), BRep_Tool::Tolerance(vOther)); - if (tol >= BRep_Tool::Tolerance(v)) { + if (tol >= BRep_Tool::Tolerance(vCurrent)) { ShapeFix_ShapeTolerance fix; - fix.SetTolerance(v, std::max(tol*0.5, myTol), TopAbs_VERTEX); + fix.SetTolerance(vCurrent, std::max(tol*0.5, myTol), TopAbs_VERTEX); } BRepBuilderAPI_MakeWire mkWire(eOther); - mkWire.Add(e); + mkWire.Add(eCurrent); auto newEdge = mkWire.Edge(); TopoDS_Vertex vFirst = TopExp::FirstVertex(newEdge); TopoDS_Vertex vLast = TopExp::LastVertex(newEdge); assertCheck(vLast.IsSame(vOther) || vFirst.IsSame(vOther)); - e = newEdge; + eCurrent = newEdge; }; - TopoDS_Edge edge = e; - TopoDS_Vertex vFirst = TopExp::FirstVertex(e); - TopoDS_Vertex vLast = TopExp::LastVertex(e); + TopoDS_Edge edge = eToAdd; + TopoDS_Vertex vFirst = TopExp::FirstVertex(eToAdd); + TopoDS_Vertex vLast = TopExp::LastVertex(eToAdd); connectEdge(edge, vFirst, ev1, v1); connectEdge(edge, vLast, ev2, v2); - if (!edge.IsSame(e)) { - auto itSource = sourceEdges.find(e); + if (!edge.IsSame(eToAdd)) { + auto itSource = sourceEdges.find(eToAdd); if (itSource != sourceEdges.end()) { TopoShape newEdge = *itSource; newEdge.setShape(edge, false); @@ -748,8 +783,9 @@ public: void add(const TopoDS_Shape &shape, bool queryBBox=false) { - for (TopExp_Explorer xp(shape,TopAbs_EDGE); xp.More(); xp.Next()) - add(TopoDS::Edge(xp.Current()),queryBBox); + for (TopExp_Explorer xp(shape, TopAbs_EDGE); xp.More(); xp.Next()) { + add(TopoDS::Edge(xp.Current()), queryBBox); + } } //This algorithm tries to join connected edges into wires @@ -777,17 +813,21 @@ public: vmap.query(bgi::nearest(pt,1),std::back_inserter(ret)); assertCheck(ret.size()==1); double d = ret[0].pt().SquareDistance(pt); - if (d > tol) break; + if (d > tol) { + break; + } const auto &info = *ret[0].it; bool start = ret[0].start; if (d > Precision::SquareConfusion()) { // insert a filling edge to solve the tolerance problem const gp_Pnt &pt = ret[idx].pt(); - if (idx) - mkWire.Add(BRepBuilderAPI_MakeEdge(pend,pt).Edge()); - else - mkWire.Add(BRepBuilderAPI_MakeEdge(pt,pstart).Edge()); + if (idx) { + mkWire.Add(BRepBuilderAPI_MakeEdge(pend, pt).Edge()); + } + else { + mkWire.Add(BRepBuilderAPI_MakeEdge(pt, pstart).Edge()); + } } if (idx==1 && start) { @@ -818,8 +858,8 @@ public: double param; TopoDS_Shape intersectShape; gp_Pnt point; - IntersectInfo(double p, const gp_Pnt &pt, const TopoDS_Shape &s) - :param(p), intersectShape(s), point(pt) + IntersectInfo(double pToIntersect, const gp_Pnt &pt, const TopoDS_Shape &sToIntersect) + :param(pToIntersect), intersectShape(sToIntersect), point(pt) {} bool operator<(const IntersectInfo &other) const { return param < other.param; @@ -829,25 +869,29 @@ public: void checkSelfIntersection(const EdgeInfo &info, std::set ¶ms) { // Early return if checking for self intersection (only for non linear spline curves) - if (info.type <= GeomAbs_Parabola || info.isLinear) + if (info.type <= GeomAbs_Parabola || info.isLinear) { return; + } IntRes2d_SequenceOfIntersectionPoint points2d; TColgp_SequenceOfPnt points3d; TColStd_SequenceOfReal errors; TopoDS_Wire wire; BRepBuilderAPI_MakeWire mkWire(info.edge); - if (!mkWire.IsDone()) + if (!mkWire.IsDone()) { return; + } if (!BRep_Tool::IsClosed(mkWire.Wire())) { BRepBuilderAPI_MakeEdge mkEdge(info.p1, info.p2); - if (!mkEdge.IsDone()) + if (!mkEdge.IsDone()) { return; + } mkWire.Add(mkEdge.Edge()); } wire = mkWire.Wire(); BRepBuilderAPI_MakeFace mkFace(wire); - if (!mkFace.IsDone()) + if (!mkFace.IsDone()) { return; + } TopoDS_Face face = mkFace.Face(); ShapeAnalysis_Wire analysis(wire, face, myTol); analysis.CheckSelfIntersectingEdge(1, points2d, points3d); @@ -874,9 +918,11 @@ public: if (!planar) { BRepExtrema_DistShapeShape extss(info.edge, other.edge); extss.Perform(); - if (extss.IsDone() && extss.NbSolution() > 0) - if (!extss.IsDone() || extss.NbSolution()<=0 || extss.Value()>=myTol) - return; + if (extss.IsDone() && extss.NbSolution() > 0) { + if (!extss.IsDone() || extss.NbSolution() <= 0 || extss.Value() >= myTol) { + return; + } + } for (int i=1; i<=extss.NbSolution(); ++i) { Standard_Real p; auto s1 = extss.SupportOnShape1(i); @@ -906,14 +952,16 @@ public: int idx; BRepBuilderAPI_MakeWire mkWire(info.edge); mkWire.Add(other.edge); - if (mkWire.IsDone()) + if (mkWire.IsDone()) { idx = 2; + } else if (mkWire.Error() == BRepBuilderAPI_DisconnectedWire) { idx = 3; BRepBuilderAPI_MakeEdge mkEdge(info.p1, other.p1); if (!mkEdge.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Failed to build edge for checking intersection"); + } return; } mkWire.Add(mkEdge.Edge()); @@ -921,8 +969,9 @@ public: } if (!mkWire.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Failed to build wire for checking intersection"); + } return; } wire = mkWire.Wire(); @@ -931,8 +980,9 @@ public: getEndPoints(wire, p1, p2); BRepBuilderAPI_MakeEdge mkEdge(p1, p2); if (!mkEdge.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Failed to build edge for checking intersection"); + } return; } mkWire.Add(mkEdge.Edge()); @@ -940,8 +990,9 @@ public: BRepBuilderAPI_MakeFace mkFace(wire); if (!mkFace.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Failed to build face for checking intersection"); + } return; } TopoDS_Face face = mkFace.Face(); @@ -959,14 +1010,16 @@ public: IntersectInfo info(param, pt, shape); auto it = params.upper_bound(info); if (it != params.end()) { - if (it->point.SquareDistance(pt) < myTol2) + if (it->point.SquareDistance(pt) < myTol2) { return; + } } if (it != params.begin()) { auto itPrev = it; --itPrev; - if (itPrev->point.SquareDistance(pt) < myTol2) + if (itPrev->point.SquareDistance(pt) < myTol2) { return; + } } params.insert(it, info); return; @@ -975,7 +1028,7 @@ public: struct SplitInfo { TopoDS_Edge edge; TopoDS_Shape intersectShape; - Box bbox; + Box bbox; }; // Try splitting any edges that intersects other edge @@ -1009,7 +1062,7 @@ public: } i=0; - std::vector splitted; + std::vector splitted; // NOLINT for (auto it=edges.begin(); it!=edges.end(); ) { ++i; auto iter = intersects.find(&(*it)); @@ -1039,7 +1092,7 @@ public: continue; } - splitted.clear(); + splitted.clear(); // NOLINT itParam = params.begin(); for (auto itPrevParam=itParam++; itParam!=params.end(); ++itParam) { const auto &intersectShape = itParam->intersectShape.IsNull() @@ -1057,14 +1110,14 @@ public: BRepBuilderAPI_MakeEdge mkEdge(info.curve, param1, param2); if (mkEdge.IsDone()) { - splitted.emplace_back(); - auto &entry = splitted.back(); + splitted.emplace_back(); // NOLINT + auto& entry = splitted.back(); // NOLINT entry.edge = mkEdge.Edge(); entry.intersectShape = intersectShape; if (getBBox(entry.edge, entry.bbox)) itPrevParam = itParam; else - splitted.pop_back(); + splitted.pop_back(); // NOLINT } else if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("edge split failed " @@ -1073,7 +1126,7 @@ public: << ": " << mkEdge.Error()); } } - if (splitted.size() <= 1) { + if (splitted.size() <= 1) { // NOLINT ++it; continue; } @@ -1081,7 +1134,7 @@ public: showShape(info.edge, "remove"); auto removedEdge = info.edge; it = remove(it); - for (const auto &v : splitted) { + for (const auto& v : splitted) { // NOLINT if (!add(v.edge, false, v.bbox, it)) continue; auto &newInfo = *it++; @@ -1222,9 +1275,13 @@ public: // populate adjacent list for (auto &info : edges) { if (info.iteration == -2) { -#if OCC_VERSION_HEX >= 0x070000 + + // Originally there was the following precompiler directive around assertCheck(): + // #if OCC_VERSION_HEX >= 0x070000 + // Removed as the minimum OCCT version supported is 7.3.0 + assertCheck(BRep_Tool::IsClosed(info.shape())); -#endif + showShape(&info,"closed"); if (!doTightBound) builder.Add(compound,info.wire()); @@ -1283,8 +1340,9 @@ public: while (!done) { done = true; - if (doMergeEdge || doTightBound) + if (doMergeEdge || doTightBound) { findSuperEdges(); + } //Skip edges that are connected to only one end for (auto &info : edges) { @@ -1365,28 +1423,17 @@ public: } } - void checkStack() - { -#if 0 - if (stack.size() <= 1) - return; - std::vector edges; - auto &r = stack[stack.size()-2]; - for (int i=r.iStart;iiStart[currentIdx];iiEnd[currentIdx];++i) { auto &vinfo = adjacentList[i]; auto &info = *vinfo.it; - if (info.iteration < 0 || currentInfo == &info) + if (info.iteration < 0 || currentInfo == &info) { continue; + } bool abort = false; if (!wireSet.empty() && wireSet.contains(info.wireInfo.get())) { showShape(&info, "wired", iteration); - if (wireInfo) + if (wireInfo) { wireInfo->purge = true; + } abort = true; } @@ -1450,13 +1501,15 @@ public: } if (abort || currentInfo->wireInfo2) { - if (wireInfo) + if (wireInfo) { wireInfo->purge = true; + } continue; } - if (info.iteration == iteration) + if (info.iteration == iteration) { continue; + } info.iteration = iteration; if (wireInfo) { @@ -1468,8 +1521,9 @@ public: r.iCurrent = r.iEnd++; --idx; proceed = false; - if (idxVertex) + if (idxVertex) { *idxVertex = idx; + } if (stackPos) *stackPos = (int)stack.size()-2; @@ -1478,15 +1532,19 @@ public: if (info != &beginInfo) { while (true) { - if (++idx == (int)wireInfo->vertices.size()) + if (++idx == (int)wireInfo->vertices.size()) { idx = 0; + } info = wireInfo->vertices[idx].edgeInfo(); - if (info == &beginInfo) + if (info == &beginInfo) { break; + } stack.emplace_back(vertexStack.size()); vertexStack.push_back(wireInfo->vertices[idx]); ++stack.back().iEnd; - checkStack(); + + // Originally here there was a call to the method checkStack(), + // which does nothing and therefor has been removed. } } break; @@ -1495,7 +1553,7 @@ public: if (wireInfo->find(VertexInfo(vinfo.it, !vinfo.start))) { showShape(&info, "rintersect", iteration); // Only used when exhausting tight bound. - wireInfo->purge = true; + wireInfo->purge = true; continue; } @@ -1507,7 +1565,9 @@ public: vertexStack.push_back(adjacentList[i]); ++r.iEnd; } - checkStack(); + + // Originally here there was a call to the method checkStack(), which does nothing and + // therefor has been removed. if (proceed) { while (true) { @@ -1547,8 +1607,9 @@ public: continue; } if (wireInfo) { - if (idxVertex) + if (idxVertex) { *idxVertex = (int)wireInfo->vertices.size(); + } if (stackPos) *stackPos = (int)stack.size()-1; } @@ -1582,7 +1643,7 @@ public: { // Assumption: all edges lies on a common manifold surface // - // Definition of 'Tight Bound': a wire that cannot be splitted into + // Definition of 'Tight Bound': a wire that cannot be split into // smaller wires by any intersecting edges internal to the wire. // // The idea of the searching algorithm is simple. The initial condition @@ -1643,7 +1704,9 @@ public: stack.emplace_back(vertexStack.size()); ++stack.back().iEnd; vertexStack.push_back(currentVertex); - checkStack(); + + // Originally here there was a call to the method checkStack(), which does + // nothing and therefor has been removed. int idxEnd = (int)wireVertices.size(); int stackStart = (int)stack.size()-1; @@ -1754,7 +1817,9 @@ public: ++stack.back().iEnd; vertexStack.push_back(wireVertices[idxV]); edgeSet.insert(wireVertices[idxV].edgeInfo()); - checkStack(); + + // Originally here there was a call to the method checkStack(), which does + // nothing and therefor has been removed. } if (!newWire) { @@ -1949,7 +2014,9 @@ public: stack.emplace_back(vertexStack.size()); ++stack.back().iEnd; vertexStack.push_back(currentVertex); - checkStack(); + + // Originally here there a call to the method checkStack(), which + // does nothing and therefor has been removed. TopoDS_Wire wire; if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { @@ -2009,7 +2076,9 @@ public: ++stack.back().iEnd; vertexStack.push_back(wireVertices[idxV]); edgeSet.insert(wireVertices[idxV].edgeInfo()); - checkStack(); + + // Originally here there a call to the method checkStack(), which does + // nothing and therefor has been removed. } } @@ -2045,8 +2114,9 @@ public: std::vector inputEdges; if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) { - for (int i=1; i<=wireData->NbEdges(); ++i) + for (int i = 1; i <= wireData->NbEdges(); ++i) { inputEdges.emplace_back(wireData->Edge(i)); + } } ShapeFix_Wire fixer; @@ -2068,7 +2138,7 @@ public: fixer.FixClosed(); result = fixer.Wire(); - auto newHistory = fixer.Context()->History(); + auto newHistory = fixer.Context()->History(); if (FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE+1) { FC_MSG("init:"); @@ -2103,35 +2173,40 @@ public: } } - bool canShowShape(int idx=-1, bool forced=false) + bool canShowShape(int idx=-1, bool forced=false) const { if (idx < 0 || catchIteration == 0 || catchIteration > idx) { - if (!forced && FC_LOG_INSTANCE.level()<=FC_LOGLEVEL_TRACE) + if (!forced && FC_LOG_INSTANCE.level() <= FC_LOGLEVEL_TRACE) { return false; + } } return true; } - + void showShape(const EdgeInfo *info, const char *name, int idx=-1, bool forced=false) { - if (!canShowShape(idx, forced)) + if (!canShowShape(idx, forced)) { return; + } showShape(info->shape(), name, idx, forced); } void showShape(WireInfo &wireInfo, const char *name, int idx=-1, bool forced=false) { - if (!canShowShape(idx, forced)) + if (!canShowShape(idx, forced)) { return; - if (wireInfo.wire.IsNull()) + } + if (wireInfo.wire.IsNull()) { initWireInfo(wireInfo); + } showShape(wireInfo.wire, name, idx, forced); } - void showShape(const TopoDS_Shape &s, const char *name, int idx=-1, bool forced=false) + void showShape(const TopoDS_Shape &sToShow, const char *name, int idx=-1, bool forced=false) { - if (!canShowShape(idx, forced)) + if (!canShowShape(idx, forced)) { return; + } std::string _name; if (idx >= 0) { _name = name; @@ -2140,10 +2215,11 @@ public: _name += "_"; name = _name.c_str(); } - auto obj = Feature::create(s, name); - FC_MSG(obj->getNameInDocument() << " " << ShapeHasher()(s)); - if (catchObject == obj->getNameInDocument()) + auto obj = Feature::create(sToShow, name); + FC_MSG(obj->getNameInDocument() << " " << ShapeHasher()(sToShow)); + if (catchObject == obj->getNameInDocument()) { FC_MSG("found"); + } return; } @@ -2155,13 +2231,15 @@ public: for (const auto &e : sourceEdgeArray) add(TopoDS::Edge(e.getShape()), true); - if (doTightBound || doSplitEdge) + if (doTightBound || doSplitEdge) { splitEdges(); + } buildAdjacentList(); - if (!doTightBound && !doOutline) + if (!doTightBound && !doOutline) { findClosedWires(); + } else { findClosedWires(true); findTightBound(); @@ -2250,8 +2328,9 @@ public: void addWire(std::shared_ptr &wireInfo) { - if (!wireInfo || !wireInfo->done || !wireSet.insertUnique(wireInfo.get())) + if (!wireInfo || !wireInfo->done || !wireSet.insertUnique(wireInfo.get())) { return; + } initWireInfo(*wireInfo); builder.Add(compound, wireInfo->wire); } @@ -2278,8 +2357,10 @@ public: if (purge) { it = wires.erase(it); touched = true; - } else + } + else { ++it; + } } if (touched) { if (wires.empty()) { @@ -2399,13 +2480,16 @@ void WireJoiner::Build() void WireJoiner::Build(const Message_ProgressRange&) #endif { - if (IsDone()) + if (IsDone()) { return; + } pimpl->build(); - if (TopoShape(pimpl->compound).countSubShapes(TopAbs_SHAPE) > 0) + if (TopoShape(pimpl->compound).countSubShapes(TopAbs_SHAPE) > 0) { myShape = pimpl->compound; - else + } + else { myShape.Nullify(); + } Done(); } @@ -2421,20 +2505,20 @@ bool WireJoiner::getResultWires(TopoShape &shape, const char *op) return pimpl->getResultWires(shape, op); } -const TopTools_ListOfShape& WireJoiner::Generated (const TopoDS_Shape& S) +const TopTools_ListOfShape& WireJoiner::Generated (const TopoDS_Shape& SThatGenerates) { Build(); - return pimpl->aHistory->Generated(S); + return pimpl->aHistory->Generated(SThatGenerates); } -const TopTools_ListOfShape& WireJoiner::Modified (const TopoDS_Shape& S) +const TopTools_ListOfShape& WireJoiner::Modified (const TopoDS_Shape& SThatModifies) { Build(); - return pimpl->aHistory->Modified(S); + return pimpl->aHistory->Modified(SThatModifies); } -Standard_Boolean WireJoiner::IsDeleted (const TopoDS_Shape& S) +Standard_Boolean WireJoiner::IsDeleted (const TopoDS_Shape& SDeleted) { Build(); - return pimpl->aHistory->IsRemoved(S); + return pimpl->aHistory->IsRemoved(SDeleted); } From 11da8d9c0a9b46180795d0f93b6f7183b1ce068b Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Sat, 16 Mar 2024 11:55:54 +0100 Subject: [PATCH 15/16] Part/Toponaming: Transfer WireJoiner * Applied modifications to reduce the number of Lint warnings (round 2) --- src/Mod/Part/App/WireJoiner.cpp | 208 +++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 54 deletions(-) diff --git a/src/Mod/Part/App/WireJoiner.cpp b/src/Mod/Part/App/WireJoiner.cpp index de8b9dc213..fbcf684688 100644 --- a/src/Mod/Part/App/WireJoiner.cpp +++ b/src/Mod/Part/App/WireJoiner.cpp @@ -98,15 +98,12 @@ static inline void getEndPoints(const TopoDS_Wire &wire, gp_Pnt &p1, gp_Pnt &p2) p2 = BRep_Tool::Pnt(TopoDS::Vertex(xp.CurrentVertex())); } -static void _assertCheck(int line, bool cond, const char *msg) -{ - if (!cond) { - _FC_ERR(__FILE__, line, "Assert failed: " << msg); - throw Base::RuntimeError("Assertion failed"); - } -} - -#define assertCheck(cond) _assertCheck(__LINE__, cond, #cond) +// Originally here there was the definition of the precompiler macro assertCheck() and of the method +// _assertCheck(), that have been replaced with the already defined precompiler macro assert(). +// See +// https://github.com/realthunder/FreeCAD/blob/6f15849be2505f98927e75d0e8352185e14e7b72/src/Mod/Part/App/WireJoiner.cpp#L107 +// for reference and https://github.com/FreeCAD/FreeCAD/pull/12535/files#r1526647457 for the +// discussion about replacing it class WireJoiner::WireJoinerP { public: @@ -129,7 +126,12 @@ public: if (box.IsVoid()) { return false; } - Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + Standard_Real xMin = Standard_Real(); + Standard_Real yMin = Standard_Real(); + Standard_Real zMin = Standard_Real(); + Standard_Real xMax = Standard_Real(); + Standard_Real yMax = Standard_Real(); + Standard_Real zMax = Standard_Real(); box.Get(xMin, yMin, zMin, xMax, yMax, zMax); return zMax - zMin <= myTol; } @@ -164,7 +166,12 @@ public: if (!getBBox(eForBBox, bound)) { return false; } - Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + Standard_Real xMin = Standard_Real(); + Standard_Real yMin = Standard_Real(); + Standard_Real zMin = Standard_Real(); + Standard_Real xMax = Standard_Real(); + Standard_Real yMax = Standard_Real(); + Standard_Real zMax = Standard_Real(); bound.Get(xMin, yMin, zMin, xMax, yMax, zMax); box = Box(gp_Pnt(xMin,yMin,zMin), gp_Pnt(xMax,yMax,zMax)); return true; @@ -206,7 +213,11 @@ public: { curve = BRep_Tool::Curve(eForInfo, firstParam, lastParam); type = GeomAdaptor_Curve(curve).GetType(); - assertCheck(!curve.IsNull()); + + // Originally here there was a call to the precompiler macro assertCheck(), which has + // been replaced with the precompiler macro assert() + + assert(!curve.IsNull()); GeomLProp_CLProps prop(curve,(firstParam+lastParam)*0.5,0,Precision::Confusion()); mid = prop.Value(); @@ -445,7 +456,11 @@ public: if (sorted.size() == vertices.size()) { return; } - assertCheck(sorted.size() < vertices.size()); + + // Originally here there was a call to the precompiler macro assertCheck(), which has + // been replaced with the precompiler macro assert() + + assert(sorted.size() < vertices.size()); sorted.reserve(vertices.size()); for (int i=(int)sorted.size(); i<(int)vertices.size(); ++i) sorted.push_back(i); @@ -677,10 +692,13 @@ public: bool add(const TopoDS_Edge &eToAdd, bool queryBBox, const Box &bbox, Edges::iterator &it) { - gp_Pnt p1,p2; + gp_Pnt p1 = gp_Pnt(); + gp_Pnt p2 = gp_Pnt(); getEndPoints(eToAdd,p1,p2); - TopoDS_Vertex v1, v2; - TopoDS_Edge ev1, ev2; + TopoDS_Vertex v1 = TopoDS_Vertex(); + TopoDS_Vertex v2 = TopoDS_Vertex(); + TopoDS_Edge ev1 = TopoDS_Edge(); + TopoDS_Edge ev2 = TopoDS_Edge(); double tol = myTol2; // search for duplicate edges showShape(eToAdd, "addcheck"); @@ -756,7 +774,11 @@ public: auto newEdge = mkWire.Edge(); TopoDS_Vertex vFirst = TopExp::FirstVertex(newEdge); TopoDS_Vertex vLast = TopExp::LastVertex(newEdge); - assertCheck(vLast.IsSame(vOther) || vFirst.IsSame(vOther)); + + // Originally here there was a call to the precompiler macro assertCheck(), which has + // been replaced with the precompiler macro assert() + + assert(vLast.IsSame(vOther) || vFirst.IsSame(vOther)); eCurrent = newEdge; }; @@ -801,7 +823,8 @@ public: auto it = edges.begin(); BRepBuilderAPI_MakeWire mkWire; mkWire.Add(it->edge); - gp_Pnt pstart(it->p1),pend(it->p2); + gp_Pnt pstart(it->p1); + gp_Pnt pend(it->p2); remove(it); bool done = false; @@ -811,7 +834,11 @@ public: ret.reserve(1); const gp_Pnt &pt = idx==0?pstart:pend; vmap.query(bgi::nearest(pt,1),std::back_inserter(ret)); - assertCheck(ret.size()==1); + + // Originally here there was a call to the precompiler macro assertCheck(), + // which has been replaced with the precompiler macro assert() + + assert(ret.size() == 1); double d = ret[0].pt().SquareDistance(pt); if (d > tol) { break; @@ -895,7 +922,11 @@ public: TopoDS_Face face = mkFace.Face(); ShapeAnalysis_Wire analysis(wire, face, myTol); analysis.CheckSelfIntersectingEdge(1, points2d, points3d); - assertCheck(points2d.Length() == points3d.Length()); + + // Originally here there was a call to the precompiler macro assertCheck(), which has been + // replaced with the precompiler macro assert() + + assert(points2d.Length() == points3d.Length()); for (int i=1; i<=points2d.Length(); ++i) { params.emplace(points2d(i).ParamOnFirst(), points3d(i), info.edge); params.emplace(points2d(i).ParamOnSecond(), points3d(i), info.edge); @@ -924,7 +955,7 @@ public: } } for (int i=1; i<=extss.NbSolution(); ++i) { - Standard_Real p; + Standard_Real p = Standard_Real(); auto s1 = extss.SupportOnShape1(i); auto s2 = extss.SupportOnShape2(i); if (s1.ShapeType() == TopAbs_EDGE) { @@ -949,7 +980,7 @@ public: TColgp_SequenceOfPnt points3d; TColStd_SequenceOfReal errors; TopoDS_Wire wire; - int idx; + int idx = 0; BRepBuilderAPI_MakeWire mkWire(info.edge); mkWire.Add(other.edge); if (mkWire.IsDone()) { @@ -976,7 +1007,8 @@ public: } wire = mkWire.Wire(); if (!BRep_Tool::IsClosed(wire)) { - gp_Pnt p1, p2; + gp_Pnt p1 = gp_Pnt(); + gp_Pnt p2 = gp_Pnt(); getEndPoints(wire, p1, p2); BRepBuilderAPI_MakeEdge mkEdge(p1, p2); if (!mkEdge.IsDone()) { @@ -998,7 +1030,11 @@ public: TopoDS_Face face = mkFace.Face(); ShapeAnalysis_Wire analysis(wire, face, myTol); analysis.CheckIntersectingEdges(1, idx, points2d, points3d, errors); - assertCheck(points2d.Length() == points3d.Length()); + + // Originally here there was a call to the precompiler macro assertCheck(), which has been + // replaced with the precompiler macro assert() + + assert(points2d.Length() == points3d.Length()); for (int i=1; i<=points2d.Length(); ++i) { pushIntersection(params1, points2d(i).ParamOnFirst(), points3d(i), other.edge); pushIntersection(params2, points2d(i).ParamOnSecond(), points3d(i), info.edge); @@ -1062,7 +1098,7 @@ public: } i=0; - std::vector splitted; // NOLINT + std::vector split; for (auto it=edges.begin(); it!=edges.end(); ) { ++i; auto iter = intersects.find(&(*it)); @@ -1092,7 +1128,7 @@ public: continue; } - splitted.clear(); // NOLINT + split.clear(); itParam = params.begin(); for (auto itPrevParam=itParam++; itParam!=params.end(); ++itParam) { const auto &intersectShape = itParam->intersectShape.IsNull() @@ -1110,14 +1146,14 @@ public: BRepBuilderAPI_MakeEdge mkEdge(info.curve, param1, param2); if (mkEdge.IsDone()) { - splitted.emplace_back(); // NOLINT - auto& entry = splitted.back(); // NOLINT + split.emplace_back(); + auto& entry = split.back(); entry.edge = mkEdge.Edge(); entry.intersectShape = intersectShape; if (getBBox(entry.edge, entry.bbox)) itPrevParam = itParam; else - splitted.pop_back(); // NOLINT + split.pop_back(); } else if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("edge split failed " @@ -1126,7 +1162,7 @@ public: << ": " << mkEdge.Error()); } } - if (splitted.size() <= 1) { // NOLINT + if (split.size() <= 1) { ++it; continue; } @@ -1134,7 +1170,7 @@ public: showShape(info.edge, "remove"); auto removedEdge = info.edge; it = remove(it); - for (const auto& v : splitted) { // NOLINT + for (const auto& v : split) { if (!add(v.edge, false, v.bbox, it)) continue; auto &newInfo = *it++; @@ -1193,12 +1229,13 @@ public: if (found // more than one branch || edgeSet.contains(next)) // or, self intersect { - // if (found) { - // showShape(found, "branch_a", k); - // showShape(next, "branch_b", k); - // } else { - // showShape(next, "insect", k); - // } + + // Originally here there were some lines of code that have been removed + // as them are commented out. + // See + // https://github.com/realthunder/FreeCAD/blob/6f15849be2505f98927e75d0e8352185e14e7b72/src/Mod/Part/App/WireJoiner.cpp#L1141 + // for reference. + found = nullptr; break; } @@ -1278,9 +1315,10 @@ public: // Originally there was the following precompiler directive around assertCheck(): // #if OCC_VERSION_HEX >= 0x070000 - // Removed as the minimum OCCT version supported is 7.3.0 + // The precompiler directive has been removed as the minimum OCCT version supported + // is 7.3.0 and the precompiler macro has been replaced with assert() - assertCheck(BRep_Tool::IsClosed(info.shape())); + assert(BRep_Tool::IsClosed(info.shape())); showShape(&info,"closed"); if (!doTightBound) @@ -1402,6 +1440,10 @@ public: continue; if (tightBound) { + + // Originally here there was a call to the precompiler macro assertCheck(), which + // has been replaced with the precompiler macro assert() + assert(!beginInfo.wireInfo); beginInfo.wireInfo.reset(new WireInfo); beginInfo.wireInfo->vertices.emplace_back(it, true); @@ -1437,8 +1479,13 @@ public: int i = 0; for (auto &info : edges) { ++i; - if (auto w = info.wireInfo.get()) - assertCheck(w->vertices.front().edgeInfo()->wireInfo.get() == w); + if (auto w = info.wireInfo.get()) { + + // Originally here there was a call to the precompiler macro assertCheck(), which + // has been replaced with the precompiler macro assert() + + assert(w->vertices.front().edgeInfo()->wireInfo.get() == w); + } } } @@ -1632,7 +1679,11 @@ public: auto &info = *v.it; showShape(info.shape(v.start), v.start ? "failed" : "failed_r", iteration); } - assertCheck(false); + + // Originally here there was a call to the precompiler macro assertCheck(), which + // has been replaced with the precompiler macro assert() + + assert(false); continue; } return wire; @@ -1750,14 +1801,23 @@ public: if (idxEnd == 0) idxEnd = (int)wireVertices.size(); ++idxV; - assertCheck(idxV<=idxEnd); + + // Originally here there was a call to the precompiler macro assertCheck(), + // which has been replaced with the precompiler macro assert() + + assert(idxV <= idxEnd); int idxStart = idxV; for (int idx=idxV; idx!=idxEnd; ++idx) { auto info = wireVertices[idx].edgeInfo(); if (info == &beginInfo) { showShape(*wireInfo, "exception", iteration, true); showShape(info, "exception", iteration, true); - assertCheck(info != &beginInfo); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(info != &beginInfo); } if (info->wireInfo == wireInfo) { if (!splitWire) { @@ -1776,14 +1836,26 @@ public: if (first) { first = false; pstart = v.pt(); - } else - assertCheck(pt.SquareDistance(v.pt()) < myTol2); + } + else { + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler + // macro assert() + + assert(pt.SquareDistance(v.pt()) < myTol2); + } pt = v.ptOther(); splitEdges.push_back(v); } for (int i=stackPos; i>=stackStart; --i) { const auto &v = vertexStack[stack[i].iCurrent]; - assertCheck(pt.SquareDistance(v.ptOther()) < myTol2); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(pt.SquareDistance(v.ptOther()) < myTol2); pt = v.pt(); // The edges in the stack are the ones to slice // the wire in half. We construct a new wire @@ -1795,11 +1867,21 @@ public: } for (int idx=idxV; idx!=idxStart; ++idx) { auto &v = wireVertices[idx]; - assertCheck(pt.SquareDistance(v.pt()) < myTol2); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(pt.SquareDistance(v.pt()) < myTol2); pt = v.ptOther(); splitEdges.push_back(v); } - assertCheck(pt.SquareDistance(pstart) < myTol2); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(pt.SquareDistance(pstart) < myTol2); showShape(*splitWire, "swire", iteration); } @@ -1854,7 +1936,11 @@ public: tmpVertices.begin(), tmpVertices.end()); } } - assertCheck(info != &beginInfo); + + // Originally here there was a call to the precompiler macro assertCheck(), + // which has been replaced with the precompiler macro assert() + + assert(info != &beginInfo); info->wireInfo = beginInfo.wireInfo; checkWireInfo(*otherWire); } @@ -1908,7 +1994,11 @@ public: showShape(&info, "begin2", iteration); int idx = info.wireInfo->find(&info); - assertCheck(idx > 0); + + // Originally here there was a call to the precompiler macro assertCheck(), which has + // been replaced with the precompiler macro assert() + + assert(idx > 0); const auto &vertices = info.wireInfo->vertices; --idx; int nextIdx = idx == (int)vertices.size()-1 ? 0 : idx + 1; @@ -2085,7 +2175,12 @@ public: if (wireInfo && wireInfo->done) { for (auto &v : wireInfo->vertices) { auto edgeInfo = v.edgeInfo(); - assertCheck(edgeInfo->wireInfo != nullptr); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(edgeInfo->wireInfo != nullptr); if (edgeInfo->wireInfo->isSame(*wireInfo)) { wireInfo = edgeInfo->wireInfo; break; @@ -2096,8 +2191,13 @@ public: if (!edgeInfo->wireInfo2 && edgeInfo->wireInfo != wireInfo) edgeInfo->wireInfo2 = wireInfo; } - assertCheck(info.wireInfo2 == wireInfo); - assertCheck(info.wireInfo2 != info.wireInfo); + + // Originally here there were two calls to the precompiler macro + // assertCheck(), which have been replaced with the precompiler macro + // assert() + + assert(info.wireInfo2 == wireInfo); + assert(info.wireInfo2 != info.wireInfo); showShape(*wireInfo, "exhaust"); break; } From 6d9c49e4549de5359fa3ffc344100d0ec8f9346c Mon Sep 17 00:00:00 2001 From: CalligaroV Date: Tue, 19 Mar 2024 09:41:51 +0100 Subject: [PATCH 16/16] Part/Toponaming: Transfer WireJoiner * Applied modifications to reduce the number of Lint warnings (round 3) * Added note about the WireJoiner class in Mod/CAM/App/Area.cpp --- src/Mod/CAM/App/Area.cpp | 7 +- src/Mod/Part/App/WireJoiner.cpp | 2734 ++++++++++++++++++------------- src/Mod/Part/App/WireJoiner.h | 10 +- 3 files changed, 1649 insertions(+), 1102 deletions(-) diff --git a/src/Mod/CAM/App/Area.cpp b/src/Mod/CAM/App/Area.cpp index 291aef72e5..309c5ad84f 100644 --- a/src/Mod/CAM/App/Area.cpp +++ b/src/Mod/CAM/App/Area.cpp @@ -756,7 +756,12 @@ static inline void getEndPoints(const TopoDS_Wire& wire, gp_Pnt& p1, gp_Pnt& p2) p2 = BRep_Tool::Pnt(TopoDS::Vertex(xp.CurrentVertex())); } - +// Toponaming integration note: there's a new class called WireJoiner in Mod/Part/App/ that has been +// imported from RT's fork. Is's an improved version of the following struct, therefor +// probably at some point this struct should be replaced with the new imported class. +// See https://github.com/realthunder/FreeCAD/blob/LinkStable/src/Mod/Part/App/WireJoiner.h for the +// original implementation of the class and https://github.com/FreeCAD/FreeCAD/pull/12535 for the +// import conversation. struct WireJoiner { using Box = bg::model::box; diff --git a/src/Mod/Part/App/WireJoiner.cpp b/src/Mod/Part/App/WireJoiner.cpp index fbcf684688..42cc2a38e5 100644 --- a/src/Mod/Part/App/WireJoiner.cpp +++ b/src/Mod/Part/App/WireJoiner.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -77,7 +78,8 @@ namespace bg = boost::geometry; namespace bgi = boost::geometry::index; -using RParameters = bgi::linear<16>; +const size_t RParametersNumber = 16UL; +using RParameters = bgi::linear; BOOST_GEOMETRY_REGISTER_POINT_3D_GET_SET( gp_Pnt,double,bg::cs::cartesian,X,Y,Z,SetX,SetY,SetZ) @@ -116,7 +118,7 @@ public: bool doTightBound = true; std::string catchObject; - int catchIteration; + int catchIteration {}; int iteration = 0; using Box = bg::model::box; @@ -137,11 +139,14 @@ public: } WireJoinerP() - { - auto hParam = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/WireJoiner"); - catchObject = hParam->GetASCII("ObjectName"); - catchIteration = hParam->GetInt("Iteration", 0); - } + : catchObject(App::GetApplication() + .GetParameterGroupByPath("User parameter:BaseApp/Preferences/WireJoiner") + ->GetASCII("ObjectName")) + , catchIteration(static_cast( + App::GetApplication() + .GetParameterGroupByPath("User parameter:BaseApp/Preferences/WireJoiner") + ->GetInt("Iteration", 0))) + {} bool getBBox(const TopoDS_Shape &eForBBox, Bnd_Box &bound) { BRepBndLib::AddOptimal(eForBBox,bound,Standard_False); @@ -189,27 +194,32 @@ public: gp_Pnt p2; gp_Pnt mid; Box box; - int iStart[2]; // adjacent list index start for p1 and p2 - int iEnd[2]; // adjacent list index end - int iteration; - int iteration2; + std::array iStart {}; // adjacent list index start for p1 and p2 + std::array iEnd {}; // adjacent list index end + int iteration {}; + int iteration2 {}; bool queryBBox; - std::shared_ptr wireInfo; - std::shared_ptr wireInfo2; // an edge can be shared by at most two tight bound wires. - std::unique_ptr geo; - Standard_Real firstParam; - Standard_Real lastParam; + std::shared_ptr wireInfo {}; + std::shared_ptr wireInfo2 {}; // an edge can be shared by at most two tight bound wires. + std::unique_ptr geo {}; + Standard_Real firstParam {}; + Standard_Real lastParam {}; Handle_Geom_Curve curve; - GeomAbs_CurveType type; + GeomAbs_CurveType type {}; bool isLinear; - EdgeInfo(const TopoDS_Edge &eForInfo, - const gp_Pnt &pt1, - const gp_Pnt &pt2, - const Box &bound, + EdgeInfo(const TopoDS_Edge& eForInfo, + const gp_Pnt& pt1, + const gp_Pnt& pt2, + const Box& bound, bool bbox, bool isLinear) - :edge(eForInfo),p1(pt1),p2(pt2),box(bound),queryBBox(bbox),isLinear(isLinear) + : edge(eForInfo) + , p1(pt1) + , p2(pt2) + , box(bound) + , queryBBox(bbox) + , isLinear(isLinear) { curve = BRep_Tool::Curve(eForInfo, firstParam, lastParam); type = GeomAdaptor_Curve(curve).GetType(); @@ -218,15 +228,16 @@ public: // been replaced with the precompiler macro assert() assert(!curve.IsNull()); - GeomLProp_CLProps prop(curve,(firstParam+lastParam)*0.5,0,Precision::Confusion()); + const double halving {0.5}; + GeomLProp_CLProps prop(curve,(firstParam+lastParam)*halving,0,Precision::Confusion()); mid = prop.Value(); - iteration = 0; reset(); } Geometry *geometry() { - if (!geo) - geo = Geometry::fromShape(edge, /*silent*/true); + if (!geo) { + geo = Geometry::fromShape(edge, /*silent*/ true); + } return geo.get(); } void reset() { @@ -279,8 +290,10 @@ public: bool contains(const T &vForContains) { if (!sorted) { - if (data.size() < 30) + const size_t dataSizeMax = 30; + if (data.size() < dataSizeMax) { return std::find(data.begin(), data.end(), vForContains) != data.end(); + } sort(); } auto it = std::lower_bound(data.begin(), data.end(), vForContains); @@ -291,40 +304,46 @@ public: if (other.size() < size()) { return other.intersects(*this); } - else if (!sorted) { - for (const auto &v : data) { - if (other.contains(v)) + if (!sorted) { + for (const auto &vector : data) { + if (other.contains(vector)) { return true; + } } - } else { + } + else { other.sort(); auto it = other.data.begin(); - for (const auto &v : data) { - it = std::lower_bound(it, other.data.end(), v); - if (it == other.data.end()) + for (const auto &vertex : data) { + it = std::lower_bound(it, other.data.end(), vertex); + if (it == other.data.end()) { return false; - if (*it == v) + } + if (*it == vertex) { return true; + } } } return false; } void insert(const T &vToInsert) { - if (sorted) + if (sorted) { data.insert(std::upper_bound(data.begin(), data.end(), vToInsert), vToInsert); - else + } + else { data.push_back(vToInsert); + } } bool insertUnique(const T &vToInsertUnique) { if (sorted) { auto it = std::lower_bound(data.begin(), data.end(), vToInsertUnique); - if (it != data.end() && *it == vToInsertUnique) { - return false; + bool insert = !(it != data.end() && *it == vToInsertUnique); + if (insert) { + data.insert(it, vToInsertUnique); } - data.insert(it, vToInsertUnique); - return true; + return insert; } if (contains(vToInsertUnique)) { @@ -346,7 +365,8 @@ public: } data.erase(it, itEnd); } - if (data.size() < 20) { + const size_t dataSizeMax = 20; + if (data.size() < dataSizeMax) { sorted = false; } } @@ -365,26 +385,25 @@ public: } private: bool sorted = false; - std::vector data; + std::vector data {}; }; Handle(BRepTools_History) aHistory = new BRepTools_History; using Edges = std::list; - Edges edges; + Edges edges {}; - std::map edgesTable; + std::map edgesTable {}; struct VertexInfo { - Edges::iterator it; - bool start; - VertexInfo() - {} + Edges::iterator it {}; + bool start {}; + VertexInfo() = default; VertexInfo(Edges::iterator it, bool start) :it(it),start(start) {} VertexInfo reversed() const { - return VertexInfo(it, !start); + return {it, !start}; } bool operator==(const VertexInfo &other) const { return it==other.it && start==other.start; @@ -398,7 +417,7 @@ public: if (thisInfo > otherInfo) { return false; } - return start < other.start; + return static_cast(start) < static_cast(other.start); } const gp_Pnt &pt() const { return start?it->p1:it->p2; @@ -437,14 +456,14 @@ public: {} }; - std::vector stack; - std::vector vertexStack; - std::vector tmpVertices; - std::vector adjacentList; + std::vector stack {}; + std::vector vertexStack {}; + std::vector tmpVertices {}; + std::vector adjacentList {}; struct WireInfo { - std::vector vertices; - mutable std::vector sorted; + std::vector vertices {}; + mutable std::vector sorted {}; TopoDS_Wire wire; TopoDS_Face face; mutable Bnd_Box box; @@ -462,24 +481,26 @@ public: assert(sorted.size() < vertices.size()); sorted.reserve(vertices.size()); - for (int i=(int)sorted.size(); i<(int)vertices.size(); ++i) + for (int i = (int)sorted.size(); i < (int)vertices.size(); ++i) { sorted.push_back(i); - std::sort(sorted.begin(), sorted.end(), [&](int a, int b) { - return vertices[a] < vertices[b]; + } + std::sort(sorted.begin(), sorted.end(), [&](int vA, int vB) { + return vertices[vA] < vertices[vB]; }); } int find(const VertexInfo &info) const { - if (vertices.size() < 20) { + const size_t verticesSizeMax = 20; + if (vertices.size() < verticesSizeMax) { auto it = std::find(vertices.begin(), vertices.end(), info); if (it == vertices.end()) { return 0; } - return it - vertices.begin() + 1; + return (static_cast(it - vertices.begin()) + 1); } sort(); auto it = std::lower_bound(sorted.begin(), sorted.end(), info, - [&](int idx, const VertexInfo &v) {return vertices[idx]edgeInfo() == info) - return it - vertices.begin() + 1; + if (it->edgeInfo() == info) { + return (static_cast(it - vertices.begin()) + 1); + } } return 0; } sort(); auto it = std::lower_bound(sorted.begin(), sorted.end(), info, - [&](int idx, const EdgeInfo *v) {return vertices[idx].edgeInfo()shape(),wireInfo.box); + for (auto& vertex : wireInfo.vertices) { + BRepBndLib::Add(vertex.it->shape(), wireInfo.box); + } wireInfo.box.Enlarge(myTol); } return wireInfo.box; } + // This method was originally part of WireJoinerP::initWireInfo(), split to reduce cognitive + // complexity + bool initWireInfoWireClosed(const WireInfo& wireInfo) + { + if (!BRep_Tool::IsClosed(wireInfo.wire)) { + showShape(wireInfo.wire, "FailedToClose"); + FC_ERR("Wire not closed"); + for (auto& vertex : wireInfo.vertices) { + showShape(vertex.edgeInfo(), vertex.start ? "failed" : "failed_r", iteration); + } + return false; + } + return true; + } + + // This method was originally part of WireJoinerP::initWireInfo(), split to reduce cognitive + // complexity + bool initWireInfoFaceDone(WireInfo& wireInfo) + { + BRepBuilderAPI_MakeFace mkFace(wireInfo.wire); + if (!mkFace.IsDone()) { + FC_ERR("Failed to create face for wire"); + showShape(wireInfo.wire, "FailedFace"); + return false; + } + wireInfo.face = mkFace.Face(); + return true; + } + bool initWireInfo(WireInfo &wireInfo) { if (!wireInfo.face.IsNull()) { @@ -555,27 +610,20 @@ public: getWireBound(wireInfo); if (wireInfo.wire.IsNull()) { wireData->Clear(); - for (auto &v : wireInfo.vertices) - wireData->Add(v.it->shape(v.start)); + for (auto& vertex : wireInfo.vertices) { + wireData->Add(vertex.it->shape(vertex.start)); + } wireInfo.wire = makeCleanWire(); } - if (!BRep_Tool::IsClosed(wireInfo.wire)) { - showShape(wireInfo.wire, "FailedToClose"); - FC_ERR("Wire not closed"); - for (auto &v : wireInfo.vertices) { - showShape(v.edgeInfo(), v.start ? "failed" : "failed_r", iteration); - } + if (!initWireInfoWireClosed(wireInfo)) { return false; } - BRepBuilderAPI_MakeFace mkFace(wireInfo.wire); - if (!mkFace.IsDone()) { - FC_ERR("Failed to create face for wire"); - showShape(wireInfo.wire, "FailedFace"); + if (!initWireInfoFaceDone(wireInfo)) { return false; } - wireInfo.face = mkFace.Face(); + return true; } @@ -605,7 +653,7 @@ public: } }; - bgi::rtree vmap; + bgi::rtree vmap {}; struct BoxGetter { @@ -614,13 +662,13 @@ public: return it->box; } }; - bgi::rtree boxMap; + bgi::rtree boxMap {}; BRep_Builder builder; TopoDS_Compound compound; - std::unordered_set sourceEdges; - std::vector sourceEdgeArray; + std::unordered_set sourceEdges {}; + std::vector sourceEdgeArray {}; TopoDS_Compound openWireCompound; Handle(ShapeExtend_WireData) wireData = new ShapeExtend_WireData(); @@ -644,8 +692,9 @@ public: Edges::iterator remove(Edges::iterator it) { - if (it->queryBBox) + if (it->queryBBox) { boxMap.remove(it); + } vmap.remove(VertexInfo(it,true)); vmap.remove(VertexInfo(it,false)); return edges.erase(it); @@ -654,8 +703,9 @@ public: void remove(EdgeInfo *info) { if (edgesTable.empty()) { - for (auto it=edges.begin(); it!=edges.end(); ++it) + for (auto it = edges.begin(); it != edges.end(); ++it) { edgesTable[&(*it)] = it; + } } auto it = edgesTable.find(info); if (it != edgesTable.end()) { @@ -668,8 +718,9 @@ public: { vmap.insert(VertexInfo(it,true)); vmap.insert(VertexInfo(it,false)); - if (it->queryBBox) + if (it->queryBBox) { boxMap.insert(it); + } showShape(it->edge, "add"); } @@ -690,6 +741,71 @@ public: return add(eToAdd, queryBBox, bbox, it) ? 1 : -1; } + // This method was originally part of WireJoinerP::add(), split to reduce cognitive complexity + bool addNoDuplicates(const TopoDS_Edge& eToAdd, + TopoDS_Vertex& v2, + TopoDS_Edge& ev2, + const bool isLinear, + const VertexInfo& vinfo, + std::unique_ptr& geo) + { + if (v2.IsNull()) { + ev2 = vinfo.edge(); + v2 = vinfo.otherVertex(); + } + if (isLinear && vinfo.edgeInfo()->isLinear) { + showShape(eToAdd, "duplicate"); + aHistory->Remove(eToAdd); + return false; + } + if (auto geoEdge = vinfo.edgeInfo()->geometry()) { + if (!geo) { + geo = Geometry::fromShape(eToAdd, /*silent*/ true); + } + if (geo && geo->isSame(*geoEdge, myTol, myAngularTol)) { + showShape(eToAdd, "duplicate"); + aHistory->Remove(eToAdd); + return false; + } + } + return true; + } + + // This method was originally part of WireJoinerP::add(), split to reduce cognitive complexity + bool addValidEdges(const TopoDS_Edge& eToAdd, + const gp_Pnt p1, + const double tol, + TopoDS_Vertex& v1, + TopoDS_Edge& ev1, + const gp_Pnt p2, + TopoDS_Vertex& v2, + TopoDS_Edge& ev2, + const bool isLinear) + { + std::unique_ptr geo; + for (auto vit = vmap.qbegin(bgi::nearest(p1, INT_MAX)); vit != vmap.qend(); ++vit) { + auto& vinfo = *vit; + if (canShowShape()) { + FC_MSG("addcheck " << vinfo.edge().HashCode(INT_MAX)); + } + double d1 = vinfo.pt().SquareDistance(p1); + if (d1 >= tol) { + break; + } + if (v1.IsNull()) { + ev1 = vinfo.edge(); + v1 = vinfo.vertex(); + } + double d2 = vinfo.ptOther().SquareDistance(p2); + if (d2 < tol) { + if (!addNoDuplicates(eToAdd, v2, ev2, isLinear, vinfo, geo)){ + return false; + } + } + } + return true; + } + bool add(const TopoDS_Edge &eToAdd, bool queryBBox, const Box &bbox, Edges::iterator &it) { gp_Pnt p1 = gp_Pnt(); @@ -703,41 +819,11 @@ public: // search for duplicate edges showShape(eToAdd, "addcheck"); bool isLinear = TopoShape(eToAdd).isLinearEdge(); - std::unique_ptr geo; - for (auto vit=vmap.qbegin(bgi::nearest(p1,INT_MAX));vit!=vmap.qend();++vit) { - auto &vinfo = *vit; - if (canShowShape()) - FC_MSG("addcheck " << vinfo.edge().HashCode(INT_MAX)); - double d1 = vinfo.pt().SquareDistance(p1); - if (d1 >= tol) - break; - if (v1.IsNull()) { - ev1 = vinfo.edge(); - v1 = vinfo.vertex(); - } - double d2 = vinfo.ptOther().SquareDistance(p2); - if (d2 < tol) { - if (v2.IsNull()) { - ev2 = vinfo.edge(); - v2 = vinfo.otherVertex(); - } - if (isLinear && vinfo.edgeInfo()->isLinear) { - showShape(eToAdd, "duplicate"); - aHistory->Remove(eToAdd); - return false; - } - else if (auto geoEdge = vinfo.edgeInfo()->geometry()) { - if (!geo) - geo = Geometry::fromShape(eToAdd, /*silent*/true); - if (geo && geo->isSame(*geoEdge, myTol, myAngularTol)) { - showShape(eToAdd, "duplicate"); - aHistory->Remove(eToAdd); - return false; - } - } - } + if (!addValidEdges(eToAdd, p1, tol, v1, ev1, p2, v2, ev2, isLinear)){ + return false; } + if (v2.IsNull()) { for (auto vit=vmap.qbegin(bgi::nearest(p2,1));vit!=vmap.qend();++vit) { auto &vinfo = *vit; @@ -767,7 +853,8 @@ public: BRep_Tool::Tolerance(vOther)); if (tol >= BRep_Tool::Tolerance(vCurrent)) { ShapeFix_ShapeTolerance fix; - fix.SetTolerance(vCurrent, std::max(tol*0.5, myTol), TopAbs_VERTEX); + const double halving {0.5}; + fix.SetTolerance(vCurrent, std::max(tol*halving, myTol), TopAbs_VERTEX); } BRepBuilderAPI_MakeWire mkWire(eOther); mkWire.Add(eCurrent); @@ -810,6 +897,67 @@ public: } } + // This method was originally part of WireJoinerP::join(), split to reduce cognitive complexity + void joinMakeWire(const int idx, + BRepBuilderAPI_MakeWire& mkWire, + const Edges::iterator it, + bool& done) + { + double tol = myTol2; + gp_Pnt pstart(it->p1); + gp_Pnt pend(it->p2); + while (!edges.empty()) { + std::vector ret; + ret.reserve(1); + const gp_Pnt& pt = idx == 0 ? pstart : pend; + vmap.query(bgi::nearest(pt, 1), std::back_inserter(ret)); + + // Originally here there was a call to the precompiler macro assertCheck(), + // which has been replaced with the precompiler macro assert() + + assert(ret.size() == 1); + double dist = ret[0].pt().SquareDistance(pt); + if (dist > tol) { + break; + } + + const auto& info = *ret[0].it; + bool start = ret[0].start; + if (dist > Precision::SquareConfusion()) { + // insert a filling edge to solve the tolerance problem + const gp_Pnt& pt = ret[idx].pt(); + if (idx != 0) { + mkWire.Add(BRepBuilderAPI_MakeEdge(pend, pt).Edge()); + } + else { + mkWire.Add(BRepBuilderAPI_MakeEdge(pt, pstart).Edge()); + } + } + + if (idx == 1 && start) { + pend = info.p2; + mkWire.Add(info.edge); + } + else if (idx == 0 && !start) { + pstart = info.p1; + mkWire.Add(info.edge); + } + else if (idx == 0 && start) { + pstart = info.p2; + mkWire.Add(TopoDS::Edge(info.edge.Reversed())); + } + else { + pend = info.p1; + mkWire.Add(TopoDS::Edge(info.edge.Reversed())); + } + remove(ret[0].it); + if (pstart.SquareDistance(pend) <= Precision::SquareConfusion()) { + done = true; + break; + } + } + } + //This algorithm tries to join connected edges into wires // //tol*tol>Precision::SquareConfusion() can be used to join points that are @@ -818,65 +966,17 @@ public: // void join() { - double tol = myTol2; - while (edges.size()) { + while (!edges.empty()) { auto it = edges.begin(); BRepBuilderAPI_MakeWire mkWire; mkWire.Add(it->edge); - gp_Pnt pstart(it->p1); - gp_Pnt pend(it->p2); remove(it); bool done = false; for (int idx=0;!done&&idx<2;++idx) { - while (edges.size()) { - std::vector ret; - ret.reserve(1); - const gp_Pnt &pt = idx==0?pstart:pend; - vmap.query(bgi::nearest(pt,1),std::back_inserter(ret)); - - // Originally here there was a call to the precompiler macro assertCheck(), - // which has been replaced with the precompiler macro assert() - - assert(ret.size() == 1); - double d = ret[0].pt().SquareDistance(pt); - if (d > tol) { - break; - } - - const auto &info = *ret[0].it; - bool start = ret[0].start; - if (d > Precision::SquareConfusion()) { - // insert a filling edge to solve the tolerance problem - const gp_Pnt &pt = ret[idx].pt(); - if (idx) { - mkWire.Add(BRepBuilderAPI_MakeEdge(pend, pt).Edge()); - } - else { - mkWire.Add(BRepBuilderAPI_MakeEdge(pt, pstart).Edge()); - } - } - - if (idx==1 && start) { - pend = info.p2; - mkWire.Add(info.edge); - }else if (idx==0 && !start) { - pstart = info.p1; - mkWire.Add(info.edge); - }else if (idx==0 && start) { - pstart = info.p2; - mkWire.Add(TopoDS::Edge(info.edge.Reversed())); - }else { - pend = info.p1; - mkWire.Add(TopoDS::Edge(info.edge.Reversed())); - } - remove(ret[0].it); - if (pstart.SquareDistance(pend)<=Precision::SquareConfusion()){ - done = true; - break; - } - } + joinMakeWire(idx, mkWire, it, done); } + builder.Add(compound,mkWire.Wire()); } } @@ -885,15 +985,17 @@ public: double param; TopoDS_Shape intersectShape; gp_Pnt point; - IntersectInfo(double pToIntersect, const gp_Pnt &pt, const TopoDS_Shape &sToIntersect) - :param(pToIntersect), intersectShape(sToIntersect), point(pt) + IntersectInfo(double pToIntersect, const gp_Pnt& pt, TopoDS_Shape sToIntersect) + : param(pToIntersect) + , intersectShape(std::move(sToIntersect)) + , point(pt) {} bool operator<(const IntersectInfo &other) const { return param < other.param; } }; - void checkSelfIntersection(const EdgeInfo &info, std::set ¶ms) + void checkSelfIntersection(const EdgeInfo &info, std::set ¶ms) const { // Early return if checking for self intersection (only for non linear spline curves) if (info.type <= GeomAbs_Parabola || info.isLinear) { @@ -919,7 +1021,7 @@ public: if (!mkFace.IsDone()) { return; } - TopoDS_Face face = mkFace.Face(); + const TopoDS_Face& face = mkFace.Face(); ShapeAnalysis_Wire analysis(wire, face, myTol); analysis.CheckSelfIntersectingEdge(1, points2d, points3d); @@ -933,10 +1035,12 @@ public: } } - void checkIntersection(const EdgeInfo &info, - const EdgeInfo &other, - std::set ¶ms1, - std::set ¶ms2) + // This method was originally part of WireJoinerP::checkIntersection(), split to reduce + // cognitive complexity + bool checkIntersectionPlanar(const EdgeInfo& info, + const EdgeInfo& other, + std::set& params1, + std::set& params2) { gp_Pln pln; bool planar = TopoShape(info.edge).findPlane(pln); @@ -951,25 +1055,121 @@ public: extss.Perform(); if (extss.IsDone() && extss.NbSolution() > 0) { if (!extss.IsDone() || extss.NbSolution() <= 0 || extss.Value() >= myTol) { - return; + return false; } } - for (int i=1; i<=extss.NbSolution(); ++i) { - Standard_Real p = Standard_Real(); + for (int i = 1; i <= extss.NbSolution(); ++i) { + Standard_Real par = Standard_Real(); auto s1 = extss.SupportOnShape1(i); auto s2 = extss.SupportOnShape2(i); if (s1.ShapeType() == TopAbs_EDGE) { - extss.ParOnEdgeS1(i,p); - pushIntersection(params1, p, extss.PointOnShape1(i), other.edge); + extss.ParOnEdgeS1(i, par); + pushIntersection(params1, par, extss.PointOnShape1(i), other.edge); } if (s2.ShapeType() == TopAbs_EDGE) { - extss.ParOnEdgeS2(i,p); - pushIntersection(params2, p, extss.PointOnShape2(i), info.edge); + extss.ParOnEdgeS2(i, par); + pushIntersection(params2, par, extss.PointOnShape2(i), info.edge); } } - return; + return false; } } + + return true; + } + + // This method was originally part of WireJoinerP::checkIntersection(), split to reduce + // cognitive complexity + static bool checkIntersectionEdgeDone(const BRepBuilderAPI_MakeEdge& mkEdge) + { + if (!mkEdge.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Failed to build edge for checking intersection"); + } + return false; + } + return true; + } + + // This method was originally part of WireJoinerP::checkIntersection(), split to reduce + // cognitive complexity + static bool checkIntersectionWireDone(const BRepBuilderAPI_MakeWire& mkWire) + { + if (!mkWire.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Failed to build wire for checking intersection"); + } + return false; + } + return true; + } + + // This method was originally part of WireJoinerP::checkIntersection(), split to reduce + // cognitive complexity + static bool checkIntersectionMakeWire(const EdgeInfo& info, + const EdgeInfo& other, + int& idx, + TopoDS_Wire& wire) + { + BRepBuilderAPI_MakeWire mkWire(info.edge); + mkWire.Add(other.edge); + if (mkWire.IsDone()) { + idx = 2; + } + else if (mkWire.Error() == BRepBuilderAPI_DisconnectedWire) { + idx = 3; + BRepBuilderAPI_MakeEdge mkEdge(info.p1, other.p1); + + if (!checkIntersectionEdgeDone(mkEdge)) { + return false; + } + + mkWire.Add(mkEdge.Edge()); + mkWire.Add(other.edge); + } + + if (!checkIntersectionWireDone(mkWire)) { + return false; + } + + wire = mkWire.Wire(); + if (!BRep_Tool::IsClosed(wire)) { + gp_Pnt p1 = gp_Pnt(); + gp_Pnt p2 = gp_Pnt(); + getEndPoints(wire, p1, p2); + BRepBuilderAPI_MakeEdge mkEdge(p1, p2); + + if (!checkIntersectionEdgeDone(mkEdge)) { + return false; + } + + mkWire.Add(mkEdge.Edge()); + } + return true; + } + + // This method was originally part of WireJoinerP::checkIntersection(), split to reduce + // cognitive complexity + static bool checkIntersectionFaceDone(const BRepBuilderAPI_MakeFace& mkFace) + { + if (!mkFace.IsDone()) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Failed to build face for checking intersection"); + } + return false; + } + return true; + } + + void checkIntersection(const EdgeInfo &info, + const EdgeInfo &other, + std::set ¶ms1, + std::set ¶ms2) + { + if(!checkIntersectionPlanar(info, other, params1, params2)){ + return; + } + // BRepExtrema_DistShapeShape has trouble finding all solutions for a // spline curve. ShapeAnalysis_Wire is better. Besides, it can check // for self intersection. It's slightly more troublesome to use, as it @@ -981,53 +1181,17 @@ public: TColStd_SequenceOfReal errors; TopoDS_Wire wire; int idx = 0; - BRepBuilderAPI_MakeWire mkWire(info.edge); - mkWire.Add(other.edge); - if (mkWire.IsDone()) { - idx = 2; - } - else if (mkWire.Error() == BRepBuilderAPI_DisconnectedWire) { - idx = 3; - BRepBuilderAPI_MakeEdge mkEdge(info.p1, other.p1); - if (!mkEdge.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Failed to build edge for checking intersection"); - } - return; - } - mkWire.Add(mkEdge.Edge()); - mkWire.Add(other.edge); - } - if (!mkWire.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Failed to build wire for checking intersection"); - } + if (!checkIntersectionMakeWire(info, other, idx, wire)){ return; } - wire = mkWire.Wire(); - if (!BRep_Tool::IsClosed(wire)) { - gp_Pnt p1 = gp_Pnt(); - gp_Pnt p2 = gp_Pnt(); - getEndPoints(wire, p1, p2); - BRepBuilderAPI_MakeEdge mkEdge(p1, p2); - if (!mkEdge.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Failed to build edge for checking intersection"); - } - return; - } - mkWire.Add(mkEdge.Edge()); - } BRepBuilderAPI_MakeFace mkFace(wire); - if (!mkFace.IsDone()) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Failed to build face for checking intersection"); - } + if (!checkIntersectionFaceDone(mkFace)) { return; } - TopoDS_Face face = mkFace.Face(); + + const TopoDS_Face& face = mkFace.Face(); ShapeAnalysis_Wire analysis(wire, face, myTol); analysis.CheckIntersectingEdges(1, idx, points2d, points3d, errors); @@ -1041,7 +1205,10 @@ public: } } - void pushIntersection(std::set ¶ms, double param, const gp_Pnt &pt, const TopoDS_Shape &shape) + void pushIntersection(std::set& params, + double param, + const gp_Pnt& pt, + const TopoDS_Shape& shape) { IntersectInfo info(param, pt, shape); auto it = params.upper_bound(info); @@ -1058,7 +1225,6 @@ public: } } params.insert(it, info); - return; } struct SplitInfo { @@ -1067,29 +1233,83 @@ public: Box bbox; }; + // This method was originally part of WireJoinerP::splitEdges(), split to reduce cognitive + // complexity + void splitEdgesMakeEdge(const std::set::iterator& itParam, + const EdgeInfo& info, + std::vector& splits, + std::set::iterator& itPrevParam, + const TopoDS_Shape& intersectShape) + { + // Using points cause MakeEdge failure for some reason. Using + // parameters is better. + // + const gp_Pnt& p1 = itPrevParam->point; + const gp_Pnt& p2 = itParam->point; + const Standard_Real& param1 = itPrevParam->param; + const Standard_Real& param2 = itParam->param; + + BRepBuilderAPI_MakeEdge mkEdge(info.curve, param1, param2); + if (mkEdge.IsDone()) { + splits.emplace_back(); + auto& entry = splits.back(); + entry.edge = mkEdge.Edge(); + entry.intersectShape = intersectShape; + if (getBBox(entry.edge, entry.bbox)) { + itPrevParam = itParam; + } + else { + splits.pop_back(); + } + } + else if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("edge split failed " << std::setprecision(16) << FC_XYZ(p1) << FC_XYZ(p2) + << ": " << mkEdge.Error()); + } + } + + // This method was originally part of WireJoinerP::splitEdges(), split to reduce cognitive + // complexity + void splitEdgesMakeEdges(std::set::iterator& itParam, + const std::set& params, + const EdgeInfo& info, + std::vector& splits) + { + for (auto itPrevParam = itParam++; itParam != params.end(); ++itParam) { + const auto& intersectShape = itParam->intersectShape.IsNull() + ? itPrevParam->intersectShape + : itParam->intersectShape; + if (intersectShape.IsNull()) { + break; + } + + splitEdgesMakeEdge(itParam, info, splits, itPrevParam, intersectShape); + } + } + // Try splitting any edges that intersects other edge void splitEdges() { std::unordered_map> intersects; - int i=0; - for (auto &info : edges) - info.iteration = ++i; + int idx=0; + for (auto& info : edges) { + info.iteration = ++idx; + } std::unique_ptr seq( new Base::SequencerLauncher("Splitting edges", edges.size())); - i = 0; - for (auto it=edges.begin(); it!=edges.end();++it) { + idx = 0; + for (auto& info : edges) { seq->next(true); - ++i; - auto &info = *it; + ++idx; auto ¶ms = intersects[&info]; checkSelfIntersection(info, params); for (auto vit=boxMap.qbegin(bgi::intersects(info.box)); vit!=boxMap.qend(); ++vit) { const auto &other = *(*vit); - if (other.iteration <= i) { + if (other.iteration <= idx) { // means the edge is before us, and we've already checked intersection continue; } @@ -1097,10 +1317,10 @@ public: } } - i=0; - std::vector split; + idx=0; + std::vector splits; for (auto it=edges.begin(); it!=edges.end(); ) { - ++i; + ++idx; auto iter = intersects.find(&(*it)); if (iter == intersects.end()) { ++it; @@ -1114,13 +1334,15 @@ public: } auto itParam = params.begin(); - if (itParam->point.SquareDistance(info.p1) < myTol2) + if (itParam->point.SquareDistance(info.p1) < myTol2) { params.erase(itParam); + } params.emplace(info.firstParam, info.p1, TopoDS_Shape()); itParam = params.end(); --itParam; - if (itParam->point.SquareDistance(info.p2) < myTol2) + if (itParam->point.SquareDistance(info.p2) < myTol2) { params.erase(itParam); + } params.emplace(info.lastParam, info.p2, TopoDS_Shape()); if (params.size() <= 2) { @@ -1128,41 +1350,12 @@ public: continue; } - split.clear(); + splits.clear(); itParam = params.begin(); - for (auto itPrevParam=itParam++; itParam!=params.end(); ++itParam) { - const auto &intersectShape = itParam->intersectShape.IsNull() - ? itPrevParam->intersectShape : itParam->intersectShape; - if (intersectShape.IsNull()) - break; - // Using points cause MakeEdge failure for some reason. Using - // parameters is better. - // - const gp_Pnt &p1 = itPrevParam->point; - const gp_Pnt &p2 = itParam->point; - const Standard_Real ¶m1 = itPrevParam->param; - const Standard_Real ¶m2 = itParam->param; + splitEdgesMakeEdges(itParam, params, info, splits); - BRepBuilderAPI_MakeEdge mkEdge(info.curve, param1, param2); - if (mkEdge.IsDone()) { - split.emplace_back(); - auto& entry = split.back(); - entry.edge = mkEdge.Edge(); - entry.intersectShape = intersectShape; - if (getBBox(entry.edge, entry.bbox)) - itPrevParam = itParam; - else - split.pop_back(); - } - else if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("edge split failed " - << std::setprecision(16) - << FC_XYZ(p1) << FC_XYZ(p2) - << ": " << mkEdge.Error()); - } - } - if (split.size() <= 1) { + if (splits.size() <= 1) { ++it; continue; } @@ -1170,11 +1363,12 @@ public: showShape(info.edge, "remove"); auto removedEdge = info.edge; it = remove(it); - for (const auto& v : split) { - if (!add(v.edge, false, v.bbox, it)) + for (const auto& split : splits) { + if (!add(split.edge, false, split.bbox, it)) { continue; + } auto &newInfo = *it++; - aHistory->AddModified(v.intersectShape, newInfo.edge); + aHistory->AddModified(split.intersectShape, newInfo.edge); // if (v.intersectShape != removedEdge) // aHistory->AddModified(removedEdge, newInfo.edge); showShape(newInfo.edge, "split"); @@ -1182,10 +1376,122 @@ public: } } + // This method was originally part of WireJoinerP::findSuperEdges(), split to reduce cognitive + // complexity + void findSuperEdgeFromAdjacent(std::deque& vertices, const int direction) + { + bool done = false; + auto begin = direction == 1 ? vertices.back().reversed() : vertices.front(); + while (true) { + auto currentVertex = direction == 1 ? vertices.front() : vertices.back(); + auto current = currentVertex.edgeInfo(); + // showShape(current, "siter", k); + const int idx = (currentVertex.start ? 1 : 0) ^ direction; + EdgeInfo* found = nullptr; + for (int i = current->iStart[idx]; i < current->iEnd[idx]; ++i) { + const auto& vertex = adjacentList[i]; + auto next = vertex.edgeInfo(); + if (next->iteration < 0 // skipped + || next == current) { // skip self (see how adjacent list is built) + continue; + } + if (vertex == begin) { + // closed + done = true; + break; + } + if (found // more than one branch + || edgeSet.contains(next)) // or, self intersect + { + + // Originally here there were some lines of code that have been removed + // as them are commented out. + // See + // https://github.com/realthunder/FreeCAD/blob/6f15849be2505f98927e75d0e8352185e14e7b72/src/Mod/Part/App/WireJoiner.cpp#L1141 + // for reference. + + found = nullptr; + break; + } + found = next; + currentVertex = vertex; + } + if (done || !found) { + break; + } + // showShape(found, "snext", k); + if (direction == 1) { + edgeSet.insert(current); + vertices.push_front(currentVertex.reversed()); + } + else { + edgeSet.insert(found); + vertices.push_back(currentVertex); + } + } + } + + // This method was originally part of WireJoinerP::findSuperEdges(), split to reduce cognitive + // complexity + void findSuperEdge(std::deque& vertices, const Edges::iterator it) + { + vertices.clear(); + vertices.emplace_back(it, true); + edgeSet.clear(); + + for (int direction = 0; direction < 2; ++direction) { // search in both direction + findSuperEdgeFromAdjacent(vertices, direction); + } + } + + // This method was originally part of WireJoinerP::findSuperEdges(), split to reduce cognitive + // complexity + void findSuperEdgesUpdateFirst(std::deque vertices) + { + Bnd_Box bbox; + for (const auto& vertex : vertices) { + auto current = vertex.edgeInfo(); + bbox.Add(current->box.min_corner()); + bbox.Add(current->box.max_corner()); + wireData->Add(current->shape(vertex.start)); + showShape(current, "edge"); + current->iteration = -1; + } + + auto first = vertices.front().edgeInfo(); + first->superEdge = makeCleanWire(false); + first->superEdgeReversed.Nullify(); + if (BRep_Tool::IsClosed(first->superEdge)) { + first->iteration = -2; + showShape(first, "super_done"); + } + else { + first->iteration = iteration; + showShape(first, "super"); + auto& vFirst = vertices.front(); + auto& vLast = vertices.back(); + auto last = vLast.edgeInfo(); + vFirst.ptOther() = vLast.ptOther(); + const int idx = vFirst.start ? 1 : 0; + first->iStart[idx] = last->iStart[vLast.start ? 1 : 0]; + first->iEnd[idx] = last->iEnd[vLast.start ? 1 : 0]; + + for (int i = first->iStart[idx]; i < first->iEnd[idx]; ++i) { + auto& vertex = adjacentList[i]; + if (vertex.it == vLast.it) { + vertex.it = vFirst.it; + vertex.start = !vFirst.start; + } + } + bbox.Enlarge(myTol); + first->box = Box(bbox.CornerMin(), bbox.CornerMax()); + } + } + void findSuperEdges() { std::unique_ptr seq( - new Base::SequencerLauncher("Combining edges", edges.size())); + new Base::SequencerLauncher("Combining edges", edges.size())); std::deque vertices; @@ -1194,123 +1500,31 @@ public: // Join edges (let's call it super edge) that are connected to only one // other edges (count == 2 counts this and the other edge) on one of // its vertices to save traverse time. - for (auto it=edges.begin(); it!=edges.end(); ++it) { + for (auto it = edges.begin(); it != edges.end(); ++it) { seq->next(true); - auto &info = *it; - if (info.iteration == iteration || info.iteration < 0) + auto& info = *it; + if (info.iteration == iteration || info.iteration < 0) { continue; + } info.iteration = iteration; // showShape(&info, "scheck"); - vertices.clear(); - vertices.emplace_back(it, true); - edgeSet.clear(); + findSuperEdge(vertices, it); - bool done = false; - for (int k=0; k<2; ++k) { // search in both direction - auto begin = k==1 ? vertices.back().reversed() : vertices.front(); - while (true) { - auto currentVertex = k==1 ? vertices.front() : vertices.back(); - auto current = currentVertex.edgeInfo(); - // showShape(current, "siter", k); - int idx = (currentVertex.start?1:0)^k; - EdgeInfo *found = nullptr; - for (int i=current->iStart[idx]; iiEnd[idx]; ++i) { - const auto &v = adjacentList[i]; - auto next = v.edgeInfo(); - if (next->iteration < 0 // skipped - || next == current) // skip self (see how adjacent list is built) - continue; - if (v == begin) { - // closed - done = true; - break; - } - if (found // more than one branch - || edgeSet.contains(next)) // or, self intersect - { - - // Originally here there were some lines of code that have been removed - // as them are commented out. - // See - // https://github.com/realthunder/FreeCAD/blob/6f15849be2505f98927e75d0e8352185e14e7b72/src/Mod/Part/App/WireJoiner.cpp#L1141 - // for reference. - - found = nullptr; - break; - } - found = next; - currentVertex = v; - } - if (done || !found) - break; - // showShape(found, "snext", k); - if (k==1) { - edgeSet.insert(current); - vertices.push_front(currentVertex.reversed()); - } else { - edgeSet.insert(found); - vertices.push_back(currentVertex); - } - } - if (done) - break; - } - - if (vertices.size() <= 1) + if (vertices.size() <= 1) { continue; + } wireData->Clear(); - Bnd_Box bbox; - for (const auto &v : vertices) { - auto current = v.edgeInfo(); - bbox.Add(current->box.min_corner()); - bbox.Add(current->box.max_corner()); - wireData->Add(current->shape(v.start)); - showShape(current, "edge"); - current->iteration = -1; - } - auto first = vertices.front().edgeInfo(); - first->superEdge = makeCleanWire(false); - first->superEdgeReversed.Nullify(); - if (BRep_Tool::IsClosed(first->superEdge)) { - first->iteration = -2; - showShape(first, "super_done"); - } else { - first->iteration = iteration; - showShape(first, "super"); - auto &vFirst = vertices.front(); - auto &vLast = vertices.back(); - auto last = vLast.edgeInfo(); - vFirst.ptOther() = vLast.ptOther(); - int idx = vFirst.start ? 1 : 0; - first->iStart[idx] = last->iStart[vLast.start?1:0]; - first->iEnd[idx] = last->iEnd[vLast.start?1:0]; - for (int i=first->iStart[idx];iiEnd[idx];++i) { - auto &v = adjacentList[i]; - if (v.it == vLast.it) { - v.it = vFirst.it; - v.start = !vFirst.start; - } - } - bbox.Enlarge(myTol); - first->box = Box(bbox.CornerMin(), bbox.CornerMax()); - } + findSuperEdgesUpdateFirst(vertices); } } - void buildAdjacentList() + void buildAdjacentListPopulate() { - builder.MakeCompound(compound); - - for (auto &info : edges) - info.reset(); - - adjacentList.clear(); - // populate adjacent list - for (auto &info : edges) { + for (auto& info : edges) { if (info.iteration == -2) { // Originally there was the following precompiler directive around assertCheck(): @@ -1320,32 +1534,41 @@ public: assert(BRep_Tool::IsClosed(info.shape())); - showShape(&info,"closed"); - if (!doTightBound) - builder.Add(compound,info.wire()); - continue; - } else if (info.iteration < 0) + showShape(&info, "closed"); + if (!doTightBound) { + builder.Add(compound, info.wire()); + } continue; + } - if (info.p1.SquareDistance(info.p2)<=myTol2) { - if (!doTightBound) - builder.Add(compound,info.wire()); + if (info.iteration < 0) { + continue; + } + + if (info.p1.SquareDistance(info.p2) <= myTol2) { + if (!doTightBound) { + builder.Add(compound, info.wire()); + } info.iteration = -2; continue; } - gp_Pnt pt[2]; + std::array pt {}; pt[0] = info.p1; pt[1] = info.p2; - for (int i=0;i<2;++i) { - if (info.iStart[i]>=0) + for (int i = 0; i < 2; ++i) { + const int ic = i; + if (info.iStart[ic] >= 0) { continue; - info.iEnd[i] = info.iStart[i] = (int)adjacentList.size(); + } + info.iEnd[ic] = info.iStart[ic] = (int)adjacentList.size(); - for (auto vit=vmap.qbegin(bgi::nearest(pt[i],INT_MAX));vit!=vmap.qend();++vit) { - auto &vinfo = *vit; - if (vinfo.pt().SquareDistance(pt[i]) > myTol2) + for (auto vit = vmap.qbegin(bgi::nearest(pt[ic], INT_MAX)); vit != vmap.qend(); + ++vit) { + auto& vinfo = *vit; + if (vinfo.pt().SquareDistance(pt[ic]) > myTol2) { break; + } // We must push ourself too, because the adjacency // information is shared among all connected edges. @@ -1353,28 +1576,31 @@ public: // if (&(*vinfo.it) == &info) // continue; - if (vinfo.it->iteration < 0) + if (vinfo.it->iteration < 0) { continue; + } adjacentList.push_back(vinfo); - ++info.iEnd[i]; + ++info.iEnd[ic]; } // copy the adjacent indices to all connected edges - for (int j=info.iStart[i];jiteration >= 0 && other != &info) + } + for (int k = 0; k < 2; ++k) { + const int kc = k; + int idx = 0; + for (idx = info.iStart[kc]; idx < info.iEnd[kc]; ++idx) { + const auto& vertex = adjacentList[idx]; + auto other = vertex.edgeInfo(); + if (other->iteration >= 0 && other != &info) { break; + } } - if (i == info.iEnd[k]) { + if (idx == info.iEnd[kc]) { // If merge or tight bound, then repeat until no edges // can be skipped. - done = !doMergeEdge & !doTightBound; + done = !doMergeEdge && !doTightBound; info.iteration = -3; showShape(&info, "skip"); break; @@ -1407,6 +1636,21 @@ public: } } + void buildAdjacentList() + { + builder.MakeCompound(compound); + + for (auto& info : edges) { + info.reset(); + } + + adjacentList.clear(); + + buildAdjacentListPopulate(); + + buildAdjacentListSkipEdges(); + } + // This algorithm tries to find a set of closed wires that includes as many // edges (added by calling add() ) as possible. One edge may be included // in more than one closed wires if it connects to more than one edges. @@ -1425,8 +1669,9 @@ public: auto &beginInfo = *it; seq->next(true); ++iteration; - if (beginInfo.iteration < 0 || beginInfo.wireInfo) + if (beginInfo.iteration < 0 || beginInfo.wireInfo) { continue; + } VertexInfo currentVertex(it, true); EdgeInfo *currentInfo = &beginInfo; @@ -1436,8 +1681,9 @@ public: edgeSet.clear(); TopoDS_Wire wire = _findClosedWires(beginVertex, currentVertex); - if (wire.IsNull()) + if (wire.IsNull()) { continue; + } if (tightBound) { @@ -1445,23 +1691,25 @@ public: // has been replaced with the precompiler macro assert() assert(!beginInfo.wireInfo); - beginInfo.wireInfo.reset(new WireInfo); + beginInfo.wireInfo.reset(new WireInfo()); beginInfo.wireInfo->vertices.emplace_back(it, true); beginInfo.wireInfo->wire = wire; } - for (auto &r : stack) { - const auto &v = vertexStack[r.iCurrent]; - auto &info = *v.it; - if (tightBound) - beginInfo.wireInfo->vertices.push_back(v); + for (auto &entry : stack) { + const auto &vertex = vertexStack[entry.iCurrent]; + auto &info = *vertex.it; + if (tightBound) { + beginInfo.wireInfo->vertices.push_back(vertex); + } if (!info.wireInfo) { info.wireInfo = beginInfo.wireInfo; // showShape(&info, "visited"); } } showShape(wire,"joined"); - if (!tightBound) + if (!tightBound) { builder.Add(compound, wire); + } } } @@ -1476,24 +1724,246 @@ public: if (FC_LOG_INSTANCE.level() <= FC_LOGLEVEL_TRACE) { return; } - int i = 0; + int idx = 0; for (auto &info : edges) { - ++i; - if (auto w = info.wireInfo.get()) { + ++idx; + if (auto wire = info.wireInfo.get()) { // Originally here there was a call to the precompiler macro assertCheck(), which // has been replaced with the precompiler macro assert() - assert(w->vertices.front().edgeInfo()->wireInfo.get() == w); + assert(wire->vertices.front().edgeInfo()->wireInfo.get() == wire); } } } + // This method was originally part of WireJoinerP::_findClosedWires(), split to reduce cognitive + // complexity + void _findClosedWiresBeginEdge(const std::shared_ptr& wireInfo, + const EdgeInfo& beginInfo, + int& idx) + { + auto info = wireInfo->vertices[idx].edgeInfo(); + showShape(info, "merge", iteration); + + if (info != &beginInfo) { + while (true) { + if (++idx == (int)wireInfo->vertices.size()) { + idx = 0; + } + + info = wireInfo->vertices[idx].edgeInfo(); + if (info == &beginInfo) { + break; + } + stack.emplace_back(vertexStack.size()); + vertexStack.push_back(wireInfo->vertices[idx]); + ++stack.back().iEnd; + + // Originally here there was a call to the method checkStack(), + // which does nothing and therefor has been removed. + } + } + } + + // This method was originally part of WireJoinerP::_findClosedWires(), split to reduce cognitive + // complexity + int _findClosedWiresWithExisting(int* idxVertex, + const std::shared_ptr& wireInfo, + int* const stackPos, + bool& proceed, + const VertexInfo& vinfo, + const int ic, + StackInfo& stackBack, + const EdgeInfo& beginInfo, + EdgeInfo& info) + { + if (wireInfo) { + // We may be called by findTightBound() with an existing wire + // to try to find a new wire by splitting the current one. So + // check if we've iterated to some edge in the existing wire. + + int idx = wireInfo->find(vinfo); + + if (idx != 0) { + vertexStack.push_back(adjacentList[ic]); + stackBack.iCurrent = stackBack.iEnd++; + --idx; + proceed = false; + if (idxVertex) { + *idxVertex = idx; + } + if (stackPos) { + *stackPos = (int)stack.size() - 2; + } + + _findClosedWiresBeginEdge(wireInfo, beginInfo, idx); + + return 1; + } + + if (wireInfo->find(VertexInfo(vinfo.it, !vinfo.start)) != 0) { + showShape(&info, "rintersect", iteration); + // Only used when exhausting tight bound. + wireInfo->purge = true; + return 2; + } + + if (isOutside(*wireInfo, info.mid)) { + showShape(&info, "outside", iteration); + return 2; + } + } + return 0; + } + + // This method was originally part of WireJoinerP::_findClosedWires(), split to reduce cognitive + // complexity + void _findClosedWiresUpdateStack(int* idxVertex, + const std::shared_ptr& wireInfo, + int* stackPos, + const EdgeInfo* currentInfo, + const int currentIdx, + bool& proceed, + const EdgeInfo& beginInfo) + { + auto& stackBack = stack.back(); + + // The loop below is to find all edges connected to pend, and save them into stack.back() + auto size = vertexStack.size(); + for (int i = currentInfo->iStart[currentIdx]; i < currentInfo->iEnd[currentIdx]; ++i) { + auto& vinfo = adjacentList[i]; + auto& info = *vinfo.it; + if (info.iteration < 0 || currentInfo == &info) { + continue; + } + + bool abort = false; + if (!wireSet.empty() && wireSet.contains(info.wireInfo.get())) { + showShape(&info, "wired", iteration); + if (wireInfo) { + wireInfo->purge = true; + } + abort = true; + } + + if (edgeSet.contains(&info)) { + showShape(&info, "intersect", iteration); + // This means the current edge connects to an + // existing edge in the middle of the stack. + // skip the current edge. + stackBack.iEnd = stackBack.iStart; + vertexStack.resize(size); + break; + } + + if (abort || currentInfo->wireInfo2) { + if (wireInfo) { + wireInfo->purge = true; + } + continue; + } + + if (info.iteration == iteration) { + continue; + } + info.iteration = iteration; + + int exists = _findClosedWiresWithExisting(idxVertex, + wireInfo, + stackPos, + proceed, + vinfo, + i, + stackBack, + beginInfo, + info); + + if (exists == 1) { + break; + } + + if (exists == 2) { + continue; + } + + vertexStack.push_back(adjacentList[i]); + ++stackBack.iEnd; + } + } + + // This method was originally part of WireJoinerP::_findClosedWires(), split to reduce cognitive + // complexity + bool _findClosedWiresUpdateEdges(VertexInfo& currentVertex, + gp_Pnt& pend, + EdgeInfo* currentInfo, + int& currentIdx, + const size_t stackEnd) + { + while (true) { + auto& stackBack = stack.back(); + if (stackBack.iCurrent < stackBack.iEnd) { + // now pick one edge from stack.back(), connect it to + // pend, then extend pend + currentVertex = vertexStack[stackBack.iCurrent]; + pend = currentVertex.ptOther(); + // update current edge info + currentInfo = currentVertex.edgeInfo(); + showShape(currentInfo, "iterate", iteration); + currentIdx = currentVertex.start ? 1 : 0; + edgeSet.insert(currentInfo); + if (!wireSet.empty()) { + wireSet.insert(currentInfo->wireInfo.get()); + } + break; + } + vertexStack.erase(vertexStack.begin() + static_cast(stackBack.iStart), vertexStack.end()); + + stack.pop_back(); + if (stack.size() == stackEnd) { + // If stack reaches the end, it means this wire is open. + return true; + } + + auto& lastInfo = *vertexStack[stack.back().iCurrent].it; + edgeSet.erase(&lastInfo); + wireSet.erase(lastInfo.wireInfo.get()); + showShape(&lastInfo, "pop", iteration); + ++stack.back().iCurrent; + } + return false; + } + + // This method was originally part of WireJoinerP::_findClosedWires(), split to reduce cognitive + // complexity + bool _findClosedWiresIsClosed(const VertexInfo& beginVertex, + const TopoDS_Wire& wire, + const EdgeInfo& beginInfo) + { + if (!BRep_Tool::IsClosed(wire)) { + FC_WARN("failed to close some wire in iteration " << iteration); + showShape(wire, "_FailedToClose", iteration); + showShape(beginInfo.shape(beginVertex.start), "failed", iteration); + for (auto& entry : stack) { + const auto& vertex = vertexStack[entry.iCurrent]; + auto& info = *vertex.it; + showShape(info.shape(vertex.start), vertex.start ? "failed" : "failed_r", iteration); + } + + // Originally here there was a call to the precompiler macro assertCheck(), which + // has been replaced with the precompiler macro assert() + + assert(false); + return false; + } + return true; + } + TopoDS_Wire _findClosedWires(VertexInfo beginVertex, VertexInfo currentVertex, - std::shared_ptr wireInfo = std::shared_ptr(), int *idxVertex = nullptr, - int *stackPos = nullptr) + const std::shared_ptr& wireInfo = std::shared_ptr(), + int* stackPos = nullptr) { Base::SequencerBase::Instance().checkAbort(); EdgeInfo &beginInfo = *beginVertex.it; @@ -1514,138 +1984,28 @@ public: while (true) { // push a new stack entry stack.emplace_back(vertexStack.size()); - auto &r = stack.back(); showShape(currentInfo, "check", iteration); bool proceed = true; - // The loop below is to find all edges connected to pend, and save them into stack.back() - auto size = vertexStack.size(); - for (int i=currentInfo->iStart[currentIdx];iiEnd[currentIdx];++i) { - auto &vinfo = adjacentList[i]; - auto &info = *vinfo.it; - if (info.iteration < 0 || currentInfo == &info) { - continue; - } - - bool abort = false; - if (!wireSet.empty() && wireSet.contains(info.wireInfo.get())) { - showShape(&info, "wired", iteration); - if (wireInfo) { - wireInfo->purge = true; - } - abort = true; - } - - if (edgeSet.contains(&info)) { - showShape(&info, "intersect", iteration); - // This means the current edge connects to an - // existing edge in the middle of the stack. - // skip the current edge. - r.iEnd = r.iStart; - vertexStack.resize(size); - break; - } - - if (abort || currentInfo->wireInfo2) { - if (wireInfo) { - wireInfo->purge = true; - } - continue; - } - - if (info.iteration == iteration) { - continue; - } - info.iteration = iteration; - - if (wireInfo) { - // We may be called by findTightBound() with an existing wire - // to try to find a new wire by splitting the current one. So - // check if we've iterated to some edge in the existing wire. - if (int idx = wireInfo->find(vinfo)) { - vertexStack.push_back(adjacentList[i]); - r.iCurrent = r.iEnd++; - --idx; - proceed = false; - if (idxVertex) { - *idxVertex = idx; - } - if (stackPos) - *stackPos = (int)stack.size()-2; - - auto info = wireInfo->vertices[idx].edgeInfo(); - showShape(info, "merge", iteration); - - if (info != &beginInfo) { - while (true) { - if (++idx == (int)wireInfo->vertices.size()) { - idx = 0; - } - info = wireInfo->vertices[idx].edgeInfo(); - if (info == &beginInfo) { - break; - } - stack.emplace_back(vertexStack.size()); - vertexStack.push_back(wireInfo->vertices[idx]); - ++stack.back().iEnd; - - // Originally here there was a call to the method checkStack(), - // which does nothing and therefor has been removed. - } - } - break; - } - - if (wireInfo->find(VertexInfo(vinfo.it, !vinfo.start))) { - showShape(&info, "rintersect", iteration); - // Only used when exhausting tight bound. - wireInfo->purge = true; - continue; - } - - if (isOutside(*wireInfo, info.mid)) { - showShape(&info, "outside", iteration); - continue; - } - } - vertexStack.push_back(adjacentList[i]); - ++r.iEnd; - } + _findClosedWiresUpdateStack(idxVertex, + wireInfo, + stackPos, + currentInfo, + currentIdx, + proceed, + beginInfo); // Originally here there was a call to the method checkStack(), which does nothing and // therefor has been removed. if (proceed) { - while (true) { - auto &r = stack.back(); - if (r.iCurrentwireInfo.get()); - break; - } - vertexStack.erase(vertexStack.begin()+r.iStart,vertexStack.end()); - - stack.pop_back(); - if (stack.size() == stackEnd) { - // If stack reaches the end, it means this wire is open. - return TopoDS_Wire(); - } - - auto &lastInfo = *vertexStack[stack.back().iCurrent].it; - edgeSet.erase(&lastInfo); - wireSet.erase(lastInfo.wireInfo.get()); - showShape(&lastInfo, "pop", iteration); - ++stack.back().iCurrent; + if (_findClosedWiresUpdateEdges(currentVertex, + pend, + currentInfo, + currentIdx, + stackEnd)) { + return {}; } if (pstart.SquareDistance(pend) > myTol2) { @@ -1657,39 +2017,294 @@ public: if (idxVertex) { *idxVertex = (int)wireInfo->vertices.size(); } - if (stackPos) - *stackPos = (int)stack.size()-1; + if (stackPos) { + *stackPos = (int)stack.size() - 1; + } } } wireData->Clear(); wireData->Add(beginInfo.shape(beginVertex.start)); - for (auto &r : stack) { - const auto &v = vertexStack[r.iCurrent]; - auto &info = *v.it; - wireData->Add(info.shape(v.start)); + for (auto &entry : stack) { + const auto &vertex = vertexStack[entry.iCurrent]; + auto &info = *vertex.it; + wireData->Add(info.shape(vertex.start)); } TopoDS_Wire wire = makeCleanWire(); - if (!BRep_Tool::IsClosed(wire)) { - FC_WARN("failed to close some wire in iteration " << iteration); - showShape(wire,"_FailedToClose", iteration); - showShape(beginInfo.shape(beginVertex.start), "failed", iteration); - for (auto &r : stack) { - const auto &v = vertexStack[r.iCurrent]; - auto &info = *v.it; - showShape(info.shape(v.start), v.start ? "failed" : "failed_r", iteration); - } - - // Originally here there was a call to the precompiler macro assertCheck(), which - // has been replaced with the precompiler macro assert() - - assert(false); + if (!_findClosedWiresIsClosed(beginVertex, wire, beginInfo)) { continue; } return wire; } } + // This method was originally part of WireJoinerP::findTightBound(), split to reduce cognitive + // complexity + void findTightBoundSplitWire(const std::shared_ptr& wireInfo, + const EdgeInfo& beginInfo, + const std::vector& wireVertices, + std::shared_ptr& splitWire, + const int idxV, + int& idxStart, + const int idxEnd) + { + for (int idx = idxV; idx != idxEnd; ++idx) { + auto info = wireVertices[idx].edgeInfo(); + if (info == &beginInfo) { + showShape(*wireInfo, "exception", iteration, true); + showShape(info, "exception", iteration, true); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(info != &beginInfo); + } + if (info->wireInfo == wireInfo) { + if (!splitWire) { + idxStart = idx; + splitWire.reset(new WireInfo()); + } + info->wireInfo = splitWire; + } + } + } + + // This method was originally part of WireJoinerP::findTightBound(), split to reduce cognitive + // complexity + void findTightBoundWithSplit(const std::vector& wireVertices, + const int idxV, + const std::shared_ptr& splitWire, + const int idxStart, + const int idxEnd, + const int stackPos, + const int stackStart) + { + auto& splitEdges = splitWire->vertices; + gp_Pnt pstart; + gp_Pnt pt; + bool first = true; + for (int idx = idxStart; idx != idxEnd; ++idx) { + auto& vertex = wireVertices[idx]; + if (first) { + first = false; + pstart = vertex.pt(); + } + else { + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler + // macro assert() + + assert(pt.SquareDistance(vertex.pt()) < myTol2); + } + pt = vertex.ptOther(); + splitEdges.push_back(vertex); + } + for (int i = stackPos; i >= stackStart; --i) { + const auto& vertex = vertexStack[stack[i].iCurrent]; + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(pt.SquareDistance(vertex.ptOther()) < myTol2); + pt = vertex.pt(); + // The edges in the stack are the ones to slice + // the wire in half. We construct a new wire + // that includes the original beginning edge in + // the loop above. And this loop contains the + // other half. Note that the slicing edges + // should run in the oppsite direction, hence reversed + splitEdges.push_back(vertex.reversed()); + } + for (int idx = idxV; idx != idxStart; ++idx) { + auto& vertex = wireVertices[idx]; + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(pt.SquareDistance(vertex.pt()) < myTol2); + pt = vertex.ptOther(); + splitEdges.push_back(vertex); + } + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(pt.SquareDistance(pstart) < myTol2); + showShape(*splitWire, "swire", iteration); + } + + // This method was originally part of WireJoinerP::findTightBound(), split to reduce cognitive + // complexity + void findTightBoundByVertices(EdgeInfo& beginInfo, + const std::vector& wireVertices, + int& idxV, + const int iteration2, + const gp_Pnt& pstart, + const std::shared_ptr& wireInfo, + const VertexInfo& beginVertex, + std::shared_ptr& newWire) + { + const int idx = wireVertices[idxV].start ? 1 : 0; + + auto current = wireVertices[idxV].edgeInfo(); + showShape(current, "current", iteration); + + for (int vertex = current->iStart[idx]; vertex < current->iEnd[idx]; ++vertex) { + const auto& currentVertex = adjacentList[vertex]; + auto next = currentVertex.edgeInfo(); + if (next == current || next->iteration2 == iteration2 || next->iteration < 0) { + continue; + } + + showShape(next, "tcheck", iteration); + + if (!isInside(*wireInfo, next->mid)) { + showShape(next, "ninside", iteration); + next->iteration2 = iteration2; + continue; + } + + edgeSet.insert(next); + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(currentVertex); + + // Originally here there was a call to the method checkStack(), which does + // nothing and therefor has been removed. + + int idxEnd = (int)wireVertices.size(); + int stackStart = (int)stack.size() - 1; + int stackPos = (int)stack.size() - 1; + + TopoDS_Wire wire; + if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { + wire = _findClosedWires(beginVertex, + currentVertex, + &idxEnd, + beginInfo.wireInfo, + &stackPos); + if (wire.IsNull()) { + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + continue; + } + } + + newWire.reset(new WireInfo()); + auto& newWireVertices = newWire->vertices; + newWireVertices.push_back(beginVertex); + for (auto& entry : stack) { + const auto& vertex = vertexStack[entry.iCurrent]; + newWireVertices.push_back(vertex); + } + if (!wire.IsNull()) { + newWire->wire = wire; + } + else if (!initWireInfo(*newWire)) { + newWire.reset(); + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + continue; + } + for (auto& vertex : newWire->vertices) { + if (vertex.edgeInfo()->wireInfo == wireInfo) { + vertex.edgeInfo()->wireInfo = newWire; + } + } + beginInfo.wireInfo = newWire; + showShape(*newWire, "nwire", iteration); + + std::shared_ptr splitWire; + if (idxEnd == 0) { + idxEnd = (int)wireVertices.size(); + } + ++idxV; + + // Originally here there was a call to the precompiler macro assertCheck(), + // which has been replaced with the precompiler macro assert() + + assert(idxV <= idxEnd); + int idxStart = idxV; + + findTightBoundSplitWire(wireInfo, + beginInfo, + wireVertices, + splitWire, + idxV, + idxStart, + idxEnd); + + if (splitWire) { + findTightBoundWithSplit(wireVertices, + idxV, + splitWire, + idxStart, + idxEnd, + stackPos, + stackStart); + } + + checkWireInfo(*newWire); + break; + } + } + + // This method was originally part of WireJoinerP::findTightBound(), split to reduce cognitive + // complexity + void findTightBoundUpdateVertices(EdgeInfo& beginInfo) + { + showShape(*beginInfo.wireInfo, "done", iteration); + beginInfo.wireInfo->done = true; + // If a wire is done, make sure all edges of this wire is + // marked as done. This can also prevent duplicated wires. + for (auto& vertex : beginInfo.wireInfo->vertices) { + auto info = vertex.edgeInfo(); + if (!info->wireInfo) { + info->wireInfo = beginInfo.wireInfo; + continue; + } + if (info->wireInfo->done) { + continue; + } + auto otherWire = info->wireInfo; + auto& otherWireVertices = info->wireInfo->vertices; + if (info == otherWireVertices.front().edgeInfo()) { + // About to change the first edge of the other wireInfo. + // Try to find a new first edge for it. + tmpVertices.clear(); + auto it = otherWireVertices.begin(); + tmpVertices.push_back(*it); + for (++it; it != otherWireVertices.end(); ++it) { + if (it->edgeInfo()->wireInfo == otherWire) { + break; + } + tmpVertices.push_back(*it); + } + if (tmpVertices.size() != otherWireVertices.size()) { + otherWireVertices.erase(otherWireVertices.begin(), it); + otherWireVertices.insert(otherWireVertices.end(), + tmpVertices.begin(), + tmpVertices.end()); + } + } + + // Originally here there was a call to the precompiler macro assertCheck(), + // which has been replaced with the precompiler macro assert() + + assert(info != &beginInfo); + info->wireInfo = beginInfo.wireInfo; + checkWireInfo(*otherWire); + } + checkWireInfo(*beginInfo.wireInfo); + } + void findTightBound() { // Assumption: all edges lies on a common manifold surface @@ -1709,8 +2324,9 @@ public: for (auto &info : edges) { ++iteration; seq->next(true); - if (info.iteration < 0 || !info.wireInfo) + if (info.iteration < 0 || !info.wireInfo) { continue; + } ++iteration2; while(!info.wireInfo->done) { @@ -1721,8 +2337,9 @@ public: auto &beginInfo = *beginVertex.it; initWireInfo(*wireInfo); showShape(wireInfo->wire, "iwire", iteration); - for (auto &v : wireVertices) - v.it->iteration2 = iteration2; + for (auto& vertex : wireVertices) { + vertex.it->iteration2 = iteration2; + } stack.clear(); vertexStack.clear(); @@ -1733,168 +2350,24 @@ public: int idxV = 0; while (true) { - int idx = wireVertices[idxV].start ? 1 : 0; - auto current = wireVertices[idxV].edgeInfo(); - showShape(current, "current", iteration); + findTightBoundByVertices(beginInfo, + wireVertices, + idxV, + iteration2, + pstart, + wireInfo, + beginVertex, + newWire); - for (int n=current->iStart[idx]; niEnd[idx]; ++n) { - const auto ¤tVertex = adjacentList[n]; - auto next = currentVertex.edgeInfo(); - if (next == current || next->iteration2 == iteration2 || next->iteration<0) - continue; - - showShape(next, "tcheck", iteration); - - if (!isInside(*wireInfo, next->mid)) { - showShape(next, "ninside", iteration); - next->iteration2 = iteration2; - continue; - } - - edgeSet.insert(next); - stack.emplace_back(vertexStack.size()); - ++stack.back().iEnd; - vertexStack.push_back(currentVertex); - - // Originally here there was a call to the method checkStack(), which does - // nothing and therefor has been removed. - - int idxEnd = (int)wireVertices.size(); - int stackStart = (int)stack.size()-1; - int stackPos = (int)stack.size()-1; - - TopoDS_Wire wire; - if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { - wire = _findClosedWires(beginVertex, currentVertex, beginInfo.wireInfo, &idxEnd, &stackPos); - if (wire.IsNull()) { - vertexStack.pop_back(); - stack.pop_back(); - edgeSet.erase(next); - continue; - } - } - - newWire.reset(new WireInfo); - auto &newWireVertices = newWire->vertices; - newWireVertices.push_back(beginVertex); - for (auto &r : stack) { - const auto &v = vertexStack[r.iCurrent]; - newWireVertices.push_back(v); - } - if (!wire.IsNull()) - newWire->wire = wire; - else if (!initWireInfo(*newWire)) { - newWire.reset(); - vertexStack.pop_back(); - stack.pop_back(); - edgeSet.erase(next); - continue; - } - for (auto &v : newWire->vertices) { - if (v.edgeInfo()->wireInfo == wireInfo) - v.edgeInfo()->wireInfo = newWire; - } - beginInfo.wireInfo = newWire; - showShape(*newWire, "nwire", iteration); - - std::shared_ptr splitWire; - if (idxEnd == 0) - idxEnd = (int)wireVertices.size(); - ++idxV; - - // Originally here there was a call to the precompiler macro assertCheck(), - // which has been replaced with the precompiler macro assert() - - assert(idxV <= idxEnd); - int idxStart = idxV; - for (int idx=idxV; idx!=idxEnd; ++idx) { - auto info = wireVertices[idx].edgeInfo(); - if (info == &beginInfo) { - showShape(*wireInfo, "exception", iteration, true); - showShape(info, "exception", iteration, true); - - // Originally here there was a call to the precompiler macro - // assertCheck(), which has been replaced with the precompiler macro - // assert() - - assert(info != &beginInfo); - } - if (info->wireInfo == wireInfo) { - if (!splitWire) { - idxStart = idx; - splitWire.reset(new WireInfo); - } - info->wireInfo = splitWire; - } - } - if (splitWire) { - auto &splitEdges = splitWire->vertices; - gp_Pnt pstart, pt; - bool first = true; - for (int idx=idxStart; idx!=idxEnd; ++idx) { - auto &v = wireVertices[idx]; - if (first) { - first = false; - pstart = v.pt(); - } - else { - - // Originally here there was a call to the precompiler macro - // assertCheck(), which has been replaced with the precompiler - // macro assert() - - assert(pt.SquareDistance(v.pt()) < myTol2); - } - pt = v.ptOther(); - splitEdges.push_back(v); - } - for (int i=stackPos; i>=stackStart; --i) { - const auto &v = vertexStack[stack[i].iCurrent]; - - // Originally here there was a call to the precompiler macro - // assertCheck(), which has been replaced with the precompiler macro - // assert() - - assert(pt.SquareDistance(v.ptOther()) < myTol2); - pt = v.pt(); - // The edges in the stack are the ones to slice - // the wire in half. We construct a new wire - // that includes the original beginning edge in - // the loop above. And this loop contains the - // other half. Note that the slicing edges - // should run in the oppsite direction, hence reversed - splitEdges.push_back(v.reversed()); - } - for (int idx=idxV; idx!=idxStart; ++idx) { - auto &v = wireVertices[idx]; - - // Originally here there was a call to the precompiler macro - // assertCheck(), which has been replaced with the precompiler macro - // assert() - - assert(pt.SquareDistance(v.pt()) < myTol2); - pt = v.ptOther(); - splitEdges.push_back(v); - } - - // Originally here there was a call to the precompiler macro - // assertCheck(), which has been replaced with the precompiler macro - // assert() - - assert(pt.SquareDistance(pstart) < myTol2); - showShape(*splitWire, "swire", iteration); - } - - checkWireInfo(*newWire); - break; - } if (newWire) { ++iteration; break; } - if (++idxV == (int)wireVertices.size()) + if (++idxV == (int)wireVertices.size()) { break; + } + stack.emplace_back(vertexStack.size()); ++stack.back().iEnd; vertexStack.push_back(wireVertices[idxV]); @@ -1905,51 +2378,283 @@ public: } if (!newWire) { - showShape(*beginInfo.wireInfo, "done", iteration); - beginInfo.wireInfo->done = true; - // If a wire is done, make sure all edges of this wire is - // marked as done. This can also prevent duplicated wires. - for (auto &v : beginInfo.wireInfo->vertices) { - auto info = v.edgeInfo(); - if (!info->wireInfo) { - info->wireInfo = beginInfo.wireInfo; - continue; - } - else if (info->wireInfo->done) - continue; - auto otherWire = info->wireInfo; - auto &otherWireVertices = info->wireInfo->vertices; - if (info == otherWireVertices.front().edgeInfo()) { - // About to change the first edge of the other wireInfo. - // Try to find a new first edge for it. - tmpVertices.clear(); - auto it = otherWireVertices.begin(); - tmpVertices.push_back(*it); - for (++it;it!=otherWireVertices.end();++it) { - if (it->edgeInfo()->wireInfo == otherWire) - break; - tmpVertices.push_back(*it); - } - if (tmpVertices.size() != otherWireVertices.size()) { - otherWireVertices.erase(otherWireVertices.begin(), it); - otherWireVertices.insert(otherWireVertices.end(), - tmpVertices.begin(), tmpVertices.end()); - } - } - - // Originally here there was a call to the precompiler macro assertCheck(), - // which has been replaced with the precompiler macro assert() - - assert(info != &beginInfo); - info->wireInfo = beginInfo.wireInfo; - checkWireInfo(*otherWire); - } - checkWireInfo(*beginInfo.wireInfo); + findTightBoundUpdateVertices(beginInfo); } } } } + // This method was originally part of WireJoinerP::exhaustTightBound(), split to reduce cognitive + // complexity + void exhaustTightBoundUpdateVertex(const int iteration2, + const VertexInfo& beginVertex, + const int idxV, + const gp_Pnt& pstart, + const std::vector& wireVertices, + std::shared_ptr& newWire, + const std::shared_ptr& wireInfo) + { + const int idx = wireVertices[idxV].start ? 1 : 0; + auto current = wireVertices[idxV].edgeInfo(); + + for (int vertex = current->iStart[idx]; vertex < current->iEnd[idx]; ++vertex) { + const auto& currentVertex = adjacentList[vertex]; + auto next = currentVertex.edgeInfo(); + if (next == current || next->iteration2 == iteration2 || next->iteration < 0) { + continue; + } + + showShape(next, "check2", iteration); + + if (!isInside(*wireInfo, next->mid)) { + showShape(next, "ninside2", iteration); + next->iteration2 = iteration2; + continue; + } + + edgeSet.insert(next); + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(currentVertex); + + // Originally here there a call to the method checkStack(), which + // does nothing and therefor has been removed. + + TopoDS_Wire wire; + if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { + wire = _findClosedWires(beginVertex, currentVertex, nullptr, wireInfo); + if (wire.IsNull()) { + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + wireSet.erase(next->wireInfo.get()); + continue; + } + } + + newWire.reset(new WireInfo()); + auto& newWireVertices = newWire->vertices; + newWireVertices.push_back(beginVertex); + for (auto& entry : stack) { + const auto& vertex = vertexStack[entry.iCurrent]; + newWireVertices.push_back(vertex); + } + if (!wire.IsNull()) { + newWire->wire = wire; + } + else if (!initWireInfo(*newWire)) { + newWire.reset(); + vertexStack.pop_back(); + stack.pop_back(); + edgeSet.erase(next); + wireSet.erase(next->wireInfo.get()); + continue; + } + for (auto& vertex : newWire->vertices) { + if (vertex.edgeInfo()->wireInfo == wireInfo) { + vertex.edgeInfo()->wireInfo = newWire; + } + } + showShape(*newWire, "nwire2", iteration); + checkWireInfo(*newWire); + break; + } + } + + // This method was originally part of WireJoinerP::exhaustTightBound(), split to reduce cognitive + // complexity + void exhaustTightBoundUpdateEdge(const int iteration2, + const VertexInfo& beginVertex, + const std::vector& wireVertices, + const gp_Pnt& pstart, + std::shared_ptr& wireInfo) + { + std::shared_ptr newWire; + + int idxV = 1; + while (true) { + exhaustTightBoundUpdateVertex(iteration2, + beginVertex, + idxV, + pstart, + wireVertices, + newWire, + wireInfo); + + if (newWire) { + ++iteration; + wireInfo = newWire; + break; + } + + if (++idxV == (int)wireVertices.size()) { + if (wireInfo->purge) { + showShape(*wireInfo, "discard2", iteration); + wireInfo.reset(); + } + else { + wireInfo->done = true; + showShape(*wireInfo, "done2", iteration); + } + break; + } + stack.emplace_back(vertexStack.size()); + ++stack.back().iEnd; + vertexStack.push_back(wireVertices[idxV]); + edgeSet.insert(wireVertices[idxV].edgeInfo()); + + // Originally here there a call to the method checkStack(), which does + // nothing and therefor has been removed. + } + } + + // This method was originally part of WireJoinerP::exhaustTightBound(), split to reduce cognitive + // complexity + void exhaustTightBoundWithAdjacent(const EdgeInfo& info, + int& iteration2, + const VertexInfo beginVertex, + const EdgeInfo* check) + { + const gp_Pnt& pstart = beginVertex.pt(); + const int vidx = beginVertex.start ? 1 : 0; + + edgeSet.clear(); + vertexStack.clear(); + stack.clear(); + stack.emplace_back(); + for (int i = info.iStart[vidx]; i < info.iEnd[vidx]; ++i) { + const auto& currentVertex = adjacentList[i]; + auto next = currentVertex.edgeInfo(); + if (next == &info || next == check || next->iteration < 0 || !next->wireInfo + || !next->wireInfo->done || next->wireInfo2) { + continue; + } + + showShape(next, "n2", iteration); + + stack.clear(); + stack.emplace_back(); + ++stack.back().iEnd; + vertexStack.clear(); + vertexStack.push_back(currentVertex); + + edgeSet.clear(); + edgeSet.insert(next); + wireSet.clear(); + wireSet.insert(next->wireInfo.get()); + + TopoDS_Wire wire; + if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { + wire = _findClosedWires(beginVertex, currentVertex); + if (wire.IsNull()) { + continue; + } + } + + std::shared_ptr wireInfo(new WireInfo()); + wireInfo->vertices.push_back(beginVertex); + for (auto& entry : stack) { + const auto& vertex = vertexStack[entry.iCurrent]; + wireInfo->vertices.push_back(vertex); + } + if (!wire.IsNull()) { + wireInfo->wire = wire; + } + else if (!initWireInfo(*wireInfo)) { + continue; + } + + showShape(*wireInfo, "nw2", iteration); + + ++iteration; + ++iteration2; + + while (wireInfo && !wireInfo->done) { + showShape(next, "next2", iteration); + + vertexStack.resize(1); + stack.resize(1); + edgeSet.clear(); + edgeSet.insert(next); + wireSet.clear(); + wireSet.insert(next->wireInfo.get()); + + const auto& wireVertices = wireInfo->vertices; + initWireInfo(*wireInfo); + for (auto& vertex : wireVertices) { + vertex.it->iteration2 = iteration2; + } + + exhaustTightBoundUpdateEdge(iteration2, + beginVertex, + wireVertices, + pstart, + wireInfo); + } + + if (wireInfo && wireInfo->done) { + for (auto& vertex : wireInfo->vertices) { + auto edgeInfo = vertex.edgeInfo(); + + // Originally here there was a call to the precompiler macro + // assertCheck(), which has been replaced with the precompiler macro + // assert() + + assert(edgeInfo->wireInfo != nullptr); + if (edgeInfo->wireInfo->isSame(*wireInfo)) { + wireInfo = edgeInfo->wireInfo; + break; + } + } + for (auto& vertex : wireInfo->vertices) { + auto edgeInfo = vertex.edgeInfo(); + if (!edgeInfo->wireInfo2 && edgeInfo->wireInfo != wireInfo) { + edgeInfo->wireInfo2 = wireInfo; + } + } + + // Originally here there were two calls to the precompiler macro + // assertCheck(), which have been replaced with the precompiler macro + // assert() + + assert(info.wireInfo2 == wireInfo); + assert(info.wireInfo2 != info.wireInfo); + showShape(*wireInfo, "exhaust"); + break; + } + } + } + + // This method was originally part of WireJoinerP::exhaustTightBound(), split to reduce cognitive + // complexity + void exhaustTightBoundUpdateWire(const EdgeInfo& info, int& iteration2) + { + + showShape(*info.wireInfo, "iwire2", iteration); + showShape(&info, "begin2", iteration); + + int idx = info.wireInfo->find(&info); + + // Originally here there was a call to the precompiler macro assertCheck(), which has + // been replaced with the precompiler macro assert() + + assert(idx > 0); + const auto& vertices = info.wireInfo->vertices; + --idx; + int nextIdx = idx == (int)vertices.size() - 1 ? 0 : idx + 1; + int prevIdx = idx == 0 ? (int)vertices.size() - 1 : idx - 1; + int count = prevIdx == nextIdx ? 1 : 2; + for (int idxV = 0; idxV < count && !info.wireInfo2; ++idxV) { + auto check = vertices[idxV == 0 ? nextIdx : prevIdx].edgeInfo(); + auto beginVertex = vertices[idx]; + if (idxV == 1) { + beginVertex.start = !beginVertex.start; + } + + exhaustTightBoundWithAdjacent(info, iteration2, beginVertex, check); + } + } + void exhaustTightBound() { // findTightBound() function will find a tight bound wire for each @@ -1961,12 +2666,14 @@ public: new Base::SequencerLauncher("Exhaust tight bound", edges.size())); for (auto &info : edges) { - if (info.iteration < 0 || !info.wireInfo || !info.wireInfo->done) + if (info.iteration < 0 || !info.wireInfo || !info.wireInfo->done) { continue; - for (auto &v : info.wireInfo->vertices) { - auto edgeInfo = v.edgeInfo(); - if (edgeInfo->wireInfo != info.wireInfo) + } + for (auto &vertex : info.wireInfo->vertices) { + auto edgeInfo = vertex.edgeInfo(); + if (edgeInfo->wireInfo != info.wireInfo) { edgeInfo->wireInfo2 = info.wireInfo; + } } } @@ -1978,10 +2685,12 @@ public: || !info.wireInfo || !info.wireInfo->done) { - if (info.wireInfo) + if (info.wireInfo) { showShape(*info.wireInfo, "iskip"); - else + } + else { showShape(&info, "iskip"); + } continue; } @@ -1990,223 +2699,37 @@ public: continue; } - showShape(*info.wireInfo, "iwire2", iteration); - showShape(&info, "begin2", iteration); + exhaustTightBoundUpdateWire(info, iteration2); - int idx = info.wireInfo->find(&info); - - // Originally here there was a call to the precompiler macro assertCheck(), which has - // been replaced with the precompiler macro assert() - - assert(idx > 0); - const auto &vertices = info.wireInfo->vertices; - --idx; - int nextIdx = idx == (int)vertices.size()-1 ? 0 : idx + 1; - int prevIdx = idx == 0 ? (int)vertices.size()-1 : idx - 1; - int count = prevIdx == nextIdx ? 1 : 2; - for (int n=0; niteration<0 - || !next->wireInfo - || !next->wireInfo->done - || next->wireInfo2) - continue; - - showShape(next, "n2", iteration); - - stack.clear(); - stack.emplace_back(); - ++stack.back().iEnd; - vertexStack.clear(); - vertexStack.push_back(currentVertex); - - edgeSet.clear(); - edgeSet.insert(next); - wireSet.clear(); - wireSet.insert(next->wireInfo.get()); - - TopoDS_Wire wire; - if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { - wire = _findClosedWires(beginVertex, currentVertex); - if (wire.IsNull()) - continue; - } - - std::shared_ptr wireInfo(new WireInfo); - wireInfo->vertices.push_back(beginVertex); - for (auto &r : stack) { - const auto &v = vertexStack[r.iCurrent]; - wireInfo->vertices.push_back(v); - } - if (!wire.IsNull()) - wireInfo->wire = wire; - else if (!initWireInfo(*wireInfo)) - continue; - - showShape(*wireInfo, "nw2", iteration); - - ++iteration; - ++iteration2; - - while (wireInfo && !wireInfo->done) { - showShape(next, "next2", iteration); - - vertexStack.resize(1); - stack.resize(1); - edgeSet.clear(); - edgeSet.insert(next); - wireSet.clear(); - wireSet.insert(next->wireInfo.get()); - - const auto &wireVertices = wireInfo->vertices; - initWireInfo(*wireInfo); - for (auto &v : wireVertices) - v.it->iteration2 = iteration2; - - std::shared_ptr newWire; - - int idxV = 1; - while (true) { - int idx = wireVertices[idxV].start ? 1 : 0; - auto current = wireVertices[idxV].edgeInfo(); - - for (int n=current->iStart[idx]; niEnd[idx]; ++n) { - const auto ¤tVertex = adjacentList[n]; - auto next = currentVertex.edgeInfo(); - if (next == current || next->iteration2 == iteration2 || next->iteration<0) - continue; - - showShape(next, "check2", iteration); - - if (!isInside(*wireInfo, next->mid)) { - showShape(next, "ninside2", iteration); - next->iteration2 = iteration2; - continue; - } - - edgeSet.insert(next); - stack.emplace_back(vertexStack.size()); - ++stack.back().iEnd; - vertexStack.push_back(currentVertex); - - // Originally here there a call to the method checkStack(), which - // does nothing and therefor has been removed. - - TopoDS_Wire wire; - if (pstart.SquareDistance(currentVertex.ptOther()) > myTol2) { - wire = _findClosedWires(beginVertex, currentVertex, wireInfo); - if (wire.IsNull()) { - vertexStack.pop_back(); - stack.pop_back(); - edgeSet.erase(next); - wireSet.erase(next->wireInfo.get()); - continue; - } - } - - newWire.reset(new WireInfo); - auto &newWireVertices = newWire->vertices; - newWireVertices.push_back(beginVertex); - for (auto &r : stack) { - const auto &v = vertexStack[r.iCurrent]; - newWireVertices.push_back(v); - } - if (!wire.IsNull()) - newWire->wire = wire; - else if (!initWireInfo(*newWire)) { - newWire.reset(); - vertexStack.pop_back(); - stack.pop_back(); - edgeSet.erase(next); - wireSet.erase(next->wireInfo.get()); - continue; - } - for (auto &v : newWire->vertices) { - if (v.edgeInfo()->wireInfo == wireInfo) - v.edgeInfo()->wireInfo = newWire; - } - showShape(*newWire, "nwire2", iteration); - checkWireInfo(*newWire); - break; - } - - if (newWire) { - ++iteration; - wireInfo = newWire; - break; - } - - if (++idxV == (int)wireVertices.size()) { - if (wireInfo->purge) { - showShape(*wireInfo, "discard2", iteration); - wireInfo.reset(); - } else { - wireInfo->done = true; - showShape(*wireInfo, "done2", iteration); - } - break; - } - stack.emplace_back(vertexStack.size()); - ++stack.back().iEnd; - vertexStack.push_back(wireVertices[idxV]); - edgeSet.insert(wireVertices[idxV].edgeInfo()); - - // Originally here there a call to the method checkStack(), which does - // nothing and therefor has been removed. - } - } - - if (wireInfo && wireInfo->done) { - for (auto &v : wireInfo->vertices) { - auto edgeInfo = v.edgeInfo(); - - // Originally here there was a call to the precompiler macro - // assertCheck(), which has been replaced with the precompiler macro - // assert() - - assert(edgeInfo->wireInfo != nullptr); - if (edgeInfo->wireInfo->isSame(*wireInfo)) { - wireInfo = edgeInfo->wireInfo; - break; - } - } - for (auto &v : wireInfo->vertices) { - auto edgeInfo = v.edgeInfo(); - if (!edgeInfo->wireInfo2 && edgeInfo->wireInfo != wireInfo) - edgeInfo->wireInfo2 = wireInfo; - } - - // Originally here there were two calls to the precompiler macro - // assertCheck(), which have been replaced with the precompiler macro - // assert() - - assert(info.wireInfo2 == wireInfo); - assert(info.wireInfo2 != info.wireInfo); - showShape(*wireInfo, "exhaust"); - break; - } - } - } } wireSet.clear(); } + // This method was originally part of WireJoinerP::makeCleanWire(), split to reduce cognitive + // complexity + void printHistoryInit(const Handle_BRepTools_History& newHistory, + const std::vector& inputEdges) + { + FC_MSG("init:"); + for (const auto& shape : sourceEdges) { + FC_MSG(shape.getShape().TShape().get() << ", " << shape.getShape().HashCode(INT_MAX)); + } + printHistory(aHistory, sourceEdges); + printHistory(newHistory, inputEdges); + } + + // This method was originally part of WireJoinerP::makeCleanWire(), split to reduce cognitive + // complexity + void printHistoryFinal() + { + printHistory(aHistory, sourceEdges); + FC_MSG("final:"); + for (int i = 1; i <= wireData->NbEdges(); ++i) { + auto shape = wireData->Edge(i); + FC_MSG(shape.TShape().get() << ", " << shape.HashCode(INT_MAX)); + } + } + TopoDS_Wire makeCleanWire(bool fixGap=true) { // Make a clean wire with sorted, oriented, connected, etc edges @@ -2220,7 +2743,8 @@ public: } ShapeFix_Wire fixer; - fixer.SetContext(new ShapeBuild_ReShape); + Handle(ShapeBuild_ReShape) reshape = new ShapeBuild_ReShape(); + fixer.SetContext(reshape); fixer.Load(wireData); fixer.SetMaxTolerance(myTol); fixer.ClosedWireMode() = Standard_True; @@ -2240,36 +2764,40 @@ public: result = fixer.Wire(); auto newHistory = fixer.Context()->History(); - if (FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE+1) { - FC_MSG("init:"); - for (const auto &s : sourceEdges) - FC_MSG(s.getShape().TShape().get() << ", " << s.getShape().HashCode(INT_MAX)); - printHistory(aHistory, sourceEdges); - printHistory(newHistory, inputEdges); + + if (FC_LOG_INSTANCE.level() > FC_LOGLEVEL_TRACE + 1) { + printHistoryInit(newHistory, inputEdges); } aHistory->Merge(newHistory); - if (FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE+1) { - printHistory(aHistory, sourceEdges); - FC_MSG("final:"); - for (int i=1; i<=wireData->NbEdges(); ++i) { - auto s = wireData->Edge(i); - FC_MSG(s.TShape().get() << ", " << s.HashCode(INT_MAX)); - } + + if (FC_LOG_INSTANCE.level() > FC_LOGLEVEL_TRACE + 1) { + printHistoryFinal(); } + return result; } + // This method was originally part of WireJoinerP::printHistory(), split to reduce cognitive + // complexity + template + void printHistoryOfShape(const Handle(BRepTools_History)& hist, const T& shape) + { + for (TopTools_ListIteratorOfListOfShape it(hist->Modified(shape.getShape())); it.More(); + it.Next()) { + FC_MSG(shape.getShape().TShape().get() + << ", " << shape.getShape().HashCode(INT_MAX) << " -> " + << it.Value().TShape().get() << ", " << it.Value().HashCode(INT_MAX)); + } + } + template void printHistory(Handle(BRepTools_History) hist, const T &input) { FC_MSG("\nHistory:\n"); - for (const auto &s : input) { - for(TopTools_ListIteratorOfListOfShape it(hist->Modified(s.getShape())); it.More(); it.Next()) { - FC_MSG(s.getShape().TShape().get() << ", " << s.getShape().HashCode(INT_MAX) - << " -> " << it.Value().TShape().get() << ", " << it.Value().HashCode(INT_MAX)); - } + for (const auto& shape : input) { + printHistoryOfShape(hist, shape); } } @@ -2283,7 +2811,7 @@ public: return true; } - void showShape(const EdgeInfo *info, const char *name, int idx=-1, bool forced=false) + void showShape(const EdgeInfo* info, const char* name, int idx = -1, bool forced = false) const { if (!canShowShape(idx, forced)) { return; @@ -2302,7 +2830,10 @@ public: showShape(wireInfo.wire, name, idx, forced); } - void showShape(const TopoDS_Shape &sToShow, const char *name, int idx=-1, bool forced=false) + void showShape(const TopoDS_Shape& sToShow, + const char* name, + int idx = -1, + bool forced = false) const { if (!canShowShape(idx, forced)) { return; @@ -2320,7 +2851,76 @@ public: if (catchObject == obj->getNameInDocument()) { FC_MSG("found"); } - return; + } + + // This method was originally part of WireJoinerP::build(), split to reduce cognitive complexity + void buildClosedWire() + { + findClosedWires(true); + findTightBound(); + exhaustTightBound(); + bool done = !doOutline; + while (!done) { + ++iteration; + done = true; + std::unordered_map counter; + std::unordered_set wires; + for (auto& info : edges) { + if (info.iteration == -2) { + continue; + } + if (info.iteration < 0 || !info.wireInfo || !info.wireInfo->done) { + if (info.iteration >= 0) { + info.iteration = -1; + done = false; + showShape(&info, "removed", iteration); + aHistory->Remove(info.edge); + } + continue; + } + if (info.wireInfo2 && wires.insert(info.wireInfo2.get()).second) { + for (auto& vertex : info.wireInfo2->vertices) { + if (++counter[vertex.edgeInfo()] == 2) { + vertex.edgeInfo()->iteration = -1; + done = false; + showShape(vertex.edgeInfo(), "removed2", iteration); + aHistory->Remove(info.edge); + } + } + } + if (!wires.insert(info.wireInfo.get()).second) { + continue; + } + for (auto& vertex : info.wireInfo->vertices) { + if (++counter[vertex.edgeInfo()] == 2) { + vertex.edgeInfo()->iteration = -1; + done = false; + showShape(vertex.edgeInfo(), "removed1", iteration); + aHistory->Remove(info.edge); + } + } + } + findClosedWires(true); + findTightBound(); + } + + builder.MakeCompound(compound); + wireSet.clear(); + for (auto& info : edges) { + if (info.iteration == -2) { + if (!info.wireInfo) { + builder.Add(compound, info.wire()); + continue; + } + addWire(info.wireInfo); + addWire(info.wireInfo2); + } + else if (info.iteration >= 0) { + addWire(info.wireInfo2); + addWire(info.wireInfo); + } + } + wireSet.clear(); } void build() @@ -2328,8 +2928,9 @@ public: clear(); sourceEdges.clear(); sourceEdges.insert(sourceEdgeArray.begin(), sourceEdgeArray.end()); - for (const auto &e : sourceEdgeArray) - add(TopoDS::Edge(e.getShape()), true); + for (const auto& edge : sourceEdgeArray) { + add(TopoDS::Edge(edge.getShape()), true); + } if (doTightBound || doSplitEdge) { splitEdges(); @@ -2341,69 +2942,7 @@ public: findClosedWires(); } else { - findClosedWires(true); - findTightBound(); - exhaustTightBound(); - bool done = !doOutline; - while(!done) { - ++iteration; - done = true; - std::unordered_map counter; - std::unordered_set wires; - for (auto &info : edges) { - if (info.iteration == -2) - continue; - if (info.iteration < 0 || !info.wireInfo || !info.wireInfo->done) { - if (info.iteration >= 0) { - info.iteration = -1; - done = false; - showShape(&info, "removed", iteration); - aHistory->Remove(info.edge); - } - continue; - } - if (info.wireInfo2 && wires.insert(info.wireInfo2.get()).second) { - for (auto &v : info.wireInfo2->vertices) { - if (++counter[v.edgeInfo()] == 2) { - v.edgeInfo()->iteration = -1; - done = false; - showShape(v.edgeInfo(), "removed2", iteration); - aHistory->Remove(info.edge); - } - } - } - if (!wires.insert(info.wireInfo.get()).second) - continue; - for (auto &v : info.wireInfo->vertices) { - if (++counter[v.edgeInfo()] == 2) { - v.edgeInfo()->iteration = -1; - done = false; - showShape(v.edgeInfo(), "removed1", iteration); - aHistory->Remove(info.edge); - } - } - } - findClosedWires(true); - findTightBound(); - } - - builder.MakeCompound(compound); - wireSet.clear(); - for (auto &info : edges) { - if (info.iteration == -2) { - if (!info.wireInfo) { - builder.Add(compound, info.wire()); - continue; - } - addWire(info.wireInfo); - addWire(info.wireInfo2); - } - else if (info.iteration >= 0) { - addWire(info.wireInfo2); - addWire(info.wireInfo); - } - } - wireSet.clear(); + buildClosedWire(); } // TODO: We choose to put open wires in a separated shape from the final @@ -2448,8 +2987,8 @@ public: bool touched = false; for (auto it=wires.begin(); it!=wires.end();) { bool purge = true; - for (const auto &e : it->getSubShapes(TopAbs_EDGE)) { - if (source.findSubShapesWithSharedVertex(TopoShape(e, -1)).empty()) { + for (const auto &edge : it->getSubShapes(TopAbs_EDGE)) { + if (source.findSubShapesWithSharedVertex(TopoShape(edge, -1)).empty()) { purge = false; break; } @@ -2496,27 +3035,27 @@ public: WireJoiner::WireJoiner() - :pimpl(new WireJoinerP) + :pimpl(new WireJoinerP()) { } -WireJoiner::~WireJoiner() -{ -} +WireJoiner::~WireJoiner() = default; void WireJoiner::addShape(const TopoShape &shape) { NotDone(); - for (auto &e : shape.getSubTopoShapes(TopAbs_EDGE)) - pimpl->sourceEdgeArray.push_back(e); + for (auto& edge : shape.getSubTopoShapes(TopAbs_EDGE)) { + pimpl->sourceEdgeArray.push_back(edge); + } } void WireJoiner::addShape(const std::vector &shapes) { NotDone(); for (const auto &shape : shapes) { - for (auto &e : shape.getSubTopoShapes(TopAbs_EDGE)) - pimpl->sourceEdgeArray.push_back(e); + for (auto& edge : shape.getSubTopoShapes(TopAbs_EDGE)) { + pimpl->sourceEdgeArray.push_back(edge); + } } } @@ -2524,8 +3063,9 @@ void WireJoiner::addShape(const std::vector &shapes) { NotDone(); for (const auto &shape : shapes) { - for (TopExp_Explorer xp(shape,TopAbs_EDGE); xp.More(); xp.Next()) + for (TopExp_Explorer xp(shape, TopAbs_EDGE); xp.More(); xp.Next()) { pimpl->sourceEdgeArray.emplace_back(TopoDS::Edge(xp.Current()), -1); + } } } @@ -2576,10 +3116,12 @@ void WireJoiner::setTolerance(double tol, double atol) #if OCC_VERSION_HEX < 0x070600 void WireJoiner::Build() -#else -void WireJoiner::Build(const Message_ProgressRange&) -#endif { +#else +void WireJoiner::Build(const Message_ProgressRange& theRange) +{ + (void)theRange; +#endif if (IsDone()) { return; } diff --git a/src/Mod/Part/App/WireJoiner.h b/src/Mod/Part/App/WireJoiner.h index 948af08d0f..23e23623ee 100644 --- a/src/Mod/Part/App/WireJoiner.h +++ b/src/Mod/Part/App/WireJoiner.h @@ -33,7 +33,7 @@ namespace Part{ class PartExport WireJoiner: public BRepBuilderAPI_MakeShape { public: WireJoiner(); - virtual ~WireJoiner(); + ~WireJoiner() override; void addShape(const TopoShape &shape); void addShape(const std::vector &shapes); @@ -43,7 +43,7 @@ public: void setTightBound(bool enable=true); void setSplitEdges(bool enable=true); void setMergeEdges(bool enable=true); - void setTolerance(double tolerance, double angularTol=0.0); + void setTolerance(double tolerance, double atol=0.0); bool getOpenWires(TopoShape &shape, const char *op="", bool noOriginal=true); bool getResultWires(TopoShape &shape, const char *op=""); @@ -53,9 +53,9 @@ public: #else void Build(const Message_ProgressRange& theRange = Message_ProgressRange()) override; #endif - const TopTools_ListOfShape& Modified (const TopoDS_Shape& S) override; - const TopTools_ListOfShape& Generated (const TopoDS_Shape& S) override; - Standard_Boolean IsDeleted (const TopoDS_Shape& S) override; + const TopTools_ListOfShape& Modified (const TopoDS_Shape& SThatModifies) override; + const TopTools_ListOfShape& Generated (const TopoDS_Shape& SThatGenerates) override; + Standard_Boolean IsDeleted (const TopoDS_Shape& SDeleted) override; private: class WireJoinerP;