Separate polygonization error from chaining error.

This way we can report anything that doesn't exactly chain while still
applying  a fudge factor for our own stuff.

Fixes https://gitlab.com/kicad/code/kicad/issues/1795
This commit is contained in:
Jeff Young 2020-12-24 21:00:41 +00:00
parent 359c29639f
commit ef25c78a04
6 changed files with 63 additions and 35 deletions

View File

@ -473,8 +473,11 @@ bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
return false; return false;
} }
int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
success = BuildFootprintPolygonOutlines( m_board, m_board_poly, success = BuildFootprintPolygonOutlines( m_board, m_board_poly,
m_board->GetDesignSettings().m_MaxError ); m_board->GetDesignSettings().m_MaxError,
chainingEpsilon );
// Make polygon strictly simple to avoid issues (especially in 3D viewer) // Make polygon strictly simple to avoid issues (especially in 3D viewer)
m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );

View File

@ -1844,8 +1844,10 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE* aCurr
bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
OUTLINE_ERROR_HANDLER* aErrorHandler ) OUTLINE_ERROR_HANDLER* aErrorHandler )
{ {
int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError, bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
aErrorHandler ); chainingEpsilon, aErrorHandler );
// Make polygon strictly simple to avoid issues (especially in 3D viewer) // Make polygon strictly simple to avoid issues (especially in 3D viewer)
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );

View File

@ -185,11 +185,13 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint,
* These closed inner outlines are considered as holes in the main outline * These closed inner outlines are considered as holes in the main outline
* @param aSegList the initial list of drawsegments (only lines, circles and arcs). * @param aSegList the initial list of drawsegments (only lines, circles and arcs).
* @param aPolygons will contain the complex polygon. * @param aPolygons will contain the complex polygon.
* @param aTolerance is the max error distance when polygonizing a curve (internal units) * @param aErrorMax is the max error distance when polygonizing a curve (internal units)
* @param aChainingEpsilon is the max error distance when polygonizing a curve (internal units)
* @param aErrorHandler = an optional error handler * @param aErrorHandler = an optional error handler
*/ */
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons, bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
int aTolerance, OUTLINE_ERROR_HANDLER* aErrorHandler ) int aErrorMax, int aChainingEpsilon,
OUTLINE_ERROR_HANDLER* aErrorHandler )
{ {
if( aSegList.size() == 0 ) if( aSegList.size() == 0 )
return true; return true;
@ -237,7 +239,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
wxPoint center = graphic->GetCenter(); wxPoint center = graphic->GetCenter();
double angle = -graphic->GetAngle(); double angle = -graphic->GetAngle();
double radius = graphic->GetRadius(); double radius = graphic->GetRadius();
int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 ); int steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
wxPoint pt; wxPoint pt;
for( int step = 1; step<=steps; ++step ) for( int step = 1; step<=steps; ++step )
@ -432,11 +434,11 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
wxPoint pcenter = graphic->GetCenter(); wxPoint pcenter = graphic->GetCenter();
double angle = -graphic->GetAngle(); double angle = -graphic->GetAngle();
double radius = graphic->GetRadius(); double radius = graphic->GetRadius();
int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 ); int steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
if( !close_enough( prevPt, pstart, aTolerance ) ) if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
{ {
wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) ); wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aChainingEpsilon ) );
angle = -angle; angle = -angle;
std::swap( pstart, pend ); std::swap( pstart, pend );
@ -514,7 +516,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// Get next closest segment. // Get next closest segment.
PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aTolerance ); PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) ) if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
{ {
@ -526,7 +528,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// Finished, or ran into trouble... // Finished, or ran into trouble...
if( close_enough( startPt, prevPt, aTolerance ) ) if( close_enough( startPt, prevPt, aChainingEpsilon ) )
{ {
polygonComplete = true; polygonComplete = true;
break; break;
@ -595,7 +597,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
double angle = 3600.0; double angle = 3600.0;
wxPoint start = center; wxPoint start = center;
int radius = graphic->GetRadius(); int radius = graphic->GetRadius();
int steps = GetArcToSegmentCount( radius, aTolerance, 360.0 ); int steps = GetArcToSegmentCount( radius, aErrorMax, 360.0 );
wxPoint nextPt; wxPoint nextPt;
start.x += radius; start.x += radius;
@ -673,11 +675,12 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
wxPoint pcenter = graphic->GetCenter(); wxPoint pcenter = graphic->GetCenter();
double angle = -graphic->GetAngle(); double angle = -graphic->GetAngle();
int radius = graphic->GetRadius(); int radius = graphic->GetRadius();
int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 ); int steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
if( !close_enough( prevPt, pstart, aTolerance ) ) if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
{ {
wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) ); wxASSERT( close_enough( prevPt, graphic->GetArcEnd(),
aChainingEpsilon ) );
angle = -angle; angle = -angle;
std::swap( pstart, pend ); std::swap( pstart, pend );
@ -757,7 +760,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// Get next closest segment. // Get next closest segment.
PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aTolerance ); PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) ) if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
{ {
@ -769,7 +772,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
// Finished, or ran into trouble... // Finished, or ran into trouble...
if( close_enough( startPt, prevPt, aTolerance ) ) if( close_enough( startPt, prevPt, aChainingEpsilon ) )
{ {
break; break;
} }
@ -842,8 +845,8 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
* Any closed outline inside the main outline is a hole * Any closed outline inside the main outline is a hole
* All contours should be closed, i.e. valid closed polygon vertices * All contours should be closed, i.e. valid closed polygon vertices
*/ */
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance, bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
OUTLINE_ERROR_HANDLER* aErrorHandler ) int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler )
{ {
PCB_TYPE_COLLECTOR items; PCB_TYPE_COLLECTOR items;
bool success = false; bool success = false;
@ -863,7 +866,8 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aT
if( segList.size() ) if( segList.size() )
{ {
success = ConvertOutlineToPolygon( segList, aOutlines, aTolerance, aErrorHandler ); success = ConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon,
aErrorHandler );
} }
if( !success || !aOutlines.OutlineCount() ) if( !success || !aOutlines.OutlineCount() )
@ -1055,8 +1059,8 @@ int findEndSegments( SHAPE_LINE_CHAIN& aChain, SEG& aStartSeg, SEG& aEndSeg )
* * If copper is located outside a closed outline, then that outline will be treated * * If copper is located outside a closed outline, then that outline will be treated
* as a hole, and the outer edge will be formed using the bounding box. * as a hole, and the outer edge will be formed using the bounding box.
*/ */
bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance, bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
OUTLINE_ERROR_HANDLER* aErrorHandler ) int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler )
{ {
PCB_TYPE_COLLECTOR items; PCB_TYPE_COLLECTOR items;
@ -1075,7 +1079,8 @@ bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, in
segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) ); segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
} }
bool success = ConvertOutlineToPolygon( segList, outlines, aTolerance, aErrorHandler ); bool success = ConvertOutlineToPolygon( segList, outlines, aErrorMax, aChainingEpsilon,
aErrorHandler );
FOOTPRINT* footprint = aBoard->GetFirstFootprint(); FOOTPRINT* footprint = aBoard->GetFirstFootprint();

View File

@ -45,20 +45,22 @@ const std::function<void( const wxString& msg, BOARD_ITEM* itemA, BOARD_ITEM* it
* These closed inner outlines are considered as holes in the main outline * These closed inner outlines are considered as holes in the main outline
* @param aSegList the initial list of drawsegments (only lines, circles and arcs). * @param aSegList the initial list of drawsegments (only lines, circles and arcs).
* @param aPolygons will contain the complex polygon. * @param aPolygons will contain the complex polygon.
* @param aTolerance is the max error distance when polygonizing a curve (internal units) * @param aErrorMax is the max error distance when polygonizing a curve (internal units)
* @param aChainingEpsilon is the max distance from one endPt to the next startPt (internal units)
* @param aErrorHandler = an optional error handler * @param aErrorHandler = an optional error handler
*/ */
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons, bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
int aTolerance, OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr ); int aErrorMax, int aChainingEpsilon,
OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr );
/** /**
* Extracts the board outlines and build a closed polygon * Extracts the board outlines and build a closed polygon from lines, arcs and circle items on
* from lines, arcs and circle items on edge cut layer * edge cut layer. Any closed outline inside the main outline is a hole. All contours should be
* Any closed outline inside the main outline is a hole * closed, i.e. are valid vertices for a closed polygon.
* All contours should be closed, i.e. are valid vertices for a closed polygon * @return true if success, false if a contour is not valid
* return true if success, false if a contour is not valid
*/ */
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance, extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
int aErrorMax, int aChainingEpsilon,
OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr ); OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr );

View File

@ -80,7 +80,7 @@ private:
void DRC_TEST_PROVIDER_MISC::testOutline() void DRC_TEST_PROVIDER_MISC::testOutline()
{ {
SHAPE_POLY_SET boardOutlines; SHAPE_POLY_SET dummyOutline;
bool errorHandled = false; bool errorHandled = false;
OUTLINE_ERROR_HANDLER errorHandler = OUTLINE_ERROR_HANDLER errorHandler =
@ -95,7 +95,12 @@ void DRC_TEST_PROVIDER_MISC::testOutline()
errorHandled = true; errorHandled = true;
}; };
if( !m_board->GetBoardPolygonOutlines( boardOutlines, &errorHandler ) ) // Use a really tight chaining epsilon here so that we report errors that might affect
// other tools (such as STEP export).
constexpr int chainingEpsilon = Millimeter2iu( 0.02 ) / 100;
if( BuildBoardPolygonOutlines( m_board, dummyOutline, m_board->GetDesignSettings().m_MaxError,
chainingEpsilon, &errorHandler ) )
{ {
if( errorHandled ) if( errorHandled )
{ {

View File

@ -1801,18 +1801,29 @@ void FOOTPRINT::BuildPolyCourtyards( OUTLINE_ERROR_HANDLER* aErrorHandler )
if( !list_front.size() && !list_back.size() ) if( !list_front.size() && !list_back.size() )
return; return;
constexpr int errorMax = Millimeter2iu( 0.02 ); /* error max for polygonization */ int errorMax = Millimeter2iu( 0.02 ); // max error for polygonization
int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
if( ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, errorMax, aErrorHandler ) ) if( ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, errorMax, chainingEpsilon,
aErrorHandler ) )
{
m_poly_courtyard_front.CacheTriangulation( false ); m_poly_courtyard_front.CacheTriangulation( false );
}
else else
{
SetFlags( MALFORMED_F_COURTYARD ); SetFlags( MALFORMED_F_COURTYARD );
}
if( ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, errorMax, aErrorHandler ) ) if( ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, errorMax, chainingEpsilon,
aErrorHandler ) )
{
m_poly_courtyard_back.CacheTriangulation( false ); m_poly_courtyard_back.CacheTriangulation( false );
}
else else
{
SetFlags( MALFORMED_B_COURTYARD ); SetFlags( MALFORMED_B_COURTYARD );
} }
}
void FOOTPRINT::SwapData( BOARD_ITEM* aImage ) void FOOTPRINT::SwapData( BOARD_ITEM* aImage )