KiCad PCB EDA Suite
specctra_export.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) 2007-2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2015-2016 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 
26 /* This source is a complement to specctra.cpp and implements the export to
27  specctra dsn file format. The specification for the grammar of the specctra
28  dsn file used to develop this code is given here:
29  http://tech.groups.yahoo.com/group/kicad-users/files/ then file "specctra.pdf"
30 
31  Also see the comments at the top of the specctra.cpp file itself.
32 */
33 
34 #include <wxPcbStruct.h>
35 #include <pcbstruct.h> // HISTORY_NUMBER
36 #include <confirm.h> // DisplayError()
37 #include <gestfich.h> // EDA_FileSelector()
38 #include <trigo.h> // RotatePoint()
39 #include <macros.h>
40 
41 #include <set> // std::set
42 #include <map> // std::map
43 
44 #include <boost/utility.hpp> // boost::addressof()
45 
46 #include <class_board.h>
47 #include <class_module.h>
48 #include <class_edge_mod.h>
49 #include <class_track.h>
50 #include <class_zone.h>
51 #include <class_drawsegment.h>
52 #include <base_units.h>
53 
54 #include <collectors.h>
55 
57 
58 #include <specctra.h>
59 
60 using namespace DSN;
61 
62 
63 // Add .1 mil to the requested clearances as a safety margin.
64 // There has been disagreement about interpretation of clearance in the past
65 // between KiCad and Freerouter, so keep this safetyMargin until the
66 // disagreement is resolved and stable. Freerouter seems to be moving
67 // (protected) traces upon loading the DSN file, and even though it seems to sometimes
68 // add its own 0.1 to the clearances, I believe this is happening after
69 // the load process (and moving traces) so I am of the opinion this is
70 // still needed.
71 static const double safetyMargin = 0.1;
72 
73 
74 // see wxPcbStruct.h
75 void PCB_EDIT_FRAME::ExportToSpecctra( wxCommandEvent& event )
76 {
77  wxString fullFileName = GetBoard()->GetFileName();
78  wxString path;
79  wxString name;
80  wxString ext;
81  wxString dsn_ext = wxT( ".dsn" );
82  wxString mask = wxT( "*" ) + dsn_ext;
83 
84  wxFileName::SplitPath( fullFileName, &path, &name, &ext );
85 
86  name += dsn_ext;
87 
88  fullFileName = EDA_FILE_SELECTOR( _( "Specctra DSN file:" ),
89  path,
90  name, // name.ext without path!
91  dsn_ext,
92  mask,
93  this,
94  wxFD_SAVE,
95  false );
96 
97  if( fullFileName == wxEmptyString )
98  return;
99 
100  ExportSpecctraFile( fullFileName );
101 }
102 
103 
104 bool PCB_EDIT_FRAME::ExportSpecctraFile( const wxString& aFullFilename )
105 {
106  SPECCTRA_DB db;
107  bool ok = true;
108  wxString errorText;
109 
110  BASE_SCREEN* screen = GetScreen();
111  bool wasModified = screen->IsModify();
112 
114 
115  LOCALE_IO toggle; // Switch the locale to standard C
116 
117  // DSN Images (=KiCad MODULES and pads) must be presented from the
118  // top view. So we temporarily flip any modules which are on the back
119  // side of the board to the front, and record this in the MODULE's flag field.
120  db.FlipMODULEs( GetBoard() );
121 
122  try
123  {
125  db.FromBOARD( GetBoard() );
126  db.ExportPCB( aFullFilename, true );
127 
128  // if an exception is thrown by FromBOARD or ExportPCB(), then
129  // ~SPECCTRA_DB() will close the file.
130  }
131  catch( const IO_ERROR& ioe )
132  {
133  ok = false;
134 
135  // copy the error string to safe place, ioe is in this scope only.
136  errorText = ioe.What();
137  }
138 
139  // done assuredly, even if an exception was thrown and caught.
140  db.RevertMODULEs( GetBoard() );
141 
142  // The two calls below to MODULE::Flip(), both set the
143  // modified flag, yet their actions cancel each other out, so it should
144  // be ok to clear the modify flag.
145  if( !wasModified )
146  screen->ClrModify();
147 
148  if( ok )
149  {
150  SetStatusText( wxString( _( "BOARD exported OK." ) ) );
151  }
152  else
153  {
154  errorText += '\n';
155  errorText += _( "Unable to export, please fix and try again." );
156  DisplayError( this, errorText );
157  }
158 
159  return ok;
160 }
161 
162 
163 namespace DSN {
164 
166 
167 // "specctra reported units" are what we tell the external router that our
168 // exported lengths are in.
169 
170 
176 static inline double scale( int kicadDist )
177 {
178  // nanometers to um
179  return kicadDist / ( IU_PER_MM / 1000.0 );
180 }
181 
182 
183 // / Convert integer internal units to float um
184 static inline double IU2um( int kicadDist )
185 {
186  return kicadDist * (1000.0 / IU_PER_MM);
187 }
188 
189 
190 static inline double mapX( int x )
191 {
192  return scale( x );
193 }
194 
195 
196 static inline double mapY( int y )
197 {
198  return -scale( y ); // make y negative, since it is increasing going down.
199 }
200 
201 
208 static POINT mapPt( const wxPoint& pt )
209 {
210  POINT ret;
211 
212  ret.x = mapX( pt.x );
213  ret.y = mapY( pt.y );
214  ret.FixNegativeZero();
215  return ret;
216 }
217 
218 
224 static bool isRoundKeepout( D_PAD* aPad )
225 {
226  if( aPad->GetShape()==PAD_SHAPE_CIRCLE )
227  {
228  if( aPad->GetDrillSize().x >= aPad->GetSize().x )
229  return true;
230 
231  if( !( aPad->GetLayerSet() & LSET::AllCuMask() ).any() )
232  return true;
233  }
234 
235  return false;
236 }
237 
238 
243 static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName )
244 {
245  PATH* path = new PATH( 0, T_path );
246 
247  path->AppendPoint( aStart );
248  path->AppendPoint( aEnd );
249  path->SetLayerId( aLayerName.c_str() );
250  return path;
251 }
252 
253 
255 {
256  char name[256]; // padstack name builder
257  std::string uniqifier;
258 
259  // caller must do these checks before calling here.
260  wxASSERT( !isRoundKeepout( aPad ) );
261 
262  PADSTACK* padstack = new PADSTACK();
263 
264  int reportedLayers = 0; // how many in reported padstack
265  const char* layerName[MAX_CU_LAYERS];
266 
267  uniqifier = '[';
268 
269  static const LSET all_cu = LSET::AllCuMask();
270 
271  bool onAllCopperLayers = ( (aPad->GetLayerSet() & all_cu) == all_cu );
272 
273  if( onAllCopperLayers )
274  uniqifier += 'A'; // A for all layers
275 
276  const int copperCount = aBoard->GetCopperLayerCount();
277 
278  for( int layer=0; layer<copperCount; ++layer )
279  {
280  LAYER_ID kilayer = pcbLayer2kicad[layer];
281 
282  if( onAllCopperLayers || aPad->IsOnLayer( kilayer ) )
283  {
284  layerName[reportedLayers++] = layerIds[layer].c_str();
285 
286  if( !onAllCopperLayers )
287  {
288  if( layer == 0 )
289  uniqifier += 'T';
290  else if( layer == copperCount - 1 )
291  uniqifier += 'B';
292  else
293  uniqifier += char('0' + layer); // layer index char
294  }
295  }
296  }
297 
298  uniqifier += ']';
299 
300  POINT dsnOffset;
301 
302  if( aPad->GetOffset().x || aPad->GetOffset().y )
303  {
304  char offsetTxt[64];
305 
306  wxPoint offset( aPad->GetOffset().x, aPad->GetOffset().y );
307 
308  dsnOffset = mapPt( offset );
309 
310  // using '(' or ')' would cause padstack name to be quote wrapped,
311  // so use other brackets, and {} locks freerouter.
312  sprintf( offsetTxt, "[%.6g,%.6g]", dsnOffset.x, dsnOffset.y );
313 
314  uniqifier += offsetTxt;
315  }
316 
317  switch( aPad->GetShape() )
318  {
319  default:
320  case PAD_SHAPE_CIRCLE:
321  {
322  double diameter = scale( aPad->GetSize().x );
323 
324  for( int ndx=0; ndx<reportedLayers; ++ndx )
325  {
326  SHAPE* shape = new SHAPE( padstack );
327 
328  padstack->Append( shape );
329 
330  CIRCLE* circle = new CIRCLE( shape );
331 
332  shape->SetShape( circle );
333 
334  circle->SetLayerId( layerName[ndx] );
335  circle->SetDiameter( diameter );
336  circle->SetVertex( dsnOffset );
337  }
338 
339  snprintf( name, sizeof(name), "Round%sPad_%.6g_um",
340  uniqifier.c_str(), IU2um( aPad->GetSize().x ) );
341 
342  name[ sizeof(name) - 1 ] = 0;
343 
344  padstack->SetPadstackId( name );
345  }
346  break;
347 
348  case PAD_SHAPE_RECT:
349  {
350  double dx = scale( aPad->GetSize().x ) / 2.0;
351  double dy = scale( aPad->GetSize().y ) / 2.0;
352 
353  POINT lowerLeft( -dx, -dy );
354  POINT upperRight( dx, dy );
355 
356  lowerLeft += dsnOffset;
357  upperRight += dsnOffset;
358 
359  for( int ndx=0; ndx<reportedLayers; ++ndx )
360  {
361  SHAPE* shape = new SHAPE( padstack );
362 
363  padstack->Append( shape );
364 
365  RECTANGLE* rect = new RECTANGLE( shape );
366 
367  shape->SetShape( rect );
368 
369  rect->SetLayerId( layerName[ndx] );
370  rect->SetCorners( lowerLeft, upperRight );
371  }
372 
373  snprintf( name, sizeof(name), "Rect%sPad_%.6gx%.6g_um",
374  uniqifier.c_str(),
375  IU2um( aPad->GetSize().x ),
376  IU2um( aPad->GetSize().y ) );
377 
378  name[ sizeof(name) - 1 ] = 0;
379 
380  padstack->SetPadstackId( name );
381  }
382  break;
383 
384  case PAD_SHAPE_OVAL:
385  {
386  double dx = scale( aPad->GetSize().x ) / 2.0;
387  double dy = scale( aPad->GetSize().y ) / 2.0;
388  double dr = dx - dy;
389  double radius;
390  POINT pstart;
391  POINT pstop;
392 
393  if( dr >= 0 ) // oval is horizontal
394  {
395  radius = dy;
396 
397  pstart = POINT( -dr, 0.0 );
398  pstop = POINT( dr, 0.0 );
399  }
400  else // oval is vertical
401  {
402  radius = dx;
403  dr = -dr;
404 
405  pstart = POINT( 0.0, -dr );
406  pstop = POINT( 0.0, dr );
407  }
408 
409  pstart += dsnOffset;
410  pstop += dsnOffset;
411 
412  for( int ndx=0; ndx<reportedLayers; ++ndx )
413  {
414  SHAPE* shape;
415  PATH* path;
416  // see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
417  shape = new SHAPE( padstack );
418 
419  padstack->Append( shape );
420  path = makePath( pstart, pstop, layerName[ndx] );
421  shape->SetShape( path );
422  path->aperture_width = 2.0 * radius;
423  }
424 
425  snprintf( name, sizeof(name), "Oval%sPad_%.6gx%.6g_um",
426  uniqifier.c_str(),
427  IU2um( aPad->GetSize().x ),
428  IU2um( aPad->GetSize().y ) );
429  name[ sizeof(name) - 1 ] = 0;
430 
431  padstack->SetPadstackId( name );
432  }
433  break;
434 
435  case PAD_SHAPE_TRAPEZOID:
436  {
437  double dx = scale( aPad->GetSize().x ) / 2.0;
438  double dy = scale( aPad->GetSize().y ) / 2.0;
439 
440  double ddx = scale( aPad->GetDelta().x ) / 2.0;
441  double ddy = scale( aPad->GetDelta().y ) / 2.0;
442 
443  // see class_pad_draw_functions.cpp which draws the trapezoid pad
444  POINT lowerLeft( -dx - ddy, -dy - ddx );
445  POINT upperLeft( -dx + ddy, +dy + ddx );
446  POINT upperRight( +dx - ddy, +dy - ddx );
447  POINT lowerRight( +dx + ddy, -dy + ddx );
448 
449  lowerLeft += dsnOffset;
450  upperLeft += dsnOffset;
451  upperRight += dsnOffset;
452  lowerRight += dsnOffset;
453 
454  for( int ndx=0; ndx<reportedLayers; ++ndx )
455  {
456  SHAPE* shape = new SHAPE( padstack );
457 
458  padstack->Append( shape );
459 
460  // a T_polygon exists as a PATH
461  PATH* polygon = new PATH( shape, T_polygon );
462 
463  shape->SetShape( polygon );
464 
465  polygon->SetLayerId( layerName[ndx] );
466 
467  polygon->AppendPoint( lowerLeft );
468  polygon->AppendPoint( upperLeft );
469  polygon->AppendPoint( upperRight );
470  polygon->AppendPoint( lowerRight );
471  }
472 
473  // this string _must_ be unique for a given physical shape
474  snprintf( name, sizeof(name), "Trapz%sPad_%.6gx%.6g_%c%.6gx%c%.6g_um",
475  uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
476  aPad->GetDelta().x < 0 ? 'n' : 'p',
477  std::abs( IU2um( aPad->GetDelta().x )),
478  aPad->GetDelta().y < 0 ? 'n' : 'p',
479  std::abs( IU2um( aPad->GetDelta().y ) )
480  );
481  name[ sizeof(name)-1 ] = 0;
482 
483  padstack->SetPadstackId( name );
484  }
485  break;
486  }
487 
488  return padstack;
489 }
490 
491 
493 typedef std::map<wxString, int> PINMAP;
494 
495 
497 {
498  PINMAP pinmap;
499  wxString padName;
500 
501  PCB_TYPE_COLLECTOR moduleItems;
502 
503  // get all the MODULE's pads.
504  moduleItems.Collect( aModule, scanPADs );
505 
506  IMAGE* image = new IMAGE(0);
507 
508  image->image_id = aModule->GetFPID().Format().c_str();
509 
510  // from the pads, and make an IMAGE using collated padstacks.
511  for( int p=0; p < moduleItems.GetCount(); ++p )
512  {
513  D_PAD* pad = (D_PAD*) moduleItems[p];
514 
515  // see if this pad is a through hole with no copper on its perimeter
516  if( isRoundKeepout( pad ) )
517  {
518  double diameter = scale( pad->GetDrillSize().x );
519  POINT vertex = mapPt( pad->GetPos0() );
520 
521  int layerCount = aBoard->GetCopperLayerCount();
522 
523  for( int layer=0; layer<layerCount; ++layer )
524  {
525  KEEPOUT* keepout = new KEEPOUT( image, T_keepout );
526 
527  image->keepouts.push_back( keepout );
528 
529  CIRCLE* circle = new CIRCLE( keepout );
530 
531  keepout->SetShape( circle );
532 
533  circle->SetDiameter( diameter );
534  circle->SetVertex( vertex );
535  circle->SetLayerId( layerIds[layer].c_str() );
536  }
537  }
538  // else if() could there be a square keepout here?
539 
540  else
541  {
542  // Pads not on copper layers (i.e. only on tech layers) are ignored
543  // because they create invalid pads in .dsn file for freeroute
544  LSET mask_copper_layers = pad->GetLayerSet() & LSET::AllCuMask();
545 
546  if( !mask_copper_layers.any() )
547  continue;
548 
549  PADSTACK* padstack = makePADSTACK( aBoard, pad );
550  PADSTACKSET::iterator iter = padstackset.find( *padstack );
551 
552  if( iter != padstackset.end() )
553  {
554  // padstack is a duplicate, delete it and use the original
555  delete padstack;
556  padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
557  }
558  else
559  {
560  padstackset.insert( padstack );
561  }
562 
563  PIN* pin = new PIN( image );
564 
565  padName = pad->GetPadName();
566  pin->pin_id = TO_UTF8( padName );
567 
568  if( padName!=wxEmptyString && pinmap.find( padName )==pinmap.end() )
569  {
570  pinmap[ padName ] = 0;
571  }
572  else // pad name is a duplicate within this module
573  {
574  char buf[32];
575 
576  int duplicates = ++pinmap[ padName ];
577 
578  sprintf( buf, "@%d", duplicates );
579 
580  pin->pin_id += buf; // append "@1" or "@2", etc. to pin name
581  }
582 
583  pin->kiNetCode = pad->GetNetCode();
584 
585  image->pins.push_back( pin );
586 
587  pin->padstack_id = padstack->padstack_id;
588 
589  double angle = pad->GetOrientationDegrees() - aModule->GetOrientationDegrees();
591  pin->SetRotation( angle );
592 
593  wxPoint pos( pad->GetPos0() );
594 
595  pin->SetVertex( mapPt( pos ) );
596  }
597  }
598 
599 #if 1 // enable image (outline) scopes.
600  static const KICAD_T scanEDGEs[] = { PCB_MODULE_EDGE_T, EOT };
601 
602  // get all the MODULE's EDGE_MODULEs and convert those to DSN outlines.
603  moduleItems.Collect( aModule, scanEDGEs );
604 
605  for( int i = 0; i<moduleItems.GetCount(); ++i )
606  {
607  EDGE_MODULE* graphic = (EDGE_MODULE*) moduleItems[i];
608  SHAPE* outline;
609  PATH* path;
610 
611  switch( graphic->GetShape() )
612  {
613  case S_SEGMENT:
614  outline = new SHAPE( image, T_outline );
615 
616  image->Append( outline );
617  path = new PATH( outline );
618 
619  outline->SetShape( path );
620  path->SetAperture( scale( graphic->GetWidth() ) );
621  path->SetLayerId( "signal" );
622  path->AppendPoint( mapPt( graphic->GetStart0() ) );
623  path->AppendPoint( mapPt( graphic->GetEnd0() ) );
624  break;
625 
626  case S_CIRCLE:
627  {
628  // this is best done by 4 QARC's but freerouter does not yet support QARCs.
629  // for now, support by using line segments.
630 
631  outline = new SHAPE( image, T_outline );
632 
633  image->Append( outline );
634  path = new PATH( outline );
635 
636  outline->SetShape( path );
637  path->SetAperture( scale( graphic->GetWidth() ) );
638  path->SetLayerId( "signal" );
639 
640  // Do the math using KiCad units, that way we stay out of the
641  // scientific notation range of floating point numbers in the
642  // DSN file. We do not parse scientific notation in our own
643  // lexer/beautifier, and the spec is not clear that this is
644  // required. Fixed point floats are all that should be needed.
645 
646  double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
647 
648  // better if evenly divisible into 360
649  const int DEGREE_INTERVAL = 18; // 18 means 20 line segments
650 
651  for( double radians = 0.0;
652  radians < 2 * M_PI;
653  radians += DEGREE_INTERVAL * M_PI / 180.0 )
654  {
655  wxPoint point( KiROUND( radius * cos( radians ) ),
656  KiROUND( radius * sin( radians ) ) );
657 
658  point += graphic->m_Start0; // an offset
659 
660  path->AppendPoint( mapPt( point ) );
661  }
662  }
663  break;
664 
665  case S_RECT:
666  case S_ARC:
667  default:
668  DBG( printf( "makeIMAGE(): unsupported shape %s\n",
669  TO_UTF8( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) ); )
670  continue;
671  }
672  }
673 
674 #endif
675 
676  return image;
677 }
678 
679 
680 PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
681  int aTopLayer, int aBotLayer )
682 {
683  char name[48];
684  PADSTACK* padstack = new PADSTACK();
685  double dsnDiameter = scale( aCopperDiameter );
686 
687  for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
688  {
689  SHAPE* shape = new SHAPE( padstack );
690 
691  padstack->Append( shape );
692 
693  CIRCLE* circle = new CIRCLE( shape );
694 
695  shape->SetShape( circle );
696 
697  circle->SetDiameter( dsnDiameter );
698  circle->SetLayerId( layerIds[layer].c_str() );
699  }
700 
701  snprintf( name, sizeof(name), "Via[%d-%d]_%.6g:%.6g_um",
702  aTopLayer, aBotLayer, dsnDiameter,
703  // encode the drill value into the name for later import
704  IU2um( aDrillDiameter )
705  );
706 
707  name[ sizeof(name) - 1 ] = 0;
708  padstack->SetPadstackId( name );
709 
710  return padstack;
711 }
712 
713 
714 PADSTACK* SPECCTRA_DB::makeVia( const ::VIA* aVia )
715 {
716  LAYER_ID topLayerNum;
717  LAYER_ID botLayerNum;
718 
719  aVia->LayerPair( &topLayerNum, &botLayerNum );
720 
721  int topLayer = kicadLayer2pcb[topLayerNum];
722  int botLayer = kicadLayer2pcb[botLayerNum];
723 
724  if( topLayer > botLayer )
725  std::swap( topLayer, botLayer );
726 
727  return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
728 }
729 
730 
731 void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
732  throw( IO_ERROR, boost::bad_pointer )
733 {
734  wxString errMessage;
735  SHAPE_POLY_SET outlines;
736 
737  aBoard->GetBoardPolygonOutlines( outlines, &errMessage );
738 
739  for( int cnt = 0; cnt < outlines.OutlineCount(); cnt++ ) // Should be one outline
740  {
741  PATH* path = new PATH( boundary );
742  boundary->paths.push_back( path );
743  path->layer_id = "pcb";
744 
745  SHAPE_LINE_CHAIN& outline = outlines.Outline( cnt );
746 
747  for( int ii = 0; ii < outline.PointCount(); ii++ )
748  {
749  wxPoint pos( outline.Point( ii ).x, outline.Point( ii ).y );
750  path->AppendPoint( mapPt( pos ) );
751  }
752 
753  // Close polygon:
754  wxPoint pos0( outline.Point( 0 ).x, outline.Point( 0 ).y );
755  path->AppendPoint( mapPt( pos0 ) );
756 
757  // Generate holes as keepout:
758  for( int ii = 0; ii < outlines.HoleCount( cnt ); ii++ )
759  {
760  // emit a signal layers keepout for every interior polygon left...
761  KEEPOUT* keepout = new KEEPOUT( NULL, T_keepout );
762  PATH* poly_ko = new PATH( NULL, T_polygon );
763 
764  keepout->SetShape( poly_ko );
765  poly_ko->SetLayerId( "signal" );
766  pcb->structure->keepouts.push_back( keepout );
767 
768  SHAPE_LINE_CHAIN& hole = outlines.Hole( cnt, ii );
769 
770  for( int jj = 0; jj < hole.PointCount(); jj++ )
771  {
772  wxPoint pos( hole.Point( jj ).x, hole.Point( jj ).y );
773  poly_ko->AppendPoint( mapPt( pos ) );
774  }
775 
776  // Close polygon:
777  wxPoint pos( hole.Point( 0 ).x, hole.Point( 0 ).y );
778  poly_ko->AppendPoint( mapPt( pos ) );
779  }
780  }
781 
782  if( !errMessage.IsEmpty() )
783  wxLogMessage( errMessage );
784 }
785 
786 
787 typedef std::set<std::string> STRINGSET;
788 typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
789 
790 
792  throw( IO_ERROR, boost::bad_ptr_container_operation )
793 {
794  PCB_TYPE_COLLECTOR items;
795 
796  static const KICAD_T scanMODULEs[] = { PCB_MODULE_T, EOT };
797 
798  // Not all boards are exportable. Check that all reference Ids are unique.
799  // Unless they are unique, we cannot import the session file which comes
800  // back to us later from the router.
801  {
802  items.Collect( aBoard, scanMODULEs );
803 
804  STRINGSET refs; // holds module reference designators
805 
806  for( int i=0; i<items.GetCount(); ++i )
807  {
808  MODULE* module = (MODULE*) items[i];
809 
810  if( module->GetReference() == wxEmptyString )
811  {
812  THROW_IO_ERROR( wxString::Format( _( "Component with value of '%s' has empty reference id." ),
813  GetChars( module->GetValue() ) ) );
814  }
815 
816  // if we cannot insert OK, that means the reference has been seen before.
817  STRINGSET_PAIR refpair = refs.insert( TO_UTF8( module->GetReference() ) );
818  if( !refpair.second ) // insert failed
819  {
820  THROW_IO_ERROR( wxString::Format( _( "Multiple components have identical reference IDs of '%s'." ),
821  GetChars( module->GetReference() ) ) );
822  }
823  }
824  }
825 
826  if( !pcb )
827  pcb = SPECCTRA_DB::MakePCB();
828 
829  //-----<layer_descriptor>-----------------------------------------------
830  {
831  // specctra wants top physical layer first, then going down to the
832  // bottom most physical layer in physical sequence.
833  // @question : why does KiCad not display layers in that order?
834 
835  buildLayerMaps( aBoard );
836 
837  int layerCount = aBoard->GetCopperLayerCount();
838 
839  for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
840  {
841  LAYER* layer = new LAYER( pcb->structure );
842 
843  pcb->structure->layers.push_back( layer );
844 
845  layer->name = layerIds[pcbNdx];
846 
847  DSN_T layerType;
848 
849  switch( aBoard->GetLayerType( pcbLayer2kicad[pcbNdx] ) )
850  {
851  default:
852  case LT_SIGNAL: layerType = T_signal; break;
853  case LT_POWER: layerType = T_power; break;
854 
855 #if 1 // Freerouter does not support type "mixed", only signal and power.
856  // Remap "mixed" to "signal".
857  case LT_MIXED: layerType = T_signal; break;
858 #else
859  case LT_MIXED: layerType = T_mixed; break;
860 #endif
861  case LT_JUMPER: layerType = T_jumper; break;
862  }
863 
864  layer->layer_type = layerType;
865 
866  layer->properties.push_back( PROPERTY() );
867  PROPERTY* property = &layer->properties.back();
868  property->name = "index";
869  char temp[32];
870  sprintf( temp, "%d", pcbNdx );
871  property->value = temp;
872  }
873  }
874 
875  // a space in a quoted token is NOT a terminator, true establishes this.
876  pcb->parser->space_in_quoted_tokens = true;
877 
878  //-----<unit_descriptor> & <resolution_descriptor>--------------------
879  {
880  // tell freerouter to use "tenths of micrometers",
881  // which is 100 nm resolution. Possibly more resolution is possible
882  // in freerouter, but it would need testing.
883 
884  pcb->unit->units = T_um;
885  pcb->resolution->units = T_um;
886  pcb->resolution->value = 10; // tenths of a um
887  // pcb->resolution->value = 1000; // "thousandths of a um" (i.e. "nm")
888  }
889 
890  //-----<boundary_descriptor>------------------------------------------
891  {
892  // Because fillBOUNDARY() can throw an exception, we link in an
893  // empty boundary so the BOUNDARY does not get lost in the event of
894  // of an exception.
895  BOUNDARY* boundary = new BOUNDARY( 0 );
896 
897  pcb->structure->SetBOUNDARY( boundary );
898  fillBOUNDARY( aBoard, boundary );
899  }
900 
901 
902  //-----<rules>--------------------------------------------------------
903  {
904  char rule[80];
905  NETCLASSPTR defaultClass = aBoard->GetDesignSettings().GetDefault();
906 
907  int defaultTrackWidth = defaultClass->GetTrackWidth();
908  int defaultClearance = defaultClass->GetClearance();
909 
910  double clearance = scale( defaultClearance );
911 
912  STRINGS& rules = pcb->structure->rules->rules;
913 
914  sprintf( rule, "(width %.6g)", scale( defaultTrackWidth ) );
915  rules.push_back( rule );
916 
917  sprintf( rule, "(clearance %.6g)", clearance + safetyMargin );
918  rules.push_back( rule );
919 
920  // On a high density board (a board with 4 mil tracks, 4 mil spacing)
921  // a typical solder mask clearance will be 2-3 mils.
922  // This exposes 2 to 3 mils of bare board around each pad, and would
923  // leave only 1 to 2 mils of solder mask between the solder mask's boundary
924  // to the edge of any trace within "clearance" of the pad. So we need at least
925  // 2 mils *extra* clearance for traces which would come near a pad on
926  // a different net. So if the baseline trace to trace clearance was say 4 mils, then
927  // the SMD to trace clearance should be at least 6 mils.
928  double default_smd = clearance + safetyMargin;
929 
930  if( default_smd <= 6.0 )
931  default_smd = 6.0;
932 
933  sprintf( rule, "(clearance %.6g (type default_smd))", default_smd );
934 
935  rules.push_back( rule );
936 
937  /* see: http://www.freerouting.net/usren/viewtopic.php?f=5&t=339#p474
938  sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance + safetyMargin );
939  rules.push_back( rule );
940 
941  sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance + safetyMargin );
942  rules.push_back( rule );
943 
944  sprintf( rule, "(clearance %.6g (type via_via))", clearance + safetyMargin );
945  rules.push_back( rule );
946 
947  sprintf( rule, "(clearance %.6g (type via_smd))", clearance + safetyMargin );
948  rules.push_back( rule );
949 
950  sprintf( rule, "(clearance %.6g (type via_pin))", clearance + safetyMargin );
951  rules.push_back( rule );
952 
953  sprintf( rule, "(clearance %.6g (type pin_pin))", clearance + safetyMargin );
954  rules.push_back( rule );
955 
956  sprintf( rule, "(clearance %.6g (type smd_pin))", clearance + safetyMargin );
957  rules.push_back( rule );
958  */
959 
960  // Pad to pad spacing on a single SMT part can be closer than our
961  // clearance, we don't want freerouter complaining about that, so
962  // output a significantly smaller pad to pad clearance to freerouter.
963  clearance = scale( defaultClearance ) / 4;
964 
965  sprintf( rule, "(clearance %.6g (type smd_smd))", clearance );
966  rules.push_back( rule );
967  }
968 
969 
970  //-----<zone containers (not keepout areas) become planes>--------------------------------
971  // Note: only zones are output here, keepout areas be be created later
972  {
973  int netlessZones = 0;
974 
975  static const KICAD_T scanZONEs[] = { PCB_ZONE_AREA_T, EOT };
976  items.Collect( aBoard, scanZONEs );
977 
978  for( int i = 0; i<items.GetCount(); ++i )
979  {
980  ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
981 
982  if( item->GetIsKeepout() )
983  continue;
984 
985  // Currently, we export only copper layers
986  if( ! IsCopperLayer( item->GetLayer() ) )
987  continue;
988 
989  COPPER_PLANE* plane = new COPPER_PLANE( pcb->structure );
990 
991  pcb->structure->planes.push_back( plane );
992 
993  PATH* mainPolygon = new PATH( plane, T_polygon );
994 
995  plane->SetShape( mainPolygon );
996 
997  plane->name = TO_UTF8( item->GetNetname() );
998 
999  if( plane->name.size() == 0 )
1000  {
1001  char name[32];
1002 
1003  // This is one of those no connection zones, netcode=0, and it has no name.
1004  // Create a unique, bogus netname.
1005  NET* no_net = new NET( pcb->network );
1006 
1007  sprintf( name, "@:no_net_%d", netlessZones++ );
1008  no_net->net_id = name;
1009 
1010  // add the bogus net name to network->nets.
1011  pcb->network->nets.push_back( no_net );
1012 
1013  // use the bogus net name in the netless zone.
1014  plane->name = no_net->net_id;
1015  }
1016 
1017  mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
1018 
1019  // Handle the main outlines
1020  SHAPE_POLY_SET::ITERATOR iterator;
1021  for( iterator = item->IterateWithHoles(); iterator; iterator++ )
1022  {
1023  wxPoint point( iterator->x, iterator->y );
1024  mainPolygon->AppendPoint( mapPt(point) );
1025 
1026  // this was the end of the main polygon
1027  if( iterator.IsEndContour() )
1028  break;
1029  }
1030 
1031  WINDOW* window = 0;
1032  PATH* cutout = 0;
1033 
1034  bool isStartContour = true;
1035 
1036  // handle the cutouts
1037  for( iterator++; iterator; iterator++ )
1038  {
1039  if( isStartContour )
1040  {
1041  window = new WINDOW( plane );
1042 
1043  plane->AddWindow( window );
1044 
1045  cutout = new PATH( window, T_polygon );
1046 
1047  window->SetShape( cutout );
1048 
1049  cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
1050  }
1051 
1052  // If the point in this iteration is the last of the contour, the next iteration
1053  // will start with a new contour.
1054  isStartContour = iterator.IsEndContour();
1055 
1056  wxASSERT( window );
1057  wxASSERT( cutout );
1058 
1059  wxPoint point(iterator->x, iterator->y );
1060  cutout->AppendPoint( mapPt(point) );
1061  }
1062  }
1063  }
1064 
1065  //-----<zone containers flagged keepout areas become keepout>--------------------------------
1066  {
1067  static const KICAD_T scanZONEs[] = { PCB_ZONE_AREA_T, EOT };
1068  items.Collect( aBoard, scanZONEs );
1069 
1070  for( int i=0; i<items.GetCount(); ++i )
1071  {
1072  ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
1073 
1074  if( ! item->GetIsKeepout() )
1075  continue;
1076 
1077  // keepout areas have a type. types are
1078  // T_place_keepout, T_via_keepout, T_wire_keepout,
1079  // T_bend_keepout, T_elongate_keepout, T_keepout.
1080  // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
1081  DSN_T keepout_type;
1082 
1083  if( item->GetDoNotAllowVias() && item->GetDoNotAllowTracks() )
1084  keepout_type = T_keepout;
1085  else if( item->GetDoNotAllowVias() )
1086  keepout_type = T_via_keepout;
1087  else if( item->GetDoNotAllowTracks() )
1088  keepout_type = T_wire_keepout;
1089  else
1090  keepout_type = T_keepout;
1091 
1092  KEEPOUT* keepout = new KEEPOUT( pcb->structure, keepout_type );
1093  pcb->structure->keepouts.push_back( keepout );
1094 
1095  PATH* mainPolygon = new PATH( keepout, T_polygon );
1096  keepout->SetShape( mainPolygon );
1097 
1098  mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
1099 
1100  // Handle the main outlines
1101  SHAPE_POLY_SET::ITERATOR iterator;
1102  for( iterator = item->IterateWithHoles(); iterator; iterator++ )
1103  {
1104  wxPoint point( iterator->x, iterator->y );
1105  mainPolygon->AppendPoint( mapPt(point) );
1106 
1107  // this was the end of the main polygon
1108  if( iterator.IsEndContour() )
1109  break;
1110  }
1111 
1112  WINDOW* window = 0;
1113  PATH* cutout = 0;
1114 
1115  bool isStartContour = true;
1116 
1117  // handle the cutouts
1118  for( iterator++; iterator; iterator++ )
1119  {
1120  if( isStartContour )
1121  {
1122  window = new WINDOW( keepout );
1123  keepout->AddWindow( window );
1124 
1125  cutout = new PATH( window, T_polygon );
1126 
1127  window->SetShape( cutout );
1128 
1129  cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
1130  }
1131 
1132  isStartContour = iterator.IsEndContour();
1133 
1134  wxASSERT( window );
1135  wxASSERT( cutout );
1136 
1137  wxPoint point(iterator->x, iterator->y );
1138  cutout->AppendPoint( mapPt(point) );
1139  }
1140  }
1141  }
1142 
1143  //-----<build the images, components, and netlist>-----------------------
1144  {
1145  PIN_REF empty( pcb->network );
1146 
1147  std::string componentId;
1148 
1149  // find the highest numbered netCode within the board.
1150  int highestNetCode = aBoard->GetNetCount() - 1;
1151 
1152  deleteNETs();
1153 
1154  // expand the net vector to highestNetCode+1, setting empty to NULL
1155  nets.resize( highestNetCode + 1, NULL );
1156 
1157  // skip netcode = 0
1158  for( unsigned i = 1; i<nets.size(); ++i )
1159  nets[i] = new NET( pcb->network );
1160 
1161  for( unsigned ii = 0; ii < aBoard->GetNetCount(); ii++ )
1162  {
1163  NETINFO_ITEM* net = aBoard->FindNet( ii );
1164  int netcode = net->GetNet();
1165 
1166  if( netcode > 0 )
1167  nets[ netcode ]->net_id = TO_UTF8( net->GetNetname() );
1168  }
1169 
1170  items.Collect( aBoard, scanMODULEs );
1171 
1172  padstackset.clear();
1173 
1174  for( int m = 0; m<items.GetCount(); ++m )
1175  {
1176  MODULE* module = (MODULE*) items[m];
1177 
1178  IMAGE* image = makeIMAGE( aBoard, module );
1179 
1180  componentId = TO_UTF8( module->GetReference() );
1181 
1182  // create a net list entry for all the actual pins in the image
1183  // for the current module. location of this code is critical
1184  // because we fabricated some pin names to ensure unique-ness
1185  // of pin names within a module, do not move this code because
1186  // the life of this 'IMAGE* image' is not necessarily long. The
1187  // exported netlist will have some fabricated pin names in it.
1188  // If you don't like fabricated pin names, then make sure all pads
1189  // within your MODULEs are uniquely named!
1190  for( unsigned p = 0; p<image->pins.size(); ++p )
1191  {
1192  PIN* pin = &image->pins[p];
1193 
1194  int netcode = pin->kiNetCode;
1195 
1196  if( netcode > 0 )
1197  {
1198  NET* net = nets[netcode];
1199 
1200  net->pins.push_back( empty );
1201 
1202  PIN_REF& pin_ref = net->pins.back();
1203 
1204  pin_ref.component_id = componentId;
1205  pin_ref.pin_id = pin->pin_id;
1206  }
1207  }
1208 
1209 
1210  IMAGE* registered = pcb->library->LookupIMAGE( image );
1211 
1212  if( registered != image )
1213  {
1214  // If our new 'image' is not a unique IMAGE, delete it.
1215  // and use the registered one, known as 'image' after this.
1216  delete image;
1217  image = registered;
1218  }
1219 
1220  COMPONENT* comp = pcb->placement->LookupCOMPONENT( image->GetImageId() );
1221 
1222  PLACE* place = new PLACE( comp );
1223 
1224  comp->places.push_back( place );
1225 
1226  place->SetRotation( module->GetOrientationDegrees() );
1227  place->SetVertex( mapPt( module->GetPosition() ) );
1228  place->component_id = componentId;
1229  place->part_number = TO_UTF8( module->GetValue() );
1230 
1231  // module is flipped from bottom side, set side to T_back
1232  if( module->GetFlag() )
1233  {
1234  double angle = 180.0 - module->GetOrientationDegrees();
1235  NORMALIZE_ANGLE_DEGREES_POS( angle );
1236  place->SetRotation( angle );
1237 
1238  place->side = T_back;
1239  }
1240  }
1241 
1242  // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
1243  // removing, do not increment the iterator
1244  for( PADSTACKSET::iterator i = padstackset.begin(); i!=padstackset.end();
1245  i = padstackset.begin() )
1246  {
1247  PADSTACKSET::auto_type ps = padstackset.release( i );
1248  PADSTACK* padstack = ps.release();
1249 
1250  pcb->library->AddPadstack( padstack );
1251  }
1252 
1253  // copy our SPECCTRA_DB::nets to the pcb->network
1254  for( unsigned n = 1; n<nets.size(); ++n )
1255  {
1256  NET* net = nets[n];
1257 
1258  if( net->pins.size() )
1259  {
1260  // give ownership to pcb->network
1261  pcb->network->nets.push_back( net );
1262  nets[n] = 0;
1263  }
1264  }
1265  }
1266 
1267 
1268  //-----< output vias used in netclasses >-----------------------------------
1269  {
1270  NETCLASSES& nclasses = aBoard->GetDesignSettings().m_NetClasses;
1271 
1272  // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
1273  // This is in lieu of either having each netclass via have its own layer pair in
1274  // the netclass dialog, or such control in the specctra export dialog.
1275 
1276 
1277  // if( aBoard->GetDesignSettings().m_CurrentViaType == VIA_THROUGH )
1278  {
1279  m_top_via_layer = 0; // first specctra cu layer is number zero.
1280  m_bot_via_layer = aBoard->GetCopperLayerCount()-1;
1281  }
1282  /*
1283  else
1284  {
1285  // again, should be in the BOARD:
1286  topLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_TOP ];
1287  botLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_BOTTOM ];
1288  }
1289  */
1290 
1291  // Add the via from the Default netclass first. The via container
1292  // in pcb->library preserves the sequence of addition.
1293 
1294  NETCLASSPTR netclass = nclasses.GetDefault();
1295 
1296  PADSTACK* via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1297  m_top_via_layer, m_bot_via_layer );
1298 
1299  // we AppendVia() this first one, there is no way it can be a duplicate,
1300  // the pcb->library via container is empty at this point. After this,
1301  // we'll have to use LookupVia().
1302  wxASSERT( pcb->library->vias.size() == 0 );
1303  pcb->library->AppendVia( via );
1304 
1305 #if 0
1306  // I've seen no way to make stock vias useable by freerouter. Also the
1307  // zero based diameter was leading to duplicates in the LookupVia() function.
1308  // User should use netclass based vias when going to freerouter.
1309 
1310  // Output the stock vias, but preserve uniqueness in the via container by
1311  // using LookupVia().
1312  for( unsigned i = 0; i < aBoard->m_ViasDimensionsList.size(); ++i )
1313  {
1314  int viaSize = aBoard->m_ViasDimensionsList[i].m_Diameter;
1315  int viaDrill = aBoard->m_ViasDimensionsList[i].m_Drill;
1316 
1317  via = makeVia( viaSize, viaDrill,
1318  m_top_via_layer, m_bot_via_layer );
1319 
1320  // maybe add 'via' to the library, but only if unique.
1321  PADSTACK* registered = pcb->library->LookupVia( via );
1322 
1323  if( registered != via )
1324  delete via;
1325  }
1326 #endif
1327 
1328  // set the "spare via" index at the start of the
1329  // pcb->library->spareViaIndex = pcb->library->vias.size();
1330 
1331  // output the non-Default netclass vias
1332  for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
1333  {
1334  netclass = nc->second;
1335 
1336  via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1337  m_top_via_layer, m_bot_via_layer );
1338 
1339  // maybe add 'via' to the library, but only if unique.
1340  PADSTACK* registered = pcb->library->LookupVia( via );
1341 
1342  if( registered != via )
1343  delete via;
1344  }
1345  }
1346 
1347 
1348 #if 1 // do existing wires and vias
1349 
1350  //-----<create the wires from tracks>-----------------------------------
1351  {
1352  // export all of them for now, later we'll decide what controls we need
1353  // on this.
1354  static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, EOT };
1355 
1356  items.Collect( aBoard, scanTRACKs );
1357 
1358  std::string netname;
1359  WIRING* wiring = pcb->wiring;
1360  PATH* path = 0;
1361 
1362  int old_netcode = -1;
1363  int old_width = -1;
1364  LAYER_NUM old_layer = UNDEFINED_LAYER;
1365 
1366  for( int i=0; i<items.GetCount(); ++i )
1367  {
1368  TRACK* track = (TRACK*) items[i];
1369 
1370  int netcode = track->GetNetCode();
1371 
1372  if( netcode == 0 )
1373  continue;
1374 
1375  if( old_netcode != netcode ||
1376  old_width != track->GetWidth() ||
1377  old_layer != track->GetLayer() ||
1378  (path && path->points.back() != mapPt(track->GetStart()) )
1379  )
1380  {
1381  old_width = track->GetWidth();
1382  old_layer = track->GetLayer();
1383 
1384  if( old_netcode != netcode )
1385  {
1386  old_netcode = netcode;
1387  NETINFO_ITEM* net = aBoard->FindNet( netcode );
1388  wxASSERT( net );
1389  netname = TO_UTF8( net->GetNetname() );
1390  }
1391 
1392  WIRE* wire = new WIRE( wiring );
1393 
1394  wiring->wires.push_back( wire );
1395  wire->net_id = netname;
1396 
1397  wire->wire_type = T_protect; // @todo, this should be configurable
1398 
1399  LAYER_NUM kiLayer = track->GetLayer();
1400  int pcbLayer = kicadLayer2pcb[kiLayer];
1401 
1402  path = new PATH( wire );
1403 
1404  wire->SetShape( path );
1405 
1406  path->layer_id = layerIds[pcbLayer];
1407  path->aperture_width = scale( old_width );
1408 
1409  path->AppendPoint( mapPt( track->GetStart() ) );
1410  }
1411 
1412  if( path ) // Should not occur
1413  path->AppendPoint( mapPt( track->GetEnd() ) );
1414  }
1415  }
1416 
1417 
1418  //-----<export the existing real BOARD instantiated vias>-----------------
1419  {
1420  // Export all vias, once per unique size and drill diameter combo.
1421  static const KICAD_T scanVIAs[] = { PCB_VIA_T, EOT };
1422 
1423  items.Collect( aBoard, scanVIAs );
1424 
1425  for( int i = 0; i<items.GetCount(); ++i )
1426  {
1427  ::VIA* via = (::VIA*) items[i];
1428  wxASSERT( via->Type() == PCB_VIA_T );
1429 
1430  int netcode = via->GetNetCode();
1431 
1432  if( netcode == 0 )
1433  continue;
1434 
1435  PADSTACK* padstack = makeVia( via );
1436  PADSTACK* registered = pcb->library->LookupVia( padstack );
1437 
1438  // if the one looked up is not our padstack, then delete our padstack
1439  // since it was a duplicate of one already registered.
1440  if( padstack != registered )
1441  {
1442  delete padstack;
1443  }
1444 
1445  WIRE_VIA* dsnVia = new WIRE_VIA( pcb->wiring );
1446 
1447  pcb->wiring->wire_vias.push_back( dsnVia );
1448 
1449  dsnVia->padstack_id = registered->padstack_id;
1450  dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) );
1451 
1452  NETINFO_ITEM* net = aBoard->FindNet( netcode );
1453  wxASSERT( net );
1454 
1455  dsnVia->net_id = TO_UTF8( net->GetNetname() );
1456 
1457  dsnVia->via_type = T_protect; // @todo, this should be configurable
1458  }
1459  }
1460 
1461 #endif // do existing wires and vias
1462 
1463  //-----<via_descriptor>-------------------------------------------------
1464  {
1465  // The pcb->library will output <padstack_descriptors> which is a combined
1466  // list of part padstacks and via padstacks. specctra dsn uses the
1467  // <via_descriptors> to say which of those padstacks are vias.
1468 
1469  // Output the vias in the padstack list here, by name only. This must
1470  // be done after exporting existing vias as WIRE_VIAs.
1471  VIA* vias = pcb->structure->via;
1472 
1473  for( unsigned viaNdx = 0; viaNdx < pcb->library->vias.size(); ++viaNdx )
1474  {
1475  vias->AppendVia( pcb->library->vias[viaNdx].padstack_id.c_str() );
1476  }
1477  }
1478 
1479 
1480  //-----<output NETCLASSs>----------------------------------------------------
1481  NETCLASSES& nclasses = aBoard->GetDesignSettings().m_NetClasses;
1482 
1483  exportNETCLASS( nclasses.GetDefault(), aBoard );
1484 
1485  for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
1486  {
1487  NETCLASSPTR netclass = nc->second;
1488  exportNETCLASS( netclass, aBoard );
1489  }
1490 }
1491 
1492 
1493 void SPECCTRA_DB::exportNETCLASS( NETCLASSPTR aNetClass, BOARD* aBoard )
1494 {
1495  /* From page 11 of specctra spec:
1496  *
1497  * Routing and Placement Rule Hierarchies
1498  *
1499  * Routing and placement rules can be defined at multiple levels of design
1500  * specification. When a routing or placement rule is defined for an object at
1501  * multiple levels, a predefined routing or placement precedence order
1502  * automatically determines which rule to apply to the object. The routing rule
1503  * precedence order is
1504  *
1505  * pcb < layer < class < class layer < group_set < group_set layer < net <
1506  * net layer < group < group layer < fromto < fromto layer < class_class <
1507  * class_class layer < padstack < region < class region < net region <
1508  * class_class region
1509  *
1510  * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1511  * hierarchy. A class-to-class region rule has the highest precedence. Rules
1512  * set at one level of the hierarchy override conflicting rules set at lower
1513  * levels. The placement rule precedence order is
1514  *
1515  * pcb < image_set < image < component < super cluster < room <
1516  * room_image_set < family_family < image_image
1517  *
1518  * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1519  * hierarchy. An image-to-image rule has the highest precedence. Rules set at
1520  * one level of the hierarchy override conflicting rules set at lower levels.
1521  */
1522 
1523  char text[256];
1524 
1525  CLASS* clazz = new CLASS( pcb->network );
1526 
1527  pcb->network->classes.push_back( clazz );
1528 
1529  // freerouter creates a class named 'default' anyway, and if we
1530  // try and use that, we end up with two 'default' via rules so use
1531  // something else as the name of our default class.
1532  clazz->class_id = TO_UTF8( aNetClass->GetName() );
1533 
1534  for( NETCLASS::iterator net = aNetClass->begin(); net != aNetClass->end(); ++net )
1535  clazz->net_ids.push_back( TO_UTF8( *net ) );
1536 
1537  clazz->rules = new RULE( clazz, T_rule );
1538 
1539  // output the track width.
1540  int trackWidth = aNetClass->GetTrackWidth();
1541  sprintf( text, "(width %.6g)", scale( trackWidth ) );
1542  clazz->rules->rules.push_back( text );
1543 
1544  // output the clearance.
1545  int clearance = aNetClass->GetClearance();
1546  sprintf( text, "(clearance %.6g)", scale( clearance ) + safetyMargin );
1547  clazz->rules->rules.push_back( text );
1548 
1549  if( aNetClass->GetName() == NETCLASS::Default )
1550  {
1551  clazz->class_id = "kicad_default";
1552  }
1553 
1554  // the easiest way to get the via name is to create a via (which generates
1555  // the name internal to the PADSTACK), and then grab the name and then
1556  // delete the via. There are not that many netclasses so
1557  // this should never become a performance issue.
1558 
1559  PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
1561 
1562  snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
1563  clazz->circuit.push_back( text );
1564 
1565  delete via;
1566 }
1567 
1568 
1570 {
1571  for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
1572  {
1573  module->SetFlag( 0 );
1574  if( module->GetLayer() == B_Cu )
1575  {
1576  module->Flip( module->GetPosition() );
1577  module->SetFlag( 1 );
1578  }
1579  }
1580 
1581  modulesAreFlipped = true;
1582 }
1583 
1584 
1586 {
1587  if( !modulesAreFlipped )
1588  return;
1589 
1590  // DSN Images (=KiCad MODULES and pads) must be presented from the
1591  // top view. Restore those that were flipped.
1592  for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
1593  {
1594  if( module->GetFlag() )
1595  {
1596  module->Flip( module->GetPosition() );
1597  module->SetFlag( 0 );
1598  }
1599  }
1600 
1601  modulesAreFlipped = false;
1602 }
1603 
1604 } // namespace DSN
int GetFlag() const
Definition: class_module.h:189
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu LAYER_IDs.
Definition: lset.cpp:638
Class VIA corresponds to the in the specctra dsn spec.
Definition: specctra.h:1008
void FixNegativeZero()
Function FixNegativeZero will change negative zero to positive zero in the IEEE floating point storag...
Definition: specctra.h:136
int GetCount() const
Function GetCount returns the number of objects in the list.
void ExportToSpecctra(wxCommandEvent &event)
Function ExporttoSPECCTRA Ask for a filename and call ExportSpecctraFile to export the current BOARD ...
Class PIN_REF corresponds to the definition in the specctra dsn spec.
Definition: specctra.h:2425
Class ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:78
static wxString ShowShape(STROKE_T aShape)
Function ShowShape converts the enum STROKE_T integer value to a wxString.
void SetAperture(double aWidth)
Definition: specctra.h:607
std::string pin_id
Definition: specctra.h:1926
static bool isRoundKeepout(D_PAD *aPad)
Function isRoundKeepout decides if the pad is a copper-less through hole which needs to be made into ...
std::map< wxString, int > PINMAP
data type used to ensure unique-ness of pin names, holding (wxString and int)
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:183
Class NET corresponds to a in the DSN spec.
Definition: specctra.h:2565
This source file implements export and import capabilities to the specctra dsn file format...
Definition: specctra.cpp:62
STRINGS circuit
circuit descriptor list
Definition: specctra.h:2727
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:107
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:166
double y
Definition: specctra.h:97
std::string net_id
Definition: specctra.h:2569
Implementation of conversion functions that require both schematic and board internal units...
This file is part of the common library.
KEEPOUTS keepouts
Definition: specctra.h:1994
std::string component_id
Definition: specctra.h:2427
T
enum T contains all this lexer's tokens.
static const double safetyMargin
int PointCount() const
Function PointCount()
PINS pins
Definition: specctra.h:1989
Class BOARD to handle a board.
const wxPoint & GetPosition() const override
Definition: class_module.h:143
PADSTACKSET padstackset
Definition: specctra.h:3635
std::string part_number
Definition: specctra.h:1685
static POINT mapPt(const wxPoint &pt)
Function mapPt converts a KiCad point into a DSN file point.
MODULE * Next() const
Definition: class_module.h:99
const wxPoint & GetEnd0() const
static double IU2um(int kicadDist)
int GetCopperLayerCount() const
Function GetCopperLayerCount.
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:114
int HoleCount(int aOutline) const
Returns the number of holes in a given outline
void SetLayerId(const char *aLayerId)
Definition: specctra.h:602
void SetLayerId(const char *aLayerId)
Definition: specctra.h:753
POINTS vertexes
Definition: specctra.h:2959
int kiNetCode
KiCad netcode.
Definition: specctra.h:1929
void fillBOUNDARY(BOARD *aBoard, BOUNDARY *aBoundary)
Function fillBOUNDARY makes the board perimeter for the DSN file by filling the BOUNDARY element in t...
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Returns the reference to aHole-th hole in the aIndex-th outline
Classes to handle copper zones.
const wxPoint & GetPos0() const
Definition: class_pad.h:176
const wxString & GetValue() const
Function GetValue.
Definition: class_module.h:439
bool IsModify() const
usual segment : line with rounded ends
void AppendPoint(const POINT &aPoint)
Definition: specctra.h:595
class D_PAD, a pad in a footprint
Definition: typeinfo.h:102
Class CLASS corresponds to the in the specctra spec.
Definition: specctra.h:2718
iterator end()
PLACES places
Definition: specctra.h:1743
void NORMALIZE_ANGLE_DEGREES_POS(double &Angle)
Definition: trigo.h:238
double x
Definition: specctra.h:96
void SetPCB(PCB *aPcb)
Function SetPCB deletes any existing PCB and replaces it with the given one.
Definition: specctra.h:3882
void SetRotation(double aRotation)
Definition: specctra.h:1941
int OutlineCount() const
Returns the number of outlines in the set
const wxSize & GetDrillSize() const
Definition: class_pad.h:188
LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
void SetLayerId(const char *aLayerId)
Definition: specctra.h:455
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:166
static PCB * MakePCB()
Function MakePCB makes a PCB with all the default ELEMs and parts on the heap.
Definition: specctra.cpp:3459
#define abs(a)
Definition: auxiliary.h:84
std::string net_id
Definition: specctra.h:2960
std::string class_id
Definition: specctra.h:2722
void SetVertex(const POINT &aPoint)
Definition: specctra.h:1947
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:106
std::string layer_id
Definition: specctra.h:580
const wxPoint & GetEnd() const
Definition: class_track.h:117
DSN_T via_type
Definition: specctra.h:2962
IMAGE * makeIMAGE(BOARD *aBoard, MODULE *aModule)
Function makeIMAGE allocates an IMAGE on the heap and creates all the PINs according to the D_PADs in...
search types array terminator (End Of Types)
Definition: typeinfo.h:94
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:90
void ExportPCB(wxString aFilename, bool aNameChange=false)
Function ExportPCB writes the internal PCB instance out as a SPECTRA DSN format file.
Definition: specctra.cpp:3434
void FlipMODULEs(BOARD *aBoard)
Function FlipMODULEs flips the modules which are on the back side of the board to the front...
Class RULE corresponds to the in the specctra dsn spec.
Definition: specctra.h:492
void SetVertex(const POINT &aVertex)
Definition: specctra.h:1715
std::set< std::string > STRINGSET
Functions relatives to tracks, vias and segments used to fill zones.
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:107
bool IsEndContour() const
Function IsEndContour.
This file contains miscellaneous commonly used macros and functions.
void SetShape(ELEM *aShape)
Definition: specctra.h:852
PADSTACK * makePADSTACK(BOARD *aBoard, D_PAD *aPad)
Function makePADSTACK creates a PADSTACK which matches the given pad.
static const KICAD_T scanPADs[]
Definition: specctra.h:3633
bool IsOnLayer(LAYER_ID aLayer) const override
Function IsOnLayer tests to see if this object is on the given layer.
Definition: class_pad.h:479
Class PATH supports both the and the per the specctra dsn spec...
Definition: specctra.h:576
bool GetIsKeepout() const
Accessors to parameters used in Keepout zones:
Definition: class_zone.h:644
std::pair< STRINGSET::iterator, bool > STRINGSET_PAIR
void exportNETCLASS(std::shared_ptr< NETCLASS > aNetClass, BOARD *aBoard)
Function exportNETCLASS exports aNetClass to the DSN file.
const std::string & GetPadstackId()
Definition: specctra.h:2136
const LIB_ID & GetFPID() const
Definition: class_module.h:151
#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
void SynchronizeNetsAndNetClasses()
Function SynchronizeNetsAndNetClasses copies NETCLASS info to each NET, based on NET membership in a ...
DSN_T layer_type
one of: T_signal, T_power, T_mixed, T_jumper
Definition: specctra.h:1178
NETCLASS_MAP::iterator iterator
segment with non rounded ends
class MODULE, a footprint
Definition: typeinfo.h:101
void SetShape(ELEM *aShape)
Definition: specctra.h:929
WIRES wires
Definition: specctra.h:3095
STRINGSET::iterator iterator
STROKE_T GetShape() const
Class LSET is a set of LAYER_IDs.
iterator begin()
Class NETCLASSES is a container for NETCLASS instances.
std::string image_id
Definition: specctra.h:1980
std::string name
Definition: specctra.h:1177
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
double GetOrientationDegrees() const
Definition: class_module.h:148
void AddWindow(WINDOW *aWindow)
Definition: specctra.h:944
Class COPPER_PLANE corresponds to a in the specctra dsn spec.
Definition: specctra.h:1322
static const char Default[]
the name of the default NETCLASS
const wxString & GetFileName() const
Definition: class_board.h:237
std::string padstack_id
Definition: specctra.h:2958
Class SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
DSN_T side
Definition: specctra.h:1662
Class BASE_SCREEN handles how to draw a screen (a board, a schematic ...)
const wxPoint & GetStart() const
Definition: class_track.h:120
Arcs (with rounded ends)
Class PLACE implements the in the specctra dsn spec.
Definition: specctra.h:1656
Classes and definitions used in Pcbnew.
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:235
wxString EDA_FILE_SELECTOR(const wxString &aTitle, const wxString &aPath, const wxString &aFileName, const wxString &aExtension, const wxString &aWildcard, wxWindow *aParent, int aStyle, const bool aKeepWorkingDirectory, const wxPoint &aPosition, wxString *aMruPath)
Function EDA_FILE_SELECTOR.
Definition: gestfich.cpp:82
const wxSize & GetSize() const
Definition: class_pad.h:182
std::string component_id
reference designator
Definition: specctra.h:1660
Class PADSTACK holds either a via or a pad definition.
Definition: specctra.h:2095
void Append(ELEM *aElem)
Definition: specctra.h:321
int m_top_via_layer
specctra cu layers, 0 based index:
Definition: specctra.h:3641
#define THROW_IO_ERROR(x)
Definition: utf8.cpp:60
std::vector< std::string > STRINGS
Definition: specctra.h:158
Class SPECCTRA_DB holds a DSN data tree, usually coming from a DSN file.
Definition: specctra.h:3604
int GetNet() const
Function GetNet.
CLASSLIST classes
Definition: specctra.h:2809
static PATH * makePath(const POINT &aStart, const POINT &aEnd, const std::string &aLayerName)
Function makePath creates a PATH element with a single straight line, a pair of vertices.
std::vector< LAYER_ID > pcbLayer2kicad
maps PCB layer number to BOARD layer numbers
Definition: specctra.h:3625
std::vector< int > kicadLayer2pcb
maps BOARD layer number to PCB layer numbers
Definition: specctra.h:3622
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Function Collect scans a BOARD_ITEM using this class's Inspector method, which does the collection...
Definition: collectors.cpp:488
std::string padstack_id
Definition: specctra.h:1923
void SetRotation(double aRotation)
Definition: specctra.h:1722
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
std::string GetImageId()
Definition: specctra.h:2022
BOARD * GetBoard()
int GetNetCode() const
Function GetNetCode.
Class SHAPE corresponds to the "(shape ..)" element in the specctra dsn spec.
Definition: specctra.h:1860
bool modulesAreFlipped
Definition: specctra.h:3615
Class NETINFO_ITEM handles the data for a net.
void RevertMODULEs(BOARD *aBoard)
Function RevertMODULEs flips the modules which were on the back side of the board back to the back...
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
Class COMPONENT implements the in the specctra dsn spec.
Definition: specctra.h:1736
bool GetDoNotAllowTracks() const
Definition: class_zone.h:647
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
Struct POINT is a holder for a point in the SPECCTRA DSN coordinate system.
Definition: specctra.h:94
void FromBOARD(BOARD *aBoard)
Function FromBOARD adds the entire BOARD to the PCB but does not write it out.
const wxString & GetNetname() const
Function GetNetname.
Class WIRE corresponds to in the specctra dsn spec.
Definition: specctra.h:2844
Class to handle a graphic segment.
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
void SetCorners(const POINT &aPoint0, const POINT &aPoint1)
Definition: specctra.h:460
bool GetDoNotAllowVias() const
Definition: class_zone.h:646
STRINGS layerIds
indexed by PCB layer number
Definition: specctra.h:3619
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:166
const wxString & GetReference() const
Function GetReference.
Definition: class_module.h:411
POINTS points
Definition: specctra.h:583
LAYER_ID
Enum LAYER_ID is the set of PCB layers.
DLIST< MODULE > m_Modules
Definition: class_board.h:243
Class SHAPE_LINE_CHAIN.
int GetWidth() const
Definition: class_track.h:114
SHAPE_POLY_SET::ITERATOR IterateWithHoles()
Function IterateWithHoles returns an iterator to visit all points of the zone's main outline with hol...
Definition: class_zone.h:486
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
static double mapY(int y)
RULE * rules
Definition: specctra.h:2729
Class ITERATOR_TEMPLATE.
NETCLASSPTR GetDefault() const
Function GetDefault.
static double mapX(int x)
const wxSize & GetDelta() const
Definition: class_pad.h:185
void SetShape(ELEM *aShape)
Definition: specctra.h:2885
static bool empty(const wxTextEntryBase *aCtrl)
static double scale(int kicadDist)
Function scale converts a distance from PCBNEW internal units to the reported specctra dsn units in f...
double aperture_width
Definition: specctra.h:581
int GetWidth() const
std::string padstack_id
Definition: specctra.h:2101
UTF8 Format() const
Function Format.
Definition: lib_id.cpp:263
void AppendVia(const char *aViaName)
Definition: specctra.h:1022
bool IsCopperLayer(LAYER_NUM aLayerId)
Function IsCopperLayer tests whether a layer is a copper layer.
VECTOR2I & Point(int aIndex)
Function Point()
PIN_REFS pins
Definition: specctra.h:2574
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:108
Class WIRE_VIA corresponds to in the specctra dsn spec.
Definition: specctra.h:2954
wxString GetPadName() const
Definition: class_pad.cpp:382
#define DBG(x)
Definition: fctsys.h:33
void SetVertex(const POINT &aVertex)
Definition: specctra.h:763
Module description (excepted pads)
const wxString & GetNetname() const
Function GetNetname.
wxPoint m_Start0
std::string pin_id
Definition: specctra.h:2428
DSN_T wire_type
Definition: specctra.h:2859
Class PCB_TYPE_COLLECTOR merely gathers up all BOARD_ITEMs of a given set of KICAD_T type(s)...
Definition: collectors.h:593
DSN_T Type() const
Definition: specctra.h:231
PROPERTIES properties
Definition: specctra.h:1185
STRINGS net_ids
Definition: specctra.h:2724
EDGE_MODULE class definition.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
NETCLASS_MAP m_NetClasses
all the NETCLASSes except the default one.
Class KEEPOUT is used for and .
Definition: specctra.h:884
std::string name
Definition: specctra.h:889
NETWORK * network
Definition: specctra.h:3143
void SetPadstackId(const char *aPadstackId)
Definition: specctra.h:2148
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:69
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:47
void SetDiameter(double aDiameter)
Definition: specctra.h:758
double GetOrientationDegrees() const
Definition: class_pad.h:215
Class WIRING corresponds to in the specctra dsn spec.
Definition: specctra.h:3090
const wxPoint & GetOffset() const
Definition: class_pad.h:191
std::string net_id
Definition: specctra.h:2857
PADSTACK * makeVia(int aCopperDiameter, int aDrillDiameter, int aTopLayer, int aBotLayer)
Function makeVia makes a round through hole PADSTACK using the given KiCad diameter in deci-mils...
bool ExportSpecctraFile(const wxString &aFullFilename)
Function ExportSpecctraFile will export the current BOARD to a specctra dsn file. ...
STRINGS rules
rules are saved in std::string form.
Definition: specctra.h:496