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-2020 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Tomasz W┼éostowski <tomasz.wlostowski@cern.ch>
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 <algorithm>
28 #include <future>
29 
30 #include <class_board.h>
31 #include <class_zone.h>
32 #include <class_module.h>
33 #include <class_edge_mod.h>
34 #include <class_drawsegment.h>
35 #include <class_pcb_text.h>
36 #include <class_pcb_target.h>
37 #include <class_track.h>
39 #include <board_commit.h>
42 #include <geometry/shape_file_io.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 
49 #include "zone_filler.h"
50 
52 {
53 public:
55  {
56  m_reporter = aReporter;
57 
58  if( aReporter )
59  aReporter->Hide();
60  }
61 
63  {
64  if( m_reporter )
65  m_reporter->Show();
66  }
67 
68 private:
70 };
71 
72 
73 static const double s_RoundPadThermalSpokeAngle = 450;
74 static const bool s_DumpZonesWhenFilling = false;
75 
76 
77 ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) :
78  m_board( aBoard ),
79  m_brdOutlinesValid( false ),
80  m_commit( aCommit ),
81  m_progressReporter( nullptr ),
82  m_high_def( 9 ),
83  m_low_def( 6 )
84 {
85 }
86 
87 
89 {
90 }
91 
92 
93 void ZONE_FILLER::InstallNewProgressReporter( wxWindow* aParent, const wxString& aTitle,
94  int aNumPhases )
95 {
96  m_uniqueReporter = std::make_unique<WX_PROGRESS_REPORTER>( aParent, aTitle, aNumPhases );
98 }
99 
100 
101 bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck )
102 {
103  std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> toFill;
104  auto connectivity = m_board->GetConnectivity();
105  bool filledPolyWithOutline = not m_board->GetDesignSettings().m_ZoneUseNoOutlineInFill;
106 
107  std::unique_lock<std::mutex> lock( connectivity->GetLock(), std::try_to_lock );
108 
109  if( !lock )
110  return false;
111 
112  if( m_progressReporter )
113  {
114  m_progressReporter->Report( aCheck ? _( "Checking zone fills..." ) : _( "Building zone fills..." ) );
115  m_progressReporter->SetMaxProgress( toFill.size() );
116  }
117 
118  // The board outlines is used to clip solid areas inside the board (when outlines are valid)
121 
122  for( auto zone : aZones )
123  {
124  // Keepout zones are not filled
125  if( zone->GetIsKeepout() )
126  continue;
127 
128  if( m_commit )
129  m_commit->Modify( zone );
130 
131  // calculate the hash value for filled areas. it will be used later
132  // to know if the current filled areas are up to date
133  zone->BuildHashValue();
134 
135  // Add the zone to the list of zones to test or refill
136  toFill.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST(zone) );
137 
138  // Remove existing fill first to prevent drawing invalid polygons
139  // on some platforms
140  zone->UnFill();
141  }
142 
143  std::atomic<size_t> nextItem( 0 );
144  size_t parallelThreadCount =
145  std::min<size_t>( std::thread::hardware_concurrency(), aZones.size() );
146  std::vector<std::future<size_t>> returns( parallelThreadCount );
147 
148  auto fill_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t
149  {
150  size_t num = 0;
151 
152  for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
153  {
154  ZONE_CONTAINER* zone = toFill[i].m_zone;
155  zone->SetFilledPolysUseThickness( filledPolyWithOutline );
156  SHAPE_POLY_SET rawPolys, finalPolys;
157  fillSingleZone( zone, rawPolys, finalPolys );
158 
159  zone->SetRawPolysList( rawPolys );
160  zone->SetFilledPolysList( finalPolys );
161  zone->SetIsFilled( true );
162 
163  if( m_progressReporter )
165 
166  num++;
167  }
168 
169  return num;
170  };
171 
172  if( parallelThreadCount <= 1 )
173  fill_lambda( m_progressReporter );
174  else
175  {
176  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
177  returns[ii] = std::async( std::launch::async, fill_lambda, m_progressReporter );
178 
179  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
180  {
181  // Here we balance returns with a 100ms timeout to allow UI updating
182  std::future_status status;
183  do
184  {
185  if( m_progressReporter )
187 
188  status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
189  } while( status != std::future_status::ready );
190  }
191  }
192 
193  // Now update the connectivity to check for copper islands
194  if( m_progressReporter )
195  {
197  m_progressReporter->Report( _( "Removing insulated copper islands..." ) );
199  }
200 
201  connectivity->SetProgressReporter( m_progressReporter );
202  connectivity->FindIsolatedCopperIslands( toFill );
203 
204  // Now remove insulated copper islands and islands outside the board edge
205  bool outOfDate = false;
206 
207  for( auto& zone : toFill )
208  {
209  std::sort( zone.m_islands.begin(), zone.m_islands.end(), std::greater<int>() );
210  SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList();
211 
212  // Remove solid areas outside the board cutouts and the insulated islands
213  // only zones with net code > 0 can have insulated islands by definition
214  if( zone.m_zone->GetNetCode() > 0 )
215  {
216  // solid areas outside the board cutouts are also removed, because they are usually
217  // insulated islands
218  for( auto idx : zone.m_islands )
219  {
220  poly.DeletePolygon( idx );
221  }
222  }
223  // Zones with no net can have areas outside the board cutouts.
224  // By definition, Zones with no net have no isolated island
225  // (in fact all filled areas are isolated islands)
226  // but they can have some areas outside the board cutouts.
227  // A filled area outside the board cutouts has all points outside cutouts,
228  // so we only need to check one point for each filled polygon.
229  // Note also non copper zones are already clipped
230  else if( m_brdOutlinesValid && zone.m_zone->IsOnCopperLayer() )
231  {
232  for( int idx = 0; idx < poly.OutlineCount(); )
233  {
234  if( poly.Polygon( idx ).empty() ||
235  !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
236  {
237  poly.DeletePolygon( idx );
238  }
239  else
240  idx++;
241  }
242  }
243 
244  zone.m_zone->SetFilledPolysList( poly );
245  zone.m_zone->CalculateFilledArea();
246 
247  if( aCheck && zone.m_zone->GetHashValue() != poly.GetHash() )
248  outOfDate = true;
249  }
250 
251  if( aCheck && outOfDate )
252  {
254  KIDIALOG dlg( m_progressReporter->GetParent(),
255  _( "Zone fills are out-of-date. Refill?" ),
256  _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
257  dlg.SetOKCancelLabels( _( "Refill" ), _( "Continue without Refill" ) );
258  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
259 
260  if( dlg.ShowModal() == wxID_CANCEL )
261  {
262  if( m_commit )
263  m_commit->Revert();
264 
265  connectivity->SetProgressReporter( nullptr );
266  return false;
267  }
268  }
269 
270  if( m_progressReporter )
271  {
273  m_progressReporter->Report( _( "Performing polygon fills..." ) );
274  m_progressReporter->SetMaxProgress( toFill.size() );
275  }
276 
277 
278  nextItem = 0;
279 
280  auto tri_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t
281  {
282  size_t num = 0;
283 
284  for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
285  {
286  toFill[i].m_zone->CacheTriangulation();
287  num++;
288 
289  if( m_progressReporter )
291  }
292 
293  return num;
294  };
295 
296  if( parallelThreadCount <= 1 )
297  tri_lambda( m_progressReporter );
298  else
299  {
300  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
301  returns[ii] = std::async( std::launch::async, tri_lambda, m_progressReporter );
302 
303  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
304  {
305  // Here we balance returns with a 100ms timeout to allow UI updating
306  std::future_status status;
307  do
308  {
309  if( m_progressReporter )
311 
312  status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
313  } while( status != std::future_status::ready );
314  }
315  }
316 
317  if( m_progressReporter )
318  {
320  m_progressReporter->Report( _( "Committing changes..." ) );
322  }
323 
324  connectivity->SetProgressReporter( nullptr );
325 
326  if( m_commit )
327  {
328  m_commit->Push( _( "Fill Zone(s)" ), false );
329  }
330  else
331  {
332  for( auto& i : toFill )
333  connectivity->Update( i.m_zone );
334 
335  connectivity->RecalculateRatsnest();
336  }
337 
338  return true;
339 }
340 
341 
345 bool hasThermalConnection( D_PAD* pad, const ZONE_CONTAINER* aZone )
346 {
347  // Rejects non-standard pads with tht-only thermal reliefs
349  && pad->GetAttribute() != PAD_ATTRIB_STANDARD )
350  {
351  return false;
352  }
353 
354  if( aZone->GetPadConnection( pad ) != ZONE_CONNECTION::THERMAL
355  && aZone->GetPadConnection( pad ) != ZONE_CONNECTION::THT_THERMAL )
356  {
357  return false;
358  }
359 
360  if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
361  return false;
362 
363  EDA_RECT item_boundingbox = pad->GetBoundingBox();
364  int thermalGap = aZone->GetThermalReliefGap( pad );
365  item_boundingbox.Inflate( thermalGap, thermalGap );
366 
367  return item_boundingbox.Intersects( aZone->GetBoundingBox() );
368 }
369 
370 
375 static void setupDummyPadForHole( const D_PAD* aPad, D_PAD& aDummyPad )
376 {
377  aDummyPad.SetNetCode( aPad->GetNetCode() );
378  aDummyPad.SetSize( aPad->GetDrillSize() );
379  aDummyPad.SetOrientation( aPad->GetOrientation() );
381  : PAD_SHAPE_CIRCLE );
382  aDummyPad.SetPosition( aPad->GetPosition() );
383 }
384 
385 
390 void ZONE_FILLER::addKnockout( D_PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles )
391 {
392  if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
393  {
394  // the pad shape in zone can be its convex hull or the shape itself
395  SHAPE_POLY_SET outline( aPad->GetCustomShapeAsPolygon() );
396  int numSegs = std::max( GetArcToSegmentCount( aGap, m_high_def, 360.0 ), 6 );
397  double correction = GetCircletoPolyCorrectionFactor( numSegs );
398  outline.Inflate( KiROUND( aGap * correction ), numSegs );
399  aPad->CustomShapeAsPolygonToBoardPosition( &outline, aPad->GetPosition(),
400  aPad->GetOrientation() );
401 
403  {
404  std::vector<wxPoint> convex_hull;
405  BuildConvexHull( convex_hull, outline );
406 
407  aHoles.NewOutline();
408 
409  for( const wxPoint& pt : convex_hull )
410  aHoles.Append( pt );
411  }
412  else
413  aHoles.Append( outline );
414  }
415  else
416  {
417  // Optimizing polygon vertex count: the high definition is used for round
418  // and oval pads (pads with large arcs) but low def for other shapes (with
419  // small arcs)
420  if( aPad->GetShape() == PAD_SHAPE_CIRCLE || aPad->GetShape() == PAD_SHAPE_OVAL ||
421  ( aPad->GetShape() == PAD_SHAPE_ROUNDRECT && aPad->GetRoundRectRadiusRatio() > 0.4 ) )
422  aPad->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_high_def );
423  else
424  aPad->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_low_def );
425  }
426 }
427 
428 
433 void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, int aGap, bool aIgnoreLineWidth,
434  SHAPE_POLY_SET& aHoles )
435 {
436  switch( aItem->Type() )
437  {
438  case PCB_LINE_T:
439  {
440  DRAWSEGMENT* seg = (DRAWSEGMENT*) aItem;
441  seg->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_high_def, aIgnoreLineWidth );
442  break;
443  }
444  case PCB_TEXT_T:
445  {
446  TEXTE_PCB* text = (TEXTE_PCB*) aItem;
447  text->TransformBoundingBoxWithClearanceToPolygon( &aHoles, aGap );
448  break;
449  }
450  case PCB_MODULE_EDGE_T:
451  {
452  EDGE_MODULE* edge = (EDGE_MODULE*) aItem;
453  edge->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_high_def, aIgnoreLineWidth );
454  break;
455  }
456  case PCB_MODULE_TEXT_T:
457  {
458  TEXTE_MODULE* text = (TEXTE_MODULE*) aItem;
459 
460  if( text->IsVisible() )
461  text->TransformBoundingBoxWithClearanceToPolygon( &aHoles, aGap );
462 
463  break;
464  }
465  default:
466  break;
467  }
468 }
469 
470 
476 {
477  SHAPE_POLY_SET holes;
478 
479  // Use a dummy pad to calculate relief when a pad has a hole but is not on the zone's
480  // copper layer. The dummy pad has the size and shape of the original pad's hole. We have
481  // to give it a parent because some functions expect a non-null parent to find clearance
482  // data, etc.
483  MODULE dummymodule( m_board );
484  D_PAD dummypad( &dummymodule );
485 
486  for( auto module : m_board->Modules() )
487  {
488  for( auto pad : module->Pads() )
489  {
490  if( !hasThermalConnection( pad, aZone ) )
491  continue;
492 
493  // If the pad isn't on the current layer but has a hole, knock out a thermal relief
494  // for the hole.
495  if( !pad->IsOnLayer( aZone->GetLayer() ) )
496  {
497  if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
498  continue;
499 
500  setupDummyPadForHole( pad, dummypad );
501  pad = &dummypad;
502  }
503 
504  addKnockout( pad, aZone->GetThermalReliefGap( pad ), holes );
505  }
506  }
507 
510 }
511 
512 
518 {
519  // a small extra clearance to be sure actual track clearance is not smaller
520  // than requested clearance due to many approximations in calculations,
521  // like arc to segment approx, rounding issues...
522  // 2 microns are a good value
523  int extra_margin = Millimeter2iu( 0.002 );
524 
525  int zone_clearance = aZone->GetClearance();
526  int edgeClearance = m_board->GetDesignSettings().m_CopperEdgeClearance;
527  int zone_to_edgecut_clearance = std::max( aZone->GetZoneClearance(), edgeClearance );
528 
529  // items outside the zone bounding box are skipped
530  // the bounding box is the zone bounding box + the biggest clearance found in Netclass list
531  EDA_RECT zone_boundingbox = aZone->GetBoundingBox();
532  int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
533  biggest_clearance = std::max( biggest_clearance, zone_clearance ) + extra_margin;
534  zone_boundingbox.Inflate( biggest_clearance );
535 
536  // Use a dummy pad to calculate hole clearance when a pad has a hole but is not on the
537  // zone's copper layer. The dummy pad has the size and shape of the original pad's hole.
538  // We have to give it a parent because some functions expect a non-null parent to find
539  // clearance data, etc.
540  MODULE dummymodule( m_board );
541  D_PAD dummypad( &dummymodule );
542 
543  // Add non-connected pad clearances
544  //
545  for( auto module : m_board->Modules() )
546  {
547  for( auto pad : module->Pads() )
548  {
549  if( !pad->IsOnLayer( aZone->GetLayer() ) )
550  {
551  if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
552  continue;
553 
554  setupDummyPadForHole( pad, dummypad );
555  pad = &dummypad;
556  }
557 
558  if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0
559  || aZone->GetPadConnection( pad ) == ZONE_CONNECTION::NONE )
560  {
561  // for pads having a netcode different from the zone, use the net clearance:
562  int gap = std::max( zone_clearance, pad->GetClearance() );
563 
564  // for pads having the same netcode as the zone, the net clearance has no
565  // meaning (clearance between object of the same net is 0) and the
566  // zone_clearance can be set to 0 (In this case the netclass clearance is used)
567  // therefore use the antipad clearance (thermal clearance) or the
568  // zone_clearance if bigger.
569  if( pad->GetNetCode() > 0 && pad->GetNetCode() == aZone->GetNetCode() )
570  {
571  int thermalGap = aZone->GetThermalReliefGap( pad );
572  gap = std::max( zone_clearance, thermalGap );;
573  }
574 
575  EDA_RECT item_boundingbox = pad->GetBoundingBox();
576  item_boundingbox.Inflate( pad->GetClearance() );
577 
578  if( item_boundingbox.Intersects( zone_boundingbox ) )
579  addKnockout( pad, gap, aHoles );
580  }
581  }
582  }
583 
584  // Add non-connected track clearances
585  //
586  for( auto track : m_board->Tracks() )
587  {
588  if( !track->IsOnLayer( aZone->GetLayer() ) )
589  continue;
590 
591  if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) )
592  continue;
593 
594  int gap = std::max( zone_clearance, track->GetClearance() ) + extra_margin;
595  EDA_RECT item_boundingbox = track->GetBoundingBox();
596 
597  if( item_boundingbox.Intersects( zone_boundingbox ) )
598  track->TransformShapeWithClearanceToPolygon( aHoles, gap, m_low_def );
599  }
600 
601  // Add graphic item clearances. They are by definition unconnected, and have no clearance
602  // definitions of their own.
603  //
604  auto doGraphicItem = [&]( BOARD_ITEM* aItem )
605  {
606  // A item on the Edge_Cuts is always seen as on any layer:
607  if( !aItem->IsOnLayer( aZone->GetLayer() ) && !aItem->IsOnLayer( Edge_Cuts ) )
608  return;
609 
610  if( !aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
611  return;
612 
613  bool ignoreLineWidth = false;
614  int gap = zone_clearance;
615 
616  if( aItem->IsOnLayer( Edge_Cuts ) )
617  {
618  gap = zone_to_edgecut_clearance;
619 
620  // edge cuts by definition don't have a width
621  ignoreLineWidth = true;
622  }
623 
624  addKnockout( aItem, gap, ignoreLineWidth, aHoles );
625  };
626 
627  for( auto module : m_board->Modules() )
628  {
629  doGraphicItem( &module->Reference() );
630  doGraphicItem( &module->Value() );
631 
632  for( auto item : module->GraphicalItems() )
633  doGraphicItem( item );
634  }
635 
636  for( auto item : m_board->Drawings() )
637  doGraphicItem( item );
638 
639  // Add zones outlines having an higher priority and keepout
640  //
641  for( ZONE_CONTAINER* zone : m_board->GetZoneList( true ) )
642  {
643 
644  // If the zones share no common layers
645  if( !aZone->CommonLayerExists( zone->GetLayerSet() ) )
646  continue;
647 
648  if( !zone->GetIsKeepout() && zone->GetPriority() <= aZone->GetPriority() )
649  continue;
650 
651  if( zone->GetIsKeepout() && !zone->GetDoNotAllowCopperPour() )
652  continue;
653 
654  // A higher priority zone or keepout area is found: remove this area
655  EDA_RECT item_boundingbox = zone->GetBoundingBox();
656 
657  if( !item_boundingbox.Intersects( zone_boundingbox ) )
658  continue;
659 
660  // Add the zone outline area. Don't use any clearance for keepouts, or for zones with
661  // the same net (they will be connected but will honor their own clearance, thermal
662  // connections, etc.).
663  bool sameNet = aZone->GetNetCode() == zone->GetNetCode();
664  bool useNetClearance = true;
665  int minClearance = zone_clearance;
666 
667  // The final clearance is obviously the max value of each zone clearance
668  minClearance = std::max( minClearance, zone->GetClearance() );
669 
670  if( zone->GetIsKeepout() || sameNet )
671  {
672  minClearance = 0;
673  useNetClearance = false;
674  }
675 
676  zone->TransformOutlinesShapeWithClearanceToPolygon( aHoles, minClearance, useNetClearance );
677  }
678 
680 }
681 
682 
695  const SHAPE_POLY_SET& aSmoothedOutline,
696  std::set<VECTOR2I>* aPreserveCorners,
697  SHAPE_POLY_SET& aRawPolys,
698  SHAPE_POLY_SET& aFinalPolys )
699 {
701  m_low_def = std::min( ARC_LOW_DEF, int( m_high_def*1.5 ) ); // Reasonable value
702 
703  // Features which are min_width should survive pruning; features that are *less* than
704  // min_width should not. Therefore we subtract epsilon from the min_width when
705  // deflating/inflating.
706  int half_min_width = aZone->GetMinThickness() / 2;
707  int epsilon = Millimeter2iu( 0.001 );
708  int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
709 
710  // solid polygons are deflated and inflated during calculations.
711  // Polygons deflate usually do not create issues.
712  // Polygons inflate is a tricky transform, because it can create excessively long and narrow 'spikes'
713  // especially for acute angles.
714  // But in very case, the inflate transform caannot create bigger shapes than initial shapes.
715  // so the corner strategy is very important.
716  // The best is SHAPE_POLY_SET::ROUND_ALL_CORNERS.
717  // unfortunately, it creates a lot of small segments.
718  // SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS is not acceptable
719  // So for intermediate transforms, we use CHAMFER_ALL_CORNERS.
720  // For final transform, we use ROUND_ALL_CORNERS
723 
724  std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
725  SHAPE_POLY_SET clearanceHoles;
726 
727  std::unique_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO(
728  s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) );
729 
730  aRawPolys = aSmoothedOutline;
731 
733  dumper->BeginGroup( "clipper-zone" );
734 
735  knockoutThermalReliefs( aZone, aRawPolys );
736 
738  dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" );
739 
740  buildCopperItemClearances( aZone, clearanceHoles );
741 
743  dumper->Write( &aRawPolys, "clearance holes" );
744 
745  buildThermalSpokes( aZone, thermalSpokes );
746 
747  // Create a temporary zone that we can hit-test spoke-ends against. It's only temporary
748  // because the "real" subtract-clearance-holes has to be done after the spokes are added.
749  static const bool USE_BBOX_CACHES = true;
750  SHAPE_POLY_SET testAreas = aRawPolys;
751  testAreas.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
752 
753  // Prune features that don't meet minimum-width criteria
754  if( half_min_width - epsilon > epsilon )
755  {
756  testAreas.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
757  testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
758  }
759 
760  // Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed
761  // things up a bit.
762  testAreas.BuildBBoxCaches();
763 
764  for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes )
765  {
766  const VECTOR2I& testPt = spoke.CPoint( 3 );
767 
768  // Hit-test against zone body
769  if( testAreas.Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
770  {
771  aRawPolys.AddOutline( spoke );
772  continue;
773  }
774 
775  // Hit-test against other spokes
776  for( const SHAPE_LINE_CHAIN& other : thermalSpokes )
777  {
778  if( &other != &spoke && other.PointInside( testPt, 1, USE_BBOX_CACHES ) )
779  {
780  aRawPolys.AddOutline( spoke );
781  break;
782  }
783  }
784  }
785 
786  // Ensure previous changes (adding thermal stubs) do not add
787  // filled areas outside the zone boundary
788  aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
789  aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
790 
792  dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" );
793 
794  aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
795  // Prune features that don't meet minimum-width criteria
796  if( half_min_width - epsilon > epsilon )
797  aRawPolys.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
798 
800  dumper->Write( &aRawPolys, "solid-areas-before-hatching" );
801 
802  // Now remove the non filled areas due to the hatch pattern
804  addHatchFillTypeOnZone( aZone, aRawPolys );
805 
807  dumper->Write( &aRawPolys, "solid-areas-after-hatching" );
808 
809  // Re-inflate after pruning of areas that don't meet minimum-width criteria
810  if( aZone->GetFilledPolysUseThickness() )
811  {
812  // If we're stroking the zone with a min_width stroke then this will naturally
813  // inflate the zone by half_min_width
814  }
815  else if( half_min_width - epsilon > epsilon )
816  {
817  aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
818  aRawPolys.Inflate( half_min_width - epsilon, numSegs, finalcornerStrategy );
819 
820  // If we've deflated/inflated by something near our corner radius then we will have
821  // ended up with too-sharp corners. Apply outline smoothing again.
822  if( aZone->GetMinThickness() > (int)aZone->GetCornerRadius() )
823  aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
824  }
825 
826  aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
827 
829  dumper->Write( &aRawPolys, "areas_fractured" );
830 
831  aFinalPolys = aRawPolys;
832 
834  dumper->EndGroup();
835 }
836 
837 
838 /*
839  * Build the filled solid areas data from real outlines (stored in m_Poly)
840  * The solid areas can be more than one on copper layers, and do not have holes
841  * ( holes are linked by overlapping segments to the main outline)
842  */
844  SHAPE_POLY_SET& aFinalPolys )
845 {
846  SHAPE_POLY_SET smoothedPoly;
847  std::set<VECTOR2I> colinearCorners;
848  aZone->GetColinearCorners( m_board, colinearCorners );
849 
850  /*
851  * convert outlines + holes to outlines without holes (adding extra segments if necessary)
852  * m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building
853  * this zone
854  */
855  if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) )
856  return false;
857 
858  if( aZone->IsOnCopperLayer() )
859  {
860  computeRawFilledArea( aZone, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys );
861  }
862  else
863  {
864  // Features which are min_width should survive pruning; features that are *less* than
865  // min_width should not. Therefore we subtract epsilon from the min_width when
866  // deflating/inflating.
867  int half_min_width = aZone->GetMinThickness() / 2;
868  int epsilon = Millimeter2iu( 0.001 );
869  int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
870 
871  if( m_brdOutlinesValid )
873 
874  smoothedPoly.Deflate( half_min_width/* - epsilon*/, numSegs );
875 
876  // Remove the non filled areas due to the hatch pattern
878  addHatchFillTypeOnZone( aZone, smoothedPoly );
879 
880  // Re-inflate after pruning of areas that don't meet minimum-width criteria
881  if( aZone->GetFilledPolysUseThickness() )
882  {
883  // If we're stroking the zone with a min_width stroke then this will naturally
884  // inflate the zone by half_min_width
885  }
886  else if( half_min_width - epsilon > epsilon )
887  smoothedPoly.Deflate( -( half_min_width - epsilon ), numSegs );
888 
889  aRawPolys = smoothedPoly;
890  aFinalPolys = smoothedPoly;
891 
893  }
894 
895  aZone->SetNeedRefill( false );
896  return true;
897 }
898 
899 
904  std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
905 {
906  auto zoneBB = aZone->GetBoundingBox();
907  int zone_clearance = aZone->GetZoneClearance();
908  int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
909  biggest_clearance = std::max( biggest_clearance, zone_clearance );
910  zoneBB.Inflate( biggest_clearance );
911 
912  // Is a point on the boundary of the polygon inside or outside? This small epsilon lets
913  // us avoid the question.
914  int epsilon = KiROUND( IU_PER_MM * 0.04 ); // about 1.5 mil
915 
916  for( auto module : m_board->Modules() )
917  {
918  for( auto pad : module->Pads() )
919  {
920  if( !hasThermalConnection( pad, aZone ) )
921  continue;
922 
923  // We currently only connect to pads, not pad holes
924  if( !pad->IsOnLayer( aZone->GetLayer() ) )
925  continue;
926 
927  int thermalReliefGap = aZone->GetThermalReliefGap( pad );
928 
929  // Calculate thermal bridge half width
930  int spoke_w = aZone->GetThermalReliefCopperBridge( pad );
931  // Avoid spoke_w bigger than the smaller pad size, because
932  // it is not possible to create stubs bigger than the pad.
933  // Possible refinement: have a separate size for vertical and horizontal stubs
934  spoke_w = std::min( spoke_w, pad->GetSize().x );
935  spoke_w = std::min( spoke_w, pad->GetSize().y );
936 
937  // Cannot create stubs having a width < zone min thickness
938  if( spoke_w <= aZone->GetMinThickness() )
939  continue;
940 
941  int spoke_half_w = spoke_w / 2;
942 
943  // Quick test here to possibly save us some work
944  BOX2I itemBB = pad->GetBoundingBox();
945  itemBB.Inflate( thermalReliefGap + epsilon );
946 
947  if( !( itemBB.Intersects( zoneBB ) ) )
948  continue;
949 
950  // Thermal spokes consist of segments from the pad center to points just outside
951  // the thermal relief.
952  //
953  // We use the bounding-box to lay out the spokes, but for this to work the
954  // bounding box has to be built at the same rotation as the spokes.
955 
956  wxPoint shapePos = pad->ShapePos();
957  wxPoint padPos = pad->GetPosition();
958  double padAngle = pad->GetOrientation();
959  pad->SetOrientation( 0.0 );
960  pad->SetPosition( { 0, 0 } );
961  BOX2I reliefBB = pad->GetBoundingBox();
962  pad->SetPosition( padPos );
963  pad->SetOrientation( padAngle );
964 
965  reliefBB.Inflate( thermalReliefGap + epsilon );
966 
967  // For circle pads, the thermal spoke orientation is 45 deg
968  if( pad->GetShape() == PAD_SHAPE_CIRCLE )
969  padAngle = s_RoundPadThermalSpokeAngle;
970 
971  for( int i = 0; i < 4; i++ )
972  {
973  SHAPE_LINE_CHAIN spoke;
974  switch( i )
975  {
976  case 0: // lower stub
977  spoke.Append( +spoke_half_w, -spoke_half_w );
978  spoke.Append( -spoke_half_w, -spoke_half_w );
979  spoke.Append( -spoke_half_w, reliefBB.GetBottom() );
980  spoke.Append( 0, reliefBB.GetBottom() ); // test pt
981  spoke.Append( +spoke_half_w, reliefBB.GetBottom() );
982  break;
983 
984  case 1: // upper stub
985  spoke.Append( +spoke_half_w, spoke_half_w );
986  spoke.Append( -spoke_half_w, spoke_half_w );
987  spoke.Append( -spoke_half_w, reliefBB.GetTop() );
988  spoke.Append( 0, reliefBB.GetTop() ); // test pt
989  spoke.Append( +spoke_half_w, reliefBB.GetTop() );
990  break;
991 
992  case 2: // right stub
993  spoke.Append( -spoke_half_w, spoke_half_w );
994  spoke.Append( -spoke_half_w, -spoke_half_w );
995  spoke.Append( reliefBB.GetRight(), -spoke_half_w );
996  spoke.Append( reliefBB.GetRight(), 0 ); // test pt
997  spoke.Append( reliefBB.GetRight(), spoke_half_w );
998  break;
999 
1000  case 3: // left stub
1001  spoke.Append( spoke_half_w, spoke_half_w );
1002  spoke.Append( spoke_half_w, -spoke_half_w );
1003  spoke.Append( reliefBB.GetLeft(), -spoke_half_w );
1004  spoke.Append( reliefBB.GetLeft(), 0 ); // test pt
1005  spoke.Append( reliefBB.GetLeft(), spoke_half_w );
1006  break;
1007  }
1008 
1009  spoke.Rotate( -DECIDEG2RAD( padAngle ) );
1010  spoke.Move( shapePos );
1011 
1012  spoke.SetClosed( true );
1013  spoke.GenerateBBoxCache();
1014  aSpokesList.push_back( std::move( spoke ) );
1015  }
1016  }
1017  }
1018 }
1019 
1020 
1022 {
1023  // Build grid:
1024 
1025  // obviously line thickness must be > zone min thickness.
1026  // It can happens if a board file was edited by hand by a python script
1027  // Use 1 micron margin to be *sure* there is no issue in Gerber files
1028  // (Gbr file unit = 1 or 10 nm) due to some truncation in coordinates or calculations
1029  // This margin also avoid problems due to rounding coordinates in next calculations
1030  // that can create incorrect polygons
1031  int thickness = std::max( aZone->GetHatchFillTypeThickness(),
1032  aZone->GetMinThickness()+Millimeter2iu( 0.001 ) );
1033 
1034  int linethickness = thickness - aZone->GetMinThickness();
1035  int gridsize = thickness + aZone->GetHatchFillTypeGap();
1036  double orientation = aZone->GetHatchFillTypeOrientation();
1037 
1038  SHAPE_POLY_SET filledPolys = aRawPolys;
1039  // Use a area that contains the rotated bbox by orientation,
1040  // and after rotate the result by -orientation.
1041  if( orientation != 0.0 )
1042  {
1043  filledPolys.Rotate( M_PI/180.0 * orientation, VECTOR2I( 0,0 ) );
1044  }
1045 
1046  BOX2I bbox = filledPolys.BBox( 0 );
1047 
1048  // Build hole shape
1049  // the hole size is aZone->GetHatchFillTypeGap(), but because the outline thickness
1050  // is aZone->GetMinThickness(), the hole shape size must be larger
1051  SHAPE_LINE_CHAIN hole_base;
1052  int hole_size = aZone->GetHatchFillTypeGap() + aZone->GetMinThickness();
1053  VECTOR2I corner( 0, 0 );;
1054  hole_base.Append( corner );
1055  corner.x += hole_size;
1056  hole_base.Append( corner );
1057  corner.y += hole_size;
1058  hole_base.Append( corner );
1059  corner.x = 0;
1060  hole_base.Append( corner );
1061  hole_base.SetClosed( true );
1062 
1063  // Calculate minimal area of a grid hole.
1064  // All holes smaller than a threshold will be removed
1065  double minimal_hole_area = hole_base.Area() / 2;
1066 
1067  // Now convert this hole to a smoothed shape:
1068  if( aZone->GetHatchFillTypeSmoothingLevel() > 0 )
1069  {
1070  // the actual size of chamfer, or rounded corner radius is the half size
1071  // of the HatchFillTypeGap scaled by aZone->GetHatchFillTypeSmoothingValue()
1072  // aZone->GetHatchFillTypeSmoothingValue() = 1.0 is the max value for the chamfer or the
1073  // radius of corner (radius = half size of the hole)
1074  int smooth_value = KiROUND( aZone->GetHatchFillTypeGap()
1075  * aZone->GetHatchFillTypeSmoothingValue() / 2 );
1076 
1077  // Minimal optimization:
1078  // make smoothing only for reasonnable smooth values, to avoid a lot of useless segments
1079  // and if the smooth value is small, use chamfer even if fillet is requested
1080  #define SMOOTH_MIN_VAL_MM 0.02
1081  #define SMOOTH_SMALL_VAL_MM 0.04
1082  if( smooth_value > Millimeter2iu( SMOOTH_MIN_VAL_MM ) )
1083  {
1084  SHAPE_POLY_SET smooth_hole;
1085  smooth_hole.AddOutline( hole_base );
1086  int smooth_level = aZone->GetHatchFillTypeSmoothingLevel();
1087 
1088  if( smooth_value < Millimeter2iu( SMOOTH_SMALL_VAL_MM ) && smooth_level > 1 )
1089  smooth_level = 1;
1090  // Use a larger smooth_value to compensate the outline tickness
1091  // (chamfer is not visible is smooth value < outline thickess)
1092  smooth_value += aZone->GetMinThickness()/2;
1093 
1094  // smooth_value cannot be bigger than the half size oh the hole:
1095  smooth_value = std::min( smooth_value, aZone->GetHatchFillTypeGap()/2 );
1096  // the error to approximate a circle by segments when smoothing corners by a arc
1097  int error_max = std::max( Millimeter2iu( 0.01), smooth_value/20 );
1098 
1099  switch( smooth_level )
1100  {
1101  case 1:
1102  // Chamfer() uses the distance from a corner to create a end point
1103  // for the chamfer.
1104  hole_base = smooth_hole.Chamfer( smooth_value ).Outline( 0 );
1105  break;
1106 
1107  default:
1108  if( aZone->GetHatchFillTypeSmoothingLevel() > 2 )
1109  error_max /= 2; // Force better smoothing
1110  hole_base = smooth_hole.Fillet( smooth_value, error_max ).Outline( 0 );
1111  break;
1112 
1113  case 0:
1114  break;
1115  };
1116  }
1117  }
1118 
1119  // Build holes
1120  SHAPE_POLY_SET holes;
1121 
1122  for( int xx = 0; ; xx++ )
1123  {
1124  int xpos = xx * gridsize;
1125 
1126  if( xpos > bbox.GetWidth() )
1127  break;
1128 
1129  for( int yy = 0; ; yy++ )
1130  {
1131  int ypos = yy * gridsize;
1132 
1133  if( ypos > bbox.GetHeight() )
1134  break;
1135 
1136  // Generate hole
1137  SHAPE_LINE_CHAIN hole( hole_base );
1138  hole.Move( VECTOR2I( xpos, ypos ) );
1139  holes.AddOutline( hole );
1140  }
1141  }
1142 
1143  holes.Move( bbox.GetPosition() );
1144 
1145  // Clamp holes to the area of filled zones with a outline thickness
1146  // > aZone->GetMinThickness() to be sure the thermal pads can be built
1147  int outline_margin = std::max( (aZone->GetMinThickness()*10)/9, linethickness/2 );
1148  filledPolys.Deflate( outline_margin, 16 );
1149  holes.BooleanIntersection( filledPolys, SHAPE_POLY_SET::PM_FAST );
1150 
1151  if( orientation != 0.0 )
1152  holes.Rotate( -M_PI/180.0 * orientation, VECTOR2I( 0,0 ) );
1153 
1154  // Now filter truncated holes to avoid small holes in pattern
1155  // It happens for holes near the zone outline
1156  for( int ii = 0; ii < holes.OutlineCount(); )
1157  {
1158  double area = holes.Outline( ii ).Area();
1159 
1160  if( area < minimal_hole_area ) // The current hole is too small: remove it
1161  holes.DeletePolygon( ii );
1162  else
1163  ++ii;
1164  }
1165 
1166  // create grid. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to
1167  // generate strictly simple polygons needed by Gerber files and Fracture()
1168  aRawPolys.BooleanSubtract( aRawPolys, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
1169 }
void SetFilledPolysList(SHAPE_POLY_SET &aPolysList)
Function SetFilledPolysList sets the list of filled polygons.
Definition: class_zone.h:585
virtual void Revert()=0
Revertes the commit by restoring the modifed items state.
void AdvancePhase()
Uses the next vailable virtual zone of the dialog progress bar.
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 })
Function Rotate rotates all vertices by a given angle.
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:60
COMMIT & Modify(EDA_ITEM *aItem)
Modifies a given item in the model.
Definition: commit.h:103
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox
Definition: confirm.cpp:53
SHAPE_FILE_IO.
Definition: shape_file_io.h:39
int GetNetCode() const
Function GetNetCode.
int OutlineCount() const
Returns the number of outlines in the set
All angles are chamfered.
TEXTE_PCB class definition.
bool CommonLayerExists(const LSET aLayerSet) const
Function CommonLayerExist Test if this zone shares a common layer with the given layer set.
Definition: class_zone.cpp:232
int GetThermalReliefCopperBridge(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:542
void SetFilledPolysUseThickness(bool aOption)
Definition: class_zone.h:618
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:44
This file is part of the common library.
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
BOARD * m_board
Definition: zone_filler.h:106
coord_type GetTop() const
Definition: box2.h:203
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox (virtual)
Definition: class_zone.cpp:506
int GetBiggestClearanceValue()
Function GetBiggestClearanceValue.
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, int aClearanceValue, int aError=ARC_HIGH_DEF, bool ignoreLineWidth=false) const override
Function TransformShapeWithClearanceToPolygon Convert the draw segment to a closed polygon Used in fi...
void Rotate(double aAngle, const VECTOR2I &aCenter=VECTOR2I(0, 0))
Function Rotate rotates all vertices by a given angle.
PROGRESS_REPORTER_HIDER(WX_PROGRESS_REPORTER *aReporter)
Definition: zone_filler.cpp:54
A progress reporter for use in multi-threaded environments.
int GetHatchFillTypeThickness() const
Definition: class_zone.h:213
bool IsVisible() const
Definition: eda_text.h:170
void addKnockout(D_PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad.
WX_PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:111
void computeRawFilledArea(const ZONE_CONTAINER *aZone, const SHAPE_POLY_SET &aSmoothedOutline, std::set< VECTOR2I > *aPreserveCorners, SHAPE_POLY_SET &aRawPolys, SHAPE_POLY_SET &aFinalPolys)
Function computeRawFilledArea Add non copper areas polygons (pads and tracks with clearance) to a fil...
void Move(const VECTOR2I &aVector) override
static const double s_RoundPadThermalSpokeAngle
Definition: zone_filler.cpp:73
virtual PCB_LAYER_ID GetLayer() const override
Function GetLayer returns the primary layer this item is on.
Definition: class_zone.cpp:213
#define SMOOTH_MIN_VAL_MM
CORNER_STRATEGY
< define how inflate transform build inflated polygon
void Report(const wxString &aMessage)
Display aMessage in the progress bar dialog.
bool GetFilledPolysUseThickness() const
Definition: class_zone.h:617
class TEXTE_PCB, text on a layer
Definition: typeinfo.h:92
double GetHatchFillTypeOrientation() const
Definition: class_zone.h:219
coord_type GetRight() const
Definition: box2.h:198
void buildCopperItemClearances(const ZONE_CONTAINER *aZone, SHAPE_POLY_SET &aHoles)
Removes clearance from the shape for copper items which share the zone's layer but are not connected ...
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:240
COMMIT.
Definition: commit.h:71
double GetHatchFillTypeSmoothingValue() const
Definition: class_zone.h:225
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:512
std::unique_ptr< WX_PROGRESS_REPORTER > m_uniqueReporter
Definition: zone_filler.h:112
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Returns true if a given subpolygon contains the point aP.
void TransformBoundingBoxWithClearanceToPolygon(SHAPE_POLY_SET *aCornerBuffer, int aClearanceValue) const
Convert the text bounding box to a rectangular polygon depending on the text orientation,...
coord_type GetBottom() const
Definition: box2.h:199
void Inflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Performs outline inflation/deflation.
int GetThermalReliefGap(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:533
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
#define SMOOTH_SMALL_VAL_MM
void SetIsFilled(bool isFilled)
Definition: class_zone.h:189
static const bool s_DumpZonesWhenFilling
Definition: zone_filler.cpp:74
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
int GetClearance(BOARD_CONNECTED_ITEM *aItem=NULL) const override
Function GetClearance returns the clearance in internal units.
Definition: class_zone.cpp:659
PAD_ATTR_T GetAttribute() const
Definition: class_pad.h:449
Definitions for tracks, vias and zones.
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
void InstallNewProgressReporter(wxWindow *aParent, const wxString &aTitle, int aNumPhases)
Definition: zone_filler.cpp:93
void DeletePolygon(int aIdx)
Deletes aIdx-th polygon from the set
WX_PROGRESS_REPORTER * m_reporter
Definition: zone_filler.cpp:69
bool Intersects(const BOX2< Vec > &aRect) const
Function Intersects.
Definition: box2.h:235
void GetColinearCorners(BOARD *aBoard, std::set< VECTOR2I > &colinearCorners)
Some intersecting zones, despite being on the same layer with the same net, cannot be merged due to o...
void SetClosed(bool aClosed)
Function SetClosed()
unsigned int GetCornerRadius() const
Definition: class_zone.h:615
void Move(const VECTOR2I &aVector) override
COMMIT * m_commit
Definition: zone_filler.h:110
MODULES & Modules()
Definition: class_board.h:229
double GetCircletoPolyCorrectionFactor(int aSegCountforCircle)
SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
ZONE_FILLER(BOARD *aBoard, COMMIT *aCommit=nullptr)
Definition: zone_filler.cpp:77
bool BuildSmoothedPoly(SHAPE_POLY_SET &aSmoothedPoly, std::set< VECTOR2I > *aPreserveCorners) const
Function GetSmoothedPoly returns a pointer to the corner-smoothed version of m_Poly.
ZONE_FILL_MODE GetFillMode() const
Definition: class_zone.h:156
coord_type GetWidth() const
Definition: box2.h:196
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: class_pad.h:432
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Function GetConnectivity() returns list of missing connections between components/tracks.
Definition: class_board.h:306
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void SetSize(const wxSize &aSize)
Definition: class_pad.h:299
bool SetNetCode(int aNetCode, bool aNoAssert=false)
Sets net using a net code.
a few functions useful in geometry calculations.
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, wxString *aErrorText=nullptr, wxPoint *aErrorLocation=nullptr)
Function GetBoardPolygonOutlines Extracts the board outlines and build a closed polygon from lines,...
bool Fill(const std::vector< ZONE_CONTAINER * > &aZones, bool aCheck=false)
ZONE_CONNECTION GetPadConnection(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:879
void knockoutThermalReliefs(const ZONE_CONTAINER *aZone, SHAPE_POLY_SET &aFill)
Removes thermal reliefs from the shape for any pads connected to the zone.
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset intersection For aFastMode meaning, see function booleanOp
int NewOutline()
Creates a new empty polygon in the set and returns its index
void addHatchFillTypeOnZone(const ZONE_CONTAINER *aZone, SHAPE_POLY_SET &aRawPolys)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
void Fracture(POLYGON_MODE aFastMode)
Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the oute...
void SetRawPolysList(SHAPE_POLY_SET &aPolysList)
Function SetFilledPolysList sets the list of filled polygons.
Definition: class_zone.h:594
Thermal relief only for THT pads.
void BuildBBoxCaches()
Constructs BBoxCaches for Contains(), below.
double Area() const
const Vec & GetPosition() const
Definition: box2.h:193
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
Definition: class_pad.h:253
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax, std::set< VECTOR2I > *aPreserveCorners=nullptr)
Function Fillet returns a filleted version of the polygon set.
class TEXTE_MODULE, text in a footprint
Definition: typeinfo.h:93
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, int aClearanceValue, int aMaxError=ARC_HIGH_DEF, bool ignoreLineWidth=false) const override
Function TransformShapeWithClearanceToPolygon Convert the pad shape to a closed polygon Used in filli...
Use thermal relief for pads.
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:301
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true)=0
Executes the changes.
unsigned GetPriority() const
Function GetPriority.
Definition: class_zone.h:100
Class to handle a graphic segment.
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:163
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
bool m_ZoneUseNoOutlineInFill
Option to handle filled polygons in zones: the "legacy" option is using thick outlines around filled ...
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees,...
Definition: class_pad.h:426
void CustomShapeAsPolygonToBoardPosition(SHAPE_POLY_SET *aMergedPolygon, wxPoint aPosition, double aRotation) const
When created, the corners coordinates are relative to the pad position, orientation 0,...
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
bool KeepRefreshing(bool aWait=false)
Update the UI dialog.
const wxSize & GetDrillSize() const
Definition: class_pad.h:306
const SHAPE_POLY_SET & GetCustomShapeAsPolygon() const
Accessor to the custom shape as one polygon.
Definition: class_pad.h:378
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
double DECIDEG2RAD(double deg)
Definition: trigo.h:218
SHAPE_POLY_SET Chamfer(int aDistance, std::set< VECTOR2I > *aPreserveCorners=nullptr)
Function Chamfer returns a chamfered version of the polygon set.
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:61
coord_type GetHeight() const
Definition: box2.h:197
int GetMinThickness() const
Definition: class_zone.h:204
Pads are not covered.
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:238
bool Intersects(const EDA_RECT &aRect) const
Function Intersects tests for a common area between rectangles.
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:438
double GetRoundRectRadiusRatio() const
has meaning only for rounded rect pads
Definition: class_pad.h:675
SHAPE_POLY_SET m_boardOutline
Definition: zone_filler.h:107
int GetHatchFillTypeSmoothingLevel() const
Definition: class_zone.h:222
PCB_TARGET class definition.
bool m_brdOutlinesValid
Definition: zone_filler.h:108
static void setupDummyPadForHole(const D_PAD *aPad, D_PAD &aDummyPad)
Setup aDummyPad to have the same size and shape of aPad's hole.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset difference For aFastMode meaning, see function booleanOp
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:237
Module description (excepted pads)
coord_type GetLeft() const
Definition: box2.h:202
int GetZoneClearance() const
Definition: class_zone.h:194
const wxPoint GetPosition() const override
Definition: class_pad.h:241
EDGE_MODULE class definition.
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
void SetMaxProgress(int aMaxProgress)
Fix the value thar gives the 100 precent progress bar length (inside the current virtual zone)
void AdvanceProgress()
Increment the progress bar length (inside the current virtual zone)
const BOX2I BBox(int aClearance=0) const override
Function BBox()
int GetHatchFillTypeGap() const
Definition: class_zone.h:216
DRAWINGS & Drawings()
Definition: class_board.h:238
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
void buildThermalSpokes(const ZONE_CONTAINER *aZone, std::deque< SHAPE_LINE_CHAIN > &aSpokes)
Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone.
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes.
Definition: class_pad.cpp:229
TRACKS & Tracks()
Definition: class_board.h:220
bool IsOnCopperLayer() const override
Function IsOnCopperLayer.
Definition: class_zone.cpp:219
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212
bool fillSingleZone(ZONE_CONTAINER *aZone, 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...
std::list< ZONE_CONTAINER * > GetZoneList(bool aIncludeZonesInFootprints=false)
Function GetZoneList.
bool hasThermalConnection(D_PAD *pad, const ZONE_CONTAINER *aZone)
Return true if the given pad has a thermal connection with the given zone.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
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:89
void SetNeedRefill(bool aNeedRefill)
Definition: class_zone.h:192