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 <tool/tool_manager.h>
36#include <view/view.h>
37#include <zone.h>
38#include <zone_utils.h>
39
40
45
46
47static std::unique_ptr<ZONE> CreateSquareZone( BOARD_ITEM_CONTAINER& aParent, PCB_LAYER_ID aLayer,
48 const VECTOR2I& aOrigin, const VECTOR2I& aSize )
49{
50 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( &aParent );
51 zone->SetLayer( aLayer );
52
53 std::unique_ptr<SHAPE_POLY_SET> outline = std::make_unique<SHAPE_POLY_SET>();
54 outline->AddOutline( KIGEOM::BoxToLineChain( BOX2I( aOrigin, aSize ) ) );
55
56 zone->SetOutline( outline.release() );
57
58 return zone;
59}
60
61
65static std::unique_ptr<ZONE> CreateSimilarZone( BOARD_ITEM_CONTAINER& aParent, PCB_LAYER_ID aLayer, const ZONE& aOther )
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
77static FOOTPRINT* MakeFootprintWithRuleArea( BOARD& aBoard, const std::vector<BOX2I>& aCutouts )
78{
79 FOOTPRINT* fp = new FOOTPRINT( &aBoard );
80 aBoard.Add( fp );
81
82 ZONE* zone = new ZONE( fp );
83 zone->SetIsRuleArea( true );
84 zone->SetLayer( F_Cu );
86 BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) ) ) );
87
88 for( const BOX2I& cutout : aCutouts )
89 zone->Outline()->AddHole( KIGEOM::BoxToLineChain( cutout ) );
90
91 fp->Add( zone );
92 return fp;
93}
94
95
96BOOST_FIXTURE_TEST_SUITE( Zone, ZONE_TEST_FIXTURE )
97
99{
100 ZONE zone( &m_board );
101
102 zone.SetLayer( F_Cu );
103
104 BOOST_TEST( zone.GetLayer() == F_Cu );
105 BOOST_TEST( zone.GetLayer() == zone.GetFirstLayer() );
106
107 BOOST_TEST( zone.IsOnCopperLayer() == true );
108}
109
110BOOST_AUTO_TEST_CASE( MultipleLayers )
111{
112 ZONE zone( &m_board );
113
114 zone.SetLayerSet( { F_Cu, B_Cu } );
115
116 // There is no "the" layer in a multi-layer zone
118 // ... but there is a first layer
119 BOOST_TEST( zone.GetFirstLayer() == F_Cu );
120
121 BOOST_TEST( zone.IsOnCopperLayer() == true );
122}
123
130BOOST_AUTO_TEST_CASE( RescuedLayers )
131{
132 ZONE zone( &m_board );
133
134 zone.SetLayer( Rescue );
135
136 BOOST_TEST( zone.GetLayer() == Rescue );
137 BOOST_TEST( zone.GetLayer() == zone.GetFirstLayer() );
138
139 BOOST_TEST( zone.IsOnCopperLayer() == false );
140}
141
152BOOST_AUTO_TEST_CASE( CircleZoneCutoutRoundTrip )
153{
154 // Build a circular zone outline the same way convert_tool.cpp does for CIRCLE shapes
155 VECTOR2I center( 0, 0 );
156 int radius = pcbIUScale.mmToIU( 10.0 );
157 SHAPE_ARC fullCircle( center - VECTOR2I( radius, 0 ), center + VECTOR2I( radius, 0 ),
158 center - VECTOR2I( radius, 0 ), 0 );
159
160 SHAPE_POLY_SET sourceOutline;
161 sourceOutline.NewOutline();
162 sourceOutline.Append( fullCircle );
163
164 BOOST_TEST_REQUIRE( sourceOutline.ArcCount() > 0 );
165 double sourceArea = sourceOutline.Area();
166 BOOST_TEST_REQUIRE( sourceArea > 0.0 );
167
168 // Draw a small rectangular cutout well inside the circle
169 SHAPE_POLY_SET cutoutOutline;
170 cutoutOutline.NewOutline();
171 int cutoutHalf = pcbIUScale.mmToIU( 1.0 );
172 cutoutOutline.Append( VECTOR2I( -cutoutHalf, -cutoutHalf ) );
173 cutoutOutline.Append( VECTOR2I( cutoutHalf, -cutoutHalf ) );
174 cutoutOutline.Append( VECTOR2I( cutoutHalf, cutoutHalf ) );
175 cutoutOutline.Append( VECTOR2I( -cutoutHalf, cutoutHalf ) );
176
177 // Mirror the fix applied in ZONE_CREATE_HELPER::performZoneCutout: drop arcs before
178 // the boolean so the saved outline cannot reference stale arc endpoints.
179 SHAPE_POLY_SET working( sourceOutline );
180 working.ClearArcs();
181
182 SHAPE_POLY_SET cutout( cutoutOutline );
183 cutout.ClearArcs();
184
185 working.BooleanSubtract( cutout );
186
187 BOOST_TEST( working.ArcCount() == 0 );
188 BOOST_TEST_REQUIRE( working.OutlineCount() == 1 );
189 BOOST_TEST_REQUIRE( working.HoleCount( 0 ) == 1 );
190
191 double expectedArea = sourceArea - cutoutOutline.Area();
192 BOOST_CHECK_CLOSE( working.Area(), expectedArea, 0.1 );
193
194 // Outline must be a simple closed curve with reasonable point count
195 const SHAPE_LINE_CHAIN& outerChain = working.COutline( 0 );
196 BOOST_TEST( outerChain.IsClosed() );
197 BOOST_TEST( outerChain.PointCount() >= 8 );
198 BOOST_TEST( outerChain.SelfIntersecting().has_value() == false );
199
200 const SHAPE_LINE_CHAIN& holeChain = working.CHole( 0, 0 );
201 BOOST_TEST( holeChain.IsClosed() );
202 BOOST_TEST( holeChain.PointCount() == 4 );
203}
204
211BOOST_AUTO_TEST_CASE( EmptyZoneGetPosition )
212{
213 ZONE zone( &m_board );
214 zone.SetLayer( F_Cu );
215
216 BOOST_TEST( zone.GetNumCorners() == 0 );
217 BOOST_CHECK_NO_THROW( zone.GetPosition() );
218 BOOST_TEST( zone.GetPosition() == VECTOR2I( 0, 0 ) );
219}
220
221
222BOOST_AUTO_TEST_CASE( ZoneMergeNull )
223{
224 std::vector<std::unique_ptr<ZONE>> zones;
225
226 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
227 zones.back()->SetLayer( F_Cu );
228
229 zones.emplace_back( std::make_unique<ZONE>( &m_board ) );
230 zones.back()->SetLayer( F_Cu );
231
232 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
233
234 // They are the same, so they do merge
235 BOOST_TEST( merged.size() == 1 );
236}
237
238
239BOOST_AUTO_TEST_CASE( ZoneMergeNonNullNoMerge )
240{
241 std::vector<std::unique_ptr<ZONE>> zones;
242
243 zones.emplace_back( CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) );
244 zones.emplace_back( CreateSquareZone( m_board, B_Cu, VECTOR2I( 200, 200 ), VECTOR2I( 300, 300 ) ) );
245
246 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
247
248 // They are different, so they don't merge
249 BOOST_TEST( merged.size() == 2 );
250}
251
252
253BOOST_AUTO_TEST_CASE( ZoneMergeNonNullMerge )
254{
255 std::vector<std::unique_ptr<ZONE>> zones;
256
257 zones.emplace_back( CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) );
258 zones.emplace_back( CreateSimilarZone( m_board, B_Cu, *zones.back() ) );
259
260 std::vector<std::unique_ptr<ZONE>> merged = MergeZonesWithSameOutline( std::move( zones ) );
261
262 // They are the same, so they do merge
263 BOOST_REQUIRE( merged.size() == 1 );
264
265 BOOST_TEST( merged[0]->GetLayerSet() == ( LSET{ F_Cu, B_Cu } ) );
266 BOOST_TEST( merged[0]->GetNumCorners() == 4 );
267}
268
269
270BOOST_AUTO_TEST_CASE( ZoneMergeMergeSameGeomDifferentOrder )
271{
272 std::vector<std::unique_ptr<ZONE>> zones;
273
274 zones.emplace_back( CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) );
275 zones.emplace_back( CreateSimilarZone( m_board, B_Cu, *zones.back() ) );
276
277 // Reverse the outline of one of them
278 // Don't go overboard here - detailed tests of CompareGeometry
279 // should be in the SHAPE_LINE_CHAIN tests.
280 auto newPolyB = std::make_unique<SHAPE_POLY_SET>( *zones.back()->Outline() );
281 newPolyB->Outline( 0 ).Reverse();
282 zones.back()->SetOutline( newPolyB.release() );
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
293static PCB_VIA* AddVia( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode,
294 PCB_LAYER_ID aTopLayer = F_Cu, PCB_LAYER_ID aBotLayer = B_Cu )
295{
296 PCB_VIA* via = new PCB_VIA( &aBoard );
297 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
298 via->SetPosition( aPos );
299 via->SetLayerPair( aTopLayer, aBotLayer );
300 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
301 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
302 via->SetNetCode( aNetCode );
303 aBoard.Add( via );
304 return via;
305}
306
307
308static PAD* AddPadToBoard( BOARD& aBoard, const VECTOR2I& aPos, int aNetCode, PCB_LAYER_ID aLayer = F_Cu )
309{
310 FOOTPRINT* fp = new FOOTPRINT( &aBoard );
311 fp->SetPosition( aPos );
312 aBoard.Add( fp );
313
314 PAD* pad = new PAD( fp );
315 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
316 pad->SetPosition( aPos );
317 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) ) );
319 pad->SetLayerSet( LSET( { aLayer } ) );
320 pad->SetNetCode( aNetCode );
321 fp->Add( pad );
322 return pad;
323}
324
325
326BOOST_AUTO_TEST_CASE( AutoPriority_NonOverlapping )
327{
328 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
329 m_board.Add( netA );
330 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
331 m_board.Add( netB );
332
333 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
334 VECTOR2I( 0, 0 ),
335 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
336 zoneA->SetNetCode( netA->GetNetCode() );
337 zoneA->SetAssignedPriority( 5 );
338
339 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
340 VECTOR2I( pcbIUScale.mmToIU( 20 ), 0 ),
341 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
342 zoneB->SetNetCode( netB->GetNetCode() );
343 zoneB->SetAssignedPriority( 10 );
344
345 ZONE* ptrA = zoneA.get();
346 ZONE* ptrB = zoneB.get();
347 m_board.Add( zoneA.release() );
348 m_board.Add( zoneB.release() );
349
350 AutoAssignZonePriorities( &m_board );
351
353}
354
355
356BOOST_AUTO_TEST_CASE( AutoPriority_ItemCountWins )
357{
358 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
359 m_board.Add( netA );
360 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
361 m_board.Add( netB );
362
363 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
364 VECTOR2I( 0, 0 ),
365 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
366 zoneA->SetNetCode( netA->GetNetCode() );
367
368 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
369 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
370 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
371 zoneB->SetNetCode( netB->GetNetCode() );
372
373 ZONE* ptrA = zoneA.get();
374 ZONE* ptrB = zoneB.get();
375 m_board.Add( zoneA.release() );
376 m_board.Add( zoneB.release() );
377
378 for( int i = 0; i < 5; i++ )
379 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 7 + i ), pcbIUScale.mmToIU( 10 ) ), netA->GetNetCode() );
380
381 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 7 ) ), netB->GetNetCode() );
382
383 AutoAssignZonePriorities( &m_board );
384
386}
387
388
389BOOST_AUTO_TEST_CASE( AutoPriority_SimilarCountsSmallerWins )
390{
391 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
392 m_board.Add( netA );
393 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
394 m_board.Add( netB );
395
396 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
397 VECTOR2I( 0, 0 ),
398 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) );
399 zoneA->SetNetCode( netA->GetNetCode() );
400
401 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
402 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
403 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
404 zoneB->SetNetCode( netB->GetNetCode() );
405
406 ZONE* ptrA = zoneA.get();
407 ZONE* ptrB = zoneB.get();
408 m_board.Add( zoneA.release() );
409 m_board.Add( zoneB.release() );
410
411 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode() );
412 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode() );
413 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode() );
414 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode() );
415
416 AutoAssignZonePriorities( &m_board );
417
419}
420
421
422BOOST_AUTO_TEST_CASE( AutoPriority_MultiLayerAggregate )
423{
424 m_board.SetCopperLayerCount( 2 );
425
426 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
427 m_board.Add( netA );
428 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
429 m_board.Add( netB );
430
431 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
432 VECTOR2I( 0, 0 ),
433 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
434 zoneA->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
435 zoneA->SetNetCode( netA->GetNetCode() );
436
437 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
438 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
439 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
440 zoneB->SetLayerSet( LSET( { F_Cu, B_Cu } ) );
441 zoneB->SetNetCode( netB->GetNetCode() );
442
443 ZONE* ptrA = zoneA.get();
444 ZONE* ptrB = zoneB.get();
445 m_board.Add( zoneA.release() );
446 m_board.Add( zoneB.release() );
447
448 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode(), F_Cu, B_Cu );
449 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 8 ) ), netA->GetNetCode(), F_Cu, B_Cu );
450
451 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode(), F_Cu, B_Cu );
452 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), netB->GetNetCode(), F_Cu, B_Cu );
453 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ), netB->GetNetCode(), F_Cu, B_Cu );
454
455 AutoAssignZonePriorities( &m_board );
456
458}
459
460
461BOOST_AUTO_TEST_CASE( AutoPriority_SameNetEqualPriority )
462{
463 NETINFO_ITEM* net = new NETINFO_ITEM( &m_board, wxT( "SharedNet" ) );
464 m_board.Add( net );
465
466 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
467 VECTOR2I( 0, 0 ),
468 VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 30 ) ) );
469 zoneA->SetNetCode( net->GetNetCode() );
470
471 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
472 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
473 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
474 zoneB->SetNetCode( net->GetNetCode() );
475
476 ZONE* ptrA = zoneA.get();
477 ZONE* ptrB = zoneB.get();
478 m_board.Add( zoneA.release() );
479 m_board.Add( zoneB.release() );
480
481 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 ), pcbIUScale.mmToIU( 8 ) ), net->GetNetCode() );
482 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 12 ), pcbIUScale.mmToIU( 12 ) ), net->GetNetCode() );
483
484 AutoAssignZonePriorities( &m_board );
485
486 // Same-net overlapping zones are cooperative and must share equal priority
488}
489
490
491BOOST_AUTO_TEST_CASE( AutoPriority_EqualAreaNoChange )
492{
493 NETINFO_ITEM* netA = new NETINFO_ITEM( &m_board, wxT( "NetA" ) );
494 m_board.Add( netA );
495 NETINFO_ITEM* netB = new NETINFO_ITEM( &m_board, wxT( "NetB" ) );
496 m_board.Add( netB );
497
498 // Two identical-sized overlapping zones with no items in the overlap
499 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
500 VECTOR2I( 0, 0 ),
501 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
502 zoneA->SetNetCode( netA->GetNetCode() );
503 zoneA->SetAssignedPriority( 50 );
504
505 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
506 VECTOR2I( 0, 0 ),
507 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
508 zoneB->SetNetCode( netB->GetNetCode() );
509 zoneB->SetAssignedPriority( 50 );
510
511 ZONE* ptrA = zoneA.get();
512 ZONE* ptrB = zoneB.get();
513 m_board.Add( zoneA.release() );
514 m_board.Add( zoneB.release() );
515
516 bool changed = AutoAssignZonePriorities( &m_board );
517
518 // Equal areas, no items: no ordering evidence, priorities must not change
519 BOOST_TEST( changed == false );
520 BOOST_TEST( ptrA->GetAssignedPriority() == 50u );
521 BOOST_TEST( ptrB->GetAssignedPriority() == 50u );
522}
523
524
525BOOST_AUTO_TEST_CASE( AutoPriority_SameNetGroupInheritsEdge )
526{
527 NETINFO_ITEM* netGND = new NETINFO_ITEM( &m_board, wxT( "GND" ) );
528 m_board.Add( netGND );
529 NETINFO_ITEM* netVCC = new NETINFO_ITEM( &m_board, wxT( "VCC" ) );
530 m_board.Add( netVCC );
531
532 // Large GND zone (A) overlaps with small VCC zone (C).
533 // Small GND zone (B) overlaps with A but NOT with C.
534 // A should beat C (more items), and B should inherit A's priority.
535 std::unique_ptr<ZONE> zoneA = CreateSquareZone( m_board, F_Cu,
536 VECTOR2I( 0, 0 ),
537 VECTOR2I( pcbIUScale.mmToIU( 40 ), pcbIUScale.mmToIU( 40 ) ) );
538 zoneA->SetNetCode( netGND->GetNetCode() );
539
540 std::unique_ptr<ZONE> zoneB = CreateSquareZone( m_board, F_Cu,
541 VECTOR2I( pcbIUScale.mmToIU( 25 ), pcbIUScale.mmToIU( 25 ) ),
542 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
543 zoneB->SetNetCode( netGND->GetNetCode() );
544
545 std::unique_ptr<ZONE> zoneC = CreateSquareZone( m_board, F_Cu,
546 VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ),
547 VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
548 zoneC->SetNetCode( netVCC->GetNetCode() );
549
550 ZONE* ptrA = zoneA.get();
551 ZONE* ptrB = zoneB.get();
552 ZONE* ptrC = zoneC.get();
553 m_board.Add( zoneA.release() );
554 m_board.Add( zoneB.release() );
555 m_board.Add( zoneC.release() );
556
557 // GND items in the A/C overlap region
558 for( int i = 0; i < 4; i++ )
559 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 8 + i * 2 ), pcbIUScale.mmToIU( 10 ) ), netGND->GetNetCode() );
560
561 AddVia( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 8 ) ), netVCC->GetNetCode() );
562
563 AutoAssignZonePriorities( &m_board );
564
565 // A beats C because GND has more items in the overlap
567
568 // B shares A's priority because they are same-net and overlap
570}
571
572
579BOOST_AUTO_TEST_CASE( RuleAreaCoverageAreaNotZero )
580{
581 std::unique_ptr<ZONE> ruleArea = CreateSquareZone( m_board, F_Cu,
582 VECTOR2I( 0, 0 ),
583 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
584 ruleArea->SetIsRuleArea( true );
585
586 double zoneArea = ruleArea->GetCoverageArea( 0 );
587
588 // The rule area covers a 20 mm x 20 mm region. Its coverage area must reflect that, not 0.
589 BOOST_TEST( zoneArea > 0.0 );
590
591 double expected = (double) pcbIUScale.mmToIU( 20 ) * pcbIUScale.mmToIU( 20 );
592 BOOST_TEST( zoneArea == expected, boost::test_tools::tolerance( 0.001 ) );
593
594 // A small pad enclosed by the rule area must read as much smaller, so the disambiguation
595 // heuristic in GuessSelectionCandidates() will prefer it over the enclosing rule area.
596 PAD* pad = AddPadToBoard( m_board, VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), 0, F_Cu );
597
598 double padArea = pad->GetCoverageArea( 0 );
599
600 BOOST_TEST( padArea > 0.0 );
601 BOOST_TEST( padArea < zoneArea );
602}
603
604
609BOOST_AUTO_TEST_CASE( FilledZoneCoverageUsesFilledPolygons )
610{
611 std::unique_ptr<ZONE> zone = CreateSquareZone( m_board, F_Cu,
612 VECTOR2I( 0, 0 ),
613 VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 20 ) ) );
614
615 // Fill only a small 2 mm x 2 mm island, far smaller than the 20 mm x 20 mm outline.
616 SHAPE_POLY_SET fill;
618 VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ) ) ) );
619 zone->SetFilledPolysList( F_Cu, fill );
620
621 double expected = (double) pcbIUScale.mmToIU( 2 ) * pcbIUScale.mmToIU( 2 );
622 BOOST_TEST( zone->GetCoverageArea( 0 ) == expected,
623 boost::test_tools::tolerance( 0.001 ) );
624}
625
626
632BOOST_AUTO_TEST_CASE( RuleAreaOutlineDrawnAboveCopper )
633{
634 const int copperPass = F_Cu;
635 const int zonePass = ZONE_LAYER_FOR( F_Cu );
636
637 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( true, zonePass ) );
638 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( true, copperPass ) );
639
640 BOOST_TEST( KIGFX::ZoneOutlineDrawnOnLayer( false, copperPass ) );
641 BOOST_TEST( !KIGFX::ZoneOutlineDrawnOnLayer( false, zonePass ) );
642}
643
644
648BOOST_AUTO_TEST_CASE( LibraryParityDetectsMovedCutout )
649{
650 const VECTOR2I cutoutSize( pcbIUScale.mmToIU( 1 ), pcbIUScale.mmToIU( 1 ) );
651 const BOX2I cutoutA( VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ), cutoutSize );
652 const BOX2I cutoutB( VECTOR2I( pcbIUScale.mmToIU( 6 ), pcbIUScale.mmToIU( 6 ) ), cutoutSize );
653
654 FOOTPRINT* fp = MakeFootprintWithRuleArea( m_board, { cutoutA } );
655 FOOTPRINT* same = MakeFootprintWithRuleArea( m_board, { cutoutA } );
656 FOOTPRINT* moved = MakeFootprintWithRuleArea( m_board, { cutoutB } );
657
658 BOOST_CHECK_MESSAGE( !fp->FootprintNeedsUpdate( same, BOARD_ITEM::COMPARE_FLAGS::DRC ),
659 "Identical rule areas must not report a mismatch" );
660
661 BOOST_CHECK_MESSAGE( fp->FootprintNeedsUpdate( moved, BOARD_ITEM::COMPARE_FLAGS::DRC ),
662 "A moved cutout with the same corner count must report a mismatch" );
663}
664
665
669BOOST_AUTO_TEST_CASE( LibraryParityDetectsCutoutCountChange )
670{
671 const VECTOR2I cutoutSize( pcbIUScale.mmToIU( 1 ), pcbIUScale.mmToIU( 1 ) );
672 const BOX2I cutout1( VECTOR2I( pcbIUScale.mmToIU( 2 ), pcbIUScale.mmToIU( 2 ) ), cutoutSize );
673 const BOX2I cutout2( VECTOR2I( pcbIUScale.mmToIU( 6 ), pcbIUScale.mmToIU( 6 ) ), cutoutSize );
674
675 FOOTPRINT* twoCutouts = MakeFootprintWithRuleArea( m_board, { cutout1, cutout2 } );
676 FOOTPRINT* oneCutout = MakeFootprintWithRuleArea( m_board, { cutout1 } );
677
678 // Match the vertex total of the two rectangles
679 ZONE* zone = oneCutout->Zones()[0];
680 SHAPE_LINE_CHAIN& hole = zone->Outline()->Hole( 0, 0 );
681
682 const int mid = pcbIUScale.mmToIU( 2 ) + pcbIUScale.mmToIU( 1 ) / 2;
683 hole.Insert( 1, VECTOR2I( mid, pcbIUScale.mmToIU( 2 ) ) );
684 hole.Insert( 3, VECTOR2I( mid, pcbIUScale.mmToIU( 3 ) ) );
685 hole.Insert( 5, VECTOR2I( pcbIUScale.mmToIU( 2 ), mid ) );
686 hole.Insert( 7, VECTOR2I( pcbIUScale.mmToIU( 3 ), mid ) );
687
688 BOOST_REQUIRE_EQUAL( twoCutouts->Zones()[0]->Outline()->TotalVertices(),
689 oneCutout->Zones()[0]->Outline()->TotalVertices() );
690
691 BOOST_CHECK_MESSAGE( twoCutouts->FootprintNeedsUpdate( oneCutout, BOARD_ITEM::COMPARE_FLAGS::DRC ),
692 "A changed cutout count with equal total vertices must report a mismatch" );
693}
694
695
699BOOST_AUTO_TEST_CASE( SimilarZoneGetsUniqueName )
700{
701 KIGFX::VIEW view;
702 TOOL_MANAGER toolMgr;
703 DRAWING_TOOL* drawingTool = new DRAWING_TOOL();
704
705 toolMgr.SetEnvironment( &m_board, &view, nullptr, nullptr, nullptr );
706 toolMgr.RegisterTool( drawingTool );
707
708 const VECTOR2I size( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) );
709
710 ZONE* named = CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), size ).release();
711 named->SetZoneName( wxS( "GND_POUR" ) );
712 m_board.Add( named );
713
714 ZONE* unnamed = CreateSquareZone( m_board, F_Cu, VECTOR2I( 0, 0 ), size ).release();
715 m_board.Add( unnamed );
716
718 ZONE_CREATE_HELPER helper( *drawingTool, params );
719
720 BOOST_CHECK_EQUAL( helper.createZoneFromExisting( *named )->GetZoneName(), wxS( "GND_POUR_1" ) );
721 BOOST_CHECK( helper.createZoneFromExisting( *unnamed )->GetZoneName().IsEmpty() );
722}
723
724
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:411
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1520
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
void SetPosition(const VECTOR2I &aPos) override
ZONES & Zones()
Definition footprint.h:361
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.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
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
Master controller class:
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
An adjunct helper to the DRAWING_TOOL interactive tool, which handles incoming geometry changes from ...
std::unique_ptr< ZONE > createZoneFromExisting(const ZONE &aSrcZone)
Create a new zone with the settings from an existing zone.
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:608
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:675
SHAPE_POLY_SET * Outline()
Definition zone.h:421
void SetIsRuleArea(bool aEnable)
Definition zone.h:833
const wxString & GetZoneName() const
Definition zone.h:160
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:700
VECTOR2I GetPosition() const override
Definition zone.cpp:599
bool IsOnCopperLayer() const override
Definition zone.cpp:650
void SetZoneName(const wxString &aName)
Definition zone.h:161
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:630
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:617
@ DIRECT
Unconstrained point-to-point.
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:393
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.
@ SIMILAR
Add a new zone with the same settings as an existing one.
Definition pcb_actions.h:37
Utility functions for working with shapes.
Parameters used to fully describe a zone creation process.
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
BOOST_CHECK_EQUAL(result, "25.4")
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:47
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:65
static FOOTPRINT * MakeFootprintWithRuleArea(BOARD &aBoard, const std::vector< BOX2I > &aCutouts)
Definition test_zone.cpp:77
BOOST_AUTO_TEST_CASE(SingleLayer)
Definition test_zone.cpp:98
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.