diff --git a/CMakeLists.txt b/CMakeLists.txt index c70a34237f..84e5bea757 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -478,6 +478,7 @@ include( CheckFindPackageResult ) find_package( OpenMP ) if( OPENMP_FOUND ) + message( "FOUND OPENMP" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" ) add_definitions( -DUSE_OPENMP ) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index cb7281846f..fa800948ca 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -179,6 +179,7 @@ set( COMMON_WIDGET_SRCS widgets/indicator_icon.cpp widgets/text_ctrl_eval.cpp widgets/unit_binder.cpp + widgets/progress_reporter.cpp ) set( COMMON_PAGE_LAYOUT_SRCS @@ -273,6 +274,7 @@ set( COMMON_SRCS kiway_player.cpp lib_table_base.cpp lockfile.cpp + md5_hash.cpp msgpanel.cpp netlist_keywords.cpp observable.cpp diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index f52969c6c5..3952352727 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -776,9 +776,52 @@ void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize ) drawPolygon( points.get(), aListSize ); } +void OPENGL_GAL::drawTriangulatedPolyset( const SHAPE_POLY_SET& aPolySet ) +{ + currentManager->Shader( SHADER_NONE ); + currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); + + + if ( isFillEnabled ) + { + for( int j = 0; j < aPolySet.OutlineCount(); ++j ) + { + auto triPoly = aPolySet.TriangulatedPolygon( j ); + + for ( int i = 0; i < triPoly->m_triangleCount; i++ ) + { + VECTOR2I a, b, c; + triPoly->GetTriangle( i ,a,b,c); + currentManager->Vertex( a.x, a.y, layerDepth ); + currentManager->Vertex( b.x, b.y, layerDepth ); + currentManager->Vertex( c.x, c.y, layerDepth ); + } + } + } + + if( isStrokeEnabled ) + { + for( int j = 0; j < aPolySet.OutlineCount(); ++j ) + { + const auto& poly = aPolySet.Polygon( j ); + + for( const auto& lc : poly ) + { + DrawPolyline( lc ); + } + } + } +} + void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet ) { + if ( aPolySet.IsTriangulationUpToDate() ) + { + drawTriangulatedPolyset( aPolySet ); + return; + } + for( int j = 0; j < aPolySet.OutlineCount(); ++j ) { const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j ); @@ -1470,7 +1513,6 @@ void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRa Restore(); } - void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount ) { currentManager->Shader( SHADER_NONE ); diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 01a36ca1fc..10e70d4f57 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -32,13 +32,18 @@ #include #include #include +#include #include +#include +#include #include #include #include +#include "poly2tri/poly2tri.h" + using namespace ClipperLib; SHAPE_POLY_SET::SHAPE_POLY_SET() : @@ -55,6 +60,8 @@ SHAPE_POLY_SET::SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther ) : SHAPE_POLY_SET::~SHAPE_POLY_SET() { + for( auto p : m_triangulatedPolys ) + delete p; } @@ -65,7 +72,7 @@ SHAPE* SHAPE_POLY_SET::Clone() const bool SHAPE_POLY_SET::GetRelativeIndices( int aGlobalIdx, - SHAPE_POLY_SET::VERTEX_INDEX* aRelativeIndices ) const + SHAPE_POLY_SET::VERTEX_INDEX* aRelativeIndices ) const { int polygonIdx = 0; unsigned int contourIdx = 0; @@ -105,15 +112,15 @@ bool SHAPE_POLY_SET::GetRelativeIndices( int aGlobalIdx, bool SHAPE_POLY_SET::GetGlobalIndex( SHAPE_POLY_SET::VERTEX_INDEX aRelativeIndices, - int& aGlobalIdx ) + int& aGlobalIdx ) { int selectedVertex = aRelativeIndices.m_vertex; - unsigned int selectedContour = aRelativeIndices.m_contour; - unsigned int selectedPolygon = aRelativeIndices.m_polygon; + unsigned int selectedContour = aRelativeIndices.m_contour; + unsigned int selectedPolygon = aRelativeIndices.m_polygon; // Check whether the vertex indices make sense in this poly set - if( selectedPolygon < m_polys.size() && selectedContour < m_polys[selectedPolygon].size() && - selectedVertex < m_polys[selectedPolygon][selectedContour].PointCount() ) + if( selectedPolygon < m_polys.size() && selectedContour < m_polys[selectedPolygon].size() + && selectedVertex < m_polys[selectedPolygon][selectedContour].PointCount() ) { POLYGON currentPolygon; @@ -131,7 +138,7 @@ bool SHAPE_POLY_SET::GetGlobalIndex( SHAPE_POLY_SET::VERTEX_INDEX aRelativeIndic currentPolygon = Polygon( selectedPolygon ); - for( unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx ++ ) + for( unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx++ ) { aGlobalIdx += currentPolygon[contourIdx].PointCount(); } @@ -151,6 +158,7 @@ int SHAPE_POLY_SET::NewOutline() { SHAPE_LINE_CHAIN empty_path; POLYGON poly; + empty_path.SetClosed( true ); poly.push_back( empty_path ); m_polys.push_back( poly ); @@ -161,6 +169,7 @@ int SHAPE_POLY_SET::NewOutline() int SHAPE_POLY_SET::NewHole( int aOutline ) { SHAPE_LINE_CHAIN empty_path; + empty_path.SetClosed( true ); // Default outline is the last one @@ -186,8 +195,8 @@ int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole, bool aAllowDu else idx = aHole + 1; - assert( aOutline < (int)m_polys.size() ); - assert( idx < (int)m_polys[aOutline].size() ); + assert( aOutline < (int) m_polys.size() ); + assert( idx < (int) m_polys[aOutline].size() ); m_polys[aOutline][idx].Append( x, y, aAllowDuplication ); @@ -214,16 +223,15 @@ void SHAPE_POLY_SET::InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex ) else throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) ); } - } -int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const +int SHAPE_POLY_SET::VertexCount( int aOutline, int aHole ) const { - if( m_polys.size() == 0 ) // Empty poly set + if( m_polys.size() == 0 ) // Empty poly set return 0; - if( aOutline < 0 ) // Use last outline + if( aOutline < 0 ) // Use last outline aOutline += m_polys.size(); int idx; @@ -233,10 +241,10 @@ int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const else idx = aHole + 1; - if( aOutline >= (int)m_polys.size() ) // not existing outline + if( aOutline >= (int) m_polys.size() ) // not existing outline return 0; - if( idx >= (int)m_polys[aOutline].size() ) // not existing hole + if( idx >= (int) m_polys[aOutline].size() ) // not existing hole return 0; return m_polys[aOutline][idx].PointCount(); @@ -270,8 +278,8 @@ VECTOR2I& SHAPE_POLY_SET::Vertex( int aIndex, int aOutline, int aHole ) else idx = aHole + 1; - assert( aOutline < (int)m_polys.size() ); - assert( idx < (int)m_polys[aOutline].size() ); + assert( aOutline < (int) m_polys.size() ); + assert( idx < (int) m_polys[aOutline].size() ); return m_polys[aOutline][idx].Point( aIndex ); } @@ -289,8 +297,8 @@ const VECTOR2I& SHAPE_POLY_SET::CVertex( int aIndex, int aOutline, int aHole ) c else idx = aHole + 1; - assert( aOutline < (int)m_polys.size() ); - assert( idx < (int)m_polys[aOutline].size() ); + assert( aOutline < (int) m_polys.size() ); + assert( idx < (int) m_polys[aOutline].size() ); return m_polys[aOutline][idx].CPoint( aIndex ); } @@ -331,6 +339,7 @@ const VECTOR2I& SHAPE_POLY_SET::CVertex( SHAPE_POLY_SET::VERTEX_INDEX index ) co return CVertex( index.m_vertex, index.m_polygon, index.m_contour - 1 ); } + bool SHAPE_POLY_SET::GetNeighbourIndexes( int aGlobalIndex, int* aPrevious, int* aNext ) { SHAPE_POLY_SET::VERTEX_INDEX index; @@ -346,8 +355,8 @@ bool SHAPE_POLY_SET::GetNeighbourIndexes( int aGlobalIndex, int* aPrevious, int* if( index.m_vertex == 0 ) { - index.m_vertex = lastpoint; - inext.m_vertex = 1; + index.m_vertex = lastpoint; + inext.m_vertex = 1; } else if( index.m_vertex == lastpoint ) { @@ -383,8 +392,8 @@ bool SHAPE_POLY_SET::IsPolygonSelfIntersecting( int aPolygonIndex ) // Get polygon const POLYGON poly = CPolygon( aPolygonIndex ); - SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex ); - SEGMENT_ITERATOR innerIterator; + SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex ); + SEGMENT_ITERATOR innerIterator; for( iterator = IterateSegmentsWithHoles( aPolygonIndex ); iterator; iterator++ ) { @@ -436,7 +445,7 @@ int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline ) int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline ) { - assert ( m_polys.size() ); + assert( m_polys.size() ); if( aOutline < 0 ) aOutline += m_polys.size(); @@ -451,7 +460,8 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline ) } -const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation ) +const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath, + bool aRequiredOrientation ) { Path c_path; @@ -482,7 +492,7 @@ const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath ) void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape, - POLYGON_MODE aFastMode ) + POLYGON_MODE aFastMode ) { Clipper c; @@ -510,9 +520,9 @@ void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, - const SHAPE_POLY_SET& aShape, - const SHAPE_POLY_SET& aOtherShape, - POLYGON_MODE aFastMode ) + const SHAPE_POLY_SET& aShape, + const SHAPE_POLY_SET& aOtherShape, + POLYGON_MODE aFastMode ) { Clipper c; @@ -557,19 +567,25 @@ void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& b, POLYGON_MODE } -void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a, + const SHAPE_POLY_SET& b, + POLYGON_MODE aFastMode ) { booleanOp( ctUnion, a, b, aFastMode ); } -void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a, + const SHAPE_POLY_SET& b, + POLYGON_MODE aFastMode ) { booleanOp( ctDifference, a, b, aFastMode ); } -void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, + const SHAPE_POLY_SET& b, + POLYGON_MODE aFastMode ) { booleanOp( ctIntersection, a, b, aFastMode ); } @@ -581,14 +597,15 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) // 1.0 - cos( M_PI/aCircleSegmentsCount) // aCircleSegmentsCount is most of time <= 64 and usually 8, 12, 16, 32 #define SEG_CNT_MAX 64 - static double arc_tolerance_factor[SEG_CNT_MAX+1]; + static double arc_tolerance_factor[SEG_CNT_MAX + 1]; ClipperOffset c; for( const POLYGON& poly : m_polys ) { for( unsigned int i = 0; i < poly.size(); i++ ) - c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound, etClosedPolygon ); + c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound, + etClosedPolygon ); } PolyTree solution; @@ -598,14 +615,14 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount ) // see: // www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm - if( aCircleSegmentsCount < 6 ) // avoid incorrect aCircleSegmentsCount values + if( aCircleSegmentsCount < 6 ) // avoid incorrect aCircleSegmentsCount values aCircleSegmentsCount = 6; double coeff; if( aCircleSegmentsCount > SEG_CNT_MAX || arc_tolerance_factor[aCircleSegmentsCount] == 0 ) { - coeff = 1.0 - cos( M_PI/aCircleSegmentsCount); + coeff = 1.0 - cos( M_PI / aCircleSegmentsCount ); if( aCircleSegmentsCount <= SEG_CNT_MAX ) arc_tolerance_factor[aCircleSegmentsCount] = coeff; @@ -641,6 +658,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree ) } } + // Polygon fracturing code. Work in progress. struct FractureEdge @@ -649,8 +667,8 @@ struct FractureEdge m_connected( connected ), m_next( NULL ) { - m_p1 = owner->CPoint( index ); - m_p2 = owner->CPoint( index + 1 ); + m_p1 = owner->CPoint( index ); + m_p2 = owner->CPoint( index + 1 ); } FractureEdge( int y = 0 ) : @@ -670,8 +688,8 @@ struct FractureEdge bool matches( int y ) const { - int y_min = std::min( m_p1.y, m_p2.y ); - int y_max = std::max( m_p1.y, m_p2.y ); + int y_min = std::min( m_p1.y, m_p2.y ); + int y_max = std::max( m_p1.y, m_p2.y ); return ( y >= y_min ) && ( y <= y_max ); } @@ -687,10 +705,10 @@ typedef std::vector FractureEdgeSet; static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) { - int x = edge->m_p1.x; - int y = edge->m_p1.y; - int min_dist = std::numeric_limits::max(); - int x_nearest = 0; + int x = edge->m_p1.x; + int y = edge->m_p1.y; + int min_dist = std::numeric_limits::max(); + int x_nearest = 0; FractureEdge* e_nearest = NULL; @@ -702,17 +720,18 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) int x_intersect; if( (*i)->m_p1.y == (*i)->m_p2.y ) // horizontal edge - x_intersect = std::max ( (*i)->m_p1.x, (*i)->m_p2.x ); + x_intersect = std::max( (*i)->m_p1.x, (*i)->m_p2.x ); else - x_intersect = (*i)->m_p1.x + rescale((*i)->m_p2.x - (*i)->m_p1.x, y - (*i)->m_p1.y, (*i)->m_p2.y - (*i)->m_p1.y ); + x_intersect = (*i)->m_p1.x + rescale( (*i)->m_p2.x - (*i)->m_p1.x, y - (*i)->m_p1.y, + (*i)->m_p2.y - (*i)->m_p1.y ); int dist = ( x - x_intersect ); if( dist >= 0 && dist < min_dist && (*i)->m_connected ) { - min_dist = dist; - x_nearest = x_intersect; - e_nearest = (*i); + min_dist = dist; + x_nearest = x_intersect; + e_nearest = (*i); } } @@ -720,9 +739,12 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) { int count = 0; - FractureEdge* lead1 = new FractureEdge( true, VECTOR2I( x_nearest, y ), VECTOR2I( x, y ) ); - FractureEdge* lead2 = new FractureEdge( true, VECTOR2I( x, y ), VECTOR2I( x_nearest, y ) ); - FractureEdge* split_2 = new FractureEdge( true, VECTOR2I( x_nearest, y ), e_nearest->m_p2 ); + FractureEdge* lead1 = + new FractureEdge( true, VECTOR2I( x_nearest, y ), VECTOR2I( x, y ) ); + FractureEdge* lead2 = + new FractureEdge( true, VECTOR2I( x, y ), VECTOR2I( x_nearest, y ) ); + FractureEdge* split_2 = + new FractureEdge( true, VECTOR2I( x_nearest, y ), e_nearest->m_p2 ); edges.push_back( split_2 ); edges.push_back( lead1 ); @@ -734,7 +756,8 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) e_nearest->m_next = lead1; lead1->m_next = edge; - FractureEdge*last; + FractureEdge* last; + for( last = edge; last->m_next != edge; last = last->m_next ) { last->m_connected = true; @@ -742,8 +765,8 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) } last->m_connected = true; - last->m_next = lead2; - lead2->m_next = split_2; + last->m_next = lead2; + lead2->m_next = split_2; split_2->m_next = link; return count + 1; @@ -757,7 +780,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) { FractureEdgeSet edges; FractureEdgeSet border_edges; - FractureEdge* root = NULL; + FractureEdge* root = NULL; bool first = true; @@ -770,7 +793,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) { int index = 0; - FractureEdge *prev = NULL, *first_edge = NULL; + FractureEdge* prev = NULL, * first_edge = NULL; int x_min = std::numeric_limits::max(); @@ -810,7 +833,8 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) if( !fe->m_connected ) num_unconnected++; } - first = false; // first path is always the outline + + first = false; // first path is always the outline } // keep connecting holes to the main outline, until there's no holes left... @@ -825,7 +849,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) { int xt = (*i)->m_p1.x; - if( ( xt < x_min ) && ! (*i)->m_connected ) + if( ( xt < x_min ) && !(*i)->m_connected ) { x_min = xt; smallestX = *i; @@ -856,7 +880,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) void SHAPE_POLY_SET::Fracture( POLYGON_MODE aFastMode ) { - Simplify( aFastMode ); // remove overlapping holes/degeneracy + Simplify( aFastMode ); // remove overlapping holes/degeneracy for( POLYGON& paths : m_polys ) { @@ -865,6 +889,169 @@ void SHAPE_POLY_SET::Fracture( POLYGON_MODE aFastMode ) } +void SHAPE_POLY_SET::unfractureSingle( SHAPE_POLY_SET::POLYGON& aPoly ) +{ + 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->CSegment( m_index ), + aOther.m_poly->CSegment( aOther.m_index ) ); + } + + bool operator!=( const EDGE& aOther ) const + { + return !compareSegs( m_poly->CSegment( m_index ), + aOther.m_poly->CSegment( aOther.m_index ) ); + } + + struct HASH + { + std::size_t operator()( const EDGE& aEdge ) const + { + const auto& a = aEdge.m_poly->CSegment( 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(); + + EDGE_LIST_ENTRY edgeList[ 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] ); + } + + EDGE_LIST_ENTRY* edgeBuf[ lc.SegmentCount() ]; + + int n = 0; + int outline = -1; + + POLYGON result; + + 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] ); + } + + outl.SetClosed( true ); + + bool cw = outl.Area() > 0.0; + + if( cw ) + outline = n; + + result.push_back( outl ); + n++; + } + + assert( outline >= 0 ); + + if( outline !=0 ) + std::swap( result[0], result[outline] ); + + aPoly = result; +} + + bool SHAPE_POLY_SET::HasHoles() const { // Iterate through all the polygons on the set @@ -880,6 +1067,17 @@ bool SHAPE_POLY_SET::HasHoles() const } +void SHAPE_POLY_SET::Unfracture( POLYGON_MODE aFastMode ) +{ + for( POLYGON& path : m_polys ) + { + unfractureSingle( path ); + } + + Simplify( aFastMode ); // remove overlapping holes/degeneracy +} + + void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode ) { SHAPE_POLY_SET empty; @@ -911,7 +1109,7 @@ int SHAPE_POLY_SET::NormalizeAreaOutlines() // If any hole, substract it to main outline if( holesBuffer.OutlineCount() ) { - holesBuffer.Simplify( SHAPE_POLY_SET::PM_FAST); + holesBuffer.Simplify( SHAPE_POLY_SET::PM_FAST ); BooleanSubtract( holesBuffer, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } @@ -930,12 +1128,15 @@ const std::string SHAPE_POLY_SET::Format() const for( unsigned i = 0; i < m_polys.size(); i++ ) { ss << "poly " << m_polys[i].size() << "\n"; + for( unsigned j = 0; j < m_polys[i].size(); j++ ) { ss << m_polys[i][j].PointCount() << "\n"; + for( int v = 0; v < m_polys[i][j].PointCount(); v++ ) ss << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n"; } + ss << "\n"; } @@ -982,6 +1183,7 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream ) aStream >> tmp; int n_vertices = atoi( tmp.c_str() ); + for( int v = 0; v < n_vertices; v++ ) { VECTOR2I p; @@ -996,6 +1198,7 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream ) m_polys.push_back( paths ); } + return true; } @@ -1062,7 +1265,7 @@ void SHAPE_POLY_SET::RemoveContour( int aContourIdx, int aPolygonIdx ) if( aPolygonIdx < 0 ) aPolygonIdx += m_polys.size(); - m_polys[aPolygonIdx].erase( m_polys[aPolygonIdx].begin() + aContourIdx ); + m_polys[aPolygonIdx].erase( m_polys[aPolygonIdx].begin() + aContourIdx ); } @@ -1072,8 +1275,8 @@ int SHAPE_POLY_SET::RemoveNullSegments() ITERATOR iterator = IterateWithHoles(); - VECTOR2I contourStart = *iterator; - VECTOR2I segmentStart, segmentEnd; + VECTOR2I contourStart = *iterator; + VECTOR2I segmentStart, segmentEnd; VERTEX_INDEX indexStart; @@ -1138,14 +1341,14 @@ void SHAPE_POLY_SET::Append( const VECTOR2I& aP, int aOutline, int aHole ) bool SHAPE_POLY_SET::CollideVertex( const VECTOR2I& aPoint, - SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) + SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) { // Shows whether there was a collision bool collision = false; // Difference vector between each vertex and aPoint. - VECTOR2D delta; - double distance, clearance; + VECTOR2D delta; + double distance, clearance; // Convert clearance to double for precission when comparing distances clearance = aClearance; @@ -1176,7 +1379,7 @@ bool SHAPE_POLY_SET::CollideVertex( const VECTOR2I& aPoint, bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint, - SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) + SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) { // Shows whether there was a collision bool collision = false; @@ -1222,7 +1425,6 @@ bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const } return false; - } @@ -1253,6 +1455,7 @@ bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) con for( int holeIdx = 0; holeIdx < HoleCount( aSubpolyIndex ); holeIdx++ ) { const SHAPE_LINE_CHAIN hole = CHole( aSubpolyIndex, holeIdx ); + // If the point is inside a hole (and not on its edge), // it is outside of the polygon if( pointInPolygon( aP, hole ) && !hole.PointOnEdge( aP ) ) @@ -1271,7 +1474,7 @@ bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& int result = 0; int cnt = aPath.PointCount(); - if ( !aPath.BBox().Contains( aP ) ) // test with bounding box first + if( !aPath.BBox().Contains( aP ) ) // test with bounding box first return false; if( cnt < 3 ) @@ -1285,8 +1488,8 @@ bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& if( ipNext.y == aP.y ) { - if( ( ipNext.x == aP.x ) || ( ip.y == aP.y && - ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) ) + if( ( ipNext.x == aP.x ) || ( ip.y == aP.y + && ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) ) return true; } @@ -1298,8 +1501,8 @@ bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& result = 1 - result; else { - int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) - - (int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y ); + int64_t d = (int64_t) ( ip.x - aP.x ) * (int64_t) ( ipNext.y - aP.y ) - + (int64_t) ( ipNext.x - aP.x ) * (int64_t) ( ip.y - aP.y ); if( !d ) return true; @@ -1312,14 +1515,14 @@ bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& { if( ipNext.x > aP.x ) { - int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) - - (int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y ); + int64_t d = (int64_t) ( ip.x - aP.x ) * (int64_t) ( ipNext.y - aP.y ) - + (int64_t) ( ipNext.x - aP.x ) * (int64_t) ( ip.y - aP.y ); - if( !d ) - return true; + if( !d ) + return true; - if( ( d > 0 ) == ( ipNext.y > ip.y ) ) - result = 1 - result; + if( ( d > 0 ) == ( ipNext.y > ip.y ) ) + result = 1 - result; } } } @@ -1333,9 +1536,9 @@ bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& void SHAPE_POLY_SET::Move( const VECTOR2I& aVector ) { - for( POLYGON &poly : m_polys ) + for( POLYGON& poly : m_polys ) { - for( SHAPE_LINE_CHAIN &path : poly ) + for( SHAPE_LINE_CHAIN& path : poly ) { path.Move( aVector ); } @@ -1366,10 +1569,10 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance, SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, - unsigned int aSegments, - int aIndex ) + unsigned int aSegments, + int aIndex ) { - return chamferFilletPolygon(CORNER_MODE::FILLETED, aRadius, aIndex, aSegments ); + return chamferFilletPolygon( CORNER_MODE::FILLETED, aRadius, aIndex, aSegments ); } @@ -1427,7 +1630,7 @@ int SHAPE_POLY_SET::DistanceToPolygon( SEG aSegment, int aPolygonIndex, int aSeg // Take into account the width of the segment if( aSegmentWidth > 0 ) - minDistance -= aSegmentWidth/2; + minDistance -= aSegmentWidth / 2; // Return the maximum of minDistance and zero return minDistance < 0 ? 0 : minDistance; @@ -1506,9 +1709,9 @@ SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments ) SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, - unsigned int aDistance, - int aIndex, - int aSegments ) + unsigned int aDistance, + int aIndex, + int aSegments ) { // Null segments create serious issues in calculations. Remove them: RemoveNullSegments(); @@ -1523,7 +1726,7 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, } // Iterate through all the contours (outline and holes) of the polygon. - for( SHAPE_LINE_CHAIN &currContour : currentPoly ) + for( SHAPE_LINE_CHAIN& currContour : currentPoly ) { // Generate a new contour in the new polygon SHAPE_LINE_CHAIN newContour; @@ -1548,16 +1751,16 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, nextVertex = currVertex == currContour.PointCount() - 1 ? 0 : currVertex + 1; // Previous vertex computation - double xa = currContour.Point( prevVertex ).x - x1; - double ya = currContour.Point( prevVertex ).y - y1; + double xa = currContour.Point( prevVertex ).x - x1; + double ya = currContour.Point( prevVertex ).y - y1; // Next vertex computation - double xb = currContour.Point( nextVertex ).x - x1; - double yb = currContour.Point( nextVertex ).y - y1; + double xb = currContour.Point( nextVertex ).x - x1; + double yb = currContour.Point( nextVertex ).y - y1; // Compute the new distances - double lena = hypot( xa, ya ); - double lenb = hypot( xb, yb ); + double lena = hypot( xa, ya ); + double lenb = hypot( xb, yb ); // Make the final computations depending on the mode selected, chamfered or filleted. if( aMode == CORNER_MODE::CHAMFERED ) @@ -1581,12 +1784,12 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, newContour.Append( x1 + nx2, y1 + ny2 ); } - else // CORNER_MODE = FILLETED + else // CORNER_MODE = FILLETED { double cosine = ( xa * xb + ya * yb ) / ( lena * lenb ); - double radius = aDistance; - double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 ); + double radius = aDistance; + double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 ); // Do nothing in case of parallel edges if( std::isinf( denom ) ) @@ -1600,18 +1803,18 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, radius = 0.5 * lenb * denom; // Calculate fillet arc absolute center point (xc, yx) - double k = radius / sqrt( .5 * ( 1 - cosine ) ); - double lenab = sqrt( ( xa / lena + xb / lenb ) * ( xa / lena + xb / lenb ) + - ( ya / lena + yb / lenb ) * ( ya / lena + yb / lenb ) ); - double xc = x1 + k * ( xa / lena + xb / lenb ) / lenab; - double yc = y1 + k * ( ya / lena + yb / lenb ) / lenab; + double k = radius / sqrt( .5 * ( 1 - cosine ) ); + double lenab = sqrt( ( xa / lena + xb / lenb ) * ( xa / lena + xb / lenb ) + + ( ya / lena + yb / lenb ) * ( ya / lena + yb / lenb ) ); + double xc = x1 + k * ( xa / lena + xb / lenb ) / lenab; + double yc = y1 + k * ( ya / lena + yb / lenb ) / lenab; // Calculate arc start and end vectors k = radius / sqrt( 2 / ( 1 + cosine ) - 1 ); - double xs = x1 + k * xa / lena - xc; - double ys = y1 + k * ya / lena - yc; - double xe = x1 + k * xb / lenb - xc; - double ye = y1 + k * yb / lenb - yc; + double xs = x1 + k * xa / lena - xc; + double ys = y1 + k * ya / lena - yc; + double xe = x1 + k * xb / lenb - xc; + double ye = y1 + k * yb / lenb - yc; // Cosine of arc angle double argument = ( xs * xe + ys * ye ) / ( radius * radius ); @@ -1628,33 +1831,33 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, // Calculate the number of segments unsigned int segments = ceil( (double) aSegments * ( arcAngle / ( 2 * M_PI ) ) ); - double deltaAngle = arcAngle / segments; - double startAngle = atan2( -ys, xs ); + double deltaAngle = arcAngle / segments; + double startAngle = atan2( -ys, xs ); // Flip arc for inner corners if( xa * yb - ya * xb <= 0 ) deltaAngle *= -1; - double nx = xc + xs; - double ny = yc + ys; + double nx = xc + xs; + double ny = yc + ys; newContour.Append( KiROUND( nx ), KiROUND( ny ) ); // Store the previous added corner to make a sanity check - int prevX = KiROUND( nx ); - int prevY = KiROUND( ny ); + int prevX = KiROUND( nx ); + int prevY = KiROUND( ny ); for( unsigned int j = 0; j < segments; j++ ) { - nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius; - ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius; + nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius; + ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius; // Sanity check: the rounding can produce repeated corners; do not add them. if( KiROUND( nx ) != prevX || KiROUND( ny ) != prevY ) { newContour.Append( KiROUND( nx ), KiROUND( ny ) ); - prevX = KiROUND( nx ); - prevY = KiROUND( ny ); + prevX = KiROUND( nx ); + prevY = KiROUND( ny ); } } } @@ -1667,3 +1870,191 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, return newPoly; } + + +typedef std::map P2T_MAP; +typedef std::vector P2T_VEC; + +static void convert( const SHAPE_LINE_CHAIN& outl, + P2T_VEC& buffer, + P2T_MAP& pointMap, + SHAPE_POLY_SET::TRIANGULATED_POLYGON& aPoly ) +{ + buffer.clear(); + + for( int i = 0; i < outl.PointCount(); i++ ) + { + const auto& p = outl.CPoint( i ); + auto p2 = new p2t::Point( p.x, p.y ); + pointMap[p2] = aPoly.AddVertex( p ); + buffer.push_back( p2 ); + } +} + + +SHAPE_POLY_SET::TRIANGULATED_POLYGON::~TRIANGULATED_POLYGON() +{ + Clear(); +} + + +void SHAPE_POLY_SET::TRIANGULATED_POLYGON::Clear() +{ + if( m_vertices ) + delete[] m_vertices; + + if( m_triangles ) + delete[] m_triangles; +} + + +void SHAPE_POLY_SET::TRIANGULATED_POLYGON::AllocateVertices( int aSize ) +{ + m_vertices = new VECTOR2I[aSize]; +} + + +void SHAPE_POLY_SET::TRIANGULATED_POLYGON::AllocateTriangles( int aSize ) +{ + m_triangles = new TRI[aSize]; +} + + +static int totalVertexCount( const SHAPE_POLY_SET::POLYGON& aPoly ) +{ + int cnt = 0; + + for( const auto& outl : aPoly ) + { + cnt += outl.PointCount(); + } + + return cnt; +} + + +void SHAPE_POLY_SET::triangulateSingle( const POLYGON& aPoly, + SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult ) +{ + assert( aPoly.size() >= 1 ); + + P2T_MAP pointMap; + P2T_VEC outline; + + aResult.AllocateVertices( totalVertexCount( aPoly ) ); + + convert( aPoly[0], outline, pointMap, aResult ); + + std::unique_ptr cdt( new p2t::CDT( outline ) ); + + for( int i = 1; i < aPoly.size(); i++ ) + { + std::vector hole; + + convert( aPoly[i], hole, pointMap, aResult ); + + cdt->AddHole( hole ); + } + + cdt->Triangulate(); + + aResult.AllocateTriangles( cdt->GetTriangles().size() ); + + int i = 0; + + for( auto tri : cdt->GetTriangles() ) + { + TRIANGULATED_POLYGON::TRI t; + + t.a = pointMap[ tri->GetPoint( 0 ) ]; + t.b = pointMap[ tri->GetPoint( 1 ) ]; + t.c = pointMap[ tri->GetPoint( 2 ) ]; + + aResult.m_triangles[ i ] = t; + i++; + } + + for( auto iter = pointMap.begin(); iter!=pointMap.end(); ++iter ) + delete iter->first; +} + + +bool SHAPE_POLY_SET::IsTriangulationUpToDate() const +{ + if( !m_triangulationValid ) + return false; + + if( !m_hash.IsValid() ) + return false; + + auto hash = checksum(); + + return hash == m_hash; +} + + +void SHAPE_POLY_SET::CacheTriangulation() +{ + bool recalculate = !m_hash.IsValid(); + MD5_HASH hash; + + if( !m_triangulationValid ) + recalculate = true; + + if( !recalculate ) + { + hash = checksum(); + + if( m_hash != hash ) + { + m_hash = hash; + recalculate = true; + } + } + + if( !recalculate ) + return; + + SHAPE_POLY_SET tmpSet = *this; + tmpSet.Unfracture( PM_FAST ); + + m_triangulatedPolys.clear(); + + for( int i = 0; i < tmpSet.OutlineCount(); i++ ) + { + auto p = new TRIANGULATED_POLYGON(); + m_triangulatedPolys.push_back( p ); + triangulateSingle( tmpSet.Polygon( i ), *p ); + } + + m_triangulationValid = true; + m_hash = checksum(); +} + + +MD5_HASH SHAPE_POLY_SET::checksum() const +{ + MD5_HASH hash; + + hash.Hash( m_polys.size() ); + + for( const auto& outline : m_polys ) + { + hash.Hash( outline.size() ); + + for( const auto& lc : outline ) + { + hash.Hash( lc.PointCount() ); + + for( int i = 0; i < lc.PointCount(); i++ ) + { + hash.Hash( lc.CPoint( i ).x ); + hash.Hash( lc.CPoint( i ).y ); + } + } + } + + hash.Finalize(); + + return hash; +} diff --git a/common/md5_hash.cpp b/common/md5_hash.cpp new file mode 100644 index 0000000000..24acb2e742 --- /dev/null +++ b/common/md5_hash.cpp @@ -0,0 +1,250 @@ +// Code by: B-Con (http://b-con.us) +// Released under the GNU GPL +// MD5 Hash Digest implementation (little endian byte order) + + +#include +#include + +#include + +// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it +#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - c) ++b; a += c; +#define ROTLEFT(a,b) ((a << b) | (a >> (32-b))) + +#define F(x,y,z) ((x & y) | (~x & z)) +#define G(x,y,z) ((x & z) | (y & ~z)) +#define H(x,y,z) (x ^ y ^ z) +#define I(x,y,z) (y ^ (x | ~z)) + +#define FF(a,b,c,d,m,s,t) { a += F(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } +#define GG(a,b,c,d,m,s,t) { a += G(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } +#define HH(a,b,c,d,m,s,t) { a += H(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } +#define II(a,b,c,d,m,s,t) { a += I(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } + + + + +MD5_HASH::MD5_HASH() +{ + Init(); +} + +MD5_HASH::MD5_HASH( const MD5_HASH& aOther ) +{ + m_valid = aOther.m_valid; + m_ctx = aOther.m_ctx; + memcpy( m_hash, aOther.m_hash, 16 ); +} + +MD5_HASH::~MD5_HASH() +{ + +} + +MD5_HASH& MD5_HASH::operator=( const MD5_HASH& aOther ) +{ + m_valid = aOther.m_valid; + m_ctx = aOther.m_ctx; + memcpy( m_hash, aOther.m_hash, 16 ); + + return *this; +} + + +void MD5_HASH::Init() +{ + //printf("%p init\n", this); + m_valid = false; + md5_init(&m_ctx); +} + +void MD5_HASH::Hash ( uint8_t *data, uint32_t length ) +{ + md5_update(&m_ctx, data, length); +} + +void MD5_HASH::Hash ( int value ) +{ + md5_update(&m_ctx, (uint8_t*) &value, sizeof(int) ); +} + +void MD5_HASH::Finalize() +{ + //printf("%p final\n", this); + md5_final(&m_ctx, m_hash); + m_valid = true; + +} + +bool MD5_HASH::operator==( const MD5_HASH& aOther ) const +{ + return ( memcmp( m_hash, aOther.m_hash, 16 ) == 0 ); +} + +bool MD5_HASH::operator!=( const MD5_HASH& aOther ) const +{ + return ( memcmp( m_hash, aOther.m_hash, 16 ) != 0 ); +} + + +void MD5_HASH::md5_transform(MD5_CTX *ctx, uint8_t data[]) +{ + uint32_t a,b,c,d,m[16],i,j; + + // MD5 specifies big endian byte order, but this implementation assumes a little + // endian byte order CPU. Reverse all the bytes upon input, and re-reverse them + // on output (in md5_final()). + for (i=0,j=0; i < 16; ++i, j += 4) + m[i] = (data[j]) + (data[j+1] << 8) + (data[j+2] << 16) + (data[j+3] << 24); + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + + FF(a,b,c,d,m[0], 7,0xd76aa478); + FF(d,a,b,c,m[1], 12,0xe8c7b756); + FF(c,d,a,b,m[2], 17,0x242070db); + FF(b,c,d,a,m[3], 22,0xc1bdceee); + FF(a,b,c,d,m[4], 7,0xf57c0faf); + FF(d,a,b,c,m[5], 12,0x4787c62a); + FF(c,d,a,b,m[6], 17,0xa8304613); + FF(b,c,d,a,m[7], 22,0xfd469501); + FF(a,b,c,d,m[8], 7,0x698098d8); + FF(d,a,b,c,m[9], 12,0x8b44f7af); + FF(c,d,a,b,m[10],17,0xffff5bb1); + FF(b,c,d,a,m[11],22,0x895cd7be); + FF(a,b,c,d,m[12], 7,0x6b901122); + FF(d,a,b,c,m[13],12,0xfd987193); + FF(c,d,a,b,m[14],17,0xa679438e); + FF(b,c,d,a,m[15],22,0x49b40821); + + GG(a,b,c,d,m[1], 5,0xf61e2562); + GG(d,a,b,c,m[6], 9,0xc040b340); + GG(c,d,a,b,m[11],14,0x265e5a51); + GG(b,c,d,a,m[0], 20,0xe9b6c7aa); + GG(a,b,c,d,m[5], 5,0xd62f105d); + GG(d,a,b,c,m[10], 9,0x02441453); + GG(c,d,a,b,m[15],14,0xd8a1e681); + GG(b,c,d,a,m[4], 20,0xe7d3fbc8); + GG(a,b,c,d,m[9], 5,0x21e1cde6); + GG(d,a,b,c,m[14], 9,0xc33707d6); + GG(c,d,a,b,m[3], 14,0xf4d50d87); + GG(b,c,d,a,m[8], 20,0x455a14ed); + GG(a,b,c,d,m[13], 5,0xa9e3e905); + GG(d,a,b,c,m[2], 9,0xfcefa3f8); + GG(c,d,a,b,m[7], 14,0x676f02d9); + GG(b,c,d,a,m[12],20,0x8d2a4c8a); + + HH(a,b,c,d,m[5], 4,0xfffa3942); + HH(d,a,b,c,m[8], 11,0x8771f681); + HH(c,d,a,b,m[11],16,0x6d9d6122); + HH(b,c,d,a,m[14],23,0xfde5380c); + HH(a,b,c,d,m[1], 4,0xa4beea44); + HH(d,a,b,c,m[4], 11,0x4bdecfa9); + HH(c,d,a,b,m[7], 16,0xf6bb4b60); + HH(b,c,d,a,m[10],23,0xbebfbc70); + HH(a,b,c,d,m[13], 4,0x289b7ec6); + HH(d,a,b,c,m[0], 11,0xeaa127fa); + HH(c,d,a,b,m[3], 16,0xd4ef3085); + HH(b,c,d,a,m[6], 23,0x04881d05); + HH(a,b,c,d,m[9], 4,0xd9d4d039); + HH(d,a,b,c,m[12],11,0xe6db99e5); + HH(c,d,a,b,m[15],16,0x1fa27cf8); + HH(b,c,d,a,m[2], 23,0xc4ac5665); + + II(a,b,c,d,m[0], 6,0xf4292244); + II(d,a,b,c,m[7], 10,0x432aff97); + II(c,d,a,b,m[14],15,0xab9423a7); + II(b,c,d,a,m[5], 21,0xfc93a039); + II(a,b,c,d,m[12], 6,0x655b59c3); + II(d,a,b,c,m[3], 10,0x8f0ccc92); + II(c,d,a,b,m[10],15,0xffeff47d); + II(b,c,d,a,m[1], 21,0x85845dd1); + II(a,b,c,d,m[8], 6,0x6fa87e4f); + II(d,a,b,c,m[15],10,0xfe2ce6e0); + II(c,d,a,b,m[6], 15,0xa3014314); + II(b,c,d,a,m[13],21,0x4e0811a1); + II(a,b,c,d,m[4], 6,0xf7537e82); + II(d,a,b,c,m[11],10,0xbd3af235); + II(c,d,a,b,m[2], 15,0x2ad7d2bb); + II(b,c,d,a,m[9], 21,0xeb86d391); + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; +} + +void MD5_HASH::md5_init(MD5_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen[0] = 0; + ctx->bitlen[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void MD5_HASH::md5_update(MD5_CTX *ctx, uint8_t data[], uint32_t len) +{ + uint32_t t,i; + + for (i=0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + md5_transform(ctx,ctx->data); + DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],512); + ctx->datalen = 0; + } + } +} + +void MD5_HASH::md5_final(MD5_CTX *ctx, uint8_t hash[]) +{ + uint32_t i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else if (ctx->datalen >= 56) { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + md5_transform(ctx,ctx->data); + memset(ctx->data,0,56); + } + + // Append to the padding the total message's length in bits and transform. + DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],8 * ctx->datalen); + ctx->data[56] = ctx->bitlen[0]; + ctx->data[57] = ctx->bitlen[0] >> 8; + ctx->data[58] = ctx->bitlen[0] >> 16; + ctx->data[59] = ctx->bitlen[0] >> 24; + ctx->data[60] = ctx->bitlen[1]; + ctx->data[61] = ctx->bitlen[1] >> 8; + ctx->data[62] = ctx->bitlen[1] >> 16; + ctx->data[63] = ctx->bitlen[1] >> 24; + md5_transform(ctx,ctx->data); + + // Since this implementation uses little endian byte ordering and MD uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i=0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (i*8)) & 0x000000ff; + hash[i+4] = (ctx->state[1] >> (i*8)) & 0x000000ff; + hash[i+8] = (ctx->state[2] >> (i*8)) & 0x000000ff; + hash[i+12] = (ctx->state[3] >> (i*8)) & 0x000000ff; + } +} diff --git a/common/widgets/progress_reporter.cpp b/common/widgets/progress_reporter.cpp new file mode 100644 index 0000000000..9638e449bf --- /dev/null +++ b/common/widgets/progress_reporter.cpp @@ -0,0 +1,102 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 CERN + * Author: Tomasz Wlostowski + * + * 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 + +PROGRESS_REPORTER::PROGRESS_REPORTER( int aNumPhases ) : + m_phase( 0 ), + m_progress( 0 ), + m_maxProgress( 1 ), + m_numPhases( aNumPhases ) +{ +}; + +void PROGRESS_REPORTER::BeginPhase( int aPhase ) +{ + std::lock_guard guard( m_lock ); + m_phase = aPhase; + m_progress = 0; + updateUI(); +} + +void PROGRESS_REPORTER::AdvancePhase( ) +{ + std::lock_guard guard( m_lock ); + m_phase++; + m_progress = 0; + updateUI(); +} + +void PROGRESS_REPORTER::Report( const wxString& aMessage ) +{ + std::lock_guard guard( m_lock ); + m_message = aMessage; + updateUI(); +} + +void PROGRESS_REPORTER::SetMaxProgress ( int aMaxProgress ) +{ + std::lock_guard guard( m_lock ); + m_maxProgress = aMaxProgress; + updateUI(); +} + +void PROGRESS_REPORTER::AdvanceProgress( ) +{ + std::lock_guard guard( m_lock ); + m_progress++; + updateUI(); +} + +int PROGRESS_REPORTER::currentProgress() const +{ + double current = (1.0 / (double)m_numPhases) * ( (double) m_phase + ( (double) m_progress / (double) m_maxProgress ) ); + + return (int)(current * 1000); +} + +WX_PROGRESS_REPORTER::WX_PROGRESS_REPORTER( wxWindow* aParent, + const wxString& aTitle, + int aNumPhases ) : + PROGRESS_REPORTER( aNumPhases ), + wxProgressDialog( aTitle, wxT( "" ), 1, aParent, wxPD_AUTO_HIDE | wxPD_CAN_ABORT | + wxPD_APP_MODAL | wxPD_ELAPSED_TIME ) +{ +} + + +WX_PROGRESS_REPORTER::~WX_PROGRESS_REPORTER() +{ + Destroy(); +} + + +void WX_PROGRESS_REPORTER::updateUI() +{ + int cur = currentProgress(); + SetRange( 1000 ); + Update( cur, m_message ); +} diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index 97ddadc69c..5c8d402fc6 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -387,6 +387,12 @@ private: */ void drawPolygon( GLdouble* aPoints, int aPointCount ); + /** + * @brief Draws a set of polygons with a cached triangulation. Way faster than drawPolygon. + */ + void drawTriangulatedPolyset( const SHAPE_POLY_SET& aPoly ); + + /** * @brief Draws a single character using bitmap font. * Its main purpose is to be used in BitmapText() function. diff --git a/include/geometry/seg.h b/include/geometry/seg.h index d3e410d223..85f146a1bb 100644 --- a/include/geometry/seg.h +++ b/include/geometry/seg.h @@ -102,6 +102,16 @@ public: return *this; } + bool operator==( const SEG& aSeg ) const + { + return (A == aSeg.A && B == aSeg.B) ; + } + + bool operator!=( const SEG& aSeg ) const + { + return (A != aSeg.A || B != aSeg.B); + } + /** * Function LineProject() * diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index be04ce3965..b849579c35 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -33,6 +33,8 @@ #include "clipper.hpp" +#include + /** * Class SHAPE_POLY_SET @@ -57,6 +59,42 @@ class SHAPE_POLY_SET : public SHAPE ///> the remaining (if any), are the holes typedef std::vector POLYGON; + struct TRIANGULATED_POLYGON + { + ~TRIANGULATED_POLYGON(); + + struct TRI + { + TRI(){}; + + int a, b, c; + }; + + void Clear(); + + void AllocateVertices( int aSize ); + void AllocateTriangles ( int aSize ); + + void GetTriangle( int index, VECTOR2I& a, VECTOR2I& b, VECTOR2I& c ) const + { + auto tri = &m_triangles[ index ]; + a = m_vertices[ tri->a ]; + b = m_vertices[ tri->b ]; + c = m_vertices[ tri->c ]; + } + + int AddVertex( const VECTOR2I& aP ) + { + m_vertices[ m_vertexCount ] = aP; + return (m_vertexCount++); + } + + TRI* m_triangles = nullptr; + VECTOR2I* m_vertices = nullptr; + int m_vertexCount = 0; + int m_triangleCount = 0; + }; + /** * Struct VERTEX_INDEX * @@ -538,6 +576,17 @@ class SHAPE_POLY_SET : public SHAPE return m_polys[aIndex]; } + const POLYGON& Polygon( int aIndex ) const + { + return m_polys[aIndex]; + } + + const TRIANGULATED_POLYGON* TriangulatedPolygon( int aIndex ) const + { + return m_triangulatedPolys[aIndex]; + } + + const SHAPE_LINE_CHAIN& COutline( int aIndex ) const { return m_polys[aIndex][0]; @@ -761,6 +810,8 @@ class SHAPE_POLY_SET : public SHAPE ///> For aFastMode meaning, see function booleanOp void Fracture( POLYGON_MODE aFastMode ); + void Unfracture( POLYGON_MODE aFastMode ); + ///> Returns true if the polygon set has any holes. bool HasHoles() const; @@ -990,6 +1041,7 @@ class SHAPE_POLY_SET : public SHAPE void fractureSingle( POLYGON& paths ); + void unfractureSingle ( POLYGON& path ); void importTree( ClipperLib::PolyTree* tree ); /** Function booleanOp @@ -1039,6 +1091,8 @@ class SHAPE_POLY_SET : public SHAPE FILLETED }; + + /** * Function chamferFilletPolygon * Returns the camfered or filleted version of the aIndex-th polygon in the set, depending @@ -1059,6 +1113,21 @@ class SHAPE_POLY_SET : public SHAPE typedef std::vector POLYSET; POLYSET m_polys; + + public: + + void CacheTriangulation(); + bool IsTriangulationUpToDate() const; + + private: + void triangulateSingle( const POLYGON& aPoly, SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult ); + + MD5_HASH checksum() const; + + std::vector m_triangulatedPolys; + bool m_triangulationValid = false; + MD5_HASH m_hash; + }; #endif diff --git a/include/md5_hash.h b/include/md5_hash.h new file mode 100644 index 0000000000..1c8fe98934 --- /dev/null +++ b/include/md5_hash.h @@ -0,0 +1,50 @@ +// Code by: B-Con (http://b-con.us) +// Released under the GNU GPL +// MD5 Hash Digest implementation (little endian byte order) + +#ifndef __MD5_HASH_H +#define __MD5_HASH_H + +#include + +class MD5_HASH +{ + +public: + MD5_HASH(); + MD5_HASH( const MD5_HASH& aOther ); + + ~MD5_HASH(); + + void Init(); + void Hash ( uint8_t *data, uint32_t length ); + void Hash ( int value ); + void Finalize(); + bool IsValid() const { return m_valid; }; + + void SetValid( bool aValid ) { m_valid = aValid; } + + MD5_HASH& operator=( const MD5_HASH& aOther ); + + bool operator==( const MD5_HASH& aOther ) const; + bool operator!=( const MD5_HASH& aOther ) const; + +private: + struct MD5_CTX { + uint8_t data[64]; + uint32_t datalen; + uint32_t bitlen[2]; + uint32_t state[4]; + }; + + void md5_transform(MD5_CTX *ctx, uint8_t data[]); + void md5_init(MD5_CTX *ctx); + void md5_update(MD5_CTX *ctx, uint8_t data[], uint32_t len); + void md5_final(MD5_CTX *ctx, uint8_t hash[]); + + bool m_valid; + MD5_CTX m_ctx; + uint8_t m_hash[16]; +}; + +#endif diff --git a/include/widgets/progress_reporter.h b/include/widgets/progress_reporter.h new file mode 100644 index 0000000000..e76813f5a9 --- /dev/null +++ b/include/widgets/progress_reporter.h @@ -0,0 +1,69 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 CERN + * Author: Tomasz Wlostowski +* + * 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 + */ + +#ifndef __PROGRESS_REPORTER +#define __PROGRESS_REPORTER + +#include + +#include + +class PROGRESS_REPORTER +{ + public: + + PROGRESS_REPORTER( int aNumPhases ); + PROGRESS_REPORTER( const PROGRESS_REPORTER& ) = delete; + + void BeginPhase( int aPhase ); + void AdvancePhase( ); + void Report ( const wxString& aMessage ); + void SetMaxProgress ( int aMaxProgress ); + void AdvanceProgress( ); + + protected: + + int currentProgress() const; + virtual void updateUI() = 0; + + wxString m_message; + int m_phase, m_numPhases; + int m_progress, m_maxProgress; + + std::mutex m_lock; +}; + +class WX_PROGRESS_REPORTER : public PROGRESS_REPORTER, public wxProgressDialog +{ +public: + + WX_PROGRESS_REPORTER( wxWindow *aParent, const wxString &aTitle, int aNumPhases ); + ~WX_PROGRESS_REPORTER(); + +private: + + virtual void updateUI() override; +}; + +#endif diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index d1b78b2e8e..2bac9b97a8 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -315,6 +315,7 @@ set( PCBNEW_CLASS_SRCS tools/tool_event_utils.cpp tools/size_menu.cpp tools/selection.cpp + tools/zone_filler_tool.cpp footprint_preview_panel.cpp ) diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index f2545b2c97..54159cf3ea 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -1312,3 +1312,8 @@ void ZONE_CONTAINER::SwapData( BOARD_ITEM* aImage ) std::swap( *((ZONE_CONTAINER*) this), *((ZONE_CONTAINER*) aImage) ); } + +void ZONE_CONTAINER::CacheTriangulation() +{ + m_FilledPolysList.CacheTriangulation(); +} diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index 0a8e72ed82..d9f63ea338 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -170,11 +170,11 @@ public: int GetClearance( BOARD_CONNECTED_ITEM* aItem = NULL ) const override; /** - * Function TestForCopperIslandAndRemoveInsulatedIslands + * Function RemoveInsulatedCopperIslands * Remove insulated copper islands found in m_FilledPolysList. * @param aPcb = the board to analyze */ - void TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb ); + void RemoveInsulatedCopperIslands( BOARD* aPcb ); /** * Function IsOnCopperLayer @@ -329,7 +329,7 @@ public: bool BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* aOutlineBuffer = NULL ); /** - * Function AddClearanceAreasPolygonsToPolysList + * Function ComputeRawFilledAreas * Add non copper areas polygons (pads and tracks with clearance) * to a filled copper area * used in BuildFilledSolidAreasPolygons when calculating filled areas in a zone @@ -340,8 +340,7 @@ public: * @param aPcb: the current board * _NG version uses SHAPE_POLY_SET instead of Boost.Polygon */ - void AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ); - void AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ); + void ComputeRawFilledAreas( BOARD* aPcb ); /** @@ -602,6 +601,8 @@ public: return m_FilledPolysList; } + void CacheTriangulation(); + /** * Function AddFilledPolysList * sets the list of filled polygons. @@ -728,7 +729,6 @@ public: const std::vector& GetHatchLines() const { return m_HatchLines; } - #if defined(DEBUG) virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } #endif @@ -810,6 +810,8 @@ private: int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines std::vector m_HatchLines; // hatch lines + std::vector m_insulatedIslands; + /** * Union to handle conversion between references to wxPoint and to VECTOR2I. * diff --git a/pcbnew/connectivity_algo.cpp b/pcbnew/connectivity_algo.cpp index 88c99f7af7..e35b52f916 100644 --- a/pcbnew/connectivity_algo.cpp +++ b/pcbnew/connectivity_algo.cpp @@ -23,6 +23,10 @@ */ #include +#include + +#include +#include #ifdef PROFILE #include @@ -289,9 +293,11 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem ) return true; } - void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) { + std::mutex cnListLock; + + //PROF_COUNTER cnt("search"); int totalDirtyCount = 0; if( m_lastSearchWithZones != aIncludeZones ) @@ -304,7 +310,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) m_lastSearchWithZones = aIncludeZones; - auto checkForConnection = [] ( const CN_ANCHOR_PTR point, CN_ITEM* aRefItem, int aMaxDist = 0 ) + auto checkForConnection = [ &cnListLock ] ( const CN_ANCHOR_PTR point, CN_ITEM* aRefItem, int aMaxDist = 0 ) { const auto parent = aRefItem->Parent(); @@ -331,7 +337,10 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) case PCB_VIA_T: if( parent->HitTest( wxPoint( point->Pos().x, point->Pos().y ) ) ) + { + std::lock_guard lock( cnListLock ); CN_ITEM::Connect( aRefItem, point->Item() ); + } break; @@ -344,7 +353,10 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) if( d_start.EuclideanNorm() < aMaxDist || d_end.EuclideanNorm() < aMaxDist ) + { + std::lock_guard lock( cnListLock ); CN_ITEM::Connect( aRefItem, point->Item() ); + } break; } @@ -363,6 +375,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) if( zoneItem->ContainsAnchor( point ) ) { + std::lock_guard lock( cnListLock ); CN_ITEM::Connect( zoneItem, point->Item() ); } @@ -374,7 +387,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) } }; - auto checkInterZoneConnection = [] ( CN_ZONE* testedZone, CN_ZONE* aRefZone ) + auto checkInterZoneConnection = [ &cnListLock ] ( CN_ZONE* testedZone, CN_ZONE* aRefZone ) { const auto parentZone = static_cast( aRefZone->Parent() ); @@ -399,6 +412,8 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) { if( testedZone->ContainsPoint( outline.CPoint( i ) ) ) { + std::lock_guard lock( cnListLock ); + CN_ITEM::Connect( aRefZone, testedZone ); return; } @@ -412,6 +427,8 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) { if( aRefZone->ContainsPoint( outline2.CPoint( i ) ) ) { + std::lock_guard lock( cnListLock ); + CN_ITEM::Connect( aRefZone, testedZone ); return; } @@ -496,8 +513,19 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) if( aIncludeZones ) { - for( auto& item : m_zoneList ) + int cnt = 0; + + if( m_progressReporter ) { + m_progressReporter->SetMaxProgress( m_zoneList.Size() ); + } + + #ifdef USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for(int i = 0; i < m_zoneList.Size(); i++ ) + { + auto item = m_zoneList[i]; auto zoneItem = static_cast (item); auto searchZones = std::bind( checkForConnection, _1, zoneItem ); @@ -509,6 +537,16 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones ) m_padList.FindNearby( zoneItem->BBox(), searchZones ); m_zoneList.FindNearbyZones( zoneItem->BBox(), std::bind( checkInterZoneConnection, _1, zoneItem ) ); } + + { + std::lock_guard lock( cnListLock ); + cnt++; + + if (m_progressReporter) + { + m_progressReporter->AdvanceProgress(); + } + } } m_zoneList.ClearDirtyFlags(); @@ -589,6 +627,7 @@ const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUST CN_ITEM* head = nullptr; CLUSTERS clusters; + if( isDirty() ) searchConnections( includeZones ); @@ -798,7 +837,6 @@ void CN_CONNECTIVITY_ALGO::PropagateNets() propagateConnections(); } - void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector& aIslands ) { if( aZone->GetFilledPolysList().IsEmpty() ) @@ -828,6 +866,40 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std wxLogTrace( "CN", "Found %u isolated islands\n", (unsigned)aIslands.size() ); } +void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector& aZones ) +{ + for ( auto& z : aZones ) + { + if( z.m_zone->GetFilledPolysList().IsEmpty() ) + continue; + + Remove( z.m_zone ); + Add( z.m_zone ); + } + + m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK ); + + for ( auto& zone : aZones ) + { + if( zone.m_zone->GetFilledPolysList().IsEmpty() ) + continue; + + for( auto cluster : m_connClusters ) + { + if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() ) + { + for( auto z : *cluster ) + { + if( z->Parent() == zone.m_zone ) + { + zone.m_islands.push_back( static_cast(z)->SubpolyIndex() ); + } + } + } + } + } +} + const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters() { @@ -995,3 +1067,8 @@ bool CN_ANCHOR::IsDangling() const return validCount <= 1; } + +void CN_CONNECTIVITY_ALGO::SetProgressReporter( PROGRESS_REPORTER* aReporter ) +{ + m_progressReporter = aReporter; +} diff --git a/pcbnew/connectivity_algo.h b/pcbnew/connectivity_algo.h index 176c6dd12e..c1bcd04f51 100644 --- a/pcbnew/connectivity_algo.h +++ b/pcbnew/connectivity_algo.h @@ -53,6 +53,7 @@ class BOARD; class BOARD_CONNECTED_ITEM; class BOARD_ITEM; class ZONE_CONTAINER; +class PROGRESS_REPORTER; class CN_ANCHOR { @@ -439,6 +440,8 @@ public: ITER begin() { return m_items.begin(); }; ITER end() { return m_items.end(); }; + CN_ITEM* operator[] ( int aIndex ) { return m_items[aIndex]; } + std::vector& Anchors() { return m_anchors; } template @@ -806,6 +809,7 @@ public: CLUSTERS m_connClusters; CLUSTERS m_ratsnestClusters; std::vector m_dirtyNets; + PROGRESS_REPORTER* m_progressReporter = nullptr; void searchConnections( bool aIncludeZones = false ); @@ -883,6 +887,8 @@ public: void PropagateNets(); void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector& aIslands ); + void FindIsolatedCopperIslands( std::vector& aZones ); + bool CheckConnectivity( std::vector& aReport ); const CLUSTERS& GetClusters(); @@ -894,6 +900,7 @@ public: void ForEachItem( std::function aFunc ); void MarkNetAsDirty( int aNet ); + void SetProgressReporter( PROGRESS_REPORTER* aReporter ); }; diff --git a/pcbnew/connectivity_data.cpp b/pcbnew/connectivity_data.cpp index 36902aa235..99ca7cd373 100644 --- a/pcbnew/connectivity_data.cpp +++ b/pcbnew/connectivity_data.cpp @@ -218,6 +218,10 @@ void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands ); } +void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector& aZones ) +{ + m_connAlgo->FindIsolatedCopperIslands( aZones ); +} void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector& aItems ) { @@ -617,3 +621,9 @@ void CONNECTIVITY_DATA::MarkItemNetAsDirty( BOARD_ITEM *aItem ) m_connAlgo->MarkNetAsDirty( static_cast( aItem )->GetNetCode() ); } } + +void CONNECTIVITY_DATA::SetProgressReporter( PROGRESS_REPORTER* aReporter ) +{ + m_progressReporter = aReporter; + m_connAlgo->SetProgressReporter( m_progressReporter ); +} diff --git a/pcbnew/connectivity_data.h b/pcbnew/connectivity_data.h index 98d73d5482..36e02c4bf5 100644 --- a/pcbnew/connectivity_data.h +++ b/pcbnew/connectivity_data.h @@ -45,6 +45,7 @@ class RN_DATA; class RN_NET; class TRACK; class D_PAD; +class PROGRESS_REPORTER; struct CN_DISJOINT_NET_ENTRY { @@ -53,6 +54,12 @@ struct CN_DISJOINT_NET_ENTRY VECTOR2I anchorA, anchorB; }; +struct CN_ZONE_ISOLATED_ISLAND_LIST +{ + ZONE_CONTAINER *m_zone; + std::vector m_islands; +}; + struct RN_DYNAMIC_LINE { int netCode; @@ -136,6 +143,7 @@ public: * @param aIslands list of islands that have no connections (outline indices in the polygon set) */ void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector& aIslands ); + void FindIsolatedCopperIslands( std::vector& aZones ); /** * Function RecalculateRatsnest() @@ -216,6 +224,8 @@ public: void MarkItemNetAsDirty( BOARD_ITEM* aItem ); + void SetProgressReporter( PROGRESS_REPORTER* aReporter ); + private: void updateRatsnest(); @@ -226,6 +236,8 @@ private: std::vector m_dynamicRatsnest; std::vector m_nets; + + PROGRESS_REPORTER* m_progressReporter; }; #endif diff --git a/pcbnew/pcb_draw_panel_gal.cpp b/pcbnew/pcb_draw_panel_gal.cpp index 2e89b60c59..07c3b545c7 100644 --- a/pcbnew/pcb_draw_panel_gal.cpp +++ b/pcbnew/pcb_draw_panel_gal.cpp @@ -136,13 +136,16 @@ PCB_DRAW_PANEL_GAL::~PCB_DRAW_PANEL_GAL() } -void PCB_DRAW_PANEL_GAL::DisplayBoard( const BOARD* aBoard ) +void PCB_DRAW_PANEL_GAL::DisplayBoard( BOARD* aBoard ) { m_view->Clear(); // Load zones - for( int i = 0; i < aBoard->GetAreaCount(); ++i ) - m_view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) ); + for( auto zone : aBoard->Zones() ) + { + zone->CacheTriangulation(); + m_view->Add( zone ); + } // Load drawings for( auto drawing : const_cast(aBoard)->Drawings() ) diff --git a/pcbnew/pcb_draw_panel_gal.h b/pcbnew/pcb_draw_panel_gal.h index 75a2ac6931..6f1f3cf18e 100644 --- a/pcbnew/pcb_draw_panel_gal.h +++ b/pcbnew/pcb_draw_panel_gal.h @@ -50,7 +50,7 @@ public: * adds all items from the current board to the VIEW, so they can be displayed by GAL. * @param aBoard is the PCB to be loaded. */ - void DisplayBoard( const BOARD* aBoard ); + void DisplayBoard( BOARD* aBoard ); /** * Function SetWorksheet @@ -106,7 +106,7 @@ public: protected: KIGFX::PCB_VIEW* view() const; - + ///> Reassigns layer order to the initial settings. void setDefaultLayerOrder(); diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index ec3992824e..a299885935 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -1143,6 +1143,9 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer ) m_gal->SetIsStroke( true ); } + m_gal->DrawPolygon( polySet ); + +#if 0 for( int i = 0; i < polySet.OutlineCount(); i++ ) { const SHAPE_LINE_CHAIN& outline = polySet.COutline( i ); @@ -1164,9 +1167,12 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer ) m_gal->DrawPolyline( corners ); } + corners.clear(); } +#endif } + } diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index f19a0e8747..dbcd72c75d 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -23,6 +23,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include +#include +#include #include "pcb_editor_control.h" #include "pcb_actions.h" @@ -55,6 +57,12 @@ #include #include +#include + +#ifdef USE_OPENMP +#include +#endif /* USE_OPENMP */ + #include #include @@ -82,24 +90,6 @@ TOOL_ACTION PCB_ACTIONS::trackViaSizeChanged( "pcbnew.EditorControl.trackViaSize AS_GLOBAL, 0, "", "", NULL, AF_NOTIFY ); - -// Zone actions -TOOL_ACTION PCB_ACTIONS::zoneFill( "pcbnew.EditorControl.zoneFill", - AS_GLOBAL, 0, - _( "Fill" ), _( "Fill zone(s)" ), fill_zone_xpm ); - -TOOL_ACTION PCB_ACTIONS::zoneFillAll( "pcbnew.EditorControl.zoneFillAll", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_FILL_OR_REFILL ), - _( "Fill All" ), _( "Fill all zones" ) ); - -TOOL_ACTION PCB_ACTIONS::zoneUnfill( "pcbnew.EditorControl.zoneUnfill", - AS_GLOBAL, 0, - _( "Unfill" ), _( "Unfill zone(s)" ), zone_unfill_xpm ); - -TOOL_ACTION PCB_ACTIONS::zoneUnfillAll( "pcbnew.EditorControl.zoneUnfillAll", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_REMOVE_FILLED ), - _( "Unfill All" ), _( "Unfill all zones" ) ); - TOOL_ACTION PCB_ACTIONS::zoneMerge( "pcbnew.EditorControl.zoneMerge", AS_GLOBAL, 0, _( "Merge Zones" ), _( "Merge zones" ) ); @@ -175,6 +165,7 @@ public: Add( PCB_ACTIONS::drawSimilarZone ); } + protected: CONTEXT_MENU* create() const override { @@ -655,139 +646,6 @@ int PCB_EDITOR_CONTROL::PlaceTarget( const TOOL_EVENT& aEvent ) return 0; } - -// Zone actions -int PCB_EDITOR_CONTROL::ZoneFill( const TOOL_EVENT& aEvent ) -{ - auto selTool = m_toolMgr->GetTool(); - const auto& selection = selTool->GetSelection(); - auto connectivity = getModel()->GetConnectivity(); - - BOARD_COMMIT commit( this ); - - for( auto item : selection ) - { - assert( item->Type() == PCB_ZONE_AREA_T ); - - ZONE_CONTAINER* zone = static_cast ( item ); - - commit.Modify( zone ); - - m_frame->Fill_Zone( zone ); - zone->SetIsFilled( true ); - } - - commit.Push( _( "Fill Zone" ) ); - - connectivity->RecalculateRatsnest(); - - return 0; -} - - -int PCB_EDITOR_CONTROL::ZoneFillAll( const TOOL_EVENT& aEvent ) -{ - BOARD* board = getModel(); - auto connectivity = getModel()->GetConnectivity(); - int areaCount = board->GetAreaCount(); - const wxString fmt = _( "Filling zone %d out of %d (net %s)..." ); - wxString msg; - bool aborted = false; - - // Create a message with a long net name, and build a wxProgressDialog - // with a correct size to show this long net name - msg.Printf( fmt, 000, areaCount, wxT("XXXXXXXXXXXXXXXXX" ) ); - - auto progressDialog = new wxProgressDialog( _( "Fill All Zones" ), msg, - areaCount, frame(), - wxPD_AUTO_HIDE | wxPD_CAN_ABORT | - wxPD_APP_MODAL | wxPD_ELAPSED_TIME ); - - BOARD_COMMIT commit( this ); - - for( int i = 0; i < areaCount; ++i ) - { - ZONE_CONTAINER* zone = board->GetArea( i ); - - msg.Printf( fmt, i, areaCount, GetChars( zone->GetNetname() ) ); - - commit.Modify( zone ); - - if( !progressDialog->Update( i, msg ) ) - { - aborted = true; - break; // Aborted by user - } - - m_frame->Fill_Zone( zone ); - zone->SetIsFilled( true ); - } - - if( aborted ) - commit.Revert(); - else - commit.Push( _( "Fill All Zones" ) ); - - connectivity->RecalculateRatsnest(); - progressDialog->Destroy(); - - return 0; -} - - -int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent ) -{ - auto selTool = m_toolMgr->GetTool(); - const auto& selection = selTool->GetSelection(); - auto connectivity = getModel()->GetConnectivity(); - - BOARD_COMMIT commit( this ); - - for( auto item : selection ) - { - assert( item->Type() == PCB_ZONE_AREA_T ); - - ZONE_CONTAINER* zone = static_cast( item ); - - commit.Modify( zone ); - - zone->SetIsFilled( false ); - zone->ClearFilledPolysList(); - } - - commit.Push( _( "Unfill Zone" ) ); - - connectivity->RecalculateRatsnest(); - - return 0; -} - - -int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) -{ - BOARD* board = getModel(); - auto connectivity = getModel()->GetConnectivity(); - - BOARD_COMMIT commit( this ); - - for( int i = 0; i < board->GetAreaCount(); ++i ) - { - ZONE_CONTAINER* zone = board->GetArea( i ); - - commit.Modify( zone ); - - zone->SetIsFilled( false ); - zone->ClearFilledPolysList(); - } - - commit.Push( _( "Unfill All Zones" ) ); - - connectivity->RecalculateRatsnest(); - - return 0; -} - - static bool mergeZones( BOARD_COMMIT& aCommit, std::vector& aOriginZones, std::vector& aMergedZones ) { @@ -819,6 +677,7 @@ static bool mergeZones( BOARD_COMMIT& aCommit, std::vector& aO aOriginZones[0]->SetLocalFlags( 1 ); aOriginZones[0]->Hatch(); + aOriginZones[0]->CacheTriangulation(); return true; } @@ -1250,10 +1109,6 @@ void PCB_EDITOR_CONTROL::setTransitions() Go( &PCB_EDITOR_CONTROL::ViaSizeDec, PCB_ACTIONS::viaSizeDec.MakeEvent() ); // Zone actions - Go( &PCB_EDITOR_CONTROL::ZoneFill, PCB_ACTIONS::zoneFill.MakeEvent() ); - Go( &PCB_EDITOR_CONTROL::ZoneFillAll, PCB_ACTIONS::zoneFillAll.MakeEvent() ); - Go( &PCB_EDITOR_CONTROL::ZoneUnfill, PCB_ACTIONS::zoneUnfill.MakeEvent() ); - Go( &PCB_EDITOR_CONTROL::ZoneUnfillAll, PCB_ACTIONS::zoneUnfillAll.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::ZoneMerge, PCB_ACTIONS::zoneMerge.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::ZoneDuplicate, PCB_ACTIONS::zoneDuplicate.MakeEvent() ); diff --git a/pcbnew/tools/pcb_editor_control.h b/pcbnew/tools/pcb_editor_control.h index 49539a2209..a26784a375 100644 --- a/pcbnew/tools/pcb_editor_control.h +++ b/pcbnew/tools/pcb_editor_control.h @@ -57,10 +57,6 @@ public: int ViaSizeDec( const TOOL_EVENT& aEvent ); // Zone actions - int ZoneFill( const TOOL_EVENT& aEvent ); - int ZoneFillAll( const TOOL_EVENT& aEvent ); - int ZoneUnfill( const TOOL_EVENT& aEvent ); - int ZoneUnfillAll( const TOOL_EVENT& aEvent ); int ZoneMerge( const TOOL_EVENT& aEvent ); ///> Duplicates a zone onto a layer (prompts for new layer) diff --git a/pcbnew/tools/tools_common.cpp b/pcbnew/tools/tools_common.cpp index 499a5f9fe0..76d2d57047 100644 --- a/pcbnew/tools/tools_common.cpp +++ b/pcbnew/tools/tools_common.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -62,4 +63,5 @@ void PCB_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager ) aToolManager->RegisterTool( new ALIGN_DISTRIBUTE_TOOL ); aToolManager->RegisterTool( new MICROWAVE_TOOL ); aToolManager->RegisterTool( new POSITION_RELATIVE_TOOL ); + aToolManager->RegisterTool( new ZONE_FILLER_TOOL ); } diff --git a/pcbnew/tools/zone_filler_tool.cpp b/pcbnew/tools/zone_filler_tool.cpp new file mode 100644 index 0000000000..667f8dc05a --- /dev/null +++ b/pcbnew/tools/zone_filler_tool.cpp @@ -0,0 +1,327 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2017 CERN + * Copyright (C) 2014-2017 KiCad Developers, see AUTHORS.txt for contributors. + * @author Maciej Suminski + * + * 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 +#include + +#include +#include +#include +#include + +#include "pcb_actions.h" +#include "selection_tool.h" +#include "zone_filler_tool.h" + +#ifdef USE_OPENMP +#include +#endif /* USE_OPENMP */ + +// Zone actions +TOOL_ACTION PCB_ACTIONS::zoneFill( "pcbnew.ZoneFiller.zoneFill", + AS_GLOBAL, 0, + _( "Fill" ), _( "Fill zone(s)" ), fill_zone_xpm ); + +TOOL_ACTION PCB_ACTIONS::zoneFillAll( "pcbnew.ZoneFiller.zoneFillAll", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_FILL_OR_REFILL ), + _( "Fill All" ), _( "Fill all zones" ) ); + +TOOL_ACTION PCB_ACTIONS::zoneUnfill( "pcbnew.ZoneFiller.zoneUnfill", + AS_GLOBAL, 0, + _( "Unfill" ), _( "Unfill zone(s)" ), zone_unfill_xpm ); + +TOOL_ACTION PCB_ACTIONS::zoneUnfillAll( "pcbnew.ZoneFiller.zoneUnfillAll", + AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_REMOVE_FILLED ), + _( "Unfill All" ), _( "Unfill all zones" ) ); + + +class ZONE_FILLER +{ +public: + ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ); + ~ZONE_FILLER(); + + void SetProgressReporter( PROGRESS_REPORTER* aReporter ); + void Fill( std::vector aZones ); + void Unfill( std::vector aZones ); + +private: + COMMIT* m_commit; + PROGRESS_REPORTER* m_progressReporter; + BOARD* m_board; +}; + +ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) : + m_commit( aCommit ), + m_board( aBoard ) +{ +} + + +ZONE_FILLER::~ZONE_FILLER() +{ +} + + +void ZONE_FILLER::SetProgressReporter( PROGRESS_REPORTER* aReporter ) +{ + m_progressReporter = aReporter; +} + + +void ZONE_FILLER::Fill( std::vector aZones ) +{ + std::vector toFill; + + assert( m_commit ); + + // Remove segment zones + m_board->m_Zone.DeleteAll(); + + int ii; + + for( auto zone : aZones ) + { + // Keepout zones are not filled + if( zone->GetIsKeepout() ) + continue; + + CN_ZONE_ISOLATED_ISLAND_LIST l; + + l.m_zone = zone; + + toFill.push_back( l ); + } + + int zoneCount = m_board->GetAreaCount(); + + for( int i = 0; i < toFill.size(); i++ ) + { + m_commit->Modify( toFill[i].m_zone ); + } + + if( m_progressReporter ) + { + m_progressReporter->Report( _( "Calculating zone fills..." ) ); + m_progressReporter->SetMaxProgress( toFill.size() ); + } + + #ifdef USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for( int i = 0; i < toFill.size(); i++ ) + { + toFill[i].m_zone->BuildFilledSolidAreasPolygons( m_board ); + + m_progressReporter->AdvanceProgress(); + } + + if( m_progressReporter ) + { + m_progressReporter->AdvancePhase(); + m_progressReporter->Report( _( "Removing insulated copper islands..." ) ); + } + + m_board->GetConnectivity()->SetProgressReporter( m_progressReporter ); + m_board->GetConnectivity()->FindIsolatedCopperIslands( toFill ); + + for( auto& zone : toFill ) + { + std::sort( zone.m_islands.begin(), zone.m_islands.end(), std::greater() ); + SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList(); + + for( auto idx : zone.m_islands ) + { + poly.DeletePolygon( idx ); + } + + zone.m_zone->AddFilledPolysList( poly ); + } + + if( m_progressReporter ) + { + m_progressReporter->AdvancePhase(); + m_progressReporter->Report( _( "Caching polygon triangulations..." ) ); + m_progressReporter->SetMaxProgress( toFill.size() ); + } + + #ifdef USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for( int i = 0; i < toFill.size(); i++ ) + { + m_progressReporter->AdvanceProgress(); + toFill[i].m_zone->CacheTriangulation(); + } + + m_progressReporter->AdvancePhase(); + m_progressReporter->Report( _( "Committing changes..." ) ); + + m_commit->Push( _( "Fill Zones" ), false ); +} + + + + +ZONE_FILLER_TOOL::ZONE_FILLER_TOOL() : + PCB_TOOL( "pcbnew.ZoneFiller" ) +{ +} + + +ZONE_FILLER_TOOL::~ZONE_FILLER_TOOL() +{ +} + + +void ZONE_FILLER_TOOL::Reset( RESET_REASON aReason ) +{ +} + +// Zone actions +int ZONE_FILLER_TOOL::ZoneFill( const TOOL_EVENT& aEvent ) +{ + auto selTool = m_toolMgr->GetTool(); + const auto& selection = selTool->GetSelection(); + std::vector toFill; + + BOARD_COMMIT commit( this ); + + for( auto item : selection ) + { + assert( item->Type() == PCB_ZONE_AREA_T ); + + ZONE_CONTAINER* zone = static_cast ( item ); + + toFill.push_back(zone); + } + + std::unique_ptr progressReporter( + new WX_PROGRESS_REPORTER( frame(), _( "Fill Zones" ), 3 ) + ); + + ZONE_FILLER filler( board(), &commit ); + filler.SetProgressReporter( progressReporter.get() ); + filler.Fill( toFill ); + + return 0; +} + + +int ZONE_FILLER_TOOL::ZoneFillAll( const TOOL_EVENT& aEvent ) +{ + std::vector toFill; + + BOARD_COMMIT commit( this ); + + for( auto zone : board()->Zones() ) + { + toFill.push_back(zone); + } + + std::unique_ptr progressReporter( + new WX_PROGRESS_REPORTER( frame(), _( "Fill All Zones" ), 3 ) + ); + + ZONE_FILLER filler( board(), &commit ); + filler.SetProgressReporter( progressReporter.get() ); + filler.Fill( toFill ); + + return 0; +} + + +int ZONE_FILLER_TOOL::ZoneUnfill( const TOOL_EVENT& aEvent ) +{ + auto selTool = m_toolMgr->GetTool(); + const auto& selection = selTool->GetSelection(); + auto connectivity = getModel()->GetConnectivity(); + + BOARD_COMMIT commit( this ); + + for( auto item : selection ) + { + assert( item->Type() == PCB_ZONE_AREA_T ); + + ZONE_CONTAINER* zone = static_cast( item ); + + commit.Modify( zone ); + + zone->SetIsFilled( false ); + zone->ClearFilledPolysList(); + } + + commit.Push( _( "Unfill Zone" ) ); + + connectivity->RecalculateRatsnest(); + + return 0; +} + + +int ZONE_FILLER_TOOL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) +{ + BOARD* board = getModel(); + auto connectivity = getModel()->GetConnectivity(); + + BOARD_COMMIT commit( this ); + + for( int i = 0; i < board->GetAreaCount(); ++i ) + { + ZONE_CONTAINER* zone = board->GetArea( i ); + + commit.Modify( zone ); + + zone->SetIsFilled( false ); + zone->ClearFilledPolysList(); + } + + commit.Push( _( "Unfill All Zones" ) ); + + connectivity->RecalculateRatsnest(); + + return 0; +} + + +void ZONE_FILLER_TOOL::setTransitions() +{ + // Zone actions + Go( &ZONE_FILLER_TOOL::ZoneFill, PCB_ACTIONS::zoneFill.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneFillAll, PCB_ACTIONS::zoneFillAll.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneUnfill, PCB_ACTIONS::zoneUnfill.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneUnfillAll, PCB_ACTIONS::zoneUnfillAll.MakeEvent() ); +} diff --git a/pcbnew/tools/zone_filler_tool.h b/pcbnew/tools/zone_filler_tool.h new file mode 100644 index 0000000000..4ff7ad521f --- /dev/null +++ b/pcbnew/tools/zone_filler_tool.h @@ -0,0 +1,58 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 - 2017 CERN + * @author Maciej Suminski + * + * 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 + */ + +#ifndef ZONE_FILLER_TOOL_H +#define ZONE_FILLER_TOOL_H + +#include + + +class PCB_EDIT_FRAME; + +/** + * Class ZONE_FILLER_TOOL + * + * Handles actions specific to filling copper zones. + */ +class ZONE_FILLER_TOOL : public PCB_TOOL +{ +public: + ZONE_FILLER_TOOL(); + ~ZONE_FILLER_TOOL(); + + /// @copydoc TOOL_INTERACTIVE::Reset() + void Reset( RESET_REASON aReason ) override; + + // Zone actions + int ZoneFill( const TOOL_EVENT& aEvent ); + int ZoneFillAll( const TOOL_EVENT& aEvent ); + int ZoneUnfill( const TOOL_EVENT& aEvent ); + int ZoneUnfillAll( const TOOL_EVENT& aEvent ); + +private: + ///> Sets up handlers for various events. + void setTransitions() override; +}; + +#endif diff --git a/pcbnew/zone_filling_algorithm.cpp b/pcbnew/zone_filling_algorithm.cpp index 5b971a42eb..ecb65b0361 100644 --- a/pcbnew/zone_filling_algorithm.cpp +++ b/pcbnew/zone_filling_algorithm.cpp @@ -29,15 +29,82 @@ #include // sort -#include -#include -#include -#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include #include +#include #include #include +#include + +#include +#include +#include + +#include + + +/* Functions to convert some board items to polygons + * (pads, tracks ..) + * This is used to calculate filled areas in copper zones. + * Filled areas are areas remainder of the full zone area after removed all polygons + * calculated from these items shapes and the clearance area + * + * Important note: + * Because filled areas must have a minimum thickness to match with Design rule, they are + * draw in 2 step: + * 1 - filled polygons are drawn + * 2 - polygon outlines are drawn with a "minimum thickness width" ( or with a minimum + * thickness pen ) + * So outlines of filled polygons are calculated with the constraint they match with clearance, + * taking in account outlines have thickness + * This ensures: + * - areas meet the minimum thickness requirement. + * - shapes are smoothed. + */ + +// Polygon calculations can use fast mode or force strickly simple polygons after calculations +// Forcing strickly simple polygons is time consuming, and we have not see issues in fast mode +// so we use fast mode when possible (intermediate calculations) +// (choice is SHAPE_POLY_SET::PM_STRICTLY_SIMPLE or SHAPE_POLY_SET::PM_FAST) +#define POLY_CALC_MODE SHAPE_POLY_SET::PM_FAST + +/* DEBUG OPTION: + * To emit zone data to a file when filling zones for the debugging purposes, + * set this 'true' and build. + */ +static const bool s_DumpZonesWhenFilling = false; + +extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, + BOARD* aPcb, ZONE_CONTAINER* aZone, + double aArcCorrection, + double aRoundPadThermalRotation); + + +extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer, + D_PAD& aPad, + int aThermalGap, + int aCopperThickness, + int aMinThicknessValue, + int aCircleToSegmentsCount, + double aCorrectionFactor, + double aThermalRot ); + +// Local Variables: +static double s_thermalRot = 450; // angle of stubs in thermal reliefs for round pads + /* Build the filled solid areas data from real outlines (stored in m_Poly) * The solid areas can be more than one on copper layers, and do not have holes @@ -51,7 +118,7 @@ * if not null: * Only the zone outline (with holes, if any) are stored in aCornerBuffer * with holes linked. Therefore only one polygon is created - * This function calls AddClearanceAreasPolygonsToPolysList() + * This function calls ComputeRawFilledAreas() * to add holes for pads and tracks and other items not in net. */ @@ -109,7 +176,7 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* if( IsOnCopperLayer() ) { - AddClearanceAreasPolygonsToPolysList_NG( aPcb ); + ComputeRawFilledAreas( aPcb ); if( m_FillMode ) // if fill mode uses segments, create them: { @@ -283,7 +350,7 @@ bool fillPolygonWithHorizontalSegments( const SHAPE_LINE_CHAIN& aPolygon, // Sort intersection points by increasing x value: // So 2 consecutive points are the ends of a segment - sort( x_coordinates.begin(), x_coordinates.end() ); + std::sort( x_coordinates.begin(), x_coordinates.end() ); // An even number of coordinates is expected, because a segment has 2 ends. // An if this algorithm always works, it must always find an even count. @@ -310,3 +377,502 @@ bool fillPolygonWithHorizontalSegments( const SHAPE_LINE_CHAIN& aPolygon, return success; } + +void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ) +{ + int segsPerCircle; + double correctionFactor; + + // Set the number of segments in arc approximations + if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) + segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; + else + segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; + + /* calculates the coeff to compensate radius reduction of holes clearance + * due to the segment approx. + * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) + * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) + */ + correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); + + aFeatures.RemoveAllContours(); + + int outline_half_thickness = m_ZoneMinThickness / 2; + + // When removing holes, the holes must be expanded by outline_half_thickness + // to take in account the thickness of the zone outlines + int zone_clearance = GetClearance() + outline_half_thickness; + + // When holes are created by non copper items (edge cut items), use only + // the m_ZoneClearance parameter (zone clearance with no netclass clearance) + int zone_to_edgecut_clearance = GetZoneClearance() + outline_half_thickness; + + /* store holes (i.e. tracks and pads areas as polygons outlines) + * in a polygon list + */ + + /* items ouside the zone bounding box are skipped + * the bounding box is the zone bounding box + the biggest clearance found in Netclass list + */ + EDA_RECT item_boundingbox; + EDA_RECT zone_boundingbox = GetBoundingBox(); + int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); + biggest_clearance = std::max( biggest_clearance, zone_clearance ); + zone_boundingbox.Inflate( biggest_clearance ); + + /* + * First : Add pads. Note: pads having the same net as zone are left in zone. + * Thermal shapes will be created later if necessary + */ + + /* Use a dummy pad to calculate hole clearance when a pad is not on all copper layers + * and this pad has a hole + * This dummy pad has the size and shape of the hole + * Therefore, this dummy pad is a circle or an oval. + * A pad must have a parent because some functions expect a non null parent + * to find the parent board, and some other data + */ + MODULE dummymodule( aPcb ); // Creates a dummy parent + D_PAD dummypad( &dummymodule ); + + for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) + { + D_PAD* nextpad; + + for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad ) + { + nextpad = pad->Next(); // pad pointer can be modified by next code, so + // calculate the next pad here + + if( !pad->IsOnLayer( GetLayer() ) ) + { + /* Test for pads that are on top or bottom only and have a hole. + * There are curious pads but they can be used for some components that are + * inside the board (in fact inside the hole. Some photo diodes and Leds are + * like this) + */ + if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) + continue; + + // Use a dummy pad to calculate a hole shape that have the same dimension as + // the pad hole + dummypad.SetSize( pad->GetDrillSize() ); + dummypad.SetOrientation( pad->GetOrientation() ); + dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? + PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); + dummypad.SetPosition( pad->GetPosition() ); + + pad = &dummypad; + } + + // Note: netcode <=0 means not connected item + if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) + { + int item_clearance = pad->GetClearance() + outline_half_thickness; + item_boundingbox = pad->GetBoundingBox(); + item_boundingbox.Inflate( item_clearance ); + + if( item_boundingbox.Intersects( zone_boundingbox ) ) + { + int clearance = std::max( zone_clearance, item_clearance ); + + // PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape + if( pad->GetShape() == PAD_SHAPE_CUSTOM && + pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) + { + // the pad shape in zone can be its convex hull or + // the shape itself + SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); + outline.Inflate( KiROUND( clearance*correctionFactor) , segsPerCircle ); + pad->CustomShapeAsPolygonToBoardPosition( &outline, + pad->GetPosition(), pad->GetOrientation() ); + + if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) + { + std::vector convex_hull; + BuildConvexHull( convex_hull, outline ); + + aFeatures.NewOutline(); + for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) + aFeatures.Append( convex_hull[ii] ); + } + else + aFeatures.Append( outline ); + } + else + pad->TransformShapeWithClearanceToPolygon( aFeatures, + clearance, + segsPerCircle, + correctionFactor ); + } + + continue; + } + + // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE + // or if they have a custom shape, because a thermal relief will break + // the shape + if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE || + pad->GetShape() == PAD_SHAPE_CUSTOM ) + { + int gap = zone_clearance; + int thermalGap = GetThermalReliefGap( pad ); + gap = std::max( gap, thermalGap ); + item_boundingbox = pad->GetBoundingBox(); + item_boundingbox.Inflate( gap ); + + if( item_boundingbox.Intersects( zone_boundingbox ) ) + { + // PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape + // the pad shape in zone can be its convex hull or the shape itself + if( pad->GetShape() == PAD_SHAPE_CUSTOM && + pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) + { + // the pad shape in zone can be its convex hull or + // the shape itself + SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); + outline.Inflate( KiROUND( gap*correctionFactor) , segsPerCircle ); + pad->CustomShapeAsPolygonToBoardPosition( &outline, + pad->GetPosition(), pad->GetOrientation() ); + + std::vector convex_hull; + BuildConvexHull( convex_hull, outline ); + + aFeatures.NewOutline(); + for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) + aFeatures.Append( convex_hull[ii] ); + } + else + pad->TransformShapeWithClearanceToPolygon( aFeatures, + gap, segsPerCircle, correctionFactor ); + } + } + } + } + + /* Add holes (i.e. tracks and vias areas as polygons outlines) + * in cornerBufferPolysToSubstract + */ + for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) + { + if( !track->IsOnLayer( GetLayer() ) ) + continue; + + if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) + continue; + + int item_clearance = track->GetClearance() + outline_half_thickness; + item_boundingbox = track->GetBoundingBox(); + + if( item_boundingbox.Intersects( zone_boundingbox ) ) + { + int clearance = std::max( zone_clearance, item_clearance ); + track->TransformShapeWithClearanceToPolygon( aFeatures, + clearance, + segsPerCircle, + correctionFactor ); + } + } + + /* Add module edge items that are on copper layers + * Pcbnew allows these items to be on copper layers in microwave applictions + * This is a bad thing, but must be handled here, until a better way is found + */ + for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) + { + for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() ) + { + if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) + continue; + + if( item->Type() != PCB_MODULE_EDGE_T ) + continue; + + item_boundingbox = item->GetBoundingBox(); + + if( item_boundingbox.Intersects( zone_boundingbox ) ) + { + int zclearance = zone_clearance; + + if( item->IsOnLayer( Edge_Cuts ) ) + // use only the m_ZoneClearance, not the clearance using + // the netclass value, because we do not have a copper item + zclearance = zone_to_edgecut_clearance; + + ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( + aFeatures, zclearance, segsPerCircle, correctionFactor ); + } + } + } + + // Add graphic items (copper texts) and board edges + // Currently copper texts have no net, so only the zone_clearance + // is used. + for( auto item : aPcb->Drawings() ) + { + if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) + continue; + + int zclearance = zone_clearance; + + if( item->GetLayer() == Edge_Cuts ) + // use only the m_ZoneClearance, not the clearance using + // the netclass value, because we do not have a copper item + zclearance = zone_to_edgecut_clearance; + + switch( item->Type() ) + { + case PCB_LINE_T: + ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( + aFeatures, + zclearance, segsPerCircle, correctionFactor ); + break; + + case PCB_TEXT_T: + ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( + aFeatures, zclearance ); + break; + + default: + break; + } + } + + // Add zones outlines having an higher priority and keepout + for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) + { + ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); + + // If the zones share no common layers + if( !CommonLayerExists( zone->GetLayerSet() ) ) + continue; + + if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) + continue; + + if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) + continue; + + // A highter priority zone or keepout area is found: remove this area + item_boundingbox = zone->GetBoundingBox(); + + if( !item_boundingbox.Intersects( zone_boundingbox ) ) + continue; + + // Add the zone outline area. + // However if the zone has the same net as the current zone, + // do not add any clearance. + // the zone will be connected to the current zone, but filled areas + // will use different parameters (clearance, thermal shapes ) + bool same_net = GetNetCode() == zone->GetNetCode(); + bool use_net_clearance = true; + int min_clearance = zone_clearance; + + // Do not forget to make room to draw the thick outlines + // of the hole created by the area of the zone to remove + int holeclearance = zone->GetClearance() + outline_half_thickness; + + // The final clearance is obviously the max value of each zone clearance + min_clearance = std::max( min_clearance, holeclearance ); + + if( zone->GetIsKeepout() || same_net ) + { + // Just take in account the fact the outline has a thickness, so + // the actual area to substract is inflated to take in account this fact + min_clearance = outline_half_thickness; + use_net_clearance = false; + } + + zone->TransformOutlinesShapeWithClearanceToPolygon( + aFeatures, min_clearance, use_net_clearance ); + } + + // Remove thermal symbols + for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) + { + for( D_PAD* pad = module->PadsList(); pad != NULL; pad = pad->Next() ) + { + // Rejects non-standard pads with tht-only thermal reliefs + if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL + && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) + continue; + + if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL + && GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) + continue; + + if( !pad->IsOnLayer( GetLayer() ) ) + continue; + + if( pad->GetNetCode() != GetNetCode() ) + continue; + + item_boundingbox = pad->GetBoundingBox(); + int thermalGap = GetThermalReliefGap( pad ); + item_boundingbox.Inflate( thermalGap, thermalGap ); + + if( item_boundingbox.Intersects( zone_boundingbox ) ) + { + CreateThermalReliefPadPolygon( aFeatures, + *pad, thermalGap, + GetThermalReliefCopperBridge( pad ), + m_ZoneMinThickness, + segsPerCircle, + correctionFactor, s_thermalRot ); + } + } + } + +} + +/** + * Function ComputeRawFilledAreas + * Supports a min thickness area constraint. + * Add non copper areas polygons (pads and tracks with clearance) + * to the filled copper area found + * in BuildFilledPolysListData after calculating filled areas in a zone + * Non filled copper areas are pads and track and their clearance areas + * The filled copper area must be computed just before. + * BuildFilledPolysListData() call this function just after creating the + * filled copper area polygon (without clearance areas) + * to do that this function: + * 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area + * with m_ZoneMinThickness/2 value. + * The result is areas with a margin of m_ZoneMinThickness/2 + * When drawing outline with segments having a thickness of m_ZoneMinThickness, the + * outlines will match exactly the initial outlines + * 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + + * m_ZoneMinThickness/2 + * in a buffer + * - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes + * 4 - calculates the polygon A - B + * 5 - put resulting list of polygons (filled areas) in m_FilledPolysList + * This zone contains pads with the same net. + * 6 - Remove insulated copper islands + * 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes: + * creates a buffer of polygons corresponding to stubs to remove + * sub them to the filled areas. + * Remove new insulated copper islands + */ + +void ZONE_CONTAINER::ComputeRawFilledAreas( BOARD* aPcb ) +{ + int segsPerCircle; + double correctionFactor; + int outline_half_thickness = m_ZoneMinThickness / 2; + + + std::unique_ptr dumper( new SHAPE_FILE_IO( + s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) ); + + // Set the number of segments in arc approximations + if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) + segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; + else + segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; + + /* calculates the coeff to compensate radius reduction of holes clearance + * due to the segment approx. + * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) + * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) + */ + correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); + + CPOLYGONS_LIST tmp; + + if(s_DumpZonesWhenFilling) + dumper->BeginGroup("clipper-zone"); + + SHAPE_POLY_SET solidAreas = *m_smoothedPoly; + + solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); + solidAreas.Simplify( POLY_CALC_MODE ); + + SHAPE_POLY_SET holes; + + if(s_DumpZonesWhenFilling) + dumper->Write( &solidAreas, "solid-areas" ); + + tmp.RemoveAllContours(); + buildFeatureHoleList( aPcb, holes ); + + if(s_DumpZonesWhenFilling) + dumper->Write( &holes, "feature-holes" ); + + holes.Simplify( POLY_CALC_MODE ); + + if (s_DumpZonesWhenFilling) + dumper->Write( &holes, "feature-holes-postsimplify" ); + + // Generate the filled areas (currently, without thermal shapes, which will + // be created later). + // Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to generate strictly simple polygons + // needed by Gerber files and Fracture() + solidAreas.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + if (s_DumpZonesWhenFilling) + dumper->Write( &solidAreas, "solid-areas-minus-holes" ); + + SHAPE_POLY_SET areas_fractured = solidAreas; + areas_fractured.Fracture( POLY_CALC_MODE ); + + if (s_DumpZonesWhenFilling) + dumper->Write( &areas_fractured, "areas_fractured" ); + + m_FilledPolysList = areas_fractured; + + SHAPE_POLY_SET thermalHoles; + + // Test thermal stubs connections and add polygons to remove unconnected stubs. + // (this is a refinement for thermal relief shapes) + if( GetNetCode() > 0 ) + BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this, + correctionFactor, s_thermalRot ); + + // remove copper areas corresponding to not connected stubs + if( !thermalHoles.IsEmpty() ) + { + thermalHoles.Simplify( POLY_CALC_MODE ); + // Remove unconnected stubs. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to + // generate strictly simple polygons + // needed by Gerber files and Fracture() + solidAreas.BooleanSubtract( thermalHoles, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + if( s_DumpZonesWhenFilling ) + dumper->Write( &thermalHoles, "thermal-holes" ); + + // put these areas in m_FilledPolysList + SHAPE_POLY_SET th_fractured = solidAreas; + th_fractured.Fracture( POLY_CALC_MODE ); + + if( s_DumpZonesWhenFilling ) + dumper->Write ( &th_fractured, "th_fractured" ); + + m_FilledPolysList = th_fractured; + + } + + m_RawPolysList = m_FilledPolysList; + + if(s_DumpZonesWhenFilling) + dumper->EndGroup(); +} + +void ZONE_CONTAINER::RemoveInsulatedCopperIslands( BOARD* aPcb ) +{ + std::vector islands; + + auto connectivity = aPcb->GetConnectivity(); + + connectivity->FindIsolatedCopperIslands( this, islands ); + + std::sort( islands.begin(), islands.end(), std::greater() ); + + for( auto idx : islands ) + { + m_FilledPolysList.DeletePolygon( idx ); + } + + connectivity->Update( this ); +} diff --git a/pcbnew/zones_by_polygon_fill_functions.cpp b/pcbnew/zones_by_polygon_fill_functions.cpp index d6cb064957..13c3ae0d07 100644 --- a/pcbnew/zones_by_polygon_fill_functions.cpp +++ b/pcbnew/zones_by_polygon_fill_functions.cpp @@ -128,9 +128,15 @@ int PCB_EDIT_FRAME::Fill_Zone( ZONE_CONTAINER* aZone ) //OnModify(); return 0; + } +int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose ) +{ + return 0; +} +/* int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose ) { int errorLevel = 0; @@ -197,3 +203,4 @@ int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose ) return errorLevel; } +*/ diff --git a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp index 3c0ea70944..e69de29bb2 100644 --- a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp +++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp @@ -1,587 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 - */ - -/* Functions to convert some board items to polygons - * (pads, tracks ..) - * This is used to calculate filled areas in copper zones. - * Filled areas are areas remainder of the full zone area after removed all polygons - * calculated from these items shapes and the clearance area - * - * Important note: - * Because filled areas must have a minimum thickness to match with Design rule, they are - * draw in 2 step: - * 1 - filled polygons are drawn - * 2 - polygon outlines are drawn with a "minimum thickness width" ( or with a minimum - * thickness pen ) - * So outlines of filled polygons are calculated with the constraint they match with clearance, - * taking in account outlines have thickness - * This ensures: - * - areas meet the minimum thickness requirement. - * - shapes are smoothed. - */ - -// Polygon calculations can use fast mode or force strickly simple polygons after calculations -// Forcing strickly simple polygons is time consuming, and we have not see issues in fast mode -// so we use fast mode when possible (intermediate calculations) -// (choice is SHAPE_POLY_SET::PM_STRICTLY_SIMPLE or SHAPE_POLY_SET::PM_FAST) -#define POLY_CALC_MODE SHAPE_POLY_SET::PM_FAST - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -/* DEBUG OPTION: - * To emit zone data to a file when filling zones for the debugging purposes, - * set this 'true' and build. - */ -static const bool s_DumpZonesWhenFilling = false; - -extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, - BOARD* aPcb, ZONE_CONTAINER* aZone, - double aArcCorrection, - double aRoundPadThermalRotation); - -extern void Test_For_Copper_Island_And_Remove( BOARD* aPcb, - ZONE_CONTAINER* aZone_container ); - -extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer, - D_PAD& aPad, - int aThermalGap, - int aCopperThickness, - int aMinThicknessValue, - int aCircleToSegmentsCount, - double aCorrectionFactor, - double aThermalRot ); - -// Local Variables: -static double s_thermalRot = 450; // angle of stubs in thermal reliefs for round pads - -void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ) -{ - int segsPerCircle; - double correctionFactor; - - // Set the number of segments in arc approximations - if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) - segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; - else - segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; - - /* calculates the coeff to compensate radius reduction of holes clearance - * due to the segment approx. - * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) - * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) - */ - correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); - - aFeatures.RemoveAllContours(); - - int outline_half_thickness = m_ZoneMinThickness / 2; - - // When removing holes, the holes must be expanded by outline_half_thickness - // to take in account the thickness of the zone outlines - int zone_clearance = GetClearance() + outline_half_thickness; - - // When holes are created by non copper items (edge cut items), use only - // the m_ZoneClearance parameter (zone clearance with no netclass clearance) - int zone_to_edgecut_clearance = GetZoneClearance() + outline_half_thickness; - - /* store holes (i.e. tracks and pads areas as polygons outlines) - * in a polygon list - */ - - /* items ouside the zone bounding box are skipped - * the bounding box is the zone bounding box + the biggest clearance found in Netclass list - */ - EDA_RECT item_boundingbox; - EDA_RECT zone_boundingbox = GetBoundingBox(); - int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); - biggest_clearance = std::max( biggest_clearance, zone_clearance ); - zone_boundingbox.Inflate( biggest_clearance ); - - /* - * First : Add pads. Note: pads having the same net as zone are left in zone. - * Thermal shapes will be created later if necessary - */ - - /* Use a dummy pad to calculate hole clearance when a pad is not on all copper layers - * and this pad has a hole - * This dummy pad has the size and shape of the hole - * Therefore, this dummy pad is a circle or an oval. - * A pad must have a parent because some functions expect a non null parent - * to find the parent board, and some other data - */ - MODULE dummymodule( aPcb ); // Creates a dummy parent - D_PAD dummypad( &dummymodule ); - - for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) - { - D_PAD* nextpad; - - for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad ) - { - nextpad = pad->Next(); // pad pointer can be modified by next code, so - // calculate the next pad here - - if( !pad->IsOnLayer( GetLayer() ) ) - { - /* Test for pads that are on top or bottom only and have a hole. - * There are curious pads but they can be used for some components that are - * inside the board (in fact inside the hole. Some photo diodes and Leds are - * like this) - */ - if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) - continue; - - // Use a dummy pad to calculate a hole shape that have the same dimension as - // the pad hole - dummypad.SetSize( pad->GetDrillSize() ); - dummypad.SetOrientation( pad->GetOrientation() ); - dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? - PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); - dummypad.SetPosition( pad->GetPosition() ); - - pad = &dummypad; - } - - // Note: netcode <=0 means not connected item - if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) - { - int item_clearance = pad->GetClearance() + outline_half_thickness; - item_boundingbox = pad->GetBoundingBox(); - item_boundingbox.Inflate( item_clearance ); - - if( item_boundingbox.Intersects( zone_boundingbox ) ) - { - int clearance = std::max( zone_clearance, item_clearance ); - - // PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape - if( pad->GetShape() == PAD_SHAPE_CUSTOM && - pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) - { - // the pad shape in zone can be its convex hull or - // the shape itself - SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); - outline.Inflate( KiROUND( clearance*correctionFactor) , segsPerCircle ); - pad->CustomShapeAsPolygonToBoardPosition( &outline, - pad->GetPosition(), pad->GetOrientation() ); - - if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) - { - std::vector convex_hull; - BuildConvexHull( convex_hull, outline ); - - aFeatures.NewOutline(); - for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) - aFeatures.Append( convex_hull[ii] ); - } - else - aFeatures.Append( outline ); - } - else - pad->TransformShapeWithClearanceToPolygon( aFeatures, - clearance, - segsPerCircle, - correctionFactor ); - } - - continue; - } - - // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE - // or if they have a custom shape, because a thermal relief will break - // the shape - if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE || - pad->GetShape() == PAD_SHAPE_CUSTOM ) - { - int gap = zone_clearance; - int thermalGap = GetThermalReliefGap( pad ); - gap = std::max( gap, thermalGap ); - item_boundingbox = pad->GetBoundingBox(); - item_boundingbox.Inflate( gap ); - - if( item_boundingbox.Intersects( zone_boundingbox ) ) - { - // PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape - // the pad shape in zone can be its convex hull or the shape itself - if( pad->GetShape() == PAD_SHAPE_CUSTOM && - pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) - { - // the pad shape in zone can be its convex hull or - // the shape itself - SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); - outline.Inflate( KiROUND( gap*correctionFactor) , segsPerCircle ); - pad->CustomShapeAsPolygonToBoardPosition( &outline, - pad->GetPosition(), pad->GetOrientation() ); - - std::vector convex_hull; - BuildConvexHull( convex_hull, outline ); - - aFeatures.NewOutline(); - for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) - aFeatures.Append( convex_hull[ii] ); - } - else - pad->TransformShapeWithClearanceToPolygon( aFeatures, - gap, segsPerCircle, correctionFactor ); - } - } - } - } - - /* Add holes (i.e. tracks and vias areas as polygons outlines) - * in cornerBufferPolysToSubstract - */ - for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) - { - if( !track->IsOnLayer( GetLayer() ) ) - continue; - - if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) - continue; - - int item_clearance = track->GetClearance() + outline_half_thickness; - item_boundingbox = track->GetBoundingBox(); - - if( item_boundingbox.Intersects( zone_boundingbox ) ) - { - int clearance = std::max( zone_clearance, item_clearance ); - track->TransformShapeWithClearanceToPolygon( aFeatures, - clearance, - segsPerCircle, - correctionFactor ); - } - } - - /* Add module edge items that are on copper layers - * Pcbnew allows these items to be on copper layers in microwave applictions - * This is a bad thing, but must be handled here, until a better way is found - */ - for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) - { - for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() ) - { - if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) - continue; - - if( item->Type() != PCB_MODULE_EDGE_T ) - continue; - - item_boundingbox = item->GetBoundingBox(); - - if( item_boundingbox.Intersects( zone_boundingbox ) ) - { - int zclearance = zone_clearance; - - if( item->IsOnLayer( Edge_Cuts ) ) - // use only the m_ZoneClearance, not the clearance using - // the netclass value, because we do not have a copper item - zclearance = zone_to_edgecut_clearance; - - ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( - aFeatures, zclearance, segsPerCircle, correctionFactor ); - } - } - } - - // Add graphic items (copper texts) and board edges - // Currently copper texts have no net, so only the zone_clearance - // is used. - for( auto item : aPcb->Drawings() ) - { - if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) - continue; - - int zclearance = zone_clearance; - - if( item->GetLayer() == Edge_Cuts ) - // use only the m_ZoneClearance, not the clearance using - // the netclass value, because we do not have a copper item - zclearance = zone_to_edgecut_clearance; - - switch( item->Type() ) - { - case PCB_LINE_T: - ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( - aFeatures, - zclearance, segsPerCircle, correctionFactor ); - break; - - case PCB_TEXT_T: - ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( - aFeatures, zclearance ); - break; - - default: - break; - } - } - - // Add zones outlines having an higher priority and keepout - for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) - { - ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); - - // If the zones share no common layers - if( !CommonLayerExists( zone->GetLayerSet() ) ) - continue; - - if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) - continue; - - if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) - continue; - - // A highter priority zone or keepout area is found: remove this area - item_boundingbox = zone->GetBoundingBox(); - - if( !item_boundingbox.Intersects( zone_boundingbox ) ) - continue; - - // Add the zone outline area. - // However if the zone has the same net as the current zone, - // do not add any clearance. - // the zone will be connected to the current zone, but filled areas - // will use different parameters (clearance, thermal shapes ) - bool same_net = GetNetCode() == zone->GetNetCode(); - bool use_net_clearance = true; - int min_clearance = zone_clearance; - - // Do not forget to make room to draw the thick outlines - // of the hole created by the area of the zone to remove - int holeclearance = zone->GetClearance() + outline_half_thickness; - - // The final clearance is obviously the max value of each zone clearance - min_clearance = std::max( min_clearance, holeclearance ); - - if( zone->GetIsKeepout() || same_net ) - { - // Just take in account the fact the outline has a thickness, so - // the actual area to substract is inflated to take in account this fact - min_clearance = outline_half_thickness; - use_net_clearance = false; - } - - zone->TransformOutlinesShapeWithClearanceToPolygon( - aFeatures, min_clearance, use_net_clearance ); - } - - // Remove thermal symbols - for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) - { - for( D_PAD* pad = module->PadsList(); pad != NULL; pad = pad->Next() ) - { - // Rejects non-standard pads with tht-only thermal reliefs - if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL - && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) - continue; - - if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL - && GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) - continue; - - if( !pad->IsOnLayer( GetLayer() ) ) - continue; - - if( pad->GetNetCode() != GetNetCode() ) - continue; - - item_boundingbox = pad->GetBoundingBox(); - int thermalGap = GetThermalReliefGap( pad ); - item_boundingbox.Inflate( thermalGap, thermalGap ); - - if( item_boundingbox.Intersects( zone_boundingbox ) ) - { - CreateThermalReliefPadPolygon( aFeatures, - *pad, thermalGap, - GetThermalReliefCopperBridge( pad ), - m_ZoneMinThickness, - segsPerCircle, - correctionFactor, s_thermalRot ); - } - } - } - -} - - -/** - * Function AddClearanceAreasPolygonsToPolysList - * Supports a min thickness area constraint. - * Add non copper areas polygons (pads and tracks with clearance) - * to the filled copper area found - * in BuildFilledPolysListData after calculating filled areas in a zone - * Non filled copper areas are pads and track and their clearance areas - * The filled copper area must be computed just before. - * BuildFilledPolysListData() call this function just after creating the - * filled copper area polygon (without clearance areas) - * to do that this function: - * 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area - * with m_ZoneMinThickness/2 value. - * The result is areas with a margin of m_ZoneMinThickness/2 - * When drawing outline with segments having a thickness of m_ZoneMinThickness, the - * outlines will match exactly the initial outlines - * 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + - * m_ZoneMinThickness/2 - * in a buffer - * - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes - * 4 - calculates the polygon A - B - * 5 - put resulting list of polygons (filled areas) in m_FilledPolysList - * This zone contains pads with the same net. - * 6 - Remove insulated copper islands - * 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes: - * creates a buffer of polygons corresponding to stubs to remove - * sub them to the filled areas. - * Remove new insulated copper islands - */ - -void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) -{ - int segsPerCircle; - double correctionFactor; - int outline_half_thickness = m_ZoneMinThickness / 2; - - - std::unique_ptr dumper( new SHAPE_FILE_IO( - s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) ); - - // Set the number of segments in arc approximations - if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) - segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; - else - segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; - - /* calculates the coeff to compensate radius reduction of holes clearance - * due to the segment approx. - * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) - * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) - */ - correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); - - CPOLYGONS_LIST tmp; - - if(s_DumpZonesWhenFilling) - dumper->BeginGroup("clipper-zone"); - - SHAPE_POLY_SET solidAreas = *m_smoothedPoly; - - solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); - solidAreas.Simplify( POLY_CALC_MODE ); - - SHAPE_POLY_SET holes; - - if(s_DumpZonesWhenFilling) - dumper->Write( &solidAreas, "solid-areas" ); - - tmp.RemoveAllContours(); - buildFeatureHoleList( aPcb, holes ); - - if(s_DumpZonesWhenFilling) - dumper->Write( &holes, "feature-holes" ); - - holes.Simplify( POLY_CALC_MODE ); - - if (s_DumpZonesWhenFilling) - dumper->Write( &holes, "feature-holes-postsimplify" ); - - // Generate the filled areas (currently, without thermal shapes, which will - // be created later). - // Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to generate strictly simple polygons - // needed by Gerber files and Fracture() - solidAreas.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - - if (s_DumpZonesWhenFilling) - dumper->Write( &solidAreas, "solid-areas-minus-holes" ); - - SHAPE_POLY_SET areas_fractured = solidAreas; - areas_fractured.Fracture( POLY_CALC_MODE ); - - if (s_DumpZonesWhenFilling) - dumper->Write( &areas_fractured, "areas_fractured" ); - - m_FilledPolysList = areas_fractured; - - SHAPE_POLY_SET thermalHoles; - - // Test thermal stubs connections and add polygons to remove unconnected stubs. - // (this is a refinement for thermal relief shapes) - if( GetNetCode() > 0 ) - BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this, - correctionFactor, s_thermalRot ); - - // remove copper areas corresponding to not connected stubs - if( !thermalHoles.IsEmpty() ) - { - thermalHoles.Simplify( POLY_CALC_MODE ); - // Remove unconnected stubs. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to - // generate strictly simple polygons - // needed by Gerber files and Fracture() - solidAreas.BooleanSubtract( thermalHoles, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - - if( s_DumpZonesWhenFilling ) - dumper->Write( &thermalHoles, "thermal-holes" ); - - // put these areas in m_FilledPolysList - SHAPE_POLY_SET th_fractured = solidAreas; - th_fractured.Fracture( POLY_CALC_MODE ); - - if( s_DumpZonesWhenFilling ) - dumper->Write ( &th_fractured, "th_fractured" ); - - m_FilledPolysList = th_fractured; - - } - - m_RawPolysList = m_FilledPolysList; - - if( GetNetCode() > 0 ) - TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); - - if(s_DumpZonesWhenFilling) - dumper->EndGroup(); -} - -void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) -{ -} diff --git a/pcbnew/zones_polygons_insulated_copper_islands.cpp b/pcbnew/zones_polygons_insulated_copper_islands.cpp index d107888c50..39a249caf6 100644 --- a/pcbnew/zones_polygons_insulated_copper_islands.cpp +++ b/pcbnew/zones_polygons_insulated_copper_islands.cpp @@ -28,21 +28,3 @@ #include #include #include - -void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb ) -{ - std::vector islands; - - auto connectivity = aPcb->GetConnectivity(); - - connectivity->FindIsolatedCopperIslands( this, islands ); - - std::sort( islands.begin(), islands.end(), std::greater() ); - - for( auto idx : islands ) - { - m_FilledPolysList.DeletePolygon( idx ); - } - - connectivity->Update( this ); -} diff --git a/polygon/CMakeLists.txt b/polygon/CMakeLists.txt index cd5959ef31..6205d07abf 100644 --- a/polygon/CMakeLists.txt +++ b/polygon/CMakeLists.txt @@ -13,6 +13,11 @@ set(POLYGON_SRCS PolyLine.cpp polygon_test_point_inside.cpp clipper.cpp + ./poly2tri/sweep/sweep.cc + ./poly2tri/sweep/sweep_context.cc + ./poly2tri/sweep/cdt.cc + ./poly2tri/sweep/advancing_front.cc + ./poly2tri/common/shapes.cc ) add_library(polygon STATIC ${POLYGON_SRCS}) diff --git a/polygon/poly2tri/common/shapes.cc b/polygon/poly2tri/common/shapes.cc index 06eb1f80c5..e56638979e 100644 --- a/polygon/poly2tri/common/shapes.cc +++ b/polygon/poly2tri/common/shapes.cc @@ -32,74 +32,63 @@ #include namespace p2t { -Triangle::Triangle( Point& a, Point& b, Point& c ) -{ - points_[0] = &a; points_[1] = &b; points_[2] = &c; - neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; - constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; - delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; - interior_ = false; -} +Triangle::Triangle(Point& a, Point& b, Point& c) +{ + points_[0] = &a; points_[1] = &b; points_[2] = &c; + neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; + constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; + interior_ = false; +} // Update neighbor pointers -void Triangle::MarkNeighbor( Point* p1, Point* p2, Triangle* t ) +void Triangle::MarkNeighbor(Point* p1, Point* p2, Triangle* t) { - if( (p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]) ) - neighbors_[0] = t; - else if( (p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]) ) - neighbors_[1] = t; - else if( (p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]) ) - neighbors_[2] = t; - else - assert( 0 ); + if ((p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2])) + neighbors_[0] = t; + else if ((p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0])) + neighbors_[1] = t; + else if ((p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0])) + neighbors_[2] = t; + else + assert(0); } - // Exhaustive search to update neighbor pointers -void Triangle::MarkNeighbor( Triangle& t ) +void Triangle::MarkNeighbor(Triangle& t) { - if( t.Contains( points_[1], points_[2] ) ) - { - neighbors_[0] = &t; - t.MarkNeighbor( points_[1], points_[2], this ); - } - else if( t.Contains( points_[0], points_[2] ) ) - { - neighbors_[1] = &t; - t.MarkNeighbor( points_[0], points_[2], this ); - } - else if( t.Contains( points_[0], points_[1] ) ) - { - neighbors_[2] = &t; - t.MarkNeighbor( points_[0], points_[1], this ); - } + if (t.Contains(points_[1], points_[2])) { + neighbors_[0] = &t; + t.MarkNeighbor(points_[1], points_[2], this); + } else if (t.Contains(points_[0], points_[2])) { + neighbors_[1] = &t; + t.MarkNeighbor(points_[0], points_[2], this); + } else if (t.Contains(points_[0], points_[1])) { + neighbors_[2] = &t; + t.MarkNeighbor(points_[0], points_[1], this); + } } - /** * Clears all references to all other triangles and points */ void Triangle::Clear() { - Triangle* t; - - for( int i = 0; i<3; i++ ) + Triangle *t; + for( int i=0; i<3; i++ ) { t = neighbors_[i]; - if( t != NULL ) { t->ClearNeighbor( this ); } } - ClearNeighbors(); - points_[0] = points_[1] = points_[2] = NULL; + points_[0]=points_[1]=points_[2] = NULL; } - -void Triangle::ClearNeighbor( Triangle* triangle ) +void Triangle::ClearNeighbor(Triangle *triangle ) { if( neighbors_[0] == triangle ) { @@ -115,386 +104,263 @@ void Triangle::ClearNeighbor( Triangle* triangle ) } } - void Triangle::ClearNeighbors() { - neighbors_[0] = NULL; - neighbors_[1] = NULL; - neighbors_[2] = NULL; + neighbors_[0] = NULL; + neighbors_[1] = NULL; + neighbors_[2] = NULL; } - void Triangle::ClearDelunayEdges() { - delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; } - -Point* Triangle::OppositePoint( Triangle& t, Point& p ) +Point* Triangle::OppositePoint(Triangle& t, Point& p) { - Point* cw = t.PointCW( p ); - - /* - double x = cw->x; - double y = cw->y; - - x = p.x; - y = p.y; - */ - - return PointCW( *cw ); + Point *cw = t.PointCW(p); + double x = cw->x; + double y = cw->y; + x = p.x; + y = p.y; + return PointCW(*cw); } - // Legalized triangle by rotating clockwise around point(0) -void Triangle::Legalize( Point& point ) +void Triangle::Legalize(Point& point) { - points_[1] = points_[0]; - points_[0] = points_[2]; - points_[2] = &point; + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &point; } - // Legalize triagnle by rotating clockwise around oPoint -void Triangle::Legalize( Point& opoint, Point& npoint ) +void Triangle::Legalize(Point& opoint, Point& npoint) { - if( &opoint == points_[0] ) - { - points_[1] = points_[0]; - points_[0] = points_[2]; - points_[2] = &npoint; - } - else if( &opoint == points_[1] ) - { - points_[2] = points_[1]; - points_[1] = points_[0]; - points_[0] = &npoint; - } - else if( &opoint == points_[2] ) - { - points_[0] = points_[2]; - points_[2] = points_[1]; - points_[1] = &npoint; - } - else - { - assert( 0 ); - } + if (&opoint == points_[0]) { + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &npoint; + } else if (&opoint == points_[1]) { + points_[2] = points_[1]; + points_[1] = points_[0]; + points_[0] = &npoint; + } else if (&opoint == points_[2]) { + points_[0] = points_[2]; + points_[2] = points_[1]; + points_[1] = &npoint; + } else { + assert(0); + } } - -int Triangle::Index( const Point* p ) +int Triangle::Index(const Point* p) { - if( p == points_[0] ) - { - return 0; - } - else if( p == points_[1] ) - { - return 1; - } - else if( p == points_[2] ) - { - return 2; - } - - assert( 0 ); - return 0; // you better hope its a Debug build. + if (p == points_[0]) { + return 0; + } else if (p == points_[1]) { + return 1; + } else if (p == points_[2]) { + return 2; + } + assert(0); } - -int Triangle::EdgeIndex( const Point* p1, const Point* p2 ) +int Triangle::EdgeIndex(const Point* p1, const Point* p2) { - if( points_[0] == p1 ) - { - if( points_[1] == p2 ) - { - return 2; - } - else if( points_[2] == p2 ) - { - return 1; - } + if (points_[0] == p1) { + if (points_[1] == p2) { + return 2; + } else if (points_[2] == p2) { + return 1; } - else if( points_[1] == p1 ) - { - if( points_[2] == p2 ) - { - return 0; - } - else if( points_[0] == p2 ) - { - return 2; - } + } else if (points_[1] == p1) { + if (points_[2] == p2) { + return 0; + } else if (points_[0] == p2) { + return 2; } - else if( points_[2] == p1 ) - { - if( points_[0] == p2 ) - { - return 1; - } - else if( points_[1] == p2 ) - { - return 0; - } + } else if (points_[2] == p1) { + if (points_[0] == p2) { + return 1; + } else if (points_[1] == p2) { + return 0; } - - return -1; + } + return -1; } - -void Triangle::MarkConstrainedEdge( const int index ) +void Triangle::MarkConstrainedEdge(const int index) { - constrained_edge[index] = true; + constrained_edge[index] = true; } - -void Triangle::MarkConstrainedEdge( Edge& edge ) +void Triangle::MarkConstrainedEdge(Edge& edge) { - MarkConstrainedEdge( edge.p, edge.q ); + MarkConstrainedEdge(edge.p, edge.q); } - // Mark edge as constrained -void Triangle::MarkConstrainedEdge( Point* p, Point* q ) +void Triangle::MarkConstrainedEdge(Point* p, Point* q) { - if( (q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0]) ) - { - constrained_edge[2] = true; - } - else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) ) - { - constrained_edge[1] = true; - } - else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) ) - { - constrained_edge[0] = true; - } + if ((q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0])) { + constrained_edge[2] = true; + } else if ((q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0])) { + constrained_edge[1] = true; + } else if ((q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1])) { + constrained_edge[0] = true; + } } - // The point counter-clockwise to given point -Point* Triangle::PointCW( Point& point ) +Point* Triangle::PointCW(Point& point) { - if( &point == points_[0] ) - { - return points_[2]; - } - else if( &point == points_[1] ) - { - return points_[0]; - } - else if( &point == points_[2] ) - { - return points_[1]; - } - - assert( 0 ); - return NULL; // you better hope its a Debug build. + if (&point == points_[0]) { + return points_[2]; + } else if (&point == points_[1]) { + return points_[0]; + } else if (&point == points_[2]) { + return points_[1]; + } + assert(0); } - // The point counter-clockwise to given point -Point* Triangle::PointCCW( Point& point ) +Point* Triangle::PointCCW(Point& point) { - if( &point == points_[0] ) - { - return points_[1]; - } - else if( &point == points_[1] ) - { - return points_[2]; - } - else if( &point == points_[2] ) - { - return points_[0]; - } - - assert( 0 ); - return NULL; // you better hope its a Debug build. + if (&point == points_[0]) { + return points_[1]; + } else if (&point == points_[1]) { + return points_[2]; + } else if (&point == points_[2]) { + return points_[0]; + } + assert(0); } - // The neighbor clockwise to given point -Triangle* Triangle::NeighborCW( Point& point ) +Triangle* Triangle::NeighborCW(Point& point) { - if( &point == points_[0] ) - { - return neighbors_[1]; - } - else if( &point == points_[1] ) - { - return neighbors_[2]; - } - - return neighbors_[0]; + if (&point == points_[0]) { + return neighbors_[1]; + } else if (&point == points_[1]) { + return neighbors_[2]; + } + return neighbors_[0]; } - // The neighbor counter-clockwise to given point -Triangle* Triangle::NeighborCCW( Point& point ) +Triangle* Triangle::NeighborCCW(Point& point) { - if( &point == points_[0] ) - { - return neighbors_[2]; - } - else if( &point == points_[1] ) - { - return neighbors_[0]; - } - - return neighbors_[1]; + if (&point == points_[0]) { + return neighbors_[2]; + } else if (&point == points_[1]) { + return neighbors_[0]; + } + return neighbors_[1]; } - -bool Triangle::GetConstrainedEdgeCCW( Point& p ) +bool Triangle::GetConstrainedEdgeCCW(Point& p) { - if( &p == points_[0] ) - { - return constrained_edge[2]; - } - else if( &p == points_[1] ) - { - return constrained_edge[0]; - } - - return constrained_edge[1]; -} - - -bool Triangle::GetConstrainedEdgeCW( Point& p ) -{ - if( &p == points_[0] ) - { - return constrained_edge[1]; - } - else if( &p == points_[1] ) - { - return constrained_edge[2]; - } - + if (&p == points_[0]) { + return constrained_edge[2]; + } else if (&p == points_[1]) { return constrained_edge[0]; + } + return constrained_edge[1]; } - -void Triangle::SetConstrainedEdgeCCW( Point& p, bool ce ) +bool Triangle::GetConstrainedEdgeCW(Point& p) { - if( &p == points_[0] ) - { - constrained_edge[2] = ce; - } - else if( &p == points_[1] ) - { - constrained_edge[0] = ce; - } - else - { - constrained_edge[1] = ce; - } + if (&p == points_[0]) { + return constrained_edge[1]; + } else if (&p == points_[1]) { + return constrained_edge[2]; + } + return constrained_edge[0]; } - -void Triangle::SetConstrainedEdgeCW( Point& p, bool ce ) +void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce) { - if( &p == points_[0] ) - { - constrained_edge[1] = ce; - } - else if( &p == points_[1] ) - { - constrained_edge[2] = ce; - } - else - { - constrained_edge[0] = ce; - } + if (&p == points_[0]) { + constrained_edge[2] = ce; + } else if (&p == points_[1]) { + constrained_edge[0] = ce; + } else { + constrained_edge[1] = ce; + } } - -bool Triangle::GetDelunayEdgeCCW( Point& p ) +void Triangle::SetConstrainedEdgeCW(Point& p, bool ce) { - if( &p == points_[0] ) - { - return delaunay_edge[2]; - } - else if( &p == points_[1] ) - { - return delaunay_edge[0]; - } - - return delaunay_edge[1]; + if (&p == points_[0]) { + constrained_edge[1] = ce; + } else if (&p == points_[1]) { + constrained_edge[2] = ce; + } else { + constrained_edge[0] = ce; + } } - -bool Triangle::GetDelunayEdgeCW( Point& p ) +bool Triangle::GetDelunayEdgeCCW(Point& p) { - if( &p == points_[0] ) - { - return delaunay_edge[1]; - } - else if( &p == points_[1] ) - { - return delaunay_edge[2]; - } - + if (&p == points_[0]) { + return delaunay_edge[2]; + } else if (&p == points_[1]) { return delaunay_edge[0]; + } + return delaunay_edge[1]; } - -void Triangle::SetDelunayEdgeCCW( Point& p, bool e ) +bool Triangle::GetDelunayEdgeCW(Point& p) { - if( &p == points_[0] ) - { - delaunay_edge[2] = e; - } - else if( &p == points_[1] ) - { - delaunay_edge[0] = e; - } - else - { - delaunay_edge[1] = e; - } + if (&p == points_[0]) { + return delaunay_edge[1]; + } else if (&p == points_[1]) { + return delaunay_edge[2]; + } + return delaunay_edge[0]; } - -void Triangle::SetDelunayEdgeCW( Point& p, bool e ) +void Triangle::SetDelunayEdgeCCW(Point& p, bool e) { - if( &p == points_[0] ) - { - delaunay_edge[1] = e; - } - else if( &p == points_[1] ) - { - delaunay_edge[2] = e; - } - else - { - delaunay_edge[0] = e; - } + if (&p == points_[0]) { + delaunay_edge[2] = e; + } else if (&p == points_[1]) { + delaunay_edge[0] = e; + } else { + delaunay_edge[1] = e; + } } +void Triangle::SetDelunayEdgeCW(Point& p, bool e) +{ + if (&p == points_[0]) { + delaunay_edge[1] = e; + } else if (&p == points_[1]) { + delaunay_edge[2] = e; + } else { + delaunay_edge[0] = e; + } +} // The neighbor across to given point -Triangle& Triangle::NeighborAcross( Point& opoint ) +Triangle& Triangle::NeighborAcross(Point& opoint) { - if( &opoint == points_[0] ) - { - return *neighbors_[0]; - } - else if( &opoint == points_[1] ) - { - return *neighbors_[1]; - } - - return *neighbors_[2]; + if (&opoint == points_[0]) { + return *neighbors_[0]; + } else if (&opoint == points_[1]) { + return *neighbors_[1]; + } + return *neighbors_[2]; } - void Triangle::DebugPrint() { - std::cout << points_[0]->x << "," << points_[0]->y << " "; - std::cout << points_[1]->x << "," << points_[1]->y << " "; - std::cout << points_[2]->x << "," << points_[2]->y << "\n"; + using namespace std; + cout << points_[0]->x << "," << points_[0]->y << " "; + cout << points_[1]->x << "," << points_[1]->y << " "; + cout << points_[2]->x << "," << points_[2]->y << endl; } + } diff --git a/polygon/poly2tri/common/shapes.h b/polygon/poly2tri/common/shapes.h index e8dd811c23..8c19d91ef8 100644 --- a/polygon/poly2tri/common/shapes.h +++ b/polygon/poly2tri/common/shapes.h @@ -39,313 +39,287 @@ #include namespace p2t { + struct Edge; -struct Point -{ - double x, y; +struct Point { - /// Default constructor does nothing (for performance). - Point() - { - x = 0.0; - y = 0.0; - } + double x, y; - /// The edges this point constitutes an upper ending point - std::vector edge_list; + /// Default constructor does nothing (for performance). + Point() + { + x = 0.0; + y = 0.0; + } - /// Construct using coordinates. - Point( double ax, double ay ) : x( ax ), y( ay ) {} + /// The edges this point constitutes an upper ending point + std::vector edge_list; - /// Set this point to all zeros. - void set_zero() - { - x = 0.0; - y = 0.0; - } + /// Construct using coordinates. + Point(double x, double y) : x(x), y(y) {} - /// Set this point to some specified coordinates. - void set( double x_, double y_ ) - { - x = x_; - y = y_; - } + /// Set this point to all zeros. + void set_zero() + { + x = 0.0; + y = 0.0; + } - /// Negate this point. - Point operator -() const - { - Point v; + /// Set this point to some specified coordinates. + void set(double x_, double y_) + { + x = x_; + y = y_; + } - v.set( -x, -y ); - return v; - } + /// Negate this point. + Point operator -() const + { + Point v; + v.set(-x, -y); + return v; + } - /// Add a point to this point. - void operator +=( const Point& v ) - { - x += v.x; - y += v.y; - } + /// Add a point to this point. + void operator +=(const Point& v) + { + x += v.x; + y += v.y; + } - /// Subtract a point from this point. - void operator -=( const Point& v ) - { - x -= v.x; - y -= v.y; - } + /// Subtract a point from this point. + void operator -=(const Point& v) + { + x -= v.x; + y -= v.y; + } - /// Multiply this point by a scalar. - void operator *=( double a ) - { - x *= a; - y *= a; - } + /// Multiply this point by a scalar. + void operator *=(double a) + { + x *= a; + y *= a; + } - /// Get the length of this point (the norm). - double Length() const - { - return sqrt( x * x + y * y ); - } + /// Get the length of this point (the norm). + double Length() const + { + return sqrt(x * x + y * y); + } - /// Convert this point into a unit point. Returns the Length. - double Normalize() - { - double len = Length(); + /// Convert this point into a unit point. Returns the Length. + double Normalize() + { + double len = Length(); + x /= len; + y /= len; + return len; + } - x /= len; - y /= len; - return len; - } }; // Represents a simple polygon's edge -struct Edge -{ - Point* p, * q; +struct Edge { - /// Constructor - Edge( Point& p1, Point& p2 ) : p( &p1 ), q( &p2 ) - { - if( p1.y > p2.y ) - { - q = &p1; - p = &p2; - } - else if( p1.y == p2.y ) - { - if( p1.x > p2.x ) - { - q = &p1; - p = &p2; - } - else if( p1.x == p2.x ) - { - // Repeat points - assert( false ); - } - } + Point* p, *q; - q->edge_list.push_back( this ); + /// Constructor + Edge(Point& p1, Point& p2) : p(&p1), q(&p2) + { + if (p1.y > p2.y) { + q = &p1; + p = &p2; + } else if (p1.y == p2.y) { + if (p1.x > p2.x) { + q = &p1; + p = &p2; + } else if (p1.x == p2.x) { + // Repeat points + assert(false); + } } + + q->edge_list.push_back(this); + } }; // Triangle-based data structures are know to have better performance than quad-edge structures // See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator" -// "Triangulations in CGAL" -class Triangle -{ +// "Triangulations in CGAL" +class Triangle { public: /// Constructor - Triangle( Point& a, Point& b, Point& c ); +Triangle(Point& a, Point& b, Point& c); /// Flags to determine if an edge is a Constrained edge - bool constrained_edge[3]; +bool constrained_edge[3]; /// Flags to determine if an edge is a Delauney edge - bool delaunay_edge[3]; +bool delaunay_edge[3]; - Point* GetPoint( const int& index ); - Point* PointCW( Point& point ); - Point* PointCCW( Point& point ); - Point* OppositePoint( Triangle& t, Point& p ); +Point* GetPoint(const int& index); +Point* PointCW(Point& point); +Point* PointCCW(Point& point); +Point* OppositePoint(Triangle& t, Point& p); - Triangle* GetNeighbor( const int& index ); - void MarkNeighbor( Point* p1, Point* p2, Triangle* t ); - void MarkNeighbor( Triangle& t ); +Triangle* GetNeighbor(const int& index); +void MarkNeighbor(Point* p1, Point* p2, Triangle* t); +void MarkNeighbor(Triangle& t); - void MarkConstrainedEdge( const int index ); - void MarkConstrainedEdge( Edge& edge ); - void MarkConstrainedEdge( Point* p, Point* q ); +void MarkConstrainedEdge(const int index); +void MarkConstrainedEdge(Edge& edge); +void MarkConstrainedEdge(Point* p, Point* q); - int Index( const Point* p ); - int EdgeIndex( const Point* p1, const Point* p2 ); +int Index(const Point* p); +int EdgeIndex(const Point* p1, const Point* p2); - Triangle* NeighborCW( Point& point ); - Triangle* NeighborCCW( Point& point ); - bool GetConstrainedEdgeCCW( Point& p ); - bool GetConstrainedEdgeCW( Point& p ); - void SetConstrainedEdgeCCW( Point& p, bool ce ); - void SetConstrainedEdgeCW( Point& p, bool ce ); - bool GetDelunayEdgeCCW( Point& p ); - bool GetDelunayEdgeCW( Point& p ); - void SetDelunayEdgeCCW( Point& p, bool e ); - void SetDelunayEdgeCW( Point& p, bool e ); - - bool Contains( Point* p ); - bool Contains( const Edge& e ); - bool Contains( Point* p, Point* q ); - void Legalize( Point& point ); - void Legalize( Point& opoint, Point& npoint ); +Triangle* NeighborCW(Point& point); +Triangle* NeighborCCW(Point& point); +bool GetConstrainedEdgeCCW(Point& p); +bool GetConstrainedEdgeCW(Point& p); +void SetConstrainedEdgeCCW(Point& p, bool ce); +void SetConstrainedEdgeCW(Point& p, bool ce); +bool GetDelunayEdgeCCW(Point& p); +bool GetDelunayEdgeCW(Point& p); +void SetDelunayEdgeCCW(Point& p, bool e); +void SetDelunayEdgeCW(Point& p, bool e); +bool Contains(Point* p); +bool Contains(const Edge& e); +bool Contains(Point* p, Point* q); +void Legalize(Point& point); +void Legalize(Point& opoint, Point& npoint); /** * Clears all references to all other triangles and points */ - void Clear(); - void ClearNeighbor( Triangle* triangle ); - void ClearNeighbors(); - void ClearDelunayEdges(); +void Clear(); +void ClearNeighbor(Triangle *triangle ); +void ClearNeighbors(); +void ClearDelunayEdges(); - inline bool IsInterior(); - inline void IsInterior( bool b ); +inline bool IsInterior(); +inline void IsInterior(bool b); - Triangle& NeighborAcross( Point& opoint ); +Triangle& NeighborAcross(Point& opoint); - void DebugPrint(); +void DebugPrint(); private: /// Triangle points - Point* points_[3]; +Point* points_[3]; /// Neighbor list - Triangle* neighbors_[3]; +Triangle* neighbors_[3]; /// Has this triangle been marked as an interior triangle? - bool interior_; +bool interior_; }; -inline bool cmp( const Point* a, const Point* b ) +inline bool cmp(const Point* a, const Point* b) { - if( a->y < b->y ) - { - return true; + if (a->y < b->y) { + return true; + } else if (a->y == b->y) { + // Make sure q is point with greater x value + if (a->x < b->x) { + return true; } - else if( a->y == b->y ) - { - // Make sure q is point with greater x value - if( a->x < b->x ) - { - return true; - } - } - - return false; + } + return false; } - /// Add two points_ component-wise. -inline Point operator +( const Point& a, const Point& b ) +inline Point operator +(const Point& a, const Point& b) { - return Point( a.x + b.x, a.y + b.y ); + return Point(a.x + b.x, a.y + b.y); } - /// Subtract two points_ component-wise. -inline Point operator -( const Point& a, const Point& b ) +inline Point operator -(const Point& a, const Point& b) { - return Point( a.x - b.x, a.y - b.y ); + return Point(a.x - b.x, a.y - b.y); } - /// Multiply point by scalar -inline Point operator *( double s, const Point& a ) +inline Point operator *(double s, const Point& a) { - return Point( s * a.x, s * a.y ); + return Point(s * a.x, s * a.y); } - -inline bool operator ==( const Point& a, const Point& b ) +inline bool operator ==(const Point& a, const Point& b) { - return a.x == b.x && a.y == b.y; + return a.x == b.x && a.y == b.y; } - -inline bool operator !=( const Point& a, const Point& b ) +inline bool operator !=(const Point& a, const Point& b) { - return !(a.x == b.x) && !(a.y == b.y); + return !(a.x == b.x) && !(a.y == b.y); } - /// Peform the dot product on two vectors. -inline double Dot( const Point& a, const Point& b ) +inline double Dot(const Point& a, const Point& b) { - return a.x * b.x + a.y * b.y; + return a.x * b.x + a.y * b.y; } - /// Perform the cross product on two vectors. In 2D this produces a scalar. -inline double Cross( const Point& a, const Point& b ) +inline double Cross(const Point& a, const Point& b) { - return a.x * b.y - a.y * b.x; + return a.x * b.y - a.y * b.x; } - /// Perform the cross product on a point and a scalar. In 2D this produces /// a point. -inline Point Cross( const Point& a, double s ) +inline Point Cross(const Point& a, double s) { - return Point( s * a.y, -s * a.x ); + return Point(s * a.y, -s * a.x); } - /// Perform the cross product on a scalar and a point. In 2D this produces /// a point. -inline Point Cross( const double s, const Point& a ) +inline Point Cross(const double s, const Point& a) { - return Point( -s * a.y, s * a.x ); + return Point(-s * a.y, s * a.x); } - -inline Point* Triangle::GetPoint( const int& index ) +inline Point* Triangle::GetPoint(const int& index) { - return points_[index]; + return points_[index]; } - -inline Triangle* Triangle::GetNeighbor( const int& index ) +inline Triangle* Triangle::GetNeighbor(const int& index) { - return neighbors_[index]; + return neighbors_[index]; } - -inline bool Triangle::Contains( Point* p ) +inline bool Triangle::Contains(Point* p) { - return p == points_[0] || p == points_[1] || p == points_[2]; + return p == points_[0] || p == points_[1] || p == points_[2]; } - -inline bool Triangle::Contains( const Edge& e ) +inline bool Triangle::Contains(const Edge& e) { - return Contains( e.p ) && Contains( e.q ); + return Contains(e.p) && Contains(e.q); } - -inline bool Triangle::Contains( Point* p, Point* q ) +inline bool Triangle::Contains(Point* p, Point* q) { - return Contains( p ) && Contains( q ); + return Contains(p) && Contains(q); } - inline bool Triangle::IsInterior() { - return interior_; + return interior_; } - -inline void Triangle::IsInterior( bool b ) +inline void Triangle::IsInterior(bool b) { - interior_ = b; + interior_ = b; } + } #endif + + diff --git a/polygon/poly2tri/common/utils.h b/polygon/poly2tri/common/utils.h index 3de9fb11bf..78416f2f31 100644 --- a/polygon/poly2tri/common/utils.h +++ b/polygon/poly2tri/common/utils.h @@ -1,4 +1,4 @@ -/* +/* * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * @@ -28,7 +28,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #ifndef UTILS_H #define UTILS_H @@ -39,13 +39,12 @@ #include namespace p2t { -const double PI_3div4 = 3 * M_PI / 4; -const double PI_div2 = 1.57079632679489661923; -const double EPSILON = 1e-12; -enum Orientation { - CW, CCW, COLLINEAR -}; +const double PI_3div4 = 3 * M_PI / 4; +const double PI_div2 = 1.57079632679489661923; +const double EPSILON = 1e-12; + +enum Orientation { CW, CCW, COLLINEAR }; /** * Forumla to calculate signed area
@@ -57,77 +56,68 @@ enum Orientation { * = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3) * */ -Orientation Orient2d( Point& pa, Point& pb, Point& pc ) +Orientation Orient2d(Point& pa, Point& pb, Point& pc) { - double detleft = (pa.x - pc.x) * (pb.y - pc.y); - double detright = (pa.y - pc.y) * (pb.x - pc.x); - double val = detleft - detright; - - if( val > -EPSILON && val < EPSILON ) - { - return COLLINEAR; - } - else if( val > 0 ) - { - return CCW; - } - - return CW; + double detleft = (pa.x - pc.x) * (pb.y - pc.y); + double detright = (pa.y - pc.y) * (pb.x - pc.x); + double val = detleft - detright; + if (val > -EPSILON && val < EPSILON) { + return COLLINEAR; + } else if (val > 0) { + return CCW; + } + return CW; } - /* - * bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) - * { - * double pdx = pd.x; - * double pdy = pd.y; - * double adx = pa.x - pdx; - * double ady = pa.y - pdy; - * double bdx = pb.x - pdx; - * double bdy = pb.y - pdy; - * - * double adxbdy = adx * bdy; - * double bdxady = bdx * ady; - * double oabd = adxbdy - bdxady; - * - * if (oabd <= EPSILON) { - * return false; - * } - * - * double cdx = pc.x - pdx; - * double cdy = pc.y - pdy; - * - * double cdxady = cdx * ady; - * double adxcdy = adx * cdy; - * double ocad = cdxady - adxcdy; - * - * if (ocad <= EPSILON) { - * return false; - * } - * - * return true; - * } - * - */ - -bool InScanArea( Point& pa, Point& pb, Point& pc, Point& pd ) +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) { - double oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y); + double pdx = pd.x; + double pdy = pd.y; + double adx = pa.x - pdx; + double ady = pa.y - pdy; + double bdx = pb.x - pdx; + double bdy = pb.y - pdy; - if( oadb >= -EPSILON ) - { - return false; - } + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; - double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y); + if (oabd <= EPSILON) { + return false; + } - if( oadc <= EPSILON ) - { - return false; - } + double cdx = pc.x - pdx; + double cdy = pc.y - pdy; - return true; + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= EPSILON) { + return false; + } + + return true; } + +*/ + +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); + if (oadb >= -EPSILON) { + return false; + } + + double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); + if (oadc <= EPSILON) { + return false; + } + return true; +} + } #endif + diff --git a/polygon/poly2tri/poly2tri.h b/polygon/poly2tri/poly2tri.h index 042cb3dcb3..487755e2e9 100644 --- a/polygon/poly2tri/poly2tri.h +++ b/polygon/poly2tri/poly2tri.h @@ -1,4 +1,4 @@ -/* +/* * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * diff --git a/polygon/poly2tri/sweep/advancing_front.cc b/polygon/poly2tri/sweep/advancing_front.cc index b87dfc5451..019df4a6eb 100644 --- a/polygon/poly2tri/sweep/advancing_front.cc +++ b/polygon/poly2tri/sweep/advancing_front.cc @@ -31,105 +31,79 @@ #include "advancing_front.h" namespace p2t { -AdvancingFront::AdvancingFront( Node& head, Node& tail ) + +AdvancingFront::AdvancingFront(Node& head, Node& tail) { - head_ = &head; - tail_ = &tail; - search_node_ = &head; + head_ = &head; + tail_ = &tail; + search_node_ = &head; } - -Node* AdvancingFront::LocateNode( const double& x ) +Node* AdvancingFront::LocateNode(const double& x) { - Node* node = search_node_; + Node* node = search_node_; - if( x < node->value ) - { - while( (node = node->prev) != NULL ) - { - if( x >= node->value ) - { - search_node_ = node; - return node; - } - } - } - else - { - while( (node = node->next) != NULL ) - { - if( x < node->value ) - { - search_node_ = node->prev; - return node->prev; - } - } - } - - return NULL; -} - - -Node* AdvancingFront::FindSearchNode( const double& x ) -{ - (void) x; // suppress compiler warnings "unused parameter 'x'" - // TODO: implement BST index - return search_node_; -} - - -Node* AdvancingFront::LocatePoint( const Point* point ) -{ - const double px = point->x; - Node* node = FindSearchNode( px ); - const double nx = node->point->x; - - if( px == nx ) - { - if( point != node->point ) - { - // We might have two nodes with same x value for a short time - if( point == node->prev->point ) - { - node = node->prev; - } - else if( point == node->next->point ) - { - node = node->next; - } - else - { - assert( 0 ); - } - } - } - else if( px < nx ) - { - while( (node = node->prev) != NULL ) - { - if( point == node->point ) - { - break; - } - } - } - else - { - while( (node = node->next) != NULL ) - { - if( point == node->point ) - break; - } - } - - if( node ) + if (x < node->value) { + while ((node = node->prev) != NULL) { + if (x >= node->value) { search_node_ = node; - - return node; + return node; + } + } + } else { + while ((node = node->next) != NULL) { + if (x < node->value) { + search_node_ = node->prev; + return node->prev; + } + } + } + return NULL; } +Node* AdvancingFront::FindSearchNode(const double& x) +{ + (void)x; // suppress compiler warnings "unused parameter 'x'" + // TODO: implement BST index + return search_node_; +} + +Node* AdvancingFront::LocatePoint(const Point* point) +{ + const double px = point->x; + Node* node = FindSearchNode(px); + const double nx = node->point->x; + + if (px == nx) { + if (point != node->point) { + // We might have two nodes with same x value for a short time + if (point == node->prev->point) { + node = node->prev; + } else if (point == node->next->point) { + node = node->next; + } else { + assert(0); + } + } + } else if (px < nx) { + while ((node = node->prev) != NULL) { + if (point == node->point) { + break; + } + } + } else { + while ((node = node->next) != NULL) { + if (point == node->point) + break; + } + } + if(node) search_node_ = node; + return node; +} AdvancingFront::~AdvancingFront() { } + } + diff --git a/polygon/poly2tri/sweep/advancing_front.h b/polygon/poly2tri/sweep/advancing_front.h index 179d22c82f..bab73d449c 100644 --- a/polygon/poly2tri/sweep/advancing_front.h +++ b/polygon/poly2tri/sweep/advancing_front.h @@ -35,91 +35,84 @@ #include "../common/shapes.h" namespace p2t { + struct Node; // Advancing front node -struct Node -{ - Point* point; - Triangle* triangle; +struct Node { + Point* point; + Triangle* triangle; - Node* next; - Node* prev; + Node* next; + Node* prev; - double value; + double value; - Node( Point& p ) : point( &p ), triangle( NULL ), next( NULL ), prev( NULL ), value( p.x ) - { - } + Node(Point& p) : point(&p), triangle(NULL), next(NULL), prev(NULL), value(p.x) + { + } + + Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x) + { + } - Node( Point& p, Triangle& t ) : point( &p ), triangle( &t ), next( NULL ), prev( NULL ), value( - p.x ) - { - } }; // Advancing front -class AdvancingFront -{ +class AdvancingFront { public: - AdvancingFront( Node& head, Node& tail ); +AdvancingFront(Node& head, Node& tail); // Destructor - ~AdvancingFront(); +~AdvancingFront(); - Node* head(); - void set_head( Node* node ); - Node* tail(); - void set_tail( Node* node ); - Node* search(); - void set_search( Node* node ); +Node* head(); +void set_head(Node* node); +Node* tail(); +void set_tail(Node* node); +Node* search(); +void set_search(Node* node); /// Locate insertion point along advancing front - Node* LocateNode( const double& x ); +Node* LocateNode(const double& x); - Node* LocatePoint( const Point* point ); +Node* LocatePoint(const Point* point); private: - Node* head_, * tail_, * search_node_; +Node* head_, *tail_, *search_node_; - Node* FindSearchNode( const double& x ); +Node* FindSearchNode(const double& x); }; inline Node* AdvancingFront::head() { - return head_; + return head_; } - - -inline void AdvancingFront::set_head( Node* node ) +inline void AdvancingFront::set_head(Node* node) { - head_ = node; + head_ = node; } - inline Node* AdvancingFront::tail() { - return tail_; + return tail_; } - - -inline void AdvancingFront::set_tail( Node* node ) +inline void AdvancingFront::set_tail(Node* node) { - tail_ = node; + tail_ = node; } - inline Node* AdvancingFront::search() { - return search_node_; + return search_node_; } - -inline void AdvancingFront::set_search( Node* node ) +inline void AdvancingFront::set_search(Node* node) { - search_node_ = node; + search_node_ = node; } + } #endif diff --git a/polygon/poly2tri/sweep/cdt.cc b/polygon/poly2tri/sweep/cdt.cc index 0cb910b6c2..d7838257c7 100644 --- a/polygon/poly2tri/sweep/cdt.cc +++ b/polygon/poly2tri/sweep/cdt.cc @@ -1,4 +1,4 @@ -/* +/* * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * @@ -31,46 +31,42 @@ #include "cdt.h" namespace p2t { -CDT::CDT( std::vector polyline ) + +CDT::CDT(std::vector polyline) { - sweep_context_ = new SweepContext( polyline ); - sweep_ = new Sweep; + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; } - -void CDT::AddHole( std::vector polyline ) +void CDT::AddHole(std::vector polyline) { - sweep_context_->AddHole( polyline ); + sweep_context_->AddHole(polyline); } - -void CDT::AddPoint( Point* point ) -{ - sweep_context_->AddPoint( point ); +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); } - void CDT::Triangulate() { - sweep_->Triangulate( *sweep_context_ ); + sweep_->Triangulate(*sweep_context_); } - std::vector CDT::GetTriangles() { - return sweep_context_->GetTriangles(); + return sweep_context_->GetTriangles(); } - std::list CDT::GetMap() { - return sweep_context_->GetMap(); + return sweep_context_->GetMap(); } - CDT::~CDT() { - delete sweep_context_; - delete sweep_; + delete sweep_context_; + delete sweep_; } + } + diff --git a/polygon/poly2tri/sweep/cdt.h b/polygon/poly2tri/sweep/cdt.h index e7b703de1b..3e6f024086 100644 --- a/polygon/poly2tri/sweep/cdt.h +++ b/polygon/poly2tri/sweep/cdt.h @@ -1,4 +1,4 @@ -/* +/* * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * @@ -28,7 +28,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #ifndef CDT_H #define CDT_H @@ -37,11 +37,11 @@ #include "sweep.h" /** - * + * * @author Mason Green * */ - + namespace p2t { class CDT @@ -50,40 +50,40 @@ public: /** * Constructor - add polyline with non repeating points - * + * * @param polyline */ CDT(std::vector polyline); - + /** * Destructor - clean up memory */ ~CDT(); - + /** * Add a hole - * + * * @param polyline */ void AddHole(std::vector polyline); - + /** * Add a steiner point - * + * * @param point */ void AddPoint(Point* point); - + /** * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points */ void Triangulate(); - + /** * Get CDT triangles */ std::vector GetTriangles(); - + /** * Get triangle map */ @@ -94,7 +94,7 @@ public: /** * Internals */ - + SweepContext* sweep_context_; Sweep* sweep_; diff --git a/polygon/poly2tri/sweep/sweep.cc b/polygon/poly2tri/sweep/sweep.cc index da3de0b051..29d7ac7358 100644 --- a/polygon/poly2tri/sweep/sweep.cc +++ b/polygon/poly2tri/sweep/sweep.cc @@ -35,973 +35,778 @@ #include "../common/utils.h" namespace p2t { + // Triangulate simple polygon with holes -void Sweep::Triangulate( SweepContext& tcx ) +void Sweep::Triangulate(SweepContext& tcx) { - tcx.InitTriangulation(); - tcx.CreateAdvancingFront( nodes_ ); - // Sweep points; build mesh - SweepPoints( tcx ); - // Clean up - FinalizationPolygon( tcx ); + tcx.InitTriangulation(); + tcx.CreateAdvancingFront(nodes_); + // Sweep points; build mesh + SweepPoints(tcx); + // Clean up + FinalizationPolygon(tcx); } - -void Sweep::SweepPoints( SweepContext& tcx ) +void Sweep::SweepPoints(SweepContext& tcx) { - for( int jj = 1; jj < tcx.point_count(); jj++ ) - { - Point& point = *tcx.GetPoint( jj ); - Node* node = &PointEvent( tcx, point ); - - for( unsigned int i = 0; i < point.edge_list.size(); i++ ) - { - EdgeEvent( tcx, point.edge_list[i], node ); - } + for (int i = 1; i < tcx.point_count(); i++) { + Point& point = *tcx.GetPoint(i); + Node* node = &PointEvent(tcx, point); + for (unsigned int i = 0; i < point.edge_list.size(); i++) { + EdgeEvent(tcx, point.edge_list[i], node); } + } } - -void Sweep::FinalizationPolygon( SweepContext& tcx ) +void Sweep::FinalizationPolygon(SweepContext& tcx) { - // Get an Internal triangle to start with - Triangle* t = tcx.front()->head()->next->triangle; - Point* p = tcx.front()->head()->next->point; + // Get an Internal triangle to start with + Triangle* t = tcx.front()->head()->next->triangle; + Point* p = tcx.front()->head()->next->point; + while (!t->GetConstrainedEdgeCW(*p)) { + t = t->NeighborCCW(*p); + } - while( !t->GetConstrainedEdgeCW( *p ) ) - { - t = t->NeighborCCW( *p ); - } - - // Collect interior triangles constrained by edges - tcx.MeshClean( *t ); + // Collect interior triangles constrained by edges + tcx.MeshClean(*t); } - -Node& Sweep::PointEvent( SweepContext& tcx, Point& point ) +Node& Sweep::PointEvent(SweepContext& tcx, Point& point) { - Node& node = tcx.LocateNode( point ); - Node& new_node = NewFrontTriangle( tcx, point, node ); + Node& node = tcx.LocateNode(point); + Node& new_node = NewFrontTriangle(tcx, point, node); - // Only need to check +epsilon since point never have smaller - // x value than node due to how we fetch nodes from the front - if( point.x <= node.point->x + EPSILON ) - { - Fill( tcx, node ); - } + // Only need to check +epsilon since point never have smaller + // x value than node due to how we fetch nodes from the front + if (point.x <= node.point->x + EPSILON) { + Fill(tcx, node); + } - // tcx.AddNode(new_node); + //tcx.AddNode(new_node); - FillAdvancingFront( tcx, new_node ); - return new_node; + FillAdvancingFront(tcx, new_node); + return new_node; } - -void Sweep::EdgeEvent( SweepContext& tcx, Edge* edge, Node* node ) +void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) { - tcx.edge_event.constrained_edge = edge; - tcx.edge_event.right = (edge->p->x > edge->q->x); + tcx.edge_event.constrained_edge = edge; + tcx.edge_event.right = (edge->p->x > edge->q->x); - if( IsEdgeSideOfTriangle( *node->triangle, *edge->p, *edge->q ) ) - { - return; - } + if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { + return; + } - // For now we will do all needed filling - // TODO: integrate with flip process might give some better performance - // but for now this avoid the issue with cases that needs both flips and fills - FillEdgeEvent( tcx, edge, node ); - EdgeEvent( tcx, *edge->p, *edge->q, node->triangle, *edge->q ); + // For now we will do all needed filling + // TODO: integrate with flip process might give some better performance + // but for now this avoid the issue with cases that needs both flips and fills + FillEdgeEvent(tcx, edge, node); + EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); } - -void Sweep::EdgeEvent( SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point ) +void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) { - if( IsEdgeSideOfTriangle( *triangle, ep, eq ) ) - { - return; + if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { + return; + } + + Point* p1 = triangle->PointCCW(point); + Orientation o1 = Orient2d(eq, *p1, ep); + if (o1 == COLLINEAR) { + if( triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p1; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); } + return; + } - Point* p1 = triangle->PointCCW( point ); - Orientation o1 = Orient2d( eq, *p1, ep ); - - if( o1 == COLLINEAR ) - { - if( triangle->Contains( &eq, p1 ) ) - { - triangle->MarkConstrainedEdge( &eq, p1 ); - // We are modifying the constraint maybe it would be better to - // not change the given constraint and just keep a variable for the new constraint - tcx.edge_event.constrained_edge->q = p1; - triangle = &triangle->NeighborAcross( point ); - EdgeEvent( tcx, ep, *p1, triangle, *p1 ); - } - else - { - std::runtime_error( "EdgeEvent - collinear points not supported" ); - assert( 0 ); - } - - return; + Point* p2 = triangle->PointCW(point); + Orientation o2 = Orient2d(eq, *p2, ep); + if (o2 == COLLINEAR) { + if( triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p2; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); } + return; + } - Point* p2 = triangle->PointCW( point ); - Orientation o2 = Orient2d( eq, *p2, ep ); - - if( o2 == COLLINEAR ) - { - if( triangle->Contains( &eq, p2 ) ) - { - triangle->MarkConstrainedEdge( &eq, p2 ); - // We are modifying the constraint maybe it would be better to - // not change the given constraint and just keep a variable for the new constraint - tcx.edge_event.constrained_edge->q = p2; - triangle = &triangle->NeighborAcross( point ); - EdgeEvent( tcx, ep, *p2, triangle, *p2 ); - } - else - { - std::runtime_error( "EdgeEvent - collinear points not supported" ); - assert( 0 ); - } - - return; - } - - if( o1 == o2 ) - { - // Need to decide if we are rotating CW or CCW to get to a triangle - // that will cross edge - if( o1 == CW ) - { - triangle = triangle->NeighborCCW( point ); - } - else - { - triangle = triangle->NeighborCW( point ); - } - - EdgeEvent( tcx, ep, eq, triangle, point ); - } - else - { - // This triangle crosses constraint so lets flippin start! - FlipEdgeEvent( tcx, ep, eq, triangle, point ); + if (o1 == o2) { + // Need to decide if we are rotating CW or CCW to get to a triangle + // that will cross edge + if (o1 == CW) { + triangle = triangle->NeighborCCW(point); + } else{ + triangle = triangle->NeighborCW(point); } + EdgeEvent(tcx, ep, eq, triangle, point); + } else { + // This triangle crosses constraint so lets flippin start! + FlipEdgeEvent(tcx, ep, eq, triangle, point); + } } - -bool Sweep::IsEdgeSideOfTriangle( Triangle& triangle, Point& ep, Point& eq ) +bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) { - int index = triangle.EdgeIndex( &ep, &eq ); + int index = triangle.EdgeIndex(&ep, &eq); - if( index != -1 ) - { - triangle.MarkConstrainedEdge( index ); - Triangle* t = triangle.GetNeighbor( index ); - - if( t ) - { - t->MarkConstrainedEdge( &ep, &eq ); - } - - return true; + if (index != -1) { + triangle.MarkConstrainedEdge(index); + Triangle* t = triangle.GetNeighbor(index); + if (t) { + t->MarkConstrainedEdge(&ep, &eq); } - - return false; + return true; + } + return false; } - -Node& Sweep::NewFrontTriangle( SweepContext& tcx, Point& point, Node& node ) +Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) { - Triangle* triangle = new Triangle( point, *node.point, *node.next->point ); + Triangle* triangle = new Triangle(point, *node.point, *node.next->point); - triangle->MarkNeighbor( *node.triangle ); - tcx.AddToMap( triangle ); + triangle->MarkNeighbor(*node.triangle); + tcx.AddToMap(triangle); - Node* new_node = new Node( point ); - nodes_.push_back( new_node ); + Node* new_node = new Node(point); + nodes_.push_back(new_node); - new_node->next = node.next; - new_node->prev = &node; - node.next->prev = new_node; - node.next = new_node; + new_node->next = node.next; + new_node->prev = &node; + node.next->prev = new_node; + node.next = new_node; - if( !Legalize( tcx, *triangle ) ) - { - tcx.MapTriangleToNodes( *triangle ); - } + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } - return *new_node; + return *new_node; } - -void Sweep::Fill( SweepContext& tcx, Node& node ) +void Sweep::Fill(SweepContext& tcx, Node& node) { - Triangle* triangle = new Triangle( *node.prev->point, *node.point, *node.next->point ); + Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); - // TODO: should copy the constrained_edge value from neighbor triangles - // for now constrained_edge values are copied during the legalize - triangle->MarkNeighbor( *node.prev->triangle ); - triangle->MarkNeighbor( *node.triangle ); + // TODO: should copy the constrained_edge value from neighbor triangles + // for now constrained_edge values are copied during the legalize + triangle->MarkNeighbor(*node.prev->triangle); + triangle->MarkNeighbor(*node.triangle); - tcx.AddToMap( triangle ); + tcx.AddToMap(triangle); - // Update the advancing front - node.prev->next = node.next; - node.next->prev = node.prev; + // Update the advancing front + node.prev->next = node.next; + node.next->prev = node.prev; + + // If it was legalized the triangle has already been mapped + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } - // If it was legalized the triangle has already been mapped - if( !Legalize( tcx, *triangle ) ) - { - tcx.MapTriangleToNodes( *triangle ); - } } - -void Sweep::FillAdvancingFront( SweepContext& tcx, Node& n ) +void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) { - // Fill right holes - Node* node = n.next; - while( node->next ) - { - // if HoleAngle exceeds 90 degrees then break. - if( LargeHole_DontFill( node ) ) - break; + // Fill right holes + Node* node = n.next; - Fill( tcx, *node ); - node = node->next; - } + while (node->next) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->next; + } - // Fill left holes - node = n.prev; + // Fill left holes + node = n.prev; - while( node->prev ) - { - // if HoleAngle exceeds 90 degrees then break. - if( LargeHole_DontFill( node ) ) - break; + while (node->prev) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->prev; + } - Fill( tcx, *node ); - node = node->prev; - } - - // Fill right basins - if( n.next && n.next->next ) - { - double angle = BasinAngle( n ); - - if( angle < PI_3div4 ) - { - FillBasin( tcx, n ); - } + // Fill right basins + if (n.next && n.next->next) { + double angle = BasinAngle(n); + if (angle < PI_3div4) { + FillBasin(tcx, n); } + } } - // True if HoleAngle exceeds 90 degrees. -bool Sweep::LargeHole_DontFill( Node* node ) -{ - Node* nextNode = node->next; - Node* prevNode = node->prev; +bool Sweep::LargeHole_DontFill(Node* node) { - if( !AngleExceeds90Degrees( node->point, nextNode->point, prevNode->point ) ) - return false; + Node* nextNode = node->next; + Node* prevNode = node->prev; + if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) + return false; - // Check additional points on front. - Node* next2Node = nextNode->next; + // Check additional points on front. + Node* next2Node = nextNode->next; + // "..Plus.." because only want angles on same side as point being added. + if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) + return false; - // "..Plus.." because only want angles on same side as point being added. - if( (next2Node != NULL) - && !AngleExceedsPlus90DegreesOrIsNegative( node->point, next2Node->point, - prevNode->point ) ) - return false; + Node* prev2Node = prevNode->prev; + // "..Plus.." because only want angles on same side as point being added. + if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) + return false; - Node* prev2Node = prevNode->prev; - - // "..Plus.." because only want angles on same side as point being added. - if( (prev2Node != NULL) - && !AngleExceedsPlus90DegreesOrIsNegative( node->point, nextNode->point, - prev2Node->point ) ) - return false; - - return true; + return true; } - -bool Sweep::AngleExceeds90Degrees( Point* origin, Point* pa, Point* pb ) -{ - double angle = Angle( *origin, *pa, *pb ); - bool exceeds90Degrees = ( (angle > PI_div2) || (angle < -PI_div2) ); - - return exceeds90Degrees; +bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); + return exceeds90Degrees; } - -bool Sweep::AngleExceedsPlus90DegreesOrIsNegative( Point* origin, Point* pa, Point* pb ) -{ - double angle = Angle( *origin, *pa, *pb ); - bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); - - return exceedsPlus90DegreesOrIsNegative; +bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); + return exceedsPlus90DegreesOrIsNegative; } - -double Sweep::Angle( Point& origin, Point& pa, Point& pb ) -{ - /* Complex plane - * ab = cosA +i*sinA - * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) - * atan2(y,x) computes the principal value of the argument function - * applied to the complex number x+iy - * Where x = ax*bx + ay*by - * y = ax*by - ay*bx - */ - double px = origin.x; - double py = origin.y; - double ax = pa.x - px; - double ay = pa.y - py; - double bx = pb.x - px; - double by = pb.y - py; - double x = ax * by - ay * bx; - double y = ax * bx + ay * by; - double angle = atan2( x, y ); - - return angle; +double Sweep::Angle(Point& origin, Point& pa, Point& pb) { + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double px = origin.x; + double py = origin.y; + double ax = pa.x- px; + double ay = pa.y - py; + double bx = pb.x - px; + double by = pb.y - py; + double x = ax * by - ay * bx; + double y = ax * bx + ay * by; + double angle = atan2(x, y); + return angle; } - -double Sweep::BasinAngle( Node& node ) +double Sweep::BasinAngle(Node& node) { - double ax = node.point->x - node.next->next->point->x; - double ay = node.point->y - node.next->next->point->y; - - return atan2( ay, ax ); + double ax = node.point->x - node.next->next->point->x; + double ay = node.point->y - node.next->next->point->y; + return atan2(ay, ax); } - -double Sweep::HoleAngle( Node& node ) +double Sweep::HoleAngle(Node& node) { - /* Complex plane - * ab = cosA +i*sinA - * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) - * atan2(y,x) computes the principal value of the argument function - * applied to the complex number x+iy - * Where x = ax*bx + ay*by - * y = ax*by - ay*bx - */ - double ax = node.next->point->x - node.point->x; - double ay = node.next->point->y - node.point->y; - double bx = node.prev->point->x - node.point->x; - double by = node.prev->point->y - node.point->y; - - return atan2( ax * by - ay * bx, ax * bx + ay * by ); + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double ax = node.next->point->x - node.point->x; + double ay = node.next->point->y - node.point->y; + double bx = node.prev->point->x - node.point->x; + double by = node.prev->point->y - node.point->y; + return atan2(ax * by - ay * bx, ax * bx + ay * by); } - -bool Sweep::Legalize( SweepContext& tcx, Triangle& t ) +bool Sweep::Legalize(SweepContext& tcx, Triangle& t) { - // To legalize a triangle we start by finding if any of the three edges - // violate the Delaunay condition - for( int i = 0; i < 3; i++ ) - { - if( t.delaunay_edge[i] ) - continue; + // To legalize a triangle we start by finding if any of the three edges + // violate the Delaunay condition + for (int i = 0; i < 3; i++) { + if (t.delaunay_edge[i]) + continue; - Triangle* ot = t.GetNeighbor( i ); + Triangle* ot = t.GetNeighbor(i); - if( ot ) - { - Point* p = t.GetPoint( i ); - Point* op = ot->OppositePoint( t, *p ); - int oi = ot->Index( op ); + if (ot) { + Point* p = t.GetPoint(i); + Point* op = ot->OppositePoint(t, *p); + int oi = ot->Index(op); - // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) - // then we should not try to legalize - if( ot->constrained_edge[oi] || ot->delaunay_edge[oi] ) - { - t.constrained_edge[i] = ot->constrained_edge[oi]; - continue; - } + // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) + // then we should not try to legalize + if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { + t.constrained_edge[i] = ot->constrained_edge[oi]; + continue; + } - bool inside = Incircle( *p, *t.PointCCW( *p ), *t.PointCW( *p ), *op ); + bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); - if( inside ) - { - // Lets mark this shared edge as Delaunay - t.delaunay_edge[i] = true; - ot->delaunay_edge[oi] = true; + if (inside) { + // Lets mark this shared edge as Delaunay + t.delaunay_edge[i] = true; + ot->delaunay_edge[oi] = true; - // Lets rotate shared edge one vertex CW to legalize it - RotateTrianglePair( t, *p, *ot, *op ); + // Lets rotate shared edge one vertex CW to legalize it + RotateTrianglePair(t, *p, *ot, *op); - // We now got one valid Delaunay Edge shared by two triangles - // This gives us 4 new edges to check for Delaunay + // We now got one valid Delaunay Edge shared by two triangles + // This gives us 4 new edges to check for Delaunay - // Make sure that triangle to node mapping is done only one time for a specific triangle - bool not_legalized = !Legalize( tcx, t ); - - if( not_legalized ) - { - tcx.MapTriangleToNodes( t ); - } - - not_legalized = !Legalize( tcx, *ot ); - - if( not_legalized ) - tcx.MapTriangleToNodes( *ot ); - - // Reset the Delaunay edges, since they only are valid Delaunay edges - // until we add a new triangle or point. - // XXX: need to think about this. Can these edges be tried after we - // return to previous recursive level? - t.delaunay_edge[i] = false; - ot->delaunay_edge[oi] = false; - - // If triangle have been legalized no need to check the other edges since - // the recursive legalization will handles those so we can end here. - return true; - } - } - } - - return false; -} - - -bool Sweep::Incircle( Point& pa, Point& pb, Point& pc, Point& pd ) -{ - double adx = pa.x - pd.x; - double ady = pa.y - pd.y; - double bdx = pb.x - pd.x; - double bdy = pb.y - pd.y; - - double adxbdy = adx * bdy; - double bdxady = bdx * ady; - double oabd = adxbdy - bdxady; - - if( oabd <= 0 ) - return false; - - double cdx = pc.x - pd.x; - double cdy = pc.y - pd.y; - - double cdxady = cdx * ady; - double adxcdy = adx * cdy; - double ocad = cdxady - adxcdy; - - if( ocad <= 0 ) - return false; - - double bdxcdy = bdx * cdy; - double cdxbdy = cdx * bdy; - - double alift = adx * adx + ady * ady; - double blift = bdx * bdx + bdy * bdy; - double clift = cdx * cdx + cdy * cdy; - - double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; - - return det > 0; -} - - -void Sweep::RotateTrianglePair( Triangle& t, Point& p, Triangle& ot, Point& op ) -{ - Triangle* n1, * n2, * n3, * n4; - - n1 = t.NeighborCCW( p ); - n2 = t.NeighborCW( p ); - n3 = ot.NeighborCCW( op ); - n4 = ot.NeighborCW( op ); - - bool ce1, ce2, ce3, ce4; - ce1 = t.GetConstrainedEdgeCCW( p ); - ce2 = t.GetConstrainedEdgeCW( p ); - ce3 = ot.GetConstrainedEdgeCCW( op ); - ce4 = ot.GetConstrainedEdgeCW( op ); - - bool de1, de2, de3, de4; - de1 = t.GetDelunayEdgeCCW( p ); - de2 = t.GetDelunayEdgeCW( p ); - de3 = ot.GetDelunayEdgeCCW( op ); - de4 = ot.GetDelunayEdgeCW( op ); - - t.Legalize( p, op ); - ot.Legalize( op, p ); - - // Remap delaunay_edge - ot.SetDelunayEdgeCCW( p, de1 ); - t.SetDelunayEdgeCW( p, de2 ); - t.SetDelunayEdgeCCW( op, de3 ); - ot.SetDelunayEdgeCW( op, de4 ); - - // Remap constrained_edge - ot.SetConstrainedEdgeCCW( p, ce1 ); - t.SetConstrainedEdgeCW( p, ce2 ); - t.SetConstrainedEdgeCCW( op, ce3 ); - ot.SetConstrainedEdgeCW( op, ce4 ); - - // Remap neighbors - // XXX: might optimize the markNeighbor by keeping track of - // what side should be assigned to what neighbor after the - // rotation. Now mark neighbor does lots of testing to find - // the right side. - t.ClearNeighbors(); - ot.ClearNeighbors(); - - if( n1 ) - ot.MarkNeighbor( *n1 ); - - if( n2 ) - t.MarkNeighbor( *n2 ); - - if( n3 ) - t.MarkNeighbor( *n3 ); - - if( n4 ) - ot.MarkNeighbor( *n4 ); - - t.MarkNeighbor( ot ); -} - - -void Sweep::FillBasin( SweepContext& tcx, Node& node ) -{ - if( Orient2d( *node.point, *node.next->point, *node.next->next->point ) == CCW ) - { - tcx.basin.left_node = node.next->next; - } - else - { - tcx.basin.left_node = node.next; - } - - // Find the bottom and right node - tcx.basin.bottom_node = tcx.basin.left_node; - - while( tcx.basin.bottom_node->next - && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y ) - { - tcx.basin.bottom_node = tcx.basin.bottom_node->next; - } - - if( tcx.basin.bottom_node == tcx.basin.left_node ) - { - // No valid basin - return; - } - - tcx.basin.right_node = tcx.basin.bottom_node; - - while( tcx.basin.right_node->next - && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y ) - { - tcx.basin.right_node = tcx.basin.right_node->next; - } - - if( tcx.basin.right_node == tcx.basin.bottom_node ) - { - // No valid basins - return; - } - - tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; - tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; - - FillBasinReq( tcx, tcx.basin.bottom_node ); -} - - -void Sweep::FillBasinReq( SweepContext& tcx, Node* node ) -{ - // if shallow stop filling - if( IsShallow( tcx, *node ) ) - { - return; - } - - Fill( tcx, *node ); - - if( node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node ) - { - return; - } - else if( node->prev == tcx.basin.left_node ) - { - Orientation o = Orient2d( *node->point, *node->next->point, *node->next->next->point ); - - if( o == CW ) - { - return; + // Make sure that triangle to node mapping is done only one time for a specific triangle + bool not_legalized = !Legalize(tcx, t); + if (not_legalized) { + tcx.MapTriangleToNodes(t); } - node = node->next; - } - else if( node->next == tcx.basin.right_node ) - { - Orientation o = Orient2d( *node->point, *node->prev->point, *node->prev->prev->point ); + not_legalized = !Legalize(tcx, *ot); + if (not_legalized) + tcx.MapTriangleToNodes(*ot); - if( o == CCW ) - { - return; - } + // Reset the Delaunay edges, since they only are valid Delaunay edges + // until we add a new triangle or point. + // XXX: need to think about this. Can these edges be tried after we + // return to previous recursive level? + t.delaunay_edge[i] = false; + ot->delaunay_edge[oi] = false; - node = node->prev; - } - else - { - // Continue with the neighbor node with lowest Y value - if( node->prev->point->y < node->next->point->y ) - { - node = node->prev; - } - else - { - node = node->next; - } - } - - FillBasinReq( tcx, node ); -} - - -bool Sweep::IsShallow( SweepContext& tcx, Node& node ) -{ - double height; - - if( tcx.basin.left_highest ) - { - height = tcx.basin.left_node->point->y - node.point->y; - } - else - { - height = tcx.basin.right_node->point->y - node.point->y; - } - - // if shallow stop filling - if( tcx.basin.width > height ) - { + // If triangle have been legalized no need to check the other edges since + // the recursive legalization will handles those so we can end here. return true; + } } + } + return false; +} +bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double adx = pa.x - pd.x; + double ady = pa.y - pd.y; + double bdx = pb.x - pd.x; + double bdy = pb.y - pd.y; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= 0) return false; + + double cdx = pc.x - pd.x; + double cdy = pc.y - pd.y; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= 0) + return false; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double alift = adx * adx + ady * ady; + double blift = bdx * bdx + bdy * bdy; + double clift = cdx * cdx + cdy * cdy; + + double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; + + return det > 0; } - -void Sweep::FillEdgeEvent( SweepContext& tcx, Edge* edge, Node* node ) +void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) { - if( tcx.edge_event.right ) - { - FillRightAboveEdgeEvent( tcx, edge, node ); - } - else - { - FillLeftAboveEdgeEvent( tcx, edge, node ); - } + Triangle* n1, *n2, *n3, *n4; + n1 = t.NeighborCCW(p); + n2 = t.NeighborCW(p); + n3 = ot.NeighborCCW(op); + n4 = ot.NeighborCW(op); + + bool ce1, ce2, ce3, ce4; + ce1 = t.GetConstrainedEdgeCCW(p); + ce2 = t.GetConstrainedEdgeCW(p); + ce3 = ot.GetConstrainedEdgeCCW(op); + ce4 = ot.GetConstrainedEdgeCW(op); + + bool de1, de2, de3, de4; + de1 = t.GetDelunayEdgeCCW(p); + de2 = t.GetDelunayEdgeCW(p); + de3 = ot.GetDelunayEdgeCCW(op); + de4 = ot.GetDelunayEdgeCW(op); + + t.Legalize(p, op); + ot.Legalize(op, p); + + // Remap delaunay_edge + ot.SetDelunayEdgeCCW(p, de1); + t.SetDelunayEdgeCW(p, de2); + t.SetDelunayEdgeCCW(op, de3); + ot.SetDelunayEdgeCW(op, de4); + + // Remap constrained_edge + ot.SetConstrainedEdgeCCW(p, ce1); + t.SetConstrainedEdgeCW(p, ce2); + t.SetConstrainedEdgeCCW(op, ce3); + ot.SetConstrainedEdgeCW(op, ce4); + + // Remap neighbors + // XXX: might optimize the markNeighbor by keeping track of + // what side should be assigned to what neighbor after the + // rotation. Now mark neighbor does lots of testing to find + // the right side. + t.ClearNeighbors(); + ot.ClearNeighbors(); + if (n1) ot.MarkNeighbor(*n1); + if (n2) t.MarkNeighbor(*n2); + if (n3) t.MarkNeighbor(*n3); + if (n4) ot.MarkNeighbor(*n4); + t.MarkNeighbor(ot); } - -void Sweep::FillRightAboveEdgeEvent( SweepContext& tcx, Edge* edge, Node* node ) +void Sweep::FillBasin(SweepContext& tcx, Node& node) { - while( node->next->point->x < edge->p->x ) - { - // Check if next node is below the edge - if( Orient2d( *edge->q, *node->next->point, *edge->p ) == CCW ) - { - FillRightBelowEdgeEvent( tcx, edge, *node ); - } - else - { - node = node->next; - } - } + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + tcx.basin.left_node = node.next->next; + } else { + tcx.basin.left_node = node.next; + } + + // Find the bottom and right node + tcx.basin.bottom_node = tcx.basin.left_node; + while (tcx.basin.bottom_node->next + && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { + tcx.basin.bottom_node = tcx.basin.bottom_node->next; + } + if (tcx.basin.bottom_node == tcx.basin.left_node) { + // No valid basin + return; + } + + tcx.basin.right_node = tcx.basin.bottom_node; + while (tcx.basin.right_node->next + && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { + tcx.basin.right_node = tcx.basin.right_node->next; + } + if (tcx.basin.right_node == tcx.basin.bottom_node) { + // No valid basins + return; + } + + tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; + tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; + + FillBasinReq(tcx, tcx.basin.bottom_node); } - -void Sweep::FillRightBelowEdgeEvent( SweepContext& tcx, Edge* edge, Node& node ) +void Sweep::FillBasinReq(SweepContext& tcx, Node* node) { - if( node.point->x < edge->p->x ) - { - if( Orient2d( *node.point, *node.next->point, *node.next->next->point ) == CCW ) - { - // Concave - FillRightConcaveEdgeEvent( tcx, edge, node ); - } - else - { - // Convex - FillRightConvexEdgeEvent( tcx, edge, node ); - // Retry this one - FillRightBelowEdgeEvent( tcx, edge, node ); - } + // if shallow stop filling + if (IsShallow(tcx, *node)) { + return; + } + + Fill(tcx, *node); + + if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { + return; + } else if (node->prev == tcx.basin.left_node) { + Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); + if (o == CW) { + return; } + node = node->next; + } else if (node->next == tcx.basin.right_node) { + Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); + if (o == CCW) { + return; + } + node = node->prev; + } else { + // Continue with the neighbor node with lowest Y value + if (node->prev->point->y < node->next->point->y) { + node = node->prev; + } else { + node = node->next; + } + } + + FillBasinReq(tcx, node); } - -void Sweep::FillRightConcaveEdgeEvent( SweepContext& tcx, Edge* edge, Node& node ) +bool Sweep::IsShallow(SweepContext& tcx, Node& node) { - Fill( tcx, *node.next ); + double height; - if( node.next->point != edge->p ) - { - // Next above or below edge? - if( Orient2d( *edge->q, *node.next->point, *edge->p ) == CCW ) - { - // Below - if( Orient2d( *node.point, *node.next->point, *node.next->next->point ) == CCW ) - { - // Next is concave - FillRightConcaveEdgeEvent( tcx, edge, node ); - } - else - { - // Next is convex - } - } - } + if (tcx.basin.left_highest) { + height = tcx.basin.left_node->point->y - node.point->y; + } else { + height = tcx.basin.right_node->point->y - node.point->y; + } + + // if shallow stop filling + if (tcx.basin.width > height) { + return true; + } + return false; } - -void Sweep::FillRightConvexEdgeEvent( SweepContext& tcx, Edge* edge, Node& node ) +void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) { - // Next concave or convex? - if( Orient2d( *node.next->point, *node.next->next->point, - *node.next->next->next->point ) == CCW ) - { - // Concave - FillRightConcaveEdgeEvent( tcx, edge, *node.next ); - } - else - { - // Convex - // Next above or below edge? - if( Orient2d( *edge->q, *node.next->next->point, *edge->p ) == CCW ) - { - // Below - FillRightConvexEdgeEvent( tcx, edge, *node.next ); - } - else - { - // Above - } - } + if (tcx.edge_event.right) { + FillRightAboveEdgeEvent(tcx, edge, node); + } else { + FillLeftAboveEdgeEvent(tcx, edge, node); + } } - -void Sweep::FillLeftAboveEdgeEvent( SweepContext& tcx, Edge* edge, Node* node ) +void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) { - while( node->prev->point->x > edge->p->x ) - { - // Check if next node is below the edge - if( Orient2d( *edge->q, *node->prev->point, *edge->p ) == CW ) - { - FillLeftBelowEdgeEvent( tcx, edge, *node ); - } - else - { - node = node->prev; - } + while (node->next->point->x < edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { + FillRightBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->next; } + } } - -void Sweep::FillLeftBelowEdgeEvent( SweepContext& tcx, Edge* edge, Node& node ) +void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) { - if( node.point->x > edge->p->x ) - { - if( Orient2d( *node.point, *node.prev->point, *node.prev->prev->point ) == CW ) - { - // Concave - FillLeftConcaveEdgeEvent( tcx, edge, node ); - } - else - { - // Convex - FillLeftConvexEdgeEvent( tcx, edge, node ); - // Retry this one - FillLeftBelowEdgeEvent( tcx, edge, node ); - } + if (node.point->x < edge->p->x) { + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else{ + // Convex + FillRightConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillRightBelowEdgeEvent(tcx, edge, node); } + } } - -void Sweep::FillLeftConvexEdgeEvent( SweepContext& tcx, Edge* edge, Node& node ) +void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) { - // Next concave or convex? - if( Orient2d( *node.prev->point, *node.prev->prev->point, - *node.prev->prev->prev->point ) == CW ) - { - // Concave - FillLeftConcaveEdgeEvent( tcx, edge, *node.prev ); - } - else - { - // Convex - // Next above or below edge? - if( Orient2d( *edge->q, *node.prev->prev->point, *edge->p ) == CW ) - { - // Below - FillLeftConvexEdgeEvent( tcx, edge, *node.prev ); - } - else - { - // Above - } + Fill(tcx, *node.next); + if (node.next->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { + // Below + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Next is concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else { + // Next is convex + } } + } + } - -void Sweep::FillLeftConcaveEdgeEvent( SweepContext& tcx, Edge* edge, Node& node ) +void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) { - Fill( tcx, *node.prev ); - - if( node.prev->point != edge->p ) - { - // Next above or below edge? - if( Orient2d( *edge->q, *node.prev->point, *edge->p ) == CW ) - { - // Below - if( Orient2d( *node.point, *node.prev->point, *node.prev->prev->point ) == CW ) - { - // Next is concave - FillLeftConcaveEdgeEvent( tcx, edge, node ); - } - else - { - // Next is convex - } - } + // Next concave or convex? + if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, *node.next); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { + // Below + FillRightConvexEdgeEvent(tcx, edge, *node.next); + } else{ + // Above } + } } - -void Sweep::FlipEdgeEvent( SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p ) +void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) { - Triangle& ot = t->NeighborAcross( p ); - Point& op = *ot.OppositePoint( *t, p ); - - if( InScanArea( p, *t->PointCCW( p ), *t->PointCW( p ), op ) ) - { - // Lets rotate shared edge one vertex CW - RotateTrianglePair( *t, p, ot, op ); - tcx.MapTriangleToNodes( *t ); - tcx.MapTriangleToNodes( ot ); - - if( p == eq && op == ep ) - { - if( eq == *tcx.edge_event.constrained_edge->q - && ep == *tcx.edge_event.constrained_edge->p ) - { - t->MarkConstrainedEdge( &ep, &eq ); - ot.MarkConstrainedEdge( &ep, &eq ); - Legalize( tcx, *t ); - Legalize( tcx, ot ); - } - else - { - // XXX: I think one of the triangles should be legalized here? - } - } - else - { - Orientation o = Orient2d( eq, op, ep ); - t = &NextFlipTriangle( tcx, (int) o, *t, ot, p, op ); - FlipEdgeEvent( tcx, ep, eq, t, p ); - } - } - else - { - Point& newP = NextFlipPoint( ep, eq, ot, op ); - FlipScanEdgeEvent( tcx, ep, eq, *t, ot, newP ); - EdgeEvent( tcx, ep, eq, t, p ); + while (node->prev->point->x > edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { + FillLeftBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->prev; } + } } - -Triangle& Sweep::NextFlipTriangle( SweepContext& tcx, - int o, - Triangle& t, - Triangle& ot, - Point& p, - Point& op ) +void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) { - if( o == CCW ) - { - // ot is not crossing edge after flip - int edge_index = ot.EdgeIndex( &p, &op ); - ot.delaunay_edge[edge_index] = true; - Legalize( tcx, ot ); - ot.ClearDelunayEdges(); - return t; + if (node.point->x > edge->p->x) { + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else { + // Convex + FillLeftConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillLeftBelowEdgeEvent(tcx, edge, node); } - - // t is not crossing edge after flip - int edge_index = t.EdgeIndex( &p, &op ); - - t.delaunay_edge[edge_index] = true; - Legalize( tcx, t ); - t.ClearDelunayEdges(); - return ot; + } } - -Point& Sweep::NextFlipPoint( Point& ep, Point& eq, Triangle& ot, Point& op ) +void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) { - Orientation o2d = Orient2d( eq, op, ep ); - - if( o2d == CW ) - { - // Right - return *ot.PointCCW( op ); + // Next concave or convex? + if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { + // Below + FillLeftConvexEdgeEvent(tcx, edge, *node.prev); + } else{ + // Above } - else if( o2d == CCW ) - { - // Left - return *ot.PointCW( op ); - } - - // throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); - assert( 0 ); - - // Never executed, due tu assert( 0 ). Just to avoid compil warning - return ep; + } } - -void Sweep::FlipScanEdgeEvent( SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, - Triangle& t, Point& p ) +void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) { - Triangle& ot = t.NeighborAcross( p ); - Point& op = *ot.OppositePoint( t, p ); + Fill(tcx, *node.prev); + if (node.prev->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { + // Below + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Next is concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else{ + // Next is convex + } + } + } - if( InScanArea( eq, *flip_triangle.PointCCW( eq ), *flip_triangle.PointCW( eq ), op ) ) - { - // flip with new edge op->eq - FlipEdgeEvent( tcx, eq, op, &ot, op ); - // TODO: Actually I just figured out that it should be possible to - // improve this by getting the next ot and op before the the above - // flip and continue the flipScanEdgeEvent here - // set new ot and op here and loop back to inScanArea test - // also need to set a new flip_triangle first - // Turns out at first glance that this is somewhat complicated - // so it will have to wait. - } - else - { - Point& newP = NextFlipPoint( ep, eq, ot, op ); - FlipScanEdgeEvent( tcx, ep, eq, flip_triangle, ot, newP ); - } } - -Sweep::~Sweep() +void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) { + Triangle& ot = t->NeighborAcross(p); + Point& op = *ot.OppositePoint(*t, p); + + if (&ot == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { + // Lets rotate shared edge one vertex CW + RotateTrianglePair(*t, p, ot, op); + tcx.MapTriangleToNodes(*t); + tcx.MapTriangleToNodes(ot); + + if (p == eq && op == ep) { + if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { + t->MarkConstrainedEdge(&ep, &eq); + ot.MarkConstrainedEdge(&ep, &eq); + Legalize(tcx, *t); + Legalize(tcx, ot); + } else { + // XXX: I think one of the triangles should be legalized here? + } + } else { + Orientation o = Orient2d(eq, op, ep); + t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); + FlipEdgeEvent(tcx, ep, eq, t, p); + } + } else { + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); + EdgeEvent(tcx, ep, eq, t, p); + } +} + +Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) +{ + if (o == CCW) { + // ot is not crossing edge after flip + int edge_index = ot.EdgeIndex(&p, &op); + ot.delaunay_edge[edge_index] = true; + Legalize(tcx, ot); + ot.ClearDelunayEdges(); + return t; + } + + // t is not crossing edge after flip + int edge_index = t.EdgeIndex(&p, &op); + + t.delaunay_edge[edge_index] = true; + Legalize(tcx, t); + t.ClearDelunayEdges(); + return ot; +} + +Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) +{ + Orientation o2d = Orient2d(eq, op, ep); + if (o2d == CW) { + // Right + return *ot.PointCCW(op); + } else if (o2d == CCW) { + // Left + return *ot.PointCW(op); + } else{ + //throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); + assert(0); + } +} + +void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, + Triangle& t, Point& p) +{ + Triangle& ot = t.NeighborAcross(p); + Point& op = *ot.OppositePoint(t, p); + + if (&t.NeighborAcross(p) == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + // flip with new edge op->eq + FlipEdgeEvent(tcx, eq, op, &ot, op); + // TODO: Actually I just figured out that it should be possible to + // improve this by getting the next ot and op before the the above + // flip and continue the flipScanEdgeEvent here + // set new ot and op here and loop back to inScanArea test + // also need to set a new flip_triangle first + // Turns out at first glance that this is somewhat complicated + // so it will have to wait. + } else{ + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); + } +} + +Sweep::~Sweep() { + // Clean up memory - for( unsigned i = 0; i < nodes_.size(); i++ ) - { + for(int i = 0; i < nodes_.size(); i++) { delete nodes_[i]; } + } + } diff --git a/polygon/poly2tri/sweep/sweep.h b/polygon/poly2tri/sweep/sweep.h index ef56005973..f62c4cc3f2 100644 --- a/polygon/poly2tri/sweep/sweep.h +++ b/polygon/poly2tri/sweep/sweep.h @@ -49,17 +49,17 @@ struct Point; struct Edge; class Triangle; -class Sweep +class Sweep { public: /** * Triangulate - * + * * @param tcx */ void Triangulate(SweepContext& tcx); - + /** * Destructor - clean up memory */ @@ -69,7 +69,7 @@ private: /** * Start sweeping the Y-sorted point set from bottom to top - * + * * @param tcx */ void SweepPoints(SweepContext& tcx); @@ -86,8 +86,8 @@ private: Node& PointEvent(SweepContext& tcx, Point& point); /** - * - * + * + * * @param tcx * @param edge * @param node @@ -98,7 +98,7 @@ private: /** * Creates a new front triangle and legalize it - * + * * @param tcx * @param point * @param node @@ -136,10 +136,10 @@ private: * a,b and c
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
* This preknowledge gives us a way to optimize the incircle test - * @param pa - triangle point, opposite d - * @param pb - triangle point - * @param pc - triangle point - * @param pd - point opposite a + * @param a - triangle point, opposite d + * @param b - triangle point + * @param c - triangle point + * @param d - point opposite a * @return true if d is inside circle, false if on circle edge */ bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd); @@ -168,8 +168,8 @@ private: * @param n */ void FillAdvancingFront(SweepContext& tcx, Node& n); - - // Decision-making about when to Fill hole. + + // Decision-making about when to Fill hole. // Contributed by ToolmakerSteve2 bool LargeHole_DontFill(Node* node); bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb); @@ -204,6 +204,7 @@ private: * * @param tcx * @param node - bottom_node + * @param cnt - counter used to alternate on even and odd numbers */ void FillBasinReq(SweepContext& tcx, Node* node); @@ -234,22 +235,22 @@ private: /** * After a flip we have two triangles and know that only one will still be * intersecting the edge. So decide which to contiune with and legalize the other - * + * * @param tcx * @param o - should be the result of an orient2d( eq, op, ep ) * @param t - triangle 1 * @param ot - triangle 2 - * @param p - a point shared by both triangles + * @param p - a point shared by both triangles * @param op - another point shared by both triangles * @return returns the triangle still intersecting the edge */ Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op); /** - * When we need to traverse from one triangle to the next we need + * When we need to traverse from one triangle to the next we need * the point in current triangle that is the opposite point to the next - * triangle. - * + * triangle. + * * @param ep * @param eq * @param ot @@ -260,14 +261,14 @@ private: /** * Scan part of the FlipScan algorithm
- * When a triangle pair isn't flippable we will scan for the next - * point that is inside the flip triangle scan area. When found + * When a triangle pair isn't flippable we will scan for the next + * point that is inside the flip triangle scan area. When found * we generate a new flipEdgeEvent - * + * * @param tcx * @param ep - last point on the edge we are traversing * @param eq - first point on the edge we are traversing - * @param flip_triangle - the current triangle sharing the point eq with edge + * @param flipTriangle - the current triangle sharing the point eq with edge * @param t * @param p */ diff --git a/polygon/poly2tri/sweep/sweep_context.cc b/polygon/poly2tri/sweep/sweep_context.cc index 012421560d..6c0b0447dd 100644 --- a/polygon/poly2tri/sweep/sweep_context.cc +++ b/polygon/poly2tri/sweep/sweep_context.cc @@ -33,193 +33,164 @@ #include "advancing_front.h" namespace p2t { -SweepContext::SweepContext( std::vector polyline ) : - front_( 0 ), - head_( 0 ), - tail_( 0 ), - af_head_( 0 ), - af_middle_( 0 ), - af_tail_( 0 ) + +SweepContext::SweepContext(std::vector polyline) : + front_(0), + head_(0), + tail_(0), + af_head_(0), + af_middle_(0), + af_tail_(0) { - basin = Basin(); - edge_event = EdgeEvent(); + basin = Basin(); + edge_event = EdgeEvent(); - points_ = polyline; + points_ = polyline; - InitEdges( points_ ); + InitEdges(points_); } - -void SweepContext::AddHole( std::vector polyline ) +void SweepContext::AddHole(std::vector polyline) { - InitEdges( polyline ); - - for( unsigned int i = 0; i < polyline.size(); i++ ) - { - points_.push_back( polyline[i] ); - } + InitEdges(polyline); + for(unsigned int i = 0; i < polyline.size(); i++) { + points_.push_back(polyline[i]); + } } - -void SweepContext::AddPoint( Point* point ) -{ - points_.push_back( point ); +void SweepContext::AddPoint(Point* point) { + points_.push_back(point); } - std::vector SweepContext::GetTriangles() { - return triangles_; + return triangles_; } - std::list SweepContext::GetMap() { - return map_; + return map_; } - void SweepContext::InitTriangulation() { - double xmax( points_[0]->x ), xmin( points_[0]->x ); - double ymax( points_[0]->y ), ymin( points_[0]->y ); + double xmax(points_[0]->x), xmin(points_[0]->x); + double ymax(points_[0]->y), ymin(points_[0]->y); - // Calculate bounds. - for( unsigned int i = 0; i < points_.size(); i++ ) - { - Point& p = *points_[i]; + // Calculate bounds. + for (unsigned int i = 0; i < points_.size(); i++) { + Point& p = *points_[i]; + if (p.x > xmax) + xmax = p.x; + if (p.x < xmin) + xmin = p.x; + if (p.y > ymax) + ymax = p.y; + if (p.y < ymin) + ymin = p.y; + } - if( p.x > xmax ) - xmax = p.x; + double dx = kAlpha * (xmax - xmin); + double dy = kAlpha * (ymax - ymin); + head_ = new Point(xmax + dx, ymin - dy); + tail_ = new Point(xmin - dx, ymin - dy); - if( p.x < xmin ) - xmin = p.x; + // Sort points along y-axis + std::sort(points_.begin(), points_.end(), cmp); - if( p.y > ymax ) - ymax = p.y; +} - if( p.y < ymin ) - ymin = p.y; +void SweepContext::InitEdges(std::vector polyline) +{ + int num_points = polyline.size(); + for (int i = 0; i < num_points; i++) { + int j = i < num_points - 1 ? i + 1 : 0; + edge_list.push_back(new Edge(*polyline[i], *polyline[j])); + } +} + +Point* SweepContext::GetPoint(const int& index) +{ + return points_[index]; +} + +void SweepContext::AddToMap(Triangle* triangle) +{ + map_.push_back(triangle); +} + +Node& SweepContext::LocateNode(Point& point) +{ + // TODO implement search tree + return *front_->LocateNode(point.x); +} + +void SweepContext::CreateAdvancingFront(std::vector nodes) +{ + + (void) nodes; + // Initial triangle + Triangle* triangle = new Triangle(*points_[0], *tail_, *head_); + + map_.push_back(triangle); + + af_head_ = new Node(*triangle->GetPoint(1), *triangle); + af_middle_ = new Node(*triangle->GetPoint(0), *triangle); + af_tail_ = new Node(*triangle->GetPoint(2)); + front_ = new AdvancingFront(*af_head_, *af_tail_); + + // TODO: More intuitive if head is middles next and not previous? + // so swap head and tail + af_head_->next = af_middle_; + af_middle_->next = af_tail_; + af_middle_->prev = af_head_; + af_tail_->prev = af_middle_; +} + +void SweepContext::RemoveNode(Node* node) +{ + delete node; +} + +void SweepContext::MapTriangleToNodes(Triangle& t) +{ + for (int i = 0; i < 3; i++) { + if (!t.GetNeighbor(i)) { + Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i))); + if (n) + n->triangle = &t; } - - double dx = kAlpha * (xmax - xmin); - double dy = kAlpha * (ymax - ymin); - head_ = new Point( xmax + dx, ymin - dy ); - tail_ = new Point( xmin - dx, ymin - dy ); - - // Sort points along y-axis - std::sort( points_.begin(), points_.end(), cmp ); + } } - -void SweepContext::InitEdges( std::vector polyline ) +void SweepContext::RemoveFromMap(Triangle* triangle) { - int num_points = polyline.size(); + map_.remove(triangle); +} - for( int i = 0; i < num_points; i++ ) - { - int j = i < num_points - 1 ? i + 1 : 0; - edge_list.push_back( new Edge( *polyline[i], *polyline[j] ) ); +void SweepContext::MeshClean(Triangle& triangle) +{ + std::vector triangles; + triangles.push_back(&triangle); + + while(!triangles.empty()){ + Triangle *t = triangles.back(); + triangles.pop_back(); + + if (t != NULL && !t->IsInterior()) { + t->IsInterior(true); + triangles_.push_back(t); + for (int i = 0; i < 3; i++) { + if (!t->constrained_edge[i]) + triangles.push_back(t->GetNeighbor(i)); + } } + } } - -Point* SweepContext::GetPoint( const int& index ) -{ - return points_[index]; -} - - -void SweepContext::AddToMap( Triangle* triangle ) -{ - map_.push_back( triangle ); -} - - -Node& SweepContext::LocateNode( Point& point ) -{ - // TODO implement search tree - return *front_->LocateNode( point.x ); -} - - -void SweepContext::CreateAdvancingFront( std::vector nodes ) -{ - (void) nodes; - // Initial triangle - Triangle* triangle = new Triangle( *points_[0], *tail_, *head_ ); - - map_.push_back( triangle ); - - af_head_ = new Node( *triangle->GetPoint( 1 ), *triangle ); - af_middle_ = new Node( *triangle->GetPoint( 0 ), *triangle ); - af_tail_ = new Node( *triangle->GetPoint( 2 ) ); - front_ = new AdvancingFront( *af_head_, *af_tail_ ); - - // TODO: More intuitive if head is middles next and not previous? - // so swap head and tail - af_head_->next = af_middle_; - af_middle_->next = af_tail_; - af_middle_->prev = af_head_; - af_tail_->prev = af_middle_; -} - - -void SweepContext::RemoveNode( Node* node ) -{ - delete node; -} - - -void SweepContext::MapTriangleToNodes( Triangle& t ) -{ - for( int i = 0; i < 3; i++ ) - { - if( !t.GetNeighbor( i ) ) - { - Node* n = front_->LocatePoint( t.PointCW( *t.GetPoint( i ) ) ); - - if( n ) - n->triangle = &t; - } - } -} - - -void SweepContext::RemoveFromMap( Triangle* triangle ) -{ - map_.remove( triangle ); -} - - -void SweepContext::MeshClean( Triangle& triangle ) -{ - std::vector triangles; - - triangles.push_back( &triangle ); - - while( !triangles.empty() ) - { - Triangle* t = triangles.back(); - triangles.pop_back(); - - if( t != NULL && !t->IsInterior() ) - { - t->IsInterior( true ); - triangles_.push_back( t ); - - for( int i = 0; i < 3; i++ ) - { - if( !t->constrained_edge[i] ) - triangles.push_back( t->GetNeighbor( i ) ); - } - } - } -} - - SweepContext::~SweepContext() { + // Clean up memory delete head_; @@ -231,15 +202,15 @@ SweepContext::~SweepContext() typedef std::list type_list; - for( type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter ) - { + for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) { Triangle* ptr = *iter; delete ptr; } - for( unsigned int i = 0; i < edge_list.size(); i++ ) - { + for(unsigned int i = 0; i < edge_list.size(); i++) { delete edge_list[i]; } + } + } diff --git a/polygon/poly2tri/sweep/sweep_context.h b/polygon/poly2tri/sweep/sweep_context.h index 241d18a729..1010c0e8a8 100644 --- a/polygon/poly2tri/sweep/sweep_context.h +++ b/polygon/poly2tri/sweep/sweep_context.h @@ -37,6 +37,7 @@ #include namespace p2t { + // Inital triangle factor, seed triangle will extend 30% of // PointSet width to both left and right. const double kAlpha = 0.3; @@ -47,147 +48,139 @@ struct Node; struct Edge; class AdvancingFront; -class SweepContext -{ +class SweepContext { public: /// Constructor - SweepContext( std::vector polyline ); +SweepContext(std::vector polyline); /// Destructor - ~SweepContext(); +~SweepContext(); - void set_head( Point* p1 ); +void set_head(Point* p1); - Point* head(); +Point* head(); - void set_tail( Point* p1 ); +void set_tail(Point* p1); - Point* tail(); +Point* tail(); - int point_count(); +int point_count(); - Node& LocateNode( Point& point ); +Node& LocateNode(Point& point); - void RemoveNode( Node* node ); +void RemoveNode(Node* node); - void CreateAdvancingFront( std::vector nodes ); +void CreateAdvancingFront(std::vector nodes); /// Try to map a node to all sides of this triangle that don't have a neighbor - void MapTriangleToNodes( Triangle& t ); +void MapTriangleToNodes(Triangle& t); - void AddToMap( Triangle* triangle ); +void AddToMap(Triangle* triangle); - Point* GetPoint( const int& index ); +Point* GetPoint(const int& index); - Point* GetPoints(); +Point* GetPoints(); - void RemoveFromMap( Triangle* triangle ); +void RemoveFromMap(Triangle* triangle); - void AddHole( std::vector polyline ); +void AddHole(std::vector polyline); - void AddPoint( Point* point ); +void AddPoint(Point* point); - AdvancingFront* front(); +AdvancingFront* front(); - void MeshClean( Triangle& triangle ); +void MeshClean(Triangle& triangle); - std::vector GetTriangles(); +std::vector GetTriangles(); +std::list GetMap(); - std::list GetMap(); +std::vector edge_list; - std::vector edge_list; +struct Basin { + Node* left_node; + Node* bottom_node; + Node* right_node; + double width; + bool left_highest; - struct Basin - { - Node* left_node; - Node* bottom_node; - Node* right_node; - double width; - bool left_highest; + Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + { + } - Basin() : left_node( NULL ), bottom_node( NULL ), right_node( NULL ), width( 0.0 ), - left_highest( false ) - { - } + void Clear() + { + left_node = NULL; + bottom_node = NULL; + right_node = NULL; + width = 0.0; + left_highest = false; + } +}; - void Clear() - { - left_node = NULL; - bottom_node = NULL; - right_node = NULL; - width = 0.0; - left_highest = false; - } - }; +struct EdgeEvent { + Edge* constrained_edge; + bool right; - struct EdgeEvent - { - Edge* constrained_edge; - bool right; + EdgeEvent() : constrained_edge(NULL), right(false) + { + } +}; - EdgeEvent() : constrained_edge( NULL ), right( false ) - { - } - }; - - Basin basin; - EdgeEvent edge_event; +Basin basin; +EdgeEvent edge_event; private: - friend class Sweep; +friend class Sweep; - std::vector triangles_; - std::list map_; - std::vector points_; +std::vector triangles_; +std::list map_; +std::vector points_; // Advancing front - AdvancingFront* front_; +AdvancingFront* front_; // head point used with advancing front - Point* head_; +Point* head_; // tail point used with advancing front - Point* tail_; +Point* tail_; - Node* af_head_, * af_middle_, * af_tail_; +Node *af_head_, *af_middle_, *af_tail_; + +void InitTriangulation(); +void InitEdges(std::vector polyline); - void InitTriangulation(); - void InitEdges( std::vector polyline ); }; inline AdvancingFront* SweepContext::front() { - return front_; + return front_; } - inline int SweepContext::point_count() { - return points_.size(); + return points_.size(); } - -inline void SweepContext::set_head( Point* p1 ) +inline void SweepContext::set_head(Point* p1) { - head_ = p1; + head_ = p1; } - inline Point* SweepContext::head() { - return head_; + return head_; } - -inline void SweepContext::set_tail( Point* p1 ) +inline void SweepContext::set_tail(Point* p1) { - tail_ = p1; + tail_ = p1; } - inline Point* SweepContext::tail() { - return tail_; + return tail_; } + } #endif diff --git a/qa/CMakeLists.txt b/qa/CMakeLists.txt index 71310a99b6..01b2033201 100644 --- a/qa/CMakeLists.txt +++ b/qa/CMakeLists.txt @@ -11,4 +11,5 @@ if( KICAD_SCRIPTING_MODULES ) endif() add_subdirectory( geometry ) -add_subdirectory( pcb_test_window ) \ No newline at end of file +add_subdirectory( pcb_test_window ) +add_subdirectory( polygon_triangulation ) \ No newline at end of file diff --git a/qa/polygon_triangulation/CMakeLists.txt b/qa/polygon_triangulation/CMakeLists.txt new file mode 100644 index 0000000000..c611e73d96 --- /dev/null +++ b/qa/polygon_triangulation/CMakeLists.txt @@ -0,0 +1,92 @@ +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2017 CERN +# @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 + +#find_package(Boost COMPONENTS unit_test_framework REQUIRED) +#find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED ) + +add_definitions(-DPCBNEW -DBOOST_TEST_DYN_LINK) + +if( BUILD_GITHUB_PLUGIN ) + set( GITHUB_PLUGIN_LIBRARIES github_plugin ) +endif() + +add_dependencies( pnsrouter pcbcommon pcad2kicadpcb ${GITHUB_PLUGIN_LIBRARIES} ) + +add_executable(test_polygon_triangulation + ../common/mocks.cpp + ../../common/base_units.cpp + test_polygon_triangulation.cpp +) + +include_directories( BEFORE ${INC_BEFORE} ) +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/3d-viewer + ${CMAKE_SOURCE_DIR}/common + ${CMAKE_SOURCE_DIR}/pcbnew + ${CMAKE_SOURCE_DIR}/pcbnew/router + ${CMAKE_SOURCE_DIR}/pcbnew/tools + ${CMAKE_SOURCE_DIR}/pcbnew/dialogs + ${CMAKE_SOURCE_DIR}/polygon + ${CMAKE_SOURCE_DIR}/common/geometry + ${CMAKE_SOURCE_DIR}/qa/common + ${Boost_INCLUDE_DIR} + ${INC_AFTER} +) + +target_link_libraries( test_polygon_triangulation + polygon + pnsrouter + common + pcbcommon + bitmaps + polygon + pnsrouter + common + pcbcommon + bitmaps + polygon + pnsrouter + common + pcbcommon + bitmaps + polygon + pnsrouter + common + pcbcommon + bitmaps + gal + pcad2kicadpcb + common + pcbcommon + ${GITHUB_PLUGIN_LIBRARIES} + common + pcbcommon + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} + ${wxWidgets_LIBRARIES} +) + + diff --git a/qa/polygon_triangulation/test_polygon_triangulation.cpp b/qa/polygon_triangulation/test_polygon_triangulation.cpp new file mode 100644 index 0000000000..2f85aa4ce8 --- /dev/null +++ b/qa/polygon_triangulation/test_polygon_triangulation.cpp @@ -0,0 +1,262 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 CERN + * @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 + + + +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->CSegment(m_index), aOther.m_poly->CSegment(aOther.m_index) ); + } + + bool operator!=( const EDGE& aOther ) const + { + return ! compareSegs( m_poly->CSegment(m_index), aOther.m_poly->CSegment(aOther.m_index) ); + } + + struct HASH + { + std::size_t operator()( const EDGE& aEdge ) const + { + const auto& a = aEdge.m_poly->CSegment(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(); + + EDGE_LIST_ENTRY edgeList[ 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 ]; + //printf("n %p\n", edgeList[i].next); + } + + 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); + + // printf("e1 %d e2 %d\n", 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] ); + //else + //printf("Skip %d\n", i); + } + + EDGE_LIST_ENTRY* edgeBuf[ 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 { + // printf("e %p cnt %d IDX %d\n", e, cnt, e->index); + 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); +// printf("append %d %d\n", p.x, p.y); + outl.Append( p ); + queue.erase( edgeBuf[i] ); + } + +// auto p_last = lc.CPoint( edgeBuf[cnt-1]->index + 1 ); +//printf("appendl %d %d\n", p_last.x, p_last.y); +// 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] ); +} + +BOARD* loadBoard( const std::string& filename ) +{ + PLUGIN::RELEASER pi( new PCB_IO ); + BOARD* brd = nullptr; + + try + { + brd = pi->Load( wxString( filename.c_str() ), NULL, NULL ); + } + catch( const IO_ERROR& ioe ) + { + wxString msg = wxString::Format( _( "Error loading board.\n%s" ), + ioe.Problem() ); + + printf( "%s\n", (const char*) msg.mb_str() ); + return nullptr; + } + + return brd; +} + +int main(int argc, char *argv[]) +{ + auto brd = loadBoard( argc > 1 ? argv[1] : "../../../../tests/dp.kicad_pcb"); + + if(!brd) + return -1; + + + PROF_COUNTER cnt("allBoard"); + + + #pragma omp parallel for schedule(dynamic) + for( int z = 0; zGetAreaCount(); z++) + { + auto zone = brd->GetArea(z); + SHAPE_POLY_SET poly = zone->GetFilledPolysList(); + + poly.CacheTriangulation(); + + (void) poly; + printf("zone %d/%d\n", (z+1), brd->GetAreaCount() ); +#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 + } + + cnt.Show(); + + delete brd; + + return 0; + +}