From a48867ea01c80dffce0169237ccec32fedb0da2a Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Thu, 12 Aug 2021 17:58:30 +0100 Subject: [PATCH] Solder mask integrity testing. ADDED DRC test for solder mask aperture bridging copper from different nets. ADDED visualization of minimum web width processing for solder masks. ADDED allow_soldermask_bridges property for footprints. Fixes https://gitlab.com/kicad/code/kicad/issues/2183 Fixes https://gitlab.com/kicad/code/kicad/issues/1792 --- common/pcb.keywords | 1 + common/view/view.cpp | 8 + include/pcb_base_frame.h | 3 + include/view/view.h | 5 + pcbnew/CMakeLists.txt | 2 +- pcbnew/board.cpp | 14 + pcbnew/board.h | 2 + pcbnew/board_commit.cpp | 10 + pcbnew/board_design_settings.cpp | 2 +- .../dialogs/dialog_footprint_properties.cpp | 4 + .../dialog_footprint_properties_base.cpp | 60 +- .../dialog_footprint_properties_base.fbp | 229 +++- .../dialog_footprint_properties_base.h | 6 +- .../dialog_footprint_properties_fp_editor.cpp | 4 + ...og_footprint_properties_fp_editor_base.cpp | 48 +- ...og_footprint_properties_fp_editor_base.fbp | 214 ++- ...alog_footprint_properties_fp_editor_base.h | 2 + pcbnew/dialogs/dialog_pad_properties_base.cpp | 34 +- pcbnew/dialogs/dialog_pad_properties_base.fbp | 42 +- .../panel_setup_mask_and_paste_base.cpp | 49 +- .../panel_setup_mask_and_paste_base.fbp | 224 ++-- .../dialogs/panel_setup_mask_and_paste_base.h | 6 +- pcbnew/drc/drc_item.cpp | 16 +- pcbnew/drc/drc_item.h | 7 +- pcbnew/drc/drc_rtree.h | 20 +- .../drc/drc_test_provider_edge_clearance.cpp | 6 +- pcbnew/drc/drc_test_provider_silk_to_mask.cpp | 209 --- pcbnew/drc/drc_test_provider_solder_mask.cpp | 572 ++++++++ pcbnew/footprint.h | 13 +- pcbnew/pcb_base_frame.cpp | 23 + pcbnew/plugins/kicad/pcb_parser.cpp | 8 +- pcbnew/plugins/kicad/pcb_plugin.cpp | 3 + pcbnew/plugins/kicad/pcb_plugin.h | 3 +- pcbnew/tools/drc_tool.cpp | 5 +- pcbnew/tools/edit_tool.cpp | 2 + pcbnew/zone.h | 6 + qa/data/issue1358.kicad_pcb | 4 +- qa/data/issue3812.kicad_pro | 16 +- qa/data/issue5320.kicad_pro | 58 +- qa/data/issue5567.kicad_pro | 55 +- qa/data/issue5990.kicad_pro | 3 +- qa/data/issue6260.kicad_pro | 27 +- qa/data/issue7325.kicad_pro | 3 + qa/data/issue7975.kicad_pro | 15 +- qa/data/solder_mask_bridge_test.kicad_pcb | 1156 +++++++++++++++++ qa/data/solder_mask_bridge_test.kicad_pro | 497 +++++++ qa/drc_proto/CMakeLists.txt | 2 +- qa/pcbnew/CMakeLists.txt | 1 + qa/pcbnew/drc/test_solder_mask_bridging.cpp | 83 ++ qa/pns/CMakeLists.txt | 2 +- 50 files changed, 3193 insertions(+), 591 deletions(-) delete mode 100644 pcbnew/drc/drc_test_provider_silk_to_mask.cpp create mode 100644 pcbnew/drc/drc_test_provider_solder_mask.cpp create mode 100644 qa/data/solder_mask_bridge_test.kicad_pcb create mode 100644 qa/data/solder_mask_bridge_test.kicad_pro create mode 100644 qa/pcbnew/drc/test_solder_mask_bridging.cpp diff --git a/common/pcb.keywords b/common/pcb.keywords index 5a1bcf4283..db9d313237 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -28,6 +28,7 @@ add_net addsublayer aligned allowed +allow_soldermask_bridges anchor angle arc diff --git a/common/view/view.cpp b/common/view/view.cpp index c83ed63b6f..c1b1a337b6 100644 --- a/common/view/view.cpp +++ b/common/view/view.cpp @@ -1567,6 +1567,14 @@ bool VIEW::IsVisible( const VIEW_ITEM* aItem ) const } +bool VIEW::HasItem( const VIEW_ITEM* aItem ) const +{ + const VIEW_ITEM_DATA* viewData = aItem->viewPrivData(); + + return viewData && viewData->m_view == this; +} + + void VIEW::Update( const VIEW_ITEM* aItem ) const { Update( aItem, ALL ); diff --git a/include/pcb_base_frame.h b/include/pcb_base_frame.h index 67a509f16a..d74acf9fa2 100644 --- a/include/pcb_base_frame.h +++ b/include/pcb_base_frame.h @@ -219,6 +219,9 @@ public: void FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer = UNDEFINED_LAYER ); + void HideSolderMask(); + void ShowSolderMask(); + // General virtual void ReCreateOptToolbar() override { } virtual void ShowChangedLanguage() override; diff --git a/include/view/view.h b/include/view/view.h index 99d45049b9..488fa598c6 100644 --- a/include/view/view.h +++ b/include/view/view.h @@ -142,6 +142,11 @@ public: */ bool IsVisible( const VIEW_ITEM* aItem ) const; + /** + * Indicates whether or not the given item has been added to the view. + */ + bool HasItem( const VIEW_ITEM* aItem ) const; + /** * For dynamic VIEWs, inform the associated VIEW that the graphical representation of * this item has changed. For static views calling has no effect. diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index baaabbef14..54bed23ee1 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -245,7 +245,7 @@ set( PCBNEW_DRC_SRCS drc/drc_test_provider_track_width.cpp drc/drc_test_provider_zone_connections.cpp drc/drc_test_provider_via_diameter.cpp - drc/drc_test_provider_silk_to_mask.cpp + drc/drc_test_provider_solder_mask.cpp drc/drc_test_provider_silk_clearance.cpp drc/drc_test_provider_matched_length.cpp drc/drc_test_provider_diff_pair_coupling.cpp diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index 597e936f79..193c714ec8 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -85,6 +85,18 @@ BOARD::BOARD() : m_layers[layer].m_type = LT_UNDEFINED; } + m_SolderMask = new ZONE( this ); + m_SolderMask->SetLayerSet( LSET().set( F_Mask ).set( B_Mask ) ); + m_SolderMask->SetOutline( new SHAPE_POLY_SET() ); + int infinity = ( std::numeric_limits::max() / 2 ) - Millimeter2iu( 1 ); + m_SolderMask->Outline()->NewOutline(); + m_SolderMask->Outline()->Append( VECTOR2I( -infinity, -infinity ) ); + m_SolderMask->Outline()->Append( VECTOR2I( -infinity, +infinity ) ); + m_SolderMask->Outline()->Append( VECTOR2I( +infinity, +infinity ) ); + m_SolderMask->Outline()->Append( VECTOR2I( +infinity, -infinity ) ); + m_SolderMask->SetMinThickness( 0 ); + m_SolderMask->SetFillVersion( 6 ); + BOARD_DESIGN_SETTINGS& bds = GetDesignSettings(); // Initialize default netclass. @@ -112,6 +124,8 @@ BOARD::~BOARD() m_zones.clear(); + delete m_SolderMask; + for( FOOTPRINT* footprint : m_footprints ) delete footprint; diff --git a/pcbnew/board.h b/pcbnew/board.h index 961418804e..d82ce2b0c2 100644 --- a/pcbnew/board.h +++ b/pcbnew/board.h @@ -1096,6 +1096,8 @@ public: std::map< ZONE*, std::unique_ptr > m_CopperZoneRTrees; + ZONE* m_SolderMask; + private: // The default copy constructor & operator= are inadequate, // either write one or do not use it at all diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index 0141836869..aacc0ff7e6 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -105,6 +105,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a std::set savedModules; PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); bool itemsDeselected = false; + bool solderMaskDirty = false; std::vector bulkAddedItems; std::vector bulkRemovedItems; @@ -151,6 +152,12 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a } } + if( boardItem->Type() == PCB_VIA_T || boardItem->Type() == PCB_FOOTPRINT_T + || boardItem->IsOnLayer( F_Mask ) || boardItem->IsOnLayer( B_Mask ) ) + { + solderMaskDirty = true; + } + switch( changeType ) { case CHT_ADD: @@ -379,7 +386,10 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a connectivity->ClearDynamicRatsnest(); if( frame ) + { + frame->HideSolderMask(); frame->GetCanvas()->RedrawRatsnest(); + } if( m_changes.size() > num_changes ) { diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 738b8a6f6b..d5e78d4a06 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -175,7 +175,7 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std: m_DRCSeverities[ DRCE_NET_CONFLICT ] = RPT_SEVERITY_WARNING; m_DRCSeverities[ DRCE_OVERLAPPING_SILK ] = RPT_SEVERITY_WARNING; - m_DRCSeverities[ DRCE_SILK_MASK_CLEARANCE ] = RPT_SEVERITY_WARNING; + m_DRCSeverities[ DRCE_SILK_CLEARANCE ] = RPT_SEVERITY_WARNING; m_DRCSeverities[ DRCE_TEXT_HEIGHT ] = RPT_SEVERITY_WARNING; m_DRCSeverities[ DRCE_TEXT_THICKNESS ] = RPT_SEVERITY_WARNING; diff --git a/pcbnew/dialogs/dialog_footprint_properties.cpp b/pcbnew/dialogs/dialog_footprint_properties.cpp index 1a4a16f9e5..7967fdfc8d 100644 --- a/pcbnew/dialogs/dialog_footprint_properties.cpp +++ b/pcbnew/dialogs/dialog_footprint_properties.cpp @@ -318,6 +318,7 @@ bool DIALOG_FOOTPRINT_PROPERTIES::TransferDataToWindow() m_solderMask.SetValue( m_footprint->GetLocalSolderMaskMargin() ); m_solderPaste.SetValue( m_footprint->GetLocalSolderPasteMargin() ); m_solderPasteRatio.SetDoubleValue( m_footprint->GetLocalSolderPasteMarginRatio() * 100.0 ); + m_allowSolderMaskBridges->SetValue( m_footprint->GetAttributes() & FP_ALLOW_SOLDERMASK_BRIDGES ); switch( m_footprint->GetZoneConnection() ) { @@ -479,6 +480,9 @@ bool DIALOG_FOOTPRINT_PROPERTIES::TransferDataFromWindow() if( m_excludeFromBOM->GetValue() ) attributes |= FP_EXCLUDE_FROM_BOM; + if( m_allowSolderMaskBridges->GetValue() ) + attributes |= FP_ALLOW_SOLDERMASK_BRIDGES; + m_footprint->SetAttributes( attributes ); m_footprint->SetPlacementCost90( m_CostRot90Ctrl->GetValue() ); diff --git a/pcbnew/dialogs/dialog_footprint_properties_base.cpp b/pcbnew/dialogs/dialog_footprint_properties_base.cpp index 6e9ac1efd2..b871e3b785 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_base.cpp +++ b/pcbnew/dialogs/dialog_footprint_properties_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.0-4761b0c) +// C++ code generated with wxFormBuilder (version Oct 26 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -49,6 +49,7 @@ DIALOG_FOOTPRINT_PROPERTIES_BASE::DIALOG_FOOTPRINT_PROPERTIES_BASE( wxWindow* pa m_itemsGrid->SetColSize( 10, 110 ); m_itemsGrid->EnableDragColMove( false ); m_itemsGrid->EnableDragColSize( false ); + m_itemsGrid->SetColLabelSize( 24 ); m_itemsGrid->SetColLabelValue( 0, _("Text Items") ); m_itemsGrid->SetColLabelValue( 1, _("Show") ); m_itemsGrid->SetColLabelValue( 2, _("Width") ); @@ -60,14 +61,13 @@ DIALOG_FOOTPRINT_PROPERTIES_BASE::DIALOG_FOOTPRINT_PROPERTIES_BASE( wxWindow* pa m_itemsGrid->SetColLabelValue( 8, _("Keep Upright") ); m_itemsGrid->SetColLabelValue( 9, _("X Offset") ); m_itemsGrid->SetColLabelValue( 10, _("Y Offset") ); - m_itemsGrid->SetColLabelSize( 24 ); m_itemsGrid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); // Rows m_itemsGrid->EnableDragRowSize( false ); + m_itemsGrid->SetRowLabelSize( 160 ); m_itemsGrid->SetRowLabelValue( 0, _("Reference designator") ); m_itemsGrid->SetRowLabelValue( 1, _("Value") ); - m_itemsGrid->SetRowLabelSize( 160 ); m_itemsGrid->SetRowLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTER ); // Label Appearance @@ -144,7 +144,7 @@ DIALOG_FOOTPRINT_PROPERTIES_BASE::DIALOG_FOOTPRINT_PROPERTIES_BASE( wxWindow* pa fgSizerPos->Add( m_BoardSideCtrl, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND|wxTOP, 5 ); - sbSizer7->Add( fgSizerPos, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + sbSizer7->Add( fgSizerPos, 0, wxEXPAND, 5 ); bSizerLeft->Add( sbSizer7, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); @@ -324,66 +324,78 @@ DIALOG_FOOTPRINT_PROPERTIES_BASE::DIALOG_FOOTPRINT_PROPERTIES_BASE( wxWindow* pa m_staticTextInfoValNeg->Wrap( -1 ); sbSizerLocalProperties->Add( m_staticTextInfoValNeg, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); - wxFlexGridSizer* fgSizerClearances; - fgSizerClearances = new wxFlexGridSizer( 5, 3, 5, 5 ); - fgSizerClearances->AddGrowableCol( 1 ); - fgSizerClearances->SetFlexibleDirection( wxBOTH ); - fgSizerClearances->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + wxGridBagSizer* gbSizer2; + gbSizer2 = new wxGridBagSizer( 4, 0 ); + gbSizer2->SetFlexibleDirection( wxBOTH ); + gbSizer2->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + gbSizer2->SetEmptyCellSize( wxSize( -1,15 ) ); m_NetClearanceLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Pad clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_NetClearanceLabel->Wrap( -1 ); m_NetClearanceLabel->SetToolTip( _("This is the local net clearance for all pad of this footprint\nIf 0, the Netclass values are used\nThis value can be superseded by a pad local value.") ); - fgSizerClearances->Add( m_NetClearanceLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + gbSizer2->Add( m_NetClearanceLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_NetClearanceCtrl = new wxTextCtrl( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_NetClearanceCtrl, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_NetClearanceCtrl, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_NetClearanceUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_NetClearanceUnits->Wrap( -1 ); - fgSizerClearances->Add( m_NetClearanceUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer2->Add( m_NetClearanceUnits, wxGBPosition( 0, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderMaskMarginLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Solder mask clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderMaskMarginLabel->Wrap( -1 ); m_SolderMaskMarginLabel->SetToolTip( _("This is the local clearance between pads and the solder mask for this footprint.\nThis value can be superseded by a pad local value.\nIf 0, the global value is used.") ); - fgSizerClearances->Add( m_SolderMaskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_SolderMaskMarginLabel, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderMaskMarginCtrl = new wxTextCtrl( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_SolderMaskMarginCtrl, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_SolderMaskMarginCtrl, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_SolderMaskMarginUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderMaskMarginUnits->Wrap( -1 ); - fgSizerClearances->Add( m_SolderMaskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer2->Add( m_SolderMaskMarginUnits, wxGBPosition( 2, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_allowSolderMaskBridges = new wxCheckBox( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Allow bridged solder mask apertures between pads"), wxDefaultPosition, wxDefaultSize, 0 ); + gbSizer2->Add( m_allowSolderMaskBridges, wxGBPosition( 3, 0 ), wxGBSpan( 1, 3 ), wxBOTTOM|wxRIGHT|wxLEFT, 5 ); m_SolderPasteMarginLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Solder paste absolute clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderPasteMarginLabel->Wrap( -1 ); m_SolderPasteMarginLabel->SetToolTip( _("This is the local clearance between pads and the solder paste for this footprint.\nThis value can be superseded by a pad local values.\nThe final clearance value is the sum of this value and the clearance value ratio.\nA negative value means a smaller mask size than pad size.") ); - fgSizerClearances->Add( m_SolderPasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_SolderPasteMarginLabel, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderPasteMarginCtrl = new wxTextCtrl( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_SolderPasteMarginCtrl, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_SolderPasteMarginCtrl, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_SolderPasteMarginUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderPasteMarginUnits->Wrap( -1 ); - fgSizerClearances->Add( m_SolderPasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer2->Add( m_SolderPasteMarginUnits, wxGBPosition( 5, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_PasteMarginRatioLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Solder paste relative clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_PasteMarginRatioLabel->Wrap( -1 ); m_PasteMarginRatioLabel->SetToolTip( _("This is the local clearance ratio in percent between pads and the solder paste for this footprint.\nA value of 10 means the clearance value is 10 percent of the pad size.\nThis value can be superseded by a pad local value.\nThe final clearance value is the sum of this value and the clearance value.\nA negative value means a smaller mask size than pad size.") ); - fgSizerClearances->Add( m_PasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_PasteMarginRatioLabel, wxGBPosition( 6, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_PasteMarginRatioCtrl = new TEXT_CTRL_EVAL( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_PasteMarginRatioCtrl, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer2->Add( m_PasteMarginRatioCtrl, wxGBPosition( 6, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_PasteMarginRatioUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("%"), wxDefaultPosition, wxDefaultSize, 0 ); m_PasteMarginRatioUnits->Wrap( -1 ); - fgSizerClearances->Add( m_PasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer2->Add( m_PasteMarginRatioUnits, wxGBPosition( 6, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - sbSizerLocalProperties->Add( fgSizerClearances, 1, wxEXPAND, 5 ); + sbSizerLocalProperties->Add( gbSizer2, 1, wxEXPAND|wxTOP|wxBOTTOM, 10 ); + + wxFlexGridSizer* fgSizerClearances; + fgSizerClearances = new wxFlexGridSizer( 5, 3, 5, 0 ); + fgSizerClearances->AddGrowableCol( 1 ); + fgSizerClearances->SetFlexibleDirection( wxBOTH ); + fgSizerClearances->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + + sbSizerLocalProperties->Add( fgSizerClearances, 0, wxTOP|wxBOTTOM, 10 ); m_staticTextInfoCopper = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Note: solder mask and paste values are used only for pads on copper layers."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextInfoCopper->Wrap( -1 ); @@ -401,13 +413,13 @@ DIALOG_FOOTPRINT_PROPERTIES_BASE::DIALOG_FOOTPRINT_PROPERTIES_BASE( wxWindow* pa m_staticText16 = new wxStaticText( sbSizerZoneConnection->GetStaticBox(), wxID_ANY, _("Pad connection to zones:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText16->Wrap( -1 ); - sbSizerZoneConnection->Add( m_staticText16, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + sbSizerZoneConnection->Add( m_staticText16, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); wxString m_ZoneConnectionChoiceChoices[] = { _("Use zone setting"), _("Solid"), _("Thermal relief"), _("None") }; int m_ZoneConnectionChoiceNChoices = sizeof( m_ZoneConnectionChoiceChoices ) / sizeof( wxString ); m_ZoneConnectionChoice = new wxChoice( sbSizerZoneConnection->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 ); m_ZoneConnectionChoice->SetSelection( 0 ); - sbSizerZoneConnection->Add( m_ZoneConnectionChoice, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + sbSizerZoneConnection->Add( m_ZoneConnectionChoice, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); bSizerPanelClearances->Add( sbSizerZoneConnection, 0, wxALL|wxEXPAND, 10 ); diff --git a/pcbnew/dialogs/dialog_footprint_properties_base.fbp b/pcbnew/dialogs/dialog_footprint_properties_base.fbp index 50f23cc464..178cb7d29e 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_base.fbp +++ b/pcbnew/dialogs/dialog_footprint_properties_base.fbp @@ -1,6 +1,6 @@ - + C++ @@ -14,7 +14,6 @@ dialog_footprint_properties_base 1000 none - 1 dialog_footprint_properties_base @@ -26,7 +25,6 @@ 1 1 UI - 0 0 0 @@ -52,7 +50,6 @@ DIALOG_SHIM; dialog_shim.h Footprint Properties - 0 @@ -304,7 +301,6 @@ - 0 @@ -388,7 +384,6 @@ - 0 @@ -485,7 +480,7 @@ none 5 - wxEXPAND|wxRIGHT|wxLEFT + wxEXPAND 0 3 @@ -1867,7 +1862,6 @@ - 0 @@ -1941,7 +1935,6 @@ - 0 @@ -2015,7 +2008,6 @@ - 0 @@ -2099,7 +2091,6 @@ - 0 @@ -2777,25 +2768,27 @@ - 5 - wxEXPAND + 10 + wxEXPAND|wxTOP|wxBOTTOM 1 - - 3 + + -1,15 wxBOTH - 1 + - 5 + 0 - fgSizerClearances + gbSizer2 wxFLEX_GROWMODE_SPECIFIED none - 5 - 5 - + 4 + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + 1 1 1 @@ -2853,10 +2846,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 0 + 1 1 1 @@ -2917,10 +2913,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + 1 1 1 @@ -2978,10 +2977,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 2 + 1 1 1 @@ -3039,10 +3041,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 2 + 1 1 1 @@ -3103,10 +3108,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 2 + 1 1 1 @@ -3164,10 +3172,80 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 + 3 + 0 + wxBOTTOM|wxRIGHT|wxLEFT + 3 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Allow bridged solder mask apertures between pads + + 0 + + + 0 + + 1 + m_allowSolderMaskBridges + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 5 + 1 1 1 @@ -3225,10 +3303,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 5 + 1 1 1 @@ -3289,10 +3370,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 5 + 1 1 1 @@ -3350,10 +3434,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 6 + 1 1 1 @@ -3411,10 +3498,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 6 + 1 1 1 @@ -3475,10 +3565,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 6 + 1 1 1 @@ -3538,6 +3631,24 @@ + + 10 + wxTOP|wxBOTTOM + 0 + + 3 + wxBOTH + 1 + + 0 + + fgSizerClearances + wxFLEX_GROWMODE_SPECIFIED + none + 5 + 5 + + 5 wxEXPAND|wxLEFT|wxRIGHT|wxTOP @@ -3676,7 +3787,7 @@ none 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT + wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL 0 1 @@ -3737,8 +3848,8 @@ 5 - wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT - 1 + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT + 0 1 1 diff --git a/pcbnew/dialogs/dialog_footprint_properties_base.h b/pcbnew/dialogs/dialog_footprint_properties_base.h index 76a7adcbe5..0121d575df 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_base.h +++ b/pcbnew/dialogs/dialog_footprint_properties_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.0-4761b0c) +// C++ code generated with wxFormBuilder (version Oct 26 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -99,6 +99,7 @@ class DIALOG_FOOTPRINT_PROPERTIES_BASE : public DIALOG_SHIM wxStaticText* m_SolderMaskMarginLabel; wxTextCtrl* m_SolderMaskMarginCtrl; wxStaticText* m_SolderMaskMarginUnits; + wxCheckBox* m_allowSolderMaskBridges; wxStaticText* m_SolderPasteMarginLabel; wxTextCtrl* m_SolderPasteMarginCtrl; wxStaticText* m_SolderPasteMarginUnits; @@ -115,7 +116,7 @@ class DIALOG_FOOTPRINT_PROPERTIES_BASE : public DIALOG_SHIM wxButton* m_sdbSizerStdButtonsOK; wxButton* m_sdbSizerStdButtonsCancel; - // Virtual event handlers, override them in your derived class + // Virtual event handlers, overide them in your derived class virtual void OnInitDlg( wxInitDialogEvent& event ) { event.Skip(); } virtual void OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } virtual void OnPageChange( wxNotebookEvent& event ) { event.Skip(); } @@ -133,7 +134,6 @@ class DIALOG_FOOTPRINT_PROPERTIES_BASE : public DIALOG_SHIM public: DIALOG_FOOTPRINT_PROPERTIES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Footprint Properties"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~DIALOG_FOOTPRINT_PROPERTIES_BASE(); }; diff --git a/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp b/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp index b2f4973555..3e7813690b 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp +++ b/pcbnew/dialogs/dialog_footprint_properties_fp_editor.cpp @@ -227,6 +227,7 @@ bool DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::TransferDataToWindow() m_solderMask.SetValue( m_footprint->GetLocalSolderMaskMargin() ); m_solderPaste.SetValue( m_footprint->GetLocalSolderPasteMargin() ); m_solderPasteRatio.SetDoubleValue( m_footprint->GetLocalSolderPasteMarginRatio() * 100.0 ); + m_allowBridges->SetValue( m_footprint->GetAttributes() & FP_ALLOW_SOLDERMASK_BRIDGES ); switch( m_footprint->GetZoneConnection() ) { @@ -421,6 +422,9 @@ bool DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::TransferDataFromWindow() if( m_excludeFromBOM->GetValue() ) attributes |= FP_EXCLUDE_FROM_BOM; + if( m_allowBridges->GetValue() ) + attributes |= FP_ALLOW_SOLDERMASK_BRIDGES; + m_footprint->SetAttributes( attributes ); m_footprint->SetPlacementCost90( m_CostRot90Ctrl->GetValue() ); diff --git a/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.cpp b/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.cpp index bd17cf13b2..d9f38ce089 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.cpp +++ b/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.cpp @@ -224,63 +224,75 @@ DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR_BASE::DIALOG_FOOTPRINT_PROPERTIES_FP_EDITO m_staticTextInfoValNeg->Wrap( -1 ); sbSizerLocalProperties->Add( m_staticTextInfoValNeg, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); - wxFlexGridSizer* fgSizerClearances; - fgSizerClearances = new wxFlexGridSizer( 5, 3, 0, 0 ); - fgSizerClearances->AddGrowableCol( 1 ); - fgSizerClearances->SetFlexibleDirection( wxBOTH ); - fgSizerClearances->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + wxGridBagSizer* gbSizer1; + gbSizer1 = new wxGridBagSizer( 4, 0 ); + gbSizer1->SetFlexibleDirection( wxBOTH ); + gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + gbSizer1->SetEmptyCellSize( wxSize( -1,15 ) ); m_NetClearanceLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Pad clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_NetClearanceLabel->Wrap( -1 ); m_NetClearanceLabel->SetToolTip( _("This is the local net clearance for all pads of this footprint.\nIf 0, the Netclass values are used.\nThis value can be overridden on a pad-by-pad basis in the Local\nClearance and Settings tab of Pad Properties.") ); - fgSizerClearances->Add( m_NetClearanceLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + gbSizer1->Add( m_NetClearanceLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_NetClearanceCtrl = new wxTextCtrl( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_NetClearanceCtrl, 1, wxEXPAND|wxALL, 5 ); + gbSizer1->Add( m_NetClearanceCtrl, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_NetClearanceUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_NetClearanceUnits->Wrap( -1 ); - fgSizerClearances->Add( m_NetClearanceUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer1->Add( m_NetClearanceUnits, wxGBPosition( 0, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderMaskMarginLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Solder mask clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderMaskMarginLabel->Wrap( -1 ); m_SolderMaskMarginLabel->SetToolTip( _("This is the local clearance between pads and the solder mask for \nthis footprint.\nIf 0, the global value is used.\nThis value can be overridden on a pad-by-pad basis in the Local\nClearance and Settings tab of Pad Properties.") ); - fgSizerClearances->Add( m_SolderMaskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + gbSizer1->Add( m_SolderMaskMarginLabel, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderMaskMarginCtrl = new wxTextCtrl( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_SolderMaskMarginCtrl, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer1->Add( m_SolderMaskMarginCtrl, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_SolderMaskMarginUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderMaskMarginUnits->Wrap( -1 ); - fgSizerClearances->Add( m_SolderMaskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer1->Add( m_SolderMaskMarginUnits, wxGBPosition( 2, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_allowBridges = new wxCheckBox( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Allow bridged solder mask apertures between pads"), wxDefaultPosition, wxDefaultSize, 0 ); + gbSizer1->Add( m_allowBridges, wxGBPosition( 3, 0 ), wxGBSpan( 1, 3 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderPasteMarginLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Solder paste absolute clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderPasteMarginLabel->Wrap( -1 ); m_SolderPasteMarginLabel->SetToolTip( _("This is the local clearance between pads and the solder paste for\nthis footprint.\nThe final clearance value is the sum of this value and the clearance value ratio.\nA negative value means a smaller mask size than pad size.\nThis value can be overridden on a pad-by-pad basis in the Local\nClearance and Settings tab of Pad Properties.") ); - fgSizerClearances->Add( m_SolderPasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + gbSizer1->Add( m_SolderPasteMarginLabel, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_SolderPasteMarginCtrl = new wxTextCtrl( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_SolderPasteMarginCtrl, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer1->Add( m_SolderPasteMarginCtrl, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_SolderPasteMarginUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); m_SolderPasteMarginUnits->Wrap( -1 ); - fgSizerClearances->Add( m_SolderPasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer1->Add( m_SolderPasteMarginUnits, wxGBPosition( 5, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_PasteMarginRatioLabel = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("Solder paste relative clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_PasteMarginRatioLabel->Wrap( -1 ); m_PasteMarginRatioLabel->SetToolTip( _("This is the local clearance ratio in percent between pads and the\nsolder paste for this footprint.\nA value of 10 means the clearance value is 10 percent of the pad size.\nThe final clearance value is the sum of this value and the clearance value.\nA negative value means a smaller mask size than pad size.\nThis value can be overridden on a pad-by-pad basis in the Local\nClearance and Settings tab of Pad Properties.") ); - fgSizerClearances->Add( m_PasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + gbSizer1->Add( m_PasteMarginRatioLabel, wxGBPosition( 6, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_PasteMarginRatioCtrl = new TEXT_CTRL_EVAL( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerClearances->Add( m_PasteMarginRatioCtrl, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + gbSizer1->Add( m_PasteMarginRatioCtrl, wxGBPosition( 6, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_PasteMarginRatioUnits = new wxStaticText( sbSizerLocalProperties->GetStaticBox(), wxID_ANY, _("%"), wxDefaultPosition, wxDefaultSize, 0 ); m_PasteMarginRatioUnits->Wrap( -1 ); - fgSizerClearances->Add( m_PasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer1->Add( m_PasteMarginRatioUnits, wxGBPosition( 6, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + + sbSizerLocalProperties->Add( gbSizer1, 0, wxEXPAND|wxTOP|wxBOTTOM, 10 ); + + wxFlexGridSizer* fgSizerClearances; + fgSizerClearances = new wxFlexGridSizer( 5, 3, 0, 0 ); + fgSizerClearances->AddGrowableCol( 1 ); + fgSizerClearances->SetFlexibleDirection( wxBOTH ); + fgSizerClearances->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); sbSizerLocalProperties->Add( fgSizerClearances, 1, wxEXPAND, 5 ); @@ -307,7 +319,7 @@ DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR_BASE::DIALOG_FOOTPRINT_PROPERTIES_FP_EDITO int m_ZoneConnectionChoiceNChoices = sizeof( m_ZoneConnectionChoiceChoices ) / sizeof( wxString ); m_ZoneConnectionChoice = new wxChoice( sbSizer5->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 ); m_ZoneConnectionChoice->SetSelection( 0 ); - sbSizer5->Add( m_ZoneConnectionChoice, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + sbSizer5->Add( m_ZoneConnectionChoice, 0, wxBOTTOM|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); bSizerPanelClearances->Add( sbSizer5, 0, wxALL|wxEXPAND, 5 ); diff --git a/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.fbp b/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.fbp index f697636143..6b83893394 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.fbp +++ b/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.fbp @@ -1766,25 +1766,27 @@ - 5 - wxEXPAND - 1 - - 3 + 10 + wxEXPAND|wxTOP|wxBOTTOM + 0 + + -1,15 wxBOTH - 1 + 0 - fgSizerClearances + gbSizer1 wxFLEX_GROWMODE_SPECIFIED none - 5 - 0 - + 4 + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + 1 1 1 @@ -1842,10 +1844,13 @@ -1 - + 5 - wxEXPAND|wxALL - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 0 + 1 1 1 @@ -1906,10 +1911,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + 1 1 1 @@ -1967,10 +1975,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 2 + 1 1 1 @@ -2028,10 +2039,13 @@ -1 - + 5 - wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 2 + 1 1 1 @@ -2092,10 +2106,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 2 + 1 1 1 @@ -2153,10 +2170,80 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 + 3 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 3 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Allow bridged solder mask apertures between pads + + 0 + + + 0 + + 1 + m_allowBridges + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 5 + 1 1 1 @@ -2214,10 +2301,13 @@ -1 - + 5 - wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 5 + 1 1 1 @@ -2278,10 +2368,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 5 + 1 1 1 @@ -2339,10 +2432,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 6 + 1 1 1 @@ -2400,10 +2496,13 @@ -1 - + 5 - wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT - 1 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 6 + 1 1 1 @@ -2464,10 +2563,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 6 + 1 1 1 @@ -2527,6 +2629,24 @@ + + 5 + wxEXPAND + 1 + + 3 + wxBOTH + 1 + + 0 + + fgSizerClearances + wxFLEX_GROWMODE_SPECIFIED + none + 5 + 0 + + 5 wxEXPAND|wxLEFT|wxRIGHT|wxTOP @@ -2726,8 +2846,8 @@ 5 - wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT - 1 + wxBOTTOM|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL + 0 1 1 diff --git a/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.h b/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.h index 6b3721950f..8cc9d5f44a 100644 --- a/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.h +++ b/pcbnew/dialogs/dialog_footprint_properties_fp_editor_base.h @@ -33,6 +33,7 @@ class WX_GRID; #include #include #include +#include #include #include @@ -80,6 +81,7 @@ class DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR_BASE : public DIALOG_SHIM wxStaticText* m_SolderMaskMarginLabel; wxTextCtrl* m_SolderMaskMarginCtrl; wxStaticText* m_SolderMaskMarginUnits; + wxCheckBox* m_allowBridges; wxStaticText* m_SolderPasteMarginLabel; wxTextCtrl* m_SolderPasteMarginCtrl; wxStaticText* m_SolderPasteMarginUnits; diff --git a/pcbnew/dialogs/dialog_pad_properties_base.cpp b/pcbnew/dialogs/dialog_pad_properties_base.cpp index 730c7e74dc..b7fe7ce5bb 100644 --- a/pcbnew/dialogs/dialog_pad_properties_base.cpp +++ b/pcbnew/dialogs/dialog_pad_properties_base.cpp @@ -595,7 +595,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind sbClearancesSizer->Add( m_staticTextInfoNegVal, 0, wxBOTTOM|wxRIGHT, 10 ); wxFlexGridSizer* fgClearancesGridSizer; - fgClearancesGridSizer = new wxFlexGridSizer( 4, 3, 0, 0 ); + fgClearancesGridSizer = new wxFlexGridSizer( 4, 3, 4, 0 ); fgClearancesGridSizer->AddGrowableCol( 1 ); fgClearancesGridSizer->SetFlexibleDirection( wxBOTH ); fgClearancesGridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); @@ -604,56 +604,56 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind m_clearanceLabel->Wrap( -1 ); m_clearanceLabel->SetToolTip( _("This is the local net clearance for this pad.\nIf 0, the footprint local value or the Netclass value is used.") ); - fgClearancesGridSizer->Add( m_clearanceLabel, 0, wxALIGN_CENTER_VERTICAL, 5 ); + fgClearancesGridSizer->Add( m_clearanceLabel, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); m_clearanceCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgClearancesGridSizer->Add( m_clearanceCtrl, 0, wxEXPAND|wxTOP|wxLEFT, 5 ); + fgClearancesGridSizer->Add( m_clearanceCtrl, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); m_clearanceUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_clearanceUnits->Wrap( -1 ); - fgClearancesGridSizer->Add( m_clearanceUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + fgClearancesGridSizer->Add( m_clearanceUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_maskMarginLabel = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Solder mask clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginLabel->Wrap( -1 ); m_maskMarginLabel->SetToolTip( _("This is the local clearance between this pad and the solder mask.\nIf 0, the footprint local value or the global value is used.") ); - fgClearancesGridSizer->Add( m_maskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_maskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); m_maskMarginCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgClearancesGridSizer->Add( m_maskMarginCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_maskMarginCtrl, 0, wxEXPAND|wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 15 ); m_maskMarginUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginUnits->Wrap( -1 ); - fgClearancesGridSizer->Add( m_maskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_maskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_pasteMarginLabel = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Solder paste absolute clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginLabel->Wrap( -1 ); m_pasteMarginLabel->SetToolTip( _("This is the local clearance between this pad and the solder paste.\nIf 0, the footprint value or the global value is used.\nThe final clearance value is the sum of this value and the clearance value ratio.\nA negative value means a smaller mask size than pad size.") ); - fgClearancesGridSizer->Add( m_pasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_pasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); m_pasteMarginCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgClearancesGridSizer->Add( m_pasteMarginCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_pasteMarginCtrl, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); m_pasteMarginUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginUnits->Wrap( -1 ); - fgClearancesGridSizer->Add( m_pasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_pasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_pasteMarginRatioLabel = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Solder paste relative clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginRatioLabel->Wrap( -1 ); m_pasteMarginRatioLabel->SetToolTip( _("This is the local clearance ratio in percent between this pad and the solder paste.\nA value of 10 means the clearance value is 10 percent of the pad size.\nIf 0, the footprint value or the global value is used.\nThe final clearance value is the sum of this value and the clearance value.\nA negative value means a smaller mask size than pad size.") ); - fgClearancesGridSizer->Add( m_pasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + fgClearancesGridSizer->Add( m_pasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_pasteMarginRatioCtrl = new TEXT_CTRL_EVAL( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgClearancesGridSizer->Add( m_pasteMarginRatioCtrl, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 5 ); + fgClearancesGridSizer->Add( m_pasteMarginRatioCtrl, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); m_pasteMarginRatioUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("%"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginRatioUnits->Wrap( -1 ); - fgClearancesGridSizer->Add( m_pasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + fgClearancesGridSizer->Add( m_pasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - sbClearancesSizer->Add( fgClearancesGridSizer, 0, 0, 5 ); + sbClearancesSizer->Add( fgClearancesGridSizer, 0, wxTOP|wxBOTTOM, 10 ); m_nonCopperWarningBook = new wxSimplebook( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); wxPanel* notePanel; @@ -719,7 +719,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind int m_ZoneConnectionChoiceNChoices = sizeof( m_ZoneConnectionChoiceChoices ) / sizeof( wxString ); m_ZoneConnectionChoice = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 ); m_ZoneConnectionChoice->SetSelection( 0 ); - fgSizerCopperZonesOpts->Add( m_ZoneConnectionChoice, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT, 5 ); + fgSizerCopperZonesOpts->Add( m_ZoneConnectionChoice, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT, 5 ); m_staticTextcps = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Zone knockout:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextcps->Wrap( -1 ); @@ -729,7 +729,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind int m_ZoneCustomPadShapeNChoices = sizeof( m_ZoneCustomPadShapeChoices ) / sizeof( wxString ); m_ZoneCustomPadShape = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneCustomPadShapeNChoices, m_ZoneCustomPadShapeChoices, 0 ); m_ZoneCustomPadShape->SetSelection( 0 ); - fgSizerCopperZonesOpts->Add( m_ZoneCustomPadShape, 1, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + fgSizerCopperZonesOpts->Add( m_ZoneCustomPadShape, 1, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); m_sbSizerZonesSettings->Add( fgSizerCopperZonesOpts, 0, 0, 5 ); @@ -751,7 +751,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind fgSizerThermalReliefs->Add( m_thermalGapLabel, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_thermalGapCtrl = new wxTextCtrl( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizerThermalReliefs->Add( m_thermalGapCtrl, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + fgSizerThermalReliefs->Add( m_thermalGapCtrl, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND, 5 ); m_thermalGapUnits = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_thermalGapUnits->Wrap( -1 ); diff --git a/pcbnew/dialogs/dialog_pad_properties_base.fbp b/pcbnew/dialogs/dialog_pad_properties_base.fbp index 4d0b721833..98d0a258cb 100644 --- a/pcbnew/dialogs/dialog_pad_properties_base.fbp +++ b/pcbnew/dialogs/dialog_pad_properties_base.fbp @@ -7148,11 +7148,11 @@ -1 - - 5 - + + 10 + wxTOP|wxBOTTOM 0 - + 3 wxBOTH 1 @@ -7163,10 +7163,10 @@ wxFLEX_GROWMODE_SPECIFIED none 4 - 0 + 4 5 - wxALIGN_CENTER_VERTICAL + wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 @@ -7227,7 +7227,7 @@ 5 - wxEXPAND|wxTOP|wxLEFT + wxEXPAND|wxALIGN_CENTER_VERTICAL 0 1 @@ -7292,7 +7292,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT 0 1 @@ -7353,7 +7353,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxTOP + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT 0 1 @@ -7413,8 +7413,8 @@ - 5 - wxEXPAND|wxLEFT|wxTOP + 15 + wxEXPAND|wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL 0 1 @@ -7478,7 +7478,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT 0 1 @@ -7539,7 +7539,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxTOP + wxALIGN_CENTER_VERTICAL|wxRIGHT 0 1 @@ -7600,7 +7600,7 @@ 5 - wxEXPAND|wxLEFT|wxTOP + wxEXPAND|wxALIGN_CENTER_VERTICAL 0 1 @@ -7664,7 +7664,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT 0 1 @@ -7725,7 +7725,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM + wxALIGN_CENTER_VERTICAL 0 1 @@ -7786,7 +7786,7 @@ 5 - wxBOTTOM|wxEXPAND|wxLEFT|wxTOP + wxEXPAND|wxALIGN_CENTER_VERTICAL 0 1 @@ -7850,7 +7850,7 @@ 5 - wxALIGN_CENTER_VERTICAL|wxALL + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT 0 1 @@ -8436,7 +8436,7 @@ 5 - wxEXPAND|wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT 1 1 @@ -8561,7 +8561,7 @@ 5 - wxEXPAND|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL + wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND 1 1 @@ -8718,7 +8718,7 @@ 5 - wxEXPAND|wxALIGN_CENTER_VERTICAL|wxLEFT + wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND 0 1 diff --git a/pcbnew/dialogs/panel_setup_mask_and_paste_base.cpp b/pcbnew/dialogs/panel_setup_mask_and_paste_base.cpp index 14f9443e56..0b565046e0 100644 --- a/pcbnew/dialogs/panel_setup_mask_and_paste_base.cpp +++ b/pcbnew/dialogs/panel_setup_mask_and_paste_base.cpp @@ -46,89 +46,80 @@ PANEL_SETUP_MASK_AND_PASTE_BASE::PANEL_SETUP_MASK_AND_PASTE_BASE( wxWindow* pare m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer3->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 ); - wxFlexGridSizer* fgGridSolderMaskSizer; - fgGridSolderMaskSizer = new wxFlexGridSizer( 0, 3, 0, 0 ); - fgGridSolderMaskSizer->SetFlexibleDirection( wxBOTH ); - fgGridSolderMaskSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + wxGridBagSizer* gbSizer1; + gbSizer1 = new wxGridBagSizer( 5, 0 ); + gbSizer1->SetFlexibleDirection( wxBOTH ); + gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); m_maskMarginLabel = new wxStaticText( this, wxID_ANY, _("Solder mask clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginLabel->Wrap( -1 ); m_maskMarginLabel->SetToolTip( _("Global clearance between pads and the solder mask.\nThis value can be superseded by local values for a footprint or a pad.") ); - fgGridSolderMaskSizer->Add( m_maskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + gbSizer1->Add( m_maskMarginLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); m_maskMarginCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginCtrl->SetToolTip( _("Positive clearance means area bigger than the pad (usual for solder mask clearance).") ); - fgGridSolderMaskSizer->Add( m_maskMarginCtrl, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + gbSizer1->Add( m_maskMarginCtrl, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_maskMarginUnits = new wxStaticText( this, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginUnits->Wrap( -1 ); - fgGridSolderMaskSizer->Add( m_maskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer1->Add( m_maskMarginUnits, wxGBPosition( 0, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_maskMinWidthLabel = new wxStaticText( this, wxID_ANY, _("Solder mask minimum web width:"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMinWidthLabel->Wrap( -1 ); m_maskMinWidthLabel->SetToolTip( _("Min. dist between 2 pad areas.\nTwo pad areas nearer than this value will be merged during plotting.\nThis parameter is only used to plot solder mask layers.\nLeave at 0 unless you know what you are doing.") ); - fgGridSolderMaskSizer->Add( m_maskMinWidthLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + gbSizer1->Add( m_maskMinWidthLabel, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); m_maskMinWidthCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_maskMinWidthCtrl->SetToolTip( _("Minimum distance between openings in the solder mask. Pad openings closer than this distance will be plotted as a single opening.") ); - fgGridSolderMaskSizer->Add( m_maskMinWidthCtrl, 0, wxEXPAND|wxALL, 5 ); + gbSizer1->Add( m_maskMinWidthCtrl, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_maskMinWidthUnits = new wxStaticText( this, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMinWidthUnits->Wrap( -1 ); - fgGridSolderMaskSizer->Add( m_maskMinWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + gbSizer1->Add( m_maskMinWidthUnits, wxGBPosition( 1, 2 ), wxGBSpan( 1, 1 ), wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - - fgGridSolderMaskSizer->Add( 0, 0, 1, wxEXPAND|wxTOP|wxBOTTOM, 10 ); - - - fgGridSolderMaskSizer->Add( 0, 0, 1, wxEXPAND, 5 ); - - - fgGridSolderMaskSizer->Add( 0, 0, 1, wxEXPAND, 5 ); + m_allowBridges = new wxCheckBox( this, wxID_ANY, _("Allow bridged solder mask apertures between pads within a footprint"), wxDefaultPosition, wxDefaultSize, 0 ); + gbSizer1->Add( m_allowBridges, wxGBPosition( 2, 0 ), wxGBSpan( 1, 3 ), wxALL, 5 ); m_pasteMarginLabel = new wxStaticText( this, wxID_ANY, _("Solder paste absolute clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginLabel->Wrap( -1 ); m_pasteMarginLabel->SetToolTip( _("Global clearance between pads and the solder paste.\nThis value can be superseded by local values for a footprint or a pad.\nFinal clearance value is the sum of this value and the clearance value ratio.") ); - fgGridSolderMaskSizer->Add( m_pasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + gbSizer1->Add( m_pasteMarginLabel, wxGBPosition( 4, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_pasteMarginCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginCtrl->SetToolTip( _("Negative clearance means area smaller than the pad (usual for solder paste clearance).") ); - fgGridSolderMaskSizer->Add( m_pasteMarginCtrl, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + gbSizer1->Add( m_pasteMarginCtrl, wxGBPosition( 4, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_pasteMarginUnits = new wxStaticText( this, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginUnits->Wrap( -1 ); - fgGridSolderMaskSizer->Add( m_pasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + gbSizer1->Add( m_pasteMarginUnits, wxGBPosition( 4, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_pasteMarginRatioLabel = new wxStaticText( this, wxID_ANY, _("Solder paste relative clearance:"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginRatioLabel->Wrap( -1 ); m_pasteMarginRatioLabel->SetToolTip( _("Global clearance ratio in percent between pads and the solder paste.\nA value of 10 means the clearance value is 10 percent of the pad size.\nThis value can be superseded by local values for a footprint or a pad.\nFinal clearance value is the sum of this value and the clearance value.") ); - fgGridSolderMaskSizer->Add( m_pasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + gbSizer1->Add( m_pasteMarginRatioLabel, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_pasteMarginRatioCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginRatioCtrl->SetToolTip( _("Additional clearance as a percentage of the pad size.") ); - fgGridSolderMaskSizer->Add( m_pasteMarginRatioCtrl, 0, wxEXPAND|wxALL, 5 ); + gbSizer1->Add( m_pasteMarginRatioCtrl, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 ); m_pasteMarginRatioUnits = new wxStaticText( this, wxID_ANY, _("%"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginRatioUnits->Wrap( -1 ); - fgGridSolderMaskSizer->Add( m_pasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + gbSizer1->Add( m_pasteMarginRatioUnits, wxGBPosition( 5, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - bSizer3->Add( fgGridSolderMaskSizer, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer3->Add( 0, 0, 0, wxEXPAND|wxTOP|wxBOTTOM, 10 ); + bSizer3->Add( gbSizer1, 1, wxEXPAND|wxTOP, 5 ); m_staticTextInfoPaste = new wxStaticText( this, wxID_ANY, _("Note: Solder paste clearances (absolute and relative) are added to determine the final clearance."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextInfoPaste->Wrap( -1 ); - bSizer3->Add( m_staticTextInfoPaste, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + bSizer3->Add( m_staticTextInfoPaste, 0, wxEXPAND|wxALL, 5 ); bMainSizer->Add( bSizer3, 1, wxRIGHT|wxLEFT|wxEXPAND, 5 ); diff --git a/pcbnew/dialogs/panel_setup_mask_and_paste_base.fbp b/pcbnew/dialogs/panel_setup_mask_and_paste_base.fbp index dcdd0c621c..3ae4eb6303 100644 --- a/pcbnew/dialogs/panel_setup_mask_and_paste_base.fbp +++ b/pcbnew/dialogs/panel_setup_mask_and_paste_base.fbp @@ -338,24 +338,26 @@ 5 - wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT - 0 - - 3 + wxEXPAND|wxTOP + 1 + + wxBOTH 0 - fgGridSolderMaskSizer + gbSizer1 wxFLEX_GROWMODE_SPECIFIED none - 0 - 0 - + 5 + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT - 0 + 1 + 0 + wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL + 0 + 1 1 1 @@ -413,10 +415,13 @@ -1 - + 5 - wxEXPAND|wxRIGHT|wxLEFT - 0 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 0 + 1 1 1 @@ -477,10 +482,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + 1 1 1 @@ -538,10 +546,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT - 0 + 1 + 0 + wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL + 1 + 1 1 1 @@ -599,10 +610,13 @@ -1 - + 5 - wxEXPAND|wxALL - 0 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 1 + 1 1 1 @@ -663,10 +677,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT - 0 + 1 + 2 + wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL + 1 + 1 1 1 @@ -724,40 +741,80 @@ -1 - - 10 - wxEXPAND|wxTOP|wxBOTTOM - 1 - - 0 + + 5 + 3 + 0 + wxALL + 2 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Allow bridged solder mask apertures between pads within a footprint + + 0 + + + 0 + + 1 + m_allowBridges + 1 + + protected - 0 + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + - + 5 - wxEXPAND - 1 - - 0 - protected - 0 - - - - 5 - wxEXPAND - 1 - - 0 - protected - 0 - - - - 5 - wxALIGN_CENTER_VERTICAL|wxLEFT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 4 + 1 1 1 @@ -815,10 +872,13 @@ -1 - + 5 - wxEXPAND|wxRIGHT|wxLEFT - 0 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 4 + 1 1 1 @@ -879,10 +939,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 4 + 1 1 1 @@ -940,10 +1003,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT - 0 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 5 + 1 1 1 @@ -1001,10 +1067,13 @@ -1 - + 5 - wxEXPAND|wxALL - 0 + 1 + 1 + wxALIGN_CENTER_VERTICAL + 5 + 1 1 1 @@ -1065,10 +1134,13 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT - 0 + 1 + 2 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 5 + 1 1 1 @@ -1128,19 +1200,9 @@ - - 10 - wxEXPAND|wxTOP|wxBOTTOM - 0 - - 0 - protected - 0 - - 5 - wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT + wxEXPAND|wxALL 0 1 diff --git a/pcbnew/dialogs/panel_setup_mask_and_paste_base.h b/pcbnew/dialogs/panel_setup_mask_and_paste_base.h index 19208fa869..596d3a8d20 100644 --- a/pcbnew/dialogs/panel_setup_mask_and_paste_base.h +++ b/pcbnew/dialogs/panel_setup_mask_and_paste_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.0-4761b0c5) +// C++ code generated with wxFormBuilder (version Oct 26 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include /////////////////////////////////////////////////////////////////////////// @@ -45,6 +47,7 @@ class PANEL_SETUP_MASK_AND_PASTE_BASE : public wxPanel wxStaticText* m_maskMinWidthLabel; wxTextCtrl* m_maskMinWidthCtrl; wxStaticText* m_maskMinWidthUnits; + wxCheckBox* m_allowBridges; wxStaticText* m_pasteMarginLabel; wxTextCtrl* m_pasteMarginCtrl; wxStaticText* m_pasteMarginUnits; @@ -56,7 +59,6 @@ class PANEL_SETUP_MASK_AND_PASTE_BASE : public wxPanel public: PANEL_SETUP_MASK_AND_PASTE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); - ~PANEL_SETUP_MASK_AND_PASTE_BASE(); }; diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index c530d4b626..75aa466fb2 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -76,8 +76,8 @@ DRC_ITEM DRC_ITEM::edgeClearance( DRCE_EDGE_CLEARANCE, wxT( "copper_edge_clearance" ) ); DRC_ITEM DRC_ITEM::zonesIntersect( DRCE_ZONES_INTERSECT, - _( "Copper areas intersect" ), - wxT( "zones_intersect" ) ); + _( "Copper areas intersect" ), + wxT( "zones_intersect" ) ); DRC_ITEM DRC_ITEM::zoneHasEmptyNet( DRCE_ZONE_HAS_EMPTY_NET, _( "Copper zone net has no pads" ), @@ -187,7 +187,11 @@ DRC_ITEM DRC_ITEM::copperSliver( DRCE_COPPER_SLIVER, _( "Copper sliver" ), wxT( "copper_sliver" ) ); -DRC_ITEM DRC_ITEM::silkMaskClearance( DRCE_SILK_MASK_CLEARANCE, +DRC_ITEM DRC_ITEM::solderMaskBridge( DRCE_SOLDERMASK_BRIDGE, + _( "Solder mask aperture bridges items with different nets" ), + wxT( "solder_mask_bridge" ) ); + +DRC_ITEM DRC_ITEM::silkClearance( DRCE_SILK_CLEARANCE, _( "Silkscreen clipped by solder mask" ), wxT( "silk_over_copper" ) ); @@ -254,6 +258,7 @@ std::vector> DRC_ITEM::allItemTypes( { DRC_ITEM::malformedCourtyard, DRC_ITEM::invalidOutline, DRC_ITEM::copperSliver, + DRC_ITEM::solderMaskBridge, DRC_ITEM::heading_schematic_parity, DRC_ITEM::duplicateFootprints, @@ -271,7 +276,7 @@ std::vector> DRC_ITEM::allItemTypes( { DRC_ITEM::heading_readability, DRC_ITEM::silkOverlaps, - DRC_ITEM::silkMaskClearance, + DRC_ITEM::silkClearance, DRC_ITEM::textHeightOutOfRange, DRC_ITEM::textThicknessOutOfRange, @@ -331,7 +336,8 @@ std::shared_ptr DRC_ITEM::Create( int aErrorCode ) case DRCE_UNRESOLVED_VARIABLE: return std::make_shared( unresolvedVariable ); case DRCE_COPPER_SLIVER: return std::make_shared( copperSliver ); case DRCE_OVERLAPPING_SILK: return std::make_shared( silkOverlaps ); - case DRCE_SILK_MASK_CLEARANCE: return std::make_shared( silkMaskClearance ); + case DRCE_SILK_CLEARANCE: return std::make_shared( silkClearance ); + case DRCE_SOLDERMASK_BRIDGE: return std::make_shared( solderMaskBridge ); case DRCE_TEXT_HEIGHT: return std::make_shared( textHeightOutOfRange ); case DRCE_TEXT_THICKNESS: return std::make_shared( textThicknessOutOfRange ); case DRCE_LENGTH_OUT_OF_RANGE: return std::make_shared( lengthOutOfRange ); diff --git a/pcbnew/drc/drc_item.h b/pcbnew/drc/drc_item.h index 1da47d9220..1e5f15f296 100644 --- a/pcbnew/drc/drc_item.h +++ b/pcbnew/drc/drc_item.h @@ -75,8 +75,10 @@ enum PCB_DRC_CODE { DRCE_UNRESOLVED_VARIABLE, DRCE_COPPER_SLIVER, + DRCE_SOLDERMASK_BRIDGE, // failure to maintain min soldermask web thickness + // between copper items with different nets - DRCE_SILK_MASK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it + DRCE_SILK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it // over pads, exposed copper, etc.) DRCE_TEXT_HEIGHT, DRCE_TEXT_THICKNESS, @@ -176,7 +178,8 @@ private: static DRC_ITEM libFootprintIssues; static DRC_ITEM unresolvedVariable; static DRC_ITEM copperSliver; - static DRC_ITEM silkMaskClearance; + static DRC_ITEM silkClearance; + static DRC_ITEM solderMaskBridge; static DRC_ITEM silkOverlaps; static DRC_ITEM textHeightOutOfRange; static DRC_ITEM textThicknessOutOfRange; diff --git a/pcbnew/drc/drc_rtree.h b/pcbnew/drc/drc_rtree.h index c1b06d81a9..53e399fd81 100644 --- a/pcbnew/drc/drc_rtree.h +++ b/pcbnew/drc/drc_rtree.h @@ -87,13 +87,23 @@ public: */ void Insert( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aWorstClearance = 0 ) { - wxCHECK( aLayer != UNDEFINED_LAYER, /* void */ ); + Insert( aItem, aLayer, aLayer, aWorstClearance ); + } + + /** + * Insert an item into the tree on a particular layer with a worst clearance. Allows the + * source layer to be different from the tree layer. + */ + void Insert( BOARD_ITEM* aItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, + int aWorstClearance ) + { + wxCHECK( aTargetLayer != UNDEFINED_LAYER, /* void */ ); if( aItem->Type() == PCB_FP_TEXT_T && !static_cast( aItem )->IsVisible() ) return; std::vector subshapes; - std::shared_ptr shape = aItem->GetEffectiveShape( aLayer ); + std::shared_ptr shape = aItem->GetEffectiveShape( aRefLayer ); subshapes.clear(); if( shape->HasIndexableSubshapes() ) @@ -122,7 +132,7 @@ public: const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() }; ITEM_WITH_SHAPE* itemShape = new ITEM_WITH_SHAPE( aItem, subshape, shape ); - m_tree[aLayer]->Insert( mmin, mmax, itemShape ); + m_tree[aTargetLayer]->Insert( mmin, mmax, itemShape ); m_count++; } } @@ -194,8 +204,8 @@ public: EDA_RECT box = aRefItem->GetBoundingBox(); box.Inflate( aClearance ); - int min[2] = { box.GetX(), box.GetY() }; - int max[2] = { box.GetRight(), box.GetBottom() }; + int min[2] = { box.GetX(), box.GetY() }; + int max[2] = { box.GetRight(), box.GetBottom() }; std::shared_ptr refShape = aRefItem->GetEffectiveShape( aRefLayer ); diff --git a/pcbnew/drc/drc_test_provider_edge_clearance.cpp b/pcbnew/drc/drc_test_provider_edge_clearance.cpp index 7891d910ee..6a5c92f398 100644 --- a/pcbnew/drc/drc_test_provider_edge_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_edge_clearance.cpp @@ -121,7 +121,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run() if( !reportPhase( _( "Checking copper to board edge clearances..." ) ) ) return false; // DRC cancelled } - else if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) ) + else if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ) ) { if( !reportPhase( _( "Checking silk to board edge clearances..." ) ) ) return false; // DRC cancelled @@ -230,7 +230,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run() for( BOARD_ITEM* item : boardItems ) { bool testCopper = !m_drcEngine->IsErrorLimitExceeded( DRCE_EDGE_CLEARANCE ); - bool testSilk = !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ); + bool testSilk = !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ); if( !testCopper && !testSilk ) break; @@ -261,7 +261,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run() { return testAgainstEdge( item, itemShape.get(), edge, SILK_CLEARANCE_CONSTRAINT, - DRCE_SILK_MASK_CLEARANCE ); + DRCE_SILK_CLEARANCE ); }, m_largestClearance ) ) { diff --git a/pcbnew/drc/drc_test_provider_silk_to_mask.cpp b/pcbnew/drc/drc_test_provider_silk_to_mask.cpp deleted file mode 100644 index 82e97d284d..0000000000 --- a/pcbnew/drc/drc_test_provider_silk_to_mask.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2004-2020 KiCad Developers. - * - * 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 -#include -#include -#include - -#include - -/* - Silk to pads clearance test. Check all pads against silkscreen (mask opening in the pad vs silkscreen) - Errors generated: - - DRCE_SILK_MASK_CLEARANCE -*/ - -class DRC_TEST_PROVIDER_SILK_TO_MASK : public ::DRC_TEST_PROVIDER -{ -public: - DRC_TEST_PROVIDER_SILK_TO_MASK (): - m_board( nullptr ), - m_largestClearance( 0 ) - { - } - - virtual ~DRC_TEST_PROVIDER_SILK_TO_MASK() - { - } - - virtual bool Run() override; - - virtual const wxString GetName() const override - { - return "silk_to_mask"; - }; - - virtual const wxString GetDescription() const override - { - return "Tests for silkscreen being clipped by solder mask"; - } - - virtual std::set GetConstraintTypes() const override; - -private: - - BOARD* m_board; - int m_largestClearance; -}; - - -bool DRC_TEST_PROVIDER_SILK_TO_MASK::Run() -{ - m_board = m_drcEngine->GetBoard(); - - if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) ) - { - reportAux( "Silkscreen clipping violations ignored. Tests not run." ); - return true; // continue with other tests - } - - DRC_CONSTRAINT worstClearanceConstraint; - m_largestClearance = 0; - - if( m_drcEngine->QueryWorstConstraint( SILK_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) ) - m_largestClearance = worstClearanceConstraint.m_Value.Min(); - - reportAux( "Worst clearance : %d nm", m_largestClearance ); - - if( !reportPhase( _( "Checking silkscreen for potential soldermask clipping..." ) ) ) - return false; // DRC cancelled - - DRC_RTREE maskTree, silkTree; - - auto addMaskToTree = - [&maskTree]( BOARD_ITEM *item ) -> bool - { - for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) - { - if( item->IsOnLayer( layer ) ) - maskTree.Insert( item, layer ); - } - - return true; - }; - - auto addSilkToTree = - [&silkTree]( BOARD_ITEM *item ) -> bool - { - for( PCB_LAYER_ID layer : { F_SilkS, B_SilkS } ) - { - if( item->IsOnLayer( layer ) ) - silkTree.Insert( item, layer ); - } - - return true; - }; - - auto checkClearance = - [&]( const DRC_RTREE::LAYER_PAIR& aLayers, DRC_RTREE::ITEM_WITH_SHAPE* aRefItem, - DRC_RTREE::ITEM_WITH_SHAPE* aTestItem, bool* aCollisionDetected ) -> bool - { - if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) ) - return false; - - if( isInvisibleText( aRefItem->parent ) ) - return true; - - if( isInvisibleText( aTestItem->parent ) ) - return true; - - auto constraint = m_drcEngine->EvalRules( SILK_CLEARANCE_CONSTRAINT, - aRefItem->parent, aTestItem->parent, - aLayers.first ); - - int minClearance = constraint.GetValue().Min(); - - if( minClearance < 0 ) - return true; - - int actual; - VECTOR2I pos; - - if( aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) ) - { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_SILK_MASK_CLEARANCE ); - - if( minClearance > 0 ) - { - m_msg.Printf( _( "(%s clearance %s; actual %s)" ), - constraint.GetName(), - MessageTextFromValue( userUnits(), minClearance ), - MessageTextFromValue( userUnits(), actual ) ); - - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); - } - - drce->SetItems( aRefItem->parent, aTestItem->parent ); - drce->SetViolatingRule( constraint.GetParentRule() ); - - reportViolation( drce, (wxPoint) pos ); - - *aCollisionDetected = true; - } - - return true; - }; - - int numMask = forEachGeometryItem( s_allBasicItems, LSET( 2, F_Mask, B_Mask ), addMaskToTree ); - int numSilk = forEachGeometryItem( s_allBasicItems, LSET( 2, F_SilkS, B_SilkS ), addSilkToTree ); - - reportAux( _("Testing %d mask apertures against %d silkscreen features."), numMask, numSilk ); - - const std::vector layerPairs = - { - DRC_RTREE::LAYER_PAIR( F_SilkS, F_Mask ), - DRC_RTREE::LAYER_PAIR( B_SilkS, B_Mask ) - }; - - // This is the number of tests between 2 calls to the progress bar - const int delta = 250; - - maskTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance, - [&]( int aCount, int aSize ) -> bool - { - return reportProgress( aCount, aSize, delta ); - } ); - - reportRuleStatistics(); - - return true; -} - - -std::set DRC_TEST_PROVIDER_SILK_TO_MASK::GetConstraintTypes() const -{ - return { SILK_CLEARANCE_CONSTRAINT }; -} - - -namespace detail -{ - static DRC_REGISTER_TEST_PROVIDER dummy; -} diff --git a/pcbnew/drc/drc_test_provider_solder_mask.cpp b/pcbnew/drc/drc_test_provider_solder_mask.cpp new file mode 100644 index 0000000000..6a51968306 --- /dev/null +++ b/pcbnew/drc/drc_test_provider_solder_mask.cpp @@ -0,0 +1,572 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2020 KiCad Developers. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + Solder mask tests. Checks for silkscreen which is clipped by mask openings and for bridges + between mask apertures with different nets. + Errors generated: + - DRCE_SILK_CLEARANCE + - DRCE_SOLDERMASK_BRIDGE +*/ + +class DRC_TEST_PROVIDER_SOLDER_MASK : public ::DRC_TEST_PROVIDER +{ +public: + DRC_TEST_PROVIDER_SOLDER_MASK (): + m_largestClearance( 0 ) + { + m_bridgeRule.m_Name = _( "board setup solder mask min width" ); + } + + virtual ~DRC_TEST_PROVIDER_SOLDER_MASK() + { + } + + virtual bool Run() override; + + virtual const wxString GetName() const override + { + return "solder_mask_issues"; + }; + + virtual const wxString GetDescription() const override + { + return "Tests for silkscreen being clipped by solder mask and copper being exposed by " + "mask apertures of other nets"; + } + + virtual std::set GetConstraintTypes() const override; + +private: + void addItemToRTrees( BOARD_ITEM* item ); + void buildRTrees(); + + void testSilkToMaskClearance(); + void testMaskBridges(); + + void testItemAgainstItems( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, + PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer ); + void testMaskItemAgainstZones( BOARD_ITEM* item, const EDA_RECT& itemBBox, + PCB_LAYER_ID refLayer, PCB_LAYER_ID targetLayer ); + +private: + DRC_RULE m_bridgeRule; + + BOARD* m_board; + int m_webWidth; + int m_maxError; + int m_largestClearance; + + std::unique_ptr m_tesselatedTree; + std::unique_ptr m_itemTree; + std::vector m_copperZones; + + std::map< std::tuple, int> m_checkedPairs; +}; + + +void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item ) +{ + ZONE* solderMask = m_board->m_SolderMask; + + if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) + { + ZONE* zone = static_cast( item ); + + for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) + { + if( zone->IsOnLayer( layer ) ) + { + solderMask->GetFill( layer )->BooleanAdd( zone->GetFilledPolysList( layer ), + SHAPE_POLY_SET::PM_FAST ); + } + } + + if( ( zone->GetLayerSet() & LSET::AllCuMask() ).any() && !zone->GetIsRuleArea() ) + m_copperZones.push_back( zone ); + } + else if( item->Type() == PCB_PAD_T ) + { + for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) + { + if( item->IsOnLayer( layer ) ) + { + PAD* pad = static_cast( item ); + int clearance = ( m_webWidth / 2 ) + pad->GetSolderMaskMargin(); + + item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), F_Cu, + clearance, m_maxError, ERROR_OUTSIDE ); + + m_itemTree->Insert( item, layer, m_largestClearance ); + } + } + } + else if( item->Type() == PCB_VIA_T ) + { + // JEY TODO: if( !aPlotOpt.GetPlotViaOnMaskLayer() + // continue; + + // Use the global mask clearance for vias + int clearance = ( m_webWidth / 2 ) + m_board->GetDesignSettings().m_SolderMaskMargin; + + if( item->IsOnLayer( F_Cu ) ) + { + item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( F_Mask ), F_Cu, + clearance, m_maxError, ERROR_OUTSIDE ); + + m_itemTree->Insert( item, F_Mask, F_Cu, m_largestClearance ); + } + + if( item->IsOnLayer( B_Cu ) ) + { + item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( B_Mask ), B_Cu, + clearance, m_maxError, ERROR_OUTSIDE ); + + m_itemTree->Insert( item, B_Mask, B_Cu, m_largestClearance ); + } + } + else + { + // JEY TODO: plotter doesn't currently expand graphics by web thickness... + + for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) + { + if( item->IsOnLayer( layer ) ) + { + item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), + layer, m_webWidth / 2, m_maxError, + ERROR_OUTSIDE ); + + m_itemTree->Insert( item, layer, m_largestClearance ); + } + } + } +} + + +void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees() +{ + ZONE* solderMask = m_board->m_SolderMask; + LSET layers = { 4, F_Mask, B_Mask, F_Cu, B_Cu }; + + size_t delta = 50; // Number of tests between 2 calls to the progress bar + int itemCount = 0; + int itemIdx = 0; + + solderMask->GetFill( F_Mask )->RemoveAllContours(); + solderMask->GetFill( B_Mask )->RemoveAllContours(); + + m_tesselatedTree = std::make_unique(); + m_itemTree = std::make_unique(); + m_copperZones.clear(); + + forEachGeometryItem( s_allBasicItems, layers, + [&]( BOARD_ITEM* item ) -> bool + { + ++itemCount; + return true; + } ); + + forEachGeometryItem( s_allBasicItems, layers, + [&]( BOARD_ITEM* item ) -> bool + { + if( !reportProgress( itemIdx++, itemCount, delta ) ) + return false; + + addItemToRTrees( item ); + return true; + } ); + + solderMask->GetFill( F_Mask )->Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + solderMask->GetFill( B_Mask )->Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + + int numSegs = GetArcToSegmentCount( m_webWidth / 2, m_maxError, 360.0 ); + + solderMask->GetFill( F_Mask )->Deflate( m_webWidth / 2, numSegs ); + solderMask->GetFill( B_Mask )->Deflate( m_webWidth / 2, numSegs ); + + solderMask->SetFillFlag( F_Mask, true ); + solderMask->SetFillFlag( B_Mask, true ); + solderMask->SetIsFilled( true ); + + solderMask->CacheTriangulation(); + + m_tesselatedTree->Insert( solderMask, F_Mask ); + m_tesselatedTree->Insert( solderMask, B_Mask ); + + m_checkedPairs.clear(); +} + + +void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance() +{ + LSET silkLayers = { 2, F_SilkS, B_SilkS }; + + size_t delta = 100; // Number of tests between 2 calls to the progress bar + int itemCount = 0; + int itemIdx = 0; + + forEachGeometryItem( s_allBasicItems, silkLayers, + [&]( BOARD_ITEM* item ) -> bool + { + ++itemCount; + return true; + } ); + + forEachGeometryItem( s_allBasicItems, silkLayers, + [&]( BOARD_ITEM* item ) -> bool + { + if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ) ) + return false; + + if( !reportProgress( itemIdx++, itemCount, delta ) ) + return false; + + if( isInvisibleText( item ) ) + return true; + + for( PCB_LAYER_ID layer : silkLayers.Seq() ) + { + if( !item->IsOnLayer( layer ) ) + continue; + + EDA_RECT itemBBox = item->GetBoundingBox(); + DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( SILK_CLEARANCE_CONSTRAINT, + item, nullptr, layer ); + int clearance = constraint.GetValue().Min(); + int actual; + VECTOR2I pos; + + if( clearance <= 0 ) + return true; + + std::shared_ptr itemShape = item->GetEffectiveShape( layer ); + + if( m_tesselatedTree->QueryColliding( itemBBox, itemShape.get(), layer, + clearance, &actual, &pos ) ) + { + auto drce = DRC_ITEM::Create( DRCE_SILK_CLEARANCE ); + + m_msg.Printf( _( "(%s clearance %s; actual %s)" ), + constraint.GetName(), + MessageTextFromValue( userUnits(), clearance ), + MessageTextFromValue( userUnits(), actual ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( item ); + drce->SetViolatingRule( constraint.GetParentRule() ); + + reportViolation( drce, (wxPoint) pos ); + } + } + + return true; + } ); +} + + +void DRC_TEST_PROVIDER_SOLDER_MASK::testItemAgainstItems( BOARD_ITEM* aItem, + const EDA_RECT& aItemBBox, + PCB_LAYER_ID aRefLayer, + PCB_LAYER_ID aTargetLayer ) +{ + int itemNet = -1; + + if( aItem->IsConnected() ) + itemNet = static_cast( aItem )->GetNetCode(); + + PAD* pad = dynamic_cast( aItem ); + std::shared_ptr itemShape = aItem->GetEffectiveShape( aRefLayer ); + + m_itemTree->QueryColliding( aItem, aRefLayer, aTargetLayer, + // Filter: + [&]( BOARD_ITEM* other ) -> bool + { + PAD* otherPad = dynamic_cast( other ); + int otherNet = -1; + + if( other->IsConnected() ) + otherNet = static_cast( other )->GetNetCode(); + + if( otherNet > 0 && otherNet == itemNet ) + return false; + + if( pad && otherPad && pad->GetParent() == otherPad->GetParent() ) + { + if( pad->GetParent()->GetAttributes() & FP_ALLOW_SOLDERMASK_BRIDGES ) + return false; + else if( pad->SameLogicalPadAs( otherPad ) ) + return false; + } + + BOARD_ITEM* a = aItem; + BOARD_ITEM* b = other; + + // store canonical order so we don't collide in both directions + // (a:b and b:a) + if( static_cast( a ) > static_cast( b ) ) + std::swap( a, b ); + + if( m_checkedPairs.count( { a, b, aTargetLayer } ) ) + { + return false; + } + else + { + m_checkedPairs[ { a, b, aTargetLayer } ] = 1; + return true; + // return aItemBBox.Intersects( other->GetBoundingBox() ); + } + }, + // Visitor: + [&]( BOARD_ITEM* other ) -> bool + { + PAD* otherPad = dynamic_cast( other ); + auto otherShape = other->GetEffectiveShape( aTargetLayer ); + int actual; + VECTOR2I pos; + int clearance = 0; // JEY TODO: probably need a board setting for mask registration? + + if( pad ) + clearance += m_webWidth / 2 + pad->GetSolderMaskMargin(); + + if( otherPad ) + clearance += m_webWidth / 2 + otherPad->GetSolderMaskMargin(); + + if( itemShape->Collide( otherShape.get(), clearance, &actual, &pos ) ) + { + auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE ); + + if( aTargetLayer == F_Mask ) + { + drce->SetErrorMessage( _( "Front solder mask aperture bridges items with " + "different nets" ) ); + } + else + { + drce->SetErrorMessage( _( "Rear solder mask aperture bridges items with " + "different nets" ) ); + } + + drce->SetItems( aItem, other ); + drce->SetViolatingRule( &m_bridgeRule ); + reportViolation( drce, (wxPoint) pos ); + } + + return true; + }, + m_largestClearance ); +} + + +void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem, + const EDA_RECT& aItemBBox, + PCB_LAYER_ID aMaskLayer, + PCB_LAYER_ID aTargetLayer ) +{ + for( ZONE* zone : m_copperZones ) + { + if( !zone->GetLayerSet().test( aTargetLayer ) ) + continue; + + if( zone->GetNetCode() && aItem->IsConnected() ) + { + if( zone->GetNetCode() == static_cast( aItem )->GetNetCode() ) + continue; + } + + if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) ) + { + DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get(); + int clearance = 0; // JEY TODO: probably need a board setting for mask registration? + int actual; + VECTOR2I pos; + + std::shared_ptr itemShape = aItem->GetEffectiveShape( aMaskLayer ); + + if( aItem->Type() == PCB_PAD_T ) + { + PAD* pad = static_cast( aItem ); + + clearance += pad->GetSolderMaskMargin(); + } + + if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer, + clearance, &actual, &pos ) ) + { + auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE ); + + if( aMaskLayer == F_Mask ) + { + drce->SetErrorMessage( _( "Front solder mask aperture bridges items with " + "different nets" ) ); + } + else + { + drce->SetErrorMessage( _( "Rear solder mask aperture bridges items with " + "different nets" ) ); + } + + drce->SetItems( aItem, zone ); + drce->SetViolatingRule( &m_bridgeRule ); + reportViolation( drce, (wxPoint) pos ); + } + } + } +} + + +void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskBridges() +{ + LSET copperAndMaskLayers = { 4, F_Mask, B_Mask, F_Cu, B_Cu }; + + size_t delta = 50; // Number of tests between 2 calls to the progress bar + int itemCount = 0; + int itemIdx = 0; + + forEachGeometryItem( s_allBasicItemsButZones, copperAndMaskLayers, + [&]( BOARD_ITEM* item ) -> bool + { + ++itemCount; + return true; + } ); + + forEachGeometryItem( s_allBasicItemsButZones, copperAndMaskLayers, + [&]( BOARD_ITEM* item ) -> bool + { + if( m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) ) + return false; + + if( !reportProgress( itemIdx++, itemCount, delta ) ) + return false; + + EDA_RECT itemBBox = item->GetBoundingBox(); + + if( item->IsOnLayer( F_Mask ) ) + { + // Test for aperture-to-aperture collisions + testItemAgainstItems( item, itemBBox, F_Mask, F_Mask ); + + // Test for aperture-to-zone collisions + testMaskItemAgainstZones( item, itemBBox, F_Mask, F_Cu ); + } + else if( item->IsOnLayer( F_Cu ) ) + { + // Test for copper-item-to-aperture collisions + testItemAgainstItems( item, itemBBox, F_Cu, F_Mask ); + } + + if( item->IsOnLayer( B_Mask ) ) + { + // Test for aperture-to-aperture collisions + testItemAgainstItems( item, itemBBox, B_Mask, B_Mask ); + + // Test for aperture-to-zone collisions + testMaskItemAgainstZones( item, itemBBox, B_Mask, B_Cu ); + } + else if( item->IsOnLayer( B_Cu ) ) + { + // Test for copper-item-to-aperture collisions + testItemAgainstItems( item, itemBBox, B_Cu, B_Mask ); + } + + return true; + } ); +} + + +bool DRC_TEST_PROVIDER_SOLDER_MASK::Run() +{ + if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ) + && m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) ) + { + reportAux( "Solder mask violations ignored. Tests not run." ); + return true; // continue with other tests + } + + m_board = m_drcEngine->GetBoard(); + m_webWidth = m_board->GetDesignSettings().m_SolderMaskMinWidth; + m_maxError = m_board->GetDesignSettings().m_MaxError; + m_largestClearance = 0; + + for( FOOTPRINT* footprint : m_board->Footprints() ) + { + for( PAD* pad : footprint->Pads() ) + m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskMargin() ); + } + + // Order is important here: m_webWidth must be added in before m_largestClearance is maxed + // with the various SILK_CLEARANCE_CONSTRAINTS. + m_largestClearance += m_largestClearance + m_webWidth; + + DRC_CONSTRAINT worstClearanceConstraint; + + if( m_drcEngine->QueryWorstConstraint( SILK_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) ) + m_largestClearance = std::max( m_largestClearance, worstClearanceConstraint.m_Value.Min() ); + + reportAux( "Worst clearance : %d nm", m_largestClearance ); + + if( !reportPhase( _( "Building solder mask..." ) ) ) + return false; // DRC cancelled + + buildRTrees(); + + if( !reportPhase( _( "Checking solder mask to silk clearance..." ) ) ) + return false; // DRC cancelled + + testSilkToMaskClearance(); + + if( !reportPhase( _( "Checking solder mask web integrity..." ) ) ) + return false; // DRC cancelled + + testMaskBridges(); + + reportRuleStatistics(); + + return true; +} + + +std::set DRC_TEST_PROVIDER_SOLDER_MASK::GetConstraintTypes() const +{ + return { SILK_CLEARANCE_CONSTRAINT }; +} + + +namespace detail +{ + static DRC_REGISTER_TEST_PROVIDER dummy; +} diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index 83d190b149..76e540049f 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -65,12 +65,13 @@ enum INCLUDE_NPTH_T */ enum FOOTPRINT_ATTR_T { - FP_THROUGH_HOLE = 0x0001, - FP_SMD = 0x0002, - FP_EXCLUDE_FROM_POS_FILES = 0x0004, - FP_EXCLUDE_FROM_BOM = 0x0008, - FP_BOARD_ONLY = 0x0010, // Footprint has no corresponding symbol - FP_JUST_ADDED = 0x0020 // Footprint just added by netlist update + FP_THROUGH_HOLE = 0x0001, + FP_SMD = 0x0002, + FP_EXCLUDE_FROM_POS_FILES = 0x0004, + FP_EXCLUDE_FROM_BOM = 0x0008, + FP_BOARD_ONLY = 0x0010, // Footprint has no corresponding symbol + FP_JUST_ADDED = 0x0020, // Footprint just added by netlist update + FP_ALLOW_SOLDERMASK_BRIDGES = 0x0040 }; class FP_3DMODEL diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp index 5f8d6be503..41747e9512 100644 --- a/pcbnew/pcb_base_frame.cpp +++ b/pcbnew/pcb_base_frame.cpp @@ -388,6 +388,29 @@ void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ) } +void PCB_BASE_FRAME::HideSolderMask() +{ + KIGFX::PCB_VIEW* view = GetCanvas()->GetView(); + + if( view && GetBoard()->m_SolderMask && view->HasItem( GetBoard()->m_SolderMask ) ) + view->Remove( GetBoard()->m_SolderMask ); +} + + +void PCB_BASE_FRAME::ShowSolderMask() +{ + KIGFX::PCB_VIEW* view = GetCanvas()->GetView(); + + if( view && GetBoard()->m_SolderMask ) + { + if( view->HasItem( GetBoard()->m_SolderMask ) ) + view->Remove( GetBoard()->m_SolderMask ); + + view->Add( GetBoard()->m_SolderMask ); + } +} + + void PCB_BASE_FRAME::SetPageSettings( const PAGE_INFO& aPageSettings ) { m_pcb->SetPageSettings( aPageSettings ); diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp index a28364b5fb..65e8812081 100644 --- a/pcbnew/plugins/kicad/pcb_parser.cpp +++ b/pcbnew/plugins/kicad/pcb_parser.cpp @@ -3437,9 +3437,13 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments attributes |= FP_EXCLUDE_FROM_BOM; break; + case T_allow_soldermask_bridges: + attributes |= FP_ALLOW_SOLDERMASK_BRIDGES; + break; + default: - Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files " - "or exclude_from_bom" ); + Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files, " + "exclude_from_bom or allow_solder_mask_bridges" ); } } diff --git a/pcbnew/plugins/kicad/pcb_plugin.cpp b/pcbnew/plugins/kicad/pcb_plugin.cpp index e309e309ef..9e31be1810 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.cpp +++ b/pcbnew/plugins/kicad/pcb_plugin.cpp @@ -1216,6 +1216,9 @@ void PCB_PLUGIN::format( const FOOTPRINT* aFootprint, int aNestLevel ) const if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) m_out->Print( 0, " exclude_from_bom" ); + if( aFootprint->GetAttributes() & FP_ALLOW_SOLDERMASK_BRIDGES ) + m_out->Print( 0, " allow_soldermask_bridges" ); + m_out->Print( 0, ")\n" ); } diff --git a/pcbnew/plugins/kicad/pcb_plugin.h b/pcbnew/plugins/kicad/pcb_plugin.h index 545849f9a6..ff2f782858 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.h +++ b/pcbnew/plugins/kicad/pcb_plugin.h @@ -104,7 +104,8 @@ class PCB_TEXT; //#define SEXPR_BOARD_FILE_VERSION 20210925 // Locked flag for fp_text //#define SEXPR_BOARD_FILE_VERSION 20211014 // Arc formatting //#define SEXPR_BOARD_FILE_VERSION 20211226 // Add radial dimension -#define SEXPR_BOARD_FILE_VERSION 20211227 // Add thermal relief spoke angle overrides +//#define SEXPR_BOARD_FILE_VERSION 20211227 // Add thermal relief spoke angle overrides +#define SEXPR_BOARD_FILE_VERSION 20211228 // Add allow_soldermask_bridges footprint attribute #define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag #define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting diff --git a/pcbnew/tools/drc_tool.cpp b/pcbnew/tools/drc_tool.cpp index 262762af7e..30b05fb89f 100644 --- a/pcbnew/tools/drc_tool.cpp +++ b/pcbnew/tools/drc_tool.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -209,6 +210,8 @@ void DRC_TOOL::RunTests( PROGRESS_REPORTER* aProgressReporter, bool aRefillZones m_drcRunning = false; + m_editFrame->ShowSolderMask(); + // update the m_drcDialog listboxes updatePointers(); } @@ -221,7 +224,7 @@ void DRC_TOOL::updatePointers() m_editFrame->ResolveDRCExclusions(); - if( m_drcDialog ) // Use diag list boxes only in DRC_TOOL dialog + if( m_drcDialog ) // Use dialog list boxes only in DRC_TOOL dialog { m_drcDialog->SetMarkersProvider( new BOARD_DRC_ITEMS_PROVIDER( m_pcb ) ); m_drcDialog->SetUnconnectedProvider( new RATSNEST_DRC_ITEMS_PROVIDER( m_editFrame, diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index 9f6d5eaf74..4c443edd1e 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -872,6 +872,8 @@ int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference ) break; } + editFrame->HideSolderMask(); + m_dragging = true; // When editing footprints, all items have the same parent diff --git a/pcbnew/zone.h b/pcbnew/zone.h index 36523d1684..81408e1aa9 100644 --- a/pcbnew/zone.h +++ b/pcbnew/zone.h @@ -636,6 +636,12 @@ public: return m_FilledPolysList.at( aLayer ); } + SHAPE_POLY_SET* GetFill( PCB_LAYER_ID aLayer ) + { + wxASSERT( m_FilledPolysList.count( aLayer ) ); + return &m_FilledPolysList.at( aLayer ); + } + /** * Create a list of triangles that "fill" the solid areas used for instance to draw * these solid areas on OpenGL. diff --git a/qa/data/issue1358.kicad_pcb b/qa/data/issue1358.kicad_pcb index bc9854df1f..d1c1804e6a 100644 --- a/qa/data/issue1358.kicad_pcb +++ b/qa/data/issue1358.kicad_pcb @@ -91,7 +91,7 @@ (descr "Micro USB Type B Receptacle") (tags "USB USB_B USB_micro USB_OTG") (path "/00000000-0000-0000-0000-00005bc9014b") - (attr smd) + (attr smd allow_soldermask_bridges) (fp_text reference "J1" (at 0 -3.45 -90) (layer "F.SilkS") (effects (font (size 1 1) (thickness 0.15))) (tstamp 19142c96-cd97-45b3-8b34-185d93a736d4) @@ -132,7 +132,7 @@ (tags "QFN 0.5") (path "/00000000-0000-0000-0000-00005bc741fc") (clearance 0.199898) - (attr smd) + (attr smd allow_soldermask_bridges) (fp_text reference "IC1" (at 0 -2.85) (layer "F.SilkS") (effects (font (size 1 1) (thickness 0.15))) (tstamp 919cfb2a-4b33-4eaa-9fcf-f71c2db9ec0d) diff --git a/qa/data/issue3812.kicad_pro b/qa/data/issue3812.kicad_pro index df0303478a..bc02db0dbb 100644 --- a/qa/data/issue3812.kicad_pro +++ b/qa/data/issue3812.kicad_pro @@ -63,6 +63,7 @@ "annular_width": "error", "clearance": "error", "copper_edge_clearance": "error", + "copper_sliver": "warning", "courtyards_overlap": "error", "diff_pair_gap_out_of_range": "error", "diff_pair_uncoupled_length_too_long": "error", @@ -75,6 +76,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "length_out_of_range": "error", + "lib_footprint_issues": "error", "malformed_courtyard": "error", "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", @@ -87,6 +89,10 @@ "silk_over_copper": "error", "silk_overlap": "error", "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", @@ -109,11 +115,15 @@ "min_hole_to_hole": 0.25, "min_microvia_diameter": 0.19999999999999998, "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.12, "min_through_hole_diameter": 0.39999999999999997, "min_track_width": 0.19812, "min_via_annular_width": 0.049999999999999996, - "min_via_diameter": 0.5 + "min_via_diameter": 0.5, + "use_height_for_length_calcs": true }, "track_widths": [ 0.0, @@ -344,7 +354,7 @@ "pinned_symbol_libs": [] }, "meta": { - "filename": "kit-dev-coldfire-xilinx_5213.kicad_pro", + "filename": "issue3812.kicad_pro", "version": 1 }, "net_settings": { @@ -390,7 +400,7 @@ } ], "meta": { - "version": 0 + "version": 1 }, "net_colors": null }, diff --git a/qa/data/issue5320.kicad_pro b/qa/data/issue5320.kicad_pro index 225eb4d514..2202699967 100755 --- a/qa/data/issue5320.kicad_pro +++ b/qa/data/issue5320.kicad_pro @@ -10,8 +10,16 @@ "copper_text_thickness": 0.3, "copper_text_upright": true, "courtyard_line_width": 0.049999999999999996, - "dimension_precision": 1, + "dimension_precision": 4, "dimension_units": 0, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": false, + "text_position": 0, + "units_format": 1 + }, "fab_line_width": 0.09999999999999999, "fab_text_italic": false, "fab_text_size_h": 1.0, @@ -49,38 +57,49 @@ ], "drc_exclusions": [], "meta": { - "version": 0 + "version": 2 }, "rule_severities": { + "annular_width": "error", "clearance": "error", "copper_edge_clearance": "error", + "copper_sliver": "warning", "courtyards_overlap": "error", - "drill_too_small": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", "duplicate_footprints": "warning", "extra_footprint": "warning", + "hole_clearance": "error", "hole_near_hole": "error", "invalid_outline": "error", "item_on_disabled_layer": "error", "items_not_allowed": "error", - "keepout": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "error", "malformed_courtyard": "error", - "microvia_drill_too_small": "error", - "microvia_too_small": "error", + "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", "missing_footprint": "warning", + "net_conflict": "warning", "npth_inside_courtyard": "ignore", "padstack": "error", "pth_inside_courtyard": "ignore", "shorting_items": "error", + "silk_over_copper": "warning", + "silk_overlap": "warning", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", + "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", "tracks_crossing": "error", "unconnected_items": "error", "unresolved_variable": "error", - "via_annulus": "error", "via_dangling": "warning", - "via_hole_larger_than_pad": "error", - "via_too_small": "error", "zone_has_empty_net": "error", "zones_intersect": "error" }, @@ -90,17 +109,20 @@ "max_error": 0.005, "min_clearance": 0.127, "min_copper_edge_clearance": 0.254, + "min_hole_clearance": 0.0, "min_hole_to_hole": 0.254, "min_microvia_diameter": 0.127, "min_microvia_drill": 0.07619999999999999, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.12, "min_through_hole_diameter": 0.254, "min_track_width": 0.127, + "min_via_annular_width": 0.049999999999999996, "min_via_annulus": 0.127, "min_via_diameter": 0.254, - "solder_mask_clearance": 0.0508, - "solder_mask_min_width": 0.1016, - "solder_paste_clearance": 0.0, - "solder_paste_margin_ratio": -0.0 + "use_height_for_length_calcs": true }, "track_widths": [ 0.127, @@ -135,6 +157,7 @@ "drill": 0.635 } ], + "zones_allow_external_fillets": false, "zones_use_no_outline": true }, "layer_presets": [] @@ -319,7 +342,7 @@ "pinned_symbol_libs": [] }, "meta": { - "filename": "VHF_Module.kicad_pro", + "filename": "issue5320.kicad_pro", "version": 1 }, "net_settings": { @@ -334,6 +357,8 @@ "microvia_diameter": 0.127, "microvia_drill": 0.762, "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "schematic_color": "rgba(0, 0, 0, 0.000)", "track_width": 0.127, "via_diameter": 0.508, "via_drill": 0.254, @@ -342,7 +367,7 @@ ], "hidden_nets": [], "meta": { - "version": 0 + "version": 1 }, "net_colors": null }, @@ -353,7 +378,8 @@ "netlist": "DC_DC_UAV_Converter.net", "specctra_dsn": "", "step": "../../../../Mechanical Design/JVAB/VHF Module/Rev 1.0A/VHF_Module_PCB.step", - "vmrl": "" + "vmrl": "", + "vrml": "" }, "page_layout_descr_file": "" }, diff --git a/qa/data/issue5567.kicad_pro b/qa/data/issue5567.kicad_pro index 83ce6ff70b..19cdecd6ec 100644 --- a/qa/data/issue5567.kicad_pro +++ b/qa/data/issue5567.kicad_pro @@ -10,8 +10,16 @@ "copper_text_thickness": 0.3, "copper_text_upright": false, "courtyard_line_width": 0.049999999999999996, - "dimension_precision": 1, + "dimension_precision": 4, "dimension_units": 0, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": false, + "text_position": 0, + "units_format": 1 + }, "fab_line_width": 0.09999999999999999, "fab_text_italic": false, "fab_text_size_h": 1.0, @@ -49,38 +57,49 @@ ], "drc_exclusions": [], "meta": { - "version": 0 + "version": 2 }, "rule_severities": { + "annular_width": "error", "clearance": "error", "copper_edge_clearance": "error", + "copper_sliver": "warning", "courtyards_overlap": "error", - "drill_too_small": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", "duplicate_footprints": "warning", "extra_footprint": "warning", + "hole_clearance": "error", "hole_near_hole": "error", "invalid_outline": "error", "item_on_disabled_layer": "error", "items_not_allowed": "error", - "keepout": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "error", "malformed_courtyard": "error", - "microvia_drill_too_small": "error", - "microvia_too_small": "error", + "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", "missing_footprint": "warning", + "net_conflict": "warning", "npth_inside_courtyard": "ignore", "padstack": "error", "pth_inside_courtyard": "ignore", "shorting_items": "error", + "silk_over_copper": "warning", + "silk_overlap": "warning", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", + "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", "tracks_crossing": "error", "unconnected_items": "error", "unresolved_variable": "error", - "via_annulus": "error", "via_dangling": "warning", - "via_hole_larger_than_pad": "error", - "via_too_small": "error", "zone_has_empty_net": "error", "zones_intersect": "error" }, @@ -90,17 +109,20 @@ "max_error": 0.005, "min_clearance": 0.0, "min_copper_edge_clearance": 0.0, + "min_hole_clearance": 0.0, "min_hole_to_hole": 0.25, "min_microvia_diameter": 0.19999999999999998, "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.12, "min_through_hole_diameter": 0.3, "min_track_width": 0.19999999999999998, + "min_via_annular_width": 0.049999999999999996, "min_via_annulus": 0.049999999999999996, "min_via_diameter": 0.39999999999999997, - "solder_mask_clearance": 0.0, - "solder_mask_min_width": 0.0, - "solder_paste_clearance": 0.0, - "solder_paste_margin_ratio": 0.0 + "use_height_for_length_calcs": true }, "track_widths": [ 0.25 @@ -297,7 +319,7 @@ "pinned_symbol_libs": [] }, "meta": { - "filename": "test.kicad_pro", + "filename": "issue5567.kicad_pro", "version": 1 }, "net_settings": { @@ -321,7 +343,7 @@ } ], "meta": { - "version": 0 + "version": 1 }, "net_colors": null }, @@ -332,7 +354,8 @@ "netlist": "", "specctra_dsn": "", "step": "", - "vmrl": "" + "vmrl": "", + "vrml": "" }, "page_layout_descr_file": "" }, diff --git a/qa/data/issue5990.kicad_pro b/qa/data/issue5990.kicad_pro index 86c625ec89..3619c3af06 100755 --- a/qa/data/issue5990.kicad_pro +++ b/qa/data/issue5990.kicad_pro @@ -89,6 +89,7 @@ "silk_over_copper": "error", "silk_overlap": "error", "skew_out_of_range": "error", + "solder_mask_bridge": "ignore", "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", @@ -347,7 +348,7 @@ "pinned_symbol_libs": [] }, "meta": { - "filename": "001.04.010.1020.kicad_pro", + "filename": "issue5990.kicad_pro", "version": 1 }, "net_settings": { diff --git a/qa/data/issue6260.kicad_pro b/qa/data/issue6260.kicad_pro index a081ca0011..6e532beaa8 100644 --- a/qa/data/issue6260.kicad_pro +++ b/qa/data/issue6260.kicad_pro @@ -57,16 +57,17 @@ ], "drc_exclusions": [], "meta": { - "version": 1 + "version": 2 }, "rule_severities": { "annular_width": "error", "clearance": "error", "copper_edge_clearance": "error", + "copper_sliver": "warning", "courtyards_overlap": "error", "diff_pair_gap_out_of_range": "error", "diff_pair_uncoupled_length_too_long": "error", - "drill_too_small": "error", + "drill_out_of_range": "error", "duplicate_footprints": "warning", "extra_footprint": "warning", "hole_clearance": "error", @@ -74,10 +75,10 @@ "invalid_outline": "error", "item_on_disabled_layer": "error", "items_not_allowed": "error", - "keepout": "error", "length_out_of_range": "error", + "lib_footprint_issues": "error", "malformed_courtyard": "error", - "microvia_drill_too_small": "error", + "microvia_drill_out_of_range": "error", "missing_courtyard": "ignore", "missing_footprint": "warning", "net_conflict": "warning", @@ -88,6 +89,10 @@ "silk_over_copper": "error", "silk_overlap": "error", "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", @@ -95,7 +100,6 @@ "unconnected_items": "error", "unresolved_variable": "error", "via_dangling": "warning", - "via_hole_larger_than_pad": "error", "zone_has_empty_net": "error", "zones_intersect": "error" }, @@ -105,19 +109,20 @@ "max_error": 0.005, "min_clearance": 0.0, "min_copper_edge_clearance": 0.01, + "min_hole_clearance": 0.0, "min_hole_to_hole": 0.25, "min_microvia_diameter": 0.19999999999999998, "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.12, "min_through_hole_diameter": 0.3, "min_track_width": 0.19999999999999998, "min_via_annular_width": 0.049999999999999996, "min_via_annulus": 0.049999999999999996, "min_via_diameter": 0.39999999999999997, - "solder_mask_clearance": 0.0, - "solder_mask_min_width": 0.0, - "solder_paste_clearance": 0.0, - "solder_paste_margin_ratio": -0.0 + "use_height_for_length_calcs": true }, "track_widths": [ 0.25, @@ -319,7 +324,7 @@ "pinned_symbol_libs": [] }, "meta": { - "filename": "moas_ii_adapter.kicad_pro", + "filename": "issue6260.kicad_pro", "version": 1 }, "net_settings": { @@ -343,7 +348,7 @@ } ], "meta": { - "version": 0 + "version": 1 }, "net_colors": null }, diff --git a/qa/data/issue7325.kicad_pro b/qa/data/issue7325.kicad_pro index 2afa7909a5..5ca784fe97 100755 --- a/qa/data/issue7325.kicad_pro +++ b/qa/data/issue7325.kicad_pro @@ -63,6 +63,7 @@ "annular_width": "error", "clearance": "error", "copper_edge_clearance": "error", + "copper_sliver": "warning", "courtyards_overlap": "error", "diff_pair_gap_out_of_range": "error", "diff_pair_uncoupled_length_too_long": "error", @@ -89,6 +90,7 @@ "silk_over_copper": "ignore", "silk_overlap": "warning", "skew_out_of_range": "error", + "solder_mask_bridge": "ignore", "starved_thermal": "error", "text_height": "warning", "text_thickness": "warning", @@ -107,6 +109,7 @@ "allow_blind_buried_vias": false, "allow_microvias": false, "max_error": 0.005, + "min_aperture_clearance": 0.049999999999999996, "min_clearance": 0.17779999999999999, "min_copper_edge_clearance": 0.19999999999999998, "min_hole_clearance": 0.0, diff --git a/qa/data/issue7975.kicad_pro b/qa/data/issue7975.kicad_pro index 279e58a00c..4cb1e77c63 100644 --- a/qa/data/issue7975.kicad_pro +++ b/qa/data/issue7975.kicad_pro @@ -63,6 +63,7 @@ "annular_width": "error", "clearance": "error", "copper_edge_clearance": "error", + "copper_sliver": "warning", "courtyards_overlap": "error", "diff_pair_gap_out_of_range": "error", "diff_pair_uncoupled_length_too_long": "error", @@ -88,6 +89,10 @@ "silk_over_copper": "error", "silk_overlap": "error", "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", @@ -108,11 +113,15 @@ "min_hole_to_hole": 0.25, "min_microvia_diameter": 0.19999999999999998, "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.12, "min_through_hole_diameter": 0.3, "min_track_width": 0.049999999999999996, "min_via_annular_width": 0.049999999999999996, - "min_via_diameter": 0.39999999999999997 + "min_via_diameter": 0.39999999999999997, + "use_height_for_length_calcs": true }, "track_widths": [ 0.0 @@ -341,7 +350,7 @@ "pinned_symbol_libs": [] }, "meta": { - "filename": "aaaaaaaaaaaaaaaaaaaa.kicad_pro", + "filename": "issue7975.kicad_pro", "version": 1 }, "net_settings": { @@ -386,7 +395,7 @@ } ], "meta": { - "version": 0 + "version": 1 }, "net_colors": null }, diff --git a/qa/data/solder_mask_bridge_test.kicad_pcb b/qa/data/solder_mask_bridge_test.kicad_pcb new file mode 100644 index 0000000000..1634b97d59 --- /dev/null +++ b/qa/data/solder_mask_bridge_test.kicad_pcb @@ -0,0 +1,1156 @@ +(kicad_pcb (version 20210812) (generator pcbnew) + + (general + (thickness 1.6) + ) + + (paper "A4") + (layers + (0 "F.Cu" signal) + (31 "B.Cu" signal) + (32 "B.Adhes" user "B.Adhesive") + (33 "F.Adhes" user "F.Adhesive") + (34 "B.Paste" user) + (35 "F.Paste" user) + (36 "B.SilkS" user "B.Silkscreen") + (37 "F.SilkS" user "F.Silkscreen") + (38 "B.Mask" user) + (39 "F.Mask" user) + (40 "Dwgs.User" user "User.Drawings") + (41 "Cmts.User" user "User.Comments") + (42 "Eco1.User" user "User.Eco1") + (43 "Eco2.User" user "User.Eco2") + (44 "Edge.Cuts" user) + (45 "Margin" user) + (46 "B.CrtYd" user "B.Courtyard") + (47 "F.CrtYd" user "F.Courtyard") + (48 "B.Fab" user) + (49 "F.Fab" user) + (50 "User.1" user) + (51 "User.2" user) + (52 "User.3" user) + (53 "User.4" user) + (54 "User.5" user) + (55 "User.6" user) + (56 "User.7" user) + (57 "User.8" user) + (58 "User.9" user) + ) + + (setup + (stackup + (layer "F.SilkS" (type "Top Silk Screen")) + (layer "F.Paste" (type "Top Solder Paste")) + (layer "F.Mask" (type "Top Solder Mask") (color "Green") (thickness 0.01)) + (layer "F.Cu" (type "copper") (thickness 0.035)) + (layer "dielectric 1" (type "core") (thickness 1.51) (material "FR4") (epsilon_r 4.5) (loss_tangent 0.02)) + (layer "B.Cu" (type "copper") (thickness 0.035)) + (layer "B.Mask" (type "Bottom Solder Mask") (color "Green") (thickness 0.01)) + (layer "B.Paste" (type "Bottom Solder Paste")) + (layer "B.SilkS" (type "Bottom Silk Screen")) + (copper_finish "None") + (dielectric_constraints no) + ) + (pad_to_mask_clearance 0) + (pcbplotparams + (layerselection 0x00010fc_ffffffff) + (disableapertmacros false) + (usegerberextensions false) + (usegerberattributes true) + (usegerberadvancedattributes true) + (creategerberjobfile true) + (svguseinch false) + (svgprecision 6) + (excludeedgelayer true) + (plotframeref false) + (viasonmask false) + (mode 1) + (useauxorigin false) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (dxfpolygonmode true) + (dxfimperialunits true) + (dxfusepcbnewfont true) + (psnegative false) + (psa4output false) + (plotreference true) + (plotvalue true) + (plotinvisibletext false) + (sketchpadsonfab false) + (subtractmaskfromsilk false) + (outputformat 1) + (mirror false) + (drillshape 1) + (scaleselection 1) + (outputdirectory "") + ) + ) + + (net 0 "") + (net 1 "signal") + (net 2 "GND") + + (footprint "Housings_DIP:DIP-14_W7.62mm_LongPads" (layer "F.Cu") + (tedit 61224B22) (tstamp 3bd3a1e0-b35d-4974-99fa-33d3eee5d601) + (at 154.305 74.93) + (descr "14-lead dip package, row spacing 7.62 mm (300 mils), longer pads") + (tags "dil dip 2.54 300") + (attr through_hole) + (fp_text reference "REF**" (at 4.445 -3.7084) (layer "F.SilkS") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 59609e9e-6f8e-414a-8e1a-68271157d0f4) + ) + (fp_text value "DIP-14_W7.62mm_LongPads" (at 0 -3.72) (layer "F.Fab") hide + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 777520c7-f251-417a-a971-c4b28beed159) + ) + (fp_line (start 0.135 -2.295) (end 0.135 -1.025) (layer "F.SilkS") (width 0.15) (tstamp fddf502b-35d9-4c79-83ea-e86b1347b469)) + (fp_line (start 0.135 -2.295) (end 7.485 -2.295) (layer "F.SilkS") (width 0.15) (tstamp e79a53c0-2b0d-4f45-9b3a-0d8f6a3f73f2)) + (fp_line (start 0.135 -1.025) (end -1.15 -1.025) (layer "F.SilkS") (width 0.15) (tstamp 4c82108f-9f12-4804-907e-f81706f9251d)) + (fp_line (start 0.135 17.535) (end 0.135 16.265) (layer "F.SilkS") (width 0.15) (tstamp 0b79ddb5-29b5-4d02-b254-210c2c0bc52a)) + (fp_line (start 0.135 17.535) (end 7.485 17.535) (layer "F.SilkS") (width 0.15) (tstamp 0f83140d-7d8e-4df7-9004-7d55d208e970)) + (fp_line (start 7.485 -2.295) (end 7.485 -1.025) (layer "F.SilkS") (width 0.15) (tstamp 47d1c952-cc7e-48d8-8bf3-43e84030b5b6)) + (fp_line (start 7.485 17.535) (end 7.485 16.265) (layer "F.SilkS") (width 0.15) (tstamp 4a6fe592-662f-4920-ac12-4fe3becc67da)) + (fp_line (start -1.4 -2.45) (end -1.4 17.7) (layer "F.CrtYd") (width 0.05) (tstamp 9873122d-04aa-4cf6-96df-5df12ef3440f)) + (fp_line (start -1.4 -2.45) (end 9 -2.45) (layer "F.CrtYd") (width 0.05) (tstamp 9abef0ec-7068-4aa5-a4b2-c8cd8b0ceb6e)) + (fp_line (start -1.4 17.7) (end 9 17.7) (layer "F.CrtYd") (width 0.05) (tstamp c1c9a072-3005-4d0b-b7f6-70565ddfb731)) + (fp_line (start 9 -2.45) (end 9 17.7) (layer "F.CrtYd") (width 0.05) (tstamp 2fe33ec7-1451-4a28-bf38-24c176dee936)) + (pad "1" thru_hole oval (at 0 0) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (net 1 "signal") (solder_mask_margin 0.4) (tstamp 2fe6f3ef-3519-45f5-bd91-8d7847304292)) + (pad "2" thru_hole oval (at 0 2.54) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (solder_mask_margin 0.4) (clearance 0.5) (tstamp 4f8caf82-f2fe-4ebb-b03c-0226aa886a96)) + (pad "2" thru_hole oval (at 0 5.08) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (solder_mask_margin 0.6) (tstamp a01719d5-72b8-433b-882b-36b730f1b10d)) + (pad "4" thru_hole oval (at 0 7.62) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (net 1 "signal") (tstamp 7616ae07-fea3-41ec-baf7-0f401270819a)) + (pad "5" thru_hole oval (at 0 10.16) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) (tstamp 3443dd40-4950-45cf-ab02-9e2253c6bb3c)) + (pad "6" thru_hole oval (at 0 12.7) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) (tstamp cf6b8e1e-93f3-4e63-9f77-8c9ec064201c)) + (pad "7" thru_hole oval (at 0 15.24) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) (tstamp f522aabe-6821-4dcd-853d-a284f451356d)) + (pad "8" thru_hole oval (at 7.62 15.24) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (net 2 "GND") (tstamp db0b3374-d270-4249-87b7-9a0a6163297e)) + (pad "9" thru_hole oval (at 7.62 12.7) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) (tstamp 07dd9b8f-a4b6-4b90-9868-4559ed6f6a32)) + (pad "10" thru_hole oval (at 7.62 10.16) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (net 1 "signal") (tstamp 036d5919-6a4b-49ac-851e-0b99804de700)) + (pad "11" thru_hole oval (at 7.62 7.62) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (solder_mask_margin 0.4) (tstamp 54ecef87-4bce-44eb-93cf-a1f86ce2bb62)) + (pad "12" thru_hole oval (at 7.62 5.08) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) (tstamp 2f3483f5-a99f-4f68-b6b1-fe893d5ab874)) + (pad "13" thru_hole oval (at 7.62 2.54) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (solder_mask_margin 0.5) (clearance 0.6) (tstamp 30760006-239e-48b2-9ad2-bdc85ca119ed)) + (pad "14" thru_hole oval (at 7.62 0) (size 2.3 1.6) (drill 0.8) (layers *.Cu *.Mask) + (solder_mask_margin 0.5) (clearance 0.6) (tstamp fefb1ddf-3d2b-4862-9521-565bc091912b)) + (model "Housings_DIP.3dshapes/DIP-14_W7.62mm_LongPads.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (gr_rect (start 78.105 50.165) (end 228.6 130.81) (layer "Edge.Cuts") (width 0.05) (fill none) (tstamp d0d883cf-992c-40b1-8baf-44f8b31bd338)) + (gr_text "No error ->" (at 150.9776 78.5876) (layer "F.SilkS") (tstamp 246e9db7-6544-4169-9c7a-178ae76a161a) + (effects (font (size 0.66 0.66) (thickness 0.1))) + ) + (gr_text "Error ->" (at 150.5712 79.9592) (layer "F.SilkS") (tstamp 60b05cd2-1727-4c72-816a-14d2b68852d4) + (effects (font (size 0.66 0.66) (thickness 0.1))) + ) + (gr_text "<- Error" (at 164.2364 81.3816) (layer "F.SilkS") (tstamp 8aa83fbc-6808-4f31-8b58-3c465cfd1f3b) + (effects (font (size 0.66 0.66) (thickness 0.1))) + ) + (gr_text "<- Errors (F & B)" (at 166.7764 76.0984) (layer "F.SilkS") (tstamp d8431fbc-8a2a-46ff-b841-c7976a19d81c) + (effects (font (size 0.66 0.66) (thickness 0.1))) + ) + + (segment (start 168.3004 81.9404) (end 167.64 81.28) (width 0.25) (layer "F.Cu") (net 1) (tstamp 066516c0-dc97-4e08-bdf9-7b64eee95cf9)) + (segment (start 167.7416 85.09) (end 168.3004 84.5312) (width 0.25) (layer "F.Cu") (net 1) (tstamp 32b42d11-33a9-4077-9dda-cfa8a7ae67b5)) + (segment (start 156.3116 74.93) (end 158.2928 76.9112) (width 0.25) (layer "F.Cu") (net 1) (tstamp 507e67d6-393c-4ae3-8b13-38f4d0cb7edf)) + (segment (start 158.2928 82.55) (end 158.2928 76.9112) (width 0.25) (layer "F.Cu") (net 1) (tstamp 6f421800-5ee4-46ac-9ad2-9579946f52e1)) + (segment (start 158.2928 81.28) (end 167.64 81.28) (width 0.25) (layer "F.Cu") (net 1) (tstamp 7cc23349-033d-49b3-a705-5330ec1196d6)) + (segment (start 154.305 82.55) (end 158.2928 82.55) (width 0.25) (layer "F.Cu") (net 1) (tstamp 9377a374-770b-446c-961a-3517ab69ece9)) + (segment (start 154.305 74.93) (end 156.3116 74.93) (width 0.25) (layer "F.Cu") (net 1) (tstamp b76f6e32-a57c-449b-b4ff-72af6ad524ff)) + (segment (start 161.925 85.09) (end 167.7416 85.09) (width 0.25) (layer "F.Cu") (net 1) (tstamp c1a128fd-53d8-4ef0-98f0-67cb97d3fd4f)) + (segment (start 168.3004 84.5312) (end 168.3004 81.9404) (width 0.25) (layer "F.Cu") (net 1) (tstamp ce22732e-36a3-49b1-836a-83ed80422c72)) + + (zone (net 2) (net_name "GND") (layer "F.Cu") (tstamp 1c42e37e-ccaf-44fa-9e0f-5f7271f40c7d) (hatch edge 0.508) + (connect_pads (clearance 0.45)) + (min_thickness 0.254) (filled_areas_thickness no) + (fill yes (thermal_gap 0.508) (thermal_bridge_width 0.508)) + (polygon + (pts + (xy 172.8724 97.8408) + (xy 142.9004 97.8408) + (xy 142.9004 67.6148) + (xy 172.8216 67.6148) + ) + ) + (filled_polygon + (layer "F.Cu") + (pts + (xy 172.763932 67.634802) + (xy 172.810425 67.688458) + (xy 172.821811 67.740588) + (xy 172.872188 97.714588) + (xy 172.8523 97.782742) + (xy 172.798723 97.829325) + (xy 172.746188 97.8408) + (xy 143.0264 97.8408) + (xy 142.958279 97.820798) + (xy 142.911786 97.767142) + (xy 142.9004 97.7148) + (xy 142.9004 77.47) + (xy 152.648027 77.47) + (xy 152.667883 77.696953) + (xy 152.726847 77.917011) + (xy 152.729169 77.921992) + (xy 152.72917 77.921993) + (xy 152.820802 78.118499) + (xy 152.820803 78.1185) + (xy 152.823128 78.123486) + (xy 152.881401 78.206708) + (xy 152.948874 78.303069) + (xy 152.953801 78.310106) + (xy 153.114894 78.471199) + (xy 153.119402 78.474356) + (xy 153.119405 78.474358) + (xy 153.130505 78.48213) + (xy 153.301514 78.601872) + (xy 153.306496 78.604195) + (xy 153.306501 78.604198) + (xy 153.353724 78.626218) + (xy 153.41199 78.653388) + (xy 153.465274 78.700304) + (xy 153.484735 78.768581) + (xy 153.464193 78.836541) + (xy 153.412984 78.881307) + (xy 153.320173 78.925576) + (xy 153.320163 78.925582) + (xy 153.315104 78.927995) + (xy 153.310553 78.931265) + (xy 153.31055 78.931267) + (xy 153.202772 79.008713) + (xy 153.13361 79.058411) + (xy 152.97808 79.218906) + (xy 152.853429 79.404406) + (xy 152.763597 79.609049) + (xy 152.711424 79.826365) + (xy 152.698559 80.049485) + (xy 152.725409 80.271358) + (xy 152.791124 80.484969) + (xy 152.793694 80.489949) + (xy 152.793696 80.489953) + (xy 152.86095 80.620255) + (xy 152.893628 80.683567) + (xy 153.029681 80.860874) + (xy 153.033826 80.864645) + (xy 153.033829 80.864649) + (xy 153.135934 80.957557) + (xy 153.194982 81.011287) + (xy 153.199738 81.01427) + (xy 153.19974 81.014272) + (xy 153.37955 81.127067) + (xy 153.379554 81.127069) + (xy 153.384306 81.13005) + (xy 153.46491 81.162453) + (xy 153.520652 81.206417) + (xy 153.543777 81.273542) + (xy 153.52694 81.342513) + (xy 153.472156 81.393083) + (xy 153.320173 81.465576) + (xy 153.320163 81.465582) + (xy 153.315104 81.467995) + (xy 153.310553 81.471265) + (xy 153.31055 81.471267) + (xy 153.202772 81.548713) + (xy 153.13361 81.598411) + (xy 152.97808 81.758906) + (xy 152.853429 81.944406) + (xy 152.763597 82.149049) + (xy 152.711424 82.366365) + (xy 152.711101 82.37197) + (xy 152.700364 82.558188) + (xy 152.698559 82.589485) + (xy 152.725409 82.811358) + (xy 152.791124 83.024969) + (xy 152.793694 83.029949) + (xy 152.793696 83.029953) + (xy 152.86095 83.160255) + (xy 152.893628 83.223567) + (xy 153.029681 83.400874) + (xy 153.033826 83.404645) + (xy 153.033829 83.404649) + (xy 153.135934 83.497557) + (xy 153.194982 83.551287) + (xy 153.199738 83.55427) + (xy 153.19974 83.554272) + (xy 153.37955 83.667067) + (xy 153.379554 83.667069) + (xy 153.384306 83.67005) + (xy 153.46491 83.702453) + (xy 153.520652 83.746417) + (xy 153.543777 83.813542) + (xy 153.52694 83.882513) + (xy 153.472156 83.933083) + (xy 153.320173 84.005576) + (xy 153.320163 84.005582) + (xy 153.315104 84.007995) + (xy 153.310553 84.011265) + (xy 153.31055 84.011267) + (xy 153.202772 84.088713) + (xy 153.13361 84.138411) + (xy 152.97808 84.298906) + (xy 152.853429 84.484406) + (xy 152.85117 84.489552) + (xy 152.851169 84.489554) + (xy 152.832888 84.5312) + (xy 152.763597 84.689049) + (xy 152.711424 84.906365) + (xy 152.698559 85.129485) + (xy 152.725409 85.351358) + (xy 152.791124 85.564969) + (xy 152.793694 85.569949) + (xy 152.793696 85.569953) + (xy 152.86095 85.700255) + (xy 152.893628 85.763567) + (xy 153.029681 85.940874) + (xy 153.033826 85.944645) + (xy 153.033829 85.944649) + (xy 153.135934 86.037557) + (xy 153.194982 86.091287) + (xy 153.199738 86.09427) + (xy 153.19974 86.094272) + (xy 153.37955 86.207067) + (xy 153.379554 86.207069) + (xy 153.384306 86.21005) + (xy 153.46491 86.242453) + (xy 153.520652 86.286417) + (xy 153.543777 86.353542) + (xy 153.52694 86.422513) + (xy 153.472156 86.473083) + (xy 153.320173 86.545576) + (xy 153.320163 86.545582) + (xy 153.315104 86.547995) + (xy 153.310553 86.551265) + (xy 153.31055 86.551267) + (xy 153.202772 86.628713) + (xy 153.13361 86.678411) + (xy 152.97808 86.838906) + (xy 152.853429 87.024406) + (xy 152.763597 87.229049) + (xy 152.711424 87.446365) + (xy 152.698559 87.669485) + (xy 152.725409 87.891358) + (xy 152.791124 88.104969) + (xy 152.793694 88.109949) + (xy 152.793696 88.109953) + (xy 152.86095 88.240255) + (xy 152.893628 88.303567) + (xy 153.029681 88.480874) + (xy 153.033826 88.484645) + (xy 153.033829 88.484649) + (xy 153.135934 88.577557) + (xy 153.194982 88.631287) + (xy 153.199738 88.63427) + (xy 153.19974 88.634272) + (xy 153.37955 88.747067) + (xy 153.379554 88.747069) + (xy 153.384306 88.75005) + (xy 153.46491 88.782453) + (xy 153.520652 88.826417) + (xy 153.543777 88.893542) + (xy 153.52694 88.962513) + (xy 153.472156 89.013083) + (xy 153.320173 89.085576) + (xy 153.320163 89.085582) + (xy 153.315104 89.087995) + (xy 153.310553 89.091265) + (xy 153.31055 89.091267) + (xy 153.203648 89.168084) + (xy 153.13361 89.218411) + (xy 152.97808 89.378906) + (xy 152.853429 89.564406) + (xy 152.763597 89.769049) + (xy 152.711424 89.986365) + (xy 152.698559 90.209485) + (xy 152.725409 90.431358) + (xy 152.791124 90.644969) + (xy 152.793694 90.649949) + (xy 152.793696 90.649953) + (xy 152.86095 90.780255) + (xy 152.893628 90.843567) + (xy 153.029681 91.020874) + (xy 153.033826 91.024645) + (xy 153.033829 91.024649) + (xy 153.135934 91.117557) + (xy 153.194982 91.171287) + (xy 153.199738 91.17427) + (xy 153.19974 91.174272) + (xy 153.37955 91.287067) + (xy 153.379554 91.287069) + (xy 153.384306 91.29005) + (xy 153.591669 91.373409) + (xy 153.810517 91.41873) + (xy 153.81513 91.418996) + (xy 153.865405 91.421895) + (xy 153.865409 91.421895) + (xy 153.867228 91.422) + (xy 154.711708 91.422) + (xy 154.714495 91.421751) + (xy 154.714501 91.421751) + (xy 154.78397 91.415551) + (xy 154.877606 91.407194) + (xy 155.005936 91.372087) + (xy 155.08776 91.349703) + (xy 155.087764 91.349702) + (xy 155.093176 91.348221) + (xy 155.294896 91.252005) + (xy 155.299447 91.248735) + (xy 155.29945 91.248733) + (xy 155.471831 91.124865) + (xy 155.47639 91.121589) + (xy 155.63192 90.961094) + (xy 155.756571 90.775594) + (xy 155.846403 90.570951) + (xy 155.878677 90.436522) + (xy 160.292273 90.436522) + (xy 160.339764 90.613761) + (xy 160.34351 90.624053) + (xy 160.435586 90.821511) + (xy 160.441069 90.831007) + (xy 160.566028 91.009467) + (xy 160.573084 91.017875) + (xy 160.727125 91.171916) + (xy 160.735533 91.178972) + (xy 160.913993 91.303931) + (xy 160.923489 91.309414) + (xy 161.120947 91.40149) + (xy 161.131239 91.405236) + (xy 161.341688 91.461625) + (xy 161.352481 91.463528) + (xy 161.51517 91.477762) + (xy 161.520635 91.478) + (xy 161.652885 91.478) + (xy 161.668124 91.473525) + (xy 161.669329 91.472135) + (xy 161.671 91.464452) + (xy 161.671 90.442115) + (xy 161.669659 90.437548) + (xy 162.179 90.437548) + (xy 162.179 91.459885) + (xy 162.183475 91.475124) + (xy 162.184865 91.476329) + (xy 162.192548 91.478) + (xy 162.329365 91.478) + (xy 162.33483 91.477762) + (xy 162.497519 91.463528) + (xy 162.508312 91.461625) + (xy 162.718761 91.405236) + (xy 162.729053 91.40149) + (xy 162.926511 91.309414) + (xy 162.936007 91.303931) + (xy 163.114467 91.178972) + (xy 163.122875 91.171916) + (xy 163.276916 91.017875) + (xy 163.283972 91.009467) + (xy 163.408931 90.831007) + (xy 163.414414 90.821511) + (xy 163.50649 90.624053) + (xy 163.510236 90.613761) + (xy 163.556394 90.441497) + (xy 163.556058 90.427401) + (xy 163.548116 90.424) + (xy 162.197115 90.424) + (xy 162.181876 90.428475) + (xy 162.180671 90.429865) + (xy 162.179 90.437548) + (xy 161.669659 90.437548) + (xy 161.666525 90.426876) + (xy 161.665135 90.425671) + (xy 161.657452 90.424) + (xy 160.307033 90.424) + (xy 160.293502 90.427973) + (xy 160.292273 90.436522) + (xy 155.878677 90.436522) + (xy 155.898576 90.353635) + (xy 155.911441 90.130515) + (xy 155.884591 89.908642) + (xy 155.818876 89.695031) + (xy 155.751456 89.564406) + (xy 155.718943 89.501414) + (xy 155.718942 89.501413) + (xy 155.716372 89.496433) + (xy 155.580319 89.319126) + (xy 155.576174 89.315355) + (xy 155.576171 89.315351) + (xy 155.419171 89.172492) + (xy 155.415018 89.168713) + (xy 155.41026 89.165728) + (xy 155.23045 89.052933) + (xy 155.230446 89.052931) + (xy 155.225694 89.04995) + (xy 155.14509 89.017547) + (xy 155.089348 88.973583) + (xy 155.066223 88.906458) + (xy 155.08306 88.837487) + (xy 155.137844 88.786917) + (xy 155.289827 88.714424) + (xy 155.289837 88.714418) + (xy 155.294896 88.712005) + (xy 155.299447 88.708735) + (xy 155.29945 88.708733) + (xy 155.471831 88.584865) + (xy 155.47639 88.581589) + (xy 155.63192 88.421094) + (xy 155.756571 88.235594) + (xy 155.846403 88.030951) + (xy 155.898576 87.813635) + (xy 155.911441 87.590515) + (xy 155.884591 87.368642) + (xy 155.818876 87.155031) + (xy 155.751456 87.024406) + (xy 155.718943 86.961414) + (xy 155.718942 86.961413) + (xy 155.716372 86.956433) + (xy 155.580319 86.779126) + (xy 155.576174 86.775355) + (xy 155.576171 86.775351) + (xy 155.419171 86.632492) + (xy 155.415018 86.628713) + (xy 155.282487 86.545576) + (xy 155.23045 86.512933) + (xy 155.230446 86.512931) + (xy 155.225694 86.50995) + (xy 155.14509 86.477547) + (xy 155.089348 86.433583) + (xy 155.066223 86.366458) + (xy 155.08306 86.297487) + (xy 155.137844 86.246917) + (xy 155.289827 86.174424) + (xy 155.289837 86.174418) + (xy 155.294896 86.172005) + (xy 155.299447 86.168735) + (xy 155.29945 86.168733) + (xy 155.471831 86.044865) + (xy 155.47639 86.041589) + (xy 155.63192 85.881094) + (xy 155.756571 85.695594) + (xy 155.764585 85.677339) + (xy 155.80337 85.588983) + (xy 155.846403 85.490951) + (xy 155.898576 85.273635) + (xy 155.911441 85.050515) + (xy 155.884591 84.828642) + (xy 155.818876 84.615031) + (xy 155.764728 84.51012) + (xy 155.718943 84.421414) + (xy 155.718942 84.421413) + (xy 155.716372 84.416433) + (xy 155.580319 84.239126) + (xy 155.576174 84.235355) + (xy 155.576171 84.235351) + (xy 155.419171 84.092492) + (xy 155.415018 84.088713) + (xy 155.282487 84.005576) + (xy 155.23045 83.972933) + (xy 155.230446 83.972931) + (xy 155.225694 83.96995) + (xy 155.14509 83.937547) + (xy 155.089348 83.893583) + (xy 155.066223 83.826458) + (xy 155.08306 83.757487) + (xy 155.137844 83.706917) + (xy 155.289827 83.634424) + (xy 155.289837 83.634418) + (xy 155.294896 83.632005) + (xy 155.299447 83.628735) + (xy 155.29945 83.628733) + (xy 155.471831 83.504865) + (xy 155.47639 83.501589) + (xy 155.63192 83.341094) + (xy 155.738341 83.182723) + (xy 155.792936 83.137339) + (xy 155.842922 83.127) + (xy 158.246722 83.127) + (xy 158.263169 83.128078) + (xy 158.2928 83.131979) + (xy 158.300988 83.130901) + (xy 158.33062 83.127) + (xy 158.435239 83.113227) + (xy 158.443427 83.112149) + (xy 158.583789 83.054009) + (xy 158.644055 83.007765) + (xy 158.697768 82.96655) + (xy 158.697771 82.966547) + (xy 158.704321 82.961521) + (xy 158.796809 82.84099) + (xy 158.811391 82.805786) + (xy 158.851789 82.708257) + (xy 158.85179 82.708254) + (xy 158.854949 82.700627) + (xy 158.874779 82.55) + (xy 158.870878 82.520368) + (xy 158.8698 82.503922) + (xy 158.8698 81.983) + (xy 158.889802 81.914879) + (xy 158.943458 81.868386) + (xy 158.9958 81.857) + (xy 160.318883 81.857) + (xy 160.387004 81.877002) + (xy 160.433497 81.930658) + (xy 160.443601 82.000932) + (xy 160.434257 82.033642) + (xy 160.383597 82.149049) + (xy 160.331424 82.366365) + (xy 160.331101 82.37197) + (xy 160.320364 82.558188) + (xy 160.318559 82.589485) + (xy 160.345409 82.811358) + (xy 160.411124 83.024969) + (xy 160.413694 83.029949) + (xy 160.413696 83.029953) + (xy 160.48095 83.160255) + (xy 160.513628 83.223567) + (xy 160.649681 83.400874) + (xy 160.653826 83.404645) + (xy 160.653829 83.404649) + (xy 160.755934 83.497557) + (xy 160.814982 83.551287) + (xy 160.819738 83.55427) + (xy 160.81974 83.554272) + (xy 160.99955 83.667067) + (xy 160.999554 83.667069) + (xy 161.004306 83.67005) + (xy 161.08491 83.702453) + (xy 161.140652 83.746417) + (xy 161.163777 83.813542) + (xy 161.14694 83.882513) + (xy 161.092156 83.933083) + (xy 160.940173 84.005576) + (xy 160.940163 84.005582) + (xy 160.935104 84.007995) + (xy 160.930553 84.011265) + (xy 160.93055 84.011267) + (xy 160.822772 84.088713) + (xy 160.75361 84.138411) + (xy 160.59808 84.298906) + (xy 160.473429 84.484406) + (xy 160.47117 84.489552) + (xy 160.471169 84.489554) + (xy 160.452888 84.5312) + (xy 160.383597 84.689049) + (xy 160.331424 84.906365) + (xy 160.318559 85.129485) + (xy 160.345409 85.351358) + (xy 160.411124 85.564969) + (xy 160.413694 85.569949) + (xy 160.413696 85.569953) + (xy 160.48095 85.700255) + (xy 160.513628 85.763567) + (xy 160.649681 85.940874) + (xy 160.653826 85.944645) + (xy 160.653829 85.944649) + (xy 160.755934 86.037557) + (xy 160.814982 86.091287) + (xy 160.819738 86.09427) + (xy 160.81974 86.094272) + (xy 160.99955 86.207067) + (xy 160.999554 86.207069) + (xy 161.004306 86.21005) + (xy 161.08491 86.242453) + (xy 161.140652 86.286417) + (xy 161.163777 86.353542) + (xy 161.14694 86.422513) + (xy 161.092156 86.473083) + (xy 160.940173 86.545576) + (xy 160.940163 86.545582) + (xy 160.935104 86.547995) + (xy 160.930553 86.551265) + (xy 160.93055 86.551267) + (xy 160.822772 86.628713) + (xy 160.75361 86.678411) + (xy 160.59808 86.838906) + (xy 160.473429 87.024406) + (xy 160.383597 87.229049) + (xy 160.331424 87.446365) + (xy 160.318559 87.669485) + (xy 160.345409 87.891358) + (xy 160.411124 88.104969) + (xy 160.413694 88.109949) + (xy 160.413696 88.109953) + (xy 160.48095 88.240255) + (xy 160.513628 88.303567) + (xy 160.649681 88.480874) + (xy 160.653826 88.484645) + (xy 160.653829 88.484649) + (xy 160.755934 88.577557) + (xy 160.814982 88.631287) + (xy 160.819738 88.63427) + (xy 160.81974 88.634272) + (xy 160.99955 88.747067) + (xy 160.999554 88.747069) + (xy 161.004306 88.75005) + (xy 161.014479 88.754139) + (xy 161.070222 88.798104) + (xy 161.093348 88.865229) + (xy 161.076512 88.934201) + (xy 161.020732 88.985241) + (xy 160.923489 89.030586) + (xy 160.913993 89.036069) + (xy 160.735533 89.161028) + (xy 160.727125 89.168084) + (xy 160.573084 89.322125) + (xy 160.566028 89.330533) + (xy 160.441069 89.508993) + (xy 160.435586 89.518489) + (xy 160.34351 89.715947) + (xy 160.339764 89.726239) + (xy 160.293606 89.898503) + (xy 160.293942 89.912599) + (xy 160.301884 89.916) + (xy 163.542967 89.916) + (xy 163.556498 89.912027) + (xy 163.557727 89.903478) + (xy 163.510236 89.726239) + (xy 163.50649 89.715947) + (xy 163.414414 89.518489) + (xy 163.408931 89.508993) + (xy 163.283972 89.330533) + (xy 163.276916 89.322125) + (xy 163.122875 89.168084) + (xy 163.114467 89.161028) + (xy 162.936007 89.036069) + (xy 162.926511 89.030586) + (xy 162.82503 88.983265) + (xy 162.771745 88.936348) + (xy 162.752284 88.86807) + (xy 162.772826 88.80011) + (xy 162.824035 88.755345) + (xy 162.909827 88.714424) + (xy 162.909837 88.714418) + (xy 162.914896 88.712005) + (xy 162.919447 88.708735) + (xy 162.91945 88.708733) + (xy 163.091831 88.584865) + (xy 163.09639 88.581589) + (xy 163.25192 88.421094) + (xy 163.376571 88.235594) + (xy 163.466403 88.030951) + (xy 163.518576 87.813635) + (xy 163.531441 87.590515) + (xy 163.504591 87.368642) + (xy 163.438876 87.155031) + (xy 163.371456 87.024406) + (xy 163.338943 86.961414) + (xy 163.338942 86.961413) + (xy 163.336372 86.956433) + (xy 163.200319 86.779126) + (xy 163.196174 86.775355) + (xy 163.196171 86.775351) + (xy 163.039171 86.632492) + (xy 163.035018 86.628713) + (xy 162.902487 86.545576) + (xy 162.85045 86.512933) + (xy 162.850446 86.512931) + (xy 162.845694 86.50995) + (xy 162.76509 86.477547) + (xy 162.709348 86.433583) + (xy 162.686223 86.366458) + (xy 162.70306 86.297487) + (xy 162.757844 86.246917) + (xy 162.909827 86.174424) + (xy 162.909837 86.174418) + (xy 162.914896 86.172005) + (xy 162.919447 86.168735) + (xy 162.91945 86.168733) + (xy 163.091831 86.044865) + (xy 163.09639 86.041589) + (xy 163.25192 85.881094) + (xy 163.358341 85.722723) + (xy 163.412936 85.677339) + (xy 163.462922 85.667) + (xy 167.695523 85.667) + (xy 167.71197 85.668078) + (xy 167.7416 85.671979) + (xy 167.779418 85.667) + (xy 167.779423 85.667) + (xy 167.884039 85.653227) + (xy 167.892227 85.652149) + (xy 167.97445 85.618091) + (xy 168.03259 85.594009) + (xy 168.122858 85.524743) + (xy 168.122859 85.524742) + (xy 168.153121 85.501521) + (xy 168.171318 85.477806) + (xy 168.18218 85.465421) + (xy 168.675826 84.971776) + (xy 168.68821 84.960915) + (xy 168.711921 84.942721) + (xy 168.716949 84.936169) + (xy 168.735129 84.912477) + (xy 168.735144 84.912458) + (xy 168.744007 84.900908) + (xy 168.804409 84.82219) + (xy 168.862549 84.681828) + (xy 168.882379 84.5312) + (xy 168.878478 84.501567) + (xy 168.8774 84.485122) + (xy 168.8774 81.986478) + (xy 168.878478 81.970032) + (xy 168.881301 81.948588) + (xy 168.882379 81.9404) + (xy 168.862549 81.789772) + (xy 168.804409 81.64941) + (xy 168.735144 81.559142) + (xy 168.735129 81.559123) + (xy 168.716951 81.535434) + (xy 168.71695 81.535433) + (xy 168.711921 81.528879) + (xy 168.688212 81.510686) + (xy 168.675822 81.49982) + (xy 168.08058 80.904579) + (xy 168.069718 80.892194) + (xy 168.051521 80.868479) + (xy 167.963704 80.801094) + (xy 167.93099 80.775991) + (xy 167.87285 80.751909) + (xy 167.790627 80.717851) + (xy 167.733878 80.71038) + (xy 167.677823 80.703) + (xy 167.677818 80.703) + (xy 167.64 80.698021) + (xy 167.631812 80.699099) + (xy 167.610368 80.701922) + (xy 167.593922 80.703) + (xy 163.531117 80.703) + (xy 163.462996 80.682998) + (xy 163.416503 80.629342) + (xy 163.406399 80.559068) + (xy 163.415743 80.526358) + (xy 163.466403 80.410951) + (xy 163.518576 80.193635) + (xy 163.531441 79.970515) + (xy 163.504591 79.748642) + (xy 163.438876 79.535031) + (xy 163.371456 79.404406) + (xy 163.338943 79.341414) + (xy 163.338942 79.341413) + (xy 163.336372 79.336433) + (xy 163.200319 79.159126) + (xy 163.196174 79.155355) + (xy 163.196171 79.155351) + (xy 163.039171 79.012492) + (xy 163.035018 79.008713) + (xy 162.915705 78.933868) + (xy 162.868627 78.880725) + (xy 162.857755 78.810566) + (xy 162.886539 78.745666) + (xy 162.930909 78.71225) + (xy 162.959307 78.699458) + (xy 163.156675 78.566582) + (xy 163.328832 78.402352) + (xy 163.470858 78.211463) + (xy 163.513296 78.127995) + (xy 163.576272 78.004129) + (xy 163.576272 78.004128) + (xy 163.57869 77.999373) + (xy 163.649245 77.772147) + (xy 163.658507 77.702268) + (xy 163.679807 77.541566) + (xy 163.679807 77.541562) + (xy 163.680507 77.536282) + (xy 163.671581 77.298521) + (xy 163.64046 77.150201) + (xy 163.623819 77.070891) + (xy 163.623818 77.070888) + (xy 163.622722 77.065664) + (xy 163.575355 76.945722) + (xy 163.537291 76.849338) + (xy 163.53729 76.849336) + (xy 163.535328 76.844368) + (xy 163.521452 76.821501) + (xy 163.414666 76.645521) + (xy 163.414664 76.645518) + (xy 163.411898 76.64096) + (xy 163.40621 76.634405) + (xy 163.259459 76.46529) + (xy 163.259457 76.465288) + (xy 163.255959 76.461257) + (xy 163.251833 76.457874) + (xy 163.251829 76.45787) + (xy 163.076101 76.313782) + (xy 163.076095 76.313778) + (xy 163.071973 76.310398) + (xy 163.066044 76.307023) + (xy 163.065607 76.306571) + (xy 163.062938 76.304736) + (xy 163.063312 76.304192) + (xy 163.01674 76.255938) + (xy 163.002882 76.186307) + (xy 163.02887 76.120237) + (xy 163.058015 76.093004) + (xy 163.118708 76.052143) + (xy 163.156675 76.026582) + (xy 163.328832 75.862352) + (xy 163.470858 75.671463) + (xy 163.527428 75.560199) + (xy 163.576272 75.464129) + (xy 163.576272 75.464128) + (xy 163.57869 75.459373) + (xy 163.649245 75.232147) + (xy 163.65539 75.185786) + (xy 163.679807 75.001566) + (xy 163.679807 75.001562) + (xy 163.680507 74.996282) + (xy 163.679291 74.963878) + (xy 163.671781 74.763852) + (xy 163.671581 74.758521) + (xy 163.622722 74.525664) + (xy 163.552993 74.349099) + (xy 163.537291 74.309338) + (xy 163.53729 74.309336) + (xy 163.535328 74.304368) + (xy 163.532557 74.299801) + (xy 163.414666 74.105521) + (xy 163.414664 74.105518) + (xy 163.411898 74.10096) + (xy 163.396814 74.083577) + (xy 163.259459 73.92529) + (xy 163.259457 73.925288) + (xy 163.255959 73.921257) + (xy 163.251833 73.917874) + (xy 163.251829 73.91787) + (xy 163.0761 73.773782) + (xy 163.076101 73.773782) + (xy 163.071973 73.770398) + (xy 162.945398 73.698347) + (xy 162.869838 73.655335) + (xy 162.869836 73.655334) + (xy 162.865198 73.652694) + (xy 162.641548 73.571513) + (xy 162.407417 73.529176) + (xy 162.403276 73.528981) + (xy 162.40327 73.52898) + (xy 162.383969 73.52807) + (xy 162.383962 73.52807) + (xy 162.382481 73.528) + (xy 161.51526 73.528) + (xy 161.337924 73.543047) + (xy 161.33276 73.544387) + (xy 161.332756 73.544388) + (xy 161.144421 73.593271) + (xy 161.107626 73.602821) + (xy 160.890693 73.700542) + (xy 160.693325 73.833418) + (xy 160.521168 73.997648) + (xy 160.379142 74.188537) + (xy 160.376726 74.193288) + (xy 160.376724 74.193292) + (xy 160.273728 74.395871) + (xy 160.27131 74.400627) + (xy 160.200755 74.627853) + (xy 160.200054 74.63314) + (xy 160.200054 74.633141) + (xy 160.184128 74.753303) + (xy 160.169493 74.863718) + (xy 160.178419 75.101479) + (xy 160.227278 75.334336) + (xy 160.229241 75.339306) + (xy 160.306759 75.535594) + (xy 160.314672 75.555632) + (xy 160.317442 75.560196) + (xy 160.317443 75.560199) + (xy 160.387558 75.675745) + (xy 160.438102 75.75904) + (xy 160.441597 75.763068) + (xy 160.441598 75.763069) + (xy 160.584292 75.927508) + (xy 160.594041 75.938743) + (xy 160.598167 75.942126) + (xy 160.598171 75.94213) + (xy 160.773899 76.086218) + (xy 160.773905 76.086222) + (xy 160.778027 76.089602) + (xy 160.782665 76.092242) + (xy 160.783956 76.092977) + (xy 160.784393 76.093429) + (xy 160.787062 76.095264) + (xy 160.786688 76.095808) + (xy 160.83326 76.144062) + (xy 160.847118 76.213693) + (xy 160.82113 76.279763) + (xy 160.791985 76.306996) + (xy 160.76681 76.323945) + (xy 160.693325 76.373418) + (xy 160.521168 76.537648) + (xy 160.379142 76.728537) + (xy 160.376726 76.733288) + (xy 160.376724 76.733292) + (xy 160.290434 76.903012) + (xy 160.27131 76.940627) + (xy 160.200755 77.167853) + (xy 160.200054 77.17314) + (xy 160.200054 77.173141) + (xy 160.184128 77.293303) + (xy 160.169493 77.403718) + (xy 160.178419 77.641479) + (xy 160.227278 77.874336) + (xy 160.314672 78.095632) + (xy 160.317442 78.100196) + (xy 160.317443 78.100199) + (xy 160.387558 78.215745) + (xy 160.438102 78.29904) + (xy 160.441597 78.303068) + (xy 160.441598 78.303069) + (xy 160.587495 78.471199) + (xy 160.594041 78.478743) + (xy 160.598167 78.482126) + (xy 160.598171 78.48213) + (xy 160.744208 78.601872) + (xy 160.778027 78.629602) + (xy 160.782657 78.632238) + (xy 160.782662 78.632241) + (xy 160.926948 78.714374) + (xy 160.976254 78.765457) + (xy 160.990115 78.835087) + (xy 160.964131 78.901158) + (xy 160.934549 78.927223) + (xy 160.935104 78.927995) + (xy 160.826927 79.005728) + (xy 160.75361 79.058411) + (xy 160.59808 79.218906) + (xy 160.473429 79.404406) + (xy 160.383597 79.609049) + (xy 160.331424 79.826365) + (xy 160.318559 80.049485) + (xy 160.345409 80.271358) + (xy 160.411124 80.484969) + (xy 160.413694 80.489949) + (xy 160.413696 80.489953) + (xy 160.428797 80.51921) + (xy 160.442266 80.588918) + (xy 160.415911 80.654841) + (xy 160.358098 80.696051) + (xy 160.316831 80.703) + (xy 158.9958 80.703) + (xy 158.927679 80.682998) + (xy 158.881186 80.629342) + (xy 158.8698 80.577) + (xy 158.8698 76.957278) + (xy 158.870878 76.940832) + (xy 158.873701 76.919388) + (xy 158.874779 76.9112) + (xy 158.854949 76.760572) + (xy 158.839906 76.724255) + (xy 158.799969 76.627838) + (xy 158.799968 76.627836) + (xy 158.796809 76.62021) + (xy 158.727543 76.529942) + (xy 158.727542 76.529941) + (xy 158.704321 76.499679) + (xy 158.680604 76.48148) + (xy 158.668214 76.470613) + (xy 156.752183 74.554582) + (xy 156.741316 74.542191) + (xy 156.72815 74.525032) + (xy 156.728147 74.525029) + (xy 156.723121 74.518479) + (xy 156.692858 74.495257) + (xy 156.60259 74.425991) + (xy 156.541355 74.400627) + (xy 156.462227 74.367851) + (xy 156.454039 74.366773) + (xy 156.349423 74.353) + (xy 156.349418 74.353) + (xy 156.3116 74.348021) + (xy 156.303412 74.349099) + (xy 156.281968 74.351922) + (xy 156.265522 74.353) + (xy 155.842974 74.353) + (xy 155.774853 74.332998) + (xy 155.731008 74.284789) + (xy 155.718945 74.261418) + (xy 155.716372 74.256433) + (xy 155.580319 74.079126) + (xy 155.576174 74.075355) + (xy 155.576171 74.075351) + (xy 155.419171 73.932492) + (xy 155.415018 73.928713) + (xy 155.41026 73.925728) + (xy 155.23045 73.812933) + (xy 155.230446 73.812931) + (xy 155.225694 73.80995) + (xy 155.018331 73.726591) + (xy 154.799483 73.68127) + (xy 154.79487 73.681004) + (xy 154.744595 73.678105) + (xy 154.744591 73.678105) + (xy 154.742772 73.678) + (xy 153.898292 73.678) + (xy 153.895505 73.678249) + (xy 153.895499 73.678249) + (xy 153.82603 73.684449) + (xy 153.732394 73.692806) + (xy 153.61306 73.725452) + (xy 153.52224 73.750297) + (xy 153.522236 73.750298) + (xy 153.516824 73.751779) + (xy 153.315104 73.847995) + (xy 153.310553 73.851265) + (xy 153.31055 73.851267) + (xy 153.202772 73.928713) + (xy 153.13361 73.978411) + (xy 152.97808 74.138906) + (xy 152.853429 74.324406) + (xy 152.85117 74.329552) + (xy 152.851169 74.329554) + (xy 152.84135 74.351922) + (xy 152.763597 74.529049) + (xy 152.711424 74.746365) + (xy 152.698559 74.969485) + (xy 152.725409 75.191358) + (xy 152.791124 75.404969) + (xy 152.793694 75.409949) + (xy 152.793696 75.409953) + (xy 152.862834 75.543905) + (xy 152.893628 75.603567) + (xy 153.029681 75.780874) + (xy 153.033826 75.784645) + (xy 153.033829 75.784649) + (xy 153.123267 75.866031) + (xy 153.194982 75.931287) + (xy 153.199738 75.93427) + (xy 153.19974 75.934272) + (xy 153.37955 76.047067) + (xy 153.379554 76.047069) + (xy 153.384306 76.05005) + (xy 153.402102 76.057204) + (xy 153.457847 76.101169) + (xy 153.480973 76.168293) + (xy 153.464137 76.237265) + (xy 153.408357 76.288306) + (xy 153.306501 76.335802) + (xy 153.306496 76.335805) + (xy 153.301514 76.338128) + (xy 153.255371 76.370438) + (xy 153.119405 76.465642) + (xy 153.119402 76.465644) + (xy 153.114894 76.468801) + (xy 152.953801 76.629894) + (xy 152.950644 76.634403) + (xy 152.950642 76.634405) + (xy 152.896472 76.711768) + (xy 152.823128 76.816513) + (xy 152.820805 76.821495) + (xy 152.820802 76.8215) + (xy 152.765253 76.940627) + (xy 152.726847 77.022989) + (xy 152.667883 77.243047) + (xy 152.648027 77.47) + (xy 142.9004 77.47) + (xy 142.9004 67.7408) + (xy 142.920402 67.672679) + (xy 142.974058 67.626186) + (xy 143.0264 67.6148) + (xy 172.695811 67.6148) + ) + ) + ) +) diff --git a/qa/data/solder_mask_bridge_test.kicad_pro b/qa/data/solder_mask_bridge_test.kicad_pro new file mode 100644 index 0000000000..4d1e7cc625 --- /dev/null +++ b/qa/data/solder_mask_bridge_test.kicad_pro @@ -0,0 +1,497 @@ +{ + "board": { + "design_settings": { + "defaults": { + "board_outline_line_width": 0.049999999999999996, + "copper_line_width": 0.19999999999999998, + "copper_text_italic": false, + "copper_text_size_h": 1.5, + "copper_text_size_v": 1.5, + "copper_text_thickness": 0.3, + "copper_text_upright": true, + "courtyard_line_width": 0.049999999999999996, + "dimension_precision": 1, + "dimension_units": 2, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": false, + "text_position": 0, + "units_format": 1 + }, + "fab_line_width": 0.09999999999999999, + "fab_text_italic": false, + "fab_text_size_h": 1.0, + "fab_text_size_v": 1.0, + "fab_text_thickness": 0.15, + "fab_text_upright": true, + "other_line_width": 0.09999999999999999, + "other_text_italic": false, + "other_text_size_h": 1.0, + "other_text_size_v": 1.0, + "other_text_thickness": 0.15, + "other_text_upright": true, + "pads": { + "drill": 0.8, + "height": 1.6, + "width": 2.3 + }, + "silk_line_width": 0.12, + "silk_text_italic": false, + "silk_text_size_h": 1.0, + "silk_text_size_v": 1.0, + "silk_text_thickness": 0.15, + "silk_text_upright": true, + "zones": { + "45_degree_only": false, + "min_clearance": 0.44999999999999996 + } + }, + "diff_pair_dimensions": [ + { + "gap": 0.0, + "via_gap": 0.0, + "width": 0.0 + } + ], + "drc_exclusions": [ + "lib_footprint_issues|154305000|81417500|3bd3a1e0-b35d-4974-99fa-33d3eee5d601|00000000-0000-0000-0000-000000000000" + ], + "meta": { + "version": 2 + }, + "rule_severities": { + "annular_width": "error", + "clearance": "error", + "copper_edge_clearance": "error", + "copper_sliver": "warning", + "courtyards_overlap": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", + "duplicate_footprints": "warning", + "extra_footprint": "warning", + "hole_clearance": "error", + "hole_near_hole": "error", + "invalid_outline": "error", + "item_on_disabled_layer": "error", + "items_not_allowed": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "error", + "malformed_courtyard": "error", + "microvia_drill_out_of_range": "error", + "missing_courtyard": "ignore", + "missing_footprint": "warning", + "net_conflict": "warning", + "npth_inside_courtyard": "ignore", + "padstack": "error", + "pth_inside_courtyard": "ignore", + "shorting_items": "error", + "silk_over_copper": "error", + "silk_overlap": "error", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", + "too_many_vias": "error", + "track_dangling": "warning", + "track_width": "error", + "tracks_crossing": "error", + "unconnected_items": "error", + "unresolved_variable": "error", + "via_dangling": "warning", + "zone_has_empty_net": "error", + "zones_intersect": "error" + }, + "rules": { + "allow_blind_buried_vias": false, + "allow_microvias": false, + "max_error": 0.005, + "min_clearance": 0.0, + "min_copper_edge_clearance": 0.01, + "min_hole_clearance": 0.0, + "min_hole_to_hole": 0.25, + "min_microvia_diameter": 0.19999999999999998, + "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.09999999999999999, + "min_text_height": 0.6, + "min_text_thickness": 0.09999999999999999, + "min_through_hole_diameter": 0.3, + "min_track_width": 0.19999999999999998, + "min_via_annular_width": 0.049999999999999996, + "min_via_annulus": 0.049999999999999996, + "min_via_diameter": 0.39999999999999997, + "use_height_for_length_calcs": true + }, + "track_widths": [ + 0.0, + 0.2, + 0.4, + 1.0, + 2.0 + ], + "via_dimensions": [ + { + "diameter": 0.0, + "drill": 0.0 + } + ], + "zones_allow_external_fillets": false, + "zones_use_no_outline": true + }, + "layer_presets": [ + { + "activeLayer": -2, + "layers": [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 41, + 44, + 49, + 50 + ], + "name": "Fabrication", + "renderLayers": [ + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 143, + 144 + ] + } + ] + }, + "boards": [], + "cvpcb": { + "equivalence_files": [] + }, + "erc": { + "meta": { + "version": 0 + }, + "pin_map": [ + [ + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 2 + ], + [ + 0, + 2, + 0, + 1, + 0, + 1, + 0, + 2, + 2, + 2, + 2 + ], + [ + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 2 + ], + [ + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 2, + 1, + 1, + 2 + ], + [ + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2 + ], + [ + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 2 + ], + [ + 0, + 2, + 1, + 2, + 0, + 1, + 0, + 2, + 2, + 2, + 2 + ], + [ + 0, + 2, + 0, + 1, + 0, + 1, + 0, + 2, + 0, + 0, + 2 + ], + [ + 0, + 2, + 1, + 1, + 0, + 1, + 0, + 2, + 0, + 0, + 2 + ], + [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2 + ] + ], + "rule_severities": { + "bus_definition_conflict": "error", + "bus_label_syntax": "error", + "bus_to_bus_conflict": "error", + "bus_to_net_conflict": "error", + "different_unit_footprint": "error", + "different_unit_net": "error", + "duplicate_sheet_names": "error", + "global_label_dangling": "error", + "hier_label_mismatch": "error", + "label_dangling": "error", + "lib_symbol_issues": "warning", + "multiple_net_names": "error", + "net_not_bus_member": "error", + "no_connect_connected": "error", + "no_connect_dangling": "error", + "pin_not_connected": "error", + "pin_not_driven": "error", + "pin_to_pin": "warning", + "similar_labels": "error", + "unresolved_variable": "error", + "wire_dangling": "error" + } + }, + "libraries": { + "pinned_footprint_libs": [], + "pinned_symbol_libs": [] + }, + "meta": { + "filename": "LoRaNode.kicad_pro", + "version": 1 + }, + "net_settings": { + "classes": [ + { + "bus_width": 12.0, + "clearance": 0.2, + "diff_pair_gap": 0.25, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.25, + "via_diameter": 0.85, + "via_drill": 0.4, + "wire_width": 6.0 + }, + { + "bus_width": 12.0, + "clearance": 0.25, + "diff_pair_gap": 0.25, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "antenna", + "nets": [ + "/ANTENNA" + ], + "pcb_color": "rgba(0, 0, 0, 0.000)", + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 1.27, + "via_diameter": 0.85, + "via_drill": 0.4, + "wire_width": 6.0 + } + ], + "meta": { + "version": 1 + }, + "net_colors": null + }, + "pcbnew": { + "last_paths": { + "gencad": "", + "idf": "", + "netlist": "", + "specctra_dsn": "", + "step": "", + "vmrl": "", + "vrml": "" + }, + "page_layout_descr_file": "" + }, + "schematic": { + "drawing": { + "default_bus_thickness": 12.0, + "default_junction_size": 40.0, + "default_line_thickness": 6.0, + "default_text_size": 50.0, + "default_wire_thickness": 6.0, + "field_names": [], + "intersheets_ref_prefix": "[", + "intersheets_ref_short": false, + "intersheets_ref_show": false, + "intersheets_ref_suffix": "]", + "pin_symbol_size": 25.0, + "text_offset_ratio": 0.3 + }, + "legacy_lib_dir": "", + "legacy_lib_list": [], + "meta": { + "version": 0 + }, + "net_format_name": "", + "page_layout_descr_file": "", + "plot_directory": "./", + "spice_adjust_passive_values": false, + "spice_external_command": "spice \"%I\"", + "subpart_first_id": 65, + "subpart_id_separator": 0 + }, + "sheets": [ + [ + "285a99a7-a1a2-4aa6-812b-d3c3533a57d7", + "" + ], + [ + "00000000-0000-0000-0000-00005d8236a8", + "battery-protection" + ], + [ + "00000000-0000-0000-0000-00005d8260b7", + "voltage-regulation" + ], + [ + "00000000-0000-0000-0000-00005d825820", + "RS485-interface" + ], + [ + "00000000-0000-0000-0000-00005d84b44c", + "I2C-interface" + ] + ], + "text_variables": {} +} diff --git a/qa/drc_proto/CMakeLists.txt b/qa/drc_proto/CMakeLists.txt index 3dfc688f93..d5adb8bf33 100644 --- a/qa/drc_proto/CMakeLists.txt +++ b/qa/drc_proto/CMakeLists.txt @@ -46,7 +46,7 @@ add_executable( drc_proto ../../pcbnew/drc/drc_test_provider_via_diameter.cpp ../../pcbnew/drc/drc_test_provider_schematic_parity.cpp ../../pcbnew/drc/drc_test_provider_misc.cpp - ../../pcbnew/drc/drc_test_provider_silk_to_mask.cpp + ../../pcbnew/drc/drc_test_provider_solder_mask.cpp ../../pcbnew/drc/drc_test_provider_silk_clearance.cpp ../../pcbnew/drc/drc_test_provider_matched_length.cpp ../../pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp diff --git a/qa/pcbnew/CMakeLists.txt b/qa/pcbnew/CMakeLists.txt index cfc927f307..d669cb59cc 100644 --- a/qa/pcbnew/CMakeLists.txt +++ b/qa/pcbnew/CMakeLists.txt @@ -44,6 +44,7 @@ set( QA_PCBNEW_SRCS drc/test_drc_courtyard_invalid.cpp drc/test_drc_courtyard_overlap.cpp drc/test_drc_regressions.cpp + drc/test_solder_mask_bridging.cpp plugins/altium/test_altium_rule_transformer.cpp diff --git a/qa/pcbnew/drc/test_solder_mask_bridging.cpp b/qa/pcbnew/drc/test_solder_mask_bridging.cpp new file mode 100644 index 0000000000..bd90cd142d --- /dev/null +++ b/qa/pcbnew/drc/test_solder_mask_bridging.cpp @@ -0,0 +1,83 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 KiCad Developers, see AUTHORS.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 +#include +#include + + +struct DRC_REGRESSION_TEST_FIXTURE +{ + DRC_REGRESSION_TEST_FIXTURE() : + m_settingsManager( true /* headless */ ) + { } + + SETTINGS_MANAGER m_settingsManager; + std::unique_ptr m_board; +}; + + +BOOST_FIXTURE_TEST_CASE( DRCSolderMaskBridgingTest, DRC_REGRESSION_TEST_FIXTURE ) +{ + KI_TEST::LoadBoard( m_settingsManager, "solder_mask_bridge_test", m_board ); + KI_TEST::FillZones( m_board.get(), 6 ); + + std::vector violations; + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + bds.m_DRCEngine->SetViolationHandler( + [&]( const std::shared_ptr& aItem, wxPoint aPos ) + { + PCB_MARKER temp( aItem, aPos ); + + if( bds.m_DrcExclusions.find( temp.Serialize() ) == bds.m_DrcExclusions.end() ) + violations.push_back( *aItem ); + } ); + + bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false ); + + if( violations.size() == 4 ) + { + BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning + BOOST_TEST_MESSAGE( "DRC solder mask bridge test passed" ); + } + else + { + BOOST_CHECK_EQUAL( violations.size(), 4 ); + + std::map itemMap; + m_board->FillItemMap( itemMap ); + + for( const DRC_ITEM& item : violations ) + { + BOOST_TEST_MESSAGE( item.ShowReport( EDA_UNITS::INCHES, RPT_SEVERITY_ERROR, + itemMap ) ); + } + + BOOST_ERROR( "DRC solder mask bridge test failed" ); + } +} diff --git a/qa/pns/CMakeLists.txt b/qa/pns/CMakeLists.txt index 5ab10c9a52..8083cd50df 100644 --- a/qa/pns/CMakeLists.txt +++ b/qa/pns/CMakeLists.txt @@ -45,7 +45,7 @@ add_executable( test_pns ../../pcbnew/drc/drc_test_provider_via_diameter.cpp ../../pcbnew/drc/drc_test_provider_schematic_parity.cpp ../../pcbnew/drc/drc_test_provider_misc.cpp - ../../pcbnew/drc/drc_test_provider_silk_to_mask.cpp + ../../pcbnew/drc/drc_test_provider_solder_mask.cpp ../../pcbnew/drc/drc_test_provider_silk_clearance.cpp ../../pcbnew/drc/drc_test_provider_matched_length.cpp ../../pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp