diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index 648c83fa4c..d7175ab741 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -375,6 +375,11 @@ public: m_bbox.Inflate( m_width ); } + const BOX2I BBoxFromCache() const + { + return m_bbox; + } + /** * Function Collide() * diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index b13603bb80..ba4f3456f0 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -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 diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index e0dc3f485b..082875f832 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -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 diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 10be9c0c59..7a40629858 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -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; diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index fbca076244..edc1c1ea9e 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -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 ) { diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index b7b7d2a183..6432f530b0 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -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 diff --git a/pcbnew/drc/drc_courtyard_tester.cpp b/pcbnew/drc/drc_courtyard_tester.cpp index 8ffba268f5..26ac05af1b 100644 --- a/pcbnew/drc/drc_courtyard_tester.cpp +++ b/pcbnew/drc/drc_courtyard_tester.cpp @@ -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 ) ) + if( !aBoard.GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD ) + && footprint->GetPolyCourtyardFront().OutlineCount() == 0 + && footprint->GetPolyCourtyardBack().OutlineCount() == 0 ) { - int outlineCount = footprint->GetPolyCourtyardFront().OutlineCount() - + footprint->GetPolyCourtyardBack().OutlineCount(); - - if( outlineCount == 0 ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MISSING_COURTYARD ); - drcItem->SetItems( footprint ); - HandleMarker( new MARKER_PCB( drcItem, footprint->GetPosition() ) ); - success = false; - } + 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,79 +94,108 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const } } - 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++ ) + if( !aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) ) { - MODULE* footprint = *it1; + wxLogTrace( DRC_COURTYARD_TRACE, "Checking for courtyard overlap" ); - if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 ) - continue; // No courtyard defined - - for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ ) + for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ ) { - MODULE* candidate = *it2; + MODULE* footprint = *it1; + SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront(); + SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack(); - if( candidate->GetPolyCourtyardFront().OutlineCount() == 0 ) - continue; // No courtyard defined + if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 ) + continue; // No courtyards 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() ) + for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ ) { - //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 ) ) ); - success = false; + 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( 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: - 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 ) ) ); - success = false; + 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; + } + } } } } diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index 5db339ac51..b7f15c010f 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -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;