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 <kicad_string.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 aExcludeAllTH,
77  bool aTopSide, bool aBottomSide, bool aFormatCSV )
78 {
79  m_board = aBoard;
80  m_unitsMM = aUnitsMM;
81  m_excludeAllTH = aExcludeAllTH;
82  m_fpCount = 0;
83 
84  if( aTopSide && aBottomSide )
86  else if( aTopSide )
88  else if( aBottomSide )
90  else
92 
93  m_formatCSV = aFormatCSV;
94 }
95 
96 
98 {
99  std::string buffer;
100  char line[1024]; // A line to print intermediate data
101 
102  // Minimal text lengths:
103  m_fpCount = 0;
104  int lenRefText = 8;
105  int lenValText = 8;
106  int lenPkgText = 16;
107 
109 
110  // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
111  m_fpCount = 0;
112 
113  // Select units:
114  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
115  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
116 
117  // Build and sort the list of footprints alphabetically
118  std::vector<LIST_MOD> list;
119 
120  for( FOOTPRINT* footprint : m_board->Footprints() )
121  {
122  if( m_side != PCB_BOTH_SIDES )
123  {
124  if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
125  continue;
126  if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
127  continue;
128  }
129 
130  if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
131  continue;
132 
133  if( m_excludeAllTH && footprint->HasThroughHolePads() )
134  continue;
135 
136  m_fpCount++;
137 
138  LIST_MOD item;
139  item.m_Footprint = footprint;
140  item.m_Reference = footprint->Reference().GetShownText();
141  item.m_Value = footprint->Value().GetShownText();
142  item.m_Layer = footprint->GetLayer();
143  list.push_back( item );
144 
145  lenRefText = std::max( lenRefText, (int) item.m_Reference.length() );
146  lenValText = std::max( lenValText, (int) item.m_Value.length() );
147  lenPkgText = std::max( lenPkgText, (int) item.m_Footprint->GetFPID().GetLibItemName().length() );
148  }
149 
150  if( list.size() > 1 )
151  sort( list.begin(), list.end(), sortFPlist );
152 
153  // Switch the locale to standard C (needed to print floating point numbers)
154  LOCALE_IO toggle;
155 
156  if( m_formatCSV )
157  {
158  wxChar csv_sep = ',';
159 
160  // Set first line:;
161  sprintf( line, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
162  csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
163 
164  buffer += line;
165 
166  for( int ii = 0; ii < m_fpCount; ii++ )
167  {
168  wxPoint footprint_pos;
169  footprint_pos = list[ii].m_Footprint->GetPosition();
170  footprint_pos -= m_place_Offset;
171 
172  LAYER_NUM layer = list[ii].m_Footprint->GetLayer();
173  wxASSERT( layer == F_Cu || layer == B_Cu );
174 
175  if( layer == B_Cu )
176  footprint_pos.x = - footprint_pos.x;
177 
178  wxString tmp = "\"" + list[ii].m_Reference;
179  tmp << "\"" << csv_sep;
180  tmp << "\"" << list[ii].m_Value;
181  tmp << "\"" << csv_sep;
182  tmp << "\"" << list[ii].m_Footprint->GetFPID().GetLibItemName().wx_str();
183  tmp << "\"" << csv_sep;
184 
185  tmp << wxString::Format( "%f%c%f%c%f",
186  footprint_pos.x * conv_unit, csv_sep,
187  // Keep the Y axis oriented from bottom to top,
188  // ( change y coordinate sign )
189  -footprint_pos.y * conv_unit, csv_sep,
190  list[ii].m_Footprint->GetOrientation() / 10.0 );
191  tmp << csv_sep;
192 
193  tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
195  tmp << '\n';
196 
197  buffer += TO_UTF8( tmp );
198  }
199  }
200  else
201  {
202  // Write file header
203  sprintf( line, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) );
204 
205  buffer += line;
206 
207  wxString Title = GetBuildVersion();
208  sprintf( line, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) );
209  buffer += line;
210 
211  buffer += unit_text;
212  buffer += "## Side : ";
213 
214  if( m_side == PCB_BACK_SIDE )
215  buffer += GetBackSideName().c_str();
216  else if( m_side == PCB_FRONT_SIDE )
217  buffer += GetFrontSideName().c_str();
218  else if( m_side == PCB_BOTH_SIDES )
219  buffer += "All";
220  else
221  buffer += "---";
222 
223  buffer += "\n";
224 
225  sprintf(line, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
226  int(lenRefText), "# Ref",
227  int(lenValText), "Val",
228  int(lenPkgText), "Package",
229  "PosX", "PosY", "Rot", "Side" );
230  buffer += line;
231 
232  for( int ii = 0; ii < m_fpCount; ii++ )
233  {
234  wxPoint footprint_pos;
235  footprint_pos = list[ii].m_Footprint->GetPosition();
236  footprint_pos -= m_place_Offset;
237 
238  LAYER_NUM layer = list[ii].m_Footprint->GetLayer();
239  wxASSERT( layer == F_Cu || layer == B_Cu );
240 
241  if( layer == B_Cu )
242  footprint_pos.x = - footprint_pos.x;
243 
244  wxString ref = list[ii].m_Reference;
245  wxString val = list[ii].m_Value;
246  wxString pkg = list[ii].m_Footprint->GetFPID().GetLibItemName();
247  ref.Replace( wxT( " " ), wxT( "_" ) );
248  val.Replace( wxT( " " ), wxT( "_" ) );
249  pkg.Replace( wxT( " " ), wxT( "_" ) );
250  sprintf(line, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
251  lenRefText, TO_UTF8( ref ),
252  lenValText, TO_UTF8( val ),
253  lenPkgText, TO_UTF8( pkg ),
254  footprint_pos.x * conv_unit,
255  // Keep the coordinates in the first quadrant,
256  // (i.e. change y sign
257  -footprint_pos.y * conv_unit,
258  list[ii].m_Footprint->GetOrientation() / 10.0,
259  (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
260  buffer += line;
261  }
262 
263  // Write EOF
264  buffer += "## End\n";
265  }
266 
267  return buffer;
268 }
269 
270 
272 {
273  std::string buffer;
274 
275  m_place_Offset = wxPoint( 0, 0 );
276 
277  // Select units:
278  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
279  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
280 
281  LOCALE_IO toggle;
282 
283  // Generate header file comments.)
284  char line[1024];
285  sprintf( line, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) );
286  buffer += line;
287 
288  wxString Title = GetBuildVersion();
289  sprintf( line, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) );
290  buffer += line;
291 
292  buffer += unit_text;
293 
294  buffer += "\n$BeginDESCRIPTION\n";
295 
297 
298  buffer += "\n$BOARD\n";
299 
300  sprintf( line, "upper_left_corner %9.6f %9.6f\n",
301  bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
302  buffer += line;
303 
304  sprintf( line, "lower_right_corner %9.6f %9.6f\n",
305  bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
306  buffer += line;
307 
308  buffer += "$EndBOARD\n\n";
309 
310  std::vector<FOOTPRINT*> sortedFootprints;
311 
312  for( FOOTPRINT* footprint : m_board->Footprints() )
313  sortedFootprints.push_back( footprint );
314 
315  std::sort( sortedFootprints.begin(), sortedFootprints.end(),
316  []( FOOTPRINT* a, FOOTPRINT* b ) -> bool
317  {
318  return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
319  });
320 
321  for( FOOTPRINT* footprint : sortedFootprints )
322  {
323  wxString ref = footprint->Reference().GetShownText();
324 
325  sprintf( line, "$MODULE %s\n", TO_UTF8( ref ) );
326  buffer += line;
327 
328  sprintf( line, "reference %s\n", TO_UTF8( ref ) );
329  sprintf( line, "value %s\n", EscapedUTF8( footprint->Value().GetShownText() ).c_str() );
330  sprintf( line, "footprint %s\n", footprint->GetFPID().Format().c_str() );
331  buffer += line;
332 
333  buffer += "attribut";
334 
335  if(( footprint->GetAttributes() & ( FP_THROUGH_HOLE | FP_SMD ) ) == 0 )
336  buffer += " virtual";
337 
338  if( footprint->GetAttributes() & FP_SMD )
339  buffer += " smd";
340 
341  if( footprint->GetAttributes() & FP_THROUGH_HOLE )
342  buffer += " none";
343 
344  buffer += "\n";
345 
346  wxPoint footprint_pos = footprint->GetPosition();
347  footprint_pos -= m_place_Offset;
348 
349  sprintf( line, "position %9.6f %9.6f orientation %.2f\n",
350  footprint_pos.x * conv_unit,
351  footprint_pos.y * conv_unit,
352  footprint->GetOrientation() / 10.0 );
353  buffer += line;
354 
355  if( footprint->GetLayer() == F_Cu )
356  buffer += "layer front\n";
357  else if( footprint->GetLayer() == B_Cu )
358  buffer += "layer back\n";
359  else
360  buffer += "layer other\n";
361 
362  std::vector<PAD*> sortedPads;
363 
364  for( PAD* pad : footprint->Pads() )
365  sortedPads.push_back( pad );
366 
367  std::sort( sortedPads.begin(), sortedPads.end(),
368  []( PAD* a, PAD* b ) -> bool
369  {
370  return StrNumCmp( a->GetName(), b->GetName(), true ) < 0;
371  });
372 
373  for( PAD* pad : sortedPads )
374  {
375  sprintf( line, "$PAD \"%s\"\n", TO_UTF8( pad->GetName() ) );
376  buffer += line;
377 
378  int layer = 0;
379 
380  if( pad->GetLayerSet()[B_Cu] )
381  layer = 1;
382 
383  if( pad->GetLayerSet()[F_Cu] )
384  layer |= 2;
385 
386  static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
387  sprintf( line, "Shape %s Layer %s\n",
388  TO_UTF8( pad->ShowPadShape() ),
389  layer_name[layer] );
390  buffer += line;
391 
392  sprintf( line, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
393  pad->GetPos0().x * conv_unit,
394  pad->GetPos0().y * conv_unit,
395  pad->GetSize().x * conv_unit,
396  pad->GetSize().y * conv_unit,
397  ( pad->GetOrientation() - footprint->GetOrientation()) / 10.0 );
398  buffer += line;
399 
400  sprintf( line, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
401  buffer += line;
402 
403  sprintf( line, "shape_offset %9.6f %9.6f\n",
404  pad->GetOffset().x * conv_unit,
405  pad->GetOffset().y * conv_unit );
406  buffer += line;
407 
408  buffer += "$EndPAD\n";
409  }
410 
411  sprintf( line, "$EndMODULE %s\n\n", TO_UTF8( ref ) );
412  buffer += line;
413  }
414 
415  // Generate EOF.
416  buffer += "$EndDESCRIPTION\n";
417 
418  return buffer;
419 }
std::string GenPositionData()
build a string filled with the position data
const UTF8 & GetLibItemName() const
Definition: lib_id.h:106
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Definition: string.cpp:507
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.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:588
PLACE_FILE_EXPORTER(BOARD *aBoard, bool aUnitsMM, bool aForceSmdItems, bool aTopSide, bool aBottomSide, bool aFormatCSV)
Create a PLACE_FILE_EXPORTER.
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
std::string EscapedUTF8(const wxString &aString)
Return an 8 bit UTF8 string given aString in Unicode form.
Definition: string.cpp:392
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 & GetName() const
Definition: pad.h:130
const wxString & GetReference() const
Definition: footprint.h:421
const LIB_ID & GetFPID() const
Definition: footprint.h:185
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, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
Handle the component boundary box.
Definition: eda_rect.h:42
#define IU_PER_MILS
Definition: plotter.cpp:137
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:1104
Definition: pad.h:57
static std::string GetBackSideName()
static std::string GetFrontSideName()
wxPoint m_AuxOrigin
origin for plot exports
wxString DateAndTime()
Definition: string.cpp:498