KiCad PCB EDA Suite
drc_test_provider_sliver_checker.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2021-2022 KiCad Developers.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <atomic>
25#include <board.h>
27#include <zone.h>
28#include <footprint.h>
29#include <pcb_shape.h>
31#include <drc/drc_rule.h>
32#include <drc/drc_item.h>
34#include <advanced_config.h>
35#include <progress_reporter.h>
36#include <thread_pool.h>
37
38/*
39 Checks for slivers in copper layers
40
41 Errors generated:
42 - DRCE_COPPER_SLIVER
43*/
44
46{
47public:
49 {
50 }
51
53 {
54 }
55
56 virtual bool Run() override;
57
58 virtual const wxString GetName() const override
59 {
60 return wxT( "sliver checker" );
61 };
62
63 virtual const wxString GetDescription() const override
64 {
65 return wxT( "Checks copper layers for slivers" );
66 }
67
68private:
69 wxString layerDesc( PCB_LAYER_ID aLayer );
70};
71
72
74{
75 return wxString::Format( wxT( "(%s)" ), m_drcEngine->GetBoard()->GetLayerName( aLayer ) );
76}
77
78
80{
82 return true; // Continue with other tests
83
84 if( !reportPhase( _( "Running sliver detection on copper layers..." ) ) )
85 return false; // DRC cancelled
86
87 int widthTolerance = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_SliverWidthTolerance );
88 double angleTolerance = ADVANCED_CFG::GetCfg().m_SliverAngleTolerance;
89 int testLength = widthTolerance / ( 2 * sin( DEG2RAD( angleTolerance / 2 ) ) );
90 LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
91 LSEQ copperLayers = copperLayerSet.Seq();
92 int layerCount = copperLayers.size();
93
94 // Report progress on board zones only. Everything else is in the noise.
95 int zoneLayerCount = 0;
96 std::atomic<size_t> done( 1 );
97
98 for( PCB_LAYER_ID layer : copperLayers )
99 {
100 for( ZONE* zone : m_drcEngine->GetBoard()->Zones() )
101 {
102 if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
103 zoneLayerCount++;
104 }
105 }
106
108
109 if( reporter && reporter->IsCancelled() )
110 return false; // DRC cancelled
111
112 std::vector<SHAPE_POLY_SET> layerPolys( layerCount );
113
114 auto sliver_checker =
115 [&]( int aItem ) -> size_t
116 {
117 PCB_LAYER_ID layer = copperLayers[aItem];
118 SHAPE_POLY_SET& poly = layerPolys[aItem];
119
120 if( m_drcEngine->IsCancelled() )
121 return 0;
122
123 SHAPE_POLY_SET fill;
124
126 [&]( BOARD_ITEM* item ) -> bool
127 {
128 if( dynamic_cast<ZONE*>( item) )
129 {
130 ZONE* zone = static_cast<ZONE*>( item );
131
132 if( !zone->GetIsRuleArea() )
133 {
134 fill = zone->GetFill( layer )->CloneDropTriangulation();
136
137 for( int jj = 0; jj < fill.OutlineCount(); ++jj )
138 poly.AddOutline( fill.Outline( jj ) );
139
140 // Report progress on board zones only. Everything else is
141 // in the noise.
142 done.fetch_add( 1 );
143 }
144 }
145 else
146 {
147 item->TransformShapeWithClearanceToPolygon( poly, layer, 0,
150 }
151
152 if( m_drcEngine->IsCancelled() )
153 return false;
154
155 return true;
156 } );
157
158
159 if( m_drcEngine->IsCancelled() )
160 return 0;
161
163
164 // Sharpen corners
165 poly.Deflate( widthTolerance / 2, ARC_LOW_DEF,
167
168 return 1;
169 };
170
172 std::vector<std::future<size_t>> returns;
173
174 returns.reserve( copperLayers.size() );
175
176 for( size_t ii = 0; ii < copperLayers.size(); ++ii )
177 returns.emplace_back( tp.submit( sliver_checker, ii ) );
178
179 for( const std::future<size_t>& ret : returns )
180 {
181 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
182
183 while( status != std::future_status::ready )
184 {
185 m_drcEngine->ReportProgress( static_cast<double>( zoneLayerCount ) / done );
186
187 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
188 }
189 }
190
191
192 for( int ii = 0; ii < layerCount; ++ii )
193 {
194 PCB_LAYER_ID layer = copperLayers[ii];
195 SHAPE_POLY_SET& poly = layerPolys[ii];
196
198 continue;
199
200 // Frequently, in filled areas, some points of the polygons are very near (dist is only
201 // a few internal units, like 2 or 3 units.
202 // We skip very small vertices: one cannot really compute a valid orientation of
203 // such a vertex
204 // So skip points near than min_len (in internal units).
205 const int min_len = 3;
206
207 for( int jj = 0; jj < poly.OutlineCount(); ++jj )
208 {
209 const std::vector<VECTOR2I>& pts = poly.Outline( jj ).CPoints();
210 int ptCount = pts.size();
211
212 for( int kk = 0; kk < ptCount; ++kk )
213 {
214 VECTOR2I pt = pts[ kk ];
215 VECTOR2I ptPrior = pts[ ( ptCount + kk - 1 ) % ptCount ];
216 VECTOR2I vPrior = ( ptPrior - pt );
217
218 if( std::abs( vPrior.x ) < min_len && std::abs( vPrior.y ) < min_len && ptCount > 5)
219 {
220 ptPrior = pts[ ( ptCount + kk - 2 ) % ptCount ];
221 vPrior = ( ptPrior - pt );
222 }
223
224 VECTOR2I ptAfter = pts[ ( kk + 1 ) % ptCount ];
225 VECTOR2I vAfter = ( ptAfter - pt );
226
227 if( std::abs( vAfter.x ) < min_len && std::abs( vAfter.y ) < min_len && ptCount > 5 )
228 {
229 ptAfter = pts[ ( kk + 2 ) % ptCount ];
230 vAfter = ( ptAfter - pt );
231 }
232
233 VECTOR2I vIncluded = vPrior.Resize( testLength ) - vAfter.Resize( testLength );
234
235 if( vIncluded.SquaredEuclideanNorm() < SEG::Square( widthTolerance ) )
236 {
237 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER );
238 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) );
239 reportViolation( drce, pt, layer );
240 }
241 }
242 }
243 }
244
245 return true;
246}
247
248
249namespace detail
250{
252}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
constexpr int ARC_LOW_DEF
Definition: base_units.h:120
double m_SliverAngleTolerance
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
virtual void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:172
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:500
ZONES & Zones()
Definition: board.h:313
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:399
BOARD * GetBoard() const
Definition: drc_engine.h:89
bool ReportProgress(double aProgress)
bool IsErrorLimitExceeded(int error_code)
PROGRESS_REPORTER * GetProgressReporter() const
Definition: drc_engine.h:124
bool IsCancelled() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:325
virtual const wxString GetDescription() const override
virtual const wxString GetName() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out #DRC_ITEMs and po...
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
static std::vector< KICAD_T > s_allBasicItems
DRC_ENGINE * m_drcEngine
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:491
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
A progress reporter interface for use in multi-threaded environments.
virtual bool IsCancelled() const =0
static SEG::ecoord Square(int a)
Definition: seg.h:123
const std::vector< VECTOR2I > & CPoints() const
Represent a set of closed polygons.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void Simplify(POLYGON_MODE aFastMode)
void Unfracture(POLYGON_MODE aFastMode)
Return true if the polygon set has any holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
SHAPE_POLY_SET CloneDropTriangulation() const
Creates a new empty polygon in the set and returns its index.
extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:300
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:378
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:691
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:602
@ DRCE_COPPER_SLIVER
Definition: drc_item.h:84
#define _(s)
@ ERROR_OUTSIDE
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:32
static thread_pool * tp
Definition: thread_pool.cpp:30
BS::thread_pool thread_pool
Definition: thread_pool.h:30
double DEG2RAD(double deg)
Definition: trigo.h:195