From d3f017d8253e4a0a2b10b0d9444cf7ccb2489f4f Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sat, 16 May 2020 00:25:33 +0100 Subject: [PATCH] DRC rules parser and engine. Fixes https://gitlab.com/kicad/code/kicad/issues/2182 Fixes https://gitlab.com/kicad/code/kicad/issues/2116 Fixes https://gitlab.com/kicad/code/kicad/issues/1958 Fixes https://gitlab.com/kicad/code/kicad/issues/1965 --- .gitignore | 2 + common/CMakeLists.txt | 10 + common/drc_rules.keywords | 25 ++ include/board_design_settings.h | 15 +- include/core/typeinfo.h | 15 ++ pcbnew/CMakeLists.txt | 1 + pcbnew/board_connected_item.cpp | 49 +++- pcbnew/board_connected_item.h | 5 +- pcbnew/board_design_settings.cpp | 57 +++- pcbnew/class_dimension.h | 14 + pcbnew/class_drawsegment.h | 16 ++ pcbnew/class_edge_mod.h | 16 ++ pcbnew/class_pad.cpp | 17 +- pcbnew/class_pad.h | 26 +- pcbnew/class_pcb_text.h | 14 + pcbnew/class_text_mod.h | 14 + pcbnew/class_track.cpp | 2 +- pcbnew/class_track.h | 23 +- pcbnew/class_zone.cpp | 36 ++- pcbnew/class_zone.h | 3 +- pcbnew/dialogs/dialog_drc.cpp | 29 +- pcbnew/dialogs/dialog_drc.h | 4 +- pcbnew/drc/drc.cpp | 122 +++++++-- pcbnew/drc/drc.h | 4 +- pcbnew/drc/drc_clearance_test_functions.cpp | 107 ++++++-- pcbnew/drc/drc_item.cpp | 1 - pcbnew/drc/drc_rule.cpp | 172 ++++++++++++ pcbnew/drc/drc_rule.h | 72 +++++ pcbnew/drc/drc_rule_parser.cpp | 278 ++++++++++++++++++++ pcbnew/drc/drc_rule_parser.h | 62 +++++ pcbnew/zone_filler.cpp | 14 +- 31 files changed, 1084 insertions(+), 141 deletions(-) create mode 100644 common/drc_rules.keywords create mode 100644 pcbnew/drc/drc_rule.cpp create mode 100644 pcbnew/drc/drc_rule.h create mode 100644 pcbnew/drc/drc_rule_parser.cpp create mode 100644 pcbnew/drc/drc_rule_parser.h diff --git a/.gitignore b/.gitignore index 0f69160b27..1deae81416 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ boost_root Build* build* common/fp_lib_table_keywords.cpp +common/drc_rules_keywords.cpp +common/drc_rules_lexer.h common/netlist_keywords.* common/netlist_lexer.h common/pcb_plot_params_lexer.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 683ad1ed3d..85255eca1a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -463,6 +463,7 @@ set( PCB_COMMON_SRCS ${CMAKE_SOURCE_DIR}/pcbnew/connectivity/connectivity_data.cpp ${CMAKE_SOURCE_DIR}/pcbnew/convert_drawsegment_list_to_polygon.cpp ${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_item.cpp + ${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_rule.cpp ${CMAKE_SOURCE_DIR}/pcbnew/eagle_plugin.cpp ${CMAKE_SOURCE_DIR}/pcbnew/footprint_editor_settings.cpp ${CMAKE_SOURCE_DIR}/pcbnew/gpcb_plugin.cpp @@ -525,6 +526,15 @@ make_lexer( PCBPLOTPARAMS_T ) +# auto-generate drc_rules_lexer.h and drc_rules_keywords.cpp +make_lexer( + common + drc_rules.keywords + drc_rules_lexer.h + drc_rules_keywords.cpp + DRCRULE_T + ) + # auto-generate pcbnew_sexpr.h and pcbnew_sexpr.cpp make_lexer( pcbcommon diff --git a/common/drc_rules.keywords b/common/drc_rules.keywords new file mode 100644 index 0000000000..9e197145e2 --- /dev/null +++ b/common/drc_rules.keywords @@ -0,0 +1,25 @@ +allow +annulus_width +blind_via +board_edge +clearance +track_width +graphic +hole +match_area +match_layer +match_netclass +match_type +micro_via +npth +pad +priority +pth +relaxed +rule +selector +text +track +version +via +zone diff --git a/include/board_design_settings.h b/include/board_design_settings.h index ddf19606ff..5e6f7ba555 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -29,6 +29,8 @@ #include #include #include +#include + #define DEFAULT_SILK_LINE_WIDTH 0.12 #define DEFAULT_COPPER_LINE_WIDTH 0.20 @@ -194,6 +196,7 @@ struct TEXT_ITEM_INFO // forward declaration from class_track.h enum class VIATYPE : int; + /** * BOARD_DESIGN_SETTINGS * contains design settings for a BOARD object. @@ -207,7 +210,9 @@ public: std::vector m_DiffPairDimensionsList; // List of netclasses. There is always the default netclass. - NETCLASSES m_NetClasses; + NETCLASSES m_NetClasses; + std::vector m_DRCRuleSelectors; + std::vector m_DRCRules; bool m_MicroViasAllowed; ///< true to allow micro vias bool m_BlindBuriedViaAllowed; ///< true to allow blind/buried vias @@ -389,6 +394,10 @@ public: */ int GetSmallestClearanceValue(); + int GetRuleClearance( const BOARD_ITEM* aItem, const NETCLASS* aItemNetclass, + const BOARD_ITEM* bItem, const NETCLASS* bItemNetclass, + wxString* aSource ); + /** * Function GetCurrentMicroViaSize * @return the current micro via size, @@ -863,10 +872,6 @@ public: bool GetTextUpright( PCB_LAYER_ID aLayer ) const; int GetLayerClass( PCB_LAYER_ID aLayer ) const; - -private: - void formatNetClass( NETCLASS* aNetClass, OUTPUTFORMATTER* aFormatter, int aNestLevel, - int aControlBits ) const; }; #endif // BOARD_DESIGN_SETTINGS_H_ diff --git a/include/core/typeinfo.h b/include/core/typeinfo.h index 754c2c1c27..4d73369814 100644 --- a/include/core/typeinfo.h +++ b/include/core/typeinfo.h @@ -103,6 +103,16 @@ enum KICAD_T PCB_ITEM_LIST_T, ///< class BOARD_ITEM_LIST, a list of board items PCB_NETINFO_T, ///< class NETINFO_ITEM, a description of a net + PCB_LOCATE_STDVIA_T, + PCB_LOCATE_UVIA_T, + PCB_LOCATE_BBVIA_T, + PCB_LOCATE_TEXT_T, + PCB_LOCATE_GRAPHIC_T, + PCB_LOCATE_HOLE_T, + PCB_LOCATE_PTH_T, + PCB_LOCATE_NPTH_T, + PCB_LOCATE_BOARD_EDGE_T, + // Schematic draw Items. The order of these items effects the sort order. // It is currently ordered to mimic the old Eeschema locate behavior where // the smallest item is the selected item. @@ -235,6 +245,11 @@ constexpr KICAD_T BaseType( const KICAD_T aType ) case SCH_COMPONENT_LOCATE_POWER_T: return SCH_COMPONENT_T; + case PCB_LOCATE_HOLE_T: + case PCB_LOCATE_PTH_T: + case PCB_LOCATE_NPTH_T: + return PCB_LOCATE_HOLE_T; + default: return aType; } diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 094c3c9ffe..de428a5ce3 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -233,6 +233,7 @@ set( PCBNEW_DRC_SRCS drc/drc_courtyard_tester.cpp drc/drc.cpp drc/drc_clearance_test_functions.cpp + drc/drc_rule_parser.cpp ) set( PCBNEW_NETLIST_SRCS diff --git a/pcbnew/board_connected_item.cpp b/pcbnew/board_connected_item.cpp index 0f975d9fa4..d85ddf5f93 100644 --- a/pcbnew/board_connected_item.cpp +++ b/pcbnew/board_connected_item.cpp @@ -79,36 +79,63 @@ bool BOARD_CONNECTED_ITEM::SetNetCode( int aNetCode, bool aNoAssert ) } -int BOARD_CONNECTED_ITEM::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const +int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const { - NETCLASSPTR netclass; + NETCLASS* myNetclass = nullptr; + NETCLASS* itemNetclass = nullptr; + BOARD_DESIGN_SETTINGS* bds = nullptr; // NB: we must check the net first, as when it is 0 GetNetClass() will return the // orphaned net netclass, not the default netclass. if( GetBoard() ) { if( m_netinfo->GetNet() == 0 ) - netclass = GetBoard()->GetDesignSettings().GetDefault(); + myNetclass = GetBoard()->GetDesignSettings().GetDefault().get(); else - netclass = GetNetClass(); + myNetclass = GetNetClass().get(); + + bds = &GetBoard()->GetDesignSettings(); } // No clearance if "this" is not (yet) linked to a board therefore no available netclass - int myClearance = netclass ? netclass->GetClearance() : 0; + if( aItem && aItem->GetBoard() && dynamic_cast( aItem ) ) + { + if( dynamic_cast( aItem )->GetNet()->GetNet() == 0 ) + itemNetclass = GetBoard()->GetDesignSettings().GetDefault().get(); + else + itemNetclass = dynamic_cast( aItem )->GetNetClass().get(); - if( aItem && aItem->GetClearance() > myClearance ) - return aItem->GetClearance( nullptr, aSource ); + bds = &aItem->GetBoard()->GetDesignSettings(); + } + + int myClearance = myNetclass ? myNetclass->GetClearance() : 0; + int itemClearance = itemNetclass ? itemNetclass->GetClearance() : 0; + wxString ruleSource; + int ruleClearance = bds ? bds->GetRuleClearance( this, myNetclass, + aItem, itemNetclass, &ruleSource ) : 0; + int clearance = std::max( std::max( myClearance, itemClearance ), ruleClearance ); if( aSource ) { - if( netclass ) - *aSource = wxString::Format( _( "%s netclass clearance" ), - netclass->GetName() ); + if( clearance == myClearance && myNetclass ) + { + *aSource = wxString::Format( _( "'%s' netclass clearance" ), myNetclass->GetName() ); + } + else if( clearance == itemClearance && itemNetclass ) + { + *aSource = wxString::Format( _( "'%s' netclass clearance" ), itemNetclass->GetName() ); + } + else if( clearance == ruleClearance && !ruleSource.IsEmpty() ) + { + *aSource = wxString::Format( _( "'%s' rule clearance" ), ruleSource ); + } else + { *aSource = _( "No netclass" ); + } } - return myClearance; + return clearance; } diff --git a/pcbnew/board_connected_item.h b/pcbnew/board_connected_item.h index 52ce1a1483..9503b375d9 100644 --- a/pcbnew/board_connected_item.h +++ b/pcbnew/board_connected_item.h @@ -157,12 +157,11 @@ public: * returned clearance is the greater of this object's NETCLASS clearance and * aItem's NETCLASS clearance. If \a aItem is NULL, then this objects clearance * is returned. - * @param aItem is an optional BOARD_CONNECTED_ITEM + * @param aItem is an optional BOARD_ITEM * @param aSource [out] optionally reports the source as a user-readable string * @return int - the clearance in internal units. */ - virtual int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr, - wxString* aSource = nullptr ) const; + virtual int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const; /** * Function GetNetClass diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index e431d7da15..4a763dd504 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #define CopperLayerCountKey wxT( "CopperLayerCount" ) #define BoardThicknessKey wxT( "BoardThickness" ) @@ -962,12 +963,11 @@ int BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue() { int clearance = m_NetClasses.GetDefault()->GetClearance(); - //Read list of Net Classes - for( NETCLASSES::const_iterator nc = m_NetClasses.begin(); nc != m_NetClasses.end(); ++nc ) - { - NETCLASSPTR netclass = nc->second; - clearance = std::max( clearance, netclass->GetClearance() ); - } + for( const std::pair& netclass : m_NetClasses.NetClasses() ) + clearance = std::max( clearance, netclass.second->GetClearance() ); + + for( const DRC_RULE* rule : m_DRCRules ) + clearance = std::max( clearance, rule->m_Clearance ); return clearance; } @@ -977,11 +977,48 @@ int BOARD_DESIGN_SETTINGS::GetSmallestClearanceValue() { int clearance = m_NetClasses.GetDefault()->GetClearance(); - //Read list of Net Classes - for( NETCLASSES::const_iterator nc = m_NetClasses.begin(); nc != m_NetClasses.end(); ++nc ) + for( const std::pair& netclass : m_NetClasses.NetClasses() ) + clearance = std::min( clearance, netclass.second->GetClearance() ); + + return clearance; +} + + +int BOARD_DESIGN_SETTINGS::GetRuleClearance( const BOARD_ITEM* aItem, const NETCLASS* aNetclass, + const BOARD_ITEM* bItem, const NETCLASS* bNetclass, + wxString* aSource ) +{ + std::vector matched; + + MatchSelectors( m_DRCRuleSelectors, aItem, aNetclass, bItem, bNetclass, &matched ); + + std::sort( matched.begin(), matched.end(), + []( DRC_SELECTOR* a, DRC_SELECTOR* b ) -> bool + { + return a->m_Priority < b->m_Priority; + }); + + int clearance = 0; + + for( DRC_SELECTOR* selector : matched ) { - NETCLASSPTR netclass = nc->second; - clearance = std::min( clearance, netclass->GetClearance() ); + // ignore hole rules; we're just interested in copper here + for( KICAD_T matchType : selector->m_MatchTypes ) + { + if( BaseType( matchType ) == PCB_LOCATE_HOLE_T ) + continue; + } + + if( selector->m_Rule->m_Clearance > 0 ) + { + clearance = std::max( clearance, selector->m_Rule->m_Clearance ); + *aSource = selector->m_Rule->m_Name; + } + else if( selector->m_Rule->m_Clearance < 0 ) + { + clearance = std::min( clearance, abs( selector->m_Rule->m_Clearance ) ); + *aSource = selector->m_Rule->m_Name; + } } return clearance; diff --git a/pcbnew/class_dimension.h b/pcbnew/class_dimension.h index deac48ae8b..3725022f2c 100644 --- a/pcbnew/class_dimension.h +++ b/pcbnew/class_dimension.h @@ -88,6 +88,20 @@ public: return aItem && PCB_DIMENSION_T == aItem->Type(); } + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( *p == PCB_LOCATE_GRAPHIC_T ) + return true; + } + + return false; + } + void SetValue( int aValue ) { m_Value = aValue; } int GetValue() const { return m_Value; } diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index 5921d59412..791996ab64 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -77,6 +77,22 @@ public: return aItem && PCB_LINE_T == aItem->Type(); } + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( *p == PCB_LOCATE_GRAPHIC_T ) + return true; + else if( *p == PCB_LOCATE_BOARD_EDGE_T ) + return m_Layer == Edge_Cuts; + } + + return false; + } + /** Polygonal shape is not always filled. * For now it is filled on all layers but Edge_Cut layer */ diff --git a/pcbnew/class_edge_mod.h b/pcbnew/class_edge_mod.h index 6d6514372e..e80a530994 100644 --- a/pcbnew/class_edge_mod.h +++ b/pcbnew/class_edge_mod.h @@ -56,6 +56,22 @@ public: return aItem && PCB_MODULE_EDGE_T == aItem->Type(); } + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( *p == PCB_LOCATE_GRAPHIC_T ) + return true; + else if( *p == PCB_LOCATE_BOARD_EDGE_T ) + return m_Layer == Edge_Cuts; + } + + return false; + } + /** * Move an edge of the footprint. * This is a footprint shape modification. diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index fe7185224e..c29a01434a 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -623,14 +623,14 @@ wxPoint D_PAD::ShapePos() const } -int D_PAD::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const +int D_PAD::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const { - int myClearance; + int clearance; // A pad can have specific clearance that overrides its NETCLASS clearance value if( m_LocalClearance ) { - myClearance = m_LocalClearance; + clearance = m_LocalClearance; if( aSource ) *aSource = wxString::Format( _( "pad %s clearance" ), GetName() ); @@ -639,22 +639,19 @@ int D_PAD::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const // A footprint can have a specific clearance value else if( GetParent() && GetParent()->GetLocalClearance() ) { - myClearance = GetParent()->GetLocalClearance(); + clearance = GetParent()->GetLocalClearance(); if( aSource ) *aSource = wxString::Format( _( "%s footprint clearance" ), GetParent()->GetReference() ); } - // Otherwise use the baseclass method to fetch the netclass setting + // Otherwise use the baseclass method to fetch the netclass and/or rule setting else { - myClearance = BOARD_CONNECTED_ITEM::GetClearance( nullptr, aSource ); + clearance = BOARD_CONNECTED_ITEM::GetClearance( aItem, aSource ); } - if( aItem && aItem->GetClearance() > myClearance ) - return aItem->GetClearance( nullptr, aSource ); - - return myClearance; + return clearance; } diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 9965ec7a23..6aeeea2097 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -140,6 +140,27 @@ public: return aItem && PCB_PAD_T == aItem->Type(); } + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_CONNECTED_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( m_Drill.x > 0 && m_Drill.y > 0 ) + { + if( *p == PCB_LOCATE_HOLE_T ) + return true; + else if( *p == PCB_LOCATE_PTH_T && m_Attribute != PAD_ATTRIB_HOLE_NOT_PLATED ) + return true; + else if( *p == PCB_LOCATE_NPTH_T && m_Attribute == PAD_ATTRIB_HOLE_NOT_PLATED ) + return true; + } + } + + return false; + } + MODULE* GetParent() const { return (MODULE*) m_Parent; } /** @@ -459,12 +480,11 @@ public: * returned clearance is the greater of this object's clearance and * aItem's clearance. If \a aItem is NULL, then this objects clearance * is returned. - * @param aItem is an optional BOARD_CONNECTED_ITEM + * @param aItem is an optional BOARD_ITEM * @param aSource [out] optionally reports the source as a user-readable string * @return int - the clearance in internal units. */ - int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr, - wxString* aSource = nullptr ) const override; + int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override; // Mask margins handling: diff --git a/pcbnew/class_pcb_text.h b/pcbnew/class_pcb_text.h index 6146e3d80d..dc9a1667bd 100644 --- a/pcbnew/class_pcb_text.h +++ b/pcbnew/class_pcb_text.h @@ -53,6 +53,20 @@ public: return aItem && PCB_TEXT_T == aItem->Type(); } + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( *p == PCB_LOCATE_TEXT_T ) + return true; + } + + return false; + } + wxString GetShownText( int aDepth = 0 ) const override; bool Matches( wxFindReplaceData& aSearchData, void* aAuxData ) override diff --git a/pcbnew/class_text_mod.h b/pcbnew/class_text_mod.h index af30c68ad5..22e7ba540d 100644 --- a/pcbnew/class_text_mod.h +++ b/pcbnew/class_text_mod.h @@ -74,6 +74,20 @@ public: return aItem && PCB_MODULE_TEXT_T == aItem->Type(); } + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( *p == PCB_LOCATE_TEXT_T ) + return true; + } + + return false; + } + bool Matches( wxFindReplaceData& aSearchData, void* aAuxData ) override { return BOARD_ITEM::Matches( GetShownText(), aSearchData ); diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index fe1a7f103b..7a54814ac6 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -117,7 +117,7 @@ BITMAP_DEF VIA::GetMenuImage() const } -int TRACK::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const +int TRACK::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const { // Currently tracks have no specific clearance parameter on a per track or per // segment basis. The NETCLASS clearance is used. diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index 0763963940..76d2e1e796 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -200,12 +200,11 @@ public: * returned clearance is the greater of this object's clearance and * aItem's clearance. If \a aItem is NULL, then this objects clearance * is returned. - * @param aItem is an optional BOARD_CONNECTED_ITEM + * @param aItem is an optional BOARD_ITEM * @param aSource [out] optionally reports the source as a user-readable string * @return int - the clearance in internal units. */ - int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr, - wxString* aSource = nullptr ) const override; + int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override; wxString GetSelectMenuText( EDA_UNITS aUnits ) const override; @@ -350,6 +349,24 @@ public: // Do not create a copy constructor. The one generated by the compiler is adequate. + bool IsType( const KICAD_T aScanTypes[] ) const override + { + if( BOARD_CONNECTED_ITEM::IsType( aScanTypes ) ) + return true; + + for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) + { + if( *p == PCB_LOCATE_STDVIA_T && m_ViaType == VIATYPE::THROUGH ) + return true; + else if( *p == PCB_LOCATE_UVIA_T && m_ViaType == VIATYPE::MICROVIA ) + return true; + else if( *p == PCB_LOCATE_BBVIA_T && m_ViaType == VIATYPE::BLIND_BURIED ) + return true; + } + + return false; + } + bool IsOnLayer( PCB_LAYER_ID aLayer ) const override; virtual LSET GetLayerSet() const override; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 3776c78fe3..c44695d9e3 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -458,33 +458,31 @@ bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccur } -int ZONE_CONTAINER::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const +int ZONE_CONTAINER::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const { + if( m_isKeepout ) + return 0; + // The actual zone clearance is the biggest of the zone netclass clearance // and the zone clearance setting in the zone properties dialog. - int myClearance = m_ZoneClearance; + int zoneClearance = m_ZoneClearance; + wxString source; + int clearance = BOARD_CONNECTED_ITEM::GetClearance( aItem, &source ); - if( aSource ) - *aSource = _( "zone clearance" ); - - if( !m_isKeepout ) // Net class has no meaning for a keepout area. + if( clearance > zoneClearance ) { - NETCLASSPTR myClass = GetNetClass(); + if( aSource ) + *aSource = source; - if( myClass->GetClearance() > myClearance ) - { - myClearance = myClass->GetClearance(); - - if( aSource ) - *aSource = wxString::Format( _( "'%s' netclass clearance" ), myClass->GetName() ); - } + return clearance; } + else + { + if( aSource ) + *aSource = _( "zone clearance" ); - // Get the final clearance between me and aItem - if( aItem && aItem->GetClearance() > myClearance ) - return aItem->GetClearance( nullptr, aSource ); - - return myClearance; + return zoneClearance; + } } diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index 7e0c086128..f3c216b3c1 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -110,8 +110,7 @@ public: */ const EDA_RECT GetBoundingBox() const override; - int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr, - wxString* aSource = nullptr ) const override; + int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override; /** * Function IsOnCopperLayer diff --git a/pcbnew/dialogs/dialog_drc.cpp b/pcbnew/dialogs/dialog_drc.cpp index 48116b58ca..19a32223c7 100644 --- a/pcbnew/dialogs/dialog_drc.cpp +++ b/pcbnew/dialogs/dialog_drc.cpp @@ -60,7 +60,6 @@ DIALOG_DRC::DIALOG_DRC( DRC* aTester, PCB_EDIT_FRAME* aEditorFrame, wxWindow* aP m_tester = aTester; m_brdEditor = aEditorFrame; m_currentBoard = m_brdEditor->GetBoard(); - m_BrdSettings = m_brdEditor->GetBoard()->GetDesignSettings(); m_markerTreeModel = new RC_TREE_MODEL( m_brdEditor, m_markerDataView ); m_markerDataView->AssociateModel( m_markerTreeModel ); @@ -117,7 +116,6 @@ void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent ) // updating data which can be modified outside the dialog (DRC parameters, units ...) // because the dialog is not modal - m_BrdSettings = m_brdEditor->GetBoard()->GetDesignSettings(); displayDRCValues(); m_markerTreeModel->SetProvider( m_markersProvider ); @@ -129,9 +127,9 @@ void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent ) void DIALOG_DRC::displayDRCValues() { - m_trackMinWidth.SetValue( m_BrdSettings.m_TrackMinWidth ); - m_viaMinSize.SetValue( m_BrdSettings.m_ViasMinSize ); - m_uviaMinSize.SetValue( m_BrdSettings.m_MicroViasMinSize ); + m_trackMinWidth.SetValue( bds().m_TrackMinWidth ); + m_viaMinSize.SetValue( bds().m_ViasMinSize ); + m_uviaMinSize.SetValue( bds().m_MicroViasMinSize ); } @@ -162,11 +160,9 @@ void DIALOG_DRC::initValues() void DIALOG_DRC::setDRCParameters() { - m_BrdSettings.m_TrackMinWidth = (int) m_trackMinWidth.GetValue(); - m_BrdSettings.m_ViasMinSize = (int) m_viaMinSize.GetValue(); - m_BrdSettings.m_MicroViasMinSize = (int) m_uviaMinSize.GetValue(); - - m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings ); + bds().m_TrackMinWidth = (int) m_trackMinWidth.GetValue(); + bds().m_ViasMinSize = (int) m_viaMinSize.GetValue(); + bds().m_MicroViasMinSize = (int) m_uviaMinSize.GetValue(); } @@ -287,7 +283,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent ) wxMenu menu; wxString msg; - switch( m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] ) + switch( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] ) { case RPT_SEVERITY_ERROR: listName = _( "errors" ); break; case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break; @@ -307,7 +303,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent ) menu.AppendSeparator(); - if( m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING ) + if( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING ) { msg.Printf( _( "Change severity to Error for all '%s' violations" ), rcItem->GetErrorText( rcItem->GetErrorCode() ), @@ -356,8 +352,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent ) break; case 3: - m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR; - m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings ); + bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR; // Rebuild model and view static_cast( aEvent.GetModel() )->SetProvider( m_markersProvider ); @@ -365,8 +360,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent ) break; case 4: - m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING; - m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings ); + bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING; // Rebuild model and view static_cast( aEvent.GetModel() )->SetProvider( m_markersProvider ); @@ -375,8 +369,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent ) case 5: { - m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE; - m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings ); + bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE; std::vector& markers = m_brdEditor->GetBoard()->Markers(); diff --git a/pcbnew/dialogs/dialog_drc.h b/pcbnew/dialogs/dialog_drc.h index d335915f4a..b2c8312074 100644 --- a/pcbnew/dialogs/dialog_drc.h +++ b/pcbnew/dialogs/dialog_drc.h @@ -47,8 +47,6 @@ class DIALOG_DRC: public DIALOG_DRC_BASE { public: - BOARD_DESIGN_SETTINGS m_BrdSettings; - /// Constructors DIALOG_DRC( DRC* aTester, PCB_EDIT_FRAME* aEditorFrame, wxWindow* aParent ); ~DIALOG_DRC(); @@ -94,6 +92,8 @@ private: void deleteAllMarkers(); void refreshBoardEditor(); + BOARD_DESIGN_SETTINGS& bds() { return m_currentBoard->GetDesignSettings(); } + BOARD* m_currentBoard; // the board currently on test DRC* m_tester; PCB_EDIT_FRAME* m_brdEditor; diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index edc1c1ea9e..cc0a9f7aa4 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -54,6 +54,8 @@ #include #include #include +#include +#include "drc_rule_parser.h" DRC::DRC() : PCB_TOOL_BASE( "pcbnew.DRCTool" ), @@ -95,6 +97,8 @@ void DRC::Reset( RESET_REASON aReason ) DestroyDRCDialog( wxID_OK ); m_pcb = m_pcbEditorFrame->GetBoard(); + + readRules(); } } @@ -350,8 +354,36 @@ int DRC::TestZoneToZoneOutlines() } +void DRC::readRules() +{ + wxString rulesFilepath = m_pcbEditorFrame->Prj().AbsolutePath( "drc-rules" ); + + BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings(); + bds.m_DRCRuleSelectors.clear(); + bds.m_DRCRules.clear(); + + FILE* fp = wxFopen( rulesFilepath, wxT( "rt" ) ); + + if( fp ) + { + try + { + DRC_RULES_PARSER parser( m_pcb, fp, rulesFilepath ); + parser.Parse( bds.m_DRCRuleSelectors, bds.m_DRCRules ); + } + catch( PARSE_ERROR& pe ) + { + DisplayError( m_drcDialog, pe.What() ); + } + } +} + + void DRC::RunTests( wxTextCtrl* aMessages ) { + // TODO: timestamp file and read only if newer + readRules(); + // be sure m_pcb is the current board, not a old one // ( the board can be reloaded ) m_pcb = m_pcbEditorFrame->GetBoard(); @@ -727,7 +759,7 @@ void DRC::testPad2Pad() void DRC::testDrilledHoles() { - BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings(); + BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings(); // Test drilled holes to minimize drill bit breakage. // @@ -738,8 +770,8 @@ void DRC::testDrilledHoles() struct DRILLED_HOLE { wxPoint m_location; - int m_drillRadius; - BOARD_ITEM* m_owner; + int m_drillRadius = 0; + BOARD_ITEM* m_owner = nullptr; }; std::vector holes; @@ -750,19 +782,37 @@ void DRC::testDrilledHoles() { for( D_PAD* pad : mod->Pads( ) ) { - int minDimension = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); + int holeSize = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); - if( minDimension == 0 ) + if( holeSize == 0 ) continue; - if( !dsnSettings.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) - && minDimension < dsnSettings.m_MinThroughDrill ) + NETCLASS* netclass = pad->GetNet()->GetNet() == 0 ? bds.GetDefault().get() + : pad->GetNetClass().get(); + int minHole = bds.m_MinThroughDrill; + wxString minHoleSource = _( "board" ); + + std::vector matched; + + MatchSelectors( bds.m_DRCRuleSelectors, pad, netclass, nullptr, nullptr, &matched ); + + for( DRC_SELECTOR* selector : matched ) + { + if( selector->m_Rule->m_Hole > minHole ) + { + minHole = selector->m_Rule->m_Hole; + minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); + } + } + + if( !bds.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) && holeSize < minHole ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_PAD_DRILL ); - msg.Printf( drcItem->GetErrorText() + _( " (board min through hole %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_MinThroughDrill, true ), - MessageTextFromValue( userUnits(), minDimension, true ) ); + msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ), + minHoleSource, + MessageTextFromValue( userUnits(), minHole, true ), + MessageTextFromValue( userUnits(), holeSize, true ) ); drcItem->SetErrorMessage( msg ); drcItem->SetItems( pad ); @@ -788,15 +838,39 @@ void DRC::testDrilledHoles() if( !via ) continue; + NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault().get() + : via->GetNetClass().get(); + int minHole = 0; + wxString minHoleSource; + + std::vector matched; + + MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched ); + + for( DRC_SELECTOR* selector : matched ) + { + if( selector->m_Rule->m_Hole > minHole ) + { + minHole = selector->m_Rule->m_Hole; + minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); + } + } + if( via->GetViaType() == VIATYPE::MICROVIA ) { - if( !dsnSettings.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) - && via->GetDrillValue() < dsnSettings.m_MicroViasMinDrill ) + if( bds.m_MicroViasMinDrill > minHole ) + { + minHole = bds.m_MicroViasMinDrill; + minHoleSource = _( "board" ); + } + + if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) && via->GetDrillValue() < minHole ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA_DRILL ); - msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_MicroViasMinDrill, true ), + msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ), + minHoleSource, + MessageTextFromValue( userUnits(), minHole, true ), MessageTextFromValue( userUnits(), via->GetDrillValue(), true ) ); drcItem->SetErrorMessage( msg ); @@ -808,13 +882,19 @@ void DRC::testDrilledHoles() } else { - if( !dsnSettings.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) - && via->GetDrillValue() < dsnSettings.m_MinThroughDrill ) + if( bds.m_MinThroughDrill > minHole ) + { + minHole = bds.m_MinThroughDrill; + minHoleSource = _( "board" ); + } + + if( !bds.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) && via->GetDrillValue() < minHole ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_DRILL ); - msg.Printf( drcItem->GetErrorText() + _( " (board min through hole %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_MinThroughDrill, true ), + msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ), + minHoleSource, + MessageTextFromValue( userUnits(), minHole, true ), MessageTextFromValue( userUnits(), via->GetDrillValue(), true ) ); drcItem->SetErrorMessage( msg ); @@ -831,7 +911,7 @@ void DRC::testDrilledHoles() } } - if( dsnSettings.m_HoleToHoleMin == 0 || dsnSettings.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) ) + if( bds.m_HoleToHoleMin == 0 || bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) ) return; for( size_t ii = 0; ii < holes.size(); ++ii ) @@ -849,12 +929,12 @@ void DRC::testDrilledHoles() int actual = KiROUND( GetLineLength( checkHole.m_location, refHole.m_location ) ); actual = std::max( 0, actual - checkHole.m_drillRadius - refHole.m_drillRadius ); - if( actual < dsnSettings.m_HoleToHoleMin ) + if( actual < bds.m_HoleToHoleMin ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_DRILLED_HOLES_TOO_CLOSE ); msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_HoleToHoleMin, true ), + MessageTextFromValue( userUnits(), bds.m_HoleToHoleMin, true ), MessageTextFromValue( userUnits(), actual, true ) ); drcItem->SetErrorMessage( msg ); diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index 6432f530b0..235e8dc0a6 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -162,7 +162,7 @@ private: PCB_EDIT_FRAME* m_pcbEditorFrame; // The pcb frame editor which owns the board BOARD* m_pcb; SHAPE_POLY_SET m_board_outlines; // The board outline including cutouts - DIALOG_DRC* m_drcDialog; + DIALOG_DRC* m_drcDialog; std::vector m_unconnected; // list of unconnected pads std::vector m_footprints; // list of footprint warnings @@ -177,6 +177,8 @@ private: */ void updatePointers(); + void readRules(); + EDA_UNITS userUnits() const { return m_pcbEditorFrame->GetUserUnits(); } /** diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index fff413b768..e9e1929b76 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -139,13 +139,18 @@ bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt, bool aTestZones ) { - BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings(); - wxString msg; + BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings(); + std::vector matched; - SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); - LSET layerMask = aRefSeg->GetLayerSet(); - EDA_RECT refSegBB = aRefSeg->GetBoundingBox(); - int refSegWidth = aRefSeg->GetWidth(); + wxString msg; + SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); + LSET layerMask = aRefSeg->GetLayerSet(); + NETCLASS* netclass = aRefSeg->GetNet()->GetNet() == 0 ? bds.GetDefault().get() + : aRefSeg->GetNetClass().get(); + EDA_RECT refSegBB = aRefSeg->GetBoundingBox(); + int refSegWidth = aRefSeg->GetWidth(); + + MatchSelectors( bds.m_DRCRuleSelectors, aRefSeg, netclass, nullptr, nullptr, &matched ); /******************************************/ @@ -154,17 +159,45 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( aRefSeg->Type() == PCB_VIA_T ) { - VIA *refvia = static_cast( aRefSeg ); + VIA *refvia = static_cast( aRefSeg ); + int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2; + int minAnnulus; + wxString minAnnulusSource; + + for( DRC_SELECTOR* selector : matched ) + { + if( selector->m_Rule->m_AnnulusWidth > minAnnulus ) + { + minAnnulus = selector->m_Rule->m_AnnulusWidth; + minAnnulusSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); + } + } // test if the via size is smaller than minimum if( refvia->GetViaType() == VIATYPE::MICROVIA ) { - if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize ) + if( viaAnnulus < minAnnulus ) + { + DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_ANNULUS ); + + msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ), + minAnnulusSource, + MessageTextFromValue( userUnits(), minAnnulus, true ), + MessageTextFromValue( userUnits(), viaAnnulus, true ) ); + + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( refvia ); + + MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() ); + addMarkerToPcb( marker ); + } + + if( refvia->GetWidth() < bds.m_MicroViasMinSize ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA ); msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_MicroViasMinSize, true ), + MessageTextFromValue( userUnits(), bds.m_MicroViasMinSize, true ), MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) ); drcItem->SetErrorMessage( msg ); @@ -176,14 +209,19 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } else { - int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2; + if( bds.m_ViasMinAnnulus > minAnnulus ) + { + minAnnulus = bds.m_ViasMinAnnulus; + minAnnulusSource = _( "board" ); + } - if( viaAnnulus < dsnSettings.m_ViasMinAnnulus ) + if( viaAnnulus < minAnnulus ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_ANNULUS ); - msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_ViasMinSize, true ), + msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ), + minAnnulusSource, + MessageTextFromValue( userUnits(), minAnnulus, true ), MessageTextFromValue( userUnits(), viaAnnulus, true ) ); drcItem->SetErrorMessage( msg ); @@ -193,12 +231,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato addMarkerToPcb( marker ); } - if( refvia->GetWidth() < dsnSettings.m_ViasMinSize ) + if( refvia->GetWidth() < bds.m_ViasMinSize ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA ); msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_ViasMinSize, true ), + MessageTextFromValue( userUnits(), bds.m_ViasMinSize, true ), MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) ); drcItem->SetErrorMessage( msg ); @@ -228,7 +266,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } // test if the type of via is allowed due to design rules - if( refvia->GetViaType() == VIATYPE::MICROVIA && !dsnSettings.m_MicroViasAllowed ) + if( refvia->GetViaType() == VIATYPE::MICROVIA && !bds.m_MicroViasAllowed ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MICROVIA_NOT_ALLOWED ); @@ -241,7 +279,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } // test if the type of via is allowed due to design rules - if( refvia->GetViaType() == VIATYPE::BLIND_BURIED && !dsnSettings.m_BlindBuriedViaAllowed ) + if( refvia->GetViaType() == VIATYPE::BLIND_BURIED && !bds.m_BlindBuriedViaAllowed ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_BURIED_VIA_NOT_ALLOWED ); @@ -266,7 +304,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( layer1 > layer2 ) std::swap( layer1, layer2 ); - if( layer2 == B_Cu && layer1 == dsnSettings.GetCopperLayerCount() - 2 ) + if( layer2 == B_Cu && layer1 == bds.GetCopperLayerCount() - 2 ) err = false; else if( layer1 == F_Cu && layer2 == In1_Cu ) err = false; @@ -290,14 +328,27 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } else // This is a track segment { - if( refSegWidth < dsnSettings.m_TrackMinWidth ) + int minWidth = bds.m_TrackMinWidth; + wxString minWidthSource = _( "board" ); + + for( DRC_SELECTOR* selector : matched ) + { + if( selector->m_Rule->m_TrackWidth > minWidth ) + { + minWidth = selector->m_Rule->m_AnnulusWidth; + minWidthSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); + } + } + + if( refSegWidth < minWidth ) { wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2; DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_TRACK_WIDTH ); - msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), dsnSettings.m_TrackMinWidth, true ), + msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ), + minWidthSource, + MessageTextFromValue( userUnits(), bds.m_TrackMinWidth, true ), MessageTextFromValue( userUnits(), refSegWidth, true ) ); drcItem->SetErrorMessage( msg ); @@ -538,15 +589,17 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato /* Phase 4: test DRC with to board edge */ /***********************************************/ { - SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); + static DRAWSEGMENT dummyEdge; + dummyEdge.SetLayer( Edge_Cuts ); + SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); wxString clearanceSource; - int minClearance = aRefSeg->GetClearance( nullptr, &clearanceSource ); + int minClearance = aRefSeg->GetClearance( &dummyEdge, &clearanceSource ); - if( dsnSettings.m_CopperEdgeClearance > minClearance ) + if( bds.m_CopperEdgeClearance > minClearance ) { - minClearance = dsnSettings.m_CopperEdgeClearance; - clearanceSource = _( "board edge clearance" ); + minClearance = bds.m_CopperEdgeClearance; + clearanceSource = _( "board edge" ); } int halfWidth = refSegWidth / 2; @@ -585,7 +638,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato int actual = std::max( 0.0, sqrt( center2center_squared ) - halfWidth ); DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_EDGE ); - msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ), + msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), clearanceSource, MessageTextFromValue( userUnits(), minClearance, true ), MessageTextFromValue( userUnits(), actual, true ) ); diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index b7f15c010f..f792bbeae6 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -94,7 +94,6 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const case DRCE_TRACK_NEAR_EDGE: msg = _HKI( "Track too close to board edge" ); break; case DRCE_INVALID_OUTLINE: msg = _HKI( "Board has malformed outline" ); break; - // use < since this is text ultimately embedded in HTML case DRCE_NETCLASS_TRACKWIDTH: msg = _HKI( "NetClass Track Width too small" ); break; case DRCE_NETCLASS_CLEARANCE: msg = _HKI( "NetClass Clearance too small" ); break; case DRCE_NETCLASS_VIAANNULUS: msg = _HKI( "NetClass via annulus too small" ); break; diff --git a/pcbnew/drc/drc_rule.cpp b/pcbnew/drc/drc_rule.cpp new file mode 100644 index 0000000000..eb97b33802 --- /dev/null +++ b/pcbnew/drc/drc_rule.cpp @@ -0,0 +1,172 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include +#include +#include +#include +#include + +/* + * Match tokens: + * match_netclass + * match_type + * match_layer + * match_all + * match_area + * + * (selector (match_area "$board") (rule "OSHParkClass3") (priority 100)) + * + * (selector (match_netclass "HV") (rule "HV_internal")) + * (selector (match_netclass "HV") (match_layer "F_Cu") (rule "HV_external")) + * (selector (match_netclass "HV") (match_layer "B_Cu") (rule "HV_external")) + * + * (selector (match_netclass "HV") (match_netclass "HV") (rule "HV2HV")) + * (selector (match_netclass "HV") (match_netclass "HV") (match_layer "F_Cu") (rule "HV2HV_external")) + * (selector (match_netclass "HV") (match_netclass "HV") (match_layer "B_Cu") (rule "HV2HV_external")) + * + * TODO: pads for connector pins or wire pads have even larger clearances. How to encode? + * User attributes on parent footprint? + * + * (selector (match_netclass "HV") (match_type "pad") (match_netclass "HV") (match_type "pad") (rule "pad2PadHV")) + * + * (selector (match_netclass "signal") (match_area "BGA") (rule "neckdown")) + * + * Type tokens: + * track + * via + * micro_via + * blind_via + * pad + * zone + * text + * graphic + * board_edge + * hole + * npth + * pth + * + * Rule tokens: + * allow + * clearance + * annulus_width + * track_width + * hole + * + * Rule modifiers: + * relaxed + * + * (rule "HV" (clearance 200) (priority 200)) + * (rule "HV_external" (clearance 400) (priority 200)) + * (rule "HV2HV" (clearance 200) (priority 200)) + * (rule "HV2HV_external" (clearance 500) (priority 200)) + * (rule "pad2padHV" (clearance 500) (priority 200)) + * + * (rule "signal" (clearance 20)) // implied priority of 1 + * (rule "neckdown" (clearance relaxed 15) (priority 2)) + * + * (rule "allowMicrovias" (allow microvia)) + */ + + +void MatchSelectors( const std::vector& aSelectors, + const BOARD_ITEM* aItem, const NETCLASS* aNetclass, + const BOARD_ITEM* bItem, const NETCLASS* bNetclass, + std::vector* aSelected ) +{ + for( DRC_SELECTOR* candidate : aSelectors ) + { + if( candidate->m_MatchNetclasses.size() == 2 ) + { + if( !bItem ) + continue; + + NETCLASS* firstNetclass = candidate->m_MatchNetclasses[0]; + NETCLASS* secondNetclass = candidate->m_MatchNetclasses[1]; + + if( !( aNetclass == firstNetclass && bNetclass == secondNetclass ) + && !( aNetclass == secondNetclass && bNetclass == firstNetclass ) ) + { + continue; + } + } + else if( candidate->m_MatchNetclasses.size() == 1 ) + { + NETCLASS* matchNetclass = candidate->m_MatchNetclasses[0]; + + if( matchNetclass != aNetclass && !( bItem && matchNetclass == bNetclass ) ) + continue; + } + + if( candidate->m_MatchTypes.size() == 2 ) + { + if( !bItem ) + continue; + + KICAD_T firstType[2] = { candidate->m_MatchTypes[0], EOT }; + KICAD_T secondType[2] = { candidate->m_MatchTypes[1], EOT }; + + if( !( aItem->IsType( firstType ) && bItem->IsType( secondType ) ) + && !( aItem->IsType( secondType ) && bItem->IsType( firstType ) ) ) + { + continue; + } + } + else if( candidate->m_MatchTypes.size() == 1 ) + { + KICAD_T matchType[2] = { candidate->m_MatchTypes[0], EOT }; + + if( !aItem->IsType( matchType ) && !( bItem && bItem->IsType( matchType ) ) ) + continue; + } + + if( candidate->m_MatchLayers.size() ) + { + PCB_LAYER_ID matchLayer = candidate->m_MatchLayers[0]; + + if( !aItem->GetLayerSet().test( matchLayer ) + && !( bItem && bItem->GetLayerSet().test( matchLayer ) ) ) + { + continue; + } + } + + if( candidate->m_MatchAreas.size() ) + { + if( candidate->m_MatchAreas[0] == "$board" ) + { + // matches everything + } + else + { + // TODO: area/room matches... + } + } + + // All tests done; if we're still here then it matches + aSelected->push_back( candidate ); + } +} + + diff --git a/pcbnew/drc/drc_rule.h b/pcbnew/drc/drc_rule.h new file mode 100644 index 0000000000..e715fdeb1b --- /dev/null +++ b/pcbnew/drc/drc_rule.h @@ -0,0 +1,72 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef DRC_RULE_H +#define DRC_RULE_H + +#include +#include +#include + + +class BOARD_ITEM; + + +class DRC_RULE +{ +public: + wxString m_Name; + + // A 0 value means the property is not modified by this rule. + // A positive value is a minimum. A negative value is a relaxed constraint: the minimum + // is reduced to the absolute value of the constraint. + int m_Clearance; + int m_AnnulusWidth; + int m_TrackWidth; + int m_Hole; +}; + + +class DRC_SELECTOR +{ +public: + std::vector m_MatchNetclasses; + std::vector m_MatchTypes; + std::vector m_MatchLayers; + std::vector m_MatchAreas; + + int m_Priority; // 0 indicates automatic priority generation + DRC_RULE* m_Rule; + +public: +}; + + +void MatchSelectors( const std::vector& aSelectors, + const BOARD_ITEM* aItem, const NETCLASS* aNetclass, + const BOARD_ITEM* bItem, const NETCLASS* bNetclass, + std::vector* aSelected ); + + + +#endif // DRC_RULE_H diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp new file mode 100644 index 0000000000..0a7a8d5ca5 --- /dev/null +++ b/pcbnew/drc/drc_rule_parser.cpp @@ -0,0 +1,278 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include +#include +#include +#include +#include + +using namespace DRCRULE_T; + + +DRC_RULES_PARSER::DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename ) : + DRC_RULES_LEXER( aFile, aFilename ), + m_board( aBoard ), + m_requiredVersion( 0 ), + m_tooRecent( false ) +{ + for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer ) + { + std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) ); + m_layerMap[ untranslated ] = PCB_LAYER_ID( layer ); + } +} + + +void DRC_RULES_PARSER::Parse( std::vector& aSelectors, + std::vector& aRules ) +{ + std::vector< std::pair > selectorRules; + + T token; + + NeedLEFT(); + + if( NextTok() != T_version ) + Expecting( "version" ); + + NeedNUMBER( "version" ); + m_requiredVersion = (int)strtol( CurText(), NULL, 10 ); + m_tooRecent = ( m_requiredVersion > DRC_RULE_FILE_VERSION ); + NeedRIGHT(); + + for( token = NextTok(); token != T_EOF; token = NextTok() ) + { + if( token != T_LEFT ) + Expecting( T_LEFT ); + + token = NextTok(); + + switch( token ) + { + case T_selector: + { + wxString ruleName; + + aSelectors.push_back( parseDRC_SELECTOR( &ruleName ) ); + selectorRules.emplace_back( aSelectors.back(), ruleName ); + } + break; + + case T_rule: + aRules.push_back( parseDRC_RULE() ); + break; + + default: + Expecting( "selector or rule" ); + } + } + + // Hook up the selectors to their rules + std::map ruleMap; + + for( DRC_RULE* rule : aRules ) + ruleMap[ rule->m_Name ] = rule; + + for( const std::pair& entry : selectorRules ) + entry.first->m_Rule = ruleMap[ entry.second ]; +} + + +DRC_SELECTOR* DRC_RULES_PARSER::parseDRC_SELECTOR( wxString* aRuleName ) +{ + NETCLASSES& netclasses = m_board->GetDesignSettings().m_NetClasses; + DRC_SELECTOR* selector = new DRC_SELECTOR(); + T token; + + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) + { + if( token != T_LEFT ) + Expecting( T_LEFT ); + + token = NextTok(); + + switch( token ) + { + case T_match_netclass: + NeedSYMBOL(); + selector->m_MatchNetclasses.push_back( netclasses.Find( FromUTF8() ).get() ); + NeedRIGHT(); + break; + + case T_match_type: + switch( NextTok() ) + { + case T_track: selector->m_MatchTypes.push_back( PCB_TRACE_T ); break; + case T_via: selector->m_MatchTypes.push_back( PCB_LOCATE_STDVIA_T ); break; + case T_micro_via: selector->m_MatchTypes.push_back( PCB_LOCATE_UVIA_T ); break; + case T_blind_via: selector->m_MatchTypes.push_back( PCB_LOCATE_BBVIA_T ); break; + case T_pad: selector->m_MatchTypes.push_back( PCB_PAD_T ); break; + case T_zone: selector->m_MatchTypes.push_back( PCB_ZONE_AREA_T ); break; + case T_text: selector->m_MatchTypes.push_back( PCB_LOCATE_TEXT_T ); break; + case T_graphic: selector->m_MatchTypes.push_back( PCB_LOCATE_GRAPHIC_T ); break; + case T_hole: selector->m_MatchTypes.push_back( PCB_LOCATE_HOLE_T ); break; + case T_npth: selector->m_MatchTypes.push_back( PCB_LOCATE_NPTH_T ); break; + case T_pth: selector->m_MatchTypes.push_back( PCB_LOCATE_PTH_T ); break; + case T_board_edge: selector->m_MatchTypes.push_back( PCB_LOCATE_BOARD_EDGE_T ); break; + default: Expecting( "track, via, micro_via, blind_via, pad, zone, text, " + "graphic, hole, npth, pth, or board_edge" ); + } + NeedRIGHT(); + break; + + case T_match_layer: + NeedSYMBOL(); + selector->m_MatchLayers.push_back( m_layerMap[ curText ] ); + NeedRIGHT(); + break; + + case T_match_area: + // TODO + break; + + case T_rule: + NeedSYMBOL(); + *aRuleName = FromUTF8(); + NeedRIGHT(); + break; + + case T_priority: + NeedNUMBER( "priority" ); + selector->m_Priority = (int)strtol( CurText(), NULL, 10 ); + NeedRIGHT(); + break; + + default: + Expecting( "match_netclass, match_type, match_layer, match_area, rule, or priority" ); + } + } + + return selector; +} + + +DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE() +{ + DRC_RULE* rule = new DRC_RULE(); + T token; + + NeedSYMBOL(); + rule->m_Name = FromUTF8(); + + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) + { + if( token != T_LEFT ) + Expecting( T_LEFT ); + + int sign = 1; + token = NextTok(); + + switch( token ) + { + case T_allow: + // TODO + break; + + case T_clearance: + if( NextTok() == T_relaxed ) + { + sign = -1; + NextTok(); + } + + rule->m_Clearance = parseValue( T_clearance ) * sign; + NeedRIGHT(); + break; + + case T_track_width: + if( NextTok() == T_relaxed ) + { + sign = -1; + NextTok(); + } + + rule->m_TrackWidth = parseValue( T_track_width ) * sign; + NeedRIGHT(); + break; + + case T_annulus_width: + if( NextTok() == T_relaxed ) + { + sign = -1; + NextTok(); + } + + rule->m_AnnulusWidth = parseValue( T_annulus_width ) * sign; + NeedRIGHT(); + break; + + case T_hole: + if( NextTok() == T_relaxed ) + { + sign = -1; + NextTok(); + } + + rule->m_Hole = parseValue( T_hole ) * sign; + NeedRIGHT(); + break; + + default: + Expecting( "allow, clearance, track_width, annulus_width, or hole" ); + } + } + + return rule; +} + + +int DRC_RULES_PARSER::parseValue( DRCRULE_T::T aToken ) +{ + char* tmp; + + errno = 0; + + double fval = strtod( CurText(), &tmp ); + + if( errno ) + { + wxString error; + error.Printf( _( "Invalid floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ), + GetChars( CurSource() ), CurLineNumber(), CurOffset() ); + + THROW_IO_ERROR( error ); + } + + if( CurText() == tmp ) + { + wxString error; + error.Printf( _( "Missing floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ), + GetChars( CurSource() ), CurLineNumber(), CurOffset() ); + + THROW_IO_ERROR( error ); + } + + return KiROUND( fval * IU_PER_MM ); +} diff --git a/pcbnew/drc/drc_rule_parser.h b/pcbnew/drc/drc_rule_parser.h new file mode 100644 index 0000000000..dc305d1c81 --- /dev/null +++ b/pcbnew/drc/drc_rule_parser.h @@ -0,0 +1,62 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef DRC_RULE_PARSER_H +#define DRC_RULE_PARSER_H + +#include +#include +#include +#include +#include + + +class BOARD_ITEM; + + +#define DRC_RULE_FILE_VERSION 20200515 + + +class DRC_RULES_PARSER : public DRC_RULES_LEXER +{ +public: + DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename ); + + void Parse( std::vector& aSelectors, std::vector& aRules ); + +private: + DRC_SELECTOR* parseDRC_SELECTOR( wxString* aRuleName ); + + DRC_RULE* parseDRC_RULE(); + + int parseValue( DRCRULE_T::T aToken ); + +private: + BOARD* m_board; + int m_requiredVersion; + bool m_tooRecent; + + std::unordered_map m_layerMap; +}; + +#endif // DRC_RULE_PARSER_H diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index bf123eff2f..936467af0b 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -516,20 +516,26 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POL */ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aHoles ) { + static DRAWSEGMENT dummyEdge; + dummyEdge.SetLayer( Edge_Cuts ); + // a small extra clearance to be sure actual track clearance is not smaller // than requested clearance due to many approximations in calculations, // like arc to segment approx, rounding issues... // 2 microns are a good value int extra_margin = Millimeter2iu( 0.002 ); + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); int zone_clearance = aZone->GetClearance(); - int edgeClearance = m_board->GetDesignSettings().m_CopperEdgeClearance; - int zone_to_edgecut_clearance = std::max( aZone->GetZoneClearance(), edgeClearance ); + int edge_clearance = aZone->GetClearance( &dummyEdge ); + + if( bds.m_CopperEdgeClearance > edge_clearance ) + edge_clearance = bds.m_CopperEdgeClearance; // items outside the zone bounding box are skipped // the bounding box is the zone bounding box + the biggest clearance found in Netclass list EDA_RECT zone_boundingbox = aZone->GetBoundingBox(); - int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); + int biggest_clearance = bds.GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ) + extra_margin; zone_boundingbox.Inflate( biggest_clearance ); @@ -615,7 +621,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_ if( aItem->IsOnLayer( Edge_Cuts ) ) { - gap = zone_to_edgecut_clearance; + gap = edge_clearance; // edge cuts by definition don't have a width ignoreLineWidth = true;