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 <[email protected]>
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->GetCenter0();
806  double radius = graphic->GetRadius() + graphic->GetWidth()/2;
807  double arcAngleDeg = graphic->GetArcAngle() / 10.0;
808 
809  VECTOR2I startRadial = graphic->GetStart() - graphic->GetCenter();
810  double arcStartDeg = ArcTangente( startRadial.y, startRadial.x ) / 10;
811  NORMALIZE_ANGLE_DEGREES_POS( arcStartDeg );
812 
813  // For some obscure reason, FreeRouter does not show the same polygonal
814  // shape for polygons CW and CCW. So used only the order of corners
815  // giving the best look.
816  if( arcAngleDeg < 0 )
817  {
818  VECTOR2I endRadial = graphic->GetEnd() - graphic->GetCenter();
819  arcStartDeg = ArcTangente( endRadial.y, endRadial.x ) / 10;
820  NORMALIZE_ANGLE_DEGREES_POS( arcStartDeg );
821 
822  arcAngleDeg = -arcAngleDeg;
823  }
824 
825  SHAPE_LINE_CHAIN polyline;
826  ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius, arcStartDeg,
827  arcAngleDeg, ARC_HIGH_DEF, ERROR_INSIDE );
828 
829  SHAPE_POLY_SET polyBuffer;
830  polyBuffer.AddOutline( polyline );
831 
832  radius -= graphic->GetWidth();
833 
834  if( radius > 0 )
835  {
836  polyline.Clear();
837  ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius, arcStartDeg,
838  arcAngleDeg, ARC_HIGH_DEF, ERROR_INSIDE );
839 
840  // Add points in reverse order, to create a closed polygon
841  for( int ii = polyline.PointCount() - 1; ii >= 0; --ii )
842  polyBuffer.Append( polyline.CPoint( ii ) );
843  }
844 
845  // ensure the polygon is closed
846  polyBuffer.Append( polyBuffer.Outline( 0 ).CPoint( 0 ) );
847 
848  wxPoint move = graphic->GetCenter() - arc_centre;
849 
850  TransformCircleToPolygon( polyBuffer, graphic->GetStart() - move,
851  graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
852 
853  TransformCircleToPolygon( polyBuffer, graphic->GetEnd() - move,
854  graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
855 
856  polyBuffer.Simplify( SHAPE_POLY_SET::PM_FAST );
857  SHAPE_LINE_CHAIN& poly = polyBuffer.Outline( 0 );
858 
859  for( int ii = 0; ii < poly.PointCount(); ++ii )
860  {
861  wxPoint corner( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
862  path->AppendPoint( mapPt( corner ) );
863  }
864 
865  break;
866  }
867 
868  default:
869  continue;
870  }
871  }
872 
873  for( FP_ZONE* zone : aFootprint->Zones() )
874  {
875  if( !zone->GetIsRuleArea() )
876  continue;
877 
878  // IMAGE object coordinates are relative to the IMAGE not absolute board coordinates.
879  ZONE untransformedZone( *zone );
880 
881  double angle = -aFootprint->GetOrientation();
883  untransformedZone.Rotate( aFootprint->GetPosition(), angle );
884 
885  // keepout areas have a type. types are
886  // T_place_keepout, T_via_keepout, T_wire_keepout,
887  // T_bend_keepout, T_elongate_keepout, T_keepout.
888  // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
889  DSN_T keepout_type;
890 
891  if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
892  keepout_type = T_keepout;
893  else if( zone->GetDoNotAllowVias() )
894  keepout_type = T_via_keepout;
895  else if( zone->GetDoNotAllowTracks() )
896  keepout_type = T_wire_keepout;
897  else
898  keepout_type = T_keepout;
899 
900  // Now, build keepout polygon on each copper layer where the zone
901  // keepout is living (keepout zones can live on many copper layers)
902  const int copperCount = aBoard->GetCopperLayerCount();
903 
904  for( int layer = 0; layer < copperCount; layer++ )
905  {
906  if( layer == copperCount-1 )
907  layer = B_Cu;
908 
909  if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
910  continue;
911 
912  KEEPOUT* keepout = new KEEPOUT( m_pcb->structure, keepout_type );
913  image->keepouts.push_back( keepout );
914 
915  PATH* mainPolygon = new PATH( keepout, T_polygon );
916  keepout->SetShape( mainPolygon );
917 
918  mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
919 
920  // Handle the main outlines
921  SHAPE_POLY_SET::ITERATOR iterator;
922  bool is_first_point = true;
923  wxPoint startpoint;
924 
925  for( iterator = untransformedZone.IterateWithHoles(); iterator; iterator++ )
926  {
927  wxPoint point( iterator->x, iterator->y );
928 
929  point -= aFootprint->GetPosition();
930 
931  if( is_first_point )
932  {
933  startpoint = point;
934  is_first_point = false;
935  }
936 
937  mainPolygon->AppendPoint( mapPt( point ) );
938 
939  // this was the end of the main polygon
940  if( iterator.IsEndContour() )
941  {
942  mainPolygon->AppendPoint( mapPt( startpoint ) );
943  break;
944  }
945  }
946 
947  WINDOW* window = nullptr;
948  PATH* cutout = nullptr;
949  bool isStartContour = true;
950 
951  // handle the cutouts
952  for( iterator++; iterator; iterator++ )
953  {
954  if( isStartContour )
955  {
956  is_first_point = true;
957  window = new WINDOW( keepout );
958  keepout->AddWindow( window );
959 
960  cutout = new PATH( window, T_polygon );
961 
962  window->SetShape( cutout );
963 
964  cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ zone->GetLayer() ] ];
965  }
966 
967  isStartContour = iterator.IsEndContour();
968 
969  wxASSERT( window );
970  wxASSERT( cutout );
971 
972  wxPoint point( iterator->x, iterator->y );
973 
974  point -= aFootprint->GetPosition();
975 
976  if( is_first_point )
977  {
978  startpoint = point;
979  is_first_point = false;
980  }
981 
982  cutout->AppendPoint( mapPt( point ) );
983 
984  // Close the polygon
985  if( iterator.IsEndContour() )
986  cutout->AppendPoint( mapPt( startpoint ) );
987  }
988  }
989  }
990 
991  return image;
992 }
993 
994 
995 PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
996  int aTopLayer, int aBotLayer )
997 {
998  char name[48];
999  PADSTACK* padstack = new PADSTACK();
1000  double dsnDiameter = scale( aCopperDiameter );
1001 
1002  for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
1003  {
1004  SHAPE* shape = new SHAPE( padstack );
1005 
1006  padstack->Append( shape );
1007 
1008  CIRCLE* circle = new CIRCLE( shape );
1009 
1010  shape->SetShape( circle );
1011 
1012  circle->SetDiameter( dsnDiameter );
1013  circle->SetLayerId( m_layerIds[layer].c_str() );
1014  }
1015 
1016  snprintf( name, sizeof( name ), "Via[%d-%d]_%.6g:%.6g_um",
1017  aTopLayer, aBotLayer, dsnDiameter,
1018  // encode the drill value into the name for later import
1019  IU2um( aDrillDiameter ) );
1020 
1021  name[ sizeof(name) - 1 ] = 0;
1022  padstack->SetPadstackId( name );
1023 
1024  return padstack;
1025 }
1026 
1027 
1028 PADSTACK* SPECCTRA_DB::makeVia( const PCB_VIA* aVia )
1029 {
1030  PCB_LAYER_ID topLayerNum;
1031  PCB_LAYER_ID botLayerNum;
1032 
1033  aVia->LayerPair( &topLayerNum, &botLayerNum );
1034 
1035  int topLayer = m_kicadLayer2pcb[topLayerNum];
1036  int botLayer = m_kicadLayer2pcb[botLayerNum];
1037 
1038  if( topLayer > botLayer )
1039  std::swap( topLayer, botLayer );
1040 
1041  return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
1042 }
1043 
1044 
1045 void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
1046 {
1047  for( int cnt = 0; cnt < m_brd_outlines.OutlineCount(); cnt++ ) // Should be one outline
1048  {
1049  PATH* path = new PATH( boundary );
1050  boundary->paths.push_back( path );
1051  path->layer_id = "pcb";
1052 
1053  SHAPE_LINE_CHAIN& outline = m_brd_outlines.Outline( cnt );
1054 
1055  for( int ii = 0; ii < outline.PointCount(); ii++ )
1056  {
1057  wxPoint pos( outline.CPoint( ii ).x, outline.CPoint( ii ).y );
1058  path->AppendPoint( mapPt( pos ) );
1059  }
1060 
1061  // Close polygon:
1062  wxPoint pos0( outline.CPoint( 0 ).x, outline.CPoint( 0 ).y );
1063  path->AppendPoint( mapPt( pos0 ) );
1064 
1065  // Generate holes as keepout:
1066  for( int ii = 0; ii < m_brd_outlines.HoleCount( cnt ); ii++ )
1067  {
1068  // emit a signal layers keepout for every interior polygon left...
1069  KEEPOUT* keepout = new KEEPOUT( nullptr, T_keepout );
1070  PATH* poly_ko = new PATH( nullptr, T_polygon );
1071 
1072  keepout->SetShape( poly_ko );
1073  poly_ko->SetLayerId( "signal" );
1074  m_pcb->structure->keepouts.push_back( keepout );
1075 
1076  SHAPE_LINE_CHAIN& hole = m_brd_outlines.Hole( cnt, ii );
1077 
1078  for( int jj = 0; jj < hole.PointCount(); jj++ )
1079  {
1080  wxPoint pos( hole.CPoint( jj ).x, hole.CPoint( jj ).y );
1081  poly_ko->AppendPoint( mapPt( pos ) );
1082  }
1083 
1084  // Close polygon:
1085  wxPoint pos( hole.CPoint( 0 ).x, hole.CPoint( 0 ).y );
1086  poly_ko->AppendPoint( mapPt( pos ) );
1087  }
1088  }
1089 }
1090 
1091 
1092 typedef std::set<std::string> STRINGSET;
1093 typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
1094 
1095 
1097 {
1098  PCB_TYPE_COLLECTOR items;
1099 
1100  static const KICAD_T scanMODULEs[] = { PCB_FOOTPRINT_T, EOT };
1101 
1102  // Not all boards are exportable. Check that all reference Ids are unique.
1103  // Unless they are unique, we cannot import the session file which comes
1104  // back to us later from the router.
1105  {
1106  items.Collect( aBoard, scanMODULEs );
1107 
1108  STRINGSET refs; // holds footprint reference designators
1109 
1110  for( int i=0; i<items.GetCount(); ++i )
1111  {
1112  FOOTPRINT* footprint = (FOOTPRINT*) items[i];
1113 
1114  if( footprint->GetReference() == wxEmptyString )
1115  {
1117  _( "Symbol with value of '%s' has empty reference id." ),
1118  footprint->GetValue() ) );
1119  }
1120 
1121  // if we cannot insert OK, that means the reference has been seen before.
1122  STRINGSET_PAIR refpair = refs.insert( TO_UTF8( footprint->GetReference() ) );
1123 
1124  if( !refpair.second ) // insert failed
1125  {
1127  _( "Multiple symbols have identical reference IDs of '%s'." ),
1128  footprint->GetReference() ) );
1129  }
1130  }
1131  }
1132 
1133  if( !m_pcb )
1135 
1136  //-----<layer_descriptor>-----------------------------------------------
1137  {
1138  // specctra wants top physical layer first, then going down to the
1139  // bottom most physical layer in physical sequence.
1140  // @question : why does KiCad not display layers in that order?
1141 
1142  buildLayerMaps( aBoard );
1143 
1144  int layerCount = aBoard->GetCopperLayerCount();
1145 
1146  for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
1147  {
1148  LAYER* layer = new LAYER( m_pcb->structure );
1149 
1150  m_pcb->structure->layers.push_back( layer );
1151 
1152  layer->name = m_layerIds[pcbNdx];
1153 
1154  DSN_T layerType;
1155 
1156  switch( aBoard->GetLayerType( m_pcbLayer2kicad[pcbNdx] ) )
1157  {
1158  default:
1159  case LT_SIGNAL: layerType = T_signal; break;
1160  case LT_POWER: layerType = T_power; break;
1161 
1162  // Freerouter does not support type "mixed", only signal and power.
1163  // Remap "mixed" to "signal".
1164  case LT_MIXED: layerType = T_signal; break;
1165  case LT_JUMPER: layerType = T_jumper; break;
1166  }
1167 
1168  layer->layer_type = layerType;
1169 
1170  layer->properties.push_back( PROPERTY() );
1171  PROPERTY* property = &layer->properties.back();
1172  property->name = "index";
1173  char temp[32];
1174  sprintf( temp, "%d", pcbNdx );
1175  property->value = temp;
1176  }
1177  }
1178 
1179  // a space in a quoted token is NOT a terminator, true establishes this.
1181 
1182  //-----<unit_descriptor> & <resolution_descriptor>--------------------
1183  {
1184  // tell freerouter to use "tenths of micrometers",
1185  // which is 100 nm resolution. Possibly more resolution is possible
1186  // in freerouter, but it would need testing.
1187 
1188  m_pcb->unit->units = T_um;
1189  m_pcb->resolution->units = T_um;
1190  m_pcb->resolution->value = 10; // tenths of a um
1191  }
1192 
1193  //-----<boundary_descriptor>------------------------------------------
1194  {
1195  // Because fillBOUNDARY() can throw an exception, we link in an
1196  // empty boundary so the BOUNDARY does not get lost in the event of
1197  // of an exception.
1198  BOUNDARY* boundary = new BOUNDARY( 0 );
1199 
1200  m_pcb->structure->SetBOUNDARY( boundary );
1201  fillBOUNDARY( aBoard, boundary );
1202  }
1203 
1204  //-----<rules>--------------------------------------------------------
1205  {
1206  char rule[80];
1207  NETCLASS* defaultClass = aBoard->GetDesignSettings().GetDefault();
1208 
1209  int defaultTrackWidth = defaultClass->GetTrackWidth();
1210  int defaultClearance = defaultClass->GetClearance();
1211 
1212  double clearance = scale( defaultClearance );
1213 
1214  STRINGS& rules = m_pcb->structure->rules->rules;
1215 
1216  sprintf( rule, "(width %.6g)", scale( defaultTrackWidth ) );
1217  rules.push_back( rule );
1218 
1219  sprintf( rule, "(clearance %.6g)", clearance + safetyMargin );
1220  rules.push_back( rule );
1221 
1222  // On a high density board (a board with 4 mil tracks, 4 mil spacing)
1223  // a typical solder mask clearance will be 2-3 mils.
1224  // This exposes 2 to 3 mils of bare board around each pad, and would
1225  // leave only 1 to 2 mils of solder mask between the solder mask's boundary
1226  // to the edge of any trace within "clearance" of the pad. So we need at least
1227  // 2 mils *extra* clearance for traces which would come near a pad on
1228  // a different net. So if the baseline trace to trace clearance was say 4 mils, then
1229  // the SMD to trace clearance should be at least 6 mils.
1230  double default_smd = clearance + safetyMargin;
1231 
1232  if( default_smd <= 6.0 )
1233  default_smd = 6.0;
1234 
1235  sprintf( rule, "(clearance %.6g (type default_smd))", default_smd );
1236 
1237  rules.push_back( rule );
1238 
1239  // Pad to pad spacing on a single SMT part can be closer than our
1240  // clearance, we don't want freerouter complaining about that, so
1241  // output a significantly smaller pad to pad clearance to freerouter.
1242  clearance = scale( defaultClearance ) / 4;
1243 
1244  sprintf( rule, "(clearance %.6g (type smd_smd))", clearance );
1245  rules.push_back( rule );
1246  }
1247 
1248  //-----<zones (not keepout areas) become planes>--------------------------------
1249  // Note: only zones are output here, keepout areas are created later.
1250  {
1251  int netlessZones = 0;
1252 
1253  static const KICAD_T scanZONEs[] = { PCB_ZONE_T, EOT };
1254  items.Collect( aBoard, scanZONEs );
1255 
1256  for( int i = 0; i < items.GetCount(); ++i )
1257  {
1258  ZONE* item = (ZONE*) items[i];
1259 
1260  if( item->GetIsRuleArea() )
1261  continue;
1262 
1263  // Currently, we export only copper layers
1264  if( ! IsCopperLayer( item->GetLayer() ) )
1265  continue;
1266 
1267  COPPER_PLANE* plane = new COPPER_PLANE( m_pcb->structure );
1268 
1269  m_pcb->structure->planes.push_back( plane );
1270 
1271  PATH* mainPolygon = new PATH( plane, T_polygon );
1272 
1273  plane->SetShape( mainPolygon );
1274 
1275  plane->name = TO_UTF8( item->GetNetname() );
1276 
1277  if( plane->name.size() == 0 )
1278  {
1279  char name[32];
1280 
1281  // This is one of those no connection zones, netcode=0, and it has no name.
1282  // Create a unique, bogus netname.
1283  NET* no_net = new NET( m_pcb->network );
1284 
1285  sprintf( name, "@:no_net_%d", netlessZones++ );
1286  no_net->net_id = name;
1287 
1288  // add the bogus net name to network->nets.
1289  m_pcb->network->nets.push_back( no_net );
1290 
1291  // use the bogus net name in the netless zone.
1292  plane->name = no_net->net_id;
1293  }
1294 
1295  mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ item->GetLayer() ] ];
1296 
1297  // Handle the main outlines
1298  SHAPE_POLY_SET::ITERATOR iterator;
1299  wxPoint startpoint;
1300  bool is_first_point = true;
1301 
1302  for( iterator = item->IterateWithHoles(); iterator; iterator++ )
1303  {
1304  wxPoint point( iterator->x, iterator->y );
1305 
1306  if( is_first_point )
1307  {
1308  startpoint = point;
1309  is_first_point = false;
1310  }
1311 
1312  mainPolygon->AppendPoint( mapPt( point ) );
1313 
1314  // this was the end of the main polygon
1315  if( iterator.IsEndContour() )
1316  {
1317  // Close polygon
1318  mainPolygon->AppendPoint( mapPt( startpoint ) );
1319  break;
1320  }
1321  }
1322 
1323  WINDOW* window = 0;
1324  PATH* cutout = 0;
1325 
1326  bool isStartContour = true;
1327 
1328  // handle the cutouts
1329  for( iterator++; iterator; iterator++ )
1330  {
1331  if( isStartContour )
1332  {
1333  is_first_point = true;
1334  window = new WINDOW( plane );
1335 
1336  plane->AddWindow( window );
1337 
1338  cutout = new PATH( window, T_polygon );
1339 
1340  window->SetShape( cutout );
1341 
1342  cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ item->GetLayer() ] ];
1343  }
1344 
1345  // If the point in this iteration is the last of the contour, the next iteration
1346  // will start with a new contour.
1347  isStartContour = iterator.IsEndContour();
1348 
1349  wxASSERT( window );
1350  wxASSERT( cutout );
1351 
1352  wxPoint point(iterator->x, iterator->y );
1353 
1354  if( is_first_point )
1355  {
1356  startpoint = point;
1357  is_first_point = false;
1358  }
1359 
1360  cutout->AppendPoint( mapPt( point ) );
1361 
1362  // Close the polygon
1363  if( iterator.IsEndContour() )
1364  cutout->AppendPoint( mapPt( startpoint ) );
1365  }
1366  }
1367  }
1368 
1369  //-----<zones flagged keepout areas become keepout>--------------------------------
1370  {
1371  static const KICAD_T scanZONEs[] = { PCB_ZONE_T, EOT };
1372  items.Collect( aBoard, scanZONEs );
1373 
1374  for( int i = 0; i < items.GetCount(); ++i )
1375  {
1376  ZONE* item = (ZONE*) items[i];
1377 
1378  if( !item->GetIsRuleArea() )
1379  continue;
1380 
1381  // keepout areas have a type. types are
1382  // T_place_keepout, T_via_keepout, T_wire_keepout,
1383  // T_bend_keepout, T_elongate_keepout, T_keepout.
1384  // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
1385  DSN_T keepout_type;
1386 
1387  if( item->GetDoNotAllowVias() && item->GetDoNotAllowTracks() )
1388  keepout_type = T_keepout;
1389  else if( item->GetDoNotAllowVias() )
1390  keepout_type = T_via_keepout;
1391  else if( item->GetDoNotAllowTracks() )
1392  keepout_type = T_wire_keepout;
1393  else
1394  keepout_type = T_keepout;
1395 
1396  // Now, build keepout polygon on each copper layer where the item
1397  // keepout is living (keepout zones can live on many copper layers)
1398  const int copperCount = aBoard->GetCopperLayerCount();
1399 
1400  for( int layer = 0; layer < copperCount; layer++ )
1401  {
1402  if( layer == copperCount - 1 )
1403  layer = B_Cu;
1404 
1405  if( !item->IsOnLayer( PCB_LAYER_ID( layer ) ) )
1406  continue;
1407 
1408  KEEPOUT* keepout = new KEEPOUT( m_pcb->structure, keepout_type );
1409  m_pcb->structure->keepouts.push_back( keepout );
1410 
1411  PATH* mainPolygon = new PATH( keepout, T_polygon );
1412  keepout->SetShape( mainPolygon );
1413 
1414  mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1415 
1416  // Handle the main outlines
1417  SHAPE_POLY_SET::ITERATOR iterator;
1418  bool is_first_point = true;
1419  wxPoint startpoint;
1420 
1421  for( iterator = item->IterateWithHoles(); iterator; iterator++ )
1422  {
1423  wxPoint point( iterator->x, iterator->y );
1424 
1425  if( is_first_point )
1426  {
1427  startpoint = point;
1428  is_first_point = false;
1429  }
1430 
1431  mainPolygon->AppendPoint( mapPt( point ) );
1432 
1433  // this was the end of the main polygon
1434  if( iterator.IsEndContour() )
1435  {
1436  mainPolygon->AppendPoint( mapPt( startpoint ) );
1437  break;
1438  }
1439  }
1440 
1441  WINDOW* window = nullptr;
1442  PATH* cutout = nullptr;
1443 
1444  bool isStartContour = true;
1445 
1446  // handle the cutouts
1447  for( iterator++; iterator; iterator++ )
1448  {
1449  if( isStartContour )
1450  {
1451  is_first_point = true;
1452  window = new WINDOW( keepout );
1453  keepout->AddWindow( window );
1454 
1455  cutout = new PATH( window, T_polygon );
1456 
1457  window->SetShape( cutout );
1458 
1459  cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ item->GetLayer() ] ];
1460  }
1461 
1462  isStartContour = iterator.IsEndContour();
1463 
1464  wxASSERT( window );
1465  wxASSERT( cutout );
1466 
1467  wxPoint point(iterator->x, iterator->y );
1468 
1469  if( is_first_point )
1470  {
1471  startpoint = point;
1472  is_first_point = false;
1473  }
1474 
1475  cutout->AppendPoint( mapPt(point) );
1476 
1477  // Close the polygon
1478  if( iterator.IsEndContour() )
1479  cutout->AppendPoint( mapPt( startpoint ) );
1480  }
1481  }
1482  }
1483  }
1484 
1485  //-----<build the images, components, and netlist>-----------------------
1486  {
1487  PIN_REF empty( m_pcb->network );
1488 
1489  std::string componentId;
1490 
1491  // find the highest numbered netCode within the board.
1492  int highestNetCode = 0;
1493  NETINFO_LIST& netInfo = aBoard->GetNetInfo();
1494 
1495  for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1496  highestNetCode = std::max( highestNetCode, i->GetNetCode() );
1497 
1498  deleteNETs();
1499 
1500  // expand the net vector to highestNetCode+1, setting empty to NULL
1501  m_nets.resize( highestNetCode + 1, nullptr );
1502 
1503  for( unsigned i = 1 /* skip "No Net" at [0] */; i < m_nets.size(); ++i )
1504  m_nets[i] = new NET( m_pcb->network );
1505 
1506  for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1507  {
1508  if( i->GetNetCode() > 0 )
1509  m_nets[i->GetNetCode()]->net_id = TO_UTF8( i->GetNetname() );
1510  }
1511 
1512  items.Collect( aBoard, scanMODULEs );
1513 
1514  m_padstackset.clear();
1515 
1516  for( int m = 0; m < items.GetCount(); ++m )
1517  {
1518  FOOTPRINT* footprint = (FOOTPRINT*) items[m];
1519 
1520  IMAGE* image = makeIMAGE( aBoard, footprint );
1521 
1522  componentId = TO_UTF8( footprint->GetReference() );
1523 
1524  // create a net list entry for all the actual pins in the image
1525  // for the current footprint. location of this code is critical
1526  // because we fabricated some pin names to ensure unique-ness
1527  // of pin names within a footprint, do not move this code because
1528  // the life of this 'IMAGE* image' is not necessarily long. The
1529  // exported netlist will have some fabricated pin names in it.
1530  // If you don't like fabricated pin names, then make sure all pads
1531  // within your FOOTPRINTs are uniquely named!
1532  for( unsigned p = 0; p < image->pins.size(); ++p )
1533  {
1534  PIN* pin = &image->pins[p];
1535 
1536  int netcode = pin->kiNetCode;
1537 
1538  if( netcode > 0 )
1539  {
1540  NET* net = m_nets[netcode];
1541 
1542  net->pins.push_back( empty );
1543 
1544  PIN_REF& pin_ref = net->pins.back();
1545 
1546  pin_ref.component_id = componentId;
1547  pin_ref.pin_id = pin->pin_id;
1548  }
1549  }
1550 
1552 
1553  if( registered != image )
1554  {
1555  // If our new 'image' is not a unique IMAGE, delete it.
1556  // and use the registered one, known as 'image' after this.
1557  delete image;
1558  image = registered;
1559  }
1560 
1561  COMPONENT* comp = m_pcb->placement->LookupCOMPONENT( image->GetImageId() );
1562 
1563  PLACE* place = new PLACE( comp );
1564 
1565  comp->places.push_back( place );
1566 
1567  place->SetRotation( footprint->GetOrientationDegrees() );
1568  place->SetVertex( mapPt( footprint->GetPosition() ) );
1569  place->component_id = componentId;
1570  place->part_number = TO_UTF8( footprint->GetValue() );
1571 
1572  // footprint is flipped from bottom side, set side to T_back
1573  if( footprint->GetFlag() )
1574  {
1575  double angle = 180.0 - footprint->GetOrientationDegrees();
1577  place->SetRotation( angle );
1578 
1579  place->side = T_back;
1580  }
1581  }
1582 
1583  // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
1584  // removing, do not increment the iterator
1585  for( PADSTACKSET::iterator i = m_padstackset.begin(); i != m_padstackset.end();
1586  i = m_padstackset.begin() )
1587  {
1588  PADSTACKSET::auto_type ps = m_padstackset.release( i );
1589  PADSTACK* padstack = ps.release();
1590 
1591  m_pcb->library->AddPadstack( padstack );
1592  }
1593 
1594  // copy our SPECCTRA_DB::nets to the pcb->network
1595  for( unsigned n = 1; n < m_nets.size(); ++n )
1596  {
1597  NET* net = m_nets[n];
1598 
1599  if( net->pins.size() )
1600  {
1601  // give ownership to pcb->network
1602  m_pcb->network->nets.push_back( net );
1603  m_nets[n] = 0;
1604  }
1605  }
1606  }
1607 
1608  //-----< output vias used in netclasses >-----------------------------------
1609  {
1610  NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses();
1611 
1612  // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
1613  // This is in lieu of either having each netclass via have its own layer pair in
1614  // the netclass dialog, or such control in the specctra export dialog.
1615 
1616 
1617  m_top_via_layer = 0; // first specctra cu layer is number zero.
1618  m_bot_via_layer = aBoard->GetCopperLayerCount()-1;
1619 
1620  // Add the via from the Default netclass first. The via container
1621  // in pcb->library preserves the sequence of addition.
1622 
1623  NETCLASSPTR netclass = nclasses.GetDefault();
1624 
1625  PADSTACK* via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1627 
1628  // we AppendVia() this first one, there is no way it can be a duplicate,
1629  // the pcb->library via container is empty at this point. After this,
1630  // we'll have to use LookupVia().
1631  wxASSERT( m_pcb->library->vias.size() == 0 );
1632  m_pcb->library->AppendVia( via );
1633 
1634  // set the "spare via" index at the start of the
1635  // pcb->library->spareViaIndex = pcb->library->vias.size();
1636 
1637  // output the non-Default netclass vias
1638  for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
1639  {
1640  netclass = nc->second;
1641 
1642  via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1644 
1645  // maybe add 'via' to the library, but only if unique.
1647 
1648  if( registered != via )
1649  delete via;
1650  }
1651  }
1652 
1653  //-----<create the wires from tracks>-----------------------------------
1654  {
1655  // export all of them for now, later we'll decide what controls we need
1656  // on this.
1657  static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
1658 
1659  items.Collect( aBoard, scanTRACKs );
1660 
1661  std::string netname;
1662  WIRING* wiring = m_pcb->wiring;
1663  PATH* path = 0;
1664 
1665  int old_netcode = -1;
1666  int old_width = -1;
1667  LAYER_NUM old_layer = UNDEFINED_LAYER;
1668 
1669  for( int i = 0; i < items.GetCount(); ++i )
1670  {
1671  PCB_TRACK* track = static_cast<PCB_TRACK*>( items[i] );
1672  int netcode = track->GetNetCode();
1673 
1674  if( netcode == 0 )
1675  continue;
1676 
1677  if( old_netcode != netcode || old_width != track->GetWidth() ||
1678  old_layer != track->GetLayer() ||
1679  ( path && path->points.back() != mapPt(track->GetStart() ) ) )
1680  {
1681  old_width = track->GetWidth();
1682  old_layer = track->GetLayer();
1683 
1684  if( old_netcode != netcode )
1685  {
1686  old_netcode = netcode;
1687  NETINFO_ITEM* net = aBoard->FindNet( netcode );
1688  wxASSERT( net );
1689  netname = TO_UTF8( net->GetNetname() );
1690  }
1691 
1692  WIRE* wire = new WIRE( wiring );
1693 
1694  wiring->wires.push_back( wire );
1695  wire->net_id = netname;
1696 
1697  if( track->IsLocked() )
1698  wire->wire_type = T_fix; // tracks with fix property are not returnned in .ses files
1699  else
1700  wire->wire_type = T_route; // could be T_protect
1701 
1702  LAYER_NUM kiLayer = track->GetLayer();
1703  int pcbLayer = m_kicadLayer2pcb[kiLayer];
1704 
1705  path = new PATH( wire );
1706 
1707  wire->SetShape( path );
1708 
1709  path->layer_id = m_layerIds[pcbLayer];
1710  path->aperture_width = scale( old_width );
1711 
1712  path->AppendPoint( mapPt( track->GetStart() ) );
1713  }
1714 
1715  if( path ) // Should not occur
1716  path->AppendPoint( mapPt( track->GetEnd() ) );
1717  }
1718  }
1719 
1720  //-----<export the existing real BOARD instantiated vias>-----------------
1721  {
1722  // Export all vias, once per unique size and drill diameter combo.
1723  static const KICAD_T scanVIAs[] = { PCB_VIA_T, EOT };
1724 
1725  items.Collect( aBoard, scanVIAs );
1726 
1727  for( int i = 0; i<items.GetCount(); ++i )
1728  {
1729  PCB_VIA* via = static_cast<PCB_VIA*>( items[i] );
1730  wxASSERT( via->Type() == PCB_VIA_T );
1731 
1732  int netcode = via->GetNetCode();
1733 
1734  if( netcode == 0 )
1735  continue;
1736 
1737  PADSTACK* padstack = makeVia( via );
1738  PADSTACK* registered = m_pcb->library->LookupVia( padstack );
1739 
1740  // if the one looked up is not our padstack, then delete our padstack
1741  // since it was a duplicate of one already registered.
1742  if( padstack != registered )
1743  {
1744  delete padstack;
1745  }
1746 
1747  WIRE_VIA* dsnVia = new WIRE_VIA( m_pcb->wiring );
1748 
1749  m_pcb->wiring->wire_vias.push_back( dsnVia );
1750 
1751  dsnVia->padstack_id = registered->padstack_id;
1752  dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) );
1753 
1754  NETINFO_ITEM* net = aBoard->FindNet( netcode );
1755  wxASSERT( net );
1756 
1757  dsnVia->net_id = TO_UTF8( net->GetNetname() );
1758 
1759  if( via->IsLocked() )
1760  dsnVia->via_type = T_fix; // vias with fix property are not returnned in .ses files
1761  else
1762  dsnVia->via_type = T_route; // could be T_protect
1763  }
1764  }
1765 
1766  //-----<via_descriptor>-------------------------------------------------
1767  {
1768  // The pcb->library will output <padstack_descriptors> which is a combined
1769  // list of part padstacks and via padstacks. specctra dsn uses the
1770  // <via_descriptors> to say which of those padstacks are vias.
1771 
1772  // Output the vias in the padstack list here, by name only. This must
1773  // be done after exporting existing vias as WIRE_VIAs.
1774  VIA* vias = m_pcb->structure->via;
1775 
1776  for( unsigned viaNdx = 0; viaNdx < m_pcb->library->vias.size(); ++viaNdx )
1777  {
1778  vias->AppendVia( m_pcb->library->vias[viaNdx].padstack_id.c_str() );
1779  }
1780  }
1781 
1782  //-----<output NETCLASSs>----------------------------------------------------
1783  NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses();
1784 
1785  exportNETCLASS( nclasses.GetDefault(), aBoard );
1786 
1787  for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
1788  {
1789  NETCLASSPTR netclass = nc->second;
1790  exportNETCLASS( netclass, aBoard );
1791  }
1792 }
1793 
1794 
1795 void SPECCTRA_DB::exportNETCLASS( const NETCLASSPTR& aNetClass, BOARD* aBoard )
1796 {
1797  /* From page 11 of specctra spec:
1798  *
1799  * Routing and Placement Rule Hierarchies
1800  *
1801  * Routing and placement rules can be defined at multiple levels of design
1802  * specification. When a routing or placement rule is defined for an object at
1803  * multiple levels, a predefined routing or placement precedence order
1804  * automatically determines which rule to apply to the object. The routing rule
1805  * precedence order is
1806  *
1807  * pcb < layer < class < class layer < group_set < group_set layer < net <
1808  * net layer < group < group layer < fromto < fromto layer < class_class <
1809  * class_class layer < padstack < region < class region < net region <
1810  * class_class region
1811  *
1812  * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1813  * hierarchy. A class-to-class region rule has the highest precedence. Rules
1814  * set at one level of the hierarchy override conflicting rules set at lower
1815  * levels. The placement rule precedence order is
1816  *
1817  * pcb < image_set < image < component < super cluster < room <
1818  * room_image_set < family_family < image_image
1819  *
1820  * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1821  * hierarchy. An image-to-image rule has the highest precedence. Rules set at
1822  * one level of the hierarchy override conflicting rules set at lower levels.
1823  */
1824 
1825  char text[256];
1826 
1827  CLASS* clazz = new CLASS( m_pcb->network );
1828 
1829  m_pcb->network->classes.push_back( clazz );
1830 
1831  // freerouter creates a class named 'default' anyway, and if we
1832  // try and use that, we end up with two 'default' via rules so use
1833  // something else as the name of our default class.
1834  clazz->class_id = TO_UTF8( aNetClass->GetName() );
1835 
1836  for( NETCLASS::iterator net = aNetClass->begin(); net != aNetClass->end(); ++net )
1837  clazz->net_ids.push_back( TO_UTF8( *net ) );
1838 
1839  clazz->rules = new RULE( clazz, T_rule );
1840 
1841  // output the track width.
1842  int trackWidth = aNetClass->GetTrackWidth();
1843  sprintf( text, "(width %.6g)", scale( trackWidth ) );
1844  clazz->rules->rules.push_back( text );
1845 
1846  // output the clearance.
1847  int clearance = aNetClass->GetClearance();
1848  sprintf( text, "(clearance %.6g)", scale( clearance ) + safetyMargin );
1849  clazz->rules->rules.push_back( text );
1850 
1851  if( aNetClass->GetName() == NETCLASS::Default )
1852  {
1853  clazz->class_id = "kicad_default";
1854  }
1855 
1856  // the easiest way to get the via name is to create a via (which generates
1857  // the name internal to the PADSTACK), and then grab the name and then
1858  // delete the via. There are not that many netclasses so
1859  // this should never become a performance issue.
1860 
1861  PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
1863 
1864  snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
1865  clazz->circuit.push_back( text );
1866 
1867  delete via;
1868 }
1869 
1870 
1872 {
1873  // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1874  // top view.
1875  // Note: to export footprints, the footprints must be flipped around the X axis,
1876  // otherwise the rotation angle is not good
1877  for( FOOTPRINT* footprint : aBoard->Footprints() )
1878  {
1879  footprint->SetFlag( 0 );
1880 
1881  if( footprint->GetLayer() == B_Cu )
1882  {
1883  footprint->Flip( footprint->GetPosition(), false );
1884  footprint->SetFlag( 1 );
1885  }
1886  }
1887 
1888  m_footprintsAreFlipped = true;
1889 }
1890 
1891 
1893 {
1894  if( !m_footprintsAreFlipped )
1895  return;
1896 
1897  // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1898  // top view. Restore those that were flipped.
1899  // Note: to export footprints, the footprints were flipped around the X axis,
1900  for( FOOTPRINT* footprint : aBoard->Footprints() )
1901  {
1902  if( footprint->GetFlag() )
1903  {
1904  footprint->Flip( footprint->GetPosition(), false );
1905  footprint->SetFlag( 0 );
1906  }
1907  }
1908 
1909  m_footprintsAreFlipped = false;
1910 }
1911 
1912 } // 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:759
iterator end() const
Definition: netinfo.h:448
A <via_descriptor> in the specctra dsn spec.
Definition: specctra.h:1038
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:1328
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
int GetWidth() const
Definition: eda_shape.h:98
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:545
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:1835
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:488
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:292
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:735
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
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:106
Definition: board.h:73
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:737
std::string part_number
Definition: specctra.h:1758
PADSTACKS vias
Definition: specctra.h:2448
static POINT mapPt(const wxPoint &pt)
Convert a KiCad point into a DSN file point.
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:118
void SetLayerId(const char *aLayerId)
Definition: specctra.h:600
void SetLayerId(const char *aLayerId)
Definition: specctra.h:757
double GetOrientation() const
Definition: footprint.h:191
static bool registered
Definition: coroutines.cpp:128
POINTS vertexes
Definition: specctra.h:3120
double GetArcAngle() const
Definition: eda_shape.cpp:536
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:568
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:41
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:314
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:680
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:622
WIRING * wiring
Definition: specctra.h:3262
virtual bool IsLocked() const
Definition: board_item.cpp:64
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:590
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
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:74
Support both the <path_descriptor> and the <polygon_descriptor> per the specctra dsn spec.
Definition: specctra.h:582
wxPoint GetCenter0() const
Definition: fp_shape.cpp:145
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:689
#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:82
void SynchronizeNetsAndNetClasses()
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:1394
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:175
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:244
STRINGSET::iterator iterator
Definition: netclass.h:94
int GetTrackWidth() const
Definition: netclass.h:128
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:131
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:516
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:147
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:322
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:222
Handles how to draw a screen (a board, a schematic ...)
Definition: base_screen.h:40
iterator begin() const
Definition: netinfo.h:443
FOOTPRINTS & Footprints()
Definition: board.h:234
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.h:79
bool GetDoNotAllowTracks() const
Definition: zone.h:738
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:126
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:569
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:434
PAD_SHAPE GetShape() const
Definition: pad.h:170
const wxString & GetReference() const
Definition: footprint.h:466
#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:195
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
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:119
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:808
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:269
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:396
double GetOrientationDegrees() const
Definition: footprint.h:192
Handle the data for a net.
Definition: netinfo.h:66
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:533
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:191
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
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:97
int GetCopperLayerCount() const
Definition: board.cpp:455
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:187
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:543
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:166
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
SHAPE_T GetShape() const
Definition: eda_shape.h:101
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:183
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
int GetRadius() const
Definition: eda_shape.cpp:472
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:143
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:947
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