/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 CERN * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. * * @author Alejandro GarcĂ­a Montoro * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include void unfracture( SHAPE_POLY_SET::POLYGON* aPoly, SHAPE_POLY_SET::POLYGON* aResult ) { assert( aPoly->size() == 1 ); struct EDGE { int m_index = 0; SHAPE_LINE_CHAIN* m_poly = nullptr; bool m_duplicate = false; EDGE( SHAPE_LINE_CHAIN *aPolygon, int aIndex ) : m_index(aIndex), m_poly(aPolygon) {} bool compareSegs( const SEG& s1, const SEG& s2) const { return (s1.A == s2.A && s1.B == s2.B) || (s1.A == s2.B && s1.B == s2.A); } bool operator==( const EDGE& aOther ) const { return compareSegs( m_poly->Segment( m_index ), aOther.m_poly->Segment( aOther.m_index ) ); } bool operator!=( const EDGE& aOther ) const { return !compareSegs( m_poly->Segment( m_index ), aOther.m_poly->Segment( aOther.m_index ) ); } struct HASH { std::size_t operator()( const EDGE& aEdge ) const { const auto& a = aEdge.m_poly->Segment( aEdge.m_index ); return (std::size_t) ( a.A.x + a.B.x + a.A.y + a.B.y ); } }; }; struct EDGE_LIST_ENTRY { int index; EDGE_LIST_ENTRY *next; }; std::unordered_set uniqueEdges; auto lc = (*aPoly)[0]; lc.Simplify(); auto edgeList = std::make_unique( lc.SegmentCount() ); for(int i = 0; i < lc.SegmentCount(); i++) { edgeList[i].index = i; edgeList[i].next = &edgeList[ (i != lc.SegmentCount() - 1) ? i + 1 : 0 ]; } std::unordered_set queue; for(int i = 0; i < lc.SegmentCount(); i++) { EDGE e ( &lc, i ); uniqueEdges.insert( e ); } for(int i = 0; i < lc.SegmentCount(); i++) { EDGE e ( &lc, i ); auto it = uniqueEdges.find(e); if (it != uniqueEdges.end() && it->m_index != i ) { int e1 = it->m_index; int e2 = i; if( e1 > e2 ) std::swap(e1, e2); int e1_prev = e1 - 1; if (e1_prev < 0) e1_prev = lc.SegmentCount() - 1; int e2_prev = e2 - 1; if (e2_prev < 0) e2_prev = lc.SegmentCount() - 1; int e1_next = e1 + 1; if (e1_next == lc.SegmentCount() ) e1_next = 0; int e2_next = e2 + 1; if (e2_next == lc.SegmentCount() ) e2_next = 0; edgeList[e1_prev].next = &edgeList[ e2_next ]; edgeList[e2_prev].next = &edgeList[ e1_next ]; edgeList[i].next = nullptr; edgeList[it->m_index].next = nullptr; } } for(int i = 0; i < lc.SegmentCount(); i++) { if ( edgeList[i].next ) queue.insert ( &edgeList[i] ); } auto edgeBuf = std::make_unique( lc.SegmentCount() ); int n = 0; int outline = -1; aResult->clear(); while (queue.size()) { auto e_first = (*queue.begin()); auto e = e_first; int cnt=0; do { edgeBuf[cnt++] = e; e = e->next; } while( e != e_first ); SHAPE_LINE_CHAIN outl; for(int i = 0; i < cnt ;i++) { auto p = lc.CPoint(edgeBuf[i]->index); outl.Append( p ); queue.erase( edgeBuf[i] ); } // auto p_last = lc.Point( edgeBuf[cnt-1]->index + 1 ); // outl.Append( p_last ); outl.SetClosed(true); bool cw = outl.Area() > 0.0; if(cw) outline = n; aResult->push_back(outl); n++; } assert(outline >= 0); if(outline !=0 ) std::swap( (*aResult) [0], (*aResult)[outline] ); } enum POLY_TRI_RET_CODES { LOAD_FAILED = KI_TEST::RET_CODES::TOOL_SPECIFIC, }; int polygon_triangulation_main( int argc, char *argv[] ) { std::string filename; if( argc > 1 ) filename = argv[1]; auto brd = KI_TEST::ReadBoardFromFileOrStream( filename ); if( !brd ) return POLY_TRI_RET_CODES::LOAD_FAILED; PROF_COUNTER cnt( "allBoard" ); std::atomic zonesToTriangulate( 0 ); std::atomic threadsFinished( 0 ); size_t parallelThreadCount = std::max( std::thread::hardware_concurrency(), 2 ); for( size_t ii = 0; ii < parallelThreadCount; ++ii ) { std::thread t = std::thread( [&brd, &zonesToTriangulate, &threadsFinished]() { for( size_t areaId = zonesToTriangulate.fetch_add( 1 ); areaId < static_cast( brd->GetAreaCount() ); areaId = zonesToTriangulate.fetch_add( 1 ) ) { auto zone = brd->GetArea( areaId ); // NOTE: this could be refactored to do multiple layers from the same zone in // parallel, but since the test case doesn't have any of these, I'm not bothering // to do that right now. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() ) { SHAPE_POLY_SET poly = zone->GetFilledPolysList( layer ); poly.CacheTriangulation(); ignore_unused( poly ); #if 0 PROF_COUNTER unfrac("unfrac"); poly.Unfracture( SHAPE_POLY_SET::PM_FAST ); unfrac.Show(); PROF_COUNTER triangulate("triangulate"); for(int i =0; i< poly.OutlineCount(); i++) { poly.triangulatePoly( &poly.Polygon(i) ); } triangulate.Show(); #endif } } threadsFinished++; } ); t.detach(); } while( threadsFinished < parallelThreadCount ) std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) ); cnt.Show(); return KI_TEST::RET_CODES::OK; } static bool registered = UTILITY_REGISTRY::Register( { "polygon_triangulation", "Process polygon triangulation on a PCB", polygon_triangulation_main, } );