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