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