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 The 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#include <optional>
27
28#include <pcbnew/pad.h>
29#include <pcbnew/pcb_track.h>
30#include <pcbnew/board.h>
31
32#include <router/pns_node.h>
33#include <router/pns_router.h>
34#include <router/pns_item.h>
35#include <router/pns_via.h>
37
38static bool isCopper( const PNS::ITEM* aItem )
39{
40 if( !aItem )
41 return false;
42
43 BOARD_ITEM* parent = aItem->Parent();
44
45 if( parent && parent->Type() == PCB_PAD_T )
46 {
47 PAD* pad = static_cast<PAD*>( parent );
48
49 if( !pad->IsOnCopperLayer() )
50 return false;
51
52 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
53 return true;
54
55 // round NPTH with a hole size >= pad size are not on a copper layer
56 // All other NPTH are seen on copper layers
57 // This is a basic criteria, but probably enough for a NPTH
58 // TODO(JE) padstacks
59 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
60 {
61 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x <= pad->GetDrillSize().x )
62 return false;
63 }
64
65 return true;
66 }
67
68 return true;
69}
70
71
72static bool isHole( const PNS::ITEM* aItem )
73{
74 if( !aItem )
75 return false;
76
77 return aItem->OfKind( PNS::ITEM::HOLE_T );
78}
79
80
81static bool isEdge( const PNS::ITEM* aItem )
82{
83 if( !aItem )
84 return false;
85
86 const BOARD_ITEM *parent = aItem->BoardItem();
87
88 return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
89}
90
91
93{
94public:
98
100
101 virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
102 bool aUseClearanceEpsilon = true ) override
103 {
104 PNS::CONSTRAINT constraint;
105 int rv = 0;
106 PNS_LAYER_RANGE layers;
107
108 if( !aB )
109 layers = aA->Layers();
110 else if( isEdge( aA ) )
111 layers = aB->Layers();
112 else if( isEdge( aB ) )
113 layers = aA->Layers();
114 else
115 layers = aA->Layers().Intersection( aB->Layers() );
116
117 // Normalize layer range (no -1 magic numbers)
119
120 for( int layer = layers.Start(); layer <= layers.End(); ++layer )
121 {
122 if( isHole( aA ) && isHole( aB) )
123 {
124 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
125 {
126 if( constraint.m_Value.Min() > rv )
127 rv = constraint.m_Value.Min();
128 }
129 }
130 else if( isHole( aA ) || isHole( aB ) )
131 {
132 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
133 {
134 if( constraint.m_Value.Min() > rv )
135 rv = constraint.m_Value.Min();
136 }
137 }
138 else if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
139 {
140 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
141 {
142 if( constraint.m_Value.Min() > rv )
143 rv = constraint.m_Value.Min();
144 }
145 }
146 else if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
147 {
148 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
149 {
150 if( constraint.m_Value.Min() > rv )
151 rv = constraint.m_Value.Min();
152 }
153 }
154 }
155
156 return rv;
157 }
158
159 virtual PNS::NET_HANDLE DpCoupledNet( PNS::NET_HANDLE aNet ) override { return nullptr; }
160 virtual int DpNetPolarity( PNS::NET_HANDLE aNet ) override { return -1; }
161
162 virtual bool DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
163 PNS::NET_HANDLE& aNetN ) override
164 {
165 return false;
166 }
167
168 virtual int NetCode( PNS::NET_HANDLE aNet ) override
169 {
170 return -1;
171 }
172
173 virtual wxString NetName( PNS::NET_HANDLE aNet ) override
174 {
175 return wxEmptyString;
176 }
177
178 virtual bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA,
179 const PNS::ITEM* aItemB, int aLayer,
180 PNS::CONSTRAINT* aConstraint ) override
181 {
182 ITEM_KEY key;
183
184 key.a = aItemA;
185 key.b = aItemB;
186 key.type = aType;
187
188 auto it = m_ruleMap.find( key );
189
190 if( it == m_ruleMap.end() )
191 {
192 int cl;
193 switch( aType )
194 {
198 default: return false;
199 }
200
201 //printf("GetDef %s %s %d cl %d\n", aItemA->KindStr().c_str(), aItemB->KindStr().c_str(), aType, cl );
202
203 aConstraint->m_Type = aType;
204 aConstraint->m_Value.SetMin( cl );
205
206 return true;
207 }
208 else
209 {
210 *aConstraint = it->second;
211 }
212
213 return true;
214 }
215
216 int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
217
218 struct ITEM_KEY
219 {
220 const PNS::ITEM* a = nullptr;
221 const PNS::ITEM* b = nullptr;
223
224 bool operator==( const ITEM_KEY& other ) const
225 {
226 return a == other.a && b == other.b && type == other.type;
227 }
228
229 bool operator<( const ITEM_KEY& other ) const
230 {
231 if( a < other.a )
232 {
233 return true;
234 }
235 else if ( a == other.a )
236 {
237 if( b < other.b )
238 return true;
239 else if ( b == other.b )
240 return type < other.type;
241 }
242
243 return false;
244 }
245 };
246
247 bool IsInNetTie( const PNS::ITEM* aA ) override { return false; }
248
249 bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos,
250 const PNS::ITEM* aCollidingItem ) override
251 {
252 return false;
253 }
254
255 bool IsDrilledHole( const PNS::ITEM* aItem ) override { return false; }
256
257 bool IsNonPlatedSlot( const PNS::ITEM* aItem ) override { return false; }
258
259 bool IsKeepout( const PNS::ITEM* aObstacle, const PNS::ITEM* aItem, bool* aEnforce ) override
260 {
261 return false;
262 }
263
264 void AddMockRule( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
265 PNS::CONSTRAINT& aConstraint )
266 {
267 ITEM_KEY key;
268
269 key.a = aItemA;
270 key.b = aItemB;
271 key.type = aType;
272
273 m_ruleMap[key] = aConstraint;
274 }
275
276 int m_defaultClearance = 200000;
277 int m_defaultHole2Hole = 220000;
279
280private:
281 std::map<ITEM_KEY, PNS::CONSTRAINT> m_ruleMap;
283};
284
285struct PNS_TEST_FIXTURE;
286
288{
289public:
291 m_testFixture( aFixture )
292 {}
293
295
296 void HideItem( PNS::ITEM* aItem ) override {};
297 void DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit = false,
298 int aFlags = 0 ) override {};
300
301private:
303};
304
305
321
322
327
328static void dumpObstacles( const PNS::NODE::OBSTACLES &obstacles )
329{
330 for( const PNS::OBSTACLE& obs : obstacles )
331 {
332 BOOST_TEST_MESSAGE( wxString::Format( "%p [%s] - %p [%s], clearance %d",
333 obs.m_head, obs.m_head->KindStr().c_str(),
334 obs.m_item, obs.m_item->KindStr().c_str(),
335 obs.m_clearance ) );
336 }
337}
338
340{
341 PNS::VIA* v1 = new PNS::VIA( VECTOR2I( 0, 1000000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
342 PNS::VIA* v2 = new PNS::VIA( VECTOR2I( 0, 2000000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
343
344 std::unique_ptr<PNS::NODE> world ( new PNS::NODE );
345
346 v1->SetNet( (PNS::NET_HANDLE) 1 );
347 v2->SetNet( (PNS::NET_HANDLE) 2 );
348
349 world->SetMaxClearance( 10000000 );
350 world->SetRuleResolver( &m_ruleResolver );
351
352 world->AddRaw( v1 );
353 world->AddRaw( v2 );
354
355 BOOST_TEST_MESSAGE( "via to via, no violations" );
356 {
357 PNS::NODE::OBSTACLES obstacles;
358 int count = world->QueryColliding( v1, obstacles );
359 dumpObstacles( obstacles );
360 BOOST_CHECK_EQUAL( obstacles.size(), 0 );
361 BOOST_CHECK_EQUAL( count, 0 );
362 }
363
364 BOOST_TEST_MESSAGE( "via to via, forced copper to copper violation" );
365 {
366 PNS::NODE::OBSTACLES obstacles;
367 m_ruleResolver.m_defaultClearance = 1000000;
368 world->QueryColliding( v1, obstacles );
369 dumpObstacles( obstacles );
370
371 BOOST_CHECK_EQUAL( obstacles.size(), 1 );
372 const auto& first = *obstacles.begin();
373
374 BOOST_CHECK_EQUAL( first.m_head, v1 );
375 BOOST_CHECK_EQUAL( first.m_item, v2 );
376 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultClearance );
377 }
378
379 BOOST_TEST_MESSAGE( "via to via, forced hole to hole violation" );
380 {
381 PNS::NODE::OBSTACLES obstacles;
382 m_ruleResolver.m_defaultClearance = 200000;
383 m_ruleResolver.m_defaultHole2Hole = 1000000;
384
385 world->QueryColliding( v1, obstacles );
386 dumpObstacles( obstacles );
387
388 BOOST_CHECK_EQUAL( obstacles.size(), 1 );
389 auto iter = obstacles.begin();
390 const auto& first = *iter++;
391
392 BOOST_CHECK_EQUAL( first.m_head, v1->Hole() );
393 BOOST_CHECK_EQUAL( first.m_item, v2->Hole() );
394 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Hole );
395 }
396
397 BOOST_TEST_MESSAGE( "via to via, forced copper to hole violation" );
398 {
399 PNS::NODE::OBSTACLES obstacles;
400 m_ruleResolver.m_defaultHole2Hole = 220000;
401 m_ruleResolver.m_defaultHole2Copper = 1000000;
402
403 world->QueryColliding( v1, obstacles );
404 dumpObstacles( obstacles );
405
406 BOOST_CHECK_EQUAL( obstacles.size(), 2 );
407 auto iter = obstacles.begin();
408 const auto& first = *iter++;
409
410 // There is no guarantee on what order the two collisions will be in...
411 BOOST_CHECK( ( first.m_head == v1 && first.m_item == v2->Hole() )
412 || ( first.m_head == v1->Hole() && first.m_item == v2 ) );
413
414 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Copper );
415 }
416}
417
418
419BOOST_FIXTURE_TEST_CASE( PNSViaBackdrillRetention, PNS_TEST_FIXTURE )
420{
421 PNS::VIA via( VECTOR2I( 1000, 2000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 40000, 20000, nullptr,
423 via.SetHoleLayers( PNS_LAYER_RANGE( F_Cu, In2_Cu ) );
424 via.SetHolePostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) );
425 via.SetSecondaryDrill( std::optional<int>( 12000 ) );
426 via.SetSecondaryHoleLayers( std::optional<PNS_LAYER_RANGE>( PNS_LAYER_RANGE( F_Cu, In1_Cu ) ) );
427 via.SetSecondaryHolePostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED ) );
428
429 PNS::VIA viaCopy( via );
430 std::unique_ptr<PNS::VIA> viaClone( via.Clone() );
431
432 auto checkVia = [&]( const PNS::VIA& candidate )
433 {
434 BOOST_CHECK_EQUAL( candidate.HoleLayers().Start(), via.HoleLayers().Start() );
435 BOOST_CHECK_EQUAL( candidate.HoleLayers().End(), via.HoleLayers().End() );
436 BOOST_CHECK( candidate.HolePostMachining().has_value() );
437 BOOST_CHECK( candidate.HolePostMachining().value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
438 BOOST_CHECK( candidate.SecondaryDrill().has_value() );
439 BOOST_CHECK_EQUAL( candidate.SecondaryDrill().value(), via.SecondaryDrill().value() );
440 BOOST_CHECK( candidate.SecondaryHoleLayers().has_value() );
441 BOOST_CHECK_EQUAL( candidate.SecondaryHoleLayers()->Start(),
442 via.SecondaryHoleLayers()->Start() );
443 BOOST_CHECK_EQUAL( candidate.SecondaryHoleLayers()->End(),
444 via.SecondaryHoleLayers()->End() );
445 BOOST_CHECK( candidate.SecondaryHolePostMachining().has_value() );
446
447 // run this BOOST_CHECK only if possible to avoid crash
448 if( candidate.SecondaryHolePostMachining().has_value() )
449 BOOST_CHECK( candidate.SecondaryHolePostMachining().value() == via.SecondaryHolePostMachining().value() );
450 };
451
452 checkVia( viaCopy );
453 checkVia( *viaClone );
454}
455
456
457BOOST_AUTO_TEST_CASE( PCBViaBackdrillCloneRetainsData )
458{
459 BOARD board;
460 PCB_VIA via( &board );
461
462 via.SetPrimaryDrillStartLayer( F_Cu );
463 via.SetPrimaryDrillEndLayer( B_Cu );
464 via.SetFrontPostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) );
465 via.SetSecondaryDrillSize( std::optional<int>( 15000 ) );
466 via.SetSecondaryDrillStartLayer( F_Cu );
467 via.SetSecondaryDrillEndLayer( In2_Cu );
468
469 via.SetBackPostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ) );
470 via.SetTertiaryDrillSize( std::optional<int>( 8000 ) );
471 via.SetTertiaryDrillStartLayer( B_Cu );
472 via.SetTertiaryDrillEndLayer( In4_Cu );
473
474 PCB_VIA viaCopy( via );
475 std::unique_ptr<PCB_VIA> viaClone( static_cast<PCB_VIA*>( via.Clone() ) );
476
477 auto checkVia = [&]( const PCB_VIA& candidate )
478 {
479 BOOST_CHECK_EQUAL( candidate.GetPrimaryDrillStartLayer(), via.GetPrimaryDrillStartLayer() );
480 BOOST_CHECK_EQUAL( candidate.GetPrimaryDrillEndLayer(), via.GetPrimaryDrillEndLayer() );
481 BOOST_CHECK( candidate.GetFrontPostMachining().has_value() );
482 BOOST_CHECK_EQUAL( static_cast<int>( candidate.GetFrontPostMachining().value() ),
483 static_cast<int>( via.GetFrontPostMachining().value() ) );
484 BOOST_CHECK( candidate.GetSecondaryDrillSize().has_value() );
485 BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillSize().value(),
486 via.GetSecondaryDrillSize().value() );
487 BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillStartLayer(),
488 via.GetSecondaryDrillStartLayer() );
489 BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillEndLayer(),
490 via.GetSecondaryDrillEndLayer() );
491
492 BOOST_CHECK( candidate.GetBackPostMachining().has_value() );
493 BOOST_CHECK_EQUAL( static_cast<int>( candidate.GetBackPostMachining().value() ),
494 static_cast<int>( via.GetBackPostMachining().value() ) );
495 BOOST_CHECK( candidate.GetTertiaryDrillSize().has_value() );
496 BOOST_CHECK_EQUAL( candidate.GetTertiaryDrillSize().value(),
497 via.GetTertiaryDrillSize().value() );
498 BOOST_CHECK_EQUAL( candidate.GetTertiaryDrillStartLayer(),
499 via.GetTertiaryDrillStartLayer() );
500 BOOST_CHECK_EQUAL( candidate.GetTertiaryDrillEndLayer(),
501 via.GetTertiaryDrillEndLayer() );
502 };
503
504 checkVia( viaCopy );
505 checkVia( *viaClone );
506}
507
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:318
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
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(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
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:177
Definition pad.h:55
Base class for PNS router board items.
Definition pns_item.h:98
BOARD_ITEM * Parent() const
Definition pns_item.h:199
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual BOARD_ITEM * BoardItem() const
Definition pns_item.h:207
Keep the router "world" - i.e.
Definition pns_node.h:232
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:244
Represent a contiguous set of PCB layers.
int Start() const
int End() const
PNS_LAYER_RANGE Intersection(const PNS_LAYER_RANGE &aOther) const
Shortcut for comparisons/overlap tests.
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START
Definition layer_ids.h:174
@ Edge_Cuts
Definition layer_ids.h:112
@ B_Cu
Definition layer_ids.h:65
@ In2_Cu
Definition layer_ids.h:67
@ Margin
Definition layer_ids.h:113
@ In4_Cu
Definition layer_ids.h:69
@ In1_Cu
Definition layer_ids.h:66
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
@ F_Cu
Definition layer_ids.h:64
CONSTRAINT_TYPE
Definition pns_node.h:52
void * NET_HANDLE
Definition pns_item.h:55
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ THROUGH
Definition pcb_track.h:68
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:88
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)
BOOST_AUTO_TEST_CASE(PCBViaBackdrillCloneRetainsData)
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
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:695