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> drcItem = DRC_ITEM::Create( DRCE_CREEPAGE );
176 wxString msg = formatMsg( _( "(%s creepage %s; actual %s)" ),
177 constraint.GetName(),
178 creepageValue, distance );
179 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
180 drcItem->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 drcItem->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( drcItem, gc1->m_path.a2, aLayer,
204 [&]( PCB_MARKER* aMarker )
205 {
206 aMarker->SetPath( path, startPoint, endPoint );
207 } );
208 }
209
210 return 1;
211}
212
213
214double DRC_TEST_PROVIDER_CREEPAGE::GetMaxConstraint( const std::vector<int>& aNetCodes )
215{
216 double maxConstraint = 0;
217 DRC_CONSTRAINT constraint;
218
219 PCB_TRACK bci1( m_board );
220 PCB_TRACK bci2( m_board );
221
222 alg::for_all_pairs( aNetCodes.begin(), aNetCodes.end(),
223 [&]( int aNet1, int aNet2 )
224 {
225 if( aNet1 == aNet2 )
226 return;
227
228 bci1.SetNetCode( aNet1 );
229 bci2.SetNetCode( aNet2 );
230
231 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_board->GetCopperLayerCount() ) )
232 {
233 bci1.SetLayer( layer );
234 bci2.SetLayer( layer );
235 constraint = m_drcEngine->EvalRules( CREEPAGE_CONSTRAINT, &bci1, &bci2, layer );
236 double value = constraint.Value().Min();
237 maxConstraint = value > maxConstraint ? value : maxConstraint;
238 }
239 } );
240
241 return maxConstraint;
242}
243
244
245void DRC_TEST_PROVIDER_CREEPAGE::CollectNetCodes( std::vector<int>& aVector )
246{
247 NETCODES_MAP nets = m_board->GetNetInfo().NetsByNetcode();
248
249 for( auto it = nets.begin(); it != nets.end(); it++ )
250 aVector.push_back( it->first );
251}
252
253
254void DRC_TEST_PROVIDER_CREEPAGE::CollectBoardEdges( std::vector<BOARD_ITEM*>& aVector )
255{
256 if( !m_board )
257 return;
258
259 for( BOARD_ITEM* drawing : m_board->Drawings() )
260 {
261 if( !drawing )
262 continue;
263
264 if( drawing->IsOnLayer( Edge_Cuts ) )
265 aVector.push_back( drawing );
266 }
267
268 for( FOOTPRINT* fp : m_board->Footprints() )
269 {
270 if( !fp )
271 continue;
272
273 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
274 {
275 if( !drawing )
276 continue;
277
278 if( drawing->IsOnLayer( Edge_Cuts ) )
279 aVector.push_back( drawing );
280 }
281 }
282
283 for( const PAD* p : m_board->GetPads() )
284 {
285 if( !p )
286 continue;
287
288 if( p->GetAttribute() != PAD_ATTRIB::NPTH )
289 continue;
290
291 PCB_SHAPE* s = new PCB_SHAPE( NULL, SHAPE_T::CIRCLE );
292 s->SetRadius( p->GetDrillSize().x / 2 );
293 s->SetPosition( p->GetPosition() );
294 aVector.push_back( s );
295 }
296}
297
298
300{
301 if( !m_board )
302 return -1;
303
304 std::vector<int> netcodes;
305
306 this->CollectNetCodes( netcodes );
307 double maxConstraint = GetMaxConstraint( netcodes );
308
309 if( maxConstraint <= 0 )
310 return 0;
311
312 SHAPE_POLY_SET outline;
313
314 if( !m_board->GetBoardPolygonOutlines( outline ) )
315 return -1;
316
317 const DRAWINGS drawings = m_board->Drawings();
318 CREEPAGE_GRAPH graph( *m_board );
319
320 if( ADVANCED_CFG::GetCfg().m_EnableCreepageSlot )
321 graph.m_minGrooveWidth = m_board->GetDesignSettings().m_MinGrooveWidth;
322 else
323 graph.m_minGrooveWidth = 0;
324
325 graph.m_boardOutline = &outline;
326
327 this->CollectBoardEdges( graph.m_boardEdge );
331
332 graph.GeneratePaths( maxConstraint, Edge_Cuts );
333
334 int beNodeSize = graph.m_nodes.size();
335 int beConnectionsSize = graph.m_connections.size();
336 bool prevTestChangedGraph = false;
337
338 size_t current = 0;
339 size_t total = ( netcodes.size() * ( netcodes.size() - 1 ) ) / 2 * m_board->GetCopperLayerCount();
340 LSET layers = m_board->GetLayerSet();
341
342 alg::for_all_pairs( netcodes.begin(), netcodes.end(),
343 [&]( int aNet1, int aNet2 )
344 {
345 if( aNet1 == aNet2 )
346 return;
347
348 for( auto it = layers.copper_layers_begin(); it != layers.copper_layers_end(); ++it )
349 {
350 PCB_LAYER_ID layer = *it;
351
352 reportProgress( current++, total );
353
354 if ( prevTestChangedGraph )
355 {
356 size_t vectorSize = graph.m_connections.size();
357
358 for( size_t i = beConnectionsSize; i < vectorSize; i++ )
359 {
360 // We need to remove the connection from its endpoints' lists.
361 graph.RemoveConnection( graph.m_connections[i], false );
362 }
363 graph.m_connections.resize( beConnectionsSize, nullptr );
364
365 vectorSize = graph.m_nodes.size();
366 graph.m_nodes.resize( beNodeSize, nullptr );
367 }
368
369 prevTestChangedGraph = testCreepage( graph, aNet1, aNet2, layer );
370 }
371 } );
372
373 return 1;
374}
375
376
377namespace detail
378{
380}
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:174
MINOPTMAX< int > & Value()
Definition drc_rule.h:167
DRC_RULE * GetParentRule() const
Definition drc_rule.h:170
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:381
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)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
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