Fixes to SHAPE_POLY_SET slitting/fracturing algo, some speed optimization.

This commit is contained in:
Tomasz Włostowski 2015-07-06 15:15:48 +02:00 committed by Maciej Suminski
parent c9739b622b
commit 24170f5588
3 changed files with 205 additions and 210 deletions

View File

@ -354,7 +354,8 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
if( bufferPolys.GetCornersCount() == 0 ) if( bufferPolys.GetCornersCount() == 0 )
continue; continue;
#if 1 // Set to 1 to use boost::polygon to subtract holes to copper areas #if 0
// Set to 1 to use boost::polygon to subtract holes to copper areas
// (due to bugs in boost::polygon, this is deprecated and Clipper is used instead // (due to bugs in boost::polygon, this is deprecated and Clipper is used instead
KI_POLYGON_SET currLayerPolyset; KI_POLYGON_SET currLayerPolyset;
KI_POLYGON_SET polysetHoles; KI_POLYGON_SET polysetHoles;

View File

@ -37,49 +37,52 @@
using namespace ClipperLib; using namespace ClipperLib;
int SHAPE_POLY_SET::NewOutline () int SHAPE_POLY_SET::NewOutline()
{ {
Path empty_path; Path empty_path;
Paths poly; Paths poly;
poly.push_back(empty_path); poly.push_back( empty_path );
m_polys.push_back(poly); m_polys.push_back( poly );
return m_polys.size() - 1; return m_polys.size() - 1;
} }
int SHAPE_POLY_SET::NewHole( int aOutline ) int SHAPE_POLY_SET::NewHole( int aOutline )
{ {
assert(false); assert( false );
return -1; return -1;
} }
int SHAPE_POLY_SET::AppendVertex ( int x, int y, int aOutline, int aHole )
int SHAPE_POLY_SET::AppendVertex( int x, int y, int aOutline, int aHole )
{ {
if(aOutline < 0) if( aOutline < 0 )
aOutline += m_polys.size(); aOutline += m_polys.size();
int idx; int idx;
if(aHole < 0) if( aHole < 0 )
idx = 0; idx = 0;
else else
idx = aHole + 1; idx = aHole + 1;
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].push_back( IntPoint( x, y ) ); m_polys[aOutline][idx].push_back( IntPoint( x, y ) );
return m_polys[aOutline][idx].size(); return m_polys[aOutline][idx].size();
} }
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 )
aOutline += m_polys.size(); aOutline += m_polys.size();
int idx; int idx;
if(aHole < 0) if( aHole < 0 )
idx = 0; idx = 0;
else else
idx = aHole + 1; idx = aHole + 1;
@ -90,34 +93,36 @@ int SHAPE_POLY_SET::VertexCount ( int aOutline , int aHole ) const
return m_polys[aOutline][idx].size(); return m_polys[aOutline][idx].size();
} }
const VECTOR2I SHAPE_POLY_SET::GetVertex ( int index, int aOutline , int aHole ) const
const VECTOR2I SHAPE_POLY_SET::GetVertex( int index, int aOutline, int aHole ) const
{ {
if(aOutline < 0) if( aOutline < 0 )
aOutline += m_polys.size(); aOutline += m_polys.size();
int idx; int idx;
if(aHole < 0) if( aHole < 0 )
idx = 0; idx = 0;
else else
idx = aHole + 1; idx = aHole + 1;
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() );
IntPoint p = m_polys[aOutline][idx][index]; IntPoint p = m_polys[aOutline][idx][index];
return VECTOR2I (p.X, p.Y); return VECTOR2I (p.X, p.Y);
} }
int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline ) int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline )
{ {
assert ( aOutline.IsClosed() ); assert( aOutline.IsClosed() );
Path p = convert ( aOutline ); Path p = convert( aOutline );
Paths poly; Paths poly;
if( !Orientation( p ) ) if( !Orientation( p ) )
ReversePath(p); // outlines are always CW ReversePath( p ); // outlines are always CW
poly.push_back( p ); poly.push_back( p );
@ -126,73 +131,80 @@ int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline )
return m_polys.size() - 1; return m_polys.size() - 1;
} }
int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
{ {
assert ( m_polys.size() ); assert ( m_polys.size() );
if(aOutline < 0)
if( aOutline < 0 )
aOutline += m_polys.size(); aOutline += m_polys.size();
Paths& poly = m_polys[ aOutline ]; Paths& poly = m_polys[aOutline];
assert ( poly.size() ); assert( poly.size() );
Path p = convert( aHole );
Path p = convert ( aHole );
if( Orientation( p ) ) if( Orientation( p ) )
ReversePath(p); // holes are always CCW ReversePath( p ); // holes are always CCW
poly.push_back( p ); poly.push_back( p );
return poly.size() - 1; return poly.size() - 1;
} }
const ClipperLib::Path SHAPE_POLY_SET::convert( const SHAPE_LINE_CHAIN& aPath ) const ClipperLib::Path SHAPE_POLY_SET::convert( const SHAPE_LINE_CHAIN& aPath )
{ {
Path c_path; Path c_path;
for(int i = 0; i < aPath.PointCount(); i++) for( int i = 0; i < aPath.PointCount(); i++ )
{ {
const VECTOR2I& vertex = aPath.CPoint(i); const VECTOR2I& vertex = aPath.CPoint( i );
c_path.push_back(ClipperLib::IntPoint ( vertex.x, vertex.y ) ); c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y ) );
} }
return c_path; return c_path;
} }
void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b ) void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b )
{ {
Clipper c; Clipper c;
c.StrictlySimple( true ); c.StrictlySimple( true );
BOOST_FOREACH ( Paths& subject, m_polys ) BOOST_FOREACH( Paths& subject, m_polys )
{ {
c.AddPaths(subject, ptSubject, true); c.AddPaths( subject, ptSubject, true );
} }
BOOST_FOREACH ( const Paths& clip, b.m_polys ) BOOST_FOREACH( const Paths& clip, b.m_polys )
{ {
c.AddPaths(clip, ptClip, true); c.AddPaths( clip, ptClip, true );
} }
PolyTree solution; PolyTree solution;
c.Execute(type, solution, pftNonZero, pftNonZero); c.Execute( type, solution, pftNonZero, pftNonZero );
importTree(&solution); importTree( &solution );
} }
void SHAPE_POLY_SET::Add( const SHAPE_POLY_SET& b ) void SHAPE_POLY_SET::Add( const SHAPE_POLY_SET& b )
{ {
booleanOp( ctUnion, b ); booleanOp( ctUnion, b );
} }
void SHAPE_POLY_SET::Subtract( const SHAPE_POLY_SET& b ) void SHAPE_POLY_SET::Subtract( const SHAPE_POLY_SET& b )
{ {
booleanOp( ctDifference, b ); booleanOp( ctDifference, b );
} }
void SHAPE_POLY_SET::Erode ( int aFactor ) void SHAPE_POLY_SET::Erode( int aFactor )
{ {
ClipperOffset c; ClipperOffset c;
@ -201,290 +213,265 @@ void SHAPE_POLY_SET::Erode ( int aFactor )
PolyTree solution; PolyTree solution;
c.Execute ( solution, aFactor ); c.Execute( solution, aFactor );
m_polys.clear(); m_polys.clear();
for (PolyNode *n = solution.GetFirst(); n; n = n->GetNext() ) for( PolyNode* n = solution.GetFirst(); n; n = n->GetNext() )
{ {
Paths ps; Paths ps;
ps.push_back(n->Contour); ps.push_back( n->Contour );
m_polys.push_back(ps); m_polys.push_back( ps );
} }
} }
void SHAPE_POLY_SET::importTree ( ClipperLib::PolyTree* tree)
void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree )
{ {
m_polys.clear(); m_polys.clear();
for (PolyNode *n = tree->GetFirst(); n; n = n->GetNext() ) for( PolyNode* n = tree->GetFirst(); n; n = n->GetNext() )
{ {
if( !n->IsHole() ) if( !n->IsHole() )
{ {
Paths paths; Paths paths;
paths.push_back(n->Contour); paths.push_back( n->Contour );
for (unsigned i = 0; i < n->Childs.size(); i++) for( unsigned i = 0; i < n->Childs.size(); i++ )
paths.push_back(n->Childs[i]->Contour); paths.push_back( n->Childs[i]->Contour );
m_polys.push_back(paths);
m_polys.push_back( paths );
} }
} }
} }
// Polygon fracturing code. Work in progress.
// Polygon fracturing code. Work in progress.
struct FractureEdge struct FractureEdge
{ {
FractureEdge(bool connected, Path* owner, int index) : FractureEdge( bool connected, Path* owner, int index ) :
m_connected(connected), m_connected( connected ),
m_owner(owner), m_next( NULL )
m_next(NULL)
{ {
m_p1 = (*owner)[index]; m_p1 = (*owner)[index];
m_p2 = (*owner)[(index + 1) % owner->size()]; m_p2 = (*owner)[(index + 1) % owner->size()];
} }
FractureEdge(int64_t y = 0) : FractureEdge( int64_t y = 0 ) :
m_connected(false), m_connected( false ),
m_owner(NULL), m_next( NULL )
m_next(NULL)
{ {
m_p1.Y = m_p2.Y = y; m_p1.Y = m_p2.Y = y;
} }
FractureEdge(bool connected, const IntPoint& p1, const IntPoint& p2) : FractureEdge( bool connected, const IntPoint& p1, const IntPoint& p2 ) :
m_connected(connected), m_connected( connected ),
m_owner(NULL), m_p1( p1 ),
m_p1(p1), m_p2( p2 ),
m_p2(p2), m_next( NULL )
m_next(NULL)
{ {
} }
bool matches ( int y ) const bool matches( int y ) const
{ {
int y_min = std::min(m_p1.Y, m_p2.Y); int y_min = std::min( m_p1.Y, m_p2.Y );
int y_max = std::max(m_p1.Y, m_p2.Y); int y_max = std::max( m_p1.Y, m_p2.Y );
return (y >= y_min) && (y <= y_max); return ( y >= y_min ) && ( y <= y_max );
} }
bool m_connected; bool m_connected;
Path* m_owner;
IntPoint m_p1, m_p2; IntPoint m_p1, m_p2;
FractureEdge *m_next; FractureEdge* m_next;
}; };
struct CompareEdges
{
bool operator()(const FractureEdge *a, const FractureEdge *b) const
{
if( std::min(a->m_p1.Y, a->m_p2.Y) < std::min(b->m_p1.Y, b->m_p2.Y) )
return true;
return false;
}
};
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 n = 0;
int64_t x = edge->m_p1.X; int64_t x = edge->m_p1.X;
int64_t y = edge->m_p1.Y; int64_t y = edge->m_p1.Y;
int64_t min_dist = std::numeric_limits<int64_t>::max();
int64_t x_nearest = 0;
FractureEdge* e_nearest = NULL;
int64_t min_dist_l = std::numeric_limits<int64_t>::max(); for( FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i )
int64_t min_dist_r = std::numeric_limits<int64_t>::max();
int64_t x_nearest_l = 0, x_nearest_r = 0, x_nearest;
// fixme: search edges in sorted multiset
// FractureEdge comp_min( std::min(edge->m_p1.Y, edge->m_p2.Y) );
// FractureEdgeSet::iterator e_begin = edges.lower_bound ( &comp_min );
FractureEdgeSet::iterator e_nearest_l = edges.end(), e_nearest_r = edges.end(), e_nearest;
for(FractureEdgeSet::iterator i = edges.begin() ; i != edges.end(); ++i)
{ {
n++; if( !(*i)->matches( y ) )
if( (*i)->matches(y) ) continue;
int64_t x_intersect;
if( (*i)->m_p1.Y == (*i)->m_p2.Y ) // horizontal edge
x_intersect = std::max( (*i)->m_p1.X, (*i)->m_p2.X );
else
x_intersect = (*i)->m_p1.X + rescale((*i)->m_p2.X - (*i)->m_p1.X,
y - (*i)->m_p1.Y, (*i)->m_p2.Y - (*i)->m_p1.Y );
int64_t dist = ( x - x_intersect );
if( dist > 0 && dist < min_dist )
{ {
int64_t x_intersect; min_dist = dist;
if( (*i)->m_p1.Y == (*i)->m_p2.Y ) // horizontal edge x_nearest = x_intersect;
x_intersect = std::max ( (*i)->m_p1.X, (*i)->m_p2.X ); e_nearest = (*i);
else
x_intersect = (*i)->m_p1.X + rescale((*i)->m_p2.X - (*i)->m_p1.X, y - (*i)->m_p1.Y, (*i)->m_p2.Y - (*i)->m_p1.Y );
int64_t dist = (x - x_intersect);
if(dist > 0 && dist < min_dist_l)
{
min_dist_l = dist;
x_nearest_l = x_intersect;
e_nearest_l = i;
}
if(dist <= 0 && (-dist) < min_dist_r)
{
min_dist_r = -dist;
x_nearest_r = x_intersect;
e_nearest_r = i;
}
} }
} }
if(e_nearest_l != edges.end() || e_nearest_r != edges.end()) if( e_nearest && e_nearest->m_connected )
{ {
if( e_nearest_l !=edges.end() && ( (*e_nearest_l)->m_connected || ((*e_nearest_l) ->m_owner != edge->m_owner ))) int count = 0;
{
e_nearest = e_nearest_l;
x_nearest = x_nearest_l;
}
else if( e_nearest_r !=edges.end() && ( (*e_nearest_r)->m_connected || ((*e_nearest_r) ->m_owner != edge->m_owner ) )) {
e_nearest = e_nearest_r;
x_nearest = x_nearest_r;
}
else
return 0;
bool connFlag = (*e_nearest)->m_connected; FractureEdge* lead1 = new FractureEdge( true, IntPoint( x_nearest, y), IntPoint( x, y ) );
FractureEdge* lead2 = new FractureEdge( true, IntPoint( x, y), IntPoint( x_nearest, y ) );
FractureEdge* split_2 = new FractureEdge( true, IntPoint( x_nearest, y ), e_nearest->m_p2 );
FractureEdge split_1 ( connFlag, (*e_nearest)->m_p1, IntPoint(x_nearest, y) ); edges.push_back( split_2 );
FractureEdge *lead1 = new FractureEdge( connFlag, IntPoint(x_nearest, y), IntPoint(x, y) ); edges.push_back( lead1 );
FractureEdge *lead2 = new FractureEdge( connFlag, IntPoint(x, y), IntPoint(x_nearest, y) ); edges.push_back( lead2 );
FractureEdge *split_2 = new FractureEdge ( connFlag, IntPoint(x_nearest, y), (*e_nearest)->m_p2 );
edges.push_back(split_2); FractureEdge* link = e_nearest->m_next;
edges.push_back(lead1);
edges.push_back(lead2);
FractureEdge* link = (*e_nearest)->m_next; e_nearest->m_p2 = IntPoint( x_nearest, y );
e_nearest->m_next = lead1;
(*e_nearest)->m_p1 = split_1.m_p1;
(*e_nearest)->m_p2 = IntPoint(x_nearest, y);
(*e_nearest)->m_connected = connFlag;
(*e_nearest)->m_next = lead1;
lead1->m_next = edge; lead1->m_next = edge;
FractureEdge* last;
FractureEdge *last; for( last = edge; last->m_next != edge; last = last->m_next )
for(last = edge; last->m_next != edge; last = last->m_next)
{ {
last->m_connected = connFlag; last->m_connected = true;
last->m_owner = NULL; count++;
} }
last->m_owner = NULL; last->m_connected = true;
last->m_connected = connFlag;
last->m_next = lead2; last->m_next = lead2;
lead2->m_next = split_2; lead2->m_next = split_2;
split_2->m_next = link; split_2->m_next = link;
return 1; return count + 1;
} }
return 0; return 0;
} }
void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths ) void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
{ {
FractureEdgeSet edges; FractureEdgeSet edges;
FractureEdge *root = NULL; FractureEdgeSet border_edges;
FractureEdge* root = NULL;
bool first = true; bool first = true;
if(paths.size() == 1) if( paths.size() == 1 )
return; return;
int num_unconnected = 0; int num_unconnected = 0;
BOOST_FOREACH(Path& path, paths) BOOST_FOREACH( Path& path, paths )
{ {
int index = 0; int index = 0;
FractureEdge *prev = NULL, *first_edge = NULL; FractureEdge *prev = NULL, *first_edge = NULL;
for(unsigned i = 0; i < path.size(); i++)
{
FractureEdge *fe = new FractureEdge ( first, &path, index++ );
if(!root) int64_t x_min = std::numeric_limits<int64_t>::max();
for( unsigned i = 0; i < path.size(); i++ )
{
if( path[i].X < x_min )
x_min = path[i].X;
}
for( unsigned i = 0; i < path.size(); i++ )
{
FractureEdge* fe = new FractureEdge( first, &path, index++ );
if( !root )
root = fe; root = fe;
if(!first_edge) if( !first_edge )
first_edge = fe; first_edge = fe;
if(prev)
if( prev )
prev->m_next = fe; prev->m_next = fe;
if(i == path.size() - 1) if( i == path.size() - 1 )
fe->m_next = first_edge; fe->m_next = first_edge;
prev = fe; prev = fe;
edges.push_back ( fe ); edges.push_back( fe );
if(!fe->m_connected) if( !first )
{
if( fe->m_p1.X == x_min )
border_edges.push_back( fe );
}
if( !fe->m_connected )
num_unconnected++; num_unconnected++;
} }
first = false; // first path is always the outline first = false; // first path is always the outline
} }
while(1) // keep connecting holes to the main outline, until there's no holes left...
while( num_unconnected > 0 )
{ {
int n_unconnected = 0; int64_t x_min = std::numeric_limits<int64_t>::max();
FractureEdge* smallestX;
for(FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i ) // find the left-most hole edge and merge with the outline
for( FractureEdgeSet::iterator i = border_edges.begin(); i != border_edges.end(); ++i )
{ {
if(!(*i)->m_connected) int64_t xt = (*i)->m_p1.X;
n_unconnected++; if( ( xt < x_min ) && ! (*i)->m_connected )
}
if(!n_unconnected)
break;
for(FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i )
{
if(!(*i)->m_connected)
{ {
if (processEdge ( edges, *i ) ) x_min = xt;
break; smallestX = *i;
} }
} }
num_unconnected -= processEdge( edges, smallestX );
} }
paths.clear(); paths.clear();
Path newPath; Path newPath;
FractureEdge *e; FractureEdge* e;
for(e = root; e->m_next != root; e = e->m_next ) for( e = root; e->m_next != root; e = e->m_next )
newPath.push_back(e->m_p1); newPath.push_back( e->m_p1 );
newPath.push_back(e->m_p1); newPath.push_back( e->m_p1 );
for(FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i ) for( FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i )
delete *i; delete *i;
paths.push_back(newPath); paths.push_back( newPath );
} }
void SHAPE_POLY_SET::Fracture ()
void SHAPE_POLY_SET::Fracture()
{ {
BOOST_FOREACH(Paths& paths, m_polys) BOOST_FOREACH( Paths& paths, m_polys )
{ {
fractureSingle( paths ); fractureSingle( paths );
} }
} }
void SHAPE_POLY_SET::Simplify() void SHAPE_POLY_SET::Simplify()
{ {
for (unsigned i = 0; i < m_polys.size(); i++) for( unsigned i = 0; i < m_polys.size(); i++ )
{ {
Paths out; Paths out;
SimplifyPolygons(m_polys[i], out, pftNonZero); SimplifyPolygons( m_polys[i], out, pftNonZero );
m_polys[i] = out; m_polys[i] = out;
} }
} }
const std::string SHAPE_POLY_SET::Format() const const std::string SHAPE_POLY_SET::Format() const
{ {
std::stringstream ss; std::stringstream ss;
@ -494,9 +481,11 @@ 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].size() << "\n"; ss << m_polys[i][j].size() << "\n";
for( unsigned v = 0; v < m_polys[i][j].size(); v++) for( unsigned v = 0; v < m_polys[i][j].size(); v++)
ss << m_polys[i][j][v].X << " " << m_polys[i][j][v].Y << "\n"; ss << m_polys[i][j][v].X << " " << m_polys[i][j][v].Y << "\n";
} }
@ -506,13 +495,14 @@ const std::string SHAPE_POLY_SET::Format() const
return ss.str(); return ss.str();
} }
bool SHAPE_POLY_SET::Parse( std::stringstream& aStream ) bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
{ {
std::string tmp; std::string tmp;
aStream >> tmp; aStream >> tmp;
if(tmp != "polyset") if( tmp != "polyset" )
return false; return false;
aStream >> tmp; aStream >> tmp;
@ -525,9 +515,9 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
for( int i = 0; i < n_polys; i++ ) for( int i = 0; i < n_polys; i++ )
{ {
ClipperLib::Paths paths; ClipperLib::Paths paths;
aStream >> tmp; aStream >> tmp;
if(tmp != "poly")
if( tmp != "poly" )
return false; return false;
aStream >> tmp; aStream >> tmp;
@ -546,17 +536,19 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
{ {
ClipperLib::IntPoint p; ClipperLib::IntPoint p;
aStream >> tmp; p.X = atoi ( tmp.c_str() ); aStream >> tmp; p.X = atoi( tmp.c_str() );
aStream >> tmp; p.Y = atoi ( tmp.c_str() ); aStream >> tmp; p.Y = atoi( tmp.c_str() );
outline.push_back(p); outline.push_back( p );
} }
paths.push_back(outline); paths.push_back( outline );
} }
m_polys.push_back(paths); m_polys.push_back( paths );
} }
return true; return true;
} }
const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
{ {
BOX2I bb; BOX2I bb;
@ -569,10 +561,11 @@ const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
for( unsigned v = 0; v < m_polys[i][j].size(); v++) for( unsigned v = 0; v < m_polys[i][j].size(); v++)
{ {
VECTOR2I p( m_polys[i][j][v].X, m_polys[i][j][v].Y ); VECTOR2I p( m_polys[i][j][v].X, m_polys[i][j][v].Y );
if(first)
bb = BOX2I(p, VECTOR2I(0, 0)); if( first )
bb = BOX2I( p, VECTOR2I( 0, 0 ) );
else else
bb.Merge (p); bb.Merge( p );
first = false; first = false;
} }

View File

@ -43,26 +43,26 @@
class SHAPE_POLY_SET : public SHAPE class SHAPE_POLY_SET : public SHAPE
{ {
public: public:
SHAPE_POLY_SET() : SHAPE (SH_POLY_SET) {}; SHAPE_POLY_SET() : SHAPE( SH_POLY_SET ) {};
~SHAPE_POLY_SET() {}; ~SHAPE_POLY_SET() {};
///> 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();
///> Cretes a new empty hole in the given outline (default: last one) and returns its index ///> Cretes a new empty hole in the given outline (default: last one) and returns its index
int NewHole( int aOutline = -1); int NewHole( int aOutline = -1);
///> Adds a new outline to the set and returns its index ///> Adds a new outline to the set and returns its index
int AddOutline ( const SHAPE_LINE_CHAIN& aOutline ); int AddOutline( const SHAPE_LINE_CHAIN& aOutline );
///> Adds a new hole to the given outline (default: last) and returns its index ///> Adds a new hole to the given outline (default: last) and returns its index
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: last hole in the last outline) ///> Appends a vertex at the end of the given outline/hole (default: last hole in the last outline)
int AppendVertex ( int x, int y, int aOutline = -1, int aHole = -1 ); int AppendVertex( int x, int y, int aOutline = -1, int aHole = -1 );
///> 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 GetVertex ( int index, int aOutline = -1, int aHole = -1) const; const VECTOR2I GetVertex( int index, int aOutline = -1, int aHole = -1) const;
///> Returns true if any of the outlines is self-intersecting ///> Returns true if any of the outlines is self-intersecting
bool IsSelfIntersecting(); bool IsSelfIntersecting();
@ -71,10 +71,10 @@ class SHAPE_POLY_SET : public SHAPE
int OutlineCount() const { return m_polys.size(); } int OutlineCount() const { return m_polys.size(); }
///> Returns the number of vertices in a given outline/hole ///> Returns the number of vertices in a given outline/hole
int VertexCount ( int aOutline = -1, int aHole = -1 ) const; int VertexCount( int aOutline = -1, int aHole = -1 ) const;
///> Returns the internal representation (ClipperLib) of a given polygon (outline + holes) ///> Returns the internal representation (ClipperLib) of a given polygon (outline + holes)
const ClipperLib::Paths& GetPoly ( int aIndex ) const const ClipperLib::Paths& GetPoly( int aIndex ) const
{ {
return m_polys[aIndex]; return m_polys[aIndex];
} }
@ -86,17 +86,17 @@ class SHAPE_POLY_SET : public SHAPE
void Add( const SHAPE_POLY_SET& b ); void Add( const SHAPE_POLY_SET& b );
///> Performs smooth outline inflation (Minkowski sum of the outline and a circle of a given radius) ///> Performs smooth outline inflation (Minkowski sum of the outline and a circle of a given radius)
void SmoothInflate ( int aFactor ); void SmoothInflate( int aFactor );
///> Performs outline erosion/shrinking ///> Performs outline erosion/shrinking
void Erode ( int aFactor ); void Erode( int aFactor );
///> Converts a set of polygons with holes to a singe outline with 'slits'/'fractures' connecting the outer ring ///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the outer ring
///> to the inner holes ///> to the inner holes
void Fracture (); void Fracture();
///> Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) ///> Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
void Simplify (); void Simplify();
/// @copydoc SHAPE::Format() /// @copydoc SHAPE::Format()
const std::string Format() const; const std::string Format() const;
@ -113,13 +113,14 @@ class SHAPE_POLY_SET : public SHAPE
const BOX2I BBox( int aClearance = 0 ) const; const BOX2I BBox( int aClearance = 0 ) const;
// fixme: add collision support
bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const { return false; } bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const { return false; }
bool Collide( const SEG& aSeg, int aClearance = 0 ) const { return false; } bool Collide( const SEG& aSeg, int aClearance = 0 ) const { return false; }
private: private:
void fractureSingle( ClipperLib::Paths& paths ); void fractureSingle( ClipperLib::Paths& paths );
void importTree ( ClipperLib::PolyTree* tree); void importTree( ClipperLib::PolyTree* tree);
void booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b ); void booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b );
const ClipperLib::Path convert( const SHAPE_LINE_CHAIN& aPath ); const ClipperLib::Path convert( const SHAPE_LINE_CHAIN& aPath );