KiCad PCB EDA Suite
Loading...
Searching...
No Matches
zone_utils.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 3
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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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
24#include "zone_utils.h"
25
26#include <board.h>
27#include <footprint.h>
28#include <pad.h>
29#include <pcb_track.h>
30#include <thread_pool.h>
31#include <zone.h>
33
34#include <algorithm>
35#include <cmath>
36#include <future>
37#include <optional>
38#include <unordered_map>
39#include <unordered_set>
40
41
42static bool RuleAreasHaveSameProps( const ZONE& a, const ZONE& b )
43{
44 // This function is only used to compare rule areas, so we can assume that both a and b are rule areas
45 wxASSERT( a.GetIsRuleArea() && b.GetIsRuleArea() );
46
52}
53
54
55std::vector<std::unique_ptr<ZONE>> MergeZonesWithSameOutline( std::vector<std::unique_ptr<ZONE>>&& aZones )
56{
57 const auto polygonsAreMergeable = []( const SHAPE_POLY_SET::POLYGON& a, const SHAPE_POLY_SET::POLYGON& b ) -> bool
58 {
59 if( a.size() != b.size() )
60 return false;
61
62 // NOTE: this assumes the polygons have their line chains in the same order
63 // But that is not actually required for same geometry (i.e. mergeability)
64 for( size_t lineChainId = 0; lineChainId < a.size(); lineChainId++ )
65 {
66 const SHAPE_LINE_CHAIN& chainA = a[lineChainId];
67 const SHAPE_LINE_CHAIN& chainB = b[lineChainId];
68
69 // Note: this assumes the polygons are either already simplified or that it's
70 // OK to not merge even if they would be the same after simplification.
71 if( chainA.PointCount() != chainB.PointCount() || chainA.BBox() != chainB.BBox()
72 || !chainA.CompareGeometry( chainB ) )
73 {
74 // Different geometry, can't merge
75 return false;
76 }
77 }
78
79 return true;
80 };
81
82 const auto zonesAreMergeable = [&]( const ZONE& a, const ZONE& b ) -> bool
83 {
84 // Can't merge rule areas with zone fills
85 if( a.GetIsRuleArea() != b.GetIsRuleArea() )
86 return false;
87
88 if( a.GetIsRuleArea() )
89 {
90 if( !RuleAreasHaveSameProps( a, b ) )
91 return false;
92 }
93 else
94 {
95 // We could also check clearances and so on
96 if( a.GetNetCode() != b.GetNetCode() )
97 return false;
98 }
99
100 const SHAPE_POLY_SET* polySetA = a.Outline();
101 const SHAPE_POLY_SET* polySetB = b.Outline();
102
103 if( polySetA->OutlineCount() != polySetB->OutlineCount() )
104 return false;
105
106 if( polySetA->OutlineCount() == 0 )
107 {
108 // both have no outline, so they are the same, but we must not
109 // derefence them, as they are empty
110 return true;
111 }
112
113 // REVIEW: this assumes the zones only have a single polygon in the
114 const SHAPE_POLY_SET::POLYGON& polyA = polySetA->CPolygon( 0 );
115 const SHAPE_POLY_SET::POLYGON& polyB = polySetB->CPolygon( 0 );
116
117 return polygonsAreMergeable( polyA, polyB );
118 };
119
120 std::vector<std::unique_ptr<ZONE>> deduplicatedZones;
121
122 // Map of zone indexes that we have already merged into a prior zone
123 std::vector<bool> merged( aZones.size(), false );
124
125 for( size_t i = 0; i < aZones.size(); i++ )
126 {
127 // This one has already been subsumed into a prior zone, so skip it
128 // and it will be dropped at the end.
129 if( merged[i] )
130 continue;
131
132 ZONE& primary = *aZones[i];
133 LSET layers = primary.GetLayerSet();
134 std::unordered_map<PCB_LAYER_ID, SHAPE_POLY_SET> mergedFills;
135
136 for( size_t j = i + 1; j < aZones.size(); j++ )
137 {
138 // This zone has already been subsumed by a prior zone, so it
139 // cannot be merged into another primary
140 if( merged[j] )
141 continue;
142
143 ZONE& candidate = *aZones[j];
144 bool canMerge = zonesAreMergeable( primary, candidate );
145
146 if( canMerge )
147 {
148 for( PCB_LAYER_ID layer : candidate.GetLayerSet() )
149 {
150 if( SHAPE_POLY_SET* fill = candidate.GetFill( layer ) )
151 mergedFills[layer] = *fill;
152 }
153
154 layers |= candidate.GetLayerSet();
155 merged[j] = true;
156 }
157 }
158
159 if( layers != primary.GetLayerSet() )
160 {
161 for( PCB_LAYER_ID layer : primary.GetLayerSet() )
162 {
163 if( SHAPE_POLY_SET* fill = primary.GetFill( layer ) )
164 mergedFills[layer] = *fill;
165 }
166
167 primary.SetLayerSet( layers );
168
169 for( const auto& [layer, fill] : mergedFills )
170 primary.SetFilledPolysList( layer, fill );
171
172 primary.SetNeedRefill( false );
173 primary.SetIsFilled( true );
174 }
175
176 // Keep this zone - it's a primary (may or may not have had other zones merged into it)
177 deduplicatedZones.push_back( std::move( aZones[i] ) );
178 }
179
180 return deduplicatedZones;
181}
182
183
184namespace
185{
186
187struct ZONE_OVERLAP_PAIR
188{
189 ZONE* zoneA;
190 ZONE* zoneB;
191 LSET sharedLayers;
192};
193
194
195struct ZONE_PRIORITY_EDGE
196{
197 ZONE* higher;
198 ZONE* lower;
199 int countDiff;
200 bool fromArea;
201};
202
203} // namespace
204
205
206static std::vector<ZONE_OVERLAP_PAIR> findOverlappingPairs( BOARD* aBoard )
207{
208 std::vector<ZONE_OVERLAP_PAIR> pairs;
209 const ZONES& zones = aBoard->Zones();
210
211 for( size_t i = 0; i < zones.size(); i++ )
212 {
213 ZONE* a = zones[i];
214
215 if( a->GetIsRuleArea() || a->IsTeardropArea() || !a->IsOnCopperLayer() )
216 continue;
217
218 BOX2I bboxA = a->GetBoundingBox();
219
220 for( size_t j = i + 1; j < zones.size(); j++ )
221 {
222 ZONE* b = zones[j];
223
224 if( b->GetIsRuleArea() || b->IsTeardropArea() || !b->IsOnCopperLayer() )
225 continue;
226
227 LSET shared = a->GetLayerSet() & b->GetLayerSet();
228 shared &= LSET::AllCuMask();
229
230 if( shared.none() )
231 continue;
232
233 if( !b->GetBoundingBox().Intersects( bboxA ) )
234 continue;
235
236 bool overlaps = a->Outline()->Collide( b->Outline() )
237 || ( b->Outline()->TotalVertices() > 0
238 && a->Outline()->Contains( b->Outline()->CVertex( 0 ) ) )
239 || ( a->Outline()->TotalVertices() > 0
240 && b->Outline()->Contains( a->Outline()->CVertex( 0 ) ) );
241
242 if( overlaps )
243 pairs.push_back( { a, b, shared } );
244 }
245 }
246
247 return pairs;
248}
249
250
251static std::optional<ZONE_PRIORITY_EDGE> computeConstraint( const ZONE_OVERLAP_PAIR& aPair,
252 BOARD* aBoard )
253{
254 SHAPE_POLY_SET polyA = aPair.zoneA->Outline()->CloneDropTriangulation();
255 SHAPE_POLY_SET polyB = aPair.zoneB->Outline()->CloneDropTriangulation();
256 polyA.ClearArcs();
257 polyB.ClearArcs();
258
259 SHAPE_POLY_SET intersection;
260 intersection.BooleanIntersection( polyA, polyB );
261
262 if( intersection.IsEmpty() )
263 return std::nullopt;
264
265 intersection.BuildBBoxCaches();
266
267 int netCodeA = aPair.zoneA->GetNetCode();
268 int netCodeB = aPair.zoneB->GetNetCode();
269
270 // Same-net overlapping zones are cooperative, not competitive. Priority
271 // between them is meaningless to the fill engine. Return no constraint
272 // here; AutoAssignZonePriorities() groups them to the same priority level.
273 if( netCodeA == netCodeB )
274 return std::nullopt;
275
276 int countA = 0;
277 int countB = 0;
278
279 auto countIfInOverlap = [&]( const VECTOR2I& aPos, int aNetCode, PCB_LAYER_ID aLayer )
280 {
281 if( !aPair.sharedLayers.test( aLayer ) )
282 return;
283
284 if( intersection.Contains( aPos ) )
285 {
286 if( aNetCode == netCodeA )
287 countA++;
288 else if( aNetCode == netCodeB )
289 countB++;
290 }
291 };
292
293 for( FOOTPRINT* fp : aBoard->Footprints() )
294 {
295 for( PAD* pad : fp->Pads() )
296 {
297 for( PCB_LAYER_ID layer : aPair.sharedLayers.Seq() )
298 {
299 if( pad->IsOnLayer( layer ) )
300 {
301 countIfInOverlap( pad->GetPosition(), pad->GetNetCode(), layer );
302 break;
303 }
304 }
305 }
306 }
307
308 for( PCB_TRACK* track : aBoard->Tracks() )
309 {
310 if( track->Type() != PCB_VIA_T )
311 continue;
312
313 PCB_VIA* via = static_cast<PCB_VIA*>( track );
314
315 for( PCB_LAYER_ID layer : aPair.sharedLayers.Seq() )
316 {
317 if( via->IsOnLayer( layer ) )
318 {
319 countIfInOverlap( via->GetPosition(), via->GetNetCode(), layer );
320 break;
321 }
322 }
323 }
324
325 if( countA == 0 && countB == 0 )
326 {
327 double areaA = aPair.zoneA->Outline()->Area();
328 double areaB = aPair.zoneB->Outline()->Area();
329
330 if( areaA == areaB )
331 return std::nullopt;
332
333 ZONE* higher = ( areaA < areaB ) ? aPair.zoneA : aPair.zoneB;
334 ZONE* lower = ( higher == aPair.zoneA ) ? aPair.zoneB : aPair.zoneA;
335 return ZONE_PRIORITY_EDGE{ higher, lower, 0, true };
336 }
337
338 int maxCount = std::max( countA, countB );
339 int diff = std::abs( countA - countB );
340 double ratio = static_cast<double>( diff ) / maxCount;
341
342 constexpr double SIMILARITY_THRESHOLD = 0.20;
343
344 if( ratio < SIMILARITY_THRESHOLD )
345 {
346 double areaA = aPair.zoneA->Outline()->Area();
347 double areaB = aPair.zoneB->Outline()->Area();
348
349 if( areaA == areaB )
350 return std::nullopt;
351
352 ZONE* higher = ( areaA < areaB ) ? aPair.zoneA : aPair.zoneB;
353 ZONE* lower = ( higher == aPair.zoneA ) ? aPair.zoneB : aPair.zoneA;
354 return ZONE_PRIORITY_EDGE{ higher, lower, diff, true };
355 }
356
357 ZONE* higher = ( countA > countB ) ? aPair.zoneA : aPair.zoneB;
358 ZONE* lower = ( higher == aPair.zoneA ) ? aPair.zoneB : aPair.zoneA;
359 return ZONE_PRIORITY_EDGE{ higher, lower, diff, false };
360}
361
362
363static void assignPrioritiesFromGraph( const std::vector<ZONE_PRIORITY_EDGE>& aEdges,
364 std::vector<ZONE*>& aAllZones )
365{
366 std::unordered_map<ZONE*, std::vector<ZONE*>> adj;
367 std::unordered_map<ZONE*, int> inDegree;
368 std::unordered_set<ZONE*> inGraph;
369
370 for( ZONE* z : aAllZones )
371 {
372 inDegree[z] = 0;
373 inGraph.insert( z );
374 }
375
376 // Sort edges so area-based (weakest) come first, then by ascending countDiff
377 std::vector<ZONE_PRIORITY_EDGE> sortedEdges = aEdges;
378
379 std::sort( sortedEdges.begin(), sortedEdges.end(),
380 []( const ZONE_PRIORITY_EDGE& a, const ZONE_PRIORITY_EDGE& b )
381 {
382 if( a.fromArea != b.fromArea )
383 return a.fromArea;
384
385 return a.countDiff < b.countDiff;
386 } );
387
388 for( const ZONE_PRIORITY_EDGE& edge : sortedEdges )
389 {
390 adj[edge.higher].push_back( edge.lower );
391 inDegree[edge.lower]++;
392 }
393
394 // Kahn's algorithm: sources (in-degree 0) have nothing constraining them to be lower,
395 // so they are the highest-priority zones. Process them first.
396 std::vector<ZONE*> queue;
397
398 for( ZONE* z : aAllZones )
399 {
400 if( inDegree[z] == 0 )
401 queue.push_back( z );
402 }
403
404 std::sort( queue.begin(), queue.end(),
405 []( const ZONE* a, const ZONE* b )
406 {
407 return a->GetAssignedPriority() < b->GetAssignedPriority();
408 } );
409
410 std::vector<ZONE*> topoOrder;
411 topoOrder.reserve( aAllZones.size() );
412
413 while( !queue.empty() )
414 {
415 ZONE* current = queue.front();
416 queue.erase( queue.begin() );
417 topoOrder.push_back( current );
418
419 auto& neighbors = adj[current];
420
421 std::sort( neighbors.begin(), neighbors.end(),
422 []( const ZONE* a, const ZONE* b )
423 {
424 return a->GetAssignedPriority() < b->GetAssignedPriority();
425 } );
426
427 for( ZONE* neighbor : neighbors )
428 {
429 inDegree[neighbor]--;
430
431 if( inDegree[neighbor] == 0 )
432 queue.push_back( neighbor );
433 }
434
435 std::sort( queue.begin(), queue.end(),
436 []( const ZONE* a, const ZONE* b )
437 {
438 return a->GetAssignedPriority() < b->GetAssignedPriority();
439 } );
440 }
441
442 // Zones stuck in cycles get appended sorted by their current priority
443 if( topoOrder.size() < aAllZones.size() )
444 {
445 std::unordered_set<ZONE*> ordered( topoOrder.begin(), topoOrder.end() );
446 std::vector<ZONE*> remaining;
447
448 for( ZONE* z : aAllZones )
449 {
450 if( ordered.find( z ) == ordered.end() )
451 remaining.push_back( z );
452 }
453
454 std::sort( remaining.begin(), remaining.end(),
455 []( const ZONE* a, const ZONE* b )
456 {
457 return a->GetAssignedPriority() < b->GetAssignedPriority();
458 } );
459
460 for( ZONE* z : remaining )
461 topoOrder.push_back( z );
462 }
463
464 // topoOrder[0] is the highest-priority zone (source node). Assign descending values.
465 for( size_t i = 0; i < topoOrder.size(); i++ )
466 topoOrder[i]->SetAssignedPriority( static_cast<unsigned>( topoOrder.size() - 1 - i ) );
467}
468
469
470static ZONE* ufFind( std::unordered_map<ZONE*, ZONE*>& aParent, ZONE* aZone )
471{
472 ZONE*& parent = aParent[aZone];
473
474 if( parent != aZone )
475 parent = ufFind( aParent, parent );
476
477 return parent;
478}
479
480
481static void ufUnion( std::unordered_map<ZONE*, ZONE*>& aParent,
482 std::unordered_map<ZONE*, int>& aRank,
483 ZONE* aA, ZONE* aB )
484{
485 ZONE* rootA = ufFind( aParent, aA );
486 ZONE* rootB = ufFind( aParent, aB );
487
488 if( rootA == rootB )
489 return;
490
491 if( aRank[rootA] < aRank[rootB] )
492 std::swap( rootA, rootB );
493
494 aParent[rootB] = rootA;
495
496 if( aRank[rootA] == aRank[rootB] )
497 aRank[rootA]++;
498}
499
500
502{
503 std::vector<ZONE*> eligibleZones;
504
505 for( ZONE* zone : aBoard->Zones() )
506 {
507 if( !zone->GetIsRuleArea() && !zone->IsTeardropArea() && zone->IsOnCopperLayer() )
508 eligibleZones.push_back( zone );
509 }
510
511 if( eligibleZones.size() < 2 )
512 return false;
513
514 std::unordered_map<ZONE*, unsigned> originalPriorities;
515
516 for( ZONE* z : eligibleZones )
517 originalPriorities[z] = z->GetAssignedPriority();
518
519 std::vector<ZONE_OVERLAP_PAIR> pairs = findOverlappingPairs( aBoard );
520
521 if( pairs.empty() )
522 return false;
523
524 // Build equivalence classes for same-net overlapping zones. These zones
525 // are cooperative and must share the same priority after assignment.
526 std::unordered_map<ZONE*, ZONE*> ufParent;
527 std::unordered_map<ZONE*, int> ufRank;
528
529 for( ZONE* z : eligibleZones )
530 {
531 ufParent[z] = z;
532 ufRank[z] = 0;
533 }
534
535 for( const ZONE_OVERLAP_PAIR& pair : pairs )
536 {
537 if( pair.zoneA->GetNetCode() == pair.zoneB->GetNetCode() )
538 ufUnion( ufParent, ufRank, pair.zoneA, pair.zoneB );
539 }
540
542 std::vector<std::future<std::optional<ZONE_PRIORITY_EDGE>>> futures;
543 futures.reserve( pairs.size() );
544
545 for( const ZONE_OVERLAP_PAIR& pair : pairs )
546 {
547 if( pair.zoneA->GetNetCode() == pair.zoneB->GetNetCode() )
548 continue;
549
550 futures.emplace_back( tp.submit_task(
551 [&pair, aBoard]()
552 {
553 return computeConstraint( pair, aBoard );
554 } ) );
555 }
556
557 std::vector<ZONE_PRIORITY_EDGE> edges;
558
559 for( auto& future : futures )
560 {
561 std::optional<ZONE_PRIORITY_EDGE> result = future.get();
562
563 if( result.has_value() )
564 edges.push_back( result.value() );
565 }
566
567 if( !edges.empty() )
568 assignPrioritiesFromGraph( edges, eligibleZones );
569
570 // Equalize priorities within each same-net equivalence class. Each group
571 // gets the maximum priority of any member so ordering constraints from
572 // different-net edges propagate to the whole group.
573 std::unordered_map<ZONE*, unsigned> groupMax;
574
575 for( ZONE* z : eligibleZones )
576 {
577 ZONE* root = ufFind( ufParent, z );
578 unsigned pri = z->GetAssignedPriority();
579 auto& maxPri = groupMax[root];
580
581 if( pri > maxPri )
582 maxPri = pri;
583 }
584
585 for( ZONE* z : eligibleZones )
586 {
587 ZONE* root = ufFind( ufParent, z );
588 z->SetAssignedPriority( groupMax[root] );
589 }
590
591 for( ZONE* z : eligibleZones )
592 {
593 if( z->GetAssignedPriority() != originalPriorities[z] )
594 return true;
595 }
596
597 return false;
598}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const ZONES & Zones() const
Definition board.h:368
const FOOTPRINTS & Footprints() const
Definition board.h:364
const TRACKS & Tracks() const
Definition board.h:362
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
Definition pad.h:55
A progress reporter interface for use in multi-threaded environments.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
bool CompareGeometry(const SHAPE_LINE_CHAIN &aOther, bool aCyclicalCompare=false, int aEpsilon=0) const
Compare this line chain with another one.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
double Area()
Return the area of this poly set.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
SHAPE_POLY_SET CloneDropTriangulation() const
const POLYGON & CPolygon(int aIndex) const
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:297
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:716
bool GetDoNotAllowVias() const
Definition zone.h:727
bool GetDoNotAllowPads() const
Definition zone.h:729
const BOX2I GetBoundingBox() const override
Definition zone.cpp:651
bool GetDoNotAllowTracks() const
Definition zone.h:728
SHAPE_POLY_SET * Outline()
Definition zone.h:336
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:609
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:631
void SetIsFilled(bool isFilled)
Definition zone.h:294
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:556
bool IsTeardropArea() const
Definition zone.h:691
bool GetDoNotAllowFootprints() const
Definition zone.h:730
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
bool GetDoNotAllowZoneFills() const
Definition zone.h:726
bool IsOnCopperLayer() const override
Definition zone.cpp:543
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
std::vector< ZONE * > ZONES
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
static void assignPrioritiesFromGraph(const std::vector< ZONE_PRIORITY_EDGE > &aEdges, std::vector< ZONE * > &aAllZones)
static ZONE * ufFind(std::unordered_map< ZONE *, ZONE * > &aParent, ZONE *aZone)
static bool RuleAreasHaveSameProps(const ZONE &a, const ZONE &b)
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.
static void ufUnion(std::unordered_map< ZONE *, ZONE * > &aParent, std::unordered_map< ZONE *, int > &aRank, ZONE *aA, ZONE *aB)
static std::vector< ZONE_OVERLAP_PAIR > findOverlappingPairs(BOARD *aBoard)
bool AutoAssignZonePriorities(BOARD *aBoard, PROGRESS_REPORTER *aReporter)
Automatically assign zone priorities based on connectivity analysis of overlapping regions.
static std::optional< ZONE_PRIORITY_EDGE > computeConstraint(const ZONE_OVERLAP_PAIR &aPair, BOARD *aBoard)