KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_creepage.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024 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( CreepageGraph& 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
73
75{
77
78 //int errorMax = m_board->GetDesignSettings().m_MaxError;
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<GraphNode> FindInGraphNodes( std::shared_ptr<GraphNode> aNode,
92 std::vector<std::shared_ptr<GraphNode>>& aGraph )
93{
94 if( !aNode )
95 return nullptr;
96
97 for( std::shared_ptr<GraphNode> 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( CreepageGraph& 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<GraphNode> NetA = aGraph.AddNetElements( aNetCodeA, aLayer, creepageValue );
138 std::shared_ptr<GraphNode> NetB = aGraph.AddNetElements( aNetCodeB, aLayer, creepageValue );
139
140
141 aGraph.GeneratePaths( creepageValue, aLayer );
142
143 std::vector<std::shared_ptr<GraphNode>> nodes1 = aGraph.m_nodes;
144 std::vector<std::shared_ptr<GraphNode>> nodes2 = aGraph.m_nodes;
145
146 alg::for_all_pairs( aGraph.m_nodes.begin(), aGraph.m_nodes.end(),
147 [&]( std::shared_ptr<GraphNode> aN1, std::shared_ptr<GraphNode> aN2 )
148 {
149 if( aN1 == aN2 )
150 return;
151
152 if( !aN1 || !aN2 )
153 return;
154
155 if( !( aN1->m_parent ) || !( aN2->m_parent ) )
156 return;
157
158 if( ( aN1->m_parent ) != ( aN2->m_parent ) )
159 return;
160
161
162 if( aN1->m_parent->IsConductive() )
163 return;
164
165 if( aN1->m_connectDirectly || aN2->m_connectDirectly )
166 return;
167
168 // We are only looking for points on circles and arcs
169
170 if( aN1->m_type != GraphNode::POINT )
171 return;
172
173 if( aN2->m_type != GraphNode::POINT )
174 return;
175
176 aN1->m_parent->ConnectChildren( aN1, aN2, aGraph );
177 } );
178
179 std::vector<std::shared_ptr<GraphConnection>> shortestPath;
180 shortestPath.clear();
181 double distance = aGraph.Solve( NetA, NetB, shortestPath );
182
183
184 if( ( shortestPath.size() > 0 ) && ( distance - creepageValue < 0 ) )
185 {
186 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CREEPAGE );
187 wxString msg = formatMsg( _( "(%s creepage %s; actual %s)" ), constraint.GetName(),
188 creepageValue, distance );
189 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
190 drce->SetViolatingRule( constraint.GetParentRule() );
191
192 if( shortestPath.size() >= 4 && shortestPath[1]->n1 && shortestPath[1]->n2 )
193 drce->SetItems( shortestPath[1]->n1->m_parent->GetParent(),
194 shortestPath[shortestPath.size() - 2]->n2->m_parent->GetParent() );
195
196 std::vector<PCB_SHAPE> shortestPathShapes1, shortestPathShapes2;
197
198 VECTOR2I startPoint = shortestPath[1]->m_path.a2;
199 VECTOR2I endPoint = shortestPath[shortestPath.size() - 2]->m_path.a2;
200
201 PCB_SHAPE s;
202 s.SetStart( startPoint );
203 s.SetEnd( endPoint );
204
205
206 std::vector<PCB_SHAPE> path;
207 for( std::shared_ptr<GraphConnection> gc : shortestPath )
208 {
209 if( !gc )
210 continue;
211
212 std::vector<PCB_SHAPE> shapes = gc->GetShapes();
213
214 for( PCB_SHAPE sh : shapes )
215 {
216 path.push_back( sh );
217 }
218 }
219
220 this->ShowPathDRC( path, startPoint, endPoint, distance );
221 reportViolation( drce, shortestPath[1]->m_path.a2, aLayer );
222 // After a ShowPathDRC() call, restore the handler
224 }
225 shortestPath.clear();
226
227 return 1;
228}
229
230double DRC_TEST_PROVIDER_CREEPAGE::GetMaxConstraint( const std::vector<int>& aNetCodes )
231{
232 double maxConstraint = 0;
233 DRC_CONSTRAINT constraint;
234
235 PCB_TRACK bci1( m_board );
236 PCB_TRACK bci2( m_board );
237
238
239 alg::for_all_pairs( aNetCodes.begin(), aNetCodes.end(),
240 [&]( int aNet1, int aNet2 )
241 {
242 if( aNet1 == aNet2 )
243 return;
244
245 bci1.SetNetCode( aNet1 );
246 bci2.SetNetCode( aNet2 );
247
248 for( PCB_LAYER_ID layer : LSET::AllCuMask().CuStack() )
249 {
250 bci1.SetLayer( layer );
251 bci2.SetLayer( layer );
252 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1,
253 &bci2, layer );
254 double value = constraint.Value().Min();
255 maxConstraint = value > maxConstraint ? value : maxConstraint;
256 }
257 } );
258
259 return maxConstraint;
260}
261
262void DRC_TEST_PROVIDER_CREEPAGE::CollectNetCodes( std::vector<int>& aVector )
263{
264 NETCODES_MAP nets = m_board->GetNetInfo().NetsByNetcode();
265
266 for( auto it = nets.begin(); it != nets.end(); it++ )
267 {
268 aVector.push_back( it->first );
269 }
270}
271
272void DRC_TEST_PROVIDER_CREEPAGE::CollectBoardEdges( std::vector<BOARD_ITEM*>& aVector )
273{
274 if( !m_board )
275 return;
276
277 for( BOARD_ITEM* drawing : m_board->Drawings() )
278 {
279 if( !drawing )
280 continue;
281
282 if( drawing->IsOnLayer( Edge_Cuts ) )
283 {
284 aVector.push_back( drawing );
285 }
286 }
287
288 for( FOOTPRINT* fp : m_board->Footprints() )
289 {
290 if( !fp )
291 continue;
292
293 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
294 {
295 if( !drawing )
296 continue;
297
298 if( drawing->IsOnLayer( Edge_Cuts ) )
299 {
300 aVector.push_back( drawing );
301 }
302 }
303 }
304
305 for( const PAD* p : m_board->GetPads() )
306 {
307 if( !p )
308 continue;
309
310
311 if( p->GetAttribute() != PAD_ATTRIB::NPTH )
312 continue;
313
314 PCB_SHAPE* s = new PCB_SHAPE( NULL, SHAPE_T::CIRCLE );
315 s->SetRadius( p->GetDrillSize().x / 2 );
316 s->SetPosition( p->GetPosition() );
317 aVector.push_back( s );
318 }
319}
320
322{
323 if( !m_board )
324 return -1;
325
326 std::vector<int> netcodes;
327
328 this->CollectNetCodes( netcodes );
329 double maxConstraint = GetMaxConstraint( netcodes );
330
331 if( maxConstraint <= 0 )
332 return 0;
333
334 SHAPE_POLY_SET outline;
335
336 if( !m_board->GetBoardPolygonOutlines( outline ) )
337 return -1;
338
339 const DRAWINGS drawings = m_board->Drawings();
340 CreepageGraph graph( *m_board );
341
342
343 if( ADVANCED_CFG::GetCfg().m_EnableCreepageSlot )
344 {
346 }
347 else
348 {
349 graph.m_minGrooveWidth = 0;
350 }
351
352 graph.m_boardOutline = &outline;
353
354 this->CollectBoardEdges( graph.m_boardEdge );
358
359 graph.GeneratePaths( maxConstraint, Edge_Cuts );
360
361
362 int beNodeSize = graph.m_nodes.size();
363 int beConnectionsSize = graph.m_connections.size();
364 bool prevTestChangedGraph = false;
365
366 alg::for_all_pairs( netcodes.begin(), netcodes.end(),
367 [&]( int aNet1, int aNet2 )
368 {
369 if( aNet1 == aNet2 )
370 return;
371
372 for( PCB_LAYER_ID layer : LSET::AllCuMask().CuStack() )
373 {
374 if( !m_board->IsLayerEnabled( layer ) )
375 continue;
376
377 if ( prevTestChangedGraph )
378 {
379 size_t vectorSize = graph.m_connections.size();
380
381 for( size_t i = beConnectionsSize; i < vectorSize; i++ )
382 {
383 // We need to remove the connection from its endpoints' lists.
384 graph.RemoveConnection( graph.m_connections[i], false );
385 }
386 graph.m_connections.resize( beConnectionsSize, nullptr );
387
388 vectorSize = graph.m_nodes.size();
389 graph.m_nodes.resize( beNodeSize, nullptr );
390 }
391
392 prevTestChangedGraph = testCreepage( graph, aNet1, aNet2, layer );
393 }
394 } );
395
396 return 1;
397}
398
399
400namespace detail
401{
403}
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:288
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:2496
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:871
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1921
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition: board.cpp:2599
const FOOTPRINTS & Footprints() const
Definition: board.h:331
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
const DRAWINGS & Drawings() const
Definition: board.h:333
ecoord_type Distance(const Vec &aP) const
Definition: box2.h:797
A graph with nodes and connections for creepage calculation.
std::vector< CREEP_SHAPE * > m_shapeCollection
std::vector< std::shared_ptr< GraphNode > > m_nodes
std::shared_ptr< GraphNode > AddNetElements(int aNetCode, PCB_LAYER_ID aLayer, int aMaxCreepage)
std::vector< std::shared_ptr< GraphConnection > > m_connections
void SetTarget(double aTarget)
void TransformCreepShapesToNodes(std::vector< CREEP_SHAPE * > &aShapes)
double Solve(std::shared_ptr< GraphNode > &aFrom, std::shared_ptr< GraphNode > &aTo, std::vector< std::shared_ptr< GraphConnection > > &aResult)
std::vector< BOARD_ITEM * > m_boardEdge
void TransformEdgeToCreepShapes()
void GeneratePaths(double aMaxWeight, PCB_LAYER_ID aLayer, bool aGenerateBoardEdges=true)
SHAPE_POLY_SET * m_boardOutline
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:99
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:679
bool IsCancelled() const
DRC_GRAPHICS_HANDLER m_graphicsHandler
Definition: drc_engine.h:276
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:372
void ShowPathDRC(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)
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_ENGINE * m_drcEngine
void SetRadius(int aX)
Definition: eda_shape.h:196
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
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.
@ DRCE_CREEPAGE
Definition: drc_item.h:44
@ CREEPAGE_CONSTRAINT
Definition: drc_rule.h:50
std::shared_ptr< GraphNode > FindInGraphNodes(std::shared_ptr< GraphNode > aNode, std::vector< std::shared_ptr< GraphNode > > &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)