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