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 )
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
KI_POLYGON_SET currLayerPolyset;
KI_POLYGON_SET polysetHoles;

View File

@ -46,12 +46,14 @@ int SHAPE_POLY_SET::NewOutline ()
return m_polys.size() - 1;
}
int SHAPE_POLY_SET::NewHole( int aOutline )
{
assert( false );
return -1;
}
int SHAPE_POLY_SET::AppendVertex( int x, int y, int aOutline, int aHole )
{
if( aOutline < 0 )
@ -72,6 +74,7 @@ int SHAPE_POLY_SET::AppendVertex ( int x, int y, int aOutline, int aHole )
return m_polys[aOutline][idx].size();
}
int SHAPE_POLY_SET::VertexCount( int aOutline, int aHole ) const
{
if( aOutline < 0 )
@ -90,6 +93,7 @@ int SHAPE_POLY_SET::VertexCount ( int aOutline , int aHole ) const
return m_polys[aOutline][idx].size();
}
const VECTOR2I SHAPE_POLY_SET::GetVertex( int index, int aOutline, int aHole ) const
{
if( aOutline < 0 )
@ -109,6 +113,7 @@ const VECTOR2I SHAPE_POLY_SET::GetVertex ( int index, int aOutline , int aHole )
return VECTOR2I (p.X, p.Y);
}
int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline )
{
assert( aOutline.IsClosed() );
@ -126,9 +131,11 @@ int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline )
return m_polys.size() - 1;
}
int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
{
assert ( m_polys.size() );
if( aOutline < 0 )
aOutline += m_polys.size();
@ -137,6 +144,7 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
assert( poly.size() );
Path p = convert( aHole );
if( Orientation( p ) )
ReversePath( p ); // holes are always CCW
@ -145,6 +153,7 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
return poly.size() - 1;
}
const ClipperLib::Path SHAPE_POLY_SET::convert( const SHAPE_LINE_CHAIN& aPath )
{
Path c_path;
@ -158,6 +167,7 @@ const ClipperLib::Path SHAPE_POLY_SET::convert( const SHAPE_LINE_CHAIN& aPath )
return c_path;
}
void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b )
{
Clipper c;
@ -181,11 +191,13 @@ void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET&
importTree( &solution );
}
void SHAPE_POLY_SET::Add( const SHAPE_POLY_SET& b )
{
booleanOp( ctUnion, b );
}
void SHAPE_POLY_SET::Subtract( const SHAPE_POLY_SET& b )
{
booleanOp( ctDifference, b );
@ -213,6 +225,7 @@ void SHAPE_POLY_SET::Erode ( int aFactor )
}
}
void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree )
{
m_polys.clear();
@ -226,18 +239,18 @@ void SHAPE_POLY_SET::importTree ( ClipperLib::PolyTree* tree)
for( unsigned i = 0; i < n->Childs.size(); i++ )
paths.push_back( n->Childs[i]->Contour );
m_polys.push_back( paths );
}
}
}
// Polygon fracturing code. Work in progress.
// Polygon fracturing code. Work in progress.
struct FractureEdge
{
FractureEdge( bool connected, Path* owner, int index ) :
m_connected( connected ),
m_owner(owner),
m_next( NULL )
{
m_p1 = (*owner)[index];
@ -246,7 +259,6 @@ struct FractureEdge
FractureEdge( int64_t y = 0 ) :
m_connected( false ),
m_owner(NULL),
m_next( NULL )
{
m_p1.Y = m_p2.Y = y;
@ -254,12 +266,10 @@ struct FractureEdge
FractureEdge( bool connected, const IntPoint& p1, const IntPoint& p2 ) :
m_connected( connected ),
m_owner(NULL),
m_p1( p1 ),
m_p2( p2 ),
m_next( NULL )
{
}
bool matches( int y ) const
@ -271,125 +281,86 @@ struct FractureEdge
}
bool m_connected;
Path* m_owner;
IntPoint m_p1, m_p2;
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;
static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
{
int n = 0;
int64_t x = edge->m_p1.X;
int64_t y = edge->m_p1.Y;
int64_t min_dist = std::numeric_limits<int64_t>::max();
int64_t x_nearest = 0;
int64_t min_dist_l = std::numeric_limits<int64_t>::max();
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;
FractureEdge* e_nearest = NULL;
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 );
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)
if( dist > 0 && dist < min_dist )
{
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;
}
min_dist = dist;
x_nearest = x_intersect;
e_nearest = (*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 )))
{
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;
int count = 0;
bool connFlag = (*e_nearest)->m_connected;
FractureEdge split_1 ( connFlag, (*e_nearest)->m_p1, IntPoint(x_nearest, y) );
FractureEdge *lead1 = new FractureEdge( connFlag, IntPoint(x_nearest, y), IntPoint(x, y) );
FractureEdge *lead2 = new FractureEdge( connFlag, IntPoint(x, y), IntPoint(x_nearest, y) );
FractureEdge *split_2 = new FractureEdge ( connFlag, IntPoint(x_nearest, y), (*e_nearest)->m_p2 );
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 );
edges.push_back( split_2 );
edges.push_back( lead1 );
edges.push_back( lead2 );
FractureEdge* link = (*e_nearest)->m_next;
FractureEdge* link = e_nearest->m_next;
(*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;
e_nearest->m_p2 = IntPoint( x_nearest, y );
e_nearest->m_next = lead1;
lead1->m_next = edge;
FractureEdge* last;
for( last = edge; last->m_next != edge; last = last->m_next )
{
last->m_connected = connFlag;
last->m_owner = NULL;
last->m_connected = true;
count++;
}
last->m_owner = NULL;
last->m_connected = connFlag;
last->m_connected = true;
last->m_next = lead2;
lead2->m_next = split_2;
split_2->m_next = link;
return 1;
return count + 1;
}
return 0;
}
void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
{
FractureEdgeSet edges;
FractureEdgeSet border_edges;
FractureEdge* root = NULL;
bool first = true;
@ -404,6 +375,15 @@ void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
int index = 0;
FractureEdge *prev = NULL, *first_edge = NULL;
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++ );
@ -413,6 +393,7 @@ void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
if( !first_edge )
first_edge = fe;
if( prev )
prev->m_next = fe;
@ -422,6 +403,12 @@ void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
prev = fe;
edges.push_back( fe );
if( !first )
{
if( fe->m_p1.X == x_min )
border_edges.push_back( fe );
}
if( !fe->m_connected )
num_unconnected++;
}
@ -429,27 +416,24 @@ void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
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)
n_unconnected++;
}
if(!n_unconnected)
break;
for(FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i )
int64_t xt = (*i)->m_p1.X;
if( ( xt < x_min ) && ! (*i)->m_connected )
{
if(!(*i)->m_connected)
{
if (processEdge ( edges, *i ) )
break;
x_min = xt;
smallestX = *i;
}
}
num_unconnected -= processEdge( edges, smallestX );
}
paths.clear();
@ -467,6 +451,7 @@ void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
paths.push_back( newPath );
}
void SHAPE_POLY_SET::Fracture()
{
BOOST_FOREACH( Paths& paths, m_polys )
@ -475,6 +460,7 @@ void SHAPE_POLY_SET::Fracture ()
}
}
void SHAPE_POLY_SET::Simplify()
{
for( unsigned i = 0; i < m_polys.size(); i++ )
@ -485,6 +471,7 @@ void SHAPE_POLY_SET::Simplify()
}
}
const std::string SHAPE_POLY_SET::Format() const
{
std::stringstream ss;
@ -494,9 +481,11 @@ const std::string SHAPE_POLY_SET::Format() const
for( unsigned i = 0; i < m_polys.size(); i++ )
{
ss << "poly " << m_polys[i].size() << "\n";
for( unsigned j = 0; j < m_polys[i].size(); j++)
{
ss << m_polys[i][j].size() << "\n";
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";
}
@ -506,6 +495,7 @@ const std::string SHAPE_POLY_SET::Format() const
return ss.str();
}
bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
{
std::string tmp;
@ -525,8 +515,8 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
for( int i = 0; i < n_polys; i++ )
{
ClipperLib::Paths paths;
aStream >> tmp;
if( tmp != "poly" )
return false;
@ -554,9 +544,11 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
}
m_polys.push_back( paths );
}
return true;
}
const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
{
BOX2I bb;
@ -569,6 +561,7 @@ const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
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 );
if( first )
bb = BOX2I( p, VECTOR2I( 0, 0 ) );
else

View File

@ -91,7 +91,7 @@ class SHAPE_POLY_SET : public SHAPE
///> Performs outline erosion/shrinking
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
void Fracture();
@ -113,6 +113,7 @@ class SHAPE_POLY_SET : public SHAPE
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 SEG& aSeg, int aClearance = 0 ) const { return false; }