KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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
81 {
82 if( !reportPhase( _( "Checking creepage..." ) ) )
83 return false; // DRC cancelled
84
86 }
87 return !m_drcEngine->IsCancelled();
88}
89
90
91std::shared_ptr<GRAPH_NODE> FindInGraphNodes( std::shared_ptr<GRAPH_NODE> aNode,
92 std::vector<std::shared_ptr<GRAPH_NODE>>& aGraph )
93{
94 if( !aNode )
95 return nullptr;
96
97 for( std::shared_ptr<GRAPH_NODE> gn : aGraph )
98 {
99 if( aNode->m_pos == gn->m_pos )
100 {
101 return gn;
102 }
103 }
104 return nullptr;
105}
106
107
108int DRC_TEST_PROVIDER_CREEPAGE::testCreepage( CREEPAGE_GRAPH& aGraph, int aNetCodeA, int aNetCodeB,
109 PCB_LAYER_ID aLayer )
110{
111 PCB_TRACK bci1( m_board );
112 PCB_TRACK bci2( m_board );
113 bci1.SetNetCode( aNetCodeA );
114 bci2.SetNetCode( aNetCodeB );
115 bci1.SetLayer( aLayer );
116 bci2.SetLayer( aLayer );
117
118
119 DRC_CONSTRAINT constraint;
120 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1, &bci2, aLayer );
121 double creepageValue = constraint.Value().Min();
122 aGraph.SetTarget( creepageValue );
123
124 if( creepageValue <= 0 )
125 return 0;
126
127 // Let's make a quick "clearance test"
128 NETINFO_ITEM* netA = m_board->FindNet( aNetCodeA );
129 NETINFO_ITEM* netB = m_board->FindNet( aNetCodeB );
130
131 if ( !netA || !netB )
132 return 0;
133
134 if ( netA->GetBoundingBox().Distance( netB->GetBoundingBox() ) > creepageValue )
135 return 0;
136
137 std::shared_ptr<GRAPH_NODE> NetA = aGraph.AddNetElements( aNetCodeA, aLayer, creepageValue );
138 std::shared_ptr<GRAPH_NODE> NetB = aGraph.AddNetElements( aNetCodeB, aLayer, creepageValue );
139
140
141 aGraph.GeneratePaths( creepageValue, aLayer, false );
142
143 std::vector<std::shared_ptr<GRAPH_NODE>> temp_nodes;
144
145 std::copy_if( aGraph.m_nodes.begin(), aGraph.m_nodes.end(), std::back_inserter( temp_nodes ),
146 []( std::shared_ptr<GRAPH_NODE> aNode )
147 {
148 return !!aNode && aNode->m_parent && aNode->m_parent->IsConductive()
149 && aNode->m_connectDirectly && aNode->m_type == GRAPH_NODE::POINT;
150 } );
151
152 alg::for_all_pairs( temp_nodes.begin(), temp_nodes.end(),
153 [&]( std::shared_ptr<GRAPH_NODE> aN1, std::shared_ptr<GRAPH_NODE> aN2 )
154 {
155 if( aN1 == aN2 )
156 return;
157
158 if( !aN1 || !aN2 )
159 return;
160
161 if( !( aN1->m_parent ) || !( aN2->m_parent ) )
162 return;
163
164 if( ( aN1->m_parent ) != ( aN2->m_parent ) )
165 return;
166
167
168 if( aN1->m_parent->IsConductive() )
169 return;
170
171 if( aN1->m_connectDirectly || aN2->m_connectDirectly )
172 return;
173
174 // We are only looking for points on circles and arcs
175
176 if( aN1->m_type != GRAPH_NODE::POINT )
177 return;
178
179 if( aN2->m_type != GRAPH_NODE::POINT )
180 return;
181
182 aN1->m_parent->ConnectChildren( aN1, aN2, aGraph );
183 } );
184
185 std::vector<std::shared_ptr<GRAPH_CONNECTION>> shortestPath;
186 shortestPath.clear();
187 double distance = aGraph.Solve( NetA, NetB, shortestPath );
188
189 if( !shortestPath.empty() && ( shortestPath.size() >= 4 ) && ( distance - creepageValue < 0 ) )
190 {
191 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CREEPAGE );
192 wxString msg = formatMsg( _( "(%s creepage %s; actual %s)" ), constraint.GetName(),
193 creepageValue, distance );
194 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
195 drce->SetViolatingRule( constraint.GetParentRule() );
196
197 std::shared_ptr<GRAPH_CONNECTION> gc1 = shortestPath[1];
198 std::shared_ptr<GRAPH_CONNECTION> gc2 = shortestPath[shortestPath.size() - 2];
199
200 if( gc1->n1 && gc2->n2 )
201 {
202 const BOARD_ITEM* item1 = gc1->n1->m_parent->GetParent();
203 const BOARD_ITEM* item2 = gc2->n2->m_parent->GetParent();
204
205 if( m_reportedPairs.insert( std::make_pair( item1, item2 ) ).second )
206 drce->SetItems( item1, item2 );
207 else
208 return 1;
209 }
210
211 std::vector<PCB_SHAPE> shortestPathShapes1, shortestPathShapes2;
212
213 VECTOR2I startPoint = gc1->m_path.a2;
214 VECTOR2I endPoint = gc2->m_path.a2;
215 std::vector<PCB_SHAPE> path;
216
217 for( std::shared_ptr<GRAPH_CONNECTION> gc : shortestPath )
218 {
219 if( !gc )
220 continue;
221
222 std::vector<PCB_SHAPE> shapes = gc->GetShapes();
223
224 for( PCB_SHAPE sh : shapes )
225 {
226 path.push_back( sh );
227 }
228 }
229
230 DRC_CUSTOM_MARKER_HANDLER handler = GetGraphicsHandler( path, startPoint, endPoint, distance );
231 reportViolation( drce, gc1->m_path.a2, aLayer, &handler );
232
233 }
234
235 return 1;
236}
237
238double DRC_TEST_PROVIDER_CREEPAGE::GetMaxConstraint( const std::vector<int>& aNetCodes )
239{
240 double maxConstraint = 0;
241 DRC_CONSTRAINT constraint;
242
243 PCB_TRACK bci1( m_board );
244 PCB_TRACK bci2( m_board );
245
246
247 alg::for_all_pairs( aNetCodes.begin(), aNetCodes.end(),
248 [&]( int aNet1, int aNet2 )
249 {
250 if( aNet1 == aNet2 )
251 return;
252
253 bci1.SetNetCode( aNet1 );
254 bci2.SetNetCode( aNet2 );
255
256 for( PCB_LAYER_ID layer : LSET::AllCuMask().CuStack() )
257 {
258 bci1.SetLayer( layer );
259 bci2.SetLayer( layer );
260 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1,
261 &bci2, layer );
262 double value = constraint.Value().Min();
263 maxConstraint = value > maxConstraint ? value : maxConstraint;
264 }
265 } );
266
267 return maxConstraint;
268}
269
270void DRC_TEST_PROVIDER_CREEPAGE::CollectNetCodes( std::vector<int>& aVector )
271{
272 NETCODES_MAP nets = m_board->GetNetInfo().NetsByNetcode();
273
274 for( auto it = nets.begin(); it != nets.end(); it++ )
275 {
276 aVector.push_back( it->first );
277 }
278}
279
280void DRC_TEST_PROVIDER_CREEPAGE::CollectBoardEdges( std::vector<BOARD_ITEM*>& aVector )
281{
282 if( !m_board )
283 return;
284
285 for( BOARD_ITEM* drawing : m_board->Drawings() )
286 {
287 if( !drawing )
288 continue;
289
290 if( drawing->IsOnLayer( Edge_Cuts ) )
291 {
292 aVector.push_back( drawing );
293 }
294 }
295
296 for( FOOTPRINT* fp : m_board->Footprints() )
297 {
298 if( !fp )
299 continue;
300
301 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
302 {
303 if( !drawing )
304 continue;
305
306 if( drawing->IsOnLayer( Edge_Cuts ) )
307 {
308 aVector.push_back( drawing );
309 }
310 }
311 }
312
313 for( const PAD* p : m_board->GetPads() )
314 {
315 if( !p )
316 continue;
317
318
319 if( p->GetAttribute() != PAD_ATTRIB::NPTH )
320 continue;
321
322 PCB_SHAPE* s = new PCB_SHAPE( NULL, SHAPE_T::CIRCLE );
323 s->SetRadius( p->GetDrillSize().x / 2 );
324 s->SetPosition( p->GetPosition() );
325 aVector.push_back( s );
326 }
327}
328
330{
331 if( !m_board )
332 return -1;
333
334 std::vector<int> netcodes;
335
336 this->CollectNetCodes( netcodes );
337 double maxConstraint = GetMaxConstraint( netcodes );
338
339 if( maxConstraint <= 0 )
340 return 0;
341
342 SHAPE_POLY_SET outline;
343
344 if( !m_board->GetBoardPolygonOutlines( outline ) )
345 return -1;
346
347 const DRAWINGS drawings = m_board->Drawings();
348 CREEPAGE_GRAPH graph( *m_board );
349
350
351 if( ADVANCED_CFG::GetCfg().m_EnableCreepageSlot )
352 {
354 }
355 else
356 {
357 graph.m_minGrooveWidth = 0;
358 }
359
360 graph.m_boardOutline = &outline;
361
362 this->CollectBoardEdges( graph.m_boardEdge );
366
367 graph.GeneratePaths( maxConstraint, Edge_Cuts, false );
368
369
370 int beNodeSize = graph.m_nodes.size();
371 int beConnectionsSize = graph.m_connections.size();
372 bool prevTestChangedGraph = false;
373
374 size_t current = 0;
375 size_t total =
376 ( netcodes.size() * ( netcodes.size() - 1 ) ) / 2 * m_board->GetCopperLayerCount();
377
378 alg::for_all_pairs( netcodes.begin(), netcodes.end(),
379 [&]( int aNet1, int aNet2 )
380 {
381 if( aNet1 == aNet2 )
382 return;
383
384 for( auto it = m_board->GetLayerSet().copper_layers_begin();
385 it != m_board->GetLayerSet().copper_layers_end();
386 ++it )
387 {
388 PCB_LAYER_ID layer = *it;
389
390 reportProgress( current++, total );
391
392 if ( prevTestChangedGraph )
393 {
394 size_t vectorSize = graph.m_connections.size();
395
396 for( size_t i = beConnectionsSize; i < vectorSize; i++ )
397 {
398 // We need to remove the connection from its endpoints' lists.
399 graph.RemoveConnection( graph.m_connections[i], false );
400 }
401 graph.m_connections.resize( beConnectionsSize, nullptr );
402
403 vectorSize = graph.m_nodes.size();
404 graph.m_nodes.resize( beNodeSize, nullptr );
405 }
406
407 prevTestChangedGraph = testCreepage( graph, aNet1, aNet2, layer );
408 }
409 } );
410
411 return 1;
412}
413
414
415namespace detail
416{
418}
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:79
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:290
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:2536
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:874
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1961
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition: board.cpp:2639
int GetCopperLayerCount() const
Definition: board.cpp:780
const FOOTPRINTS & Footprints() const
Definition: board.h:331
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:934
const DRAWINGS & Drawings() const
Definition: board.h:333
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:395
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:103
EDA_ITEM * m_parent
Linked list: Link (parent struct).
Definition: eda_item.h:502
void SetRadius(int aX)
Definition: eda_shape.h:196
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:76
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)