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 ); m_bbox.Inflate( m_width );
} }
const BOX2I BBoxFromCache() const
{
return m_bbox;
}
/** /**
* Function Collide() * Function Collide()
* *

View File

@ -1042,6 +1042,8 @@ class SHAPE_POLY_SET : public SHAPE
*/ */
void BuildBBoxCaches(); void BuildBBoxCaches();
const BOX2I BBoxFromCaches() const;
/** /**
* Returns true if a given subpolygon contains the point aP * 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, void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape,
const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode ); 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 * 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

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 bool SHAPE_POLY_SET::PointOnEdge( const VECTOR2I& aP ) const
{ {
// Iterate through all the polygons in the set // 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[ errorCode ] = RPT_SEVERITY_ERROR;
m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE; 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_MISSING_FOOTPRINT ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_DUPLICATE_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 // test courtyards
if( !m_pcb->GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) if( !m_pcb->GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD ) || !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 ) if( aMessages )
{ {

View File

@ -87,6 +87,8 @@ enum PCB_DRC_CODE {
DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined
DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes) ///< (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_DISABLED_LAYER_ITEM, ///< item on a disabled layer
DRCE_INVALID_OUTLINE, ///< invalid board outline DRCE_INVALID_OUTLINE, ///< invalid board outline
DRCE_MISSING_FOOTPRINT, ///< footprint not found for netlist item 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" ); wxLogTrace( DRC_COURTYARD_TRACE, "Running DRC: Courtyard" );
// Detects missing (or malformed) footprint courtyard, // Detects missing (or malformed) footprint courtyards and courtyard incursions (for those
// and for footprint with courtyard, courtyards overlap. // with a courtyard).
wxString msg; wxString msg;
bool success = true; bool success = true;
@ -63,18 +63,19 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
{ {
if( footprint->BuildPolyCourtyard() ) if( footprint->BuildPolyCourtyard() )
{ {
if( !aBoard.GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD ) ) if( !aBoard.GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD )
&& footprint->GetPolyCourtyardFront().OutlineCount() == 0
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
{ {
int outlineCount = footprint->GetPolyCourtyardFront().OutlineCount() DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MISSING_COURTYARD );
+ footprint->GetPolyCourtyardBack().OutlineCount(); drcItem->SetItems( footprint );
HandleMarker( new MARKER_PCB( drcItem, footprint->GetPosition() ) );
if( outlineCount == 0 ) success = false;
{ }
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MISSING_COURTYARD ); else
drcItem->SetItems( footprint ); {
HandleMarker( new MARKER_PCB( drcItem, footprint->GetPosition() ) ); footprint->GetPolyCourtyardFront().BuildBBoxCaches();
success = false; footprint->GetPolyCourtyardBack().BuildBBoxCaches();
}
} }
} }
else else
@ -93,79 +94,108 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
} }
} }
if( aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) ) if( !aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) )
return success;
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; wxLogTrace( DRC_COURTYARD_TRACE, "Checking for courtyard overlap" );
if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 ) for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ )
continue; // No courtyard defined
for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ )
{ {
MODULE* candidate = *it2; MODULE* footprint = *it1;
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack();
if( candidate->GetPolyCourtyardFront().OutlineCount() == 0 ) if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 )
continue; // No courtyard defined continue; // No courtyards defined
courtyard.RemoveAllContours(); for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ )
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() )
{ {
//Overlap between footprint and candidate MODULE* test = *it2;
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_OVERLAPPING_FOOTPRINTS ); SHAPE_POLY_SET& testFront = test->GetPolyCourtyardFront();
drcItem->SetItems( footprint, candidate ); SHAPE_POLY_SET& testBack = test->GetPolyCourtyardBack();
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) courtyard.CVertex( 0, 0, -1 ) ) ); SHAPE_POLY_SET intersection;
success = false; bool overlap = false;
wxPoint pos;
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 )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_OVERLAPPING_FOOTPRINTS );
drcItem->SetItems( footprint, test );
HandleMarker( new MARKER_PCB( drcItem, pos ) );
success = false;
}
} }
} }
} }
// Test for overlapping on bottom layer: if( !aBoard.GetDesignSettings().Ignore( DRCE_PTH_IN_COURTYARD )
for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ ) || !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 ) for( MODULE* footprint : aBoard.Modules() )
continue; // No courtyard defined
for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ )
{ {
MODULE* candidate = *it2; SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack();
if( candidate->GetPolyCourtyardBack().OutlineCount() == 0 ) if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 )
continue; // No courtyard defined continue; // No courtyards defined
courtyard.RemoveAllContours(); for( MODULE* candidate : aBoard.Modules() )
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() )
{ {
//Overlap between footprint and candidate if( footprint == candidate )
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_OVERLAPPING_FOOTPRINTS ); continue;
drcItem->SetItems( footprint, candidate );
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) courtyard.CVertex( 0, 0, -1 ) ) ); for( D_PAD* pad : candidate->Pads() )
success = false; {
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;
}
}
} }
} }
} }

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_OVERLAPPING_FOOTPRINTS: msg = _HKI( "Courtyards overlap" ); break;
case DRCE_MISSING_COURTYARD: msg = _HKI( "Footprint has no courtyard defined" ); 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_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_DUPLICATE_FOOTPRINT: msg = _HKI( "Duplicate footprints" ); break;
case DRCE_MISSING_FOOTPRINT: msg = _HKI( "Missing footprint" ); break; case DRCE_MISSING_FOOTPRINT: msg = _HKI( "Missing footprint" ); break;