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 module report (pos and module descr) (ascii file)
27  */
28 
29 #include <kicad_string.h>
30 #include <locale_io.h>
31 #include <build_version.h>
33 
34 
35 class LIST_MOD // An helper class used to build a list of useful footprints.
36 {
37 public:
38  MODULE* m_Module; // Link to the actual footprint
39  wxString m_Reference; // Its schematic reference
40  wxString m_Value; // Its schematic value
41  LAYER_NUM m_Layer; // its side (B_Cu, or F_Cu)
42 };
43 
44 
45 // Defined values to write coordinates using inches or mm:
46 static const double conv_unit_inch = 0.001 / IU_PER_MILS ; // units = INCHES
47 static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n";
48 
49 static const double conv_unit_mm = 1.0 / IU_PER_MM; // units = mm
50 static const char unit_text_mm[] = "## Unit = mm, Angle = deg.\n";
51 
52 // Sort function use by GenerefootprintsPosition()
53 // sort is made by side (layer) top layer first
54 // then by reference increasing order
55 static bool sortFPlist( const LIST_MOD& ref, const LIST_MOD& tst )
56 {
57  if( ref.m_Layer == tst.m_Layer )
58  return StrNumCmp( ref.m_Reference, tst.m_Reference ) < 0;
59 
60  return ref.m_Layer > tst.m_Layer;
61 }
62 
63 
65 {
70 };
71 
72 PLACE_FILE_EXPORTER::PLACE_FILE_EXPORTER( BOARD* aBoard, bool aUnitsMM, bool aExcludeAllTH,
73  bool aTopSide, bool aBottomSide, bool aFormatCSV )
74 {
75  m_board = aBoard;
76  m_unitsMM = aUnitsMM;
77  m_excludeAllTH = aExcludeAllTH;
78  m_fpCount = 0;
79 
80  if( aTopSide && aBottomSide )
82  else if( aTopSide )
84  else if( aBottomSide )
86  else
88 
89  m_formatCSV = aFormatCSV;
90 }
91 
92 
94 {
95  std::string buffer;
96  char line[1024]; // A line to print intermediate data
97 
98  // Minimal text lengths:
99  m_fpCount = 0;
100  int lenRefText = 8;
101  int lenValText = 8;
102  int lenPkgText = 16;
103 
105 
106  // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
107  m_fpCount = 0;
108 
109  // Select units:
110  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
111  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
112 
113  // Build and sort the list of footprints alphabetically
114  std::vector<LIST_MOD> list;
115 
116  for( MODULE* footprint : m_board->Modules() )
117  {
118  if( m_side != PCB_BOTH_SIDES )
119  {
120  if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
121  continue;
122  if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
123  continue;
124  }
125 
126  if( footprint->GetAttributes() & MOD_EXCLUDE_FROM_POS_FILES )
127  continue;
128 
129  if( m_excludeAllTH && footprint->HasThroughHolePads() )
130  continue;
131 
132  m_fpCount++;
133 
134  LIST_MOD item;
135  item.m_Module = footprint;
136  item.m_Reference = footprint->Reference().GetShownText();
137  item.m_Value = footprint->Value().GetShownText();
138  item.m_Layer = footprint->GetLayer();
139  list.push_back( item );
140 
141  lenRefText = std::max( lenRefText, (int) item.m_Reference.length() );
142  lenValText = std::max( lenValText, (int) item.m_Value.length() );
143  lenPkgText = std::max( lenPkgText, (int) item.m_Module->GetFPID().GetLibItemName().length() );
144  }
145 
146  if( list.size() > 1 )
147  sort( list.begin(), list.end(), sortFPlist );
148 
149  // Switch the locale to standard C (needed to print floating point numbers)
150  LOCALE_IO toggle;
151 
152  if( m_formatCSV )
153  {
154  wxChar csv_sep = ',';
155 
156  // Set first line:;
157  sprintf( line, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
158  csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
159 
160  buffer += line;
161 
162  for( int ii = 0; ii < m_fpCount; ii++ )
163  {
164  wxPoint footprint_pos;
165  footprint_pos = list[ii].m_Module->GetPosition();
166  footprint_pos -= m_place_Offset;
167 
168  LAYER_NUM layer = list[ii].m_Module->GetLayer();
169  wxASSERT( layer == F_Cu || layer == B_Cu );
170 
171  if( layer == B_Cu )
172  footprint_pos.x = - footprint_pos.x;
173 
174  wxString tmp = "\"" + list[ii].m_Reference;
175  tmp << "\"" << csv_sep;
176  tmp << "\"" << list[ii].m_Value;
177  tmp << "\"" << csv_sep;
178  tmp << "\"" << list[ii].m_Module->GetFPID().GetLibItemName().wx_str();
179  tmp << "\"" << csv_sep;
180 
181  tmp << wxString::Format( "%f%c%f%c%f",
182  footprint_pos.x * conv_unit, csv_sep,
183  // Keep the Y axis oriented from bottom to top,
184  // ( change y coordinate sign )
185  -footprint_pos.y * conv_unit, csv_sep,
186  list[ii].m_Module->GetOrientation() / 10.0 );
187  tmp << csv_sep;
188 
189  tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
191  tmp << '\n';
192 
193  buffer += TO_UTF8( tmp );
194  }
195  }
196  else
197  {
198  // Write file header
199  sprintf( line, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) );
200 
201  buffer += line;
202 
203  wxString Title = GetBuildVersion();
204  sprintf( line, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) );
205  buffer += line;
206 
207  buffer += unit_text;
208  buffer += "## Side : ";
209 
210  if( m_side == PCB_BACK_SIDE )
211  buffer += GetBackSideName().c_str();
212  else if( m_side == PCB_FRONT_SIDE )
213  buffer += GetFrontSideName().c_str();
214  else if( m_side == PCB_BOTH_SIDES )
215  buffer += "All";
216  else
217  buffer += "---";
218 
219  buffer += "\n";
220 
221  sprintf(line, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
222  int(lenRefText), "# Ref",
223  int(lenValText), "Val",
224  int(lenPkgText), "Package",
225  "PosX", "PosY", "Rot", "Side" );
226  buffer += line;
227 
228  for( int ii = 0; ii < m_fpCount; ii++ )
229  {
230  wxPoint footprint_pos;
231  footprint_pos = list[ii].m_Module->GetPosition();
232  footprint_pos -= m_place_Offset;
233 
234  LAYER_NUM layer = list[ii].m_Module->GetLayer();
235  wxASSERT( layer == F_Cu || layer == B_Cu );
236 
237  if( layer == B_Cu )
238  footprint_pos.x = - footprint_pos.x;
239 
240  wxString ref = list[ii].m_Reference;
241  wxString val = list[ii].m_Value;
242  wxString pkg = list[ii].m_Module->GetFPID().GetLibItemName();
243  ref.Replace( wxT( " " ), wxT( "_" ) );
244  val.Replace( wxT( " " ), wxT( "_" ) );
245  pkg.Replace( wxT( " " ), wxT( "_" ) );
246  sprintf(line, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
247  lenRefText, TO_UTF8( ref ),
248  lenValText, TO_UTF8( val ),
249  lenPkgText, TO_UTF8( pkg ),
250  footprint_pos.x * conv_unit,
251  // Keep the coordinates in the first quadrant,
252  // (i.e. change y sign
253  -footprint_pos.y * conv_unit,
254  list[ii].m_Module->GetOrientation() / 10.0,
255  (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
256  buffer += line;
257  }
258 
259  // Write EOF
260  buffer += "## End\n";
261  }
262 
263  return buffer;
264 }
265 
266 
268 {
269  std::string buffer;
270 
271  m_place_Offset = wxPoint( 0, 0 );
272 
273  // Select units:
274  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
275  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
276 
277  LOCALE_IO toggle;
278 
279  // Generate header file comments.)
280  char line[1024];
281  sprintf( line, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) );
282  buffer += line;
283 
284  wxString Title = GetBuildVersion();
285  sprintf( line, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) );
286  buffer += line;
287 
288  buffer += unit_text;
289 
290  buffer += "\n$BeginDESCRIPTION\n";
291 
293 
294  buffer += "\n$BOARD\n";
295 
296  sprintf( line, "upper_left_corner %9.6f %9.6f\n",
297  bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
298  buffer += line;
299 
300  sprintf( line, "lower_right_corner %9.6f %9.6f\n",
301  bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
302  buffer += line;
303 
304  buffer += "$EndBOARD\n\n";
305 
306  std::vector<MODULE*> sortedModules;
307 
308  for( MODULE* module : m_board->Modules() )
309  sortedModules.push_back( module );
310 
311  std::sort( sortedModules.begin(), sortedModules.end(),
312  []( MODULE* a, MODULE* b ) -> bool
313  {
314  return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
315  });
316 
317  for( MODULE* module : sortedModules )
318  {
319  wxString ref = module->Reference().GetShownText();
320 
321  sprintf( line, "$MODULE %s\n", TO_UTF8( ref ) );
322  buffer += line;
323 
324  sprintf( line, "reference %s\n", TO_UTF8( ref ) );
325  sprintf( line, "value %s\n", EscapedUTF8( module->Value().GetShownText() ).c_str() );
326  sprintf( line, "footprint %s\n", module->GetFPID().Format().c_str() );
327  buffer += line;
328 
329  buffer += "attribut";
330 
331  if( ( module->GetAttributes() & ( MOD_THROUGH_HOLE | MOD_SMD ) ) == 0 )
332  buffer += " virtual";
333 
334  if( module->GetAttributes() & MOD_SMD )
335  buffer += " smd";
336 
337  if( module->GetAttributes() & MOD_THROUGH_HOLE )
338  buffer += " none";
339 
340  buffer += "\n";
341 
342  wxPoint module_pos = module->GetPosition();
343  module_pos -= m_place_Offset;
344 
345  sprintf( line, "position %9.6f %9.6f orientation %.2f\n",
346  module_pos.x * conv_unit,
347  module_pos.y * conv_unit,
348  module->GetOrientation() / 10.0 );
349  buffer += line;
350 
351  if( module->GetLayer() == F_Cu )
352  buffer += "layer front\n";
353  else if( module->GetLayer() == B_Cu )
354  buffer += "layer back\n";
355  else
356  buffer += "layer other\n";
357 
358  std::vector<D_PAD*> sortedPads;
359 
360  for( D_PAD* pad : module->Pads() )
361  sortedPads.push_back( pad );
362 
363  std::sort( sortedPads.begin(), sortedPads.end(),
364  []( D_PAD* a, D_PAD* b ) -> bool
365  {
366  return StrNumCmp( a->GetName(), b->GetName(), true ) < 0;
367  });
368 
369  for( D_PAD* pad : sortedPads )
370  {
371  sprintf( line, "$PAD \"%s\"\n", TO_UTF8( pad->GetName() ) );
372  buffer += line;
373 
374  int layer = 0;
375 
376  if( pad->GetLayerSet()[B_Cu] )
377  layer = 1;
378 
379  if( pad->GetLayerSet()[F_Cu] )
380  layer |= 2;
381 
382  static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
383  sprintf( line, "Shape %s Layer %s\n",
384  TO_UTF8( pad->ShowPadShape() ),
385  layer_name[layer] );
386  buffer += line;
387 
388  sprintf( line, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
389  pad->GetPos0().x * conv_unit,
390  pad->GetPos0().y * conv_unit,
391  pad->GetSize().x * conv_unit,
392  pad->GetSize().y * conv_unit,
393  ( pad->GetOrientation() - module->GetOrientation()) / 10.0 );
394  buffer += line;
395 
396  sprintf( line, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
397  buffer += line;
398 
399  sprintf( line, "shape_offset %9.6f %9.6f\n",
400  pad->GetOffset().x * conv_unit,
401  pad->GetOffset().y * conv_unit );
402  buffer += line;
403 
404  buffer += "$EndPAD\n";
405  }
406 
407  sprintf( line, "$EndMODULE %s\n\n", TO_UTF8( ref ) );
408  buffer += line;
409  }
410 
411  // Generate EOF.
412  buffer += "$EndDESCRIPTION\n";
413 
414  return buffer;
415 }
std::string GenPositionData()
build a string filled with the position data
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Definition: string.cpp:409
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:111
static constexpr double IU_PER_MM
Mock up a conversion function.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: class_board.h:558
const LIB_ID & GetFPID() const
Definition: class_module.h:210
PLACE_FILE_EXPORTER(BOARD *aBoard, bool aUnitsMM, bool aForceSmdItems, bool aTopSide, bool aBottomSide, bool aFormatCSV)
Create a PLACE_FILE_EXPORTER.
int GetBottom() const
Definition: eda_rect.h:124
const wxString GetReference() const
Function GetReference.
Definition: class_module.h:435
static const double conv_unit_inch
std::string EscapedUTF8(wxString aString)
Function EscapedUTF8 returns an 8 bit UTF8 string given aString in unicode form.
Definition: string.cpp:304
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:95
MODULES & Modules()
Definition: class_board.h:284
wxString GetBuildVersion()
Get the full KiCad version string.
static const char unit_text_inch[]
std::string::size_type length() const
Definition: utf8.h:114
int GetRight() const
Definition: eda_rect.h:121
const wxString & GetName() const
Definition: class_pad.h:132
std::string GenReportData()
build a string filled with the pad report data This report does not used options aForceSmdItems,...
int LAYER_NUM
This can be replaced with int and removed.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
Information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:186
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
#define IU_PER_MILS
Definition: plotter.cpp:137
int GetY() const
Definition: eda_rect.h:112
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).
static std::string GetBackSideName()
static std::string GetFrontSideName()
wxPoint m_AuxOrigin
origin for plot exports
wxString DateAndTime()
Definition: string.cpp:400