/*************************************************************************** * Copyright (c) 2016 Wandererfan * * * * 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 * * * ***************************************************************************/ /* * * Some material based on Boost sample code */ // Distributed under the Boost Software License, Version 1.0. (See // http://www.boost.org/LICENSE_1_0.txt) //************************************************************************** #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #include #include #include #include #include #include #include #endif #include #include #include #include "DrawUtil.h" #include "EdgeWalker.h" using namespace TechDraw; using namespace boost; //******************************************************* //* edgeVisior methods //******************************************************* template void edgeVisitor::next_edge(Edge e) { graph_traits::vertex_descriptor s = source(e,m_g); graph_traits::vertex_descriptor t = target(e,m_g); WalkerEdge we; we.v1 = s; we.v2 = t; we.idx = get(edge_index,m_g,e); wireEdges.push_back(we); } void edgeVisitor::begin_face() { wireEdges.clear(); } void edgeVisitor::end_face() { graphWires.push_back(wireEdges); } TechDraw::ewWireList edgeVisitor::getResult(void) { return graphWires; } void edgeVisitor::setGraph(TechDraw::graph& g) { m_g = g; } //******************************************************* //* EdgeWalker //******************************************************* EdgeWalker::EdgeWalker() : duplicateInput(false) { } EdgeWalker::~EdgeWalker() { } //loads a list of unique edges into the traversal mechanism bool EdgeWalker::loadEdges(std::vector edges) { for (auto e: edges) { add_edge(e.v1,e.v2,m_g); } return true; } bool EdgeWalker::loadEdges(std::vector edges) { if (edges.empty()) { throw Base::Exception("EdgeWalker has no edges to load\n"); } std::vector verts = makeUniqueVList(edges); setSize(verts.size()); std::vector we = makeWalkerEdges(edges, verts); return loadEdges(we); } bool EdgeWalker::setSize(int size) { m_g.clear(); for (int i = 0; i < size; i++) { boost::adjacency_list<>::vertex_descriptor vd = boost::add_vertex(m_g); } return true; } bool EdgeWalker::perform() { // Initialize the interior edge index //property property_map::type e_index = get(edge_index, m_g); graph_traits::edges_size_type edge_count = 0; graph_traits::edge_iterator ei, ei_end; for(boost::tie(ei, ei_end) = edges(m_g); ei != ei_end; ++ei) put(e_index, *ei, edge_count++); // Test for planarity typedef std::vector< graph_traits::edge_descriptor > vec_t; std::vector embedding(num_vertices(m_g)); typedef std::vector< graph_traits::edge_descriptor > kura_edges_t; kura_edges_t kEdges; kura_edges_t::iterator ki, ki_end; graph_traits::edge_descriptor e1; // Get the index associated with edge graph_traits::edges_size_type get(boost::edge_index_t, const TechDraw::graph& m_g, graph_traits::edge_descriptor edge); bool isPlanar = boyer_myrvold_planarity_test(boyer_myrvold_params::graph = m_g, boyer_myrvold_params::embedding = &embedding[0], boyer_myrvold_params::kuratowski_subgraph = std::back_inserter(kEdges)); if (!isPlanar) { //TODO: remove kura subgraph to make planar?? Base::Console().Log("LOG - EW::perform - input is NOT planar\n"); ki_end = kEdges.end(); std::stringstream ss; ss << "EW::perform - obstructing edges: "; for(ki = kEdges.begin(); ki != ki_end; ++ki) { e1 = *ki; ss << boost::get(edge_index,m_g,e1) << ","; } ss << std::endl; Base::Console().Log("LOG - %s\n",ss.str().c_str()); return false; } m_eV.setGraph(m_g); //Base::Console().Message("TRACE - EW::perform - setGraph complete\n"); planar_face_traversal(m_g, &embedding[0], m_eV); //Base::Console().Message("TRACE - EW::perform - traversal complete\n"); return true; } ewWireList EdgeWalker::getResult() { ewWireList result = m_eV.getResult(); // result is a list of many wires each of which is a list of many WE return result; } std::vector EdgeWalker::getResultWires() { std::vector fw; ewWireList result = m_eV.getResult(); if (result.wires.empty()) { return fw; } //convert from noduplicate index to duplicates index for (auto& w:result.wires) { for (auto& we:w.wedges) { //we.idx is the edge index in the short list (no duplicates) //saveIndex[we.idx] should be the index in the long list we.idx = saveIndex[we.idx]; } } std::vector::iterator iWire = result.wires.begin(); // a WE within [WE] for (;iWire != result.wires.end(); iWire++) { std::vector::iterator iEdge = (*iWire).wedges.begin(); std::vector topoEdges; for (;iEdge != (*iWire).wedges.end(); iEdge++) { TopoDS_Edge e = saveInEdges.at((*iEdge).idx); topoEdges.push_back(e); } TopoDS_Wire w = makeCleanWire(topoEdges); //make 1 clean wire from its edges fw.push_back(w); } return fw; } std::vector EdgeWalker::getResultNoDups() { std::vector fw; ewWireList result = m_eV.getResult(); if (result.wires.empty()) { return fw; } //convert from noduplicate index to duplicates index for (auto& w:result.wires) { for (auto& we:w.wedges) { //we.idx is the edge index in the short list (no duplicates) //saveIndex[we.idx] should be the index in the long list we.idx = saveIndex[we.idx]; } } result = result.removeDuplicates(); std::vector::iterator iWire = result.wires.begin(); int edgeCount = 1; for (;iWire != result.wires.end(); iWire++) { std::vector::iterator iEdge = (*iWire).wedges.begin(); std::vector topoEdges; for (;iEdge != (*iWire).wedges.end(); iEdge++) { TopoDS_Edge e = saveInEdges.at((*iEdge).idx); topoEdges.push_back(e); edgeCount++; } TopoDS_Wire w = makeCleanWire(topoEdges); //make 1 clean wire from its edges fw.push_back(w); } return fw; } //! make a clean wire with sorted, oriented, connected, etc edges TopoDS_Wire EdgeWalker::makeCleanWire(std::vector edges, double tol) { TopoDS_Wire result; BRepBuilderAPI_MakeWire mkWire; ShapeFix_ShapeTolerance sTol; Handle(ShapeExtend_WireData) wireData = new ShapeExtend_WireData(); for (auto e:edges) { wireData->Add(e); } Handle(ShapeFix_Wire) fixer = new ShapeFix_Wire; fixer->Load(wireData); fixer->Perform(); fixer->FixReorder(); fixer->SetMaxTolerance(tol); fixer->ClosedWireMode() = Standard_True; fixer->FixConnected(Precision::Confusion()); fixer->FixClosed(Precision::Confusion()); for (int i = 1; i <= wireData->NbEdges(); i ++) { TopoDS_Edge edge = fixer->WireData()->Edge(i); sTol.SetTolerance(edge, tol, TopAbs_VERTEX); mkWire.Add(edge); } result = mkWire.Wire(); return result; } std::vector EdgeWalker:: makeUniqueVList(std::vector edges) { std::vector uniqueVert; for(auto& e:edges) { TopoDS_Vertex v1 = TopExp::FirstVertex(e); TopoDS_Vertex v2 = TopExp::LastVertex(e); bool addv1 = true; bool addv2 = true; for (auto v:uniqueVert) { if (DrawUtil::isSamePoint(v,v1)) addv1 = false; if (DrawUtil::isSamePoint(v,v2)) addv2 = false; } if (addv1) uniqueVert.push_back(v1); if (addv2) uniqueVert.push_back(v2); } return uniqueVert; } //!make WalkerEdges (unique Vertex index pairs) from edge list //remove duplicate edges from input std::vector EdgeWalker::makeWalkerEdges(std::vector edges, std::vector verts) { saveInEdges = edges; std::vector rawList; for (auto e:edges) { TopoDS_Vertex ev1 = TopExp::FirstVertex(e); TopoDS_Vertex ev2 = TopExp::LastVertex(e); int v1dx = findUniqueVert(ev1, verts); int v2dx = findUniqueVert(ev2, verts); WalkerEdge rl; rl.v1 = v1dx; rl.v2 = v2dx; rawList.push_back(rl); } std::vector we = removeDuplicateInput(rawList); for (auto& w:we) { saveIndex.push_back(w.idx); } return we; } int EdgeWalker::findUniqueVert(TopoDS_Vertex vx, std::vector &uniqueVert) { int idx = 0; int result = 0; for(auto& v:uniqueVert) { //we're always going to find vx, right? if (DrawUtil::isSamePoint(v,vx)) { result = idx; break; } idx++; } //if idx >= uniqueVert.size() TARFU return result; } //removes duplicates from input and sets idx to position in original list std::vector EdgeWalker::removeDuplicateInput(std::vector input) { std::vector result; //std::vector ref; if (input.empty()) { return result; } result.push_back(*(input.begin())); //save the first WE result[0].idx = 0; //ref.push_back(0); std::vector::iterator iWE = (input.begin()) + 1; //starting with second int i = 1; for (; iWE != input.end(); iWE++, i++) { bool addToResult = true; for (auto& w:result) { if ((*iWE).isEqual(w)) { //already in result? addToResult = false; Base::Console().Log("LOG - EW::removeDuplicateInput - input edge: %d is a duplicate\n",i); break; } } if (addToResult) { (*iWE).idx = i; result.push_back((*iWE)); //ref.push_back(i); } } return result; } bool WalkerEdge::isEqual(WalkerEdge w) { bool result = false; if ((( v1 == w.v1) && (v2 == w.v2)) || (( v1 == w.v2) && (v2 == w.v1)) ) { result = true; } return result; } /*static*/ bool WalkerEdge::weCompare(WalkerEdge i, WalkerEdge j) //used for sorting { return (i.idx < j.idx); } bool ewWire::isEqual(ewWire w2) { bool result = true; if (wedges.size() != w2.wedges.size()) { result = false; } else { std::sort(wedges.begin(),wedges.end(),WalkerEdge::weCompare); std::sort(w2.wedges.begin(),w2.wedges.end(),WalkerEdge::weCompare); for (unsigned int i = 0; i < w2.wedges.size(); i ++) { if (wedges.at(i).idx != w2.wedges.at(i).idx) { result = false; break; } } } return result; } void ewWire::push_back(WalkerEdge w) { wedges.push_back(w); } //check wirelist for wires that use the same set of edges, but maybe in a different order. ewWireList ewWireList::removeDuplicates() { ewWireList result; if (wires.empty()) { return result; } result.push_back(*(wires.begin())); //save the first ewWire std::vector::iterator iWire = (wires.begin()) + 1; //starting with second for (; iWire != wires.end(); iWire++) { bool addToResult = true; for (auto& w:result.wires) { if ((*iWire).isEqual(w)) { //already in result? addToResult = false; break; } } if (addToResult) { result.push_back((*iWire)); } } return result; } void ewWireList::push_back(ewWire w) { wires.push_back(w); } std::vector EdgeWalker::sortStrip(std::vector fw, bool includeBiggest) { std::vector sortedWires = sortWiresBySize(fw,false); //biggest 1st if (!sortedWires.size()) { Base::Console().Log("INFO - DVP::extractFaces - no sorted Wires!\n"); return sortedWires; // might happen in the middle of changes? } //find the largest wire (OuterWire of graph) using bbox Bnd_Box bigBox; if (sortedWires.size() && !sortedWires.front().IsNull()) { BRepBndLib::Add(sortedWires.front(), bigBox); bigBox.SetGap(0.0); } std::vector toBeChecked; std::vector::iterator it = sortedWires.begin() + 1; for (; it != sortedWires.end(); it++) { if (!(*it).IsNull()) { Bnd_Box littleBox; BRepBndLib::Add((*it), littleBox); littleBox.SetGap(0.0); if (bigBox.SquareExtent() > littleBox.SquareExtent()) { break; } else { auto position = std::distance( sortedWires.begin(), it ); //get an index from iterator toBeChecked.push_back(position); } } } //unfortuneately, faces can have same bbox, but not be same size. need to weed out biggest if (toBeChecked.size() == 0) { //nobody had as big a bbox as first element of sortedWires if (!includeBiggest) { sortedWires.erase(sortedWires.begin()); } } else if (toBeChecked.size() > 0) { BRepBuilderAPI_MakeFace mkFace(sortedWires.front()); const TopoDS_Face& face = mkFace.Face(); GProp_GProps props; BRepGProp::SurfaceProperties(face, props); double bigArea = props.Mass(); unsigned int bigIndex = 0; for (unsigned int idx = 0; idx < toBeChecked.size(); idx++) { int iCheck = toBeChecked.at(idx); BRepBuilderAPI_MakeFace mkFace2(sortedWires.at(iCheck)); const TopoDS_Face& face2 = mkFace2.Face(); BRepGProp::SurfaceProperties(face2, props); double area = props.Mass(); if (area > bigArea) { bigArea = area; bigIndex = iCheck; } } if (bigIndex == 0) { //first wire is the biggest if (!includeBiggest) { sortedWires.erase(sortedWires.begin()); } } else { //first wire is not the biggest TopoDS_Wire bigWire = *(sortedWires.begin() + bigIndex); sortedWires.erase(sortedWires.begin() + bigIndex); if (includeBiggest) { sortedWires.insert(sortedWires.begin(),bigWire); //doesn't happen often } } } return sortedWires; } //sort wires in order of bbox diagonal. std::vector EdgeWalker::sortWiresBySize(std::vector& w, bool ascend) { std::vector wires = w; std::sort(wires.begin(), wires.end(), EdgeWalker::wireCompare); if (ascend) { std::reverse(wires.begin(),wires.end()); } return wires; } //! return true if w1 bbox is bigger than w2 bbox //NOTE: this won't necessarily sort the OuterWire correctly (ex smaller wire, same bbox) /*static*/bool EdgeWalker::wireCompare(const TopoDS_Wire& w1, const TopoDS_Wire& w2) { Bnd_Box box1, box2; if (!w1.IsNull()) { BRepBndLib::Add(w1, box1); box1.SetGap(0.0); } if (!w2.IsNull()) { BRepBndLib::Add(w2, box2); box2.SetGap(0.0); } return box1.SquareExtent() > box2.SquareExtent(); }