CPolyLine -> SHAPE_POLY_SET refactor.

Removes the need of using the legacy code in polygon/PolyLine.{h,cpp},
refactoring all CPolyLine instances with SHAPE_POLY_SET instances.

The remaining legacy methods have been ported to SHAPE_POLY_SET;
mainly: Chamfer, Fillet, {,Un}Hatch.

The iteration over the polygon vertices have been simplified using the
family of ITERATOR classes.
This commit is contained in:
Alejandro García Montoro 2017-03-07 13:06:00 +01:00 committed by Maciej Suminski
parent 5aa1610362
commit f68ce306bd
36 changed files with 2556 additions and 673 deletions

View File

@ -1,8 +1,9 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
* *
* Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson, * Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson,
* subject to Clipper library license. * subject to Clipper library license.
@ -32,6 +33,8 @@
#include <list> #include <list>
#include <algorithm> #include <algorithm>
#include <common.h>
#include <geometry/shape.h> #include <geometry/shape.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include <geometry/shape_poly_set.h> #include <geometry/shape_poly_set.h>
@ -41,7 +44,12 @@ using namespace ClipperLib;
SHAPE_POLY_SET::SHAPE_POLY_SET() : SHAPE_POLY_SET::SHAPE_POLY_SET() :
SHAPE( SH_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() int SHAPE_POLY_SET::NewOutline()
{ {
SHAPE_LINE_CHAIN empty_path; SHAPE_LINE_CHAIN empty_path;
@ -66,13 +163,18 @@ int SHAPE_POLY_SET::NewHole( int aOutline )
SHAPE_LINE_CHAIN empty_path; SHAPE_LINE_CHAIN empty_path;
empty_path.SetClosed( true ); 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; 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 ) if( aOutline < 0 )
aOutline += m_polys.size(); 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( aOutline < (int)m_polys.size() );
assert( idx < (int)m_polys[aOutline].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(); 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 int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const
{ {
if( aOutline < 0 ) 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 ) assert( aFirstPolygon >= 0 && aLastPolygon <= OutlineCount() );
aOutline += m_polys.size();
int idx; SHAPE_POLY_SET newPolySet;
if( aHole < 0 ) for( int index = aFirstPolygon; index < aLastPolygon; index++ )
idx = 0; {
else newPolySet.m_polys.push_back( Polygon( index ) );
idx = aHole + 1; }
assert( aOutline < (int)m_polys.size() ); return newPolySet;
assert( idx < (int)m_polys[aOutline].size() );
return m_polys[aOutline][idx].CPoint( index );
} }
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 ) if( aOutline < 0 )
aOutline += m_polys.size(); 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( aOutline < (int)m_polys.size() );
assert( idx < (int)m_polys[aOutline].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++ ) for( unsigned int i = 0; i < aPath.size(); i++ )
lc.Append( aPath[i].X, aPath[i].Y ); lc.Append( aPath[i].X, aPath[i].Y );
lc.SetClosed( true );
return lc; return lc;
} }
void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape, void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape,
POLYGON_MODE aFastMode ) POLYGON_MODE aFastMode )
{ {
@ -363,7 +595,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree )
for( unsigned int i = 0; i < n->Childs.size(); i++ ) for( unsigned int i = 0; i < n->Childs.size(); i++ )
paths.push_back( convertFromClipper( n->Childs[i]->Contour ) ); 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<FractureEdge*> FractureEdgeSet; typedef std::vector<FractureEdge*> FractureEdgeSet;
static int processEdge( FractureEdgeSet& edges, FractureEdge* edge ) static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
{ {
int x = edge->m_p1.x; int x = edge->m_p1.x;
@ -478,6 +711,7 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
return 0; return 0;
} }
void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
{ {
FractureEdgeSet edges; 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 ) void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode )
{ {
SHAPE_POLY_SET empty; 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 const std::string SHAPE_POLY_SET::Format() const
{ {
std::stringstream ss; std::stringstream ss;
@ -607,10 +889,10 @@ const std::string SHAPE_POLY_SET::Format() const
for( unsigned i = 0; i < m_polys.size(); i++ ) for( unsigned i = 0; i < m_polys.size(); i++ )
{ {
ss << "poly " << m_polys[i].size() << "\n"; 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"; 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 << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n";
} }
ss << "\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() void SHAPE_POLY_SET::RemoveAllContours()
{ {
m_polys.clear(); 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 ) void SHAPE_POLY_SET::DeletePolygon( int aIdx )
{ {
m_polys.erase( m_polys.begin() + 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 bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const
{ {
// fixme: support holes!
if( m_polys.size() == 0 ) // empty set? if( m_polys.size() == 0 ) // empty set?
return false; return false;
// If there is a polygon specified, check the condition against that polygon
if( aSubpolyIndex >= 0 ) 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 ) if( containsSingle( aP, polygonIdx ) )
continue;
if( pointInPolygon( aP, polys[0] ) )
return true; 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; return false;
} }
@ -832,3 +1316,313 @@ int SHAPE_POLY_SET::TotalVertices() const
return c; 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;
}

View File

@ -70,6 +70,7 @@ principle should be easily implemented by adapting the current STL containers.
%ignore GetCommandOptions; %ignore GetCommandOptions;
%rename(getWxRect) operator wxRect; %rename(getWxRect) operator wxRect;
%rename(getBOX2I) operator BOX2I;
%ignore operator <<; %ignore operator <<;
%ignore operator=; %ignore operator=;

View File

@ -30,6 +30,7 @@
#define CLASS_EDA_RECT_H #define CLASS_EDA_RECT_H
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
#include <math/box2.h>
/** /**
* Class EDA_RECT * 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 * @return true if aPoint is inside the boundary box. A point on a edge is seen as inside
*/ */
bool Contains( const wxPoint& aPoint ) const; bool Contains( const wxPoint& aPoint ) const;
/** /**
* Function Contains * Function Contains
* @param x = the x coordinate of the point to test * @param x = the x coordinate of the point to test
@ -177,6 +179,18 @@ public:
return wxRect( rect.m_Pos, rect.m_Size ); 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 * Function Inflate
* inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx * inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx

View File

@ -38,6 +38,8 @@ class SEG
{ {
private: private:
typedef VECTOR2I::extended_type ecoord; typedef VECTOR2I::extended_type ecoord;
VECTOR2I m_a;
VECTOR2I m_b;
public: public:
friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg ); 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 * to an object the segment belongs to (e.g. a line chain) or references to locally stored
* points (m_a, m_b). * points (m_a, m_b).
*/ */
VECTOR2I A; VECTOR2I& A;
VECTOR2I B; VECTOR2I& B;
/** Default constructor /** Default constructor
* Creates an empty (0, 0) segment, locally-referenced * Creates an empty (0, 0) segment, locally-referenced
*/ */
SEG() SEG() :
A( m_a ),
B( m_b )
{ {
m_index = -1; m_index = -1;
} }
@ -62,9 +66,11 @@ public:
* Creates a segment between (aX1, aY1) and (aX2, aY2), locally referenced * Creates a segment between (aX1, aY1) and (aX2, aY2), locally referenced
*/ */
SEG( int aX1, int aY1, int aX2, int aY2 ) : SEG( int aX1, int aY1, int aX2, int aY2 ) :
A ( VECTOR2I( aX1, aY1 ) ), A( m_a ),
B ( VECTOR2I( aX2, aY2 ) ) B( m_b )
{ {
A = VECTOR2I( aX1, aY1 );
B = VECTOR2I( aX2, aY2 );
m_index = -1; m_index = -1;
} }
@ -72,11 +78,37 @@ public:
* Constructor * Constructor
* Creates a segment between (aA) and (aB), locally referenced * 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; 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 * Constructor
* Creates a segment between (aA) and (aB), referenced to a multi-segment shape * 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 aB reference to the end point in the parent shape
* @param aIndex index of the segment within 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; m_index = aIndex;
} }
/** /**
* Copy constructor * 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 ) SEG& operator=( const SEG& aSeg )

View File

@ -410,6 +410,16 @@ public:
*/ */
void Remove( int aStartIndex, int aEndIndex ); 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() * Function Split()
* *

View File

@ -1,8 +1,9 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 * Represents a set of closed polygons. Polygons may be nonconvex, self-intersecting
* and have holes. Provides boolean operations (using Clipper library as the backend). * 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 * TODO: add convex partitioning & spatial index
*/ */
class SHAPE_POLY_SET : public SHAPE class SHAPE_POLY_SET : public SHAPE
@ -48,39 +57,97 @@ class SHAPE_POLY_SET : public SHAPE
///> the remaining (if any), are the holes ///> the remaining (if any), are the holes
typedef std::vector<SHAPE_LINE_CHAIN> POLYGON; typedef std::vector<SHAPE_LINE_CHAIN> 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 * 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 T> template <class T>
class ITERATOR_TEMPLATE class ITERATOR_TEMPLATE
{ {
public: 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 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 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() void Advance()
{ {
// Advance vertex index
m_currentVertex ++; 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; // If the last vertex of the contour was reached, advance the contour index
m_currentOutline++; 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() 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*() T& operator*()
@ -109,22 +176,230 @@ class SHAPE_POLY_SET : public SHAPE
return &Get(); 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: private:
friend class SHAPE_POLY_SET; friend class SHAPE_POLY_SET;
SHAPE_POLY_SET* m_poly; SHAPE_POLY_SET* m_poly;
int m_currentOutline; int m_currentPolygon;
int m_lastOutline; int m_currentContour;
int m_currentVertex; 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 T>
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<T> 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<VECTOR2I> ITERATOR; typedef ITERATOR_TEMPLATE<VECTOR2I> ITERATOR;
typedef ITERATOR_TEMPLATE<const VECTOR2I> CONST_ITERATOR; typedef ITERATOR_TEMPLATE<const VECTOR2I> CONST_ITERATOR;
// Iterator and const iterator types to visit polygon's edges.
typedef SEGMENT_ITERATOR_TEMPLATE<SEG> SEGMENT_ITERATOR;
typedef SEGMENT_ITERATOR_TEMPLATE<const SEG> CONST_SEGMENT_ITERATOR;
SHAPE_POLY_SET(); 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(); ~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 ///> Creates a new empty polygon in the set and returns its index
int NewOutline(); int NewOutline();
@ -138,7 +413,20 @@ class SHAPE_POLY_SET : public SHAPE
int AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline = -1 ); 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) ///> 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. ///> Merges polygons from two sets.
void Append( const SHAPE_POLY_SET& aSet ); 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) ///> 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 ); 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 ///> 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(); bool IsSelfIntersecting();
///> Returns the number of outlines in the set ///> Returns the number of outlines in the set
@ -175,6 +509,22 @@ class SHAPE_POLY_SET : public SHAPE
return m_polys[aIndex][0]; 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 ///> Returns the reference to aHole-th hole in the aIndex-th outline
SHAPE_LINE_CHAIN& Hole( int aOutline, int aHole ) SHAPE_LINE_CHAIN& Hole( int aOutline, int aHole )
{ {
@ -202,39 +552,83 @@ class SHAPE_POLY_SET : public SHAPE
return m_polys[aIndex]; 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; ITERATOR iter;
iter.m_poly = this; iter.m_poly = this;
iter.m_currentOutline = aFirst; iter.m_currentPolygon = aFirst;
iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast; iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
iter.m_currentContour = 0;
iter.m_currentVertex = 0; iter.m_currentVertex = 0;
iter.m_iterateHoles = aIterateHoles;
return iter; 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 ) ITERATOR Iterate( int aOutline )
{ {
return Iterate( aOutline, 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() ITERATOR Iterate()
{ {
return Iterate( 0, OutlineCount() - 1 ); 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; CONST_ITERATOR iter;
iter.m_poly = const_cast<SHAPE_POLY_SET*>( this ); iter.m_poly = const_cast<SHAPE_POLY_SET*>( this );
iter.m_currentOutline = aFirst; iter.m_currentPolygon = aFirst;
iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast; iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
iter.m_currentContour = 0;
iter.m_currentVertex = 0; iter.m_currentVertex = 0;
iter.m_iterateHoles = aIterateHoles;
return iter; return iter;
} }
@ -244,11 +638,80 @@ class SHAPE_POLY_SET : public SHAPE
return CIterate( aOutline, aOutline ); return CIterate( aOutline, aOutline );
} }
CONST_ITERATOR CIterateWithHoles( int aOutline ) const
{
return CIterate( aOutline, aOutline, true );
}
CONST_ITERATOR CIterate() const CONST_ITERATOR CIterate() const
{ {
return CIterate( 0, OutlineCount() - 1 ); 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 /** operations on polygons use a aFastMode param
* if aFastMode is PM_FAST (true) the result can be a weak polygon * 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 * 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 ///> For aFastMode meaning, see function booleanOp
void Simplify( POLYGON_MODE aFastMode ); 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() /// @copydoc SHAPE::Format()
const std::string Format() const override; const std::string Format() const override;
@ -324,13 +796,59 @@ class SHAPE_POLY_SET : public SHAPE
const BOX2I BBox( int aClearance = 0 ) const override; 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 // 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; } 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; bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1 ) const;
///> Returns true if the set is empty (no polygons at all) ///> 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; 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. ///> Removes all outlines & holes (clears) the polygon set.
void RemoveAllContours(); 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. ///> Returns total number of vertices stored in the set.
int TotalVertices() const; int TotalVertices() const;
///> Deletes aIdx-th polygon from the set ///> Deletes aIdx-th polygon from the set
void DeletePolygon( int aIdx ); 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: private:
SHAPE_LINE_CHAIN& getContourForCorner( int aCornerId, int& aIndexWithinContour ); 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 ClipperLib::Path convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation );
const SHAPE_LINE_CHAIN convertFromClipper( const ClipperLib::Path& aPath ); 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<POLYGON> Polyset; typedef std::vector<POLYGON> Polyset;
Polyset m_polys; Polyset m_polys;

View File

@ -116,6 +116,16 @@ public:
return VECTOR2<CastedType>( (CastedType) x, (CastedType) y ); return VECTOR2<CastedType>( (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 /// Destructor
// virtual ~VECTOR2(); // virtual ~VECTOR2();

View File

@ -2361,7 +2361,8 @@ ZONE_CONTAINER* BOARD::InsertArea( int netcode, int iarea, LAYER_ID layer, int x
else else
m_ZoneDescriptorList.push_back( new_area ); 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; 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 ) 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 // mark all areas as unmodified except this one, if modified
for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ ) for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ )
m_ZoneDescriptorList[ia]->SetLocalFlags( 0 ); m_ZoneDescriptorList[ia]->SetLocalFlags( 0 );
aCurrArea->SetLocalFlags( 1 ); aCurrArea->SetLocalFlags( 1 );
if( curr_polygon->IsPolygonSelfIntersecting() ) if( aCurrArea->Outline()->IsSelfIntersecting() )
{ {
std::vector<CPolyLine*>* pa = new std::vector<CPolyLine*>; aCurrArea->UnHatch();
curr_polygon->UnHatch();
int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines( pa ); // 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 clipping has created some polygons, we must add these new copper areas.
if( n_poly > 1 ) if( n_poly > 1 )
{ {
ZONE_CONTAINER* NewArea; 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++ ) for( int ip = 1; ip < n_poly; ip++ )
{ {
// create new copper area and copy poly into it // Create new copper area and copy poly into it
CPolyLine* new_p = (*pa)[ip - 1]; SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( ip ) );
NewArea = AddArea( aNewZonesList, aCurrArea->GetNetCode(), aCurrArea->GetLayer(), 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 // remove the poly that was automatically created for the new area
// and replace it with a poly from NormalizeAreaOutlines // and replace it with a poly from NormalizeAreaOutlines
delete NewArea->Outline(); delete NewArea->Outline();
NewArea->SetOutline( new_p ); NewArea->SetOutline( new_p );
NewArea->Outline()->Hatch(); NewArea->Hatch();
NewArea->SetLocalFlags( 1 ); 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; return true;
} }

View File

@ -53,7 +53,7 @@
ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) : ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) :
BOARD_CONNECTED_ITEM( aBoard, PCB_ZONE_AREA_T ) 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_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_FillMode = 0; // How to fill areas: 0 = use filled polygons, != 0 fill with segments
m_priority = 0; m_priority = 0;
@ -65,7 +65,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) :
SetDoNotAllowTracks( true ); // has meaning only if m_isKeepout == true SetDoNotAllowTracks( true ); // has meaning only if m_isKeepout == true
m_cornerRadius = 0; m_cornerRadius = 0;
SetLocalFlags( 0 ); // flags tempoarry used in zone calculations SetLocalFlags( 0 ); // flags tempoarry used in zone calculations
m_Poly = new CPolyLine(); // Outlines m_Poly = new SHAPE_POLY_SET(); // Outlines
aBoard->GetZoneSettings().ExportSetting( *this ); aBoard->GetZoneSettings().ExportSetting( *this );
} }
@ -77,10 +77,10 @@ ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) :
// Should the copy be on the same net? // Should the copy be on the same net?
SetNetCode( aZone.GetNetCode() ); 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 // For corner moving, corner index to drag, or nullptr if no selection
m_CornerSelection = -1; m_CornerSelection = nullptr;
m_IsFilled = aZone.m_IsFilled; m_IsFilled = aZone.m_IsFilled;
m_ZoneClearance = aZone.m_ZoneClearance; // clearance value m_ZoneClearance = aZone.m_ZoneClearance; // clearance value
m_ZoneMinThickness = aZone.m_ZoneMinThickness; m_ZoneMinThickness = aZone.m_ZoneMinThickness;
@ -101,6 +101,10 @@ ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) :
m_cornerSmoothingType = aZone.m_cornerSmoothingType; m_cornerSmoothingType = aZone.m_cornerSmoothingType;
m_cornerRadius = aZone.m_cornerRadius; m_cornerRadius = aZone.m_cornerRadius;
m_hatchStyle = aZone.m_hatchStyle;
m_hatchPitch = aZone.m_hatchPitch;
m_HatchLines = aZone.m_HatchLines;
SetLocalFlags( aZone.GetLocalFlags() ); SetLocalFlags( aZone.GetLocalFlags() );
} }
@ -109,9 +113,11 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
{ {
BOARD_CONNECTED_ITEM::operator=( aOther ); BOARD_CONNECTED_ITEM::operator=( aOther );
m_Poly->RemoveAllContours(); // Replace the outlines for aOther outlines.
m_Poly->Copy( aOther.m_Poly ); // copy outlines delete m_Poly;
m_CornerSelection = -1; // for corner moving, corner index to drag or -1 if no selection 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_ZoneClearance = aOther.m_ZoneClearance; // clearance value
m_ZoneMinThickness = aOther.m_ZoneMinThickness; m_ZoneMinThickness = aOther.m_ZoneMinThickness;
m_FillMode = aOther.m_FillMode; // filling mode (segments/polygons) 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_PadConnection = aOther.m_PadConnection;
m_ThermalReliefGap = aOther.m_ThermalReliefGap; m_ThermalReliefGap = aOther.m_ThermalReliefGap;
m_ThermalReliefCopperBridge = aOther.m_ThermalReliefCopperBridge; m_ThermalReliefCopperBridge = aOther.m_ThermalReliefCopperBridge;
m_Poly->SetHatchStyle( aOther.m_Poly->GetHatchStyle() ); SetHatchStyle( aOther.GetHatchStyle() );
m_Poly->SetHatchPitch( aOther.m_Poly->GetHatchPitch() ); SetHatchPitch( aOther.GetHatchPitch() );
m_Poly->m_HatchLines = aOther.m_Poly->m_HatchLines; // copy vector <CSegment> m_HatchLines = aOther.m_HatchLines; // copy vector <SEG>
m_FilledPolysList.RemoveAllContours(); m_FilledPolysList.RemoveAllContours();
m_FilledPolysList.Append( aOther.m_FilledPolysList ); m_FilledPolysList.Append( aOther.m_FilledPolysList );
m_FillSegmList.clear(); m_FillSegmList.clear();
@ -134,7 +140,8 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
ZONE_CONTAINER::~ZONE_CONTAINER() ZONE_CONTAINER::~ZONE_CONTAINER()
{ {
delete m_Poly; 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 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<const WX_VECTOR_CONVERTER*>( &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; color.a = 0.588;
// draw the lines // draw the lines
int i_start_contour = 0;
std::vector<wxPoint> lines; std::vector<wxPoint> lines;
lines.reserve( (GetNumCorners() * 2) + 2 ); 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 ) lines.push_back( static_cast<wxPoint>( segment.A ) + offset );
{ lines.push_back( static_cast<wxPoint>( segment.B ) + offset );
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 );
} }
GRLineArray( panel->GetClipBox(), DC, lines, 0, color ); GRLineArray( panel->GetClipBox(), DC, lines, 0, color );
// draw hatches // draw hatches
lines.clear(); 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_start = static_cast<wxPoint>( m_HatchLines[ic].A ) + offset;
seg_end = m_Poly->m_HatchLines[ic].m_End + offset; seg_end = static_cast<wxPoint>( m_HatchLines[ic].B ) + offset;
lines.push_back( seg_start ); lines.push_back( seg_start );
lines.push_back( seg_end ); lines.push_back( seg_end );
} }
@ -357,7 +359,7 @@ const EDA_RECT ZONE_CONTAINER::GetBoundingBox() const
for( int i = 0; i<count; ++i ) for( int i = 0; i<count; ++i )
{ {
wxPoint corner = GetCornerPosition( i ); wxPoint corner = static_cast<wxPoint>( GetCornerPosition( i ) );
ymax = std::max( ymax, corner.y ); ymax = std::max( ymax, corner.y );
xmax = std::max( xmax, corner.x ); xmax = std::max( xmax, corner.x );
@ -391,45 +393,63 @@ void ZONE_CONTAINER::DrawWhileCreateOutline( EDA_DRAW_PANEL* panel, wxDC* DC,
color = COLOR4D( DARKDARKGRAY ); color = COLOR4D( DARKDARKGRAY );
} }
// draw the lines // Object to iterate through the corners of the outlines
wxPoint start_contour_pos = GetCornerPosition( 0 ); SHAPE_POLY_SET::ITERATOR iterator = m_Poly->Iterate();
int icmax = GetNumCorners() - 1;
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; // Get the first point of the current segment
int yi = GetCornerPosition( ic ).y; seg_start = *iterator;
int xf, yf;
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; // Set GR mode to default
xf = GetCornerPosition( ic + 1 ).x; current_gr_mode = draw_mode;
yf = GetCornerPosition( ic + 1 ).y;
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; 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; 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 seg_end = contour_first_point;
if( ic < icmax )
start_contour_pos = GetCornerPosition( ic + 1 ); // 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 ); GRSetDrawMode( DC, current_gr_mode );
if( is_close_segment ) 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 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 bool ZONE_CONTAINER::HitTest( const wxPoint& aPosition ) const
{ {
if( HitTestForCorner( aPosition ) >= 0 ) return HitTestForCorner( aPosition ) || HitTestForEdge( aPosition );
return true;
if( HitTestForEdge( aPosition ) >= 0 )
return true;
return false;
} }
void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition ) void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition )
{ {
m_CornerSelection = HitTestForCorner( aPosition ); SHAPE_POLY_SET::VERTEX_INDEX corner;
if( m_CornerSelection < 0 ) // If there is some corner to be selected, assign it to m_CornerSelection
m_CornerSelection = HitTestForEdge( aPosition ); 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 // Zones outlines have no thickness, so it Hit Test functions
// we must have a default distance between the test point // we must have a default distance between the test point
// and a corner or a zone edge: // and a corner or a zone edge:
#define MAX_DIST_IN_MM 0.25 #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 ); 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 ); 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 bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
{ {
EDA_RECT arect = aRect; // Convert to BOX2I
arect.Inflate( aAccuracy ); BOX2I aBox = aRect;
EDA_RECT bbox = m_Poly->GetBoundingBox(); aBox.Inflate( aAccuracy );
BOX2I bbox = m_Poly->BBox();
bbox.Normalize(); bbox.Normalize();
if( aContained ) if( aContained )
return arect.Contains( bbox ); return aBox.Contains( bbox );
else // Test for intersection between aRect and the polygon else // Test for intersection between aBox and the polygon
// For a polygon, using its bounding box has no sense here // 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 // rectangles cannot intersect
if( ! bbox.Intersects( arect ) ) if( ! bbox.Intersects( aBox ) )
return false; 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. // 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 // is inside the polygon
wxPoint corner = arect.GetOrigin(); wxPoint corner = static_cast<wxPoint>( aBox.GetOrigin() );
if( HitTestInsideZone( corner ) ) if( HitTestInsideZone( corner ) )
return true; return true;
corner.x = arect.GetEnd().x; corner.x = aBox.GetEnd().x;
if( HitTestInsideZone( corner ) ) if( HitTestInsideZone( corner ) )
return true; return true;
corner = arect.GetEnd(); corner = static_cast<wxPoint>( aBox.GetEnd() );
if( HitTestInsideZone( corner ) ) if( HitTestInsideZone( corner ) )
return true; return true;
corner.x = arect.GetOrigin().x; corner.x = aBox.GetOrigin().x;
if( HitTestInsideZone( corner ) ) if( HitTestInsideZone( corner ) )
return true; return true;
// No corner inside arect, but outlines can intersect arect // No corner inside aBox, but outlines can intersect aBox
// if one of outline corners is inside arect // if one of outline corners is inside aBox
int count = m_Poly->GetCornersCount(); int count = m_Poly->TotalVertices();
for( int ii =0; ii < count; ii++ ) for( int ii =0; ii < count; ii++ )
{ {
if( arect.Contains( m_Poly->GetPos( ii ) ) ) if( aBox.Contains( m_Poly->Vertex( ii ) ) )
return true; 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 // Display Cutout instead of Outline for holes inside a zone
// i.e. when num contour !=0 // i.e. when num contour !=0
int ncont = m_Poly->GetContour( m_CornerSelection ); // 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 )
if( ncont )
msg << wxT( " " ) << _( "(Cutout)" ); msg << wxT( " " ) << _( "(Cutout)" );
aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) ); 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 ) ); 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 ) ); aList.push_back( MSG_PANEL_ITEM( _( "Corners" ), msg, BLUE ) );
if( m_FillMode ) 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 ) ); aList.push_back( MSG_PANEL_ITEM( _( "Fill Mode" ), msg, BROWN ) );
// Useful for statistics : // 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 ) ); aList.push_back( MSG_PANEL_ITEM( _( "Hatch Lines" ), msg, BLUE ) );
if( !m_FilledPolysList.IsEmpty() ) 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 ) void ZONE_CONTAINER::Move( const wxPoint& offset )
{ {
/* move outlines */ /* move outlines */
for( unsigned ii = 0; ii < m_Poly->m_CornersList.GetCornersCount(); ii++ ) m_Poly->Move( VECTOR2I( offset ) );
{
SetCornerPosition( ii, GetCornerPosition( ii ) + offset );
}
m_Poly->Hatch(); Hatch();
m_FilledPolysList.Move( VECTOR2I( offset.x, offset.y ) ); 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 ) void ZONE_CONTAINER::MoveEdge( const wxPoint& offset, int aEdge )
{ {
// Move the start point of the selected edge: m_Poly->Edge( aEdge ).A += VECTOR2I( offset );
SetCornerPosition( aEdge, GetCornerPosition( aEdge ) + offset ); m_Poly->Edge( aEdge ).B += VECTOR2I( offset );
// Move the end point of the selected edge: Hatch();
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();
} }
@ -718,18 +740,18 @@ void ZONE_CONTAINER::Rotate( const wxPoint& centre, double angle )
{ {
wxPoint pos; 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<wxPoint>( *iterator );
RotatePoint( &pos, centre, angle ); RotatePoint( &pos, centre, angle );
m_Poly->SetX( ic, pos.x ); iterator->x = pos.x;
m_Poly->SetY( ic, pos.y ); iterator->y = pos.y;
} }
m_Poly->Hatch(); Hatch();
/* rotate filled areas: */ /* 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 ); RotatePoint( &ic->x, &ic->y, centre.x, centre.y, angle );
for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ ) 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 ) 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 ); int py = mirror_ref.y - iterator->y;
m_Poly->m_CornersList.SetY( ic, py + mirror_ref.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; int py = mirror_ref.y - ic->y;
ic->y = py + mirror_ref.y; ic->y = py + mirror_ref.y;
@ -786,29 +808,29 @@ void ZONE_CONTAINER::AddPolygon( std::vector< wxPoint >& aPolygon )
if( aPolygon.empty() ) if( aPolygon.empty() )
return; return;
SHAPE_LINE_CHAIN outline;
// Create an outline and populate it with the points of aPolygon
for( unsigned i = 0; i < aPolygon.size(); i++ ) for( unsigned i = 0; i < aPolygon.size(); i++ )
{ {
if( i == 0 ) outline.Append( VECTOR2I( aPolygon[i] ) );
m_Poly->Start( GetLayer(), aPolygon[i].x, aPolygon[i].y, GetHatchStyle() );
else
AppendCorner( 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 ZONE_CONTAINER::GetSelectMenuText() const
{ {
wxString text; wxString text;
NETINFO_ITEM* net; NETINFO_ITEM* net;
BOARD* board = GetBoard(); BOARD* board = GetBoard();
int ncont = m_Poly->GetContour( m_CornerSelection ); // Check whether the selected contour is a hole (contour index > 0)
if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 )
if( ncont ) text << wxT( " " ) << _( "(Cutout)" );
text << wxT( " " ) << _( "(Cutout)" );
if( GetIsKeepout() ) if( GetIsKeepout() )
text << wxT( " " ) << _( "(Keepout)" ); 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<VECTOR2I> 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 BITMAP_DEF ZONE_CONTAINER::GetMenuImage() const
{ {
return add_zone_xpm; return add_zone_xpm;

View File

@ -1,4 +1,3 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
@ -38,6 +37,7 @@
#include <class_board_connected_item.h> #include <class_board_connected_item.h>
#include <layers_id_colors_and_visibility.h> #include <layers_id_colors_and_visibility.h>
#include <PolyLine.h> #include <PolyLine.h>
#include <geometry/shape_poly_set.h>
#include <class_zone_settings.h> #include <class_zone_settings.h>
@ -79,6 +79,11 @@ class ZONE_CONTAINER : public BOARD_CONNECTED_ITEM
{ {
public: public:
/**
* Zone hatch styles
*/
typedef enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE } HATCH_STYLE;
ZONE_CONTAINER( BOARD* parent ); ZONE_CONTAINER( BOARD* parent );
ZONE_CONTAINER( const ZONE_CONTAINER& aZone ); ZONE_CONTAINER( const ZONE_CONTAINER& aZone );
@ -88,6 +93,14 @@ public:
/** /**
* Function GetPosition * 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 * @return a wxPoint, position of the first point of the outline
*/ */
const wxPoint& GetPosition() const override; const wxPoint& GetPosition() const override;
@ -196,8 +209,30 @@ public:
int GetMinThickness() const { return m_ZoneMinThickness; } int GetMinThickness() const { return m_ZoneMinThickness; }
void SetMinThickness( int aMinThickness ) { m_ZoneMinThickness = aMinThickness; } void SetMinThickness( int aMinThickness ) { m_ZoneMinThickness = aMinThickness; }
int GetSelectedCorner() const { return m_CornerSelection; } int GetSelectedCorner() const
void SetSelectedCorner( int aCorner ) { m_CornerSelection = aCorner; } {
// 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 // Like HitTest but selects the current corner to be operated on
@ -209,10 +244,10 @@ public:
std::vector <SEGMENT>& FillSegments() { return m_FillSegmList; } std::vector <SEGMENT>& FillSegments() { return m_FillSegmList; }
const std::vector <SEGMENT>& FillSegments() const { return m_FillSegmList; } const std::vector <SEGMENT>& FillSegments() const { return m_FillSegmList; }
CPolyLine* Outline() { return m_Poly; } SHAPE_POLY_SET* Outline() { return m_Poly; }
const CPolyLine* Outline() const { return const_cast< CPolyLine* >( 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 * Function HitTest
@ -231,7 +266,7 @@ public:
*/ */
bool HitTestInsideZone( const wxPoint& aPosition ) const 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. * if both aMinClearanceValue = 0 and aUseNetClearance = false: create the zone outline polygon.
*/ */
void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aMinClearanceValue, int aMinClearanceValue,
bool aUseNetClearance ); bool aUseNetClearance );
/** /**
* Function HitTestForCorner * Function HitTestForCorner
* tests if the given wxPoint near a corner * tests if the given wxPoint is near a corner.
* Set m_CornerSelection to -1 if nothing found, or index of corner * @param refPos is the wxPoint to test.
* @return true if found * @param aCornerHit [out] is the index of the closest vertex found, useless when return
* @param refPos : A wxPoint to test * 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 * Function HitTestForEdge
* tests if the given wxPoint is near a segment defined by 2 corners. * 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 * @param refPos is the wxPoint to test.
* @return true if found * @param aCornerHit [out] is the index of the closest vertex found, useless when return
* @param refPos : A wxPoint to test * 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, /** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect,
* bool aContained = true, int aAccuracy ) const * bool aContained = true, int aAccuracy ) const
@ -410,7 +465,37 @@ public:
int GetNumCorners( void ) const 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 ) void RemoveAllContours( void )
@ -418,30 +503,62 @@ public:
m_Poly->RemoveAllContours(); 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 ) void SetCornerPosition( int aCornerIndex, wxPoint new_pos )
{ {
m_Poly->SetX( aCornerIndex, new_pos.x ); SHAPE_POLY_SET::VERTEX_INDEX relativeIndices;
m_Poly->SetY( aCornerIndex, new_pos.y );
// 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 * Function GetSmoothedPoly
* returns a pointer to the corner-smoothed version of * returns a pointer to the corner-smoothed version of
* m_Poly if it exists, otherwise it returns m_Poly. * 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 ) if( m_smoothedPoly )
return m_smoothedPoly; return m_smoothedPoly;
@ -534,6 +651,58 @@ public:
void SetDoNotAllowVias( bool aEnable ) { m_doNotAllowVias = aEnable; } void SetDoNotAllowVias( bool aEnable ) { m_doNotAllowVias = aEnable; }
void SetDoNotAllowTracks( bool aEnable ) { m_doNotAllowTracks = 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<SEG>& GetHatchLines() const { return m_HatchLines; }
#if defined(DEBUG) #if defined(DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif #endif
@ -543,8 +712,8 @@ public:
private: private:
void buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ); void buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures );
CPolyLine* m_Poly; ///< Outline of the zone. SHAPE_POLY_SET* m_Poly; ///< Outline of the zone.
CPolyLine* m_smoothedPoly; // Corner-smoothed version of m_Poly SHAPE_POLY_SET* m_smoothedPoly; // Corner-smoothed version of m_Poly
int m_cornerSmoothingType; int m_cornerSmoothingType;
unsigned int m_cornerRadius; unsigned int m_cornerRadius;
@ -587,8 +756,8 @@ private:
/// How to fill areas: 0 => use filled polygons, 1 => fill with segments. /// How to fill areas: 0 => use filled polygons, 1 => fill with segments.
int m_FillMode; int m_FillMode;
/// The index of the corner being moved or -1 if no corner is selected. /// The index of the corner being moved or nullptr if no corner is selected.
int m_CornerSelection; SHAPE_POLY_SET::VERTEX_INDEX* m_CornerSelection;
/// Variable used in polygon calculations. /// Variable used in polygon calculations.
int m_localFlgs; int m_localFlgs;
@ -602,14 +771,52 @@ private:
* from outlines (m_Poly) but unlike m_Poly these filled polygons have no hole * 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 * (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 * 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 * a polygon equivalent to m_Poly, without holes but with extra outline segment
* connecting "holes" with external main outline. In complex cases an outline * connecting "holes" with external main outline. In complex cases an outline
* described by m_Poly can have many filled areas * 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<SEG> 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");
}; };

View File

@ -45,7 +45,7 @@ ZONE_SETTINGS::ZONE_SETTINGS()
m_ZoneMinThickness = Mils2iu( ZONE_THICKNESS_MIL ); m_ZoneMinThickness = Mils2iu( ZONE_THICKNESS_MIL );
m_NetcodeSelection = 0; // Net code selection for the current zone m_NetcodeSelection = 0; // Net code selection for the current zone
m_CurrentZone_Layer = F_Cu; // Layer used to create 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 m_ArcToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; // Option to select number of segments to approximate a circle
// ARC_APPROX_SEGMENTS_COUNT_LOW_DEF // 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.SetPriority( m_ZonePriority );
aTarget.SetNetCode( m_NetcodeSelection ); aTarget.SetNetCode( m_NetcodeSelection );
aTarget.SetLayer( m_CurrentZone_Layer ); aTarget.SetLayer( m_CurrentZone_Layer );
aTarget.Outline()->SetLayer( m_CurrentZone_Layer );
} }
// call SetHatch last, because hatch lines will be rebuilt, // call SetHatch last, because hatch lines will be rebuilt,
// using new parameters values // using new parameters values
aTarget.Outline()->SetHatch( m_Zone_HatchingStyle, Mils2iu( 20 ), true ); aTarget.SetHatch( m_Zone_HatchingStyle, Mils2iu( aTarget.GetDefaultHatchPitchMils() ), true );
} }

View File

@ -214,15 +214,15 @@ void DIALOG_COPPER_ZONE::initDialog()
switch( m_settings.m_Zone_HatchingStyle ) switch( m_settings.m_Zone_HatchingStyle )
{ {
case CPolyLine::NO_HATCH: case ZONE_CONTAINER::NO_HATCH:
m_OutlineAppearanceCtrl->SetSelection( 0 ); m_OutlineAppearanceCtrl->SetSelection( 0 );
break; break;
case CPolyLine::DIAGONAL_EDGE: case ZONE_CONTAINER::DIAGONAL_EDGE:
m_OutlineAppearanceCtrl->SetSelection( 1 ); m_OutlineAppearanceCtrl->SetSelection( 1 );
break; break;
case CPolyLine::DIAGONAL_FULL: case ZONE_CONTAINER::DIAGONAL_FULL:
m_OutlineAppearanceCtrl->SetSelection( 2 ); m_OutlineAppearanceCtrl->SetSelection( 2 );
break; break;
} }
@ -355,15 +355,15 @@ bool DIALOG_COPPER_ZONE::AcceptOptions( bool aPromptForErrors, bool aUseExportab
switch( m_OutlineAppearanceCtrl->GetSelection() ) switch( m_OutlineAppearanceCtrl->GetSelection() )
{ {
case 0: case 0:
m_settings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH; m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH;
break; break;
case 1: case 1:
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE;
break; break;
case 2: case 2:
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL; m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL;
break; break;
} }

View File

@ -33,6 +33,7 @@
#include <confirm.h> #include <confirm.h>
#include <pcbnew.h> #include <pcbnew.h>
#include <wxPcbStruct.h> #include <wxPcbStruct.h>
#include <class_zone.h>
#include <zones.h> #include <zones.h>
#include <base_units.h> #include <base_units.h>
@ -131,15 +132,15 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog()
switch( m_zonesettings.m_Zone_HatchingStyle ) switch( m_zonesettings.m_Zone_HatchingStyle )
{ {
case CPolyLine::NO_HATCH: case ZONE_CONTAINER::NO_HATCH:
m_OutlineAppearanceCtrl->SetSelection( 0 ); m_OutlineAppearanceCtrl->SetSelection( 0 );
break; break;
case CPolyLine::DIAGONAL_EDGE: case ZONE_CONTAINER::DIAGONAL_EDGE:
m_OutlineAppearanceCtrl->SetSelection( 1 ); m_OutlineAppearanceCtrl->SetSelection( 1 );
break; break;
case CPolyLine::DIAGONAL_FULL: case ZONE_CONTAINER::DIAGONAL_FULL:
m_OutlineAppearanceCtrl->SetSelection( 2 ); m_OutlineAppearanceCtrl->SetSelection( 2 );
break; break;
} }
@ -226,15 +227,15 @@ bool DIALOG_KEEPOUT_AREA_PROPERTIES::AcceptOptionsForKeepOut()
switch( m_OutlineAppearanceCtrl->GetSelection() ) switch( m_OutlineAppearanceCtrl->GetSelection() )
{ {
case 0: case 0:
m_zonesettings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH; m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH;
break; break;
case 1: case 1:
m_zonesettings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE;
break; break;
case 2: case 2:
m_zonesettings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL; m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL;
break; break;
} }

View File

@ -121,15 +121,15 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::Init()
switch( m_settings.m_Zone_HatchingStyle ) switch( m_settings.m_Zone_HatchingStyle )
{ {
case CPolyLine::NO_HATCH: case ZONE_CONTAINER::NO_HATCH:
m_OutlineAppearanceCtrl->SetSelection( 0 ); m_OutlineAppearanceCtrl->SetSelection( 0 );
break; break;
case CPolyLine::DIAGONAL_EDGE: case ZONE_CONTAINER::DIAGONAL_EDGE:
m_OutlineAppearanceCtrl->SetSelection( 1 ); m_OutlineAppearanceCtrl->SetSelection( 1 );
break; break;
case CPolyLine::DIAGONAL_FULL: case ZONE_CONTAINER::DIAGONAL_FULL:
m_OutlineAppearanceCtrl->SetSelection( 2 ); m_OutlineAppearanceCtrl->SetSelection( 2 );
break; break;
} }
@ -202,15 +202,15 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::OnOkClick( wxCommandEvent& event )
switch( m_OutlineAppearanceCtrl->GetSelection() ) switch( m_OutlineAppearanceCtrl->GetSelection() )
{ {
case 0: case 0:
m_settings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH; m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH;
break; break;
case 1: case 1:
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE;
break; break;
case 2: case 2:
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL; m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL;
break; break;
} }

View File

@ -1,4 +1,3 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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() ) if( segm->GetLayer() != area->GetLayer() )
continue; continue;
if( area->Outline()->Distance( segm->GetStart(), segm->GetEnd(), if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ),
segm->GetWidth() ) == 0 ) segm->GetWidth() ) == 0 )
{ {
addMarkerToPcb( fillMarker( segm, NULL, addMarkerToPcb( fillMarker( segm, NULL,
@ -821,7 +820,7 @@ bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
if( aRefSeg->GetLayer() != area->GetLayer() ) if( aRefSeg->GetLayer() != area->GetLayer() )
continue; continue;
if( area->Outline()->Distance( aRefSeg->GetStart(), aRefSeg->GetEnd(), if( area->Outline()->Distance( SEG( aRefSeg->GetStart(), aRefSeg->GetEnd() ),
aRefSeg->GetWidth() ) == 0 ) aRefSeg->GetWidth() ) == 0 )
{ {
m_currentMarker = fillMarker( aRefSeg, NULL, m_currentMarker = fillMarker( aRefSeg, NULL,
@ -1056,7 +1055,7 @@ bool DRC::doFootprintOverlappingDrc()
msg.Printf( _( "footprints '%s' and '%s' overlap on front (top) layer" ), msg.Printf( _( "footprints '%s' and '%s' overlap on front (top) layer" ),
footprint->GetReference().GetData(), footprint->GetReference().GetData(),
candidate->GetReference().GetData() ); candidate->GetReference().GetData() );
VECTOR2I& pos = courtyard.Vertex( 0, 0 ); VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
wxPoint loc( pos.x, pos.y ); wxPoint loc( pos.x, pos.y );
m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker ); m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker );
addMarkerToPcb( m_currentMarker ); addMarkerToPcb( m_currentMarker );
@ -1091,7 +1090,7 @@ bool DRC::doFootprintOverlappingDrc()
msg.Printf( _( "footprints '%s' and '%s' overlap on back (bottom) layer" ), msg.Printf( _( "footprints '%s' and '%s' overlap on back (bottom) layer" ),
footprint->GetReference().GetData(), footprint->GetReference().GetData(),
candidate->GetReference().GetData() ); candidate->GetReference().GetData() );
VECTOR2I& pos = courtyard.Vertex( 0, 0 ); VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
wxPoint loc( pos.x, pos.y ); wxPoint loc( pos.x, pos.y );
m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker ); m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker );
addMarkerToPcb( m_currentMarker ); addMarkerToPcb( m_currentMarker );

View File

@ -1597,17 +1597,15 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics )
zone->SetLayer( layer ); zone->SetLayer( layer );
zone->SetNetCode( NETINFO_LIST::UNCONNECTED ); 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.y1 ) ) );
zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ) ); zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ) );
zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) ); zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
zone->Outline()->CloseLastContour();
// this is not my fault: // this is not my fault:
zone->Outline()->SetHatch( zone->SetHatch( outline_hatch, Mils2iu( zone->GetDefaultHatchPitchMils() ), true );
outline_hatch, Mils2iu( zone->Outline()->GetDefaultHatchPitchMils() ), true );
} }
m_xpath->pop(); m_xpath->pop();
@ -2736,7 +2734,6 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals )
zone->SetLayer( layer ); zone->SetLayer( layer );
zone->SetNetCode( netCode ); zone->SetNetCode( netCode );
bool first = true;
for( CITER vi = it->second.begin(); vi != it->second.end(); ++vi ) for( CITER vi = it->second.begin(); vi != it->second.end(); ++vi )
{ {
if( vi->first != "vertex" ) // skip <xmlattr> node if( vi->first != "vertex" ) // skip <xmlattr> node
@ -2744,32 +2741,21 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals )
EVERTEX v( vi->second ); EVERTEX v( vi->second );
// the ZONE_CONTAINER API needs work, as you can see: // Append the corner
if( first ) zone->AppendCorner( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) );
{
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 ) ) );
} }
zone->Outline()->CloseLastContour();
// If the pour is a cutout it needs to be set to a keepout // If the pour is a cutout it needs to be set to a keepout
if( p.pour == EPOLYGON::CUTOUT ) if( p.pour == EPOLYGON::CUTOUT )
{ {
zone->SetIsKeepout( true ); zone->SetIsKeepout( true );
zone->SetDoNotAllowCopperPour( 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 spacing is set the zone should be hatched
if( p.spacing ) if( p.spacing )
zone->Outline()->SetHatch( CPolyLine::DIAGONAL_EDGE, zone->SetHatch( ZONE_CONTAINER::DIAGONAL_EDGE, *p.spacing, true );
*p.spacing,
true );
// clearances, etc. // clearances, etc.
zone->SetArcSegmentCount( 32 ); // @todo: should be a constructor default? zone->SetArcSegmentCount( 32 ); // @todo: should be a constructor default?

View File

@ -613,7 +613,7 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
* and start move the new corner * and start move the new corner
*/ */
zone_cont->Draw( m_canvas, &dc, GR_XOR ); 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->SetSelectedCorner( zone_cont->GetSelectedCorner() + 1 );
zone_cont->Draw( m_canvas, &dc, GR_XOR ); zone_cont->Draw( m_canvas, &dc, GR_XOR );
m_canvas->SetAutoPanRequest( true ); m_canvas->SetAutoPanRequest( true );

View File

@ -1518,13 +1518,13 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
switch( aZone->GetHatchStyle() ) switch( aZone->GetHatchStyle() )
{ {
default: default:
case CPolyLine::NO_HATCH: hatch = "none"; break; case ZONE_CONTAINER::NO_HATCH: hatch = "none"; break;
case CPolyLine::DIAGONAL_EDGE: hatch = "edge"; break; case ZONE_CONTAINER::DIAGONAL_EDGE: hatch = "edge"; break;
case CPolyLine::DIAGONAL_FULL: hatch = "full"; break; case ZONE_CONTAINER::DIAGONAL_FULL: hatch = "full"; break;
} }
m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(), 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 ) if( aZone->GetPriority() > 0 )
m_out->Print( aNestLevel+1, "(priority %d)\n", aZone->GetPriority() ); 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" ); m_out->Print( 0, ")\n" );
const CPOLYGONS_LIST& cv = aZone->Outline()->m_CornersList;
int newLine = 0; int newLine = 0;
if( cv.GetCornersCount() ) if( aZone->GetNumCorners() )
{ {
m_out->Print( aNestLevel+1, "(polygon\n"); m_out->Print( aNestLevel+1, "(polygon\n");
m_out->Print( aNestLevel+2, "(pts\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 ) if( newLine == 0 )
m_out->Print( aNestLevel+3, "(xy %s %s)", 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 else
m_out->Print( 0, " (xy %s %s)", 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 ) if( newLine < 4 )
{ {
@ -1633,14 +1632,14 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
m_out->Print( 0, "\n" ); m_out->Print( 0, "\n" );
} }
if( cv.IsEndContour( it ) ) if( iterator.IsEndContour() )
{ {
if( newLine != 0 ) if( newLine != 0 )
m_out->Print( 0, "\n" ); m_out->Print( 0, "\n" );
m_out->Print( aNestLevel+2, ")\n" ); m_out->Print( aNestLevel+2, ")\n" );
if( it+1 != cv.GetCornersCount() ) if( !iterator.IsLastPolygon() )
{ {
newLine = 0; newLine = 0;
m_out->Print( aNestLevel+1, ")\n" ); 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+1, "(filled_polygon\n" );
m_out->Print( aNestLevel+2, "(pts\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 ) if( newLine == 0 )
m_out->Print( aNestLevel+3, "(xy %s %s)", 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" ); m_out->Print( aNestLevel+2, ")\n" );
if( !it.IsLastContour() ) if( !it.IsLastPolygon() )
{ {
newLine = 0; newLine = 0;
m_out->Print( aNestLevel+1, ")\n" ); m_out->Print( aNestLevel+1, ")\n" );

View File

@ -2474,7 +2474,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
{ {
unique_ptr<ZONE_CONTAINER> zc( new ZONE_CONTAINER( m_board ) ); unique_ptr<ZONE_CONTAINER> 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; bool sawCorner = false;
char buf[1024]; char buf[1024];
char* line; char* line;
@ -2489,17 +2489,13 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
// e.g. "ZCorner 25650 49500 0" // e.g. "ZCorner 25650 49500 0"
BIU x = biuParse( line + SZ( "ZCorner" ), &data ); BIU x = biuParse( line + SZ( "ZCorner" ), &data );
BIU y = biuParse( data, &data ); BIU y = biuParse( data, &data );
int flag = intParse( data );
if( !sawCorner ) if( !sawCorner )
zc->Outline()->Start( zc->GetLayer(), x, y, outline_hatch ); zc->NewHole();
else else
zc->AppendCorner( wxPoint( x, y ) ); zc->AppendCorner( wxPoint( x, y ) );
sawCorner = true; sawCorner = true;
if( flag )
zc->Outline()->CloseLastContour();
} }
else if( TESTLINE( "ZInfo" ) ) // general info found else if( TESTLINE( "ZInfo" ) ) // general info found
@ -2540,9 +2536,9 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
switch( *hopt ) // upper case required switch( *hopt ) // upper case required
{ {
case 'N': outline_hatch = CPolyLine::NO_HATCH; break; case 'N': outline_hatch = ZONE_CONTAINER::NO_HATCH; break;
case 'E': outline_hatch = CPolyLine::DIAGONAL_EDGE; break; case 'E': outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE; break;
case 'F': outline_hatch = CPolyLine::DIAGONAL_FULL; break; case 'F': outline_hatch = ZONE_CONTAINER::DIAGONAL_FULL; break;
default: default:
m_error.Printf( wxT( "Bad ZAux for CZONE_CONTAINER '%s'" ), zc->GetNetname().GetData() ); 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 // Hatch here, after outlines corners are read
// Set hatch here, after outlines corners are read // Set hatch here, after outlines corners are read
zc->Outline()->SetHatch( outline_hatch, zc->SetHatch( outline_hatch, Mils2iu( ZONE_CONTAINER::GetDefaultHatchPitchMils() ),
Mils2iu( CPolyLine::GetDefaultHatchPitchMils() ), true );
true );
m_board->Add( zc.release() ); m_board->Add( zc.release() );
} }

View File

@ -716,14 +716,14 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu*
edge_zone->GetIsKeepout() ? _("Keepout Area") : _( "Zones" ), edge_zone->GetIsKeepout() ? _("Keepout Area") : _( "Zones" ),
KiBitmap( add_zone_xpm ) ); 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, AddMenuItem( zones_menu, ID_POPUP_PCB_MOVE_ZONE_CORNER,
_( "Move Corner" ), KiBitmap( move_xpm ) ); _( "Move Corner" ), KiBitmap( move_xpm ) );
AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CORNER, AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CORNER,
_( "Delete Corner" ), KiBitmap( delete_xpm ) ); _( "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, AddMenuItem( zones_menu, ID_POPUP_PCB_ADD_ZONE_CORNER,
_( "Create Corner" ), KiBitmap( add_corner_xpm ) ); _( "Create Corner" ), KiBitmap( add_corner_xpm ) );
@ -771,7 +771,7 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu*
zones_menu->AppendSeparator(); zones_menu->AppendSeparator();
if( edge_zone->GetSelectedCorner() >= 0 && 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, AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CUTOUT,
_( "Delete Cutout" ), KiBitmap( delete_xpm ) ); _( "Delete Cutout" ), KiBitmap( delete_xpm ) );

View File

@ -177,26 +177,19 @@ void PCB_POLYGON::AddToBoard()
zone->SetNetCode( m_netCode ); zone->SetNetCode( m_netCode );
// add outline // 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 ), for( i = 0; i < (int) m_outline.GetCount(); i++ )
KiROUND( m_outline[i]->y ), outline_hatch );
for( i = 1; i < (int) m_outline.GetCount(); i++ )
{ {
zone->AppendCorner( wxPoint( KiROUND( m_outline[i]->x ), zone->AppendCorner( wxPoint( KiROUND( m_outline[i]->x ),
KiROUND( m_outline[i]->y ) ) ); KiROUND( m_outline[i]->y ) ) );
} }
zone->Outline()->CloseLastContour();
zone->SetZoneClearance( m_width ); zone->SetZoneClearance( m_width );
zone->SetPriority( m_priority ); zone->SetPriority( m_priority );
zone->Outline()->SetHatch( outline_hatch, zone->SetHatch( outline_hatch, Mils2iu( zone->GetDefaultHatchPitchMils() ), true );
Mils2iu( zone->Outline()->GetDefaultHatchPitchMils() ),
true );
if ( m_objType == wxT( 'K' ) ) if ( m_objType == wxT( 'K' ) )
{ {

View File

@ -973,12 +973,11 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone )
m_gal->SetIsStroke( true ); m_gal->SetIsStroke( true );
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
const CPolyLine* polygon = aZone->Outline(); for( auto iterator = aZone->CIterateWithHoles(); iterator; iterator++ )
for( int i = 0; i < polygon->GetCornersCount(); ++i )
{ {
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 // The last point for closing the polyline
corners.push_back( corners[0] ); corners.push_back( corners[0] );
@ -986,11 +985,8 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone )
corners.clear(); corners.clear();
} }
for( unsigned ic = 0; ic < polygon->m_HatchLines.size(); ic++ ) for( const SEG& hatchLine : aZone->GetHatchLines() )
{ m_gal->DrawLine( hatchLine.A, hatchLine.B );
auto& hatchLine = polygon->m_HatchLines[ic];
m_gal->DrawLine( hatchLine.m_Start, hatchLine.m_End );
}
} }
// Draw the filling // Draw the filling

View File

@ -2644,9 +2644,9 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
wxT( " as ZONE_CONTAINER." ) ); 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; wxPoint pt;
T token; T token;
int tmp; int tmp;
@ -2709,9 +2709,9 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
switch( token ) switch( token )
{ {
default: default:
case T_none: hatchStyle = CPolyLine::NO_HATCH; break; case T_none: hatchStyle = ZONE_CONTAINER::NO_HATCH; break;
case T_edge: hatchStyle = CPolyLine::DIAGONAL_EDGE; break; case T_edge: hatchStyle = ZONE_CONTAINER::DIAGONAL_EDGE; break;
case T_full: hatchStyle = CPolyLine::DIAGONAL_FULL; case T_full: hatchStyle = ZONE_CONTAINER::DIAGONAL_FULL;
} }
hatchPitch = parseBoardUnits( "hatch pitch" ); 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 // Set hatch here, after outlines corners are read
zone->Outline()->SetHatch( hatchStyle, hatchPitch, true ); zone->SetHatch( hatchStyle, hatchPitch, true );
} }
if( !pts.IsEmpty() ) if( !pts.IsEmpty() )

View File

@ -646,7 +646,7 @@ void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone )
* *
* in non filled mode the outline is plotted, but not the filling items * 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 ); wxPoint pos( ic->x, ic->y );
cornerList.push_back( pos ); cornerList.push_back( pos );

View File

@ -418,7 +418,7 @@ RN_POLY::RN_POLY( const SHAPE_POLY_SET* aParent,
m_bbox( aBBox ), m_bbox( aBBox ),
m_parentPolyset( aParent ) 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 ); m_node = aConnections.AddNode( p.x, p.y );

View File

@ -1016,26 +1016,27 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
int count = item->Outline()->m_CornersList.GetCornersCount(); // Handle the main outlines
int ndx = 0; // used in 2 for() loops below SHAPE_POLY_SET::ITERATOR iterator;
for( ; ndx<count; ++ndx ) for( iterator = item->IterateWithHoles(); iterator; iterator++ )
{ {
wxPoint point( item->Outline()->m_CornersList[ndx].x, wxPoint point( iterator->x, iterator->y );
item->Outline()->m_CornersList[ndx].y );
mainPolygon->AppendPoint( mapPt(point) ); mainPolygon->AppendPoint( mapPt(point) );
// this was the end of the main polygon // this was the end of the main polygon
if( item->Outline()->m_CornersList[ndx].end_contour ) if( iterator.IsEndContour() )
break; break;
} }
WINDOW* window = 0; WINDOW* window = 0;
PATH* cutout = 0; PATH* cutout = 0;
bool isStartContour = true;
// handle the cutouts // handle the cutouts
for( ++ndx; ndx<count; ++ndx ) for( iterator++; iterator; iterator++ )
{ {
if( item->Outline()->m_CornersList[ndx-1].end_contour ) if( isStartContour )
{ {
window = new WINDOW( plane ); window = new WINDOW( plane );
@ -1048,11 +1049,14 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; 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( window );
wxASSERT( cutout ); wxASSERT( cutout );
wxPoint point(item->Outline()->m_CornersList[ndx].x, wxPoint point(iterator->x, iterator->y );
item->Outline()->m_CornersList[ndx].y );
cutout->AppendPoint( mapPt(point) ); cutout->AppendPoint( mapPt(point) );
} }
} }
@ -1093,41 +1097,44 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
int count = item->Outline()->m_CornersList.GetCornersCount(); // Handle the main outlines
int ndx = 0; // used in 2 for() loops below SHAPE_POLY_SET::ITERATOR iterator;
for( ; ndx<count; ++ndx ) for( iterator = item->IterateWithHoles(); iterator; iterator++ )
{ {
wxPoint point( item->Outline()->m_CornersList[ndx].x, wxPoint point( iterator->x, iterator->y );
item->Outline()->m_CornersList[ndx].y );
mainPolygon->AppendPoint( mapPt(point) ); mainPolygon->AppendPoint( mapPt(point) );
// this was the end of the main polygon // this was the end of the main polygon
if( item->Outline()->m_CornersList[ndx].end_contour ) if( iterator.IsEndContour() )
break; break;
} }
WINDOW* window = 0; WINDOW* window = 0;
PATH* cutout = 0; PATH* cutout = 0;
bool isStartContour = true;
// handle the cutouts // handle the cutouts
for( ++ndx; ndx<count; ++ndx ) for( iterator++; iterator; iterator++ )
{ {
if( item->Outline()->m_CornersList[ndx-1].end_contour ) if( isStartContour )
{ {
window = new WINDOW( keepout ); window = new WINDOW( keepout );
keepout->AddWindow( window ); keepout->AddWindow( window );
cutout = new PATH( window, T_polygon ); cutout = new PATH( window, T_polygon );
window->SetShape( cutout ); window->SetShape( cutout );
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ]; cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
} }
isStartContour = iterator.IsEndContour();
wxASSERT( window ); wxASSERT( window );
wxASSERT( cutout ); wxASSERT( cutout );
wxPoint point(item->Outline()->m_CornersList[ndx].x, wxPoint point(iterator->x, iterator->y );
item->Outline()->m_CornersList[ndx].y );
cutout->AppendPoint( mapPt(point) ); cutout->AppendPoint( mapPt(point) );
} }
} }

View File

@ -339,17 +339,15 @@ void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos )
case PCB_ZONE_AREA_T: case PCB_ZONE_AREA_T:
{ {
const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline(); const SHAPE_POLY_SET* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
int cornersCount = outline->GetCornersCount();
SHAPE_LINE_CHAIN lc; SHAPE_LINE_CHAIN lc;
lc.SetClosed( true ); lc.SetClosed( true );
for( int i = 0; i < cornersCount; ++i ) for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
{ {
const VECTOR2I p ( outline->GetPos( i ) ); addAnchor( *iter, CORNER, aItem );
addAnchor( p, CORNER, aItem ); lc.Append( *iter );
lc.Append( p );
} }
addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem ); addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );

View File

@ -750,22 +750,19 @@ int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones, static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones,
std::vector<ZONE_CONTAINER *>& aMergedZones ) std::vector<ZONE_CONTAINER *>& aMergedZones )
{ {
SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( aOriginZones[0]->Outline()->m_CornersList );
for( unsigned int i = 1; i < aOriginZones.size(); i++ ) for( unsigned int i = 1; i < aOriginZones.size(); i++ )
{ {
SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( aOriginZones[i]->Outline()->m_CornersList ); aOriginZones[0]->Outline()->BooleanAdd( *aOriginZones[i]->Outline(),
SHAPE_POLY_SET::PM_FAST );
mergedOutlines.BooleanAdd( areaToMergePoly, 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 should have one polygon with hole
// We can have 2 polygons with hole, if the 2 initial polygons have only one common corner // 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) // and therefore cannot be merged (they are dectected as intersecting)
// but we should never have more than 2 polys // 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" ) ); wxLogMessage( wxT( "BOARD::CombineAreas error: more than 2 polys after merging" ) );
return false; return false;
@ -779,9 +776,8 @@ static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aO
aCommit.Modify( aOriginZones[0] ); aCommit.Modify( aOriginZones[0] );
aMergedZones.push_back( aOriginZones[0] ); aMergedZones.push_back( aOriginZones[0] );
aOriginZones[0]->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
aOriginZones[0]->SetLocalFlags( 1 ); aOriginZones[0]->SetLocalFlags( 1 );
aOriginZones[0]->Outline()->Hatch(); aOriginZones[0]->Hatch();
return true; return true;
} }

View File

@ -128,14 +128,16 @@ public:
case PCB_ZONE_AREA_T: case PCB_ZONE_AREA_T:
{ {
const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline(); const SHAPE_POLY_SET* outline;
int cornersCount = outline->GetCornersCount(); outline = static_cast<const ZONE_CONTAINER*>( 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(); points->AddBreak();
} }
@ -456,16 +458,15 @@ void POINT_EDITOR::updateItem() const
{ {
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item ); ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
zone->ClearFilledPolysList(); 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(); VECTOR2I point = m_editPoints->Point( i ).GetPosition();
outline->SetX( i, point.x ); outline->Vertex( i ) = point;
outline->SetY( i, point.y );
} }
outline->Hatch(); zone->Hatch();
break; break;
} }
@ -584,9 +585,9 @@ void POINT_EDITOR::updatePoints()
case PCB_ZONE_AREA_T: case PCB_ZONE_AREA_T:
{ {
const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item ); const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( 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() ); getView()->Remove( m_editPoints.get() );
m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() ); m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
@ -594,8 +595,8 @@ void POINT_EDITOR::updatePoints()
} }
else else
{ {
for( int i = 0; i < outline->GetCornersCount(); ++i ) for( int i = 0; i < outline->TotalVertices(); ++i )
m_editPoints->Point( i ).SetPosition( outline->GetPos( i ) ); m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
} }
break; break;
@ -775,19 +776,18 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
if( item->Type() == PCB_ZONE_AREA_T ) if( item->Type() == PCB_ZONE_AREA_T )
{ {
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item ); ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
CPolyLine* outline = zone->Outline(); SHAPE_POLY_SET* outline = zone->Outline();
commit.Modify( zone ); commit.Modify( zone );
// Handle the last segment, so other segments can be easily handled in a loop // Handle the last segment, so other segments can be easily handled in a loop
unsigned int nearestIdx = outline->GetCornersCount() - 1, nextNearestIdx = 0; unsigned int nearestIdx = outline->TotalVertices() - 1, nextNearestIdx = 0;
SEG side( VECTOR2I( outline->GetPos( nearestIdx ) ), SEG side( outline->Vertex( nearestIdx ), outline->Vertex( nextNearestIdx ) );
VECTOR2I( outline->GetPos( nextNearestIdx ) ) );
unsigned int nearestDist = side.Distance( cursorPos ); 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 ); unsigned int distance = side.Distance( cursorPos );
if( distance < nearestDist ) if( distance < nearestDist )
@ -799,8 +799,8 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
} }
// Find the point on the closest segment // Find the point on the closest segment
VECTOR2I sideOrigin( outline->GetPos( nearestIdx ) ); VECTOR2I sideOrigin = outline->Vertex( nearestIdx );
VECTOR2I sideEnd( outline->GetPos( nextNearestIdx ) ); VECTOR2I sideEnd = outline->Vertex( nextNearestIdx );
SEG nearestSide( sideOrigin, sideEnd ); SEG nearestSide( sideOrigin, sideEnd );
VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos ); VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
@ -809,7 +809,7 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
if( nearestPoint == sideOrigin || nearestPoint == sideEnd ) if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
nearestPoint = ( sideOrigin + sideEnd ) / 2; nearestPoint = ( sideOrigin + sideEnd ) / 2;
outline->InsertCorner( nearestIdx, nearestPoint.x, nearestPoint.y ); outline->InsertVertex( nearestIdx, nearestPoint );
commit.Push( _( "Add a zone corner" ) ); commit.Push( _( "Add a zone corner" ) );
} }
@ -871,14 +871,14 @@ int POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
BOARD_COMMIT commit( frame ); BOARD_COMMIT commit( frame );
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item ); ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
CPolyLine* outline = zone->Outline(); SHAPE_POLY_SET* outline = zone->Outline();
commit.Modify( zone ); 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 ); setEditedPoint( NULL );
commit.Push( _( "Remove a zone corner" ) ); commit.Push( _( "Remove a zone corner" ) );
break; break;

View File

@ -110,14 +110,16 @@ void ZONE_CREATE_HELPER::performZoneCutout( ZONE_CONTAINER& aExistingZone,
auto& board = *m_tool.getModel<BOARD>(); auto& board = *m_tool.getModel<BOARD>();
auto& toolMgr = *m_tool.GetManager(); auto& toolMgr = *m_tool.GetManager();
aExistingZone.Outline()->NewOutline();
// Copy cutout corners into existing zone // Copy cutout corners into existing zone
for( int ii = 0; ii < aCutout.GetNumCorners(); ii++ ) for( int ii = 0; ii < aCutout.GetNumCorners(); ii++ )
{ {
aExistingZone.AppendCorner( aCutout.GetCornerPosition( ii ) ); aExistingZone.Outline()->Append( aCutout.GetCornerPosition( ii ) );
} }
// Close the current corner list // Close the current corner list
aExistingZone.Outline()->CloseLastContour(); aExistingZone.Outline()->Outline( 0 ).SetClosed( true );
board.OnAreaPolygonModified( nullptr, &aExistingZone ); board.OnAreaPolygonModified( nullptr, &aExistingZone );
@ -217,18 +219,16 @@ void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr )
} }
else else
{ {
m_zone->Outline()->Start( m_zone->GetLayer(), m_zone->Outline()->NewOutline();
finalPoints[0].x, finalPoints[0].y,
m_zone->GetHatchStyle() );
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()->RemoveNullSegments();
m_zone->Outline()->Hatch(); m_zone->Hatch();
// hand the zone over to the committer // hand the zone over to the committer
commitZone( std::move( m_zone ) ); commitZone( std::move( m_zone ) );

View File

@ -75,11 +75,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
switch( m_cornerSmoothingType ) switch( m_cornerSmoothingType )
{ {
case ZONE_SETTINGS::SMOOTHING_CHAMFER: 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; break;
case ZONE_SETTINGS::SMOOTHING_FILLET: 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; break;
default: 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, // 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 // or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create
// clearance areas // 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; break;
} }
if( aOutlineBuffer ) 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. /* For copper layers, we now must add holes in the Polygon list.
* holes are pads and tracks with their clearance area * 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 m_FillMode = 0; // Fill by segments is no more used in non copper layers
// force use solid polygons (usefull only for old boards) // 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 filled areas are deflated by -m_ZoneMinThickness / 2, because
// the outlines are drawn with a line thickness = m_ZoneMinThickness to // the outlines are drawn with a line thickness = m_ZoneMinThickness to

View File

@ -137,7 +137,7 @@ void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone )
if( success ) if( success )
{ {
zoneSettings.ExportSetting( *newZone ); zoneSettings.ExportSetting( *newZone );
newZone->Outline()->Hatch(); newZone->Hatch();
s_AuxiliaryList.ClearListAndDeleteItems(); s_AuxiliaryList.ClearListAndDeleteItems();
s_PickedList.ClearListAndDeleteItems(); s_PickedList.ClearListAndDeleteItems();
@ -187,7 +187,7 @@ int PCB_EDIT_FRAME::Delete_LastCreatedCorner( wxDC* DC )
if( zone->GetNumCorners() > 2 ) if( zone->GetNumCorners() > 2 )
{ {
zone->Outline()->DeleteCorner( zone->GetNumCorners() - 1 ); zone->Outline()->RemoveVertex( zone->GetNumCorners() - 1 );
if( m_canvas->IsMouseCaptured() ) if( m_canvas->IsMouseCaptured() )
m_canvas->CallMouseCapture( DC, wxDefaultPosition, false ); 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. // Prepare copy of old zones, for undo/redo.
// if the corner is new, remove it from list, save and insert it in list // if the corner is new, remove it from list, save and insert it in list
int cx = aZone->Outline()->GetX( corner_id ); VECTOR2I corner = aZone->Outline()->Vertex( corner_id );
int cy = aZone->Outline()->GetY( corner_id );
if ( IsNewCorner ) if ( IsNewCorner )
aZone->Outline()->DeleteCorner( corner_id ); aZone->Outline()->RemoveVertex( corner_id );
s_AuxiliaryList.ClearListAndDeleteItems(); s_AuxiliaryList.ClearListAndDeleteItems();
s_PickedList.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() ); SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
if ( IsNewCorner ) if ( IsNewCorner )
aZone->Outline()->InsertCorner(corner_id-1, cx, cy ); aZone->Outline()->InsertVertex(corner_id-1, corner );
aZone->SetFlags( IN_EDIT ); aZone->SetFlags( IN_EDIT );
m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse, m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse,
Abort_Zone_Move_Corner_Or_Outlines ); Abort_Zone_Move_Corner_Or_Outlines );
s_CornerInitialPosition = aZone->GetCornerPosition( corner_id ); s_CornerInitialPosition = static_cast<wxPoint>( aZone->GetCornerPosition( corner_id ) );
s_CornerIsNew = IsNewCorner; s_CornerIsNew = IsNewCorner;
s_AddCutoutToCurrentZone = false; s_AddCutoutToCurrentZone = false;
s_CurrentZone = NULL; s_CurrentZone = NULL;
@ -370,7 +369,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
{ {
OnModify(); OnModify();
if( aZone->Outline()->GetCornersCount() <= 3 ) if( aZone->Outline()->TotalVertices() <= 3 )
{ {
m_canvas->RefreshDrawingRect( aZone->GetBoundingBox() ); 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_AuxiliaryList.ClearListAndDeleteItems();
s_PickedList. ClearListAndDeleteItems(); s_PickedList. ClearListAndDeleteItems();
SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() ); 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 // modify zones outlines according to the new aZone shape
GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone ); 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 ) if( s_CornerIsNew )
{ {
zone->Outline()->DeleteCorner( zone->GetSelectedCorner() ); zone->Outline()->RemoveVertex( zone->GetSelectedCorner() );
} }
else else
{ {
wxPoint pos = s_CornerInitialPosition; 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 else
{ {
zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y ); zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos;
} }
zone->Draw( aPanel, aDC, GR_XOR ); zone->Draw( aPanel, aDC, GR_XOR );
@ -667,12 +666,11 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC )
{ {
zoneInfo.ExportSetting( *zone ); zoneInfo.ExportSetting( *zone );
zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer, zone->SetLayer( zoneInfo.m_CurrentZone_Layer );
GetCrossHairPosition().x,
GetCrossHairPosition().y,
zone->GetHatchStyle() );
// A duplicated corner is needed; null segments are removed when the zone is finished.
zone->AppendCorner( GetCrossHairPosition() ); zone->AppendCorner( GetCrossHairPosition() );
zone->AppendCorner( GetCrossHairPosition(), true );
if( g_Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() ) 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 // Ok, we can add a new corner
if( m_canvas->IsMouseCaptured() ) if( m_canvas->IsMouseCaptured() )
m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); 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(). SetCurItem( zone ); // calls DisplayInfo().
if( m_canvas->IsMouseCaptured() ) if( m_canvas->IsMouseCaptured() )
m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); 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 // Put new zone in list
if( !s_CurrentZone ) if( !s_CurrentZone )
{ {
zone->Outline()->CloseLastContour(); // Close the current corner list
GetBoard()->Add( zone ); GetBoard()->Add( zone );
// Add this zone in picked list, as new item // 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 else // Append this outline as a cutout to an existing zone
{ {
for( int ii = 0; ii < zone->GetNumCorners(); ii++ ) s_CurrentZone->Outline()->AddHole( zone->Outline()->Outline( 0 ) );
{
s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) );
}
s_CurrentZone->Outline()->CloseLastContour(); // Close the current corner list
zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list. zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list.
zone = s_CurrentZone; 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 ) if( pcbframe->GetZoneSettings().m_Zone_45_Only )
{ {
// calculate the new position as allowed // calculate the new position as allowed
wxPoint StartPoint = zone->GetCornerPosition( icorner - 1 ); wxPoint StartPoint = static_cast<wxPoint>( zone->GetCornerPosition( icorner - 1 ) );
CalculateSegmentEndPoint( c_pos, StartPoint.x, StartPoint.y, &c_pos.x, &c_pos.y ); 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 ) 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(); EDA_RECT dirty = aZone->GetBoundingBox();
@ -974,7 +978,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
// Remove current filling: // Remove current filling:
aZone->UnFill(); 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 ); SaveCopyInUndoList( aZone, UR_DELETED );
GetBoard()->Remove( aZone ); GetBoard()->Remove( aZone );
@ -983,7 +987,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
else else
{ {
SaveCopyInUndoList( aZone, UR_CHANGED ); SaveCopyInUndoList( aZone, UR_CHANGED );
aZone->Outline()->RemoveContour( ncont ); aZone->Outline()->RemoveContour( indices.m_contour, indices.m_polygon );
} }
m_canvas->RefreshDrawingRect( dirty ); m_canvas->RefreshDrawingRect( dirty );

View File

@ -435,7 +435,7 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
if(g_DumpZonesWhenFilling) if(g_DumpZonesWhenFilling)
dumper->BeginGroup("clipper-zone"); 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.Inflate( -outline_half_thickness, segsPerCircle );
solidAreas.Simplify( POLY_CALC_MODE ); solidAreas.Simplify( POLY_CALC_MODE );

View File

@ -115,8 +115,7 @@ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare )
wxASSERT( m_Poly ); // m_Poly == NULL Should never happen wxASSERT( m_Poly ); // m_Poly == NULL Should never happen
wxASSERT( aZoneToCompare.Outline() ); wxASSERT( aZoneToCompare.Outline() );
if( Outline()->m_CornersList.GetList() != if( Outline() != aZoneToCompare.Outline() ) // Compare vector
aZoneToCompare.Outline()->m_CornersList.GetList() ) // Compare vector
return false; return false;
return true; return true;

View File

@ -102,7 +102,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
continue; continue;
// legal polygon // legal polygon
EDA_RECT b1 = curr_area->Outline()->GetBoundingBox(); BOX2I b1 = curr_area->Outline()->BBox();
bool mod_ia1 = false; bool mod_ia1 = false;
for( unsigned ia2 = m_ZoneDescriptorList.size() - 1; ia2 > ia1; ia2-- ) 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() ) if( curr_area->GetLayer() != area2->GetLayer() )
continue; continue;
EDA_RECT b2 = area2->Outline()->GetBoundingBox(); BOX2I b2 = area2->Outline()->BBox();
if( b1.Intersects( b2 ) ) 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() ) if( area_ref->GetLayer() != area_to_test->GetLayer() )
return false; return false;
CPolyLine* poly1 = area_ref->Outline(); SHAPE_POLY_SET* poly1 = area_ref->Outline();
CPolyLine* poly2 = area_to_test->Outline(); SHAPE_POLY_SET* poly2 = area_to_test->Outline();
// test bounding rects // test bounding rects
EDA_RECT b1 = poly1->GetBoundingBox(); BOX2I b1 = poly1->BBox();
EDA_RECT b2 = poly2->GetBoundingBox(); BOX2I b2 = poly2->BBox();
if( ! b1.Intersects( b2 ) ) if( ! b1.Intersects( b2 ) )
return false; return false;
// now test for intersecting segments // Now test for intersecting segments
for( int icont1 = 0; icont1<poly1->GetContoursCount(); icont1++ ) for( auto segIterator1 = poly1->IterateSegmentsWithHoles(); segIterator1; segIterator1++ )
{ {
int is1 = poly1->GetContourStart( icont1 ); // Build segment
int ie1 = poly1->GetContourEnd( icont1 ); SEG firstSegment = *segIterator1;
for( int ic1 = is1; ic1<=ie1; ic1++ ) for( auto segIterator2 = poly2->IterateSegmentsWithHoles(); segIterator2; segIterator2++ )
{ {
int xi1 = poly1->GetX( ic1 ); // Build second segment
int yi1 = poly1->GetY( ic1 ); SEG secondSegment = *segIterator2;
int xf1, yf1;
if( ic1 < ie1 ) // Check whether the two segments built collide
{ if( firstSegment.Collide( secondSegment, 0 ) )
xf1 = poly1->GetX( ic1 + 1 ); return true;
yf1 = poly1->GetY( ic1 + 1 );
}
else
{
xf1 = poly1->GetX( is1 );
yf1 = poly1->GetY( is1 );
}
for( int icont2 = 0; icont2<poly2->GetContoursCount(); 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;
}
}
} }
} }
// If a contour is inside an other contour, no segments intersects, but the zones // 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 enought) // can be combined if a corner is inside an outline (only one corner is enough)
for( int ic2 = 0; ic2 < poly2->GetCornersCount(); ic2++ ) for( auto iter = poly2->IterateWithHoles(); iter; iter++ )
{ {
int x = poly2->GetX( ic2 ); if( poly1->Contains( *iter ) )
int y = poly2->GetY( ic2 );
if( poly1->TestPointInside( x, y ) )
{
return true; return true;
}
} }
for( int ic1 = 0; ic1 < poly1->GetCornersCount(); ic1++ ) for( auto iter = poly1->IterateWithHoles(); iter; iter++ )
{ {
int x = poly1->GetX( ic1 ); if( poly2->Contains( *iter ) )
int y = poly1->GetY( ic1 );
if( poly2->TestPointInside( x, y ) )
{
return true; return true;
}
} }
return false; return false;
@ -290,8 +243,8 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_
return false; return false;
} }
SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( area_ref->Outline()->m_CornersList ); SHAPE_POLY_SET mergedOutlines = *area_ref->Outline();
SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( area_to_combine->Outline()->m_CornersList ); SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline();
mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST ); mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
mergedOutlines.Simplify( 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 ) if( mergedOutlines.OutlineCount() > 1 )
return false; 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 ); RemoveArea( aDeletedList, area_to_combine );
area_ref->SetLocalFlags( 1 ); area_ref->SetLocalFlags( 1 );
area_ref->Outline()->Hatch(); area_ref->Hatch();
return true; 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++ ) for( int ia = 0; ia < GetAreaCount(); ia++ )
{ {
ZONE_CONTAINER* Area_Ref = GetArea( ia ); ZONE_CONTAINER* Area_Ref = GetArea( ia );
CPolyLine* refSmoothedPoly = Area_Ref->GetSmoothedPoly(); SHAPE_POLY_SET* refSmoothedPoly = Area_Ref->GetSmoothedPoly();
if( !Area_Ref->IsOnCopperLayer() ) if( !Area_Ref->IsOnCopperLayer() )
continue; 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++ ) for( int ia2 = 0; ia2 < GetAreaCount(); ia2++ )
{ {
ZONE_CONTAINER* area_to_test = GetArea( 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 ) if( Area_Ref == area_to_test )
continue; continue;
@ -375,16 +330,18 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
zone2zoneClearance = 1; zone2zoneClearance = 1;
// test for some corners of Area_Ref inside area_to_test // 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 ); VECTOR2I currentVertex = *iterator;
int y = refSmoothedPoly->GetY( ic );
if( testSmoothedPoly->TestPointInside( x, y ) ) if( testSmoothedPoly->Contains( currentVertex ) )
{ {
// COPPERAREA_COPPERAREA error: copper area ref corner inside copper area // COPPERAREA_COPPERAREA error: copper area ref corner inside copper area
if( aCreate_Markers ) if( aCreate_Markers )
{ {
int x = currentVertex.x;
int y = currentVertex.y;
wxString msg1 = Area_Ref->GetSelectMenuText(); wxString msg1 = Area_Ref->GetSelectMenuText();
wxString msg2 = area_to_test->GetSelectMenuText(); wxString msg2 = area_to_test->GetSelectMenuText();
MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA, 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 // 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 ); VECTOR2I currentVertex = *iterator;
int y = testSmoothedPoly->GetY( ic2 );
if( refSmoothedPoly->TestPointInside( x, y ) ) if( refSmoothedPoly->Contains( currentVertex ) )
{ {
// COPPERAREA_COPPERAREA error: copper area corner inside copper area ref // COPPERAREA_COPPERAREA error: copper area corner inside copper area ref
if( aCreate_Markers ) if( aCreate_Markers )
{ {
int x = currentVertex.x;
int y = currentVertex.y;
wxString msg1 = area_to_test->GetSelectMenuText(); wxString msg1 = area_to_test->GetSelectMenuText();
wxString msg2 = Area_Ref->GetSelectMenuText(); wxString msg2 = Area_Ref->GetSelectMenuText();
MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA, 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 ); // Build ref segment
int ic_end = refSmoothedPoly->GetContourEnd( icont ); 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 ); // Build test segment
int ay1 = refSmoothedPoly->GetY( ic ); SEG testSegment = *testIt;
int ax2, ay2;
if( ic == ic_end ) int x, y;
{
ax2 = refSmoothedPoly->GetX( ic_start );
ay2 = refSmoothedPoly->GetY( ic_start );
}
else
{
ax2 = refSmoothedPoly->GetX( ic + 1 );
ay2 = refSmoothedPoly->GetY( ic + 1 );
}
for( int icont2 = 0; icont2 < testSmoothedPoly->GetContoursCount(); icont2++ ) int ax1, ay1, ax2, ay2;
{ ax1 = refSegment.A.x;
int ic_start2 = testSmoothedPoly->GetContourStart( icont2 ); ay1 = refSegment.A.y;
int ic_end2 = testSmoothedPoly->GetContourEnd( icont2 ); 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 ); wxString msg1 = Area_Ref->GetSelectMenuText();
int by1 = testSmoothedPoly->GetY( ic2 ); wxString msg2 = area_to_test->GetSelectMenuText();
int bx2, by2; MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_CLOSE_TO_COPPERAREA,
wxPoint( x, y ),
if( ic2 == ic_end2 ) msg1, wxPoint( x, y ),
{ msg2, wxPoint( x, y ) );
bx2 = testSmoothedPoly->GetX( ic_start2 ); Add( marker );
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++;
}
} }
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 if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
return true; return true;
// Get polygon, contour and vertex index.
SHAPE_POLY_SET::VERTEX_INDEX index;
wxPoint start = aArea->GetCornerPosition( aCornerIndex ); // If the vertex does not exist, there is no conflict
wxPoint end; if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
return true;
// Search the end point of the edge starting at aCornerIndex // Retrieve the selected contour
if( aArea->Outline()->m_CornersList[aCornerIndex].end_contour == false SHAPE_LINE_CHAIN contour;
&& aCornerIndex < (aArea->GetNumCorners() - 1) ) contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
{
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 );
while( ii >= 0 ) // Retrieve the segment that starts at aCornerIndex-th corner.
{ SEG selectedSegment = contour.Segment( index.m_vertex );
if( aArea->Outline()->m_CornersList[ii].end_contour )
break;
end = aArea->GetCornerPosition( ii ); VECTOR2I start = selectedSegment.A;
ii--; VECTOR2I end = selectedSegment.B;
}
}
// iterate through all areas // iterate through all areas
for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ ) for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
@ -562,10 +490,10 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
zone_clearance = 1; zone_clearance = 1;
// test for ending line inside area_to_test // 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 // COPPERAREA_COPPERAREA error: corner inside copper area
m_currentMarker = fillMarker( aArea, end, m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
COPPERAREA_INSIDE_COPPERAREA, COPPERAREA_INSIDE_COPPERAREA,
m_currentMarker ); m_currentMarker );
return false; return false;
@ -577,45 +505,34 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
int ax2 = end.x; int ax2 = end.x;
int ay2 = end.y; 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 ); SEG segment = *iterator;
int ic_end2 = area_to_test->Outline()->GetContourEnd( icont2 );
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 ); // COPPERAREA_COPPERAREA error : edge intersect or too close
int by1 = area_to_test->Outline()->GetY( ic2 ); m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
int bx2, by2; COPPERAREA_CLOSE_TO_COPPERAREA,
m_currentMarker );
if( ic2 == ic_end2 ) return false;
{
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;
}
} }
} }
} }

View File

@ -354,7 +354,10 @@ CPolyLine* CPolyLine::Chamfer( unsigned int aDistance )
int nx2 = KiROUND( distance * xb / lenb ); int nx2 = KiROUND( distance * xb / lenb );
int ny2 = KiROUND( distance * yb / 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(); newPoly->CloseLastContour();
@ -423,7 +426,7 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 ); double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 );
// Do nothing in case of parallel edges // Do nothing in case of parallel edges
if( !std::isfinite( denom ) ) if( std::isinf( denom ) )
continue; continue;
// Limit rounding distance to one half of an edge // Limit rounding distance to one half of an edge
@ -480,11 +483,22 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
else else
newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) ); 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++ ) for( unsigned int j = 0; j < segments; j++ )
{ {
nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius; nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius;
ny = yc - sin( 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 ) if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim )
{ {
unsigned tmp = first_corner_pos; 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 end_segm = tmp; // end_segm is the beginning of the current outline
} }