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:
parent
aedc624340
commit
13493437d0
|
@ -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()
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue