KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pns_basics.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
26
27#include <pcbnew/pad.h>
28#include <pcbnew/pcb_track.h>
29
30#include <router/pns_node.h>
31#include <router/pns_router.h>
32#include <router/pns_item.h>
33#include <router/pns_via.h>
35
36static bool isCopper( const PNS::ITEM* aItem )
37{
38 if( !aItem )
39 return false;
40
41 BOARD_ITEM* parent = aItem->Parent();
42
43 if( parent && parent->Type() == PCB_PAD_T )
44 {
45 PAD* pad = static_cast<PAD*>( parent );
46
47 if( !pad->IsOnCopperLayer() )
48 return false;
49
50 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
51 return true;
52
53 // round NPTH with a hole size >= pad size are not on a copper layer
54 // All other NPTH are seen on copper layers
55 // This is a basic criteria, but probably enough for a NPTH
56 // TODO(JE) padstacks
57 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
58 {
59 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x <= pad->GetDrillSize().x )
60 return false;
61 }
62
63 return true;
64 }
65
66 return true;
67}
68
69
70static bool isHole( const PNS::ITEM* aItem )
71{
72 if( !aItem )
73 return false;
74
75 return aItem->OfKind( PNS::ITEM::HOLE_T );
76}
77
78
79static bool isEdge( const PNS::ITEM* aItem )
80{
81 if( !aItem )
82 return false;
83
84 const BOARD_ITEM *parent = aItem->BoardItem();
85
86 return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
87}
88
89
91{
92public:
94 {
95 }
96
98
99 virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
100 bool aUseClearanceEpsilon = true ) override
101 {
102 PNS::CONSTRAINT constraint;
103 int rv = 0;
104 PNS_LAYER_RANGE layers;
105
106 if( !aB )
107 layers = aA->Layers();
108 else if( isEdge( aA ) )
109 layers = aB->Layers();
110 else if( isEdge( aB ) )
111 layers = aA->Layers();
112 else
113 layers = aA->Layers().Intersection( aB->Layers() );
114
115 // Normalize layer range (no -1 magic numbers)
117
118 for( int layer = layers.Start(); layer <= layers.End(); ++layer )
119 {
120 if( isHole( aA ) && isHole( aB) )
121 {
122 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
123 {
124 if( constraint.m_Value.Min() > rv )
125 rv = constraint.m_Value.Min();
126 }
127 }
128 else if( isHole( aA ) || isHole( aB ) )
129 {
130 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
131 {
132 if( constraint.m_Value.Min() > rv )
133 rv = constraint.m_Value.Min();
134 }
135 }
136 else if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
137 {
138 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
139 {
140 if( constraint.m_Value.Min() > rv )
141 rv = constraint.m_Value.Min();
142 }
143 }
144 else if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
145 {
146 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
147 {
148 if( constraint.m_Value.Min() > rv )
149 rv = constraint.m_Value.Min();
150 }
151 }
152 }
153
154 return rv;
155 }
156
157 virtual PNS::NET_HANDLE DpCoupledNet( PNS::NET_HANDLE aNet ) override { return nullptr; }
158 virtual int DpNetPolarity( PNS::NET_HANDLE aNet ) override { return -1; }
159
160 virtual bool DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
161 PNS::NET_HANDLE& aNetN ) override
162 {
163 return false;
164 }
165
166 virtual int NetCode( PNS::NET_HANDLE aNet ) override
167 {
168 return -1;
169 }
170
171 virtual wxString NetName( PNS::NET_HANDLE aNet ) override
172 {
173 return wxEmptyString;
174 }
175
176 virtual bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA,
177 const PNS::ITEM* aItemB, int aLayer,
178 PNS::CONSTRAINT* aConstraint ) override
179 {
180 ITEM_KEY key;
181
182 key.a = aItemA;
183 key.b = aItemB;
184 key.type = aType;
185
186 auto it = m_ruleMap.find( key );
187
188 if( it == m_ruleMap.end() )
189 {
190 int cl;
191 switch( aType )
192 {
196 default: return false;
197 }
198
199 //printf("GetDef %s %s %d cl %d\n", aItemA->KindStr().c_str(), aItemB->KindStr().c_str(), aType, cl );
200
201 aConstraint->m_Type = aType;
202 aConstraint->m_Value.SetMin( cl );
203
204 return true;
205 }
206 else
207 {
208 *aConstraint = it->second;
209 }
210
211 return true;
212 }
213
214 int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
215
216 struct ITEM_KEY
217 {
218 const PNS::ITEM* a = nullptr;
219 const PNS::ITEM* b = nullptr;
221
222 bool operator==( const ITEM_KEY& other ) const
223 {
224 return a == other.a && b == other.b && type == other.type;
225 }
226
227 bool operator<( const ITEM_KEY& other ) const
228 {
229 if( a < other.a )
230 {
231 return true;
232 }
233 else if ( a == other.a )
234 {
235 if( b < other.b )
236 return true;
237 else if ( b == other.b )
238 return type < other.type;
239 }
240
241 return false;
242 }
243 };
244
245 bool IsInNetTie( const PNS::ITEM* aA ) override { return false; }
246
247 bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos,
248 const PNS::ITEM* aCollidingItem ) override
249 {
250 return false;
251 }
252
253 bool IsDrilledHole( const PNS::ITEM* aItem ) override { return false; }
254
255 bool IsNonPlatedSlot( const PNS::ITEM* aItem ) override { return false; }
256
257 bool IsKeepout( const PNS::ITEM* aObstacle, const PNS::ITEM* aItem, bool* aEnforce ) override
258 {
259 return false;
260 }
261
262 void AddMockRule( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
263 PNS::CONSTRAINT& aConstraint )
264 {
265 ITEM_KEY key;
266
267 key.a = aItemA;
268 key.b = aItemB;
269 key.type = aType;
270
271 m_ruleMap[key] = aConstraint;
272 }
273
274 int m_defaultClearance = 200000;
275 int m_defaultHole2Hole = 220000;
277
278private:
279 std::map<ITEM_KEY, PNS::CONSTRAINT> m_ruleMap;
281};
282
283struct PNS_TEST_FIXTURE;
284
286{
287public:
289 m_testFixture( aFixture )
290 {}
291
293
294 void HideItem( PNS::ITEM* aItem ) override {};
295 void DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit = false,
296 int aFlags = 0 ) override {};
298
299private:
301};
302
303
305{
307 m_settingsManager( true /* headless */ )
308 {
309 m_router = new PNS::ROUTER;
310 m_iface = new MOCK_PNS_KICAD_IFACE( this );
312 }
313
318 //std::unique_ptr<BOARD> m_board;
319};
320
321
323{
325}
326
327static void dumpObstacles( const PNS::NODE::OBSTACLES &obstacles )
328{
329 for( const PNS::OBSTACLE& obs : obstacles )
330 {
331 BOOST_TEST_MESSAGE( wxString::Format( "%p [%s] - %p [%s], clearance %d",
332 obs.m_head, obs.m_head->KindStr().c_str(),
333 obs.m_item, obs.m_item->KindStr().c_str(),
334 obs.m_clearance ) );
335 }
336}
337
339{
340 PNS::VIA* v1 = new PNS::VIA( VECTOR2I( 0, 1000000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
341 PNS::VIA* v2 = new PNS::VIA( VECTOR2I( 0, 2000000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
342
343 std::unique_ptr<PNS::NODE> world ( new PNS::NODE );
344
345 v1->SetNet( (PNS::NET_HANDLE) 1 );
346 v2->SetNet( (PNS::NET_HANDLE) 2 );
347
348 world->SetMaxClearance( 10000000 );
349 world->SetRuleResolver( &m_ruleResolver );
350
351 world->AddRaw( v1 );
352 world->AddRaw( v2 );
353
354 BOOST_TEST_MESSAGE( "via to via, no violations" );
355 {
356 PNS::NODE::OBSTACLES obstacles;
357 int count = world->QueryColliding( v1, obstacles );
358 dumpObstacles( obstacles );
359 BOOST_CHECK_EQUAL( obstacles.size(), 0 );
360 BOOST_CHECK_EQUAL( count, 0 );
361 }
362
363 BOOST_TEST_MESSAGE( "via to via, forced copper to copper violation" );
364 {
365 PNS::NODE::OBSTACLES obstacles;
366 m_ruleResolver.m_defaultClearance = 1000000;
367 world->QueryColliding( v1, obstacles );
368 dumpObstacles( obstacles );
369
370 BOOST_CHECK_EQUAL( obstacles.size(), 1 );
371 const auto& first = *obstacles.begin();
372
373 BOOST_CHECK_EQUAL( first.m_head, v1 );
374 BOOST_CHECK_EQUAL( first.m_item, v2 );
375 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultClearance );
376 }
377
378 BOOST_TEST_MESSAGE( "via to via, forced hole to hole violation" );
379 {
380 PNS::NODE::OBSTACLES obstacles;
381 m_ruleResolver.m_defaultClearance = 200000;
382 m_ruleResolver.m_defaultHole2Hole = 1000000;
383
384 world->QueryColliding( v1, obstacles );
385 dumpObstacles( obstacles );
386
387 BOOST_CHECK_EQUAL( obstacles.size(), 1 );
388 auto iter = obstacles.begin();
389 const auto& first = *iter++;
390
391 BOOST_CHECK_EQUAL( first.m_head, v1->Hole() );
392 BOOST_CHECK_EQUAL( first.m_item, v2->Hole() );
393 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Hole );
394 }
395
396 BOOST_TEST_MESSAGE( "via to via, forced copper to hole violation" );
397 {
398 PNS::NODE::OBSTACLES obstacles;
399 m_ruleResolver.m_defaultHole2Hole = 220000;
400 m_ruleResolver.m_defaultHole2Copper = 1000000;
401
402 world->QueryColliding( v1, obstacles );
403 dumpObstacles( obstacles );
404
405 BOOST_CHECK_EQUAL( obstacles.size(), 2 );
406 auto iter = obstacles.begin();
407 const auto& first = *iter++;
408
409 // There is no guarantee on what order the two collisions will be in...
410 BOOST_CHECK( ( first.m_head == v1 && first.m_item == v2->Hole() )
411 || ( first.m_head == v1->Hole() && first.m_item == v2 ) );
412
413 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Copper );
414 }
415}
416
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:319
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
T Min() const
Definition: minoptmax.h:33
void SetMin(T v)
Definition: minoptmax.h:41
PNS::RULE_RESOLVER * GetRuleResolver() override
void DisplayItem(const PNS::ITEM *aItem, int aClearance, bool aEdit=false, int aFlags=0) override
void HideItem(PNS::ITEM *aItem) override
~MOCK_PNS_KICAD_IFACE() override
MOCK_PNS_KICAD_IFACE(PNS_TEST_FIXTURE *aFixture)
PNS_TEST_FIXTURE * m_testFixture
virtual int NetCode(PNS::NET_HANDLE aNet) override
bool IsNetTieExclusion(const PNS::ITEM *aItem, const VECTOR2I &aCollisionPos, const PNS::ITEM *aCollidingItem) override
bool IsKeepout(const PNS::ITEM *aObstacle, const PNS::ITEM *aItem, bool *aEnforce) override
virtual int Clearance(const PNS::ITEM *aA, const PNS::ITEM *aB, bool aUseClearanceEpsilon=true) override
virtual bool QueryConstraint(PNS::CONSTRAINT_TYPE aType, const PNS::ITEM *aItemA, const PNS::ITEM *aItemB, int aLayer, PNS::CONSTRAINT *aConstraint) override
bool IsInNetTie(const PNS::ITEM *aA) override
std::map< ITEM_KEY, PNS::CONSTRAINT > m_ruleMap
virtual PNS::NET_HANDLE DpCoupledNet(PNS::NET_HANDLE aNet) override
bool IsDrilledHole(const PNS::ITEM *aItem) override
void AddMockRule(PNS::CONSTRAINT_TYPE aType, const PNS::ITEM *aItemA, const PNS::ITEM *aItemB, PNS::CONSTRAINT &aConstraint)
bool IsNonPlatedSlot(const PNS::ITEM *aItem) override
virtual bool DpNetPair(const PNS::ITEM *aItem, PNS::NET_HANDLE &aNetP, PNS::NET_HANDLE &aNetN) override
virtual wxString NetName(PNS::NET_HANDLE aNet) override
virtual ~MOCK_RULE_RESOLVER()
int ClearanceEpsilon() const override
virtual int DpNetPolarity(PNS::NET_HANDLE aNet) override
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
Definition: pad.h:54
Base class for PNS router board items.
Definition: pns_item.h:97
BOARD_ITEM * Parent() const
Definition: pns_item.h:189
const PNS_LAYER_RANGE & Layers() const
Definition: pns_item.h:199
bool OfKind(int aKindMask) const
Definition: pns_item.h:178
virtual BOARD_ITEM * BoardItem() const
Definition: pns_item.h:194
Keep the router "world" - i.e.
Definition: pns_node.h:231
std::set< OBSTACLE > OBSTACLES
Definition: pns_node.h:243
void SetInterface(ROUTER_IFACE *aIface)
Represent a contiguous set of PCB layers.
Definition: pns_layerset.h:32
int Start() const
Definition: pns_layerset.h:86
int End() const
Definition: pns_layerset.h:91
PNS_LAYER_RANGE Intersection(const PNS_LAYER_RANGE &aOther) const
Shortcut for comparisons/overlap tests.
Definition: pns_layerset.h:112
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START
Definition: layer_ids.h:138
@ Edge_Cuts
Definition: layer_ids.h:112
@ B_Cu
Definition: layer_ids.h:65
@ Margin
Definition: layer_ids.h:113
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:135
@ F_Cu
Definition: layer_ids.h:64
CONSTRAINT_TYPE
Definition: pns_node.h:52
void * NET_HANDLE
Definition: pns_item.h:54
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
bool operator<(const ITEM_KEY &other) const
bool operator==(const ITEM_KEY &other) const
An abstract function object, returning a design rule (clearance, diff pair gap, etc) required between...
Definition: pns_node.h:73
MINOPTMAX< int > m_Value
Definition: pns_node.h:75
CONSTRAINT_TYPE m_Type
Definition: pns_node.h:74
Hold an object colliding with another object, along with some useful data about the collision.
Definition: pns_node.h:87
SETTINGS_MANAGER m_settingsManager
MOCK_RULE_RESOLVER m_ruleResolver
PNS::ROUTER * m_router
MOCK_PNS_KICAD_IFACE * m_iface
VECTOR3I v1(5, 5, 5)
static bool isEdge(const PNS::ITEM *aItem)
static bool isHole(const PNS::ITEM *aItem)
static void dumpObstacles(const PNS::NODE::OBSTACLES &obstacles)
BOOST_FIXTURE_TEST_CASE(PNSHoleCollisions, PNS_TEST_FIXTURE)
static bool isCopper(const PNS::ITEM *aItem)
VECTOR2I v2(1, 0)
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691