From 4f7bcded78e0161ef545a1ec0b57cfe56df93d22 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 9 Nov 2017 15:27:58 +0800 Subject: [PATCH] Path.Area: improve projection algorithm efficiency --- src/Mod/Path/App/Area.cpp | 205 +++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 72 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 237969b29f..960f2f2e77 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -510,33 +510,41 @@ struct WireJoiner { gp_Pnt p2; Box box; int iteration; + int iStart[2]; // adjacent list index start for p1 and p2 + int iEnd[2]; // adjacent list index end bool used; bool hasBox; EdgeInfo(const TopoDS_Edge &e, bool bbox) - :edge(e),iteration(0) ,used(false),hasBox(false) + :edge(e),hasBox(false) { getEndPoints(e,p1,p2); if(bbox) hasBox= getBBox(e,box); + reset(); } EdgeInfo(const TopoDS_Edge &e, const gp_Pnt &pt1, const gp_Pnt &pt2, bool bbox) - :edge(e),p1(pt1),p2(pt2),iteration(0) - ,used(false),hasBox(false) + :edge(e),p1(pt1),p2(pt2),hasBox(false) { if(bbox) hasBox= getBBox(e,box); + reset(); + } + void reset() { + iteration = 0; + used = false; + iStart[0] = iStart[1] = iEnd[0] = iEnd[1] = -1; } }; typedef std::list Edges; Edges edges; - struct EdgeValue { + struct VertexInfo { Edges::iterator it; bool start; - EdgeValue(Edges::iterator _it, bool _start) - :it(_it),start(_start) + VertexInfo(Edges::iterator it, bool start) + :it(it),start(start) {} - bool operator==(const EdgeValue &other) const { + bool operator==(const VertexInfo &other) const { return it==other.it && start==other.start; } const gp_Pnt &pt() const { @@ -550,12 +558,12 @@ struct WireJoiner { struct PntGetter { typedef const gp_Pnt& result_type; - result_type operator()(const EdgeValue &v) const { + result_type operator()(const VertexInfo &v) const { return v.pt(); } }; - bgi::rtree vmap; + bgi::rtree vmap; struct BoxGetter { @@ -576,14 +584,14 @@ struct WireJoiner { void remove(Edges::iterator it) { if(it->hasBox) boxMap.remove(it); - vmap.remove(EdgeValue(it,true)); - vmap.remove(EdgeValue(it,false)); + vmap.remove(VertexInfo(it,true)); + vmap.remove(VertexInfo(it,false)); edges.erase(it); } void add(Edges::iterator it) { - vmap.insert(EdgeValue(it,true)); - vmap.insert(EdgeValue(it,false)); + vmap.insert(VertexInfo(it,true)); + vmap.insert(VertexInfo(it,false)); if(it->hasBox) boxMap.insert(it); } @@ -626,7 +634,7 @@ struct WireJoiner { bool done = false; for(int idx=0;!done&&idx<2;++idx) { while(edges.size()) { - std::vector ret; + std::vector ret; ret.reserve(1); const gp_Pnt &pt = idx?pstart:pend; vmap.query(bgi::nearest(pt,1),std::back_inserter(ret)); @@ -749,12 +757,6 @@ struct WireJoiner { #endif } - struct StackInfo { - std::deque ret; - int idx; - StackInfo():idx(0){} - }; - // 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. @@ -767,76 +769,122 @@ struct WireJoiner { // below, and call makeCleanWire to fix the tolerance const double tol = 1e-10; - std::map edgesToVisit; + std::vector adjacentList; + std::set edgesToVisit; int count = 0; - for(const auto &info : edges) { + int skips = 0; + + for(auto &info : edges) + info.reset(); + + FC_TIME_INIT(t); + int rcount = 0; + + for(auto &info : edges) { #if OCC_VERSION_HEX >= 0x070000 if(BRep_Tool::IsClosed(info.edge)) #else - gp_Pnt p1,p2; - getEndPoints(info.edge,p1,p2); - if(p1.SquareDistance(p2)=0) + continue; + info.iEnd[i] = info.iStart[i] = (int)adjacentList.size(); + + // populate adjacent list + for(auto vit=vmap.qbegin(bgi::nearest(pt[i],INT_MAX));vit!=vmap.qend();++vit) { + ++rcount; + if(vit->pt().SquareDistance(pt[i]) > tol) + break; + auto &vinfo = *vit; + // yse, we push ourself, too, because other edges require + // this info in the adjcent list. We'll do filtering later. + adjacentList.push_back(vinfo); + ++info.iEnd[i]; + } + // copy the adjacent indices to all connected edges + for(int j=info.iStart[i];j stack; - int skips = 0; + FC_TIME_LOG(t,"rtree::nearest (" << rcount << ')'); + + struct StackInfo { + size_t iStart; + size_t iEnd; + size_t iCurrent; + StackInfo(size_t idx):iStart(idx),iEnd(idx),iCurrent(idx){} + }; + std::vector stack; + std::vector vertexStack; for(int iteration=1;edgesToVisit.size();++iteration) { - auto it = edgesToVisit.begin(); - TopoDS_Edge e = it->second; - edgesToVisit.erase(it); - gp_Pnt pstart,pend; - getEndPoints(e,pstart,pend); - bool skip_me = true; + EdgeInfo *currentInfo = *edgesToVisit.begin(); + int currentIdx = 1; // used to tell whether search connection from the start(0) or end(1) + TopoDS_Edge &e = currentInfo->edge; + edgesToVisit.erase(edgesToVisit.begin()); + gp_Pnt pstart=currentInfo->p1; + gp_Pnt pend=currentInfo->p2; + currentInfo->used = true; + currentInfo->iteration = iteration; stack.clear(); + vertexStack.clear(); // pstart and pend is the start and end vertex of the current wire while(true) { // push a new stack entry - stack.emplace_back(); + stack.emplace_back(vertexStack.size()); auto &r = stack.back(); // this loop is to find all edges connected to pend, and save them into stack.back() - for(auto vit=vmap.qbegin(bgi::nearest(pend,INT_MAX)); - vit!=vmap.qend();++vit) - { - if(vit->pt().SquareDistance(pend) > tol) - break; - - auto &info = *vit->it; - - // use 'iteration' to make sure we don't visit an edge twice. - if(skip_me && info.edge.IsSame(e)) { - skip_me = false; - info.iteration = iteration; - info.used = true; - continue; - } + for(int i=currentInfo->iStart[currentIdx];iiEnd[currentIdx];++i) { + auto &info = *adjacentList[i].it; if(info.iteration!=iteration) { info.iteration = iteration; - r.ret.push_back(*vit); + vertexStack.push_back(adjacentList[i]); + ++r.iEnd; } } - while(stack.size()) { + while(true) { auto &r = stack.back(); - if(r.idx<(int)r.ret.size()) { + if(r.iCurrentiteration; + vertexStack.erase(vertexStack.begin()+r.iStart,vertexStack.end()); stack.pop_back(); - if(stack.size()) - ++stack.back().idx; + if(stack.empty()) + break; + ++stack.back().iCurrent; } if(stack.empty()) { // If stack is empty, it means this wire is open. Try a new @@ -845,21 +893,16 @@ struct WireJoiner { break; } if(pstart.SquareDistance(pend) > tol) { - // check if the wire is closed + // if the wire is not closed yet, continue search for the + // next connected edge continue; } Handle(ShapeExtend_WireData) wireData = new ShapeExtend_WireData(); wireData->Add(e); for(auto &r : stack) { - const auto &v = r.ret[r.idx]; + const auto &v = vertexStack[r.iCurrent]; auto &info = *v.it; - if(!info.used) { - info.used = true; - auto it = edgesToVisit.find(info.edge.HashCode(INT_MAX)); - if(it!=edgesToVisit.end()) - edgesToVisit.erase(it); - } if(v.start) wireData->Add(info.edge); else @@ -867,14 +910,26 @@ struct WireJoiner { } // TechDraw even uses 0.1 as tolerance. Really? Why? TopoDS_Wire wire = makeCleanWire(wireData,0.01); - if(!BRep_Tool::IsClosed(wire)) - throw Base::RuntimeError("Area: failed to close projection wire"); - builder.Add(comp,wire); - ++count; + if(!BRep_Tool::IsClosed(wire)) { + FC_WARN("failed to close some projection wire"); + ++skips; + }else{ + for(auto &r : stack) { + const auto &v = vertexStack[r.iCurrent]; + auto &info = *v.it; + if(!info.used) { + info.used = true; + edgesToVisit.erase(&info); + } + } + Area::showShape(wire,"joined"); + builder.Add(comp,wire); + ++count; + } break; } } - AREA_TRACE("found " << count << " closed wires, skipped " << skips); + FC_TIME_LOG(t,"found " << count << " closed wires, skipped " << skips << "edges. "); return skips; #endif } @@ -1122,9 +1177,15 @@ int Area::project(TopoDS_Shape &shape_out, TopoDS_Shape shape; HLRBRep_HLRToShape hlrToShape(brep_hlr); ADD_HLR_SHAPE(V) - // ADD_HLR_SHAPE(H) ADD_HLR_SHAPE(OutLineV); + // ADD_HLR_SHAPE(Rg1LineV); + // ADD_HLR_SHAPE(RgNLineV); + // ADD_HLR_SHAPE(IsoLineV); + // ADD_HLR_SHAPE(H) + // ADD_HLR_SHAPE(Rg1LineH); + // ADD_HLR_SHAPE(RgNLineH); // ADD_HLR_SHAPE(OutLineH); + // ADD_HLR_SHAPE(IsoLineH); } catch (...) { AREA_ERR("error occurred while extracting edges");