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-2021 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 <pcb_edit_frame.h>
35 #include <confirm.h> // DisplayError()
36 #include <gestfich.h> // EDA_FileSelector()
37 #include <trigo.h> // RotatePoint()
38 #include <locale_io.h>
39 #include <macros.h>
40 #include <math/util.h> // for KiROUND
41 
42 #include <set> // std::set
43 #include <map> // std::map
44 
45 #include <board.h>
46 #include <footprint.h>
47 #include <fp_shape.h>
48 #include <track.h>
49 #include <zone.h>
50 #include <base_units.h>
51 #include <collectors.h>
53 #include <geometry/convex_hull.h>
56 #include <pcbnew_settings.h>
57 
58 #include "specctra.h"
59 
60 using namespace DSN;
61 
62 // comment the line #define EXPORT_CUSTOM_PADS_CONVEX_HULL to export CUSTOM pads exact shapes.
63 // Keep in mind shapes can be non convex polygons with holes (linked to outline)
64 // that can create issues.
65 // Especially Freerouter does not handle them very well:
66 // - too complex shapes are not accepted, especially shapes with holes (dsn files are not loaded).
67 // - and Freerouter actually uses something like a convex hull of the shape (that works not very
68 // well).
69 // I am guessing non convex polygons with holes linked could create issues with any Router.
70 #define EXPORT_CUSTOM_PADS_CONVEX_HULL
71 
72 // Add .1 mil to the requested clearances as a safety margin.
73 // There has been disagreement about interpretation of clearance in the past
74 // between KiCad and Freerouter, so keep this safetyMargin until the
75 // disagreement is resolved and stable. Freerouter seems to be moving
76 // (protected) traces upon loading the DSN file, and even though it seems to sometimes
77 // add its own 0.1 to the clearances, I believe this is happening after
78 // the load process (and moving traces) so I am of the opinion this is
79 // still needed.
80 static const double safetyMargin = 0.1;
81 
82 
83 bool PCB_EDIT_FRAME::ExportSpecctraFile( const wxString& aFullFilename )
84 {
85  SPECCTRA_DB db;
86  bool ok = true;
87  wxString errorText;
88 
89  BASE_SCREEN* screen = GetScreen();
90  bool wasModified = screen->IsModify();
91 
93 
94  LOCALE_IO toggle; // Switch the locale to standard C
95 
96  // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the top view. So we
97  // temporarily flip any footprints which are on the back side of the board to the front,
98  // and record this in the FOOTPRINT's flag field.
99  db.FlipFOOTPRINTs( GetBoard() );
100 
101  try
102  {
104  db.FromBOARD( GetBoard() );
105  db.ExportPCB( aFullFilename, true );
106 
107  // if an exception is thrown by FromBOARD or ExportPCB(), then
108  // ~SPECCTRA_DB() will close the file.
109  }
110  catch( const IO_ERROR& ioe )
111  {
112  ok = false;
113 
114  // copy the error string to safe place, ioe is in this scope only.
115  errorText = ioe.What();
116  }
117 
118  // done assuredly, even if an exception was thrown and caught.
119  db.RevertFOOTPRINTs( GetBoard() );
120 
121  // The two calls below to FOOTPRINT::Flip(), both set the
122  // modified flag, yet their actions cancel each other out, so it should
123  // be ok to clear the modify flag.
124  if( !wasModified )
125  screen->ClrModify();
126 
127  if( ok )
128  {
129  SetStatusText( wxString( _( "BOARD exported OK." ) ) );
130  }
131  else
132  {
133  DisplayErrorMessage( this,
134  _( "Unable to export, please fix and try again" ),
135  errorText );
136  }
137 
138  return ok;
139 }
140 
141 
142 namespace DSN {
143 
145 
146 // "specctra reported units" are what we tell the external router that our
147 // exported lengths are in.
148 
149 
154 static inline double scale( int kicadDist )
155 {
156  // nanometers to um
157  return kicadDist / ( IU_PER_MM / 1000.0 );
158 }
159 
160 
162 static inline double IU2um( int kicadDist )
163 {
164  return kicadDist * (1000.0 / IU_PER_MM);
165 }
166 
167 
168 static inline double mapX( int x )
169 {
170  return scale( x );
171 }
172 
173 
174 static inline double mapY( int y )
175 {
176  return -scale( y ); // make y negative, since it is increasing going down.
177 }
178 
179 
186 static POINT mapPt( const wxPoint& pt )
187 {
188  POINT ret;
189 
190  ret.x = mapX( pt.x );
191  ret.y = mapY( pt.y );
192  ret.FixNegativeZero();
193  return ret;
194 }
195 
196 
200 static bool isRoundKeepout( PAD* aPad )
201 {
202  if( aPad->GetShape() == PAD_SHAPE::CIRCLE )
203  {
204  if( aPad->GetDrillSize().x >= aPad->GetSize().x )
205  return true;
206 
207  if( !( aPad->GetLayerSet() & LSET::AllCuMask() ).any() )
208  return true;
209  }
210 
211  return false;
212 }
213 
214 
218 static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName )
219 {
220  PATH* path = new PATH( 0, T_path );
221 
222  path->AppendPoint( aStart );
223  path->AppendPoint( aEnd );
224  path->SetLayerId( aLayerName.c_str() );
225  return path;
226 }
227 
228 
230 {
231  char name[256]; // padstack name builder
232  std::string uniqifier;
233 
234  // caller must do these checks before calling here.
235  wxASSERT( !isRoundKeepout( aPad ) );
236 
237  PADSTACK* padstack = new PADSTACK();
238 
239  int reportedLayers = 0; // how many in reported padstack
240  const char* layerName[MAX_CU_LAYERS];
241 
242  uniqifier = '[';
243 
244  static const LSET all_cu = LSET::AllCuMask();
245 
246  bool onAllCopperLayers = ( (aPad->GetLayerSet() & all_cu) == all_cu );
247 
248  if( onAllCopperLayers )
249  uniqifier += 'A'; // A for all layers
250 
251  const int copperCount = aBoard->GetCopperLayerCount();
252 
253  for( int layer=0; layer<copperCount; ++layer )
254  {
255  PCB_LAYER_ID kilayer = m_pcbLayer2kicad[layer];
256 
257  if( onAllCopperLayers || aPad->IsOnLayer( kilayer ) )
258  {
259  layerName[reportedLayers++] = m_layerIds[layer].c_str();
260 
261  if( !onAllCopperLayers )
262  {
263  if( layer == 0 )
264  uniqifier += 'T';
265  else if( layer == copperCount - 1 )
266  uniqifier += 'B';
267  else
268  uniqifier += char('0' + layer); // layer index char
269  }
270  }
271  }
272 
273  uniqifier += ']';
274 
275  POINT dsnOffset;
276 
277  if( aPad->GetOffset().x || aPad->GetOffset().y )
278  {
279  char offsetTxt[64];
280 
281  wxPoint offset( aPad->GetOffset().x, aPad->GetOffset().y );
282 
283  dsnOffset = mapPt( offset );
284 
285  // using '(' or ')' would cause padstack name to be quote wrapped,
286  // so use other brackets, and {} locks freerouter.
287  sprintf( offsetTxt, "[%.6g,%.6g]", dsnOffset.x, dsnOffset.y );
288 
289  uniqifier += offsetTxt;
290  }
291 
292  switch( aPad->GetShape() )
293  {
294  case PAD_SHAPE::CIRCLE:
295  {
296  double diameter = scale( aPad->GetSize().x );
297 
298  for( int ndx=0; ndx<reportedLayers; ++ndx )
299  {
300  SHAPE* shape = new SHAPE( padstack );
301 
302  padstack->Append( shape );
303 
304  CIRCLE* circle = new CIRCLE( shape );
305 
306  shape->SetShape( circle );
307 
308  circle->SetLayerId( layerName[ndx] );
309  circle->SetDiameter( diameter );
310  circle->SetVertex( dsnOffset );
311  }
312 
313  snprintf( name, sizeof(name), "Round%sPad_%.6g_um",
314  uniqifier.c_str(), IU2um( aPad->GetSize().x ) );
315 
316  name[ sizeof(name) - 1 ] = 0;
317 
318  padstack->SetPadstackId( name );
319  }
320  break;
321 
322  case PAD_SHAPE::RECT:
323  {
324  double dx = scale( aPad->GetSize().x ) / 2.0;
325  double dy = scale( aPad->GetSize().y ) / 2.0;
326 
327  POINT lowerLeft( -dx, -dy );
328  POINT upperRight( dx, dy );
329 
330  lowerLeft += dsnOffset;
331  upperRight += dsnOffset;
332 
333  for( int ndx=0; ndx<reportedLayers; ++ndx )
334  {
335  SHAPE* shape = new SHAPE( padstack );
336 
337  padstack->Append( shape );
338 
339  RECTANGLE* rect = new RECTANGLE( shape );
340 
341  shape->SetShape( rect );
342 
343  rect->SetLayerId( layerName[ndx] );
344  rect->SetCorners( lowerLeft, upperRight );
345  }
346 
347  snprintf( name, sizeof(name), "Rect%sPad_%.6gx%.6g_um",
348  uniqifier.c_str(),
349  IU2um( aPad->GetSize().x ),
350  IU2um( aPad->GetSize().y ) );
351 
352  name[ sizeof(name) - 1 ] = 0;
353 
354  padstack->SetPadstackId( name );
355  }
356  break;
357 
358  case PAD_SHAPE::OVAL:
359  {
360  double dx = scale( aPad->GetSize().x ) / 2.0;
361  double dy = scale( aPad->GetSize().y ) / 2.0;
362  double dr = dx - dy;
363  double radius;
364  POINT pstart;
365  POINT pstop;
366 
367  if( dr >= 0 ) // oval is horizontal
368  {
369  radius = dy;
370 
371  pstart = POINT( -dr, 0.0 );
372  pstop = POINT( dr, 0.0 );
373  }
374  else // oval is vertical
375  {
376  radius = dx;
377  dr = -dr;
378 
379  pstart = POINT( 0.0, -dr );
380  pstop = POINT( 0.0, dr );
381  }
382 
383  pstart += dsnOffset;
384  pstop += dsnOffset;
385 
386  for( int ndx=0; ndx<reportedLayers; ++ndx )
387  {
388  SHAPE* shape;
389  PATH* path;
390  // see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
391  shape = new SHAPE( padstack );
392 
393  padstack->Append( shape );
394  path = makePath( pstart, pstop, layerName[ndx] );
395  shape->SetShape( path );
396  path->aperture_width = 2.0 * radius;
397  }
398 
399  snprintf( name, sizeof(name), "Oval%sPad_%.6gx%.6g_um",
400  uniqifier.c_str(),
401  IU2um( aPad->GetSize().x ),
402  IU2um( aPad->GetSize().y ) );
403  name[ sizeof(name) - 1 ] = 0;
404 
405  padstack->SetPadstackId( name );
406  }
407  break;
408 
410  {
411  double dx = scale( aPad->GetSize().x ) / 2.0;
412  double dy = scale( aPad->GetSize().y ) / 2.0;
413 
414  double ddx = scale( aPad->GetDelta().x ) / 2.0;
415  double ddy = scale( aPad->GetDelta().y ) / 2.0;
416 
417  // see class_pad_draw_functions.cpp which draws the trapezoid pad
418  POINT lowerLeft( -dx - ddy, -dy - ddx );
419  POINT upperLeft( -dx + ddy, +dy + ddx );
420  POINT upperRight( +dx - ddy, +dy - ddx );
421  POINT lowerRight( +dx + ddy, -dy + ddx );
422 
423  lowerLeft += dsnOffset;
424  upperLeft += dsnOffset;
425  upperRight += dsnOffset;
426  lowerRight += dsnOffset;
427 
428  for( int ndx=0; ndx<reportedLayers; ++ndx )
429  {
430  SHAPE* shape = new SHAPE( padstack );
431 
432  padstack->Append( shape );
433 
434  // a T_polygon exists as a PATH
435  PATH* polygon = new PATH( shape, T_polygon );
436 
437  shape->SetShape( polygon );
438 
439  polygon->SetLayerId( layerName[ndx] );
440 
441  polygon->AppendPoint( lowerLeft );
442  polygon->AppendPoint( upperLeft );
443  polygon->AppendPoint( upperRight );
444  polygon->AppendPoint( lowerRight );
445  }
446 
447  // this string _must_ be unique for a given physical shape
448  snprintf( name, sizeof(name), "Trapz%sPad_%.6gx%.6g_%c%.6gx%c%.6g_um",
449  uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
450  aPad->GetDelta().x < 0 ? 'n' : 'p',
451  std::abs( IU2um( aPad->GetDelta().x )),
452  aPad->GetDelta().y < 0 ? 'n' : 'p',
453  std::abs( IU2um( aPad->GetDelta().y ) )
454  );
455  name[ sizeof(name)-1 ] = 0;
456 
457  padstack->SetPadstackId( name );
458  }
459  break;
460 
463  {
464  // Export the shape as as polygon, round rect does not exist as primitive
465  const int circleToSegmentsCount = 36;
466  int rradius = aPad->GetRoundRectCornerRadius();
467  SHAPE_POLY_SET cornerBuffer;
468  // Use a slightly bigger shape because the round corners are approximated by
469  // segments, giving to the polygon a slightly smaller shape than the actual shape
470 
471  /* calculates the coeff to compensate radius reduction of holes clearance
472  * due to the segment approx.
473  * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
474  * correctionFactor is cos( PI/s_CircleToSegmentsCount )
475  */
476  double correctionFactor = cos( M_PI / (double) circleToSegmentsCount );
477  int extra_clearance = KiROUND( rradius * (1.0 - correctionFactor ) );
478  wxSize psize = aPad->GetSize();
479  psize.x += extra_clearance*2;
480  psize.y += extra_clearance*2;
481  rradius += extra_clearance;
482  bool doChamfer = aPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT;
483 
484  TransformRoundChamferedRectToPolygon( cornerBuffer, wxPoint(0,0), psize,
485  0, rradius,
486  aPad->GetChamferRectRatio(),
487  doChamfer ? aPad->GetChamferPositions() : 0,
488  aBoard->GetDesignSettings().m_MaxError,
489  ERROR_INSIDE );
490  SHAPE_LINE_CHAIN& polygonal_shape = cornerBuffer.Outline( 0 );
491 
492  for( int ndx=0; ndx < reportedLayers; ++ndx )
493  {
494  SHAPE* shape = new SHAPE( padstack );
495 
496  padstack->Append( shape );
497 
498  // a T_polygon exists as a PATH
499  PATH* polygon = new PATH( shape, T_polygon );
500 
501  shape->SetShape( polygon );
502 
503  polygon->SetLayerId( layerName[ndx] );
504  // append a closed polygon
505  POINT first_corner;
506 
507  for( int idx = 0; idx < polygonal_shape.PointCount(); idx++ )
508  {
509  POINT corner( scale( polygonal_shape.CPoint( idx ).x ),
510  scale( -polygonal_shape.CPoint( idx ).y ) );
511  corner += dsnOffset;
512  polygon->AppendPoint( corner );
513 
514  if( idx == 0 )
515  first_corner = corner;
516  }
517  polygon->AppendPoint( first_corner ); // Close polygon
518  }
519 
520  // this string _must_ be unique for a given physical shape
521  snprintf( name, sizeof(name), "RoundRect%sPad_%.6gx%.6g_%.6g_um_%f_%X",
522  uniqifier.c_str(),
523  IU2um( aPad->GetSize().x ),
524  IU2um( aPad->GetSize().y ), IU2um( rradius ),
525  doChamfer ? aPad->GetChamferRectRatio() : 0.0,
526  doChamfer ? aPad->GetChamferPositions() : 0 );
527 
528  name[ sizeof(name) - 1 ] = 0;
529 
530  padstack->SetPadstackId( name );
531  }
532  break;
533 
534  case PAD_SHAPE::CUSTOM:
535  {
536  std::vector<wxPoint> polygonal_shape;
537  SHAPE_POLY_SET pad_shape;
538  aPad->MergePrimitivesAsPolygon( &pad_shape, UNDEFINED_LAYER );
539 
540 #ifdef EXPORT_CUSTOM_PADS_CONVEX_HULL
541  BuildConvexHull( polygonal_shape, pad_shape );
542 #else
543  const SHAPE_LINE_CHAIN& p_outline = pad_shape.COutline( 0 );
544 
545  for( int ii = 0; ii < p_outline.PointCount(); ++ii )
546  polygonal_shape.push_back( wxPoint( p_outline.CPoint( ii ) ) );
547 #endif
548 
549  // The polygon must be closed
550  if( polygonal_shape.front() != polygonal_shape.back() )
551  polygonal_shape.push_back( polygonal_shape.front() );
552 
553  for( int ndx=0; ndx < reportedLayers; ++ndx )
554  {
555  SHAPE* shape = new SHAPE( padstack );
556 
557  padstack->Append( shape );
558 
559  // a T_polygon exists as a PATH
560  PATH* polygon = new PATH( shape, T_polygon );
561 
562  shape->SetShape( polygon );
563 
564  polygon->SetLayerId( layerName[ndx] );
565 
566  for( unsigned idx = 0; idx < polygonal_shape.size(); idx++ )
567  {
568  POINT corner( scale( polygonal_shape[idx].x ),
569  scale( -polygonal_shape[idx].y ) );
570  corner += dsnOffset;
571  polygon->AppendPoint( corner );
572  }
573  }
574 
575  // this string _must_ be unique for a given physical shape, so try to make it unique
576  MD5_HASH hash = pad_shape.GetHash();
577  EDA_RECT rect = aPad->GetBoundingBox();
578  snprintf( name, sizeof(name), "Cust%sPad_%.6gx%.6g_%.6gx_%.6g_%d_um_%s",
579  uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
580  IU2um( rect.GetWidth() ), IU2um( rect.GetHeight() ),
581  (int)polygonal_shape.size(), hash.Format( true ).c_str() );
582  name[ sizeof(name)-1 ] = 0;
583 
584  padstack->SetPadstackId( name );
585  }
586  break;
587  }
588 
589  return padstack;
590 }
591 
592 
594 typedef std::map<wxString, int> PINMAP;
595 
596 
597 IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint )
598 {
599  PINMAP pinmap;
600  wxString padName;
601 
602  PCB_TYPE_COLLECTOR fpItems;
603 
604  // get all the FOOTPRINT's pads.
605  fpItems.Collect( aFootprint, scanPADs );
606 
607  IMAGE* image = new IMAGE( 0 );
608 
609  image->image_id = aFootprint->GetFPID().Format().c_str();
610 
611  // from the pads, and make an IMAGE using collated padstacks.
612  for( int p=0; p < fpItems.GetCount(); ++p )
613  {
614  PAD* pad = (PAD*) fpItems[p];
615 
616  // see if this pad is a through hole with no copper on its perimeter
617  if( isRoundKeepout( pad ) )
618  {
619  double diameter = scale( pad->GetDrillSize().x );
620  POINT vertex = mapPt( pad->GetPos0() );
621 
622  int layerCount = aBoard->GetCopperLayerCount();
623 
624  for( int layer=0; layer<layerCount; ++layer )
625  {
626  KEEPOUT* keepout = new KEEPOUT( image, T_keepout );
627 
628  image->keepouts.push_back( keepout );
629 
630  CIRCLE* circle = new CIRCLE( keepout );
631 
632  keepout->SetShape( circle );
633 
634  circle->SetDiameter( diameter );
635  circle->SetVertex( vertex );
636  circle->SetLayerId( m_layerIds[layer].c_str() );
637  }
638  }
639  // else if() could there be a square keepout here?
640 
641  else
642  {
643  // Pads not on copper layers (i.e. only on tech layers) are ignored
644  // because they create invalid pads in .dsn file for freeroute
645  LSET mask_copper_layers = pad->GetLayerSet() & LSET::AllCuMask();
646 
647  if( !mask_copper_layers.any() )
648  continue;
649 
650  PADSTACK* padstack = makePADSTACK( aBoard, pad );
651  PADSTACKSET::iterator iter = m_padstackset.find( *padstack );
652 
653  if( iter != m_padstackset.end() )
654  {
655  // padstack is a duplicate, delete it and use the original
656  delete padstack;
657  padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
658  }
659  else
660  {
661  m_padstackset.insert( padstack );
662  }
663 
664  PIN* pin = new PIN( image );
665 
666  padName = pad->GetName();
667  pin->pin_id = TO_UTF8( padName );
668 
669  if( padName != wxEmptyString && pinmap.find( padName ) == pinmap.end() )
670  {
671  pinmap[ padName ] = 0;
672  }
673  else // pad name is a duplicate within this footprint
674  {
675  char buf[32];
676 
677  int duplicates = ++pinmap[ padName ];
678 
679  sprintf( buf, "@%d", duplicates );
680 
681  pin->pin_id += buf; // append "@1" or "@2", etc. to pin name
682  }
683 
684  pin->kiNetCode = pad->GetNetCode();
685 
686  image->pins.push_back( pin );
687 
688  pin->padstack_id = padstack->padstack_id;
689 
690  double angle = pad->GetOrientationDegrees() - aFootprint->GetOrientationDegrees();
692  pin->SetRotation( angle );
693 
694  wxPoint pos( pad->GetPos0() );
695 
696  pin->SetVertex( mapPt( pos ) );
697  }
698  }
699 
700  static const KICAD_T scanEDGEs[] = { PCB_FP_SHAPE_T, EOT };
701 
702  // get all the FOOTPRINT's FP_SHAPEs and convert those to DSN outlines.
703  fpItems.Collect( aFootprint, scanEDGEs );
704 
705  for( int i = 0; i < fpItems.GetCount(); ++i )
706  {
707  FP_SHAPE* graphic = (FP_SHAPE*) fpItems[i];
708  SHAPE* outline;
709  PATH* path;
710 
711  switch( graphic->GetShape() )
712  {
714  outline = new SHAPE( image, T_outline );
715 
716  image->Append( outline );
717  path = new PATH( outline );
718 
719  outline->SetShape( path );
720  path->SetAperture( scale( graphic->GetWidth() ) );
721  path->SetLayerId( "signal" );
722  path->AppendPoint( mapPt( graphic->GetStart0() ) );
723  path->AppendPoint( mapPt( graphic->GetEnd0() ) );
724  break;
725 
727  {
728  // this is best done by 4 QARC's but freerouter does not yet support QARCs.
729  // for now, support by using line segments.
730 
731  outline = new SHAPE( image, T_outline );
732 
733  image->Append( outline );
734  path = new PATH( outline );
735 
736  outline->SetShape( path );
737  path->SetAperture( scale( graphic->GetWidth() ) );
738  path->SetLayerId( "signal" );
739 
740  // Do the math using KiCad units, that way we stay out of the
741  // scientific notation range of floating point numbers in the
742  // DSN file. We do not parse scientific notation in our own
743  // lexer/beautifier, and the spec is not clear that this is
744  // required. Fixed point floats are all that should be needed.
745 
746  double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
747 
748  // seg count to approximate circle by line segments
749  int seg_per_circle = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 );
750 
751  for( int ii = 0; ii < seg_per_circle; ++ii )
752  {
753  double radians = 2*M_PI / seg_per_circle * ii;
754  wxPoint point( KiROUND( radius * cos( radians ) ),
755  KiROUND( radius * sin( radians ) ) );
756 
757  point += graphic->m_Start0; // an offset
758 
759  path->AppendPoint( mapPt( point ) );
760  }
761  // The shape must be closed
762  wxPoint point( radius , 0 );
763  point += graphic->m_Start0;
764  path->AppendPoint( mapPt( point ) );
765  }
766  break;
767 
769  case PCB_SHAPE_TYPE::ARC:
770  default:
771  continue;
772  }
773  }
774 
775  for( FP_ZONE* zone : aFootprint->Zones() )
776  {
777  if( !zone->GetIsRuleArea() )
778  continue;
779 
780  // IMAGE object coordinates are relative to the IMAGE not absolute board coordinates.
781  ZONE untransformedZone( *zone );
782 
783  double angle = -aFootprint->GetOrientation();
785  untransformedZone.Rotate( aFootprint->GetPosition(), angle );
786 
787  // keepout areas have a type. types are
788  // T_place_keepout, T_via_keepout, T_wire_keepout,
789  // T_bend_keepout, T_elongate_keepout, T_keepout.
790  // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
791  DSN_T keepout_type;
792 
793  if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
794  keepout_type = T_keepout;
795  else if( zone->GetDoNotAllowVias() )
796  keepout_type = T_via_keepout;
797  else if( zone->GetDoNotAllowTracks() )
798  keepout_type = T_wire_keepout;
799  else
800  keepout_type = T_keepout;
801 
802  // Now, build keepout polygon on each copper layer where the zone
803  // keepout is living (keepout zones can live on many copper layers)
804  const int copperCount = aBoard->GetCopperLayerCount();
805 
806  for( int layer = 0; layer < copperCount; layer++ )
807  {
808  if( layer == copperCount-1 )
809  layer = B_Cu;
810 
811  if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
812  continue;
813 
814  KEEPOUT* keepout = new KEEPOUT( m_pcb->structure, keepout_type );
815  image->keepouts.push_back( keepout );
816 
817  PATH* mainPolygon = new PATH( keepout, T_polygon );
818  keepout->SetShape( mainPolygon );
819 
820  mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
821 
822  // Handle the main outlines
823  SHAPE_POLY_SET::ITERATOR iterator;
824  bool is_first_point = true;
825  wxPoint startpoint;
826 
827  for( iterator = untransformedZone.IterateWithHoles(); iterator; iterator++ )
828  {
829  wxPoint point( iterator->x, iterator->y );
830 
831  point -= aFootprint->GetPosition();
832 
833  if( is_first_point )
834  {
835  startpoint = point;
836  is_first_point = false;
837  }
838 
839  mainPolygon->AppendPoint( mapPt( point ) );
840 
841  // this was the end of the main polygon
842  if( iterator.IsEndContour() )
843  {
844  mainPolygon->AppendPoint( mapPt( startpoint ) );
845  break;
846  }
847  }
848 
849  WINDOW* window = nullptr;
850  PATH* cutout = nullptr;
851  bool isStartContour = true;
852 
853  // handle the cutouts
854  for( iterator++; iterator; iterator++ )
855  {
856  if( isStartContour )
857  {
858  is_first_point = true;
859  window = new WINDOW( keepout );
860  keepout->AddWindow( window );
861 
862  cutout = new PATH( window, T_polygon );
863 
864  window->SetShape( cutout );
865 
866  cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ zone->GetLayer() ] ];
867  }
868 
869  isStartContour = iterator.IsEndContour();
870 
871  wxASSERT( window );
872  wxASSERT( cutout );
873 
874  wxPoint point( iterator->x, iterator->y );
875 
876  point -= aFootprint->GetPosition();
877 
878  if( is_first_point )
879  {
880  startpoint = point;
881  is_first_point = false;
882  }
883 
884  cutout->AppendPoint( mapPt( point ) );
885 
886  // Close the polygon
887  if( iterator.IsEndContour() )
888  cutout->AppendPoint( mapPt( startpoint ) );
889  }
890  }
891  }
892 
893  return image;
894 }
895 
896 
897 PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
898  int aTopLayer, int aBotLayer )
899 {
900  char name[48];
901  PADSTACK* padstack = new PADSTACK();
902  double dsnDiameter = scale( aCopperDiameter );
903 
904  for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
905  {
906  SHAPE* shape = new SHAPE( padstack );
907 
908  padstack->Append( shape );
909 
910  CIRCLE* circle = new CIRCLE( shape );
911 
912  shape->SetShape( circle );
913 
914  circle->SetDiameter( dsnDiameter );
915  circle->SetLayerId( m_layerIds[layer].c_str() );
916  }
917 
918  snprintf( name, sizeof( name ), "Via[%d-%d]_%.6g:%.6g_um",
919  aTopLayer, aBotLayer, dsnDiameter,
920  // encode the drill value into the name for later import
921  IU2um( aDrillDiameter ) );
922 
923  name[ sizeof(name) - 1 ] = 0;
924  padstack->SetPadstackId( name );
925 
926  return padstack;
927 }
928 
929 
931 {
932  PCB_LAYER_ID topLayerNum;
933  PCB_LAYER_ID botLayerNum;
934 
935  aVia->LayerPair( &topLayerNum, &botLayerNum );
936 
937  int topLayer = m_kicadLayer2pcb[topLayerNum];
938  int botLayer = m_kicadLayer2pcb[botLayerNum];
939 
940  if( topLayer > botLayer )
941  std::swap( topLayer, botLayer );
942 
943  return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
944 }
945 
946 
947 void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
948 {
949  SHAPE_POLY_SET outlines;
950 
951  if( !aBoard->GetBoardPolygonOutlines( outlines ) )
952  {
953  wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
954  }
955 
956  for( int cnt = 0; cnt < outlines.OutlineCount(); cnt++ ) // Should be one outline
957  {
958  PATH* path = new PATH( boundary );
959  boundary->paths.push_back( path );
960  path->layer_id = "pcb";
961 
962  SHAPE_LINE_CHAIN& outline = outlines.Outline( cnt );
963 
964  for( int ii = 0; ii < outline.PointCount(); ii++ )
965  {
966  wxPoint pos( outline.CPoint( ii ).x, outline.CPoint( ii ).y );
967  path->AppendPoint( mapPt( pos ) );
968  }
969 
970  // Close polygon:
971  wxPoint pos0( outline.CPoint( 0 ).x, outline.CPoint( 0 ).y );
972  path->AppendPoint( mapPt( pos0 ) );
973 
974  // Generate holes as keepout:
975  for( int ii = 0; ii < outlines.HoleCount( cnt ); ii++ )
976  {
977  // emit a signal layers keepout for every interior polygon left...
978  KEEPOUT* keepout = new KEEPOUT( NULL, T_keepout );
979  PATH* poly_ko = new PATH( NULL, T_polygon );
980 
981  keepout->SetShape( poly_ko );
982  poly_ko->SetLayerId( "signal" );
983  m_pcb->structure->keepouts.push_back( keepout );
984 
985  SHAPE_LINE_CHAIN& hole = outlines.Hole( cnt, ii );
986 
987  for( int jj = 0; jj < hole.PointCount(); jj++ )
988  {
989  wxPoint pos( hole.CPoint( jj ).x, hole.CPoint( jj ).y );
990  poly_ko->AppendPoint( mapPt( pos ) );
991  }
992 
993  // Close polygon:
994  wxPoint pos( hole.CPoint( 0 ).x, hole.CPoint( 0 ).y );
995  poly_ko->AppendPoint( mapPt( pos ) );
996  }
997  }
998 }
999 
1000 
1001 typedef std::set<std::string> STRINGSET;
1002 typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
1003 
1004 
1006 {
1007  PCB_TYPE_COLLECTOR items;
1008 
1009  static const KICAD_T scanMODULEs[] = { PCB_FOOTPRINT_T, EOT };
1010 
1011  // Not all boards are exportable. Check that all reference Ids are unique.
1012  // Unless they are unique, we cannot import the session file which comes
1013  // back to us later from the router.
1014  {
1015  items.Collect( aBoard, scanMODULEs );
1016 
1017  STRINGSET refs; // holds footprint reference designators
1018 
1019  for( int i=0; i<items.GetCount(); ++i )
1020  {
1021  FOOTPRINT* footprint = (FOOTPRINT*) items[i];
1022 
1023  if( footprint->GetReference() == wxEmptyString )
1024  {
1026  _( "Symbol with value of \"%s\" has empty reference id." ),
1027  footprint->GetValue() ) );
1028  }
1029 
1030  // if we cannot insert OK, that means the reference has been seen before.
1031  STRINGSET_PAIR refpair = refs.insert( TO_UTF8( footprint->GetReference() ) );
1032 
1033  if( !refpair.second ) // insert failed
1034  {
1036  _( "Multiple symbols have identical reference IDs of \"%s\"." ),
1037  footprint->GetReference() ) );
1038  }
1039  }
1040  }
1041 
1042  if( !m_pcb )
1044 
1045  //-----<layer_descriptor>-----------------------------------------------
1046  {
1047  // specctra wants top physical layer first, then going down to the
1048  // bottom most physical layer in physical sequence.
1049  // @question : why does KiCad not display layers in that order?
1050 
1051  buildLayerMaps( aBoard );
1052 
1053  int layerCount = aBoard->GetCopperLayerCount();
1054 
1055  for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
1056  {
1057  LAYER* layer = new LAYER( m_pcb->structure );
1058 
1059  m_pcb->structure->layers.push_back( layer );
1060 
1061  layer->name = m_layerIds[pcbNdx];
1062 
1063  DSN_T layerType;
1064 
1065  switch( aBoard->GetLayerType( m_pcbLayer2kicad[pcbNdx] ) )
1066  {
1067  default:
1068  case LT_SIGNAL: layerType = T_signal; break;
1069  case LT_POWER: layerType = T_power; break;
1070 
1071  // Freerouter does not support type "mixed", only signal and power.
1072  // Remap "mixed" to "signal".
1073  case LT_MIXED: layerType = T_signal; break;
1074  case LT_JUMPER: layerType = T_jumper; break;
1075  }
1076 
1077  layer->layer_type = layerType;
1078 
1079  layer->properties.push_back( PROPERTY() );
1080  PROPERTY* property = &layer->properties.back();
1081  property->name = "index";
1082  char temp[32];
1083  sprintf( temp, "%d", pcbNdx );
1084  property->value = temp;
1085  }
1086  }
1087 
1088  // a space in a quoted token is NOT a terminator, true establishes this.
1090 
1091  //-----<unit_descriptor> & <resolution_descriptor>--------------------
1092  {
1093  // tell freerouter to use "tenths of micrometers",
1094  // which is 100 nm resolution. Possibly more resolution is possible
1095  // in freerouter, but it would need testing.
1096 
1097  m_pcb->unit->units = T_um;
1098  m_pcb->resolution->units = T_um;
1099  m_pcb->resolution->value = 10; // tenths of a um
1100  }
1101 
1102  //-----<boundary_descriptor>------------------------------------------
1103  {
1104  // Because fillBOUNDARY() can throw an exception, we link in an
1105  // empty boundary so the BOUNDARY does not get lost in the event of
1106  // of an exception.
1107  BOUNDARY* boundary = new BOUNDARY( 0 );
1108 
1109  m_pcb->structure->SetBOUNDARY( boundary );
1110  fillBOUNDARY( aBoard, boundary );
1111  }
1112 
1113  //-----<rules>--------------------------------------------------------
1114  {
1115  char rule[80];
1116  NETCLASS* defaultClass = aBoard->GetDesignSettings().GetDefault();
1117 
1118  int defaultTrackWidth = defaultClass->GetTrackWidth();
1119  int defaultClearance = defaultClass->GetClearance();
1120 
1121  double clearance = scale( defaultClearance );
1122 
1123  STRINGS& rules = m_pcb->structure->rules->rules;
1124 
1125  sprintf( rule, "(width %.6g)", scale( defaultTrackWidth ) );
1126  rules.push_back( rule );
1127 
1128  sprintf( rule, "(clearance %.6g)", clearance + safetyMargin );
1129  rules.push_back( rule );
1130 
1131  // On a high density board (a board with 4 mil tracks, 4 mil spacing)
1132  // a typical solder mask clearance will be 2-3 mils.
1133  // This exposes 2 to 3 mils of bare board around each pad, and would
1134  // leave only 1 to 2 mils of solder mask between the solder mask's boundary
1135  // to the edge of any trace within "clearance" of the pad. So we need at least
1136  // 2 mils *extra* clearance for traces which would come near a pad on
1137  // a different net. So if the baseline trace to trace clearance was say 4 mils, then
1138  // the SMD to trace clearance should be at least 6 mils.
1139  double default_smd = clearance + safetyMargin;
1140 
1141  if( default_smd <= 6.0 )
1142  default_smd = 6.0;
1143 
1144  sprintf( rule, "(clearance %.6g (type default_smd))", default_smd );
1145 
1146  rules.push_back( rule );
1147 
1148  // Pad to pad spacing on a single SMT part can be closer than our
1149  // clearance, we don't want freerouter complaining about that, so
1150  // output a significantly smaller pad to pad clearance to freerouter.
1151  clearance = scale( defaultClearance ) / 4;
1152 
1153  sprintf( rule, "(clearance %.6g (type smd_smd))", clearance );
1154  rules.push_back( rule );
1155  }
1156 
1157  //-----<zones (not keepout areas) become planes>--------------------------------
1158  // Note: only zones are output here, keepout areas are created later.
1159  {
1160  int netlessZones = 0;
1161 
1162  static const KICAD_T scanZONEs[] = { PCB_ZONE_T, EOT };
1163  items.Collect( aBoard, scanZONEs );
1164 
1165  for( int i = 0; i<items.GetCount(); ++i )
1166  {
1167  ZONE* item = (ZONE*) items[i];
1168 
1169  if( item->GetIsRuleArea() )
1170  continue;
1171 
1172  // Currently, we export only copper layers
1173  if( ! IsCopperLayer( item->GetLayer() ) )
1174  continue;
1175 
1176  COPPER_PLANE* plane = new COPPER_PLANE( m_pcb->structure );
1177 
1178  m_pcb->structure->planes.push_back( plane );
1179 
1180  PATH* mainPolygon = new PATH( plane, T_polygon );
1181 
1182  plane->SetShape( mainPolygon );
1183 
1184  plane->name = TO_UTF8( item->GetNetname() );
1185 
1186  if( plane->name.size() == 0 )
1187  {
1188  char name[32];
1189 
1190  // This is one of those no connection zones, netcode=0, and it has no name.
1191  // Create a unique, bogus netname.
1192  NET* no_net = new NET( m_pcb->network );
1193 
1194  sprintf( name, "@:no_net_%d", netlessZones++ );
1195  no_net->net_id = name;
1196 
1197  // add the bogus net name to network->nets.
1198  m_pcb->network->nets.push_back( no_net );
1199 
1200  // use the bogus net name in the netless zone.
1201  plane->name = no_net->net_id;
1202  }
1203 
1204  mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ item->GetLayer() ] ];
1205 
1206  // Handle the main outlines
1207  SHAPE_POLY_SET::ITERATOR iterator;
1208  wxPoint startpoint;
1209  bool is_first_point = true;
1210 
1211  for( iterator = item->IterateWithHoles(); iterator; iterator++ )
1212  {
1213  wxPoint point( iterator->x, iterator->y );
1214 
1215  if( is_first_point )
1216  {
1217  startpoint = point;
1218  is_first_point = false;
1219  }
1220 
1221  mainPolygon->AppendPoint( mapPt( point ) );
1222 
1223  // this was the end of the main polygon
1224  if( iterator.IsEndContour() )
1225  {
1226  // Close polygon
1227  mainPolygon->AppendPoint( mapPt( startpoint ) );
1228  break;
1229  }
1230  }
1231 
1232  WINDOW* window = 0;
1233  PATH* cutout = 0;
1234 
1235  bool isStartContour = true;
1236 
1237  // handle the cutouts
1238  for( iterator++; iterator; iterator++ )
1239  {
1240  if( isStartContour )
1241  {
1242  is_first_point = true;
1243  window = new WINDOW( plane );
1244 
1245  plane->AddWindow( window );
1246 
1247  cutout = new PATH( window, T_polygon );
1248 
1249  window->SetShape( cutout );
1250 
1251  cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ item->GetLayer() ] ];
1252  }
1253 
1254  // If the point in this iteration is the last of the contour, the next iteration
1255  // will start with a new contour.
1256  isStartContour = iterator.IsEndContour();
1257 
1258  wxASSERT( window );
1259  wxASSERT( cutout );
1260 
1261  wxPoint point(iterator->x, iterator->y );
1262 
1263  if( is_first_point )
1264  {
1265  startpoint = point;
1266  is_first_point = false;
1267  }
1268 
1269  cutout->AppendPoint( mapPt( point ) );
1270 
1271  // Close the polygon
1272  if( iterator.IsEndContour() )
1273  cutout->AppendPoint( mapPt( startpoint ) );
1274  }
1275  }
1276  }
1277 
1278  //-----<zones flagged keepout areas become keepout>--------------------------------
1279  {
1280  static const KICAD_T scanZONEs[] = { PCB_ZONE_T, EOT };
1281  items.Collect( aBoard, scanZONEs );
1282 
1283  for( int i=0; i<items.GetCount(); ++i )
1284  {
1285  ZONE* item = (ZONE*) items[i];
1286 
1287  if( !item->GetIsRuleArea() )
1288  continue;
1289 
1290  // keepout areas have a type. types are
1291  // T_place_keepout, T_via_keepout, T_wire_keepout,
1292  // T_bend_keepout, T_elongate_keepout, T_keepout.
1293  // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
1294  DSN_T keepout_type;
1295 
1296  if( item->GetDoNotAllowVias() && item->GetDoNotAllowTracks() )
1297  keepout_type = T_keepout;
1298  else if( item->GetDoNotAllowVias() )
1299  keepout_type = T_via_keepout;
1300  else if( item->GetDoNotAllowTracks() )
1301  keepout_type = T_wire_keepout;
1302  else
1303  keepout_type = T_keepout;
1304 
1305  // Now, build keepout polygon on each copper layer where the item
1306  // keepout is living (keepout zones can live on many copper layers)
1307  const int copperCount = aBoard->GetCopperLayerCount();
1308 
1309  for( int layer = 0; layer < copperCount; layer++ )
1310  {
1311  if( layer == copperCount - 1 )
1312  layer = B_Cu;
1313 
1314  if( !item->IsOnLayer( PCB_LAYER_ID( layer ) ) )
1315  continue;
1316 
1317  KEEPOUT* keepout = new KEEPOUT( m_pcb->structure, keepout_type );
1318  m_pcb->structure->keepouts.push_back( keepout );
1319 
1320  PATH* mainPolygon = new PATH( keepout, T_polygon );
1321  keepout->SetShape( mainPolygon );
1322 
1323  mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1324 
1325  // Handle the main outlines
1326  SHAPE_POLY_SET::ITERATOR iterator;
1327  bool is_first_point = true;
1328  wxPoint startpoint;
1329 
1330  for( iterator = item->IterateWithHoles(); iterator; iterator++ )
1331  {
1332  wxPoint point( iterator->x, iterator->y );
1333 
1334  if( is_first_point )
1335  {
1336  startpoint = point;
1337  is_first_point = false;
1338  }
1339 
1340  mainPolygon->AppendPoint( mapPt( point ) );
1341 
1342  // this was the end of the main polygon
1343  if( iterator.IsEndContour() )
1344  {
1345  mainPolygon->AppendPoint( mapPt( startpoint ) );
1346  break;
1347  }
1348  }
1349 
1350  WINDOW* window = nullptr;
1351  PATH* cutout = nullptr;
1352 
1353  bool isStartContour = true;
1354 
1355  // handle the cutouts
1356  for( iterator++; iterator; iterator++ )
1357  {
1358  if( isStartContour )
1359  {
1360  is_first_point = true;
1361  window = new WINDOW( keepout );
1362  keepout->AddWindow( window );
1363 
1364  cutout = new PATH( window, T_polygon );
1365 
1366  window->SetShape( cutout );
1367 
1368  cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ item->GetLayer() ] ];
1369  }
1370 
1371  isStartContour = iterator.IsEndContour();
1372 
1373  wxASSERT( window );
1374  wxASSERT( cutout );
1375 
1376  wxPoint point(iterator->x, iterator->y );
1377 
1378  if( is_first_point )
1379  {
1380  startpoint = point;
1381  is_first_point = false;
1382  }
1383 
1384  cutout->AppendPoint( mapPt(point) );
1385 
1386  // Close the polygon
1387  if( iterator.IsEndContour() )
1388  cutout->AppendPoint( mapPt( startpoint ) );
1389  }
1390  }
1391  }
1392  }
1393 
1394  //-----<build the images, components, and netlist>-----------------------
1395  {
1396  PIN_REF empty( m_pcb->network );
1397 
1398  std::string componentId;
1399 
1400  // find the highest numbered netCode within the board.
1401  int highestNetCode = 0;
1402  NETINFO_LIST& netInfo = aBoard->GetNetInfo();
1403 
1404  for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1405  highestNetCode = std::max( highestNetCode, i->GetNetCode() );
1406 
1407  deleteNETs();
1408 
1409  // expand the net vector to highestNetCode+1, setting empty to NULL
1410  m_nets.resize( highestNetCode + 1, NULL );
1411 
1412  for( unsigned i = 1 /* skip "No Net" at [0] */; i < m_nets.size(); ++i )
1413  m_nets[i] = new NET( m_pcb->network );
1414 
1415  for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1416  {
1417  if( i->GetNetCode() > 0 )
1418  m_nets[i->GetNetCode()]->net_id = TO_UTF8( i->GetNetname() );
1419  }
1420 
1421  items.Collect( aBoard, scanMODULEs );
1422 
1423  m_padstackset.clear();
1424 
1425  for( int m = 0; m<items.GetCount(); ++m )
1426  {
1427  FOOTPRINT* footprint = (FOOTPRINT*) items[m];
1428 
1429  IMAGE* image = makeIMAGE( aBoard, footprint );
1430 
1431  componentId = TO_UTF8( footprint->GetReference() );
1432 
1433  // create a net list entry for all the actual pins in the image
1434  // for the current footprint. location of this code is critical
1435  // because we fabricated some pin names to ensure unique-ness
1436  // of pin names within a footprint, do not move this code because
1437  // the life of this 'IMAGE* image' is not necessarily long. The
1438  // exported netlist will have some fabricated pin names in it.
1439  // If you don't like fabricated pin names, then make sure all pads
1440  // within your FOOTPRINTs are uniquely named!
1441  for( unsigned p = 0; p<image->pins.size(); ++p )
1442  {
1443  PIN* pin = &image->pins[p];
1444 
1445  int netcode = pin->kiNetCode;
1446 
1447  if( netcode > 0 )
1448  {
1449  NET* net = m_nets[netcode];
1450 
1451  net->pins.push_back( empty );
1452 
1453  PIN_REF& pin_ref = net->pins.back();
1454 
1455  pin_ref.component_id = componentId;
1456  pin_ref.pin_id = pin->pin_id;
1457  }
1458  }
1459 
1461 
1462  if( registered != image )
1463  {
1464  // If our new 'image' is not a unique IMAGE, delete it.
1465  // and use the registered one, known as 'image' after this.
1466  delete image;
1467  image = registered;
1468  }
1469 
1470  COMPONENT* comp = m_pcb->placement->LookupCOMPONENT( image->GetImageId() );
1471 
1472  PLACE* place = new PLACE( comp );
1473 
1474  comp->places.push_back( place );
1475 
1476  place->SetRotation( footprint->GetOrientationDegrees() );
1477  place->SetVertex( mapPt( footprint->GetPosition() ) );
1478  place->component_id = componentId;
1479  place->part_number = TO_UTF8( footprint->GetValue() );
1480 
1481  // footprint is flipped from bottom side, set side to T_back
1482  if( footprint->GetFlag() )
1483  {
1484  double angle = 180.0 - footprint->GetOrientationDegrees();
1486  place->SetRotation( angle );
1487 
1488  place->side = T_back;
1489  }
1490  }
1491 
1492  // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
1493  // removing, do not increment the iterator
1494  for( PADSTACKSET::iterator i = m_padstackset.begin(); i != m_padstackset.end();
1495  i = m_padstackset.begin() )
1496  {
1497  PADSTACKSET::auto_type ps = m_padstackset.release( i );
1498  PADSTACK* padstack = ps.release();
1499 
1500  m_pcb->library->AddPadstack( padstack );
1501  }
1502 
1503  // copy our SPECCTRA_DB::nets to the pcb->network
1504  for( unsigned n = 1; n < m_nets.size(); ++n )
1505  {
1506  NET* net = m_nets[n];
1507 
1508  if( net->pins.size() )
1509  {
1510  // give ownership to pcb->network
1511  m_pcb->network->nets.push_back( net );
1512  m_nets[n] = 0;
1513  }
1514  }
1515  }
1516 
1517  //-----< output vias used in netclasses >-----------------------------------
1518  {
1519  NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses();
1520 
1521  // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
1522  // This is in lieu of either having each netclass via have its own layer pair in
1523  // the netclass dialog, or such control in the specctra export dialog.
1524 
1525 
1526  m_top_via_layer = 0; // first specctra cu layer is number zero.
1527  m_bot_via_layer = aBoard->GetCopperLayerCount()-1;
1528 
1529  // Add the via from the Default netclass first. The via container
1530  // in pcb->library preserves the sequence of addition.
1531 
1532  NETCLASSPTR netclass = nclasses.GetDefault();
1533 
1534  PADSTACK* via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1536 
1537  // we AppendVia() this first one, there is no way it can be a duplicate,
1538  // the pcb->library via container is empty at this point. After this,
1539  // we'll have to use LookupVia().
1540  wxASSERT( m_pcb->library->vias.size() == 0 );
1541  m_pcb->library->AppendVia( via );
1542 
1543  // set the "spare via" index at the start of the
1544  // pcb->library->spareViaIndex = pcb->library->vias.size();
1545 
1546  // output the non-Default netclass vias
1547  for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
1548  {
1549  netclass = nc->second;
1550 
1551  via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1553 
1554  // maybe add 'via' to the library, but only if unique.
1556 
1557  if( registered != via )
1558  delete via;
1559  }
1560  }
1561 
1562  //-----<create the wires from tracks>-----------------------------------
1563  {
1564  // export all of them for now, later we'll decide what controls we need
1565  // on this.
1566  static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, EOT };
1567 
1568  items.Collect( aBoard, scanTRACKs );
1569 
1570  std::string netname;
1571  WIRING* wiring = m_pcb->wiring;
1572  PATH* path = 0;
1573 
1574  int old_netcode = -1;
1575  int old_width = -1;
1576  LAYER_NUM old_layer = UNDEFINED_LAYER;
1577 
1578  for( int i=0; i<items.GetCount(); ++i )
1579  {
1580  TRACK* track = (TRACK*) items[i];
1581 
1582  int netcode = track->GetNetCode();
1583 
1584  if( netcode == 0 )
1585  continue;
1586 
1587  if( old_netcode != netcode || old_width != track->GetWidth() ||
1588  old_layer != track->GetLayer() ||
1589  ( path && path->points.back() != mapPt(track->GetStart() ) ) )
1590  {
1591  old_width = track->GetWidth();
1592  old_layer = track->GetLayer();
1593 
1594  if( old_netcode != netcode )
1595  {
1596  old_netcode = netcode;
1597  NETINFO_ITEM* net = aBoard->FindNet( netcode );
1598  wxASSERT( net );
1599  netname = TO_UTF8( net->GetNetname() );
1600  }
1601 
1602  WIRE* wire = new WIRE( wiring );
1603 
1604  wiring->wires.push_back( wire );
1605  wire->net_id = netname;
1606 
1607  wire->wire_type = T_protect; // @todo, this should be configurable
1608 
1609  LAYER_NUM kiLayer = track->GetLayer();
1610  int pcbLayer = m_kicadLayer2pcb[kiLayer];
1611 
1612  path = new PATH( wire );
1613 
1614  wire->SetShape( path );
1615 
1616  path->layer_id = m_layerIds[pcbLayer];
1617  path->aperture_width = scale( old_width );
1618 
1619  path->AppendPoint( mapPt( track->GetStart() ) );
1620  }
1621 
1622  if( path ) // Should not occur
1623  path->AppendPoint( mapPt( track->GetEnd() ) );
1624  }
1625  }
1626 
1627  //-----<export the existing real BOARD instantiated vias>-----------------
1628  {
1629  // Export all vias, once per unique size and drill diameter combo.
1630  static const KICAD_T scanVIAs[] = { PCB_VIA_T, EOT };
1631 
1632  items.Collect( aBoard, scanVIAs );
1633 
1634  for( int i = 0; i<items.GetCount(); ++i )
1635  {
1636  ::VIA* via = (::VIA*) items[i];
1637  wxASSERT( via->Type() == PCB_VIA_T );
1638 
1639  int netcode = via->GetNetCode();
1640 
1641  if( netcode == 0 )
1642  continue;
1643 
1644  PADSTACK* padstack = makeVia( via );
1645  PADSTACK* registered = m_pcb->library->LookupVia( padstack );
1646 
1647  // if the one looked up is not our padstack, then delete our padstack
1648  // since it was a duplicate of one already registered.
1649  if( padstack != registered )
1650  {
1651  delete padstack;
1652  }
1653 
1654  WIRE_VIA* dsnVia = new WIRE_VIA( m_pcb->wiring );
1655 
1656  m_pcb->wiring->wire_vias.push_back( dsnVia );
1657 
1658  dsnVia->padstack_id = registered->padstack_id;
1659  dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) );
1660 
1661  NETINFO_ITEM* net = aBoard->FindNet( netcode );
1662  wxASSERT( net );
1663 
1664  dsnVia->net_id = TO_UTF8( net->GetNetname() );
1665 
1666  dsnVia->via_type = T_protect; // @todo, this should be configurable
1667  }
1668  }
1669 
1670  //-----<via_descriptor>-------------------------------------------------
1671  {
1672  // The pcb->library will output <padstack_descriptors> which is a combined
1673  // list of part padstacks and via padstacks. specctra dsn uses the
1674  // <via_descriptors> to say which of those padstacks are vias.
1675 
1676  // Output the vias in the padstack list here, by name only. This must
1677  // be done after exporting existing vias as WIRE_VIAs.
1678  VIA* vias = m_pcb->structure->via;
1679 
1680  for( unsigned viaNdx = 0; viaNdx < m_pcb->library->vias.size(); ++viaNdx )
1681  {
1682  vias->AppendVia( m_pcb->library->vias[viaNdx].padstack_id.c_str() );
1683  }
1684  }
1685 
1686  //-----<output NETCLASSs>----------------------------------------------------
1687  NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses();
1688 
1689  exportNETCLASS( nclasses.GetDefault(), aBoard );
1690 
1691  for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
1692  {
1693  NETCLASSPTR netclass = nc->second;
1694  exportNETCLASS( netclass, aBoard );
1695  }
1696 }
1697 
1698 
1699 void SPECCTRA_DB::exportNETCLASS( const NETCLASSPTR& aNetClass, BOARD* aBoard )
1700 {
1701  /* From page 11 of specctra spec:
1702  *
1703  * Routing and Placement Rule Hierarchies
1704  *
1705  * Routing and placement rules can be defined at multiple levels of design
1706  * specification. When a routing or placement rule is defined for an object at
1707  * multiple levels, a predefined routing or placement precedence order
1708  * automatically determines which rule to apply to the object. The routing rule
1709  * precedence order is
1710  *
1711  * pcb < layer < class < class layer < group_set < group_set layer < net <
1712  * net layer < group < group layer < fromto < fromto layer < class_class <
1713  * class_class layer < padstack < region < class region < net region <
1714  * class_class region
1715  *
1716  * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1717  * hierarchy. A class-to-class region rule has the highest precedence. Rules
1718  * set at one level of the hierarchy override conflicting rules set at lower
1719  * levels. The placement rule precedence order is
1720  *
1721  * pcb < image_set < image < component < super cluster < room <
1722  * room_image_set < family_family < image_image
1723  *
1724  * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1725  * hierarchy. An image-to-image rule has the highest precedence. Rules set at
1726  * one level of the hierarchy override conflicting rules set at lower levels.
1727  */
1728 
1729  char text[256];
1730 
1731  CLASS* clazz = new CLASS( m_pcb->network );
1732 
1733  m_pcb->network->classes.push_back( clazz );
1734 
1735  // freerouter creates a class named 'default' anyway, and if we
1736  // try and use that, we end up with two 'default' via rules so use
1737  // something else as the name of our default class.
1738  clazz->class_id = TO_UTF8( aNetClass->GetName() );
1739 
1740  for( NETCLASS::iterator net = aNetClass->begin(); net != aNetClass->end(); ++net )
1741  clazz->net_ids.push_back( TO_UTF8( *net ) );
1742 
1743  clazz->rules = new RULE( clazz, T_rule );
1744 
1745  // output the track width.
1746  int trackWidth = aNetClass->GetTrackWidth();
1747  sprintf( text, "(width %.6g)", scale( trackWidth ) );
1748  clazz->rules->rules.push_back( text );
1749 
1750  // output the clearance.
1751  int clearance = aNetClass->GetClearance();
1752  sprintf( text, "(clearance %.6g)", scale( clearance ) + safetyMargin );
1753  clazz->rules->rules.push_back( text );
1754 
1755  if( aNetClass->GetName() == NETCLASS::Default )
1756  {
1757  clazz->class_id = "kicad_default";
1758  }
1759 
1760  // the easiest way to get the via name is to create a via (which generates
1761  // the name internal to the PADSTACK), and then grab the name and then
1762  // delete the via. There are not that many netclasses so
1763  // this should never become a performance issue.
1764 
1765  PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
1767 
1768  snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
1769  clazz->circuit.push_back( text );
1770 
1771  delete via;
1772 }
1773 
1774 
1776 {
1777  // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1778  // top view.
1779  // Note: to export footprints, the footprints must be flipped around the X axis,
1780  // otherwise the rotation angle is not good
1781  for( FOOTPRINT* footprint : aBoard->Footprints() )
1782  {
1783  footprint->SetFlag( 0 );
1784 
1785  if( footprint->GetLayer() == B_Cu )
1786  {
1787  footprint->Flip( footprint->GetPosition(), false );
1788  footprint->SetFlag( 1 );
1789  }
1790  }
1791 
1792  m_footprintsAreFlipped = true;
1793 }
1794 
1795 
1797 {
1798  if( !m_footprintsAreFlipped )
1799  return;
1800 
1801  // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1802  // top view. Restore those that were flipped.
1803  // Note: to export footprints, the footprints were flipped around the X axis,
1804  for( FOOTPRINT* footprint : aBoard->Footprints() )
1805  {
1806  if( footprint->GetFlag() )
1807  {
1808  footprint->Flip( footprint->GetPosition(), false );
1809  footprint->SetFlag( 0 );
1810  }
1811  }
1812 
1813  m_footprintsAreFlipped = false;
1814 }
1815 
1816 } // namespace DSN
DSN_T units
Definition: specctra.h:404
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
iterator end() const
Definition: netinfo.h:441
VIA corresponds to the <via_descriptor> in the specctra dsn spec.
Definition: specctra.h:1024
void FixNegativeZero()
Function FixNegativeZero will change negative zero to positive zero in the IEEE floating point storag...
Definition: specctra.h:135
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1291
PIN_REF corresponds to the <pin_reference> definition in the specctra dsn spec.
Definition: specctra.h:2449
bool space_in_quoted_tokens
Definition: specctra.h:371
void buildLayerMaps(BOARD *aBoard)
Function buildLayerMaps creates a few data translation structures for layer name and number mapping b...
Definition: specctra.cpp:74
DSN::T DSN_T
Definition: specctra.h:46
SHAPE_POLY_SET::ITERATOR IterateWithHoles()
Return an iterator to visit all points of the zone's main outline with holes.
Definition: zone.h:540
std::map< wxString, int > PINMAP
data type used to ensure unique-ness of pin names, holding (wxString and int)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:223
void exportNETCLASS(const std::shared_ptr< NETCLASS > &aNetClass, BOARD *aBoard)
Function exportNETCLASS exports aNetClass to the DSN file.
NET corresponds to a <net_descriptor> in the DSN spec.
Definition: specctra.h:2591
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:1925
This source file implements export and import capabilities to the specctra dsn file format.
Definition: specctra.cpp:62
COPPER_PLANES planes
Definition: specctra.h:1558
const wxString & GetValue() const
Definition: footprint.h:448
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:265
STRINGS circuit
circuit descriptor list
Definition: specctra.h:2756
static bool isRoundKeepout(PAD *aPad)
Decide if the pad is a copper-less through hole which needs to be made into a round keepout.
This file is part of the common library TODO brief description.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
double y
Definition: specctra.h:96
std::string net_id
Definition: specctra.h:2595
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
Definition: pcb_shape.h:156
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:730
Implementation of conversion functions that require both schematic and board internal units.
This file is part of the common library.
std::string component_id
Definition: specctra.h:2451
Definition: board.h:67
RULE * rules
Definition: specctra.h:1554
static const double safetyMargin
const wxPoint & GetStart() const
Definition: track.h:116
static constexpr double IU_PER_MM
Mock up a conversion function.
bool m_footprintsAreFlipped
Definition: specctra.h:3642
PCB_SHAPE_TYPE GetShape() const
Definition: pcb_shape.h:130
bool GetDoNotAllowVias() const
Definition: zone.h:732
std::string part_number
Definition: specctra.h:1703
PADSTACKS vias
Definition: specctra.h:2259
static POINT mapPt(const wxPoint &pt)
Convert a KiCad point into a DSN file point.
int GetWidth() const
Definition: pcb_shape.h:118
static double IU2um(int kicadDist)
IMAGE * LookupIMAGE(IMAGE *aImage)
Function LookupIMAGE will add the image only if one exactly like it does not already exist in the ima...
Definition: specctra.h:2337
int GetWidth() const
Definition: eda_rect.h:114
void SetLayerId(const char *aLayerId)
Definition: specctra.h:604
void SetLayerId(const char *aLayerId)
Definition: specctra.h:759
double GetOrientation() const
Definition: footprint.h:186
static bool registered
Definition: coroutines.cpp:128
POINTS vertexes
Definition: specctra.h:2988
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pad.h:567
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)
Return the aIndex-th subpolygon in the set.
const wxPoint & GetStart0() const
Definition: fp_shape.h:112
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aError, ERROR_LOC aErrorLoc)
convert a rectangle with rounded corners and/or chamfered corners to a polygon Convert rounded corner...
UNIT_RES * resolution
Definition: specctra.h:3167
wxString GetNetname() const
STRINGS m_layerIds
indexed by PCB layer number
Definition: specctra.h:3646
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:314
IMAGE * makeIMAGE(BOARD *aBoard, FOOTPRINT *aFootprint)
Function makeIMAGE allocates an IMAGE on the heap and creates all the PINs according to the PADs in t...
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:593
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:754
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
void AppendPoint(const POINT &aPoint)
Definition: specctra.h:597
class PAD, a pad in a footprint
Definition: typeinfo.h:89
CLASS corresponds to the <class_descriptor> in the specctra spec.
Definition: specctra.h:2747
iterator end()
Definition: netclass.h:236
PLACES places
Definition: specctra.h:1761
void NORMALIZE_ANGLE_DEGREES_POS(double &Angle)
Definition: trigo.h:306
double x
Definition: specctra.h:95
void SetPCB(PCB *aPcb)
Function SetPCB deletes any existing PCB and replaces it with the given one.
Definition: specctra.h:3904
STRUCTURE * structure
Definition: specctra.h:3169
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:288
WIRING * wiring
Definition: specctra.h:3173
int PointCount() const
Function PointCount()
void SetLayerId(const char *aLayerId)
Definition: specctra.h:451
static PCB * MakePCB()
Function MakePCB makes a PCB with all the default ELEMs and parts on the heap.
Definition: specctra.cpp:3467
std::string net_id
Definition: specctra.h:2989
std::string class_id
Definition: specctra.h:2751
std::string layer_id
Definition: specctra.h:582
DSN_T via_type
Definition: specctra.h:2991
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
RULE corresponds to the <rule_descriptor> in the specctra dsn spec.
Definition: specctra.h:488
void SetVertex(const POINT &aVertex)
Definition: specctra.h:1733
std::set< std::string > STRINGSET
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
This file contains miscellaneous commonly used macros and functions.
void SetShape(ELEM *aShape)
Definition: specctra.h:861
static const KICAD_T scanPADs[]
Definition: specctra.h:3657
const char * c_str() const
Definition: utf8.h:102
Definition: board.h:68
PATH supports both the <path_descriptor> and the <polygon_descriptor> per the specctra dsn spec.
Definition: specctra.h:578
std::pair< STRINGSET::iterator, bool > STRINGSET_PAIR
std::vector< NET * > m_nets
we don't want ownership here permanently, so we don't use boost::ptr_vector
Definition: specctra.h:3662
void Rotate(const wxPoint &aCentre, double aAngle) override
Move the outlines.
Definition: zone.cpp:707
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:87
void SynchronizeNetsAndNetClasses()
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:1436
DSN_T layer_type
one of: T_signal, T_power, T_mixed, T_jumper
Definition: specctra.h:1191
const VECTOR2I & CPoint(int aIndex) const
Function Point()
FP_ZONES & Zones()
Definition: footprint.h:170
NETCLASS_MAP::iterator iterator
Definition: netclass.h:234
void SetShape(ELEM *aShape)
Definition: specctra.h:940
const wxPoint & GetOffset() const
Definition: pad.h:249
WIRES wires
Definition: specctra.h:3124
const wxSize & GetDrillSize() const
Definition: pad.h:242
int GetFlag() const
Definition: footprint.h:236
STRINGSET::iterator iterator
Definition: netclass.h:94
PCB_LAYER_ID
A quick note on layer IDs:
int GetTrackWidth() const
Definition: netclass.h:128
std::vector< int > m_kicadLayer2pcb
maps BOARD layer number to PCB layer numbers
Definition: specctra.h:3648
LSET is a set of PCB_LAYER_IDs.
iterator begin()
Definition: netclass.h:235
A container for NETCLASS instances.
Definition: netclass.h:218
std::string name
Definition: specctra.h:1190
Normal via.
Definition: router_tool.cpp:67
#define NULL
void AddWindow(WINDOW *aWindow)
Definition: specctra.h:957
MD5_HASH GetHash() const
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:29
COPPER_PLANE corresponds to a <plane_descriptor> in the specctra dsn spec.
Definition: specctra.h:1338
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:49
void deleteNETs()
Function deleteNETs deletes all the NETs that may be in here.
Definition: specctra.h:3830
Container for NETINFO_ITEM elements, which are the nets.
Definition: netinfo.h:315
std::string padstack_id
Definition: specctra.h:2987
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
DSN_T side
Definition: specctra.h:1680
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:216
Handles how to draw a screen (a board, a schematic ...)
Definition: base_screen.h:40
iterator begin() const
Definition: netinfo.h:436
segment with non rounded ends
FOOTPRINTS & Footprints()
Definition: board.h:305
void RevertFOOTPRINTs(BOARD *aBoard)
Function RevertFOOTPRINTs flips the footprints which were on the back side of the board back to the b...
PLACE implements the <placement_reference> in the specctra dsn spec.
Definition: specctra.h:1674
const wxSize & GetSize() const
Definition: pad.h:232
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
const int seg_per_circle
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
Definition: pcb_shape.h:145
bool GetDoNotAllowTracks() const
Definition: zone.h:733
const wxString & GetNetname() const
Definition: netinfo.h:119
void AddPadstack(PADSTACK *aPadstack)
Definition: specctra.h:2274
const EDA_RECT GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition: pad.cpp:517
wxPoint m_Start0
Start point or center, relative to footprint origin, orient 0.
Definition: fp_shape.h:160
PAD_SHAPE GetShape() const
Definition: pad.h:169
const wxString & GetReference() const
Definition: footprint.h:426
std::string component_id
reference designator
Definition: specctra.h:1678
PADSTACK holds either a via or a pad definition.
Definition: specctra.h:2114
void Append(ELEM *aElem)
Definition: specctra.h:320
NETCLASSES & GetNetClasses() const
a few functions useful in geometry calculations.
const LIB_ID & GetFPID() const
Definition: footprint.h:190
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:368
WIRE_VIAS wire_vias
Definition: specctra.h:3125
class ZONE, a copper pour area
Definition: typeinfo.h:105
bool IsModify() const
Definition: base_screen.h:63
int m_top_via_layer
specctra cu layers, 0 based index:
Definition: specctra.h:3665
std::vector< std::string > STRINGS
Definition: specctra.h:157
SPECCTRA_DB holds a DSN data tree, usually coming from a DSN file.
Definition: specctra.h:3632
CLASSLIST classes
Definition: specctra.h:2838
static PATH * makePath(const POINT &aStart, const POINT &aEnd, const std::string &aLayerName)
Create a PATH element with a single straight line, a pair of vertices.
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
COMPONENT * LookupCOMPONENT(const std::string &imageName)
Function LookupCOMPONENT looks up a COMPONENT by name.
Definition: specctra.h:1832
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Collect BOARD_ITEM objects using this class's Inspector method, which does the collection.
Definition: collectors.cpp:605
int GetHeight() const
Definition: eda_rect.h:115
UTF8 Format() const
Definition: lib_id.cpp:233
PARSER * parser
Definition: specctra.h:3166
PADSTACK * makePADSTACK(BOARD *aBoard, PAD *aPad)
Function makePADSTACK creates a PADSTACK which matches the given pad.
void SetRotation(double aRotation)
Definition: specctra.h:1740
int LAYER_NUM
This can be replaced with int and removed.
LIBRARY * library
Definition: specctra.h:3171
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
BOARD * GetBoard()
int GetRoundRectCornerRadius() const
Definition: pad.cpp:236
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
const wxSize & GetDelta() const
Definition: pad.h:239
SHAPE corresponds to the "(shape ..)" element in the specctra dsn spec.
Definition: specctra.h:1878
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:376
double GetOrientationDegrees() const
Definition: footprint.h:187
Handle the data for a net.
Definition: netinfo.h:64
PADSTACK * LookupVia(PADSTACK *aVia)
Function LookupVia will add the via only if one exactly like it does not already exist in the padstac...
Definition: specctra.h:2392
COMPONENT implements the <component_descriptor> in the specctra dsn spec.
Definition: specctra.h:1754
std::vector< PCB_LAYER_ID > m_pcbLayer2kicad
maps PCB layer number to BOARD layer numbers
Definition: specctra.h:3649
Struct POINT is a holder for a point in the SPECCTRA DSN coordinate system.
Definition: specctra.h:93
void FromBOARD(BOARD *aBoard)
Function FromBOARD adds the entire BOARD to the PCB but does not write it out.
int GetWidth() const
Definition: track.h:110
WIRE corresponds to <wire_shape_descriptor> in the specctra dsn spec.
Definition: specctra.h:2873
const char * name
Definition: DXF_plotter.cpp:59
double GetChamferRectRatio() const
Definition: pad.h:532
PATHS paths
Definition: specctra.h:659
void SetCorners(const POINT &aPoint0, const POINT &aPoint1)
Definition: specctra.h:456
void ExportPCB(const wxString &aFilename, bool aNameChange=false)
Function ExportPCB writes the internal PCB instance out as a SPECTRA DSN format file.
Definition: specctra.cpp:3442
void AppendVia(PADSTACK *aVia)
Function AppendVia adds aVia to the internal via container.
Definition: specctra.h:2367
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
int GetClearance() const
Definition: netclass.h:124
PLACEMENT * placement
Definition: specctra.h:3170
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
static double mapY(int y)
RULE * rules
Definition: specctra.h:2758
Base class for iterating over all vertices in a given SHAPE_POLY_SET.
std::string Format(bool aCompactForm=false)
Definition: md5_hash.cpp:93
int GetCopperLayerCount() const
Definition: board.cpp:435
static double mapX(int x)
Handle the component boundary box.
Definition: eda_rect.h:42
NETCLASS * GetDefault() const
void ClrModify()
Definition: base_screen.h:60
usual segment : line with rounded ends
const wxPoint & GetEnd0() const
Definition: fp_shape.h:115
void SetShape(ELEM *aShape)
Definition: specctra.h:2914
static bool empty(const wxTextEntryBase *aCtrl)
static double scale(int kicadDist)
Convert a distance from Pcbnew internal units to the reported Specctra DSN units in floating point fo...
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:68
void SetBOUNDARY(BOUNDARY *aBoundary)
Definition: specctra.h:1595
wxPoint GetPosition() const override
Definition: footprint.h:182
std::string padstack_id
Definition: specctra.h:2120
const wxPoint & GetEnd() const
Definition: track.h:113
void AppendVia(const char *aViaName)
Definition: specctra.h:1038
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
UNIT_RES * unit
Definition: specctra.h:3168
int GetChamferPositions() const
Definition: pad.h:542
NETCLASSPTR GetDefault() const
Definition: netclass.h:253
LAYERS layers
Definition: specctra.h:1546
PIN_REFS pins
Definition: specctra.h:2600
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
WIRE_VIA corresponds to <wire_via_descriptor> in the specctra dsn spec.
Definition: specctra.h:2983
void SetVertex(const POINT &aVertex)
Definition: specctra.h:769
std::string pin_id
Definition: specctra.h:2452
DSN_T wire_type
Definition: specctra.h:2888
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:618
Definition: pad.h:60
PROPERTIES properties
Definition: specctra.h:1198
STRINGS net_ids
Definition: specctra.h:2753
Arcs (with rounded ends)
PADSTACKSET m_padstackset
Definition: specctra.h:3659
KEEPOUT is used for <keepout_descriptor> and <plane_descriptor>.
Definition: specctra.h:895
std::string name
Definition: specctra.h:900
NETWORK * network
Definition: specctra.h:3172
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:173
void SetPadstackId(const char *aPadstackId)
Definition: specctra.h:2167
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
void SetDiameter(double aDiameter)
Definition: specctra.h:764
A specialization of ZONE for use in footprints.
Definition: zone.h:943
WIRING corresponds to <wiring_descriptor> in the specctra dsn spec.
Definition: specctra.h:3119
Definition: track.h:83
std::string net_id
Definition: specctra.h:2886
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.
void FlipFOOTPRINTs(BOARD *aBoard)
Function FlipFOOTPRINTs flips the footprints which are on the back side of the board to the front.
bool ExportSpecctraFile(const wxString &aFullFilename)
Export the current BOARD to a specctra dsn file.
STRINGS rules
rules are saved in std::string form.
Definition: specctra.h:492
void BuildConvexHull(std::vector< wxPoint > &aResult, const std::vector< wxPoint > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
Definition: convex_hull.cpp:87
KEEPOUTS keepouts
Definition: specctra.h:1556