KiCad PCB EDA Suite
eagle_plugin.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) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2012-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 /*
27 
28 Pcbnew PLUGIN for Eagle 6.x XML *.brd and footprint format.
29 
30 XML parsing and converting:
31 Getting line numbers and byte offsets from the source XML file is not
32 possible using currently available XML libraries within KiCad project:
33 wxXmlDocument and boost::property_tree.
34 
35 property_tree will give line numbers but no byte offsets, and only during
36 document loading. This means that if we have a problem after the document is
37 successfully loaded, there is no way to correlate back to line number and byte
38 offset of the problem. So a different approach is taken, one which relies on the
39 XML elements themselves using an XPATH type of reporting mechanism. The path to
40 the problem is reported in the error messages. This means keeping track of that
41 path as we traverse the XML document for the sole purpose of accurate error
42 reporting.
43 
44 User can load the source XML file into firefox or other xml browser and follow
45 our error message.
46 
47 Load() TODO's
48 
49 *) verify zone fill clearances are correct
50 
51 */
52 
53 #include <cerrno>
54 
55 #include <wx/string.h>
56 #include <wx/xml/xml.h>
57 #include <wx/filename.h>
58 #include <wx/log.h>
59 #include <wx/wfstream.h>
60 
62 #include <core/arraydim.h>
64 #include <string_utils.h>
65 #include <locale_io.h>
66 #include <macros.h>
67 #include <properties.h>
68 #include <trigo.h>
69 #include <math/util.h> // for KiROUND
70 #include <progress_reporter.h>
71 
72 #include <board.h>
73 #include <board_design_settings.h>
74 #include <footprint.h>
75 #include <pad.h>
76 #include <pcb_track.h>
77 #include <fp_shape.h>
78 #include <zone.h>
79 #include <pad_shapes.h>
80 #include <pcb_text.h>
81 #include <pcb_dimension.h>
82 
84 
85 using namespace std;
86 
87 
90 static int parseEagle( const wxString& aDistance )
91 {
92  ECOORD::EAGLE_UNIT unit = ( aDistance.npos != aDistance.find( "mil" ) )
93  ? ECOORD::EAGLE_UNIT::EU_MIL : ECOORD::EAGLE_UNIT::EU_MM;
94 
95  ECOORD coord( aDistance, unit );
96 
97  return coord.ToPcbUnits();
98 }
99 
100 
101 // In Eagle one can specify DRC rules where min value > max value,
102 // in such case the max value has the priority
103 template<typename T>
104 static T eagleClamp( T aMin, T aValue, T aMax )
105 {
106  T ret = std::max( aMin, aValue );
107  return std::min( aMax, ret );
108 }
109 
110 
113 static wxString makeKey( const wxString& aFirst, const wxString& aSecond )
114 {
115  wxString key = aFirst + '\x02' + aSecond;
116  return key;
117 }
118 
119 
121 static wxString interpret_text( const wxString& aText )
122 {
123  wxString text;
124  bool sectionOpen = false;
125 
126  for ( wxString::size_type i = 0; i < aText.size(); i++ )
127  {
128  // Interpret escaped characters
129  if ( aText[ i ] == '\\' )
130  {
131  if ( i + 1 != aText.size() )
132  text.Append( aText[ i + 1 ] );
133 
134  i++;
135  continue;
136  }
137 
138  // Escape ~ for KiCAD
139  if( aText[i] == '~' )
140  {
141  text.Append( '~' );
142  text.Append( '~' );
143  continue;
144  }
145 
146  if ( aText[ i ] == '!' )
147  {
148  if ( sectionOpen )
149  {
150  text.Append( '~' );
151  sectionOpen = false;
152  continue;
153  }
154 
155  static wxString escapeChars( " )]}'\"" );
156 
157  if( i + 1 != aText.size() && escapeChars.Find( aText[i + 1] ) == wxNOT_FOUND )
158  {
159  sectionOpen = true;
160  text.Append( '~' );
161  }
162  else
163  {
164  text.Append( aText[ i ] );
165  }
166  continue;
167  }
168 
169  if( aText[i] == ',' && sectionOpen )
170  {
171  text.Append( '~' );
172  sectionOpen = false;
173  }
174 
175  text.Append( aText[ i ] );
176  }
177 
178  return text;
179 }
180 
181 
182 static void setKeepoutSettingsToZone( ZONE* aZone, LAYER_NUM aLayer )
183 {
184  if( aLayer == EAGLE_LAYER::TRESTRICT || aLayer == EAGLE_LAYER::BRESTRICT )
185  {
186  aZone->SetIsRuleArea( true );
187  aZone->SetDoNotAllowVias( true );
188  aZone->SetDoNotAllowTracks( true );
189  aZone->SetDoNotAllowCopperPour( true );
190  aZone->SetDoNotAllowPads( true );
191  aZone->SetDoNotAllowFootprints( false );
192 
193  if( aLayer == EAGLE_LAYER::TRESTRICT ) // front layer keepout
194  aZone->SetLayer( F_Cu );
195  else // bottom layer keepout
196  aZone->SetLayer( B_Cu );
197  }
198  else if( aLayer == EAGLE_LAYER::VRESTRICT )
199  {
200  aZone->SetIsRuleArea( true );
201  aZone->SetDoNotAllowVias( true );
202  aZone->SetDoNotAllowTracks( false );
203  aZone->SetDoNotAllowCopperPour( false );
204  aZone->SetDoNotAllowPads( false );
205  aZone->SetDoNotAllowFootprints( false );
206 
207  aZone->SetLayerSet( LSET::AllCuMask() );
208  }
209 }
210 
211 
212 void ERULES::parse( wxXmlNode* aRules, std::function<void()> aCheckpoint )
213 {
214  wxXmlNode* child = aRules->GetChildren();
215 
216  while( child )
217  {
218  aCheckpoint();
219 
220  if( child->GetName() == "param" )
221  {
222  const wxString& name = child->GetAttribute( "name" );
223  const wxString& value = child->GetAttribute( "value" );
224 
225  if( name == "psElongationLong" )
226  psElongationLong = wxAtoi( value );
227  else if( name == "psElongationOffset" )
228  psElongationOffset = wxAtoi( value );
229  else if( name == "mvStopFrame" )
230  value.ToCDouble( &mvStopFrame );
231  else if( name == "mvCreamFrame" )
232  value.ToCDouble( &mvCreamFrame );
233  else if( name == "mlMinStopFrame" )
234  mlMinStopFrame = parseEagle( value );
235  else if( name == "mlMaxStopFrame" )
236  mlMaxStopFrame = parseEagle( value );
237  else if( name == "mlMinCreamFrame" )
238  mlMinCreamFrame = parseEagle( value );
239  else if( name == "mlMaxCreamFrame" )
240  mlMaxCreamFrame = parseEagle( value );
241  else if( name == "srRoundness" )
242  value.ToCDouble( &srRoundness );
243  else if( name == "srMinRoundness" )
244  srMinRoundness = parseEagle( value );
245  else if( name == "srMaxRoundness" )
246  srMaxRoundness = parseEagle( value );
247  else if( name == "psTop" )
248  psTop = wxAtoi( value );
249  else if( name == "psBottom" )
250  psBottom = wxAtoi( value );
251  else if( name == "psFirst" )
252  psFirst = wxAtoi( value );
253  else if( name == "rvPadTop" )
254  value.ToCDouble( &rvPadTop );
255  else if( name == "rlMinPadTop" )
256  rlMinPadTop = parseEagle( value );
257  else if( name == "rlMaxPadTop" )
258  rlMaxPadTop = parseEagle( value );
259  else if( name == "rvViaOuter" )
260  value.ToCDouble( &rvViaOuter );
261  else if( name == "rlMinViaOuter" )
262  rlMinViaOuter = parseEagle( value );
263  else if( name == "rlMaxViaOuter" )
264  rlMaxViaOuter = parseEagle( value );
265  else if( name == "mdWireWire" )
266  mdWireWire = parseEagle( value );
267  }
268 
269  child = child->GetNext();
270  }
271 }
272 
273 
275  m_rules( new ERULES() ),
276  m_xpath( new XPATH() ),
277  m_progressReporter( nullptr ),
278  m_doneCount( 0 ),
279  m_lastProgressCount( 0 ),
280  m_totalCount( 0 ),
281  m_mod_time( wxDateTime::Now() )
282 {
283  using namespace std::placeholders;
284 
285  init( nullptr );
286  clear_cu_map();
288  this, _1 ) );
289 }
290 
291 
293 {
294  deleteTemplates();
295  delete m_rules;
296  delete m_xpath;
297 }
298 
299 
300 const wxString EAGLE_PLUGIN::PluginName() const
301 {
302  return wxT( "Eagle" );
303 }
304 
305 
306 const wxString EAGLE_PLUGIN::GetFileExtension() const
307 {
308  return wxT( "brd" );
309 }
310 
311 
313 {
314  const unsigned PROGRESS_DELTA = 50;
315 
316  if( m_progressReporter )
317  {
318  if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
319  {
321  / std::max( 1U, m_totalCount ) );
322 
324  THROW_IO_ERROR( ( "Open cancelled by user." ) );
325 
327  }
328  }
329 }
330 
331 
332 wxSize inline EAGLE_PLUGIN::kicad_fontz( const ECOORD& d, int aTextThickness ) const
333 {
334  // Eagle includes stroke thickness in the text size, KiCAD does not
335  int kz = d.ToPcbUnits();
336  return wxSize( kz - aTextThickness, kz - aTextThickness );
337 }
338 
339 
340 BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe,
341  const PROPERTIES* aProperties, PROJECT* aProject,
342  PROGRESS_REPORTER* aProgressReporter )
343 {
344  LOCALE_IO toggle; // toggles on, then off, the C locale.
345  wxXmlNode* doc;
346 
347  init( aProperties );
348 
349  m_board = aAppendToMe ? aAppendToMe : new BOARD();
350  m_progressReporter = aProgressReporter;
351 
352  // Give the filename to the board if it's new
353  if( !aAppendToMe )
354  m_board->SetFileName( aFileName );
355 
356  // delete on exception, if I own m_board, according to aAppendToMe
357  unique_ptr<BOARD> deleter( aAppendToMe ? nullptr : m_board );
358 
359  try
360  {
361  if( m_progressReporter )
362  {
363  m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
364 
366  THROW_IO_ERROR( ( "Open cancelled by user." ) );
367  }
368 
369  wxFileName fn = aFileName;
370 
371  // Load the document
372  wxFFileInputStream stream( fn.GetFullPath() );
373  wxXmlDocument xmlDocument;
374 
375  if( !stream.IsOk() || !xmlDocument.Load( stream ) )
376  {
377  THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ),
378  fn.GetFullPath() ) );
379  }
380 
381  doc = xmlDocument.GetRoot();
382 
383  m_min_trace = INT_MAX;
384  m_min_hole = INT_MAX;
385  m_min_via = INT_MAX;
386  m_min_annulus = INT_MAX;
387 
388  loadAllSections( doc );
389 
390  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
391 
392  if( m_min_trace < designSettings.m_TrackMinWidth )
393  designSettings.m_TrackMinWidth = m_min_trace;
394 
395  if( m_min_via < designSettings.m_ViasMinSize )
396  designSettings.m_ViasMinSize = m_min_via;
397 
398  if( m_min_hole < designSettings.m_MinThroughDrill )
399  designSettings.m_MinThroughDrill = m_min_hole;
400 
401  if( m_min_annulus < designSettings.m_ViasMinAnnularWidth )
402  designSettings.m_ViasMinAnnularWidth = m_min_annulus;
403 
404  if( m_rules->mdWireWire )
405  {
406  NETCLASS* defaultNetclass = designSettings.GetDefault();
407  int clearance = KiROUND( m_rules->mdWireWire );
408 
409  if( clearance < defaultNetclass->GetClearance() )
410  defaultNetclass->SetClearance( clearance );
411  }
412 
413  // should be empty, else missing m_xpath->pop()
414  wxASSERT( m_xpath->Contents().size() == 0 );
415  }
416  catch( const XML_PARSER_ERROR &exc )
417  {
418  wxString errmsg = exc.what();
419 
420  errmsg += "\n@ ";
421  errmsg += m_xpath->Contents();
422 
423  THROW_IO_ERROR( errmsg );
424  }
425 
426  // IO_ERROR exceptions are left uncaught, they pass upwards from here.
427 
428  // Ensure the copper layers count is a multiple of 2
429  // Pcbnew does not like boards with odd layers count
430  // (these boards cannot exist. they actually have a even layers count)
431  int lyrcnt = m_board->GetCopperLayerCount();
432 
433  if( (lyrcnt % 2) != 0 )
434  {
435  lyrcnt++;
436  m_board->SetCopperLayerCount( lyrcnt );
437  }
438 
439  centerBoard();
440 
441  deleter.release();
442  return m_board;
443 }
444 
445 
447 {
448  std::vector<FOOTPRINT*> retval;
449 
450  for( std::pair<wxString, FOOTPRINT*> fp : m_templates )
451  retval.push_back( static_cast<FOOTPRINT*>( fp.second->Clone() ) );
452 
453  return retval;
454 }
455 
456 
457 void EAGLE_PLUGIN::init( const PROPERTIES* aProperties )
458 {
459  m_hole_count = 0;
460  m_min_trace = 0;
461  m_min_hole = 0;
462  m_min_via = 0;
463  m_min_annulus = 0;
464  m_xpath->clear();
465  m_pads_to_nets.clear();
466 
467  m_board = nullptr;
468  m_props = aProperties;
469 
470 
471  delete m_rules;
472  m_rules = new ERULES();
473 }
474 
475 
477 {
478  // All cu layers are invalid until we see them in the <layers> section while
479  // loading either a board or library. See loadLayerDefs().
480  for( unsigned i = 0; i < arrayDim(m_cu_map); ++i )
481  m_cu_map[i] = -1;
482 }
483 
484 
485 void EAGLE_PLUGIN::loadAllSections( wxXmlNode* aDoc )
486 {
487  wxXmlNode* drawing = MapChildren( aDoc )["drawing"];
488  NODE_MAP drawingChildren = MapChildren( drawing );
489 
490  wxXmlNode* board = drawingChildren["board"];
491  NODE_MAP boardChildren = MapChildren( board );
492 
493  auto count_children = [this]( wxXmlNode* aNode )
494  {
495  if( aNode )
496  {
497  wxXmlNode* child = aNode->GetChildren();
498 
499  while( child )
500  {
501  m_totalCount++;
502  child = child->GetNext();
503  }
504  }
505  };
506 
507  wxXmlNode* designrules = boardChildren["designrules"];
508  wxXmlNode* layers = drawingChildren["layers"];
509  wxXmlNode* plain = boardChildren["plain"];
510  wxXmlNode* signals = boardChildren["signals"];
511  wxXmlNode* libs = boardChildren["libraries"];
512  wxXmlNode* elems = boardChildren["elements"];
513 
514  if( m_progressReporter )
515  {
516  m_totalCount = 0;
517  m_doneCount = 0;
518 
519  count_children( designrules );
520  count_children( layers );
521  count_children( plain );
522  count_children( signals );
523  count_children( elems );
524 
525  while( libs )
526  {
527  count_children( MapChildren( libs )["packages"] );
528  libs = libs->GetNext();
529  }
530 
531  // Rewind
532  libs = boardChildren["libraries"];
533  }
534 
535  m_xpath->push( "eagle.drawing" );
536 
537  {
538  m_xpath->push( "board" );
539 
540  loadDesignRules( designrules );
541 
542  m_xpath->pop();
543  }
544 
545  {
546  m_xpath->push( "layers" );
547 
548  loadLayerDefs( layers );
550 
551  m_xpath->pop();
552  }
553 
554  {
555  m_xpath->push( "board" );
556 
557  loadPlain( plain );
558  loadSignals( signals );
559  loadLibraries( libs );
560  loadElements( elems );
561 
562  m_xpath->pop();
563  }
564 
565  m_xpath->pop(); // "eagle.drawing"
566 }
567 
568 
569 void EAGLE_PLUGIN::loadDesignRules( wxXmlNode* aDesignRules )
570 {
571  if( aDesignRules )
572  {
573  m_xpath->push( "designrules" );
574  m_rules->parse( aDesignRules, [this](){ checkpoint(); } );
575  m_xpath->pop(); // "designrules"
576  }
577 }
578 
579 
580 void EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers )
581 {
582  if( !aLayers )
583  return;
584 
585  ELAYERS cu; // copper layers
586 
587  // Get the first layer and iterate
588  wxXmlNode* layerNode = aLayers->GetChildren();
589 
590  m_eagleLayers.clear();
591  m_eagleLayersIds.clear();
592 
593  while( layerNode )
594  {
595  ELAYER elayer( layerNode );
596  m_eagleLayers.insert( std::make_pair( elayer.number, elayer ) );
597  m_eagleLayersIds.insert( std::make_pair( elayer.name, elayer.number ) );
598 
599  // find the subset of layers that are copper and active
600  if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) )
601  cu.push_back( elayer );
602 
603  layerNode = layerNode->GetNext();
604  }
605 
606  // establish cu layer map:
607  int ki_layer_count = 0;
608 
609  for( EITER it = cu.begin(); it != cu.end(); ++it, ++ki_layer_count )
610  {
611  if( ki_layer_count == 0 )
612  {
613  m_cu_map[it->number] = F_Cu;
614  }
615  else if( ki_layer_count == int( cu.size()-1 ) )
616  {
617  m_cu_map[it->number] = B_Cu;
618  }
619  else
620  {
621  // some eagle boards do not have contiguous layer number sequences.
622  m_cu_map[it->number] = ki_layer_count;
623  }
624  }
625 
626  // Set the layer names and cu count if we're loading a board.
627  if( m_board )
628  {
629  m_board->SetCopperLayerCount( cu.size() );
630 
631  for( EITER it = cu.begin(); it != cu.end(); ++it )
632  {
633  PCB_LAYER_ID layer = kicad_layer( it->number );
634 
635  // these function provide their own protection against non enabled layers:
636  if( layer >= 0 && layer < PCB_LAYER_ID_COUNT ) // layer should be valid
637  {
638  m_board->SetLayerName( layer, FROM_UTF8( it->name.c_str() ) );
639  m_board->SetLayerType( layer, LT_SIGNAL );
640  }
641 
642  // could map the colors here
643  }
644  }
645 }
646 
647 
648 #define DIMENSION_PRECISION 1 // 0.001 mm
649 
650 
651 void EAGLE_PLUGIN::loadPlain( wxXmlNode* aGraphics )
652 {
653  if( !aGraphics )
654  return;
655 
656  m_xpath->push( "plain" );
657 
658  // Get the first graphic and iterate
659  wxXmlNode* gr = aGraphics->GetChildren();
660 
661  // (polygon | wire | text | circle | rectangle | frame | hole)*
662  while( gr )
663  {
664  checkpoint();
665 
666  wxString grName = gr->GetName();
667 
668  if( grName == "wire" )
669  {
670  m_xpath->push( "wire" );
671 
672  EWIRE w( gr );
673  PCB_LAYER_ID layer = kicad_layer( w.layer );
674 
675  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
676  wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
677 
678  if( layer != UNDEFINED_LAYER )
679  {
680  PCB_SHAPE* shape = new PCB_SHAPE( m_board );
681  int width = w.width.ToPcbUnits();
682 
683  // KiCad cannot handle zero or negative line widths
684  if( width <= 0 )
685  width = m_board->GetDesignSettings().GetLineThickness( layer );
686 
687  m_board->Add( shape, ADD_MODE::APPEND );
688 
689  if( !w.curve )
690  {
691  shape->SetStart( start );
692  shape->SetEnd( end );
693  }
694  else
695  {
696  wxPoint center = ConvertArcCenter( start, end, *w.curve );
697 
698  shape->SetShape( SHAPE_T::ARC );
699  shape->SetStart( center );
700  shape->SetEnd( start );
701  shape->SetAngle( *w.curve * -10.0 ); // KiCad rotates the other way
702  }
703 
704  shape->SetLayer( layer );
705  shape->SetWidth( width );
706  }
707 
708  m_xpath->pop();
709  }
710  else if( grName == "text" )
711  {
712  m_xpath->push( "text" );
713 
714  ETEXT t( gr );
715  PCB_LAYER_ID layer = kicad_layer( t.layer );
716 
717  if( layer != UNDEFINED_LAYER )
718  {
719  PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
720  m_board->Add( pcbtxt, ADD_MODE::APPEND );
721 
722  pcbtxt->SetLayer( layer );
723  wxString kicadText = interpret_text( t.text );
724  pcbtxt->SetText( FROM_UTF8( kicadText.c_str() ) );
725  pcbtxt->SetTextPos( wxPoint( kicad_x( t.x ), kicad_y( t.y ) ) );
726 
727  double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
728  int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
729  pcbtxt->SetTextThickness( textThickness );
730  pcbtxt->SetTextSize( kicad_fontz( t.size, textThickness ) );
731 
732  int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT;
733 
734  if( t.rot )
735  {
736  int sign = t.rot->mirror ? -1 : 1;
737  pcbtxt->SetMirrored( t.rot->mirror );
738 
739  double degrees = t.rot->degrees;
740 
741  if( degrees == 90 || t.rot->spin )
742  pcbtxt->SetTextAngle( sign * t.rot->degrees * 10 );
743  else if( degrees == 180 )
744  align = ETEXT::TOP_RIGHT;
745  else if( degrees == 270 )
746  {
747  pcbtxt->SetTextAngle( sign * 90 * 10 );
748  align = ETEXT::TOP_RIGHT;
749  }
750  else
751  {
752  // Ok so text is not at 90,180 or 270 so do some funny stuff to get
753  // placement right.
754  if( ( degrees > 0 ) && ( degrees < 90 ) )
755  {
756  pcbtxt->SetTextAngle( sign * t.rot->degrees * 10 );
757  }
758  else if( ( degrees > 90 ) && ( degrees < 180 ) )
759  {
760  pcbtxt->SetTextAngle( sign * ( t.rot->degrees + 180 ) * 10 );
761  align = ETEXT::TOP_RIGHT;
762  }
763  else if( ( degrees > 180 ) && ( degrees < 270 ) )
764  {
765  pcbtxt->SetTextAngle( sign * ( t.rot->degrees - 180 ) * 10 );
766  align = ETEXT::TOP_RIGHT;
767  }
768  else if( ( degrees > 270 ) && ( degrees < 360 ) )
769  {
770  pcbtxt->SetTextAngle( sign * t.rot->degrees * 10 );
771  align = ETEXT::BOTTOM_LEFT;
772  }
773  }
774  }
775 
776  switch( align )
777  {
778  case ETEXT::CENTER:
779  // this was the default in pcbtxt's constructor
780  break;
781 
782  case ETEXT::CENTER_LEFT:
784  break;
785 
786  case ETEXT::CENTER_RIGHT:
788  break;
789 
790  case ETEXT::TOP_CENTER:
792  break;
793 
794  case ETEXT::TOP_LEFT:
797  break;
798 
799  case ETEXT::TOP_RIGHT:
802  break;
803 
806  break;
807 
808  case ETEXT::BOTTOM_LEFT:
811  break;
812 
813  case ETEXT::BOTTOM_RIGHT:
816  break;
817  }
818  }
819 
820  m_xpath->pop();
821  }
822  else if( grName == "circle" )
823  {
824  m_xpath->push( "circle" );
825 
826  ECIRCLE c( gr );
827 
828  int width = c.width.ToPcbUnits();
829  int radius = c.radius.ToPcbUnits();
830 
832  || c.layer == EAGLE_LAYER::VRESTRICT )
833  {
834  ZONE* zone = new ZONE( m_board );
835  m_board->Add( zone, ADD_MODE::APPEND );
836 
837  setKeepoutSettingsToZone( zone, c.layer );
838 
839  // approximate circle as polygon with a edge every 10 degree
840  wxPoint center( kicad_x( c.x ), kicad_y( c.y ) );
841  int outlineRadius = radius + ( width / 2 );
842 
843  for( int angle = 0; angle < 360; angle += 10 )
844  {
845  wxPoint rotatedPoint( outlineRadius, 0 );
846  RotatePoint( &rotatedPoint, angle * 10. );
847  zone->AppendCorner( center + rotatedPoint, -1 );
848  }
849 
850  if( width > 0 )
851  {
852  zone->NewHole();
853  int innerRadius = radius - ( width / 2 );
854 
855  for( int angle = 0; angle < 360; angle += 10 )
856  {
857  wxPoint rotatedPoint( innerRadius, 0 );
858  RotatePoint( &rotatedPoint, angle * 10. );
859  zone->AppendCorner( center + rotatedPoint, 0 );
860  }
861  }
862 
864  ZONE::GetDefaultHatchPitch(), true );
865  }
866  else
867  {
868  PCB_LAYER_ID layer = kicad_layer( c.layer );
869 
870  if( layer != UNDEFINED_LAYER ) // unsupported layer
871  {
872  PCB_SHAPE* shape = new PCB_SHAPE( m_board );
873  m_board->Add( shape, ADD_MODE::APPEND );
874 
875  shape->SetShape( SHAPE_T::CIRCLE );
876  shape->SetFilled( false );
877  shape->SetLayer( layer );
878  shape->SetStart( wxPoint( kicad_x( c.x ), kicad_y( c.y ) ) );
879  shape->SetEnd( wxPoint( kicad_x( c.x ) + radius, kicad_y( c.y ) ) );
880  shape->SetWidth( width );
881  }
882  }
883 
884  m_xpath->pop();
885  }
886  else if( grName == "rectangle" )
887  {
888  // This seems to be a simplified rectangular [copper] zone, cannot find any
889  // net related info on it from the DTD.
890  m_xpath->push( "rectangle" );
891 
892  ERECT r( gr );
893  PCB_LAYER_ID layer = kicad_layer( r.layer );
894 
895  if( IsCopperLayer( layer ) )
896  {
897  // use a "netcode = 0" type ZONE:
898  ZONE* zone = new ZONE( m_board );
899  m_board->Add( zone, ADD_MODE::APPEND );
900 
901  zone->SetLayer( layer );
903 
905 
906  const int outlineIdx = -1; // this is the id of the copper zone main outline
907  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
908  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
909  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
910  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
911 
912  if( r.rot )
913  zone->Rotate( zone->GetPosition(), r.rot->degrees * 10 );
914 
915  // this is not my fault:
916  zone->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(),
917  true );
918  }
919 
920  m_xpath->pop();
921  }
922  else if( grName == "hole" )
923  {
924  m_xpath->push( "hole" );
925 
926  // Fabricate a FOOTPRINT with a single PAD_ATTRIB::NPTH pad.
927  // Use m_hole_count to gen up a unique name.
928 
929  FOOTPRINT* footprint = new FOOTPRINT( m_board );
930  m_board->Add( footprint, ADD_MODE::APPEND );
931  footprint->SetReference( wxString::Format( "@HOLE%d", m_hole_count++ ) );
932  footprint->Reference().SetVisible( false );
933 
934  packageHole( footprint, gr, true );
935 
936  m_xpath->pop();
937  }
938  else if( grName == "frame" )
939  {
940  // picture this
941  }
942  else if( grName == "polygon" )
943  {
944  m_xpath->push( "polygon" );
945  loadPolygon( gr );
946  m_xpath->pop(); // "polygon"
947  }
948  else if( grName == "dimension" )
949  {
950  EDIMENSION d( gr );
951  PCB_LAYER_ID layer = kicad_layer( d.layer );
952 
953  if( layer != UNDEFINED_LAYER )
954  {
955  const BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
956  PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board );
957  m_board->Add( dimension, ADD_MODE::APPEND );
958 
959  if( d.dimensionType )
960  {
961  // Eagle dimension graphic arms may have different lengths, but they look
962  // incorrect in KiCad (the graphic is tilted). Make them even length in
963  // such case.
964  if( *d.dimensionType == "horizontal" )
965  {
966  int newY = ( d.y1.ToPcbUnits() + d.y2.ToPcbUnits() ) / 2;
967  d.y1 = ECOORD( newY, ECOORD::EAGLE_UNIT::EU_NM );
968  d.y2 = ECOORD( newY, ECOORD::EAGLE_UNIT::EU_NM );
969  }
970  else if( *d.dimensionType == "vertical" )
971  {
972  int newX = ( d.x1.ToPcbUnits() + d.x2.ToPcbUnits() ) / 2;
973  d.x1 = ECOORD( newX, ECOORD::EAGLE_UNIT::EU_NM );
974  d.x2 = ECOORD( newX, ECOORD::EAGLE_UNIT::EU_NM );
975  }
976  }
977 
978  dimension->SetLayer( layer );
979  dimension->SetPrecision( DIMENSION_PRECISION );
980 
981  // The origin and end are assumed to always be in this order from eagle
982  dimension->SetStart( wxPoint( kicad_x( d.x1 ), kicad_y( d.y1 ) ) );
983  dimension->SetEnd( wxPoint( kicad_x( d.x2 ), kicad_y( d.y2 ) ) );
984  dimension->Text().SetTextSize( designSettings.GetTextSize( layer ) );
985  dimension->Text().SetTextThickness( designSettings.GetTextThickness( layer ) );
986  dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
987  dimension->SetUnits( EDA_UNITS::MILLIMETRES );
988 
989  // check which axis the dimension runs in
990  // because the "height" of the dimension is perpendicular to that axis
991  // Note the check is just if two axes are close enough to each other
992  // Eagle appears to have some rounding errors
993  if( abs( ( d.x1 - d.x2 ).ToPcbUnits() ) < 50000 ) // 50000 nm = 0.05 mm
994  dimension->SetHeight( kicad_x( d.x3 - d.x1 ) );
995  else
996  dimension->SetHeight( kicad_y( d.y3 - d.y1 ) );
997  }
998  }
999 
1000  // Get next graphic
1001  gr = gr->GetNext();
1002  }
1003 
1004  m_xpath->pop();
1005 }
1006 
1007 
1008 void EAGLE_PLUGIN::loadLibrary( wxXmlNode* aLib, const wxString* aLibName )
1009 {
1010  if( !aLib )
1011  return;
1012 
1013  // library will have <xmlattr> node, skip that and get the single packages node
1014  wxXmlNode* packages = MapChildren( aLib )["packages"];
1015 
1016  if( !packages )
1017  return;
1018 
1019  m_xpath->push( "packages" );
1020 
1021  // Create a FOOTPRINT for all the eagle packages, for use later via a copy constructor
1022  // to instantiate needed footprints in our BOARD. Save the FOOTPRINT templates in
1023  // a FOOTPRINT_MAP using a single lookup key consisting of libname+pkgname.
1024 
1025  // Get the first package and iterate
1026  wxXmlNode* package = packages->GetChildren();
1027 
1028  while( package )
1029  {
1030  checkpoint();
1031 
1032  m_xpath->push( "package", "name" );
1033 
1034  wxString pack_ref = package->GetAttribute( "name" );
1035  ReplaceIllegalFileNameChars( pack_ref, '_' );
1036 
1037  m_xpath->Value( pack_ref.ToUTF8() );
1038 
1039  wxString key = aLibName ? makeKey( *aLibName, pack_ref ) : pack_ref;
1040 
1041  FOOTPRINT* m = makeFootprint( package, pack_ref );
1042 
1043  // add the templating FOOTPRINT to the FOOTPRINT template factory "m_templates"
1044  std::pair<FOOTPRINT_MAP::iterator, bool> r = m_templates.insert( {key, m} );
1045 
1046  if( !r.second /* && !( m_props && m_props->Value( "ignore_duplicates" ) ) */ )
1047  {
1048  wxString lib = aLibName ? *aLibName : m_lib_path;
1049  const wxString& pkg = pack_ref;
1050 
1051  wxString emsg = wxString::Format( _( "<package> '%s' duplicated in <library> '%s'" ),
1052  pkg,
1053  lib );
1054  THROW_IO_ERROR( emsg );
1055  }
1056 
1057  m_xpath->pop();
1058 
1059  package = package->GetNext();
1060  }
1061 
1062  m_xpath->pop(); // "packages"
1063 }
1064 
1065 
1066 void EAGLE_PLUGIN::loadLibraries( wxXmlNode* aLibs )
1067 {
1068  if( !aLibs )
1069  return;
1070 
1071  m_xpath->push( "libraries.library", "name" );
1072 
1073  // Get the first library and iterate
1074  wxXmlNode* library = aLibs->GetChildren();
1075 
1076  while( library )
1077  {
1078  const wxString& lib_name = library->GetAttribute( "name" );
1079 
1080  m_xpath->Value( lib_name.c_str() );
1081  loadLibrary( library, &lib_name );
1082  library = library->GetNext();
1083  }
1084 
1085  m_xpath->pop();
1086 }
1087 
1088 
1089 void EAGLE_PLUGIN::loadElements( wxXmlNode* aElements )
1090 {
1091  if( !aElements )
1092  return;
1093 
1094  m_xpath->push( "elements.element", "name" );
1095 
1096  EATTR name;
1097  EATTR value;
1098  bool refanceNamePresetInPackageLayout;
1099  bool valueNamePresetInPackageLayout;
1100 
1101  // Get the first element and iterate
1102  wxXmlNode* element = aElements->GetChildren();
1103 
1104  while( element )
1105  {
1106  checkpoint();
1107 
1108  if( element->GetName() != "element" )
1109  {
1110  // Get next item
1111  element = element->GetNext();
1112  continue;
1113  }
1114 
1115  EELEMENT e( element );
1116 
1117  // use "NULL-ness" as an indication of presence of the attribute:
1118  EATTR* nameAttr = nullptr;
1119  EATTR* valueAttr = nullptr;
1120 
1121  m_xpath->Value( e.name.c_str() );
1122 
1123  wxString pkg_key = makeKey( e.library, e.package );
1124 
1125  FOOTPRINT_MAP::const_iterator it = m_templates.find( pkg_key );
1126 
1127  if( it == m_templates.end() )
1128  {
1129  wxString emsg = wxString::Format( _( "No '%s' package in library '%s'." ),
1130  FROM_UTF8( e.package.c_str() ),
1131  FROM_UTF8( e.library.c_str() ) );
1132  THROW_IO_ERROR( emsg );
1133  }
1134 
1135  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( it->second->Duplicate() );
1136 
1137  m_board->Add( footprint, ADD_MODE::APPEND );
1138 
1139  // update the nets within the pads of the clone
1140  for( PAD* pad : footprint->Pads() )
1141  {
1142  wxString pn_key = makeKey( e.name, pad->GetNumber() );
1143 
1144  NET_MAP_CITER ni = m_pads_to_nets.find( pn_key );
1145  if( ni != m_pads_to_nets.end() )
1146  {
1147  const ENET* enet = &ni->second;
1148  pad->SetNetCode( enet->netcode );
1149  }
1150  }
1151 
1152  refanceNamePresetInPackageLayout = true;
1153  valueNamePresetInPackageLayout = true;
1154  footprint->SetPosition( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
1155 
1156  // Is >NAME field set in package layout ?
1157  if( footprint->GetReference().size() == 0 )
1158  {
1159  footprint->Reference().SetVisible( false ); // No so no show
1160  refanceNamePresetInPackageLayout = false;
1161  }
1162 
1163  // Is >VALUE field set in package layout
1164  if( footprint->GetValue().size() == 0 )
1165  {
1166  footprint->Value().SetVisible( false ); // No so no show
1167  valueNamePresetInPackageLayout = false;
1168  }
1169 
1170  footprint->SetReference( FROM_UTF8( e.name.c_str() ) );
1171  footprint->SetValue( FROM_UTF8( e.value.c_str() ) );
1172 
1173  if( !e.smashed )
1174  {
1175  // Not smashed so show NAME & VALUE
1176  if( valueNamePresetInPackageLayout )
1177  footprint->Value().SetVisible( true ); // Only if place holder in package layout
1178 
1179  if( refanceNamePresetInPackageLayout )
1180  footprint->Reference().SetVisible( true ); // Only if place holder in package layout
1181  }
1182  else if( *e.smashed == true )
1183  {
1184  // Smashed so set default to no show for NAME and VALUE
1185  footprint->Value().SetVisible( false );
1186  footprint->Reference().SetVisible( false );
1187 
1188  // initialize these to default values in case the <attribute> elements are not present.
1189  m_xpath->push( "attribute", "name" );
1190 
1191  // VALUE and NAME can have something like our text "effects" overrides
1192  // in SWEET and new schematic. Eagle calls these XML elements "attribute".
1193  // There can be one for NAME and/or VALUE both. Features present in the
1194  // EATTR override the ones established in the package only if they are
1195  // present here (except for rot, which if not present means angle zero).
1196  // So the logic is a bit different than in packageText() and in plain text.
1197 
1198  // Get the first attribute and iterate
1199  wxXmlNode* attribute = element->GetChildren();
1200 
1201  while( attribute )
1202  {
1203  if( attribute->GetName() != "attribute" )
1204  {
1205  attribute = attribute->GetNext();
1206  continue;
1207  }
1208 
1209  EATTR a( attribute );
1210 
1211  if( a.name == "NAME" )
1212  {
1213  name = a;
1214  nameAttr = &name;
1215 
1216  // do we have a display attribute ?
1217  if( a.display )
1218  {
1219  // Yes!
1220  switch( *a.display )
1221  {
1222  case EATTR::VALUE :
1223  {
1224  wxString reference = e.name;
1225 
1226  // EAGLE allows references to be single digits. This breaks KiCad
1227  // netlisting, which requires parts to have non-digit + digit
1228  // annotation. If the reference begins with a number, we prepend
1229  // 'UNK' (unknown) for the symbol designator.
1230  if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1231  reference.Prepend( "UNK" );
1232 
1233  nameAttr->name = reference;
1234  footprint->SetReference( reference );
1235 
1236  if( refanceNamePresetInPackageLayout )
1237  footprint->Reference().SetVisible( true );
1238 
1239  break;
1240  }
1241 
1242  case EATTR::NAME :
1243  if( refanceNamePresetInPackageLayout )
1244  {
1245  footprint->SetReference( "NAME" );
1246  footprint->Reference().SetVisible( true );
1247  }
1248 
1249  break;
1250 
1251  case EATTR::BOTH :
1252  if( refanceNamePresetInPackageLayout )
1253  footprint->Reference().SetVisible( true );
1254 
1255  nameAttr->name = nameAttr->name + " = " + e.name;
1256  footprint->SetReference( "NAME = " + e.name );
1257  break;
1258 
1259  case EATTR::Off :
1260  footprint->Reference().SetVisible( false );
1261  break;
1262 
1263  default:
1264  nameAttr->name = e.name;
1265 
1266  if( refanceNamePresetInPackageLayout )
1267  footprint->Reference().SetVisible( true );
1268  }
1269  }
1270  else
1271  {
1272  // No display, so default is visible, and show value of NAME
1273  footprint->Reference().SetVisible( true );
1274  }
1275  }
1276  else if( a.name == "VALUE" )
1277  {
1278  value = a;
1279  valueAttr = &value;
1280 
1281  if( a.display )
1282  {
1283  // Yes!
1284  switch( *a.display )
1285  {
1286  case EATTR::VALUE :
1287  valueAttr->value = opt_wxString( e.value );
1288  footprint->SetValue( e.value );
1289 
1290  if( valueNamePresetInPackageLayout )
1291  footprint->Value().SetVisible( true );
1292 
1293  break;
1294 
1295  case EATTR::NAME :
1296  if( valueNamePresetInPackageLayout )
1297  footprint->Value().SetVisible( true );
1298 
1299  footprint->SetValue( "VALUE" );
1300  break;
1301 
1302  case EATTR::BOTH :
1303  if( valueNamePresetInPackageLayout )
1304  footprint->Value().SetVisible( true );
1305 
1306  valueAttr->value = opt_wxString( "VALUE = " + e.value );
1307  footprint->SetValue( "VALUE = " + e.value );
1308  break;
1309 
1310  case EATTR::Off :
1311  footprint->Value().SetVisible( false );
1312  break;
1313 
1314  default:
1315  valueAttr->value = opt_wxString( e.value );
1316 
1317  if( valueNamePresetInPackageLayout )
1318  footprint->Value().SetVisible( true );
1319  }
1320  }
1321  else
1322  {
1323  // No display, so default is visible, and show value of NAME
1324  footprint->Value().SetVisible( true );
1325  }
1326 
1327  }
1328 
1329  attribute = attribute->GetNext();
1330  }
1331 
1332  m_xpath->pop(); // "attribute"
1333  }
1334 
1335  orientFootprintAndText( footprint, e, nameAttr, valueAttr );
1336 
1337  // Set the local coordinates for the footprint text items
1338  footprint->Reference().SetLocalCoord();
1339  footprint->Value().SetLocalCoord();
1340 
1341  // Get next element
1342  element = element->GetNext();
1343  }
1344 
1345  m_xpath->pop(); // "elements.element"
1346 }
1347 
1348 
1349 ZONE* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode )
1350 {
1351  EPOLYGON p( aPolyNode );
1352  PCB_LAYER_ID layer = kicad_layer( p.layer );
1353  ZONE* zone = nullptr;
1354  bool keepout = ( p.layer == EAGLE_LAYER::TRESTRICT
1356  || p.layer == EAGLE_LAYER::VRESTRICT );
1357 
1358  if( layer == UNDEFINED_LAYER )
1359  {
1360  wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
1361  "was not mapped" ),
1362  eagle_layer_name( p.layer ), p.layer ) );
1363  return nullptr;
1364  }
1365 
1366  if( !IsCopperLayer( layer ) && !keepout )
1367  return nullptr;
1368 
1369  // use a "netcode = 0" type ZONE:
1370  zone = new ZONE( m_board );
1371  m_board->Add( zone, ADD_MODE::APPEND );
1372 
1373  if( !keepout )
1374  zone->SetLayer( layer );
1375  else
1376  setKeepoutSettingsToZone( zone, p.layer );
1377 
1378  // Get the first vertex and iterate
1379  wxXmlNode* vertex = aPolyNode->GetChildren();
1380  std::vector<EVERTEX> vertices;
1381 
1382  // Create a circular vector of vertices
1383  // The "curve" parameter indicates a curve from the current
1384  // to the next vertex, so we keep the first at the end as well
1385  // to allow the curve to link back
1386  while( vertex )
1387  {
1388  if( vertex->GetName() == "vertex" )
1389  vertices.emplace_back( vertex );
1390 
1391  vertex = vertex->GetNext();
1392  }
1393 
1394  vertices.push_back( vertices[0] );
1395 
1396  SHAPE_POLY_SET polygon;
1397  polygon.NewOutline();
1398 
1399  for( size_t i = 0; i < vertices.size() - 1; i++ )
1400  {
1401  EVERTEX v1 = vertices[i];
1402 
1403  // Append the corner
1404  polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) );
1405 
1406  if( v1.curve )
1407  {
1408  EVERTEX v2 = vertices[i + 1];
1409  wxPoint center = ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
1410  wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ),
1411  *v1.curve );
1412  double angle = DEG2RAD( *v1.curve );
1413  double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
1414  double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
1415  + pow( center.y - kicad_y( v1.y ), 2 ) );
1416 
1417  int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
1418  double delta_angle = angle / segCount;
1419 
1420  for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle );
1421  a -= delta_angle )
1422  {
1423  polygon.Append( KiROUND( radius * cos( a ) ) + center.x,
1424  KiROUND( radius * sin( a ) ) + center.y );
1425  }
1426  }
1427  }
1428 
1429  // Eagle traces the zone such that half of the pen width is outside the polygon.
1430  // We trace the zone such that the copper is completely inside.
1431  if( p.width.ToPcbUnits() > 0 )
1432  {
1435  }
1436 
1437  zone->AddPolygon( polygon.COutline( 0 ) );
1438 
1439  // If the pour is a cutout it needs to be set to a keepout
1440  if( p.pour == EPOLYGON::CUTOUT )
1441  {
1442  zone->SetIsRuleArea( true );
1443  zone->SetDoNotAllowVias( false );
1444  zone->SetDoNotAllowTracks( false );
1445  zone->SetDoNotAllowPads( false );
1446  zone->SetDoNotAllowFootprints( false );
1447  zone->SetDoNotAllowCopperPour( true );
1449  }
1450  else if( p.pour == EPOLYGON::HATCH )
1451  {
1452  int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * IU_PER_MILS;
1453 
1455  zone->SetHatchThickness( p.width.ToPcbUnits() );
1456  zone->SetHatchGap( spacing - p.width.ToPcbUnits() );
1457  zone->SetHatchOrientation( 0 );
1458  }
1459 
1460  // We divide the thickness by half because we are tracing _inside_ the zone outline
1461  // This means the radius of curvature will be twice the size for an equivalent EAGLE zone
1463  p.width.ToPcbUnits() / 2 ) );
1464 
1465  if( p.isolate )
1466  zone->SetLocalClearance( p.isolate->ToPcbUnits() );
1467  else
1468  zone->SetLocalClearance( 1 ); // @todo: set minimum clearance value based on board settings
1469 
1470  // missing == yes per DTD.
1471  bool thermals = !p.thermals || *p.thermals;
1473 
1474  if( thermals )
1475  {
1476  // FIXME: eagle calculates dimensions for thermal spokes
1477  // based on what the zone is connecting to.
1478  // (i.e. width of spoke is half of the smaller side of an smd pad)
1479  // This is a basic workaround
1480  zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
1481  zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 );
1482  }
1483 
1484  int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
1485  zone->SetPriority( rank );
1486 
1487  return zone;
1488 }
1489 
1490 
1492  const EATTR* aNameAttr, const EATTR* aValueAttr )
1493 {
1494  if( e.rot )
1495  {
1496  if( e.rot->mirror )
1497  {
1498  double orientation = e.rot->degrees + 180.0;
1499  aFootprint->SetOrientation( orientation * 10 );
1500  aFootprint->Flip( aFootprint->GetPosition(), false );
1501  }
1502  else
1503  {
1504  aFootprint->SetOrientation( e.rot->degrees * 10 );
1505  }
1506  }
1507 
1508  orientFPText( aFootprint, e, &aFootprint->Reference(), aNameAttr );
1509  orientFPText( aFootprint, e, &aFootprint->Value(), aValueAttr );
1510 }
1511 
1512 
1513 void EAGLE_PLUGIN::orientFPText( FOOTPRINT* aFootprint, const EELEMENT& e, FP_TEXT* aFPText,
1514  const EATTR* aAttr )
1515 {
1516  // Smashed part ?
1517  if( aAttr )
1518  {
1519  // Yes
1520  const EATTR& a = *aAttr;
1521 
1522  if( a.value )
1523  {
1524  aFPText->SetText( FROM_UTF8( a.value->c_str() ) );
1525  }
1526 
1527  if( a.x && a.y ) // OPT
1528  {
1529  wxPoint pos( kicad_x( *a.x ), kicad_y( *a.y ) );
1530  aFPText->SetTextPos( pos );
1531  }
1532 
1533  // Even though size and ratio are both optional, I am not seeing
1534  // a case where ratio is present but size is not.
1535  double ratio = 8;
1536  wxSize fontz = aFPText->GetTextSize();
1537  int textThickness = KiROUND( fontz.y * ratio / 100 );
1538 
1539  aFPText->SetTextThickness( textThickness );
1540  if( a.size )
1541  {
1542  fontz = kicad_fontz( *a.size, textThickness );
1543  aFPText->SetTextSize( fontz );
1544 
1545  if( a.ratio )
1546  ratio = *a.ratio;
1547  }
1548 
1549 
1550 
1551  int align = ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1552 
1553  if( a.align )
1554  align = a.align;
1555 
1556  // The "rot" in a EATTR seems to be assumed to be zero if it is not
1557  // present, and this zero rotation becomes an override to the
1558  // package's text field. If they did not want zero, they specify
1559  // what they want explicitly.
1560  double degrees = a.rot ? a.rot->degrees : 0;
1561  double orient; // relative to parent
1562 
1563  int sign = 1;
1564  bool spin = false;
1565 
1566  if( a.rot )
1567  {
1568  spin = a.rot->spin;
1569  sign = a.rot->mirror ? -1 : 1;
1570  aFPText->SetMirrored( a.rot->mirror );
1571  }
1572 
1573  if( degrees == 90 || degrees == 0 || spin )
1574  {
1575  orient = degrees - aFootprint->GetOrientation() / 10;
1576  aFPText->SetTextAngle( sign * orient * 10 );
1577  }
1578  else if( degrees == 180 )
1579  {
1580  orient = 0 - aFootprint->GetOrientation() / 10;
1581  aFPText->SetTextAngle( sign * orient * 10 );
1582  align = -align;
1583  }
1584  else if( degrees == 270 )
1585  {
1586  orient = 90 - aFootprint->GetOrientation() / 10;
1587  align = -align;
1588  aFPText->SetTextAngle( sign * orient * 10 );
1589  }
1590  else
1591  {
1592  orient = 90 - degrees - aFootprint->GetOrientation() / 10;
1593  aFPText->SetTextAngle( sign * orient * 10 );
1594  }
1595 
1596  switch( align )
1597  {
1598  case ETEXT::TOP_RIGHT:
1601  break;
1602 
1603  case ETEXT::BOTTOM_LEFT:
1606  break;
1607 
1608  case ETEXT::TOP_LEFT:
1611  break;
1612 
1613  case ETEXT::BOTTOM_RIGHT:
1616  break;
1617 
1618  case ETEXT::TOP_CENTER:
1621  break;
1622 
1623  case ETEXT::BOTTOM_CENTER:
1626  break;
1627 
1628  default:
1629  ;
1630  }
1631  }
1632  else
1633  {
1634  // Part is not smash so use Lib default for NAME/VALUE // the text is per the original
1635  // package, sans <attribute>.
1636  double degrees = ( aFPText->GetTextAngle() + aFootprint->GetOrientation() ) / 10;
1637 
1638  // @todo there are a few more cases than these to contend with:
1639  if( ( !aFPText->IsMirrored() && ( abs( degrees ) == 180 || abs( degrees ) == 270 ) )
1640  || ( aFPText->IsMirrored() && ( degrees == 360 ) ) )
1641  {
1642  // ETEXT::TOP_RIGHT:
1645  }
1646  }
1647 }
1648 
1649 
1650 FOOTPRINT* EAGLE_PLUGIN::makeFootprint( wxXmlNode* aPackage, const wxString& aPkgName )
1651 {
1652  std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>( m_board );
1653 
1654  LIB_ID fpID;
1655  fpID.Parse( aPkgName, true );
1656  m->SetFPID( fpID );
1657 
1658  // Get the first package item and iterate
1659  wxXmlNode* packageItem = aPackage->GetChildren();
1660 
1661  while( packageItem )
1662  {
1663  const wxString& itemName = packageItem->GetName();
1664 
1665  if( itemName == "description" )
1666  m->SetDescription( FROM_UTF8( packageItem->GetNodeContent().c_str() ) );
1667  else if( itemName == "wire" )
1668  packageWire( m.get(), packageItem );
1669  else if( itemName == "pad" )
1670  packagePad( m.get(), packageItem );
1671  else if( itemName == "text" )
1672  packageText( m.get(), packageItem );
1673  else if( itemName == "rectangle" )
1674  packageRectangle( m.get(), packageItem );
1675  else if( itemName == "polygon" )
1676  packagePolygon( m.get(), packageItem );
1677  else if( itemName == "circle" )
1678  packageCircle( m.get(), packageItem );
1679  else if( itemName == "hole" )
1680  packageHole( m.get(), packageItem, false );
1681  else if( itemName == "smd" )
1682  packageSMD( m.get(), packageItem );
1683 
1684  packageItem = packageItem->GetNext();
1685  }
1686 
1687  return m.release();
1688 }
1689 
1690 
1691 void EAGLE_PLUGIN::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1692 {
1693  EWIRE w( aTree );
1694  PCB_LAYER_ID layer = kicad_layer( w.layer );
1695  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
1696  wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
1697  int width = w.width.ToPcbUnits();
1698 
1699  if( layer == UNDEFINED_LAYER )
1700  {
1701  wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) "
1702  "was not mapped" ),
1703  eagle_layer_name( w.layer ), w.layer ) );
1704  return;
1705  }
1706 
1707  // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
1708  if( width <= 0 )
1709  {
1710  BOARD* board = aFootprint->GetBoard();
1711 
1712  if( board )
1713  {
1714  width = board->GetDesignSettings().GetLineThickness( layer );
1715  }
1716  else
1717  {
1718  // When loading footprint libraries, there is no board so use the default KiCad
1719  // line widths.
1720  switch( layer )
1721  {
1722  case Edge_Cuts: width = Millimeter2iu( DEFAULT_EDGE_WIDTH ); break;
1723 
1724  case F_SilkS:
1725  case B_SilkS: width = Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ); break;
1726 
1727  case F_CrtYd:
1728  case B_CrtYd: width = Millimeter2iu( DEFAULT_COURTYARD_WIDTH ); break;
1729 
1730  default: width = Millimeter2iu( DEFAULT_LINE_WIDTH ); break;
1731  }
1732  }
1733  }
1734 
1735  // FIXME: the cap attribute is ignored because KiCad can't create lines
1736  // with flat ends.
1737  FP_SHAPE* dwg;
1738 
1739  if( !w.curve )
1740  {
1741  dwg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
1742 
1743  dwg->SetStart0( start );
1744  dwg->SetEnd0( end );
1745  }
1746  else
1747  {
1748  dwg = new FP_SHAPE( aFootprint, SHAPE_T::ARC );
1749  wxPoint center = ConvertArcCenter( start, end, *w.curve );
1750 
1751  dwg->SetStart0( center );
1752  dwg->SetEnd0( start );
1753  dwg->SetAngle( *w.curve * -10.0 ); // KiCad rotates the other way
1754  }
1755 
1756  dwg->SetLayer( layer );
1757  dwg->SetWidth( width );
1758  dwg->SetDrawCoord();
1759 
1760  aFootprint->Add( dwg );
1761 }
1762 
1763 
1764 void EAGLE_PLUGIN::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
1765 {
1766  // this is thru hole technology here, no SMDs
1767  EPAD e( aTree );
1768  int shape = EPAD::UNDEF;
1769  int eagleDrillz = e.drill.ToPcbUnits();
1770 
1771  PAD* pad = new PAD( aFootprint );
1772  aFootprint->Add( pad );
1773  transferPad( e, pad );
1774 
1775  if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
1776  shape = m_rules->psFirst;
1777  else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
1778  shape = m_rules->psTop;
1779  else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
1780  shape = m_rules->psBottom;
1781 
1782  pad->SetDrillSize( wxSize( eagleDrillz, eagleDrillz ) );
1783  pad->SetLayerSet( LSET::AllCuMask() );
1784 
1785  if( eagleDrillz < m_min_hole )
1786  m_min_hole = eagleDrillz;
1787 
1788  // Solder mask
1789  if( !e.stop || *e.stop == true ) // enabled by default
1790  pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
1791 
1792  if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
1793  e.shape = shape;
1794 
1795  if( e.shape )
1796  {
1797  switch( *e.shape )
1798  {
1799  case EPAD::ROUND:
1800  pad->SetShape( PAD_SHAPE::CIRCLE );
1801  break;
1802 
1803  case EPAD::OCTAGON:
1804  // no KiCad octagonal pad shape, use PAD_CIRCLE for now.
1805  // pad->SetShape( PAD_OCTAGON );
1806  wxASSERT( pad->GetShape() == PAD_SHAPE::CIRCLE ); // verify set in PAD constructor
1807  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
1808  pad->SetChamferPositions( RECT_CHAMFER_ALL );
1809  pad->SetChamferRectRatio( 0.25 );
1810  break;
1811 
1812  case EPAD::LONG:
1813  pad->SetShape( PAD_SHAPE::OVAL );
1814  break;
1815 
1816  case EPAD::SQUARE:
1817  pad->SetShape( PAD_SHAPE::RECT );
1818  break;
1819 
1820  case EPAD::OFFSET:
1821  pad->SetShape( PAD_SHAPE::OVAL );
1822  break;
1823  }
1824  }
1825  else
1826  {
1827  // if shape is not present, our default is circle and that matches their default "round"
1828  }
1829 
1830  if( e.diameter && e.diameter->value > 0 )
1831  {
1832  int diameter = e.diameter->ToPcbUnits();
1833  pad->SetSize( wxSize( diameter, diameter ) );
1834  }
1835  else
1836  {
1837  double drillz = pad->GetDrillSize().x;
1838  double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
1839  annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
1840  int diameter = KiROUND( drillz + 2 * annulus );
1841  pad->SetSize( wxSize( KiROUND( diameter ), KiROUND( diameter ) ) );
1842  }
1843 
1844  if( pad->GetShape() == PAD_SHAPE::OVAL )
1845  {
1846  // The Eagle "long" pad is wider than it is tall,
1847  // m_elongation is percent elongation
1848  wxSize sz = pad->GetSize();
1849  sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
1850  pad->SetSize( sz );
1851 
1852  if( e.shape && *e.shape == EPAD::OFFSET )
1853  {
1854  int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
1855  pad->SetOffset( wxPoint( offset, 0 ) );
1856  }
1857  }
1858 
1859  if( e.rot )
1860  {
1861  pad->SetOrientation( e.rot->degrees * 10 );
1862  }
1863 }
1864 
1865 
1866 void EAGLE_PLUGIN::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1867 {
1868  ETEXT t( aTree );
1869  PCB_LAYER_ID layer = kicad_layer( t.layer );
1870 
1871  if( layer == UNDEFINED_LAYER )
1872  {
1873  wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) "
1874  "was not mapped" ),
1875  eagle_layer_name( t.layer ), t.layer ) );
1876  return;
1877  }
1878 
1879  FP_TEXT* txt;
1880 
1881  if( t.text == ">NAME" || t.text == ">name" )
1882  txt = &aFootprint->Reference();
1883  else if( t.text == ">VALUE" || t.text == ">value" )
1884  txt = &aFootprint->Value();
1885  else
1886  {
1887  // FIXME: graphical text items are rotated for some reason.
1888  txt = new FP_TEXT( aFootprint );
1889  aFootprint->Add( txt );
1890  }
1891 
1892  txt->SetText( FROM_UTF8( t.text.c_str() ) );
1893 
1894  wxPoint pos( kicad_x( t.x ), kicad_y( t.y ) );
1895 
1896  txt->SetTextPos( pos );
1897  txt->SetPos0( pos - aFootprint->GetPosition() );
1898 
1899  txt->SetLayer( layer );
1900 
1901  double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
1902  int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
1903 
1904  txt->SetTextThickness( textThickness );
1905  txt->SetTextSize( kicad_fontz( t.size, textThickness ) );
1906 
1907  int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1908 
1909  // An eagle package is never rotated, the DTD does not allow it.
1910  // angle -= aFootprint->GetOrienation();
1911 
1912  if( t.rot )
1913  {
1914  int sign = t.rot->mirror ? -1 : 1;
1915  txt->SetMirrored( t.rot->mirror );
1916 
1917  double degrees = t.rot->degrees;
1918 
1919  if( degrees == 90 || t.rot->spin )
1920  {
1921  txt->SetTextAngle( sign * degrees * 10 );
1922  }
1923  else if( degrees == 180 )
1924  {
1925  align = ETEXT::TOP_RIGHT;
1926  }
1927  else if( degrees == 270 )
1928  {
1929  align = ETEXT::TOP_RIGHT;
1930  txt->SetTextAngle( sign * 90 * 10 );
1931  }
1932  }
1933 
1934  switch( align )
1935  {
1936  case ETEXT::CENTER:
1937  // this was the default in pcbtxt's constructor
1938  break;
1939 
1940  case ETEXT::CENTER_LEFT:
1942  break;
1943 
1944  case ETEXT::CENTER_RIGHT:
1946  break;
1947 
1948  case ETEXT::TOP_CENTER:
1950  break;
1951 
1952  case ETEXT::TOP_LEFT:
1955  break;
1956 
1957  case ETEXT::TOP_RIGHT:
1960  break;
1961 
1962  case ETEXT::BOTTOM_CENTER:
1964  break;
1965 
1966  case ETEXT::BOTTOM_LEFT:
1969  break;
1970 
1971  case ETEXT::BOTTOM_RIGHT:
1974  break;
1975  }
1976 }
1977 
1978 
1979 void EAGLE_PLUGIN::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1980 {
1981  ERECT r( aTree );
1982 
1984  || r.layer == EAGLE_LAYER::VRESTRICT )
1985  {
1986  FP_ZONE* zone = new FP_ZONE( aFootprint );
1987  aFootprint->Add( zone, ADD_MODE::APPEND );
1988 
1989  setKeepoutSettingsToZone( zone, r.layer );
1990 
1991  const int outlineIdx = -1; // this is the id of the copper zone main outline
1992  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
1993  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
1994  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
1995  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
1996 
1997  if( r.rot )
1998  {
1999  wxPoint center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
2000  ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
2001  zone->Rotate( center, r.rot->degrees * 10 );
2002  }
2003 
2005  ZONE::GetDefaultHatchPitch(), true );
2006  }
2007  else
2008  {
2009  PCB_LAYER_ID layer = kicad_layer( r.layer );
2010 
2011  if( layer == UNDEFINED_LAYER )
2012  {
2013  wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) "
2014  "was not mapped" ),
2015  eagle_layer_name( r.layer ), r.layer ) );
2016  return;
2017  }
2018 
2019  FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2020 
2021  aFootprint->Add( dwg );
2022 
2023  dwg->SetLayer( layer );
2024  dwg->SetWidth( 0 );
2025 
2026  std::vector<wxPoint> pts;
2027 
2028  wxPoint start( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
2029  wxPoint end( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
2030 
2031  pts.push_back( start );
2032  pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
2033  pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
2034  pts.push_back( end );
2035 
2036  dwg->SetPolyPoints( pts );
2037 
2038  dwg->SetStart0( start );
2039  dwg->SetEnd0( end );
2040 
2041  if( r.rot )
2042  dwg->Rotate( dwg->GetCenter(), r.rot->degrees * 10 );
2043  }
2044 }
2045 
2046 
2047 void EAGLE_PLUGIN::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2048 {
2049  EPOLYGON p( aTree );
2050 
2051  std::vector<wxPoint> pts;
2052 
2053  // Get the first vertex and iterate
2054  wxXmlNode* vertex = aTree->GetChildren();
2055  std::vector<EVERTEX> vertices;
2056 
2057  // Create a circular vector of vertices
2058  // The "curve" parameter indicates a curve from the current
2059  // to the next vertex, so we keep the first at the end as well
2060  // to allow the curve to link back
2061  while( vertex )
2062  {
2063  if( vertex->GetName() == "vertex" )
2064  vertices.emplace_back( vertex );
2065 
2066  vertex = vertex->GetNext();
2067  }
2068 
2069  vertices.push_back( vertices[0] );
2070 
2071  for( size_t i = 0; i < vertices.size() - 1; i++ )
2072  {
2073  EVERTEX v1 = vertices[i];
2074 
2075  // Append the corner
2076  pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
2077 
2078  if( v1.curve )
2079  {
2080  EVERTEX v2 = vertices[i + 1];
2081  wxPoint center =
2082  ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
2083  wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
2084  double angle = DEG2RAD( *v1.curve );
2085  double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
2086  double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
2087  + pow( center.y - kicad_y( v1.y ), 2 ) );
2088 
2089  // Don't allow a zero-radius curve
2090  if( KiROUND( radius ) == 0 )
2091  radius = 1.0;
2092 
2093  int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
2094  double delta = angle / segCount;
2095 
2096  for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
2097  {
2098  pts.push_back( wxPoint( KiROUND( radius * cos( a ) ),
2099  KiROUND( radius * sin( a ) ) ) + center );
2100  }
2101  }
2102  }
2103 
2104  if( p.layer == EAGLE_LAYER::TRESTRICT
2106  || p.layer == EAGLE_LAYER::VRESTRICT )
2107  {
2108  FP_ZONE* zone = new FP_ZONE( aFootprint );
2109  aFootprint->Add( zone, ADD_MODE::APPEND );
2110 
2111  setKeepoutSettingsToZone( zone, p.layer );
2112 
2113  SHAPE_LINE_CHAIN outline( pts );
2114  outline.SetClosed( true );
2115  zone->Outline()->AddOutline( outline );
2116 
2118  ZONE::GetDefaultHatchPitch(), true );
2119  }
2120  else
2121  {
2122  PCB_LAYER_ID layer = kicad_layer( p.layer );
2123 
2124  if( layer == UNDEFINED_LAYER )
2125  {
2126  wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
2127  "was not mapped" ),
2128  eagle_layer_name( p.layer ), p.layer ) );
2129  return;
2130  }
2131 
2132  FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2133 
2134  aFootprint->Add( dwg );
2135 
2136  dwg->SetWidth( 0 ); // it's filled, no need for boundary width
2137  dwg->SetLayer( layer );
2138 
2139  dwg->SetPolyPoints( pts );
2140  dwg->SetStart0( *pts.begin() );
2141  dwg->SetEnd0( pts.back() );
2142  dwg->SetDrawCoord();
2143  dwg->GetPolyShape().Inflate( p.width.ToPcbUnits() / 2, 32,
2145  }
2146 }
2147 
2148 
2149 void EAGLE_PLUGIN::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2150 {
2151  ECIRCLE e( aTree );
2152 
2153  int width = e.width.ToPcbUnits();
2154  int radius = e.radius.ToPcbUnits();
2155 
2156  if( e.layer == EAGLE_LAYER::TRESTRICT
2158  || e.layer == EAGLE_LAYER::VRESTRICT )
2159  {
2160  FP_ZONE* zone = new FP_ZONE( aFootprint );
2161  aFootprint->Add( zone, ADD_MODE::APPEND );
2162 
2163  setKeepoutSettingsToZone( zone, e.layer );
2164 
2165  // approximate circle as polygon with a edge every 10 degree
2166  wxPoint center( kicad_x( e.x ), kicad_y( e.y ) );
2167  int outlineRadius = radius + ( width / 2 );
2168 
2169  for( int angle = 0; angle < 360; angle += 10 )
2170  {
2171  wxPoint rotatedPoint( outlineRadius, 0 );
2172  RotatePoint( &rotatedPoint, angle * 10. );
2173  zone->AppendCorner( center + rotatedPoint, -1 );
2174  }
2175 
2176  if( width > 0 )
2177  {
2178  zone->NewHole();
2179  int innerRadius = radius - ( width / 2 );
2180 
2181  for( int angle = 0; angle < 360; angle += 10 )
2182  {
2183  wxPoint rotatedPoint( innerRadius, 0 );
2184  RotatePoint( &rotatedPoint, angle * 10. );
2185  zone->AppendCorner( center + rotatedPoint, 0 );
2186  }
2187  }
2188 
2190  ZONE::GetDefaultHatchPitch(), true );
2191  }
2192  else
2193  {
2194  PCB_LAYER_ID layer = kicad_layer( e.layer );
2195 
2196  if( layer == UNDEFINED_LAYER )
2197  {
2198  wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) "
2199  "was not mapped" ),
2200  eagle_layer_name( e.layer ), e.layer ) );
2201  return;
2202  }
2203 
2204  FP_SHAPE* gr = new FP_SHAPE( aFootprint, SHAPE_T::CIRCLE );
2205 
2206  // with == 0 means filled circle
2207  if( width <= 0 )
2208  {
2209  width = radius;
2210  radius = radius / 2;
2211  }
2212 
2213  aFootprint->Add( gr );
2214  gr->SetWidth( width );
2215 
2216  switch( (int) layer )
2217  {
2218  case UNDEFINED_LAYER:
2219  layer = Cmts_User;
2220  break;
2221  default:
2222  break;
2223  }
2224 
2225  gr->SetLayer( layer );
2226  gr->SetStart0( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
2227  gr->SetEnd0( wxPoint( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
2228  gr->SetDrawCoord();
2229  }
2230 }
2231 
2232 
2233 void EAGLE_PLUGIN::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
2234 {
2235  EHOLE e( aTree );
2236 
2237  if( e.drill.value == 0 )
2238  return;
2239 
2240  // we add a PAD_ATTRIB::NPTH pad to this footprint.
2241  PAD* pad = new PAD( aFootprint );
2242  aFootprint->Add( pad );
2243 
2244  pad->SetShape( PAD_SHAPE::CIRCLE );
2245  pad->SetAttribute( PAD_ATTRIB::NPTH );
2246 
2247  // Mechanical purpose only:
2248  // no offset, no net name, no pad name allowed
2249  // pad->SetOffset( wxPoint( 0, 0 ) );
2250  // pad->SetNumber( wxEmptyString );
2251 
2252  wxPoint padpos( kicad_x( e.x ), kicad_y( e.y ) );
2253 
2254  if( aCenter )
2255  {
2256  pad->SetPos0( wxPoint( 0, 0 ) );
2257  aFootprint->SetPosition( padpos );
2258  pad->SetPosition( padpos );
2259  }
2260  else
2261  {
2262  pad->SetPos0( padpos );
2263  pad->SetPosition( padpos + aFootprint->GetPosition() );
2264  }
2265 
2266  wxSize sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
2267 
2268  pad->SetDrillSize( sz );
2269  pad->SetSize( sz );
2270 
2271  pad->SetLayerSet( LSET::AllCuMask().set( B_Mask ).set( F_Mask ) );
2272 }
2273 
2274 
2275 void EAGLE_PLUGIN::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2276 {
2277  ESMD e( aTree );
2278  PCB_LAYER_ID layer = kicad_layer( e.layer );
2279 
2280  if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
2281  return;
2282 
2283  PAD* pad = new PAD( aFootprint );
2284  aFootprint->Add( pad );
2285  transferPad( e, pad );
2286 
2287  pad->SetShape( PAD_SHAPE::RECT );
2288  pad->SetAttribute( PAD_ATTRIB::SMD );
2289 
2290  wxSize padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
2291  pad->SetSize( padSize );
2292  pad->SetLayer( layer );
2293 
2294  const LSET front( 3, F_Cu, F_Paste, F_Mask );
2295  const LSET back( 3, B_Cu, B_Paste, B_Mask );
2296 
2297  if( layer == F_Cu )
2298  pad->SetLayerSet( front );
2299  else if( layer == B_Cu )
2300  pad->SetLayerSet( back );
2301 
2302  int minPadSize = std::min( padSize.x, padSize.y );
2303 
2304  // Rounded rectangle pads
2305  int roundRadius =
2306  eagleClamp( m_rules->srMinRoundness * 2, (int) ( minPadSize * m_rules->srRoundness ),
2307  m_rules->srMaxRoundness * 2 );
2308 
2309  if( e.roundness || roundRadius > 0 )
2310  {
2311  double roundRatio = (double) roundRadius / minPadSize / 2.0;
2312 
2313  // Eagle uses a different definition of roundness, hence division by 200
2314  if( e.roundness )
2315  roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
2316 
2317  pad->SetShape( PAD_SHAPE::ROUNDRECT );
2318  pad->SetRoundRectRadiusRatio( roundRatio );
2319  }
2320 
2321  if( e.rot )
2322  pad->SetOrientation( e.rot->degrees * 10 );
2323 
2324  pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
2325  (int) ( m_rules->mvCreamFrame * minPadSize ),
2326  m_rules->mlMaxCreamFrame ) );
2327 
2328  // Solder mask
2329  if( e.stop && *e.stop == false ) // enabled by default
2330  {
2331  if( layer == F_Cu )
2332  pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
2333  else if( layer == B_Cu )
2334  pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
2335  }
2336 
2337  // Solder paste (only for SMD pads)
2338  if( e.cream && *e.cream == false ) // enabled by default
2339  {
2340  if( layer == F_Cu )
2341  pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
2342  else if( layer == B_Cu )
2343  pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
2344  }
2345 }
2346 
2347 
2348 void EAGLE_PLUGIN::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
2349 {
2350  aPad->SetNumber( FROM_UTF8( aEaglePad.name.c_str() ) );
2351 
2352  // pad's "Position" is not relative to the footprint's,
2353  // whereas Pos0 is relative to the footprint's but is the unrotated coordinate.
2354  wxPoint padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
2355  aPad->SetPos0( padPos );
2356 
2357  // Solder mask
2358  const wxSize& padSize( aPad->GetSize() );
2359 
2362  (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
2363  m_rules->mlMaxStopFrame ) );
2364 
2365  // Solid connection to copper zones
2366  if( aEaglePad.thermals && !*aEaglePad.thermals )
2368 
2369  FOOTPRINT* footprint = aPad->GetParent();
2370  wxCHECK( footprint, /* void */ );
2371  RotatePoint( &padPos, footprint->GetOrientation() );
2372  aPad->SetPosition( padPos + footprint->GetPosition() );
2373 }
2374 
2375 
2377 {
2378  for( auto& t : m_templates )
2379  delete t.second;
2380 
2381  m_templates.clear();
2382 }
2383 
2384 
2385 void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
2386 {
2387  ZONES zones; // per net
2388 
2389  m_xpath->push( "signals.signal", "name" );
2390 
2391  int netCode = 1;
2392 
2393  // Get the first signal and iterate
2394  wxXmlNode* net = aSignals->GetChildren();
2395 
2396  while( net )
2397  {
2398  checkpoint();
2399 
2400  bool sawPad = false;
2401 
2402  zones.clear();
2403 
2404  const wxString& netName = escapeName( net->GetAttribute( "name" ) );
2405  m_board->Add( new NETINFO_ITEM( m_board, netName, netCode ) );
2406 
2407  m_xpath->Value( netName.c_str() );
2408 
2409  // Get the first net item and iterate
2410  wxXmlNode* netItem = net->GetChildren();
2411 
2412  // (contactref | polygon | wire | via)*
2413  while( netItem )
2414  {
2415  const wxString& itemName = netItem->GetName();
2416 
2417  if( itemName == "wire" )
2418  {
2419  m_xpath->push( "wire" );
2420 
2421  EWIRE w( netItem );
2422  PCB_LAYER_ID layer = kicad_layer( w.layer );
2423 
2424  if( IsCopperLayer( layer ) )
2425  {
2426  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
2427  double angle = 0.0;
2428  double end_angle = 0.0;
2429  double radius = 0.0;
2430  double delta_angle = 0.0;
2431  wxPoint center;
2432 
2433  int width = w.width.ToPcbUnits();
2434 
2435  if( width < m_min_trace )
2436  m_min_trace = width;
2437 
2438  if( w.curve )
2439  {
2440  center = ConvertArcCenter( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ),
2441  wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ),
2442  *w.curve );
2443 
2444  angle = DEG2RAD( *w.curve );
2445 
2446  end_angle = atan2( kicad_y( w.y2 ) - center.y,
2447  kicad_x( w.x2 ) - center.x );
2448 
2449  radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
2450  pow( center.y - kicad_y( w.y1 ), 2 ) );
2451 
2452  int segs = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
2453  *w.curve );
2454  delta_angle = angle / segs;
2455  }
2456 
2457  while( fabs( angle ) > fabs( delta_angle ) )
2458  {
2459  wxASSERT( radius > 0.0 );
2460  wxPoint end( KiROUND( radius * cos( end_angle + angle ) + center.x ),
2461  KiROUND( radius * sin( end_angle + angle ) + center.y ) );
2462 
2463  PCB_TRACK* t = new PCB_TRACK( m_board );
2464 
2465  t->SetPosition( start );
2466  t->SetEnd( end );
2467  t->SetWidth( width );
2468  t->SetLayer( layer );
2469  t->SetNetCode( netCode );
2470 
2471  m_board->Add( t );
2472 
2473  start = end;
2474  angle -= delta_angle;
2475  }
2476 
2477  PCB_TRACK* t = new PCB_TRACK( m_board );
2478 
2479  t->SetPosition( start );
2480  t->SetEnd( wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
2481  t->SetWidth( width );
2482  t->SetLayer( layer );
2483  t->SetNetCode( netCode );
2484 
2485  m_board->Add( t );
2486  }
2487  else
2488  {
2489  // put non copper wires where the sun don't shine.
2490  }
2491 
2492  m_xpath->pop();
2493  }
2494  else if( itemName == "via" )
2495  {
2496  m_xpath->push( "via" );
2497  EVIA v( netItem );
2498 
2499  PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most );
2500  PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most );
2501 
2502  if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most ) )
2503  {
2504  int kidiam;
2505  int drillz = v.drill.ToPcbUnits();
2506  PCB_VIA* via = new PCB_VIA( m_board );
2507  m_board->Add( via );
2508 
2509  via->SetLayerPair( layer_front_most, layer_back_most );
2510 
2511  if( v.diam )
2512  {
2513  kidiam = v.diam->ToPcbUnits();
2514  via->SetWidth( kidiam );
2515  }
2516  else
2517  {
2518  double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
2519  annulus = eagleClamp( m_rules->rlMinViaOuter, annulus,
2521  kidiam = KiROUND( drillz + 2 * annulus );
2522  via->SetWidth( kidiam );
2523  }
2524 
2525  via->SetDrill( drillz );
2526 
2527  // make sure the via diameter respects the restring rules
2528 
2529  if( !v.diam || via->GetWidth() <= via->GetDrill() )
2530  {
2531  double annulus =
2533  (double) ( via->GetWidth() / 2 - via->GetDrill() ),
2535  via->SetWidth( drillz + 2 * annulus );
2536  }
2537 
2538  if( kidiam < m_min_via )
2539  m_min_via = kidiam;
2540 
2541  if( drillz < m_min_hole )
2542  m_min_hole = drillz;
2543 
2544  if( ( kidiam - drillz ) / 2 < m_min_annulus )
2545  m_min_annulus = ( kidiam - drillz ) / 2;
2546 
2547  if( layer_front_most == F_Cu && layer_back_most == B_Cu )
2548  via->SetViaType( VIATYPE::THROUGH );
2549  else if( layer_front_most == F_Cu || layer_back_most == B_Cu )
2550  via->SetViaType( VIATYPE::MICROVIA );
2551  else
2552  via->SetViaType( VIATYPE::BLIND_BURIED );
2553 
2554  wxPoint pos( kicad_x( v.x ), kicad_y( v.y ) );
2555 
2556  via->SetPosition( pos );
2557  via->SetEnd( pos );
2558 
2559  via->SetNetCode( netCode );
2560  }
2561 
2562  m_xpath->pop();
2563  }
2564 
2565  else if( itemName == "contactref" )
2566  {
2567  m_xpath->push( "contactref" );
2568  // <contactref element="RN1" pad="7"/>
2569 
2570  const wxString& reference = netItem->GetAttribute( "element" );
2571  const wxString& pad = netItem->GetAttribute( "pad" );
2572  wxString key = makeKey( reference, pad ) ;
2573 
2574  m_pads_to_nets[ key ] = ENET( netCode, netName );
2575 
2576  m_xpath->pop();
2577 
2578  sawPad = true;
2579  }
2580 
2581  else if( itemName == "polygon" )
2582  {
2583  m_xpath->push( "polygon" );
2584  auto* zone = loadPolygon( netItem );
2585 
2586  if( zone )
2587  {
2588  zones.push_back( zone );
2589 
2590  if( !zone->GetIsRuleArea() )
2591  zone->SetNetCode( netCode );
2592  }
2593 
2594  m_xpath->pop(); // "polygon"
2595  }
2596 
2597  netItem = netItem->GetNext();
2598  }
2599 
2600  if( zones.size() && !sawPad )
2601  {
2602  // KiCad does not support an unconnected zone with its own non-zero netcode,
2603  // but only when assigned netcode = 0 w/o a name...
2604  for( ZONE* zone : zones )
2605  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
2606 
2607  // therefore omit this signal/net.
2608  }
2609  else
2610  {
2611  netCode++;
2612  }
2613 
2614  // Get next signal
2615  net = net->GetNext();
2616  }
2617 
2618  m_xpath->pop(); // "signals.signal"
2619 }
2620 
2621 
2622 std::map<wxString, PCB_LAYER_ID> EAGLE_PLUGIN::DefaultLayerMappingCallback(
2623  const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2624 {
2625  std::map<wxString, PCB_LAYER_ID> layer_map;
2626 
2627  for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
2628  {
2629  PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
2630  layer_map.emplace( layer.Name, layerId );
2631  }
2632 
2633  return layer_map;
2634 }
2635 
2636 
2638 {
2639  std::vector<INPUT_LAYER_DESC> inputDescs;
2640 
2641  for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
2642  {
2643  const ELAYER& eLayer = layerPair.second;
2644 
2645  INPUT_LAYER_DESC layerDesc;
2646  std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
2647  defaultKicadLayer( eLayer.number );
2648 
2649  if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
2650  continue; // Ignore unused copper layers
2651 
2652  layerDesc.Name = eLayer.name;
2653 
2654  inputDescs.push_back( layerDesc );
2655  }
2656 
2657  if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
2658  dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
2659 
2660  m_layer_map = m_layer_mapping_handler( inputDescs );
2661 
2662  if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
2663  dynamic_cast<wxWindow*>( m_progressReporter )->Show();
2664 }
2665 
2666 
2667 PCB_LAYER_ID EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) const
2668 {
2669  auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
2670  return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
2671 }
2672 
2673 
2674 std::tuple<PCB_LAYER_ID, LSET, bool> EAGLE_PLUGIN::defaultKicadLayer( int aEagleLayer ) const
2675 {
2676  // eagle copper layer:
2677  if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
2678  {
2679  LSET copperLayers;
2680 
2681  for( int copperLayer : m_cu_map )
2682  {
2683  if( copperLayer >= 0 )
2684  copperLayers[copperLayer] = true;
2685  }
2686 
2687  return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
2688  }
2689 
2690  int kiLayer = UNSELECTED_LAYER;
2691  bool required = false;
2692  LSET permittedLayers;
2693 
2694  permittedLayers.set();
2695 
2696  // translate non-copper eagle layer to pcbnew layer
2697  switch( aEagleLayer )
2698  {
2699  // Eagle says "Dimension" layer, but it's for board perimeter
2701  kiLayer = Edge_Cuts;
2702  required = true;
2703  permittedLayers = LSET( 1, Edge_Cuts );
2704  break;
2705 
2706  case EAGLE_LAYER::TPLACE:
2707  kiLayer = F_SilkS;
2708  break;
2709  case EAGLE_LAYER::BPLACE:
2710  kiLayer = B_SilkS;
2711  break;
2712  case EAGLE_LAYER::TNAMES:
2713  kiLayer = F_SilkS;
2714  break;
2715  case EAGLE_LAYER::BNAMES:
2716  kiLayer = B_SilkS;
2717  break;
2718  case EAGLE_LAYER::TVALUES:
2719  kiLayer = F_Fab;
2720  break;
2721  case EAGLE_LAYER::BVALUES:
2722  kiLayer = B_Fab;
2723  break;
2724  case EAGLE_LAYER::TSTOP:
2725  kiLayer = F_Mask;
2726  break;
2727  case EAGLE_LAYER::BSTOP:
2728  kiLayer = B_Mask;
2729  break;
2730  case EAGLE_LAYER::TCREAM:
2731  kiLayer = F_Paste;
2732  break;
2733  case EAGLE_LAYER::BCREAM:
2734  kiLayer = B_Paste;
2735  break;
2736  case EAGLE_LAYER::TFINISH:
2737  kiLayer = F_Mask;
2738  break;
2739  case EAGLE_LAYER::BFINISH:
2740  kiLayer = B_Mask;
2741  break;
2742  case EAGLE_LAYER::TGLUE:
2743  kiLayer = F_Adhes;
2744  break;
2745  case EAGLE_LAYER::BGLUE:
2746  kiLayer = B_Adhes;
2747  break;
2748  case EAGLE_LAYER::DOCUMENT:
2749  kiLayer = Cmts_User;
2750  break;
2752  kiLayer = Cmts_User;
2753  break;
2755  kiLayer = Cmts_User;
2756  break;
2757 
2758  // Packages show the future chip pins on SMD parts using layer 51.
2759  // This is an area slightly smaller than the PAD/SMD copper area.
2760  // Carry those visual aids into the FOOTPRINT on the fabrication layer,
2761  // not silkscreen. This is perhaps not perfect, but there is not a lot
2762  // of other suitable paired layers
2763  case EAGLE_LAYER::TDOCU:
2764  kiLayer = F_Fab;
2765  break;
2766  case EAGLE_LAYER::BDOCU:
2767  kiLayer = B_Fab;
2768  break;
2769 
2770  // these layers are defined as user layers. put them on ECO layers
2772  kiLayer = Eco1_User;
2773  break;
2775  kiLayer = Eco2_User;
2776  break;
2777 
2778  // these will also appear in the ratsnest, so there's no need for a warning
2779  case EAGLE_LAYER::UNROUTED:
2780  kiLayer = Dwgs_User;
2781  break;
2782 
2783  case EAGLE_LAYER::TKEEPOUT:
2784  kiLayer = F_CrtYd;
2785  break;
2786  case EAGLE_LAYER::BKEEPOUT:
2787  kiLayer = B_CrtYd;
2788  break;
2789 
2790  case EAGLE_LAYER::MILLING:
2791  case EAGLE_LAYER::TTEST:
2792  case EAGLE_LAYER::BTEST:
2793  case EAGLE_LAYER::HOLES:
2794  default:
2795  kiLayer = UNSELECTED_LAYER;
2796  break;
2797  }
2798 
2799  return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
2800 }
2801 
2802 
2803 const wxString& EAGLE_PLUGIN::eagle_layer_name( int aLayer ) const
2804 {
2805  static const wxString unknown( "unknown" );
2806  auto it = m_eagleLayers.find( aLayer );
2807  return it == m_eagleLayers.end() ? unknown : it->second.name;
2808 }
2809 
2810 
2811 int EAGLE_PLUGIN::eagle_layer_id( const wxString& aLayerName ) const
2812 {
2813  static const int unknown = -1;
2814  auto it = m_eagleLayersIds.find( aLayerName );
2815  return it == m_eagleLayersIds.end() ? unknown : it->second;
2816 }
2817 
2818 
2820 {
2821  if( m_props )
2822  {
2823  UTF8 page_width;
2824  UTF8 page_height;
2825 
2826  if( m_props->Value( "page_width", &page_width ) &&
2827  m_props->Value( "page_height", &page_height ) )
2828  {
2830 
2831  int w = atoi( page_width.c_str() );
2832  int h = atoi( page_height.c_str() );
2833 
2834  int desired_x = ( w - bbbox.GetWidth() ) / 2;
2835  int desired_y = ( h - bbbox.GetHeight() ) / 2;
2836 
2837  m_board->Move( wxPoint( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
2838  }
2839  }
2840 }
2841 
2842 
2843 wxDateTime EAGLE_PLUGIN::getModificationTime( const wxString& aPath )
2844 {
2845  // File hasn't been loaded yet.
2846  if( aPath.IsEmpty() )
2847  return wxDateTime::Now();
2848 
2849  wxFileName fn( aPath );
2850 
2851  if( fn.IsFileReadable() )
2852  return fn.GetModificationTime();
2853  else
2854  return wxDateTime( 0.0 );
2855 }
2856 
2857 
2858 void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath )
2859 {
2860  try
2861  {
2862  wxDateTime modtime = getModificationTime( aLibPath );
2863 
2864  // Fixes assertions in wxWidgets debug builds for the wxDateTime object. Refresh the
2865  // cache if either of the wxDateTime objects are invalid or the last file modification
2866  // time differs from the current file modification time.
2867  bool load = !m_mod_time.IsValid() || !modtime.IsValid() || m_mod_time != modtime;
2868 
2869  if( aLibPath != m_lib_path || load )
2870  {
2871  wxXmlNode* doc;
2872  LOCALE_IO toggle; // toggles on, then off, the C locale.
2873 
2874  deleteTemplates();
2875 
2876  // Set this before completion of loading, since we rely on it for
2877  // text of an exception. Delay setting m_mod_time until after successful load
2878  // however.
2879  m_lib_path = aLibPath;
2880 
2881  // 8 bit "filename" should be encoded according to disk filename encoding,
2882  // (maybe this is current locale, maybe not, its a filesystem issue),
2883  // and is not necessarily utf8.
2884  string filename = (const char*) aLibPath.char_str( wxConvFile );
2885 
2886  // Load the document
2887  wxFileName fn( filename );
2888  wxFFileInputStream stream( fn.GetFullPath() );
2889  wxXmlDocument xmlDocument;
2890 
2891  if( !stream.IsOk() || !xmlDocument.Load( stream ) )
2892  {
2893  THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ),
2894  fn.GetFullPath() ) );
2895  }
2896 
2897  doc = xmlDocument.GetRoot();
2898 
2899  wxXmlNode* drawing = MapChildren( doc )["drawing"];
2900  NODE_MAP drawingChildren = MapChildren( drawing );
2901 
2902  // clear the cu map and then rebuild it.
2903  clear_cu_map();
2904 
2905  m_xpath->push( "eagle.drawing.layers" );
2906  wxXmlNode* layers = drawingChildren["layers"];
2907  loadLayerDefs( layers );
2909  m_xpath->pop();
2910 
2911  m_xpath->push( "eagle.drawing.library" );
2912  wxXmlNode* library = drawingChildren["library"];
2913  loadLibrary( library, nullptr );
2914  m_xpath->pop();
2915 
2916  m_mod_time = modtime;
2917  }
2918  }
2919  catch(...){}
2920  // TODO: Handle exceptions
2921  // catch( file_parser_error fpe )
2922  // {
2923  // // for xml_parser_error, what() has the line number in it,
2924  // // but no byte offset. That should be an adequate error message.
2925  // THROW_IO_ERROR( fpe.what() );
2926  // }
2927  //
2928  // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
2929  // // so one catch should be OK for all errors.
2930  // catch( ptree_error pte )
2931  // {
2932  // string errmsg = pte.what();
2933  //
2934  // errmsg += " @\n";
2935  // errmsg += m_xpath->Contents();
2936  //
2937  // THROW_IO_ERROR( errmsg );
2938  // }
2939 }
2940 
2941 
2942 void EAGLE_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
2943  bool aBestEfforts, const PROPERTIES* aProperties )
2944 {
2945  wxString errorMsg;
2946 
2947  init( aProperties );
2948 
2949  try
2950  {
2951  cacheLib( aLibraryPath );
2952  }
2953  catch( const IO_ERROR& ioe )
2954  {
2955  errorMsg = ioe.What();
2956  }
2957 
2958  // Some of the files may have been parsed correctly so we want to add the valid files to
2959  // the library.
2960 
2961  for( FOOTPRINT_MAP::const_iterator it = m_templates.begin(); it != m_templates.end(); ++it )
2962  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
2963 
2964  if( !errorMsg.IsEmpty() && !aBestEfforts )
2965  THROW_IO_ERROR( errorMsg );
2966 }
2967 
2968 
2969 FOOTPRINT* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
2970  const wxString& aFootprintName, bool aKeepUUID,
2971  const PROPERTIES* aProperties )
2972 {
2973  init( aProperties );
2974  cacheLib( aLibraryPath );
2975  FOOTPRINT_MAP::const_iterator it = m_templates.find( aFootprintName );
2976 
2977  if( it == m_templates.end() )
2978  return nullptr;
2979 
2980  // Return a copy of the template
2981  FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate();
2982  copy->SetParent( nullptr );
2983  return copy;
2984 }
2985 
2986 
2987 void EAGLE_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const
2988 {
2989  PLUGIN::FootprintLibOptions( aListToAppendTo );
2990 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
void SetReference(const wxString &aReference)
Definition: footprint.h:439
std::vector< ZONE * > ZONES
Definition: eagle_plugin.h:43
wxString Contents()
return the contents of the XPATH as a single string
Definition: eagle_parser.h:137
Eagle vertex.
Definition: eagle_parser.h:759
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
#define DEFAULT_EDGE_WIDTH
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:70
int sign(T val)
Definition: util.h:104
void loadAllSections(wxXmlNode *aDocument)
double rlMaxViaOuter
maximum copper annulus on via
Definition: eagle_plugin.h:119
void SetUnits(EDA_UNITS aUnits)
bool mirror
Definition: eagle_parser.h:472
opt_double curve
range is -359.9..359.9
Definition: eagle_parser.h:508
#define DEFAULT_COURTYARD_WIDTH
int kicad_x(const ECOORD &x) const
Definition: eagle_plugin.h:188
Arcs (with rounded ends)
void SetHatchThickness(int aThickness)
Definition: zone.h:254
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
void SetEnd0(const wxPoint &aPoint)
Definition: fp_shape.h:114
bool IsMirrored() const
Definition: eda_text.h:189
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
opt_int rank
Definition: eagle_parser.h:792
void packageHole(FOOTPRINT *aFootprint, wxXmlNode *aTree, bool aCenter) const
void clear_cu_map()
Container for project specific data.
Definition: project.h:62
static T eagleClamp(T aMin, T aValue, T aMax)
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
void centerBoard()
move the BOARD into the center of the page
BOARD * m_board
which BOARD is being worked on, no ownership here
Definition: eagle_plugin.h:303
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
void SetShape(SHAPE_T aShape)
Definition: pcb_shape.h:109
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:745
polygon (not yet used for tracks, but could be in microwave apps)
const wxString & GetValue() const
Definition: footprint.h:452
int eagle_layer_id(const wxString &aLayerName) const
Get Eagle layer number by its name.
PCB_LAYER_ID kicad_layer(int aLayer) const
Convert an Eagle layer to a KiCad layer.
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:240
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
void FootprintLibOptions(PROPERTIES *aProperties) const override
Append supported PLUGIN options to aListToAppenTo along with internationalized descriptions.
opt_ecoord diam
Definition: eagle_parser.h:554
int psTop
Shape of the top pads.
Definition: eagle_plugin.h:99
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:192
void clear()
Definition: eagle_parser.h:120
std::map< wxString, PCB_LAYER_ID > DefaultLayerMappingCallback(const std::vector< INPUT_LAYER_DESC > &aInputLayerDescriptionVector)
Return the automapped layers.
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition: zone.h:614
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aHatchStyle, int aHatchPitch, bool aRebuildHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:896
int GetX() const
Definition: eda_rect.h:98
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
ERULES * m_rules
Eagle design rules.
Definition: eagle_plugin.h:289
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:742
void SetTextAngle(double aAngle) override
Definition: pcb_text.cpp:104
PCB_TEXT & Text()
A progress reporter interface for use in multi-threaded environments.
void SetFilled(bool aFlag)
Definition: pcb_shape.h:73
BOARD * Load(const wxString &aFileName, BOARD *aAppendToMe, const PROPERTIES *aProperties=nullptr, PROJECT *aProject=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Load information from some input file format that this PLUGIN implementation knows about into either ...
void SetTextAngle(double aAngle) override
Definition: fp_text.cpp:73
ECOORD drill
< inclusive
Definition: eagle_parser.h:553
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:246
SHAPE_POLY_SET * Outline()
Definition: zone.h:320
ECOORD y
Definition: eagle_parser.h:762
void push(const char *aPathSegment, const char *aAttribute="")
Definition: eagle_parser.h:115
Eagle element element.
Definition: eagle_parser.h:810
unsigned m_lastProgressCount
Definition: eagle_plugin.h:307
Keep track of what we are working on within a PTREE.
Definition: eagle_parser.h:110
ECOORD x2
Definition: eagle_parser.h:579
static const int max_priority
Definition: eagle_parser.h:781
Smd pad, appears on the solder paste layer (default)
double rlMaxPadTop
maximum copper annulus on through hole pads
Definition: eagle_plugin.h:115
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:246
void parse(wxXmlNode *aRules, std::function< void()> aCheckpoint)
percent over 100%.
int GetWidth() const
Definition: eda_rect.h:109
int mlMinStopFrame
solder mask, minimum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:94
usual segment : line with rounded ends
opt_double ratio
Definition: eagle_parser.h:645
int number
Definition: eagle_parser.h:828
double GetOrientation() const
Definition: footprint.h:190
double GetTextAngle() const
Definition: eda_text.h:174
void SetCopperLayerCount(int aCount)
Definition: board.cpp:460
#define DEFAULT_LINE_WIDTH
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
opt_erot rot
Definition: eagle_parser.h:820
static int parseEagle(const wxString &aDistance)
Parse an eagle distance which is either mm, or mils if there is "mil" suffix.
int srMinRoundness
corner rounding radius, maximum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:106
ECOORD width
Definition: eagle_parser.h:497
opt_bool thermals
Definition: eagle_parser.h:791
int LAYER_NUM
This can be replaced with int and removed.
Definition: layer_ids.h:40
std::map< wxString, int > m_eagleLayersIds
Eagle layer ids stored by layer name.
Definition: eagle_plugin.h:286
opt_bool smashed
Definition: eagle_parser.h:819
Definition: bitmap.cpp:64
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: eagle_plugin.h:305
wxString name
Definition: eagle_parser.h:695
NET_MAP::const_iterator NET_MAP_CITER
Definition: eagle_plugin.h:45
void init(const PROPERTIES *aProperties)
initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed.
opt_erot rot
Definition: eagle_parser.h:582
double degrees
Definition: eagle_parser.h:474
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
ECOORD width
Definition: eagle_parser.h:772
virtual void FootprintLibOptions(PROPERTIES *aListToAppendTo) const
Append supported PLUGIN options to aListToAppenTo along with internationalized descriptions.
Definition: plugin.cpp:149
Eagle SMD pad.
Definition: eagle_parser.h:728
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:409
ECOORD y
Definition: eagle_parser.h:565
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
int m_min_via
smallest via we find on Load(), in BIU.
Definition: eagle_plugin.h:312
double rvViaOuter
copper annulus is this percent of via hole
Definition: eagle_plugin.h:117
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
ECOORD y
Definition: eagle_parser.h:817
A name/value tuple with unique names and optional values.
Definition: properties.h:33
Eagle hole element.
Definition: eagle_parser.h:799
Implement a simple wrapper around runtime_error to isolate the errors thrown by the Eagle XML parser.
Definition: eagle_parser.h:68
void SetPriority(unsigned aPriority)
Definition: zone.h:117
Eagle text element.
Definition: eagle_parser.h:637
const wxString & eagle_layer_name(int aLayer) const
Get Eagle layer name by its number.
FOOTPRINT_MAP m_templates
is part of a FOOTPRINT factory that operates using copy construction.
Definition: eagle_plugin.h:297
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:589
Describes an imported layer and how it could be mapped to KiCad Layers.
void loadLayerDefs(wxXmlNode *aLayers)
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: zone.h:180
opt_erot rot
Definition: eagle_parser.h:603
NODE_MAP MapChildren(wxXmlNode *aCurrentNode)
Provide an easy access to the children of an XML node via their names.
opt_bool thermals
Definition: eagle_parser.h:699
ECOORD width
Definition: eagle_parser.h:567
ECOORD x
Definition: eagle_parser.h:564
XPATH * m_xpath
keeps track of what we are working on within XML document during a Load().
Definition: eagle_plugin.h:290
virtual void RegisterLayerMappingCallback(LAYER_MAPPING_HANDLER aLayerMappingHandler)
Register a different handler to be called when mapping of input layers to KiCad layers occurs.
int m_cu_map[17]
map eagle to KiCad, cu layers only.
Definition: eagle_plugin.h:284
PADS & Pads()
Definition: footprint.h:168
This file contains miscellaneous commonly used macros and functions.
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:466
const char * c_str() const
Definition: utf8.h:102
void SetLineThickness(int aWidth)
For better understanding of the points that make a dimension:
OPTIONAL_XML_ATTRIBUTE< wxString > opt_wxString
Definition: eagle_parser.h:365
int layer_back_most
< extent
Definition: eagle_parser.h:552
void SetIsRuleArea(bool aEnable)
Definition: zone.h:742
unsigned m_doneCount
Definition: eagle_plugin.h:306
FP_TEXT & Reference()
Definition: footprint.h:467
opt_int align
Definition: eagle_parser.h:612
void packageWire(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
ECOORD dy
Definition: eagle_parser.h:731
void Rotate(const wxPoint &aCentre, double aAngle) override
Move the outlines.
Definition: zone.cpp:707
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
opt_ecoord isolate
Definition: eagle_parser.h:789
wxString name
Definition: eagle_parser.h:596
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:185
void SetLocalSolderMaskMargin(int aMargin)
Definition: pad.h:387
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:607
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
like PAD_PTH, but not plated
wxString name
Definition: eagle_parser.h:829
ECOORD y1
Definition: eagle_parser.h:494
static wxString makeKey(const wxString &aFirst, const wxString &aSecond)
Assemble a two part key as a simple concatenation of aFirst and aSecond parts, using a separator.
wxString value
Definition: eagle_parser.h:815
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:502
#define DEFAULT_SILK_LINE_WIDTH
void NewHole()
Create a new hole on the zone; i.e., a new contour on the zone's outline.
Definition: zone.h:597
void cacheLib(const wxString &aLibraryPath)
This PLUGIN only caches one footprint library, this determines which one.
wxString escapeName(const wxString &aNetName)
pads are covered by copper
const PROPERTIES * m_props
passed via Save() or Load(), no ownership, may be NULL.
Definition: eagle_plugin.h:302
ECOORD x
Definition: eagle_parser.h:801
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
LAYER_NUM layer
Definition: eagle_parser.h:498
void SetHatchGap(int aStep)
Definition: zone.h:257
void SetPos0(const wxPoint &aPos)
Definition: pad.h:226
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
std::map< int, ELAYER > m_eagleLayers
Eagle layer data stored by layer number.
Definition: eagle_plugin.h:285
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
opt_ecoord y
Definition: eagle_parser.h:599
int layer
Definition: eagle_parser.h:581
Eagle thru hole pad.
Definition: eagle_parser.h:706
static int GetDefaultHatchPitch()
Definition: zone.cpp:1088
opt_ecoord spacing
Definition: eagle_parser.h:774
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:746
void SetClearance(int aClearance)
Definition: netclass.h:125
wxString name
Definition: eagle_parser.h:812
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
Represent a set of closed polygons.
virtual void SetEnd(const wxPoint &aPoint)
ECOORD y1
Definition: eagle_parser.h:578
void SetOrientation(double aNewAngle)
Definition: footprint.cpp:1618
const wxSize & GetTextSize() const
Definition: eda_text.h:238
void packageRectangle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void Rotate(const wxPoint &aRotCentre, double aAngle) override
Rotate an edge of the footprint.
Definition: fp_shape.cpp:277
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:202
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
void packagePad(FOOTPRINT *aFootprint, wxXmlNode *aTree)
void SetAngle(double aAngle, bool aUpdateEnd=true) override
Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: fp_shape.cpp:142
Eagle circle.
Definition: eagle_parser.h:562
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
void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1428
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:377
ECOORD dx
Definition: eagle_parser.h:730
wxString m_lib_path
Definition: eagle_plugin.h:315
bool Required
Should we require the layer to be assigned?
ECOORD x
Definition: eagle_parser.h:640
FOOTPRINT * makeFootprint(wxXmlNode *aPackage, const wxString &aPkgName)
Create a FOOTPRINT from an Eagle package.
const wxString & GetReference() const
Definition: footprint.h:430
#define _(s)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:744
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
a few functions useful in geometry calculations.
subset of eagle.drawing.board.designrules in the XML document
Definition: eagle_plugin.h:49
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
void orientFootprintAndText(FOOTPRINT *aFootprint, const EELEMENT &e, const EATTR *aNameAttr, const EATTR *aValueAttr)
void SetZoneConnection(ZONE_CONNECTION aType)
Definition: pad.h:475
void SetFileName(const wxString &aFileName)
Definition: board.h:226
ECOORD size
Definition: eagle_parser.h:642
void SetMinThickness(int aMinThickness)
Definition: zone.h:245
bool Value(const char *aName, UTF8 *aFetchedValue=nullptr) const
Fetch a property by aName and returns true if that property was found, else false.
Definition: properties.cpp:24
void SetPrecision(int aPrecision)
int layer
Definition: eagle_parser.h:732
static wxDateTime getModificationTime(const wxString &aPath)
get a file's or dir's modification time.
opt_wxString value
Definition: eagle_parser.h:597
void SetValue(const wxString &aValue)
Definition: footprint.h:460
double mdWireWire
wire to wire spacing I presume.
Definition: eagle_plugin.h:120
wxString text
Definition: eagle_parser.h:639
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
Definition: fp_shape.cpp:82
int m_min_trace
smallest trace we find on Load(), in BIU.
Definition: eagle_plugin.h:310
ECOORD x
Definition: eagle_parser.h:816
opt_int roundness
Definition: eagle_parser.h:733
int NewOutline()
Creates a new hole in a given outline.
int m_hole_count
generates unique footprint names from eagle "hole"s.
Definition: eagle_plugin.h:293
LAYER_NUM layer
Definition: eagle_parser.h:568
Eagle XML rectangle in binary.
Definition: eagle_parser.h:575
double srRoundness
corner rounding ratio for SMD pads (percentage)
Definition: eagle_plugin.h:103
opt_int align
Definition: eagle_parser.h:662
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
void SetStart0(const wxPoint &aPoint)
Definition: fp_shape.h:111
int GetHeight() const
Definition: eda_rect.h:110
Eagle net.
Definition: eagle_parser.h:453
void SetPos0(const wxPoint &aPos)
Definition: fp_text.h:165
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:787
int psElongationLong
Definition: eagle_plugin.h:85
int psFirst
Shape of the first pads.
Definition: eagle_plugin.h:101
opt_double curve
range is -359.9..359.9
Definition: eagle_parser.h:763
void loadLibraries(wxXmlNode *aLibs)
void SetPosition(const wxPoint &aPos) override
Definition: pcb_track.h:97
void loadPlain(wxXmlNode *aPlain)
std::tuple< PCB_LAYER_ID, LSET, bool > defaultKicadLayer(int aEagleLayer) const
Get default KiCad layer corresponding to an Eagle layer of the board, a set of sensible layer mapping...
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
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
FOOTPRINT * GetParent() const
Definition: pad.cpp:1354
void transferPad(const EPAD_COMMON &aEaglePad, PAD *aPad) const
Deletes the footprint templates list.
ECOORD y2
Definition: eagle_parser.h:496
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
void Move(const wxPoint &aMoveVector) override
Move this object.
Definition: board.cpp:267
Use thermal relief for pads.
Parse an Eagle "attribute" XML element.
Definition: eagle_parser.h:594
opt_ecoord x
Definition: eagle_parser.h:598
void SetWidth(int aWidth)
Definition: pcb_track.h:101
Eagle via.
Definition: eagle_parser.h:547
static wxString interpret_text(const wxString &aText)
interpret special characters in Eagle text and converts them to KiCAD notation
std::vector< ELAYER > ELAYERS
Definition: eagle_plugin.h:281
Handle the data for a net.
Definition: netinfo.h:64
ECOORD x1
Definition: eagle_parser.h:577
wxSize kicad_fontz(const ECOORD &d, int aTextThickness) const
create a font size (fontz) from an eagle font size scalar and KiCad font thickness
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:242
opt_wxString dimensionType
Definition: eagle_parser.h:630
int mlMinCreamFrame
solder paste mask, minimum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:96
bool AppendCorner(wxPoint aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition: zone.cpp:836
#define ZONE_THICKNESS_MIN_VALUE_MIL
Definition: zones.h:32
opt_erot rot
Definition: eagle_parser.h:646
int netcode
Definition: eagle_parser.h:455
wxPoint GetPosition() const override
Definition: zone.cpp:212
void loadElements(wxXmlNode *aElements)
const char * name
Definition: DXF_plotter.cpp:56
ECOORD x
Definition: eagle_parser.h:761
std::map< wxString, PCB_LAYER_ID > m_layer_map
Map of Eagle layers to KiCad layers.
Definition: eagle_plugin.h:287
opt_ecoord diameter
Definition: eagle_parser.h:709
void packageCircle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
double DEG2RAD(double deg)
Definition: trigo.h:229
void SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:127
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
void packagePolygon(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void packageText(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:201
Structure holding common properties for through-hole and SMD pads.
Definition: eagle_parser.h:693
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:743
wxString Name
Imported layer name as displayed in original application.
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
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition: pad.h:128
wxPoint ConvertArcCenter(const wxPoint &aStart, const wxPoint &aEnd, double aAngle)
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Definition: layer_ids.h:70
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetPosition(const wxPoint &aPos) override
Definition: pad.h:172
#define DIMENSION_PRECISION
ECOORD radius
Definition: eagle_parser.h:566
ELAYERS::const_iterator EITER
Definition: eagle_plugin.h:282
int GetCopperLayerCount() const
Definition: board.cpp:454
wxDateTime m_mod_time
Definition: eagle_plugin.h:316
void packageSMD(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
Handles common pad properties.
Handle the component boundary box.
Definition: eda_rect.h:42
virtual void SetStart(const wxPoint &aPoint)
NETCLASS * GetDefault() const
void loadLibrary(wxXmlNode *aLib, const wxString *aLibName)
Load the Eagle "library" XML element, which can occur either under a "libraries" element (if a *....
opt_double ratio
Definition: eagle_parser.h:602
opt_erot rot
Definition: eagle_parser.h:697
void SetWidth(int aWidth)
Definition: pcb_shape.h:96
void SetLocalCoord()
Definition: fp_text.cpp:209
double mvStopFrame
solderpaste mask, expressed as percentage of the smaller pad/via dimension
Definition: eagle_plugin.h:90
#define IU_PER_MILS
Definition: plotter.cpp:136
int GetY() const
Definition: eda_rect.h:99
ECOORD y
Definition: eagle_parser.h:550
void SetHatchOrientation(double aStep)
Definition: zone.h:260
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
int m_min_annulus
smallest via annulus we find on Load(), in BIU.
Definition: eagle_plugin.h:313
NET_MAP m_pads_to_nets
net list
Definition: eagle_plugin.h:295
static void setKeepoutSettingsToZone(ZONE *aZone, LAYER_NUM aLayer)
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const PROPERTIES *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PL...
wxPoint GetPosition() const override
Definition: footprint.h:186
opt_bool first
Definition: eagle_parser.h:721
Eagle wire.
Definition: eagle_parser.h:491
ECOORD x
Definition: eagle_parser.h:549
void SetThermalReliefGap(int aThermalReliefGap)
Definition: zone.h:183
void orientFPText(FOOTPRINT *aFootprint, const EELEMENT &e, FP_TEXT *aFPText, const EATTR *aAttr)
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
Definition: zone_settings.h:46
constexpr int delta
PCB_LAYER_ID AutoMapLayer
Best guess as to what the equivalent KiCad layer might be.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:50
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:238
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:159
opt_int shape
Definition: eagle_parser.h:720
int m_min_hole
smallest diameter hole we find on Load(), in BIU.
Definition: eagle_plugin.h:311
Eagle dimension element.
Definition: eagle_parser.h:620
double rvPadTop
top pad size as percent of drill size
Definition: eagle_plugin.h:111
wxString package
Definition: eagle_parser.h:814
void deleteTemplates()
void SetLocalClearance(int aClearance)
Definition: zone.h:158
bool spin
Definition: eagle_parser.h:473
void AddPolygon(std::vector< wxPoint > &aPolygon)
Add a polygon to the zone outline.
Definition: zone.cpp:819
ECOORD y2
Definition: eagle_parser.h:580
ECOORD y
Definition: eagle_parser.h:641
Eagle polygon, without vertices which are parsed as needed.
Definition: eagle_parser.h:770
opt_int display
Definition: eagle_parser.h:611
void pop()
Definition: eagle_parser.h:122
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Removes an item from the container.
Definition: footprint.cpp:513
void loadDesignRules(wxXmlNode *aDesignRules)
ECOORD drill
Definition: eagle_parser.h:708
Definition: pad.h:57
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1499
ECOORD y
Definition: eagle_parser.h:802
opt_bool stop
Definition: eagle_parser.h:698
const wxString PluginName() const override
Return a brief hard coded name for this PLUGIN.
int layer
Definition: eagle_parser.h:643
ECOORD x1
Definition: eagle_parser.h:493
virtual void SetAngle(double aAngle, bool aUpdateEnd=true)
Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: pcb_shape.cpp:519
wxString library
Definition: eagle_parser.h:813
int psBottom
Shape of the bottom pads.
Definition: eagle_plugin.h:100
static constexpr int Millimeter2iu(double mm)
double rlMinPadTop
minimum copper annulus on through hole pads
Definition: eagle_plugin.h:114
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:171
double rlMinViaOuter
minimum copper annulus on via
Definition: eagle_plugin.h:118
unsigned m_totalCount
for progress reporting
Definition: eagle_plugin.h:308
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:365
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
ECOORD drill
Definition: eagle_parser.h:803
just inflate the polygon. Acute angles create spikes
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: pcb_shape.cpp:1153
int mlMaxCreamFrame
solder paste mask, maximum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:97
A specialization of ZONE for use in footprints.
Definition: zone.h:946
wxSize GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int srMaxRoundness
Definition: eagle_plugin.h:109
opt_bool active
Definition: eagle_parser.h:833
void mapEagleLayersToKicad()
Generate mapping between Eagle na KiCad layers.
ECOORD x2
Definition: eagle_parser.h:495
void loadSignals(wxXmlNode *aSignals)
int kicad_y(const ECOORD &y) const
Convert an Eagle distance to a KiCad distance.
Definition: eagle_plugin.h:187
int ToPcbUnits() const
Definition: eagle_parser.h:430
std::unordered_map< wxString, wxXmlNode * > NODE_MAP
Definition: eagle_parser.h:47
void SetEnd(const wxPoint &aEnd)
Definition: pcb_shape.h:137
long long int value
Unit used for the value field.
Definition: eagle_parser.h:392
const wxString GetFileExtension() const override
Returns the file extension for the PLUGIN.
opt_ecoord size
Definition: eagle_parser.h:600
ZONE * loadPolygon(wxXmlNode *aPolyNode)
Load a copper or keepout polygon and adds it to the board.
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:747
void Value(const char *aValue)
modify the last path node's value
Definition: eagle_parser.h:125
Container for design settings for a BOARD object.
int layer_front_most
Definition: eagle_parser.h:551
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const PROPERTIES *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
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...
int mlMaxStopFrame
solder mask, maximum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:95
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition: zone.h:194
double mvCreamFrame
Definition: eagle_plugin.h:93
opt_bool cream
Definition: eagle_parser.h:734
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).