KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_zone_connections.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 <board.h>
27#include <zone.h>
28#include <footprint.h>
29#include <pad.h>
30#include <pcb_track.h>
31#include <thread_pool.h>
32
35#include <drc/drc_rule.h>
36#include <drc/drc_item.h>
38
39
40/*
41 This loads some rule resolvers for the ZONE_FILLER, and checks that pad thermal relief
42 connections have at least the required number of spokes.
43
44 Errors generated:
45 - DRCE_STARVED_THERMAL
46*/
47
49{
50public:
52 {
53 }
54
56 {
57 }
58
59 virtual bool Run() override;
60
61 virtual const wxString GetName() const override
62 {
63 return wxT( "zone connections" );
64 };
65
66 virtual const wxString GetDescription() const override
67 {
68 return wxT( "Checks thermal reliefs for a sufficient number of connecting spokes" );
69 }
70
71private:
72 void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer );
73};
74
75
77{
78 BOARD* board = m_drcEngine->GetBoard();
80 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
81 DRC_CONSTRAINT constraint;
82
83 const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = aZone->GetFilledPolysList( aLayer );
84 ISOLATED_ISLANDS isolatedIslands;
85
86 auto zoneIter = board->m_ZoneIsolatedIslandsMap.find( aZone );
87
88 if( zoneIter != board->m_ZoneIsolatedIslandsMap.end() )
89 {
90 auto layerIter = zoneIter->second.find( aLayer );
91
92 if( layerIter != zoneIter->second.end() )
93 isolatedIslands = layerIter->second;
94 }
95
96 for( FOOTPRINT* footprint : board->Footprints() )
97 {
98 for( PAD* pad : footprint->Pads() )
99 {
101 return;
102
103 if( m_drcEngine->IsCancelled() )
104 return;
105
106 //
107 // Quick tests for "connected":
108 //
109
110 if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
111 continue;
112
113 BOX2I item_bbox = pad->GetBoundingBox();
114
115 if( !item_bbox.Intersects( aZone->GetBoundingBox() ) )
116 continue;
117
118 if( !pad->FlashLayer( aLayer ) )
119 continue;
120
121 //
122 // If those passed, do a thorough test:
123 //
124
125 constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
126 ZONE_CONNECTION conn = constraint.m_ZoneConnection;
127
128 if( conn != ZONE_CONNECTION::THERMAL )
129 continue;
130
131 constraint = bds.m_DRCEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT, pad, aZone,
132 aLayer );
133 int minCount = constraint.m_Value.Min();
134
135 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || minCount <= 0 )
136 continue;
137
138 SHAPE_POLY_SET padPoly;
139 pad->TransformShapeToPolygon( padPoly, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
140
141 SHAPE_LINE_CHAIN& padOutline = padPoly.Outline( 0 );
142 BOX2I padBBox( item_bbox );
143 std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
144
145 for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
146 {
147 // If we connect to an island that only connects to a single item then we *are*
148 // that item. Thermal spokes to this (otherwise isolated) island don't provide
149 // electrical connectivity to anything, so we don't count them.
150 if( alg::contains( isolatedIslands.m_SingleConnectionOutlines, jj ) )
151 continue;
152
153 zoneFill->Outline( jj ).Intersect( padOutline, intersections, true, &padBBox );
154 }
155
156 int spokes = intersections.size() / 2;
157
158 if( spokes <= 0 ) // Not connected at all
159 continue;
160
161 if( spokes >= minCount ) // We already have enough
162 continue;
163
164 //
165 // See if there are any other manual spokes added:
166 //
167
168 for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
169 {
170 if( padOutline.PointInside( track->GetStart() ) )
171 {
172 if( aZone->GetFilledPolysList( aLayer )->Collide( track->GetEnd() ) )
173 spokes++;
174 }
175 else if( padOutline.PointInside( track->GetEnd() ) )
176 {
177 if( aZone->GetFilledPolysList( aLayer )->Collide( track->GetStart() ) )
178 spokes++;
179 }
180 }
181
182 //
183 // And finally report it if there aren't enough:
184 //
185
186 if( spokes < minCount )
187 {
188 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );
189 wxString msg = wxString::Format( _( "(%s min spoke count %d; actual %d)" ),
190 constraint.GetName(),
191 minCount,
192 spokes );
193
194 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
195 drce->SetItems( aZone, pad );
196 drce->SetViolatingRule( constraint.GetParentRule() );
197
198 reportViolation( drce, pad->GetPosition(), aLayer );
199 }
200 }
201 }
202}
203
204
206{
207 BOARD* board = m_drcEngine->GetBoard();
208 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
209 DRC_CONSTRAINT constraint;
210
211 if( !reportPhase( _( "Checking thermal reliefs..." ) ) )
212 return false; // DRC cancelled
213
214 std::vector< std::pair<ZONE*, PCB_LAYER_ID> > zoneLayers;
215 std::atomic<size_t> done( 1 );
216 size_t total_effort = 0;
217
218 for( ZONE* zone : board->m_DRCCopperZones )
219 {
220 if( !zone->IsTeardropArea() )
221 {
222 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
223 {
224 zoneLayers.push_back( { zone, layer } );
225 total_effort += zone->GetFilledPolysList( layer )->FullPointCount();
226 }
227 }
228 }
229
230 total_effort = std::max( (size_t) 1, total_effort );
231
233 std::vector<std::future<int>> returns;
234
235 returns.reserve( zoneLayers.size() );
236
237 for( const std::pair<ZONE*, PCB_LAYER_ID>& zonelayer : zoneLayers )
238 {
239 returns.emplace_back( tp.submit(
240 [&]( ZONE* aZone, PCB_LAYER_ID aLayer ) -> int
241 {
242 if( !m_drcEngine->IsCancelled() )
243 {
244 testZoneLayer( aZone, aLayer );
245 done.fetch_add( aZone->GetFilledPolysList( aLayer )->FullPointCount() );
246 }
247
248 return 0;
249 },
250 zonelayer.first, zonelayer.second ) );
251 }
252
253 for( const std::future<int>& ret : returns )
254 {
255 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
256
257 while( status != std::future_status::ready )
258 {
259 m_drcEngine->ReportProgress( static_cast<double>( done ) / total_effort );
260 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
261 }
262 }
263
264 return !m_drcEngine->IsCancelled();
265}
266
267
268namespace detail
269{
271}
constexpr int ARC_LOW_DEF
Definition: base_units.h:120
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:270
std::map< ZONE *, std::map< PCB_LAYER_ID, ISOLATED_ISLANDS > > m_ZoneIsolatedIslandsMap
Definition: board.h:1193
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1189
FOOTPRINTS & Footprints()
Definition: board.h:312
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:728
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:432
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:269
wxString GetName() const
Definition: drc_rule.h:149
SEVERITY GetSeverity() const
Definition: drc_rule.h:162
ZONE_CONNECTION m_ZoneConnection
Definition: drc_rule.h:174
MINOPTMAX< int > m_Value
Definition: drc_rule.h:172
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:145
BOARD * GetBoard() const
Definition: drc_engine.h:89
bool IsErrorLimitExceeded(int error_code)
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 bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetName() const override
void testZoneLayer(ZONE *aZone, PCB_LAYER_ID aLayer)
virtual const wxString GetDescription() const override
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_ENGINE * m_drcEngine
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
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const
Check if point aP lies inside a polygon (any type) defined by the line chain.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:615
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:351
bool IsTeardropArea() const
Definition: zone.h:694
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:129
@ DRCE_STARVED_THERMAL
Definition: drc_item.h:48
@ MIN_RESOLVED_SPOKES_CONSTRAINT
Definition: drc_rule.h:61
#define _(s)
@ ERROR_OUTSIDE
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
A struct recording the isolated and single-pad islands within a zone.
Definition: zone.h:59
std::vector< int > m_SingleConnectionOutlines
Definition: zone.h:61
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
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:49