ADDED: Minimum copper connection width DRC check

Checks all copper connections in each net/layer for minimum width
setting.

Fixes https://gitlab.com/kicad/code/kicad/issues/9870
This commit is contained in:
Seth Hillbrand 2022-07-11 19:26:56 +00:00
parent e454595348
commit 3081023b5e
25 changed files with 2108 additions and 14 deletions

View File

@ -504,6 +504,7 @@ set( BMAPS_MID
via_sketch
via_diameter
via_hole_diameter
width_conn
width_track_via
width_track
zip

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Слой_1"
data-name="Слой 1"
viewBox="0 0 24 24"
version="1.1"
sodipodi:docname="width_conn.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2099"
id="namedview30"
showgrid="true"
inkscape:zoom="32"
inkscape:cx="9.3450346"
inkscape:cy="18.573014"
inkscape:window-x="0"
inkscape:window-y="37"
inkscape:window-maximized="1"
inkscape:document-rotation="0"
inkscape:current-layer="Слой_1">
<inkscape:grid
type="xygrid"
id="grid_kicad"
spacingx="0.5"
spacingy="0.5"
color="#9999ff"
opacity="0.13"
empspacing="2" />
</sodipodi:namedview>
<metadata
id="metadata43">
<rdf:RDF>
<cc:Work
rdf:about="">
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>width_track</dc:title>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs160374">
<style
id="style160372">.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;}.cls-1{stroke:#1a81c4;stroke-width:2px;}.cls-2{stroke:#545454;}.cls-3{fill:#545454;}</style>
</defs>
<title
id="title160376">width_track</title>
<path
id="polyline160378"
style="fill:#42b8eb;fill-opacity:1;stroke:#42b8eb;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 19,20 h 4 V 4 h -4 l -3,8 3,8"
sodipodi:nodetypes="cccccc" />
<line
class="cls-2"
x1="8.5"
y1="10"
x2="14.5"
y2="10"
id="line160380"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ded3dd;stroke-opacity:1" />
<polygon
class="cls-5"
points="16.038,14.028 13.984,16.082 11.178,11.222 "
id="polygon858"
transform="matrix(-1.5444005,-0.7071068,1.5444005,-0.7071068,11.432046,25.339192)"
style="fill:#ded3dd;stroke:none;stroke-width:0.676648;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1" />
<path
id="polyline160378-3"
style="fill:#42b8eb;fill-opacity:1;stroke:#42b8eb;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 1,4 V 20 H 4 L 7,12 4,4 H 1"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#42b8eb;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7,12 h 9.666667"
id="path959" />
<line
class="cls-2"
x1="11.5"
y1="7"
x2="11.5"
y2="1"
id="line993"
style="stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ded3dd;stroke-opacity:1" />
<line
class="cls-2"
x1="14.5"
y1="14"
x2="8.5"
y2="14"
id="line995"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ded3dd;stroke-opacity:1" />
<polygon
class="cls-5"
points="16.038,14.028 13.984,16.082 11.178,11.222 "
id="polygon997"
transform="matrix(1.5444005,0.7071068,-1.5444005,0.7071068,11.567954,-1.339192)"
style="fill:#ded3dd;stroke:none;stroke-width:0.676648;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1" />
<line
class="cls-2"
x1="11.5"
y1="17"
x2="11.5"
y2="23"
id="line999"
style="stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ded3dd;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Слой_1"
data-name="Слой 1"
viewBox="0 0 24 24"
version="1.1"
sodipodi:docname="width_conn.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<path
id="polyline160378"
style="fill:#1a81c4;fill-opacity:1;stroke:#1a81c4;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 19,20 h 4 V 4 h -4 l -3,8 3,8"
sodipodi:nodetypes="cccccc" />
<path
id="polyline160378-3"
style="fill:#1a81c4;fill-opacity:1;stroke:#1a81c4;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 1,4 V 20 H 4 L 7,12 4,4 H 1"
sodipodi:nodetypes="cccccc" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1793"
inkscape:window-height="959"
id="namedview30"
showgrid="true"
inkscape:zoom="22.627417"
inkscape:cx="10.171091"
inkscape:cy="14.382296"
inkscape:window-x="4119"
inkscape:window-y="214"
inkscape:window-maximized="0"
inkscape:document-rotation="0"
inkscape:current-layer="Слой_1">
<inkscape:grid
type="xygrid"
id="grid_kicad"
spacingx="0.5"
spacingy="0.5"
color="#9999ff"
opacity="0.13"
empspacing="2" />
</sodipodi:namedview>
<metadata
id="metadata43">
<rdf:RDF>
<cc:Work
rdf:about="">
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>width_track</dc:title>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs160374">
<style
id="style160372">.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;}.cls-1{stroke:#1a81c4;stroke-width:2px;}.cls-2{stroke:#545454;}.cls-3{fill:#545454;}</style>
</defs>
<title
id="title160376">width_track</title>
<line
class="cls-2"
x1="8.5"
y1="10"
x2="14.5"
y2="10"
id="line160380"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
<polygon
class="cls-5"
points="16.038,14.028 13.984,16.082 11.178,11.222 "
id="polygon858"
transform="matrix(-1.5444005,-0.7071068,1.5444005,-0.7071068,11.432046,25.339192)"
style="fill:#545454;stroke:none;stroke-width:0.676648;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<path
style="fill:none;stroke:#1a81c4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7,12 h 9.666667"
id="path959" />
<line
class="cls-2"
x1="11.5"
y1="7"
x2="11.5"
y2="1"
id="line993"
style="stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none" />
<line
class="cls-2"
x1="14.5"
y1="14"
x2="8.5"
y2="14"
id="line995"
style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
<polygon
class="cls-5"
points="16.038,14.028 13.984,16.082 11.178,11.222 "
id="polygon997"
transform="matrix(1.5444005,0.7071068,-1.5444005,0.7071068,11.567954,-1.339192)"
style="fill:#545454;stroke:none;stroke-width:0.676648;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
<line
class="cls-2"
x1="11.5"
y1="17"
x2="11.5"
y2="23"
id="line999"
style="stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none" />
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -191,8 +191,6 @@ static const wxChar AllowManualCanvasScale[] = wxT( "AllowManualCanvasScale" );
static const wxChar UpdateUIEventInterval[] = wxT( "UpdateUIEventInterval" );
static const wxChar AllowTeardrops[] = wxT( "AllowTeardrops" );
static const wxChar V3DRT_BevelHeight_um[] = wxT( "V3DRT_BevelHeight_um" );
static const wxChar V3DRT_BevelExtentFactor[] = wxT( "V3DRT_BevelExtentFactor" );

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2022 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
@ -525,6 +525,7 @@ const std::vector<BITMAP_INFO> g_BitmapInfo = {
{ BITMAPS::via_sketch, wxT( "via_sketch_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::via_diameter, wxT( "via_diameter_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::via_hole_diameter, wxT( "via_hole_diameter_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::width_conn, wxT( "width_conn_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::width_track_via, wxT( "width_track_via_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::width_track, wxT( "width_track_24.png" ), 24, wxT( "light" ) },
{ BITMAPS::zip, wxT( "zip_24.png" ), 24, wxT( "light" ) },
@ -900,6 +901,7 @@ const std::vector<BITMAP_INFO> g_BitmapInfo = {
{ BITMAPS::via_sketch, wxT( "via_sketch_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::via_diameter, wxT( "via_diameter_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::via_hole_diameter, wxT( "via_hole_diameter_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::width_conn, wxT( "width_conn_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::width_track_via, wxT( "width_track_via_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::width_track, wxT( "width_track_dark_24.png" ), 24, wxT( "dark" ) },
{ BITMAPS::zip, wxT( "zip_dark_24.png" ), 24, wxT( "dark" ) },

View File

@ -596,6 +596,7 @@ enum class BITMAPS : unsigned int
viewlibs_icon,
visibility,
visibility_off,
width_conn,
width_track,
width_track_via,
wizard_add_fplib_icon,

View File

@ -76,6 +76,7 @@
#define DEFAULT_CUSTOMDPAIRVIAGAP 0.18
#define DEFAULT_MINCLEARANCE 0.0 // overall min clearance
#define DEFAULT_MINCONNECTION 0.0 // overall min connection width
#define DEFAULT_TRACKMINWIDTH 0.2 // track width min value
#define DEFAULT_VIASMINSIZE 0.4 // vias (not micro vias) min diameter
#define DEFAULT_MINTHROUGHDRILL 0.3 // through holes (not micro vias) min drill diameter
@ -682,6 +683,7 @@ public:
bool m_TempOverrideTrackWidth; // use selected track width temporarily even when
// using connected track width
int m_MinClearance; // overall min clearance
int m_MinConn; // overall min connection width
int m_TrackMinWidth; // overall min track width
int m_ViasMinAnnularWidth; // overall minimum width of the via copper ring
int m_ViasMinSize; // overall vias (not micro vias) min diameter

View File

@ -239,6 +239,7 @@ set( PCBNEW_DRC_SRCS
drc/drc_test_provider_annular_width.cpp
drc/drc_test_provider_disallow.cpp
drc/drc_test_provider_connectivity.cpp
drc/drc_test_provider_connection_width.cpp
drc/drc_test_provider_copper_clearance.cpp
drc/drc_test_provider_physical_clearance.cpp
drc/drc_test_provider_courtyard_clearance.cpp

View File

@ -141,6 +141,7 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
m_customDiffPair.m_ViaGap = Millimeter2iu( DEFAULT_CUSTOMDPAIRVIAGAP );
m_MinClearance = Millimeter2iu( DEFAULT_MINCLEARANCE );
m_MinConn = Millimeter2iu( DEFAULT_MINCONNECTION );
m_TrackMinWidth = Millimeter2iu( DEFAULT_TRACKMINWIDTH );
m_ViasMinAnnularWidth = Millimeter2iu( DEFAULT_VIASMINSIZE - DEFAULT_MINTHROUGHDRILL ) / 2;
m_ViasMinSize = Millimeter2iu( DEFAULT_VIASMINSIZE );
@ -188,6 +189,9 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = RPT_SEVERITY_WARNING;
// TODO: Change to warning after testing
m_DRCSeverities[ DRCE_CONNECTION_WIDTH ] = RPT_SEVERITY_IGNORE;
m_MaxError = ARC_HIGH_DEF;
m_ZoneKeepExternalFillets = false;
m_UseHeightForLengthCalcs = true;
@ -231,6 +235,10 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
&m_MinClearance, Millimeter2iu( DEFAULT_MINCLEARANCE ),
Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), MM_PER_IU ) );
m_params.emplace_back( new PARAM_SCALED<int>( "rules.min_connection",
&m_MinConn, Millimeter2iu( DEFAULT_MINCONNECTION ),
Millimeter2iu( 0.00 ), Millimeter2iu( 100.0 ), MM_PER_IU ) );
m_params.emplace_back( new PARAM_SCALED<int>( "rules.min_track_width",
&m_TrackMinWidth, Millimeter2iu( DEFAULT_TRACKMINWIDTH ),
Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), MM_PER_IU ) );
@ -818,6 +826,7 @@ void BOARD_DESIGN_SETTINGS::initFromOther( const BOARD_DESIGN_SETTINGS& aOther )
m_CurrentViaType = aOther.m_CurrentViaType;
m_UseConnectedTrackWidth = aOther.m_UseConnectedTrackWidth;
m_MinClearance = aOther.m_MinClearance;
m_MinConn = aOther.m_MinConn;
m_TrackMinWidth = aOther.m_TrackMinWidth;
m_ViasMinAnnularWidth = aOther.m_ViasMinAnnularWidth;
m_ViasMinSize = aOther.m_ViasMinSize;

View File

@ -36,6 +36,7 @@
PANEL_SETUP_CONSTRAINTS::PANEL_SETUP_CONSTRAINTS( PAGED_DIALOG* aParent, PCB_EDIT_FRAME* aFrame ) :
PANEL_SETUP_CONSTRAINTS_BASE( aParent->GetTreebook() ),
m_minClearance( aFrame, m_clearanceTitle, m_clearanceCtrl, m_clearanceUnits ),
m_minConn( aFrame, m_MinConnTitle, m_MinConnCtrl, m_MinConnUnits ),
m_trackMinWidth( aFrame, m_TrackMinWidthTitle, m_TrackMinWidthCtrl, m_TrackMinWidthUnits ),
m_viaMinAnnulus( aFrame, m_ViaMinAnnulusTitle, m_ViaMinAnnulusCtrl, m_ViaMinAnnulusUnits ),
m_viaMinSize( aFrame, m_ViaMinTitle, m_SetViasMinSizeCtrl, m_ViaMinUnits ),
@ -78,6 +79,7 @@ bool PANEL_SETUP_CONSTRAINTS::TransferDataToWindow()
m_minResolvedSpokeCountCtrl->SetValue( m_BrdSettings->m_MinResolvedSpokes );
m_minClearance.SetValue( m_BrdSettings->m_MinClearance );
m_minConn.SetValue( m_BrdSettings->m_MinConn );
m_trackMinWidth.SetValue( m_BrdSettings->m_TrackMinWidth );
m_viaMinAnnulus.SetValue( m_BrdSettings->m_ViasMinAnnularWidth );
m_viaMinSize.SetValue(m_BrdSettings->m_ViasMinSize );
@ -103,6 +105,9 @@ bool PANEL_SETUP_CONSTRAINTS::TransferDataFromWindow()
if( !m_minClearance.Validate( 0, 10, EDA_UNITS::INCHES ) )
return false;
if( !m_minConn.Validate( 0, 10, EDA_UNITS::INCHES ) )
return false;
if( !m_trackMinWidth.Validate( 0, 10, EDA_UNITS::INCHES ) )
return false;
@ -138,6 +143,7 @@ bool PANEL_SETUP_CONSTRAINTS::TransferDataFromWindow()
m_BrdSettings->m_MinResolvedSpokes = m_minResolvedSpokeCountCtrl->GetValue();
m_BrdSettings->m_MinClearance = m_minClearance.GetValue();
m_BrdSettings->m_MinConn = m_minConn.GetValue();
m_BrdSettings->m_TrackMinWidth = m_trackMinWidth.GetValue();
m_BrdSettings->m_ViasMinAnnularWidth = m_viaMinAnnulus.GetValue();
m_BrdSettings->m_ViasMinSize = m_viaMinSize.GetValue();
@ -171,6 +177,7 @@ bool PANEL_SETUP_CONSTRAINTS::Show( bool aShow )
m_spokeBitmap->SetBitmap( KiBitmap( BITMAPS::thermal_spokes ) );
m_bitmapClearance->SetBitmap( KiBitmap( BITMAPS::ps_diff_pair_gap ) );
m_bitmapMinTrackWidth->SetBitmap( KiBitmap( BITMAPS::width_track ) );
m_bitmapMinConn->SetBitmap( KiBitmap( BITMAPS::width_conn ) );
m_bitmapMinViaAnnulus->SetBitmap( KiBitmap( BITMAPS::via_annulus ) );
m_bitmapMinViaDiameter->SetBitmap( KiBitmap( BITMAPS::via_diameter ) );
m_bitmapMinViaDrill->SetBitmap( KiBitmap( BITMAPS::via_hole_diameter ) );

View File

@ -50,6 +50,7 @@ private:
public:
UNIT_BINDER m_minClearance;
UNIT_BINDER m_minConn;
UNIT_BINDER m_trackMinWidth;
UNIT_BINDER m_viaMinAnnulus;
UNIT_BINDER m_viaMinSize;

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// C++ code generated with wxFormBuilder (version 3.10.0)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -208,6 +208,23 @@ PANEL_SETUP_CONSTRAINTS_BASE::PANEL_SETUP_CONSTRAINTS_BASE( wxWindow* parent, wx
m_TrackMinWidthUnits->Wrap( -1 );
fgFeatureConstraints->Add( m_TrackMinWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxLEFT, 5 );
m_bitmapMinConn = new wxStaticBitmap( m_scrolledWindow, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
fgFeatureConstraints->Add( m_bitmapMinConn, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 );
m_MinConnTitle = new wxStaticText( m_scrolledWindow, wxID_ANY, _("Minimum connection width:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
m_MinConnTitle->Wrap( -1 );
fgFeatureConstraints->Add( m_MinConnTitle, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxRIGHT, 5 );
m_MinConnCtrl = new wxTextCtrl( m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_MinConnCtrl->SetToolTip( _("The minimum track width. If set, this is an absolute minimum and cannot be reduced by netclasses, custom rules, or other settings.") );
m_MinConnCtrl->SetMinSize( wxSize( 120,-1 ) );
fgFeatureConstraints->Add( m_MinConnCtrl, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxBOTTOM|wxEXPAND|wxTOP, 5 );
m_MinConnUnits = new wxStaticText( m_scrolledWindow, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
m_MinConnUnits->Wrap( -1 );
fgFeatureConstraints->Add( m_MinConnUnits, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxLEFT, 5 );
m_bitmapMinViaAnnulus = new wxStaticBitmap( m_scrolledWindow, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
fgFeatureConstraints->Add( m_bitmapMinViaAnnulus, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<FileVersion major="1" minor="16" />
<object class="Project" expanded="1">
<property name="class_decoration"></property>
<property name="code_generation">C++</property>
@ -14,6 +14,7 @@
<property name="file">panel_setup_constraints_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">panel_setup_constraints_base</property>
@ -25,6 +26,7 @@
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">1</property>
<property name="use_microsoft_bom">0</property>
<object class="Panel" expanded="1">
@ -46,6 +48,7 @@
<property name="size">-1,-1</property>
<property name="subclass">; forward_declare</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
@ -2118,6 +2121,250 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticBitmap" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_bitmapMinConn</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Minimum connection width:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_MinConnTitle</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxALIGN_LEFT</property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxBOTTOM|wxEXPAND|wxTOP</property>
<property name="proportion">0</property>
<object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">120,-1</property>
<property name="moveable">1</property>
<property name="name">m_MinConnCtrl</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip">The minimum track width. If set, this is an absolute minimum and cannot be reduced by netclasses, custom rules, or other settings.</property>
<property name="validator_data_type">wxString</property>
<property name="validator_style">wxFILTER_NUMERIC</property>
<property name="validator_type">wxTextValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">mm</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_MinConnUnits</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxALIGN_LEFT</property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// C++ code generated with wxFormBuilder (version 3.10.0)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -70,6 +70,10 @@ class PANEL_SETUP_CONSTRAINTS_BASE : public wxPanel
wxStaticText* m_TrackMinWidthTitle;
wxTextCtrl* m_TrackMinWidthCtrl;
wxStaticText* m_TrackMinWidthUnits;
wxStaticBitmap* m_bitmapMinConn;
wxStaticText* m_MinConnTitle;
wxTextCtrl* m_MinConnCtrl;
wxStaticText* m_MinConnUnits;
wxStaticBitmap* m_bitmapMinViaAnnulus;
wxStaticText* m_ViaMinAnnulusTitle;
wxTextCtrl* m_ViaMinAnnulusCtrl;
@ -131,6 +135,7 @@ class PANEL_SETUP_CONSTRAINTS_BASE : public wxPanel
wxScrolledWindow* m_scrolledWindow;
PANEL_SETUP_CONSTRAINTS_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_CONSTRAINTS_BASE();
};

View File

@ -108,6 +108,10 @@ DRC_ITEM DRC_ITEM::holesCoLocated( DRCE_DRILLED_HOLES_COLOCATED,
_( "Drilled holes co-located" ),
wxT( "holes_co_located" ) );
DRC_ITEM DRC_ITEM::connectionWidth( DRCE_CONNECTION_WIDTH,
_( "Copper connection too narrow" ),
wxT( "connection_width" ) );
DRC_ITEM DRC_ITEM::trackWidth( DRCE_TRACK_WIDTH,
_( "Track width" ),
wxT( "track_width" ) );
@ -276,6 +280,7 @@ std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( {
DRC_ITEM::invalidOutline,
DRC_ITEM::copperSliver,
DRC_ITEM::solderMaskBridge,
DRC_ITEM::connectionWidth,
DRC_ITEM::heading_schematic_parity,
DRC_ITEM::duplicateFootprints,
@ -335,6 +340,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
case DRCE_DRILLED_HOLES_TOO_CLOSE: return std::make_shared<DRC_ITEM>( holeNearHole );
case DRCE_DRILLED_HOLES_COLOCATED: return std::make_shared<DRC_ITEM>( holesCoLocated );
case DRCE_HOLE_CLEARANCE: return std::make_shared<DRC_ITEM>( holeClearance );
case DRCE_CONNECTION_WIDTH: return std::make_shared<DRC_ITEM>( connectionWidth );
case DRCE_TRACK_WIDTH: return std::make_shared<DRC_ITEM>( trackWidth );
case DRCE_ANNULAR_WIDTH: return std::make_shared<DRC_ITEM>( annularWidth );
case DRCE_DRILL_OUT_OF_RANGE: return std::make_shared<DRC_ITEM>( drillTooSmall );

View File

@ -53,6 +53,7 @@ enum PCB_DRC_CODE {
DRCE_HOLE_CLEARANCE, //
DRCE_TRACK_WIDTH, // Track width is too small or too large
DRCE_ANNULAR_WIDTH, // Via size and drill leave annular ring too small
DRCE_CONNECTION_WIDTH, // Net connection too small
DRCE_DRILL_OUT_OF_RANGE, // Too small via or pad drill
DRCE_VIA_DIAMETER, // Via diameter checks (min/max)
DRCE_PADSTACK, // something is wrong with a pad or via stackup
@ -168,6 +169,7 @@ private:
static DRC_ITEM holeNearHole;
static DRC_ITEM holesCoLocated;
static DRC_ITEM holeClearance;
static DRC_ITEM connectionWidth;
static DRC_ITEM trackWidth;
static DRC_ITEM annularWidth;
static DRC_ITEM drillTooSmall;

View File

@ -374,6 +374,30 @@ public:
return collision;
}
/**
* Gets the BOARD_ITEMs that overlap the specified point/layer
* @param aPt Position on the tree
* @param aLayer Layer to search
* @return vector of overlapping BOARD_ITEMS*
*/
std::set<BOARD_ITEM*> GetObjectsAt( const VECTOR2I& aPt, PCB_LAYER_ID aLayer, int aClearance = 0 )
{
std::set<BOARD_ITEM*> retval;
int min[2] = { aPt.x - aClearance, aPt.y - aClearance };
int max[2] = { aPt.x + aClearance, aPt.y + aClearance };
auto visitor =
[&]( ITEM_WITH_SHAPE* aItem ) -> bool
{
retval.insert( aItem->parent );
return true;
};
m_tree[aLayer]->Search( min, max, visitor );
return retval;
}
typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> LAYER_PAIR;
struct PAIR_INFO

View File

@ -0,0 +1,699 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 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 3
* 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/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <algorithm>
#include <atomic>
#include <deque>
#include <optional>
#include <utility>
#include <wx/debug.h>
#include <advanced_config.h>
#include <board.h>
#include <board_connected_item.h>
#include <board_design_settings.h>
#include <drc/drc_rule.h>
#include <drc/drc_item.h>
#include <drc/drc_test_provider.h>
#include <drc/drc_rtree.h>
#include <footprint.h>
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <math/box2.h>
#include <math/vector2d.h>
#include <pcb_shape.h>
#include <progress_reporter.h>
#include <thread_pool.h>
#include <pcb_track.h>
#include <pad.h>
#include <zone.h>
/*
Checks for copper connections that are less than the specified minimum width
Errors generated:
- DRCE_CONNECTION_WIDTH
*/
class DRC_TEST_PROVIDER_CONNECTION_WIDTH : public DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER_CONNECTION_WIDTH()
{
}
virtual ~DRC_TEST_PROVIDER_CONNECTION_WIDTH()
{
}
virtual bool Run() override;
virtual const wxString GetName() const override
{
return wxT( "copper width" );
};
virtual const wxString GetDescription() const override
{
return wxT( "Checks copper nets for connections less than a specified minimum" );
}
private:
wxString layerDesc( PCB_LAYER_ID aLayer );
};
class POLYGON_TEST
{
public:
POLYGON_TEST( int aLimit ) :
m_limit( aLimit )
{
};
bool FindPairs( const SHAPE_LINE_CHAIN& aPoly )
{
m_hits.clear();
m_vertices.clear();
m_bbox = aPoly.BBox();
createList( aPoly );
m_vertices.front().updateList();
Vertex* p = m_vertices.front().next;
std::set<Vertex*> all_hits;
while( p != &m_vertices.front() )
{
Vertex* match = nullptr;
// Only run the expensive search if we don't already have a match for the point
if( ( all_hits.empty() || all_hits.count( p ) == 0 ) && ( match = getKink( p ) ) )
{
if( !all_hits.count( match ) && m_hits.emplace( p->i, match->i ).second )
{
all_hits.emplace( p );
all_hits.emplace( match );
all_hits.emplace( p->next );
all_hits.emplace( p->prev );
all_hits.emplace( match->next );
all_hits.emplace( match->prev );
}
}
p = p->next;
}
return !m_hits.empty();
}
std::set<std::pair<int, int>>& GetVertices()
{
return m_hits;
}
private:
struct Vertex
{
Vertex( int aIndex, double aX, double aY, POLYGON_TEST* aParent ) :
i( aIndex ),
x( aX ),
y( aY ),
parent( aParent )
{
}
Vertex& operator=( const Vertex& ) = delete;
Vertex& operator=( Vertex&& ) = delete;
bool operator==( const Vertex& rhs ) const
{
return this->x == rhs.x && this->y == rhs.y;
}
bool operator!=( const Vertex& rhs ) const { return !( *this == rhs ); }
/**
* Remove the node from the linked list and z-ordered linked list.
*/
void remove()
{
next->prev = prev;
prev->next = next;
if( prevZ )
prevZ->nextZ = nextZ;
if( nextZ )
nextZ->prevZ = prevZ;
next = nullptr;
prev = nullptr;
nextZ = nullptr;
prevZ = nullptr;
}
void updateOrder()
{
if( !z )
z = parent->zOrder( x, y );
}
/**
* After inserting or changing nodes, this function should be called to
* remove duplicate vertices and ensure z-ordering is correct.
*/
void updateList()
{
Vertex* p = next;
while( p != this )
{
/**
* Remove duplicates
*/
if( *p == *p->next )
{
p = p->prev;
p->next->remove();
if( p == p->next )
break;
}
p->updateOrder();
p = p->next;
};
updateOrder();
zSort();
}
/**
* Sort all vertices in this vertex's list by their Morton code.
*/
void zSort()
{
std::deque<Vertex*> queue;
queue.push_back( this );
for( Vertex* p = next; p && p != this; p = p->next )
queue.push_back( p );
std::sort( queue.begin(), queue.end(), []( const Vertex* a, const Vertex* b )
{
return a->z < b->z;
} );
Vertex* prev_elem = nullptr;
for( Vertex* elem : queue )
{
if( prev_elem )
prev_elem->nextZ = elem;
elem->prevZ = prev_elem;
prev_elem = elem;
}
prev_elem->nextZ = nullptr;
}
const int i;
const double x;
const double y;
POLYGON_TEST* parent;
// previous and next vertices nodes in a polygon ring
Vertex* prev = nullptr;
Vertex* next = nullptr;
// z-order curve value
int32_t z = 0;
// previous and next nodes in z-order
Vertex* prevZ = nullptr;
Vertex* nextZ = nullptr;
};
/**
* Calculate the Morton code of the Vertex
* http://www.graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
*
*/
int32_t zOrder( const double aX, const double aY ) const
{
int32_t x = static_cast<int32_t>( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() );
int32_t y = static_cast<int32_t>( 32767.0 * ( aY - m_bbox.GetY() ) / m_bbox.GetHeight() );
x = ( x | ( x << 8 ) ) & 0x00FF00FF;
x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;
x = ( x | ( x << 2 ) ) & 0x33333333;
x = ( x | ( x << 1 ) ) & 0x55555555;
y = ( y | ( y << 8 ) ) & 0x00FF00FF;
y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;
y = ( y | ( y << 2 ) ) & 0x33333333;
y = ( y | ( y << 1 ) ) & 0x55555555;
return x | ( y << 1 );
}
/**
* Checks to see if there is a "substantial" protrusion in each polygon produced by the cut from
* aA to aB. Substantial in this case means that the polygon bulges out to a wider cross-section
* than the distance from aA to aB
* @param aA Starting point in the polygon
* @param aB Ending point in the polygon
* @return True if the two polygons are both "substantial"
*/
bool isSubstantial( Vertex* aA, Vertex* aB ) const
{
// `directions` is a bitfield where
// bit 0 = pos y
// bit 1 = neg y
// bit 2 = pos x
// bit 3 = neg x
// So, once directions = 15, we have all directions
int directions = 0;
// This is a failsafe in case of invalid lists. Never check
// more than the total number of points in m_vertices
size_t checked = 0;
size_t total_pts = m_vertices.size();
Vertex* p0 = aA;
Vertex* p;
Vertex* nz = p0->nextZ;
Vertex* pz = p0->prevZ;
auto same_point = []( const Vertex* a, const Vertex* b ) -> bool
{
return a && b && a->x == b->x && a->y == b->y;
};
// If we hit a fracture point, we want to continue around the
// edge we are working on and not switch to the pair edge
// However, this will depend on which direction the initial
// fracture hit is. If we find that we skip directly to
// a new fracture point, then we know that we are proceeding
// in the wrong direction from the fracture and should
// fall through to the next point
if( same_point( p0, nz ) &&
!( same_point( nz->next, nz->next->prevZ ) || same_point( nz->next, nz->next->nextZ ) ) )
p = nz->next;
else if( same_point( p0, pz ) &&
!( same_point( pz->next, pz->next->prevZ ) || same_point( pz->next, pz->next->nextZ ) ) )
p = pz->next;
else
p = p0->next;
while( p0 != aB && checked < total_pts && directions != 15 )
{
int bit2x = std::signbit( p->x - p0->x );
int bit2y = std::signbit( p->y - p0->y );
directions |= ( 1 << ( 2 + bit2x ) ) + ( 1 << bit2y );
p0 = p;
if( same_point( p0, p0->nextZ ) )
p = p->nextZ->next;
else if( same_point( p0, p0->prevZ ) )
p = p->prevZ->next;
else
p = p->next;
++checked;
}
wxCHECK_MSG( checked < total_pts, false, wxT( "Invalid polygon detected. Missing points to check" ) );
if( directions != 15 )
return false;
p0 = aA;
nz = p0->nextZ;
pz = p0->prevZ;
if( nz && same_point( p0, nz ) &&
!( same_point( nz->prev, nz->prev->nextZ ) || same_point( nz->prev, nz->prev->prevZ ) ) )
p = nz->prev;
else if( pz && same_point( p0, pz ) &&
!( same_point( pz->prev, pz->prev->nextZ ) || same_point( pz->prev, pz->prev->prevZ ) ) )
p = pz->prev;
else
p = p0->prev;
directions = 0;
checked = 0;
while( p0 != aB && checked < total_pts && directions != 15 )
{
int bit2x = std::signbit( p->x - p0->x );
int bit2y = std::signbit( p->y - p0->y );
directions |= ( 1 << ( 2 + bit2x ) ) + ( 1 << bit2y );
p0 = p;
if( same_point( p, p->nextZ ) )
p = p->nextZ->prev;
else if( same_point( p, p->prevZ ) )
p = p->prevZ->prev;
else
p = p->prev;
++checked;
}
wxCHECK_MSG( checked < total_pts, false, wxT( "Invalid polygon detected. Missing points to check" ) );
return ( directions == 15 );
}
/**
* Take a #SHAPE_LINE_CHAIN and links each point into a circular, doubly-linked list.
*/
Vertex* createList( const SHAPE_LINE_CHAIN& points )
{
Vertex* tail = nullptr;
double sum = 0.0;
// Check for winding order
for( int i = 0; i < points.PointCount(); i++ )
{
VECTOR2D p1 = points.CPoint( i );
VECTOR2D p2 = points.CPoint( i + 1 );
sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
}
if( sum > 0.0 )
for( int i = points.PointCount() - 1; i >= 0; i--)
tail = insertVertex( i, points.CPoint( i ), tail );
else
for( int i = 0; i < points.PointCount(); i++ )
tail = insertVertex( i, points.CPoint( i ), tail );
if( tail && ( *tail == *tail->next ) )
{
tail->next->remove();
}
return tail;
}
Vertex* getKink( Vertex* aPt ) const
{
// The point needs to be at a concave surface
if( locallyInside( aPt->prev, aPt->next ) )
return nullptr;
// z-order range for the current point ± limit bounding box
const int32_t maxZ = zOrder( aPt->x + m_limit, aPt->y + m_limit );
const int32_t minZ = zOrder( aPt->x - m_limit, aPt->y - m_limit );
const SEG::ecoord limit2 = SEG::Square( m_limit );
// first look for points in increasing z-order
Vertex* p = aPt->nextZ;
long min_dist = std::numeric_limits<long>::max();
Vertex* retval = nullptr;
while( p && p->z <= maxZ )
{
int delta_i = std::abs( p->i - aPt->i );
VECTOR2D diff( p->x - aPt->x, p->y - aPt->y );
SEG::ecoord dist2 = diff.SquaredEuclideanNorm();
if( delta_i > 1 && dist2 < limit2 && dist2 < min_dist && dist2 > 0.0
&& locallyInside( p, aPt ) && isSubstantial( p, aPt ) )
{
min_dist = dist2;
retval = p;
}
p = p->nextZ;
}
p = aPt->prevZ;
while( p && p->z >= minZ )
{
int delta_i = std::abs( p->i - aPt->i );
VECTOR2D diff( p->x - aPt->x, p->y - aPt->y );
SEG::ecoord dist2 = diff.SquaredEuclideanNorm();
if( delta_i > 1 && dist2 < limit2 && dist2 < min_dist && dist2 > 0.0
&& locallyInside( p, aPt ) && isSubstantial( p, aPt ) )
{
min_dist = dist2;
retval = p;
}
p = p->prevZ;
}
return retval;
}
/**
* Return the twice the signed area of the triangle formed by vertices p, q, and r.
*/
double area( const Vertex* p, const Vertex* q, const Vertex* r ) const
{
return ( q->y - p->y ) * ( r->x - q->x ) - ( q->x - p->x ) * ( r->y - q->y );
}
/**
* Check whether the segment from vertex a -> vertex b is inside the polygon
* around the immediate area of vertex a.
*
* We don't define the exact area over which the segment is inside but it is guaranteed to
* be inside the polygon immediately adjacent to vertex a.
*
* @return true if the segment from a->b is inside a's polygon next to vertex a.
*/
bool locallyInside( const Vertex* a, const Vertex* b ) const
{
if( area( a->prev, a, a->next ) < 0 )
return area( a, b, a->next ) >= 0 && area( a, a->prev, b ) >= 0;
else
return area( a, b, a->prev ) < 0 || area( a, a->next, b ) < 0;
}
/**
* Create an entry in the vertices lookup and optionally inserts the newly created vertex
* into an existing linked list.
*
* @return a pointer to the newly created vertex.
*/
Vertex* insertVertex( int aIndex, const VECTOR2I& pt, Vertex* last )
{
m_vertices.emplace_back( aIndex, pt.x, pt.y, this );
Vertex* p = &m_vertices.back();
if( !last )
{
p->prev = p;
p->next = p;
}
else
{
p->next = last->next;
p->prev = last;
last->next->prev = p;
last->next = p;
}
return p;
}
private:
int m_limit;
BOX2I m_bbox;
std::deque<Vertex> m_vertices;
std::set<std::pair<int, int>> m_hits;
};
wxString DRC_TEST_PROVIDER_CONNECTION_WIDTH::layerDesc( PCB_LAYER_ID aLayer )
{
return wxString::Format( wxT( "(%s)" ), m_drcEngine->GetBoard()->GetLayerName( aLayer ) );
}
bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_CONNECTION_WIDTH ) )
return true; // Continue with other tests
if( !reportPhase( _( "Checking nets for minimum connection width..." ) ) )
return false; // DRC cancelled
LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
LSEQ copperLayers = copperLayerSet.Seq();
BOARD* board = m_drcEngine->GetBoard();
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
PROGRESS_REPORTER* reporter = m_drcEngine->GetProgressReporter();
if( reporter && reporter->IsCancelled() )
return false; // DRC cancelled
std::map<PCB_LAYER_ID, std::map<int, std::set<BOARD_CONNECTED_ITEM*>>> net_items;
DRC_RTREE* tree = board->m_CopperItemRTreeCache.get();
auto min_checker =
[&](const std::set<BOARD_CONNECTED_ITEM*>& aItems, PCB_LAYER_ID aLayer ) -> size_t
{
if( reporter && reporter->IsCancelled() )
return 0;
SHAPE_POLY_SET poly;
for( BOARD_CONNECTED_ITEM* item : aItems )
item->TransformShapeWithClearanceToPolygon( poly, aLayer, 0, ARC_HIGH_DEF, ERROR_OUTSIDE);
poly.Fracture(SHAPE_POLY_SET::PM_FAST);
int minimum_width = bds.m_MinConn;
POLYGON_TEST test( minimum_width );
for( int ii = 0; ii < poly.OutlineCount(); ++ii )
{
const SHAPE_LINE_CHAIN& chain = poly.COutline( ii );
test.FindPairs( chain );
auto& ret = test.GetVertices();
for( const std::pair<int, int>& pt : ret )
{
VECTOR2I location = ( chain.CPoint( pt.first ) + chain.CPoint( pt.second ) ) / 2;
int dist = ( chain.CPoint( pt.first ) - chain.CPoint( pt.second ) ).EuclideanNorm();
std::set<BOARD_ITEM*> items = tree->GetObjectsAt( location, aLayer, minimum_width );
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CONNECTION_WIDTH );
wxString msg;
msg.Printf( _( "Minimum connection width %s; actual %s" ),
MessageTextFromValue( userUnits(), minimum_width ),
MessageTextFromValue( userUnits(), dist ) );
drce->SetErrorMessage( msg + wxS( " " ) + layerDesc( aLayer ) );
for( BOARD_ITEM* item : items )
{
if( item->HitTest( location, minimum_width ) )
drce->AddItem( item );
}
reportViolation( drce, location, aLayer );
}
}
if( reporter )
reporter->AdvanceProgress();
return 1;
};
for( PCB_LAYER_ID layer : copperLayers )
{
auto& layer_items = net_items[layer];
for( ZONE* zone : board->Zones() )
{
if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
layer_items[zone->GetNetCode()].emplace( zone );
}
for( PCB_TRACK* track : board->Tracks() )
{
if( PCB_VIA* via = dyn_cast<PCB_VIA*>( track ) )
{
if( via->FlashLayer( static_cast<int>( layer ) ) )
layer_items[via->GetNetCode()].emplace( via );
}
else if( track->IsOnLayer( layer ) )
{
layer_items[track->GetNetCode()].emplace( track );
}
}
for( FOOTPRINT* fp : board->Footprints() )
{
for( PAD* pad : fp->Pads() )
{
if( pad->FlashLayer( static_cast<int>( layer ) ) )
layer_items[pad->GetNetCode()].emplace( pad );
}
for( FP_ZONE* zone : fp->Zones() )
{
if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
layer_items[zone->GetNetCode()].emplace( zone );
}
}
}
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<size_t>> returns;
size_t return_count = 0;
for( auto& layer_items : net_items )
return_count += layer_items.second.size();
returns.reserve( return_count );
if( reporter )
reporter->SetMaxProgress( return_count );
for( auto& layer_items : net_items )
{
for( const auto& items : layer_items.second )
{
returns.emplace_back( tp.submit( min_checker, items.second, layer_items.first ) );
}
}
for( auto& retval : returns )
{
std::future_status status;
do
{
status = retval.wait_for( std::chrono::milliseconds( 100 ) );
} while( status != std::future_status::ready );
}
return true;
}
namespace detail
{
static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_CONNECTION_WIDTH> dummy;
}

View File

@ -1300,18 +1300,23 @@ void ZONE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
if( !m_FilledPolysList.count( aLayer ) )
return;
aCornerBuffer = *m_FilledPolysList.at( aLayer );
if( !aClearance )
{
aCornerBuffer.Append( *m_FilledPolysList.at( aLayer ) );
return;
}
SHAPE_POLY_SET temp_buf = m_FilledPolysList.at( aLayer )->CloneDropTriangulation();
// Rebuild filled areas only if clearance is not 0
if( aClearance )
{
int numSegs = GetArcToSegmentCount( aClearance, aError, FULL_CIRCLE );
int numSegs = GetArcToSegmentCount( aClearance, aError, FULL_CIRCLE );
if( aErrorLoc == ERROR_OUTSIDE )
aClearance += aError;
if( aErrorLoc == ERROR_OUTSIDE )
aClearance += aError;
aCornerBuffer.InflateWithLinkedHoles( aClearance, numSegs, SHAPE_POLY_SET::PM_FAST );
}
temp_buf.InflateWithLinkedHoles( aClearance, numSegs, SHAPE_POLY_SET::PM_FAST );
aCornerBuffer.Append( temp_buf );
}

View File

@ -0,0 +1,192 @@
(kicad_pcb (version 20220609) (generator pcbnew)
(general
(thickness 0)
)
(paper "A4")
(title_block
(rev "1")
(company "Kicad Demo")
)
(layers
(0 "F.Cu" signal "top_copper")
(31 "B.Cu" signal "bottom_copper")
(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)
)
(setup
(pad_to_mask_clearance 0)
(pcbplotparams
(layerselection 0x0001030_ffffffff)
(plot_on_all_layers_selection 0x0000000_00000000)
(disableapertmacros false)
(usegerberextensions false)
(usegerberattributes true)
(usegerberadvancedattributes true)
(creategerberjobfile true)
(dashed_line_dash_ratio 12.000000)
(dashed_line_gap_ratio 3.000000)
(svgprecision 6)
(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 0)
(scaleselection 1)
(outputdirectory "plots")
)
)
(net 0 "")
(net 1 "Net-(D1-Pad2)")
(footprint "Diode_THT:D_A-405_P7.62mm_Horizontal" (layer "F.Cu")
(tstamp 00000000-0000-0000-0000-00005a56d054)
(at 134.62 72.39 180)
(descr "D, A-405 series, Axial, Horizontal, pin pitch=7.62mm, , length*diameter=5.2*2.7mm^2, , http://www.diodes.com/_files/packages/A-405.pdf")
(tags "D A-405 series Axial Horizontal pin pitch 7.62mm length 5.2mm diameter 2.7mm")
(property "Sheetfile" "sonde xilinx.kicad_sch")
(property "Sheetname" "")
(path "/00000000-0000-0000-0000-00003ebf815e")
(attr through_hole)
(fp_text reference "D1" (at 3.81 -2.47 180) (layer "F.SilkS") hide
(effects (font (size 1 1) (thickness 0.15)))
(tstamp c8f10b4e-4d84-43c3-b9a8-d3f6adbab717)
)
(fp_text value "BAT46" (at 3.81 2.47 180) (layer "F.Fab") hide
(effects (font (size 1 1) (thickness 0.15)))
(tstamp ffcfed93-af35-4520-90de-0339323bff1b)
)
(fp_line (start -1.15 -1.75) (end -1.15 1.75)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp f7cb6518-da50-4c83-a51a-5cdc52aa486c))
(fp_line (start -1.15 1.75) (end 8.8 1.75)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp dff4e628-3677-4cd4-b624-2b4ed43d10c6))
(fp_line (start 8.8 -1.75) (end -1.15 -1.75)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp bb93c298-090a-46cc-8824-0ab3dbd4e704))
(fp_line (start 8.8 1.75) (end 8.8 -1.75)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 707fefea-0e97-4c8d-8f18-6c94026a6ff8))
(pad "2" thru_hole oval (at 7.62 0 180) (size 1.8 1.8) (drill 0.9) (layers *.Cu *.Mask)
(net 1 "Net-(D1-Pad2)") (pinfunction "K") (pintype "passive") (tstamp fe2f4a9c-c9df-401c-acfb-1f0a9f7d438a))
(model "${KICAD6_3DMODEL_DIR}/Diode_THT.3dshapes/D_A-405_P7.62mm_Horizontal.wrl"
(offset (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)
(gr_rect (start 119.5 69.1) (end 137.3 80.8)
(stroke (width 0.1) (type default)) (fill none) (layer "Edge.Cuts") (tstamp 8900a23a-a318-4bb8-8db1-6b52ef189e07))
(via (at 126.4 78.5) (size 1.651) (drill 0.635) (layers "F.Cu" "B.Cu") (net 1) (tstamp 84387f2a-9959-4d79-a875-53244f1f4e68))
(via (at 128.753787 79.665232) (size 0.5) (drill 0.2) (layers "F.Cu" "B.Cu") (net 1) (tstamp b06ca4b8-22df-4ec6-bf66-cf080ffc53d7))
(segment (start 121.060406 75.523254) (end 120.717987 74.4931) (width 0.635) (layer "B.Cu") (net 1) (tstamp 0d2541aa-78dc-4d71-8d09-70964c8b4654))
(segment (start 122.327576 73.37773) (end 122.669995 74.407884) (width 0.635) (layer "B.Cu") (net 1) (tstamp 1cd2361d-d7b8-46c4-b4c3-b2f9ca61d1df))
(segment (start 127.531503 74.6208) (end 127.189084 73.590646) (width 0.635) (layer "B.Cu") (net 1) (tstamp 221799a2-d92f-49a1-94fe-44d3679f5dec))
(segment (start 125.5268 73.8632) (end 126.556954 73.520781) (width 0.635) (layer "B.Cu") (net 1) (tstamp 2bf8d6b1-a5d9-4860-b639-a10275827821))
(segment (start 125.092037 76.719181) (end 125.692037 77.619181) (width 0.635) (layer "B.Cu") (net 1) (tstamp 318fd778-f069-4bd2-88df-2d5d847e84a5))
(segment (start 122.705989 75.036817) (end 121.675835 75.379236) (width 0.635) (layer "B.Cu") (net 1) (tstamp 6362c510-89e2-442a-9bef-a423f67b5f2a))
(segment (start 120.684923 73.871209) (end 121.715077 73.52879) (width 0.635) (layer "B.Cu") (net 1) (tstamp 78890dfd-c95e-4f20-85c9-bb0c2e7400d3))
(segment (start 126.617499 71.234484) (end 126.27508 70.20433) (width 0.635) (layer "B.Cu") (net 1) (tstamp c0d146af-73e9-4b0c-9981-517c8336104e))
(segment (start 130.804351 76.175706) (end 131.834505 75.833287) (width 0.635) (layer "B.Cu") (net 1) (tstamp c3d0bba0-dc16-4de5-8b82-ebcebda9e1dd))
(segment (start 125.299606 76.121706) (end 126.32976 75.779287) (width 0.635) (layer "B.Cu") (net 1) (tstamp f955ff3b-6368-47ac-b9bb-dcbce17cd2cd))
(zone (net 1) (net_name "Net-(D1-Pad2)") (layer "B.Cu") (tstamp 14c4dfe2-825b-4c9b-962f-b07daede2793) (hatch edge 0.508)
(connect_pads (clearance 0.508))
(min_thickness 0.254) (filled_areas_thickness no)
(fill yes (thermal_gap 0.508) (thermal_bridge_width 0.508) (island_removal_mode 1) (island_area_min 10))
(polygon
(pts
(xy 131.5 77.3)
(xy 132.8 77)
(xy 132.9 77.8)
(xy 134.2 77.7)
(xy 134.6 77.2)
(xy 133.1 79.9)
(xy 128.6 79.4)
(xy 130.8 76.4)
)
)
(polygon
(pts
(xy 131.962958 77.241665)
(xy 132.01481 77.562503)
(xy 132.754165 77.357194)
(xy 132.622685 77.072469)
)
)
(filled_polygon
(layer "B.Cu")
(island)
(pts
(xy 130.891171 76.519065)
(xy 130.902415 76.531677)
(xy 131.323031 77.072469)
(xy 131.5 77.3)
(xy 131.521234 77.2951)
(xy 131.624843 77.27119)
(xy 131.824172 77.225191)
(xy 131.895044 77.229363)
(xy 131.952412 77.27119)
(xy 131.976888 77.327861)
(xy 132.01481 77.562503)
(xy 132.700979 77.371963)
(xy 132.771967 77.373009)
(xy 132.831121 77.412269)
(xy 132.859717 77.47774)
(xy 132.9 77.8)
(xy 134.094101 77.708146)
(xy 134.163554 77.722865)
(xy 134.214026 77.772796)
(xy 134.229489 77.842088)
(xy 134.213908 77.894966)
(xy 133.140721 79.826702)
(xy 133.090154 79.876536)
(xy 133.016663 79.89074)
(xy 131.216605 79.690734)
(xy 128.816526 79.424058)
(xy 128.751031 79.396657)
(xy 128.710748 79.338195)
(xy 128.708467 79.267235)
(xy 128.728834 79.224318)
(xy 129.960613 77.544619)
(xy 130.701351 76.534521)
(xy 130.757764 76.491418)
(xy 130.828526 76.485656)
)
)
)
)

View File

@ -0,0 +1,490 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"board_outline_line_width": 0.09999999999999999,
"copper_line_width": 0.3,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.30479999999999996,
"copper_text_upright": true,
"courtyard_line_width": 0.049999999999999996,
"dimension_precision": 4,
"dimension_units": 3,
"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": false,
"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.762,
"height": 1.524,
"width": 1.524
},
"silk_line_width": 0.3,
"silk_text_italic": false,
"silk_text_size_h": 1.5,
"silk_text_size_v": 1.5,
"silk_text_thickness": 0.3,
"silk_text_upright": true,
"zones": {
"45_degree_only": false,
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "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",
"footprint_type_mismatch": "error",
"hole_clearance": "error",
"hole_near_hole": "error",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"overlapping_pads": "warning",
"padstack": "error",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"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",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rule_severitieslegacy_courtyards_overlap": true,
"rule_severitieslegacy_no_courtyard_defined": false,
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.20099999999999998,
"min_copper_edge_clearance": 0.01,
"min_hole_clearance": 0.0,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.508,
"min_microvia_drill": 0.127,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.7999999999999999,
"min_text_thickness": 0.12,
"min_through_hole_diameter": 0.508,
"min_track_width": 0.2032,
"min_via_annular_width": 0.049999999999999996,
"min_via_diameter": 0.889,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 5,
"td_on_pad_in_zone": false,
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
0.4,
1.0
],
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
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_reference": "error",
"duplicate_sheet_names": "error",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "small_conn.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12.0,
"clearance": 0.254,
"diff_pair_gap": 0.4,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.4,
"line_style": 0,
"microvia_diameter": 0.508,
"microvia_drill": 0.127,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.635,
"via_diameter": 1.651,
"via_drill": 0.635,
"wire_width": 6.0
}
],
"meta": {
"version": 2
},
"net_colors": null
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "sonde xilinx.net",
"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_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.3
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 0
},
"net_format_name": "",
"ngspice": {
"meta": {
"version": 0
},
"model_mode": 0
},
"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": [
[
"5ade2176-b41a-41ec-91fe-ba9644669489",
""
]
],
"text_variables": {}
}

View File

@ -45,6 +45,7 @@ set( QA_PCBNEW_SRCS
drc/test_drc_courtyard_invalid.cpp
drc/test_drc_courtyard_overlap.cpp
drc/test_drc_regressions.cpp
drc/test_drc_copper_conn.cpp
drc/test_solder_mask_bridging.cpp
plugins/altium/test_altium_rule_transformer.cpp

View File

@ -0,0 +1,104 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 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 <qa_utils/wx_utils/unit_test_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <board.h>
#include <board_design_settings.h>
#include <pad.h>
#include <pcb_track.h>
#include <pcb_marker.h>
#include <footprint.h>
#include <drc/drc_item.h>
#include <settings/settings_manager.h>
struct DRC_REGRESSION_TEST_FIXTURE
{
DRC_REGRESSION_TEST_FIXTURE() :
m_settingsManager( true /* headless */ )
{ }
SETTINGS_MANAGER m_settingsManager;
std::unique_ptr<BOARD> m_board;
};
BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE )
{
// Check for minimum copper connection errors
std::vector<wxString> tests =
{
"issue9870"
};
for( const wxString& relPath : tests )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
KI_TEST::FillZones( m_board.get() );
std::vector<DRC_ITEM> violations;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
// Disable DRC tests not useful or not handled in this testcase
bds.m_DRCSeverities[ DRCE_INVALID_OUTLINE ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_UNCONNECTED_ITEMS ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_STARVED_THERMAL ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_DRILL_OUT_OF_RANGE ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_VIA_DIAMETER ] = SEVERITY::RPT_SEVERITY_IGNORE;
// These DRC tests are not useful and do not work because they need a footprint library
// associated to the board
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, PCB_LAYER_ID aLayer )
{
if( bds.GetSeverity( aItem->GetErrorCode() ) == SEVERITY::RPT_SEVERITY_ERROR )
violations.push_back( *aItem );
} );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.size() == 12 )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( wxString::Format( "DRC connection width: %s, passed", relPath ) );
}
else
{
std::map<KIID, EDA_ITEM*> 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( wxString::Format( "DRC connection width: %s, failed", relPath ) );
}
}
}