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/board.h>
29#include <pcbnew/pad.h>
30#include <pcbnew/pcb_track.h>
32
35#include <router/pns_item.h>
37#include <router/pns_node.h>
38#include <router/pns_router.h>
39#include <router/pns_segment.h>
40#include <router/pns_solid.h>
41#include <router/pns_via.h>
42
43static bool isCopper( const PNS::ITEM* aItem )
44{
45 if( !aItem )
46 return false;
47
48 BOARD_ITEM* parent = aItem->Parent();
49
50 if( parent && parent->Type() == PCB_PAD_T )
51 {
52 PAD* pad = static_cast<PAD*>( parent );
53
54 if( pad->IsAperturePad() || pad->IsNPTHWithNoCopper() )
55 return false;
56 }
57
58 return true;
59}
60
61
62static bool isHole( const PNS::ITEM* aItem )
63{
64 if( !aItem )
65 return false;
66
67 return aItem->OfKind( PNS::ITEM::HOLE_T );
68}
69
70
71static bool isEdge( const PNS::ITEM* aItem )
72{
73 if( !aItem )
74 return false;
75
76 const BOARD_ITEM *parent = aItem->BoardItem();
77
78 return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
79}
80
81
83{
84public:
88
90
91 virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
92 bool aUseClearanceEpsilon = true ) override
93 {
94 PNS::CONSTRAINT constraint;
95 int rv = 0;
96 PNS_LAYER_RANGE layers;
97
98 if( !aB )
99 layers = aA->Layers();
100 else if( isEdge( aA ) )
101 layers = aB->Layers();
102 else if( isEdge( aB ) )
103 layers = aA->Layers();
104 else
105 layers = aA->Layers().Intersection( aB->Layers() );
106
107 // Normalize layer range (no -1 magic numbers)
109
110 // electrical clearances are net-aware; physical clearances are net-blind; same-net or
111 // free-pad pairs with no positive physical rule fall back to -1.
112 const bool sameNet = aA && aB && aA->Net() && aA->Net() == aB->Net();
113 const bool freePad = aA && aB && ( aA->IsFreePad() || aB->IsFreePad() );
114
115 for( int layer = layers.Start(); layer <= layers.End(); ++layer )
116 {
117 if( !sameNet && !freePad )
118 {
119 if( isHole( aA ) && isHole( aB ) )
120 {
121 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
122 {
123 if( constraint.m_Value.Min() > rv )
124 rv = constraint.m_Value.Min();
125 }
126 }
127 else if( isHole( aA ) || isHole( aB ) )
128 {
129 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
130 {
131 if( constraint.m_Value.Min() > rv )
132 rv = constraint.m_Value.Min();
133 }
134 }
135 else if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
136 {
137 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
138 {
139 if( constraint.m_Value.Min() > rv )
140 rv = constraint.m_Value.Min();
141 }
142 }
143 else if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
144 {
145 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
146 {
147 if( constraint.m_Value.Min() > rv )
148 rv = constraint.m_Value.Min();
149 }
150 }
151 }
152
153 if( isHole( aA ) || isHole( aB ) )
154 {
155 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_PHYSICAL_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
156 {
157 if( constraint.m_Value.Min() > rv )
158 rv = constraint.m_Value.Min();
159 }
160 }
161
162 if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_PHYSICAL_CLEARANCE, aA, aB, layer, &constraint ) )
163 {
164 if( constraint.m_Value.Min() > rv )
165 rv = constraint.m_Value.Min();
166 }
167 }
168
169 if( ( sameNet || freePad ) && rv == 0 )
170 rv = -1;
171
172 return rv;
173 }
174
176
177 virtual PNS::NET_HANDLE DpCoupledNet( PNS::NET_HANDLE aNet ) override { return nullptr; }
178 virtual int DpNetPolarity( PNS::NET_HANDLE aNet ) override { return -1; }
179
180 virtual bool DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
181 PNS::NET_HANDLE& aNetN ) override
182 {
183 return false;
184 }
185
186 virtual int NetCode( PNS::NET_HANDLE aNet ) override
187 {
188 return -1;
189 }
190
191 virtual wxString NetName( PNS::NET_HANDLE aNet ) override
192 {
193 return wxEmptyString;
194 }
195
196 virtual bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA,
197 const PNS::ITEM* aItemB, int aLayer,
198 PNS::CONSTRAINT* aConstraint ) override
199 {
200 ITEM_KEY key;
201
202 key.a = aItemA;
203 key.b = aItemB;
204 key.type = aType;
205
206 auto it = m_ruleMap.find( key );
207
208 if( it == m_ruleMap.end() )
209 {
210 int cl;
211 switch( aType )
212 {
218 return false;
220 break;
223 return false;
225 break;
226 default: return false;
227 }
228
229 //printf("GetDef %s %s %d cl %d\n", aItemA->KindStr().c_str(), aItemB->KindStr().c_str(), aType, cl );
230
231 aConstraint->m_Type = aType;
232 aConstraint->m_Value.SetMin( cl );
233
234 return true;
235 }
236 else
237 {
238 *aConstraint = it->second;
239 }
240
241 return true;
242 }
243
244 int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
245
246 struct ITEM_KEY
247 {
248 const PNS::ITEM* a = nullptr;
249 const PNS::ITEM* b = nullptr;
251
252 bool operator==( const ITEM_KEY& other ) const
253 {
254 return a == other.a && b == other.b && type == other.type;
255 }
256
257 bool operator<( const ITEM_KEY& other ) const
258 {
259 if( a < other.a )
260 {
261 return true;
262 }
263 else if ( a == other.a )
264 {
265 if( b < other.b )
266 return true;
267 else if ( b == other.b )
268 return type < other.type;
269 }
270
271 return false;
272 }
273 };
274
275 bool IsInNetTie( const PNS::ITEM* aA ) override { return false; }
276
277 bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos,
278 const PNS::ITEM* aCollidingItem ) override
279 {
280 return false;
281 }
282
283 bool IsDrilledHole( const PNS::ITEM* aItem ) override { return false; }
284
285 bool IsNonPlatedSlot( const PNS::ITEM* aItem ) override { return false; }
286
287 bool IsKeepout( const PNS::ITEM* aObstacle, const PNS::ITEM* aItem, bool* aEnforce ) override
288 {
289 return false;
290 }
291
292 void AddMockRule( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
293 PNS::CONSTRAINT& aConstraint )
294 {
295 ITEM_KEY key;
296
297 key.a = aItemA;
298 key.b = aItemB;
299 key.type = aType;
300
301 m_ruleMap[key] = aConstraint;
302 }
303
304 int m_defaultClearance = 200000;
305 int m_defaultHole2Hole = 220000;
307 int m_defaultPhysicalClearance = 0; // 0 means "rule does not match this pair"
308 int m_defaultPhysicalHoleClearance = 0; // 0 means "rule does not match this pair"
310
311private:
312 std::map<ITEM_KEY, PNS::CONSTRAINT> m_ruleMap;
314};
315
316struct PNS_TEST_FIXTURE;
317
319{
320public:
322 m_testFixture( aFixture )
323 {}
324
326
327 void HideItem( PNS::ITEM* aItem ) override {};
328 void DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit = false,
329 int aFlags = 0 ) override {};
331
332 bool TestInheritTrackWidth( PNS::ITEM* aItem, int* aInheritedWidth,
333 const VECTOR2I& aStartPosition = VECTOR2I() )
334 {
335 m_startLayer = aItem->Layer();
336 return inheritTrackWidth( aItem, aInheritedWidth, aStartPosition );
337 }
338
339private:
341};
342
343
359
360
365
366static void dumpObstacles( const PNS::NODE::OBSTACLES &obstacles )
367{
368 for( const PNS::OBSTACLE& obs : obstacles )
369 {
370 BOOST_TEST_MESSAGE( wxString::Format( "%p [%s] - %p [%s], clearance %d",
371 obs.m_head, obs.m_head->KindStr().c_str(),
372 obs.m_item, obs.m_item->KindStr().c_str(),
373 obs.m_clearance ) );
374 }
375}
376
378{
379 PNS::VIA* v1 = new PNS::VIA( VECTOR2I( 0, 1000000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
380 PNS::VIA* v2 = new PNS::VIA( VECTOR2I( 0, 2000000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
381
382 std::unique_ptr<PNS::NODE> world ( new PNS::NODE );
383
384 v1->SetNet( (PNS::NET_HANDLE) 1 );
385 v2->SetNet( (PNS::NET_HANDLE) 2 );
386
387 world->SetMaxClearance( 10000000 );
388 world->SetRuleResolver( &m_ruleResolver );
389
390 world->AddRaw( v1 );
391 world->AddRaw( v2 );
392
393 BOOST_TEST_MESSAGE( "via to via, no violations" );
394 {
395 PNS::NODE::OBSTACLES obstacles;
396 int count = world->QueryColliding( v1, obstacles );
397 dumpObstacles( obstacles );
398 BOOST_CHECK_EQUAL( obstacles.size(), 0 );
399 BOOST_CHECK_EQUAL( count, 0 );
400 }
401
402 BOOST_TEST_MESSAGE( "via to via, forced copper to copper violation" );
403 {
404 PNS::NODE::OBSTACLES obstacles;
405 m_ruleResolver.m_defaultClearance = 1000000;
406 world->QueryColliding( v1, obstacles );
407 dumpObstacles( obstacles );
408
409 BOOST_CHECK_EQUAL( obstacles.size(), 1 );
410 const auto& first = *obstacles.begin();
411
412 BOOST_CHECK_EQUAL( first.m_head, v1 );
413 BOOST_CHECK_EQUAL( first.m_item, v2 );
414 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultClearance );
415 }
416
417 BOOST_TEST_MESSAGE( "via to via, forced hole to hole violation" );
418 {
419 PNS::NODE::OBSTACLES obstacles;
420 m_ruleResolver.m_defaultClearance = 200000;
421 m_ruleResolver.m_defaultHole2Hole = 1000000;
422
423 world->QueryColliding( v1, obstacles );
424 dumpObstacles( obstacles );
425
426 BOOST_CHECK_EQUAL( obstacles.size(), 1 );
427 auto iter = obstacles.begin();
428 const auto& first = *iter++;
429
430 BOOST_CHECK_EQUAL( first.m_head, v1->Hole() );
431 BOOST_CHECK_EQUAL( first.m_item, v2->Hole() );
432 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Hole );
433 }
434
435 BOOST_TEST_MESSAGE( "via to via, forced copper to hole violation" );
436 {
437 PNS::NODE::OBSTACLES obstacles;
438 m_ruleResolver.m_defaultHole2Hole = 220000;
439 m_ruleResolver.m_defaultHole2Copper = 1000000;
440
441 world->QueryColliding( v1, obstacles );
442 dumpObstacles( obstacles );
443
444 BOOST_CHECK_EQUAL( obstacles.size(), 2 );
445 auto iter = obstacles.begin();
446 const auto& first = *iter++;
447
448 // There is no guarantee on what order the two collisions will be in...
449 BOOST_CHECK( ( first.m_head == v1 && first.m_item == v2->Hole() )
450 || ( first.m_head == v1->Hole() && first.m_item == v2 ) );
451
452 BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Copper );
453 }
454}
455
456
457BOOST_FIXTURE_TEST_CASE( PNSViaBackdrillRetention, PNS_TEST_FIXTURE )
458{
459 PNS::VIA via( VECTOR2I( 1000, 2000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 40000, 20000, nullptr,
461 via.SetHoleLayers( PNS_LAYER_RANGE( F_Cu, In2_Cu ) );
462 via.SetHolePostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) );
463 via.SetSecondaryDrill( std::optional<int>( 12000 ) );
464 via.SetSecondaryHoleLayers( std::optional<PNS_LAYER_RANGE>( PNS_LAYER_RANGE( F_Cu, In1_Cu ) ) );
465 via.SetSecondaryHolePostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED ) );
466
467 PNS::VIA viaCopy( via );
468 std::unique_ptr<PNS::VIA> viaClone( via.Clone() );
469
470 auto checkVia = [&]( const PNS::VIA& candidate )
471 {
472 BOOST_CHECK_EQUAL( candidate.HoleLayers().Start(), via.HoleLayers().Start() );
473 BOOST_CHECK_EQUAL( candidate.HoleLayers().End(), via.HoleLayers().End() );
474 BOOST_CHECK( candidate.HolePostMachining().has_value() );
475 BOOST_CHECK( candidate.HolePostMachining().value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
476 BOOST_CHECK( candidate.SecondaryDrill().has_value() );
477 BOOST_CHECK_EQUAL( candidate.SecondaryDrill().value(), via.SecondaryDrill().value() );
478 BOOST_CHECK( candidate.SecondaryHoleLayers().has_value() );
479 BOOST_CHECK_EQUAL( candidate.SecondaryHoleLayers()->Start(),
480 via.SecondaryHoleLayers()->Start() );
481 BOOST_CHECK_EQUAL( candidate.SecondaryHoleLayers()->End(),
482 via.SecondaryHoleLayers()->End() );
483 BOOST_CHECK( candidate.SecondaryHolePostMachining().has_value() );
484
485 // run this BOOST_CHECK only if possible to avoid crash
486 if( candidate.SecondaryHolePostMachining().has_value() )
487 BOOST_CHECK( candidate.SecondaryHolePostMachining().value() == via.SecondaryHolePostMachining().value() );
488 };
489
490 checkVia( viaCopy );
491 checkVia( *viaClone );
492}
493
494
495BOOST_AUTO_TEST_CASE( PCBViaBackdrillCloneRetainsData )
496{
497 BOARD board;
498 PCB_VIA via( &board );
499
500 via.SetPrimaryDrillStartLayer( F_Cu );
501 via.SetPrimaryDrillEndLayer( B_Cu );
502 via.SetFrontPostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) );
503 via.SetSecondaryDrillSize( std::optional<int>( 15000 ) );
504 via.SetSecondaryDrillStartLayer( F_Cu );
505 via.SetSecondaryDrillEndLayer( In2_Cu );
506
507 via.SetBackPostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ) );
508 via.SetTertiaryDrillSize( std::optional<int>( 8000 ) );
509 via.SetTertiaryDrillStartLayer( B_Cu );
510 via.SetTertiaryDrillEndLayer( In4_Cu );
511
512 PCB_VIA viaCopy( via );
513 std::unique_ptr<PCB_VIA> viaClone( static_cast<PCB_VIA*>( via.Clone() ) );
514
515 auto checkVia = [&]( const PCB_VIA& candidate )
516 {
517 BOOST_CHECK_EQUAL( candidate.GetPrimaryDrillStartLayer(), via.GetPrimaryDrillStartLayer() );
518 BOOST_CHECK_EQUAL( candidate.GetPrimaryDrillEndLayer(), via.GetPrimaryDrillEndLayer() );
519 BOOST_CHECK( candidate.GetFrontPostMachining().has_value() );
520 BOOST_CHECK_EQUAL( static_cast<int>( candidate.GetFrontPostMachining().value() ),
521 static_cast<int>( via.GetFrontPostMachining().value() ) );
522 BOOST_CHECK( candidate.GetSecondaryDrillSize().has_value() );
523 BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillSize().value(),
524 via.GetSecondaryDrillSize().value() );
525 BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillStartLayer(),
526 via.GetSecondaryDrillStartLayer() );
527 BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillEndLayer(),
528 via.GetSecondaryDrillEndLayer() );
529
530 BOOST_CHECK( candidate.GetBackPostMachining().has_value() );
531 BOOST_CHECK_EQUAL( static_cast<int>( candidate.GetBackPostMachining().value() ),
532 static_cast<int>( via.GetBackPostMachining().value() ) );
533 BOOST_CHECK( candidate.GetTertiaryDrillSize().has_value() );
534 BOOST_CHECK_EQUAL( candidate.GetTertiaryDrillSize().value(),
535 via.GetTertiaryDrillSize().value() );
536 BOOST_CHECK_EQUAL( candidate.GetTertiaryDrillStartLayer(),
537 via.GetTertiaryDrillStartLayer() );
538 BOOST_CHECK_EQUAL( candidate.GetTertiaryDrillEndLayer(),
539 via.GetTertiaryDrillEndLayer() );
540 };
541
542 checkVia( viaCopy );
543 checkVia( *viaClone );
544}
545
546
555BOOST_AUTO_TEST_CASE( PNSLayerRangeSwapBehavior )
556{
557 // On a 2-layer board with FRONT_INNER_BACK mode, BoardCopperLayerCount() returns 2.
558 // The code would calculate PNS_LAYER_RANGE(1, 2 - 2) = PNS_LAYER_RANGE(1, 0)
559 // Since start > end, the constructor swaps them to (0, 1), which would span
560 // both F_Cu and B_Cu incorrectly.
561
562 PNS_LAYER_RANGE innerLayersRange2Layer( 1, 0 ); // What would happen on 2-layer board
563
564 // Verify the swap behavior that causes the bug
565 BOOST_CHECK_EQUAL( innerLayersRange2Layer.Start(), 0 );
566 BOOST_CHECK_EQUAL( innerLayersRange2Layer.End(), 1 );
567 BOOST_CHECK( innerLayersRange2Layer.Overlaps( 0 ) ); // F_Cu
568 BOOST_CHECK( innerLayersRange2Layer.Overlaps( 1 ) ); // B_Cu
569
570 // On a 4-layer board, inner layers are 1 and 2, so PNS_LAYER_RANGE(1, 4-2) = (1, 2)
571 PNS_LAYER_RANGE innerLayersRange4Layer( 1, 2 ); // Correct for 4-layer board
572
573 BOOST_CHECK_EQUAL( innerLayersRange4Layer.Start(), 1 );
574 BOOST_CHECK_EQUAL( innerLayersRange4Layer.End(), 2 );
575 BOOST_CHECK( !innerLayersRange4Layer.Overlaps( 0 ) ); // F_Cu - should not overlap
576 BOOST_CHECK( innerLayersRange4Layer.Overlaps( 1 ) ); // In1_Cu
577 BOOST_CHECK( innerLayersRange4Layer.Overlaps( 2 ) ); // In2_Cu
578 BOOST_CHECK( !innerLayersRange4Layer.Overlaps( 3 ) ); // B_Cu - should not overlap
579}
580
581
589BOOST_FIXTURE_TEST_CASE( PNSSegmentSplitPreservesLockedState, PNS_TEST_FIXTURE )
590{
591 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
592 world->SetMaxClearance( 10000000 );
593 world->SetRuleResolver( &m_ruleResolver );
594
596
597 VECTOR2I segStart( 0, 0 );
598 VECTOR2I segEnd( 10000000, 0 );
599 VECTOR2I splitPt( 5000000, 0 );
600
601 PNS::SEGMENT* lockedSeg = new PNS::SEGMENT( SEG( segStart, segEnd ), net );
602 lockedSeg->SetWidth( 250000 );
603 lockedSeg->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
604 lockedSeg->Mark( PNS::MK_LOCKED );
605
606 BOOST_CHECK( lockedSeg->IsLocked() );
607
608 world->AddRaw( lockedSeg );
609
610 // Clone the locked segment and set up two halves (simulating SplitAdjacentSegments)
611 std::unique_ptr<PNS::SEGMENT> clone1( PNS::Clone( *lockedSeg ) );
612 std::unique_ptr<PNS::SEGMENT> clone2( PNS::Clone( *lockedSeg ) );
613
614 clone1->SetEnds( segStart, splitPt );
615 clone2->SetEnds( splitPt, segEnd );
616
617 BOOST_CHECK_MESSAGE( clone1->IsLocked(),
618 "First half of split locked segment must retain locked state" );
619 BOOST_CHECK_MESSAGE( clone2->IsLocked(),
620 "Second half of split locked segment must retain locked state" );
621 BOOST_CHECK_EQUAL( clone1->Width(), lockedSeg->Width() );
622 BOOST_CHECK_EQUAL( clone2->Width(), lockedSeg->Width() );
623}
624
625
633BOOST_FIXTURE_TEST_CASE( PNSInheritTrackWidthCursorProximity, PNS_TEST_FIXTURE )
634{
635 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
636 world->SetMaxClearance( 10000000 );
637 world->SetRuleResolver( &m_ruleResolver );
638
639 VECTOR2I padPos( 0, 0 );
641
642 // Pad at origin with a small circular shape
644 pad->SetShape( new SHAPE_CIRCLE( padPos, 500000 ) );
645 pad->SetPos( padPos );
646 pad->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
647 pad->SetNet( net );
648 world->AddRaw( pad );
649
650 // Narrow track going right (250um width)
651 int narrowWidth = 250000;
652 PNS::SEGMENT* narrowSeg = new PNS::SEGMENT( SEG( padPos, VECTOR2I( 5000000, 0 ) ), net );
653 narrowSeg->SetWidth( narrowWidth );
654 narrowSeg->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
655 world->AddRaw( narrowSeg );
656
657 // Wide track going up (500um width)
658 int wideWidth = 500000;
659 PNS::SEGMENT* wideSeg = new PNS::SEGMENT( SEG( padPos, VECTOR2I( 0, -5000000 ) ), net );
660 wideSeg->SetWidth( wideWidth );
661 wideSeg->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
662 world->AddRaw( wideSeg );
663
664 int inherited = 0;
665
666 // Without cursor position, should fall back to minimum width
667 BOOST_CHECK( m_iface->TestInheritTrackWidth( pad, &inherited ) );
668 BOOST_CHECK_EQUAL( inherited, narrowWidth );
669
670 // Cursor near the narrow track (to the right) should select narrow width
671 inherited = 0;
672 BOOST_CHECK( m_iface->TestInheritTrackWidth( pad, &inherited, VECTOR2I( 2000000, 0 ) ) );
673 BOOST_CHECK_EQUAL( inherited, narrowWidth );
674
675 // Cursor near the wide track (upward) should select wide width
676 inherited = 0;
677 BOOST_CHECK( m_iface->TestInheritTrackWidth( pad, &inherited, VECTOR2I( 0, -2000000 ) ) );
678 BOOST_CHECK_EQUAL( inherited, wideWidth );
679
680 // Cursor slightly offset toward narrow track should still select narrow
681 inherited = 0;
682 BOOST_CHECK( m_iface->TestInheritTrackWidth( pad, &inherited, VECTOR2I( 100000, 50000 ) ) );
683 BOOST_CHECK_EQUAL( inherited, narrowWidth );
684
685 // Cursor slightly offset toward wide track should select wide
686 inherited = 0;
687 BOOST_CHECK( m_iface->TestInheritTrackWidth( pad, &inherited, VECTOR2I( 50000, -100000 ) ) );
688 BOOST_CHECK_EQUAL( inherited, wideWidth );
689}
690
691
699BOOST_AUTO_TEST_CASE( PCBExprGeometryDependentFunctionDetection )
700{
701 PCBEXPR_COMPILER compiler( new PCBEXPR_UNIT_RESOLVER() );
702
703 auto compileAndCheck = [&]( const wxString& aExpr, bool aExpectGeometry )
704 {
705 PCBEXPR_UCODE ucode;
706 PCBEXPR_CONTEXT ctx( 0, F_Cu );
707
708 bool ok = compiler.Compile( aExpr.ToUTF8().data(), &ucode, &ctx );
709 BOOST_CHECK_MESSAGE( ok, "Failed to compile: " + aExpr );
710
711 if( ok )
712 {
713 BOOST_CHECK_MESSAGE( ucode.HasGeometryDependentFunctions() == aExpectGeometry,
714 wxString::Format( "Expression '%s': expected geometry=%s, got %s",
715 aExpr,
716 aExpectGeometry ? "true" : "false",
718 ? "true" : "false" ) );
719 }
720 };
721
722 // Property-based conditions should NOT be geometry-dependent
723 compileAndCheck( wxT( "A.NetClass == 'Power'" ), false );
724 compileAndCheck( wxT( "A.Type == 'via'" ), false );
725 compileAndCheck( wxT( "A.NetName == '/VCC'" ), false );
726
727 // Geometry-dependent functions SHOULD be detected
728 compileAndCheck( wxT( "A.intersectsCourtyard('U1')" ), true );
729 compileAndCheck( wxT( "A.intersectsArea('Zone1')" ), true );
730 compileAndCheck( wxT( "A.enclosedByArea('Zone1')" ), true );
731 compileAndCheck( wxT( "A.intersectsFrontCourtyard('U1')" ), true );
732 compileAndCheck( wxT( "A.intersectsBackCourtyard('U1')" ), true );
733
734 // Deprecated aliases should also be detected
735 compileAndCheck( wxT( "A.insideCourtyard('U1')" ), true );
736 compileAndCheck( wxT( "A.insideArea('Zone1')" ), true );
737}
738
739
748BOOST_FIXTURE_TEST_CASE( PNSCollideSimpleNullShapeGuard, PNS_TEST_FIXTURE )
749{
750 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
751 world->SetMaxClearance( 10000000 );
752 world->SetRuleResolver( &m_ruleResolver );
753
756
757 PNS::SOLID* solid1 = new PNS::SOLID;
758 solid1->SetShape( new SHAPE_CIRCLE( VECTOR2I( 0, 0 ), 500000 ) );
759 solid1->SetPos( VECTOR2I( 0, 0 ) );
760 solid1->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
761 solid1->SetNet( net1 );
762
763 PNS::SOLID* solid2 = new PNS::SOLID;
764 solid2->SetShape( new SHAPE_CIRCLE( VECTOR2I( 100000, 0 ), 500000 ) );
765 solid2->SetPos( VECTOR2I( 100000, 0 ) );
766 solid2->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
767 solid2->SetNet( net2 );
768
769 world->AddRaw( solid1 );
770 world->AddRaw( solid2 );
771
772 // Verify collision works normally with valid shapes
773 PNS::NODE::OBSTACLES obstacles;
774 int count = world->QueryColliding( solid1, obstacles );
775 BOOST_CHECK( count > 0 );
776
777 // Exercise the null-shape guard in collideSimple by calling Collide() directly with a
778 // null-shape solid as the head item. This bypasses the spatial index (which requires a
779 // valid shape for bounding-box computation) and hits the exact code path the guard protects.
780 PNS::SOLID nullShapeSolid;
781 nullShapeSolid.SetPos( VECTOR2I( 0, 0 ) );
782 nullShapeSolid.SetLayers( PNS_LAYER_RANGE( F_Cu ) );
783 nullShapeSolid.SetNet( net2 );
784
785 bool collided = solid1->Collide( &nullShapeSolid, world.get(), F_Cu, nullptr );
786 BOOST_CHECK( !collided );
787}
788
789
798BOOST_FIXTURE_TEST_CASE( PNSComponentDraggerBasicDrag, PNS_TEST_FIXTURE )
799{
800 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
801 world->SetMaxClearance( 10000000 );
802 world->SetRuleResolver( &m_ruleResolver );
803
805
806 VECTOR2I pad1Pos( 0, 0 );
807
808 PNS::SOLID* pad1 = new PNS::SOLID;
809 pad1->SetShape( new SHAPE_CIRCLE( pad1Pos, 500000 ) );
810 pad1->SetPos( pad1Pos );
811 pad1->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
812 pad1->SetNet( net1 );
813 pad1->SetRoutable( true );
814
815 VECTOR2I traceEnd( 2500000, 2500000 );
816 PNS::SEGMENT* trace = new PNS::SEGMENT( SEG( pad1Pos, traceEnd ), net1 );
817 trace->SetWidth( 250000 );
818 trace->SetLayers( PNS_LAYER_RANGE( F_Cu ) );
819
820 world->AddRaw( pad1 );
821 world->AddRaw( trace );
822
823 PNS::COMPONENT_DRAGGER dragger( m_router );
824 dragger.SetWorld( world.get() );
825
826 PNS::ITEM_SET itemsToDrag;
827 itemsToDrag.Add( pad1 );
828
829 bool started = dragger.Start( pad1Pos, itemsToDrag );
830 BOOST_REQUIRE( started );
831
832 // Simulate multiple drag events (mimics mouse movement during drag)
833 VECTOR2I dragPositions[] = {
834 VECTOR2I( 100000, 100000 ),
835 VECTOR2I( 500000, 500000 ),
836 VECTOR2I( 1000000, 1000000 ),
837 VECTOR2I( 500000, 200000 ),
838 VECTOR2I( 0, 0 )
839 };
840
841 for( const VECTOR2I& pos : dragPositions )
842 {
843 bool dragOk = dragger.Drag( pos );
844 BOOST_CHECK( dragOk );
845
846 PNS::NODE* currentNode = dragger.CurrentNode();
847 BOOST_CHECK( currentNode != nullptr );
848 }
849
850 // Verify the dragged items set is populated
851 PNS::ITEM_SET traces = dragger.Traces();
852 BOOST_CHECK( traces.Size() > 0 );
853
854 // Clean up branch nodes before the world is destroyed
855 world->KillChildren();
856}
857
858
859// Regression tests for issues #18658 and #24132. Physical clearance rules must be
860// enforced for same-net and free-pad pairs without disturbing the fast path on
861// boards that do not define them.
862
863namespace
864{
865PNS::VIA* makeVia( const VECTOR2I& aPos, PNS::NET_HANDLE aNet )
866{
867 PNS::VIA* v = new PNS::VIA( aPos, PNS_LAYER_RANGE( F_Cu, B_Cu ), 50000, 10000 );
868 v->SetNet( aNet );
869 return v;
870}
871
872PNS::SOLID* makePad( const VECTOR2I& aPos, PNS::NET_HANDLE aNet, bool aFreePad = false )
873{
874 PNS::SOLID* s = new PNS::SOLID;
875 s->SetShape( new SHAPE_CIRCLE( aPos, 250000 ) );
876 s->SetPos( aPos );
878 s->SetNet( aNet );
879 s->SetIsFreePad( aFreePad );
880 return s;
881}
882} // namespace
883
884
885// Cross-net via vs via with no physical rules: ordinary CT_CLEARANCE applies.
886BOOST_FIXTURE_TEST_CASE( PNSCrossNetViaViaElectricalClearanceBaseline, PNS_TEST_FIXTURE )
887{
888 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
889 world->SetMaxClearance( 10000000 );
890 world->SetRuleResolver( &m_ruleResolver );
891
892 PNS::VIA* v1 = makeVia( VECTOR2I( 0, 0 ), (PNS::NET_HANDLE) 1 );
893 PNS::VIA* v2 = makeVia( VECTOR2I( 0, 100000 ), (PNS::NET_HANDLE) 2 );
894 world->AddRaw( v1 );
895 world->AddRaw( v2 );
896
897 m_ruleResolver.m_defaultClearance = 1000000;
898
899 PNS::NODE::OBSTACLES obstacles;
900 world->QueryColliding( v1, obstacles );
901
902 BOOST_CHECK_GE( obstacles.size(), (size_t) 1 );
903}
904
905
906// Same-net via vs via with no physical rules: fast path keeps clearance = -1.
907BOOST_FIXTURE_TEST_CASE( PNSSameNetNoPhysicalRulesFastPathNoCollision, PNS_TEST_FIXTURE )
908{
909 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
910 world->SetMaxClearance( 10000000 );
911 world->SetRuleResolver( &m_ruleResolver );
912
914 world->AddRaw( makeVia( VECTOR2I( 0, 0 ), net ) );
915 PNS::VIA* v1 = makeVia( VECTOR2I( 0, 100000 ), net );
916 world->AddRaw( v1 );
917
918 m_ruleResolver.m_hasUserPhysicalRules = false;
919 m_ruleResolver.m_defaultClearance = 1000000;
920
921 PNS::NODE::OBSTACLES obstacles;
922 world->QueryColliding( v1, obstacles );
923
924 BOOST_CHECK_EQUAL( obstacles.size(), (size_t) 0 );
925}
926
927
928// Same-net via vs via with a matching physical_clearance rule: collision reported.
929// Regression scenario for issues #18658 and #24132.
930BOOST_FIXTURE_TEST_CASE( PNSSameNetWithPhysicalRuleCollides, PNS_TEST_FIXTURE )
931{
932 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
933 world->SetMaxClearance( 10000000 );
934 world->SetRuleResolver( &m_ruleResolver );
935
937 world->AddRaw( makeVia( VECTOR2I( 0, 0 ), net ) );
938 PNS::VIA* v1 = makeVia( VECTOR2I( 0, 100000 ), net );
939 world->AddRaw( v1 );
940
941 m_ruleResolver.m_hasUserPhysicalRules = true;
942 m_ruleResolver.m_defaultPhysicalClearance = 1000000;
943
944 PNS::NODE::OBSTACLES obstacles;
945 world->QueryColliding( v1, obstacles );
946
947 BOOST_CHECK_GE( obstacles.size(), (size_t) 1 );
948 if( !obstacles.empty() )
949 BOOST_CHECK_EQUAL( obstacles.begin()->m_clearance, 1000000 );
950}
951
952
953// Free pad vs cross-net pair, no physical rules: fast path applies.
954BOOST_FIXTURE_TEST_CASE( PNSFreePadNoPhysicalRulesFastPath, PNS_TEST_FIXTURE )
955{
956 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
957 world->SetMaxClearance( 10000000 );
958 world->SetRuleResolver( &m_ruleResolver );
959
960 PNS::SOLID* freePad = makePad( VECTOR2I( 0, 0 ), (PNS::NET_HANDLE) 1, /*aFreePad=*/true );
961 PNS::VIA* v = makeVia( VECTOR2I( 0, 100000 ), (PNS::NET_HANDLE) 2 );
962 world->AddRaw( freePad );
963 world->AddRaw( v );
964
965 m_ruleResolver.m_hasUserPhysicalRules = false;
966 m_ruleResolver.m_defaultClearance = 1000000;
967
968 PNS::NODE::OBSTACLES obstacles;
969 world->QueryColliding( v, obstacles );
970
971 BOOST_CHECK_EQUAL( obstacles.size(), (size_t) 0 );
972}
973
974
975// Free pad vs cross-net pair, physical rules present but no match: safety net must
976// keep free pads from reporting collisions just because the board has a physical
977// rule somewhere.
979{
980 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
981 world->SetMaxClearance( 10000000 );
982 world->SetRuleResolver( &m_ruleResolver );
983
984 PNS::SOLID* freePad = makePad( VECTOR2I( 0, 0 ), (PNS::NET_HANDLE) 1, /*aFreePad=*/true );
985 PNS::VIA* v = makeVia( VECTOR2I( 0, 100000 ), (PNS::NET_HANDLE) 2 );
986 world->AddRaw( freePad );
987 world->AddRaw( v );
988
989 m_ruleResolver.m_hasUserPhysicalRules = true;
990 m_ruleResolver.m_defaultPhysicalClearance = 0; // rule does not match
991 m_ruleResolver.m_defaultClearance = 1000000; // would collide if !sameNet block runs
992
993 PNS::NODE::OBSTACLES obstacles;
994 world->QueryColliding( v, obstacles );
995
996 BOOST_CHECK_EQUAL( obstacles.size(), (size_t) 0 );
997}
998
999
1000// Free pad with a matching physical_clearance rule: collision reported.
1001BOOST_FIXTURE_TEST_CASE( PNSFreePadPhysicalRuleEnforced, PNS_TEST_FIXTURE )
1002{
1003 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
1004 world->SetMaxClearance( 10000000 );
1005 world->SetRuleResolver( &m_ruleResolver );
1006
1007 PNS::SOLID* freePad = makePad( VECTOR2I( 0, 0 ), (PNS::NET_HANDLE) 1, /*aFreePad=*/true );
1008 PNS::VIA* v = makeVia( VECTOR2I( 0, 100000 ), (PNS::NET_HANDLE) 2 );
1009 world->AddRaw( freePad );
1010 world->AddRaw( v );
1011
1012 m_ruleResolver.m_hasUserPhysicalRules = true;
1013 m_ruleResolver.m_defaultPhysicalClearance = 1000000;
1014
1015 PNS::NODE::OBSTACLES obstacles;
1016 world->QueryColliding( v, obstacles );
1017
1018 BOOST_CHECK_GE( obstacles.size(), (size_t) 1 );
1019}
1020
1021
1022// Same-net via vs pad with a matching physical_hole_clearance rule. Covers the
1023// drill-into-pad half of issue #24132 and exercises CT_PHYSICAL_HOLE_CLEARANCE.
1024BOOST_FIXTURE_TEST_CASE( PNSSameNetPhysicalHoleClearance, PNS_TEST_FIXTURE )
1025{
1026 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
1027 world->SetMaxClearance( 10000000 );
1028 world->SetRuleResolver( &m_ruleResolver );
1029
1031 PNS::SOLID* pad = makePad( VECTOR2I( 0, 0 ), net );
1032 PNS::VIA* via = makeVia( VECTOR2I( 0, 100000 ), net );
1033 world->AddRaw( pad );
1034 world->AddRaw( via );
1035
1036 m_ruleResolver.m_hasUserPhysicalRules = true;
1037 m_ruleResolver.m_defaultPhysicalHoleClearance = 1000000;
1038
1039 PNS::NODE::OBSTACLES obstacles;
1040 world->QueryColliding( via, obstacles );
1041
1042 BOOST_CHECK_GE( obstacles.size(), (size_t) 1 );
1043}
1044
1045
1046BOOST_FIXTURE_TEST_CASE( PNSSameNetSafetyNetOnOverlap, PNS_TEST_FIXTURE )
1047{
1048 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
1049 world->SetMaxClearance( 10000000 );
1050 world->SetRuleResolver( &m_ruleResolver );
1051
1053 world->AddRaw( makeVia( VECTOR2I( 0, 0 ), net ) );
1054 PNS::VIA* v1 = makeVia( VECTOR2I( 0, 30000 ), net ); // overlaps the first via
1055 world->AddRaw( v1 );
1056
1057 m_ruleResolver.m_hasUserPhysicalRules = true;
1058 m_ruleResolver.m_defaultPhysicalClearance = 0;
1059
1060 PNS::NODE::OBSTACLES obstacles;
1061 world->QueryColliding( v1, obstacles );
1062
1063 BOOST_CHECK_EQUAL( obstacles.size(), (size_t) 0 );
1064}
1065
1066
1067// Same-net pad+via with both physical_clearance and physical_hole_clearance
1068// matching: the resolver returns max across the two query points.
1069BOOST_FIXTURE_TEST_CASE( PNSBothPhysicalConstraintsMaxWins, PNS_TEST_FIXTURE )
1070{
1071 std::unique_ptr<PNS::NODE> world( new PNS::NODE );
1072 world->SetMaxClearance( 10000000 );
1073 world->SetRuleResolver( &m_ruleResolver );
1074
1076 PNS::SOLID* pad = makePad( VECTOR2I( 0, 0 ), net );
1077 PNS::VIA* via = makeVia( VECTOR2I( 0, 100000 ), net );
1078 world->AddRaw( pad );
1079 world->AddRaw( via );
1080
1081 m_ruleResolver.m_hasUserPhysicalRules = true;
1082 m_ruleResolver.m_defaultPhysicalClearance = 100000;
1083 m_ruleResolver.m_defaultPhysicalHoleClearance = 2000000;
1084
1085 PNS::NODE::OBSTACLES obstacles;
1086 world->QueryColliding( via, obstacles );
1087
1088 BOOST_CHECK_GE( obstacles.size(), (size_t) 1 );
1089
1090 // The recursive collideSimple call for the via's hole inserts a separate OBSTACLE
1091 // with the max-accumulated clearance, so look across all entries rather than
1092 // relying on std::set ordering (which is by pointer).
1093 int maxClearance = 0;
1094 for( const PNS::OBSTACLE& obs : obstacles )
1095 maxClearance = std::max( maxClearance, obs.m_clearance );
1096 BOOST_CHECK_EQUAL( maxClearance, 2000000 );
1097}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:350
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
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
bool TestInheritTrackWidth(PNS::ITEM *aItem, int *aInheritedWidth, const VECTOR2I &aStartPosition=VECTOR2I())
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
bool HasUserDefinedPhysicalConstraint() 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
Definition pad.h:65
bool HasGeometryDependentFunctions() const
const ITEM_SET Traces() override
Function Traces()
NODE * CurrentNode() const override
Function CurrentNode()
bool Start(const VECTOR2I &aP, ITEM_SET &aPrimitives) override
Function Start()
bool Drag(const VECTOR2I &aP) override
Function Drag()
virtual void SetWorld(NODE *aWorld)
Function SetWorld()
int Size() const
void Add(const LINE &aLine)
Base class for PNS router board items.
Definition pns_item.h:98
BOARD_ITEM * Parent() const
Definition pns_item.h:199
bool IsFreePad() const
Definition pns_item.h:288
void SetLayers(const PNS_LAYER_RANGE &aLayers)
Definition pns_item.h:213
void SetIsFreePad(bool aIsFreePad=true)
Definition pns_item.h:286
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
virtual NET_HANDLE Net() const
Definition pns_item.h:210
void SetNet(NET_HANDLE aNet)
Definition pns_item.h:209
virtual int Layer() const
Definition pns_item.h:216
bool Collide(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx=nullptr) const
Check for a collision (clearance violation) with between us and item aOther.
Definition pns_item.cpp:305
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual void Mark(int aMarker) const
Definition pns_item.h:261
virtual BOARD_ITEM * BoardItem() const
Definition pns_item.h:207
void SetRoutable(bool aRoutable)
Definition pns_item.h:283
bool IsLocked() const
Definition pns_item.h:278
Keep the router "world" - i.e.
Definition pns_node.h:242
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:254
int Width() const override
Definition pns_segment.h:96
void SetWidth(int aWidth) override
Definition pns_segment.h:91
void SetPos(const VECTOR2I &aCenter)
Definition pns_solid.cpp:81
void SetShape(SHAPE *shape)
Definition pns_solid.h:113
bool inheritTrackWidth(PNS::ITEM *aItem, int *aInheritedWidth, const VECTOR2I &aStartPosition)
Represent a contiguous set of PCB layers.
int Start() const
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
int End() const
PNS_LAYER_RANGE Intersection(const PNS_LAYER_RANGE &aOther) const
Shortcut for comparisons/overlap tests.
Definition seg.h:42
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
@ MK_LOCKED
Definition pns_item.h:45
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition pns_item.h:344
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:74
MINOPTMAX< int > m_Value
Definition pns_node.h:76
CONSTRAINT_TYPE m_Type
Definition pns_node.h:75
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:89
SETTINGS_MANAGER m_settingsManager
MOCK_RULE_RESOLVER m_ruleResolver
PNS::ROUTER * m_router
MOCK_PNS_KICAD_IFACE * m_iface
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
VECTOR3I v1(5, 5, 5)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
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_CHECK_EQUAL(result, "25.4")
VECTOR2I v2(1, 0)
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687