KiCad PCB EDA Suite
board_adapter.cpp File Reference

Go to the source code of this file.

Macros

#define ADD_COLOR(list, r, g, b, a, name)   list.push_back( CUSTOM_COLOR_ITEM( r/255.0, g/255.0, b/255.0, a, name ) )
 
#define COPPER_THICKNESS   Millimeter2iu( 0.035 )
 
#define TECH_LAYER_THICKNESS   Millimeter2iu( 0.025 )
 
#define SOLDERPASTE_LAYER_THICKNESS   Millimeter2iu( 0.04 )
 
#define layerThicknessMargin   1.1
 

Functions

bool BuildFootprintPolygonOutlines (BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
 This function is used to extract a board outline for a footprint view. More...
 

Variables

static bool g_ColorsLoaded = false
 

Macro Definition Documentation

◆ ADD_COLOR

#define ADD_COLOR (   list,
  r,
  g,
  b,
  a,
  name 
)    list.push_back( CUSTOM_COLOR_ITEM( r/255.0, g/255.0, b/255.0, a, name ) )

◆ COPPER_THICKNESS

#define COPPER_THICKNESS   Millimeter2iu( 0.035 )

Definition at line 322 of file board_adapter.cpp.

◆ layerThicknessMargin

#define layerThicknessMargin   1.1

◆ SOLDERPASTE_LAYER_THICKNESS

#define SOLDERPASTE_LAYER_THICKNESS   Millimeter2iu( 0.04 )

Definition at line 327 of file board_adapter.cpp.

◆ TECH_LAYER_THICKNESS

#define TECH_LAYER_THICKNESS   Millimeter2iu( 0.025 )

Definition at line 324 of file board_adapter.cpp.

Function Documentation

◆ BuildFootprintPolygonOutlines()

bool BuildFootprintPolygonOutlines ( BOARD aBoard,
SHAPE_POLY_SET aOutlines,
int  aErrorMax,
int  aChainingEpsilon,
OUTLINE_ERROR_HANDLER aErrorHandler 
)

This function is used to extract a board outline for a footprint view.

Notes:

  • Incomplete outlines will be closed by joining the end of the outline onto the bounding box (by simply projecting the end points) and then take the area that contains the copper.
  • If all copper lies inside a closed outline, than that outline will be treated as an external board outline.
  • If copper is located outside a closed outline, then that outline will be treated as a hole, and the outer edge will be formed using the bounding box.

Definition at line 1060 of file convert_shape_list_to_polygon.cpp.

1063 {
1064  FOOTPRINT* footprint = aBoard->GetFirstFootprint();
1065 
1066  // No footprint loaded
1067  if( !footprint )
1068  {
1069  wxLogTrace( traceBoardOutline, "No footprint found on board" );
1070  return false;
1071  }
1072 
1073  PCB_TYPE_COLLECTOR items;
1074  SHAPE_POLY_SET outlines;
1075  bool success = false;
1076 
1077  // Get all the SHAPEs into 'items', then keep only those on layer == Edge_Cuts.
1078  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
1079  items.Collect( aBoard, scan_graphics );
1080 
1081  // Make a working copy of aSegList, because the list is modified during calculations
1082  std::vector<PCB_SHAPE*> segList;
1083 
1084  for( int ii = 0; ii < items.GetCount(); ii++ )
1085  {
1086  if( items[ii]->GetLayer() == Edge_Cuts )
1087  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
1088  }
1089 
1090  if( !segList.empty() )
1091  {
1092  success = ConvertOutlineToPolygon( segList, outlines, aErrorMax, aChainingEpsilon,
1093  aErrorHandler );
1094  }
1095 
1096  // A closed outline was found on Edge_Cuts
1097  if( success )
1098  {
1099  wxLogTrace( traceBoardOutline, "Closed outline found" );
1100 
1101  // If copper is outside a closed polygon, treat it as a hole
1102  if( isCopperOutside( footprint, outlines ) )
1103  {
1104  wxLogTrace( traceBoardOutline, "Treating outline as a hole" );
1105 
1106  buildBoardBoundingBoxPoly( aBoard, aOutlines );
1107 
1108  // Copy all outlines from the conversion as holes into the new outline
1109  for( int i = 0; i < outlines.OutlineCount(); i++ )
1110  {
1111  SHAPE_LINE_CHAIN& out = outlines.Outline( i );
1112 
1113  if( out.IsClosed() )
1114  aOutlines.AddHole( out, -1 );
1115 
1116  for( int j = 0; j < outlines.HoleCount( i ); j++ )
1117  {
1118  SHAPE_LINE_CHAIN& hole = outlines.Hole( i, j );
1119 
1120  if( hole.IsClosed() )
1121  aOutlines.AddHole( hole, -1 );
1122  }
1123  }
1124  }
1125  // If all copper is inside, then the computed outline is the board outline
1126  else
1127  {
1128  wxLogTrace( traceBoardOutline, "Treating outline as board edge" );
1129  aOutlines = outlines;
1130  }
1131 
1132  return true;
1133  }
1134  // No board outlines were found, so use the bounding box
1135  else if( outlines.OutlineCount() == 0 )
1136  {
1137  wxLogTrace( traceBoardOutline, "Using footprint bounding box" );
1138  buildBoardBoundingBoxPoly( aBoard, aOutlines );
1139 
1140  return true;
1141  }
1142  // There is an outline present, but it is not closed
1143  else
1144  {
1145  wxLogTrace( traceBoardOutline, "Trying to build outline" );
1146 
1147  std::vector<SHAPE_LINE_CHAIN> closedChains;
1148  std::vector<SHAPE_LINE_CHAIN> openChains;
1149 
1150  // The ConvertOutlineToPolygon function returns only one main outline and the rest as
1151  // holes, so we promote the holes and process them
1152  openChains.push_back( outlines.Outline( 0 ) );
1153 
1154  for( int j = 0; j < outlines.HoleCount( 0 ); j++ )
1155  {
1156  SHAPE_LINE_CHAIN hole = outlines.Hole( 0, j );
1157 
1158  if( hole.IsClosed() )
1159  {
1160  wxLogTrace( traceBoardOutline, "Found closed hole" );
1161  closedChains.push_back( hole );
1162  }
1163  else
1164  {
1165  wxLogTrace( traceBoardOutline, "Found open hole" );
1166  openChains.push_back( hole );
1167  }
1168  }
1169 
1170  SHAPE_POLY_SET bbox;
1171  buildBoardBoundingBoxPoly( aBoard, bbox );
1172 
1173  // Treat the open polys as the board edge
1174  SHAPE_LINE_CHAIN chain = openChains[0];
1175  SHAPE_LINE_CHAIN rect = bbox.Outline( 0 );
1176 
1177  // We know the outline chain is open, so set to non-closed to get better segment count
1178  chain.SetClosed( false );
1179 
1180  SEG startSeg;
1181  SEG endSeg;
1182 
1183  // The two possible board outlines
1184  SHAPE_LINE_CHAIN upper;
1185  SHAPE_LINE_CHAIN lower;
1186 
1187  findEndSegments( chain, startSeg, endSeg );
1188 
1189  if( chain.SegmentCount() == 0 )
1190  {
1191  // Something is wrong, bail out with the overall footprint bounding box
1192  wxLogTrace( traceBoardOutline, "No line segments in provided outline" );
1193  aOutlines = bbox;
1194  return true;
1195  }
1196  else if( chain.SegmentCount() == 1 )
1197  {
1198  // This case means there is only 1 line segment making up the edge cuts of the
1199  // footprint, so we just need to use it to cut the bounding box in half.
1200  wxLogTrace( traceBoardOutline, "Only 1 line segment in provided outline" );
1201 
1202  startSeg = chain.Segment( 0 );
1203 
1204  // Intersect with all the sides of the rectangle
1205  OPT_VECTOR2I inter0 = startSeg.IntersectLines( rect.Segment( 0 ) );
1206  OPT_VECTOR2I inter1 = startSeg.IntersectLines( rect.Segment( 1 ) );
1207  OPT_VECTOR2I inter2 = startSeg.IntersectLines( rect.Segment( 2 ) );
1208  OPT_VECTOR2I inter3 = startSeg.IntersectLines( rect.Segment( 3 ) );
1209 
1210  if( inter0 && inter2 && !inter1 && !inter3 )
1211  {
1212  // Intersects the vertical rectangle sides only
1213  wxLogTrace( traceBoardOutline, "Segment intersects only vertical bbox sides" );
1214 
1215  // The upper half
1216  upper.Append( *inter0 );
1217  upper.Append( rect.GetPoint( 1 ) );
1218  upper.Append( rect.GetPoint( 2 ) );
1219  upper.Append( *inter2 );
1220  upper.SetClosed( true );
1221 
1222  // The lower half
1223  lower.Append( *inter0 );
1224  lower.Append( rect.GetPoint( 0 ) );
1225  lower.Append( rect.GetPoint( 3 ) );
1226  lower.Append( *inter2 );
1227  lower.SetClosed( true );
1228  }
1229  else if( inter1 && inter3 && !inter0 && !inter2 )
1230  {
1231  // Intersects the horizontal rectangle sides only
1232  wxLogTrace( traceBoardOutline, "Segment intersects only horizontal bbox sides" );
1233 
1234  // The left half
1235  upper.Append( *inter1 );
1236  upper.Append( rect.GetPoint( 1 ) );
1237  upper.Append( rect.GetPoint( 0 ) );
1238  upper.Append( *inter3 );
1239  upper.SetClosed( true );
1240 
1241  // The right half
1242  lower.Append( *inter1 );
1243  lower.Append( rect.GetPoint( 2 ) );
1244  lower.Append( rect.GetPoint( 3 ) );
1245  lower.Append( *inter3 );
1246  lower.SetClosed( true );
1247  }
1248  else
1249  {
1250  // Angled line segment that cuts across a corner
1251  wxLogTrace( traceBoardOutline, "Segment intersects two perpendicular bbox sides" );
1252 
1253  // Figure out which actual lines are intersected, since IntersectLines assumes
1254  // an infinite line
1255  bool hit0 = rect.Segment( 0 ).Contains( *inter0 );
1256  bool hit1 = rect.Segment( 1 ).Contains( *inter1 );
1257  bool hit2 = rect.Segment( 2 ).Contains( *inter2 );
1258  bool hit3 = rect.Segment( 3 ).Contains( *inter3 );
1259 
1260  if( hit0 && hit1 )
1261  {
1262  // Cut across the upper left corner
1263  wxLogTrace( traceBoardOutline, "Segment cuts upper left corner" );
1264 
1265  // The upper half
1266  upper.Append( *inter0 );
1267  upper.Append( rect.GetPoint( 1 ) );
1268  upper.Append( *inter1 );
1269  upper.SetClosed( true );
1270 
1271  // The lower half
1272  lower.Append( *inter0 );
1273  lower.Append( rect.GetPoint( 0 ) );
1274  lower.Append( rect.GetPoint( 3 ) );
1275  lower.Append( rect.GetPoint( 2 ) );
1276  lower.Append( *inter1 );
1277  lower.SetClosed( true );
1278  }
1279  else if( hit1 && hit2 )
1280  {
1281  // Cut across the upper right corner
1282  wxLogTrace( traceBoardOutline, "Segment cuts upper right corner" );
1283 
1284  // The upper half
1285  upper.Append( *inter1 );
1286  upper.Append( rect.GetPoint( 2 ) );
1287  upper.Append( *inter2 );
1288  upper.SetClosed( true );
1289 
1290  // The lower half
1291  lower.Append( *inter1 );
1292  lower.Append( rect.GetPoint( 1 ) );
1293  lower.Append( rect.GetPoint( 0 ) );
1294  lower.Append( rect.GetPoint( 3 ) );
1295  lower.Append( *inter2 );
1296  lower.SetClosed( true );
1297  }
1298  else if( hit2 && hit3 )
1299  {
1300  // Cut across the lower right corner
1301  wxLogTrace( traceBoardOutline, "Segment cuts lower right corner" );
1302 
1303  // The upper half
1304  upper.Append( *inter2 );
1305  upper.Append( rect.GetPoint( 2 ) );
1306  upper.Append( rect.GetPoint( 1 ) );
1307  upper.Append( rect.GetPoint( 0 ) );
1308  upper.Append( *inter3 );
1309  upper.SetClosed( true );
1310 
1311  // The bottom half
1312  lower.Append( *inter2 );
1313  lower.Append( rect.GetPoint( 3 ) );
1314  lower.Append( *inter3 );
1315  lower.SetClosed( true );
1316  }
1317  else
1318  {
1319  // Cut across the lower left corner
1320  wxLogTrace( traceBoardOutline, "Segment cuts upper left corner" );
1321 
1322  // The upper half
1323  upper.Append( *inter0 );
1324  upper.Append( rect.GetPoint( 1 ) );
1325  upper.Append( rect.GetPoint( 2 ) );
1326  upper.Append( rect.GetPoint( 3 ) );
1327  upper.Append( *inter3 );
1328  upper.SetClosed( true );
1329 
1330  // The bottom half
1331  lower.Append( *inter0 );
1332  lower.Append( rect.GetPoint( 0 ) );
1333  lower.Append( *inter3 );
1334  lower.SetClosed( true );
1335  }
1336  }
1337  }
1338  else
1339  {
1340  // More than 1 segment
1341  wxLogTrace( traceBoardOutline, "Multiple segments in outline" );
1342 
1343  // Just a temporary thing
1344  aOutlines = bbox;
1345  return true;
1346  }
1347 
1348  // Figure out which is the correct outline
1349  SHAPE_POLY_SET poly1;
1350  SHAPE_POLY_SET poly2;
1351 
1352  poly1.NewOutline();
1353  poly1.Append( upper );
1354 
1355  poly2.NewOutline();
1356  poly2.Append( lower );
1357 
1358  if( isCopperOutside( footprint, poly1 ) )
1359  {
1360  wxLogTrace( traceBoardOutline, "Using lower shape" );
1361  aOutlines = poly2;
1362  }
1363  else
1364  {
1365  wxLogTrace( traceBoardOutline, "Using upper shape" );
1366  aOutlines = poly1;
1367  }
1368 
1369  // Add all closed polys as holes to the main outline
1370  for( SHAPE_LINE_CHAIN& closedChain : closedChains )
1371  {
1372  wxLogTrace( traceBoardOutline, "Adding hole to main outline" );
1373  aOutlines.AddHole( closedChain, -1 );
1374  }
1375 
1376  return true;
1377  }
1378 
1379  // We really shouldn't reach this point
1380  return false;
1381 }
int OutlineCount() const
Return the number of vertices in a given outline/hole.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:209
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
const wxChar * traceBoardOutline
Flag to enable debug tracing for the board outline creation.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:82
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:317
bool IsClosed() const override
OPT< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:38
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
bool isCopperOutside(const FOOTPRINT *aMod, SHAPE_POLY_SET &aShape)
int NewOutline()
Creates a new hole in a given outline.
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
int SegmentCount() const
Return the number of segments in this line chain.
virtual const VECTOR2I GetPoint(int aIndex) const override
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Collect BOARD_ITEM objects using this class's Inspector method, which does the collection.
Definition: collectors.cpp:609
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Return the area of this poly set.
Definition: seg.h:40
int findEndSegments(SHAPE_LINE_CHAIN &aChain, SEG &aStartSeg, SEG &aEndSeg)
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
Represent a polyline (an zero-thickness chain of connected line segments).
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aSegList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
Function ConvertOutlineToPolygon Build a polygon (with holes) from a PCB_SHAPE list,...
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:614
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
bool Contains(const SEG &aSeg) const
Definition: seg.h:331
void buildBoardBoundingBoxPoly(const BOARD *aBoard, SHAPE_POLY_SET &aOutline)
Get the complete bounding box of the board (including all items).
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...

References SHAPE_POLY_SET::AddHole(), SHAPE_LINE_CHAIN::Append(), SHAPE_POLY_SET::Append(), buildBoardBoundingBoxPoly(), PCB_TYPE_COLLECTOR::Collect(), SEG::Contains(), ConvertOutlineToPolygon(), Edge_Cuts, EOT, findEndSegments(), COLLECTOR::GetCount(), BOARD::GetFirstFootprint(), SHAPE_LINE_CHAIN::GetPoint(), SHAPE_POLY_SET::Hole(), SHAPE_POLY_SET::HoleCount(), SEG::IntersectLines(), SHAPE_LINE_CHAIN::IsClosed(), isCopperOutside(), SHAPE_POLY_SET::NewOutline(), SHAPE_POLY_SET::Outline(), SHAPE_POLY_SET::OutlineCount(), PCB_FP_SHAPE_T, PCB_SHAPE_T, SHAPE_LINE_CHAIN::Segment(), SHAPE_LINE_CHAIN::SegmentCount(), SHAPE_LINE_CHAIN::SetClosed(), and traceBoardOutline.

Referenced by BOARD_ADAPTER::createBoardPolygon().

Variable Documentation

◆ g_ColorsLoaded

bool g_ColorsLoaded = false
static

Definition at line 55 of file board_adapter.cpp.

Referenced by BOARD_ADAPTER::BOARD_ADAPTER().