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