From 386cefbe8463f5f5c4f1e8900312face41c3f85c Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sun, 25 Oct 2020 15:02:49 +0000 Subject: [PATCH] Do footprint keepouts by courtyard. Fixes https://gitlab.com/kicad/code/kicad/issues/6162 --- include/eda_item.h | 2 +- pcbnew/autorouter/ar_autoplacer.cpp | 12 +- pcbnew/class_module.cpp | 26 ++-- pcbnew/class_module.h | 8 +- pcbnew/drc/drc_engine.cpp | 2 + pcbnew/drc/drc_test_provider.cpp | 10 +- .../drc_test_provider_courtyard_clearance.cpp | 35 +++-- pcbnew/exporters/gerber_placefile_writer.cpp | 4 +- pcbnew/pcb_expr_evaluator.cpp | 123 ++++++++++++------ pcbnew/tools/pcb_inspection_tool.cpp | 4 +- 10 files changed, 142 insertions(+), 84 deletions(-) diff --git a/include/eda_item.h b/include/eda_item.h index 4775c15691..776ac9df90 100644 --- a/include/eda_item.h +++ b/include/eda_item.h @@ -120,7 +120,7 @@ typedef const INSPECTOR_FUNC& INSPECTOR; /// std::function passed to nested u #define TRACK_LOCKED (1 << 18) ///< Pcbnew: track locked: protected from global deletion #define TRACK_AR (1 << 19) ///< Pcbnew: autorouted track #define OBSOLETE_1 (1 << 20) ///< Not presently used -#define OBSOLETE_2 (1 << 21) ///< Not presently used +#define MALFORMED_COURTYARD (1 << 21) #define BEGIN_ONPAD (1 << 22) ///< Pcbnew: flag set for track segment starting on a pad #define END_ONPAD (1 << 23) ///< Pcbnew: flag set for track segment ending on a pad #define HOLE_PROXY (1 << 24) ///< Indicates the BOARD_ITEM is a proxy for its hole diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index 6128e5d8a2..34b3ea2448 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -336,13 +336,11 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance ) m_fpAreaTop.RemoveAllContours(); m_fpAreaBottom.RemoveAllContours(); - if( aFootprint->BuildPolyCourtyard() ) - { - m_fpAreaTop = aFootprint->GetPolyCourtyardFront(); - m_fpAreaBottom = aFootprint->GetPolyCourtyardBack(); - } + aFootprint->BuildPolyCourtyards(); + m_fpAreaTop = aFootprint->GetPolyCourtyardFront(); + m_fpAreaBottom = aFootprint->GetPolyCourtyardBack(); - LSET layerMask; + LSET layerMask; if( aFootprint->GetLayer() == F_Cu ) layerMask.set( F_Cu ); @@ -350,7 +348,7 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance ) if( aFootprint->GetLayer() == B_Cu ) layerMask.set( B_Cu ); - EDA_RECT fpBBox = aFootprint->GetBoundingBox(); + EDA_RECT fpBBox = aFootprint->GetBoundingBox(); fpBBox.Inflate( ( m_matrix.m_GridRouting / 2 ) + aFpClearance ); diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 4fe38ed25f..c1c4e1c1e1 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -1676,10 +1676,12 @@ std::shared_ptr MODULE::GetEffectiveShape( PCB_LAYER_ID aLayer ) const } -bool MODULE::BuildPolyCourtyard() +void MODULE::BuildPolyCourtyards() { m_poly_courtyard_front.RemoveAllContours(); m_poly_courtyard_back.RemoveAllContours(); + ClearFlags( MALFORMED_COURTYARD ); + // Build the courtyard area from graphic items on the courtyard. // Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored. // Collect items: @@ -1695,22 +1697,22 @@ bool MODULE::BuildPolyCourtyard() list_front.push_back( static_cast( item ) ); } - // Note: if no item found on courtyard layers, return true. - // false is returned only when the shape defined on courtyard layers - // is not convertible to a polygon if( !list_front.size() && !list_back.size() ) - return true; + return; wxString error_msg; #define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */ - bool success = ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, - (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ); - - if( success ) + if( !ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, + (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) ) { - success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, - (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ); + SetFlags( MALFORMED_COURTYARD ); + } + + if( !ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, + (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) ) + { + SetFlags( MALFORMED_COURTYARD ); } if( !error_msg.IsEmpty() ) @@ -1719,8 +1721,6 @@ bool MODULE::BuildPolyCourtyard() GetFPID().Format().wx_str(), error_msg ) ); } - - return success; } diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index 4a814a0d46..dead267f12 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -668,11 +668,11 @@ public: SHAPE_POLY_SET& GetPolyCourtyardBack() { return m_poly_courtyard_back; } /** - * Builds a complex polygon of the courtyard area from graphic items on the courtyard layer - * @return true if OK, or no courtyard defined, - * false only if the polygon cannot be built due to a malformed courtyard shape + * Builds complex polygons of the courtyard areas from graphic items on the courtyard layers + * @remark sets the MALFORMED_FRONT_COURTYARD and MALFORMED_BACK_COURTYARD status flags if + * the given courtyard layer does not contain a (single) closed shape */ - bool BuildPolyCourtyard(); + void BuildPolyCourtyards(); virtual std::shared_ptr GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override; diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp index 4e755ecf71..f4ffc22854 100644 --- a/pcbnew/drc/drc_engine.cpp +++ b/pcbnew/drc/drc_engine.cpp @@ -662,6 +662,8 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aTestTracksAgainstZones, { for( ZONE_CONTAINER* zone : module->Zones() ) zone->CacheBoundingBox(); + + module->BuildPolyCourtyards(); } for( DRC_TEST_PROVIDER* provider : m_testProviders ) diff --git a/pcbnew/drc/drc_test_provider.cpp b/pcbnew/drc/drc_test_provider.cpp index 1fcc735616..3e681036f7 100644 --- a/pcbnew/drc/drc_test_provider.cpp +++ b/pcbnew/drc/drc_test_provider.cpp @@ -270,13 +270,21 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector& aTypes, { if( (zone->GetLayerSet() & aLayers).any() ) { - if( ! aFunc( zone ) ) + if( !aFunc( zone ) ) return n; n++; } } } + + if( typeMask[ PCB_MODULE_T ] ) + { + if( !aFunc( mod ) ) + return n; + + n++; + } } return n; diff --git a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp index c4081b3e8c..6dc0d4b4d5 100644 --- a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp @@ -88,25 +88,7 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions() if( !reportProgress( ii++, m_board->Modules().size(), delta ) ) return; - if( footprint->BuildPolyCourtyard() ) - { - if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 - && footprint->GetPolyCourtyardBack().OutlineCount() == 0 ) - { - if( m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD ) ) - continue; - - std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD ); - drcItem->SetItems( footprint ); - reportViolation( drcItem, footprint->GetPosition()); - } - else - { - footprint->GetPolyCourtyardFront().BuildBBoxCaches(); - footprint->GetPolyCourtyardBack().BuildBBoxCaches(); - } - } - else + if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 ) { if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) ) continue; @@ -119,6 +101,21 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions() drcItem->SetItems( footprint ); reportViolation( drcItem, footprint->GetPosition()); } + else if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 + && footprint->GetPolyCourtyardBack().OutlineCount() == 0 ) + { + if( m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD ) ) + continue; + + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD ); + drcItem->SetItems( footprint ); + reportViolation( drcItem, footprint->GetPosition()); + } + else + { + footprint->GetPolyCourtyardFront().BuildBBoxCaches(); + footprint->GetPolyCourtyardBack().BuildBBoxCaches(); + } } } diff --git a/pcbnew/exporters/gerber_placefile_writer.cpp b/pcbnew/exporters/gerber_placefile_writer.cpp index 93b1831ffc..09d97a8b6a 100644 --- a/pcbnew/exporters/gerber_placefile_writer.cpp +++ b/pcbnew/exporters/gerber_placefile_writer.cpp @@ -177,7 +177,9 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER // If not, the pads bounding box will be used. bool useFpPadsBbox = true; - if( footprint->BuildPolyCourtyard() ) + footprint->BuildPolyCourtyards(); + + if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) == 0 ) { gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_COURTYARD ); diff --git a/pcbnew/pcb_expr_evaluator.cpp b/pcbnew/pcb_expr_evaluator.cpp index b7d16c57da..2989d26258 100644 --- a/pcbnew/pcb_expr_evaluator.cpp +++ b/pcbnew/pcb_expr_evaluator.cpp @@ -213,18 +213,97 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) PCB_EXPR_VAR_REF* vref = static_cast( self ); BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; - ZONE_CONTAINER* zone = nullptr; if( !item ) return; + auto insideZone = + [&]( ZONE_CONTAINER* zone ) -> bool + { + if( !zone ) + return false; + + if( !zone->GetCachedBoundingBox().Intersects( item->GetBoundingBox() ) ) + return false; + + if( item->GetFlags() & HOLE_PROXY ) + { + if( item->Type() == PCB_PAD_T ) + { + D_PAD* pad = static_cast( item ); + const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape(); + + return zone->Outline()->Collide( holeShape ); + } + else if( item->Type() == PCB_VIA_T ) + { + VIA* via = static_cast( item ); + const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() ); + + return zone->Outline()->Collide( &holeShape ); + } + + return false; + } + + if( item->Type() == PCB_MODULE_T ) + { + MODULE* footprint = static_cast( item ); + + if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 ) + { + aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) ); + return false; + } + + if( ( zone->GetLayerSet() & LSET::FrontMask() ).any() ) + { + SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardFront(); + + if( courtyard.OutlineCount() == 0 ) + { + aCtx->ReportError( _( "Footprint has no front courtyard." ) ); + return false; + } + else + { + return zone->Outline()->Collide( &courtyard.Outline( 0 ) ); + } + } + + if( ( zone->GetLayerSet() & LSET::BackMask() ).any() ) + { + SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardBack(); + + if( courtyard.OutlineCount() == 0 ) + { + aCtx->ReportError( _( "Footprint has no back courtyard." ) ); + return false; + } + else + { + return zone->Outline()->Collide( &courtyard.Outline( 0 ) ); + } + } + + return false; + } + + + std::shared_ptr shape = item->GetEffectiveShape( context->GetLayer() ); + + return zone->Outline()->Collide( shape.get() ); + }; + if( arg->AsString() == "A" ) { - zone = dynamic_cast( context->GetItem( 0 ) ); + if( insideZone( dynamic_cast( context->GetItem( 0 ) ) ) ) + result->Set( 1.0 ); } else if( arg->AsString() == "B" ) { - zone = dynamic_cast( context->GetItem( 1 ) ); + if( insideZone( dynamic_cast( context->GetItem( 1 ) ) ) ) + result->Set( 1.0 ); } else { @@ -232,42 +311,12 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) { if( candidate->GetZoneName().Matches( arg->AsString() ) ) { - zone = candidate; - break; - } - } - } - - if( zone ) - { - if( !zone->GetCachedBoundingBox().Intersects( item->GetBoundingBox() ) ) - return; - - if( item->GetFlags() & HOLE_PROXY ) - { - if( item->Type() == PCB_PAD_T ) - { - D_PAD* pad = static_cast( item ); - const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape(); - - if( zone->Outline()->Collide( holeShape ) ) + if( insideZone( candidate ) ) + { result->Set( 1.0 ); + return; + } } - else if( item->Type() == PCB_VIA_T ) - { - VIA* via = static_cast( item ); - const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() ); - - if( zone->Outline()->Collide( &holeShape ) ) - result->Set( 1.0 ); - } - } - else - { - std::shared_ptr itemShape = item->GetEffectiveShape( context->GetLayer() ); - - if( zone->Outline()->Collide( itemShape.get() ) ) - result->Set( 1.0 ); } } } diff --git a/pcbnew/tools/pcb_inspection_tool.cpp b/pcbnew/tools/pcb_inspection_tool.cpp index 91292f11f3..10aa696e63 100644 --- a/pcbnew/tools/pcb_inspection_tool.cpp +++ b/pcbnew/tools/pcb_inspection_tool.cpp @@ -408,6 +408,8 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) { for( ZONE_CONTAINER* zone : module->Zones() ) zone->CacheBoundingBox(); + + module->BuildPolyCourtyards(); } if( item->Type() == PCB_TRACE_T ) @@ -570,7 +572,7 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) r->Report( "" ); if( constraint.m_DisallowFlags ) - r->Report( _( "Item disallowed at current location." ) ); + r->Report( _( "Item disallowed at current location." ) ); else r->Report( _( "Item allowed at current location." ) ); }