KiCad PCB EDA Suite
ZONE_FILLER Class Reference

#include <zone_filler.h>

Public Member Functions

 ZONE_FILLER (BOARD *aBoard, COMMIT *aCommit=nullptr)
 
 ~ZONE_FILLER ()
 
void InstallNewProgressReporter (wxWindow *aParent, const wxString &aTitle, int aNumPhases)
 
bool Fill (const std::vector< ZONE_CONTAINER * > &aZones, bool aCheck=false)
 

Private Member Functions

void addKnockout (D_PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
 Add a knockout for a pad. More...
 
void addKnockout (BOARD_ITEM *aItem, int aGap, bool aIgnoreLineWidth, SHAPE_POLY_SET &aHoles)
 Add a knockout for a graphic item. More...
 
void knockoutThermalReliefs (const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFill)
 Removes thermal reliefs from the shape for any pads connected to the zone. More...
 
void buildCopperItemClearances (const ZONE_CONTAINER *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 to it. More...
 
void computeRawFilledArea (const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, 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 filled copper area used in BuildFilledSolidAreasPolygons when calculating filled areas in a zone Non copper areas are pads and track and their clearance area The filled copper area must be computed before BuildFilledSolidAreasPolygons() call this function just after creating the filled copper area polygon (without clearance areas. More...
 
void buildThermalSpokes (const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, std::deque< SHAPE_LINE_CHAIN > &aSpokes)
 Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone. More...
 
bool fillSingleZone (ZONE_CONTAINER *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 more than one on copper layers, and do not have holes ( holes are linked by overlapping segments to the main outline) in order to have drawable (and plottable) filled polygons. More...
 
void addHatchFillTypeOnZone (const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawPolys)
 for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled areas of aZone, giving to the filled polygons a fill style like a grid More...
 

Private Attributes

BOARDm_board
 
SHAPE_POLY_SET m_boardOutline
 
bool m_brdOutlinesValid
 
COMMITm_commit
 
WX_PROGRESS_REPORTERm_progressReporter
 
std::unique_ptr< WX_PROGRESS_REPORTERm_uniqueReporter
 
int m_high_def
 
int m_low_def
 

Detailed Description

Definition at line 39 of file zone_filler.h.

Constructor & Destructor Documentation

◆ ZONE_FILLER()

ZONE_FILLER::ZONE_FILLER ( BOARD aBoard,
COMMIT aCommit = nullptr 
)

Definition at line 79 of file zone_filler.cpp.

79  :
80  m_board( aBoard ),
81  m_brdOutlinesValid( false ),
82  m_commit( aCommit ),
83  m_progressReporter( nullptr ),
84  m_high_def( 9 ),
85  m_low_def( 6 )
86 {
87 }
BOARD * m_board
Definition: zone_filler.h:110
WX_PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:115
COMMIT * m_commit
Definition: zone_filler.h:114
bool m_brdOutlinesValid
Definition: zone_filler.h:112

◆ ~ZONE_FILLER()

ZONE_FILLER::~ZONE_FILLER ( )

Definition at line 90 of file zone_filler.cpp.

91 {
92 }

Member Function Documentation

◆ addHatchFillTypeOnZone()

void ZONE_FILLER::addHatchFillTypeOnZone ( const ZONE_CONTAINER aZone,
PCB_LAYER_ID  aLayer,
SHAPE_POLY_SET aRawPolys 
)
private

for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled areas of aZone, giving to the filled polygons a fill style like a grid

Parameters
aZoneis the zone to modify
aRawPolysA reference to a SHAPE_POLY_SET buffer containing the initial filled areas, and after adding the grid pattern, the modified filled areas with holes

Definition at line 1128 of file zone_filler.cpp.

1130 {
1131  // Build grid:
1132 
1133  // obviously line thickness must be > zone min thickness.
1134  // It can happens if a board file was edited by hand by a python script
1135  // Use 1 micron margin to be *sure* there is no issue in Gerber files
1136  // (Gbr file unit = 1 or 10 nm) due to some truncation in coordinates or calculations
1137  // This margin also avoid problems due to rounding coordinates in next calculations
1138  // that can create incorrect polygons
1139  int thickness = std::max( aZone->GetHatchThickness(),
1140  aZone->GetMinThickness() + Millimeter2iu( 0.001 ) );
1141 
1142  int linethickness = thickness - aZone->GetMinThickness();
1143  int gridsize = thickness + aZone->GetHatchGap();
1144  double orientation = aZone->GetHatchOrientation();
1145 
1146  SHAPE_POLY_SET filledPolys = aRawPolys;
1147  // Use a area that contains the rotated bbox by orientation,
1148  // and after rotate the result by -orientation.
1149  if( orientation != 0.0 )
1150  filledPolys.Rotate( M_PI/180.0 * orientation, VECTOR2I( 0,0 ) );
1151 
1152  BOX2I bbox = filledPolys.BBox( 0 );
1153 
1154  // Build hole shape
1155  // the hole size is aZone->GetHatchGap(), but because the outline thickness
1156  // is aZone->GetMinThickness(), the hole shape size must be larger
1157  SHAPE_LINE_CHAIN hole_base;
1158  int hole_size = aZone->GetHatchGap() + aZone->GetMinThickness();
1159  VECTOR2I corner( 0, 0 );;
1160  hole_base.Append( corner );
1161  corner.x += hole_size;
1162  hole_base.Append( corner );
1163  corner.y += hole_size;
1164  hole_base.Append( corner );
1165  corner.x = 0;
1166  hole_base.Append( corner );
1167  hole_base.SetClosed( true );
1168 
1169  // Calculate minimal area of a grid hole.
1170  // All holes smaller than a threshold will be removed
1171  double minimal_hole_area = hole_base.Area() * aZone->GetHatchHoleMinArea();
1172 
1173  // Now convert this hole to a smoothed shape:
1174  if( aZone->GetHatchSmoothingLevel() > 0 )
1175  {
1176  // the actual size of chamfer, or rounded corner radius is the half size
1177  // of the HatchFillTypeGap scaled by aZone->GetHatchSmoothingValue()
1178  // aZone->GetHatchSmoothingValue() = 1.0 is the max value for the chamfer or the
1179  // radius of corner (radius = half size of the hole)
1180  int smooth_value = KiROUND( aZone->GetHatchGap()
1181  * aZone->GetHatchSmoothingValue() / 2 );
1182 
1183  // Minimal optimization:
1184  // make smoothing only for reasonnable smooth values, to avoid a lot of useless segments
1185  // and if the smooth value is small, use chamfer even if fillet is requested
1186  #define SMOOTH_MIN_VAL_MM 0.02
1187  #define SMOOTH_SMALL_VAL_MM 0.04
1188 
1189  if( smooth_value > Millimeter2iu( SMOOTH_MIN_VAL_MM ) )
1190  {
1191  SHAPE_POLY_SET smooth_hole;
1192  smooth_hole.AddOutline( hole_base );
1193  int smooth_level = aZone->GetHatchSmoothingLevel();
1194 
1195  if( smooth_value < Millimeter2iu( SMOOTH_SMALL_VAL_MM ) && smooth_level > 1 )
1196  smooth_level = 1;
1197 
1198  // Use a larger smooth_value to compensate the outline tickness
1199  // (chamfer is not visible is smooth value < outline thickess)
1200  smooth_value += aZone->GetMinThickness() / 2;
1201 
1202  // smooth_value cannot be bigger than the half size oh the hole:
1203  smooth_value = std::min( smooth_value, aZone->GetHatchGap() / 2 );
1204 
1205  // the error to approximate a circle by segments when smoothing corners by a arc
1206  int error_max = std::max( Millimeter2iu( 0.01 ), smooth_value / 20 );
1207 
1208  switch( smooth_level )
1209  {
1210  case 1:
1211  // Chamfer() uses the distance from a corner to create a end point
1212  // for the chamfer.
1213  hole_base = smooth_hole.Chamfer( smooth_value ).Outline( 0 );
1214  break;
1215 
1216  default:
1217  if( aZone->GetHatchSmoothingLevel() > 2 )
1218  error_max /= 2; // Force better smoothing
1219 
1220  hole_base = smooth_hole.Fillet( smooth_value, error_max ).Outline( 0 );
1221  break;
1222 
1223  case 0:
1224  break;
1225  };
1226  }
1227  }
1228 
1229  // Build holes
1230  SHAPE_POLY_SET holes;
1231 
1232  for( int xx = 0; ; xx++ )
1233  {
1234  int xpos = xx * gridsize;
1235 
1236  if( xpos > bbox.GetWidth() )
1237  break;
1238 
1239  for( int yy = 0; ; yy++ )
1240  {
1241  int ypos = yy * gridsize;
1242 
1243  if( ypos > bbox.GetHeight() )
1244  break;
1245 
1246  // Generate hole
1247  SHAPE_LINE_CHAIN hole( hole_base );
1248  hole.Move( VECTOR2I( xpos, ypos ) );
1249  holes.AddOutline( hole );
1250  }
1251  }
1252 
1253  holes.Move( bbox.GetPosition() );
1254 
1255  // We must buffer holes by at least aZone->GetMinThickness() to guarantee that thermal
1256  // reliefs can be built (and to give the zone a solid outline). However, it looks more
1257  // visually consistent if the buffer width is the same as the hatch width.
1258  int outline_margin = KiROUND( aZone->GetMinThickness() * 1.1 );
1259 
1260  if( aZone->GetHatchBorderAlgorithm() )
1261  outline_margin = std::max( outline_margin, aZone->GetHatchThickness() );
1262 
1263  if( outline_margin > linethickness / 2 )
1264  filledPolys.Deflate( outline_margin - linethickness / 2, 16 );
1265 
1266  holes.BooleanIntersection( filledPolys, SHAPE_POLY_SET::PM_FAST );
1267 
1268  if( orientation != 0.0 )
1269  holes.Rotate( -M_PI/180.0 * orientation, VECTOR2I( 0,0 ) );
1270 
1271  if( aZone->GetNetCode() != 0 )
1272  {
1273  // Vias and pads connected to the zone must not be allowed to become isolated inside
1274  // one of the holes. Effectively this means their copper outline needs to be expanded
1275  // to be at least as wide as the gap so that it is guaranteed to touch at least one
1276  // edge.
1277  EDA_RECT zone_boundingbox = aZone->GetBoundingBox();
1278  SHAPE_POLY_SET aprons;
1279  int min_apron_radius = ( aZone->GetHatchGap() * 10 ) / 19;
1280 
1281  for( TRACK* track : m_board->Tracks() )
1282  {
1283  if( track->Type() == PCB_VIA_T )
1284  {
1285  VIA* via = static_cast<VIA*>( track );
1286 
1287  if( via->GetNetCode() == aZone->GetNetCode()
1288  && via->IsOnLayer( aLayer )
1289  && via->GetBoundingBox().Intersects( zone_boundingbox ) )
1290  {
1291  int r = std::max( min_apron_radius,
1292  via->GetDrillValue() / 2 + outline_margin );
1293 
1294  TransformCircleToPolygon( aprons, via->GetPosition(), r, ARC_HIGH_DEF );
1295  }
1296  }
1297  }
1298 
1299  for( MODULE* module : m_board->Modules() )
1300  {
1301  for( D_PAD* pad : module->Pads() )
1302  {
1303  if( pad->GetNetCode() == aZone->GetNetCode()
1304  && pad->IsOnLayer( aLayer )
1305  && pad->GetBoundingBox().Intersects( zone_boundingbox ) )
1306  {
1307  // What we want is to bulk up the pad shape so that the narrowest bit of
1308  // copper between the hole and the apron edge is at least outline_margin
1309  // wide (and that the apron itself meets min_apron_radius. But that would
1310  // take a lot of code and math, and the following approximation is close
1311  // enough.
1312  int pad_width = std::min( pad->GetSize().x, pad->GetSize().y );
1313  int slot_width = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
1314  int min_annulus = ( pad_width - slot_width ) / 2;
1315  int clearance = std::max( min_apron_radius - pad_width / 2,
1316  outline_margin - min_annulus );
1317 
1318  clearance = std::max( 0, clearance - linethickness / 2 );
1319  pad->TransformShapeWithClearanceToPolygon( aprons, clearance, ARC_HIGH_DEF );
1320  }
1321  }
1322  }
1323 
1324  holes.BooleanSubtract( aprons, SHAPE_POLY_SET::PM_FAST );
1325  }
1326 
1327  // Now filter truncated holes to avoid small holes in pattern
1328  // It happens for holes near the zone outline
1329  for( int ii = 0; ii < holes.OutlineCount(); )
1330  {
1331  double area = holes.Outline( ii ).Area();
1332 
1333  if( area < minimal_hole_area ) // The current hole is too small: remove it
1334  holes.DeletePolygon( ii );
1335  else
1336  ++ii;
1337  }
1338 
1339  // create grid. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to
1340  // generate strictly simple polygons needed by Gerber files and Fracture()
1341  aRawPolys.BooleanSubtract( aRawPolys, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
1342 }
int GetNetCode() const
Function GetNetCode.
int OutlineCount() const
Returns the number of outlines in the set
int GetHatchBorderAlgorithm() const
Definition: class_zone.h:233
BOARD * m_board
Definition: zone_filler.h:110
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox (virtual)
Definition: class_zone.cpp:311
#define SMOOTH_MIN_VAL_MM
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes.
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Function Rotate rotates all vertices by a given angle.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
#define SMOOTH_SMALL_VAL_MM
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
void DeletePolygon(int aIdx)
Deletes aIdx-th polygon from the set
int GetHatchGap() const
Definition: class_zone.h:218
void SetClosed(bool aClosed)
Function SetClosed()
void Move(const VECTOR2I &aVector) override
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
MODULES & Modules()
Definition: class_board.h:266
SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
coord_type GetWidth() const
Definition: box2.h:197
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset intersection For aFastMode meaning, see function booleanOp
double GetHatchOrientation() const
Definition: class_zone.h:221
const Vec & GetPosition() const
Definition: box2.h:194
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax, std::set< VECTOR2I > *aPreserveCorners=nullptr)
Function Fillet returns a filleted version of the polygon set.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index
double GetHatchSmoothingValue() const
Definition: class_zone.h:227
SHAPE_LINE_CHAIN.
void TransformCircleToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCenter, int aRadius, int aError)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
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:68
coord_type GetHeight() const
Definition: box2.h:198
int GetMinThickness() const
Definition: class_zone.h:206
bool Intersects(const EDA_RECT &aRect) const
Function Intersects tests for a common area between rectangles.
int GetHatchSmoothingLevel() const
Definition: class_zone.h:224
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
int GetHatchThickness() const
Definition: class_zone.h:215
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset difference For aFastMode meaning, see function booleanOp
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Function IsOnLayer tests to see if this object is on the given layer.
wxPoint GetPosition() const override
Definition: class_track.h:416
static constexpr int Millimeter2iu(double mm)
const BOX2I BBox(int aClearance=0) const override
Function BBox()
TRACKS & Tracks()
Definition: class_board.h:257
double GetHatchHoleMinArea() const
Definition: class_zone.h:230

References SHAPE_POLY_SET::AddOutline(), SHAPE_LINE_CHAIN::Append(), SHAPE_LINE_CHAIN::Area(), SHAPE_POLY_SET::BBox(), SHAPE_POLY_SET::BooleanIntersection(), SHAPE_POLY_SET::BooleanSubtract(), SHAPE_POLY_SET::Chamfer(), SHAPE_POLY_SET::Deflate(), SHAPE_POLY_SET::DeletePolygon(), SHAPE_POLY_SET::Fillet(), ZONE_CONTAINER::GetBoundingBox(), TRACK::GetBoundingBox(), VIA::GetDrillValue(), ZONE_CONTAINER::GetHatchBorderAlgorithm(), ZONE_CONTAINER::GetHatchGap(), ZONE_CONTAINER::GetHatchHoleMinArea(), ZONE_CONTAINER::GetHatchOrientation(), ZONE_CONTAINER::GetHatchSmoothingLevel(), ZONE_CONTAINER::GetHatchSmoothingValue(), ZONE_CONTAINER::GetHatchThickness(), BOX2< Vec >::GetHeight(), ZONE_CONTAINER::GetMinThickness(), BOARD_CONNECTED_ITEM::GetNetCode(), BOX2< Vec >::GetPosition(), VIA::GetPosition(), BOX2< Vec >::GetWidth(), EDA_RECT::Intersects(), VIA::IsOnLayer(), KiROUND(), m_board, Millimeter2iu(), BOARD::Modules(), SHAPE_LINE_CHAIN::Move(), SHAPE_POLY_SET::Move(), SHAPE_POLY_SET::Outline(), SHAPE_POLY_SET::OutlineCount(), PCB_VIA_T, SHAPE_POLY_SET::PM_FAST, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE, SHAPE_POLY_SET::Rotate(), SHAPE_LINE_CHAIN::SetClosed(), SMOOTH_MIN_VAL_MM, SMOOTH_SMALL_VAL_MM, BOARD::Tracks(), TransformCircleToPolygon(), VECTOR2< T >::x, and VECTOR2< T >::y.

Referenced by computeRawFilledArea(), and fillSingleZone().

◆ addKnockout() [1/2]

void ZONE_FILLER::addKnockout ( D_PAD aPad,
int  aGap,
SHAPE_POLY_SET aHoles 
)
private

Add a knockout for a pad.

The knockout is 'aGap' larger than the pad (which might be either the thermal clearance or the electrical clearance).

Definition at line 480 of file zone_filler.cpp.

481 {
482  if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
483  {
484  SHAPE_POLY_SET poly;
486 
487  // the pad shape in zone can be its convex hull or the shape itself
489  {
490  std::vector<wxPoint> convex_hull;
491  BuildConvexHull( convex_hull, poly );
492 
493  aHoles.NewOutline();
494 
495  for( const wxPoint& pt : convex_hull )
496  aHoles.Append( pt );
497  }
498  else
499  aHoles.Append( poly );
500  }
501  else
502  {
503  // Optimizing polygon vertex count: the high definition is used for round
504  // and oval pads (pads with large arcs) but low def for other shapes (with
505  // small arcs)
506  if( aPad->GetShape() == PAD_SHAPE_CIRCLE || aPad->GetShape() == PAD_SHAPE_OVAL ||
507  ( aPad->GetShape() == PAD_SHAPE_ROUNDRECT && aPad->GetRoundRectRadiusRatio() > 0.4 ) )
508  aPad->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_high_def );
509  else
510  aPad->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_low_def );
511  }
512 }
SHAPE_POLY_SET.
int NewOutline()
Creates a new empty polygon in the set and returns its index
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
Definition: class_pad.h:177
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.
double GetRoundRectRadiusRatio() const
Definition: class_pad.h:480
PAD_SHAPE_T GetShape() const
Definition: class_pad.h:157
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

References SHAPE_POLY_SET::Append(), BuildConvexHull(), CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL, D_PAD::GetCustomShapeInZoneOpt(), D_PAD::GetRoundRectRadiusRatio(), D_PAD::GetShape(), m_high_def, m_low_def, SHAPE_POLY_SET::NewOutline(), PAD_SHAPE_CIRCLE, PAD_SHAPE_CUSTOM, PAD_SHAPE_OVAL, PAD_SHAPE_ROUNDRECT, and D_PAD::TransformShapeWithClearanceToPolygon().

Referenced by buildCopperItemClearances(), and knockoutThermalReliefs().

◆ addKnockout() [2/2]

void ZONE_FILLER::addKnockout ( BOARD_ITEM aItem,
int  aGap,
bool  aIgnoreLineWidth,
SHAPE_POLY_SET aHoles 
)
private

Add a knockout for a graphic item.

The knockout is 'aGap' larger than the item (which might be either the electrical clearance or the board edge clearance).

Definition at line 519 of file zone_filler.cpp.

521 {
522  switch( aItem->Type() )
523  {
524  case PCB_LINE_T:
525  {
526  DRAWSEGMENT* seg = (DRAWSEGMENT*) aItem;
527  seg->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_high_def, aIgnoreLineWidth );
528  break;
529  }
530  case PCB_TEXT_T:
531  {
532  TEXTE_PCB* text = (TEXTE_PCB*) aItem;
533  text->TransformBoundingBoxWithClearanceToPolygon( &aHoles, aGap );
534  break;
535  }
536  case PCB_MODULE_EDGE_T:
537  {
538  EDGE_MODULE* edge = (EDGE_MODULE*) aItem;
539  edge->TransformShapeWithClearanceToPolygon( aHoles, aGap, m_high_def, aIgnoreLineWidth );
540  break;
541  }
542  case PCB_MODULE_TEXT_T:
543  {
544  TEXTE_MODULE* text = (TEXTE_MODULE*) aItem;
545 
546  if( text->IsVisible() )
547  text->TransformBoundingBoxWithClearanceToPolygon( &aHoles, aGap );
548 
549  break;
550  }
551  default:
552  break;
553  }
554 }
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...
bool IsVisible() const
Definition: eda_text.h:186
class TEXTE_PCB, text on a layer
Definition: typeinfo.h:92
void TransformBoundingBoxWithClearanceToPolygon(SHAPE_POLY_SET *aCornerBuffer, int aClearanceValue) const
Convert the text bounding box to a rectangular polygon depending on the text orientation,...
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
class TEXTE_MODULE, text in a footprint
Definition: typeinfo.h:93
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193

References EDA_TEXT::IsVisible(), m_high_def, PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_MODULE_TEXT_T, PCB_TEXT_T, EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon(), DRAWSEGMENT::TransformShapeWithClearanceToPolygon(), and EDA_ITEM::Type().

◆ buildCopperItemClearances()

void ZONE_FILLER::buildCopperItemClearances ( const ZONE_CONTAINER aZone,
PCB_LAYER_ID  aLayer,
SHAPE_POLY_SET aHoles 
)
private

Removes clearance from the shape for copper items which share the zone's layer but are not connected to it.

Definition at line 604 of file zone_filler.cpp.

606 {
607  static DRAWSEGMENT dummyEdge;
608  dummyEdge.SetLayer( Edge_Cuts );
609 
610  // a small extra clearance to be sure actual track clearance is not smaller
611  // than requested clearance due to many approximations in calculations,
612  // like arc to segment approx, rounding issues...
613  // 2 microns are a good value
614  int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_extraClearance );
615 
617  int zone_clearance = aZone->GetLocalClearance();
618  EDA_RECT zone_boundingbox = aZone->GetBoundingBox();
619 
620  // items outside the zone bounding box are skipped, so it needs to be inflated by
621  // the largest clearance value found in the netclasses and rules
622  int biggest_clearance = std::max( zone_clearance, bds.GetBiggestClearanceValue() );
623  zone_boundingbox.Inflate( biggest_clearance + extra_margin );
624 
625  // Use a dummy pad to calculate hole clearance when a pad has a hole but is not on the
626  // zone's copper layer. The dummy pad has the size and shape of the original pad's hole.
627  // We have to give it a parent because some functions expect a non-null parent to find
628  // clearance data, etc.
629  MODULE dummymodule( m_board );
630  D_PAD dummypad( &dummymodule );
631 
632  // Add non-connected pad clearances
633  //
634  for( MODULE* module : m_board->Modules() )
635  {
636  for( D_PAD* pad : module->Pads() )
637  {
638  if( !pad->IsOnLayer( aLayer ) )
639  {
640  if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
641  continue;
642 
643  setupDummyPadForHole( pad, dummypad );
644  pad = &dummypad;
645  }
646 
647  if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0
648  || aZone->GetPadConnection( pad ) == ZONE_CONNECTION::NONE )
649  {
650  if( pad->GetBoundingBox().Intersects( zone_boundingbox ) )
651  {
652  int gap;
653 
654  // for pads having the same netcode as the zone, the net clearance has no
655  // meaning so use the greater of the zone clearance and the thermal relief
656  if( pad->GetNetCode() > 0 && pad->GetNetCode() == aZone->GetNetCode() )
657  gap = std::max( zone_clearance, aZone->GetThermalReliefGap( pad ) );
658  else
659  gap = aZone->GetClearance( aLayer, pad );
660 
661  addKnockout( pad, gap, aHoles );
662  }
663  }
664  }
665  }
666 
667  // Add non-connected track clearances
668  //
669  for( TRACK* track : m_board->Tracks() )
670  {
671  if( !track->IsOnLayer( aLayer ) )
672  continue;
673 
674  if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) )
675  continue;
676 
677  if( track->GetBoundingBox().Intersects( zone_boundingbox ) )
678  {
679  int gap = aZone->GetClearance( aLayer, track ) + extra_margin;
680 
681  track->TransformShapeWithClearanceToPolygon( aHoles, gap, m_low_def );
682  }
683  }
684 
685  // Add graphic item clearances. They are by definition unconnected, and have no clearance
686  // definitions of their own.
687  //
688  auto doGraphicItem =
689  [&]( BOARD_ITEM* aItem )
690  {
691  // A item on the Edge_Cuts is always seen as on any layer:
692  if( !aItem->IsOnLayer( aLayer ) && !aItem->IsOnLayer( Edge_Cuts ) )
693  return;
694 
695  if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
696  {
697  bool ignoreLineWidth = aItem->IsOnLayer( Edge_Cuts );
698  int gap = aZone->GetClearance( aLayer, aItem );
699 
700  addKnockout( aItem, gap, ignoreLineWidth, aHoles );
701  }
702  };
703 
704  for( MODULE* module : m_board->Modules() )
705  {
706  doGraphicItem( &module->Reference() );
707  doGraphicItem( &module->Value() );
708 
709  for( BOARD_ITEM* item : module->GraphicalItems() )
710  doGraphicItem( item );
711  }
712 
713  for( BOARD_ITEM* item : m_board->Drawings() )
714  doGraphicItem( item );
715 
716  // Add zones outlines having an higher priority and keepout
717  //
718  for( ZONE_CONTAINER* zone : m_board->GetZoneList( true ) )
719  {
720 
721  // If the zones share no common layers
722  if( !zone->GetLayerSet().test( aLayer ) )
723  continue;
724 
725  if( !zone->GetIsKeepout() && zone->GetPriority() <= aZone->GetPriority() )
726  continue;
727 
728  if( zone->GetIsKeepout() && !zone->GetDoNotAllowCopperPour() )
729  continue;
730 
731  // A higher priority zone or keepout area is found: remove this area
732  EDA_RECT item_boundingbox = zone->GetBoundingBox();
733 
734  if( item_boundingbox.Intersects( zone_boundingbox ) )
735  {
736  // Add the zone outline area. Don't use any clearance for keepouts, or for zones
737  // with the same net (they will be connected but will honor their own clearance,
738  // thermal connections, etc.).
739  int gap = 0;
740 
741  if( !zone->GetIsKeepout() && aZone->GetNetCode() != zone->GetNetCode() )
742  gap = aZone->GetClearance( aLayer, zone );
743 
744  zone->TransformOutlinesShapeWithClearanceToPolygon( aHoles, gap );
745  }
746  }
747 
749 }
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:61
int GetNetCode() const
Function GetNetCode.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
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:110
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox (virtual)
Definition: class_zone.cpp:311
int GetBiggestClearanceValue()
Function GetBiggestClearanceValue.
void addKnockout(D_PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:553
int GetThermalReliefGap(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:321
virtual int GetClearance(PCB_LAYER_ID aLayer, BOARD_ITEM *aItem=nullptr, wxString *aSource=nullptr) const
Function GetClearance returns the clearance in internal units.
MODULES & Modules()
Definition: class_board.h:266
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
int GetLocalClearance(wxString *aSource=nullptr) const override
Function GetLocalClearance returns any local clearances set in the "classic" (ie: pre-rule) system.
Definition: class_zone.cpp:524
ZONE_CONNECTION GetPadConnection(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:818
unsigned GetPriority() const
Function GetPriority.
Definition: class_zone.h:106
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
Pads are not covered.
bool Intersects(const EDA_RECT &aRect) const
Function Intersects tests for a common area between rectangles.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.
static void setupDummyPadForHole(const D_PAD *aPad, D_PAD &aDummyPad)
Setup aDummyPad to have the same size and shape of aPad's hole.
static constexpr int Millimeter2iu(double mm)
DRAWINGS & Drawings()
Definition: class_board.h:275
TRACKS & Tracks()
Definition: class_board.h:257
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
std::list< ZONE_CONTAINER * > GetZoneList(bool aIncludeZonesInFootprints=false)
Function GetZoneList.
BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.

References addKnockout(), BOARD::Drawings(), Edge_Cuts, BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue(), ZONE_CONTAINER::GetBoundingBox(), ADVANCED_CFG::GetCfg(), BOARD_CONNECTED_ITEM::GetClearance(), BOARD::GetDesignSettings(), ZONE_CONTAINER::GetLocalClearance(), BOARD_CONNECTED_ITEM::GetNetCode(), ZONE_CONTAINER::GetPadConnection(), ZONE_CONTAINER::GetPriority(), ZONE_CONTAINER::GetThermalReliefGap(), BOARD::GetZoneList(), EDA_RECT::Inflate(), EDA_RECT::Intersects(), m_board, m_low_def, Millimeter2iu(), BOARD::Modules(), NONE, SHAPE_POLY_SET::PM_FAST, BOARD_ITEM::SetLayer(), setupDummyPadForHole(), SHAPE_POLY_SET::Simplify(), and BOARD::Tracks().

Referenced by computeRawFilledArea().

◆ buildThermalSpokes()

void ZONE_FILLER::buildThermalSpokes ( const ZONE_CONTAINER aZone,
PCB_LAYER_ID  aLayer,
std::deque< SHAPE_LINE_CHAIN > &  aSpokes 
)
private

Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone.

Function buildThermalSpokes.

Definition at line 1012 of file zone_filler.cpp.

1014 {
1015  auto zoneBB = aZone->GetBoundingBox();
1016  int zone_clearance = aZone->GetZoneClearance();
1017  int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
1018  biggest_clearance = std::max( biggest_clearance, zone_clearance );
1019  zoneBB.Inflate( biggest_clearance );
1020 
1021  // Is a point on the boundary of the polygon inside or outside? This small epsilon lets
1022  // us avoid the question.
1023  int epsilon = KiROUND( IU_PER_MM * 0.04 ); // about 1.5 mil
1024 
1025  for( auto module : m_board->Modules() )
1026  {
1027  for( auto pad : module->Pads() )
1028  {
1029  if( !hasThermalConnection( pad, aZone ) )
1030  continue;
1031 
1032  // We currently only connect to pads, not pad holes
1033  if( !pad->IsOnLayer( aLayer ) )
1034  continue;
1035 
1036  int thermalReliefGap = aZone->GetThermalReliefGap( pad );
1037 
1038  // Calculate thermal bridge half width
1039  int spoke_w = aZone->GetThermalReliefCopperBridge( pad );
1040  // Avoid spoke_w bigger than the smaller pad size, because
1041  // it is not possible to create stubs bigger than the pad.
1042  // Possible refinement: have a separate size for vertical and horizontal stubs
1043  spoke_w = std::min( spoke_w, pad->GetSize().x );
1044  spoke_w = std::min( spoke_w, pad->GetSize().y );
1045 
1046  // Cannot create stubs having a width < zone min thickness
1047  if( spoke_w <= aZone->GetMinThickness() )
1048  continue;
1049 
1050  int spoke_half_w = spoke_w / 2;
1051 
1052  // Quick test here to possibly save us some work
1053  BOX2I itemBB = pad->GetBoundingBox();
1054  itemBB.Inflate( thermalReliefGap + epsilon );
1055 
1056  if( !( itemBB.Intersects( zoneBB ) ) )
1057  continue;
1058 
1059  // Thermal spokes consist of segments from the pad center to points just outside
1060  // the thermal relief.
1061  //
1062  // We use the bounding-box to lay out the spokes, but for this to work the
1063  // bounding box has to be built at the same rotation as the spokes.
1064  // We have to use a dummy pad to avoid dirtying the cached shapes
1065  wxPoint shapePos = pad->ShapePos();
1066  double padAngle = pad->GetOrientation();
1067  D_PAD dummy_pad( *pad );
1068  dummy_pad.SetOrientation( 0.0 );
1069  dummy_pad.SetPosition( { 0, 0 } );
1070 
1071  BOX2I reliefBB = dummy_pad.GetBoundingBox();
1072  reliefBB.Inflate( thermalReliefGap + epsilon );
1073 
1074  // For circle pads, the thermal spoke orientation is 45 deg
1075  if( pad->GetShape() == PAD_SHAPE_CIRCLE )
1076  padAngle = s_RoundPadThermalSpokeAngle;
1077 
1078  for( int i = 0; i < 4; i++ )
1079  {
1080  SHAPE_LINE_CHAIN spoke;
1081  switch( i )
1082  {
1083  case 0: // lower stub
1084  spoke.Append( +spoke_half_w, -spoke_half_w );
1085  spoke.Append( -spoke_half_w, -spoke_half_w );
1086  spoke.Append( -spoke_half_w, reliefBB.GetBottom() );
1087  spoke.Append( 0, reliefBB.GetBottom() ); // test pt
1088  spoke.Append( +spoke_half_w, reliefBB.GetBottom() );
1089  break;
1090 
1091  case 1: // upper stub
1092  spoke.Append( +spoke_half_w, spoke_half_w );
1093  spoke.Append( -spoke_half_w, spoke_half_w );
1094  spoke.Append( -spoke_half_w, reliefBB.GetTop() );
1095  spoke.Append( 0, reliefBB.GetTop() ); // test pt
1096  spoke.Append( +spoke_half_w, reliefBB.GetTop() );
1097  break;
1098 
1099  case 2: // right stub
1100  spoke.Append( -spoke_half_w, spoke_half_w );
1101  spoke.Append( -spoke_half_w, -spoke_half_w );
1102  spoke.Append( reliefBB.GetRight(), -spoke_half_w );
1103  spoke.Append( reliefBB.GetRight(), 0 ); // test pt
1104  spoke.Append( reliefBB.GetRight(), spoke_half_w );
1105  break;
1106 
1107  case 3: // left stub
1108  spoke.Append( spoke_half_w, spoke_half_w );
1109  spoke.Append( spoke_half_w, -spoke_half_w );
1110  spoke.Append( reliefBB.GetLeft(), -spoke_half_w );
1111  spoke.Append( reliefBB.GetLeft(), 0 ); // test pt
1112  spoke.Append( reliefBB.GetLeft(), spoke_half_w );
1113  break;
1114  }
1115 
1116  spoke.Rotate( -DECIDEG2RAD( padAngle ) );
1117  spoke.Move( shapePos );
1118 
1119  spoke.SetClosed( true );
1120  spoke.GenerateBBoxCache();
1121  aSpokesList.push_back( std::move( spoke ) );
1122  }
1123  }
1124  }
1125 }
int GetThermalReliefCopperBridge(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:330
BOARD * m_board
Definition: zone_filler.h:110
coord_type GetTop() const
Definition: box2.h:204
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox (virtual)
Definition: class_zone.cpp:311
int GetBiggestClearanceValue()
Function GetBiggestClearanceValue.
static constexpr double IU_PER_MM
Mock up a conversion function.
void Move(const VECTOR2I &aVector) override
static const double s_RoundPadThermalSpokeAngle
Definition: zone_filler.cpp:75
coord_type GetRight() const
Definition: box2.h:199
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:553
coord_type GetBottom() const
Definition: box2.h:200
int GetThermalReliefGap(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:321
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
bool Intersects(const BOX2< Vec > &aRect) const
Function Intersects.
Definition: box2.h:236
void SetClosed(bool aClosed)
Function SetClosed()
MODULES & Modules()
Definition: class_board.h:266
void Rotate(double aAngle, const VECTOR2I &aCenter=VECTOR2I(0, 0)) override
Function Rotate rotates all vertices by a given angle.
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:302
SHAPE_LINE_CHAIN.
double DECIDEG2RAD(double deg)
Definition: trigo.h:218
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:68
coord_type GetLeft() const
Definition: box2.h:203
int GetZoneClearance() const
Definition: class_zone.h:196
bool hasThermalConnection(D_PAD *pad, const ZONE_CONTAINER *aZone)
Return true if the given pad has a thermal connection with the given zone.

References SHAPE_LINE_CHAIN::Append(), DECIDEG2RAD(), SHAPE_LINE_CHAIN::GenerateBBoxCache(), BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue(), BOX2< Vec >::GetBottom(), ZONE_CONTAINER::GetBoundingBox(), D_PAD::GetBoundingBox(), BOARD::GetDesignSettings(), BOX2< Vec >::GetLeft(), BOX2< Vec >::GetRight(), ZONE_CONTAINER::GetThermalReliefCopperBridge(), ZONE_CONTAINER::GetThermalReliefGap(), BOX2< Vec >::GetTop(), ZONE_CONTAINER::GetZoneClearance(), hasThermalConnection(), BOX2< Vec >::Inflate(), BOX2< Vec >::Intersects(), IU_PER_MM, KiROUND(), m_board, BOARD::Modules(), SHAPE_LINE_CHAIN::Move(), PAD_SHAPE_CIRCLE, SHAPE_LINE_CHAIN::Rotate(), s_RoundPadThermalSpokeAngle, SHAPE_LINE_CHAIN::SetClosed(), D_PAD::SetOrientation(), and D_PAD::SetPosition().

Referenced by computeRawFilledArea().

◆ computeRawFilledArea()

void ZONE_FILLER::computeRawFilledArea ( const ZONE_CONTAINER aZone,
PCB_LAYER_ID  aLayer,
const SHAPE_POLY_SET aSmoothedOutline,
std::set< VECTOR2I > *  aPreserveCorners,
SHAPE_POLY_SET aRawPolys,
SHAPE_POLY_SET aFinalPolys 
)
private

Function computeRawFilledArea Add non copper areas polygons (pads and tracks with clearance) to a filled copper area used in BuildFilledSolidAreasPolygons when calculating filled areas in a zone Non copper areas are pads and track and their clearance area The filled copper area must be computed before BuildFilledSolidAreasPolygons() call this function just after creating the filled copper area polygon (without clearance areas.

1 - Creates the main zone outline using a correction to shrink the resulting area by m_ZoneMinThickness / 2.

Parameters
aPcbthe current board

The result is areas with a margin of m_ZoneMinThickness / 2 so that when drawing outline with segments having a thickness of m_ZoneMinThickness the outlines will match exactly the initial outlines 2 - Knocks out thermal reliefs around thermally-connected pads 3 - Builds a set of thermal spoke for the whole zone 4 - Knocks out unconnected copper items, deleting any affected spokes 5 - Removes unconnected copper islands, deleting any affected spokes 6 - Adds in the remaining spokes

Definition at line 763 of file zone_filler.cpp.

768 {
770  m_low_def = std::min( ARC_LOW_DEF, int( m_high_def*1.5 ) ); // Reasonable value
771 
772  // Features which are min_width should survive pruning; features that are *less* than
773  // min_width should not. Therefore we subtract epsilon from the min_width when
774  // deflating/inflating.
775  int half_min_width = aZone->GetMinThickness() / 2;
776  int epsilon = Millimeter2iu( 0.001 );
777  int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
778 
779  // solid polygons are deflated and inflated during calculations.
780  // Polygons deflate usually do not create issues.
781  // Polygons inflate is a tricky transform, because it can create excessively long and narrow 'spikes'
782  // especially for acute angles.
783  // But in very case, the inflate transform caannot create bigger shapes than initial shapes.
784  // so the corner strategy is very important.
785  // The best is SHAPE_POLY_SET::ROUND_ALL_CORNERS.
786  // unfortunately, it creates a lot of small segments.
787  // SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS is not acceptable
788  // So for intermediate transforms, we use CHAMFER_ALL_CORNERS.
789  // For final transform, we use ROUND_ALL_CORNERS
792 
793  std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
794  SHAPE_POLY_SET clearanceHoles;
795 
796  std::unique_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO(
797  s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) );
798 
799  aRawPolys = aSmoothedOutline;
800 
802  dumper->BeginGroup( "clipper-zone" );
803 
805  return;
806 
807  knockoutThermalReliefs( aZone, aLayer, aRawPolys );
808 
810  dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" );
811 
813  return;
814 
815  buildCopperItemClearances( aZone, aLayer, clearanceHoles );
816 
818  dumper->Write( &aRawPolys, "clearance holes" );
819 
821  return;
822 
823  buildThermalSpokes( aZone, aLayer, thermalSpokes );
824 
826  return;
827 
828  // Create a temporary zone that we can hit-test spoke-ends against. It's only temporary
829  // because the "real" subtract-clearance-holes has to be done after the spokes are added.
830  static const bool USE_BBOX_CACHES = true;
831  SHAPE_POLY_SET testAreas = aRawPolys;
832  testAreas.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
833 
834  // Prune features that don't meet minimum-width criteria
835  if( half_min_width - epsilon > epsilon )
836  {
837  testAreas.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
838  testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
839  }
840 
842  return;
843 
844  // Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed
845  // things up a bit.
846  testAreas.BuildBBoxCaches();
847  int interval = 0;
848 
849  for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes )
850  {
851  const VECTOR2I& testPt = spoke.CPoint( 3 );
852 
853  // Hit-test against zone body
854  if( testAreas.Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
855  {
856  aRawPolys.AddOutline( spoke );
857  continue;
858  }
859 
860  if( interval++ > 400 )
861  {
863  return;
864 
865  interval = 0;
866  }
867 
868  // Hit-test against other spokes
869  for( const SHAPE_LINE_CHAIN& other : thermalSpokes )
870  {
871  if( &other != &spoke && other.PointInside( testPt, 1, USE_BBOX_CACHES ) )
872  {
873  aRawPolys.AddOutline( spoke );
874  break;
875  }
876  }
877  }
878 
880  return;
881 
882  // Ensure previous changes (adding thermal stubs) do not add
883  // filled areas outside the zone boundary
884  aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
885  aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
886 
888  dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" );
889 
891  return;
892 
893  aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
894  // Prune features that don't meet minimum-width criteria
895  if( half_min_width - epsilon > epsilon )
896  aRawPolys.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
897 
899  dumper->Write( &aRawPolys, "solid-areas-before-hatching" );
900 
902  return;
903 
904  // Now remove the non filled areas due to the hatch pattern
906  addHatchFillTypeOnZone( aZone, aLayer, aRawPolys );
907 
909  dumper->Write( &aRawPolys, "solid-areas-after-hatching" );
910 
912  return;
913 
914  // Re-inflate after pruning of areas that don't meet minimum-width criteria
915  if( aZone->GetFilledPolysUseThickness() )
916  {
917  // If we're stroking the zone with a min_width stroke then this will naturally
918  // inflate the zone by half_min_width
919  }
920  else if( half_min_width - epsilon > epsilon )
921  {
922  aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
923  aRawPolys.Inflate( half_min_width - epsilon, numSegs, finalcornerStrategy );
924 
925  // If we've deflated/inflated by something near our corner radius then we will have
926  // ended up with too-sharp corners. Apply outline smoothing again.
927  if( aZone->GetMinThickness() > (int)aZone->GetCornerRadius() )
928  aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
929  }
930 
931  aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
932 
934  dumper->Write( &aRawPolys, "areas_fractured" );
935 
936  aFinalPolys = aRawPolys;
937 
939  dumper->EndGroup();
940 }
SHAPE_FILE_IO.
Definition: shape_file_io.h:39
All angles are chamfered.
BOARD * m_board
Definition: zone_filler.h:110
WX_PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:115
CORNER_STRATEGY
< define how inflate transform build inflated polygon
bool GetFilledPolysUseThickness() const
Definition: class_zone.h:671
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:553
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 Inflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Performs outline inflation/deflation.
void buildCopperItemClearances(const ZONE_CONTAINER *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 ...
static const bool s_DumpZonesWhenFilling
Definition: zone_filler.cpp:76
bool IsCancelled() const
unsigned int GetCornerRadius() const
Definition: class_zone.h:669
SHAPE_POLY_SET.
ZONE_FILL_MODE GetFillMode() const
Definition: class_zone.h:153
void addHatchFillTypeOnZone(const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawPolys)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
void buildThermalSpokes(const ZONE_CONTAINER *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 BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset intersection For aFastMode meaning, see function booleanOp
void Fracture(POLYGON_MODE aFastMode)
Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the oute...
void BuildBBoxCaches()
Constructs BBoxCaches for Contains(), below.
void knockoutThermalReliefs(const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFill)
Removes thermal reliefs from the shape for any pads connected to the zone.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index
SHAPE_LINE_CHAIN.
int GetMinThickness() const
Definition: class_zone.h:206
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset difference For aFastMode meaning, see function booleanOp
static constexpr int Millimeter2iu(double mm)
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)

References addHatchFillTypeOnZone(), SHAPE_POLY_SET::AddOutline(), SHAPE_POLY_SET::BooleanIntersection(), SHAPE_POLY_SET::BooleanSubtract(), SHAPE_POLY_SET::BuildBBoxCaches(), buildCopperItemClearances(), buildThermalSpokes(), SHAPE_POLY_SET::CHAMFER_ALL_CORNERS, SHAPE_POLY_SET::Contains(), SHAPE_POLY_SET::Deflate(), SHAPE_POLY_SET::Fracture(), GetArcToSegmentCount(), ZONE_CONTAINER::GetCornerRadius(), BOARD::GetDesignSettings(), ZONE_CONTAINER::GetFilledPolysUseThickness(), ZONE_CONTAINER::GetFillMode(), ZONE_CONTAINER::GetMinThickness(), HATCH_PATTERN, SHAPE_POLY_SET::Inflate(), SHAPE_FILE_IO::IOM_APPEND, PROGRESS_REPORTER::IsCancelled(), knockoutThermalReliefs(), m_board, m_high_def, m_low_def, BOARD_DESIGN_SETTINGS::m_MaxError, m_progressReporter, Millimeter2iu(), SHAPE_POLY_SET::PM_FAST, SHAPE_POLY_SET::ROUND_ALL_CORNERS, s_DumpZonesWhenFilling, and SHAPE_POLY_SET::Simplify().

Referenced by fillSingleZone().

◆ Fill()

bool ZONE_FILLER::Fill ( const std::vector< ZONE_CONTAINER * > &  aZones,
bool  aCheck = false 
)

Definition at line 103 of file zone_filler.cpp.

104 {
105  std::vector<std::pair<ZONE_CONTAINER*, PCB_LAYER_ID>> toFill;
106  std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> islandsList;
107 
108  auto connectivity = m_board->GetConnectivity();
109  bool filledPolyWithOutline = not m_board->GetDesignSettings().m_ZoneUseNoOutlineInFill;
110 
111  std::unique_lock<std::mutex> lock( connectivity->GetLock(), std::try_to_lock );
112 
113  if( !lock )
114  return false;
115 
116  if( m_progressReporter )
117  {
118  m_progressReporter->Report( aCheck ? _( "Checking zone fills..." )
119  : _( "Building zone fills..." ) );
120  m_progressReporter->SetMaxProgress( aZones.size() );
121  }
122 
123  // The board outlines is used to clip solid areas inside the board (when outlines are valid)
126 
127  // Update the bounding box shape caches in the pads to prevent multi-threaded rebuilds
128  for( auto module : m_board->Modules() )
129  {
130  for( auto pad : module->Pads() )
131  {
132  if( pad->IsDirty() )
133  pad->BuildEffectiveShapes();
134  }
135  }
136 
137  for( ZONE_CONTAINER* zone : aZones )
138  {
139  // Keepout zones are not filled
140  if( zone->GetIsKeepout() )
141  continue;
142 
143  if( m_commit )
144  m_commit->Modify( zone );
145 
146  // calculate the hash value for filled areas. it will be used later
147  // to know if the current filled areas are up to date
148  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
149  {
150  zone->BuildHashValue( layer );
151 
152  // Add the zone to the list of zones to test or refill
153  toFill.emplace_back( std::make_pair( zone, layer ) );
154  }
155 
156  islandsList.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone ) );
157 
158  // Remove existing fill first to prevent drawing invalid polygons
159  // on some platforms
160  zone->UnFill();
161  }
162 
163  auto cleanupAfterCancel =
164  [&]()
165  {
166  if( m_commit )
167  m_commit->Revert();
168 
169  for( ZONE_CONTAINER* zone : aZones )
170  zone->UnFill();
171  };
172 
173  std::atomic<size_t> nextItem( 0 );
174  size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
175  aZones.size() );
176  std::vector<std::future<size_t>> returns( parallelThreadCount );
177 
178  auto fill_lambda =
179  [&]( PROGRESS_REPORTER* aReporter ) -> size_t
180  {
181  size_t num = 0;
182 
183  for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
184  {
185  PCB_LAYER_ID layer = toFill[i].second;
186  ZONE_CONTAINER* zone = toFill[i].first;
187 
188  zone->SetFilledPolysUseThickness( filledPolyWithOutline );
189 
190  SHAPE_POLY_SET rawPolys, finalPolys;
191  fillSingleZone( zone, layer, rawPolys, finalPolys );
192 
193  std::unique_lock<std::mutex> zoneLock( zone->GetLock() );
194 
195  zone->SetRawPolysList( layer, rawPolys );
196  zone->SetFilledPolysList( layer, finalPolys );
197  zone->SetIsFilled( true );
198 
199  if( m_progressReporter )
200  {
202 
204  break;
205  }
206 
207  num++;
208  }
209 
210  return num;
211  };
212 
213  if( parallelThreadCount <= 1 )
214  fill_lambda( m_progressReporter );
215  else
216  {
217  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
218  returns[ii] = std::async( std::launch::async, fill_lambda, m_progressReporter );
219 
220  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
221  {
222  // Here we balance returns with a 100ms timeout to allow UI updating
223  std::future_status status;
224  do
225  {
226  if( m_progressReporter )
228 
229  status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
230  } while( status != std::future_status::ready );
231  }
232  }
233 
234  // Now update the connectivity to check for copper islands
235  if( m_progressReporter )
236  {
238  {
239  cleanupAfterCancel();
240  return false;
241  }
242 
244  m_progressReporter->Report( _( "Removing insulated copper islands..." ) );
246  }
247 
248  connectivity->SetProgressReporter( m_progressReporter );
249  connectivity->FindIsolatedCopperIslands( islandsList );
250  connectivity->SetProgressReporter( nullptr );
251 
253  {
254  cleanupAfterCancel();
255  return false;
256  }
257 
258  // Now remove insulated copper islands and islands outside the board edge
259  bool outOfDate = false;
260 
261  for( auto& zone : islandsList )
262  {
263  for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
264  {
265  if( !zone.m_islands.count( layer ) )
266  continue;
267 
268  std::vector<int>& islands = zone.m_islands.at( layer );
269 
270  std::sort( islands.begin(), islands.end(), std::greater<int>() );
271  SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList( layer );
272 
273  long long int minArea = zone.m_zone->GetMinIslandArea();
274  ISLAND_REMOVAL_MODE mode = zone.m_zone->GetIslandRemovalMode();
275 
276  // Remove solid areas outside the board cutouts and the insulated islands
277  // only zones with net code > 0 can have insulated islands by definition
278  if( zone.m_zone->GetNetCode() > 0 )
279  {
280  // solid areas outside the board cutouts are also removed, because they are usually
281  // insulated islands
282  for( auto idx : islands )
283  {
284  if( mode == ISLAND_REMOVAL_MODE::ALWAYS
285  || ( mode == ISLAND_REMOVAL_MODE::AREA
286  && poly.Outline( idx ).Area() < minArea )
287  || !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
288  poly.DeletePolygon( idx );
289  else
290  zone.m_zone->SetIsIsland( layer, idx );
291  }
292  }
293  // Zones with no net can have areas outside the board cutouts.
294  // By definition, Zones with no net have no isolated island
295  // (in fact all filled areas are isolated islands)
296  // but they can have some areas outside the board cutouts.
297  // A filled area outside the board cutouts has all points outside cutouts,
298  // so we only need to check one point for each filled polygon.
299  // Note also non copper zones are already clipped
300  else if( m_brdOutlinesValid && zone.m_zone->IsOnCopperLayer() )
301  {
302  for( int idx = 0; idx < poly.OutlineCount(); )
303  {
304  if( poly.Polygon( idx ).empty()
305  || !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
306  {
307  poly.DeletePolygon( idx );
308  }
309  else
310  idx++;
311  }
312  }
313 
314  zone.m_zone->SetFilledPolysList( layer, poly );
315  zone.m_zone->CalculateFilledArea();
316 
317  if( aCheck && zone.m_zone->GetHashValue( layer ) != poly.GetHash() )
318  outOfDate = true;
319 
321  {
322  cleanupAfterCancel();
323  return false;
324  }
325  }
326  }
327 
328  if( aCheck && outOfDate )
329  {
331  KIDIALOG dlg( m_progressReporter->GetParent(),
332  _( "Zone fills are out-of-date. Refill?" ),
333  _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
334  dlg.SetOKCancelLabels( _( "Refill" ), _( "Continue without Refill" ) );
335  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
336 
337  if( dlg.ShowModal() == wxID_CANCEL )
338  {
339  cleanupAfterCancel();
340  return false;
341  }
342  }
343 
344  if( m_progressReporter )
345  {
347  m_progressReporter->Report( _( "Performing polygon fills..." ) );
348  m_progressReporter->SetMaxProgress( toFill.size() );
349  }
350 
351  nextItem = 0;
352 
353  auto tri_lambda =
354  [&]( PROGRESS_REPORTER* aReporter ) -> size_t
355  {
356  size_t num = 0;
357 
358  for( size_t i = nextItem++; i < islandsList.size(); i = nextItem++ )
359  {
360  islandsList[i].m_zone->CacheTriangulation();
361  num++;
362 
363  if( m_progressReporter )
364  {
366 
368  break;
369  }
370  }
371 
372  return num;
373  };
374 
375  if( parallelThreadCount <= 1 )
376  tri_lambda( m_progressReporter );
377  else
378  {
379  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
380  returns[ii] = std::async( std::launch::async, tri_lambda, m_progressReporter );
381 
382  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
383  {
384  // Here we balance returns with a 100ms timeout to allow UI updating
385  std::future_status status;
386  do
387  {
388  if( m_progressReporter )
389  {
391 
393  break;
394  }
395 
396  status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
397  } while( status != std::future_status::ready );
398  }
399  }
400 
401  if( m_progressReporter )
402  {
404  {
405  cleanupAfterCancel();
406  return false;
407  }
408 
410  m_progressReporter->Report( _( "Committing changes..." ) );
412  }
413 
414  connectivity->SetProgressReporter( nullptr );
415 
416  if( m_commit )
417  {
418  m_commit->Push( _( "Fill Zone(s)" ), false );
419  }
420  else
421  {
422  for( auto& i : toFill )
423  connectivity->Update( i.first );
424 
425  connectivity->RecalculateRatsnest();
426  }
427 
428  return true;
429 }
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.
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:61
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
int GetNetCode() const
Function GetNetCode.
void SetFilledPolysUseThickness(bool aOption)
Definition: class_zone.h:672
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:44
void SetRawPolysList(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aPolysList)
Function SetFilledPolysList sets the list of filled polygons.
Definition: class_zone.h:636
BOARD * m_board
Definition: zone_filler.h:110
A progress reporter for use in multi-threaded environments.
void SetIsIsland(PCB_LAYER_ID aLayer, int aPolyIdx)
Definition: class_zone.h:649
WX_PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:115
void Report(const wxString &aMessage)
Display aMessage in the progress bar dialog.
const SHAPE_POLY_SET & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Function GetFilledPolysList returns a reference to the list of filled polygons.
Definition: class_zone.h:612
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:553
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.
std::mutex & GetLock()
Definition: class_zone.h:185
void SetIsFilled(bool isFilled)
Definition: class_zone.h:191
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Function Seq returns an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:377
MD5_HASH GetHashValue(PCB_LAYER_ID aLayer)
Definition: class_zone.h:797
long long int GetMinIslandArea() const
Definition: class_zone.h:739
virtual LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
Definition: class_zone.cpp:288
PCB_LAYER_ID
A quick note on layer IDs:
bool IsCancelled() const
COMMIT * m_commit
Definition: zone_filler.h:114
MODULES & Modules()
Definition: class_board.h:266
SHAPE_POLY_SET.
bool fillSingleZone(ZONE_CONTAINER *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...
const ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition: class_zone.h:735
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Function GetConnectivity() returns list of missing connections between components/tracks.
Definition: class_board.h:355
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,...
void SetFilledPolysList(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aPolysList)
Function SetFilledPolysList sets the list of filled polygons.
Definition: class_zone.h:627
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true)=0
Executes the changes.
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
#define _(s)
Definition: 3d_actions.cpp:33
bool m_ZoneUseNoOutlineInFill
Option to handle filled polygons in zones: the "legacy" option is using thick outlines around filled ...
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
Definition: zone_settings.h:54
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
bool KeepRefreshing(bool aWait=false)
Update the UI dialog.
SHAPE_POLY_SET m_boardOutline
Definition: zone_filler.h:111
bool m_brdOutlinesValid
Definition: zone_filler.h:112
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)
bool IsOnCopperLayer() const override
Function IsOnCopperLayer.
Definition: class_zone.cpp:218
A structure used for calculating isolated islands on a given zone across all its layers.

References _, PROGRESS_REPORTER::AdvancePhase(), PROGRESS_REPORTER::AdvanceProgress(), ALWAYS, AREA, SHAPE_POLY_SET::Contains(), SHAPE_POLY_SET::DeletePolygon(), KIDIALOG::DoNotShowCheckbox(), fillSingleZone(), BOARD::GetBoardPolygonOutlines(), BOARD::GetConnectivity(), BOARD::GetDesignSettings(), ZONE_CONTAINER::GetLock(), PROGRESS_REPORTER::IsCancelled(), PROGRESS_REPORTER::KeepRefreshing(), m_board, m_boardOutline, m_brdOutlinesValid, m_commit, m_progressReporter, BOARD_DESIGN_SETTINGS::m_ZoneUseNoOutlineInFill, COMMIT::Modify(), BOARD::Modules(), COMMIT::Push(), SHAPE_POLY_SET::RemoveAllContours(), PROGRESS_REPORTER::Report(), COMMIT::Revert(), ZONE_CONTAINER::SetFilledPolysList(), ZONE_CONTAINER::SetFilledPolysUseThickness(), ZONE_CONTAINER::SetIsFilled(), PROGRESS_REPORTER::SetMaxProgress(), and ZONE_CONTAINER::SetRawPolysList().

Referenced by ZONE_FILLER_TOOL::CheckAllZones(), ZONE_CREATE_HELPER::commitZone(), PCB_EDIT_FRAME::Edit_Zone_Params(), export_vrml_zones(), ZONE_FILLER_TOOL::FillAllZones(), ZONE_CREATE_HELPER::performZoneCutout(), EDIT_TOOL::Remove(), and ZONE_FILLER_TOOL::ZoneFill().

◆ fillSingleZone()

bool ZONE_FILLER::fillSingleZone ( ZONE_CONTAINER aZone,
PCB_LAYER_ID  aLayer,
SHAPE_POLY_SET aRawPolys,
SHAPE_POLY_SET aFinalPolys 
)
private

Build the filled solid areas polygons from zone outlines (stored in m_Poly) The solid areas can be more than one on copper layers, and do not have holes ( holes are linked by overlapping segments to the main outline) in order to have drawable (and plottable) filled polygons.

Returns
true if OK, false if the solid polygons cannot be built
Parameters
aZoneis the zone to fill
aRawPolysA reference to a SHAPE_POLY_SET buffer to store filled solid areas polygons (with holes)
aFinalPolysA reference to a SHAPE_POLY_SET buffer to store polygons with no holes (holes are linked to main outline by overlapping segments, and these polygons are shrinked by aZone->GetMinThickness() / 2 to be drawn with a outline thickness = aZone->GetMinThickness() aFinalPolys are polygons that will be drawn on screen and plotted

Definition at line 948 of file zone_filler.cpp.

950 {
951  SHAPE_POLY_SET smoothedPoly;
952  std::set<VECTOR2I> colinearCorners;
953  aZone->GetColinearCorners( m_board, colinearCorners );
954 
955  /*
956  * convert outlines + holes to outlines without holes (adding extra segments if necessary)
957  * m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building
958  * this zone
959  */
960  if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) )
961  return false;
962 
964  return false;
965 
966  if( aZone->IsOnCopperLayer() )
967  {
968  computeRawFilledArea( aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys,
969  aFinalPolys );
970  }
971  else
972  {
973  // Features which are min_width should survive pruning; features that are *less* than
974  // min_width should not. Therefore we subtract epsilon from the min_width when
975  // deflating/inflating.
976  int half_min_width = aZone->GetMinThickness() / 2;
977  int epsilon = Millimeter2iu( 0.001 );
978  int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
979 
980  if( m_brdOutlinesValid )
982 
983  smoothedPoly.Deflate( half_min_width/* - epsilon*/, numSegs );
984 
985  // Remove the non filled areas due to the hatch pattern
987  addHatchFillTypeOnZone( aZone, aLayer, smoothedPoly );
988 
989  // Re-inflate after pruning of areas that don't meet minimum-width criteria
990  if( aZone->GetFilledPolysUseThickness() )
991  {
992  // If we're stroking the zone with a min_width stroke then this will naturally
993  // inflate the zone by half_min_width
994  }
995  else if( half_min_width - epsilon > epsilon )
996  smoothedPoly.Deflate( -( half_min_width - epsilon ), numSegs );
997 
998  aRawPolys = smoothedPoly;
999  aFinalPolys = smoothedPoly;
1000 
1002  }
1003 
1004  aZone->SetNeedRefill( false );
1005  return true;
1006 }
BOARD * m_board
Definition: zone_filler.h:110
WX_PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:115
void computeRawFilledArea(const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, 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...
bool GetFilledPolysUseThickness() const
Definition: class_zone.h:671
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...
bool IsCancelled() const
SHAPE_POLY_SET.
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:153
void addHatchFillTypeOnZone(const ZONE_CONTAINER *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawPolys)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset intersection For aFastMode meaning, see function booleanOp
void Fracture(POLYGON_MODE aFastMode)
Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the oute...
int GetMinThickness() const
Definition: class_zone.h:206
SHAPE_POLY_SET m_boardOutline
Definition: zone_filler.h:111
bool m_brdOutlinesValid
Definition: zone_filler.h:112
static constexpr int Millimeter2iu(double mm)
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
bool IsOnCopperLayer() const override
Function IsOnCopperLayer.
Definition: class_zone.cpp:218
void SetNeedRefill(bool aNeedRefill)
Definition: class_zone.h:194

References addHatchFillTypeOnZone(), SHAPE_POLY_SET::BooleanIntersection(), ZONE_CONTAINER::BuildSmoothedPoly(), computeRawFilledArea(), SHAPE_POLY_SET::Deflate(), SHAPE_POLY_SET::Fracture(), GetArcToSegmentCount(), ZONE_CONTAINER::GetColinearCorners(), ZONE_CONTAINER::GetFilledPolysUseThickness(), ZONE_CONTAINER::GetFillMode(), ZONE_CONTAINER::GetMinThickness(), HATCH_PATTERN, PROGRESS_REPORTER::IsCancelled(), ZONE_CONTAINER::IsOnCopperLayer(), m_board, m_boardOutline, m_brdOutlinesValid, m_high_def, m_progressReporter, Millimeter2iu(), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE, and ZONE_CONTAINER::SetNeedRefill().

Referenced by Fill().

◆ InstallNewProgressReporter()

void ZONE_FILLER::InstallNewProgressReporter ( wxWindow *  aParent,
const wxString &  aTitle,
int  aNumPhases 
)

Definition at line 95 of file zone_filler.cpp.

97 {
98  m_uniqueReporter = std::make_unique<WX_PROGRESS_REPORTER>( aParent, aTitle, aNumPhases );
100 }
WX_PROGRESS_REPORTER * m_progressReporter
Definition: zone_filler.h:115
std::unique_ptr< WX_PROGRESS_REPORTER > m_uniqueReporter
Definition: zone_filler.h:116

References m_progressReporter, and m_uniqueReporter.

Referenced by ZONE_FILLER_TOOL::CheckAllZones(), PCB_EDIT_FRAME::Edit_Zone_Params(), ZONE_FILLER_TOOL::FillAllZones(), EDIT_TOOL::Remove(), and ZONE_FILLER_TOOL::ZoneFill().

◆ knockoutThermalReliefs()

void ZONE_FILLER::knockoutThermalReliefs ( const ZONE_CONTAINER aZone,
PCB_LAYER_ID  aLayer,
SHAPE_POLY_SET aFill 
)
private

Removes thermal reliefs from the shape for any pads connected to the zone.

Does NOT add in spokes, which must be done later.

Definition at line 561 of file zone_filler.cpp.

563 {
564  SHAPE_POLY_SET holes;
565 
566  // Use a dummy pad to calculate relief when a pad has a hole but is not on the zone's
567  // copper layer. The dummy pad has the size and shape of the original pad's hole. We have
568  // to give it a parent because some functions expect a non-null parent to find clearance
569  // data, etc.
570  MODULE dummymodule( m_board );
571  D_PAD dummypad( &dummymodule );
572 
573  for( auto module : m_board->Modules() )
574  {
575  for( auto pad : module->Pads() )
576  {
577  if( !hasThermalConnection( pad, aZone ) )
578  continue;
579 
580  // If the pad isn't on the current layer but has a hole, knock out a thermal relief
581  // for the hole.
582  if( !pad->IsOnLayer( aLayer ) )
583  {
584  if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
585  continue;
586 
587  setupDummyPadForHole( pad, dummypad );
588  pad = &dummypad;
589  }
590 
591  addKnockout( pad, aZone->GetThermalReliefGap( pad ), holes );
592  }
593  }
594 
597 }
BOARD * m_board
Definition: zone_filler.h:110
void addKnockout(D_PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad.
int GetThermalReliefGap(D_PAD *aPad=NULL) const
Definition: class_zone.cpp:321
MODULES & Modules()
Definition: class_board.h:266
SHAPE_POLY_SET.
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
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
bool hasThermalConnection(D_PAD *pad, const ZONE_CONTAINER *aZone)
Return true if the given pad has a thermal connection with the given zone.

References addKnockout(), SHAPE_POLY_SET::BooleanSubtract(), ZONE_CONTAINER::GetThermalReliefGap(), hasThermalConnection(), m_board, BOARD::Modules(), SHAPE_POLY_SET::PM_FAST, setupDummyPadForHole(), and SHAPE_POLY_SET::Simplify().

Referenced by computeRawFilledArea().

Member Data Documentation

◆ m_board

◆ m_boardOutline

SHAPE_POLY_SET ZONE_FILLER::m_boardOutline
private

Definition at line 111 of file zone_filler.h.

Referenced by Fill(), and fillSingleZone().

◆ m_brdOutlinesValid

bool ZONE_FILLER::m_brdOutlinesValid
private

Definition at line 112 of file zone_filler.h.

Referenced by Fill(), and fillSingleZone().

◆ m_commit

COMMIT* ZONE_FILLER::m_commit
private

Definition at line 114 of file zone_filler.h.

Referenced by Fill().

◆ m_high_def

int ZONE_FILLER::m_high_def
private

Definition at line 119 of file zone_filler.h.

Referenced by addKnockout(), computeRawFilledArea(), and fillSingleZone().

◆ m_low_def

int ZONE_FILLER::m_low_def
private

Definition at line 125 of file zone_filler.h.

Referenced by addKnockout(), buildCopperItemClearances(), and computeRawFilledArea().

◆ m_progressReporter

WX_PROGRESS_REPORTER* ZONE_FILLER::m_progressReporter
private

◆ m_uniqueReporter

std::unique_ptr<WX_PROGRESS_REPORTER> ZONE_FILLER::m_uniqueReporter
private

Definition at line 116 of file zone_filler.h.

Referenced by InstallNewProgressReporter().


The documentation for this class was generated from the following files: