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