KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
drc_test_provider_creepage.cpp
Go to the documentation of this file.
1/*
2 * Copyright The KiCad Developers.
3 * Copyright (C) 2024 Fabien Corona f.corona<at>laposte.net
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, you may find one here:
17 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18 * or you may search the http://www.gnu.org website for the version 2 license,
19 * or you may write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23#include <common.h>
24#include <macros.h>
26#include <footprint.h>
27#include <pad.h>
28#include <pcb_track.h>
29#include <pcb_shape.h>
30#include <zone.h>
31#include <advanced_config.h>
32#include <geometry/shape_rect.h>
33#include <geometry/seg.h>
35#include <drc/drc_item.h>
36#include <drc/drc_rule.h>
39
41
42
43/*
44 Physical creepage tests.
45
46 Errors generated:
47 - DRCE_CREEPAGE
48 */
49
51{
52public:
54
56
57 virtual bool Run() override;
58
59 virtual const wxString GetName() const override { return wxT( "creepage" ); };
60
61 virtual const wxString GetDescription() const override { return wxT( "Tests creepage" ); }
62
63 double GetMaxConstraint( const std::vector<int>& aNetCodes );
64
65private:
66 int testCreepage();
67 int testCreepage( CREEPAGE_GRAPH& aGraph, int aNetCodeA, int aNetCodeB, PCB_LAYER_ID aLayer );
68
69 void CollectBoardEdges( std::vector<BOARD_ITEM*>& aVector );
70 void CollectNetCodes( std::vector<int>& aVector );
71
72 std::set<std::pair<const BOARD_ITEM*, const BOARD_ITEM*>> m_reportedPairs;
73};
74
75
77{
79 m_reportedPairs.clear();
80
82 {
83 if( !reportPhase( _( "Checking creepage..." ) ) )
84 return false; // DRC cancelled
85
87 }
88 return !m_drcEngine->IsCancelled();
89}
90
91
92std::shared_ptr<GRAPH_NODE> FindInGraphNodes( std::shared_ptr<GRAPH_NODE> aNode,
93 std::vector<std::shared_ptr<GRAPH_NODE>>& aGraph )
94{
95 if( !aNode )
96 return nullptr;
97
98 for( std::shared_ptr<GRAPH_NODE> gn : aGraph )
99 {
100 if( aNode->m_pos == gn->m_pos )
101 {
102 return gn;
103 }
104 }
105 return nullptr;
106}
107
108
109int DRC_TEST_PROVIDER_CREEPAGE::testCreepage( CREEPAGE_GRAPH& aGraph, int aNetCodeA, int aNetCodeB,
110 PCB_LAYER_ID aLayer )
111{
112 PCB_TRACK bci1( m_board );
113 PCB_TRACK bci2( m_board );
114 bci1.SetNetCode( aNetCodeA );
115 bci2.SetNetCode( aNetCodeB );
116 bci1.SetLayer( aLayer );
117 bci2.SetLayer( aLayer );
118
119
120 DRC_CONSTRAINT constraint;
121 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1, &bci2, aLayer );
122 double creepageValue = constraint.Value().Min();
123 aGraph.SetTarget( creepageValue );
124
125 if( creepageValue <= 0 )
126 return 0;
127
128 // Let's make a quick "clearance test"
129 NETINFO_ITEM* netA = m_board->FindNet( aNetCodeA );
130 NETINFO_ITEM* netB = m_board->FindNet( aNetCodeB );
131
132 if ( !netA || !netB )
133 return 0;
134
135 if ( netA->GetBoundingBox().Distance( netB->GetBoundingBox() ) > creepageValue )
136 return 0;
137
138 std::shared_ptr<GRAPH_NODE> NetA = aGraph.AddNetElements( aNetCodeA, aLayer, creepageValue );
139 std::shared_ptr<GRAPH_NODE> NetB = aGraph.AddNetElements( aNetCodeB, aLayer, creepageValue );
140
141
142 aGraph.GeneratePaths( creepageValue, aLayer, false );
143
144 std::vector<std::shared_ptr<GRAPH_NODE>> temp_nodes;
145
146 std::copy_if( aGraph.m_nodes.begin(), aGraph.m_nodes.end(), std::back_inserter( temp_nodes ),
147 []( std::shared_ptr<GRAPH_NODE> aNode )
148 {
149 return !!aNode && aNode->m_parent && aNode->m_parent->IsConductive()
150 && aNode->m_connectDirectly && aNode->m_type == GRAPH_NODE::POINT;
151 } );
152
153 alg::for_all_pairs( temp_nodes.begin(), temp_nodes.end(),
154 [&]( std::shared_ptr<GRAPH_NODE> aN1, std::shared_ptr<GRAPH_NODE> aN2 )
155 {
156 if( aN1 == aN2 )
157 return;
158
159 if( !aN1 || !aN2 )
160 return;
161
162 if( !( aN1->m_parent ) || !( aN2->m_parent ) )
163 return;
164
165 if( ( aN1->m_parent ) != ( aN2->m_parent ) )
166 return;
167
168
169 if( aN1->m_parent->IsConductive() )
170 return;
171
172 if( aN1->m_connectDirectly || aN2->m_connectDirectly )
173 return;
174
175 // We are only looking for points on circles and arcs
176
177 if( aN1->m_type != GRAPH_NODE::POINT )
178 return;
179
180 if( aN2->m_type != GRAPH_NODE::POINT )
181 return;
182
183 aN1->m_parent->ConnectChildren( aN1, aN2, aGraph );
184 } );
185
186 std::vector<std::shared_ptr<GRAPH_CONNECTION>> shortestPath;
187 shortestPath.clear();
188 double distance = aGraph.Solve( NetA, NetB, shortestPath );
189
190 if( !shortestPath.empty() && ( shortestPath.size() >= 4 ) && ( distance - creepageValue < 0 ) )
191 {
192 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CREEPAGE );
193 wxString msg = formatMsg( _( "(%s creepage %s; actual %s)" ), constraint.GetName(),
194 creepageValue, distance );
195 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
196 drce->SetViolatingRule( constraint.GetParentRule() );
197
198 std::shared_ptr<GRAPH_CONNECTION> gc1 = shortestPath[1];
199 std::shared_ptr<GRAPH_CONNECTION> gc2 = shortestPath[shortestPath.size() - 2];
200
201 if( gc1->n1 && gc2->n2 )
202 {
203 const BOARD_ITEM* item1 = gc1->n1->m_parent->GetParent();
204 const BOARD_ITEM* item2 = gc2->n2->m_parent->GetParent();
205
206 if( m_reportedPairs.insert( std::make_pair( item1, item2 ) ).second )
207 drce->SetItems( item1, item2 );
208 else
209 return 1;
210 }
211
212 std::vector<PCB_SHAPE> shortestPathShapes1, shortestPathShapes2;
213
214 VECTOR2I startPoint = gc1->m_path.a2;
215 VECTOR2I endPoint = gc2->m_path.a2;
216 std::vector<PCB_SHAPE> path;
217
218 for( std::shared_ptr<GRAPH_CONNECTION> gc : shortestPath )
219 {
220 if( !gc )
221 continue;
222
223 std::vector<PCB_SHAPE> shapes = gc->GetShapes();
224
225 for( PCB_SHAPE sh : shapes )
226 {
227 path.push_back( sh );
228 }
229 }
230
231 DRC_CUSTOM_MARKER_HANDLER handler = GetGraphicsHandler( path, startPoint, endPoint, distance );
232 reportViolation( drce, gc1->m_path.a2, aLayer, &handler );
233
234 }
235
236 return 1;
237}
238
239double DRC_TEST_PROVIDER_CREEPAGE::GetMaxConstraint( const std::vector<int>& aNetCodes )
240{
241 double maxConstraint = 0;
242 DRC_CONSTRAINT constraint;
243
244 PCB_TRACK bci1( m_board );
245 PCB_TRACK bci2( m_board );
246
247
248 alg::for_all_pairs( aNetCodes.begin(), aNetCodes.end(),
249 [&]( int aNet1, int aNet2 )
250 {
251 if( aNet1 == aNet2 )
252 return;
253
254 bci1.SetNetCode( aNet1 );
255 bci2.SetNetCode( aNet2 );
256
257 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_board->GetCopperLayerCount() ) )
258 {
259 bci1.SetLayer( layer );
260 bci2.SetLayer( layer );
261 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1,
262 &bci2, layer );
263 double value = constraint.Value().Min();
264 maxConstraint = value > maxConstraint ? value : maxConstraint;
265 }
266 } );
267
268 return maxConstraint;
269}
270
271void DRC_TEST_PROVIDER_CREEPAGE::CollectNetCodes( std::vector<int>& aVector )
272{
273 NETCODES_MAP nets = m_board->GetNetInfo().NetsByNetcode();
274
275 for( auto it = nets.begin(); it != nets.end(); it++ )
276 {
277 aVector.push_back( it->first );
278 }
279}
280
281void DRC_TEST_PROVIDER_CREEPAGE::CollectBoardEdges( std::vector<BOARD_ITEM*>& aVector )
282{
283 if( !m_board )
284 return;
285
286 for( BOARD_ITEM* drawing : m_board->Drawings() )
287 {
288 if( !drawing )
289 continue;
290
291 if( drawing->IsOnLayer( Edge_Cuts ) )
292 {
293 aVector.push_back( drawing );
294 }
295 }
296
297 for( FOOTPRINT* fp : m_board->Footprints() )
298 {
299 if( !fp )
300 continue;
301
302 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
303 {
304 if( !drawing )
305 continue;
306
307 if( drawing->IsOnLayer( Edge_Cuts ) )
308 {
309 aVector.push_back( drawing );
310 }
311 }
312 }
313
314 for( const PAD* p : m_board->GetPads() )
315 {
316 if( !p )
317 continue;
318
319
320 if( p->GetAttribute() != PAD_ATTRIB::NPTH )
321 continue;
322
323 PCB_SHAPE* s = new PCB_SHAPE( NULL, SHAPE_T::CIRCLE );
324 s->SetRadius( p->GetDrillSize().x / 2 );
325 s->SetPosition( p->GetPosition() );
326 aVector.push_back( s );
327 }
328}
329
331{
332 if( !m_board )
333 return -1;
334
335 std::vector<int> netcodes;
336
337 this->CollectNetCodes( netcodes );
338 double maxConstraint = GetMaxConstraint( netcodes );
339
340 if( maxConstraint <= 0 )
341 return 0;
342
343 SHAPE_POLY_SET outline;
344
345 if( !m_board->GetBoardPolygonOutlines( outline ) )
346 return -1;
347
348 const DRAWINGS drawings = m_board->Drawings();
349 CREEPAGE_GRAPH graph( *m_board );
350
351
352 if( ADVANCED_CFG::GetCfg().m_EnableCreepageSlot )
353 {
355 }
356 else
357 {
358 graph.m_minGrooveWidth = 0;
359 }
360
361 graph.m_boardOutline = &outline;
362
363 this->CollectBoardEdges( graph.m_boardEdge );
367
368 graph.GeneratePaths( maxConstraint, Edge_Cuts, false );
369
370
371 int beNodeSize = graph.m_nodes.size();
372 int beConnectionsSize = graph.m_connections.size();
373 bool prevTestChangedGraph = false;
374
375 size_t current = 0;
376 size_t total =
377 ( netcodes.size() * ( netcodes.size() - 1 ) ) / 2 * m_board->GetCopperLayerCount();
378 LSET layers = m_board->GetLayerSet();
379
380 alg::for_all_pairs( netcodes.begin(), netcodes.end(),
381 [&]( int aNet1, int aNet2 )
382 {
383 if( aNet1 == aNet2 )
384 return;
385
386
387
388 for( auto it = layers.copper_layers_begin();
389 it != layers.copper_layers_end();
390 ++it )
391 {
392 PCB_LAYER_ID layer = *it;
393
394 reportProgress( current++, total );
395
396 if ( prevTestChangedGraph )
397 {
398 size_t vectorSize = graph.m_connections.size();
399
400 for( size_t i = beConnectionsSize; i < vectorSize; i++ )
401 {
402 // We need to remove the connection from its endpoints' lists.
403 graph.RemoveConnection( graph.m_connections[i], false );
404 }
405 graph.m_connections.resize( beConnectionsSize, nullptr );
406
407 vectorSize = graph.m_nodes.size();
408 graph.m_nodes.resize( beNodeSize, nullptr );
409 }
410
411 prevTestChangedGraph = testCreepage( graph, aNet1, aNet2, layer );
412 }
413 } );
414
415 return 1;
416}
417
418
419namespace detail
420{
422}
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:286
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:2531
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:897
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: board.h:612
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:2011
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition: board.cpp:2642
int GetCopperLayerCount() const
Definition: board.cpp:781
const FOOTPRINTS & Footprints() const
Definition: board.h:338
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:946
const DRAWINGS & Drawings() const
Definition: board.h:340
ecoord_type Distance(const Vec &aP) const
Definition: box2.h:797
void SetTarget(double aTarget)
double Solve(std::shared_ptr< GRAPH_NODE > &aFrom, std::shared_ptr< GRAPH_NODE > &aTo, std::vector< std::shared_ptr< GRAPH_CONNECTION > > &aResult)
std::vector< CREEP_SHAPE * > m_shapeCollection
void TransformCreepShapesToNodes(std::vector< CREEP_SHAPE * > &aShapes)
SHAPE_POLY_SET * m_boardOutline
void GeneratePaths(double aMaxWeight, PCB_LAYER_ID aLayer, bool aClearance)
std::vector< BOARD_ITEM * > m_boardEdge
std::vector< std::shared_ptr< GRAPH_NODE > > m_nodes
std::vector< std::shared_ptr< GRAPH_CONNECTION > > m_connections
std::shared_ptr< GRAPH_NODE > AddNetElements(int aNetCode, PCB_LAYER_ID aLayer, int aMaxCreepage)
wxString GetName() const
Definition: drc_rule.h:160
MINOPTMAX< int > & Value()
Definition: drc_rule.h:153
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:156
BOARD * GetBoard() const
Definition: drc_engine.h:96
bool IsErrorLimitExceeded(int error_code)
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:693
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
DRC_CUSTOM_MARKER_HANDLER GetGraphicsHandler(const std::vector< PCB_SHAPE > &aShapes, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aLength)
void CollectBoardEdges(std::vector< BOARD_ITEM * > &aVector)
double GetMaxConstraint(const std::vector< int > &aNetCodes)
std::set< std::pair< const BOARD_ITEM *, const BOARD_ITEM * > > m_reportedPairs
virtual const wxString GetName() const override
virtual const wxString GetDescription() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void CollectNetCodes(std::vector< int > &aVector)
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
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)
DRC_ENGINE * m_drcEngine
EDA_ITEM * GetParent() const
Definition: eda_item.h:109
EDA_ITEM * m_parent
Linked list: Link (parent struct).
Definition: eda_item.h:506
void SetRadius(int aX)
Definition: eda_shape.h:240
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
T Min() const
Definition: minoptmax.h:33
Handle the data for a net.
Definition: netinfo.h:56
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
const NETCODES_MAP & NetsByNetcode() const
Return the netcode map, at least for python.
Definition: netinfo.h:375
Definition: pad.h:54
void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_shape.h:78
Represent a set of closed polygons.
The common library.
std::function< void(PCB_MARKER *aMarker)> DRC_CUSTOM_MARKER_HANDLER
Definition: drc_engine.h:69
@ DRCE_CREEPAGE
Definition: drc_item.h:44
@ CREEPAGE_CONSTRAINT
Definition: drc_rule.h:50
std::shared_ptr< GRAPH_NODE > FindInGraphNodes(std::shared_ptr< GRAPH_NODE > aNode, std::vector< std::shared_ptr< GRAPH_NODE > > &aGraph)
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ Edge_Cuts
Definition: layer_ids.h:112
This file contains miscellaneous commonly used macros and functions.
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition: kicad_algo.h:84
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)