/*************************************************************************** * 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 #endif #include #include #include #include #include #include #include "DrawUtil.h" #include "EWTOLERANCE.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.ed = e; we.idx = get(edge_index, m_g, e); wireEdges.push_back(we); } void edgeVisitor::begin_face() { // Base::Console().Message("EV::begin_face()\n"); wireEdges.clear(); } void edgeVisitor::end_face() { // Base::Console().Message("EV::end_face()\n"); graphWires.push_back(wireEdges); } TechDraw::ewWireList edgeVisitor::getResult() { return graphWires; } void edgeVisitor::setGraph(TechDraw::graph& g) { m_g = g; } //******************************************************* //* EdgeWalker methods //******************************************************* EdgeWalker::EdgeWalker() { } EdgeWalker::~EdgeWalker() { } //loads a list of unique edges into the traversal mechanism bool EdgeWalker::loadEdges(std::vector& edges) { // Base::Console().Message("EW::loadEdges(we) - WEdgesIn: %d\n", edges.size()); int idx = 0; for (auto& e: edges) { std::pair p; p = add_edge(e.v1, e.v2, m_g); e.ed = p.first; e.idx = idx; idx++; m_saveWalkerEdges.push_back(e); } return true; } bool EdgeWalker::loadEdges(std::vector edges) { // Base::Console().Message("EW::loadEdges(TopoDS) - edges: %d\n", edges.size()); if (edges.empty()) { throw Base::ValueError("EdgeWalker has no edges to load\n"); } std::vector verts = makeUniqueVList(edges); setSize(verts.size()); std::vector we = makeWalkerEdges(edges, verts); loadEdges(we); m_embedding = makeEmbedding(edges, verts); return true; } bool EdgeWalker::setSize(std::size_t size) { m_g.clear(); for (std::size_t i = 0; i < size; i++) { boost::adjacency_list<>::vertex_descriptor vd = boost::add_vertex(m_g); (void)vd; } return true; } bool EdgeWalker::prepare() { //Base::Console().Message("TRACE - EW::prepare()\n"); // Initialize the interior edge index 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++); //make planar embedding planar_embedding_storage_t planar_embedding_storage(num_vertices(m_g)); planar_embedding_t planar_embedding(planar_embedding_storage.begin(), get(vertex_index, m_g) ); for (auto& e : m_embedding) { for (auto& i: e.incidenceList) { planar_embedding[e.iVertex].push_back(i.eDesc); } } // Test for planarity using vec_t = std::vector< graph_traits::edge_descriptor >; std::vector embedding(num_vertices(m_g)); using kura_edges_t = std::vector< graph_traits::edge_descriptor >; kura_edges_t kEdges; kura_edges_t::iterator ki, ki_end; graph_traits::edge_descriptor e1; #if 0 // Function declared in block and lacking of implementation // 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); #endif bool isPlanar = boyer_myrvold_planarity_test(boyer_myrvold_params::graph = m_g, boyer_myrvold_params::embedding = &embedding[0], // this is "an" embedding but not one for finding boyer_myrvold_params::kuratowski_subgraph = // closed regions in an edge pile. std::back_inserter(kEdges)); if (!isPlanar) { //TODO: remove kura subgraph to make planar?? Base::Console().Message("EW::prepare - input is NOT planar\n"); ki_end = kEdges.end(); std::stringstream ss; ss << "EW::prepare - 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().Message("%s\n", ss.str().c_str()); return false; } m_eV.setGraph(m_g); planar_face_traversal(m_g, &planar_embedding[0], m_eV); return true; } std::vector EdgeWalker::execute(std::vector edgeList, bool biggie) { std::vector sortedWires; loadEdges(edgeList); bool success = prepare(); if (success) { std::vector rw = getResultNoDups(); sortedWires = sortStrip(rw, biggie); } return sortedWires; } ewWireList EdgeWalker::getResult() { //Base::Console().Message("TRACE - EW::getResult()\n"); 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() { //Base::Console().Message("TRACE - EW::getResultWires()\n"); std::vector fw; ewWireList result = m_eV.getResult(); if (result.wires.empty()) { return fw; } 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 = m_saveInEdges.at((*iEdge).idx); topoEdges.push_back(e); } TopoDS_Wire w = makeCleanWire(topoEdges, EWTOLERANCE); //make 1 clean wire from its edges fw.push_back(w); } return fw; } std::vector EdgeWalker::getResultNoDups() { //Base::Console().Message("TRACE - EW::getResultNoDups()\n"); std::vector fw; ewWireList result = m_eV.getResult(); if (result.wires.empty()) { return fw; } result = result.removeDuplicateWires(); std::vector::iterator iWire = result.wires.begin(); 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 = m_saveInEdges.at((*iEdge).idx); topoEdges.push_back(e); } TopoDS_Wire w = makeCleanWire(topoEdges, EWTOLERANCE); //make 1 clean wire from its edges fw.push_back(w); } return fw; } //* static *// //! make a clean wire with sorted, oriented, connected, etc edges TopoDS_Wire EdgeWalker::makeCleanWire(std::vector edges, double tol) { //Base::Console().Message("TRACE - EW::makeCleanWire()\n"); TopoDS_Wire result; BRepBuilderAPI_MakeWire mkWire; ShapeFix_ShapeTolerance sTol; Handle(ShapeExtend_WireData) wireData = new ShapeExtend_WireData(); for (const auto& e:edges) { wireData->Add(e); } Handle(ShapeFix_Wire) fixer = new ShapeFix_Wire; fixer->Load(wireData); fixer->SetPrecision(2.0 * EWTOLERANCE); fixer->SetMaxTolerance(tol); fixer->ClosedWireMode() = Standard_True; fixer->ModifyGeometryMode() = Standard_True; fixer->ModifyTopologyMode() = Standard_False; fixer->FixSelfIntersectingEdgeMode() = Standard_True; fixer->FixIntersectingEdgesMode() = Standard_True; fixer->FixIntersectingEdgesMode() = Standard_True; fixer->FixConnectedMode() = Standard_True; fixer->FixReorderMode() = Standard_True; fixer->Perform(); result = fixer->WireAPIMake(); return result; } std::vector EdgeWalker:: makeUniqueVList(std::vector edges) { // Base::Console().Message("TRACE - EW::makeUniqueVList() - edgesIn: %d\n", edges.size()); std::vector uniqueVert; for(auto& e:edges) { Base::Vector3d v1 = DrawUtil::vertex2Vector(TopExp::FirstVertex(e)); Base::Vector3d v2 = DrawUtil::vertex2Vector(TopExp::LastVertex(e)); bool addv1 = true; bool addv2 = true; //check if we've already added this vertex for (const auto& v: uniqueVert) { Base::Vector3d v3d = DrawUtil::vertex2Vector(v); if (v3d.IsEqual(v1, EWTOLERANCE)) { addv1 = false; } if (v3d.IsEqual(v2, EWTOLERANCE)) { addv2 = false; } } if (addv1) { uniqueVert.push_back(TopExp::FirstVertex(e)); } if (addv2) { uniqueVert.push_back(TopExp::LastVertex(e)); } } // Base::Console().Message("EW::makeUniqueVList - verts out: %d\n", uniqueVert.size()); return uniqueVert; } //!make WalkerEdges (unique Vertex index pairs) from edge list std::vector EdgeWalker::makeWalkerEdges(std::vector edges, std::vector verts) { // Base::Console().Message("TRACE - EW::makeWalkerEdges() - edges: %d verts: %d\n", edges.size(), verts.size()); m_saveInEdges = edges; std::vector walkerEdges; for (const auto& e:edges) { TopoDS_Vertex edgeVertex1 = TopExp::FirstVertex(e); TopoDS_Vertex edgeVertex2 = TopExp::LastVertex(e); std::size_t vertex1Index = findUniqueVert(edgeVertex1, verts); if (vertex1Index == SIZE_MAX) { continue; } std::size_t vertex2Index = findUniqueVert(edgeVertex2, verts); if (vertex2Index == SIZE_MAX) { continue; } WalkerEdge we; we.v1 = vertex1Index; we.v2 = vertex2Index; we.idx = 0; walkerEdges.push_back(we); } //Base::Console().Message("TRACE - EW::makeWalkerEdges - returns we: %d\n", walkerEdges.size()); return walkerEdges; } size_t EdgeWalker::findUniqueVert(TopoDS_Vertex vx, std::vector &uniqueVert) { // Base::Console().Message("TRACE - EW::findUniqueVert()\n"); std::size_t idx = 0; std::size_t result = SIZE_MAX; Base::Vector3d vx3d = DrawUtil::vertex2Vector(vx); for(auto& v : uniqueVert) { Base::Vector3d v3d = DrawUtil::vertex2Vector(v); if (vx3d.IsEqual(v3d, EWTOLERANCE)) { result = idx; break; } idx++; } return result; } std::vector EdgeWalker::sortStrip(std::vector fw, bool includeBiggest) { std::vector closedWires; //all the wires should be closed, but anomalies happen for (auto& w: fw) { if (BRep_Tool::IsClosed(w)) { closedWires.push_back(w); } } std::vector sortedWires = sortWiresBySize(closedWires, false); //biggest 1st if (sortedWires.empty()) { Base::Console().Message("EW::sortStrip - no sorted Wires!\n"); return sortedWires; } if (!includeBiggest) { sortedWires.erase(sortedWires.begin()); } return sortedWires; } // sort (closed) wires in order of enclosed area std::vector EdgeWalker::sortWiresBySize(std::vector& w, bool ascend) { //Base::Console().Message("TRACE - EW::sortWiresBySize()\n"); 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 enclosed area is bigger than w2 enclosed area /*static*/bool EdgeWalker::wireCompare(const TopoDS_Wire& w1, const TopoDS_Wire& w2) { double area1 = ShapeAnalysis::ContourArea(w1); double area2 = ShapeAnalysis::ContourArea(w2); return area1 > area2; } std::vector EdgeWalker::makeEmbedding(const std::vector edges, const std::vector uniqueVList) { // Base::Console().Message("TRACE - EW::makeEmbedding(edges: %d, verts: %d)\n", // edges.size(), uniqueVList.size()); std::vector result; std::size_t iVert = 0; //make an embedItem for each vertex in uniqueVList //for each vertex v // find all the edges that have v as first or last vertex for (auto& v: uniqueVList) { TopoDS_Vertex cv = v; //v is const but we need non-const for vertexEqual std::size_t iEdge = 0; std::vector iiList; for (auto& e: edges) { double angle = 0; TopoDS_Vertex edgeVertex1 = TopExp::FirstVertex(e); TopoDS_Vertex edgeVertex2 = TopExp::LastVertex(e); if (DrawUtil::vertexEqual(cv, edgeVertex1)) { angle = DrawUtil::incidenceAngleAtVertex(e,v,EWTOLERANCE); incidenceItem ii(iEdge, angle, m_saveWalkerEdges[iEdge].ed); iiList.push_back(ii); } else if (DrawUtil::vertexEqual(cv, edgeVertex2)) { angle = DrawUtil::incidenceAngleAtVertex(e,v,EWTOLERANCE); incidenceItem ii(iEdge, angle, m_saveWalkerEdges[iEdge].ed); iiList.push_back(ii); } iEdge++; } //sort incidenceList by angle iiList = embedItem::sortIncidenceList(iiList, false); embedItem embed(iVert, iiList); result.push_back(embed); iVert++; } return result; } //! get incidence row as edge indices for v'th vertex std::vector EdgeWalker::getEmbeddingRowIx(int v) { // //Base::Console().Message("TRACE - EW::getEmbeddingRowIx(%d)\n", v); std::vector result; embedItem ei = m_embedding[v]; for (auto& ii: ei.incidenceList) { result.push_back(ii.iEdge); } return result; } //! get incidence row as edgeDescriptors for v'th vertex std::vector EdgeWalker::getEmbeddingRow(int v) { // //Base::Console().Message("TRACE - EW::getEmbeddingRow(%d)\n", v); std::vector result; embedItem ei = m_embedding[v]; for (auto& ii: ei.incidenceList) { result.push_back(ii.eDesc); } return result; } //******************************************* // WalkerEdge Methods //******************************************* 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); } std::string WalkerEdge::dump() { std::string result; std::stringstream builder; builder << "WalkerEdge - v1: " << v1 << " v2: " << v2 << " idx: " << idx << " ed: " << ed; result = builder.str(); return result; } //***************************************** // ewWire Methods //***************************************** 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); } std::size_t ewWire::size(void) { return wedges.size(); } //*************************************** // ewWireList methods //*************************************** //check wirelist for wires that use the same set of edges, but maybe in a different order. ewWireList ewWireList::removeDuplicateWires() { 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::size_t ewWireList::size(void) { return wires.size(); } //************************************* //* embedItem Methods //************************************* std::string embedItem::dump() { std::string result; std::stringstream builder; builder << "embedItem - vertex: " << iVertex << " incidenceList: "; for (auto& ii : incidenceList) { builder << " e:" << ii.iEdge << "/a:" << (ii.angle * (180.0/M_PI)) << "/ed:" << ii.eDesc; } result = builder.str(); return result; } std::vector embedItem::sortIncidenceList (std::vector &list, bool ascend) { //Base::Console().Message("TRACE - eI::sortIncidenceList()\n"); std::vector< incidenceItem > tempList = list; std::sort(tempList.begin(), tempList.end(), incidenceItem::iiCompare); if (ascend) { std::reverse(tempList.begin(), tempList.end()); } return tempList; } //************************************* //* incidenceItem Methods //************************************* /*static*/ bool incidenceItem::iiCompare(const incidenceItem& i1, const incidenceItem& i2) { return (i1.angle > i2.angle); } /*static*/bool incidenceItem::iiEqual(const incidenceItem& i1, const incidenceItem& i2) { //TODO: this should compare edges also but eDesc comparison is by address bool result = false; if (i1.angle == i2.angle) { } return result; }