KiCad PCB EDA Suite
zone_filler.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 (C) 2014-2017 CERN
5  * Copyright (C) 2014-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Tomasz W┼éostowski <[email protected]>
7  *
8  * This program is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation, either version 3 of the License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <thread>
27 #include <future>
28 #include <core/kicad_algo.h>
29 #include <advanced_config.h>
30 #include <board.h>
31 #include <board_design_settings.h>
32 #include <zone.h>
33 #include <footprint.h>
34 #include <pad.h>
35 #include <pcb_shape.h>
36 #include <pcb_target.h>
37 #include <pcb_track.h>
40 #include <board_commit.h>
41 #include <progress_reporter.h>
43 #include <geometry/convex_hull.h>
45 #include <confirm.h>
46 #include <convert_to_biu.h>
47 #include <math/util.h> // for KiROUND
48 #include "zone_filler.h"
49 
50 static const double s_RoundPadThermalSpokeAngle = 450; // in deci-degrees
51 
52 
53 ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) :
54  m_board( aBoard ),
55  m_brdOutlinesValid( false ),
56  m_commit( aCommit ),
57  m_progressReporter( nullptr ),
58  m_maxError( ARC_HIGH_DEF ),
59  m_worstClearance( 0 )
60 {
61  // To enable add "DebugZoneFiller=1" to kicad_advanced settings file.
63 }
64 
65 
67 {
68 }
69 
70 
72 {
73  m_progressReporter = aReporter;
74  wxASSERT_MSG( m_commit, wxT( "ZONE_FILLER must have a valid commit to call "
75  "SetProgressReporter" ) );
76 }
77 
78 
79 bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aParent )
80 {
81  std::lock_guard<KISPINLOCK> lock( m_board->GetConnectivity()->GetLock() );
82 
83  std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
84  std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> islandsList;
85 
86  std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
87 
88  // Rebuild just in case. This really needs to be reliable.
89  connectivity->Clear();
90  connectivity->Build( m_board, m_progressReporter );
91 
93 
95 
96  if( m_progressReporter )
97  {
98  m_progressReporter->Report( aCheck ? _( "Checking zone fills..." )
99  : _( "Building zone fills..." ) );
100  m_progressReporter->SetMaxProgress( aZones.size() );
102  }
103 
104  // The board outlines is used to clip solid areas inside the board (when outlines are valid)
107 
108  // Update and cache zone bounding boxes and pad effective shapes so that we don't have to
109  // make them thread-safe.
110  for( ZONE* zone : m_board->Zones() )
111  {
112  zone->CacheBoundingBox();
113  m_worstClearance = std::max( m_worstClearance, zone->GetLocalClearance() );
114  }
115 
116  for( FOOTPRINT* footprint : m_board->Footprints() )
117  {
118  for( PAD* pad : footprint->Pads() )
119  {
120  if( pad->IsDirty() )
121  {
122  pad->BuildEffectiveShapes( UNDEFINED_LAYER );
123  pad->BuildEffectivePolygon();
124  }
125 
126  m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() );
127  }
128 
129  for( ZONE* zone : footprint->Zones() )
130  {
131  zone->CacheBoundingBox();
132  m_worstClearance = std::max( m_worstClearance, zone->GetLocalClearance() );
133  }
134 
135  // Rules may depend on insideCourtyard() or other expressions
136  footprint->BuildPolyCourtyards();
137  }
138 
139  // Sort by priority to reduce deferrals waiting on higher priority zones.
140  std::sort( aZones.begin(), aZones.end(),
141  []( const ZONE* lhs, const ZONE* rhs )
142  {
143  return lhs->GetPriority() > rhs->GetPriority();
144  } );
145 
146  for( ZONE* zone : aZones )
147  {
148  // Rule areas are not filled
149  if( zone->GetIsRuleArea() )
150  continue;
151 
152  if( m_commit )
153  m_commit->Modify( zone );
154 
155  // calculate the hash value for filled areas. it will be used later
156  // to know if the current filled areas are up to date
157  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
158  {
159  zone->BuildHashValue( layer );
160 
161  // Add the zone to the list of zones to test or refill
162  toFill.emplace_back( std::make_pair( zone, layer ) );
163  }
164 
165  islandsList.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone ) );
166 
167  // Remove existing fill first to prevent drawing invalid polygons
168  // on some platforms
169  zone->UnFill();
170 
171  zone->SetFillVersion( bds.m_ZoneFillVersion );
172  }
173 
174  size_t cores = std::thread::hardware_concurrency();
175  std::atomic<size_t> nextItem;
176 
177  auto check_fill_dependency =
178  [&]( ZONE* aZone, PCB_LAYER_ID aLayer, ZONE* aOtherZone ) -> bool
179  {
180  // Check to see if we have to knock-out the filled areas of a higher-priority
181  // zone. If so we have to wait until said zone is filled before we can fill.
182 
183  // If the other zone is already filled then we're good-to-go
184  if( aOtherZone->GetFillFlag( aLayer ) )
185  return false;
186 
187  // Even if keepouts exclude copper pours the exclusion is by outline, not by
188  // filled area, so we're good-to-go here too.
189  if( aOtherZone->GetIsRuleArea() )
190  return false;
191 
192  // If the zones share no common layers
193  if( !aOtherZone->GetLayerSet().test( aLayer ) )
194  return false;
195 
196  if( aOtherZone->GetPriority() <= aZone->GetPriority() )
197  return false;
198 
199  // Same-net zones always use outline to produce predictable results
200  if( aOtherZone->GetNetCode() == aZone->GetNetCode() )
201  return false;
202 
203  // A higher priority zone is found: if we intersect and it's not filled yet
204  // then we have to wait.
205  EDA_RECT inflatedBBox = aZone->GetCachedBoundingBox();
206  inflatedBBox.Inflate( m_worstClearance );
207 
208  return inflatedBBox.Intersects( aOtherZone->GetCachedBoundingBox() );
209  };
210 
211  auto fill_lambda =
212  [&]( PROGRESS_REPORTER* aReporter )
213  {
214  size_t num = 0;
215 
216  for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
217  {
218  PCB_LAYER_ID layer = toFill[i].second;
219  ZONE* zone = toFill[i].first;
220  bool canFill = true;
221 
222  // Check for any fill dependencies. If our zone needs to be clipped by
223  // another zone then we can't fill until that zone is filled.
224  for( ZONE* otherZone : aZones )
225  {
226  if( otherZone == zone )
227  continue;
228 
229  if( check_fill_dependency( zone, layer, otherZone ) )
230  {
231  canFill = false;
232  break;
233  }
234  }
235 
237  break;
238 
239  if( !canFill )
240  continue;
241 
242  // Now we're ready to fill.
243  SHAPE_POLY_SET rawPolys, finalPolys;
244  fillSingleZone( zone, layer, rawPolys, finalPolys );
245 
246  std::unique_lock<std::mutex> zoneLock( zone->GetLock() );
247 
248  zone->SetRawPolysList( layer, rawPolys );
249  zone->SetFilledPolysList( layer, finalPolys );
250  zone->SetFillFlag( layer, true );
251 
252  if( m_progressReporter )
254 
255  num++;
256  }
257 
258  return num;
259  };
260 
261  while( !toFill.empty() )
262  {
263  size_t parallelThreadCount = std::min( cores, toFill.size() );
264  std::vector<std::future<size_t>> returns( parallelThreadCount );
265 
266  nextItem = 0;
267 
268  if( parallelThreadCount <= 1 )
269  fill_lambda( m_progressReporter );
270  else
271  {
272  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
273  returns[ii] = std::async( std::launch::async, fill_lambda, m_progressReporter );
274 
275  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
276  {
277  // Here we balance returns with a 100ms timeout to allow UI updating
278  std::future_status status;
279  do
280  {
281  if( m_progressReporter )
283 
284  status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
285  } while( status != std::future_status::ready );
286  }
287  }
288 
289  alg::delete_if( toFill, [&]( const std::pair<ZONE*, PCB_LAYER_ID> pair ) -> bool
290  {
291  return pair.first->GetFillFlag( pair.second );
292  } );
293 
295  break;
296  }
297 
298  // Now update the connectivity to check for copper islands
299  if( m_progressReporter )
300  {
302  return false;
303 
305  m_progressReporter->Report( _( "Removing isolated copper islands..." ) );
307  }
308 
309  connectivity->SetProgressReporter( m_progressReporter );
310  connectivity->FindIsolatedCopperIslands( islandsList );
311  connectivity->SetProgressReporter( nullptr );
312 
314  return false;
315 
316  for( ZONE* zone : aZones )
317  {
318  // Keepout zones are not filled
319  if( zone->GetIsRuleArea() )
320  continue;
321 
322  zone->SetIsFilled( true );
323  }
324 
325  // Now remove insulated copper islands
326  for( CN_ZONE_ISOLATED_ISLAND_LIST& zone : islandsList )
327  {
328  for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
329  {
331  continue;
332 
333  if( !zone.m_islands.count( layer ) )
334  continue;
335 
336  std::vector<int>& islands = zone.m_islands.at( layer );
337 
338  // The list of polygons to delete must be explored from last to first in list,
339  // to allow deleting a polygon from list without breaking the remaining of the list
340  std::sort( islands.begin(), islands.end(), std::greater<int>() );
341 
342  SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList( layer );
343  long long int minArea = zone.m_zone->GetMinIslandArea();
344  ISLAND_REMOVAL_MODE mode = zone.m_zone->GetIslandRemovalMode();
345 
346  for( int idx : islands )
347  {
348  SHAPE_LINE_CHAIN& outline = poly.Outline( idx );
349 
350  if( mode == ISLAND_REMOVAL_MODE::ALWAYS )
351  poly.DeletePolygon( idx );
352  else if ( mode == ISLAND_REMOVAL_MODE::AREA && outline.Area() < minArea )
353  poly.DeletePolygon( idx );
354  else
355  zone.m_zone->SetIsIsland( layer, idx );
356  }
357 
358  zone.m_zone->SetFilledPolysList( layer, poly );
359  zone.m_zone->CalculateFilledArea();
360 
362  return false;
363  }
364  }
365 
366  // Now remove islands which are either outside the board edge or fail to meet the minimum
367  // area requirements
368  for( ZONE* zone : aZones )
369  {
370  LSET zoneCopperLayers = zone->GetLayerSet() & LSET::AllCuMask( MAX_CU_LAYERS );
371 
372  // Min-thickness is the web thickness. On the other hand, a blob min-thickness by
373  // min-thickness is not useful. Since there's no obvious definition of web vs. blob, we
374  // arbitrarily choose "at least 2X the area".
375  double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 2;
376 
377  for( PCB_LAYER_ID layer : zoneCopperLayers.Seq() )
378  {
380  continue;
381 
382  SHAPE_POLY_SET poly = zone->GetFilledPolysList( layer );
383 
384  for( int ii = poly.OutlineCount() - 1; ii >= 0; ii-- )
385  {
386  std::vector<SHAPE_LINE_CHAIN>& island = poly.Polygon( ii );
387 
388  if( island.empty()
389  || !m_boardOutline.Contains( island.front().CPoint( 0 ) )
390  || island.front().Area() < minArea )
391  {
392  poly.DeletePolygon( ii );
393  }
394  }
395 
396  zone->SetFilledPolysList( layer, poly );
397  zone->CalculateFilledArea();
398 
400  return false;
401  }
402  }
403 
404  if( aCheck )
405  {
406  bool outOfDate = false;
407 
408  for( ZONE* zone : aZones )
409  {
410  // Keepout zones are not filled
411  if( zone->GetIsRuleArea() )
412  continue;
413 
414  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
415  {
416  MD5_HASH was = zone->GetHashValue( layer );
417  zone->CacheTriangulation( layer );
418  zone->BuildHashValue( layer );
419  MD5_HASH is = zone->GetHashValue( layer );
420 
421  if( is != was )
422  outOfDate = true;
423  }
424  }
425 
426  if( outOfDate )
427  {
428  KIDIALOG dlg( aParent, _( "Zone fills are out-of-date. Refill?" ),
429  _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
430  dlg.SetOKCancelLabels( _( "Refill" ), _( "Continue without Refill" ) );
431  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
432 
433  if( dlg.ShowModal() == wxID_CANCEL )
434  return false;
435  }
436  else
437  {
438  // No need to commit something that hasn't changed (and committing will set
439  // the modified flag).
440  return false;
441  }
442  }
443 
444  if( m_progressReporter )
445  {
447  m_progressReporter->Report( _( "Performing polygon fills..." ) );
448  m_progressReporter->SetMaxProgress( islandsList.size() );
449  }
450 
451  nextItem = 0;
452 
453  auto tri_lambda =
454  [&]( PROGRESS_REPORTER* aReporter ) -> size_t
455  {
456  size_t num = 0;
457 
458  for( size_t i = nextItem++; i < islandsList.size(); i = nextItem++ )
459  {
460  islandsList[i].m_zone->CacheTriangulation();
461  num++;
462 
463  if( m_progressReporter )
464  {
466 
468  break;
469  }
470  }
471 
472  return num;
473  };
474 
475  size_t parallelThreadCount = std::min( cores, islandsList.size() );
476  std::vector<std::future<size_t>> returns( parallelThreadCount );
477 
478  if( parallelThreadCount <= 1 )
479  tri_lambda( m_progressReporter );
480  else
481  {
482  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
483  returns[ii] = std::async( std::launch::async, tri_lambda, m_progressReporter );
484 
485  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
486  {
487  // Here we balance returns with a 100ms timeout to allow UI updating
488  std::future_status status;
489  do
490  {
491  if( m_progressReporter )
492  {
494 
496  break;
497  }
498 
499  status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
500  } while( status != std::future_status::ready );
501  }
502  }
503 
504  if( m_progressReporter )
505  {
507  return false;
508 
511  }
512 
513  return true;
514 }
515 
516 
520 bool hasThermalConnection( PAD* pad, const ZONE* aZone )
521 {
522  // Rejects non-standard pads with tht-only thermal reliefs
524  && pad->GetAttribute() != PAD_ATTRIB::PTH )
525  {
526  return false;
527  }
528 
531  {
532  return false;
533  }
534 
535  if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
536  return false;
537 
538  EDA_RECT item_boundingbox = pad->GetBoundingBox();
539  int thermalGap = aZone->GetThermalReliefGap( pad );
540  item_boundingbox.Inflate( thermalGap, thermalGap );
541 
542  return item_boundingbox.Intersects( aZone->GetCachedBoundingBox() );
543 }
544 
545 
550 void ZONE_FILLER::addKnockout( PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_POLY_SET& aHoles )
551 {
552  if( aPad->GetShape() == PAD_SHAPE::CUSTOM )
553  {
554  SHAPE_POLY_SET poly;
555  aPad->TransformShapeWithClearanceToPolygon( poly, aLayer, aGap, m_maxError,
556  ERROR_OUTSIDE );
557 
558  // the pad shape in zone can be its convex hull or the shape itself
560  {
561  std::vector<wxPoint> convex_hull;
562  BuildConvexHull( convex_hull, poly );
563 
564  aHoles.NewOutline();
565 
566  for( const wxPoint& pt : convex_hull )
567  aHoles.Append( pt );
568  }
569  else
570  aHoles.Append( poly );
571  }
572  else
573  {
574  aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
575  ERROR_OUTSIDE );
576  }
577 }
578 
579 
583 void ZONE_FILLER::addHoleKnockout( PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles )
584 {
585  // Note: drill size represents finish size, which means the actual hole size is the plating
586  // thickness larger.
587  if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
589 
591 }
592 
593 
598 void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
599  bool aIgnoreLineWidth, SHAPE_POLY_SET& aHoles )
600 {
601  switch( aItem->Type() )
602  {
603  case PCB_SHAPE_T:
604  case PCB_TEXT_T:
605  case PCB_FP_SHAPE_T:
606  case PCB_TARGET_T:
607  aItem->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
608  ERROR_OUTSIDE, aIgnoreLineWidth );
609  break;
610 
611  case PCB_FP_TEXT_T:
612  {
613  FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
614 
615  if( text->IsVisible() )
616  {
617  text->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
618  ERROR_OUTSIDE, aIgnoreLineWidth );
619  }
620  }
621  break;
622 
623  default:
624  break;
625  }
626 }
627 
628 
634  SHAPE_POLY_SET& aFill )
635 {
636  SHAPE_POLY_SET holes;
637 
638  for( FOOTPRINT* footprint : m_board->Footprints() )
639  {
640  for( PAD* pad : footprint->Pads() )
641  {
642  if( !hasThermalConnection( pad, aZone ) )
643  continue;
644 
645  int gap = aZone->GetThermalReliefGap( pad );
646 
647  // If the pad is flashed to the current layer, or is on the same layer and shares a netcode, then
648  // we need to knock out the thermal relief.
649  if( pad->FlashLayer( aLayer ) || ( pad->IsOnLayer( aLayer ) && pad->GetNetCode() == aZone->GetNetCode() ) )
650  {
651  addKnockout( pad, aLayer, gap, holes );
652  }
653  else
654  {
655  // If the pad isn't on the current layer but has a hole, knock out a thermal relief
656  // for the hole.
657  if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
658  continue;
659 
660  // Note: drill size represents finish size, which means the actual holes size is
661  // the plating thickness larger.
662  if( pad->GetAttribute() == PAD_ATTRIB::PTH )
663  gap += pad->GetBoard()->GetDesignSettings().GetHolePlatingThickness();
664 
665  pad->TransformHoleWithClearanceToPolygon( holes, gap, m_maxError, ERROR_OUTSIDE );
666  }
667  }
668  }
669 
671 }
672 
673 
679  SHAPE_POLY_SET& aHoles )
680 {
681  long ticker = 0;
682 
683  auto checkForCancel =
684  [&ticker]( PROGRESS_REPORTER* aReporter ) -> bool
685  {
686  return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
687  };
688 
689  // A small extra clearance to be sure actual track clearances are not smaller than
690  // requested clearance due to many approximations in calculations, like arc to segment
691  // approx, rounding issues, etc.
692  int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_ExtraClearance );
693 
695  int zone_clearance = aZone->GetLocalClearance();
696  EDA_RECT zone_boundingbox = aZone->GetCachedBoundingBox();
697 
698  // Items outside the zone bounding box are skipped, so it needs to be inflated by the
699  // largest clearance value found in the netclasses and rules
700  zone_boundingbox.Inflate( m_worstClearance + extra_margin );
701 
702  auto evalRulesForItems =
703  [&bds]( DRC_CONSTRAINT_T aConstraint, const BOARD_ITEM* a, const BOARD_ITEM* b,
704  PCB_LAYER_ID aEvalLayer ) -> int
705  {
706  auto c = bds.m_DRCEngine->EvalRules( aConstraint, a, b, aEvalLayer );
707  return c.Value().Min();
708  };
709 
710  // Add non-connected pad clearances
711  //
712  auto knockoutPadClearance =
713  [&]( PAD* aPad )
714  {
715  if( aPad->GetBoundingBox().Intersects( zone_boundingbox ) )
716  {
717  int gap = 0;
718  bool knockoutHoleClearance = true;
719 
720  if( aPad->GetNetCode() > 0 && aPad->GetNetCode() == aZone->GetNetCode() )
721  {
722  // For unconnected but same-net pads, electrical clearances don't apply.
723  // Use the greater of the zone's local clearance and thermal relief.
724  gap = std::max( zone_clearance, aZone->GetThermalReliefGap( aPad ) );
725  knockoutHoleClearance = false;
726  }
727  else
728  {
729  gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aPad, aLayer );
730  }
731 
732  gap += extra_margin;
733 
734  if( aPad->FlashLayer( aLayer ) )
735  addKnockout( aPad, aLayer, gap, aHoles );
736  else if( aPad->GetDrillSize().x > 0 )
737  addHoleKnockout( aPad, gap, aHoles );
738 
739  if( knockoutHoleClearance && aPad->GetDrillSize().x > 0 )
740  {
741  gap = evalRulesForItems( HOLE_CLEARANCE_CONSTRAINT, aZone, aPad, aLayer );
742  gap += extra_margin;
743 
744  addHoleKnockout( aPad, gap, aHoles );
745  }
746  }
747  };
748 
749  for( FOOTPRINT* footprint : m_board->Footprints() )
750  {
751  for( PAD* pad : footprint->Pads() )
752  {
753  if( checkForCancel( m_progressReporter ) )
754  return;
755 
756  if( pad->GetNetCode() != aZone->GetNetCode()
757  || pad->GetNetCode() <= 0
758  || aZone->GetPadConnection( pad ) == ZONE_CONNECTION::NONE )
759  {
760  knockoutPadClearance( pad );
761  }
762  }
763  }
764 
765  // Add non-connected track clearances
766  //
767  auto knockoutTrackClearance =
768  [&]( PCB_TRACK* aTrack )
769  {
770  if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
771  {
772  if( aTrack->Type() == PCB_VIA_T )
773  {
774  PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
775  int gap = 0;
776  bool checkHoleClearance = true;
777 
778  gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer );
779 
780  if( via->FlashLayer( aLayer ) )
781  {
782  via->TransformShapeWithClearanceToPolygon( aHoles, aLayer,
783  gap + extra_margin,
785  }
786 
787  if( checkHoleClearance )
788  {
789  gap = std::max( gap, evalRulesForItems( HOLE_CLEARANCE_CONSTRAINT,
790  aZone, via, aLayer ) );
791  }
792 
793  int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness();
794 
795  TransformCircleToPolygon( aHoles, via->GetPosition(),
796  radius + gap + extra_margin,
798  }
799  else
800  {
801  int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer );
802 
803  aTrack->TransformShapeWithClearanceToPolygon( aHoles, aLayer,
804  gap + extra_margin,
806  }
807  }
808  };
809 
810  for( PCB_TRACK* track : m_board->Tracks() )
811  {
812  if( !track->IsOnLayer( aLayer ) )
813  continue;
814 
815  if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) )
816  continue;
817 
818  if( checkForCancel( m_progressReporter ) )
819  return;
820 
821  knockoutTrackClearance( track );
822  }
823 
824  // Add graphic item clearances. They are by definition unconnected, and have no clearance
825  // definitions of their own.
826  //
827  auto knockoutGraphicClearance =
828  [&]( BOARD_ITEM* aItem )
829  {
830  // A item on the Edge_Cuts or Margin is always seen as on any layer:
831  if( aItem->IsOnLayer( aLayer )
832  || aItem->IsOnLayer( Edge_Cuts )
833  || aItem->IsOnLayer( Margin ) )
834  {
835  if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
836  {
837  bool ignoreLineWidths = false;
838  int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aItem, aLayer );
839 
840  if( aItem->IsOnLayer( Edge_Cuts ) )
841  {
842  gap = std::max( gap, evalRulesForItems( EDGE_CLEARANCE_CONSTRAINT,
843  aZone, aItem, Edge_Cuts ) );
844 
845  ignoreLineWidths = true;
846  gap = std::max( gap, bds.GetDRCEpsilon() );
847  }
848 
849  if( aItem->IsOnLayer( Margin ) )
850  {
851  gap = std::max( gap, evalRulesForItems( EDGE_CLEARANCE_CONSTRAINT,
852  aZone, aItem, Margin ) );
853  }
854 
855  addKnockout( aItem, aLayer, gap + extra_margin, ignoreLineWidths, aHoles );
856  }
857  }
858  };
859 
860  for( FOOTPRINT* footprint : m_board->Footprints() )
861  {
862  bool skipFootprint = false;
863 
864  knockoutGraphicClearance( &footprint->Reference() );
865  knockoutGraphicClearance( &footprint->Value() );
866 
867  // Don't knock out holes in zones that share a net with a nettie footprint
868  if( footprint->IsNetTie() )
869  {
870  for( PAD* pad : footprint->Pads() )
871  {
872  if( aZone->GetNetCode() == pad->GetNetCode() )
873  {
874  skipFootprint = true;
875  break;
876  }
877  }
878  }
879 
880  if( skipFootprint )
881  continue;
882 
883  for( BOARD_ITEM* item : footprint->GraphicalItems() )
884  {
885  if( checkForCancel( m_progressReporter ) )
886  return;
887 
888  knockoutGraphicClearance( item );
889  }
890  }
891 
892  for( BOARD_ITEM* item : m_board->Drawings() )
893  {
894  if( checkForCancel( m_progressReporter ) )
895  return;
896 
897  knockoutGraphicClearance( item );
898  }
899 
900  // Add non-connected zone clearances
901  //
902  auto knockoutZoneClearance =
903  [&]( ZONE* aKnockout )
904  {
905  // If the zones share no common layers
906  if( !aKnockout->GetLayerSet().test( aLayer ) )
907  return;
908 
909  if( aKnockout->GetCachedBoundingBox().Intersects( zone_boundingbox ) )
910  {
911  if( aKnockout->GetIsRuleArea() )
912  {
913  // Keepouts use outline with no clearance
914  aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0, m_maxError,
915  ERROR_OUTSIDE, nullptr );
916  }
917  else if( bds.m_ZoneFillVersion == 5 )
918  {
919  // 5.x used outline with clearance
920  int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aKnockout,
921  aLayer );
922 
923  aKnockout->TransformSmoothedOutlineToPolygon( aHoles, gap, m_maxError,
924  ERROR_OUTSIDE, nullptr );
925  }
926  else
927  {
928  // 6.0 uses filled areas with clearance
929  int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aKnockout,
930  aLayer );
931 
932  SHAPE_POLY_SET poly;
933  aKnockout->TransformShapeWithClearanceToPolygon( poly, aLayer,
934  gap + extra_margin,
935  m_maxError,
936  ERROR_OUTSIDE );
937  aHoles.Append( poly );
938  }
939  }
940  };
941 
942  for( ZONE* otherZone : m_board->Zones() )
943  {
944  if( checkForCancel( m_progressReporter ) )
945  return;
946 
947  if( otherZone->GetIsRuleArea() )
948  {
949  if( otherZone->GetDoNotAllowCopperPour() )
950  knockoutZoneClearance( otherZone );
951  }
952  else
953  {
954  if( otherZone->GetNetCode() != aZone->GetNetCode()
955  && otherZone->GetPriority() > aZone->GetPriority() )
956  {
957  knockoutZoneClearance( otherZone );
958  }
959  }
960  }
961 
962  for( FOOTPRINT* footprint : m_board->Footprints() )
963  {
964  for( ZONE* otherZone : footprint->Zones() )
965  {
966  if( checkForCancel( m_progressReporter ) )
967  return;
968 
969  if( otherZone->GetIsRuleArea() )
970  {
971  if( otherZone->GetDoNotAllowCopperPour() )
972  knockoutZoneClearance( otherZone );
973  }
974  else
975  {
976  if( otherZone->GetNetCode() != aZone->GetNetCode()
977  && otherZone->GetPriority() > aZone->GetPriority() )
978  {
979  knockoutZoneClearance( otherZone );
980  }
981  }
982  }
983  }
984 
986 }
987 
988 
994  SHAPE_POLY_SET& aRawFill )
995 {
996  auto knockoutZoneOutline =
997  [&]( ZONE* aKnockout )
998  {
999  // If the zones share no common layers
1000  if( !aKnockout->GetLayerSet().test( aLayer ) )
1001  return;
1002 
1003  if( aKnockout->GetCachedBoundingBox().Intersects( aZone->GetCachedBoundingBox() ) )
1004  {
1005  // Processing of arc shapes in zones is not yet supported because Clipper
1006  // can't do boolean operations on them. The poly outline must be converted to
1007  // segments first.
1008  SHAPE_POLY_SET outline = *aKnockout->Outline();
1009  outline.ClearArcs();
1010 
1011  aRawFill.BooleanSubtract( outline, SHAPE_POLY_SET::PM_FAST );
1012  }
1013  };
1014 
1015  for( ZONE* otherZone : m_board->Zones() )
1016  {
1017  if( otherZone->GetNetCode() == aZone->GetNetCode()
1018  && otherZone->GetPriority() > aZone->GetPriority() )
1019  {
1020  knockoutZoneOutline( otherZone );
1021  }
1022  }
1023 
1024  for( FOOTPRINT* footprint : m_board->Footprints() )
1025  {
1026  for( ZONE* otherZone : footprint->Zones() )
1027  {
1028  if( otherZone->GetNetCode() == aZone->GetNetCode()
1029  && otherZone->GetPriority() > aZone->GetPriority() )
1030  {
1031  knockoutZoneOutline( otherZone );
1032  }
1033  }
1034  }
1035 }
1036 
1037 
1038 #define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
1039  { if( m_debugZoneFiller && aDebugLayer == b ) \
1040  { \
1041  m_board->SetLayerName( b, c ); \
1042  SHAPE_POLY_SET d = a; \
1043  d.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); \
1044  d.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); \
1045  aRawPolys = d; \
1046  return false; \
1047  } \
1048  }
1049 
1050 
1063  PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer,
1064  const SHAPE_POLY_SET& aSmoothedOutline,
1065  const SHAPE_POLY_SET& aMaxExtents,
1066  SHAPE_POLY_SET& aRawPolys )
1067 {
1069 
1070  // Features which are min_width should survive pruning; features that are *less* than
1071  // min_width should not. Therefore we subtract epsilon from the min_width when
1072  // deflating/inflating.
1073  int half_min_width = aZone->GetMinThickness() / 2;
1074  int epsilon = Millimeter2iu( 0.001 );
1075  int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 );
1076 
1077  // Solid polygons are deflated and inflated during calculations. Deflating doesn't cause
1078  // issues, but inflate is tricky as it can create excessively long and narrow spikes for
1079  // acute angles.
1080  // ALLOW_ACUTE_CORNERS cannot be used due to the spike problem.
1081  // CHAMFER_ACUTE_CORNERS is tempting, but can still produce spikes in some unusual
1082  // circumstances (https://gitlab.com/kicad/code/kicad/-/issues/5581).
1083  // It's unclear if ROUND_ACUTE_CORNERS would have the same issues, but is currently avoided
1084  // as a "less-safe" option.
1085  // ROUND_ALL_CORNERS produces the uniformly nicest shapes, but also a lot of segments.
1086  // CHAMFER_ALL_CORNERS improves the segment count.
1089 
1090  std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
1091  SHAPE_POLY_SET clearanceHoles;
1092 
1093  aRawPolys = aSmoothedOutline;
1094  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In1_Cu, wxT( "smoothed-outline" ) );
1095 
1097  return false;
1098 
1099  knockoutThermalReliefs( aZone, aLayer, aRawPolys );
1100  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In2_Cu, wxT( "minus-thermal-reliefs" ) );
1101 
1103  return false;
1104 
1105  buildCopperItemClearances( aZone, aLayer, clearanceHoles );
1106  DUMP_POLYS_TO_COPPER_LAYER( clearanceHoles, In3_Cu, wxT( "clearance-holes" ) );
1107 
1109  return false;
1110 
1111  buildThermalSpokes( aZone, aLayer, thermalSpokes );
1112 
1114  return false;
1115 
1116  // Create a temporary zone that we can hit-test spoke-ends against. It's only temporary
1117  // because the "real" subtract-clearance-holes has to be done after the spokes are added.
1118  static const bool USE_BBOX_CACHES = true;
1119  SHAPE_POLY_SET testAreas = aRawPolys;
1120  testAreas.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
1121  DUMP_POLYS_TO_COPPER_LAYER( testAreas, In4_Cu, wxT( "minus-clearance-holes" ) );
1122 
1123  // Prune features that don't meet minimum-width criteria
1124  if( half_min_width - epsilon > epsilon )
1125  {
1126  testAreas.Deflate( half_min_width - epsilon, numSegs, fastCornerStrategy );
1127  DUMP_POLYS_TO_COPPER_LAYER( testAreas, In5_Cu, wxT( "spoke-test-deflated" ) );
1128 
1129  testAreas.Inflate( half_min_width - epsilon, numSegs, fastCornerStrategy );
1130  DUMP_POLYS_TO_COPPER_LAYER( testAreas, In6_Cu, wxT( "spoke-test-reinflated" ) );
1131  }
1132 
1134  return false;
1135 
1136  // Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed
1137  // things up a bit.
1138  testAreas.BuildBBoxCaches();
1139  int interval = 0;
1140 
1141  SHAPE_POLY_SET debugSpokes;
1142 
1143  for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes )
1144  {
1145  const VECTOR2I& testPt = spoke.CPoint( 3 );
1146 
1147  // Hit-test against zone body
1148  if( testAreas.Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
1149  {
1150  if( m_debugZoneFiller )
1151  debugSpokes.AddOutline( spoke );
1152 
1153  aRawPolys.AddOutline( spoke );
1154  continue;
1155  }
1156 
1157  if( interval++ > 400 )
1158  {
1160  return false;
1161 
1162  interval = 0;
1163  }
1164 
1165  // Hit-test against other spokes
1166  for( const SHAPE_LINE_CHAIN& other : thermalSpokes )
1167  {
1168  if( &other != &spoke && other.PointInside( testPt, 1, USE_BBOX_CACHES ) )
1169  {
1170  if( m_debugZoneFiller )
1171  debugSpokes.AddOutline( spoke );
1172 
1173  aRawPolys.AddOutline( spoke );
1174  break;
1175  }
1176  }
1177  }
1178 
1179  DUMP_POLYS_TO_COPPER_LAYER( debugSpokes, In7_Cu, wxT( "spokes" ) );
1180 
1182  return false;
1183 
1184  aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
1185  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In8_Cu, wxT( "after-spoke-trimming" ) );
1186 
1187  // Prune features that don't meet minimum-width criteria
1188  if( half_min_width - epsilon > epsilon )
1189  aRawPolys.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
1190 
1191  // Min-thickness is the web thickness. On the other hand, a blob min-thickness by
1192  // min-thickness is not useful. Since there's no obvious definition of web vs. blob, we
1193  // arbitrarily choose "at least 1/2 min-thickness on one axis".
1194  for( int ii = aRawPolys.OutlineCount() - 1; ii >= 0; ii-- )
1195  {
1196  std::vector<SHAPE_LINE_CHAIN>& island = aRawPolys.Polygon( ii );
1197  EDA_RECT islandExtents = island.front().BBox();
1198 
1199  if( islandExtents.GetSizeMax() < half_min_width )
1200  aRawPolys.DeletePolygon( ii );
1201  }
1202 
1203  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In9_Cu, wxT( "deflated" ) );
1204 
1206  return false;
1207 
1208  // Now remove the non filled areas due to the hatch pattern
1209  if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1210  {
1211  if( !addHatchFillTypeOnZone( aZone, aLayer, aDebugLayer, aRawPolys ) )
1212  return false;
1213  }
1214 
1216  return false;
1217 
1218  // Re-inflate after pruning of areas that don't meet minimum-width criteria
1219  if( aZone->GetFilledPolysUseThickness() )
1220  {
1221  // If we're stroking the zone with a min_width stroke then this will naturally inflate
1222  // the zone by half_min_width
1223  }
1224  else if( half_min_width - epsilon > epsilon )
1225  {
1226  aRawPolys.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
1227  }
1228 
1229  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In15_Cu, wxT( "after-reinflating" ) );
1230 
1231  // Ensure additive changes (thermal stubs and particularly inflating acute corners) do not
1232  // add copper outside the zone boundary or inside the clearance holes
1233  aRawPolys.BooleanIntersection( aMaxExtents, SHAPE_POLY_SET::PM_FAST );
1234  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In16_Cu, wxT( "after-trim-to-outline" ) );
1235  aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
1236  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In17_Cu, wxT( "after-trim-to-clearance-holes" ) );
1237 
1238  // Lastly give any same-net but higher-priority zones control over their own area.
1239  subtractHigherPriorityZones( aZone, aLayer, aRawPolys );
1240  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In18_Cu, wxT( "minus-higher-priority-zones" ) );
1241 
1242  aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
1243  return true;
1244 }
1245 
1246 
1247 /*
1248  * Build the filled solid areas data from real outlines (stored in m_Poly)
1249  * The solid areas can be more than one on copper layers, and do not have holes
1250  * ( holes are linked by overlapping segments to the main outline)
1251  */
1253  SHAPE_POLY_SET& aFinalPolys )
1254 {
1255  SHAPE_POLY_SET* boardOutline = m_brdOutlinesValid ? &m_boardOutline : nullptr;
1256  SHAPE_POLY_SET maxExtents;
1257  SHAPE_POLY_SET smoothedPoly;
1258  PCB_LAYER_ID debugLayer = UNDEFINED_LAYER;
1259 
1260  if( m_debugZoneFiller && LSET::InternalCuMask().Contains( aLayer ) )
1261  {
1262  debugLayer = aLayer;
1263  aLayer = F_Cu;
1264  }
1265 
1266  if ( !aZone->BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
1267  return false;
1268 
1270  return false;
1271 
1272  if( aZone->IsOnCopperLayer() )
1273  {
1274  if( computeRawFilledArea( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aRawPolys ) )
1275  aZone->SetNeedRefill( false );
1276 
1277  aFinalPolys = aRawPolys;
1278  }
1279  else
1280  {
1281  // Features which are min_width should survive pruning; features that are *less* than
1282  // min_width should not. Therefore we subtract epsilon from the min_width when
1283  // deflating/inflating.
1284  int half_min_width = aZone->GetMinThickness() / 2;
1285  int epsilon = Millimeter2iu( 0.001 );
1286  int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 );
1287 
1288  smoothedPoly.Deflate( half_min_width - epsilon, numSegs );
1289 
1290  // Remove the non filled areas due to the hatch pattern
1291  if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1292  addHatchFillTypeOnZone( aZone, aLayer, debugLayer, smoothedPoly );
1293 
1294  // Re-inflate after pruning of areas that don't meet minimum-width criteria
1295  if( aZone->GetFilledPolysUseThickness() )
1296  {
1297  // If we're stroking the zone with a min_width stroke then this will naturally
1298  // inflate the zone by half_min_width
1299  }
1300  else if( half_min_width - epsilon > epsilon )
1301  {
1302  smoothedPoly.Inflate( half_min_width - epsilon, numSegs );
1303  }
1304 
1305  aRawPolys = smoothedPoly;
1306  aFinalPolys = smoothedPoly;
1307 
1309  aZone->SetNeedRefill( false );
1310  }
1311 
1312  return true;
1313 }
1314 
1315 
1320  std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
1321 {
1322  auto zoneBB = aZone->GetCachedBoundingBox();
1323  int zone_clearance = aZone->GetLocalClearance();
1324  int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
1325  biggest_clearance = std::max( biggest_clearance, zone_clearance );
1326  zoneBB.Inflate( biggest_clearance );
1327 
1328  // Is a point on the boundary of the polygon inside or outside? This small epsilon lets
1329  // us avoid the question.
1330  int epsilon = KiROUND( IU_PER_MM * 0.04 ); // about 1.5 mil
1331 
1332  for( FOOTPRINT* footprint : m_board->Footprints() )
1333  {
1334  for( PAD* pad : footprint->Pads() )
1335  {
1336  if( !hasThermalConnection( pad, aZone ) )
1337  continue;
1338 
1339  // We currently only connect to pads, not pad holes
1340  if( !pad->IsOnLayer( aLayer ) )
1341  continue;
1342 
1343  int thermalReliefGap = aZone->GetThermalReliefGap( pad );
1344 
1345  // Calculate thermal bridge half width
1346  int spoke_w = aZone->GetThermalReliefSpokeWidth( pad );
1347  // Avoid spoke_w bigger than the smaller pad size, because
1348  // it is not possible to create stubs bigger than the pad.
1349  // Possible refinement: have a separate size for vertical and horizontal stubs
1350  spoke_w = std::min( spoke_w, pad->GetSize().x );
1351  spoke_w = std::min( spoke_w, pad->GetSize().y );
1352 
1353  // Cannot create stubs having a width < zone min thickness
1354  if( spoke_w < aZone->GetMinThickness() )
1355  continue;
1356 
1357  int spoke_half_w = spoke_w / 2;
1358 
1359  // Quick test here to possibly save us some work
1360  BOX2I itemBB = pad->GetBoundingBox();
1361  itemBB.Inflate( thermalReliefGap + epsilon );
1362 
1363  if( !( itemBB.Intersects( zoneBB ) ) )
1364  continue;
1365 
1366  // Thermal spokes consist of segments from the pad center to points just outside
1367  // the thermal relief.
1368  //
1369  // We use the bounding-box to lay out the spokes, but for this to work the
1370  // bounding box has to be built at the same rotation as the spokes.
1371  // We have to use a dummy pad to avoid dirtying the cached shapes
1372  wxPoint shapePos = pad->ShapePos();
1373  double padAngle = pad->GetOrientation();
1374  PAD dummy_pad( *pad );
1375  dummy_pad.SetOrientation( 0.0 );
1376 
1377  // Spokes are from center of pad, not from hole
1378  dummy_pad.SetPosition( -pad->GetOffset() );
1379 
1380  BOX2I reliefBB = dummy_pad.GetBoundingBox();
1381  reliefBB.Inflate( thermalReliefGap + epsilon );
1382 
1383  // For circle pads, the thermal spoke orientation is 45 deg
1384  if( pad->GetShape() == PAD_SHAPE::CIRCLE )
1385  padAngle = s_RoundPadThermalSpokeAngle;
1386 
1387  for( int i = 0; i < 4; i++ )
1388  {
1389  SHAPE_LINE_CHAIN spoke;
1390  switch( i )
1391  {
1392  case 0: // lower stub
1393  spoke.Append( +spoke_half_w, -spoke_half_w );
1394  spoke.Append( -spoke_half_w, -spoke_half_w );
1395  spoke.Append( -spoke_half_w, reliefBB.GetBottom() );
1396  spoke.Append( 0, reliefBB.GetBottom() ); // test pt
1397  spoke.Append( +spoke_half_w, reliefBB.GetBottom() );
1398  break;
1399 
1400  case 1: // upper stub
1401  spoke.Append( +spoke_half_w, spoke_half_w );
1402  spoke.Append( -spoke_half_w, spoke_half_w );
1403  spoke.Append( -spoke_half_w, reliefBB.GetTop() );
1404  spoke.Append( 0, reliefBB.GetTop() ); // test pt
1405  spoke.Append( +spoke_half_w, reliefBB.GetTop() );
1406  break;
1407 
1408  case 2: // right stub
1409  spoke.Append( -spoke_half_w, spoke_half_w );
1410  spoke.Append( -spoke_half_w, -spoke_half_w );
1411  spoke.Append( reliefBB.GetRight(), -spoke_half_w );
1412  spoke.Append( reliefBB.GetRight(), 0 ); // test pt
1413  spoke.Append( reliefBB.GetRight(), spoke_half_w );
1414  break;
1415 
1416  case 3: // left stub
1417  spoke.Append( spoke_half_w, spoke_half_w );
1418  spoke.Append( spoke_half_w, -spoke_half_w );
1419  spoke.Append( reliefBB.GetLeft(), -spoke_half_w );
1420  spoke.Append( reliefBB.GetLeft(), 0 ); // test pt
1421  spoke.Append( reliefBB.GetLeft(), spoke_half_w );
1422  break;
1423  }
1424 
1425  spoke.Rotate( -DECIDEG2RAD( padAngle ) );
1426  spoke.Move( shapePos );
1427 
1428  spoke.SetClosed( true );
1429  spoke.GenerateBBoxCache();
1430  aSpokesList.push_back( std::move( spoke ) );
1431  }
1432  }
1433  }
1434 }
1435 
1436 
1438  PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET& aRawPolys )
1439 {
1440  // Build grid:
1441 
1442  // obviously line thickness must be > zone min thickness.
1443  // It can happens if a board file was edited by hand by a python script
1444  // Use 1 micron margin to be *sure* there is no issue in Gerber files
1445  // (Gbr file unit = 1 or 10 nm) due to some truncation in coordinates or calculations
1446  // This margin also avoid problems due to rounding coordinates in next calculations
1447  // that can create incorrect polygons
1448  int thickness = std::max( aZone->GetHatchThickness(),
1449  aZone->GetMinThickness() + Millimeter2iu( 0.001 ) );
1450 
1451  int linethickness = thickness - aZone->GetMinThickness();
1452  int gridsize = thickness + aZone->GetHatchGap();
1453  double orientation = aZone->GetHatchOrientation();
1454 
1455  SHAPE_POLY_SET filledPolys = aRawPolys;
1456  // Use a area that contains the rotated bbox by orientation,
1457  // and after rotate the result by -orientation.
1458  if( orientation != 0.0 )
1459  filledPolys.Rotate( M_PI / 180.0 * orientation, VECTOR2I( 0, 0 ) );
1460 
1461  BOX2I bbox = filledPolys.BBox( 0 );
1462 
1463  // Build hole shape
1464  // the hole size is aZone->GetHatchGap(), but because the outline thickness
1465  // is aZone->GetMinThickness(), the hole shape size must be larger
1466  SHAPE_LINE_CHAIN hole_base;
1467  int hole_size = aZone->GetHatchGap() + aZone->GetMinThickness();
1468  VECTOR2I corner( 0, 0 );;
1469  hole_base.Append( corner );
1470  corner.x += hole_size;
1471  hole_base.Append( corner );
1472  corner.y += hole_size;
1473  hole_base.Append( corner );
1474  corner.x = 0;
1475  hole_base.Append( corner );
1476  hole_base.SetClosed( true );
1477 
1478  // Calculate minimal area of a grid hole.
1479  // All holes smaller than a threshold will be removed
1480  double minimal_hole_area = hole_base.Area() * aZone->GetHatchHoleMinArea();
1481 
1482  // Now convert this hole to a smoothed shape:
1483  if( aZone->GetHatchSmoothingLevel() > 0 )
1484  {
1485  // the actual size of chamfer, or rounded corner radius is the half size
1486  // of the HatchFillTypeGap scaled by aZone->GetHatchSmoothingValue()
1487  // aZone->GetHatchSmoothingValue() = 1.0 is the max value for the chamfer or the
1488  // radius of corner (radius = half size of the hole)
1489  int smooth_value = KiROUND( aZone->GetHatchGap()
1490  * aZone->GetHatchSmoothingValue() / 2 );
1491 
1492  // Minimal optimization:
1493  // make smoothing only for reasonable smooth values, to avoid a lot of useless segments
1494  // and if the smooth value is small, use chamfer even if fillet is requested
1495  #define SMOOTH_MIN_VAL_MM 0.02
1496  #define SMOOTH_SMALL_VAL_MM 0.04
1497 
1498  if( smooth_value > Millimeter2iu( SMOOTH_MIN_VAL_MM ) )
1499  {
1500  SHAPE_POLY_SET smooth_hole;
1501  smooth_hole.AddOutline( hole_base );
1502  int smooth_level = aZone->GetHatchSmoothingLevel();
1503 
1504  if( smooth_value < Millimeter2iu( SMOOTH_SMALL_VAL_MM ) && smooth_level > 1 )
1505  smooth_level = 1;
1506 
1507  // Use a larger smooth_value to compensate the outline tickness
1508  // (chamfer is not visible is smooth value < outline thickess)
1509  smooth_value += aZone->GetMinThickness() / 2;
1510 
1511  // smooth_value cannot be bigger than the half size oh the hole:
1512  smooth_value = std::min( smooth_value, aZone->GetHatchGap() / 2 );
1513 
1514  // the error to approximate a circle by segments when smoothing corners by a arc
1515  int error_max = std::max( Millimeter2iu( 0.01 ), smooth_value / 20 );
1516 
1517  switch( smooth_level )
1518  {
1519  case 1:
1520  // Chamfer() uses the distance from a corner to create a end point
1521  // for the chamfer.
1522  hole_base = smooth_hole.Chamfer( smooth_value ).Outline( 0 );
1523  break;
1524 
1525  default:
1526  if( aZone->GetHatchSmoothingLevel() > 2 )
1527  error_max /= 2; // Force better smoothing
1528 
1529  hole_base = smooth_hole.Fillet( smooth_value, error_max ).Outline( 0 );
1530  break;
1531 
1532  case 0:
1533  break;
1534  };
1535  }
1536  }
1537 
1538  // Build holes
1539  SHAPE_POLY_SET holes;
1540 
1541  for( int xx = 0; ; xx++ )
1542  {
1543  int xpos = xx * gridsize;
1544 
1545  if( xpos > bbox.GetWidth() )
1546  break;
1547 
1548  for( int yy = 0; ; yy++ )
1549  {
1550  int ypos = yy * gridsize;
1551 
1552  if( ypos > bbox.GetHeight() )
1553  break;
1554 
1555  // Generate hole
1556  SHAPE_LINE_CHAIN hole( hole_base );
1557  hole.Move( VECTOR2I( xpos, ypos ) );
1558  holes.AddOutline( hole );
1559  }
1560  }
1561 
1562  holes.Move( bbox.GetPosition() );
1563 
1564  if( orientation != 0.0 )
1565  holes.Rotate( -M_PI/180.0 * orientation, VECTOR2I( 0,0 ) );
1566 
1567  DUMP_POLYS_TO_COPPER_LAYER( holes, In10_Cu, wxT( "hatch-holes" ) );
1568 
1569  int outline_margin = aZone->GetMinThickness() * 1.1;
1570 
1571  // Using GetHatchThickness() can look more consistent than GetMinThickness().
1572  if( aZone->GetHatchBorderAlgorithm() && aZone->GetHatchThickness() > outline_margin )
1573  outline_margin = aZone->GetHatchThickness();
1574 
1575  // The fill has already been deflated to ensure GetMinThickness() so we just have to
1576  // account for anything beyond that.
1577  SHAPE_POLY_SET deflatedFilledPolys = aRawPolys;
1578  deflatedFilledPolys.Deflate( outline_margin - aZone->GetMinThickness(), 16 );
1579  holes.BooleanIntersection( deflatedFilledPolys, SHAPE_POLY_SET::PM_FAST );
1580  DUMP_POLYS_TO_COPPER_LAYER( holes, In11_Cu, wxT( "fill-clipped-hatch-holes" ) );
1581 
1582  SHAPE_POLY_SET deflatedOutline = *aZone->Outline();
1583  deflatedOutline.Deflate( outline_margin, 16 );
1584  holes.BooleanIntersection( deflatedOutline, SHAPE_POLY_SET::PM_FAST );
1585  DUMP_POLYS_TO_COPPER_LAYER( holes, In12_Cu, wxT( "outline-clipped-hatch-holes" ) );
1586 
1587  if( aZone->GetNetCode() != 0 )
1588  {
1589  // Vias and pads connected to the zone must not be allowed to become isolated inside
1590  // one of the holes. Effectively this means their copper outline needs to be expanded
1591  // to be at least as wide as the gap so that it is guaranteed to touch at least one
1592  // edge.
1593  EDA_RECT zone_boundingbox = aZone->GetCachedBoundingBox();
1594  SHAPE_POLY_SET aprons;
1595  int min_apron_radius = ( aZone->GetHatchGap() * 10 ) / 19;
1596 
1597  for( PCB_TRACK* track : m_board->Tracks() )
1598  {
1599  if( track->Type() == PCB_VIA_T )
1600  {
1601  PCB_VIA* via = static_cast<PCB_VIA*>( track );
1602 
1603  if( via->GetNetCode() == aZone->GetNetCode()
1604  && via->IsOnLayer( aLayer )
1605  && via->GetBoundingBox().Intersects( zone_boundingbox ) )
1606  {
1607  int r = std::max( min_apron_radius,
1608  via->GetDrillValue() / 2 + outline_margin );
1609 
1610  TransformCircleToPolygon( aprons, via->GetPosition(), r, ARC_HIGH_DEF,
1611  ERROR_OUTSIDE );
1612  }
1613  }
1614  }
1615 
1616  for( FOOTPRINT* footprint : m_board->Footprints() )
1617  {
1618  for( PAD* pad : footprint->Pads() )
1619  {
1620  if( pad->GetNetCode() == aZone->GetNetCode()
1621  && pad->IsOnLayer( aLayer )
1622  && pad->GetBoundingBox().Intersects( zone_boundingbox ) )
1623  {
1624  // What we want is to bulk up the pad shape so that the narrowest bit of
1625  // copper between the hole and the apron edge is at least outline_margin
1626  // wide (and that the apron itself meets min_apron_radius. But that would
1627  // take a lot of code and math, and the following approximation is close
1628  // enough.
1629  int pad_width = std::min( pad->GetSize().x, pad->GetSize().y );
1630  int slot_width = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
1631  int min_annular_ring_width = ( pad_width - slot_width ) / 2;
1632  int clearance = std::max( min_apron_radius - pad_width / 2,
1633  outline_margin - min_annular_ring_width );
1634 
1635  clearance = std::max( 0, clearance - linethickness / 2 );
1636  pad->TransformShapeWithClearanceToPolygon( aprons, aLayer, clearance,
1637  ARC_HIGH_DEF, ERROR_OUTSIDE );
1638  }
1639  }
1640  }
1641 
1642  holes.BooleanSubtract( aprons, SHAPE_POLY_SET::PM_FAST );
1643  }
1644  DUMP_POLYS_TO_COPPER_LAYER( holes, In13_Cu, wxT( "pad-via-clipped-hatch-holes" ) );
1645 
1646  // Now filter truncated holes to avoid small holes in pattern
1647  // It happens for holes near the zone outline
1648  for( int ii = 0; ii < holes.OutlineCount(); )
1649  {
1650  double area = holes.Outline( ii ).Area();
1651 
1652  if( area < minimal_hole_area ) // The current hole is too small: remove it
1653  holes.DeletePolygon( ii );
1654  else
1655  ++ii;
1656  }
1657 
1658  // create grid. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to
1659  // generate strictly simple polygons needed by Gerber files and Fracture()
1660  aRawPolys.BooleanSubtract( aRawPolys, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
1661  DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In14_Cu, wxT( "after-hatching" ) );
1662 
1663  return true;
1664 }
bool TransformHoleWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, int aInflateValue, int aError, ERROR_LOC aErrorLoc) const
Build the corner list of the polygonal drill shape in the board coordinate system.
Definition: pad.cpp:1537
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:759
int m_ZoneFillVersion
Option to select different fill algorithms.
#define DUMP_POLYS_TO_COPPER_LAYER(a, b, c)
int GetHatchGap() const
Definition: zone.h:256
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
virtual void SetMaxProgress(int aMaxProgress)=0
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:55
int OutlineCount() const
Return the number of vertices in a given outline/hole.
All angles are chamfered.
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:127
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:1835
ZONES & Zones()
Definition: board.h:240
int GetHatchThickness() const
Definition: zone.h:253
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:45
unsigned GetPriority() const
Definition: zone.h:122
This file is part of the common library.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
BOARD * m_board
Definition: zone_filler.h:123
coord_type GetTop() const
Definition: box2.h:187
static constexpr double IU_PER_MM
Mock up a conversion function.
A progress reporter interface for use in multi-threaded environments.
int GetHolePlatingThickness() const
Pad & via drills are finish size.
SHAPE_POLY_SET * Outline()
Definition: zone.h:320
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:587
void Move(const VECTOR2I &aVector) override
static const double s_RoundPadThermalSpokeAngle
Definition: zone_filler.cpp:50
#define SMOOTH_MIN_VAL_MM
double GetHatchSmoothingValue() const
Definition: zone.h:265
CORNER_STRATEGY
< define how inflate transform build inflated polygon
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
void addHoleKnockout(PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad's hole.
coord_type GetRight() const
Definition: box2.h:182
bool BuildSmoothedPoly(SHAPE_POLY_SET &aSmoothedPoly, PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aBoardOutline, SHAPE_POLY_SET *aSmoothedPolyWithApron=nullptr) const
Definition: zone.cpp:1117
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition: commit.h:71
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
ZONE_FILLER(BOARD *aBoard, COMMIT *aCommit)
Definition: zone_filler.cpp:53
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon 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.
double Area(bool aAbsolute=true) const
Return the area of this chain.
coord_type GetBottom() const
Definition: box2.h:183
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
#define SMOOTH_SMALL_VAL_MM
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
virtual void AdvanceProgress()=0
Increment the progress bar length (inside the current virtual zone).
ZONE_FILL_MODE GetFillMode() const
Definition: zone.h:181
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Plated through hole pad.
void DeletePolygon(int aIdx)
void buildCopperItemClearances(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aHoles)
Removes clearance from the shape for copper items which share the zone's layer but are not connected ...
int GetThermalReliefGap() const
Definition: zone.h:191
DRC_CONSTRAINT_T
Definition: drc_rule.h:41
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:217
double GetHatchHoleMinArea() const
Definition: zone.h:268
bool Fill(std::vector< ZONE * > &aZones, bool aCheck=false, wxWindow *aParent=nullptr)
Fills the given list of zones.
Definition: zone_filler.cpp:79
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:516
int GetMinThickness() const
Definition: zone.h:244
void SetProgressReporter(PROGRESS_REPORTER *aReporter)
Definition: zone_filler.cpp:71
#define MAX_CU_LAYERS
Definition: layer_ids.h:147
void Move(const VECTOR2I &aVector) override
COMMIT * m_commit
Definition: zone_filler.h:126
virtual void AdvancePhase()=0
Use the next available virtual zone of the dialog progress bar.
int GetHatchSmoothingLevel() const
Definition: zone.h:262
const EDA_RECT GetCachedBoundingBox() const
ONLY TO BE USED BY CLIENTS WHICH SET UP THE CACHE!
Definition: zone.h:145
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: zone.cpp:492
coord_type GetWidth() const
Definition: box2.h:180
FOOTPRINTS & Footprints()
Definition: board.h:234
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const wxPoint &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void addKnockout(PAD *aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad.
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:719
const EDA_RECT GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition: pad.cpp:569
bool addHatchFillTypeOnZone(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET &aRawPolys)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
PAD_SHAPE GetShape() const
Definition: pad.h:170
#define _(s)
a few functions useful in geometry calculations.
void Simplify(POLYGON_MODE aFastMode)
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
E_SERIE r
Definition: eserie.cpp:41
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union between a and b, store the result in it self For aFastMode meaning,...
int NewOutline()
Creates a new hole in a given outline.
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:652
Thermal relief only for THT pads.
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:104
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
const Vec & GetPosition() const
Definition: box2.h:177
void knockoutThermalReliefs(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFill)
Removes thermal reliefs from the shape for any pads connected to the zone.
void Rotate(double aAngle, const VECTOR2I &aCenter=VECTOR2I(0, 0)) override
Rotate all vertices by a given angle.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
double GetHatchOrientation() const
Definition: zone.h:259
Use thermal relief for pads.
bool computeRawFilledArea(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, const SHAPE_POLY_SET &aSmoothedOutline, const SHAPE_POLY_SET &aMaxExtents, SHAPE_POLY_SET &aRawPolys)
Function computeRawFilledArea Add non copper areas polygons (pads and tracks with clearance) to a fil...
int GetHatchBorderAlgorithm() const
Definition: zone.h:271
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aMaxError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition: pad.cpp:1554
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:281
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
Definition: pad.h:188
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
Shows the 'do not show again' checkbox.
Definition: confirm.h:56
SHAPE_POLY_SET Chamfer(int aDistance)
Return a chamfered version of the polygon set.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
PAD_ATTRIB GetAttribute() const
Definition: pad.h:371
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
Definition: zone_settings.h:54
bool m_debugZoneFiller
Definition: zone_filler.h:132
Definition: layer_ids.h:71
void subtractHigherPriorityZones(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawFill)
Removes the outlines of higher-proirity zones with the same net.
void SetPosition(const wxPoint &aPos) override
Definition: pad.h:172
Handle the component boundary box.
Definition: eda_rect.h:42
double DECIDEG2RAD(double deg)
Definition: trigo.h:233
void ClearArcs()
Appends a vertex at the end of the given outline/hole (default: the last outline)
bool IsOnCopperLayer() const override
Definition: zone.cpp:228
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
coord_type GetHeight() const
Definition: box2.h:181
Pads are not covered.
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
Definition: zone.h:232
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
std::mutex & GetLock()
Definition: zone.h:222
int ShowModal() override
Definition: confirm.cpp:99
SHAPE_POLY_SET m_boardOutline
Definition: zone_filler.h:124
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:36
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void GenerateBBoxCache() const
virtual void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:156
virtual bool IsCancelled() const =0
bool fillSingleZone(ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawPolys, SHAPE_POLY_SET &aFinalPolys)
Build the filled solid areas polygons from zone outlines (stored in m_Poly) The solid areas can be mo...
bool m_brdOutlinesValid
Definition: zone_filler.h:125
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:238
int GetThermalReliefSpokeWidth() const
Definition: zone.h:202
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
coord_type GetLeft() const
Definition: box2.h:186
POLYGON & Polygon(int aIndex)
void SetRawPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:660
ZONE_CONNECTION GetPadConnection(PAD *aPad, wxString *aSource=nullptr) const
Definition: zone.cpp:753
Definition: pad.h:57
bool hasThermalConnection(PAD *pad, const ZONE *aZone)
Return true if the given pad has a thermal connection with the given zone.
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
static constexpr int Millimeter2iu(double mm)
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
bool m_DebugZoneFiller
A mode that dumps the various stages of a F_Cu fill into In1_Cu through In9_Cu.
DRAWINGS & Drawings()
Definition: board.h:237
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
void buildThermalSpokes(const ZONE *aZone, PCB_LAYER_ID aLayer, std::deque< SHAPE_LINE_CHAIN > &aSpokes)
Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone.
void delete_if(_Container &__c, _Function &&__f)
Deletes all values from __c for which __f returns true.
Definition: kicad_algo.h:173
int GetSizeMax() const
Definition: eda_rect.h:105
TRACKS & Tracks()
Definition: board.h:231
std::shared_ptr< DRC_ENGINE > m_DRCEngine
A structure used for calculating isolated islands on a given zone across all its layers.
int m_worstClearance
Definition: zone_filler.h:130
void SetOrientation(double aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:630
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Inflate the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:364
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
bool GetFilledPolysUseThickness() const
Definition: zone.h:691
Container for design settings for a BOARD object.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
void BuildConvexHull(std::vector< wxPoint > &aResult, const std::vector< wxPoint > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
Definition: convex_hull.cpp:87