KiCad PCB EDA Suite
zone_filler.cpp File Reference
#include <cstdint>
#include <thread>
#include <mutex>
#include <algorithm>
#include <future>
#include <class_board.h>
#include <class_zone.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <class_drawsegment.h>
#include <class_track.h>
#include <class_pcb_text.h>
#include <class_pcb_target.h>
#include <connectivity/connectivity_data.h>
#include <board_commit.h>
#include <widgets/progress_reporter.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_file_io.h>
#include <geometry/convex_hull.h>
#include <geometry/geometry_utils.h>
#include <confirm.h>
#include "zone_filler.h"

Go to the source code of this file.

Functions

void CreateThermalReliefPadPolygon (SHAPE_POLY_SET &aCornerBuffer, const D_PAD &aPad, int aThermalGap, int aCopperThickness, int aMinThicknessValue, int aCircleToSegmentsCount, double aCorrectionFactor, double aThermalRot)
 Function CreateThermalReliefPadPolygon Add holes around a pad to create a thermal relief copper thickness is min (dx/2, aCopperWitdh) or min (dy/2, aCopperWitdh) More...
 

Variables

static double s_thermalRot = 450
 
static const bool s_DumpZonesWhenFilling = false
 

Function Documentation

void CreateThermalReliefPadPolygon ( SHAPE_POLY_SET aCornerBuffer,
const D_PAD aPad,
int  aThermalGap,
int  aCopperThickness,
int  aMinThicknessValue,
int  aCircleToSegmentsCount,
double  aCorrectionFactor,
double  aThermalRot 
)

Function CreateThermalReliefPadPolygon Add holes around a pad to create a thermal relief copper thickness is min (dx/2, aCopperWitdh) or min (dy/2, aCopperWitdh)

Parameters
aCornerBuffer= a buffer to store the polygon
aPad= the current pad used to create the thermal shape
aThermalGap= gap in thermal shape
aCopperThickness= stubs thickness in thermal shape
aMinThicknessValue= min copper thickness allowed
aCircleToSegmentsCount= the number of segments to approximate a circle
aCorrectionFactor= the correction to apply to circles radius to keep
aThermalRot= for rond pads the rotation of thermal stubs (450 usually for 45 deg.)

Definition at line 909 of file board_items_to_polygon_shape_transform.cpp.

References AddAngles(), PNS::angle(), SHAPE_POLY_SET::Append(), SHAPE_POLY_SET::BooleanSubtract(), delta, DIM, D_PAD::GetBoundingBox(), EDA_RECT::GetHeight(), D_PAD::GetOrientation(), D_PAD::GetShape(), D_PAD::GetSize(), EDA_RECT::GetWidth(), i, KiROUND(), max, min, SHAPE_POLY_SET::NewOutline(), PAD_SHAPE_CIRCLE, PAD_SHAPE_OVAL, PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT, PAD_SHAPE_TRAPEZOID, SHAPE_POLY_SET::PM_FAST, SHAPE_POLY_SET::RemoveAllContours(), RotatePoint(), D_PAD::ShapePos(), SHAPE_POLY_SET::Simplify(), D_PAD::TransformShapeWithClearanceToPolygon(), wxPoint::x, and wxPoint::y.

Referenced by ZONE_FILLER::buildZoneFeatureHoleList().

917 {
918  wxPoint corner, corner_end;
919  wxPoint padShapePos = aPad.ShapePos(); // Note: for pad having a shape offset,
920  // the pad position is NOT the shape position
921  wxSize copper_thickness;
922 
923  double delta = 3600.0 / aCircleToSegmentsCount; // rot angle in 0.1 degree
924 
925  /* Keep in account the polygon outline thickness
926  * aThermalGap must be increased by aMinThicknessValue/2 because drawing external outline
927  * with a thickness of aMinThicknessValue will reduce gap by aMinThicknessValue/2
928  */
929  aThermalGap += aMinThicknessValue / 2;
930 
931  /* Keep in account the polygon outline thickness
932  * copper_thickness must be decreased by aMinThicknessValue because drawing outlines
933  * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue
934  */
935  aCopperThickness -= aMinThicknessValue;
936 
937  if( aCopperThickness < 0 )
938  aCopperThickness = 0;
939 
940  int dx = aPad.GetSize().x / 2;
941  int dy = aPad.GetSize().y / 2;
942 
943  copper_thickness.x = std::min( dx, aCopperThickness );
944  copper_thickness.y = std::min( dy, aCopperThickness );
945 
946  switch( aPad.GetShape() )
947  {
948  case PAD_SHAPE_CIRCLE: // Add 4 similar holes
949  {
950  /* we create 4 copper holes and put them in position 1, 2, 3 and 4
951  * here is the area of the rectangular pad + its thermal gap
952  * the 4 copper holes remove the copper in order to create the thermal gap
953  * 4 ------ 1
954  * | |
955  * | |
956  * | |
957  * | |
958  * 3 ------ 2
959  * holes 2, 3, 4 are the same as hole 1, rotated 90, 180, 270 deg
960  */
961 
962  // Build the hole pattern, for the hole in the X >0, Y > 0 plane:
963  // The pattern roughtly is a 90 deg arc pie
964  std::vector <wxPoint> corners_buffer;
965 
966  // Radius of outer arcs of the shape corrected for arc approximation by lines
967  int outer_radius = KiROUND( (dx + aThermalGap) * aCorrectionFactor );
968 
969  // Crosspoint of thermal spoke sides, the first point of polygon buffer
970  corners_buffer.push_back( wxPoint( copper_thickness.x / 2, copper_thickness.y / 2 ) );
971 
972  // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
973  // and first seg of arc approx
974  corner.x = copper_thickness.x / 2;
975  int y = outer_radius - (aThermalGap / 4);
976  corner.y = KiROUND( sqrt( ( (double) y * y - (double) corner.x * corner.x ) ) );
977 
978  if( aThermalRot != 0 )
979  corners_buffer.push_back( corner );
980 
981  // calculate the starting point of the outter arc
982  corner.x = copper_thickness.x / 2;
983 
984  corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) -
985  ( (double) corner.x * corner.x ) ) );
986  RotatePoint( &corner, 90 ); // 9 degrees is the spoke fillet size
987 
988  // calculate the ending point of the outter arc
989  corner_end.x = corner.y;
990  corner_end.y = corner.x;
991 
992  // calculate intermediate points (y coordinate from corner.y to corner_end.y
993  while( (corner.y > corner_end.y) && (corner.x < corner_end.x) )
994  {
995  corners_buffer.push_back( corner );
996  RotatePoint( &corner, delta );
997  }
998 
999  corners_buffer.push_back( corner_end );
1000 
1001  /* add an intermediate point, to avoid angles < 90 deg between last arc approx line
1002  * and radius line
1003  */
1004  corner.x = corners_buffer[1].y;
1005  corner.y = corners_buffer[1].x;
1006  corners_buffer.push_back( corner );
1007 
1008  // Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270 deg
1009  // aThermalRot = 450 (45.0 degrees orientation) work fine.
1010  double angle_pad = aPad.GetOrientation(); // Pad orientation
1011  double th_angle = aThermalRot;
1012 
1013  for( unsigned ihole = 0; ihole < 4; ihole++ )
1014  {
1015  aCornerBuffer.NewOutline();
1016 
1017  for( unsigned ii = 0; ii < corners_buffer.size(); ii++ )
1018  {
1019  corner = corners_buffer[ii];
1020  RotatePoint( &corner, th_angle + angle_pad ); // Rotate by segment angle and pad orientation
1021  corner += padShapePos;
1022  aCornerBuffer.Append( corner.x, corner.y );
1023  }
1024 
1025  th_angle += 900; // Note: th_angle in in 0.1 deg.
1026  }
1027  }
1028  break;
1029 
1030  case PAD_SHAPE_OVAL:
1031  {
1032  // Oval pad support along the lines of round and rectangular pads
1033  std::vector <wxPoint> corners_buffer; // Polygon buffer as vector
1034 
1035  dx = (aPad.GetSize().x / 2) + aThermalGap; // Cutout radius x
1036  dy = (aPad.GetSize().y / 2) + aThermalGap; // Cutout radius y
1037 
1038  wxPoint shape_offset;
1039 
1040  // We want to calculate an oval shape with dx > dy.
1041  // if this is not the case, exchange dx and dy, and rotate the shape 90 deg.
1042  int supp_angle = 0;
1043 
1044  if( dx < dy )
1045  {
1046  std::swap( dx, dy );
1047  supp_angle = 900;
1048  std::swap( copper_thickness.x, copper_thickness.y );
1049  }
1050 
1051  int deltasize = dx - dy; // = distance between shape position and the 2 demi-circle ends centre
1052  // here we have dx > dy
1053  // Radius of outer arcs of the shape:
1054  int outer_radius = dy; // The radius of the outer arc is radius end + aThermalGap
1055 
1056  // Some coordinate fiddling, depending on the shape offset direction
1057  shape_offset = wxPoint( deltasize, 0 );
1058 
1059  // Crosspoint of thermal spoke sides, the first point of polygon buffer
1060  corner.x = copper_thickness.x / 2;
1061  corner.y = copper_thickness.y / 2;
1062  corners_buffer.push_back( corner );
1063 
1064  // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge
1065  // If copper thickness is more than shape offset, we need to calculate arc intercept point.
1066  if( copper_thickness.x > deltasize )
1067  {
1068  corner.x = copper_thickness.x / 2;
1069  corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) -
1070  ( (double) ( corner.x - delta ) * ( corner.x - deltasize ) ) ) );
1071  corner.x -= deltasize;
1072 
1073  /* creates an intermediate point, to have a > 90 deg angle
1074  * between the side and the first segment of arc approximation
1075  */
1076  wxPoint intpoint = corner;
1077  intpoint.y -= aThermalGap / 4;
1078  corners_buffer.push_back( intpoint + shape_offset );
1079  RotatePoint( &corner, 90 ); // 9 degrees of thermal fillet
1080  }
1081  else
1082  {
1083  corner.x = copper_thickness.x / 2;
1084  corner.y = outer_radius;
1085  corners_buffer.push_back( corner );
1086  }
1087 
1088  // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
1089  // and first seg of arc approx
1090  wxPoint last_corner;
1091  last_corner.y = copper_thickness.y / 2;
1092  int px = outer_radius - (aThermalGap / 4);
1093  last_corner.x =
1094  KiROUND( sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) ) );
1095 
1096  // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge
1097  corner_end.y = copper_thickness.y / 2;
1098  corner_end.x =
1099  KiROUND( sqrt( ( (double) outer_radius *
1100  outer_radius ) - ( (double) corner_end.y * corner_end.y ) ) );
1101  RotatePoint( &corner_end, -90 ); // 9 degrees of thermal fillet
1102 
1103  // calculate intermediate arc points till limit is reached
1104  while( (corner.y > corner_end.y) && (corner.x < corner_end.x) )
1105  {
1106  corners_buffer.push_back( corner + shape_offset );
1107  RotatePoint( &corner, delta );
1108  }
1109 
1110  //corners_buffer.push_back(corner + shape_offset); // TODO: about one mil geometry error forms somewhere.
1111  corners_buffer.push_back( corner_end + shape_offset );
1112  corners_buffer.push_back( last_corner + shape_offset ); // Enabling the line above shows intersection point.
1113 
1114  /* Create 2 holes, rotated by pad rotation.
1115  */
1116  double angle = aPad.GetOrientation() + supp_angle;
1117 
1118  for( int irect = 0; irect < 2; irect++ )
1119  {
1120  aCornerBuffer.NewOutline();
1121  for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
1122  {
1123  wxPoint cpos = corners_buffer[ic];
1124  RotatePoint( &cpos, angle );
1125  cpos += padShapePos;
1126  aCornerBuffer.Append( cpos.x, cpos.y );
1127  }
1128 
1129  angle = AddAngles( angle, 1800 ); // this is calculate hole 3
1130  }
1131 
1132  // Create holes, that are the mirrored from the previous holes
1133  for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
1134  {
1135  wxPoint swap = corners_buffer[ic];
1136  swap.x = -swap.x;
1137  corners_buffer[ic] = swap;
1138  }
1139 
1140  // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
1141  angle = aPad.GetOrientation() + supp_angle;
1142 
1143  for( int irect = 0; irect < 2; irect++ )
1144  {
1145  aCornerBuffer.NewOutline();
1146 
1147  for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
1148  {
1149  wxPoint cpos = corners_buffer[ic];
1150  RotatePoint( &cpos, angle );
1151  cpos += padShapePos;
1152  aCornerBuffer.Append( cpos.x, cpos.y );
1153  }
1154 
1155  angle = AddAngles( angle, 1800 );
1156  }
1157  }
1158  break;
1159 
1160  case PAD_SHAPE_ROUNDRECT: // thermal shape is the same for round rect and rect.
1161  case PAD_SHAPE_RECT:
1162  {
1163  /* we create 4 copper holes and put them in position 1, 2, 3 and 4
1164  * here is the area of the rectangular pad + its thermal gap
1165  * the 4 copper holes remove the copper in order to create the thermal gap
1166  * 4 ------ 1
1167  * | |
1168  * | |
1169  * | |
1170  * | |
1171  * 3 ------ 2
1172  * hole 3 is the same as hole 1, rotated 180 deg
1173  * hole 4 is the same as hole 2, rotated 180 deg and is the same as hole 1, mirrored
1174  */
1175 
1176  // First, create a rectangular hole for position 1 :
1177  // 2 ------- 3
1178  // | |
1179  // | |
1180  // | |
1181  // 1 -------4
1182 
1183  // Modified rectangles with one corner rounded. TODO: merging with oval thermals
1184  // and possibly round too.
1185 
1186  std::vector <wxPoint> corners_buffer; // Polygon buffer as vector
1187 
1188  dx = (aPad.GetSize().x / 2) + aThermalGap; // Cutout radius x
1189  dy = (aPad.GetSize().y / 2) + aThermalGap; // Cutout radius y
1190 
1191  // The first point of polygon buffer is left lower corner, second the crosspoint of
1192  // thermal spoke sides, the third is upper right corner and the rest are rounding
1193  // vertices going anticlockwise. Note the inveted Y-axis in CG.
1194  corners_buffer.push_back( wxPoint( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ) ); // Adds small miters to zone
1195  corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) ); // fill and spoke corner
1196  corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) );
1197  corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) );
1198  corners_buffer.push_back( wxPoint( -(aThermalGap / 4 + copper_thickness.x / 2), -dy ) );
1199 
1200  double angle = aPad.GetOrientation();
1201  int rounding_radius = KiROUND( aThermalGap * aCorrectionFactor ); // Corner rounding radius
1202 
1203  for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
1204  {
1205  wxPoint corner_position = wxPoint( 0, -rounding_radius );
1206 
1207  // Start at half increment offset
1208  RotatePoint( &corner_position, 1800.0 / aCircleToSegmentsCount );
1209  double angle_pg = i * delta;
1210 
1211  RotatePoint( &corner_position, angle_pg ); // Rounding vector rotation
1212  corner_position -= aPad.GetSize() / 2; // Rounding vector + Pad corner offset
1213 
1214  corners_buffer.push_back( wxPoint( corner_position.x, corner_position.y ) );
1215  }
1216 
1217  for( int irect = 0; irect < 2; irect++ )
1218  {
1219  aCornerBuffer.NewOutline();
1220 
1221  for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
1222  {
1223  wxPoint cpos = corners_buffer[ic];
1224  RotatePoint( &cpos, angle ); // Rotate according to module orientation
1225  cpos += padShapePos; // Shift origin to position
1226  aCornerBuffer.Append( cpos.x, cpos.y );
1227  }
1228 
1229  angle = AddAngles( angle, 1800 ); // this is calculate hole 3
1230  }
1231 
1232  // Create holes, that are the mirrored from the previous holes
1233  for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
1234  {
1235  wxPoint swap = corners_buffer[ic];
1236  swap.x = -swap.x;
1237  corners_buffer[ic] = swap;
1238  }
1239 
1240  // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
1241  for( int irect = 0; irect < 2; irect++ )
1242  {
1243  aCornerBuffer.NewOutline();
1244 
1245  for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
1246  {
1247  wxPoint cpos = corners_buffer[ic];
1248  RotatePoint( &cpos, angle );
1249  cpos += padShapePos;
1250  aCornerBuffer.Append( cpos.x, cpos.y );
1251  }
1252 
1253  angle = AddAngles( angle, 1800 );
1254  }
1255  }
1256  break;
1257 
1258  case PAD_SHAPE_TRAPEZOID:
1259  {
1260  SHAPE_POLY_SET antipad; // The full antipad area
1261 
1262  // We need a length to build the stubs of the thermal reliefs
1263  // the value is not very important. The pad bounding box gives a reasonable value
1264  EDA_RECT bbox = aPad.GetBoundingBox();
1265  int stub_len = std::max( bbox.GetWidth(), bbox.GetHeight() );
1266 
1267  aPad.TransformShapeWithClearanceToPolygon( antipad, aThermalGap,
1268  aCircleToSegmentsCount, aCorrectionFactor );
1269 
1270  SHAPE_POLY_SET stub; // A basic stub ( a rectangle)
1271  SHAPE_POLY_SET stubs; // the full stubs shape
1272 
1273 
1274  // We now substract the stubs (connections to the copper zone)
1275  //ClipperLib::Clipper clip_engine;
1276  // Prepare a clipping transform
1277  //clip_engine.AddPath( antipad, ClipperLib::ptSubject, true );
1278 
1279  // Create stubs and add them to clipper engine
1280  wxPoint stubBuffer[4];
1281  stubBuffer[0].x = stub_len;
1282  stubBuffer[0].y = copper_thickness.y/2;
1283  stubBuffer[1] = stubBuffer[0];
1284  stubBuffer[1].y = -copper_thickness.y/2;
1285  stubBuffer[2] = stubBuffer[1];
1286  stubBuffer[2].x = -stub_len;
1287  stubBuffer[3] = stubBuffer[2];
1288  stubBuffer[3].y = copper_thickness.y/2;
1289 
1290  stub.NewOutline();
1291 
1292  for( unsigned ii = 0; ii < DIM( stubBuffer ); ii++ )
1293  {
1294  wxPoint cpos = stubBuffer[ii];
1295  RotatePoint( &cpos, aPad.GetOrientation() );
1296  cpos += padShapePos;
1297  stub.Append( cpos.x, cpos.y );
1298  }
1299 
1300  stubs.Append( stub );
1301 
1302  stubBuffer[0].y = stub_len;
1303  stubBuffer[0].x = copper_thickness.x/2;
1304  stubBuffer[1] = stubBuffer[0];
1305  stubBuffer[1].x = -copper_thickness.x/2;
1306  stubBuffer[2] = stubBuffer[1];
1307  stubBuffer[2].y = -stub_len;
1308  stubBuffer[3] = stubBuffer[2];
1309  stubBuffer[3].x = copper_thickness.x/2;
1310 
1311  stub.RemoveAllContours();
1312  stub.NewOutline();
1313 
1314  for( unsigned ii = 0; ii < DIM( stubBuffer ); ii++ )
1315  {
1316  wxPoint cpos = stubBuffer[ii];
1317  RotatePoint( &cpos, aPad.GetOrientation() );
1318  cpos += padShapePos;
1319  stub.Append( cpos.x, cpos.y );
1320  }
1321 
1322  stubs.Append( stub );
1324 
1325  antipad.BooleanSubtract( stubs, SHAPE_POLY_SET::PM_FAST );
1326  aCornerBuffer.Append( antipad );
1327 
1328  break;
1329  }
1330 
1331  default:
1332  ;
1333  }
1334 }
#define DIM(x)
of elements in an array
Definition: macros.h:98
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:120
int GetHeight() const
Definition: eda_rect.h:118
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:216
static const int delta[8][2]
Definition: solve.cpp:112
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, int aClearanceValue, int aCircleToSegmentsCount, double aCorrectionFactor, bool ignoreLineWidth=false) const override
Function TransformShapeWithClearanceToPolygon Convert the pad shape to a closed polygon Used in filli...
T AddAngles(T a1, T2 a2)
Add two angles (keeping the result normalized). T2 is here.
Definition: trigo.h:288
Class SHAPE_POLY_SET.
const wxSize & GetSize() const
Definition: class_pad.h:269
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
int NewOutline()
Creates a new empty polygon in the set and returns its index
#define max(a, b)
Definition: auxiliary.h:86
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
size_t i
Definition: json11.cpp:597
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees, but soon degrees.
Definition: class_pad.h:382
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
int GetWidth() const
Definition: eda_rect.h:117
wxPoint ShapePos() const
Definition: class_pad.cpp:517
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset difference For aFastMode meaning, see function booleanOp ...
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes...
Definition: class_pad.cpp:216
#define min(a, b)
Definition: auxiliary.h:85
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) ...

Variable Documentation

const bool s_DumpZonesWhenFilling = false
static

Definition at line 65 of file zone_filler.cpp.

Referenced by ZONE_FILLER::computeRawFilledAreas().

double s_thermalRot = 450
static