From ce46fe6581149a00a659e9bf765767bbf5ac940c Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sat, 20 Mar 2021 20:09:57 +0000 Subject: [PATCH] Add insideFrontCourtyard and insideBackCourtyard DRC functions. Fixes https://gitlab.com/kicad/code/kicad/issues/7972 --- pcbnew/board.h | 4 + pcbnew/dialogs/panel_setup_rules_help.md | 32 +++- pcbnew/pcb_expr_evaluator.cpp | 226 +++++++++++++++++++---- 3 files changed, 225 insertions(+), 37 deletions(-) diff --git a/pcbnew/board.h b/pcbnew/board.h index 9929cca758..8f238a30e1 100644 --- a/pcbnew/board.h +++ b/pcbnew/board.h @@ -276,6 +276,8 @@ public: std::unique_lock cacheLock( m_CachesMutex ); m_InsideAreaCache.clear(); m_InsideCourtyardCache.clear(); + m_InsideFCourtyardCache.clear(); + m_InsideBCourtyardCache.clear(); } m_CopperZoneRTrees.clear(); @@ -1149,6 +1151,8 @@ public: std::mutex m_CachesMutex; std::map< std::pair, bool > m_InsideCourtyardCache; + std::map< std::pair, bool > m_InsideFCourtyardCache; + std::map< std::pair, bool > m_InsideBCourtyardCache; std::map< std::pair, bool > m_InsideAreaCache; std::map< ZONE*, std::unique_ptr > m_CopperZoneRTrees; diff --git a/pcbnew/dialogs/panel_setup_rules_help.md b/pcbnew/dialogs/panel_setup_rules_help.md index f26a85fba9..cf75729ab4 100644 --- a/pcbnew/dialogs/panel_setup_rules_help.md +++ b/pcbnew/dialogs/panel_setup_rules_help.md @@ -5,6 +5,8 @@ (rule ...) +

+ ### Rule Clauses (constraint ...) @@ -14,6 +16,8 @@ (layer "") +

+ ### Constraint Types * annular_width @@ -32,6 +36,8 @@ * via_count +

+ ### Item Types * buried_via @@ -44,6 +50,7 @@ * via * zone +
### Examples @@ -67,7 +74,7 @@ (rule HV_unshielded (constraint clearance (min 2mm)) (condition "A.NetClass == 'HV' && !A.insideArea('Shield*')")) - +

### Notes @@ -78,32 +85,45 @@ precedence over earlier rules; once a matching rule is found no further rules will be checked. Use Ctrl+/ to comment or uncomment line(s). - - +


### Expression functions All function parameters support simple wildcards (`*` and `?`). +

A.insideCourtyard('') -True if any part of `A` lies within the given footprint's courtyard. +True if any part of `A` lies within the given footprint's principal courtyard. +

+ + A.insideFrontCourtyard('') +True if any part of `A` lies within the given footprint's front courtyard. +

+ + A.insideBackCourtyard('') +True if any part of `A` lies within the given footprint's back courtyard. +

A.insideArea('') True if any part of `A` lies within the given zone's outline. +

A.isPlated() True if `A` has a hole which is plated. +

A.isDiffPair() True if `A` has a net that is part of a differential pair. +

A.inDiffPair('') True if `A` has net that is part of the specified differential pair. `` is the base name of the differential pair. For example, `inDiffPair('CLK')` -matches items in the `CLK_P` and `CLK_N` nets. +matches items in the `CLK_P` and `CLK_N` nets.
A.memberOf('') True if `A` is a member of the given group. Includes nested membership. +

A.existsOnLayer('') True if `A` exists on the given layer. The layer name can be @@ -113,7 +133,7 @@ the canonical name (ie: `F.Cu`). NB: this returns true if `A` is on the given layer, independently of whether or not the rule is being evaluated for that layer. For the latter use a `(layer "layer_name")` clause in the rule. - +


### More Examples diff --git a/pcbnew/pcb_expr_evaluator.cpp b/pcbnew/pcb_expr_evaluator.cpp index 046b41123a..8608b5b904 100644 --- a/pcbnew/pcb_expr_evaluator.cpp +++ b/pcbnew/pcb_expr_evaluator.cpp @@ -135,6 +135,40 @@ static void isPlated( LIBEVAL::CONTEXT* aCtx, void* self ) } +static bool insideFootprintCourtyard( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, + std::shared_ptr aItemShape, PCB_EXPR_CONTEXT* aCtx, + FOOTPRINT* aFootprint, PCB_LAYER_ID aSide = UNDEFINED_LAYER ) +{ + SHAPE_POLY_SET footprintCourtyard; + + if( aSide == F_Cu ) + footprintCourtyard = aFootprint->GetPolyCourtyardFront(); + else if( aSide == B_Cu ) + footprintCourtyard = aFootprint->GetPolyCourtyardBack(); + else if( aFootprint->IsFlipped() ) + footprintCourtyard = aFootprint->GetPolyCourtyardBack(); + else + footprintCourtyard = aFootprint->GetPolyCourtyardFront(); + + if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T ) + { + // A zone must be entirely inside the courtyard to be considered + if( !aFootprint->GetBoundingBox().Contains( aItemBBox ) ) + return false; + } + else + { + if( !aFootprint->GetBoundingBox().Intersects( aItemBBox ) ) + return false; + } + + if( !aItemShape ) + aItemShape = aItem->GetEffectiveShape( aCtx->GetLayer() ); + + return footprintCourtyard.Collide( aItemShape.get() ); +}; + + static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) { PCB_EXPR_CONTEXT* context = static_cast( aCtx ); @@ -166,34 +200,6 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) else itemBBox = item->GetBoundingBox(); - auto realInsideFootprint = - [&]( FOOTPRINT* footprint ) -> bool - { - SHAPE_POLY_SET footprintCourtyard; - - if( footprint->IsFlipped() ) - footprintCourtyard = footprint->GetPolyCourtyardBack(); - else - footprintCourtyard = footprint->GetPolyCourtyardFront(); - - if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) - { - // A zone must be entirely inside the courtyard to be considered - if( !footprint->GetBoundingBox().Contains( itemBBox ) ) - return false; - } - else - { - if( !footprint->GetBoundingBox().Intersects( itemBBox ) ) - return false; - } - - if( !shape ) - shape = item->GetEffectiveShape( context->GetLayer() ); - - return footprintCourtyard.Collide( shape.get() ); - }; - auto insideFootprint = [&]( FOOTPRINT* footprint ) -> bool { @@ -207,10 +213,166 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) if( i != board->m_InsideCourtyardCache.end() ) return i->second; - bool isInside = realInsideFootprint( footprint ); + bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint ); - board->m_InsideCourtyardCache[ key ] = isInside; - return isInside; + board->m_InsideCourtyardCache[ key ] = result; + return result; + }; + + if( arg->AsString() == "A" ) + { + if( insideFootprint( dynamic_cast( context->GetItem( 0 ) ) ) ) + result->Set( 1.0 ); + } + else if( arg->AsString() == "B" ) + { + if( insideFootprint( dynamic_cast( context->GetItem( 1 ) ) ) ) + result->Set( 1.0 ); + } + else + { + for( FOOTPRINT* candidate : board->Footprints() ) + { + if( candidate->GetReference().Matches( arg->AsString() ) ) + { + if( insideFootprint( candidate ) ) + { + result->Set( 1.0 ); + return; + } + } + } + } +} + + +static void insideFrontCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) +{ + PCB_EXPR_CONTEXT* context = static_cast( aCtx ); + LIBEVAL::VALUE* arg = aCtx->Pop(); + LIBEVAL::VALUE* result = aCtx->AllocValue(); + + result->Set( 0.0 ); + aCtx->Push( result ); + + if( !arg ) + { + aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ), + wxT( "insideFrontCourtyard()" ) ) ); + return; + } + + PCB_EXPR_VAR_REF* vref = static_cast( self ); + BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; + + if( !item ) + return; + + BOARD* board = item->GetBoard(); + EDA_RECT itemBBox; + std::shared_ptr shape; + + if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) + itemBBox = static_cast( item )->GetCachedBoundingBox(); + else + itemBBox = item->GetBoundingBox(); + + auto insideFootprint = + [&]( FOOTPRINT* footprint ) -> bool + { + if( !footprint ) + return false; + + std::unique_lock cacheLock( board->m_CachesMutex ); + std::pair key( footprint, item ); + auto i = board->m_InsideFCourtyardCache.find( key ); + + if( i != board->m_InsideFCourtyardCache.end() ) + return i->second; + + bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint, + F_Cu ); + + board->m_InsideFCourtyardCache[ key ] = result; + return result; + }; + + if( arg->AsString() == "A" ) + { + if( insideFootprint( dynamic_cast( context->GetItem( 0 ) ) ) ) + result->Set( 1.0 ); + } + else if( arg->AsString() == "B" ) + { + if( insideFootprint( dynamic_cast( context->GetItem( 1 ) ) ) ) + result->Set( 1.0 ); + } + else + { + for( FOOTPRINT* candidate : board->Footprints() ) + { + if( candidate->GetReference().Matches( arg->AsString() ) ) + { + if( insideFootprint( candidate ) ) + { + result->Set( 1.0 ); + return; + } + } + } + } +} + + +static void insideBackCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) +{ + PCB_EXPR_CONTEXT* context = static_cast( aCtx ); + LIBEVAL::VALUE* arg = aCtx->Pop(); + LIBEVAL::VALUE* result = aCtx->AllocValue(); + + result->Set( 0.0 ); + aCtx->Push( result ); + + if( !arg ) + { + aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ), + wxT( "insideBackCourtyard()" ) ) ); + return; + } + + PCB_EXPR_VAR_REF* vref = static_cast( self ); + BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; + + if( !item ) + return; + + BOARD* board = item->GetBoard(); + EDA_RECT itemBBox; + std::shared_ptr shape; + + if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) + itemBBox = static_cast( item )->GetCachedBoundingBox(); + else + itemBBox = item->GetBoundingBox(); + + auto insideFootprint = + [&]( FOOTPRINT* footprint ) -> bool + { + if( !footprint ) + return false; + + std::unique_lock cacheLock( board->m_CachesMutex ); + std::pair key( footprint, item ); + auto i = board->m_InsideBCourtyardCache.find( key ); + + if( i != board->m_InsideBCourtyardCache.end() ) + return i->second; + + bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint, + B_Cu ); + + board->m_InsideBCourtyardCache[ key ] = result; + return result; }; if( arg->AsString() == "A" ) @@ -613,6 +775,8 @@ void PCB_EXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions() RegisterFunc( "existsOnLayer('x')", existsOnLayer ); RegisterFunc( "isPlated()", isPlated ); RegisterFunc( "insideCourtyard('x')", insideCourtyard ); + RegisterFunc( "insideFrontCourtyard('x')", insideFrontCourtyard ); + RegisterFunc( "insideBackCourtyard('x')", insideBackCourtyard ); RegisterFunc( "insideArea('x')", insideArea ); RegisterFunc( "isMicroVia()", isMicroVia ); RegisterFunc( "isBlindBuriedVia()", isBlindBuriedVia );