KiCad PCB EDA Suite
export_footprints_placefile.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 /*
25  * 1 - create ascii/csv files for automatic placement of smd components
26  * 2 - create a footprint report (pos and footprint descr) (ascii file)
27  */
28 
29 #include <string_utils.h>
30 #include <macros.h>
31 #include <locale_io.h>
32 #include <board_design_settings.h>
33 #include <build_version.h>
35 #include <pad.h>
36 
37 #include <wx/dirdlg.h>
38 
39 class LIST_MOD // An helper class used to build a list of useful footprints.
40 {
41 public:
42  FOOTPRINT* m_Footprint; // Link to the actual footprint
43  wxString m_Reference; // Its schematic reference
44  wxString m_Value; // Its schematic value
45  LAYER_NUM m_Layer; // its side (B_Cu, or F_Cu)
46 };
47 
48 
49 // Defined values to write coordinates using inches or mm:
50 static const double conv_unit_inch = 0.001 / IU_PER_MILS ; // units = INCHES
51 static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n";
52 
53 static const double conv_unit_mm = 1.0 / IU_PER_MM; // units = mm
54 static const char unit_text_mm[] = "## Unit = mm, Angle = deg.\n";
55 
56 // Sort function use by GenerefootprintsPosition()
57 // sort is made by side (layer) top layer first
58 // then by reference increasing order
59 static bool sortFPlist( const LIST_MOD& ref, const LIST_MOD& tst )
60 {
61  if( ref.m_Layer == tst.m_Layer )
62  return StrNumCmp( ref.m_Reference, tst.m_Reference ) < 0;
63 
64  return ref.m_Layer > tst.m_Layer;
65 }
66 
67 
69 {
74 };
75 
76 PLACE_FILE_EXPORTER::PLACE_FILE_EXPORTER( BOARD* aBoard, bool aUnitsMM, bool aOnlySMD,
77  bool aExcludeAllTH, bool aTopSide, bool aBottomSide,
78  bool aFormatCSV, bool aUseAuxOrigin )
79 {
80  m_board = aBoard;
81  m_unitsMM = aUnitsMM;
82  m_onlySMD = aOnlySMD;
83  m_excludeAllTH = aExcludeAllTH;
84  m_fpCount = 0;
85 
86  if( aTopSide && aBottomSide )
88  else if( aTopSide )
90  else if( aBottomSide )
92  else
94 
95  m_formatCSV = aFormatCSV;
96 
97  if( aUseAuxOrigin )
99  else
100  m_place_Offset = wxPoint( 0, 0 );
101 }
102 
103 
105 {
106  std::string buffer;
107  char line[1024]; // A line to print intermediate data
108 
109  // Minimal text lengths:
110  m_fpCount = 0;
111  int lenRefText = 8;
112  int lenValText = 8;
113  int lenPkgText = 16;
114 
115  // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
116  m_fpCount = 0;
117 
118  // Select units:
119  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
120  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
121 
122  // Build and sort the list of footprints alphabetically
123  std::vector<LIST_MOD> list;
124 
125  for( FOOTPRINT* footprint : m_board->Footprints() )
126  {
127  if( m_side != PCB_BOTH_SIDES )
128  {
129  if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
130  continue;
131  if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
132  continue;
133  }
134 
135  if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
136  continue;
137 
138  if( m_onlySMD && !( footprint->GetAttributes() & FP_SMD ) )
139  continue;
140 
141  if( m_excludeAllTH && footprint->HasThroughHolePads() )
142  continue;
143 
144  m_fpCount++;
145 
146  LIST_MOD item;
147  item.m_Footprint = footprint;
148  item.m_Reference = footprint->Reference().GetShownText();
149  item.m_Value = footprint->Value().GetShownText();
150  item.m_Layer = footprint->GetLayer();
151  list.push_back( item );
152 
153  lenRefText = std::max( lenRefText, (int) item.m_Reference.length() );
154  lenValText = std::max( lenValText, (int) item.m_Value.length() );
155  lenPkgText = std::max( lenPkgText, (int) item.m_Footprint->GetFPID().GetLibItemName().length() );
156  }
157 
158  if( list.size() > 1 )
159  sort( list.begin(), list.end(), sortFPlist );
160 
161  // Switch the locale to standard C (needed to print floating point numbers)
162  LOCALE_IO toggle;
163 
164  if( m_formatCSV )
165  {
166  wxChar csv_sep = ',';
167 
168  // Set first line:;
169  sprintf( line, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
170  csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
171 
172  buffer += line;
173 
174  for( int ii = 0; ii < m_fpCount; ii++ )
175  {
176  wxPoint footprint_pos;
177  footprint_pos = list[ii].m_Footprint->GetPosition();
178  footprint_pos -= m_place_Offset;
179 
180  LAYER_NUM layer = list[ii].m_Footprint->GetLayer();
181  wxASSERT( layer == F_Cu || layer == B_Cu );
182 
183  if( layer == B_Cu )
184  footprint_pos.x = - footprint_pos.x;
185 
186  wxString tmp = "\"" + list[ii].m_Reference;
187  tmp << "\"" << csv_sep;
188  tmp << "\"" << list[ii].m_Value;
189  tmp << "\"" << csv_sep;
190  tmp << "\"" << list[ii].m_Footprint->GetFPID().GetLibItemName().wx_str();
191  tmp << "\"" << csv_sep;
192 
193  tmp << wxString::Format( "%f%c%f%c%f",
194  footprint_pos.x * conv_unit, csv_sep,
195  // Keep the Y axis oriented from bottom to top,
196  // ( change y coordinate sign )
197  -footprint_pos.y * conv_unit, csv_sep,
198  list[ii].m_Footprint->GetOrientation() / 10.0 );
199  tmp << csv_sep;
200 
201  tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
203  tmp << '\n';
204 
205  buffer += TO_UTF8( tmp );
206  }
207  }
208  else
209  {
210  // Write file header
211  sprintf( line, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) );
212 
213  buffer += line;
214 
215  wxString Title = GetBuildVersion();
216  sprintf( line, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) );
217  buffer += line;
218 
219  buffer += unit_text;
220  buffer += "## Side : ";
221 
222  if( m_side == PCB_BACK_SIDE )
223  buffer += GetBackSideName().c_str();
224  else if( m_side == PCB_FRONT_SIDE )
225  buffer += GetFrontSideName().c_str();
226  else if( m_side == PCB_BOTH_SIDES )
227  buffer += "All";
228  else
229  buffer += "---";
230 
231  buffer += "\n";
232 
233  sprintf(line, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
234  int(lenRefText), "# Ref",
235  int(lenValText), "Val",
236  int(lenPkgText), "Package",
237  "PosX", "PosY", "Rot", "Side" );
238  buffer += line;
239 
240  for( int ii = 0; ii < m_fpCount; ii++ )
241  {
242  wxPoint footprint_pos;
243  footprint_pos = list[ii].m_Footprint->GetPosition();
244  footprint_pos -= m_place_Offset;
245 
246  LAYER_NUM layer = list[ii].m_Footprint->GetLayer();
247  wxASSERT( layer == F_Cu || layer == B_Cu );
248 
249  if( layer == B_Cu )
250  footprint_pos.x = - footprint_pos.x;
251 
252  wxString ref = list[ii].m_Reference;
253  wxString val = list[ii].m_Value;
254  wxString pkg = list[ii].m_Footprint->GetFPID().GetLibItemName();
255  ref.Replace( wxT( " " ), wxT( "_" ) );
256  val.Replace( wxT( " " ), wxT( "_" ) );
257  pkg.Replace( wxT( " " ), wxT( "_" ) );
258  sprintf(line, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
259  lenRefText, TO_UTF8( ref ),
260  lenValText, TO_UTF8( val ),
261  lenPkgText, TO_UTF8( pkg ),
262  footprint_pos.x * conv_unit,
263  // Keep the coordinates in the first quadrant,
264  // (i.e. change y sign
265  -footprint_pos.y * conv_unit,
266  list[ii].m_Footprint->GetOrientation() / 10.0,
267  (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
268  buffer += line;
269  }
270 
271  // Write EOF
272  buffer += "## End\n";
273  }
274 
275  return buffer;
276 }
277 
278 
280 {
281  std::string buffer;
282 
283  m_place_Offset = wxPoint( 0, 0 );
284 
285  // Select units:
286  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
287  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
288 
289  LOCALE_IO toggle;
290 
291  // Generate header file comments.)
292  char line[1024];
293  sprintf( line, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) );
294  buffer += line;
295 
296  wxString Title = GetBuildVersion();
297  sprintf( line, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) );
298  buffer += line;
299 
300  buffer += unit_text;
301 
302  buffer += "\n$BeginDESCRIPTION\n";
303 
305 
306  buffer += "\n$BOARD\n";
307 
308  sprintf( line, "upper_left_corner %9.6f %9.6f\n",
309  bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
310  buffer += line;
311 
312  sprintf( line, "lower_right_corner %9.6f %9.6f\n",
313  bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
314  buffer += line;
315 
316  buffer += "$EndBOARD\n\n";
317 
318  std::vector<FOOTPRINT*> sortedFootprints;
319 
320  for( FOOTPRINT* footprint : m_board->Footprints() )
321  sortedFootprints.push_back( footprint );
322 
323  std::sort( sortedFootprints.begin(), sortedFootprints.end(),
324  []( FOOTPRINT* a, FOOTPRINT* b ) -> bool
325  {
326  return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
327  });
328 
329  for( FOOTPRINT* footprint : sortedFootprints )
330  {
331  wxString ref = footprint->Reference().GetShownText();
332 
333  sprintf( line, "$MODULE %s\n", TO_UTF8( ref ) );
334  buffer += line;
335 
336  sprintf( line, "reference %s\n", TO_UTF8( ref ) );
337  sprintf( line, "value %s\n", EscapedUTF8( footprint->Value().GetShownText() ).c_str() );
338  sprintf( line, "footprint %s\n", footprint->GetFPID().Format().c_str() );
339  buffer += line;
340 
341  buffer += "attribut";
342 
343  if(( footprint->GetAttributes() & ( FP_THROUGH_HOLE | FP_SMD ) ) == 0 )
344  buffer += " virtual";
345 
346  if( footprint->GetAttributes() & FP_SMD )
347  buffer += " smd";
348 
349  if( footprint->GetAttributes() & FP_THROUGH_HOLE )
350  buffer += " none";
351 
352  buffer += "\n";
353 
354  wxPoint footprint_pos = footprint->GetPosition();
355  footprint_pos -= m_place_Offset;
356 
357  sprintf( line, "position %9.6f %9.6f orientation %.2f\n",
358  footprint_pos.x * conv_unit,
359  footprint_pos.y * conv_unit,
360  footprint->GetOrientation() / 10.0 );
361  buffer += line;
362 
363  if( footprint->GetLayer() == F_Cu )
364  buffer += "layer front\n";
365  else if( footprint->GetLayer() == B_Cu )
366  buffer += "layer back\n";
367  else
368  buffer += "layer other\n";
369 
370  std::vector<PAD*> sortedPads;
371 
372  for( PAD* pad : footprint->Pads() )
373  sortedPads.push_back( pad );
374 
375  std::sort( sortedPads.begin(), sortedPads.end(),
376  []( PAD* a, PAD* b ) -> bool
377  {
378  return StrNumCmp( a->GetNumber(), b->GetNumber(), true ) < 0;
379  });
380 
381  for( PAD* pad : sortedPads )
382  {
383  sprintf( line, "$PAD \"%s\"\n", TO_UTF8( pad->GetNumber() ) );
384  buffer += line;
385 
386  int layer = 0;
387 
388  if( pad->GetLayerSet()[B_Cu] )
389  layer = 1;
390 
391  if( pad->GetLayerSet()[F_Cu] )
392  layer |= 2;
393 
394  static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
395  sprintf( line, "Shape %s Layer %s\n",
396  TO_UTF8( pad->ShowPadShape() ),
397  layer_name[layer] );
398  buffer += line;
399 
400  sprintf( line, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
401  pad->GetPos0().x * conv_unit,
402  pad->GetPos0().y * conv_unit,
403  pad->GetSize().x * conv_unit,
404  pad->GetSize().y * conv_unit,
405  ( pad->GetOrientation() - footprint->GetOrientation()) / 10.0 );
406  buffer += line;
407 
408  sprintf( line, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
409  buffer += line;
410 
411  sprintf( line, "shape_offset %9.6f %9.6f\n",
412  pad->GetOffset().x * conv_unit,
413  pad->GetOffset().y * conv_unit );
414  buffer += line;
415 
416  buffer += "$EndPAD\n";
417  }
418 
419  sprintf( line, "$EndMODULE %s\n\n", TO_UTF8( ref ) );
420  buffer += line;
421  }
422 
423  // Generate EOF.
424  buffer += "$EndDESCRIPTION\n";
425 
426  return buffer;
427 }
std::string GenPositionData()
build a string filled with the position data
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
static const double conv_unit_mm
static const char unit_text_mm[]
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
int GetX() const
Definition: eda_rect.h:98
static constexpr double IU_PER_MM
Mock up a conversion function.
int LAYER_NUM
This can be replaced with int and removed.
Definition: layer_ids.h:41
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
This file contains miscellaneous commonly used macros and functions.
int GetBottom() const
Definition: eda_rect.h:114
static const double conv_unit_inch
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
wxString GetBuildVersion()
Get the full KiCad version string.
static const char unit_text_inch[]
std::string::size_type length() const
Definition: utf8.h:109
FOOTPRINTS & Footprints()
Definition: board.h:233
int GetRight() const
Definition: eda_rect.h:111
const wxString & GetNumber() const
Definition: pad.h:129
const wxString & GetReference() const
Definition: footprint.h:449
const LIB_ID & GetFPID() const
Definition: footprint.h:194
std::string GenReportData()
build a string filled with the pad report data This report does not used options aForceSmdItems,...
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
PLACE_FILE_EXPORTER(BOARD *aBoard, bool aUnitsMM, bool aOnlySMD, bool aExcludeAllTH, bool aTopSide, bool aBottomSide, bool aFormatCSV, bool aUseAuxOrigin)
Create a PLACE_FILE_EXPORTER.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
Definition: layer_ids.h:71
Handle the component boundary box.
Definition: eda_rect.h:42
#define IU_PER_MILS
Definition: plotter.cpp:136
int GetY() const
Definition: eda_rect.h:99
static bool sortFPlist(const LIST_MOD &ref, const LIST_MOD &tst)
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1082
std::string EscapedUTF8(const wxString &aString)
Return an 8 bit UTF8 string given aString in Unicode form.
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Definition: pad.h:57
wxString DateAndTime()
static std::string GetBackSideName()
static std::string GetFrontSideName()
wxPoint m_AuxOrigin
origin for plot exports