KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_zone.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, see <https://www.gnu.org/licenses/>.
18 */
19
22
23#include <board.h>
24#include <collectors.h>
25#include <footprint.h>
26#include <geometry/shape_arc.h>
30#include <netinfo.h>
31#include <pad.h>
32#include <padstack.h>
33#include <pcb_painter.h>
34#include <pcb_track.h>
35#include <zone.h>
36#include <zone_utils.h>
37
38
43
44
45static std::unique_ptr<ZONE> CreateSquareZone( BOARD_ITEM_CONTAINER& aParent, BOX2I aBox, PCB_LAYER_ID aLayer )
46{
47 auto zone = std::make_unique<ZONE>( &aParent );
48 zone->SetLayer( aLayer );
49
50 auto outline = std::make_unique<SHAPE_POLY_SET>();
51 outline->AddOutline( KIGEOM::BoxToLineChain( aBox ) );
52
53 zone->SetOutline( outline.release() );
54
55 return zone;
56}
57
58
62static std::unique_ptr<ZONE> CreateSimilarZone( BOARD_ITEM_CONTAINER& aParent, const ZONE& aOther, PCB_LAYER_ID aLayer )
63{
64 auto zone = std::make_unique<ZONE>( &aParent );
65 zone->SetLayer( aLayer );
66
67 std::unique_ptr<SHAPE_POLY_SET> outline = std::make_unique<SHAPE_POLY_SET>( *aOther.Outline() );
68 zone->SetOutline( outline.release() );
69
70 return zone;
71}
72
73
74BOOST_FIXTURE_TEST_SUITE( Zone, ZONE_TEST_FIXTURE )
75
77{
78 ZONE zone( &m_board );
79
80 zone.SetLayer( F_Cu );
81
82 BOOST_TEST( zone.GetLayer() == F_Cu );
83 BOOST_TEST( zone.GetLayer() == zone.GetFirstLayer() );
84
85 BOOST_TEST( zone.IsOnCopperLayer() == true );
86}
87
88BOOST_AUTO_TEST_CASE( MultipleLayers )
89{
90 ZONE zone( &m_board );
91
92 zone.SetLayerSet( { F_Cu, B_Cu } );
93
94 // There is no "the" layer in a multi-layer zone
96 // ... but there is a first layer
97 BOOST_TEST( zone.GetFirstLayer() == F_Cu );
98
99 BOOST_TEST( zone.IsOnCopperLayer() == true );
100}
101
108BOOST_AUTO_TEST_CASE( RescuedLayers )
109{
110 ZONE zone( &m_board );
111
112 zone.SetLayer( Rescue );
113
114 BOOST_TEST( zone.GetLayer() == Rescue );
115 BOOST_TEST( zone.GetLayer() == zone.GetFirstLayer() );
116
117 BOOST_TEST( zone.IsOnCopperLayer() == false );
118}
119
127BOOST_AUTO_TEST_CASE( RuleAreaInnerLayersExpandMode )
128{
129 FOOTPRINT footprint( &m_board );
131
132 ZONE* ruleArea = new ZONE( &footprint );
133 ruleArea->SetIsRuleArea( true );
134 ruleArea->SetLayerSet( LSET::InternalCuMask() );
135 footprint.Add( ruleArea );
136
137 // Collect all layers used by the footprint (mirrors GetAllUsedFootprintLayers
138 // from dialog_footprint_properties_fp_editor.cpp)
139 LSET usedLayers;
140
141 footprint.RunOnChildren(
142 [&]( BOARD_ITEM* aItem )
143 {
144 if( aItem->Type() == PCB_ZONE_T )
145 usedLayers |= static_cast<ZONE*>( aItem )->GetLayerSet();
146 else
147 usedLayers.set( aItem->GetLayer() );
148 },
150
151 // In EXPAND_INNER_LAYERS mode, F_Cu, B_Cu and all inner copper layers
152 // are valid, along with tech, user, and user-defined layers.
153 LSET allowedLayers = LSET{ F_Cu, B_Cu } | LSET::InternalCuMask();
154 allowedLayers |= LSET::UserDefinedLayersMask( 4 );
155
156 usedLayers &= ~allowedLayers;
157 usedLayers &= ~LSET::AllTechMask();
158 usedLayers &= ~LSET::UserMask();
159
160 BOOST_TEST( usedLayers.none() );
161}
162
173BOOST_AUTO_TEST_CASE( CircleZoneCutoutRoundTrip )
174{
175 // Build a circular zone outline the same way convert_tool.cpp does for CIRCLE shapes
176 VECTOR2I center( 0, 0 );
177 int radius = pcbIUScale.mmToIU( 10.0 );
178 SHAPE_ARC fullCircle( center - VECTOR2I( radius, 0 ), center + VECTOR2I( radius, 0 ),
179 center - VECTOR2I( radius, 0 ), 0 );
180
181 SHAPE_POLY_SET sourceOutline;
182 sourceOutline.NewOutline();
183 sourceOutline.Append( fullCircle );
184
185 BOOST_TEST_REQUIRE( sourceOutline.ArcCount() > 0 );
186 double sourceArea = sourceOutline.Area();
187 BOOST_TEST_REQUIRE( sourceArea > 0.0 );
188
189 // Draw a small rectangular cutout well inside the circle
190 SHAPE_POLY_SET cutoutOutline;
191 cutoutOutline.NewOutline();
192 int cutoutHalf = pcbIUScale.mmToIU( 1.0 );
193 cutoutOutline.Append( VECTOR2I( -cutoutHalf, -cutoutHalf ) );
194 cutoutOutline.Append( VECTOR2I( cutoutHalf, -cutoutHalf ) );
195 cutoutOutline.Append( VECTOR2I( cutoutHalf, cutoutHalf ) );
196 cutoutOutline.Append( VECTOR2I( -cutoutHalf, cutoutHalf ) );
197
198 // Mirror the fix applied in ZONE_CREATE_HELPER::performZoneCutout: drop arcs before
199 // the boolean so the saved outline cannot reference stale arc endpoints.
200 SHAPE_POLY_SET working( sourceOutline );
201 working.ClearArcs();
202
203 SHAPE_POLY_SET cutout( cutoutOutline );
204 cutout.ClearArcs();
205
206 working.BooleanSubtract( cutout );
207
208 BOOST_TEST( working.ArcCount() == 0 );
209 BOOST_TEST_REQUIRE( working.OutlineCount() == 1 );
210 BOOST_TEST_REQUIRE( working.HoleCount( 0 ) == 1 );
211
212 double expectedArea = sourceArea - cutoutOutline.Area();
213 BOOST_CHECK_CLOSE( working.Area(), expectedArea, 0.1 );
214
215 // Outline must be a simple closed curve with reasonable point count
216 const SHAPE_LINE_CHAIN& outerChain = working.COutline( 0 );
217 BOOST_TEST( outerChain.IsClosed() );
218 BOOST_TEST( outerChain.PointCount() >= 8 );
219 BOOST_TEST( outerChain.SelfIntersecting().has_value() == false );
220
221 const SHAPE_LINE_CHAIN& holeChain = working.CHole( 0, 0 );
222 BOOST_TEST( holeChain.IsClosed() );
223 BOOST_TEST( holeChain.PointCount() == 4 );
224}
225
232BOOST_AUTO_TEST_CASE( EmptyZoneGetPosition )
233{
234 ZONE zone( &m_board );
235 zone.SetLayer( F_Cu );
236
237 BOOST_TEST( zone.GetNumCorners() == 0 );
238 BOOST_CHECK_NO_THROW( zone.GetPosition() );
239 BOOST_TEST( zone.GetPosition() == VECTOR2I( 0, 0 ) );
240}
241
242
243BOOST_AUTO_TEST_CASE( ZoneMergeNull )
244{
245 std::vector<std::unique_ptr<ZONE>> zones;
246
247 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
248 zones.back()->SetLayer( F_Cu );
249
250 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
251 zones.back()->SetLayer( F_Cu );
252
253 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
254
255 // They are the same, so they do merge
256 BOOST_TEST( merged.size() == 1 );
257}
258
259
260BOOST_AUTO_TEST_CASE( ZoneMergeNonNullNoMerge )
261{
262 std::vector<std::unique_ptr<ZONE>> zones;
263
264 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ), F_Cu ) );
265 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 200, 200 ), VECTOR2I( 300, 300 ) ), B_Cu ) );
266
267 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
268
269 // They are different, so they don't merge
270 BOOST_TEST( merged.size() == 2 );
271}
272
273
274BOOST_AUTO_TEST_CASE( ZoneMergeNonNullMerge )
275{
276 std::vector<std::unique_ptr<ZONE>> zones;
277
278 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ), F_Cu ) );
279 zones.emplace_back( CreateSimilarZone( m_board, *zones.back(), B_Cu ) );
280
281 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
282
283 // They are the same, so they do merge
284 BOOST_REQUIRE( merged.size() == 1 );
285
286 BOOST_TEST( merged[0]->GetLayerSet() == ( LSET{ F_Cu, B_Cu } ) );
287 BOOST_TEST( merged[0]->GetNumCorners() == 4 );
288}
289
290
291BOOST_AUTO_TEST_CASE( ZoneMergeMergeSameGeomDifferentOrder )
292{
293 std::vector<std::unique_ptr<ZONE>> zones;
294
295 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ), F_Cu ) );
296 zones.emplace_back( CreateSimilarZone( m_board, *zones.back(), B_Cu ) );
297
298 // Reverse the outline of one of them
299 // Don't go overboard here - detailed tests of CompareGeometry
300 // should be in the SHAPE_LINE_CHAIN tests.
301 auto newPolyB = std::make_unique<SHAPE_POLY_SET>( *zones.back()->Outline() );
302 newPolyB->Outline( 0 ).Reverse();
303 zones.back()->SetOutline( newPolyB.release() );
304
305 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
306
307 // They are the same, so they do merge
308 BOOST_REQUIRE( merged.size() == 1 );
309
310 BOOST_TEST( merged[0]->GetLayerSet() == LSET( { F_Cu, B_Cu } ) );
311 BOOST_TEST( merged[0]->GetNumCorners() == 4 );
312}
313
314static PCB_VIA* AddVia( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode,
315 PCB_LAYER_ID aTopLayer = F_Cu, PCB_LAYER_ID aBotLayer = B_Cu )
316{
317 PCB_VIA* via = new PCB_VIA( &aBoard );
318 via->SetPosition( aPos );
319 via->SetLayerPair( aTopLayer, aBotLayer );
320 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
321 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
322 via->SetNetCode( aNetCode );
323 aBoard.Add( via );
324 return via;
325}
326
327
328static PAD* AddPadToBoard( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode,
329 PCB_LAYER_ID aLayer = F_Cu )
330{
331 FOOTPRINT* fp = new FOOTPRINT( &aBoard );
332 fp->SetPosition( aPos );
333 aBoard.Add( fp );
334
335 PAD* pad = new PAD( fp );
336 pad->SetPosition( aPos );
337 pad->SetSize( PADSTACK::ALL_LAYERS,
338 VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) ) );
340 pad->SetLayerSet( LSET( { aLayer } ) );
341 pad->SetNetCode( aNetCode );
342 fp->Add( pad );
343 return pad;
344}
345
346
347BOOST_AUTO_TEST_CASE( AutoPriority_NonOverlapping )
348{
349 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
350 m_board.Add( netA );
351 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
352 m_board.Add( netB );
353
354 auto zoneA = CreateSquareZone( m_board,
355 BOX2I( VECTOR2I( 0, 0 ),
356 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
357 F_Cu );
358 zoneA->SetNetCode( netA->GetNetCode() );
359 zoneA->SetAssignedPriority( 5 );
360
361 auto zoneB = CreateSquareZone( m_board,
362 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 20 ), 0 ),
363 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
364 F_Cu );
365 zoneB->SetNetCode( netB->GetNetCode() );
366 zoneB->SetAssignedPriority( 10 );
367
368 ZONE* ptrA = zoneA.get();
369 ZONE* ptrB = zoneB.get();
370 m_board.Add( zoneA.release() );
371 m_board.Add( zoneB.release() );
372
373 AutoAssignZonePriorities( &m_board );
374
376}
377
378
379BOOST_AUTO_TEST_CASE( AutoPriority_ItemCountWins )
380{
381 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
382 m_board.Add( netA );
383 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
384 m_board.Add( netB );
385
386 auto zoneA = CreateSquareZone( m_board,
387 BOX2I( VECTOR2I( 0, 0 ),
388 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
389 F_Cu );
390 zoneA->SetNetCode( netA->GetNetCode() );
391
392 auto zoneB = CreateSquareZone( m_board,
393 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
394 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
395 F_Cu );
396 zoneB->SetNetCode( netB->GetNetCode() );
397
398 ZONE* ptrA = zoneA.get();
399 ZONE* ptrB = zoneB.get();
400 m_board.Add( zoneA.release() );
401 m_board.Add( zoneB.release() );
402
403 for( int i = 0; i < 5; i++ )
404 {
405 AddVia( m_board,
406 VECTOR2I( pcbIUScale.mmToIU( 7 + i ), pcbIUScale.mmToIU( 10 ) ),
407 netA->GetNetCode() );
408 }
409
410 AddVia( m_board,
411 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 7 ) ),
412 netB->GetNetCode() );
413
414 AutoAssignZonePriorities( &m_board );
415
417}
418
419
420BOOST_AUTO_TEST_CASE( AutoPriority_SimilarCountsSmallerWins )
421{
422 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
423 m_board.Add( netA );
424 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
425 m_board.Add( netB );
426
427 auto zoneA = CreateSquareZone( m_board,
428 BOX2I( VECTOR2I( 0, 0 ),
429 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) ),
430 F_Cu );
431 zoneA->SetNetCode( netA->GetNetCode() );
432
433 auto zoneB = CreateSquareZone( m_board,
434 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
435 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
436 F_Cu );
437 zoneB->SetNetCode( netB->GetNetCode() );
438
439 ZONE* ptrA = zoneA.get();
440 ZONE* ptrB = zoneB.get();
441 m_board.Add( zoneA.release() );
442 m_board.Add( zoneB.release() );
443
444 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ),
445 netA->GetNetCode() );
446 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ),
447 netA->GetNetCode() );
448 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ),
449 netB->GetNetCode() );
450 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ),
451 netB->GetNetCode() );
452
453 AutoAssignZonePriorities( &m_board );
454
456}
457
458
459BOOST_AUTO_TEST_CASE( AutoPriority_MultiLayerAggregate )
460{
461 m_board.SetCopperLayerCount( 2 );
462
463 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
464 m_board.Add( netA );
465 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
466 m_board.Add( netB );
467
468 auto zoneA = CreateSquareZone( m_board,
469 BOX2I( VECTOR2I( 0, 0 ),
470 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
471 F_Cu );
472 zoneA->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
473 zoneA->SetNetCode( netA->GetNetCode() );
474
475 auto zoneB = CreateSquareZone( m_board,
476 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
477 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
478 F_Cu );
479 zoneB->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
480 zoneB->SetNetCode( netB->GetNetCode() );
481
482 ZONE* ptrA = zoneA.get();
483 ZONE* ptrB = zoneB.get();
484 m_board.Add( zoneA.release() );
485 m_board.Add( zoneB.release() );
486
487 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ),
488 netA->GetNetCode(), F_Cu, B_Cu );
489 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ),
490 netA->GetNetCode(), F_Cu, B_Cu );
491
492 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ),
493 netB->GetNetCode(), F_Cu, B_Cu );
494 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ),
495 netB->GetNetCode(), F_Cu, B_Cu );
496 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ),
497 netB->GetNetCode(), F_Cu, B_Cu );
498
499 AutoAssignZonePriorities( &m_board );
500
502}
503
504
505BOOST_AUTO_TEST_CASE( AutoPriority_SameNetEqualPriority )
506{
507 NETINFO_ITEM* net = new NETINFO_ITEM( &m_board, wxT( "SharedNet" ) );
508 m_board.Add( net );
509
510 auto zoneA = CreateSquareZone( m_board,
511 BOX2I( VECTOR2I( 0, 0 ),
512 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) ),
513 F_Cu );
514 zoneA->SetNetCode( net->GetNetCode() );
515
516 auto zoneB = CreateSquareZone( m_board,
517 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
518 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
519 F_Cu );
520 zoneB->SetNetCode( net->GetNetCode() );
521
522 ZONE* ptrA = zoneA.get();
523 ZONE* ptrB = zoneB.get();
524 m_board.Add( zoneA.release() );
525 m_board.Add( zoneB.release() );
526
527 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ),
528 net->GetNetCode() );
529 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ),
530 net->GetNetCode() );
531
532 AutoAssignZonePriorities( &m_board );
533
534 // Same-net overlapping zones are cooperative and must share equal priority
536}
537
538
539BOOST_AUTO_TEST_CASE( AutoPriority_EqualAreaNoChange )
540{
541 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
542 m_board.Add( netA );
543 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
544 m_board.Add( netB );
545
546 // Two identical-sized overlapping zones with no items in the overlap
547 auto zoneA = CreateSquareZone( m_board,
548 BOX2I( VECTOR2I( 0, 0 ),
549 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
550 F_Cu );
551 zoneA->SetNetCode( netA->GetNetCode() );
552 zoneA->SetAssignedPriority( 50 );
553
554 auto zoneB = CreateSquareZone( m_board,
555 BOX2I( VECTOR2I( 0, 0 ),
556 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
557 F_Cu );
558 zoneB->SetNetCode( netB->GetNetCode() );
559 zoneB->SetAssignedPriority( 50 );
560
561 ZONE* ptrA = zoneA.get();
562 ZONE* ptrB = zoneB.get();
563 m_board.Add( zoneA.release() );
564 m_board.Add( zoneB.release() );
565
566 bool changed = AutoAssignZonePriorities( &m_board );
567
568 // Equal areas, no items: no ordering evidence, priorities must not change
569 BOOST_TEST( changed == false );
570 BOOST_TEST( ptrA->GetAssignedPriority() == 50u );
571 BOOST_TEST( ptrB->GetAssignedPriority() == 50u );
572}
573
574
575BOOST_AUTO_TEST_CASE( AutoPriority_SameNetGroupInheritsEdge )
576{
577 NETINFO_ITEM* netGND = new NETINFO_ITEM( &m_board, wxT( "GND" ) );
578 m_board.Add( netGND );
579 NETINFO_ITEM* netVCC = new NETINFO_ITEM( &m_board, wxT( "VCC" ) );
580 m_board.Add( netVCC );
581
582 // Large GND zone (A) overlaps with small VCC zone (C).
583 // Small GND zone (B) overlaps with A but NOT with C.
584 // A should beat C (more items), and B should inherit A's priority.
585 auto zoneA = CreateSquareZone( m_board,
586 BOX2I( VECTOR2I( 0, 0 ),
587 VECTOR2I( pcbIUScale.mmToIU( 40 ), pcbIUScale.mmToIU( 40 ) ) ),
588 F_Cu );
589 zoneA->SetNetCode( netGND->GetNetCode() );
590
591 auto zoneB = CreateSquareZone( m_board,
592 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 25 ), pcbIUScale.mmToIU( 25 ) ),
593 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
594 F_Cu );
595 zoneB->SetNetCode( netGND->GetNetCode() );
596
597 auto zoneC = CreateSquareZone( m_board,
598 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
599 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
600 F_Cu );
601 zoneC->SetNetCode( netVCC->GetNetCode() );
602
603 ZONE* ptrA = zoneA.get();
604 ZONE* ptrB = zoneB.get();
605 ZONE* ptrC = zoneC.get();
606 m_board.Add( zoneA.release() );
607 m_board.Add( zoneB.release() );
608 m_board.Add( zoneC.release() );
609
610 // GND items in the A/C overlap region
611 for( int i = 0; i < 4; i++ )
612 {
613 AddVia( m_board,
614 VECTOR2I( pcbIUScale.mmToIU( 8 + i * 2 ), pcbIUScale.mmToIU( 10 ) ),
615 netGND->GetNetCode() );
616 }
617
618 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 8 ) ),
619 netVCC->GetNetCode() );
620
621 AutoAssignZonePriorities( &m_board );
622
623 // A beats C because GND has more items in the overlap
625
626 // B shares A's priority because they are same-net and overlap
628}
629
630
636{
637public:
638 bool IsLayerVisible( PCB_LAYER_ID ) const override { return true; }
639 PCB_LAYER_ID GetPreferredLayer() const override { return F_Cu; }
640 bool IgnoreLockedItems() const override { return false; }
641 bool IncludeSecondary() const override { return true; }
642 bool IgnoreFPTextOnBack() const override { return false; }
643 bool IgnoreFPTextOnFront() const override { return false; }
644 bool IgnoreFootprintsOnBack() const override { return false; }
645 bool IgnoreFootprintsOnFront() const override { return false; }
646 bool IgnorePadsOnBack() const override { return false; }
647 bool IgnorePadsOnFront() const override { return false; }
648 bool IgnoreThroughHolePads() const override { return false; }
649 bool IgnoreFPValues() const override { return false; }
650 bool IgnoreFPReferences() const override { return false; }
651 bool IgnoreThroughVias() const override { return false; }
652 bool IgnoreBlindBuriedVias() const override { return false; }
653 bool IgnoreMicroVias() const override { return false; }
654 bool IgnoreTracks() const override { return false; }
655 bool IgnoreZoneFills() const override { return true; }
656 bool IgnoreNoNets() const override { return false; }
657 int Accuracy() const override { return 0; }
658 double OnePixelInIU() const override { return 1.0; }
659};
660
661
668BOOST_AUTO_TEST_CASE( RuleAreaCoverageAreaNotZero )
669{
671 GENERAL_COLLECTOR collector;
672 collector.SetGuide( &guide );
673
674 auto ruleArea = CreateSquareZone( m_board,
675 BOX2I( VECTOR2I( 0, 0 ),
676 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
677 F_Cu );
678 ruleArea->SetIsRuleArea( true );
679
680 double zoneArea = FOOTPRINT::GetCoverageArea( ruleArea.get(), collector );
681
682 // The rule area covers a 20 mm x 20 mm region. Its coverage area must reflect that, not 0.
683 BOOST_TEST( zoneArea > 0.0 );
684
685 double expected = (double) pcbIUScale.mmToIU( 20 ) * pcbIUScale.mmToIU( 20 );
686 BOOST_TEST( zoneArea == expected, boost::test_tools::tolerance( 0.001 ) );
687
688 // A small pad enclosed by the rule area must read as much smaller, so the disambiguation
689 // heuristic in GuessSelectionCandidates() will prefer it over the enclosing rule area.
690 PAD* pad = AddPadToBoard( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ),
691 0, F_Cu );
692
693 double padArea = FOOTPRINT::GetCoverageArea( pad, collector );
694
695 BOOST_TEST( padArea > 0.0 );
696 BOOST_TEST( padArea < zoneArea );
697}
698
699
704BOOST_AUTO_TEST_CASE( FilledZoneCoverageUsesFilledPolygons )
705{
707 GENERAL_COLLECTOR collector;
708 collector.SetGuide( &guide );
709
710 auto zone = CreateSquareZone( m_board,
711 BOX2I( VECTOR2I( 0, 0 ),
712 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
713 F_Cu );
714
715 // Fill only a small 2 mm x 2 mm island, far smaller than the 20 mm x 20 mm outline.
716 SHAPE_POLY_SET fill;
718 BOX2I( VECTOR2I( 0, 0 ),
719 VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ) ) ) );
720 zone->SetFilledPolysList( F_Cu, fill );
721
722 double expected = (double) pcbIUScale.mmToIU( 2 ) * pcbIUScale.mmToIU( 2 );
723 BOOST_TEST( FOOTPRINT::GetCoverageArea( zone.get(), collector ) == expected,
724 boost::test_tools::tolerance( 0.001 ) );
725}
726
727
733BOOST_AUTO_TEST_CASE( RuleAreaOutlineDrawnAboveCopper )
734{
735 const int copperPass = F_Cu;
736 const int zonePass = ZONE_LAYER_FOR( F_Cu );
737
738 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( true, zonePass ) );
739 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( true, copperPass ) );
740
741 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( false, copperPass ) );
742 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( false, zonePass ) );
743}
744
745
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
BASE_SET & set(size_t pos)
Definition base_set.h:116
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:265
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1295
An abstract base class whose derivatives may be passed to a GENERAL_COLLECTOR telling it what should ...
Definition collectors.h:50
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void SetPosition(const VECTOR2I &aPos) override
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
void SetStackupMode(FOOTPRINT_STACKUP aMode)
Set the stackup mode for this footprint.
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
void SetGuide(const COLLECTORS_GUIDE *aGuide)
Record which COLLECTORS_GUIDE to use.
Definition collectors.h:287
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:700
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:573
Handle the data for a net.
Definition netinfo.h:46
int GetNetCode() const
Definition netinfo.h:94
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:61
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const std::optional< INTERSECTION > SelfIntersecting() const
Check if the line chain is self-intersecting.
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
Represent a set of closed polygons.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
double Area()
Return the area of this poly set.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int ArcCount() const
Count the number of arc shapes present.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Minimal COLLECTORS_GUIDE so GetCoverageArea() can be exercised headlessly.
bool IgnoreMicroVias() const override
bool IgnoreThroughHolePads() const override
bool IgnoreBlindBuriedVias() const override
bool IgnorePadsOnFront() const override
int Accuracy() const override
bool IgnoreFootprintsOnFront() const override
bool IgnoreFPReferences() const override
bool IgnoreTracks() const override
bool IgnoreFPTextOnFront() const override
bool IgnoreNoNets() const override
bool IsLayerVisible(PCB_LAYER_ID) const override
bool IgnoreFootprintsOnBack() const override
PCB_LAYER_ID GetPreferredLayer() const override
bool IgnoreZoneFills() const override
bool IncludeSecondary() const override
Determine if the secondary criteria or 2nd choice items should be included.
bool IgnoreThroughVias() const override
bool IgnoreFPValues() const override
double OnePixelInIU() const override
bool IgnorePadsOnBack() const override
bool IgnoreLockedItems() const override
bool IgnoreFPTextOnBack() const override
Handle a list of polygons defining a copper zone.
Definition zone.h:70
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition zone.cpp:532
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:599
SHAPE_POLY_SET * Outline()
Definition zone.h:418
void SetIsRuleArea(bool aEnable)
Definition zone.h:812
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:624
VECTOR2I GetPosition() const override
Definition zone.cpp:523
bool IsOnCopperLayer() const override
Definition zone.cpp:574
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:554
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:615
@ RECURSE
Definition eda_item.h:49
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:148
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ Rescue
Definition layer_ids.h:117
@ F_Cu
Definition layer_ids.h:60
#define ZONE_LAYER_FOR(boardLayer)
Definition layer_ids.h:366
SHAPE_LINE_CHAIN BoxToLineChain(const BOX2I &aBox)
bool ZoneOutlineDrawnOnLayer(bool aIsRuleArea, int aLayer)
Decide which GAL draw pass paints a zone's outline.
Utility functions for working with shapes.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST(netlist.find("R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)") !=std::string::npos)
VECTOR3I expected(15, 30, 45)
VECTOR2I center
int radius
static std::unique_ptr< ZONE > CreateSquareZone(BOARD_ITEM_CONTAINER &aParent, BOX2I aBox, PCB_LAYER_ID aLayer)
Definition test_zone.cpp:45
static PCB_VIA * AddVia(BOARD &aBoard, const VECTOR2I &aPos, int aNetCode, PCB_LAYER_ID aTopLayer=F_Cu, PCB_LAYER_ID aBotLayer=B_Cu)
static std::unique_ptr< ZONE > CreateSimilarZone(BOARD_ITEM_CONTAINER &aParent, const ZONE &aOther, PCB_LAYER_ID aLayer)
Create a similar zone (same outline) on a different layer.
Definition test_zone.cpp:62
BOOST_AUTO_TEST_CASE(SingleLayer)
Definition test_zone.cpp:76
static PAD * AddPadToBoard(BOARD &aBoard, const VECTOR2I &aPos, int aNetCode, PCB_LAYER_ID aLayer=F_Cu)
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
std::vector< std::unique_ptr< ZONE > > MergeZonesWithSameOutline(std::vector< std::unique_ptr< ZONE > > &&aZones)
Merges zones with identical outlines and nets on different layers into single multi-layer zones.
bool AutoAssignZonePriorities(BOARD *aBoard, PROGRESS_REPORTER *aReporter)
Automatically assign zone priorities based on connectivity analysis of overlapping regions.