diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 287ebd6b1d..aa4a0214a0 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -1,8 +1,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 CERN + * Copyright (C) 2015-2017 CERN * @author Tomasz Wlostowski + * @author Alejandro García Montoro * * Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson, * subject to Clipper library license. @@ -32,6 +33,8 @@ #include #include +#include + #include #include #include @@ -41,7 +44,12 @@ using namespace ClipperLib; SHAPE_POLY_SET::SHAPE_POLY_SET() : SHAPE( SH_POLY_SET ) { +} + +SHAPE_POLY_SET::SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther ) : + SHAPE( SH_POLY_SET ), m_polys( aOther.m_polys ) +{ } @@ -50,6 +58,95 @@ SHAPE_POLY_SET::~SHAPE_POLY_SET() } +SHAPE* SHAPE_POLY_SET::Clone() const +{ + return new SHAPE_POLY_SET( *this ); +} + + +bool SHAPE_POLY_SET::GetRelativeIndices( int aGlobalIdx, + SHAPE_POLY_SET::VERTEX_INDEX* aRelativeIndices ) const +{ + int polygonIdx = 0; + unsigned int contourIdx = 0; + int vertexIdx = 0; + + int currentGlobalIdx = 0; + + for( polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ ) + { + const POLYGON currentPolygon = CPolygon( polygonIdx ); + + for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ ) + { + SHAPE_LINE_CHAIN currentContour = currentPolygon[contourIdx]; + int totalPoints = currentContour.PointCount(); + + for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ ) + { + // Check if the current vertex is the globally indexed as aGlobalIdx + if( currentGlobalIdx == aGlobalIdx ) + { + aRelativeIndices->m_polygon = polygonIdx; + aRelativeIndices->m_contour = contourIdx; + aRelativeIndices->m_vertex = vertexIdx; + + return true; + } + + // Advance + currentGlobalIdx++; + } + } + } + + return false; +} + + +bool SHAPE_POLY_SET::GetGlobalIndex( SHAPE_POLY_SET::VERTEX_INDEX aRelativeIndices, + int& aGlobalIdx ) +{ + int selectedVertex = aRelativeIndices.m_vertex; + 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() ) + { + POLYGON currentPolygon; + + aGlobalIdx = 0; + + for( unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ ) + { + currentPolygon = Polygon( polygonIdx ); + + for( unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ ) + { + aGlobalIdx += currentPolygon[contourIdx].PointCount(); + } + } + + currentPolygon = Polygon( selectedPolygon ); + + for( unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx ++ ) + { + aGlobalIdx += currentPolygon[contourIdx].PointCount(); + } + + aGlobalIdx += selectedVertex; + + return true; + } + else + { + return false; + } +} + + int SHAPE_POLY_SET::NewOutline() { SHAPE_LINE_CHAIN empty_path; @@ -66,13 +163,18 @@ int SHAPE_POLY_SET::NewHole( int aOutline ) SHAPE_LINE_CHAIN empty_path; empty_path.SetClosed( true ); - m_polys.back().push_back( empty_path ); + // Default outline is the last one + if( aOutline < 0 ) + aOutline += m_polys.size(); + + // Add hole to the selected outline + m_polys[aOutline].push_back( empty_path ); return m_polys.back().size() - 2; } -int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole ) +int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole, bool aAllowDuplication ) { if( aOutline < 0 ) aOutline += m_polys.size(); @@ -87,12 +189,34 @@ int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole ) assert( aOutline < (int)m_polys.size() ); assert( idx < (int)m_polys[aOutline].size() ); - m_polys[aOutline][idx].Append( x, y ); + m_polys[aOutline][idx].Append( x, y, aAllowDuplication ); return m_polys[aOutline][idx].PointCount(); } +void SHAPE_POLY_SET::InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex ) +{ + VERTEX_INDEX index; + + if( aGlobalIndex < 0 ) + aGlobalIndex = 0; + + if( aGlobalIndex >= TotalVertices() ){ + Append( aNewVertex ); + } + else + { + // Assure the position to be inserted exists; throw an exception otherwise + if( GetRelativeIndices( aGlobalIndex, &index ) ) + m_polys[index.m_polygon][index.m_contour].Insert( index.m_vertex, aNewVertex ); + else + throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) ); + } + +} + + int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const { if( aOutline < 0 ) @@ -112,26 +236,22 @@ int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const } -const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) const +SHAPE_POLY_SET SHAPE_POLY_SET::Subset( int aFirstPolygon, int aLastPolygon ) { - if( aOutline < 0 ) - aOutline += m_polys.size(); + assert( aFirstPolygon >= 0 && aLastPolygon <= OutlineCount() ); - int idx; + SHAPE_POLY_SET newPolySet; - if( aHole < 0 ) - idx = 0; - else - idx = aHole + 1; + for( int index = aFirstPolygon; index < aLastPolygon; index++ ) + { + newPolySet.m_polys.push_back( Polygon( index ) ); + } - assert( aOutline < (int)m_polys.size() ); - assert( idx < (int)m_polys[aOutline].size() ); - - return m_polys[aOutline][idx].CPoint( index ); + return newPolySet; } -VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole ) +VECTOR2I& SHAPE_POLY_SET::Vertex( int aIndex, int aOutline, int aHole ) { if( aOutline < 0 ) aOutline += m_polys.size(); @@ -146,7 +266,116 @@ VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole ) assert( aOutline < (int)m_polys.size() ); assert( idx < (int)m_polys[aOutline].size() ); - return m_polys[aOutline][idx].Point( index ); + return m_polys[aOutline][idx].Point( aIndex ); +} + + +const VECTOR2I& SHAPE_POLY_SET::CVertex( int aIndex, int aOutline, int aHole ) const +{ + if( aOutline < 0 ) + aOutline += m_polys.size(); + + int idx; + + if( aHole < 0 ) + idx = 0; + else + idx = aHole + 1; + + assert( aOutline < (int)m_polys.size() ); + assert( idx < (int)m_polys[aOutline].size() ); + + return m_polys[aOutline][idx].CPoint( aIndex ); +} + + +VECTOR2I& SHAPE_POLY_SET::Vertex( int aGlobalIndex ) +{ + SHAPE_POLY_SET::VERTEX_INDEX index; + + // Assure the passed index references a legal position; abort otherwise + if( !GetRelativeIndices( aGlobalIndex, &index ) ) + throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) ); + + return m_polys[index.m_polygon][index.m_contour].Point( index.m_vertex ); +} + + +const VECTOR2I& SHAPE_POLY_SET::CVertex( int aGlobalIndex ) const +{ + SHAPE_POLY_SET::VERTEX_INDEX index; + + // Assure the passed index references a legal position; abort otherwise + if( !GetRelativeIndices( aGlobalIndex, &index ) ) + throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) ); + + return m_polys[index.m_polygon][index.m_contour].CPoint( index.m_vertex ); +} + + +VECTOR2I& SHAPE_POLY_SET::Vertex( SHAPE_POLY_SET::VERTEX_INDEX index ) +{ + return Vertex( index.m_vertex, index.m_polygon, index.m_contour - 1 ); +} + + +const VECTOR2I& SHAPE_POLY_SET::CVertex( SHAPE_POLY_SET::VERTEX_INDEX index ) const +{ + return CVertex( index.m_vertex, index.m_polygon, index.m_contour - 1 ); +} + + +SEG SHAPE_POLY_SET::Edge( int aGlobalIndex ) +{ + SHAPE_POLY_SET::VERTEX_INDEX indices; + + // If the edge does not exist, throw an exception, it is an illegal access memory error + if( !GetRelativeIndices( aGlobalIndex, &indices ) ) + throw( std::out_of_range( "aGlobalIndex-th edge does not exist" ) ); + + return m_polys[indices.m_polygon][indices.m_contour].Segment( indices.m_vertex ); +} + + +bool SHAPE_POLY_SET::IsPolygonSelfIntersecting( int aPolygonIndex ) +{ + // Get polygon + const POLYGON poly = CPolygon( aPolygonIndex ); + + SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex ); + SEGMENT_ITERATOR innerIterator; + + for( iterator = IterateSegmentsWithHoles( aPolygonIndex ); iterator; iterator++ ) + { + SEG firstSegment = *iterator; + + // Iterate through all remaining segments. + innerIterator = iterator; + + // Start in the next segment, we don't want to check collision between a segment and itself + for( innerIterator++; innerIterator; innerIterator++ ) + { + SEG secondSegment = *innerIterator; + + // Check whether the two segments built collide, only when they are not adjacent. + if( !iterator.IsAdjacent( innerIterator ) && firstSegment.Collide( secondSegment, 0 ) ) + return true; + } + } + + return false; +} + + +bool SHAPE_POLY_SET::IsSelfIntersecting() +{ + for( unsigned int polygon = 0; polygon < m_polys.size(); polygon++ ) + { + if( IsPolygonSelfIntersecting( polygon ) ) + return true; + } + + return false; } @@ -205,9 +434,12 @@ const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath ) for( unsigned int i = 0; i < aPath.size(); i++ ) lc.Append( aPath[i].X, aPath[i].Y ); + lc.SetClosed( true ); + return lc; } + void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode ) { @@ -363,7 +595,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree ) for( unsigned int i = 0; i < n->Childs.size(); i++ ) paths.push_back( convertFromClipper( n->Childs[i]->Contour ) ); - m_polys.push_back(paths); + m_polys.push_back( paths ); } } } @@ -411,6 +643,7 @@ struct FractureEdge typedef std::vector FractureEdgeSet; + static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) { int x = edge->m_p1.x; @@ -478,6 +711,7 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) return 0; } + void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) { FractureEdgeSet edges; @@ -590,6 +824,21 @@ void SHAPE_POLY_SET::Fracture( POLYGON_MODE aFastMode ) } +bool SHAPE_POLY_SET::HasHoles() const +{ + // Iterate through all the polygons on the set + for( const POLYGON& paths : m_polys ) + { + // If any of them has more than one contour, it is a hole. + if( paths.size() > 1 ) + return true; + } + + // Return false if and only if every polygon has just one outline, without holes. + return false; +} + + void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode ) { SHAPE_POLY_SET empty; @@ -598,6 +847,39 @@ void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode ) } +int SHAPE_POLY_SET::NormalizeAreaOutlines() +{ + // We are expecting only one main outline, but this main outline can have holes + // if holes: combine holes and remove them from the main outline. + // Note also we are using SHAPE_POLY_SET::PM_STRICTLY_SIMPLE in polygon + // calculations, but it is not mandatory. It is used mainly + // because there is usually only very few vertices in area outlines + SHAPE_POLY_SET::POLYGON& outline = Polygon( 0 ); + SHAPE_POLY_SET holesBuffer; + + // Move holes stored in outline to holesBuffer: + // The first SHAPE_LINE_CHAIN is the main outline, others are holes + while( outline.size() > 1 ) + { + holesBuffer.AddOutline( outline.back() ); + outline.pop_back(); + } + + Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + // If any hole, substract it to main outline + if( holesBuffer.OutlineCount() ) + { + holesBuffer.Simplify( SHAPE_POLY_SET::PM_FAST); + BooleanSubtract( holesBuffer, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + } + + RemoveNullSegments(); + + return OutlineCount(); +} + + const std::string SHAPE_POLY_SET::Format() const { std::stringstream ss; @@ -607,10 +889,10 @@ 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++) + 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++) + 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"; @@ -694,12 +976,108 @@ const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const } +bool SHAPE_POLY_SET::PointOnEdge( const VECTOR2I& aP ) const +{ + // Iterate through all the polygons in the set + for( const POLYGON& polygon : m_polys ) + { + // Iterate through all the line chains in the polygon + for( const SHAPE_LINE_CHAIN& lineChain : polygon ) + { + if( lineChain.PointOnEdge( aP ) ) + return true; + } + } + + return false; +} + + +bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance ) const +{ + SHAPE_POLY_SET polySet = SHAPE_POLY_SET( *this ); + + // Inflate the polygon if necessary. + if( aClearance > 0 ) + { + // fixme: the number of arc segments should not be hardcoded + polySet.Inflate( aClearance, 8 ); + } + + // There is a collision if and only if the point is inside of the polygon. + return polySet.Contains( aP ); +} + + void SHAPE_POLY_SET::RemoveAllContours() { m_polys.clear(); } +void SHAPE_POLY_SET::RemoveContour( int aContourIdx, int aPolygonIdx ) +{ + // Default polygon is the last one + if( aPolygonIdx < 0 ) + aPolygonIdx += m_polys.size(); + + m_polys[aPolygonIdx].erase( m_polys[aPolygonIdx].begin() + aContourIdx ); +} + + +int SHAPE_POLY_SET::RemoveNullSegments() +{ + int removed = 0; + + ITERATOR iterator = IterateWithHoles(); + + VECTOR2I contourStart = *iterator; + VECTOR2I segmentStart, segmentEnd; + + VERTEX_INDEX indexStart; + + while( iterator ) + { + // Obtain first point and its index + segmentStart = *iterator; + indexStart = iterator.GetIndex(); + + // Obtain last point + if( iterator.IsEndContour() ) + { + segmentEnd = contourStart; + + // Advance + iterator++; + + if( iterator ) + contourStart = *iterator; + } + else + { + // Advance + iterator++; + + if( iterator ) + segmentEnd = *iterator; + } + + // Remove segment start if both points are equal + if( segmentStart == segmentEnd ) + { + RemoveVertex( indexStart ); + removed++; + + // Advance the iterator one position, as there is one vertex less. + if( iterator ) + iterator++; + } + } + + return removed; +} + + void SHAPE_POLY_SET::DeletePolygon( int aIdx ) { m_polys.erase( m_polys.begin() + aIdx ); @@ -718,25 +1096,131 @@ 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 ) +{ + // Shows whether there was a collision + bool collision = false; + + // Difference vector between each vertex and aPoint. + VECTOR2D delta; + double distance, clearance; + + // Convert clearance to double for precission when comparing distances + clearance = aClearance; + + for( ITERATOR iterator = IterateWithHoles(); iterator; iterator++ ) + { + // Get the difference vector between current vertex and aPoint + delta = *iterator - aPoint; + + // Compute distance + distance = delta.EuclideanNorm(); + + // Check for collisions + if( distance <= clearance ) + { + collision = true; + + // Update aClearance to look for closer vertices + clearance = distance; + + // Store the indices that identify the vertex + aClosestVertex = iterator.GetIndex(); + } + } + + return collision; +} + + +bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint, + SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) +{ + // Shows whether there was a collision + bool collision = false; + + SEGMENT_ITERATOR iterator; + + for( iterator = IterateSegmentsWithHoles(); iterator; iterator++ ) + { + SEG currentSegment = *iterator; + int distance = currentSegment.Distance( aPoint ); + + // Check for collisions + if( distance <= aClearance ) + { + collision = true; + + // Update aClearance to look for closer edges + aClearance = distance; + + // Store the indices that identify the vertex + aClosestVertex = iterator.GetIndex(); + } + } + + return collision; +} + + bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const { - // fixme: support holes! - if( m_polys.size() == 0 ) // empty set? return false; + // If there is a polygon specified, check the condition against that polygon if( aSubpolyIndex >= 0 ) - return pointInPolygon( aP, m_polys[aSubpolyIndex][0] ); + return containsSingle( aP, aSubpolyIndex ); - for( const POLYGON& polys : m_polys ) + // In any other case, check it against all polygons in the set + for( int polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ ) { - if( polys.size() == 0 ) - continue; - - if( pointInPolygon( aP, polys[0] ) ) + if( containsSingle( aP, polygonIdx ) ) return true; } + return false; + +} + + +void SHAPE_POLY_SET::RemoveVertex( int aGlobalIndex ) +{ + VERTEX_INDEX index; + + // Assure the to be removed vertex exists, abort otherwise + if( GetRelativeIndices( aGlobalIndex, &index ) ) + RemoveVertex( index ); + else + throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) ); +} + + +void SHAPE_POLY_SET::RemoveVertex( VERTEX_INDEX aIndex ) +{ + m_polys[aIndex.m_polygon][aIndex.m_contour].Remove( aIndex.m_vertex ); +} + + +bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) const +{ + // Check that the point is inside the outline + if( pointInPolygon( aP, m_polys[aSubpolyIndex][0] ) ) + { + // Check that the point is not in any of the holes + 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 ) ) + return false; + } + + return true; + } + return false; } @@ -832,3 +1316,313 @@ int SHAPE_POLY_SET::TotalVertices() const return c; } + + +SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance, int aIndex ) +{ + return chamferFilletPolygon( CORNER_MODE::CHAMFERED, aDistance, aIndex ); +} + + +SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, + unsigned int aSegments, + int aIndex ) +{ + return chamferFilletPolygon(CORNER_MODE::FILLETED, aRadius, aIndex, aSegments ); +} + + +int SHAPE_POLY_SET::DistanceToPolygon( VECTOR2I aPoint, int aPolygonIndex ) +{ + // We calculate the min dist between the segment and each outline segment + // However, if the segment to test is inside the outline, and does not cross + // any edge, it can be seen outside the polygon. + // Therefore test if a segment end is inside ( testing only one end is enough ) + if( containsSingle( aPoint, aPolygonIndex ) ) + return 0; + + SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex ); + + SEG polygonEdge = *iterator; + int minDistance = polygonEdge.Distance( aPoint ); + + for( iterator++; iterator && minDistance > 0; iterator++ ) + { + polygonEdge = *iterator; + + int currentDistance = polygonEdge.Distance( aPoint ); + + if( currentDistance < minDistance ) + minDistance = currentDistance; + } + + return minDistance; +} + + +int SHAPE_POLY_SET::DistanceToPolygon( SEG aSegment, int aPolygonIndex, int aSegmentWidth ) +{ + // We calculate the min dist between the segment and each outline segment + // However, if the segment to test is inside the outline, and does not cross + // any edge, it can be seen outside the polygon. + // Therefore test if a segment end is inside ( testing only one end is enough ) + if( containsSingle( aSegment.A, aPolygonIndex ) ) + return 0; + + SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex ); + + SEG polygonEdge = *iterator; + int minDistance = polygonEdge.Distance( aSegment ); + + for( iterator++; iterator && minDistance > 0; iterator++ ) + { + polygonEdge = *iterator; + + int currentDistance = polygonEdge.Distance( aSegment ); + + if( currentDistance < minDistance ) + minDistance = currentDistance; + } + + // Take into account the width of the segment + if( aSegmentWidth > 0 ) + minDistance -= aSegmentWidth/2; + + // Return the maximum of minDistance and zero + return minDistance < 0 ? 0 : minDistance; +} + + +int SHAPE_POLY_SET::Distance( VECTOR2I aPoint ) +{ + int currentDistance; + int minDistance = DistanceToPolygon( aPoint, 0 ); + + // Iterate through all the polygons and get the minimum distance. + for( unsigned int polygonIdx = 1; polygonIdx < m_polys.size(); polygonIdx++ ) + { + currentDistance = DistanceToPolygon( aPoint, polygonIdx ); + + if( currentDistance < minDistance ) + minDistance = currentDistance; + } + + return minDistance; +} + + +int SHAPE_POLY_SET::Distance( SEG aSegment, int aSegmentWidth ) +{ + int currentDistance; + int minDistance = DistanceToPolygon( aSegment, 0 ); + + // Iterate through all the polygons and get the minimum distance. + for( unsigned int polygonIdx = 1; polygonIdx < m_polys.size(); polygonIdx++ ) + { + currentDistance = DistanceToPolygon( aSegment, polygonIdx, aSegmentWidth ); + + if( currentDistance < minDistance ) + minDistance = currentDistance; + } + + return minDistance; +} + + +bool SHAPE_POLY_SET::IsVertexInHole( int aGlobalIdx ) +{ + VERTEX_INDEX index; + + // Get the polygon and contour where the vertex is. If the vertex does not exist, return false + if( !GetRelativeIndices( aGlobalIdx, &index ) ) + return false; + + // The contour is a hole if its index is greater than zero + return index.m_contour > 0; +} + + +SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance ) +{ + SHAPE_POLY_SET chamfered; + + for( unsigned int polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ ) + chamfered.m_polys.push_back( ChamferPolygon( aDistance, polygonIdx ) ); + + return chamfered; +} + + +SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments ) +{ + SHAPE_POLY_SET filleted; + + for( size_t polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ ) + filleted.m_polys.push_back( FilletPolygon( aRadius, aSegments, polygonIdx ) ); + + return filleted; +} + + +SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, + unsigned int aDistance, + int aIndex, + int aSegments ) +{ + // Null segments create serious issues in calculations. Remove them: + RemoveNullSegments(); + + SHAPE_POLY_SET::POLYGON currentPoly = Polygon( aIndex ); + SHAPE_POLY_SET::POLYGON newPoly; + + // If the chamfering distance is zero, then the polygon remain intact. + if( aDistance == 0 ) + { + return currentPoly; + } + + // Iterate through all the contours (outline and holes) of the polygon. + for( SHAPE_LINE_CHAIN &currContour : currentPoly ) + { + // Generate a new contour in the new polygon + SHAPE_LINE_CHAIN newContour; + + // Iterate through the vertices of the contour + for( int currVertex = 0; currVertex < currContour.PointCount(); currVertex++ ) + { + // Current vertex + int x1 = currContour.Point( currVertex ).x; + int y1 = currContour.Point( currVertex ).y; + + // Indices for previous and next vertices. + int prevVertex; + int nextVertex; + + // Previous and next vertices indices computation. Necessary to manage the edge cases. + + // Previous vertex is the last one if the current vertex is the first one + prevVertex = currVertex == 0 ? currContour.PointCount() - 1 : currVertex - 1; + + // next vertex is the first one if the current vertex is the last one. + 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; + + // Next vertex computation + 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 ); + + // Make the final computations depending on the mode selected, chamfered or filleted. + if( aMode == CORNER_MODE::CHAMFERED ) + { + double distance = aDistance; + + // Chamfer one half of an edge at most + if( 0.5 * lena < distance ) + distance = 0.5 * lena; + + if( 0.5 * lenb < distance ) + distance = 0.5 * lenb; + + int nx1 = KiROUND( distance * xa / lena ); + int ny1 = KiROUND( distance * ya / lena ); + + newContour.Append( x1 + nx1, y1 + ny1 ); + + int nx2 = KiROUND( distance * xb / lenb ); + int ny2 = KiROUND( distance * yb / lenb ); + + newContour.Append( x1 + nx2, y1 + ny2 ); + } + else // CORNER_MODE = FILLETED + { + double cosine = ( xa * xb + ya * yb ) / ( lena * lenb ); + + double radius = aDistance; + double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 ); + + // Do nothing in case of parallel edges + if( std::isinf( denom ) ) + continue; + + // Limit rounding distance to one half of an edge + if( 0.5 * lena * denom < radius ) + radius = 0.5 * lena * denom; + + if( 0.5 * lenb * denom < radius ) + 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; + + // 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; + + // Cosine of arc angle + double argument = ( xs * xe + ys * ye ) / ( radius * radius ); + + // Make sure the argument is in [-1,1], interval in which the acos function is + // defined + if( argument < -1 ) + argument = -1; + else if( argument > 1 ) + argument = 1; + + double arcAngle = acos( argument ); + + // Calculate the number of segments + unsigned int segments = ceil( (double) aSegments * ( arcAngle / ( 2 * M_PI ) ) ); + + 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; + + newContour.Append( KiROUND( nx ), KiROUND( ny ) ); + + // Store the previous added corner to make a sanity check + 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; + + // 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 ); + } + } + } + } + + // Close the current contour and add it the new polygon + newContour.SetClosed( true ); + newPoly.push_back( newContour ); + } + + return newPoly; +} diff --git a/common/swig/kicad.i b/common/swig/kicad.i index 768b9b66ef..6004a89873 100644 --- a/common/swig/kicad.i +++ b/common/swig/kicad.i @@ -70,6 +70,7 @@ principle should be easily implemented by adapting the current STL containers. %ignore GetCommandOptions; %rename(getWxRect) operator wxRect; +%rename(getBOX2I) operator BOX2I; %ignore operator <<; %ignore operator=; diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h index ba14b981cf..8d74d8781a 100644 --- a/include/class_eda_rect.h +++ b/include/class_eda_rect.h @@ -30,6 +30,7 @@ #define CLASS_EDA_RECT_H #include +#include /** * Class EDA_RECT @@ -78,6 +79,7 @@ public: * @return true if aPoint is inside the boundary box. A point on a edge is seen as inside */ bool Contains( const wxPoint& aPoint ) const; + /** * Function Contains * @param x = the x coordinate of the point to test @@ -177,6 +179,18 @@ public: return wxRect( rect.m_Pos, rect.m_Size ); } + /** + * Function operator(BOX2I) + * overloads the cast operator to return a BOX2I + * @return BOX2I - this box shaped as a BOX2I object. + */ + operator BOX2I() const + { + EDA_RECT rect( m_Pos, m_Size ); + rect.Normalize(); + return BOX2I( rect.GetPosition(), rect.GetEnd() ); + } + /** * Function Inflate * inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx diff --git a/include/geometry/seg.h b/include/geometry/seg.h index 164457b93f..ee754fd517 100644 --- a/include/geometry/seg.h +++ b/include/geometry/seg.h @@ -38,6 +38,8 @@ class SEG { private: typedef VECTOR2I::extended_type ecoord; + VECTOR2I m_a; + VECTOR2I m_b; public: friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg ); @@ -46,13 +48,15 @@ public: * to an object the segment belongs to (e.g. a line chain) or references to locally stored * points (m_a, m_b). */ - VECTOR2I A; - VECTOR2I B; + VECTOR2I& A; + VECTOR2I& B; /** Default constructor * Creates an empty (0, 0) segment, locally-referenced */ - SEG() + SEG() : + A( m_a ), + B( m_b ) { m_index = -1; } @@ -62,9 +66,11 @@ public: * Creates a segment between (aX1, aY1) and (aX2, aY2), locally referenced */ SEG( int aX1, int aY1, int aX2, int aY2 ) : - A ( VECTOR2I( aX1, aY1 ) ), - B ( VECTOR2I( aX2, aY2 ) ) + A( m_a ), + B( m_b ) { + A = VECTOR2I( aX1, aY1 ); + B = VECTOR2I( aX2, aY2 ); m_index = -1; } @@ -72,11 +78,37 @@ public: * Constructor * Creates a segment between (aA) and (aB), locally referenced */ - SEG( const VECTOR2I& aA, const VECTOR2I& aB ) : A( aA ), B( aB ) + SEG( const VECTOR2I& aA, const VECTOR2I& aB ) : + A( m_a ), + B( m_b ) + { + A = aA; + B = aB; + m_index = -1; + } + + /** + * Constructor + * Creates a segment between (aA) and (aB), referencing the passed points + */ + SEG( VECTOR2I& aA, VECTOR2I& aB ) : + A( aA ), + B( aB ) { m_index = -1; } + /** + * Constructor + * Creates a segment between (aA) and (aB), referencing the passed points + */ + SEG( VECTOR2I& aA, VECTOR2I& aB, int aIndex ) : + A( aA ), + B( aB ) + { + m_index = aIndex; + } + /** * Constructor * Creates a segment between (aA) and (aB), referenced to a multi-segment shape @@ -84,16 +116,25 @@ public: * @param aB reference to the end point in the parent shape * @param aIndex index of the segment within the parent shape */ - SEG( const VECTOR2I& aA, const VECTOR2I& aB, int aIndex ) : A( aA ), B( aB ) + SEG( const VECTOR2I& aA, const VECTOR2I& aB, int aIndex ) : + A( m_a ), + B( m_b ) { + A = aA; + B = aB; m_index = aIndex; } /** * Copy constructor */ - SEG( const SEG& aSeg ) : A( aSeg.A ), B( aSeg.B ), m_index( aSeg.m_index ) + SEG( const SEG& aSeg ) : + A( m_a ), + B( m_b ), + m_index( aSeg.m_index ) { + A = aSeg.A; + B = aSeg.B; } SEG& operator=( const SEG& aSeg ) diff --git a/include/geometry/shape_line_chain.h b/include/geometry/shape_line_chain.h index 9f62aaabcd..df57d10783 100644 --- a/include/geometry/shape_line_chain.h +++ b/include/geometry/shape_line_chain.h @@ -410,6 +410,16 @@ public: */ void Remove( int aStartIndex, int aEndIndex ); + /** + * Function Remove() + * removes the aIndex-th point from the line chain. + * @param aIndex is the index of the point to be removed. + */ + void Remove( int aIndex ) + { + Remove( aIndex, aIndex ); + } + /** * Function Split() * diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index 842f2de720..efdd91e9f9 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -1,8 +1,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 CERN + * Copyright (C) 2015-2017 CERN * @author Tomasz Wlostowski + * @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 @@ -39,6 +40,14 @@ * Represents a set of closed polygons. Polygons may be nonconvex, self-intersecting * and have holes. Provides boolean operations (using Clipper library as the backend). * + * Let us define the terms used on this class to clarify methods names and comments: + * - Polygon: each polygon in the set. + * - Outline: first polyline in each polygon; represents its outer contour. + * - Hole: second and following polylines in the polygon. + * - Contour: each polyline of each polygon in the set, whether or not it is an + * outline or a hole. + * - Vertex (or corner): each one of the points that define a contour. + * * TODO: add convex partitioning & spatial index */ class SHAPE_POLY_SET : public SHAPE @@ -48,39 +57,97 @@ class SHAPE_POLY_SET : public SHAPE ///> the remaining (if any), are the holes typedef std::vector POLYGON; + /** + * Struct VERTEX_INDEX + * + * Structure to hold the necessary information in order to index a vertex on a + * SHAPE_POLY_SET object: the polygon index, the contour index relative to the polygon and + * the vertex index relative the contour. + */ + typedef struct VERTEX_INDEX + { + int m_polygon; /*!< m_polygon is the index of the polygon. */ + int m_contour; /*!< m_contour is the index of the contour relative to the polygon. */ + int m_vertex; /*!< m_vertex is the index of the vertex relative to the contour. */ + + VERTEX_INDEX() : m_polygon(-1), m_contour(-1), m_vertex(-1) + { + } + } VERTEX_INDEX; + /** * Class ITERATOR_TEMPLATE * - * Base class for iterating over all vertices in a given SHAPE_POLY_SET + * Base class for iterating over all vertices in a given SHAPE_POLY_SET. */ template class ITERATOR_TEMPLATE { public: + /** + * Function IsEndContour. + * @return bool - true if the current vertex is the last one of the current contour + * (outline or hole); false otherwise. + */ bool IsEndContour() const { - return m_currentVertex + 1 == m_poly->CPolygon( m_currentOutline )[0].PointCount(); + return m_currentVertex + 1 == m_poly->CPolygon( m_currentPolygon )[m_currentContour].PointCount(); } - bool IsLastContour() const + /** + * Function IsLastOutline. + * @return bool - true if the current outline is the last one; false otherwise. + */ + bool IsLastPolygon() const { - return m_currentOutline == m_lastOutline; + return m_currentPolygon == m_lastPolygon; } operator bool() const { - return m_currentOutline <= m_lastOutline; + return m_currentPolygon <= m_lastPolygon; } + /** + * Function Advance + * advances the indices of the current vertex/outline/contour, checking whether the + * vertices in the holes have to be iterated through + */ void Advance() { + // Advance vertex index m_currentVertex ++; - if( m_currentVertex >= m_poly->CPolygon( m_currentOutline )[0].PointCount() ) + // Check whether the user wants to iterate through the vertices of the holes + // and behave accordingly + if( m_iterateHoles ) { - m_currentVertex = 0; - m_currentOutline++; + // If the last vertex of the contour was reached, advance the contour index + if( m_currentVertex >= m_poly->CPolygon( m_currentPolygon )[m_currentContour].PointCount() ) + { + m_currentVertex = 0; + m_currentContour++; + + // If the last contour of the current polygon was reached, advance the + // outline index + int totalContours = m_poly->CPolygon( m_currentPolygon ).size(); + + if( m_currentContour >= totalContours ) + { + m_currentContour = 0; + m_currentPolygon++; + } + } + } + else + { + // If the last vertex of the outline was reached, advance to the following polygon + if( m_currentVertex >= m_poly->CPolygon( m_currentPolygon )[0].PointCount() ) + { + m_currentVertex = 0; + m_currentPolygon++; + } } } @@ -96,7 +163,7 @@ class SHAPE_POLY_SET : public SHAPE T& Get() { - return m_poly->Polygon( m_currentOutline )[0].Point( m_currentVertex ); + return m_poly->Polygon( m_currentPolygon )[m_currentContour].Point( m_currentVertex ); } T& operator*() @@ -109,22 +176,230 @@ class SHAPE_POLY_SET : public SHAPE return &Get(); } + /** + * Function GetIndex + * @return VERTEX_INDEX - indices of the current polygon, contour and vertex. + */ + VERTEX_INDEX GetIndex() + { + VERTEX_INDEX index; + + index.m_polygon = m_currentPolygon; + index.m_contour = m_currentContour; + index.m_vertex = m_currentVertex; + + return index; + } + private: friend class SHAPE_POLY_SET; SHAPE_POLY_SET* m_poly; - int m_currentOutline; - int m_lastOutline; + int m_currentPolygon; + int m_currentContour; int m_currentVertex; + int m_lastPolygon; + bool m_iterateHoles; }; + /** + * Class SEGMENT_ITERATOR_TEMPLATE + * + * Base class for iterating over all segments in a given SHAPE_POLY_SET. + */ + template + class SEGMENT_ITERATOR_TEMPLATE + { + public: + /** + * Function IsLastOutline. + * @return bool - true if the current outline is the last one. + */ + bool IsLastPolygon() const + { + return m_currentPolygon == m_lastPolygon; + } + + operator bool() const + { + return m_currentPolygon <= m_lastPolygon; + } + + /** + * Function Advance + * advances the indices of the current vertex/outline/contour, checking whether the + * vertices in the holes have to be iterated through + */ + void Advance() + { + // Advance vertex index + m_currentSegment++; + int last; + + // Check whether the user wants to iterate through the vertices of the holes + // and behave accordingly + if( m_iterateHoles ) + { + last = m_poly->CPolygon( m_currentPolygon )[m_currentContour].SegmentCount(); + + // If the last vertex of the contour was reached, advance the contour index + if( m_currentSegment >= last ) + { + m_currentSegment = 0; + m_currentContour++; + + // If the last contour of the current polygon was reached, advance the + // outline index + int totalContours = m_poly->CPolygon( m_currentPolygon ).size(); + + if( m_currentContour >= totalContours ) + { + m_currentContour = 0; + m_currentPolygon++; + } + } + } + else + { + last = m_poly->CPolygon( m_currentPolygon )[0].SegmentCount(); + // If the last vertex of the outline was reached, advance to the following + // polygon + if( m_currentSegment >= last ) + { + m_currentSegment = 0; + m_currentPolygon++; + } + } + } + + void operator++( int dummy ) + { + Advance(); + } + + void operator++() + { + Advance(); + } + + T Get() + { + return m_poly->Polygon( m_currentPolygon )[m_currentContour].Segment( m_currentSegment ); + } + + T operator*() + { + return Get(); + } + + /** + * Function GetIndex + * @return VERTEX_INDEX - indices of the current polygon, contour and vertex. + */ + VERTEX_INDEX GetIndex() + { + VERTEX_INDEX index; + + index.m_polygon = m_currentPolygon; + index.m_contour = m_currentContour; + index.m_vertex = m_currentSegment; + + return index; + } + + /** + * Function IsAdjacent + * @param aOther is an iterator pointing to another segment. + * @return bool - true if both iterators point to the same segment of the same + * contour of the same polygon of the same polygon set; false + * otherwise. + */ + bool IsAdjacent( SEGMENT_ITERATOR_TEMPLATE aOther ) + { + // Check that both iterators point to the same contour of the same polygon of the + // same polygon set + if( m_poly == aOther.m_poly && m_currentPolygon == aOther.m_currentPolygon && + m_currentContour == aOther.m_currentContour ) + { + // Compute the total number of segments + int numSeg; + numSeg = m_poly->CPolygon( m_currentPolygon )[m_currentContour].SegmentCount(); + + // Compute the difference of the segment indices. If it is exactly one, they + // are adjacent. The only missing case where they also are adjacent is when + // the segments are the first and last one, in which case the difference + // always equals the total number of segments minus one. + int indexDiff = abs( m_currentSegment - aOther.m_currentSegment ); + + return ( indexDiff == 1 ) || ( indexDiff == (numSeg - 1) ); + } + + return false; + } + + private: + friend class SHAPE_POLY_SET; + + SHAPE_POLY_SET* m_poly; + int m_currentPolygon; + int m_currentContour; + int m_currentSegment; + int m_lastPolygon; + bool m_iterateHoles; + }; + + // Iterator and const iterator types to visit polygon's points. typedef ITERATOR_TEMPLATE ITERATOR; typedef ITERATOR_TEMPLATE CONST_ITERATOR; + // Iterator and const iterator types to visit polygon's edges. + typedef SEGMENT_ITERATOR_TEMPLATE SEGMENT_ITERATOR; + typedef SEGMENT_ITERATOR_TEMPLATE CONST_SEGMENT_ITERATOR; + SHAPE_POLY_SET(); + + /** + * Copy constructor SHAPE_POLY_SET + * Performs a deep copy of \p aOther into \p this. + * @param aOther is the SHAPE_POLY_SET object that will be copied. + */ + SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther ); + ~SHAPE_POLY_SET(); + /** + * Function GetRelativeIndices + * + * Converts a global vertex index ---i.e., a number that globally identifies a vertex in a + * concatenated list of all vertices in all contours--- and get the index of the vertex + * relative to the contour relative to the polygon in which it is. + * @param aGlobalIdx is the global index of the corner whose structured index wants to + * be found + * @param aPolygonIdx is the index of the polygon in which the expected vertex is. + * @param aContourIdx is the index of the contour in the aPolygonIdx-th polygon in which + * the expected vertex is. + * @param aVertexIdx is the index of the vertex in the aContourIdx-th contour in which + * the expected vertex is. + * @return bool - true if the global index is correct and the information in + * aRelativeIndices is valid; false otherwise. + */ + bool GetRelativeIndices( int aGlobalIdx, VERTEX_INDEX* aRelativeIndices) const; + + /** + * Function GetGlobalIndex + * computes the global index of a vertex from the relative indices of polygon, contour and + * vertex. + * @param aRelativeIndices is the set of relative indices. + * @param aGlobalIdx [out] is the computed global index. + * @return bool - true if the relative indices are correct; false otherwise. The cmoputed + * global index is returned in the \p aGlobalIdx reference. + */ + bool GetGlobalIndex( VERTEX_INDEX aRelativeIndices, int& aGlobalIdx ); + + /// @copydoc SHAPE::Clone() + SHAPE* Clone() const override; + ///> Creates a new empty polygon in the set and returns its index int NewOutline(); @@ -138,7 +413,20 @@ class SHAPE_POLY_SET : public SHAPE int AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline = -1 ); ///> Appends a vertex at the end of the given outline/hole (default: the last outline) - int Append( int x, int y, int aOutline = -1, int aHole = -1 ); + /** + * Function Append + * adds a new vertex to the contour indexed by \p aOutline and \p aHole (defaults to the + * outline of the last polygon). + * @param x is the x coordinate of the new vertex. + * @param y is the y coordinate of the new vertex. + * @param aOutline is the index of the polygon. + * @param aHole is the index of the hole (-1 for the main outline), + * @param aAllowDuplication is a flag to indicate whether it is allowed to add this + * corner even if it is duplicated. + * @return int - the number of corners of the selected contour after the addition. + */ + int Append( int x, int y, int aOutline = -1, int aHole = -1, + bool aAllowDuplication = false ); ///> Merges polygons from two sets. void Append( const SHAPE_POLY_SET& aSet ); @@ -146,13 +434,59 @@ class SHAPE_POLY_SET : public SHAPE ///> Appends a vertex at the end of the given outline/hole (default: the last outline) void Append( const VECTOR2I& aP, int aOutline = -1, int aHole = -1 ); - ///> Returns the index-th vertex in a given hole outline within a given outline - VECTOR2I& Vertex( int index, int aOutline = -1, int aHole = -1 ); + /** + * Function InsertVertex + * Adds a vertex in the globally indexed position aGlobalIndex. + * @param aGlobalIndex is the global index of the position in which teh new vertex will be + * inserted. + * @param aNewVertex is the new inserted vertex. + */ + void InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex ); ///> Returns the index-th vertex in a given hole outline within a given outline - const VECTOR2I& CVertex( int index, int aOutline = -1, int aHole = -1 ) const; + VECTOR2I& Vertex( int aIndex, int aOutline, int aHole ); - ///> Returns true if any of the outlines is self-intersecting + ///> Returns the index-th vertex in a given hole outline within a given outline + const VECTOR2I& CVertex( int aIndex, int aOutline, int aHole ) const; + + ///> Returns the aGlobalIndex-th vertex in the poly set + VECTOR2I& Vertex( int aGlobalIndex ); + + ///> Returns the aGlobalIndex-th vertex in the poly set + const VECTOR2I& CVertex( int aGlobalIndex ) const; + + ///> Returns the index-th vertex in a given hole outline within a given outline + VECTOR2I& Vertex( VERTEX_INDEX aIndex ); + + ///> Returns the index-th vertex in a given hole outline within a given outline + const VECTOR2I& CVertex( VERTEX_INDEX aIndex ) const; + + /** + * Function Edge + * Returns a reference to the aGlobalIndex-th segment in the polygon set. Modifying the + * points in the returned object will modify the corresponding vertices on the polygon set. + * @param aGlobalIndex is index of the edge, globally indexed between all edges in all + * contours + * @return SEG - the aGlobalIndex-th segment, whose points are references to the polygon + * points. + */ + SEG Edge( int aGlobalIndex ); + + + /** + * Function IsPolygonSelfIntersecting. + * Checks whether the aPolygonIndex-th polygon in the set is self intersecting. + * @param aPolygonIndex index of the polygon that wants to be checked. + * @return bool - true if the aPolygonIndex-th polygon is self intersecting, false + * otherwise. + */ + bool IsPolygonSelfIntersecting( int aPolygonIndex ); + + /** + * Function IsSelfIntersecting + * Checks whether any of the polygons in the set is self intersecting. + * @return bool - true if any of the polygons is self intersecting, false otherwise. + */ bool IsSelfIntersecting(); ///> Returns the number of outlines in the set @@ -175,6 +509,22 @@ class SHAPE_POLY_SET : public SHAPE return m_polys[aIndex][0]; } + /** + * Function Subset + * returns a subset of the polygons in this set, the ones between aFirstPolygon and + * aLastPolygon. + * @param aFirstPolygon is the first polygon to be included in the returned set. + * @param aLastPolygon is the first polygon to be excluded of the returned set. + * @return SHAPE_POLY_SET - a set containing the polygons between aFirstPolygon (included) + * and aLastPolygon (excluded). + */ + SHAPE_POLY_SET Subset( int aFirstPolygon, int aLastPolygon ); + + SHAPE_POLY_SET UnitSet( int aPolygonIndex ) + { + return Subset( aPolygonIndex, aPolygonIndex + 1 ); + } + ///> Returns the reference to aHole-th hole in the aIndex-th outline SHAPE_LINE_CHAIN& Hole( int aOutline, int aHole ) { @@ -202,39 +552,83 @@ class SHAPE_POLY_SET : public SHAPE return m_polys[aIndex]; } - ///> Returns an iterator object, for iterating between aFirst and aLast outline. - ITERATOR Iterate( int aFirst, int aLast ) + /** + * Function Iterate + * returns an object to iterate through the points of the polygons between \p aFirst and + * \p aLast. + * @param aFirst is the first polygon whose points will be iterated. + * @param aLast is the last polygon whose points will be iterated. + * @param aIterateHoles is a flag to indicate whether the points of the holes should be + * iterated. + * @return ITERATOR - the iterator object. + */ + ITERATOR Iterate( int aFirst, int aLast, bool aIterateHoles = false ) { ITERATOR iter; iter.m_poly = this; - iter.m_currentOutline = aFirst; - iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast; + iter.m_currentPolygon = aFirst; + iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast; + iter.m_currentContour = 0; iter.m_currentVertex = 0; + iter.m_iterateHoles = aIterateHoles; return iter; } - ///> Returns an iterator object, for iterating aOutline-th outline + /** + * Function Iterate + * @param aOutline the index of the polygon to be iterated. + * @return ITERATOR - an iterator object to visit all points in the main outline of the + * aOutline-th polygon, without visiting the points in the holes. + */ ITERATOR Iterate( int aOutline ) { return Iterate( aOutline, aOutline ); } - ///> Returns an iterator object, for all outlines in the set (no holes) + /** + * Function IterateWithHoles + * @param aOutline the index of the polygon to be iterated. + * @return ITERATOR - an iterator object to visit all points in the main outline of the + * aOutline-th polygon, visiting also the points in the holes. + */ + ITERATOR IterateWithHoles( int aOutline ) + { + return Iterate( aOutline, aOutline, true ); + } + + /** + * Function Iterate + * @return ITERATOR - an iterator object to visit all points in all outlines of the set, + * without visiting the points in the holes. + */ ITERATOR Iterate() { return Iterate( 0, OutlineCount() - 1 ); } - CONST_ITERATOR CIterate( int aFirst, int aLast ) const + /** + * Function IterateWithHoles + * @return ITERATOR - an iterator object to visit all points in all outlines of the set, + * visiting also the points in the holes. + */ + ITERATOR IterateWithHoles() + { + return Iterate( 0, OutlineCount() - 1, true ); + } + + + CONST_ITERATOR CIterate( int aFirst, int aLast, bool aIterateHoles = false ) const { CONST_ITERATOR iter; iter.m_poly = const_cast( this ); - iter.m_currentOutline = aFirst; - iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast; + iter.m_currentPolygon = aFirst; + iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast; + iter.m_currentContour = 0; iter.m_currentVertex = 0; + iter.m_iterateHoles = aIterateHoles; return iter; } @@ -244,11 +638,80 @@ class SHAPE_POLY_SET : public SHAPE return CIterate( aOutline, aOutline ); } + CONST_ITERATOR CIterateWithHoles( int aOutline ) const + { + return CIterate( aOutline, aOutline, true ); + } + CONST_ITERATOR CIterate() const { return CIterate( 0, OutlineCount() - 1 ); } + CONST_ITERATOR CIterateWithHoles() const + { + return CIterate( 0, OutlineCount() - 1, true ); + } + + ITERATOR IterateFromVertexWithHoles( int aGlobalIdx ) + { + // Build iterator + ITERATOR iter = IterateWithHoles(); + + // Get the relative indices of the globally indexed vertex + VERTEX_INDEX indices; + + if( !GetRelativeIndices( aGlobalIdx, &indices ) ) + throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) ); + + // Adjust where the iterator is pointing + iter.m_currentPolygon = indices.m_polygon; + iter.m_currentContour = indices.m_contour; + iter.m_currentVertex = indices.m_vertex; + + return iter; + } + + ///> Returns an iterator object, for iterating between aFirst and aLast outline, with or + /// without holes (default: without) + SEGMENT_ITERATOR IterateSegments( int aFirst, int aLast, bool aIterateHoles = false ) + { + SEGMENT_ITERATOR iter; + + iter.m_poly = this; + iter.m_currentPolygon = aFirst; + iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast; + iter.m_currentContour = 0; + iter.m_currentSegment = 0; + iter.m_iterateHoles = aIterateHoles; + + return iter; + } + + ///> Returns an iterator object, for iterating aPolygonIdx-th polygon edges + SEGMENT_ITERATOR IterateSegments( int aPolygonIdx ) + { + return IterateSegments( aPolygonIdx, aPolygonIdx ); + } + + ///> Returns an iterator object, for all outlines in the set (no holes) + SEGMENT_ITERATOR IterateSegments() + { + return IterateSegments( 0, OutlineCount() - 1 ); + } + + ///> Returns an iterator object, for all outlines in the set (with holes) + SEGMENT_ITERATOR IterateSegmentsWithHoles() + { + return IterateSegments( 0, OutlineCount() - 1, true ); + } + + ///> Returns an iterator object, for the aOutline-th outline in the set (with holes) + SEGMENT_ITERATOR IterateSegmentsWithHoles( int aOutline ) + { + return IterateSegments( aOutline, aOutline, true ); + } + /** operations on polygons use a aFastMode param * if aFastMode is PM_FAST (true) the result can be a weak polygon * if aFastMode is PM_STRICTLY_SIMPLE (false) (default) the result is (theorically) a strictly @@ -307,6 +770,15 @@ class SHAPE_POLY_SET : public SHAPE ///> For aFastMode meaning, see function booleanOp void Simplify( POLYGON_MODE aFastMode ); + /** + * Function NormalizeAreaOutlines + * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s) + * Removes null segments. + * @return int - the polygon count (always >= 1, because there is at least one polygon) + * There are new polygons only if the polygon count is > 1. + */ + int NormalizeAreaOutlines(); + /// @copydoc SHAPE::Format() const std::string Format() const override; @@ -324,13 +796,59 @@ class SHAPE_POLY_SET : public SHAPE const BOX2I BBox( int aClearance = 0 ) const override; + /** + * Function PointOnEdge() + * + * Checks if point aP lies on an edge or vertex of some of the outlines or holes. + * @param aP is the point to check. + * @return bool - true if the point lies on the edge of any polygon. + */ + bool PointOnEdge( const VECTOR2I& aP ) const; + + /** + * Function Collide + * Checks whether the point aP collides with the inside of the polygon set; if the point + * lies on an edge or on a corner of any of the polygons, there is no collision: the edges + * does not belong to the polygon itself. + * @param aP is the VECTOR2I point whose collision with respect to the poly set + * will be tested. + * @param aClearance is the security distance; if the point lies closer to the polygon + * than aClearance distance, then there is a collision. + * @return bool - true if the point aP collides with the polygon; false in any other case. + */ + bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override; + // fixme: add collision support - bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override { return false; } bool Collide( const SEG& aSeg, int aClearance = 0 ) const override { return false; } + /** + * Function CollideVertex + * Checks whether aPoint collides with any vertex of any of the contours of the polygon. + * @param aPoint is the VECTOR2I point whose collision with respect to the polygon + * will be tested. + * @param aClearance is the security distance; if \p aPoint lies closer to a vertex than + * aClearance distance, then there is a collision. + * @param aClosestVertex is the index of the closes vertex to \p aPoint. + * @return bool - true if there is a collision, false in any other case. + */ + bool CollideVertex( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex, + int aClearance = 0 ); - ///> Returns true is a given subpolygon contains the point aP. If aSubpolyIndex < 0 (default value), - ///> checks all polygons in the set + /** + * Function CollideEdge + * Checks whether aPoint collides with any edge of any of the contours of the polygon. + * @param aPoint is the VECTOR2I point whose collision with respect to the polygon + * will be tested. + * @param aClearance is the security distance; if \p aPoint lies closer to a vertex than + * aClearance distance, then there is a collision. + * @param aClosestVertex is the index of the closes vertex to \p aPoint. + * @return bool - true if there is a collision, false in any other case. + */ + bool CollideEdge( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex, + int aClearance = 0 ); + + ///> Returns true if a given subpolygon contains the point aP. If aSubpolyIndex < 0 + ///> (default value), checks all polygons in the set bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1 ) const; ///> Returns true if the set is empty (no polygons at all) @@ -339,15 +857,133 @@ class SHAPE_POLY_SET : public SHAPE return m_polys.size() == 0; } + /** + * Function RemoveVertex + * deletes the aGlobalIndex-th vertex. + * @param aGlobalIndex is the global index of the to-be-removed vertex. + */ + void RemoveVertex( int aGlobalIndex ); + + /** + * Function RemoveVertex + * deletes the vertex indexed by aIndex (index of polygon, contour and vertex). + * @param aindex is the set of relative indices of the to-be-removed vertex. + */ + void RemoveVertex( VERTEX_INDEX aRelativeIndices ); + ///> Removes all outlines & holes (clears) the polygon set. void RemoveAllContours(); + /** + * Function RemoveContour + * deletes the aContourIdx-th contour of the aPolygonIdx-th polygon in the set. + * @param aContourIdx is the index of the contour in the aPolygonIdx-th polygon to be + * removed. + * @param aPolygonIdx is the index of the polygon in which the to-be-removed contour is. + * Defaults to the last polygon in the set. + */ + void RemoveContour( int aContourIdx, int aPolygonIdx = -1 ); + + /** + * Function RemoveNullSegments + * looks for null segments; ie, segments whose ends are exactly the same and deletes them. + * @return int - the number of deleted segments. + */ + int RemoveNullSegments(); + ///> Returns total number of vertices stored in the set. int TotalVertices() const; ///> Deletes aIdx-th polygon from the set void DeletePolygon( int aIdx ); + /** + * Function Chamfer + * returns a chamfered version of the aIndex-th polygon. + * @param aDistance is the chamfering distance. + * @param aIndex is the index of the polygon to be chamfered. + * @return POLYGON - A polygon containing the chamfered version of the aIndex-th polygon. + */ + POLYGON ChamferPolygon( unsigned int aDistance, int aIndex = 0 ); + + /** + * Function Fillet + * returns a filleted version of the aIndex-th polygon. + * @param aRadius is the fillet radius. + * @param aSegments is the number of segments / fillet. + * @param aIndex is the index of the polygon to be filleted + * @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon. + */ + POLYGON FilletPolygon( unsigned int aRadius, unsigned int aSegments, int aIndex = 0 ); + + /** + * Function Chamfer + * returns a chamfered version of the polygon set. + * @param aDistance is the chamfering distance. + * @return SHAPE_POLY_SET - A set containing the chamfered version of this set. + */ + SHAPE_POLY_SET Chamfer( int aDistance ); + + /** + * Function Fillet + * returns a filleted version of the polygon set. + * @param aRadius is the fillet radius. + * @param aSegments is the number of segments / fillet. + * @return SHAPE_POLY_SET - A set containing the filleted version of this set. + */ + SHAPE_POLY_SET Fillet( int aRadius, int aSegments ); + + /** + * Function DistanceToPolygon + * computes the minimum distance between the aIndex-th polygon and aPoint. + * @param aPoint is the point whose distance to the aIndex-th polygon has to be measured. + * @param aIndex is the index of the polygon whose distace to aPoint has to be measured. + * @return int - The minimum distance between aPoint and all the segments of the aIndex-th + * polygon. If the point is contained in the polygon, the distance is zero. + */ + int DistanceToPolygon( VECTOR2I aPoint, int aIndex ); + + /** + * Function DistanceToPolygon + * computes the minimum distance between the aIndex-th polygon and aSegment with a + * possible width. + * @param aSegment is the segment whose distance to the aIndex-th polygon has to be + * measured. + * @param aIndex is the index of the polygon whose distace to aPoint has to be measured. + * @param aSegmentWidth is the width of the segment; defaults to zero. + * @return int - The minimum distance between aSegment and all the segments of the + * aIndex-th polygon. If the point is contained in the polygon, the + * distance is zero. + */ + int DistanceToPolygon( SEG aSegment, int aIndex, int aSegmentWidth = 0 ); + + /** + * Function DistanceToPolygon + * computes the minimum distance between aPoint and all the polygons in the set + * @param aPoint is the point whose distance to the set has to be measured. + * @return int - The minimum distance between aPoint and all the polygons in the set. If + * the point is contained in any of the polygons, the distance is zero. + */ + int Distance( VECTOR2I point ); + + /** + * Function DistanceToPolygon + * computes the minimum distance between aSegment and all the polygons in the set. + * @param aSegment is the segment whose distance to the polygon set has to be measured. + * @param aSegmentWidth is the width of the segment; defaults to zero. + * @return int - The minimum distance between aSegment and all the polygons in the set. + * If the point is contained in the polygon, the distance is zero. + */ + int Distance( SEG aSegment, int aSegmentWidth = 0 ); + + /** + * Function IsVertexInHole. + * checks whether the aGlobalIndex-th vertex belongs to a hole. + * @param aGlobalIdx is the index of the vertex. + * @return bool - true if the globally indexed aGlobalIdx-th vertex belongs to a hole. + */ + bool IsVertexInHole( int aGlobalIdx ); + private: SHAPE_LINE_CHAIN& getContourForCorner( int aCornerId, int& aIndexWithinContour ); @@ -381,6 +1017,47 @@ class SHAPE_POLY_SET : public SHAPE const ClipperLib::Path convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation ); const SHAPE_LINE_CHAIN convertFromClipper( const ClipperLib::Path& aPath ); + /** + * containsSingle function + * Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If + * the points lies on an edge, the polygon is considered to contain it. + * @param aP is the VECTOR2I point whose position with respect to the inside of + * the aSubpolyIndex-th polygon will be tested. + * @param aSubpolyIndex is an integer specifying which polygon in the set has to be + * checked. + * @return bool - true if aP is inside aSubpolyIndex-th polygon; false in any other + * case. + */ + bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) const; + + /** + * Operations ChamferPolygon and FilletPolygon are computed under the private chamferFillet + * method; this enum is defined to make the necessary distinction when calling this method + * from the public ChamferPolygon and FilletPolygon methods. + */ + enum CORNER_MODE + { + CHAMFERED, + FILLETED + }; + + /** + * Function chamferFilletPolygon + * Returns the camfered or filleted version of the aIndex-th polygon in the set, depending + * on the aMode selected + * @param aMode represent which action will be taken: CORNER_MODE::CHAMFERED will + * return a chamfered version of the polygon, CORNER_MODE::FILLETED will + * return a filleted version of the polygon. + * @param aDistance is the chamfering distance if aMode = CHAMFERED; if aMode = FILLETED, + * is the filleting radius. + * @param aIndex is the index of the polygon that will be chamfered/filleted. + * @param aSegments is the number of filleting segments if aMode = FILLETED. If aMode = + * CHAMFERED, it is unused. + * @return POLYGON - the chamfered/filleted version of the polygon. + */ + POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance, + int aIndex, int aSegments = -1 ); + typedef std::vector Polyset; Polyset m_polys; diff --git a/include/math/vector2d.h b/include/math/vector2d.h index df2cfda261..4374e0f978 100644 --- a/include/math/vector2d.h +++ b/include/math/vector2d.h @@ -116,6 +116,16 @@ public: return VECTOR2( (CastedType) x, (CastedType) y ); } + /** + * (wxPoint) + * implements the cast to wxPoint. + * @return wxPoint - the vector cast to wxPoint. + */ + explicit operator wxPoint() const + { + return wxPoint( x, y ); + } + /// Destructor // virtual ~VECTOR2(); diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 09f9de8656..31b2737f62 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -2361,7 +2361,8 @@ ZONE_CONTAINER* BOARD::InsertArea( int netcode, int iarea, LAYER_ID layer, int x else m_ZoneDescriptorList.push_back( new_area ); - new_area->Outline()->Start( layer, x, y, hatch ); + new_area->SetHatchStyle( (ZONE_CONTAINER::HATCH_STYLE) hatch ); + new_area->AppendCorner( wxPoint( x, y ) ); return new_area; } @@ -2369,45 +2370,47 @@ ZONE_CONTAINER* BOARD::InsertArea( int netcode, int iarea, LAYER_ID layer, int x bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAINER* aCurrArea ) { - CPolyLine* curr_polygon = aCurrArea->Outline(); - // mark all areas as unmodified except this one, if modified for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ ) m_ZoneDescriptorList[ia]->SetLocalFlags( 0 ); aCurrArea->SetLocalFlags( 1 ); - if( curr_polygon->IsPolygonSelfIntersecting() ) + if( aCurrArea->Outline()->IsSelfIntersecting() ) { - std::vector* pa = new std::vector; - curr_polygon->UnHatch(); - int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines( pa ); + aCurrArea->UnHatch(); + + // Normalize copied area and store resulting number of polygons + int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines(); // If clipping has created some polygons, we must add these new copper areas. if( n_poly > 1 ) { ZONE_CONTAINER* NewArea; + // Move the newly created polygons to new areas, removing them from the current area for( int ip = 1; ip < n_poly; ip++ ) { - // create new copper area and copy poly into it - CPolyLine* new_p = (*pa)[ip - 1]; + // Create new copper area and copy poly into it + SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( ip ) ); NewArea = AddArea( aNewZonesList, aCurrArea->GetNetCode(), aCurrArea->GetLayer(), - wxPoint(0, 0), CPolyLine::NO_HATCH ); + wxPoint(0, 0), aCurrArea->GetHatchStyle() ); // remove the poly that was automatically created for the new area // and replace it with a poly from NormalizeAreaOutlines delete NewArea->Outline(); NewArea->SetOutline( new_p ); - NewArea->Outline()->Hatch(); + NewArea->Hatch(); NewArea->SetLocalFlags( 1 ); } - } - delete pa; + SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) ); + delete aCurrArea->Outline(); + aCurrArea->SetOutline( new_p ); + } } - curr_polygon->Hatch(); + aCurrArea->Hatch(); return true; } diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index a716184ab9..49c06c657e 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -53,7 +53,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) : BOARD_CONNECTED_ITEM( aBoard, PCB_ZONE_AREA_T ) { - m_CornerSelection = -1; + m_CornerSelection = nullptr; // no corner is selected m_IsFilled = false; // fill status : true when the zone is filled m_FillMode = 0; // How to fill areas: 0 = use filled polygons, != 0 fill with segments m_priority = 0; @@ -65,7 +65,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) : SetDoNotAllowTracks( true ); // has meaning only if m_isKeepout == true m_cornerRadius = 0; SetLocalFlags( 0 ); // flags tempoarry used in zone calculations - m_Poly = new CPolyLine(); // Outlines + m_Poly = new SHAPE_POLY_SET(); // Outlines aBoard->GetZoneSettings().ExportSetting( *this ); } @@ -77,10 +77,10 @@ ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) : // Should the copy be on the same net? SetNetCode( aZone.GetNetCode() ); - m_Poly = new CPolyLine( *aZone.m_Poly ); + m_Poly = new SHAPE_POLY_SET( *aZone.m_Poly ); - // For corner moving, corner index to drag, or -1 if no selection - m_CornerSelection = -1; + // For corner moving, corner index to drag, or nullptr if no selection + m_CornerSelection = nullptr; m_IsFilled = aZone.m_IsFilled; m_ZoneClearance = aZone.m_ZoneClearance; // clearance value m_ZoneMinThickness = aZone.m_ZoneMinThickness; @@ -101,6 +101,10 @@ ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) : m_cornerSmoothingType = aZone.m_cornerSmoothingType; m_cornerRadius = aZone.m_cornerRadius; + m_hatchStyle = aZone.m_hatchStyle; + m_hatchPitch = aZone.m_hatchPitch; + m_HatchLines = aZone.m_HatchLines; + SetLocalFlags( aZone.GetLocalFlags() ); } @@ -109,9 +113,11 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther ) { BOARD_CONNECTED_ITEM::operator=( aOther ); - m_Poly->RemoveAllContours(); - m_Poly->Copy( aOther.m_Poly ); // copy outlines - m_CornerSelection = -1; // for corner moving, corner index to drag or -1 if no selection + // Replace the outlines for aOther outlines. + delete m_Poly; + m_Poly = new SHAPE_POLY_SET( *aOther.m_Poly ); + + m_CornerSelection = nullptr; // for corner moving, corner index to (null if no selection) m_ZoneClearance = aOther.m_ZoneClearance; // clearance value m_ZoneMinThickness = aOther.m_ZoneMinThickness; m_FillMode = aOther.m_FillMode; // filling mode (segments/polygons) @@ -119,9 +125,9 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther ) m_PadConnection = aOther.m_PadConnection; m_ThermalReliefGap = aOther.m_ThermalReliefGap; m_ThermalReliefCopperBridge = aOther.m_ThermalReliefCopperBridge; - m_Poly->SetHatchStyle( aOther.m_Poly->GetHatchStyle() ); - m_Poly->SetHatchPitch( aOther.m_Poly->GetHatchPitch() ); - m_Poly->m_HatchLines = aOther.m_Poly->m_HatchLines; // copy vector + SetHatchStyle( aOther.GetHatchStyle() ); + SetHatchPitch( aOther.GetHatchPitch() ); + m_HatchLines = aOther.m_HatchLines; // copy vector m_FilledPolysList.RemoveAllContours(); m_FilledPolysList.Append( aOther.m_FilledPolysList ); m_FillSegmList.clear(); @@ -134,7 +140,8 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther ) ZONE_CONTAINER::~ZONE_CONTAINER() { delete m_Poly; - m_Poly = NULL; + delete m_smoothedPoly; + delete m_CornerSelection; } @@ -159,9 +166,13 @@ bool ZONE_CONTAINER::UnFill() const wxPoint& ZONE_CONTAINER::GetPosition() const { - static const wxPoint dummy; + const WX_VECTOR_CONVERTER* pos; - return m_Poly ? GetCornerPosition( 0 ) : dummy; + // The retrieved vertex is a VECTOR2I. Casting it to a union WX_VECTOR_CONVERTER, we can later + // return the object shaped as a wxPoint. See the definition of the union in class_zone.h for + // more information on this hack. + pos = reinterpret_cast( &GetCornerPosition( 0 ) ); + return pos->wx; } @@ -195,38 +206,29 @@ void ZONE_CONTAINER::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE aDrawMod color.a = 0.588; // draw the lines - int i_start_contour = 0; std::vector lines; lines.reserve( (GetNumCorners() * 2) + 2 ); - for( int ic = 0; ic < GetNumCorners(); ic++ ) + // Iterate through the segments of the outline + for( auto iterator = m_Poly->IterateSegmentsWithHoles(); iterator; iterator++ ) { - seg_start = GetCornerPosition( ic ) + offset; + // Create the segment + SEG segment = *iterator; - if( !m_Poly->m_CornersList.IsEndContour( ic ) && ic < GetNumCorners() - 1 ) - { - seg_end = GetCornerPosition( ic + 1 ) + offset; - } - else - { - seg_end = GetCornerPosition( i_start_contour ) + offset; - i_start_contour = ic + 1; - } - - lines.push_back( seg_start ); - lines.push_back( seg_end ); + lines.push_back( static_cast( segment.A ) + offset ); + lines.push_back( static_cast( segment.B ) + offset ); } GRLineArray( panel->GetClipBox(), DC, lines, 0, color ); // draw hatches lines.clear(); - lines.reserve( (m_Poly->m_HatchLines.size() * 2) + 2 ); + lines.reserve( (m_HatchLines.size() * 2) + 2 ); - for( unsigned ic = 0; ic < m_Poly->m_HatchLines.size(); ic++ ) + for( unsigned ic = 0; ic < m_HatchLines.size(); ic++ ) { - seg_start = m_Poly->m_HatchLines[ic].m_Start + offset; - seg_end = m_Poly->m_HatchLines[ic].m_End + offset; + seg_start = static_cast( m_HatchLines[ic].A ) + offset; + seg_end = static_cast( m_HatchLines[ic].B ) + offset; lines.push_back( seg_start ); lines.push_back( seg_end ); } @@ -357,7 +359,7 @@ const EDA_RECT ZONE_CONTAINER::GetBoundingBox() const for( int i = 0; i( GetCornerPosition( i ) ); ymax = std::max( ymax, corner.y ); xmax = std::max( xmax, corner.x ); @@ -391,45 +393,63 @@ void ZONE_CONTAINER::DrawWhileCreateOutline( EDA_DRAW_PANEL* panel, wxDC* DC, color = COLOR4D( DARKDARKGRAY ); } - // draw the lines - wxPoint start_contour_pos = GetCornerPosition( 0 ); - int icmax = GetNumCorners() - 1; + // Object to iterate through the corners of the outlines + SHAPE_POLY_SET::ITERATOR iterator = m_Poly->Iterate(); - for( int ic = 0; ic <= icmax; ic++ ) + // Segment start and end + VECTOR2I seg_start, seg_end; + + // Remember the first point of this contour + VECTOR2I contour_first_point = *iterator; + + // Iterate through all the corners of the outlines and build the segments to draw + while( iterator ) { - int xi = GetCornerPosition( ic ).x; - int yi = GetCornerPosition( ic ).y; - int xf, yf; + // Get the first point of the current segment + seg_start = *iterator; - if( !m_Poly->m_CornersList.IsEndContour( ic ) && ic < icmax ) + // Get the last point of the current segment, handling the case where the end of the + // contour is reached, when the last point of the segment is the first point of the + // contour + if( !iterator.IsEndContour() ) { - is_close_segment = false; - xf = GetCornerPosition( ic + 1 ).x; - yf = GetCornerPosition( ic + 1 ).y; + // Set GR mode to default + current_gr_mode = draw_mode; - if( m_Poly->m_CornersList.IsEndContour( ic + 1 ) || (ic == icmax - 1) ) + SHAPE_POLY_SET::ITERATOR iterator_copy = iterator; + iterator_copy++; + if( iterator_copy.IsEndContour() ) current_gr_mode = GR_XOR; - else - current_gr_mode = draw_mode; + + is_close_segment = false; + + iterator++; + seg_end = *iterator; } - else // Draw the line from last corner to the first corner of the current contour + else { is_close_segment = true; - current_gr_mode = GR_XOR; - xf = start_contour_pos.x; - yf = start_contour_pos.y; - // Prepare the next contour for drawing, if exists - if( ic < icmax ) - start_contour_pos = GetCornerPosition( ic + 1 ); + seg_end = contour_first_point; + + // Reassign first point of the contour to the next contour start + iterator++; + + if( iterator ) + contour_first_point = *iterator; + + // Set GR mode to XOR + current_gr_mode = GR_XOR; } GRSetDrawMode( DC, current_gr_mode ); if( is_close_segment ) - GRLine( panel->GetClipBox(), DC, xi, yi, xf, yf, 0, WHITE ); + GRLine( panel->GetClipBox(), DC, seg_start.x, seg_start.y, seg_end.x, seg_end.y, 0, + WHITE ); else - GRLine( panel->GetClipBox(), DC, xi, yi, xf, yf, 0, color ); + GRLine( panel->GetClipBox(), DC, seg_start.x, seg_start.y, seg_end.x, seg_end.y, 0, + color ); } } @@ -462,90 +482,109 @@ void ZONE_CONTAINER::SetCornerRadius( unsigned int aRadius ) bool ZONE_CONTAINER::HitTest( const wxPoint& aPosition ) const { - if( HitTestForCorner( aPosition ) >= 0 ) - return true; - - if( HitTestForEdge( aPosition ) >= 0 ) - return true; - - return false; + return HitTestForCorner( aPosition ) || HitTestForEdge( aPosition ); } + void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition ) { - m_CornerSelection = HitTestForCorner( aPosition ); + SHAPE_POLY_SET::VERTEX_INDEX corner; - if( m_CornerSelection < 0 ) - m_CornerSelection = HitTestForEdge( aPosition ); + // If there is some corner to be selected, assign it to m_CornerSelection + if( HitTestForCorner( aPosition, corner ) || HitTestForEdge( aPosition, corner ) ) + { + if( m_CornerSelection == nullptr ) + m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX; + + *m_CornerSelection = corner; + } } - // Zones outlines have no thickness, so it Hit Test functions // we must have a default distance between the test point // and a corner or a zone edge: #define MAX_DIST_IN_MM 0.25 -int ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) const +bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos, + SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const { int distmax = Millimeter2iu( MAX_DIST_IN_MM ); - return m_Poly->HitTestForCorner( refPos, distmax ); + + return m_Poly->CollideVertex( VECTOR2I( refPos ), aCornerHit, distmax ); } -int ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) const +bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) const +{ + SHAPE_POLY_SET::VERTEX_INDEX dummy; + return HitTestForCorner( refPos, dummy ); +} + + +bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos, + SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const { int distmax = Millimeter2iu( MAX_DIST_IN_MM ); - return m_Poly->HitTestForEdge( refPos, distmax ); + + return m_Poly->CollideEdge( VECTOR2I( refPos ), aCornerHit, distmax ); +} + + +bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) const +{ + SHAPE_POLY_SET::VERTEX_INDEX dummy; + return HitTestForEdge( refPos, dummy ); } bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const { - EDA_RECT arect = aRect; - arect.Inflate( aAccuracy ); - EDA_RECT bbox = m_Poly->GetBoundingBox(); + // Convert to BOX2I + BOX2I aBox = aRect; + aBox.Inflate( aAccuracy ); + BOX2I bbox = m_Poly->BBox(); bbox.Normalize(); if( aContained ) - return arect.Contains( bbox ); - else // Test for intersection between aRect and the polygon + return aBox.Contains( bbox ); + else // Test for intersection between aBox and the polygon // For a polygon, using its bounding box has no sense here { - // Fast test: if aRect is outside the polygon bounding box, + // Fast test: if aBox is outside the polygon bounding box, // rectangles cannot intersect - if( ! bbox.Intersects( arect ) ) + if( ! bbox.Intersects( aBox ) ) return false; - // aRect is inside the polygon bounding box, + // aBox is inside the polygon bounding box, // and can intersect the polygon: use a fine test. - // aRect intersects the polygon if at least one aRect corner + // aBox intersects the polygon if at least one aBox corner // is inside the polygon - wxPoint corner = arect.GetOrigin(); + wxPoint corner = static_cast( aBox.GetOrigin() ); if( HitTestInsideZone( corner ) ) return true; - corner.x = arect.GetEnd().x; + corner.x = aBox.GetEnd().x; if( HitTestInsideZone( corner ) ) return true; - corner = arect.GetEnd(); + corner = static_cast( aBox.GetEnd() ); if( HitTestInsideZone( corner ) ) return true; - corner.x = arect.GetOrigin().x; + corner.x = aBox.GetOrigin().x; if( HitTestInsideZone( corner ) ) return true; - // No corner inside arect, but outlines can intersect arect - // if one of outline corners is inside arect - int count = m_Poly->GetCornersCount(); + // No corner inside aBox, but outlines can intersect aBox + // if one of outline corners is inside aBox + int count = m_Poly->TotalVertices(); for( int ii =0; ii < count; ii++ ) { - if( arect.Contains( m_Poly->GetPos( ii ) ) ) + if( aBox.Contains( m_Poly->Vertex( ii ) ) ) return true; } @@ -595,9 +634,8 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) // Display Cutout instead of Outline for holes inside a zone // i.e. when num contour !=0 - int ncont = m_Poly->GetContour( m_CornerSelection ); - - if( ncont ) + // Check whether the selected corner is in a hole; i.e., in any contour but the first one. + if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 ) msg << wxT( " " ) << _( "(Cutout)" ); aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) ); @@ -648,7 +686,7 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), GetLayerName(), BROWN ) ); - msg.Printf( wxT( "%d" ), (int) m_Poly->m_CornersList.GetCornersCount() ); + msg.Printf( wxT( "%d" ), (int) m_Poly->TotalVertices() ); aList.push_back( MSG_PANEL_ITEM( _( "Corners" ), msg, BLUE ) ); if( m_FillMode ) @@ -659,7 +697,7 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) aList.push_back( MSG_PANEL_ITEM( _( "Fill Mode" ), msg, BROWN ) ); // Useful for statistics : - msg.Printf( wxT( "%d" ), (int) m_Poly->m_HatchLines.size() ); + msg.Printf( wxT( "%d" ), (int) m_HatchLines.size() ); aList.push_back( MSG_PANEL_ITEM( _( "Hatch Lines" ), msg, BLUE ) ); if( !m_FilledPolysList.IsEmpty() ) @@ -675,12 +713,9 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) void ZONE_CONTAINER::Move( const wxPoint& offset ) { /* move outlines */ - for( unsigned ii = 0; ii < m_Poly->m_CornersList.GetCornersCount(); ii++ ) - { - SetCornerPosition( ii, GetCornerPosition( ii ) + offset ); - } + m_Poly->Move( VECTOR2I( offset ) ); - m_Poly->Hatch(); + Hatch(); m_FilledPolysList.Move( VECTOR2I( offset.x, offset.y ) ); @@ -694,23 +729,10 @@ void ZONE_CONTAINER::Move( const wxPoint& offset ) void ZONE_CONTAINER::MoveEdge( const wxPoint& offset, int aEdge ) { - // Move the start point of the selected edge: - SetCornerPosition( aEdge, GetCornerPosition( aEdge ) + offset ); + m_Poly->Edge( aEdge ).A += VECTOR2I( offset ); + m_Poly->Edge( aEdge ).B += VECTOR2I( offset ); - // Move the end point of the selected edge: - if( m_Poly->m_CornersList.IsEndContour( aEdge ) || aEdge == GetNumCorners() - 1 ) - { - int icont = m_Poly->GetContour( aEdge ); - aEdge = m_Poly->GetContourStart( icont ); - } - else - { - aEdge++; - } - - SetCornerPosition( aEdge, GetCornerPosition( aEdge ) + offset ); - - m_Poly->Hatch(); + Hatch(); } @@ -718,18 +740,18 @@ void ZONE_CONTAINER::Rotate( const wxPoint& centre, double angle ) { wxPoint pos; - for( unsigned ic = 0; ic < m_Poly->m_CornersList.GetCornersCount(); ic++ ) + for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ ) { - pos = m_Poly->m_CornersList.GetPos( ic ); + pos = static_cast( *iterator ); RotatePoint( &pos, centre, angle ); - m_Poly->SetX( ic, pos.x ); - m_Poly->SetY( ic, pos.y ); + iterator->x = pos.x; + iterator->y = pos.y; } - m_Poly->Hatch(); + Hatch(); /* rotate filled areas: */ - for( SHAPE_POLY_SET::ITERATOR ic = m_FilledPolysList.Iterate(); ic; ++ic ) + for( auto ic = m_FilledPolysList.Iterate(); ic; ++ic ) RotatePoint( &ic->x, &ic->y, centre.x, centre.y, angle ); for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ ) @@ -750,15 +772,15 @@ void ZONE_CONTAINER::Flip( const wxPoint& aCentre ) void ZONE_CONTAINER::Mirror( const wxPoint& mirror_ref ) { - for( unsigned ic = 0; ic < m_Poly->m_CornersList.GetCornersCount(); ic++ ) + for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ ) { - int py = mirror_ref.y - m_Poly->m_CornersList.GetY( ic ); - m_Poly->m_CornersList.SetY( ic, py + mirror_ref.y ); + int py = mirror_ref.y - iterator->y; + iterator->y = py + mirror_ref.y; } - m_Poly->Hatch(); + Hatch(); - for( SHAPE_POLY_SET::ITERATOR ic = m_FilledPolysList.Iterate(); ic; ++ic ) + for( auto ic = m_FilledPolysList.Iterate(); ic; ++ic ) { int py = mirror_ref.y - ic->y; ic->y = py + mirror_ref.y; @@ -786,29 +808,29 @@ void ZONE_CONTAINER::AddPolygon( std::vector< wxPoint >& aPolygon ) if( aPolygon.empty() ) return; + SHAPE_LINE_CHAIN outline; + + // Create an outline and populate it with the points of aPolygon for( unsigned i = 0; i < aPolygon.size(); i++ ) { - if( i == 0 ) - m_Poly->Start( GetLayer(), aPolygon[i].x, aPolygon[i].y, GetHatchStyle() ); - else - AppendCorner( aPolygon[i] ); + outline.Append( VECTOR2I( aPolygon[i] ) ); } - m_Poly->CloseLastContour(); + outline.SetClosed( true ); + + // Add the outline as a new polygon in the polygon set + m_Poly->AddOutline( outline ); } - - wxString ZONE_CONTAINER::GetSelectMenuText() const { wxString text; NETINFO_ITEM* net; BOARD* board = GetBoard(); - int ncont = m_Poly->GetContour( m_CornerSelection ); - - if( ncont ) - text << wxT( " " ) << _( "(Cutout)" ); + // Check whether the selected contour is a hole (contour index > 0) + if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 ) + text << wxT( " " ) << _( "(Cutout)" ); if( GetIsKeepout() ) text << wxT( " " ) << _( "(Keepout)" ); @@ -850,6 +872,204 @@ wxString ZONE_CONTAINER::GetSelectMenuText() const } +int ZONE_CONTAINER::GetHatchPitch() const +{ + return m_hatchPitch; +} + + +void ZONE_CONTAINER::SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch ) +{ + SetHatchPitch( aHatchPitch ); + m_hatchStyle = (ZONE_CONTAINER::HATCH_STYLE) aHatchStyle; + + if( aRebuildHatch ) + Hatch(); +} + + +void ZONE_CONTAINER::SetHatchPitch( int aPitch ) +{ + m_hatchPitch = aPitch; +} + + +void ZONE_CONTAINER::UnHatch() +{ + m_HatchLines.clear(); +} + + +// Creates hatch lines inside the outline of the complex polygon +// sort function used in ::Hatch to sort points by descending wxPoint.x values +bool sortEndsByDescendingX( const VECTOR2I& ref, const VECTOR2I& tst ) +{ + return tst.x < ref.x; +} + +// Implementation copied from old CPolyLine +void ZONE_CONTAINER::Hatch() +{ + UnHatch(); + + if( m_hatchStyle == NO_HATCH || m_hatchPitch == 0 || m_Poly->IsEmpty() ) + return; + + // define range for hatch lines + int min_x = m_Poly->Vertex( 0 ).x; + int max_x = m_Poly->Vertex( 0 ).x; + int min_y = m_Poly->Vertex( 0 ).y; + int max_y = m_Poly->Vertex( 0 ).y; + + for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ ) + { + if( iterator->x < min_x ) + min_x = iterator->x; + + if( iterator->x > max_x ) + max_x = iterator->x; + + if( iterator->y < min_y ) + min_y = iterator->y; + + if( iterator->y > max_y ) + max_y = iterator->y; + } + + // Calculate spacing between 2 hatch lines + int spacing; + + if( m_hatchStyle == DIAGONAL_EDGE ) + spacing = m_hatchPitch; + else + spacing = m_hatchPitch * 2; + + // set the "length" of hatch lines (the length on horizontal axis) + int hatch_line_len = m_hatchPitch; + + // To have a better look, give a slope depending on the layer + LAYER_NUM layer = GetLayer(); + int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1 + double slope = 0.707106 * slope_flag; // 45 degrees slope + int max_a, min_a; + + if( slope_flag == 1 ) + { + max_a = KiROUND( max_y - slope * min_x ); + min_a = KiROUND( min_y - slope * max_x ); + } + else + { + max_a = KiROUND( max_y - slope * max_x ); + min_a = KiROUND( min_y - slope * min_x ); + } + + min_a = (min_a / spacing) * spacing; + + // calculate an offset depending on layer number, + // for a better look of hatches on a multilayer board + int offset = (layer * 7) / 8; + min_a += offset; + + // loop through hatch lines + #define MAXPTS 200 // Usually we store only few values per one hatch line + // depending on the complexity of the zone outline + + static std::vector pointbuffer; + pointbuffer.clear(); + pointbuffer.reserve( MAXPTS + 2 ); + + for( int a = min_a; a < max_a; a += spacing ) + { + // get intersection points for this hatch line + + // Note: because we should have an even number of intersections with the + // current hatch line and the zone outline (a closed polygon, + // or a set of closed polygons), if an odd count is found + // we skip this line (should not occur) + pointbuffer.clear(); + + // Iterate through all vertices + for( auto iterator = m_Poly->IterateSegmentsWithHoles(); iterator; iterator++ ) + { + double x, y, x2, y2; + int ok; + + SEG segment = *iterator; + + ok = FindLineSegmentIntersection( a, slope, + segment.A.x, segment.A.y, + segment.B.x, segment.B.y, + &x, &y, &x2, &y2 ); + + if( ok ) + { + VECTOR2I point( KiROUND( x ), KiROUND( y ) ); + pointbuffer.push_back( point ); + } + + if( ok == 2 ) + { + VECTOR2I point( KiROUND( x2 ), KiROUND( y2 ) ); + pointbuffer.push_back( point ); + } + + if( pointbuffer.size() >= MAXPTS ) // overflow + { + wxASSERT( 0 ); + break; + } + } + + // ensure we have found an even intersection points count + // because intersections are the ends of segments + // inside the polygon(s) and a segment has 2 ends. + // if not, this is a strange case (a bug ?) so skip this hatch + if( pointbuffer.size() % 2 != 0 ) + continue; + + // sort points in order of descending x (if more than 2) to + // ensure the starting point and the ending point of the same segment + // are stored one just after the other. + if( pointbuffer.size() > 2 ) + sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX ); + + // creates lines or short segments inside the complex polygon + for( unsigned ip = 0; ip < pointbuffer.size(); ip += 2 ) + { + int dx = pointbuffer[ip + 1].x - pointbuffer[ip].x; + + // Push only one line for diagonal hatch, + // or for small lines < twice the line length + // else push 2 small lines + if( m_hatchStyle == DIAGONAL_FULL || fabs( dx ) < 2 * hatch_line_len ) + { + m_HatchLines.push_back( SEG( pointbuffer[ip], pointbuffer[ip + 1] ) ); + } + else + { + double dy = pointbuffer[ip + 1].y - pointbuffer[ip].y; + slope = dy / dx; + + if( dx > 0 ) + dx = hatch_line_len; + else + dx = -hatch_line_len; + + int x1 = KiROUND( pointbuffer[ip].x + dx ); + int x2 = KiROUND( pointbuffer[ip + 1].x - dx ); + int y1 = KiROUND( pointbuffer[ip].y + dx * slope ); + int y2 = KiROUND( pointbuffer[ip + 1].y - dx * slope ); + + m_HatchLines.push_back(SEG(pointbuffer[ip].x, pointbuffer[ip].y, x1, y1)); + + m_HatchLines.push_back( SEG( pointbuffer[ip+1].x, pointbuffer[ip+1].y, x2, y2 ) ); + } + } + } +} + + BITMAP_DEF ZONE_CONTAINER::GetMenuImage() const { return add_zone_xpm; diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index d4e08d2886..20964c6cbb 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -1,4 +1,3 @@ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -38,6 +37,7 @@ #include #include #include +#include #include @@ -79,6 +79,11 @@ class ZONE_CONTAINER : public BOARD_CONNECTED_ITEM { public: + /** + * Zone hatch styles + */ + typedef enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE } HATCH_STYLE; + ZONE_CONTAINER( BOARD* parent ); ZONE_CONTAINER( const ZONE_CONTAINER& aZone ); @@ -88,6 +93,14 @@ public: /** * Function GetPosition + * + * Returns a reference to the first corner of the polygon set. + * + * \warning The implementation of this function relies on the fact that wxPoint and VECTOR2I + * have the same layout. If you intend to use the returned reference directly, please note + * that you are _only_ allowed to use members x and y. Any use on anything that is not one of + * these members will have undefined behaviour. + * * @return a wxPoint, position of the first point of the outline */ const wxPoint& GetPosition() const override; @@ -196,8 +209,30 @@ public: int GetMinThickness() const { return m_ZoneMinThickness; } void SetMinThickness( int aMinThickness ) { m_ZoneMinThickness = aMinThickness; } - int GetSelectedCorner() const { return m_CornerSelection; } - void SetSelectedCorner( int aCorner ) { m_CornerSelection = aCorner; } + int GetSelectedCorner() const + { + // Transform relative indices to global index + int globalIndex; + m_Poly->GetGlobalIndex( *m_CornerSelection, globalIndex ); + + return globalIndex; + } + + void SetSelectedCorner( int aCorner ) + { + SHAPE_POLY_SET::VERTEX_INDEX selectedCorner; + + // If the global index of the corner is correct, assign it to m_CornerSelection + if( m_Poly->GetRelativeIndices( aCorner, &selectedCorner ) ) + { + if( m_CornerSelection == nullptr ) + m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX; + + *m_CornerSelection = selectedCorner; + } + else + throw( std::out_of_range( "aCorner-th vertex does not exist" ) ); + } /// // Like HitTest but selects the current corner to be operated on @@ -209,10 +244,10 @@ public: std::vector & FillSegments() { return m_FillSegmList; } const std::vector & FillSegments() const { return m_FillSegmList; } - CPolyLine* Outline() { return m_Poly; } - const CPolyLine* Outline() const { return const_cast< CPolyLine* >( m_Poly ); } + SHAPE_POLY_SET* Outline() { return m_Poly; } + const SHAPE_POLY_SET* Outline() const { return const_cast< SHAPE_POLY_SET* >( m_Poly ); } - void SetOutline( CPolyLine* aOutline ) { m_Poly = aOutline; } + void SetOutline( SHAPE_POLY_SET* aOutline ) { m_Poly = aOutline; } /** * Function HitTest @@ -231,7 +266,7 @@ public: */ bool HitTestInsideZone( const wxPoint& aPosition ) const { - return m_Poly->TestPointInside( aPosition.x, aPosition.y ); + return m_Poly->Contains( VECTOR2I( aPosition ), 0 ); } /** @@ -310,25 +345,45 @@ public: * if both aMinClearanceValue = 0 and aUseNetClearance = false: create the zone outline polygon. */ void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, - int aMinClearanceValue, - bool aUseNetClearance ); + int aMinClearanceValue, + bool aUseNetClearance ); /** * Function HitTestForCorner - * tests if the given wxPoint near a corner - * Set m_CornerSelection to -1 if nothing found, or index of corner - * @return true if found - * @param refPos : A wxPoint to test + * tests if the given wxPoint is near a corner. + * @param refPos is the wxPoint to test. + * @param aCornerHit [out] is the index of the closest vertex found, useless when return + * value is false. + * @return bool - true if some corner was found to be closer to refPos than aClearance; false + * otherwise. */ - int HitTestForCorner( const wxPoint& refPos ) const; + bool HitTestForCorner( const wxPoint& refPos, SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const; + + /** + * Function HitTestForCorner + * tests if the given wxPoint is near a corner. + * @param refPos is the wxPoint to test. + * @return bool - true if some corner was found to be closer to refPos than aClearance; false + * otherwise. + */ + bool HitTestForCorner( const wxPoint& refPos ) const; /** * Function HitTestForEdge * tests if the given wxPoint is near a segment defined by 2 corners. - * Set m_CornerSelection to -1 if nothing found, or index of the starting corner of vertice - * @return true if found - * @param refPos : A wxPoint to test + * @param refPos is the wxPoint to test. + * @param aCornerHit [out] is the index of the closest vertex found, useless when return + * value is false. + * @return bool - true if some edge was found to be closer to refPos than aClearance. */ - int HitTestForEdge( const wxPoint& refPos ) const; + bool HitTestForEdge( const wxPoint& refPos, SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const; + + /** + * Function HitTestForEdge + * tests if the given wxPoint is near a segment defined by 2 corners. + * @param refPos is the wxPoint to test. + * @return bool - true if some edge was found to be closer to refPos than aClearance. + */ + bool HitTestForEdge( const wxPoint& refPos ) const; /** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect, * bool aContained = true, int aAccuracy ) const @@ -410,7 +465,37 @@ public: int GetNumCorners( void ) const { - return m_Poly->GetCornersCount(); + return m_Poly->TotalVertices(); + } + + /** + * Function Iterate + * returns an iterator to visit all points of the zone's main outline without holes. + * @return SHAPE_POLY_SET::ITERATOR - an iterator to visit the zone vertices without holes. + */ + SHAPE_POLY_SET::ITERATOR Iterate() + { + return m_Poly->Iterate(); + } + + /** + * Function IterateWithHoles + * returns an iterator to visit all points of the zone's main outline with holes. + * @return SHAPE_POLY_SET::ITERATOR - an iterator to visit the zone vertices with holes. + */ + SHAPE_POLY_SET::ITERATOR IterateWithHoles() + { + return m_Poly->IterateWithHoles(); + } + + /** + * Function CIterateWithHoles + * returns an iterator to visit all points of the zone's main outline with holes. + * @return SHAPE_POLY_SET::ITERATOR - an iterator to visit the zone vertices with holes. + */ + SHAPE_POLY_SET::CONST_ITERATOR CIterateWithHoles() const + { + return m_Poly->CIterateWithHoles(); } void RemoveAllContours( void ) @@ -418,30 +503,62 @@ public: m_Poly->RemoveAllContours(); } - const wxPoint& GetCornerPosition( int aCornerIndex ) const + const VECTOR2I& GetCornerPosition( int aCornerIndex ) const { - return m_Poly->GetPos( aCornerIndex ); + SHAPE_POLY_SET::VERTEX_INDEX index; + + // Convert global to relative indices + if( !m_Poly->GetRelativeIndices( aCornerIndex, &index ) ) + throw( std::out_of_range( "aCornerIndex-th vertex does not exist" ) ); + + return m_Poly->CVertex( index ); } void SetCornerPosition( int aCornerIndex, wxPoint new_pos ) { - m_Poly->SetX( aCornerIndex, new_pos.x ); - m_Poly->SetY( aCornerIndex, new_pos.y ); + SHAPE_POLY_SET::VERTEX_INDEX relativeIndices; + + // Convert global to relative indices + if( m_Poly->GetRelativeIndices( aCornerIndex, &relativeIndices ) ) + { + m_Poly->Vertex( relativeIndices ).x = new_pos.x; + m_Poly->Vertex( relativeIndices ).y = new_pos.y; + } + else + throw( std::out_of_range( "aCornerIndex-th vertex does not exist" ) ); } - void AppendCorner( wxPoint position ) + /** + * Function NewHole + * creates a new hole on the zone; i.e., a new contour on the zone's outline. + */ + void NewHole() { - m_Poly->AppendCorner( position.x, position.y ); + m_Poly->NewHole(); } - int GetHatchStyle() const + /** + * Function AppendCorner + * @param position is the position of the new corner. + * @param aAllowDuplication is a flag to indicate whether it is allowed to add this corner + * even if it is duplicated. + */ + void AppendCorner( wxPoint position, bool aAllowDuplication = false ) { - return m_Poly->GetHatchStyle(); + if( m_Poly->OutlineCount() == 0 ) + m_Poly->NewOutline(); + + m_Poly->Append( position.x, position.y, -1, -1, aAllowDuplication ); } - void SetHatchStyle( CPolyLine::HATCH_STYLE aStyle ) + HATCH_STYLE GetHatchStyle() const { - m_Poly->SetHatchStyle( aStyle ); + return m_hatchStyle; + } + + void SetHatchStyle( HATCH_STYLE aStyle ) + { + m_hatchStyle = aStyle; } /** @@ -485,9 +602,9 @@ public: * Function GetSmoothedPoly * returns a pointer to the corner-smoothed version of * m_Poly if it exists, otherwise it returns m_Poly. - * @return CPolyLine* - pointer to the polygon. + * @return SHAPE_POLY_SET* - pointer to the polygon. */ - CPolyLine* GetSmoothedPoly() const + SHAPE_POLY_SET* GetSmoothedPoly() const { if( m_smoothedPoly ) return m_smoothedPoly; @@ -534,6 +651,58 @@ public: void SetDoNotAllowVias( bool aEnable ) { m_doNotAllowVias = aEnable; } void SetDoNotAllowTracks( bool aEnable ) { m_doNotAllowTracks = aEnable; } + /** + * Hatch related methods + */ + + /** + * Function GetHatchPitch + * @return int - the zone hatch pitch in iu. + */ + int GetHatchPitch() const; + + /** + * Function GetDefaultHatchPitchMils + * @return int - the default hatch pitch in mils. + * + * \todo This value is hardcoded, but it should be user configurable. + */ + static int GetDefaultHatchPitchMils() { return 20; } + + /** + * Function SetHatch + * sets all hatch parameters for the zone. + * @param aHatchStyle is the style of the hatch, specified as one of HATCH_STYLE possible + * values. + * @param aHatchPitch is the hatch pitch in iu. + * @param aRebuildHatch is a flag to indicate whether to re-hatch after having set the + * previous parameters. + */ + void SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch ); + + /** + * Function SetHatchPitch + * sets the hatch pitch parameter for the zone. + * @param aPitch is the hatch pitch in iu. + */ + void SetHatchPitch( int aPitch ); + + /** + * Function UnHatch + * clears the zone's hatch. + */ + void UnHatch(); + + /** + * Function Hatch + * computes the hatch lines depending on the hatch parameters and stores it in the zone's + * attribute m_HatchLines. + */ + void Hatch(); + + const std::vector& GetHatchLines() const { return m_HatchLines; } + + #if defined(DEBUG) virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } #endif @@ -543,8 +712,8 @@ public: private: void buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ); - CPolyLine* m_Poly; ///< Outline of the zone. - CPolyLine* m_smoothedPoly; // Corner-smoothed version of m_Poly + SHAPE_POLY_SET* m_Poly; ///< Outline of the zone. + SHAPE_POLY_SET* m_smoothedPoly; // Corner-smoothed version of m_Poly int m_cornerSmoothingType; unsigned int m_cornerRadius; @@ -587,8 +756,8 @@ private: /// How to fill areas: 0 => use filled polygons, 1 => fill with segments. int m_FillMode; - /// The index of the corner being moved or -1 if no corner is selected. - int m_CornerSelection; + /// The index of the corner being moved or nullptr if no corner is selected. + SHAPE_POLY_SET::VERTEX_INDEX* m_CornerSelection; /// Variable used in polygon calculations. int m_localFlgs; @@ -602,14 +771,52 @@ private: * from outlines (m_Poly) but unlike m_Poly these filled polygons have no hole * (they are all in one piece) In very simple cases m_FilledPolysList is same * as m_Poly. In less simple cases (when m_Poly has holes) m_FilledPolysList is - - - * a polygon equivalent to m_Poly, without holes but with extra outline segment * connecting "holes" with external main outline. In complex cases an outline * described by m_Poly can have many filled areas */ - SHAPE_POLY_SET m_FilledPolysList; + SHAPE_POLY_SET m_FilledPolysList; + + HATCH_STYLE m_hatchStyle; // hatch style, see enum above + int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines + std::vector m_HatchLines; // hatch lines + + /** + * Union to handle conversion between references to wxPoint and to VECTOR2I. + * + * The function GetPosition(), that returns a reference to a wxPoint, needs some existing + * wxPoint object that it can point to. The header of this function cannot be changed, as it + * overrides the function from the base class BOARD_ITEM. This made sense when ZONE_CONTAINER + * was implemented using the legacy CPolyLine class, that worked with wxPoints. However, + * m_Poly is now a SHAPE_POLY_SET, whose corners are objects of type VECTOR2I, not wxPoint. + * Thus, we cannot directly reference the first corner of m_Poly, so a modified version of it + * that can be read as a wxPoint needs to be handled. + * Taking advantage of the fact that both wxPoint and VECTOR2I have the same memory layout + * (two integers: x, y), this union let us convert a reference to a VECTOR2I into a reference + * to a wxPoint. + * + * The idea is the following: in GetPosition(), m_Poly->GetCornerPosition( 0 ) returns a + * reference to the first corner of the polygon set. If we retrieve its memory direction, we + * can tell the compiler to cast that pointer to a WX_VECTOR_CONVERTER pointer. We can finally + * shape that memory layout as a wxPoint picking the wx member of the union. + * + * Although this solution is somewhat unstable, as it relies on the fact that the memory + * layout is exactly the same, it is the best attempt to keep backwards compatibility while + * using the new SHAPE_POLY_SET. + */ + typedef union { + wxPoint wx; + VECTOR2I vector; + } WX_VECTOR_CONVERTER; + + // Sanity check: assure that the conversion VECTOR2I->wxPoint using the previous union is + // correct, making sure that the access for x and y attributes is still safe. + static_assert(offsetof(wxPoint,x) == offsetof(VECTOR2I,x), + "wxPoint::x and VECTOR2I::x have different offsets"); + + static_assert(offsetof(wxPoint,y) == offsetof(VECTOR2I,y), + "wxPoint::y and VECTOR2I::y have different offsets"); + }; diff --git a/pcbnew/class_zone_settings.cpp b/pcbnew/class_zone_settings.cpp index 9dc88ba7dd..7aa2fc7084 100644 --- a/pcbnew/class_zone_settings.cpp +++ b/pcbnew/class_zone_settings.cpp @@ -45,7 +45,7 @@ ZONE_SETTINGS::ZONE_SETTINGS() m_ZoneMinThickness = Mils2iu( ZONE_THICKNESS_MIL ); m_NetcodeSelection = 0; // Net code selection for the current zone m_CurrentZone_Layer = F_Cu; // Layer used to create the current zone - m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; // Option to show the zone area (outlines only, short hatches or full hatches + m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE; // Option to show the zone area (outlines only, short hatches or full hatches m_ArcToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; // Option to select number of segments to approximate a circle // ARC_APPROX_SEGMENTS_COUNT_LOW_DEF @@ -115,12 +115,11 @@ void ZONE_SETTINGS::ExportSetting( ZONE_CONTAINER& aTarget, bool aFullExport ) c aTarget.SetPriority( m_ZonePriority ); aTarget.SetNetCode( m_NetcodeSelection ); aTarget.SetLayer( m_CurrentZone_Layer ); - aTarget.Outline()->SetLayer( m_CurrentZone_Layer ); } // call SetHatch last, because hatch lines will be rebuilt, // using new parameters values - aTarget.Outline()->SetHatch( m_Zone_HatchingStyle, Mils2iu( 20 ), true ); + aTarget.SetHatch( m_Zone_HatchingStyle, Mils2iu( aTarget.GetDefaultHatchPitchMils() ), true ); } diff --git a/pcbnew/dialogs/dialog_copper_zones.cpp b/pcbnew/dialogs/dialog_copper_zones.cpp index b20b24106b..617dfd142d 100644 --- a/pcbnew/dialogs/dialog_copper_zones.cpp +++ b/pcbnew/dialogs/dialog_copper_zones.cpp @@ -214,15 +214,15 @@ void DIALOG_COPPER_ZONE::initDialog() switch( m_settings.m_Zone_HatchingStyle ) { - case CPolyLine::NO_HATCH: + case ZONE_CONTAINER::NO_HATCH: m_OutlineAppearanceCtrl->SetSelection( 0 ); break; - case CPolyLine::DIAGONAL_EDGE: + case ZONE_CONTAINER::DIAGONAL_EDGE: m_OutlineAppearanceCtrl->SetSelection( 1 ); break; - case CPolyLine::DIAGONAL_FULL: + case ZONE_CONTAINER::DIAGONAL_FULL: m_OutlineAppearanceCtrl->SetSelection( 2 ); break; } @@ -355,15 +355,15 @@ bool DIALOG_COPPER_ZONE::AcceptOptions( bool aPromptForErrors, bool aUseExportab switch( m_OutlineAppearanceCtrl->GetSelection() ) { case 0: - m_settings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH; + m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH; break; case 1: - m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; + m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE; break; case 2: - m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL; + m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL; break; } diff --git a/pcbnew/dialogs/dialog_keepout_area_properties.cpp b/pcbnew/dialogs/dialog_keepout_area_properties.cpp index 9da362d898..e5b8570654 100644 --- a/pcbnew/dialogs/dialog_keepout_area_properties.cpp +++ b/pcbnew/dialogs/dialog_keepout_area_properties.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -131,15 +132,15 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog() switch( m_zonesettings.m_Zone_HatchingStyle ) { - case CPolyLine::NO_HATCH: + case ZONE_CONTAINER::NO_HATCH: m_OutlineAppearanceCtrl->SetSelection( 0 ); break; - case CPolyLine::DIAGONAL_EDGE: + case ZONE_CONTAINER::DIAGONAL_EDGE: m_OutlineAppearanceCtrl->SetSelection( 1 ); break; - case CPolyLine::DIAGONAL_FULL: + case ZONE_CONTAINER::DIAGONAL_FULL: m_OutlineAppearanceCtrl->SetSelection( 2 ); break; } @@ -226,15 +227,15 @@ bool DIALOG_KEEPOUT_AREA_PROPERTIES::AcceptOptionsForKeepOut() switch( m_OutlineAppearanceCtrl->GetSelection() ) { case 0: - m_zonesettings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH; + m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH; break; case 1: - m_zonesettings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; + m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE; break; case 2: - m_zonesettings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL; + m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL; break; } diff --git a/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp b/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp index d55eea78f1..07ecbde00e 100644 --- a/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp +++ b/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp @@ -121,15 +121,15 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::Init() switch( m_settings.m_Zone_HatchingStyle ) { - case CPolyLine::NO_HATCH: + case ZONE_CONTAINER::NO_HATCH: m_OutlineAppearanceCtrl->SetSelection( 0 ); break; - case CPolyLine::DIAGONAL_EDGE: + case ZONE_CONTAINER::DIAGONAL_EDGE: m_OutlineAppearanceCtrl->SetSelection( 1 ); break; - case CPolyLine::DIAGONAL_FULL: + case ZONE_CONTAINER::DIAGONAL_FULL: m_OutlineAppearanceCtrl->SetSelection( 2 ); break; } @@ -202,15 +202,15 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::OnOkClick( wxCommandEvent& event ) switch( m_OutlineAppearanceCtrl->GetSelection() ) { case 0: - m_settings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH; + m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH; break; case 1: - m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; + m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE; break; case 2: - m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL; + m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL; break; } diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 86eeb7abaa..b7a86ab689 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -1,4 +1,3 @@ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -650,7 +649,7 @@ void DRC::testKeepoutAreas() if( segm->GetLayer() != area->GetLayer() ) continue; - if( area->Outline()->Distance( segm->GetStart(), segm->GetEnd(), + if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ), segm->GetWidth() ) == 0 ) { addMarkerToPcb( fillMarker( segm, NULL, @@ -821,7 +820,7 @@ bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg ) if( aRefSeg->GetLayer() != area->GetLayer() ) continue; - if( area->Outline()->Distance( aRefSeg->GetStart(), aRefSeg->GetEnd(), + if( area->Outline()->Distance( SEG( aRefSeg->GetStart(), aRefSeg->GetEnd() ), aRefSeg->GetWidth() ) == 0 ) { m_currentMarker = fillMarker( aRefSeg, NULL, @@ -1056,7 +1055,7 @@ bool DRC::doFootprintOverlappingDrc() msg.Printf( _( "footprints '%s' and '%s' overlap on front (top) layer" ), footprint->GetReference().GetData(), candidate->GetReference().GetData() ); - VECTOR2I& pos = courtyard.Vertex( 0, 0 ); + VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 ); wxPoint loc( pos.x, pos.y ); m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker ); addMarkerToPcb( m_currentMarker ); @@ -1091,7 +1090,7 @@ bool DRC::doFootprintOverlappingDrc() msg.Printf( _( "footprints '%s' and '%s' overlap on back (bottom) layer" ), footprint->GetReference().GetData(), candidate->GetReference().GetData() ); - VECTOR2I& pos = courtyard.Vertex( 0, 0 ); + VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 ); wxPoint loc( pos.x, pos.y ); m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker ); addMarkerToPcb( m_currentMarker ); diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index a4ce2a58f3..9061c78a4a 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -1597,17 +1597,15 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics ) zone->SetLayer( layer ); zone->SetNetCode( NETINFO_LIST::UNCONNECTED ); - CPolyLine::HATCH_STYLE outline_hatch = CPolyLine::DIAGONAL_EDGE; + ZONE_CONTAINER::HATCH_STYLE outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE; - zone->Outline()->Start( layer, kicad_x( r.x1 ), kicad_y( r.y1 ), outline_hatch ); + zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ) ); zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ) ); zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ) ); zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) ); - zone->Outline()->CloseLastContour(); // this is not my fault: - zone->Outline()->SetHatch( - outline_hatch, Mils2iu( zone->Outline()->GetDefaultHatchPitchMils() ), true ); + zone->SetHatch( outline_hatch, Mils2iu( zone->GetDefaultHatchPitchMils() ), true ); } m_xpath->pop(); @@ -2736,7 +2734,6 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) zone->SetLayer( layer ); zone->SetNetCode( netCode ); - bool first = true; for( CITER vi = it->second.begin(); vi != it->second.end(); ++vi ) { if( vi->first != "vertex" ) // skip node @@ -2744,32 +2741,21 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals ) EVERTEX v( vi->second ); - // the ZONE_CONTAINER API needs work, as you can see: - if( first ) - { - zone->Outline()->Start( layer, kicad_x( v.x ), kicad_y( v.y ), - CPolyLine::NO_HATCH); - first = false; - } - else - zone->AppendCorner( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) ); + // Append the corner + zone->AppendCorner( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) ); } - zone->Outline()->CloseLastContour(); - // If the pour is a cutout it needs to be set to a keepout if( p.pour == EPOLYGON::CUTOUT ) { zone->SetIsKeepout( true ); zone->SetDoNotAllowCopperPour( true ); - zone->Outline()->SetHatchStyle( CPolyLine::NO_HATCH ); + zone->SetHatchStyle( ZONE_CONTAINER::NO_HATCH ); } // if spacing is set the zone should be hatched if( p.spacing ) - zone->Outline()->SetHatch( CPolyLine::DIAGONAL_EDGE, - *p.spacing, - true ); + zone->SetHatch( ZONE_CONTAINER::DIAGONAL_EDGE, *p.spacing, true ); // clearances, etc. zone->SetArcSegmentCount( 32 ); // @todo: should be a constructor default? diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index 22307a8864..eb78d129ee 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.cpp @@ -613,7 +613,7 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) * and start move the new corner */ zone_cont->Draw( m_canvas, &dc, GR_XOR ); - zone_cont->Outline()->InsertCorner( zone_cont->GetSelectedCorner(), pos.x, pos.y ); + zone_cont->Outline()->InsertVertex( zone_cont->GetSelectedCorner(), pos ); zone_cont->SetSelectedCorner( zone_cont->GetSelectedCorner() + 1 ); zone_cont->Draw( m_canvas, &dc, GR_XOR ); m_canvas->SetAutoPanRequest( true ); diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index eedff9ec35..c073b3e8f5 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -1518,13 +1518,13 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const switch( aZone->GetHatchStyle() ) { default: - case CPolyLine::NO_HATCH: hatch = "none"; break; - case CPolyLine::DIAGONAL_EDGE: hatch = "edge"; break; - case CPolyLine::DIAGONAL_FULL: hatch = "full"; break; + case ZONE_CONTAINER::NO_HATCH: hatch = "none"; break; + case ZONE_CONTAINER::DIAGONAL_EDGE: hatch = "edge"; break; + case ZONE_CONTAINER::DIAGONAL_FULL: hatch = "full"; break; } m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(), - FMT_IU( aZone->Outline()->GetHatchPitch() ).c_str() ); + FMT_IU( aZone->GetHatchPitch() ).c_str() ); if( aZone->GetPriority() > 0 ) m_out->Print( aNestLevel+1, "(priority %d)\n", aZone->GetPriority() ); @@ -1606,22 +1606,21 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const m_out->Print( 0, ")\n" ); - const CPOLYGONS_LIST& cv = aZone->Outline()->m_CornersList; int newLine = 0; - if( cv.GetCornersCount() ) + if( aZone->GetNumCorners() ) { m_out->Print( aNestLevel+1, "(polygon\n"); m_out->Print( aNestLevel+2, "(pts\n" ); - for( unsigned it = 0; it < cv.GetCornersCount(); ++it ) + for( auto iterator = aZone->IterateWithHoles(); iterator; iterator++ ) { if( newLine == 0 ) m_out->Print( aNestLevel+3, "(xy %s %s)", - FMT_IU( cv.GetX( it ) ).c_str(), FMT_IU( cv.GetY( it ) ).c_str() ); + FMT_IU( iterator->x ).c_str(), FMT_IU( iterator->y ).c_str() ); else m_out->Print( 0, " (xy %s %s)", - FMT_IU( cv.GetX( it ) ).c_str(), FMT_IU( cv.GetY( it ) ).c_str() ); + FMT_IU( iterator->x ).c_str(), FMT_IU( iterator->y ).c_str() ); if( newLine < 4 ) { @@ -1633,14 +1632,14 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const m_out->Print( 0, "\n" ); } - if( cv.IsEndContour( it ) ) + if( iterator.IsEndContour() ) { if( newLine != 0 ) m_out->Print( 0, "\n" ); m_out->Print( aNestLevel+2, ")\n" ); - if( it+1 != cv.GetCornersCount() ) + if( !iterator.IsLastPolygon() ) { newLine = 0; m_out->Print( aNestLevel+1, ")\n" ); @@ -1662,7 +1661,7 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const m_out->Print( aNestLevel+1, "(filled_polygon\n" ); m_out->Print( aNestLevel+2, "(pts\n" ); - for( SHAPE_POLY_SET::CONST_ITERATOR it = fv.CIterate(); it; ++it ) + for( auto it = fv.CIterate(); it; ++it ) { if( newLine == 0 ) m_out->Print( aNestLevel+3, "(xy %s %s)", @@ -1688,7 +1687,7 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const m_out->Print( aNestLevel+2, ")\n" ); - if( !it.IsLastContour() ) + if( !it.IsLastPolygon() ) { newLine = 0; m_out->Print( aNestLevel+1, ")\n" ); diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 545489218d..722100a6eb 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -2474,7 +2474,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER() { unique_ptr zc( new ZONE_CONTAINER( m_board ) ); - CPolyLine::HATCH_STYLE outline_hatch = CPolyLine::NO_HATCH; + ZONE_CONTAINER::HATCH_STYLE outline_hatch = ZONE_CONTAINER::NO_HATCH; bool sawCorner = false; char buf[1024]; char* line; @@ -2489,17 +2489,13 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER() // e.g. "ZCorner 25650 49500 0" BIU x = biuParse( line + SZ( "ZCorner" ), &data ); BIU y = biuParse( data, &data ); - int flag = intParse( data ); if( !sawCorner ) - zc->Outline()->Start( zc->GetLayer(), x, y, outline_hatch ); + zc->NewHole(); else zc->AppendCorner( wxPoint( x, y ) ); sawCorner = true; - - if( flag ) - zc->Outline()->CloseLastContour(); } else if( TESTLINE( "ZInfo" ) ) // general info found @@ -2540,9 +2536,9 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER() switch( *hopt ) // upper case required { - case 'N': outline_hatch = CPolyLine::NO_HATCH; break; - case 'E': outline_hatch = CPolyLine::DIAGONAL_EDGE; break; - case 'F': outline_hatch = CPolyLine::DIAGONAL_FULL; break; + case 'N': outline_hatch = ZONE_CONTAINER::NO_HATCH; break; + case 'E': outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE; break; + case 'F': outline_hatch = ZONE_CONTAINER::DIAGONAL_FULL; break; default: m_error.Printf( wxT( "Bad ZAux for CZONE_CONTAINER '%s'" ), zc->GetNetname().GetData() ); @@ -2724,9 +2720,8 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER() // Hatch here, after outlines corners are read // Set hatch here, after outlines corners are read - zc->Outline()->SetHatch( outline_hatch, - Mils2iu( CPolyLine::GetDefaultHatchPitchMils() ), - true ); + zc->SetHatch( outline_hatch, Mils2iu( ZONE_CONTAINER::GetDefaultHatchPitchMils() ), + true ); m_board->Add( zc.release() ); } diff --git a/pcbnew/onrightclick.cpp b/pcbnew/onrightclick.cpp index afb7c4989d..13f769fbd7 100644 --- a/pcbnew/onrightclick.cpp +++ b/pcbnew/onrightclick.cpp @@ -716,14 +716,14 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu* edge_zone->GetIsKeepout() ? _("Keepout Area") : _( "Zones" ), KiBitmap( add_zone_xpm ) ); - if( edge_zone->HitTestForCorner( RefPos( true ) ) >= 0 ) + if( edge_zone->HitTestForCorner( RefPos( true ) ) ) { AddMenuItem( zones_menu, ID_POPUP_PCB_MOVE_ZONE_CORNER, _( "Move Corner" ), KiBitmap( move_xpm ) ); AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CORNER, _( "Delete Corner" ), KiBitmap( delete_xpm ) ); } - else if( edge_zone->HitTestForEdge( RefPos( true ) ) >= 0 ) + else if( edge_zone->HitTestForEdge( RefPos( true ) ) ) { AddMenuItem( zones_menu, ID_POPUP_PCB_ADD_ZONE_CORNER, _( "Create Corner" ), KiBitmap( add_corner_xpm ) ); @@ -771,7 +771,7 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu* zones_menu->AppendSeparator(); if( edge_zone->GetSelectedCorner() >= 0 && - edge_zone->Outline()->IsCutoutContour( edge_zone->GetSelectedCorner() ) ) + edge_zone->Outline()->IsVertexInHole( edge_zone->GetSelectedCorner() ) ) AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CUTOUT, _( "Delete Cutout" ), KiBitmap( delete_xpm ) ); diff --git a/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp b/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp index b3b4ec5317..7a09351ceb 100644 --- a/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp +++ b/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp @@ -177,26 +177,19 @@ void PCB_POLYGON::AddToBoard() zone->SetNetCode( m_netCode ); // add outline - int outline_hatch = CPolyLine::DIAGONAL_EDGE; + int outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE; - zone->Outline()->Start( m_KiCadLayer, KiROUND( m_outline[i]->x ), - KiROUND( m_outline[i]->y ), outline_hatch ); - - for( i = 1; i < (int) m_outline.GetCount(); i++ ) + for( i = 0; i < (int) m_outline.GetCount(); i++ ) { zone->AppendCorner( wxPoint( KiROUND( m_outline[i]->x ), KiROUND( m_outline[i]->y ) ) ); } - zone->Outline()->CloseLastContour(); - zone->SetZoneClearance( m_width ); zone->SetPriority( m_priority ); - zone->Outline()->SetHatch( outline_hatch, - Mils2iu( zone->Outline()->GetDefaultHatchPitchMils() ), - true ); + zone->SetHatch( outline_hatch, Mils2iu( zone->GetDefaultHatchPitchMils() ), true ); if ( m_objType == wxT( 'K' ) ) { diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 3783d63609..17d54d8897 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -973,12 +973,11 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone ) m_gal->SetIsStroke( true ); m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); - const CPolyLine* polygon = aZone->Outline(); - for( int i = 0; i < polygon->GetCornersCount(); ++i ) + for( auto iterator = aZone->CIterateWithHoles(); iterator; iterator++ ) { - corners.push_back( VECTOR2D( polygon->GetPos( i ) ) ); + corners.push_back( VECTOR2D( *iterator ) ); - if( polygon->IsEndContour( i ) ) + if( iterator.IsEndContour() ) { // The last point for closing the polyline corners.push_back( corners[0] ); @@ -986,11 +985,8 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone ) corners.clear(); } - for( unsigned ic = 0; ic < polygon->m_HatchLines.size(); ic++ ) - { - auto& hatchLine = polygon->m_HatchLines[ic]; - m_gal->DrawLine( hatchLine.m_Start, hatchLine.m_End ); - } + for( const SEG& hatchLine : aZone->GetHatchLines() ) + m_gal->DrawLine( hatchLine.A, hatchLine.B ); } // Draw the filling diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index d8e5e1c8cd..4ef8fc53eb 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -2644,9 +2644,9 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ) wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ZONE_CONTAINER." ) ); - CPolyLine::HATCH_STYLE hatchStyle = CPolyLine::NO_HATCH; + ZONE_CONTAINER::HATCH_STYLE hatchStyle = ZONE_CONTAINER::NO_HATCH; - int hatchPitch = Mils2iu( CPolyLine::GetDefaultHatchPitchMils() ); + int hatchPitch = Mils2iu( ZONE_CONTAINER::GetDefaultHatchPitchMils() ); wxPoint pt; T token; int tmp; @@ -2709,9 +2709,9 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ) switch( token ) { default: - case T_none: hatchStyle = CPolyLine::NO_HATCH; break; - case T_edge: hatchStyle = CPolyLine::DIAGONAL_EDGE; break; - case T_full: hatchStyle = CPolyLine::DIAGONAL_FULL; + case T_none: hatchStyle = ZONE_CONTAINER::NO_HATCH; break; + case T_edge: hatchStyle = ZONE_CONTAINER::DIAGONAL_EDGE; break; + case T_full: hatchStyle = ZONE_CONTAINER::DIAGONAL_FULL; } hatchPitch = parseBoardUnits( "hatch pitch" ); @@ -2956,7 +2956,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ) } // Set hatch here, after outlines corners are read - zone->Outline()->SetHatch( hatchStyle, hatchPitch, true ); + zone->SetHatch( hatchStyle, hatchPitch, true ); } if( !pts.IsEmpty() ) diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index 704f0c32bf..e2ebda5ae8 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -646,7 +646,7 @@ void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone ) * * in non filled mode the outline is plotted, but not the filling items */ - for( SHAPE_POLY_SET::CONST_ITERATOR ic = polysList.CIterate(); ic; ++ic ) + for( auto ic = polysList.CIterate(); ic; ++ic ) { wxPoint pos( ic->x, ic->y ); cornerList.push_back( pos ); diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp index b2e1f4da28..fb2d6032ce 100644 --- a/pcbnew/ratsnest_data.cpp +++ b/pcbnew/ratsnest_data.cpp @@ -418,7 +418,7 @@ RN_POLY::RN_POLY( const SHAPE_POLY_SET* aParent, m_bbox( aBBox ), m_parentPolyset( aParent ) { - const VECTOR2I& p = aParent->CVertex( 0, aSubpolygonIndex ); + const VECTOR2I& p = aParent->CVertex( 0, aSubpolygonIndex, -1 ); m_node = aConnections.AddNode( p.x, p.y ); diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 102bde7372..7eb253e389 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -1016,26 +1016,27 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; - int count = item->Outline()->m_CornersList.GetCornersCount(); - int ndx = 0; // used in 2 for() loops below - for( ; ndxIterateWithHoles(); iterator; iterator++ ) { - wxPoint point( item->Outline()->m_CornersList[ndx].x, - item->Outline()->m_CornersList[ndx].y ); + wxPoint point( iterator->x, iterator->y ); mainPolygon->AppendPoint( mapPt(point) ); // this was the end of the main polygon - if( item->Outline()->m_CornersList[ndx].end_contour ) + if( iterator.IsEndContour() ) break; } WINDOW* window = 0; PATH* cutout = 0; + bool isStartContour = true; + // handle the cutouts - for( ++ndx; ndxOutline()->m_CornersList[ndx-1].end_contour ) + if( isStartContour ) { window = new WINDOW( plane ); @@ -1048,11 +1049,14 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; } + // If the point in this iteration is the last of the contour, the next iteration + // will start with a new contour. + isStartContour = iterator.IsEndContour(); + wxASSERT( window ); wxASSERT( cutout ); - wxPoint point(item->Outline()->m_CornersList[ndx].x, - item->Outline()->m_CornersList[ndx].y ); + wxPoint point(iterator->x, iterator->y ); cutout->AppendPoint( mapPt(point) ); } } @@ -1093,41 +1097,44 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; - int count = item->Outline()->m_CornersList.GetCornersCount(); - int ndx = 0; // used in 2 for() loops below - for( ; ndxIterateWithHoles(); iterator; iterator++ ) { - wxPoint point( item->Outline()->m_CornersList[ndx].x, - item->Outline()->m_CornersList[ndx].y ); + wxPoint point( iterator->x, iterator->y ); mainPolygon->AppendPoint( mapPt(point) ); // this was the end of the main polygon - if( item->Outline()->m_CornersList[ndx].end_contour ) + if( iterator.IsEndContour() ) break; } WINDOW* window = 0; PATH* cutout = 0; + bool isStartContour = true; + // handle the cutouts - for( ++ndx; ndxOutline()->m_CornersList[ndx-1].end_contour ) + if( isStartContour ) { window = new WINDOW( keepout ); keepout->AddWindow( window ); cutout = new PATH( window, T_polygon ); + window->SetShape( cutout ); cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; } + isStartContour = iterator.IsEndContour(); + wxASSERT( window ); wxASSERT( cutout ); - wxPoint point(item->Outline()->m_CornersList[ndx].x, - item->Outline()->m_CornersList[ndx].y ); + wxPoint point(iterator->x, iterator->y ); cutout->AppendPoint( mapPt(point) ); } } diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp index 2ae97e0348..edc70eac61 100644 --- a/pcbnew/tools/grid_helper.cpp +++ b/pcbnew/tools/grid_helper.cpp @@ -339,17 +339,15 @@ void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos ) case PCB_ZONE_AREA_T: { - const CPolyLine* outline = static_cast( aItem )->Outline(); - int cornersCount = outline->GetCornersCount(); + const SHAPE_POLY_SET* outline = static_cast( aItem )->Outline(); SHAPE_LINE_CHAIN lc; lc.SetClosed( true ); - for( int i = 0; i < cornersCount; ++i ) + for( auto iter = outline->CIterateWithHoles(); iter; iter++ ) { - const VECTOR2I p ( outline->GetPos( i ) ); - addAnchor( p, CORNER, aItem ); - lc.Append( p ); + addAnchor( *iter, CORNER, aItem ); + lc.Append( *iter ); } addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem ); diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index 256294de4c..118364e1ba 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -750,22 +750,19 @@ int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) static bool mergeZones( BOARD_COMMIT& aCommit, std::vector& aOriginZones, std::vector& aMergedZones ) { - SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( aOriginZones[0]->Outline()->m_CornersList ); - for( unsigned int i = 1; i < aOriginZones.size(); i++ ) { - SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( aOriginZones[i]->Outline()->m_CornersList ); - - mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST ); + aOriginZones[0]->Outline()->BooleanAdd( *aOriginZones[i]->Outline(), + SHAPE_POLY_SET::PM_FAST ); } - mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST ); + aOriginZones[0]->Outline()->Simplify( SHAPE_POLY_SET::PM_FAST ); // We should have one polygon with hole // We can have 2 polygons with hole, if the 2 initial polygons have only one common corner // and therefore cannot be merged (they are dectected as intersecting) // but we should never have more than 2 polys - if( mergedOutlines.OutlineCount() > 1 ) + if( aOriginZones[0]->Outline()->OutlineCount() > 1 ) { wxLogMessage( wxT( "BOARD::CombineAreas error: more than 2 polys after merging" ) ); return false; @@ -779,9 +776,8 @@ static bool mergeZones( BOARD_COMMIT& aCommit, std::vector& aO aCommit.Modify( aOriginZones[0] ); aMergedZones.push_back( aOriginZones[0] ); - aOriginZones[0]->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines ); aOriginZones[0]->SetLocalFlags( 1 ); - aOriginZones[0]->Outline()->Hatch(); + aOriginZones[0]->Hatch(); return true; } diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp index f69c6051be..e7661191f3 100644 --- a/pcbnew/tools/point_editor.cpp +++ b/pcbnew/tools/point_editor.cpp @@ -128,14 +128,16 @@ public: case PCB_ZONE_AREA_T: { - const CPolyLine* outline = static_cast( aItem )->Outline(); - int cornersCount = outline->GetCornersCount(); + const SHAPE_POLY_SET* outline; + outline = static_cast( aItem )->Outline(); - for( int i = 0; i < cornersCount; ++i ) + int cornersCount = outline->TotalVertices(); + + for( auto iterator = outline->CIterateWithHoles(); iterator; iterator++ ) { - points->AddPoint( outline->GetPos( i ) ); + points->AddPoint( *iterator ); - if( outline->IsEndContour( i ) ) + if( iterator.IsEndContour() ) points->AddBreak(); } @@ -456,16 +458,15 @@ void POINT_EDITOR::updateItem() const { ZONE_CONTAINER* zone = static_cast( item ); zone->ClearFilledPolysList(); - CPolyLine* outline = zone->Outline(); + SHAPE_POLY_SET* outline = zone->Outline(); - for( int i = 0; i < outline->GetCornersCount(); ++i ) + for( int i = 0; i < outline->TotalVertices(); ++i ) { VECTOR2I point = m_editPoints->Point( i ).GetPosition(); - outline->SetX( i, point.x ); - outline->SetY( i, point.y ); + outline->Vertex( i ) = point; } - outline->Hatch(); + zone->Hatch(); break; } @@ -584,9 +585,9 @@ void POINT_EDITOR::updatePoints() case PCB_ZONE_AREA_T: { const ZONE_CONTAINER* zone = static_cast( item ); - const CPolyLine* outline = zone->Outline(); + const SHAPE_POLY_SET* outline = zone->Outline(); - if( m_editPoints->PointsSize() != (unsigned) outline->GetCornersCount() ) + if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() ) { getView()->Remove( m_editPoints.get() ); m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() ); @@ -594,8 +595,8 @@ void POINT_EDITOR::updatePoints() } else { - for( int i = 0; i < outline->GetCornersCount(); ++i ) - m_editPoints->Point( i ).SetPosition( outline->GetPos( i ) ); + for( int i = 0; i < outline->TotalVertices(); ++i ) + m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) ); } break; @@ -775,19 +776,18 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent ) if( item->Type() == PCB_ZONE_AREA_T ) { ZONE_CONTAINER* zone = static_cast( item ); - CPolyLine* outline = zone->Outline(); + SHAPE_POLY_SET* outline = zone->Outline(); commit.Modify( zone ); // Handle the last segment, so other segments can be easily handled in a loop - unsigned int nearestIdx = outline->GetCornersCount() - 1, nextNearestIdx = 0; - SEG side( VECTOR2I( outline->GetPos( nearestIdx ) ), - VECTOR2I( outline->GetPos( nextNearestIdx ) ) ); + unsigned int nearestIdx = outline->TotalVertices() - 1, nextNearestIdx = 0; + SEG side( outline->Vertex( nearestIdx ), outline->Vertex( nextNearestIdx ) ); unsigned int nearestDist = side.Distance( cursorPos ); - for( int i = 0; i < outline->GetCornersCount() - 1; ++i ) + for( int i = 0; i < outline->TotalVertices() - 1; ++i ) { - side = SEG( VECTOR2I( outline->GetPos( i ) ), VECTOR2I( outline->GetPos( i + 1 ) ) ); + side = SEG( outline->Vertex( i ), outline->Vertex( i + 1 ) ); unsigned int distance = side.Distance( cursorPos ); if( distance < nearestDist ) @@ -799,8 +799,8 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent ) } // Find the point on the closest segment - VECTOR2I sideOrigin( outline->GetPos( nearestIdx ) ); - VECTOR2I sideEnd( outline->GetPos( nextNearestIdx ) ); + VECTOR2I sideOrigin = outline->Vertex( nearestIdx ); + VECTOR2I sideEnd = outline->Vertex( nextNearestIdx ); SEG nearestSide( sideOrigin, sideEnd ); VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos ); @@ -809,7 +809,7 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent ) if( nearestPoint == sideOrigin || nearestPoint == sideEnd ) nearestPoint = ( sideOrigin + sideEnd ) / 2; - outline->InsertCorner( nearestIdx, nearestPoint.x, nearestPoint.y ); + outline->InsertVertex( nearestIdx, nearestPoint ); commit.Push( _( "Add a zone corner" ) ); } @@ -871,14 +871,14 @@ int POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent ) BOARD_COMMIT commit( frame ); ZONE_CONTAINER* zone = static_cast( item ); - CPolyLine* outline = zone->Outline(); + SHAPE_POLY_SET* outline = zone->Outline(); commit.Modify( zone ); - for( int i = 0; i < outline->GetCornersCount(); ++i ) + for( int i = 0; i < outline->TotalVertices(); ++i ) { - if( VECTOR2I( outline->GetPos( i ) ) == m_editedPoint->GetPosition() ) + if( outline->Vertex( i ) == m_editedPoint->GetPosition() ) { - outline->DeleteCorner( i ); + outline->RemoveVertex( i ); setEditedPoint( NULL ); commit.Push( _( "Remove a zone corner" ) ); break; diff --git a/pcbnew/tools/zone_create_helper.cpp b/pcbnew/tools/zone_create_helper.cpp index b1def20802..c40ec45166 100644 --- a/pcbnew/tools/zone_create_helper.cpp +++ b/pcbnew/tools/zone_create_helper.cpp @@ -110,14 +110,16 @@ void ZONE_CREATE_HELPER::performZoneCutout( ZONE_CONTAINER& aExistingZone, auto& board = *m_tool.getModel(); auto& toolMgr = *m_tool.GetManager(); + aExistingZone.Outline()->NewOutline(); + // Copy cutout corners into existing zone for( int ii = 0; ii < aCutout.GetNumCorners(); ii++ ) { - aExistingZone.AppendCorner( aCutout.GetCornerPosition( ii ) ); + aExistingZone.Outline()->Append( aCutout.GetCornerPosition( ii ) ); } // Close the current corner list - aExistingZone.Outline()->CloseLastContour(); + aExistingZone.Outline()->Outline( 0 ).SetClosed( true ); board.OnAreaPolygonModified( nullptr, &aExistingZone ); @@ -217,18 +219,16 @@ void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr ) } else { - m_zone->Outline()->Start( m_zone->GetLayer(), - finalPoints[0].x, finalPoints[0].y, - m_zone->GetHatchStyle() ); + m_zone->Outline()->NewOutline(); - for( size_t i = 1; i < finalPoints.size(); ++i ) + for( const auto& pt : finalPoints ) { - m_zone->AppendCorner( { finalPoints[i].x, finalPoints[i].y } ); + m_zone->Outline()->Append( pt ); } - m_zone->Outline()->CloseLastContour(); + m_zone->Outline()->Outline( 0 ).SetClosed( true ); m_zone->Outline()->RemoveNullSegments(); - m_zone->Outline()->Hatch(); + m_zone->Hatch(); // hand the zone over to the committer commitZone( std::move( m_zone ) ); diff --git a/pcbnew/zone_filling_algorithm.cpp b/pcbnew/zone_filling_algorithm.cpp index a8eed7ebb4..c3c5b9802d 100644 --- a/pcbnew/zone_filling_algorithm.cpp +++ b/pcbnew/zone_filling_algorithm.cpp @@ -75,11 +75,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* switch( m_cornerSmoothingType ) { case ZONE_SETTINGS::SMOOTHING_CHAMFER: - m_smoothedPoly = m_Poly->Chamfer( m_cornerRadius ); + m_smoothedPoly = new SHAPE_POLY_SET(); + *m_smoothedPoly = m_Poly->Chamfer( m_cornerRadius ); break; case ZONE_SETTINGS::SMOOTHING_FILLET: - m_smoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount ); + m_smoothedPoly = new SHAPE_POLY_SET(); + *m_smoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount ); break; default: @@ -88,12 +90,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* // We can avoid issues by creating a very small chamfer which remove acute angles, // or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create // clearance areas - m_smoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) ); + m_smoothedPoly = new SHAPE_POLY_SET(); + *m_smoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) ); break; } if( aOutlineBuffer ) - aOutlineBuffer->Append( ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ) ); + aOutlineBuffer->Append( *m_smoothedPoly ); /* For copper layers, we now must add holes in the Polygon list. * holes are pads and tracks with their clearance area @@ -118,7 +121,7 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* { m_FillMode = 0; // Fill by segments is no more used in non copper layers // force use solid polygons (usefull only for old boards) - m_FilledPolysList = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ); + m_FilledPolysList = *m_smoothedPoly; // The filled areas are deflated by -m_ZoneMinThickness / 2, because // the outlines are drawn with a line thickness = m_ZoneMinThickness to diff --git a/pcbnew/zones_by_polygon.cpp b/pcbnew/zones_by_polygon.cpp index 531973aa90..d52435f7ca 100644 --- a/pcbnew/zones_by_polygon.cpp +++ b/pcbnew/zones_by_polygon.cpp @@ -137,7 +137,7 @@ void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone ) if( success ) { zoneSettings.ExportSetting( *newZone ); - newZone->Outline()->Hatch(); + newZone->Hatch(); s_AuxiliaryList.ClearListAndDeleteItems(); s_PickedList.ClearListAndDeleteItems(); @@ -187,7 +187,7 @@ int PCB_EDIT_FRAME::Delete_LastCreatedCorner( wxDC* DC ) if( zone->GetNumCorners() > 2 ) { - zone->Outline()->DeleteCorner( zone->GetNumCorners() - 1 ); + zone->Outline()->RemoveVertex( zone->GetNumCorners() - 1 ); if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxDefaultPosition, false ); @@ -256,11 +256,10 @@ void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone, // Prepare copy of old zones, for undo/redo. // if the corner is new, remove it from list, save and insert it in list - int cx = aZone->Outline()->GetX( corner_id ); - int cy = aZone->Outline()->GetY( corner_id ); + VECTOR2I corner = aZone->Outline()->Vertex( corner_id ); if ( IsNewCorner ) - aZone->Outline()->DeleteCorner( corner_id ); + aZone->Outline()->RemoveVertex( corner_id ); s_AuxiliaryList.ClearListAndDeleteItems(); s_PickedList.ClearListAndDeleteItems(); @@ -268,12 +267,12 @@ void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone, SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() ); if ( IsNewCorner ) - aZone->Outline()->InsertCorner(corner_id-1, cx, cy ); + aZone->Outline()->InsertVertex(corner_id-1, corner ); aZone->SetFlags( IN_EDIT ); m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse, Abort_Zone_Move_Corner_Or_Outlines ); - s_CornerInitialPosition = aZone->GetCornerPosition( corner_id ); + s_CornerInitialPosition = static_cast( aZone->GetCornerPosition( corner_id ) ); s_CornerIsNew = IsNewCorner; s_AddCutoutToCurrentZone = false; s_CurrentZone = NULL; @@ -370,7 +369,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone ) { OnModify(); - if( aZone->Outline()->GetCornersCount() <= 3 ) + if( aZone->Outline()->TotalVertices() <= 3 ) { m_canvas->RefreshDrawingRect( aZone->GetBoundingBox() ); @@ -395,7 +394,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone ) s_AuxiliaryList.ClearListAndDeleteItems(); s_PickedList. ClearListAndDeleteItems(); SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() ); - aZone->Outline()->DeleteCorner( aZone->GetSelectedCorner() ); + aZone->Outline()->RemoveVertex( aZone->GetSelectedCorner() ); // modify zones outlines according to the new aZone shape GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone ); @@ -449,12 +448,12 @@ void Abort_Zone_Move_Corner_Or_Outlines( EDA_DRAW_PANEL* Panel, wxDC* DC ) { if( s_CornerIsNew ) { - zone->Outline()->DeleteCorner( zone->GetSelectedCorner() ); + zone->Outline()->RemoveVertex( zone->GetSelectedCorner() ); } else { wxPoint pos = s_CornerInitialPosition; - zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y ); + zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos; } } @@ -500,7 +499,7 @@ void Show_Zone_Corner_Or_Outline_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* } else { - zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y ); + zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos; } zone->Draw( aPanel, aDC, GR_XOR ); @@ -667,12 +666,11 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC ) { zoneInfo.ExportSetting( *zone ); - zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer, - GetCrossHairPosition().x, - GetCrossHairPosition().y, - zone->GetHatchStyle() ); + zone->SetLayer( zoneInfo.m_CurrentZone_Layer ); + // A duplicated corner is needed; null segments are removed when the zone is finished. zone->AppendCorner( GetCrossHairPosition() ); + zone->AppendCorner( GetCrossHairPosition(), true ); if( g_Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() ) { @@ -704,8 +702,14 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC ) // Ok, we can add a new corner if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); - zone->AppendCorner( GetCrossHairPosition() ); + + // It is necessary to allow duplication of the points, as we have to handle the + // continuous drawing while creating the zone at the same time as we build it. Null + // segments are removed when the zone is finished, in End_Zone. + zone->AppendCorner( GetCrossHairPosition(), true ); + SetCurItem( zone ); // calls DisplayInfo(). + if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); } @@ -768,7 +772,6 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC ) // Put new zone in list if( !s_CurrentZone ) { - zone->Outline()->CloseLastContour(); // Close the current corner list GetBoard()->Add( zone ); // Add this zone in picked list, as new item @@ -777,12 +780,8 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC ) } else // Append this outline as a cutout to an existing zone { - for( int ii = 0; ii < zone->GetNumCorners(); ii++ ) - { - s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) ); - } + s_CurrentZone->Outline()->AddHole( zone->Outline()->Outline( 0 ) ); - s_CurrentZone->Outline()->CloseLastContour(); // Close the current corner list zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list. zone = s_CurrentZone; } @@ -847,7 +846,7 @@ static void Show_New_Edge_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* aDC, if( pcbframe->GetZoneSettings().m_Zone_45_Only ) { // calculate the new position as allowed - wxPoint StartPoint = zone->GetCornerPosition( icorner - 1 ); + wxPoint StartPoint = static_cast( zone->GetCornerPosition( icorner - 1 ) ); CalculateSegmentEndPoint( c_pos, StartPoint.x, StartPoint.y, &c_pos.x, &c_pos.y ); } @@ -964,7 +963,12 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* aZone ) void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone ) { - int ncont = aZone->Outline()->GetContour( aZone->GetSelectedCorner() ); + // Get contour in which the selected corner is + SHAPE_POLY_SET::VERTEX_INDEX indices; + + // If the selected corner does not exist, abort + if( !aZone->Outline()->GetRelativeIndices( aZone->GetSelectedCorner(), &indices ) ) + throw( std::out_of_range( "Zone selected corner does not exist" ) ); EDA_RECT dirty = aZone->GetBoundingBox(); @@ -974,7 +978,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone ) // Remove current filling: aZone->UnFill(); - if( ncont == 0 ) // This is the main outline: remove all + if( indices.m_contour == 0 ) // This is the main outline: remove all { SaveCopyInUndoList( aZone, UR_DELETED ); GetBoard()->Remove( aZone ); @@ -983,7 +987,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone ) else { SaveCopyInUndoList( aZone, UR_CHANGED ); - aZone->Outline()->RemoveContour( ncont ); + aZone->Outline()->RemoveContour( indices.m_contour, indices.m_polygon ); } m_canvas->RefreshDrawingRect( dirty ); 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 f975c6a553..4ae50562ee 100644 --- a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp +++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp @@ -435,7 +435,7 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) if(g_DumpZonesWhenFilling) dumper->BeginGroup("clipper-zone"); - SHAPE_POLY_SET solidAreas = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ); + SHAPE_POLY_SET solidAreas = *m_smoothedPoly; solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); solidAreas.Simplify( POLY_CALC_MODE ); diff --git a/pcbnew/zones_functions_for_undo_redo.cpp b/pcbnew/zones_functions_for_undo_redo.cpp index 35d1392453..4d4f9d1af1 100644 --- a/pcbnew/zones_functions_for_undo_redo.cpp +++ b/pcbnew/zones_functions_for_undo_redo.cpp @@ -115,8 +115,7 @@ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare ) wxASSERT( m_Poly ); // m_Poly == NULL Should never happen wxASSERT( aZoneToCompare.Outline() ); - if( Outline()->m_CornersList.GetList() != - aZoneToCompare.Outline()->m_CornersList.GetList() ) // Compare vector + if( Outline() != aZoneToCompare.Outline() ) // Compare vector return false; return true; diff --git a/pcbnew/zones_test_and_combine_areas.cpp b/pcbnew/zones_test_and_combine_areas.cpp index 0fe0848b18..ba7face3f4 100644 --- a/pcbnew/zones_test_and_combine_areas.cpp +++ b/pcbnew/zones_test_and_combine_areas.cpp @@ -102,7 +102,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode, continue; // legal polygon - EDA_RECT b1 = curr_area->Outline()->GetBoundingBox(); + BOX2I b1 = curr_area->Outline()->BBox(); bool mod_ia1 = false; for( unsigned ia2 = m_ZoneDescriptorList.size() - 1; ia2 > ia1; ia2-- ) @@ -121,7 +121,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode, if( curr_area->GetLayer() != area2->GetLayer() ) continue; - EDA_RECT b2 = area2->Outline()->GetBoundingBox(); + BOX2I b2 = area2->Outline()->BBox(); if( b1.Intersects( b2 ) ) { @@ -189,92 +189,45 @@ bool BOARD::TestAreaIntersection( ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area if( area_ref->GetLayer() != area_to_test->GetLayer() ) return false; - CPolyLine* poly1 = area_ref->Outline(); - CPolyLine* poly2 = area_to_test->Outline(); + SHAPE_POLY_SET* poly1 = area_ref->Outline(); + SHAPE_POLY_SET* poly2 = area_to_test->Outline(); // test bounding rects - EDA_RECT b1 = poly1->GetBoundingBox(); - EDA_RECT b2 = poly2->GetBoundingBox(); + BOX2I b1 = poly1->BBox(); + BOX2I b2 = poly2->BBox(); if( ! b1.Intersects( b2 ) ) return false; - // now test for intersecting segments - for( int icont1 = 0; icont1GetContoursCount(); icont1++ ) + // Now test for intersecting segments + for( auto segIterator1 = poly1->IterateSegmentsWithHoles(); segIterator1; segIterator1++ ) { - int is1 = poly1->GetContourStart( icont1 ); - int ie1 = poly1->GetContourEnd( icont1 ); + // Build segment + SEG firstSegment = *segIterator1; - for( int ic1 = is1; ic1<=ie1; ic1++ ) + for( auto segIterator2 = poly2->IterateSegmentsWithHoles(); segIterator2; segIterator2++ ) { - int xi1 = poly1->GetX( ic1 ); - int yi1 = poly1->GetY( ic1 ); - int xf1, yf1; + // Build second segment + SEG secondSegment = *segIterator2; - if( ic1 < ie1 ) - { - xf1 = poly1->GetX( ic1 + 1 ); - yf1 = poly1->GetY( ic1 + 1 ); - } - else - { - xf1 = poly1->GetX( is1 ); - yf1 = poly1->GetY( is1 ); - } - - for( int icont2 = 0; icont2GetContoursCount(); icont2++ ) - { - int is2 = poly2->GetContourStart( icont2 ); - int ie2 = poly2->GetContourEnd( icont2 ); - - for( int ic2 = is2; ic2<=ie2; ic2++ ) - { - int xi2 = poly2->GetX( ic2 ); - int yi2 = poly2->GetY( ic2 ); - int xf2, yf2; - - if( ic2 < ie2 ) - { - xf2 = poly2->GetX( ic2 + 1 ); - yf2 = poly2->GetY( ic2 + 1 ); - } - else - { - xf2 = poly2->GetX( is2 ); - yf2 = poly2->GetY( is2 ); - } - - bool intersect = FindSegmentIntersections( xi1, yi1, xf1, yf1, - xi2, yi2, xf2, yf2 ); - if( intersect ) - return true; - } - } + // Check whether the two segments built collide + if( firstSegment.Collide( secondSegment, 0 ) ) + return true; } } - // If a contour is inside an other contour, no segments intersects, but the zones - // can be combined if a corner is inside an outline (only one corner is enought) - for( int ic2 = 0; ic2 < poly2->GetCornersCount(); ic2++ ) + // If a contour is inside another contour, no segments intersects, but the zones + // can be combined if a corner is inside an outline (only one corner is enough) + for( auto iter = poly2->IterateWithHoles(); iter; iter++ ) { - int x = poly2->GetX( ic2 ); - int y = poly2->GetY( ic2 ); - - if( poly1->TestPointInside( x, y ) ) - { + if( poly1->Contains( *iter ) ) return true; - } } - for( int ic1 = 0; ic1 < poly1->GetCornersCount(); ic1++ ) + for( auto iter = poly1->IterateWithHoles(); iter; iter++ ) { - int x = poly1->GetX( ic1 ); - int y = poly1->GetY( ic1 ); - - if( poly2->TestPointInside( x, y ) ) - { + if( poly2->Contains( *iter ) ) return true; - } } return false; @@ -290,8 +243,8 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_ return false; } - SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( area_ref->Outline()->m_CornersList ); - SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( area_to_combine->Outline()->m_CornersList ); + SHAPE_POLY_SET mergedOutlines = *area_ref->Outline(); + SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline(); mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST ); mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST ); @@ -309,12 +262,14 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_ if( mergedOutlines.OutlineCount() > 1 ) return false; - area_ref->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines ); + // Update the area with the new merged outline + delete area_ref->Outline(); + area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) ); RemoveArea( aDeletedList, area_to_combine ); area_ref->SetLocalFlags( 1 ); - area_ref->Outline()->Hatch(); + area_ref->Hatch(); return true; } @@ -329,7 +284,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E for( int ia = 0; ia < GetAreaCount(); ia++ ) { ZONE_CONTAINER* Area_Ref = GetArea( ia ); - CPolyLine* refSmoothedPoly = Area_Ref->GetSmoothedPoly(); + SHAPE_POLY_SET* refSmoothedPoly = Area_Ref->GetSmoothedPoly(); if( !Area_Ref->IsOnCopperLayer() ) continue; @@ -341,7 +296,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E for( int ia2 = 0; ia2 < GetAreaCount(); ia2++ ) { ZONE_CONTAINER* area_to_test = GetArea( ia2 ); - CPolyLine* testSmoothedPoly = area_to_test->GetSmoothedPoly(); + SHAPE_POLY_SET* testSmoothedPoly = area_to_test->GetSmoothedPoly(); if( Area_Ref == area_to_test ) continue; @@ -375,16 +330,18 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E zone2zoneClearance = 1; // test for some corners of Area_Ref inside area_to_test - for( int ic = 0; ic < refSmoothedPoly->GetCornersCount(); ic++ ) + for( auto iterator = refSmoothedPoly->IterateWithHoles(); iterator; iterator++ ) { - int x = refSmoothedPoly->GetX( ic ); - int y = refSmoothedPoly->GetY( ic ); + VECTOR2I currentVertex = *iterator; - if( testSmoothedPoly->TestPointInside( x, y ) ) + if( testSmoothedPoly->Contains( currentVertex ) ) { // COPPERAREA_COPPERAREA error: copper area ref corner inside copper area if( aCreate_Markers ) { + int x = currentVertex.x; + int y = currentVertex.y; + wxString msg1 = Area_Ref->GetSelectMenuText(); wxString msg2 = area_to_test->GetSelectMenuText(); MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA, @@ -399,16 +356,18 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E } // test for some corners of area_to_test inside Area_Ref - for( int ic2 = 0; ic2 < testSmoothedPoly->GetCornersCount(); ic2++ ) + for( auto iterator = testSmoothedPoly->IterateWithHoles(); iterator; iterator++ ) { - int x = testSmoothedPoly->GetX( ic2 ); - int y = testSmoothedPoly->GetY( ic2 ); + VECTOR2I currentVertex = *iterator; - if( refSmoothedPoly->TestPointInside( x, y ) ) + if( refSmoothedPoly->Contains( currentVertex ) ) { // COPPERAREA_COPPERAREA error: copper area corner inside copper area ref if( aCreate_Markers ) { + int x = currentVertex.x; + int y = currentVertex.y; + wxString msg1 = area_to_test->GetSelectMenuText(); wxString msg2 = Area_Ref->GetSelectMenuText(); MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA, @@ -422,77 +381,55 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E } } - // now test spacing between areas - for( int icont = 0; icont < refSmoothedPoly->GetContoursCount(); icont++ ) + + // Iterate through all the segments of refSmoothedPoly + for( auto refIt = refSmoothedPoly->IterateSegmentsWithHoles(); refIt; refIt++ ) { - int ic_start = refSmoothedPoly->GetContourStart( icont ); - int ic_end = refSmoothedPoly->GetContourEnd( icont ); + // Build ref segment + SEG refSegment = *refIt; - for( int ic = ic_start; ic<=ic_end; ic++ ) + // Iterate through all the segments in testSmoothedPoly + for( auto testIt = testSmoothedPoly->IterateSegmentsWithHoles(); testIt; testIt++ ) { - int ax1 = refSmoothedPoly->GetX( ic ); - int ay1 = refSmoothedPoly->GetY( ic ); - int ax2, ay2; + // Build test segment + SEG testSegment = *testIt; - if( ic == ic_end ) - { - ax2 = refSmoothedPoly->GetX( ic_start ); - ay2 = refSmoothedPoly->GetY( ic_start ); - } - else - { - ax2 = refSmoothedPoly->GetX( ic + 1 ); - ay2 = refSmoothedPoly->GetY( ic + 1 ); - } + int x, y; - for( int icont2 = 0; icont2 < testSmoothedPoly->GetContoursCount(); icont2++ ) - { - int ic_start2 = testSmoothedPoly->GetContourStart( icont2 ); - int ic_end2 = testSmoothedPoly->GetContourEnd( icont2 ); + int ax1, ay1, ax2, ay2; + ax1 = refSegment.A.x; + ay1 = refSegment.A.y; + ax2 = refSegment.B.x; + ay2 = refSegment.B.y; - for( int ic2 = ic_start2; ic2<=ic_end2; ic2++ ) + int bx1, by1, bx2, by2; + bx1 = testSegment.A.x; + by1 = testSegment.A.y; + bx2 = testSegment.B.x; + by2 = testSegment.B.y; + + int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, + 0, + ax1, ay1, ax2, ay2, + 0, + zone2zoneClearance, + &x, &y ); + + if( d < zone2zoneClearance ) + { + // COPPERAREA_COPPERAREA error : intersect or too close + if( aCreate_Markers ) { - int bx1 = testSmoothedPoly->GetX( ic2 ); - int by1 = testSmoothedPoly->GetY( ic2 ); - int bx2, by2; - - if( ic2 == ic_end2 ) - { - bx2 = testSmoothedPoly->GetX( ic_start2 ); - by2 = testSmoothedPoly->GetY( ic_start2 ); - } - else - { - bx2 = testSmoothedPoly->GetX( ic2 + 1 ); - by2 = testSmoothedPoly->GetY( ic2 + 1 ); - } - - int x, y; - - int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, - 0, - ax1, ay1, ax2, ay2, - 0, - zone2zoneClearance, - &x, &y ); - - if( d < zone2zoneClearance ) - { - // COPPERAREA_COPPERAREA error : intersect or too close - if( aCreate_Markers ) - { - wxString msg1 = Area_Ref->GetSelectMenuText(); - wxString msg2 = area_to_test->GetSelectMenuText(); - MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_CLOSE_TO_COPPERAREA, - wxPoint( x, y ), - msg1, wxPoint( x, y ), - msg2, wxPoint( x, y ) ); - Add( marker ); - } - - nerrors++; - } + wxString msg1 = Area_Ref->GetSelectMenuText(); + wxString msg2 = area_to_test->GetSelectMenuText(); + MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_CLOSE_TO_COPPERAREA, + wxPoint( x, y ), + msg1, wxPoint( x, y ), + msg2, wxPoint( x, y ) ); + Add( marker ); } + + nerrors++; } } } @@ -507,31 +444,22 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex ) { if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer return true; + // Get polygon, contour and vertex index. + SHAPE_POLY_SET::VERTEX_INDEX index; - wxPoint start = aArea->GetCornerPosition( aCornerIndex ); - wxPoint end; + // If the vertex does not exist, there is no conflict + if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) ) + return true; - // Search the end point of the edge starting at aCornerIndex - if( aArea->Outline()->m_CornersList[aCornerIndex].end_contour == false - && aCornerIndex < (aArea->GetNumCorners() - 1) ) - { - end = aArea->GetCornerPosition( aCornerIndex + 1 ); - } - else // aCornerIndex is the last corner of an outline. - // the corresponding end point of the segment is the first corner of the outline - { - int ii = aCornerIndex - 1; - end = aArea->GetCornerPosition( ii ); + // Retrieve the selected contour + SHAPE_LINE_CHAIN contour; + contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour]; - while( ii >= 0 ) - { - if( aArea->Outline()->m_CornersList[ii].end_contour ) - break; + // Retrieve the segment that starts at aCornerIndex-th corner. + SEG selectedSegment = contour.Segment( index.m_vertex ); - end = aArea->GetCornerPosition( ii ); - ii--; - } - } + VECTOR2I start = selectedSegment.A; + VECTOR2I end = selectedSegment.B; // iterate through all areas for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ ) @@ -562,10 +490,10 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex ) zone_clearance = 1; // test for ending line inside area_to_test - if( area_to_test->Outline()->TestPointInside( end.x, end.y ) ) + if( area_to_test->Outline()->Contains( end ) ) { // COPPERAREA_COPPERAREA error: corner inside copper area - m_currentMarker = fillMarker( aArea, end, + m_currentMarker = fillMarker( aArea, static_cast( end ), COPPERAREA_INSIDE_COPPERAREA, m_currentMarker ); return false; @@ -577,45 +505,34 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex ) int ax2 = end.x; int ay2 = end.y; - for( int icont2 = 0; icont2 < area_to_test->Outline()->GetContoursCount(); icont2++ ) + // Iterate through all edges in the polygon. + SHAPE_POLY_SET::SEGMENT_ITERATOR iterator; + for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ ) { - int ic_start2 = area_to_test->Outline()->GetContourStart( icont2 ); - int ic_end2 = area_to_test->Outline()->GetContourEnd( icont2 ); + SEG segment = *iterator; - for( int ic2 = ic_start2; ic2<=ic_end2; ic2++ ) + int bx1 = segment.A.x; + int by1 = segment.A.y; + int bx2 = segment.B.x; + int by2 = segment.B.y; + + int x, y; // variables containing the intersecting point coordinates + int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, + 0, + ax1, ay1, ax2, ay2, + 0, + zone_clearance, + &x, &y ); + + if( d < zone_clearance ) { - int bx1 = area_to_test->Outline()->GetX( ic2 ); - int by1 = area_to_test->Outline()->GetY( ic2 ); - int bx2, by2; - - if( ic2 == ic_end2 ) - { - bx2 = area_to_test->Outline()->GetX( ic_start2 ); - by2 = area_to_test->Outline()->GetY( ic_start2 ); - } - else - { - bx2 = area_to_test->Outline()->GetX( ic2 + 1 ); - by2 = area_to_test->Outline()->GetY( ic2 + 1 ); - } - - int x, y; // variables containing the intersecting point coordinates - int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, - 0, - ax1, ay1, ax2, ay2, - 0, - zone_clearance, - &x, &y ); - - if( d < zone_clearance ) - { - // COPPERAREA_COPPERAREA error : edge intersect or too close - m_currentMarker = fillMarker( aArea, wxPoint( x, y ), - COPPERAREA_CLOSE_TO_COPPERAREA, - m_currentMarker ); - return false; - } + // COPPERAREA_COPPERAREA error : edge intersect or too close + m_currentMarker = fillMarker( aArea, wxPoint( x, y ), + COPPERAREA_CLOSE_TO_COPPERAREA, + m_currentMarker ); + return false; } + } } diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp index 304fba5bf2..50946ac184 100644 --- a/polygon/PolyLine.cpp +++ b/polygon/PolyLine.cpp @@ -354,7 +354,10 @@ CPolyLine* CPolyLine::Chamfer( unsigned int aDistance ) int nx2 = KiROUND( distance * xb / lenb ); int ny2 = KiROUND( distance * yb / lenb ); - newPoly->AppendCorner( x1 + nx2, y1 + ny2 ); + + // Due to rounding errors, repeated corners could be added; this check prevents it + if(nx1 != nx2 || ny1 != ny2) + newPoly->AppendCorner( x1 + nx2, y1 + ny2 ); } newPoly->CloseLastContour(); @@ -423,7 +426,7 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments ) double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 ); // Do nothing in case of parallel edges - if( !std::isfinite( denom ) ) + if( std::isinf( denom ) ) continue; // Limit rounding distance to one half of an edge @@ -480,11 +483,22 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments ) else newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) ); + // Store the previous added corner to make a sanity check + 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; - newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) ); + + // Due to rounding errors, repeated corners could be added; this check prevents it + if(KiROUND(nx) != prevX || KiROUND(ny) != prevY) + { + newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) ); + prevX = KiROUND(nx); + prevY = KiROUND(ny); + } } } @@ -1154,7 +1168,7 @@ int CPolyLine::HitTestForEdge( const wxPoint& aPos, int aDistMax ) const if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim ) { unsigned tmp = first_corner_pos; - first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline + first_corner_pos = end_segm; // first_corner_pos is the beginning of next outline end_segm = tmp; // end_segm is the beginning of the current outline }