pcbnew: Limit zone simplification

Commit 73c229714 was a bit of a sledgehammer for the associated problem
of degenerate points.  This commit replaces that one by only performing
additional simplification of the zone polygons on those polygons that
fail our initial triangulation attempt.
This commit is contained in:
Seth Hillbrand 2018-09-12 03:19:11 -07:00
parent a04ef37d67
commit 3ebba6cbe1
6 changed files with 108 additions and 75 deletions

View File

@ -27,6 +27,25 @@
#include <common.h> #include <common.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h> #include <geometry/shape_circle.h>
#include "clipper.hpp"
ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation ) const
{
ClipperLib::Path c_path;
for( int i = 0; i < PointCount(); i++ )
{
const VECTOR2I& vertex = CPoint( i );
c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y ) );
}
if( Orientation( c_path ) != aRequiredOrientation )
ReversePath( c_path );
return c_path;
}
bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const
{ {

View File

@ -455,62 +455,10 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
} }
const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath,
bool aRequiredOrientation )
{
Path c_path;
for( int i = 0; i < aPath.PointCount(); i++ )
{
const VECTOR2I& vertex = aPath.CPoint( i );
c_path.push_back( IntPoint( vertex.x, vertex.y ) );
}
if( Orientation( c_path ) != aRequiredOrientation )
ReversePath( c_path );
return c_path;
}
const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath )
{
SHAPE_LINE_CHAIN lc;
for( unsigned int i = 0; i < aPath.size(); i++ )
lc.Append( aPath[i].X, aPath[i].Y );
lc.SetClosed( true );
return lc;
}
void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape, void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape,
POLYGON_MODE aFastMode ) POLYGON_MODE aFastMode )
{ {
Clipper c; booleanOp( aType, *this, aOtherShape, aFastMode );
if( aFastMode == PM_STRICTLY_SIMPLE )
c.StrictlySimple( true );
for( const POLYGON& poly : m_polys )
{
for( unsigned int i = 0; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true );
}
for( const POLYGON& poly : aOtherShape.m_polys )
{
for( unsigned int i = 0; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true );
}
PolyTree solution;
c.Execute( aType, solution, pftNonZero, pftNonZero );
importTree( &solution );
} }
@ -521,19 +469,18 @@ void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType,
{ {
Clipper c; Clipper c;
if( aFastMode == PM_STRICTLY_SIMPLE ) c.StrictlySimple( aFastMode == PM_STRICTLY_SIMPLE );
c.StrictlySimple( true );
for( const POLYGON& poly : aShape.m_polys ) for( auto poly : aShape.m_polys )
{ {
for( unsigned int i = 0; i < poly.size(); i++ ) for( size_t i = 0 ; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true ); c.AddPath( poly[i].convertToClipper( i == 0 ), ptSubject, true );
} }
for( const POLYGON& poly : aOtherShape.m_polys ) for( auto poly : aOtherShape.m_polys )
{ {
for( unsigned int i = 0; i < poly.size(); i++ ) for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true ); c.AddPath( poly[i].convertToClipper( i == 0 ), ptClip, true );
} }
PolyTree solution; PolyTree solution;
@ -598,9 +545,8 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount )
for( const POLYGON& poly : m_polys ) for( const POLYGON& poly : m_polys )
{ {
for( unsigned int i = 0; i < poly.size(); i++ ) for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound, c.AddPath( poly[i].convertToClipper( i == 0 ), jtRound, etClosedPolygon );
etClosedPolygon );
} }
PolyTree solution; PolyTree solution;
@ -643,10 +589,10 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree )
{ {
POLYGON paths; POLYGON paths;
paths.reserve( n->Childs.size() + 1 ); paths.reserve( n->Childs.size() + 1 );
paths.push_back( convertFromClipper( n->Contour ) ); paths.push_back( n->Contour );
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( n->Childs[i]->Contour );
m_polys.push_back( paths ); m_polys.push_back( paths );
} }
@ -1908,7 +1854,8 @@ void SHAPE_POLY_SET::CacheTriangulation()
SHAPE_POLY_SET tmpSet = *this; SHAPE_POLY_SET tmpSet = *this;
tmpSet.Fracture( PM_FAST ); if( tmpSet.HasHoles() )
tmpSet.Fracture( PM_FAST );
m_triangulatedPolys.clear(); m_triangulatedPolys.clear();

View File

@ -139,6 +139,7 @@ typedef long time_t;
// Contains VECTOR2I // Contains VECTOR2I
%include math.i %include math.i
%ignore SHAPE_LINE_CHAIN::convertFromClipper;
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
%include <geometry/shape_line_chain.h> %include <geometry/shape_line_chain.h>

View File

@ -51,6 +51,8 @@
#include <vector> #include <vector>
#include <math/box2.h> #include <math/box2.h>
#include "clipper.hpp"
class PolygonTriangulation class PolygonTriangulation
{ {
@ -291,6 +293,27 @@ private:
return retval; return retval;
} }
/**
* Function createList
* Takes a Clipper path and converts it into a circular, doubly-linked
* list for triangulation
*/
Vertex* createList( const ClipperLib::Path& aPath )
{
Vertex* tail = nullptr;
for( auto point : aPath )
tail = insertVertex( VECTOR2I( point.X, point.Y ), tail );
if( tail && ( *tail == *tail->next ) )
{
tail->next->remove();
}
return tail;
}
/** /**
* Function createList * Function createList
* Takes the SHAPE_LINE_CHAIN and links each point into a * Takes the SHAPE_LINE_CHAIN and links each point into a
@ -322,10 +345,10 @@ private:
* there is an intersection (not technically allowed by KiCad, but could exist in an edited file), * there is an intersection (not technically allowed by KiCad, but could exist in an edited file),
* we create a single triangle and remove both vertices before attempting to * we create a single triangle and remove both vertices before attempting to
*/ */
void earcutList( Vertex* aPoint, int pass = 0 ) bool earcutList( Vertex* aPoint, int pass = 0 )
{ {
if( !aPoint ) if( !aPoint )
return; return true;
Vertex* stop = aPoint; Vertex* stop = aPoint;
Vertex* prev; Vertex* prev;
@ -386,7 +409,7 @@ private:
/** /**
* At this point, our polygon should be fully tesselated. * At this point, our polygon should be fully tesselated.
*/ */
assert( aPoint->prev == aPoint->next ); return( aPoint->prev == aPoint->next );
} }
/** /**
@ -585,10 +608,12 @@ private:
return p; return p;
} }
public: public:
void TesselatePolygon( const SHAPE_LINE_CHAIN& aPoly ) void TesselatePolygon( const SHAPE_LINE_CHAIN& aPoly )
{ {
ClipperLib::Clipper c;
m_bbox = aPoly.BBox(); m_bbox = aPoly.BBox();
if( !m_bbox.GetWidth() || !m_bbox.GetHeight() ) if( !m_bbox.GetWidth() || !m_bbox.GetHeight() )
@ -599,7 +624,23 @@ public:
return; return;
outerNode->updateList(); outerNode->updateList();
earcutList( outerNode ); if( !earcutList( outerNode ) )
{
m_vertices.clear();
m_result.Clear();
ClipperLib::Paths simplified;
ClipperLib::SimplifyPolygon( aPoly.convertToClipper( true ), simplified );
for( auto path : simplified )
{
outerNode = createList( path );
if( !outerNode )
return;
earcutList( outerNode );
}
}
m_vertices.clear(); m_vertices.clear();
} }

View File

@ -34,6 +34,7 @@
#include <math/vector2d.h> #include <math/vector2d.h>
#include <geometry/shape.h> #include <geometry/shape.h>
#include <geometry/seg.h> #include <geometry/seg.h>
#include "clipper.hpp"
/** /**
* Class SHAPE_LINE_CHAIN * Class SHAPE_LINE_CHAIN
@ -125,6 +126,16 @@ public:
m_points[i] = *aV++; m_points[i] = *aV++;
} }
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) :
SHAPE( SH_LINE_CHAIN ),
m_closed( true )
{
m_points.reserve( aPath.size() );
for( auto point : aPath )
m_points.emplace_back( point.X, point.Y );
}
~SHAPE_LINE_CHAIN() ~SHAPE_LINE_CHAIN()
{} {}
@ -579,6 +590,19 @@ public:
*/ */
SHAPE_LINE_CHAIN& Simplify(); SHAPE_LINE_CHAIN& Simplify();
/**
* Function convertFromClipper()
* Appends the Clipper path to the current SHAPE_LINE_CHAIN
*
*/
void convertFromClipper( const ClipperLib::Path& aPath );
/**
* Creates a new Clipper path from the SHAPE_LINE_CHAIN in a given orientation
*
*/
ClipperLib::Path convertToClipper( bool aRequiredOrientation ) const;
/** /**
* Function NearestPoint() * Function NearestPoint()
* *

View File

@ -73,7 +73,11 @@ class SHAPE_POLY_SET : public SHAPE
int a, b, c; int a, b, c;
}; };
void Clear(); void Clear()
{
m_vertices.clear();
m_triangles.clear();
}
void GetTriangle( int index, VECTOR2I& a, VECTOR2I& b, VECTOR2I& c ) const void GetTriangle( int index, VECTOR2I& a, VECTOR2I& b, VECTOR2I& c ) const
{ {
@ -1116,9 +1120,6 @@ class SHAPE_POLY_SET : public SHAPE
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const; bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const;
const ClipperLib::Path convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation );
const SHAPE_LINE_CHAIN convertFromClipper( const ClipperLib::Path& aPath );
/** /**
* containsSingle function * containsSingle function
* Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If * Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If