KiCad PCB EDA Suite
place_file_exporter.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-2022 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>
33#include <build_version.h>
35#include <pad.h>
36
37#include <wx/dirdlg.h>
38
39class LIST_MOD // An helper class used to build a list of useful footprints.
40{
41public:
42 FOOTPRINT* m_Footprint; // Link to the actual footprint
43 wxString m_Reference; // Its schematic reference
44 wxString m_Value; // Its schematic value
45 int m_Layer; // its side (B_Cu, or F_Cu)
46};
47
48
49// Defined values to write coordinates using inches or mm:
50static const double conv_unit_inch = 0.001 / pcbIUScale.IU_PER_MILS ; // units = INCHES
51static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n";
52
53static const double conv_unit_mm = 1.0 / pcbIUScale.IU_PER_MM; // units = mm
54static 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
59static 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
76PLACE_FILE_EXPORTER::PLACE_FILE_EXPORTER( BOARD* aBoard, bool aUnitsMM, bool aOnlySMD,
77 bool aExcludeAllTH, bool aTopSide, bool aBottomSide,
78 bool aFormatCSV, bool aUseAuxOrigin, bool aNegateBottomX )
79{
80 m_board = aBoard;
81 m_unitsMM = aUnitsMM;
82 m_onlySMD = aOnlySMD;
83 m_excludeAllTH = aExcludeAllTH;
84 m_fpCount = 0;
85 m_negateBottomX = aNegateBottomX;
86
87 if( aTopSide && aBottomSide )
89 else if( aTopSide )
91 else if( aBottomSide )
93 else
95
96 m_formatCSV = aFormatCSV;
97
98 if( aUseAuxOrigin )
100 else
101 m_place_Offset = VECTOR2I( 0, 0 );
102}
103
104
106{
107 std::string buffer;
108 char line[1024]; // A line to print intermediate data
109
110 // Minimal text lengths:
111 m_fpCount = 0;
112 int lenRefText = 8;
113 int lenValText = 8;
114 int lenPkgText = 16;
115
116 // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
117 m_fpCount = 0;
118
119 // Select units:
120 double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
121 const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
122
123 // Build and sort the list of footprints alphabetically
124 std::vector<LIST_MOD> list;
125
126 for( FOOTPRINT* footprint : m_board->Footprints() )
127 {
128 if( m_side != PCB_BOTH_SIDES )
129 {
130 if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
131 continue;
132 if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
133 continue;
134 }
135
136 if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
137 continue;
138
139 if( m_onlySMD && !( footprint->GetAttributes() & FP_SMD ) )
140 continue;
141
142 if( m_excludeAllTH && footprint->HasThroughHolePads() )
143 continue;
144
145 m_fpCount++;
146
147 LIST_MOD item;
148 item.m_Footprint = footprint;
149 item.m_Reference = footprint->Reference().GetShownText();
150 item.m_Value = footprint->Value().GetShownText();
151 item.m_Layer = footprint->GetLayer();
152 list.push_back( item );
153
154 lenRefText = std::max( lenRefText, (int) item.m_Reference.length() );
155 lenValText = std::max( lenValText, (int) item.m_Value.length() );
156 lenPkgText = std::max( lenPkgText, (int) item.m_Footprint->GetFPID().GetLibItemName().length() );
157 }
158
159 if( list.size() > 1 )
160 sort( list.begin(), list.end(), sortFPlist );
161
162 // Switch the locale to standard C (needed to print floating point numbers)
163 LOCALE_IO toggle;
164
165 if( m_formatCSV )
166 {
167 wxChar csv_sep = ',';
168
169 // Set first line:;
170 snprintf( line, sizeof(line), "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
171 csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
172
173 buffer += line;
174
175 for( int ii = 0; ii < m_fpCount; ii++ )
176 {
177 VECTOR2I footprint_pos;
178 footprint_pos = list[ii].m_Footprint->GetPosition();
179 footprint_pos -= m_place_Offset;
180
181 int layer = list[ii].m_Footprint->GetLayer();
182 wxASSERT( layer == F_Cu || layer == B_Cu );
183
184 if( layer == B_Cu && m_negateBottomX )
185 footprint_pos.x = - footprint_pos.x;
186
187 wxString tmp = wxT( "\"" ) + list[ii].m_Reference;
188 tmp << wxT( "\"" ) << csv_sep;
189 tmp << wxT( "\"" ) << list[ii].m_Value;
190 tmp << wxT( "\"" ) << csv_sep;
191 tmp << wxT( "\"" ) << list[ii].m_Footprint->GetFPID().GetLibItemName().wx_str();
192 tmp << wxT( "\"" ) << csv_sep;
193
194 tmp << wxString::Format( wxT( "%f%c%f%c%f" ),
195 footprint_pos.x * conv_unit,
196 csv_sep,
197 // Keep the Y axis oriented from bottom to top,
198 // ( change y coordinate sign )
199 -footprint_pos.y * conv_unit,
200 csv_sep,
201 list[ii].m_Footprint->GetOrientation().AsDegrees() );
202 tmp << csv_sep;
203
204 tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
206 tmp << '\n';
207
208 buffer += TO_UTF8( tmp );
209 }
210 }
211 else
212 {
213 // Write file header
214 snprintf( line, sizeof(line), "### Footprint positions - created on %s ###\n", TO_UTF8( DateAndTime() ) );
215
216 buffer += line;
217
218 wxString Title = GetBuildVersion();
219 snprintf( line, sizeof(line), "### Printed by KiCad version %s\n", TO_UTF8( Title ) );
220 buffer += line;
221
222 buffer += unit_text;
223 buffer += "## Side : ";
224
225 if( m_side == PCB_BACK_SIDE )
226 buffer += GetBackSideName().c_str();
227 else if( m_side == PCB_FRONT_SIDE )
228 buffer += GetFrontSideName().c_str();
229 else if( m_side == PCB_BOTH_SIDES )
230 buffer += "All";
231 else
232 buffer += "---";
233
234 buffer += "\n";
235
236 snprintf(line, sizeof(line), "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
237 int(lenRefText), "# Ref",
238 int(lenValText), "Val",
239 int(lenPkgText), "Package",
240 "PosX", "PosY", "Rot", "Side" );
241 buffer += line;
242
243 for( int ii = 0; ii < m_fpCount; ii++ )
244 {
245 VECTOR2I footprint_pos;
246 footprint_pos = list[ii].m_Footprint->GetPosition();
247 footprint_pos -= m_place_Offset;
248
249 int layer = list[ii].m_Footprint->GetLayer();
250 wxASSERT( layer == F_Cu || layer == B_Cu );
251
252 if( layer == B_Cu && m_negateBottomX )
253 footprint_pos.x = - footprint_pos.x;
254
255 wxString ref = list[ii].m_Reference;
256 wxString val = list[ii].m_Value;
257 wxString pkg = list[ii].m_Footprint->GetFPID().GetLibItemName();
258 ref.Replace( wxT( " " ), wxT( "_" ) );
259 val.Replace( wxT( " " ), wxT( "_" ) );
260 pkg.Replace( wxT( " " ), wxT( "_" ) );
261 snprintf(line, sizeof(line), "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
262 lenRefText, TO_UTF8( ref ),
263 lenValText, TO_UTF8( val ),
264 lenPkgText, TO_UTF8( pkg ),
265 footprint_pos.x * conv_unit,
266 // Keep the coordinates in the first quadrant,
267 // (i.e. change y sign
268 -footprint_pos.y * conv_unit,
269 list[ii].m_Footprint->GetOrientation().AsDegrees(),
270 (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
271 buffer += line;
272 }
273
274 // Write EOF
275 buffer += "## End\n";
276 }
277
278 return buffer;
279}
280
281
283{
284 std::string buffer;
285
286 m_place_Offset = VECTOR2I( 0, 0 );
287
288 // Select units:
289 double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
290 const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
291
292 LOCALE_IO toggle;
293
294 // Generate header file comments.)
295 char line[1024];
296 snprintf( line, sizeof(line), "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) );
297 buffer += line;
298
299 wxString Title = GetBuildVersion();
300 snprintf( line, sizeof(line), "## Created by KiCad version %s\n", TO_UTF8( Title ) );
301 buffer += line;
302
303 buffer += unit_text;
304
305 buffer += "\n$BeginDESCRIPTION\n";
306
308
309 buffer += "\n$BOARD\n";
310
311 snprintf( line, sizeof(line), "upper_left_corner %9.6f %9.6f\n",
312 bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
313 buffer += line;
314
315 snprintf( line, sizeof(line), "lower_right_corner %9.6f %9.6f\n",
316 bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
317 buffer += line;
318
319 buffer += "$EndBOARD\n\n";
320
321 std::vector<FOOTPRINT*> sortedFootprints;
322
323 for( FOOTPRINT* footprint : m_board->Footprints() )
324 sortedFootprints.push_back( footprint );
325
326 std::sort( sortedFootprints.begin(), sortedFootprints.end(),
327 []( FOOTPRINT* a, FOOTPRINT* b ) -> bool
328 {
329 return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
330 });
331
332 for( FOOTPRINT* footprint : sortedFootprints )
333 {
334 wxString ref = footprint->Reference().GetShownText();
335
336 snprintf( line, sizeof(line), "$MODULE %s\n", TO_UTF8( ref ) );
337 buffer += line;
338
339 snprintf( line, sizeof(line), "reference %s\n", TO_UTF8( ref ) );
340 snprintf( line, sizeof(line), "value %s\n", EscapedUTF8( footprint->Value().GetShownText() ).c_str() );
341 snprintf( line, sizeof(line), "footprint %s\n", footprint->GetFPID().Format().c_str() );
342 buffer += line;
343
344 buffer += "attribut";
345
346 if(( footprint->GetAttributes() & ( FP_THROUGH_HOLE | FP_SMD ) ) == 0 )
347 buffer += " virtual";
348
349 if( footprint->GetAttributes() & FP_SMD )
350 buffer += " smd";
351
352 if( footprint->GetAttributes() & FP_THROUGH_HOLE )
353 buffer += " none";
354
355 buffer += "\n";
356
357 VECTOR2I footprint_pos = footprint->GetPosition();
358 footprint_pos -= m_place_Offset;
359
360 snprintf( line, sizeof(line), "position %9.6f %9.6f orientation %.2f\n",
361 footprint_pos.x * conv_unit,
362 footprint_pos.y * conv_unit,
363 footprint->GetOrientation().AsDegrees() );
364 buffer += line;
365
366 if( footprint->GetLayer() == F_Cu )
367 buffer += "layer front\n";
368 else if( footprint->GetLayer() == B_Cu )
369 buffer += "layer back\n";
370 else
371 buffer += "layer other\n";
372
373 std::vector<PAD*> sortedPads;
374
375 for( PAD* pad : footprint->Pads() )
376 sortedPads.push_back( pad );
377
378 std::sort( sortedPads.begin(), sortedPads.end(),
379 []( PAD* a, PAD* b ) -> bool
380 {
381 return StrNumCmp( a->GetNumber(), b->GetNumber(), true ) < 0;
382 });
383
384 for( PAD* pad : sortedPads )
385 {
386 snprintf( line, sizeof(line), "$PAD \"%s\"\n", TO_UTF8( pad->GetNumber() ) );
387 buffer += line;
388
389 int layer = 0;
390
391 if( pad->GetLayerSet()[B_Cu] )
392 layer = 1;
393
394 if( pad->GetLayerSet()[F_Cu] )
395 layer |= 2;
396
397 static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
398 snprintf( line, sizeof(line), "Shape %s Layer %s\n",
399 TO_UTF8( pad->ShowPadShape() ),
400 layer_name[layer] );
401 buffer += line;
402
403 snprintf( line, sizeof(line), "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
404 pad->GetPos0().x * conv_unit,
405 pad->GetPos0().y * conv_unit,
406 pad->GetSize().x * conv_unit,
407 pad->GetSize().y * conv_unit,
408 ( pad->GetOrientation() - footprint->GetOrientation() ).AsDegrees() );
409 buffer += line;
410
411 snprintf( line, sizeof(line), "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
412 buffer += line;
413
414 snprintf( line, sizeof(line), "shape_offset %9.6f %9.6f\n",
415 pad->GetOffset().x * conv_unit,
416 pad->GetOffset().y * conv_unit );
417 buffer += line;
418
419 buffer += "$EndPAD\n";
420 }
421
422 snprintf( line, sizeof(line), "$EndMODULE %s\n\n", TO_UTF8( ref ) );
423 buffer += line;
424 }
425
426 // Generate EOF.
427 buffer += "$EndDESCRIPTION\n";
428
429 return buffer;
430}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
wxString GetBuildVersion()
Get the full KiCad version string.
const VECTOR2I & GetAuxOrigin()
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1235
FOOTPRINTS & Footprints()
Definition: board.h:307
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:686
coord_type GetY() const
Definition: box2.h:181
coord_type GetX() const
Definition: box2.h:180
coord_type GetRight() const
Definition: box2.h:189
coord_type GetBottom() const
Definition: box2.h:190
const LIB_ID & GetFPID() const
Definition: footprint.h:212
const UTF8 & GetLibItemName() const
Definition: lib_id.h:102
wxString m_Reference
FOOTPRINT * m_Footprint
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
Definition: pad.h:59
static std::string GetFrontSideName()
std::string GenPositionData()
build a string filled with the position data
static std::string GetBackSideName()
PLACE_FILE_EXPORTER(BOARD *aBoard, bool aUnitsMM, bool aOnlySMD, bool aExcludeAllTH, bool aTopSide, bool aBottomSide, bool aFormatCSV, bool aUseAuxOrigin, bool aNegateBottomX)
Create a PLACE_FILE_EXPORTER.
std::string GenReportData()
build a string filled with the pad report data This report does not used options aForceSmdItems,...
std::string::size_type length() const
Definition: utf8.h:109
@ FP_SMD
Definition: footprint.h:69
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:70
@ FP_THROUGH_HOLE
Definition: footprint.h:68
@ B_Cu
Definition: layer_ids.h:95
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
static const double conv_unit_mm
static bool sortFPlist(const LIST_MOD &ref, const LIST_MOD &tst)
static const double conv_unit_inch
static const char unit_text_mm[]
@ PCB_BACK_SIDE
@ PCB_NO_SIDE
@ PCB_BOTH_SIDES
@ PCB_FRONT_SIDE
static const char unit_text_inch[]
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
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString DateAndTime()
std::string EscapedUTF8(const wxString &aString)
Return an 8 bit UTF8 string given aString in Unicode form.
const double IU_PER_MM
Definition: base_units.h:77
const double IU_PER_MILS
Definition: base_units.h:78
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618