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
130BOOST_AUTO_TEST_CASE( CircleZoneCutoutRoundTrip )
131{
132 // Build a circular zone outline the same way convert_tool.cpp does for CIRCLE shapes
133 VECTOR2I center( 0, 0 );
134 int radius = pcbIUScale.mmToIU( 10.0 );
135 SHAPE_ARC fullCircle( center - VECTOR2I( radius, 0 ), center + VECTOR2I( radius, 0 ),
136 center - VECTOR2I( radius, 0 ), 0 );
137
138 SHAPE_POLY_SET sourceOutline;
139 sourceOutline.NewOutline();
140 sourceOutline.Append( fullCircle );
141
142 BOOST_TEST_REQUIRE( sourceOutline.ArcCount() > 0 );
143 double sourceArea = sourceOutline.Area();
144 BOOST_TEST_REQUIRE( sourceArea > 0.0 );
145
146 // Draw a small rectangular cutout well inside the circle
147 SHAPE_POLY_SET cutoutOutline;
148 cutoutOutline.NewOutline();
149 int cutoutHalf = pcbIUScale.mmToIU( 1.0 );
150 cutoutOutline.Append( VECTOR2I( -cutoutHalf, -cutoutHalf ) );
151 cutoutOutline.Append( VECTOR2I( cutoutHalf, -cutoutHalf ) );
152 cutoutOutline.Append( VECTOR2I( cutoutHalf, cutoutHalf ) );
153 cutoutOutline.Append( VECTOR2I( -cutoutHalf, cutoutHalf ) );
154
155 // Mirror the fix applied in ZONE_CREATE_HELPER::performZoneCutout: drop arcs before
156 // the boolean so the saved outline cannot reference stale arc endpoints.
157 SHAPE_POLY_SET working( sourceOutline );
158 working.ClearArcs();
159
160 SHAPE_POLY_SET cutout( cutoutOutline );
161 cutout.ClearArcs();
162
163 working.BooleanSubtract( cutout );
164
165 BOOST_TEST( working.ArcCount() == 0 );
166 BOOST_TEST_REQUIRE( working.OutlineCount() == 1 );
167 BOOST_TEST_REQUIRE( working.HoleCount( 0 ) == 1 );
168
169 double expectedArea = sourceArea - cutoutOutline.Area();
170 BOOST_CHECK_CLOSE( working.Area(), expectedArea, 0.1 );
171
172 // Outline must be a simple closed curve with reasonable point count
173 const SHAPE_LINE_CHAIN& outerChain = working.COutline( 0 );
174 BOOST_TEST( outerChain.IsClosed() );
175 BOOST_TEST( outerChain.PointCount() >= 8 );
176 BOOST_TEST( outerChain.SelfIntersecting().has_value() == false );
177
178 const SHAPE_LINE_CHAIN& holeChain = working.CHole( 0, 0 );
179 BOOST_TEST( holeChain.IsClosed() );
180 BOOST_TEST( holeChain.PointCount() == 4 );
181}
182
189BOOST_AUTO_TEST_CASE( EmptyZoneGetPosition )
190{
191 ZONE zone( &m_board );
192 zone.SetLayer( F_Cu );
193
194 BOOST_TEST( zone.GetNumCorners() == 0 );
195 BOOST_CHECK_NO_THROW( zone.GetPosition() );
196 BOOST_TEST( zone.GetPosition() == VECTOR2I( 0, 0 ) );
197}
198
199
200BOOST_AUTO_TEST_CASE( ZoneMergeNull )
201{
202 std::vector<std::unique_ptr<ZONE>> zones;
203
204 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
205 zones.back()->SetLayer( F_Cu );
206
207 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
208 zones.back()->SetLayer( F_Cu );
209
210 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
211
212 // They are the same, so they do merge
213 BOOST_TEST( merged.size() == 1 );
214}
215
216
217BOOST_AUTO_TEST_CASE( ZoneMergeNonNullNoMerge )
218{
219 std::vector<std::unique_ptr<ZONE>> zones;
220
221 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ), F_Cu ) );
222 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 200, 200 ), VECTOR2I( 300, 300 ) ), B_Cu ) );
223
224 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
225
226 // They are different, so they don't merge
227 BOOST_TEST( merged.size() == 2 );
228}
229
230
231BOOST_AUTO_TEST_CASE( ZoneMergeNonNullMerge )
232{
233 std::vector<std::unique_ptr<ZONE>> zones;
234
235 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ), F_Cu ) );
236 zones.emplace_back( CreateSimilarZone( m_board, *zones.back(), B_Cu ) );
237
238 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
239
240 // They are the same, so they do merge
241 BOOST_REQUIRE( merged.size() == 1 );
242
243 BOOST_TEST( merged[0]->GetLayerSet() == ( LSET{ F_Cu, B_Cu } ) );
244 BOOST_TEST( merged[0]->GetNumCorners() == 4 );
245}
246
247
248BOOST_AUTO_TEST_CASE( ZoneMergeMergeSameGeomDifferentOrder )
249{
250 std::vector<std::unique_ptr<ZONE>> zones;
251
252 zones.emplace_back( CreateSquareZone( m_board, BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ), F_Cu ) );
253 zones.emplace_back( CreateSimilarZone( m_board, *zones.back(), B_Cu ) );
254
255 // Reverse the outline of one of them
256 // Don't go overboard here - detailed tests of CompareGeometry
257 // should be in the SHAPE_LINE_CHAIN tests.
258 auto newPolyB = std::make_unique<SHAPE_POLY_SET>( *zones.back()->Outline() );
259 newPolyB->Outline( 0 ).Reverse();
260 zones.back()->SetOutline( newPolyB.release() );
261
262 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
263
264 // They are the same, so they do merge
265 BOOST_REQUIRE( merged.size() == 1 );
266
267 BOOST_TEST( merged[0]->GetLayerSet() == LSET( { F_Cu, B_Cu } ) );
268 BOOST_TEST( merged[0]->GetNumCorners() == 4 );
269}
270
271static PCB_VIA* AddVia( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode,
272 PCB_LAYER_ID aTopLayer = F_Cu, PCB_LAYER_ID aBotLayer = B_Cu )
273{
274 PCB_VIA* via = new PCB_VIA( &aBoard );
275 via->SetPosition( aPos );
276 via->SetLayerPair( aTopLayer, aBotLayer );
277 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
278 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
279 via->SetNetCode( aNetCode );
280 aBoard.Add( via );
281 return via;
282}
283
284
285static PAD* AddPadToBoard( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode,
286 PCB_LAYER_ID aLayer = F_Cu )
287{
288 FOOTPRINT* fp = new FOOTPRINT( &aBoard );
289 fp->SetPosition( aPos );
290 aBoard.Add( fp );
291
292 PAD* pad = new PAD( fp );
293 pad->SetPosition( aPos );
294 pad->SetSize( PADSTACK::ALL_LAYERS,
295 VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) ) );
297 pad->SetLayerSet( LSET( { aLayer } ) );
298 pad->SetNetCode( aNetCode );
299 fp->Add( pad );
300 return pad;
301}
302
303
304BOOST_AUTO_TEST_CASE( AutoPriority_NonOverlapping )
305{
306 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
307 m_board.Add( netA );
308 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
309 m_board.Add( netB );
310
311 auto zoneA = CreateSquareZone( m_board,
312 BOX2I( VECTOR2I( 0, 0 ),
313 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
314 F_Cu );
315 zoneA->SetNetCode( netA->GetNetCode() );
316 zoneA->SetAssignedPriority( 5 );
317
318 auto zoneB = CreateSquareZone( m_board,
319 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 20 ), 0 ),
320 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
321 F_Cu );
322 zoneB->SetNetCode( netB->GetNetCode() );
323 zoneB->SetAssignedPriority( 10 );
324
325 ZONE* ptrA = zoneA.get();
326 ZONE* ptrB = zoneB.get();
327 m_board.Add( zoneA.release() );
328 m_board.Add( zoneB.release() );
329
330 AutoAssignZonePriorities( &m_board );
331
333}
334
335
336BOOST_AUTO_TEST_CASE( AutoPriority_ItemCountWins )
337{
338 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
339 m_board.Add( netA );
340 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
341 m_board.Add( netB );
342
343 auto zoneA = CreateSquareZone( m_board,
344 BOX2I( VECTOR2I( 0, 0 ),
345 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
346 F_Cu );
347 zoneA->SetNetCode( netA->GetNetCode() );
348
349 auto zoneB = CreateSquareZone( m_board,
350 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
351 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
352 F_Cu );
353 zoneB->SetNetCode( netB->GetNetCode() );
354
355 ZONE* ptrA = zoneA.get();
356 ZONE* ptrB = zoneB.get();
357 m_board.Add( zoneA.release() );
358 m_board.Add( zoneB.release() );
359
360 for( int i = 0; i < 5; i++ )
361 {
362 AddVia( m_board,
363 VECTOR2I( pcbIUScale.mmToIU( 7 + i ), pcbIUScale.mmToIU( 10 ) ),
364 netA->GetNetCode() );
365 }
366
367 AddVia( m_board,
368 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 7 ) ),
369 netB->GetNetCode() );
370
371 AutoAssignZonePriorities( &m_board );
372
374}
375
376
377BOOST_AUTO_TEST_CASE( AutoPriority_SimilarCountsSmallerWins )
378{
379 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
380 m_board.Add( netA );
381 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
382 m_board.Add( netB );
383
384 auto zoneA = CreateSquareZone( m_board,
385 BOX2I( VECTOR2I( 0, 0 ),
386 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) ),
387 F_Cu );
388 zoneA->SetNetCode( netA->GetNetCode() );
389
390 auto zoneB = CreateSquareZone( m_board,
391 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
392 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
393 F_Cu );
394 zoneB->SetNetCode( netB->GetNetCode() );
395
396 ZONE* ptrA = zoneA.get();
397 ZONE* ptrB = zoneB.get();
398 m_board.Add( zoneA.release() );
399 m_board.Add( zoneB.release() );
400
401 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ),
402 netA->GetNetCode() );
403 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ),
404 netA->GetNetCode() );
405 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ),
406 netB->GetNetCode() );
407 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ),
408 netB->GetNetCode() );
409
410 AutoAssignZonePriorities( &m_board );
411
413}
414
415
416BOOST_AUTO_TEST_CASE( AutoPriority_MultiLayerAggregate )
417{
418 m_board.SetCopperLayerCount( 2 );
419
420 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
421 m_board.Add( netA );
422 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
423 m_board.Add( netB );
424
425 auto zoneA = CreateSquareZone( m_board,
426 BOX2I( VECTOR2I( 0, 0 ),
427 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
428 F_Cu );
429 zoneA->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
430 zoneA->SetNetCode( netA->GetNetCode() );
431
432 auto zoneB = CreateSquareZone( m_board,
433 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
434 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
435 F_Cu );
436 zoneB->SetLayerSet( LSET( { F_Cu, B_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(), F_Cu, B_Cu );
446 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ),
447 netA->GetNetCode(), F_Cu, B_Cu );
448
449 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ),
450 netB->GetNetCode(), F_Cu, B_Cu );
451 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ),
452 netB->GetNetCode(), F_Cu, B_Cu );
453 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ),
454 netB->GetNetCode(), F_Cu, B_Cu );
455
456 AutoAssignZonePriorities( &m_board );
457
459}
460
461
462BOOST_AUTO_TEST_CASE( AutoPriority_SameNetEqualPriority )
463{
464 NETINFO_ITEM* net = new NETINFO_ITEM( &m_board, wxT( "SharedNet" ) );
465 m_board.Add( net );
466
467 auto zoneA = CreateSquareZone( m_board,
468 BOX2I( VECTOR2I( 0, 0 ),
469 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) ),
470 F_Cu );
471 zoneA->SetNetCode( net->GetNetCode() );
472
473 auto zoneB = CreateSquareZone( m_board,
474 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
475 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
476 F_Cu );
477 zoneB->SetNetCode( net->GetNetCode() );
478
479 ZONE* ptrA = zoneA.get();
480 ZONE* ptrB = zoneB.get();
481 m_board.Add( zoneA.release() );
482 m_board.Add( zoneB.release() );
483
484 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ),
485 net->GetNetCode() );
486 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ),
487 net->GetNetCode() );
488
489 AutoAssignZonePriorities( &m_board );
490
491 // Same-net overlapping zones are cooperative and must share equal priority
493}
494
495
496BOOST_AUTO_TEST_CASE( AutoPriority_EqualAreaNoChange )
497{
498 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
499 m_board.Add( netA );
500 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
501 m_board.Add( netB );
502
503 // Two identical-sized overlapping zones with no items in the overlap
504 auto zoneA = CreateSquareZone( m_board,
505 BOX2I( VECTOR2I( 0, 0 ),
506 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
507 F_Cu );
508 zoneA->SetNetCode( netA->GetNetCode() );
509 zoneA->SetAssignedPriority( 50 );
510
511 auto zoneB = CreateSquareZone( m_board,
512 BOX2I( VECTOR2I( 0, 0 ),
513 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
514 F_Cu );
515 zoneB->SetNetCode( netB->GetNetCode() );
516 zoneB->SetAssignedPriority( 50 );
517
518 ZONE* ptrA = zoneA.get();
519 ZONE* ptrB = zoneB.get();
520 m_board.Add( zoneA.release() );
521 m_board.Add( zoneB.release() );
522
523 bool changed = AutoAssignZonePriorities( &m_board );
524
525 // Equal areas, no items: no ordering evidence, priorities must not change
526 BOOST_TEST( changed == false );
527 BOOST_TEST( ptrA->GetAssignedPriority() == 50u );
528 BOOST_TEST( ptrB->GetAssignedPriority() == 50u );
529}
530
531
532BOOST_AUTO_TEST_CASE( AutoPriority_SameNetGroupInheritsEdge )
533{
534 NETINFO_ITEM* netGND = new NETINFO_ITEM( &m_board, wxT( "GND" ) );
535 m_board.Add( netGND );
536 NETINFO_ITEM* netVCC = new NETINFO_ITEM( &m_board, wxT( "VCC" ) );
537 m_board.Add( netVCC );
538
539 // Large GND zone (A) overlaps with small VCC zone (C).
540 // Small GND zone (B) overlaps with A but NOT with C.
541 // A should beat C (more items), and B should inherit A's priority.
542 auto zoneA = CreateSquareZone( m_board,
543 BOX2I( VECTOR2I( 0, 0 ),
544 VECTOR2I( pcbIUScale.mmToIU( 40 ), pcbIUScale.mmToIU( 40 ) ) ),
545 F_Cu );
546 zoneA->SetNetCode( netGND->GetNetCode() );
547
548 auto zoneB = CreateSquareZone( m_board,
549 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 25 ), pcbIUScale.mmToIU( 25 ) ),
550 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
551 F_Cu );
552 zoneB->SetNetCode( netGND->GetNetCode() );
553
554 auto zoneC = CreateSquareZone( m_board,
555 BOX2I( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
556 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ),
557 F_Cu );
558 zoneC->SetNetCode( netVCC->GetNetCode() );
559
560 ZONE* ptrA = zoneA.get();
561 ZONE* ptrB = zoneB.get();
562 ZONE* ptrC = zoneC.get();
563 m_board.Add( zoneA.release() );
564 m_board.Add( zoneB.release() );
565 m_board.Add( zoneC.release() );
566
567 // GND items in the A/C overlap region
568 for( int i = 0; i < 4; i++ )
569 {
570 AddVia( m_board,
571 VECTOR2I( pcbIUScale.mmToIU( 8 + i * 2 ), pcbIUScale.mmToIU( 10 ) ),
572 netGND->GetNetCode() );
573 }
574
575 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 8 ) ),
576 netVCC->GetNetCode() );
577
578 AutoAssignZonePriorities( &m_board );
579
580 // A beats C because GND has more items in the overlap
582
583 // B shares A's priority because they are same-net and overlap
585}
586
587
593{
594public:
595 bool IsLayerVisible( PCB_LAYER_ID ) const override { return true; }
596 PCB_LAYER_ID GetPreferredLayer() const override { return F_Cu; }
597 bool IgnoreLockedItems() const override { return false; }
598 bool IncludeSecondary() const override { return true; }
599 bool IgnoreFPTextOnBack() const override { return false; }
600 bool IgnoreFPTextOnFront() const override { return false; }
601 bool IgnoreFootprintsOnBack() const override { return false; }
602 bool IgnoreFootprintsOnFront() const override { return false; }
603 bool IgnorePadsOnBack() const override { return false; }
604 bool IgnorePadsOnFront() const override { return false; }
605 bool IgnoreThroughHolePads() const override { return false; }
606 bool IgnoreFPValues() const override { return false; }
607 bool IgnoreFPReferences() const override { return false; }
608 bool IgnoreThroughVias() const override { return false; }
609 bool IgnoreBlindBuriedVias() const override { return false; }
610 bool IgnoreMicroVias() const override { return false; }
611 bool IgnoreTracks() const override { return false; }
612 bool IgnoreZoneFills() const override { return true; }
613 bool IgnoreNoNets() const override { return false; }
614 int Accuracy() const override { return 0; }
615 double OnePixelInIU() const override { return 1.0; }
616};
617
618
625BOOST_AUTO_TEST_CASE( RuleAreaCoverageAreaNotZero )
626{
628 GENERAL_COLLECTOR collector;
629 collector.SetGuide( &guide );
630
631 auto ruleArea = CreateSquareZone( m_board,
632 BOX2I( VECTOR2I( 0, 0 ),
633 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
634 F_Cu );
635 ruleArea->SetIsRuleArea( true );
636
637 double zoneArea = FOOTPRINT::GetCoverageArea( ruleArea.get(), collector );
638
639 // The rule area covers a 20 mm x 20 mm region. Its coverage area must reflect that, not 0.
640 BOOST_TEST( zoneArea > 0.0 );
641
642 double expected = (double) pcbIUScale.mmToIU( 20 ) * pcbIUScale.mmToIU( 20 );
643 BOOST_TEST( zoneArea == expected, boost::test_tools::tolerance( 0.001 ) );
644
645 // A small pad enclosed by the rule area must read as much smaller, so the disambiguation
646 // heuristic in GuessSelectionCandidates() will prefer it over the enclosing rule area.
647 PAD* pad = AddPadToBoard( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ),
648 0, F_Cu );
649
650 double padArea = FOOTPRINT::GetCoverageArea( pad, collector );
651
652 BOOST_TEST( padArea > 0.0 );
653 BOOST_TEST( padArea < zoneArea );
654}
655
656
661BOOST_AUTO_TEST_CASE( FilledZoneCoverageUsesFilledPolygons )
662{
664 GENERAL_COLLECTOR collector;
665 collector.SetGuide( &guide );
666
667 auto zone = CreateSquareZone( m_board,
668 BOX2I( VECTOR2I( 0, 0 ),
669 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) ),
670 F_Cu );
671
672 // Fill only a small 2 mm x 2 mm island, far smaller than the 20 mm x 20 mm outline.
673 SHAPE_POLY_SET fill;
675 BOX2I( VECTOR2I( 0, 0 ),
676 VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ) ) ) );
677 zone->SetFilledPolysList( F_Cu, fill );
678
679 double expected = (double) pcbIUScale.mmToIU( 2 ) * pcbIUScale.mmToIU( 2 );
680 BOOST_TEST( FOOTPRINT::GetCoverageArea( zone.get(), collector ) == expected,
681 boost::test_tools::tolerance( 0.001 ) );
682}
683
684
690BOOST_AUTO_TEST_CASE( RuleAreaOutlineDrawnAboveCopper )
691{
692 const int copperPass = F_Cu;
693 const int zonePass = ZONE_LAYER_FOR( F_Cu );
694
695 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( true, zonePass ) );
696 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( true, copperPass ) );
697
698 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( false, copperPass ) );
699 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( false, zonePass ) );
700}
701
702
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
Abstract interface for BOARD_ITEMs capable of storing other items inside.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1355
An abstract base class whose derivatives may be passed to a GENERAL_COLLECTOR telling it what should ...
Definition collectors.h:50
void SetPosition(const VECTOR2I &aPos) override
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
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
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:552
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:619
SHAPE_POLY_SET * Outline()
Definition zone.h:418
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:644
VECTOR2I GetPosition() const override
Definition zone.cpp:543
bool IsOnCopperLayer() const override
Definition zone.cpp:594
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:574
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:615
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:374
SHAPE_LINE_CHAIN BoxToLineChain(const BOX2I &aBox)
bool ZoneOutlineDrawnOnLayer(bool aOutlineOnly, 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)
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.