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 <footprint.h>
25#include <geometry/shape_arc.h>
29#include <netinfo.h>
30#include <pad.h>
31#include <padstack.h>
32#include <pcb_painter.h>
33#include <pcb_track.h>
34#include <zone.h>
35#include <zone_utils.h>
36
37
42
43
44static std::unique_ptr<ZONE> CreateSquareZone( BOARD_ITEM_CONTAINER& aParent, PCB_LAYER_ID aLayer,
45 const VECTOR2I& aOrigin, const VECTOR2I& aSize )
46{
47 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( &aParent );
48 zone->SetLayer( aLayer );
49
50 std::unique_ptr<SHAPE_POLY_SET> outline = std::make_unique<SHAPE_POLY_SET>();
51 outline->AddOutline( KIGEOM::BoxToLineChain( BOX2I( aOrigin, aSize ) ) );
52
53 zone->SetOutline( outline.release() );
54
55 return zone;
56}
57
58
62static std::unique_ptr<ZONE> CreateSimilarZone( BOARD_ITEM_CONTAINER& aParent, PCB_LAYER_ID aLayer, const ZONE& aOther )
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
74static FOOTPRINT* MakeFootprintWithRuleArea( BOARD& aBoard, const std::vector<BOX2I>& aCutouts )
75{
76 FOOTPRINT* fp = new FOOTPRINT( &aBoard );
77 aBoard.Add( fp );
78
79 ZONE* zone = new ZONE( fp );
80 zone->SetIsRuleArea( true );
81 zone->SetLayer( F_Cu );
83 BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ) ) );
84
85 for( const BOX2I& cutout : aCutouts )
86 zone->Outline()->AddHole( KIGEOM::BoxToLineChain( cutout ) );
87
88 fp->Add( zone );
89 return fp;
90}
91
92
93BOOST_FIXTURE_TEST_SUITE( Zone, ZONE_TEST_FIXTURE )
94
96{
97 ZONE zone( &m_board );
98
99 zone.SetLayer( F_Cu );
100
101 BOOST_TEST( zone.GetLayer() == F_Cu );
102 BOOST_TEST( zone.GetLayer() == zone.GetFirstLayer() );
103
104 BOOST_TEST( zone.IsOnCopperLayer() == true );
105}
106
107BOOST_AUTO_TEST_CASE( MultipleLayers )
108{
109 ZONE zone( &m_board );
110
111 zone.SetLayerSet( { F_Cu, B_Cu } );
112
113 // There is no "the" layer in a multi-layer zone
115 // ... but there is a first layer
116 BOOST_TEST( zone.GetFirstLayer() == F_Cu );
117
118 BOOST_TEST( zone.IsOnCopperLayer() == true );
119}
120
127BOOST_AUTO_TEST_CASE( RescuedLayers )
128{
129 ZONE zone( &m_board );
130
131 zone.SetLayer( Rescue );
132
133 BOOST_TEST( zone.GetLayer() == Rescue );
134 BOOST_TEST( zone.GetLayer() == zone.GetFirstLayer() );
135
136 BOOST_TEST( zone.IsOnCopperLayer() == false );
137}
138
149BOOST_AUTO_TEST_CASE( CircleZoneCutoutRoundTrip )
150{
151 // Build a circular zone outline the same way convert_tool.cpp does for CIRCLE shapes
152 VECTOR2I center( 0, 0 );
153 int radius = pcbIUScale.mmToIU( 10.0 );
154 SHAPE_ARC fullCircle( center - VECTOR2I( radius, 0 ), center + VECTOR2I( radius, 0 ),
155 center - VECTOR2I( radius, 0 ), 0 );
156
157 SHAPE_POLY_SET sourceOutline;
158 sourceOutline.NewOutline();
159 sourceOutline.Append( fullCircle );
160
161 BOOST_TEST_REQUIRE( sourceOutline.ArcCount() > 0 );
162 double sourceArea = sourceOutline.Area();
163 BOOST_TEST_REQUIRE( sourceArea > 0.0 );
164
165 // Draw a small rectangular cutout well inside the circle
166 SHAPE_POLY_SET cutoutOutline;
167 cutoutOutline.NewOutline();
168 int cutoutHalf = pcbIUScale.mmToIU( 1.0 );
169 cutoutOutline.Append( VECTOR2I( -cutoutHalf, -cutoutHalf ) );
170 cutoutOutline.Append( VECTOR2I( cutoutHalf, -cutoutHalf ) );
171 cutoutOutline.Append( VECTOR2I( cutoutHalf, cutoutHalf ) );
172 cutoutOutline.Append( VECTOR2I( -cutoutHalf, cutoutHalf ) );
173
174 // Mirror the fix applied in ZONE_CREATE_HELPER::performZoneCutout: drop arcs before
175 // the boolean so the saved outline cannot reference stale arc endpoints.
176 SHAPE_POLY_SET working( sourceOutline );
177 working.ClearArcs();
178
179 SHAPE_POLY_SET cutout( cutoutOutline );
180 cutout.ClearArcs();
181
182 working.BooleanSubtract( cutout );
183
184 BOOST_TEST( working.ArcCount() == 0 );
185 BOOST_TEST_REQUIRE( working.OutlineCount() == 1 );
186 BOOST_TEST_REQUIRE( working.HoleCount( 0 ) == 1 );
187
188 double expectedArea = sourceArea - cutoutOutline.Area();
189 BOOST_CHECK_CLOSE( working.Area(), expectedArea, 0.1 );
190
191 // Outline must be a simple closed curve with reasonable point count
192 const SHAPE_LINE_CHAIN& outerChain = working.COutline( 0 );
193 BOOST_TEST( outerChain.IsClosed() );
194 BOOST_TEST( outerChain.PointCount() >= 8 );
195 BOOST_TEST( outerChain.SelfIntersecting().has_value() == false );
196
197 const SHAPE_LINE_CHAIN& holeChain = working.CHole( 0, 0 );
198 BOOST_TEST( holeChain.IsClosed() );
199 BOOST_TEST( holeChain.PointCount() == 4 );
200}
201
208BOOST_AUTO_TEST_CASE( EmptyZoneGetPosition )
209{
210 ZONE zone( &m_board );
211 zone.SetLayer( F_Cu );
212
213 BOOST_TEST( zone.GetNumCorners() == 0 );
214 BOOST_CHECK_NO_THROW( zone.GetPosition() );
215 BOOST_TEST( zone.GetPosition() == VECTOR2I( 0, 0 ) );
216}
217
218
219BOOST_AUTO_TEST_CASE( ZoneMergeNull )
220{
221 std::vector<std::unique_ptr<ZONE>> zones;
222
223 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
224 zones.back()->SetLayer( F_Cu );
225
226 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
227 zones.back()->SetLayer( F_Cu );
228
229 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
230
231 // They are the same, so they do merge
232 BOOST_TEST( merged.size() == 1 );
233}
234
235
236BOOST_AUTO_TEST_CASE( ZoneMergeNonNullNoMerge )
237{
238 std::vector<std::unique_ptr<ZONE>> zones;
239
240 zones.emplace_back( CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) );
241 zones.emplace_back( CreateSquareZone( m_board, B_Cu, VECTOR2I( 200, 200 ), VECTOR2I( 300, 300 ) ) );
242
243 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
244
245 // They are different, so they don't merge
246 BOOST_TEST( merged.size() == 2 );
247}
248
249
250BOOST_AUTO_TEST_CASE( ZoneMergeNonNullMerge )
251{
252 std::vector<std::unique_ptr<ZONE>> zones;
253
254 zones.emplace_back( CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) );
255 zones.emplace_back( CreateSimilarZone( m_board, B_Cu, *zones.back() ) );
256
257 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
258
259 // They are the same, so they do merge
260 BOOST_REQUIRE( merged.size() == 1 );
261
262 BOOST_TEST( merged[0]->GetLayerSet() == ( LSET{ F_Cu, B_Cu } ) );
263 BOOST_TEST( merged[0]->GetNumCorners() == 4 );
264}
265
266
267BOOST_AUTO_TEST_CASE( ZoneMergeMergeSameGeomDifferentOrder )
268{
269 std::vector<std::unique_ptr<ZONE>> zones;
270
271 zones.emplace_back( CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) );
272 zones.emplace_back( CreateSimilarZone( m_board, B_Cu, *zones.back() ) );
273
274 // Reverse the outline of one of them
275 // Don't go overboard here - detailed tests of CompareGeometry
276 // should be in the SHAPE_LINE_CHAIN tests.
277 auto newPolyB = std::make_unique<SHAPE_POLY_SET>( *zones.back()->Outline() );
278 newPolyB->Outline( 0 ).Reverse();
279 zones.back()->SetOutline( newPolyB.release() );
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
290static PCB_VIA* AddVia( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode,
291 PCB_LAYER_ID aTopLayer = F_Cu, PCB_LAYER_ID aBotLayer = B_Cu )
292{
293 PCB_VIA* via = new PCB_VIA( &aBoard );
294 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
295 via->SetPosition( aPos );
296 via->SetLayerPair( aTopLayer, aBotLayer );
297 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
298 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
299 via->SetNetCode( aNetCode );
300 aBoard.Add( via );
301 return via;
302}
303
304
305static PAD* AddPadToBoard( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode, PCB_LAYER_ID aLayer = F_Cu )
306{
307 FOOTPRINT* fp = new FOOTPRINT( &aBoard );
308 fp->SetPosition( aPos );
309 aBoard.Add( fp );
310
311 PAD* pad = new PAD( fp );
312 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
313 pad->SetPosition( aPos );
314 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) ) );
316 pad->SetLayerSet( LSET( { aLayer } ) );
317 pad->SetNetCode( aNetCode );
318 fp->Add( pad );
319 return pad;
320}
321
322
323BOOST_AUTO_TEST_CASE( AutoPriority_NonOverlapping )
324{
325 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
326 m_board.Add( netA );
327 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
328 m_board.Add( netB );
329
330 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
331 VECTOR2I( 0, 0 ),
332 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
333 zoneA->SetNetCode( netA->GetNetCode() );
334 zoneA->SetAssignedPriority( 5 );
335
336 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
337 VECTOR2I( pcbIUScale.mmToIU( 20 ), 0 ),
338 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
339 zoneB->SetNetCode( netB->GetNetCode() );
340 zoneB->SetAssignedPriority( 10 );
341
342 ZONE* ptrA = zoneA.get();
343 ZONE* ptrB = zoneB.get();
344 m_board.Add( zoneA.release() );
345 m_board.Add( zoneB.release() );
346
347 AutoAssignZonePriorities( &m_board );
348
350}
351
352
353BOOST_AUTO_TEST_CASE( AutoPriority_ItemCountWins )
354{
355 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
356 m_board.Add( netA );
357 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
358 m_board.Add( netB );
359
360 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
361 VECTOR2I( 0, 0 ),
362 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
363 zoneA->SetNetCode( netA->GetNetCode() );
364
365 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
366 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
367 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
368 zoneB->SetNetCode( netB->GetNetCode() );
369
370 ZONE* ptrA = zoneA.get();
371 ZONE* ptrB = zoneB.get();
372 m_board.Add( zoneA.release() );
373 m_board.Add( zoneB.release() );
374
375 for( int i = 0; i < 5; i++ )
376 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 7 + i ), pcbIUScale.mmToIU( 10 ) ), netA->GetNetCode() );
377
378 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 7 ) ), netB->GetNetCode() );
379
380 AutoAssignZonePriorities( &m_board );
381
383}
384
385
386BOOST_AUTO_TEST_CASE( AutoPriority_SimilarCountsSmallerWins )
387{
388 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
389 m_board.Add( netA );
390 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
391 m_board.Add( netB );
392
393 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
394 VECTOR2I( 0, 0 ),
395 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) );
396 zoneA->SetNetCode( netA->GetNetCode() );
397
398 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
399 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
400 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
401 zoneB->SetNetCode( netB->GetNetCode() );
402
403 ZONE* ptrA = zoneA.get();
404 ZONE* ptrB = zoneB.get();
405 m_board.Add( zoneA.release() );
406 m_board.Add( zoneB.release() );
407
408 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode() );
409 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode() );
410 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode() );
411 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode() );
412
413 AutoAssignZonePriorities( &m_board );
414
416}
417
418
419BOOST_AUTO_TEST_CASE( AutoPriority_MultiLayerAggregate )
420{
421 m_board.SetCopperLayerCount( 2 );
422
423 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
424 m_board.Add( netA );
425 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
426 m_board.Add( netB );
427
428 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
429 VECTOR2I( 0, 0 ),
430 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
431 zoneA->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
432 zoneA->SetNetCode( netA->GetNetCode() );
433
434 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
435 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
436 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
437 zoneB->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
438 zoneB->SetNetCode( netB->GetNetCode() );
439
440 ZONE* ptrA = zoneA.get();
441 ZONE* ptrB = zoneB.get();
442 m_board.Add( zoneA.release() );
443 m_board.Add( zoneB.release() );
444
445 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode(), F_Cu, B_Cu );
446 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode(), F_Cu, B_Cu );
447
448 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode(), F_Cu, B_Cu );
449 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), netB->GetNetCode(), F_Cu, B_Cu );
450 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode(), F_Cu, B_Cu );
451
452 AutoAssignZonePriorities( &m_board );
453
455}
456
457
458BOOST_AUTO_TEST_CASE( AutoPriority_SameNetEqualPriority )
459{
460 NETINFO_ITEM* net = new NETINFO_ITEM( &m_board, wxT( "SharedNet" ) );
461 m_board.Add( net );
462
463 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
464 VECTOR2I( 0, 0 ),
465 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) );
466 zoneA->SetNetCode( net->GetNetCode() );
467
468 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
469 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
470 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
471 zoneB->SetNetCode( net->GetNetCode() );
472
473 ZONE* ptrA = zoneA.get();
474 ZONE* ptrB = zoneB.get();
475 m_board.Add( zoneA.release() );
476 m_board.Add( zoneB.release() );
477
478 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ), net->GetNetCode() );
479 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ), net->GetNetCode() );
480
481 AutoAssignZonePriorities( &m_board );
482
483 // Same-net overlapping zones are cooperative and must share equal priority
485}
486
487
488BOOST_AUTO_TEST_CASE( AutoPriority_EqualAreaNoChange )
489{
490 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
491 m_board.Add( netA );
492 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
493 m_board.Add( netB );
494
495 // Two identical-sized overlapping zones with no items in the overlap
496 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
497 VECTOR2I( 0, 0 ),
498 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
499 zoneA->SetNetCode( netA->GetNetCode() );
500 zoneA->SetAssignedPriority( 50 );
501
502 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
503 VECTOR2I( 0, 0 ),
504 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
505 zoneB->SetNetCode( netB->GetNetCode() );
506 zoneB->SetAssignedPriority( 50 );
507
508 ZONE* ptrA = zoneA.get();
509 ZONE* ptrB = zoneB.get();
510 m_board.Add( zoneA.release() );
511 m_board.Add( zoneB.release() );
512
513 bool changed = AutoAssignZonePriorities( &m_board );
514
515 // Equal areas, no items: no ordering evidence, priorities must not change
516 BOOST_TEST( changed == false );
517 BOOST_TEST( ptrA->GetAssignedPriority() == 50u );
518 BOOST_TEST( ptrB->GetAssignedPriority() == 50u );
519}
520
521
522BOOST_AUTO_TEST_CASE( AutoPriority_SameNetGroupInheritsEdge )
523{
524 NETINFO_ITEM* netGND = new NETINFO_ITEM( &m_board, wxT( "GND" ) );
525 m_board.Add( netGND );
526 NETINFO_ITEM* netVCC = new NETINFO_ITEM( &m_board, wxT( "VCC" ) );
527 m_board.Add( netVCC );
528
529 // Large GND zone (A) overlaps with small VCC zone (C).
530 // Small GND zone (B) overlaps with A but NOT with C.
531 // A should beat C (more items), and B should inherit A's priority.
532 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
533 VECTOR2I( 0, 0 ),
534 VECTOR2I( pcbIUScale.mmToIU( 40 ), pcbIUScale.mmToIU( 40 ) ) );
535 zoneA->SetNetCode( netGND->GetNetCode() );
536
537 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
538 VECTOR2I( pcbIUScale.mmToIU( 25 ), pcbIUScale.mmToIU( 25 ) ),
539 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
540 zoneB->SetNetCode( netGND->GetNetCode() );
541
542 std::unique_ptr<ZONE> zoneC = CreateSquareZone( m_board, F_Cu,
543 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
544 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
545 zoneC->SetNetCode( netVCC->GetNetCode() );
546
547 ZONE* ptrA = zoneA.get();
548 ZONE* ptrB = zoneB.get();
549 ZONE* ptrC = zoneC.get();
550 m_board.Add( zoneA.release() );
551 m_board.Add( zoneB.release() );
552 m_board.Add( zoneC.release() );
553
554 // GND items in the A/C overlap region
555 for( int i = 0; i < 4; i++ )
556 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 + i * 2 ), pcbIUScale.mmToIU( 10 ) ), netGND->GetNetCode() );
557
558 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 8 ) ), netVCC->GetNetCode() );
559
560 AutoAssignZonePriorities( &m_board );
561
562 // A beats C because GND has more items in the overlap
564
565 // B shares A's priority because they are same-net and overlap
567}
568
569
576BOOST_AUTO_TEST_CASE( RuleAreaCoverageAreaNotZero )
577{
578 std::unique_ptr<ZONE> ruleArea = CreateSquareZone( m_board, F_Cu,
579 VECTOR2I( 0, 0 ),
580 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
581 ruleArea->SetIsRuleArea( true );
582
583 double zoneArea = ruleArea->GetCoverageArea( 0 );
584
585 // The rule area covers a 20 mm x 20 mm region. Its coverage area must reflect that, not 0.
586 BOOST_TEST( zoneArea > 0.0 );
587
588 double expected = (double) pcbIUScale.mmToIU( 20 ) * pcbIUScale.mmToIU( 20 );
589 BOOST_TEST( zoneArea == expected, boost::test_tools::tolerance( 0.001 ) );
590
591 // A small pad enclosed by the rule area must read as much smaller, so the disambiguation
592 // heuristic in GuessSelectionCandidates() will prefer it over the enclosing rule area.
593 PAD* pad = AddPadToBoard( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), 0, F_Cu );
594
595 double padArea = pad->GetCoverageArea( 0 );
596
597 BOOST_TEST( padArea > 0.0 );
598 BOOST_TEST( padArea < zoneArea );
599}
600
601
606BOOST_AUTO_TEST_CASE( FilledZoneCoverageUsesFilledPolygons )
607{
608 std::unique_ptr<ZONE> zone = CreateSquareZone( m_board, F_Cu,
609 VECTOR2I( 0, 0 ),
610 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
611
612 // Fill only a small 2 mm x 2 mm island, far smaller than the 20 mm x 20 mm outline.
613 SHAPE_POLY_SET fill;
615 VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ) ) ) );
616 zone->SetFilledPolysList( F_Cu, fill );
617
618 double expected = (double) pcbIUScale.mmToIU( 2 ) * pcbIUScale.mmToIU( 2 );
619 BOOST_TEST( zone->GetCoverageArea( 0 ) == expected,
620 boost::test_tools::tolerance( 0.001 ) );
621}
622
623
629BOOST_AUTO_TEST_CASE( RuleAreaOutlineDrawnAboveCopper )
630{
631 const int copperPass = F_Cu;
632 const int zonePass = ZONE_LAYER_FOR( F_Cu );
633
634 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( true, zonePass ) );
635 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( true, copperPass ) );
636
637 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( false, copperPass ) );
638 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( false, zonePass ) );
639}
640
641
645BOOST_AUTO_TEST_CASE( LibraryParityDetectsMovedCutout )
646{
647 const VECTOR2I cutoutSize( pcbIUScale.mmToIU( 1 ), pcbIUScale.mmToIU( 1 ) );
648 const BOX2I cutoutA( VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ), cutoutSize );
649 const BOX2I cutoutB( VECTOR2I( pcbIUScale.mmToIU( 6 ), pcbIUScale.mmToIU( 6 ) ), cutoutSize );
650
651 FOOTPRINT* fp = MakeFootprintWithRuleArea( m_board, { cutoutA } );
652 FOOTPRINT* same = MakeFootprintWithRuleArea( m_board, { cutoutA } );
653 FOOTPRINT* moved = MakeFootprintWithRuleArea( m_board, { cutoutB } );
654
655 BOOST_CHECK_MESSAGE( !fp->FootprintNeedsUpdate( same, BOARD_ITEM::COMPARE_FLAGS::DRC ),
656 "Identical rule areas must not report a mismatch" );
657
658 BOOST_CHECK_MESSAGE( fp->FootprintNeedsUpdate( moved, BOARD_ITEM::COMPARE_FLAGS::DRC ),
659 "A moved cutout with the same corner count must report a mismatch" );
660}
661
662
666BOOST_AUTO_TEST_CASE( LibraryParityDetectsCutoutCountChange )
667{
668 const VECTOR2I cutoutSize( pcbIUScale.mmToIU( 1 ), pcbIUScale.mmToIU( 1 ) );
669 const BOX2I cutout1( VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ), cutoutSize );
670 const BOX2I cutout2( VECTOR2I( pcbIUScale.mmToIU( 6 ), pcbIUScale.mmToIU( 6 ) ), cutoutSize );
671
672 FOOTPRINT* twoCutouts = MakeFootprintWithRuleArea( m_board, { cutout1, cutout2 } );
673 FOOTPRINT* oneCutout = MakeFootprintWithRuleArea( m_board, { cutout1 } );
674
675 // Match the vertex total of the two rectangles
676 ZONE* zone = oneCutout->Zones()[0];
677 SHAPE_LINE_CHAIN& hole = zone->Outline()->Hole( 0, 0 );
678
679 const int mid = pcbIUScale.mmToIU( 2 ) + pcbIUScale.mmToIU( 1 ) / 2;
680 hole.Insert( 1, VECTOR2I( mid, pcbIUScale.mmToIU( 2 ) ) );
681 hole.Insert( 3, VECTOR2I( mid, pcbIUScale.mmToIU( 3 ) ) );
682 hole.Insert( 5, VECTOR2I( pcbIUScale.mmToIU( 2 ), mid ) );
683 hole.Insert( 7, VECTOR2I( pcbIUScale.mmToIU( 3 ), mid ) );
684
685 BOOST_REQUIRE_EQUAL( twoCutouts->Zones()[0]->Outline()->TotalVertices(),
686 oneCutout->Zones()[0]->Outline()->TotalVertices() );
687
688 BOOST_CHECK_MESSAGE( twoCutouts->FootprintNeedsUpdate( oneCutout, BOARD_ITEM::COMPARE_FLAGS::DRC ),
689 "A changed cutout count with equal total vertices must report a mismatch" );
690}
691
692
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
Abstract interface for BOARD_ITEMs capable of storing other items inside.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
void SetPosition(const VECTOR2I &aPos) override
ZONES & Zones()
Definition footprint.h:411
bool FootprintNeedsUpdate(const FOOTPRINT *aLibFP, int aCompareFlags=0, REPORTER *aReporter=nullptr)
Return true if a board footprint differs from the library version.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:50
int GetNetCode() const
Definition netinfo.h:104
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
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.
void Insert(size_t aVertex, const VECTOR2I &aP)
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 AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
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
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:573
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:640
SHAPE_POLY_SET * Outline()
Definition zone.h:421
void SetIsRuleArea(bool aEnable)
Definition zone.h:833
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:665
VECTOR2I GetPosition() const override
Definition zone.cpp:564
bool IsOnCopperLayer() const override
Definition zone.cpp:615
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:595
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:617
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:387
SHAPE_LINE_CHAIN BoxToLineChain(const BOX2I &aBox)
Get a SHAPE_LINE_CHAIN representing the outline of a box.
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()
bool moved
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 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 > CreateSquareZone(BOARD_ITEM_CONTAINER &aParent, PCB_LAYER_ID aLayer, const VECTOR2I &aOrigin, const VECTOR2I &aSize)
Definition test_zone.cpp:44
static std::unique_ptr< ZONE > CreateSimilarZone(BOARD_ITEM_CONTAINER &aParent, PCB_LAYER_ID aLayer, const ZONE &aOther)
Create a similar zone (same outline) on a different layer.
Definition test_zone.cpp:62
static FOOTPRINT * MakeFootprintWithRuleArea(BOARD &aBoard, const std::vector< BOX2I > &aCutouts)
Definition test_zone.cpp:74
BOOST_AUTO_TEST_CASE(SingleLayer)
Definition test_zone.cpp:95
static PAD * AddPadToBoard(BOARD &aBoard, const VECTOR2I &aPos, int aNetCode, PCB_LAYER_ID aLayer=F_Cu)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
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.