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