Add hole-inside-courtyard DRC checks.

One for NPTH and one for PTH; both default to "ignore".

Fixes https://gitlab.com/kicad/code/kicad/issues/3434
This commit is contained in:
Jeff Young 2020-05-14 22:01:39 +01:00
parent aedc624340
commit 13493437d0
8 changed files with 132 additions and 74 deletions

View File

@ -375,6 +375,11 @@ public:
m_bbox.Inflate( m_width );
}
const BOX2I BBoxFromCache() const
{
return m_bbox;
}
/**
* Function Collide()
*

View File

@ -1042,6 +1042,8 @@ class SHAPE_POLY_SET : public SHAPE
*/
void BuildBBoxCaches();
const BOX2I BBoxFromCaches() const;
/**
* Returns true if a given subpolygon contains the point aP
*
@ -1236,9 +1238,6 @@ class SHAPE_POLY_SET : public SHAPE
void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape,
const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode );
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath,
bool aIgnoreEdges, bool aUseBBoxCaches = false ) const;
/**
* containsSingle function
* Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If

View File

@ -1185,6 +1185,22 @@ const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
}
const BOX2I SHAPE_POLY_SET::BBoxFromCaches() const
{
BOX2I bb;
for( unsigned i = 0; i < m_polys.size(); i++ )
{
if( i == 0 )
bb = m_polys[i][0].BBoxFromCache();
else
bb.Merge( m_polys[i][0].BBoxFromCache() );
}
return bb;
}
bool SHAPE_POLY_SET::PointOnEdge( const VECTOR2I& aP ) const
{
// Iterate through all the polygons in the set

View File

@ -629,6 +629,8 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() :
m_DRCSeverities[ errorCode ] = RPT_SEVERITY_ERROR;
m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE;
m_DRCSeverities[ DRCE_PTH_IN_COURTYARD ] = RPT_SEVERITY_IGNORE;
m_DRCSeverities[ DRCE_NPTH_IN_COURTYARD ] = RPT_SEVERITY_IGNORE;
m_DRCSeverities[ DRCE_MISSING_FOOTPRINT ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_DUPLICATE_FOOTPRINT ] = RPT_SEVERITY_WARNING;

View File

@ -478,7 +478,9 @@ void DRC::RunTests( wxTextCtrl* aMessages )
// test courtyards
if( !m_pcb->GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_MALFORMED_COURTYARD ) )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_MALFORMED_COURTYARD )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_PTH_IN_COURTYARD )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_NPTH_IN_COURTYARD ) )
{
if( aMessages )
{

View File

@ -87,6 +87,8 @@ enum PCB_DRC_CODE {
DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined
DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes)
DRCE_PTH_IN_COURTYARD,
DRCE_NPTH_IN_COURTYARD,
DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
DRCE_INVALID_OUTLINE, ///< invalid board outline
DRCE_MISSING_FOOTPRINT, ///< footprint not found for netlist item

View File

@ -53,8 +53,8 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
{
wxLogTrace( DRC_COURTYARD_TRACE, "Running DRC: Courtyard" );
// Detects missing (or malformed) footprint courtyard,
// and for footprint with courtyard, courtyards overlap.
// Detects missing (or malformed) footprint courtyards and courtyard incursions (for those
// with a courtyard).
wxString msg;
bool success = true;
@ -63,18 +63,19 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
{
if( footprint->BuildPolyCourtyard() )
{
if( !aBoard.GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD ) )
{
int outlineCount = footprint->GetPolyCourtyardFront().OutlineCount()
+ footprint->GetPolyCourtyardBack().OutlineCount();
if( outlineCount == 0 )
if( !aBoard.GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD )
&& footprint->GetPolyCourtyardFront().OutlineCount() == 0
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MISSING_COURTYARD );
drcItem->SetItems( footprint );
HandleMarker( new MARKER_PCB( drcItem, footprint->GetPosition() ) );
success = false;
}
else
{
footprint->GetPolyCourtyardFront().BuildBBoxCaches();
footprint->GetPolyCourtyardBack().BuildBBoxCaches();
}
}
else
@ -93,82 +94,111 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
}
}
if( aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) )
return success;
if( !aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) )
{
wxLogTrace( DRC_COURTYARD_TRACE, "Checking for courtyard overlap" );
// Now test for overlapping on top layer:
SHAPE_POLY_SET courtyard; // temporary storage of the courtyard of current footprint
for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ )
{
MODULE* footprint = *it1;
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack();
if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 )
continue; // No courtyard defined
if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 )
continue; // No courtyards defined
for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ )
{
MODULE* candidate = *it2;
MODULE* test = *it2;
SHAPE_POLY_SET& testFront = test->GetPolyCourtyardFront();
SHAPE_POLY_SET& testBack = test->GetPolyCourtyardBack();
SHAPE_POLY_SET intersection;
bool overlap = false;
wxPoint pos;
if( candidate->GetPolyCourtyardFront().OutlineCount() == 0 )
continue; // No courtyard defined
courtyard.RemoveAllContours();
courtyard.Append( footprint->GetPolyCourtyardFront() );
// Build the common area between footprint and the candidate:
courtyard.BooleanIntersection( candidate->GetPolyCourtyardFront(),
SHAPE_POLY_SET::PM_FAST );
// If no overlap, courtyard is empty (no common area).
// Therefore if a common polygon exists, this is a DRC error
if( courtyard.OutlineCount() )
if( footprintFront.OutlineCount() > 0 && testFront.OutlineCount() > 0
&& footprintFront.BBoxFromCaches().Intersects( testFront.BBoxFromCaches() ) )
{
intersection.RemoveAllContours();
intersection.Append( footprintFront );
// Build the common area between footprint and the test:
intersection.BooleanIntersection( testFront, SHAPE_POLY_SET::PM_FAST );
// If the intersection exists then they overlap
if( intersection.OutlineCount() > 0 )
{
overlap = true;
pos = (wxPoint) intersection.CVertex( 0, 0, -1 );
}
}
if( footprintBack.OutlineCount() > 0 && testBack.OutlineCount() > 0
&& footprintBack.BBoxFromCaches().Intersects( testBack.BBoxFromCaches() ) )
{
intersection.RemoveAllContours();
intersection.Append( footprintBack );
intersection.BooleanIntersection( testBack, SHAPE_POLY_SET::PM_FAST );
if( intersection.OutlineCount() > 0 )
{
overlap = true;
pos = (wxPoint) intersection.CVertex( 0, 0, -1 );
}
}
if( overlap )
{
//Overlap between footprint and candidate
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_OVERLAPPING_FOOTPRINTS );
drcItem->SetItems( footprint, candidate );
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) courtyard.CVertex( 0, 0, -1 ) ) );
drcItem->SetItems( footprint, test );
HandleMarker( new MARKER_PCB( drcItem, pos ) );
success = false;
}
}
}
}
// Test for overlapping on bottom layer:
for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ )
if( !aBoard.GetDesignSettings().Ignore( DRCE_PTH_IN_COURTYARD )
|| !aBoard.GetDesignSettings().Ignore( DRCE_NPTH_IN_COURTYARD ) )
{
MODULE* footprint = *it1;
wxLogTrace( DRC_COURTYARD_TRACE, "Checking for through-holes in courtyards" );
if( footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
continue; // No courtyard defined
for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ )
for( MODULE* footprint : aBoard.Modules() )
{
MODULE* candidate = *it2;
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack();
if( candidate->GetPolyCourtyardBack().OutlineCount() == 0 )
continue; // No courtyard defined
if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 )
continue; // No courtyards defined
courtyard.RemoveAllContours();
courtyard.Append( footprint->GetPolyCourtyardBack() );
// Build the common area between footprint and the candidate:
courtyard.BooleanIntersection( candidate->GetPolyCourtyardBack(),
SHAPE_POLY_SET::PM_FAST );
// If no overlap, courtyard is empty (no common area).
// Therefore if a common polygon exists, this is a DRC error
if( courtyard.OutlineCount() )
for( MODULE* candidate : aBoard.Modules() )
{
//Overlap between footprint and candidate
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_OVERLAPPING_FOOTPRINTS );
drcItem->SetItems( footprint, candidate );
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) courtyard.CVertex( 0, 0, -1 ) ) );
if( footprint == candidate )
continue;
for( D_PAD* pad : candidate->Pads() )
{
if( pad->GetDrillSize().x == 0 || pad->GetDrillSize().y == 0 )
continue;
wxPoint pos = pad->GetPosition();
if( footprintFront.Contains( pos, -1, 0, true /* use bbox caches */ )
|| footprintBack.Contains( pos, -1, 0, true /* use bbox caches */ ) )
{
int code = pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ?
DRCE_NPTH_IN_COURTYARD :
DRCE_PTH_IN_COURTYARD;
DRC_ITEM* drcItem = new DRC_ITEM( code );
drcItem->SetItems( footprint, pad );
HandleMarker( new MARKER_PCB( drcItem, pos ) );
success = false;
}
}
}
}
}
return success;
}

View File

@ -115,6 +115,8 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const
case DRCE_OVERLAPPING_FOOTPRINTS: msg = _HKI( "Courtyards overlap" ); break;
case DRCE_MISSING_COURTYARD: msg = _HKI( "Footprint has no courtyard defined" ); break;
case DRCE_MALFORMED_COURTYARD: msg = _HKI( "Footprint has malformed courtyard" ); break;
case DRCE_PTH_IN_COURTYARD: msg = _HKI( "PTH inside courtyard" ); break;
case DRCE_NPTH_IN_COURTYARD: msg = _HKI( "NPTH inside courtyard" ); break;
case DRCE_DUPLICATE_FOOTPRINT: msg = _HKI( "Duplicate footprints" ); break;
case DRCE_MISSING_FOOTPRINT: msg = _HKI( "Missing footprint" ); break;