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