KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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
52
53 virtual bool Run() override;
54
55 virtual const wxString GetName() const override { return wxT( "sliver checker" ); };
56
57private:
58 wxString layerDesc( PCB_LAYER_ID aLayer );
59};
60
61
63{
64 return wxString::Format( wxT( "(%s)" ), m_drcEngine->GetBoard()->GetLayerName( aLayer ) );
65}
66
67
69{
71 return true; // Continue with other tests
72
73 if( !reportPhase( _( "Running sliver detection on copper layers..." ) ) )
74 return false; // DRC cancelled
75
76 int64_t widthTolerance = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_SliverWidthTolerance );
77 int64_t squared_width = widthTolerance * widthTolerance;
78
79 double angleTolerance = ADVANCED_CFG::GetCfg().m_SliverAngleTolerance;
80 double cosangleTol = 2.0 * cos( DEG2RAD( angleTolerance ) );
81 LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
82 LSEQ copperLayers = copperLayerSet.Seq();
83 int layerCount = copperLayers.size();
84
85 // Report progress on board zones only. Everything else is in the noise.
86 int zoneLayerCount = 0;
87 std::atomic<size_t> done( 1 );
88
89 for( PCB_LAYER_ID layer : copperLayers )
90 {
91 for( ZONE* zone : m_drcEngine->GetBoard()->Zones() )
92 {
93 if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
94 zoneLayerCount++;
95 }
96 }
97
99
100 if( reporter && reporter->IsCancelled() )
101 return false; // DRC cancelled
102
103 std::vector<SHAPE_POLY_SET> layerPolys( layerCount );
104
105 auto build_layer_polys =
106 [&]( int layerIdx ) -> size_t
107 {
108 PCB_LAYER_ID layer = copperLayers[layerIdx];
109 SHAPE_POLY_SET& poly = layerPolys[layerIdx];
110
111 if( m_drcEngine->IsCancelled() )
112 return 0;
113
114 SHAPE_POLY_SET fill;
115
117 [&]( BOARD_ITEM* item ) -> bool
118 {
119 if( ZONE* zone = dynamic_cast<ZONE*>( item) )
120 {
121 if( !zone->GetIsRuleArea() )
122 {
123 fill = zone->GetFill( layer )->CloneDropTriangulation();
124 poly.Append( fill );
125
126 // Report progress on board zones only. Everything else is
127 // in the noise.
128 done.fetch_add( 1 );
129 }
130 }
131 else
132 {
133 item->TransformShapeToPolygon( poly, layer, 0, ARC_LOW_DEF,
134 ERROR_INSIDE );
135 }
136
137 if( m_drcEngine->IsCancelled() )
138 return false;
139
140 return true;
141 } );
142
143
144 if( m_drcEngine->IsCancelled() )
145 return 0;
146
147 poly.Simplify();
148
149 return 1;
150 };
151
153 std::vector<std::future<size_t>> returns;
154
155 returns.reserve( copperLayers.size() );
156
157 for( size_t ii = 0; ii < copperLayers.size(); ++ii )
158 returns.emplace_back( tp.submit( build_layer_polys, ii ) );
159
160 for( const std::future<size_t>& ret : returns )
161 {
162 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
163
164 while( status != std::future_status::ready )
165 {
166 reportProgress( zoneLayerCount, done );
167 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
168 }
169 }
170
171 for( int ii = 0; ii < layerCount; ++ii )
172 {
173 PCB_LAYER_ID layer = copperLayers[ii];
174 SHAPE_POLY_SET& poly = layerPolys[ii];
175
177 continue;
178
179 // Frequently, in filled areas, some points of the polygons are very near (dist is only
180 // a few internal units, like 2 or 3 units.
181 // We skip very small vertices: one cannot really compute a valid orientation of
182 // such a vertex
183 // So skip points near than min_len (in internal units).
184 const int min_len = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_SliverMinimumLength );
185
186 for( int jj = 0; jj < poly.OutlineCount(); ++jj )
187 {
188 const std::vector<VECTOR2I>& pts = poly.Outline( jj ).CPoints();
189 int ptCount = pts.size();
190 int offset = 0;
191
192 auto area = [&]( const VECTOR2I& p, const VECTOR2I& q, const VECTOR2I& r ) -> VECTOR2I::extended_type
193 {
194 return static_cast<VECTOR2I::extended_type>( q.y - p.y ) * ( r.x - q.x ) -
195 static_cast<VECTOR2I::extended_type>( q.x - p.x ) * ( r.y - q.y );
196 };
197
198 auto isLocallyInside = [&]( int aA, int aB ) -> bool
199 {
200 int prev = ( ptCount + aA - 1 ) % ptCount;
201 int next = ( aA + 1 ) % ptCount;
202
203 if( area( pts[prev], pts[aA], pts[next] ) < 0 )
204 return area( pts[aA], pts[aB], pts[next] ) >= 0 && area( pts[aA], pts[prev], pts[aB] ) >= 0;
205 else
206 return area( pts[aA], pts[aB], pts[prev] ) < 0 || area( pts[aA], pts[next], pts[aB] ) < 0;
207 };
208
209 if( ptCount <= 5 )
210 continue;
211
212 for( int kk = 0; kk < ptCount; kk += offset )
213 {
214 int prior_index = ( ptCount + kk - 1 ) % ptCount;
215 int next_index = ( kk + 1 ) % ptCount;
216 VECTOR2I pt = pts[ kk ];
217 VECTOR2I ptPrior = pts[ prior_index ];
218 VECTOR2I vPrior = ( ptPrior - pt );
219 int forward_offset = 1;
220
221 offset = 1;
222
223 while( std::abs( vPrior.x ) < min_len && std::abs( vPrior.y ) < min_len
224 && offset < ptCount )
225 {
226 pt = pts[ ( kk + offset++ ) % ptCount ];
227 vPrior = ( ptPrior - pt );
228 }
229
230 if( offset >= ptCount )
231 break;
232
233 VECTOR2I ptAfter = pts[ next_index ];
234 VECTOR2I vAfter = ( ptAfter - pt );
235
236 while( std::abs( vAfter.x ) < min_len && std::abs( vAfter.y ) < min_len
237 && forward_offset < ptCount )
238 {
239 next_index = ( kk + forward_offset++ ) % ptCount;
240 ptAfter = pts[ next_index ];
241 vAfter = ( ptAfter - pt );
242 }
243
244 if( offset >= ptCount )
245 break;
246
247 // Negative dot product means that the angle is > 90°
248 if( vPrior.Dot( vAfter ) <= 0 )
249 continue;
250
251 if( !isLocallyInside( prior_index, next_index ) )
252 continue;
253
254 VECTOR2I vIncluded = ptAfter - ptPrior;
255 double arm1 = vPrior.SquaredEuclideanNorm();
256 double arm2 = vAfter.SquaredEuclideanNorm();
257 double opp = vIncluded.SquaredEuclideanNorm();
258
259 double cos_ang = std::abs( ( opp - arm1 - arm2 ) / ( std::sqrt( arm1 ) * std::sqrt( arm2 ) ) );
260
261 if( cos_ang > cosangleTol && 2.0 - cos_ang > std::numeric_limits<float>::epsilon() && opp > squared_width )
262 {
263 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER );
264 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) );
265 reportViolation( drce, pt, layer );
266 }
267 }
268 }
269 }
270
271 return true;
272}
273
274
275namespace detail
276{
278}
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
constexpr int ARC_LOW_DEF
Definition: base_units.h:128
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:79
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:303
const ZONES & Zones() const
Definition: board.h:362
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:679
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:894
BOARD * GetBoard() const
Definition: drc_engine.h:95
bool IsErrorLimitExceeded(int error_code)
PROGRESS_REPORTER * GetProgressReporter() const
Definition: drc_engine.h:130
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:393
virtual ~DRC_TEST_PROVIDER_SLIVER_CHECKER()=default
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_ITEM and posi...
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
static std::vector< KICAD_T > s_allBasicItems
DRC_ENGINE * m_drcEngine
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:582
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:296
A progress reporter interface for use in multi-threaded environments.
virtual bool IsCancelled() const =0
const std::vector< VECTOR2I > & CPoints() const
Represent a set of closed polygons.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:307
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition: vector2d.h:73
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition: vector2d.h:554
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
@ DRCE_COPPER_SLIVER
Definition: drc_item.h:92
#define _(s)
double m_SliverAngleTolerance
Sliver angle to tolerance for DRC.
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:395
CITER next(CITER it)
Definition: ptree.cpp:124
constexpr int mmToIU(double mm) const
Definition: base_units.h:92
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:30
static thread_pool * tp
Definition: thread_pool.cpp:28
BS::thread_pool thread_pool
Definition: thread_pool.h:31
double DEG2RAD(double deg)
Definition: trigo.h:166