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.

(cherry picked from commit 3ebba6cbe1)
This commit is contained in:
Seth Hillbrand 2018-09-12 03:19:11 -07:00
parent 4c409fbac9
commit 48b4a897af
6 changed files with 108 additions and 75 deletions

View File

@ -27,6 +27,25 @@
#include <common.h>
#include <geometry/shape_line_chain.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
{

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,
POLYGON_MODE aFastMode )
{
Clipper c;
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 );
booleanOp( aType, *this, aOtherShape, aFastMode );
}
@ -521,19 +469,18 @@ void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType,
{
Clipper c;
if( aFastMode == PM_STRICTLY_SIMPLE )
c.StrictlySimple( true );
c.StrictlySimple( aFastMode == PM_STRICTLY_SIMPLE );
for( const POLYGON& poly : aShape.m_polys )
for( auto poly : aShape.m_polys )
{
for( unsigned int i = 0; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true );
for( size_t i = 0 ; i < poly.size(); i++ )
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++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true );
for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( poly[i].convertToClipper( i == 0 ), ptClip, true );
}
PolyTree solution;
@ -598,9 +545,8 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount )
for( const POLYGON& poly : m_polys )
{
for( unsigned int i = 0; i < poly.size(); i++ )
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound,
etClosedPolygon );
for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( poly[i].convertToClipper( i == 0 ), jtRound, etClosedPolygon );
}
PolyTree solution;
@ -643,10 +589,10 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree )
{
POLYGON paths;
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++ )
paths.push_back( convertFromClipper( n->Childs[i]->Contour ) );
paths.push_back( n->Childs[i]->Contour );
m_polys.push_back( paths );
}
@ -1908,7 +1854,8 @@ void SHAPE_POLY_SET::CacheTriangulation()
SHAPE_POLY_SET tmpSet = *this;
tmpSet.Fracture( PM_FAST );
if( tmpSet.HasHoles() )
tmpSet.Fracture( PM_FAST );
m_triangulatedPolys.clear();

View File

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

View File

@ -51,6 +51,8 @@
#include <vector>
#include <math/box2.h>
#include "clipper.hpp"
class PolygonTriangulation
{
@ -291,6 +293,27 @@ private:
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
* 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),
* 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 )
return;
return true;
Vertex* stop = aPoint;
Vertex* prev;
@ -386,7 +409,7 @@ private:
/**
* 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;
}
public:
void TesselatePolygon( const SHAPE_LINE_CHAIN& aPoly )
{
ClipperLib::Clipper c;
m_bbox = aPoly.BBox();
if( !m_bbox.GetWidth() || !m_bbox.GetHeight() )
@ -599,7 +624,23 @@ public:
return;
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();
}

View File

@ -34,6 +34,7 @@
#include <math/vector2d.h>
#include <geometry/shape.h>
#include <geometry/seg.h>
#include "clipper.hpp"
/**
* Class SHAPE_LINE_CHAIN
@ -125,6 +126,16 @@ public:
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()
{}
@ -579,6 +590,19 @@ public:
*/
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()
*

View File

@ -73,7 +73,11 @@ class SHAPE_POLY_SET : public SHAPE
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
{
@ -1116,9 +1120,6 @@ class SHAPE_POLY_SET : public SHAPE
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
* Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If