KiCad PCB EDA Suite
export_gencad.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) 2016 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
7  * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
32 #include <fctsys.h>
33 #include <class_drawpanel.h>
34 #include <confirm.h>
35 #include <gestfich.h>
36 #include <pgm_base.h>
37 #include <wxPcbStruct.h>
38 #include <trigo.h>
39 #include <build_version.h>
40 #include <macros.h>
41 
42 #include <pcbnew.h>
44 
45 #include <class_board.h>
46 #include <class_module.h>
47 #include <class_track.h>
48 #include <class_edge_mod.h>
49 
50 #include <hash_eda.h>
51 
52 static bool CreateHeaderInfoData( FILE* aFile, PCB_EDIT_FRAME* frame );
53 static void CreateArtworksSection( FILE* aFile );
54 static void CreateTracksInfoData( FILE* aFile, BOARD* aPcb );
55 static void CreateBoardSection( FILE* aFile, BOARD* aPcb );
56 static void CreateComponentsSection( FILE* aFile, BOARD* aPcb );
57 static void CreateDevicesSection( FILE* aFile, BOARD* aPcb );
58 static void CreateRoutesSection( FILE* aFile, BOARD* aPcb );
59 static void CreateSignalsSection( FILE* aFile, BOARD* aPcb );
60 static void CreateShapesSection( FILE* aFile, BOARD* aPcb );
61 static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb );
62 static void FootprintWriteShape( FILE* File, MODULE* module, const wxString& aShapeName );
63 
64 // layer names for Gencad export
65 
66 #if 0 // was:
67 static const wxString GenCADLayerName[] =
68 {
69  wxT( "BOTTOM" ), wxT( "INNER1" ), wxT( "INNER2" ),
70  wxT( "INNER3" ), wxT( "INNER4" ), wxT( "INNER5" ),
71  wxT( "INNER6" ), wxT( "INNER7" ), wxT( "INNER8" ),
72  wxT( "INNER9" ), wxT( "INNER10" ), wxT( "INNER11" ),
73  wxT( "INNER12" ), wxT( "INNER13" ), wxT( "INNER14" ),
74  wxT( "TOP" ), wxT( "LAYER17" ), wxT( "LAYER18" ),
75  wxT( "SOLDERPASTE_BOTTOM" ), wxT( "SOLDERPASTE_TOP" ),
76  wxT( "SILKSCREEN_BOTTOM" ), wxT( "SILKSCREEN_TOP" ),
77  wxT( "SOLDERMASK_BOTTOM" ), wxT( "SOLDERMASK_TOP" ), wxT( "LAYER25" ),
78  wxT( "LAYER26" ), wxT( "LAYER27" ), wxT( "LAYER28" ),
79  wxT( "LAYER29" ), wxT( "LAYER30" ), wxT( "LAYER31" ),
80  wxT( "LAYER32" )
81 };
82 
83 // flipped layer name for Gencad export (to make CAM350 imports correct)
84 static const wxString GenCADLayerNameFlipped[32] =
85 {
86  wxT( "TOP" ), wxT( "INNER14" ), wxT( "INNER13" ),
87  wxT( "INNER12" ), wxT( "INNER11" ), wxT( "INNER10" ),
88  wxT( "INNER9" ), wxT( "INNER8" ), wxT( "INNER7" ),
89  wxT( "INNER6" ), wxT( "INNER5" ), wxT( "INNER4" ),
90  wxT( "INNER3" ), wxT( "INNER2" ), wxT( "INNER1" ),
91  wxT( "BOTTOM" ), wxT( "LAYER17" ), wxT( "LAYER18" ),
92  wxT( "SOLDERPASTE_TOP" ), wxT( "SOLDERPASTE_BOTTOM" ),
93  wxT( "SILKSCREEN_TOP" ), wxT( "SILKSCREEN_BOTTOM" ),
94  wxT( "SOLDERMASK_TOP" ), wxT( "SOLDERMASK_BOTTOM" ), wxT( "LAYER25" ),
95  wxT( "LAYER26" ), wxT( "LAYER27" ), wxT( "LAYER28" ),
96  wxT( "LAYER29" ), wxT( "LAYER30" ), wxT( "LAYER31" ),
97  wxT( "LAYER32" )
98 };
99 
100 #else
101 
102 static std::string GenCADLayerName( int aCuCount, PCB_LAYER_ID aId )
103 {
104  if( IsCopperLayer( aId ) )
105  {
106  if( aId == F_Cu )
107  return "TOP";
108  else if( aId == B_Cu )
109  return "BOTTOM";
110 
111  else if( aId <= 14 )
112  {
113  return StrPrintf( "INNER%d", aCuCount - aId - 1 );
114  }
115  else
116  {
117  return StrPrintf( "LAYER%d", aId );
118  }
119  }
120 
121  else
122  {
123  const char* txt;
124 
125  // using a switch to clearly show mapping & catch out of bounds index.
126  switch( aId )
127  {
128  // Technicals
129  case B_Adhes: txt = "B.Adhes"; break;
130  case F_Adhes: txt = "F.Adhes"; break;
131  case B_Paste: txt = "SOLDERPASTE_BOTTOM"; break;
132  case F_Paste: txt = "SOLDERPASTE_TOP"; break;
133  case B_SilkS: txt = "SILKSCREEN_BOTTOM"; break;
134  case F_SilkS: txt = "SILKSCREEN_TOP"; break;
135  case B_Mask: txt = "SOLDERMASK_BOTTOM"; break;
136  case F_Mask: txt = "SOLDERMASK_TOP"; break;
137 
138  // Users
139  case Dwgs_User: txt = "Dwgs.User"; break;
140  case Cmts_User: txt = "Cmts.User"; break;
141  case Eco1_User: txt = "Eco1.User"; break;
142  case Eco2_User: txt = "Eco2.User"; break;
143  case Edge_Cuts: txt = "Edge.Cuts"; break;
144  case Margin: txt = "Margin"; break;
145 
146  // Footprint
147  case F_CrtYd: txt = "F_CrtYd"; break;
148  case B_CrtYd: txt = "B_CrtYd"; break;
149  case F_Fab: txt = "F_Fab"; break;
150  case B_Fab: txt = "B_Fab"; break;
151 
152  default:
153  wxASSERT_MSG( 0, wxT( "aId UNEXPECTED" ) );
154  txt = "BAD-INDEX!"; break;
155  }
156 
157  return txt;
158  }
159 }
160 
161 
162 static const PCB_LAYER_ID gc_seq[] = {
163  B_Cu,
164  In30_Cu,
165  In29_Cu,
166  In28_Cu,
167  In27_Cu,
168  In26_Cu,
169  In25_Cu,
170  In24_Cu,
171  In23_Cu,
172  In22_Cu,
173  In21_Cu,
174  In20_Cu,
175  In19_Cu,
176  In18_Cu,
177  In17_Cu,
178  In16_Cu,
179  In15_Cu,
180  In14_Cu,
181  In13_Cu,
182  In12_Cu,
183  In11_Cu,
184  In10_Cu,
185  In9_Cu,
186  In8_Cu,
187  In7_Cu,
188  In6_Cu,
189  In5_Cu,
190  In4_Cu,
191  In3_Cu,
192  In2_Cu,
193  In1_Cu,
194  F_Cu,
195 };
196 
197 
198 // flipped layer name for Gencad export (to make CAM350 imports correct)
199 static std::string GenCADLayerNameFlipped( int aCuCount, PCB_LAYER_ID aId )
200 {
201  if( 1<= aId && aId <= 14 )
202  {
203  return StrPrintf( "INNER%d", 14 - aId );
204  }
205 
206  return GenCADLayerName( aCuCount, aId );
207 }
208 
209 
210 static wxString escapeString( const wxString& aString )
211 {
212  wxString copy( aString );
213  copy.Replace( "\"", "\\\"" );
214  return copy;
215 }
216 
217 #endif
218 
219 static std::string fmt_mask( LSET aSet )
220 {
221 #if 0
222  return aSet.FmtHex();
223 #else
224  return StrPrintf( "%08x", (unsigned) ( aSet & LSET::AllCuMask() ).to_ulong() );
225 #endif
226 }
227 
228 // Export options
229 static bool flipBottomPads;
230 static bool uniquePins;
231 static bool individualShapes;
232 static bool storeOriginCoords;
233 
234 // These are the export origin (the auxiliary axis)
236 
237 // Association between shape names (using shapeName index) and components
238 static std::map<MODULE*, int> componentShapes;
239 static std::map<int, wxString> shapeNames;
240 
241 static const wxString& getShapeName( MODULE* aModule )
242 {
243  static const wxString invalid( "invalid" );
244 
245  if( individualShapes )
246  return aModule->GetReference();
247 
248  auto itShape = componentShapes.find( aModule );
249  wxCHECK( itShape != componentShapes.end(), invalid );
250 
251  auto itName = shapeNames.find( itShape->second );
252  wxCHECK( itName != shapeNames.end(), invalid );
253 
254  return itName->second;
255 }
256 
257 // GerbTool chokes on units different than INCH so this is the conversion factor
258 const static double SCALE_FACTOR = 1000.0 * IU_PER_MILS;
259 
260 /* Two helper functions to calculate coordinates of modules in gencad values
261  * (GenCAD Y axis from bottom to top)
262  */
263 static double MapXTo( int aX )
264 {
265  return (aX - GencadOffsetX) / SCALE_FACTOR;
266 }
267 
268 
269 static double MapYTo( int aY )
270 {
271  return (GencadOffsetY - aY) / SCALE_FACTOR;
272 }
273 
274 
275 /* Driver function: processing starts here */
276 void PCB_EDIT_FRAME::ExportToGenCAD( wxCommandEvent& aEvent )
277 {
278  DIALOG_GENCAD_EXPORT_OPTIONS optionsDialog( this );
279 
280  if( optionsDialog.ShowModal() == wxID_CANCEL )
281  return;
282 
283  FILE* file = wxFopen( optionsDialog.GetFileName(), "wt" );
284 
285  if( !file )
286  {
287  DisplayError( this, wxString::Format( _( "Unable to create <%s>" ),
288  GetChars( optionsDialog.GetFileName() ) ) );
289  return;
290  }
291 
292  // Get options
293  flipBottomPads = optionsDialog.GetOption( FLIP_BOTTOM_PADS );
294  uniquePins = optionsDialog.GetOption( UNIQUE_PIN_NAMES );
295  individualShapes = optionsDialog.GetOption( INDIVIDUAL_SHAPES );
297 
298  // Switch the locale to standard C (needed to print floating point numbers)
299  LOCALE_IO toggle;
300 
301  // Update some board data, to ensure a reliable gencad export
303 
304  // Save the auxiliary origin for the rest of the module
305  GencadOffsetX = optionsDialog.GetOption( USE_AUX_ORIGIN ) ? GetAuxOrigin().x : 0;
306  GencadOffsetY = optionsDialog.GetOption( USE_AUX_ORIGIN ) ? GetAuxOrigin().y : 0;
307 
308  // No idea on *why* this should be needed... maybe to fix net names?
309  Compile_Ratsnest( NULL, true );
310 
311  /* Temporary modification of footprints that are flipped (i.e. on bottom
312  * layer) to convert them to non flipped footprints.
313  * This is necessary to easily export shapes to GenCAD,
314  * that are given as normal orientation (non flipped, rotation = 0))
315  * these changes will be undone later
316  */
317  BOARD* pcb = GetBoard();
318  MODULE* module;
319 
320  for( module = pcb->m_Modules; module; module = module->Next() )
321  {
322  module->SetFlag( 0 );
323 
324  if( module->GetLayer() == B_Cu )
325  {
326  module->Flip( module->GetPosition() );
327  module->SetFlag( 1 );
328  }
329  }
330 
331  /* Gencad has some mandatory and some optional sections: some importer
332  * need the padstack section (which is optional) anyway. Also the
333  * order of the section *is* important */
334 
335  CreateHeaderInfoData( file, this ); // Gencad header
336  CreateBoardSection( file, pcb ); // Board perimeter
337 
338  CreatePadsShapesSection( file, pcb ); // Pads and padstacks
339  CreateArtworksSection( file ); // Empty but mandatory
340 
341  /* Gencad splits a component info in shape, component and device.
342  * We don't do any sharing (it would be difficult since each module is
343  * customizable after placement) */
344  CreateShapesSection( file, pcb );
345  CreateComponentsSection( file, pcb );
346  CreateDevicesSection( file, pcb );
347 
348  // In a similar way the netlist is split in net, track and route
349  CreateSignalsSection( file, pcb );
350  CreateTracksInfoData( file, pcb );
351  CreateRoutesSection( file, pcb );
352 
353  fclose( file );
354 
355  // Undo the footprints modifications (flipped footprints)
356  for( module = pcb->m_Modules; module; module = module->Next() )
357  {
358  if( module->GetFlag() )
359  {
360  module->Flip( module->GetPosition() );
361  module->SetFlag( 0 );
362  }
363  }
364 
365  componentShapes.clear();
366  shapeNames.clear();
367 }
368 
369 
370 // Comparator for sorting pads with qsort
371 static int PadListSortByShape( const void* aRefptr, const void* aObjptr )
372 {
373  const D_PAD* padref = *(D_PAD**) aRefptr;
374  const D_PAD* padcmp = *(D_PAD**) aObjptr;
375 
376  return D_PAD::Compare( padref, padcmp );
377 }
378 
379 
380 // Sort vias for uniqueness
381 static int ViaSort( const void* aRefptr, const void* aObjptr )
382 {
383  VIA* padref = *(VIA**) aRefptr;
384  VIA* padcmp = *(VIA**) aObjptr;
385 
386  if( padref->GetWidth() != padcmp->GetWidth() )
387  return padref->GetWidth() - padcmp->GetWidth();
388 
389  if( padref->GetDrillValue() != padcmp->GetDrillValue() )
390  return padref->GetDrillValue() - padcmp->GetDrillValue();
391 
392  if( padref->GetLayerSet() != padcmp->GetLayerSet() )
393  return padref->GetLayerSet().FmtBin().compare( padcmp->GetLayerSet().FmtBin() );
394 
395  return 0;
396 }
397 
398 
399 // The ARTWORKS section is empty but (officially) mandatory
400 static void CreateArtworksSection( FILE* aFile )
401 {
402  /* The artworks section is empty */
403  fputs( "$ARTWORKS\n", aFile );
404  fputs( "$ENDARTWORKS\n\n", aFile );
405 }
406 
407 
408 // Emit PADS and PADSTACKS. They are sorted and emitted uniquely.
409 // Via name is synthesized from their attributes, pads are numbered
410 static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
411 {
412  std::vector<D_PAD*> pads;
413  std::vector<D_PAD*> padstacks;
414  std::vector<VIA*> vias;
415  std::vector<VIA*> viastacks;
416 
417  padstacks.resize( 1 ); // We count pads from 1
418 
419  // The master layermask (i.e. the enabled layers) for padstack generation
420  LSET master_layermask = aPcb->GetDesignSettings().GetEnabledLayers();
421  int cu_count = aPcb->GetCopperLayerCount();
422 
423  fputs( "$PADS\n", aFile );
424 
425  // Enumerate and sort the pads
426  if( aPcb->GetPadCount() > 0 )
427  {
428  pads = aPcb->GetPads();
429  qsort( &pads[0], aPcb->GetPadCount(), sizeof( D_PAD* ),
431  }
432 
433  // The same for vias
434  for( VIA* via = GetFirstVia( aPcb->m_Track ); via;
435  via = GetFirstVia( via->Next() ) )
436  {
437  vias.push_back( via );
438  }
439 
440  qsort( &vias[0], vias.size(), sizeof(VIA*), ViaSort );
441 
442  // Emit vias pads
443  TRACK* old_via = 0;
444 
445  for( unsigned i = 0; i < vias.size(); i++ )
446  {
447  VIA* via = vias[i];
448 
449  if( old_via && 0 == ViaSort( &old_via, &via ) )
450  continue;
451 
452  old_via = via;
453  viastacks.push_back( via );
454  fprintf( aFile, "PAD V%d.%d.%s ROUND %g\nCIRCLE 0 0 %g\n",
455  via->GetWidth(), via->GetDrillValue(),
456  fmt_mask( via->GetLayerSet() & master_layermask ).c_str(),
457  via->GetDrillValue() / SCALE_FACTOR,
458  via->GetWidth() / (SCALE_FACTOR * 2) );
459  }
460 
461  // Emit component pads
462  D_PAD* old_pad = 0;
463  int pad_name_number = 0;
464 
465  for( unsigned i = 0; i<pads.size(); ++i )
466  {
467  D_PAD* pad = pads[i];
468  const wxPoint& off = pad->GetOffset();
469 
470  pad->SetSubRatsnest( pad_name_number );
471 
472  if( old_pad && 0==D_PAD::Compare( old_pad, pad ) )
473  continue; // already created
474 
475  old_pad = pad;
476 
477  pad_name_number++;
478  pad->SetSubRatsnest( pad_name_number );
479 
480  fprintf( aFile, "PAD P%d", pad->GetSubRatsnest() );
481 
482  padstacks.push_back( pad ); // Will have its own padstack later
483  int dx = pad->GetSize().x / 2;
484  int dy = pad->GetSize().y / 2;
485 
486  switch( pad->GetShape() )
487  {
488  default:
489  wxASSERT_MSG( false, "Pad type not implemented" );
490  // fall-through
491 
492  case PAD_SHAPE_CIRCLE:
493  fprintf( aFile, " ROUND %g\n",
494  pad->GetDrillSize().x / SCALE_FACTOR );
495  /* Circle is center, radius */
496  fprintf( aFile, "CIRCLE %g %g %g\n",
497  off.x / SCALE_FACTOR,
498  -off.y / SCALE_FACTOR,
499  pad->GetSize().x / (SCALE_FACTOR * 2) );
500  break;
501 
502  case PAD_SHAPE_RECT:
503  fprintf( aFile, " RECTANGULAR %g\n",
504  pad->GetDrillSize().x / SCALE_FACTOR );
505 
506  // Rectangle is begin, size *not* begin, end!
507  fprintf( aFile, "RECTANGLE %g %g %g %g\n",
508  (-dx + off.x ) / SCALE_FACTOR,
509  (-dy - off.y ) / SCALE_FACTOR,
510  dx / (SCALE_FACTOR / 2), dy / (SCALE_FACTOR / 2) );
511  break;
512 
513  case PAD_SHAPE_ROUNDRECT:
514  case PAD_SHAPE_OVAL:
515  {
516  const wxSize& size = pad->GetSize();
517  int radius;
518 
519  if( pad->GetShape() == PAD_SHAPE_ROUNDRECT )
520  radius = pad->GetRoundRectCornerRadius();
521  else
522  radius = std::min( size.x, size.y ) / 2;
523 
524  int lineX = size.x / 2 - radius;
525  int lineY = size.y / 2 - radius;
526 
527  fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
528 
529  // bottom left arc
530  fprintf( aFile, "ARC %g %g %g %g %g %g\n",
531  ( off.x - lineX - radius ) / SCALE_FACTOR,
532  ( -off.y - lineY ) / SCALE_FACTOR, ( off.x - lineX ) / SCALE_FACTOR,
533  ( -off.y - lineY - radius ) / SCALE_FACTOR,
534  ( off.x - lineX ) / SCALE_FACTOR, ( -off.y - lineY ) / SCALE_FACTOR );
535 
536  // bottom line
537  if( lineX > 0 )
538  {
539  fprintf( aFile, "LINE %g %g %g %g\n",
540  ( off.x - lineX ) / SCALE_FACTOR,
541  ( -off.y - lineY - radius ) / SCALE_FACTOR,
542  ( off.x + lineX ) / SCALE_FACTOR,
543  ( -off.y - lineY - radius ) / SCALE_FACTOR );
544  }
545 
546  // bottom right arc
547  fprintf( aFile, "ARC %g %g %g %g %g %g\n",
548  ( off.x + lineX ) / SCALE_FACTOR,
549  ( -off.y - lineY - radius ) / SCALE_FACTOR,
550  ( off.x + lineX + radius ) / SCALE_FACTOR,
551  ( -off.y - lineY ) / SCALE_FACTOR, ( off.x + lineX ) / SCALE_FACTOR,
552  ( -off.y - lineY ) / SCALE_FACTOR );
553 
554  // right line
555  if( lineY > 0 )
556  {
557  fprintf( aFile, "LINE %g %g %g %g\n",
558  ( off.x + lineX + radius ) / SCALE_FACTOR,
559  ( -off.y + lineY ) / SCALE_FACTOR,
560  ( off.x + lineX + radius ) / SCALE_FACTOR,
561  ( -off.y - lineY ) / SCALE_FACTOR );
562  }
563 
564  // top right arc
565  fprintf( aFile, "ARC %g %g %g %g %g %g\n",
566  ( off.x + lineX + radius ) / SCALE_FACTOR,
567  ( -off.y + lineY ) / SCALE_FACTOR, ( off.x + lineX ) / SCALE_FACTOR,
568  ( -off.y + lineY + radius ) / SCALE_FACTOR,
569  ( off.x + lineX ) / SCALE_FACTOR, ( -off.y + lineY ) / SCALE_FACTOR );
570 
571  // top line
572  if( lineX > 0 )
573  {
574  fprintf( aFile, "LINE %g %g %g %g\n"
575  , ( off.x - lineX ) / SCALE_FACTOR,
576  ( -off.y + lineY + radius ) / SCALE_FACTOR,
577  ( off.x + lineX ) / SCALE_FACTOR,
578  ( -off.y + lineY + radius ) / SCALE_FACTOR );
579  }
580 
581  // top left arc
582  fprintf( aFile, "ARC %g %g %g %g %g %g\n",
583  ( off.x - lineX ) / SCALE_FACTOR,
584  ( -off.y + lineY + radius ) / SCALE_FACTOR,
585  ( off.x - lineX - radius ) / SCALE_FACTOR,
586  ( -off.y + lineY ) / SCALE_FACTOR, ( off.x - lineX ) / SCALE_FACTOR,
587  ( -off.y + lineY ) / SCALE_FACTOR );
588 
589  // left line
590  if( lineY > 0 )
591  {
592  fprintf( aFile, "LINE %g %g %g %g\n",
593  ( off.x - lineX - radius ) / SCALE_FACTOR,
594  ( -off.y - lineY ) / SCALE_FACTOR,
595  ( off.x - lineX - radius ) / SCALE_FACTOR,
596  ( -off.y + lineY ) / SCALE_FACTOR );
597  }
598  }
599  break;
600 
601  case PAD_SHAPE_TRAPEZOID:
602  {
603  fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
604 
605  wxPoint poly[4];
606  pad->BuildPadPolygon( poly, wxSize( 0, 0 ), 0 );
607 
608  for( int cur = 0; cur < 4; ++cur )
609  {
610  int next = ( cur + 1 ) % 4;
611  fprintf( aFile, "LINE %g %g %g %g\n",
612  ( off.x + poly[cur].x ) / SCALE_FACTOR,
613  ( -off.y - poly[cur].y ) / SCALE_FACTOR,
614  ( off.x + poly[next].x ) / SCALE_FACTOR,
615  ( -off.y - poly[next].y ) / SCALE_FACTOR );
616  }
617  }
618  break;
619 
620  case PAD_SHAPE_CUSTOM:
621  {
622  fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
623 
624  const SHAPE_POLY_SET& outline = pad->GetCustomShapeAsPolygon();
625 
626  for( int jj = 0; jj < outline.OutlineCount(); ++jj )
627  {
628  const SHAPE_LINE_CHAIN& poly = outline.COutline( jj );
629  int pointCount = poly.PointCount();
630 
631  for( int ii = 0; ii < pointCount; ii++ )
632  {
633  int next = ( ii + 1 ) % pointCount;
634  fprintf( aFile, "LINE %g %g %g %g\n",
635  ( off.x + poly.CPoint( ii ).x ) / SCALE_FACTOR,
636  ( -off.y - poly.CPoint( ii ).y ) / SCALE_FACTOR,
637  ( off.x + poly.CPoint( next ).x ) / SCALE_FACTOR,
638  ( -off.y - poly.CPoint( next ).y ) / SCALE_FACTOR );
639  }
640  }
641  }
642  break;
643  }
644  }
645 
646  fputs( "\n$ENDPADS\n\n", aFile );
647 
648  // Now emit the padstacks definitions, using the combined layer masks
649  fputs( "$PADSTACKS\n", aFile );
650 
651  // Via padstacks
652  for( unsigned i = 0; i < viastacks.size(); i++ )
653  {
654  VIA* via = viastacks[i];
655 
656  LSET mask = via->GetLayerSet() & master_layermask;
657 
658  fprintf( aFile, "PADSTACK VIA%d.%d.%s %g\n",
659  via->GetWidth(), via->GetDrillValue(),
660  fmt_mask( mask ).c_str(),
661  via->GetDrillValue() / SCALE_FACTOR );
662 
663  for( LSEQ seq = mask.Seq( gc_seq, DIM( gc_seq ) ); seq; ++seq )
664  {
665  PCB_LAYER_ID layer = *seq;
666 
667  fprintf( aFile, "PAD V%d.%d.%s %s 0 0\n",
668  via->GetWidth(), via->GetDrillValue(),
669  fmt_mask( mask ).c_str(),
670  GenCADLayerName( cu_count, layer ).c_str()
671  );
672  }
673  }
674 
675  /* Component padstacks
676  * Older versions of CAM350 don't apply correctly the FLIP semantics for
677  * padstacks, i.e. doesn't swap the top and bottom layers... so I need to
678  * define the shape as MIRRORX and define a separate 'flipped' padstack...
679  * until it appears yet another noncompliant importer */
680  for( unsigned i = 1; i < padstacks.size(); i++ )
681  {
682  D_PAD* pad = padstacks[i];
683 
684  // Straight padstack
685  fprintf( aFile, "PADSTACK PAD%u %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );
686 
687  LSET pad_set = pad->GetLayerSet() & master_layermask;
688 
689  // the special gc_seq
690  for( LSEQ seq = pad_set.Seq( gc_seq, DIM( gc_seq ) ); seq; ++seq )
691  {
692  PCB_LAYER_ID layer = *seq;
693 
694  fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerName( cu_count, layer ).c_str() );
695  }
696 
697  // Flipped padstack
698  if( flipBottomPads )
699  {
700  fprintf( aFile, "PADSTACK PAD%uF %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );
701 
702  // the normal PCB_LAYER_ID sequence is inverted from gc_seq[]
703  for( LSEQ seq = pad_set.Seq(); seq; ++seq )
704  {
705  PCB_LAYER_ID layer = *seq;
706 
707  fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerNameFlipped( cu_count, layer ).c_str() );
708  }
709  }
710  }
711 
712  fputs( "$ENDPADSTACKS\n\n", aFile );
713 }
714 
715 
717 static size_t hashModule( const MODULE* aModule )
718 {
719  size_t ret = 0x11223344;
720  constexpr int flags = HASH_FLAGS::POSITION | HASH_FLAGS::REL_COORD
722 
723  for( const BOARD_ITEM* i = aModule->GraphicalItemsList(); i; i = i->Next() )
724  ret ^= hash_eda( i, flags );
725 
726  for( const D_PAD* i = aModule->PadsList(); i; i = i->Next() )
727  ret ^= hash_eda( i, flags );
728 
729  return ret;
730 }
731 
732 
733 /* Creates the footprint shape list.
734  * Since module shape is customizable after the placement we cannot share them;
735  * instead we opt for the one-module-one-shape-one-component-one-device approach
736  */
737 static void CreateShapesSection( FILE* aFile, BOARD* aPcb )
738 {
739  MODULE* module;
740  D_PAD* pad;
741  const char* layer;
742  wxString pinname;
743  const char* mirror = "0";
744  std::map<wxString, size_t> shapes;
745 
746  fputs( "$SHAPES\n", aFile );
747 
748  for( module = aPcb->m_Modules; module; module = module->Next() )
749  {
750  if( !individualShapes )
751  {
752  // Check if such shape has been already generated, and if so - reuse it
753  // It is necessary to compute hash (i.e. check all children objects) as
754  // certain components instances might have been modified on the board.
755  // In such case the shape will be different despite the same LIB_ID.
756  wxString shapeName = module->GetFPID().Format();
757 
758  auto shapeIt = shapes.find( shapeName );
759  size_t modHash = hashModule( module );
760 
761  if( shapeIt != shapes.end() )
762  {
763  if( modHash != shapeIt->second )
764  {
765  // there is an entry for this footprint, but it has a modified shape,
766  // so we need to create a new entry
767  wxString newShapeName;
768  int suffix = 0;
769 
770  // find an unused name or matching entry
771  do
772  {
773  newShapeName = wxString::Format( "%s_%d", shapeName, suffix );
774  shapeIt = shapes.find( newShapeName );
775  ++suffix;
776  }
777  while( shapeIt != shapes.end() && shapeIt->second != modHash );
778 
779  shapeName = newShapeName;
780  }
781 
782  if( shapeIt != shapes.end() && modHash == shapeIt->second )
783  {
784  // shape found, so reuse it
785  componentShapes[module] = modHash;
786  continue;
787  }
788  }
789 
790  // new shape
791  componentShapes[module] = modHash;
792  shapeNames[modHash] = shapeName;
793  shapes[shapeName] = modHash;
794  FootprintWriteShape( aFile, module, shapeName );
795  }
796  else // individual shape for each component
797  {
798  FootprintWriteShape( aFile, module, module->GetReference() );
799  }
800 
801  // set of already emitted pins to check for duplicates
802  std::set<wxString> pins;
803 
804  for( pad = module->PadsList(); pad; pad = pad->Next() )
805  {
806  /* Padstacks are defined using the correct layers for the pads, therefore to
807  * all pads need to be marked as TOP to use the padstack information correctly.
808  */
809  layer = "TOP";
810  pinname = pad->GetName();
811 
812  if( pinname.IsEmpty() )
813  pinname = wxT( "none" );
814 
815  if( uniquePins )
816  {
817  int suffix = 0;
818  wxString origPinname( pinname );
819 
820  auto it = pins.find( pinname );
821 
822  while( it != pins.end() )
823  {
824  pinname = wxString::Format( "%s_%d", origPinname, suffix );
825  ++suffix;
826  it = pins.find( pinname );
827  }
828 
829  pins.insert( pinname );
830  }
831 
832  double orient = pad->GetOrientation() - module->GetOrientation();
833  NORMALIZE_ANGLE_POS( orient );
834 
835  // Bottom side modules use the flipped padstack
836  fprintf( aFile, ( flipBottomPads && module->GetFlag() ) ?
837  "PIN \"%s\" PAD%dF %g %g %s %g %s\n" :
838  "PIN \"%s\" PAD%d %g %g %s %g %s\n",
839  TO_UTF8( escapeString( pinname ) ), pad->GetSubRatsnest(),
840  pad->GetPos0().x / SCALE_FACTOR,
841  -pad->GetPos0().y / SCALE_FACTOR,
842  layer, orient / 10.0, mirror );
843  }
844  }
845 
846  fputs( "$ENDSHAPES\n\n", aFile );
847 }
848 
849 
850 /* Creates the section $COMPONENTS (Footprints placement)
851  * Bottom side components are difficult to handle: shapes must be mirrored or
852  * flipped, silk layers need to be handled correctly and so on. Also it seems
853  * that *noone* follows the specs...
854  */
855 static void CreateComponentsSection( FILE* aFile, BOARD* aPcb )
856 {
857  fputs( "$COMPONENTS\n", aFile );
858 
859  int cu_count = aPcb->GetCopperLayerCount();
860 
861  for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
862  {
863  const char* mirror;
864  const char* flip;
865  double fp_orient = module->GetOrientation();
866 
867  if( module->GetFlag() )
868  {
869  mirror = "MIRRORX";
870  flip = "FLIP";
871  NEGATE_AND_NORMALIZE_ANGLE_POS( fp_orient );
872  }
873  else
874  {
875  mirror = "0";
876  flip = "0";
877  }
878 
879  fprintf( aFile, "\nCOMPONENT \"%s\"\n",
880  TO_UTF8( escapeString( module->GetReference() ) ) );
881  fprintf( aFile, "DEVICE \"DEV_%s\"\n",
882  TO_UTF8( escapeString( getShapeName( module ) ) ) );
883  fprintf( aFile, "PLACE %g %g\n",
884  MapXTo( module->GetPosition().x ),
885  MapYTo( module->GetPosition().y ) );
886  fprintf( aFile, "LAYER %s\n",
887  module->GetFlag() ? "BOTTOM" : "TOP" );
888  fprintf( aFile, "ROTATION %g\n",
889  fp_orient / 10.0 );
890  fprintf( aFile, "SHAPE \"%s\" %s %s\n",
891  TO_UTF8( escapeString( getShapeName( module ) ) ),
892  mirror, flip );
893 
894  // Text on silk layer: RefDes and value (are they actually useful?)
895  TEXTE_MODULE *textmod = &module->Reference();
896 
897  for( int ii = 0; ii < 2; ii++ )
898  {
899  double txt_orient = textmod->GetTextAngle();
900  std::string layer = GenCADLayerName( cu_count, module->GetFlag() ? B_SilkS : F_SilkS );
901 
902  fprintf( aFile, "TEXT %g %g %g %g %s %s \"%s\"",
903  textmod->GetPos0().x / SCALE_FACTOR,
904  -textmod->GetPos0().y / SCALE_FACTOR,
905  textmod->GetTextWidth() / SCALE_FACTOR,
906  txt_orient / 10.0,
907  mirror,
908  layer.c_str(),
909  TO_UTF8( escapeString( textmod->GetText() ) ) );
910 
911  // Please note, the width is approx
912  fprintf( aFile, " 0 0 %g %g\n",
913  ( textmod->GetTextWidth() * textmod->GetLength() ) / SCALE_FACTOR,
914  textmod->GetTextHeight() / SCALE_FACTOR );
915 
916  textmod = &module->Value(); // Dirty trick for the second iteration
917  }
918 
919  // The SHEET is a 'generic description' for referencing the component
920  fprintf( aFile, "SHEET \"RefDes: %s, Value: %s\"\n",
921  TO_UTF8( module->GetReference() ),
922  TO_UTF8( module->GetValue() ) );
923  }
924 
925  fputs( "$ENDCOMPONENTS\n\n", aFile );
926 }
927 
928 
929 /* Emit the netlist (which is actually the thing for which GenCAD is used these
930  * days!); tracks are handled later */
931 static void CreateSignalsSection( FILE* aFile, BOARD* aPcb )
932 {
933  wxString msg;
934  NETINFO_ITEM* net;
935  D_PAD* pad;
936  MODULE* module;
937  int NbNoConn = 1;
938 
939  fputs( "$SIGNALS\n", aFile );
940 
941  for( unsigned ii = 0; ii < aPcb->GetNetCount(); ii++ )
942  {
943  net = aPcb->FindNet( ii );
944 
945  if( net->GetNetname() == wxEmptyString ) // dummy netlist (no connection)
946  {
947  msg.Printf( "NoConnection%d", NbNoConn++ );
948  }
949 
950  if( net->GetNet() <= 0 ) // dummy netlist (no connection)
951  continue;
952 
953  msg = wxT( "SIGNAL \"" ) + escapeString( net->GetNetname() ) + "\"";
954 
955  fputs( TO_UTF8( msg ), aFile );
956  fputs( "\n", aFile );
957 
958  for( module = aPcb->m_Modules; module; module = module->Next() )
959  {
960  for( pad = module->PadsList(); pad; pad = pad->Next() )
961  {
962  if( pad->GetNetCode() != net->GetNet() )
963  continue;
964 
965  msg.Printf( wxT( "NODE \"%s\" \"%s\"" ),
966  GetChars( escapeString( module->GetReference() ) ),
967  GetChars( escapeString( pad->GetName() ) ) );
968 
969  fputs( TO_UTF8( msg ), aFile );
970  fputs( "\n", aFile );
971  }
972  }
973  }
974 
975  fputs( "$ENDSIGNALS\n\n", aFile );
976 }
977 
978 
979 // Creates the header section
980 static bool CreateHeaderInfoData( FILE* aFile, PCB_EDIT_FRAME* aFrame )
981 {
982  wxString msg;
983  BOARD *board = aFrame->GetBoard();
984 
985  fputs( "$HEADER\n", aFile );
986  fputs( "GENCAD 1.4\n", aFile );
987 
988  // Please note: GenCAD syntax requires quoted strings if they can contain spaces
989  msg.Printf( wxT( "USER \"%s %s\"\n" ),
990  GetChars( Pgm().App().GetAppName() ),
991  GetChars( GetBuildVersion() ) );
992  fputs( TO_UTF8( msg ), aFile );
993 
994  msg = wxT( "DRAWING \"" ) + board->GetFileName() + wxT( "\"\n" );
995  fputs( TO_UTF8( msg ), aFile );
996 
997  const TITLE_BLOCK& tb = aFrame->GetTitleBlock();
998 
999  msg = wxT( "REVISION \"" ) + tb.GetRevision() + wxT( " " ) + tb.GetDate() + wxT( "\"\n" );
1000 
1001  fputs( TO_UTF8( msg ), aFile );
1002  fputs( "UNITS INCH\n", aFile );
1003 
1004  // giving 0 as the argument to Map{X,Y}To returns the scaled origin point
1005  msg.Printf( wxT( "ORIGIN %g %g\n" ),
1006  storeOriginCoords ? MapXTo( 0 ) : 0,
1007  storeOriginCoords ? MapYTo( 0 ) : 0 );
1008  fputs( TO_UTF8( msg ), aFile );
1009 
1010  fputs( "INTERTRACK 0\n", aFile );
1011  fputs( "$ENDHEADER\n\n", aFile );
1012 
1013  return true;
1014 }
1015 
1016 
1017 /*
1018  * Sort function used to sort tracks segments:
1019  * items are sorted by netcode, then by width then by layer
1020  */
1021 static int TrackListSortByNetcode( const void* refptr, const void* objptr )
1022 {
1023  const TRACK* ref, * cmp;
1024  int diff;
1025 
1026  ref = *( (TRACK**) refptr );
1027  cmp = *( (TRACK**) objptr );
1028 
1029  if( ( diff = ref->GetNetCode() - cmp->GetNetCode() ) )
1030  return diff;
1031 
1032  if( ( diff = ref->GetWidth() - cmp->GetWidth() ) )
1033  return diff;
1034 
1035  if( ( diff = ref->GetLayer() - cmp->GetLayer() ) )
1036  return diff;
1037 
1038  return 0;
1039 }
1040 
1041 
1042 /* Creates the section ROUTES
1043  * that handles tracks, vias
1044  * TODO: add zones
1045  * section:
1046  * $ROUTE
1047  * ...
1048  * $ENROUTE
1049  * Track segments must be sorted by nets
1050  */
1051 static void CreateRoutesSection( FILE* aFile, BOARD* aPcb )
1052 {
1053  TRACK* track, ** tracklist;
1054  int vianum = 1;
1055  int old_netcode, old_width, old_layer;
1056  int nbitems, ii;
1057  LSET master_layermask = aPcb->GetDesignSettings().GetEnabledLayers();
1058 
1059  int cu_count = aPcb->GetCopperLayerCount();
1060 
1061  // Count items
1062  nbitems = 0;
1063 
1064  for( track = aPcb->m_Track; track; track = track->Next() )
1065  nbitems++;
1066 
1067  for( track = aPcb->m_Zone; track; track = track->Next() )
1068  {
1069  if( track->Type() == PCB_ZONE_T )
1070  nbitems++;
1071  }
1072 
1073  tracklist = (TRACK**) operator new( (nbitems + 1)* sizeof( TRACK* ) );
1074 
1075  nbitems = 0;
1076 
1077  for( track = aPcb->m_Track; track; track = track->Next() )
1078  tracklist[nbitems++] = track;
1079 
1080  for( track = aPcb->m_Zone; track; track = track->Next() )
1081  {
1082  if( track->Type() == PCB_ZONE_T )
1083  tracklist[nbitems++] = track;
1084  }
1085 
1086  tracklist[nbitems] = NULL;
1087 
1088  qsort( tracklist, nbitems, sizeof(TRACK*), TrackListSortByNetcode );
1089 
1090  fputs( "$ROUTES\n", aFile );
1091 
1092  old_netcode = -1; old_width = -1; old_layer = -1;
1093 
1094  for( ii = 0; ii < nbitems; ii++ )
1095  {
1096  track = tracklist[ii];
1097 
1098  if( old_netcode != track->GetNetCode() )
1099  {
1100  old_netcode = track->GetNetCode();
1101  NETINFO_ITEM* net = track->GetNet();
1102  wxString netname;
1103 
1104  if( net && (net->GetNetname() != wxEmptyString) )
1105  netname = net->GetNetname();
1106  else
1107  netname = wxT( "_noname_" );
1108 
1109  fprintf( aFile, "ROUTE \"%s\"\n", TO_UTF8( escapeString( netname ) ) );
1110  }
1111 
1112  if( old_width != track->GetWidth() )
1113  {
1114  old_width = track->GetWidth();
1115  fprintf( aFile, "TRACK TRACK%d\n", track->GetWidth() );
1116  }
1117 
1118  if( (track->Type() == PCB_TRACE_T) || (track->Type() == PCB_ZONE_T) )
1119  {
1120  if( old_layer != track->GetLayer() )
1121  {
1122  old_layer = track->GetLayer();
1123  fprintf( aFile, "LAYER %s\n",
1124  GenCADLayerName( cu_count, track->GetLayer() ).c_str()
1125  );
1126  }
1127 
1128  fprintf( aFile, "LINE %g %g %g %g\n",
1129  MapXTo( track->GetStart().x ), MapYTo( track->GetStart().y ),
1130  MapXTo( track->GetEnd().x ), MapYTo( track->GetEnd().y ) );
1131  }
1132 
1133  if( track->Type() == PCB_VIA_T )
1134  {
1135  const VIA* via = static_cast<const VIA*>(track);
1136 
1137  LSET vset = via->GetLayerSet() & master_layermask;
1138 
1139  fprintf( aFile, "VIA VIA%d.%d.%s %g %g ALL %g via%d\n",
1140  via->GetWidth(), via->GetDrillValue(),
1141  fmt_mask( vset ).c_str(),
1142  MapXTo( via->GetStart().x ), MapYTo( via->GetStart().y ),
1143  via->GetDrillValue() / SCALE_FACTOR, vianum++ );
1144  }
1145  }
1146 
1147  fputs( "$ENDROUTES\n\n", aFile );
1148 
1149  delete tracklist;
1150 }
1151 
1152 
1153 /* Creates the section $DEVICES
1154  * This is a list of footprints properties
1155  * ( Shapes are in section $SHAPE )
1156  */
1157 static void CreateDevicesSection( FILE* aFile, BOARD* aPcb )
1158 {
1159  std::set<wxString> emitted;
1160  fputs( "$DEVICES\n", aFile );
1161 
1162  for( const auto& componentShape : componentShapes )
1163  {
1164  const wxString& shapeName = shapeNames[componentShape.second];
1165  bool newDevice;
1166  std::tie( std::ignore, newDevice ) = emitted.insert( shapeName );
1167 
1168  if( !newDevice ) // do not repeat device definitions
1169  continue;
1170 
1171  const MODULE* module = componentShape.first;
1172  fprintf( aFile, "\nDEVICE \"DEV_%s\"\n", TO_UTF8( escapeString( shapeName ) ) );
1173  fprintf( aFile, "PART \"%s\"\n", TO_UTF8( escapeString( module->GetValue() ) ) );
1174  fprintf( aFile, "PACKAGE \"%s\"\n", TO_UTF8( escapeString( module->GetFPID().Format() ) ) );
1175 
1176  // The TYPE attribute is almost freeform
1177  const char* ty = "TH";
1178 
1179  if( module->GetAttributes() & MOD_CMS )
1180  ty = "SMD";
1181 
1182  if( module->GetAttributes() & MOD_VIRTUAL )
1183  ty = "VIRTUAL";
1184 
1185  fprintf( aFile, "TYPE %s\n", ty );
1186  }
1187 
1188  fputs( "$ENDDEVICES\n\n", aFile );
1189 }
1190 
1191 
1192 /* Creates the section $BOARD.
1193  * We output here only the board perimeter
1194  */
1195 static void CreateBoardSection( FILE* aFile, BOARD* aPcb )
1196 {
1197  fputs( "$BOARD\n", aFile );
1198 
1199  // Extract the board edges
1200  for( auto drawing : aPcb->Drawings() )
1201  {
1202  if( drawing->Type() == PCB_LINE_T )
1203  {
1204  DRAWSEGMENT* drawseg = static_cast<DRAWSEGMENT*>( drawing );
1205  if( drawseg->GetLayer() == Edge_Cuts )
1206  {
1207  // XXX GenCAD supports arc boundaries but I've seen nothing that reads them
1208  fprintf( aFile, "LINE %g %g %g %g\n",
1209  MapXTo( drawseg->GetStart().x ), MapYTo( drawseg->GetStart().y ),
1210  MapXTo( drawseg->GetEnd().x ), MapYTo( drawseg->GetEnd().y ) );
1211  }
1212  }
1213  }
1214 
1215  fputs( "$ENDBOARD\n\n", aFile );
1216 }
1217 
1218 
1219 /* Creates the section "$TRACKS"
1220  * This sections give the list of widths (tools) used in tracks and vias
1221  * format:
1222  * $TRACK
1223  * TRACK <name> <width>
1224  * $ENDTRACK
1225  *
1226  * Each tool name is build like this: "TRACK" + track width.
1227  * For instance for a width = 120 : name = "TRACK120".
1228  */
1229 static void CreateTracksInfoData( FILE* aFile, BOARD* aPcb )
1230 {
1231  TRACK* track;
1232  int last_width = -1;
1233 
1234  // Find thickness used for traces
1235  // XXX could use the same sorting approach used for pads
1236 
1237  std::vector <int> trackinfo;
1238 
1239  unsigned ii;
1240 
1241  for( track = aPcb->m_Track; track; track = track->Next() )
1242  {
1243  if( last_width != track->GetWidth() ) // Find a thickness already used.
1244  {
1245  for( ii = 0; ii < trackinfo.size(); ii++ )
1246  {
1247  if( trackinfo[ii] == track->GetWidth() )
1248  break;
1249  }
1250 
1251  if( ii == trackinfo.size() ) // not found
1252  trackinfo.push_back( track->GetWidth() );
1253 
1254  last_width = track->GetWidth();
1255  }
1256  }
1257 
1258  for( track = aPcb->m_Zone; track; track = track->Next() )
1259  {
1260  if( last_width != track->GetWidth() ) // Find a thickness already used.
1261  {
1262  for( ii = 0; ii < trackinfo.size(); ii++ )
1263  {
1264  if( trackinfo[ii] == track->GetWidth() )
1265  break;
1266  }
1267 
1268  if( ii == trackinfo.size() ) // not found
1269  trackinfo.push_back( track->GetWidth() );
1270 
1271  last_width = track->GetWidth();
1272  }
1273  }
1274 
1275  // Write data
1276  fputs( "$TRACKS\n", aFile );
1277 
1278  for( ii = 0; ii < trackinfo.size(); ii++ )
1279  {
1280  fprintf( aFile, "TRACK TRACK%d %g\n", trackinfo[ii],
1281  trackinfo[ii] / SCALE_FACTOR );
1282  }
1283 
1284  fputs( "$ENDTRACKS\n\n", aFile );
1285 }
1286 
1287 
1288 /* Creates the shape of a footprint (section SHAPE)
1289  * The shape is always given "normal" (Orient 0, not mirrored)
1290  * It's almost guaranteed that the silk layer will be imported wrong but
1291  * the shape also contains the pads!
1292  */
1293 static void FootprintWriteShape( FILE* aFile, MODULE* module, const wxString& aShapeName )
1294 {
1295  EDGE_MODULE* PtEdge;
1296  EDA_ITEM* PtStruct;
1297 
1298  /* creates header: */
1299  fprintf( aFile, "\nSHAPE \"%s\"\n", TO_UTF8( escapeString( aShapeName ) ) );
1300 
1301  if( module->GetAttributes() & MOD_VIRTUAL )
1302  {
1303  fprintf( aFile, "INSERT SMD\n" );
1304  }
1305  else
1306  {
1307  if( module->GetAttributes() & MOD_CMS )
1308  {
1309  fprintf( aFile, "INSERT SMD\n" );
1310  }
1311  else
1312  {
1313  fprintf( aFile, "INSERT TH\n" );
1314  }
1315  }
1316 
1317 #if 0 /* ATTRIBUTE name and value is unspecified and the original exporter
1318  * got the syntax wrong, so CAM350 rejected the whole shape! */
1319 
1320  if( module->m_Attributs != MOD_DEFAULT )
1321  {
1322  fprintf( aFile, "ATTRIBUTE" );
1323 
1324  if( module->m_Attributs & MOD_CMS )
1325  fprintf( aFile, " PAD_SMD" );
1326 
1327  if( module->m_Attributs & MOD_VIRTUAL )
1328  fprintf( aFile, " VIRTUAL" );
1329 
1330  fprintf( aFile, "\n" );
1331  }
1332 #endif
1333 
1334  // Silk outline; wildly interpreted by various importers:
1335  // CAM350 read it right but only closed shapes
1336  // ProntoPlace double-flip it (at least the pads are correct)
1337  // GerberTool usually get it right...
1338  for( PtStruct = module->GraphicalItemsList(); PtStruct; PtStruct = PtStruct->Next() )
1339  {
1340  switch( PtStruct->Type() )
1341  {
1342  case PCB_MODULE_TEXT_T:
1343 
1344  // If we wanted to export text, this is not the correct section
1345  break;
1346 
1347  case PCB_MODULE_EDGE_T:
1348  PtEdge = (EDGE_MODULE*) PtStruct;
1349  if( PtEdge->GetLayer() == F_SilkS
1350  || PtEdge->GetLayer() == B_SilkS )
1351  {
1352  switch( PtEdge->GetShape() )
1353  {
1354  case S_SEGMENT:
1355  fprintf( aFile, "LINE %g %g %g %g\n",
1356  PtEdge->m_Start0.x / SCALE_FACTOR,
1357  -PtEdge->m_Start0.y / SCALE_FACTOR,
1358  PtEdge->m_End0.x / SCALE_FACTOR,
1359  -PtEdge->m_End0.y / SCALE_FACTOR );
1360  break;
1361 
1362  case S_CIRCLE:
1363  {
1364  int radius = KiROUND( GetLineLength( PtEdge->m_End0,
1365  PtEdge->m_Start0 ) );
1366  fprintf( aFile, "CIRCLE %g %g %g\n",
1367  PtEdge->m_Start0.x / SCALE_FACTOR,
1368  -PtEdge->m_Start0.y / SCALE_FACTOR,
1369  radius / SCALE_FACTOR );
1370  break;
1371  }
1372 
1373  case S_ARC:
1374  {
1375  int arcendx, arcendy;
1376  arcendx = PtEdge->m_End0.x - PtEdge->m_Start0.x;
1377  arcendy = PtEdge->m_End0.y - PtEdge->m_Start0.y;
1378  RotatePoint( &arcendx, &arcendy, -PtEdge->GetAngle() );
1379  arcendx += PtEdge->GetStart0().x;
1380  arcendy += PtEdge->GetStart0().y;
1381 
1382  fprintf( aFile, "ARC %g %g %g %g %g %g\n",
1383  PtEdge->m_End0.x / SCALE_FACTOR,
1384  -PtEdge->GetEnd0().y / SCALE_FACTOR,
1385  arcendx / SCALE_FACTOR,
1386  -arcendy / SCALE_FACTOR,
1387  PtEdge->GetStart0().x / SCALE_FACTOR,
1388  -PtEdge->GetStart0().y / SCALE_FACTOR );
1389  break;
1390  }
1391 
1392  case S_POLYGON:
1393  // Not exported (TODO)
1394  break;
1395 
1396  default:
1397  DisplayError( NULL, wxString::Format( "Type Edge Module %d invalid.", PtStruct->Type() ) );
1398  break;
1399  }
1400  }
1401  break;
1402 
1403  default:
1404  break;
1405  }
1406  }
1407 }
int GetFlag() const
Definition: class_module.h:222
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:646
static void CreateBoardSection(FILE *aFile, BOARD *aPcb)
CITER next(CITER it)
Definition: ptree.cpp:130
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212
#define DIM(x)
of elements in an array
Definition: macros.h:98
int GetLength() const
static int PadListSortByShape(const void *aRefptr, const void *aObjptr)
use coordinates relative to the parent object
Definition: hash_eda.h:40
static void CreateShapesSection(FILE *aFile, BOARD *aPcb)
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Function GetLineLength returns the length of a line segment defined by aPointA and aPointB...
Definition: trigo.h:191
std::string FmtHex() const
Function FmtHex returns a hex string showing contents of this LSEQ.
Definition: lset.cpp:253
virtual LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
This file is part of the common library TODO brief description.
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:106
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:165
static int ViaSort(const void *aRefptr, const void *aObjptr)
This file is part of the common library.
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
Definition: hash_eda.h:42
const SHAPE_POLY_SET & GetCustomShapeAsPolygon() const
Accessor to the custom shape as one polygon.
Definition: class_pad.h:341
void BuildPadPolygon(wxPoint aCoord[4], wxSize aInflateValue, double aRotation) const
Function BuildPadPolygon Has meaning only for polygonal pads (trapezoid and rectangular) Build the Co...
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
int PointCount() const
Function PointCount()
const wxPoint & GetPos0() const
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Function ComputeBoundingBox calculates the bounding box containing all board items (or board edge seg...
static void CreateRoutesSection(FILE *aFile, BOARD *aPcb)
void Flip(const wxPoint &aCentre) override
Function Flip Flip this object, i.e.
Class BOARD to handle a board.
static void CreateSignalsSection(FILE *aFile, BOARD *aPcb)
const wxPoint & GetPosition() const override
Definition: class_module.h:175
int GetRoundRectCornerRadius() const
Function GetRoundRectCornerRadius Has meaning only for rounded rect pads.
Definition: class_pad.h:511
polygon (not yet used for tracks, but could be in microwave apps)
int GetTextWidth() const
Definition: eda_text.h:218
MODULE * Next() const
Definition: class_module.h:120
const wxPoint & GetEnd0() const
int GetCopperLayerCount() const
Function GetCopperLayerCount.
static double MapYTo(int aY)
Set for modules listed in the automatic insertion list (usually SMD footprints)
Definition: class_module.h:76
static bool uniquePins
BOARD * GetBoard() const
const wxPoint & GetPos0() const
Definition: class_pad.h:263
const wxString & GetValue() const
Function GetValue.
Definition: class_module.h:467
DLIST< SEGZONE > m_Zone
Definition: class_board.h:247
void ExportToGenCAD(wxCommandEvent &event)
Function ExportToGenCAD creates a file in GenCAD 1.4 format from the current board.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
usual segment : line with rounded ends
const wxString & GetDate() const
void SetSubRatsnest(int aSubRatsnest)
Definition: class_pad.h:633
EDA_ITEM * Next() const
Definition: base_struct.h:220
static wxString escapeString(const wxString &aString)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
static int GencadOffsetY
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:241
const wxSize & GetDrillSize() const
Definition: class_pad.h:275
static bool flipBottomPads
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:216
Class TITLE_BLOCK holds the information shown in the lower right corner of a plot, printout, or editing view.
static void CreateTracksInfoData(FILE *aFile, BOARD *aPcb)
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
const wxPoint & GetEnd() const
Definition: class_track.h:120
const wxPoint & GetAuxOrigin() const override
Function GetAuxOrigin returns the origin of the axis used for plotting and various exports...
static void CreateArtworksSection(FILE *aFile)
double GetTextAngle() const
Definition: eda_text.h:164
Functions relatives to tracks, vias and segments used to fill zones.
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
wxPoint m_End0
void vset(double *v, double x, double y, double z)
Definition: trackball.cpp:82
This file contains miscellaneous commonly used macros and functions.
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Function Seq returns an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:337
int StrPrintf(std::string *result, const char *format,...)
Function StrPrintf is like sprintf() but the output is appended to a std::string instead of to a char...
Definition: richio.cpp:74
BOARD_ITEM * Next() const
const LIB_ID & GetFPID() const
Definition: class_module.h:184
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
static std::string GenCADLayerNameFlipped(int aCuCount, PCB_LAYER_ID aId)
static void CreateComponentsSection(FILE *aFile, BOARD *aPcb)
PCB_LAYER_ID
A quick note on layer IDs:
static bool individualShapes
STROKE_T GetShape() const
bool GetOption(GENCAD_EXPORT_OPT aOption) const
Checks whether an option has been selected
Class LSET is a set of PCB_LAYER_IDs.
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
wxString GetBuildVersion()
Function GetBuildVersion Return the build version string.
static void CreatePadsShapesSection(FILE *aFile, BOARD *aPcb)
double GetOrientation() const
Definition: class_module.h:180
const wxString & GetFileName() const
Definition: class_board.h:234
Class SHAPE_POLY_SET.
int GetSubRatsnest() const
Function GetSubRatsnest.
Definition: class_pad.h:632
const wxString & GetRevision() const
const wxPoint & GetStart() const
Definition: class_track.h:123
const wxString & GetText() const
Function GetText returns the string associated with the text object.
Definition: eda_text.h:130
Arcs (with rounded ends)
static int Compare(const D_PAD *padref, const D_PAD *padcmp)
Function Compare compares two pads and return 0 if they are equal.
Definition: class_pad.cpp:1063
static const PCB_LAYER_ID gc_seq[]
LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
Definition: class_pad.h:395
static int TrackListSortByNetcode(const void *refptr, const void *objptr)
std::string::size_type find(char c) const
Definition: utf8.h:110
D_PAD * Next() const
Definition: class_pad.h:160
const wxSize & GetSize() const
Definition: class_pad.h:269
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:532
int GetAttributes() const
Definition: class_module.h:217
const wxString & GetName() const
Definition: class_pad.h:190
class SEGZONE, a segment used to fill a zone area (segment on a copper layer)
Definition: typeinfo.h:97
int GetNet() const
Function GetNet.
static bool storeOriginCoords
unsigned GetPadCount() const
Function GetPadCount.
Class LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
void Compile_Ratsnest(wxDC *aDC, bool aDisplayStatus)
Function Compile_Ratsnest Create the entire board ratsnest.
Definition: ratsnest.cpp:56
default
Definition: class_module.h:75
int GetNetCode() const
Function GetNetCode.
class TEXTE_MODULE, text in a footprint
Definition: typeinfo.h:93
static std::string GenCADLayerName(int aCuCount, PCB_LAYER_ID aId)
void NEGATE_AND_NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:284
const TITLE_BLOCK & GetTitleBlock() const override
const std::vector< D_PAD * > GetPads()
Function GetPads returns a reference to a list of all the pads.
Class NETINFO_ITEM handles the data for a net.
Definition: class_netinfo.h:69
TRACK * Next() const
Definition: class_track.h:100
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
double GetAngle() const
see class PGM_BASE
const wxPoint & GetStart0() const
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static size_t hashModule(const MODULE *aModule)
Compute hashes for modules without taking into account their position, rotation or layer...
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
static bool CreateHeaderInfoData(FILE *aFile, PCB_EDIT_FRAME *frame)
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:169
static void CreateDevicesSection(FILE *aFile, BOARD *aPcb)
const wxString & GetReference() const
Function GetReference.
Definition: class_module.h:439
static void FootprintWriteShape(FILE *File, MODULE *module, const wxString &aShapeName)
DLIST< MODULE > m_Modules
Definition: class_board.h:245
Class SHAPE_LINE_CHAIN.
int GetWidth() const
Definition: class_track.h:117
Virtual component: when created by copper shapes on board (Like edge card connectors, mounting hole...)
Definition: class_module.h:78
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees, but soon degrees.
Definition: class_pad.h:375
std::string FmtBin() const
Function FmtBin returns a binary string showing contents of this LSEQ.
Definition: lset.cpp:229
NETINFO_ITEM * FindNet(int aNetcode) const
Function FindNet searches for a net with the given netcode.
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:165
static const double SCALE_FACTOR
UTF8 Format() const
Function Format.
Definition: lib_id.cpp:263
int GetTextHeight() const
Definition: eda_text.h:221
bool IsCopperLayer(LAYER_NUM aLayerId)
Function IsCopperLayer tests whether a layer is a copper layer.
static int GencadOffsetX
wxString GetFileName() const
Returns the selected file path
DLIST< BOARD_ITEM > & GraphicalItemsList()
Definition: class_module.h:157
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
DLIST< D_PAD > & PadsList()
Definition: class_module.h:154
DLIST< TRACK > m_Track
Definition: class_board.h:246
static std::string fmt_mask(LSET aSet)
bool cmp(const Point *a, const Point *b)
Definition: shapes.h:227
Module description (excepted pads)
const wxString & GetNetname() const
Function GetNetname.
wxPoint m_Start0
LSET GetEnabledLayers() const
Function GetEnabledLayers returns a bit-mask of all the layers that are enabled.
static std::map< MODULE *, int > componentShapes
static std::map< int, wxString > shapeNames
EDGE_MODULE class definition.
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
size_t hash_eda(const EDA_ITEM *aItem, int aFlags)
Definition: hash_eda.cpp:48
static const wxString & getShapeName(MODULE *aModule)
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:73
const wxPoint & GetOffset() const
Definition: class_pad.h:278
NETINFO_ITEM * GetNet() const
Function GetNet Returns NET_INFO object for a given item.
void SetFlag(int aFlag)
Definition: class_module.h:220
DLIST_ITERATOR_WRAPPER< BOARD_ITEM > Drawings()
Definition: class_board.h:251
unsigned GetNetCount() const
Function GetNetCount.
Definition: class_board.h:772
int m_Attributs
Flag bits ( see Mod_Attribut )
Definition: class_module.h:700
#define min(a, b)
Definition: auxiliary.h:85
static double MapXTo(int aX)
VIA * GetFirstVia(TRACK *aTrk, const TRACK *aStopPoint=NULL)
Scan a track list for the first VIA o NULL if not found (or NULL passed)
Definition: class_track.h:496