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:
56
57 virtual ~DRC_TEST_PROVIDER_CREEPAGE() = default;
58
59 virtual bool Run() override;
60
61 virtual const wxString GetName() const override { return wxT( "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{
78 m_board = m_drcEngine->GetBoard();
79 m_reportedPairs.clear();
80
81 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CREEPAGE ) )
82 {
83 if( !reportPhase( _( "Checking creepage..." ) ) )
84 return false; // DRC cancelled
85
87 }
88 return !m_drcEngine->IsCancelled();
89}
90
91
92int DRC_TEST_PROVIDER_CREEPAGE::testCreepage( CREEPAGE_GRAPH& aGraph, int aNetCodeA, int aNetCodeB,
93 PCB_LAYER_ID aLayer )
94{
95 PCB_TRACK bci1( m_board );
96 PCB_TRACK bci2( m_board );
97 bci1.SetNetCode( aNetCodeA );
98 bci2.SetNetCode( aNetCodeB );
99 bci1.SetLayer( aLayer );
100 bci2.SetLayer( aLayer );
101
102
103 DRC_CONSTRAINT constraint;
104 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1, &bci2, aLayer );
105 double creepageValue = constraint.Value().Min();
106 aGraph.SetTarget( creepageValue );
107
108 if( creepageValue <= 0 )
109 return 0;
110
111 // Let's make a quick "clearance test"
112 NETINFO_ITEM* netA = m_board->FindNet( aNetCodeA );
113 NETINFO_ITEM* netB = m_board->FindNet( aNetCodeB );
114
115 if ( !netA || !netB )
116 return 0;
117
118 if ( netA->GetBoundingBox().Distance( netB->GetBoundingBox() ) > creepageValue )
119 return 0;
120
121 std::shared_ptr<GRAPH_NODE> NetA = aGraph.AddNetElements( aNetCodeA, aLayer, creepageValue );
122 std::shared_ptr<GRAPH_NODE> NetB = aGraph.AddNetElements( aNetCodeB, aLayer, creepageValue );
123
124
125 aGraph.GeneratePaths( creepageValue, aLayer );
126
127 std::vector<std::shared_ptr<GRAPH_NODE>> temp_nodes;
128
129 std::copy_if( aGraph.m_nodes.begin(), aGraph.m_nodes.end(), std::back_inserter( temp_nodes ),
130 []( std::shared_ptr<GRAPH_NODE> aNode )
131 {
132 return !!aNode && aNode->m_parent && aNode->m_parent->IsConductive()
133 && aNode->m_connectDirectly && aNode->m_type == GRAPH_NODE::POINT;
134 } );
135
136 alg::for_all_pairs( temp_nodes.begin(), temp_nodes.end(),
137 [&]( std::shared_ptr<GRAPH_NODE> aN1, std::shared_ptr<GRAPH_NODE> aN2 )
138 {
139 if( aN1 == aN2 )
140 return;
141
142 if( !aN1 || !aN2 )
143 return;
144
145 if( !( aN1->m_parent ) || !( aN2->m_parent ) )
146 return;
147
148 if( ( aN1->m_parent ) != ( aN2->m_parent ) )
149 return;
150
151
152 if( aN1->m_parent->IsConductive() )
153 return;
154
155 if( aN1->m_connectDirectly || aN2->m_connectDirectly )
156 return;
157
158 // We are only looking for points on circles and arcs
159
160 if( aN1->m_type != GRAPH_NODE::POINT )
161 return;
162
163 if( aN2->m_type != GRAPH_NODE::POINT )
164 return;
165
166 aN1->m_parent->ConnectChildren( aN1, aN2, aGraph );
167 } );
168
169 std::vector<std::shared_ptr<GRAPH_CONNECTION>> shortestPath;
170 shortestPath.clear();
171 double distance = aGraph.Solve( NetA, NetB, shortestPath );
172
173 if( !shortestPath.empty() && ( shortestPath.size() >= 4 ) && ( distance - creepageValue < 0 ) )
174 {
175 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CREEPAGE );
176 wxString msg = formatMsg( _( "(%s creepage %s; actual %s)" ),
177 constraint.GetName(),
178 creepageValue, distance );
179 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
180 drce->SetViolatingRule( constraint.GetParentRule() );
181
182 std::shared_ptr<GRAPH_CONNECTION> gc1 = shortestPath[1];
183 std::shared_ptr<GRAPH_CONNECTION> gc2 = shortestPath[shortestPath.size() - 2];
184
185 if( gc1->n1 && gc2->n2 )
186 {
187 const BOARD_ITEM* item1 = gc1->n1->m_parent->GetParent();
188 const BOARD_ITEM* item2 = gc2->n2->m_parent->GetParent();
189
190 if( m_reportedPairs.insert( std::make_pair( item1, item2 ) ).second )
191 drce->SetItems( item1, item2 );
192 else
193 return 1;
194 }
195
196 VECTOR2I startPoint = gc1->m_path.a2;
197 VECTOR2I endPoint = gc2->m_path.a2;
198 std::vector<PCB_SHAPE> path;
199
200 for( const std::shared_ptr<GRAPH_CONNECTION>& gc : shortestPath )
201 gc->GetShapes( path );
202
203 reportViolation( drce, gc1->m_path.a2, aLayer, GetShapes( path, startPoint, endPoint, distance ) );
204 }
205
206 return 1;
207}
208
209
210double DRC_TEST_PROVIDER_CREEPAGE::GetMaxConstraint( const std::vector<int>& aNetCodes )
211{
212 double maxConstraint = 0;
213 DRC_CONSTRAINT constraint;
214
215 PCB_TRACK bci1( m_board );
216 PCB_TRACK bci2( m_board );
217
218 alg::for_all_pairs( aNetCodes.begin(), aNetCodes.end(),
219 [&]( int aNet1, int aNet2 )
220 {
221 if( aNet1 == aNet2 )
222 return;
223
224 bci1.SetNetCode( aNet1 );
225 bci2.SetNetCode( aNet2 );
226
227 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_board->GetCopperLayerCount() ) )
228 {
229 bci1.SetLayer( layer );
230 bci2.SetLayer( layer );
231 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1, &bci2, layer );
232 double value = constraint.Value().Min();
233 maxConstraint = value > maxConstraint ? value : maxConstraint;
234 }
235 } );
236
237 return maxConstraint;
238}
239
240
241void DRC_TEST_PROVIDER_CREEPAGE::CollectNetCodes( std::vector<int>& aVector )
242{
243 NETCODES_MAP nets = m_board->GetNetInfo().NetsByNetcode();
244
245 for( auto it = nets.begin(); it != nets.end(); it++ )
246 aVector.push_back( it->first );
247}
248
249
250void DRC_TEST_PROVIDER_CREEPAGE::CollectBoardEdges( std::vector<BOARD_ITEM*>& aVector )
251{
252 if( !m_board )
253 return;
254
255 for( BOARD_ITEM* drawing : m_board->Drawings() )
256 {
257 if( !drawing )
258 continue;
259
260 if( drawing->IsOnLayer( Edge_Cuts ) )
261 aVector.push_back( drawing );
262 }
263
264 for( FOOTPRINT* fp : m_board->Footprints() )
265 {
266 if( !fp )
267 continue;
268
269 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
270 {
271 if( !drawing )
272 continue;
273
274 if( drawing->IsOnLayer( Edge_Cuts ) )
275 aVector.push_back( drawing );
276 }
277 }
278
279 for( const PAD* p : m_board->GetPads() )
280 {
281 if( !p )
282 continue;
283
284 if( p->GetAttribute() != PAD_ATTRIB::NPTH )
285 continue;
286
287 PCB_SHAPE* s = new PCB_SHAPE( NULL, SHAPE_T::CIRCLE );
288 s->SetRadius( p->GetDrillSize().x / 2 );
289 s->SetPosition( p->GetPosition() );
290 aVector.push_back( s );
291 }
292}
293
294
296{
297 if( !m_board )
298 return -1;
299
300 std::vector<int> netcodes;
301
302 this->CollectNetCodes( netcodes );
303 double maxConstraint = GetMaxConstraint( netcodes );
304
305 if( maxConstraint <= 0 )
306 return 0;
307
308 SHAPE_POLY_SET outline;
309
310 if( !m_board->GetBoardPolygonOutlines( outline ) )
311 return -1;
312
313 const DRAWINGS drawings = m_board->Drawings();
314 CREEPAGE_GRAPH graph( *m_board );
315
316 if( ADVANCED_CFG::GetCfg().m_EnableCreepageSlot )
317 graph.m_minGrooveWidth = m_board->GetDesignSettings().m_MinGrooveWidth;
318 else
319 graph.m_minGrooveWidth = 0;
320
321 graph.m_boardOutline = &outline;
322
323 this->CollectBoardEdges( graph.m_boardEdge );
327
328 graph.GeneratePaths( maxConstraint, Edge_Cuts );
329
330 int beNodeSize = graph.m_nodes.size();
331 int beConnectionsSize = graph.m_connections.size();
332 bool prevTestChangedGraph = false;
333
334 size_t current = 0;
335 size_t total = ( netcodes.size() * ( netcodes.size() - 1 ) ) / 2 * m_board->GetCopperLayerCount();
336 LSET layers = m_board->GetLayerSet();
337
338 alg::for_all_pairs( netcodes.begin(), netcodes.end(),
339 [&]( int aNet1, int aNet2 )
340 {
341 if( aNet1 == aNet2 )
342 return;
343
344 for( auto it = layers.copper_layers_begin(); it != layers.copper_layers_end(); ++it )
345 {
346 PCB_LAYER_ID layer = *it;
347
348 reportProgress( current++, total );
349
350 if ( prevTestChangedGraph )
351 {
352 size_t vectorSize = graph.m_connections.size();
353
354 for( size_t i = beConnectionsSize; i < vectorSize; i++ )
355 {
356 // We need to remove the connection from its endpoints' lists.
357 graph.RemoveConnection( graph.m_connections[i], false );
358 }
359 graph.m_connections.resize( beConnectionsSize, nullptr );
360
361 vectorSize = graph.m_nodes.size();
362 graph.m_nodes.resize( beNodeSize, nullptr );
363 }
364
365 prevTestChangedGraph = testCreepage( graph, aNet1, aNet2, layer );
366 }
367 } );
368
369 return 1;
370}
371
372
373namespace detail
374{
376}
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:280
ecoord_type Distance(const Vec &aP) const
Definition box2.h:797
A graph with nodes and connections for creepage calculation.
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
std::vector< BOARD_ITEM * > m_boardEdge
void GeneratePaths(double aMaxWeight, PCB_LAYER_ID aLayer)
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:170
MINOPTMAX< int > & Value()
Definition drc_rule.h:163
DRC_RULE * GetParentRule() const
Definition drc_rule.h:166
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:381
std::vector< PCB_SHAPE > GetShapes(const std::vector< PCB_SHAPE > &aShapes, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aLength)
void CollectBoardEdges(std::vector< BOARD_ITEM * > &aVector)
virtual ~DRC_TEST_PROVIDER_CREEPAGE()=default
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 bool Run() override
Run this provider against the given PCB with configured options (if any).
void CollectNetCodes(std::vector< int > &aVector)
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::vector< PCB_SHAPE > &aShapes={})
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
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:54
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition pad.h:54
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_shape.h:78
Represent a set of closed polygons.
The common library.
@ DRCE_CREEPAGE
Definition drc_item.h:44
@ CREEPAGE_CONSTRAINT
Definition drc_rule.h:50
#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
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695