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 <[email protected]>
5  * Copyright (C) 2012-2022 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 #include <project.h>
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( wxT( " )]}'\"" ) );
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() == wxT( "param" ) )
221  {
222  const wxString& name = child->GetAttribute( wxT( "name" ) );
223  const wxString& value = child->GetAttribute( wxT( "value" ) );
224 
225  if( name == wxT( "psElongationLong" ) )
226  psElongationLong = wxAtoi( value );
227  else if( name == wxT( "psElongationOffset" ) )
228  psElongationOffset = wxAtoi( value );
229  else if( name == wxT( "mvStopFrame" ) )
230  value.ToCDouble( &mvStopFrame );
231  else if( name == wxT( "mvCreamFrame" ) )
232  value.ToCDouble( &mvCreamFrame );
233  else if( name == wxT( "mlMinStopFrame" ) )
234  mlMinStopFrame = parseEagle( value );
235  else if( name == wxT( "mlMaxStopFrame" ) )
236  mlMaxStopFrame = parseEagle( value );
237  else if( name == wxT( "mlMinCreamFrame" ) )
238  mlMinCreamFrame = parseEagle( value );
239  else if( name == wxT( "mlMaxCreamFrame" ) )
240  mlMaxCreamFrame = parseEagle( value );
241  else if( name == wxT( "srRoundness" ) )
242  value.ToCDouble( &srRoundness );
243  else if( name == wxT( "srMinRoundness" ) )
244  srMinRoundness = parseEagle( value );
245  else if( name == wxT( "srMaxRoundness" ) )
246  srMaxRoundness = parseEagle( value );
247  else if( name == wxT( "psTop" ) )
248  psTop = wxAtoi( value );
249  else if( name == wxT( "psBottom" ) )
250  psBottom = wxAtoi( value );
251  else if( name == wxT( "psFirst" ) )
252  psFirst = wxAtoi( value );
253  else if( name == wxT( "rvPadTop" ) )
254  value.ToCDouble( &rvPadTop );
255  else if( name == wxT( "rlMinPadTop" ) )
256  rlMinPadTop = parseEagle( value );
257  else if( name == wxT( "rlMaxPadTop" ) )
258  rlMaxPadTop = parseEagle( value );
259  else if( name == wxT( "rvViaOuter" ) )
260  value.ToCDouble( &rvViaOuter );
261  else if( name == wxT( "rlMinViaOuter" ) )
262  rlMinViaOuter = parseEagle( value );
263  else if( name == wxT( "rlMaxViaOuter" ) )
264  rlMaxViaOuter = parseEagle( value );
265  else if( name == wxT( "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  designSettings.m_MinClearance = KiROUND( m_rules->mdWireWire );
406 
407  NETCLASS defaults( wxT( "dummy" ) );
408 
409  auto finishNetclass =
410  [&]( NETCLASSPTR netclass )
411  {
412  // If Eagle has a clearance matrix then we'll build custom rules from that.
413  // Netclasses should just be the board minimum clearance.
414  netclass->SetClearance( KiROUND( designSettings.m_MinClearance ) );
415 
416  if( netclass->GetTrackWidth() == INT_MAX )
417  netclass->SetTrackWidth( defaults.GetTrackWidth() );
418 
419  if( netclass->GetViaDiameter() == INT_MAX )
420  netclass->SetViaDiameter( defaults.GetViaDiameter() );
421 
422  if( netclass->GetViaDrill() == INT_MAX )
423  netclass->SetViaDrill( defaults.GetViaDrill() );
424  };
425 
426  finishNetclass( designSettings.GetNetClasses().GetDefault() );
427 
428  for( const std::pair<const wxString, NETCLASSPTR>& entry : designSettings.GetNetClasses() )
429  finishNetclass( entry.second );
430 
433 
434  fn.SetExt( wxT( "kicad_dru" ) );
435  wxFile rulesFile( fn.GetFullPath(), wxFile::write );
436  rulesFile.Write( m_customRules );
437 
438  // should be empty, else missing m_xpath->pop()
439  wxASSERT( m_xpath->Contents().size() == 0 );
440  }
441  catch( const XML_PARSER_ERROR &exc )
442  {
443  wxString errmsg = exc.what();
444 
445  errmsg += wxT( "\[email protected] " );
446  errmsg += m_xpath->Contents();
447 
448  THROW_IO_ERROR( errmsg );
449  }
450 
451  // IO_ERROR exceptions are left uncaught, they pass upwards from here.
452 
453  // Ensure the copper layers count is a multiple of 2
454  // Pcbnew does not like boards with odd layers count
455  // (these boards cannot exist. they actually have a even layers count)
456  int lyrcnt = m_board->GetCopperLayerCount();
457 
458  if( (lyrcnt % 2) != 0 )
459  {
460  lyrcnt++;
461  m_board->SetCopperLayerCount( lyrcnt );
462  }
463 
464  centerBoard();
465 
466  deleter.release();
467  return m_board;
468 }
469 
470 
472 {
473  std::vector<FOOTPRINT*> retval;
474 
475  for( std::pair<wxString, FOOTPRINT*> fp : m_templates )
476  retval.push_back( static_cast<FOOTPRINT*>( fp.second->Clone() ) );
477 
478  return retval;
479 }
480 
481 
482 void EAGLE_PLUGIN::init( const PROPERTIES* aProperties )
483 {
484  m_hole_count = 0;
485  m_min_trace = 0;
486  m_min_hole = 0;
487  m_min_via = 0;
488  m_min_annulus = 0;
489  m_xpath->clear();
490  m_pads_to_nets.clear();
491 
492  m_board = nullptr;
493  m_props = aProperties;
494 
495 
496  delete m_rules;
497  m_rules = new ERULES();
498 }
499 
500 
502 {
503  // All cu layers are invalid until we see them in the <layers> section while
504  // loading either a board or library. See loadLayerDefs().
505  for( unsigned i = 0; i < arrayDim(m_cu_map); ++i )
506  m_cu_map[i] = -1;
507 }
508 
509 
510 void EAGLE_PLUGIN::loadAllSections( wxXmlNode* aDoc )
511 {
512  wxXmlNode* drawing = MapChildren( aDoc )["drawing"];
513  NODE_MAP drawingChildren = MapChildren( drawing );
514 
515  wxXmlNode* board = drawingChildren["board"];
516  NODE_MAP boardChildren = MapChildren( board );
517 
518  auto count_children = [this]( wxXmlNode* aNode )
519  {
520  if( aNode )
521  {
522  wxXmlNode* child = aNode->GetChildren();
523 
524  while( child )
525  {
526  m_totalCount++;
527  child = child->GetNext();
528  }
529  }
530  };
531 
532  wxXmlNode* designrules = boardChildren["designrules"];
533  wxXmlNode* layers = drawingChildren["layers"];
534  wxXmlNode* plain = boardChildren["plain"];
535  wxXmlNode* classes = boardChildren["classes"];
536  wxXmlNode* signals = boardChildren["signals"];
537  wxXmlNode* libs = boardChildren["libraries"];
538  wxXmlNode* elems = boardChildren["elements"];
539 
540  if( m_progressReporter )
541  {
542  m_totalCount = 0;
543  m_doneCount = 0;
544 
545  count_children( designrules );
546  count_children( layers );
547  count_children( plain );
548  count_children( signals );
549  count_children( elems );
550 
551  while( libs )
552  {
553  count_children( MapChildren( libs )["packages"] );
554  libs = libs->GetNext();
555  }
556 
557  // Rewind
558  libs = boardChildren["libraries"];
559  }
560 
561  m_xpath->push( "eagle.drawing" );
562 
563  {
564  m_xpath->push( "board" );
565 
566  loadDesignRules( designrules );
567 
568  m_xpath->pop();
569  }
570 
571  {
572  m_xpath->push( "layers" );
573 
574  loadLayerDefs( layers );
576 
577  m_xpath->pop();
578  }
579 
580  {
581  m_xpath->push( "board" );
582 
583  loadPlain( plain );
584  loadClasses( classes );
585  loadSignals( signals );
586  loadLibraries( libs );
587  loadElements( elems );
588 
589  m_xpath->pop();
590  }
591 
592  m_xpath->pop(); // "eagle.drawing"
593 }
594 
595 
596 void EAGLE_PLUGIN::loadDesignRules( wxXmlNode* aDesignRules )
597 {
598  if( aDesignRules )
599  {
600  m_xpath->push( "designrules" );
601  m_rules->parse( aDesignRules, [this](){ checkpoint(); } );
602  m_xpath->pop(); // "designrules"
603  }
604 }
605 
606 
607 void EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers )
608 {
609  if( !aLayers )
610  return;
611 
612  ELAYERS cu; // copper layers
613 
614  // Get the first layer and iterate
615  wxXmlNode* layerNode = aLayers->GetChildren();
616 
617  m_eagleLayers.clear();
618  m_eagleLayersIds.clear();
619 
620  while( layerNode )
621  {
622  ELAYER elayer( layerNode );
623  m_eagleLayers.insert( std::make_pair( elayer.number, elayer ) );
624  m_eagleLayersIds.insert( std::make_pair( elayer.name, elayer.number ) );
625 
626  // find the subset of layers that are copper and active
627  if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) )
628  cu.push_back( elayer );
629 
630  layerNode = layerNode->GetNext();
631  }
632 
633  // establish cu layer map:
634  int ki_layer_count = 0;
635 
636  for( EITER it = cu.begin(); it != cu.end(); ++it, ++ki_layer_count )
637  {
638  if( ki_layer_count == 0 )
639  {
640  m_cu_map[it->number] = F_Cu;
641  }
642  else if( ki_layer_count == int( cu.size()-1 ) )
643  {
644  m_cu_map[it->number] = B_Cu;
645  }
646  else
647  {
648  // some eagle boards do not have contiguous layer number sequences.
649  m_cu_map[it->number] = ki_layer_count;
650  }
651  }
652 
653  // Set the layer names and cu count if we're loading a board.
654  if( m_board )
655  {
656  m_board->SetCopperLayerCount( cu.size() );
657 
658  for( EITER it = cu.begin(); it != cu.end(); ++it )
659  {
660  PCB_LAYER_ID layer = kicad_layer( it->number );
661 
662  // these function provide their own protection against non enabled layers:
663  if( layer >= 0 && layer < PCB_LAYER_ID_COUNT ) // layer should be valid
664  {
665  m_board->SetLayerName( layer, FROM_UTF8( it->name.c_str() ) );
666  m_board->SetLayerType( layer, LT_SIGNAL );
667  }
668 
669  // could map the colors here
670  }
671  }
672 }
673 
674 
675 #define DIMENSION_PRECISION 1 // 0.001 mm
676 
677 
678 void EAGLE_PLUGIN::loadPlain( wxXmlNode* aGraphics )
679 {
680  if( !aGraphics )
681  return;
682 
683  m_xpath->push( "plain" );
684 
685  // Get the first graphic and iterate
686  wxXmlNode* gr = aGraphics->GetChildren();
687 
688  // (polygon | wire | text | circle | rectangle | frame | hole)*
689  while( gr )
690  {
691  checkpoint();
692 
693  wxString grName = gr->GetName();
694 
695  if( grName == wxT( "wire" ) )
696  {
697  m_xpath->push( "wire" );
698 
699  EWIRE w( gr );
700  PCB_LAYER_ID layer = kicad_layer( w.layer );
701 
702  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
703  wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
704 
705  if( layer != UNDEFINED_LAYER )
706  {
707  PCB_SHAPE* shape = new PCB_SHAPE( m_board );
708  int width = w.width.ToPcbUnits();
709 
710  // KiCad cannot handle zero or negative line widths
711  if( width <= 0 )
712  width = m_board->GetDesignSettings().GetLineThickness( layer );
713 
714  m_board->Add( shape, ADD_MODE::APPEND );
715 
716  if( !w.curve )
717  {
718  shape->SetShape( SHAPE_T::SEGMENT );
719  shape->SetStart( start );
720  shape->SetEnd( end );
721  }
722  else
723  {
724  wxPoint center = ConvertArcCenter( start, end, *w.curve );
725 
726  shape->SetShape( SHAPE_T::ARC );
727  shape->SetCenter( center );
728  shape->SetStart( start );
729  shape->SetArcAngleAndEnd( *w.curve * -10.0, true ); // KiCad rotates the other way
730  }
731 
732  shape->SetLayer( layer );
733  shape->SetWidth( width );
734  }
735 
736  m_xpath->pop();
737  }
738  else if( grName == wxT( "text" ) )
739  {
740  m_xpath->push( "text" );
741 
742  ETEXT t( gr );
743  PCB_LAYER_ID layer = kicad_layer( t.layer );
744 
745  if( layer != UNDEFINED_LAYER )
746  {
747  PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
748  m_board->Add( pcbtxt, ADD_MODE::APPEND );
749 
750  pcbtxt->SetLayer( layer );
751  wxString kicadText = interpret_text( t.text );
752  pcbtxt->SetText( FROM_UTF8( kicadText.c_str() ) );
753  pcbtxt->SetTextPos( wxPoint( kicad_x( t.x ), kicad_y( t.y ) ) );
754 
755  double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
756  int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
757  pcbtxt->SetTextThickness( textThickness );
758  pcbtxt->SetTextSize( kicad_fontz( t.size, textThickness ) );
759 
760  int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT;
761 
762  if( t.rot )
763  {
764  int sign = t.rot->mirror ? -1 : 1;
765  pcbtxt->SetMirrored( t.rot->mirror );
766 
767  double degrees = t.rot->degrees;
768 
769  if( degrees == 90 || t.rot->spin )
770  {
771  pcbtxt->SetTextAngle( sign * t.rot->degrees * 10 );
772  }
773  else if( degrees == 180 )
774  {
775  align = -align;
776  }
777  else if( degrees == 270 )
778  {
779  pcbtxt->SetTextAngle( sign * 90 * 10 );
780  align = -align;
781  }
782  else
783  {
784  // Ok so text is not at 90,180 or 270 so do some funny stuff to get
785  // placement right.
786  if( ( degrees > 0 ) && ( degrees < 90 ) )
787  {
788  pcbtxt->SetTextAngle( sign * t.rot->degrees * 10 );
789  }
790  else if( ( degrees > 90 ) && ( degrees < 180 ) )
791  {
792  pcbtxt->SetTextAngle( sign * ( t.rot->degrees + 180 ) * 10 );
793  align = ETEXT::TOP_RIGHT;
794  }
795  else if( ( degrees > 180 ) && ( degrees < 270 ) )
796  {
797  pcbtxt->SetTextAngle( sign * ( t.rot->degrees - 180 ) * 10 );
798  align = ETEXT::TOP_RIGHT;
799  }
800  else if( ( degrees > 270 ) && ( degrees < 360 ) )
801  {
802  pcbtxt->SetTextAngle( sign * t.rot->degrees * 10 );
803  align = ETEXT::BOTTOM_LEFT;
804  }
805  }
806  }
807 
808  switch( align )
809  {
810  case ETEXT::CENTER:
813  break;
814 
815  case ETEXT::CENTER_LEFT:
818  break;
819 
820  case ETEXT::CENTER_RIGHT:
823  break;
824 
825  case ETEXT::TOP_CENTER:
828  break;
829 
830  case ETEXT::TOP_LEFT:
833  break;
834 
835  case ETEXT::TOP_RIGHT:
838  break;
839 
843  break;
844 
845  case ETEXT::BOTTOM_LEFT:
848  break;
849 
850  case ETEXT::BOTTOM_RIGHT:
853  break;
854  }
855  }
856 
857  m_xpath->pop();
858  }
859  else if( grName == wxT( "circle" ) )
860  {
861  m_xpath->push( "circle" );
862 
863  ECIRCLE c( gr );
864 
865  int width = c.width.ToPcbUnits();
866  int radius = c.radius.ToPcbUnits();
867 
869  || c.layer == EAGLE_LAYER::VRESTRICT )
870  {
871  ZONE* zone = new ZONE( m_board );
872  m_board->Add( zone, ADD_MODE::APPEND );
873 
874  setKeepoutSettingsToZone( zone, c.layer );
875 
876  // approximate circle as polygon with a edge every 10 degree
877  wxPoint center( kicad_x( c.x ), kicad_y( c.y ) );
878  int outlineRadius = radius + ( width / 2 );
879 
880  for( int angle = 0; angle < 360; angle += 10 )
881  {
882  wxPoint rotatedPoint( outlineRadius, 0 );
883  RotatePoint( &rotatedPoint, angle * 10. );
884  zone->AppendCorner( center + rotatedPoint, -1 );
885  }
886 
887  if( width > 0 )
888  {
889  zone->NewHole();
890  int innerRadius = radius - ( width / 2 );
891 
892  for( int angle = 0; angle < 360; angle += 10 )
893  {
894  wxPoint rotatedPoint( innerRadius, 0 );
895  RotatePoint( &rotatedPoint, angle * 10. );
896  zone->AppendCorner( center + rotatedPoint, 0 );
897  }
898  }
899 
901  ZONE::GetDefaultHatchPitch(), true );
902  }
903  else
904  {
905  PCB_LAYER_ID layer = kicad_layer( c.layer );
906 
907  if( layer != UNDEFINED_LAYER ) // unsupported layer
908  {
909  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::CIRCLE );
910  m_board->Add( shape, ADD_MODE::APPEND );
911  shape->SetFilled( false );
912  shape->SetLayer( layer );
913  shape->SetStart( wxPoint( kicad_x( c.x ), kicad_y( c.y ) ) );
914  shape->SetEnd( wxPoint( kicad_x( c.x ) + radius, kicad_y( c.y ) ) );
915  shape->SetWidth( width );
916  }
917  }
918 
919  m_xpath->pop();
920  }
921  else if( grName == wxT( "rectangle" ) )
922  {
923  // This seems to be a simplified rectangular [copper] zone, cannot find any
924  // net related info on it from the DTD.
925  m_xpath->push( "rectangle" );
926 
927  ERECT r( gr );
928  PCB_LAYER_ID layer = kicad_layer( r.layer );
929 
930  if( IsCopperLayer( layer ) )
931  {
932  // use a "netcode = 0" type ZONE:
933  ZONE* zone = new ZONE( m_board );
934  m_board->Add( zone, ADD_MODE::APPEND );
935 
936  zone->SetLayer( layer );
938 
940 
941  const int outlineIdx = -1; // this is the id of the copper zone main outline
942  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
943  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
944  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
945  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
946 
947  if( r.rot )
948  zone->Rotate( zone->GetPosition(), r.rot->degrees * 10 );
949 
950  // this is not my fault:
951  zone->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(),
952  true );
953  }
954 
955  m_xpath->pop();
956  }
957  else if( grName == wxT( "hole" ) )
958  {
959  m_xpath->push( "hole" );
960 
961  // Fabricate a FOOTPRINT with a single PAD_ATTRIB::NPTH pad.
962  // Use m_hole_count to gen up a unique name.
963 
964  FOOTPRINT* footprint = new FOOTPRINT( m_board );
965  m_board->Add( footprint, ADD_MODE::APPEND );
966  footprint->SetReference( wxString::Format( wxT( "@HOLE%d" ), m_hole_count++ ) );
967  footprint->Reference().SetVisible( false );
968 
969  packageHole( footprint, gr, true );
970 
971  m_xpath->pop();
972  }
973  else if( grName == wxT( "frame" ) )
974  {
975  // picture this
976  }
977  else if( grName == wxT( "polygon" ) )
978  {
979  m_xpath->push( "polygon" );
980  loadPolygon( gr );
981  m_xpath->pop(); // "polygon"
982  }
983  else if( grName == wxT( "dimension" ) )
984  {
985  const BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
986 
987  EDIMENSION d( gr );
988  PCB_LAYER_ID layer = kicad_layer( d.layer );
989  VECTOR2I pt1( kicad_x( d.x1 ), kicad_y( d.y1 ) );
990  VECTOR2I pt2( kicad_x( d.x2 ), kicad_y( d.y2 ) );
991  VECTOR2I pt3( kicad_x( d.x3 ), kicad_y( d.y3 ) );
992  wxSize textSize = designSettings.GetTextSize( layer );
993  int textThickness = designSettings.GetLineThickness( layer );
994 
995  if( d.textsize )
996  {
997  double ratio = 8; // DTD says 8 is default
998  textThickness = KiROUND( d.textsize->ToPcbUnits() * ratio / 100 );
999  textSize = kicad_fontz( *d.textsize, textThickness );
1000  }
1001 
1002  if( layer != UNDEFINED_LAYER )
1003  {
1004  if( d.dimensionType == wxT( "angle" ) )
1005  {
1006  // Kicad doesn't (at present) support angle dimensions
1007  }
1008  else if( d.dimensionType == wxT( "radius" ) )
1009  {
1010  // Radial dimensions added in 7.0....
1011  }
1012  else if( d.dimensionType == wxT( "leader" ) )
1013  {
1014  PCB_DIM_LEADER* leader = new PCB_DIM_LEADER( m_board );
1015  m_board->Add( leader, ADD_MODE::APPEND );
1016 
1017  leader->SetLayer( layer );
1018  leader->SetPrecision( DIMENSION_PRECISION );
1019 
1020  leader->SetStart( (wxPoint) pt1 );
1021  leader->SetEnd( (wxPoint) pt2 );
1022  leader->Text().SetPosition( (wxPoint) pt3 );
1023  leader->Text().SetTextSize( textSize );
1024  leader->Text().SetTextThickness( textThickness );
1025  leader->SetLineThickness( designSettings.GetLineThickness( layer ) );
1026  }
1027  else // horizontal, vertical, <default>, diameter
1028  {
1029  PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board );
1030  m_board->Add( dimension, ADD_MODE::APPEND );
1031 
1032  if( d.dimensionType )
1033  {
1034  // Eagle dimension graphic arms may have different lengths, but they look
1035  // incorrect in KiCad (the graphic is tilted). Make them even length in
1036  // such case.
1037  if( *d.dimensionType == wxT( "horizontal" ) )
1038  {
1039  int newY = ( pt1.y + pt2.y ) / 2;
1040  pt1.y = newY;
1041  pt2.y = newY;
1042  }
1043  else if( *d.dimensionType == wxT( "vertical" ) )
1044  {
1045  int newX = ( pt1.x + pt2.x ) / 2;
1046  pt1.x = newX;
1047  pt2.x = newX;
1048  }
1049  }
1050 
1051  dimension->SetLayer( layer );
1052  dimension->SetPrecision( DIMENSION_PRECISION );
1053 
1054  // The origin and end are assumed to always be in this order from eagle
1055  dimension->SetStart( (wxPoint) pt1 );
1056  dimension->SetEnd( (wxPoint) pt2 );
1057  dimension->Text().SetTextSize( textSize );
1058  dimension->Text().SetTextThickness( textThickness );
1059  dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
1060  dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1061 
1062  // check which axis the dimension runs in
1063  // because the "height" of the dimension is perpendicular to that axis
1064  // Note the check is just if two axes are close enough to each other
1065  // Eagle appears to have some rounding errors
1066  if( abs( pt1.x - pt2.x ) < 50000 ) // 50000 nm = 0.05 mm
1067  {
1068  int offset = pt3.x - pt1.x;
1069 
1070  if( pt1.y > pt2.y )
1071  dimension->SetHeight( offset );
1072  else
1073  dimension->SetHeight( -offset );
1074  }
1075  else if( abs( pt1.y - pt2.y ) < 50000 )
1076  {
1077  int offset = pt3.y - pt1.y;
1078 
1079  if( pt1.x > pt2.x )
1080  dimension->SetHeight( -offset );
1081  else
1082  dimension->SetHeight( offset );
1083  }
1084  else
1085  {
1086  int offset = GetLineLength( (wxPoint) pt3, (wxPoint) pt1 );
1087 
1088  if( pt1.y > pt2.y )
1089  dimension->SetHeight( offset );
1090  else
1091  dimension->SetHeight( -offset );
1092  }
1093  }
1094  }
1095  }
1096 
1097  // Get next graphic
1098  gr = gr->GetNext();
1099  }
1100 
1101  m_xpath->pop();
1102 }
1103 
1104 
1105 void EAGLE_PLUGIN::loadLibrary( wxXmlNode* aLib, const wxString* aLibName )
1106 {
1107  if( !aLib )
1108  return;
1109 
1110  // library will have <xmlattr> node, skip that and get the single packages node
1111  wxXmlNode* packages = MapChildren( aLib )["packages"];
1112 
1113  if( !packages )
1114  return;
1115 
1116  m_xpath->push( "packages" );
1117 
1118  // Create a FOOTPRINT for all the eagle packages, for use later via a copy constructor
1119  // to instantiate needed footprints in our BOARD. Save the FOOTPRINT templates in
1120  // a FOOTPRINT_MAP using a single lookup key consisting of libname+pkgname.
1121 
1122  // Get the first package and iterate
1123  wxXmlNode* package = packages->GetChildren();
1124 
1125  while( package )
1126  {
1127  checkpoint();
1128 
1129  m_xpath->push( "package", "name" );
1130 
1131  wxString pack_ref = package->GetAttribute( "name" );
1132  ReplaceIllegalFileNameChars( pack_ref, '_' );
1133 
1134  m_xpath->Value( pack_ref.ToUTF8() );
1135 
1136  wxString key = aLibName ? makeKey( *aLibName, pack_ref ) : pack_ref;
1137 
1138  FOOTPRINT* m = makeFootprint( package, pack_ref );
1139 
1140  // add the templating FOOTPRINT to the FOOTPRINT template factory "m_templates"
1141  std::pair<FOOTPRINT_MAP::iterator, bool> r = m_templates.insert( {key, m} );
1142 
1143  if( !r.second /* && !( m_props && m_props->Value( "ignore_duplicates" ) ) */ )
1144  {
1145  wxString lib = aLibName ? *aLibName : m_lib_path;
1146  const wxString& pkg = pack_ref;
1147 
1148  wxString emsg = wxString::Format( _( "<package> '%s' duplicated in <library> '%s'" ),
1149  pkg,
1150  lib );
1151  THROW_IO_ERROR( emsg );
1152  }
1153 
1154  m_xpath->pop();
1155 
1156  package = package->GetNext();
1157  }
1158 
1159  m_xpath->pop(); // "packages"
1160 }
1161 
1162 
1163 void EAGLE_PLUGIN::loadLibraries( wxXmlNode* aLibs )
1164 {
1165  if( !aLibs )
1166  return;
1167 
1168  m_xpath->push( "libraries.library", "name" );
1169 
1170  // Get the first library and iterate
1171  wxXmlNode* library = aLibs->GetChildren();
1172 
1173  while( library )
1174  {
1175  const wxString& lib_name = library->GetAttribute( "name" );
1176 
1177  m_xpath->Value( lib_name.c_str() );
1178  loadLibrary( library, &lib_name );
1179  library = library->GetNext();
1180  }
1181 
1182  m_xpath->pop();
1183 }
1184 
1185 
1186 void EAGLE_PLUGIN::loadElements( wxXmlNode* aElements )
1187 {
1188  if( !aElements )
1189  return;
1190 
1191  m_xpath->push( "elements.element", "name" );
1192 
1193  EATTR name;
1194  EATTR value;
1195  bool refanceNamePresetInPackageLayout;
1196  bool valueNamePresetInPackageLayout;
1197 
1198  // Get the first element and iterate
1199  wxXmlNode* element = aElements->GetChildren();
1200 
1201  while( element )
1202  {
1203  checkpoint();
1204 
1205  if( element->GetName() != wxT( "element" ) )
1206  {
1207  // Get next item
1208  element = element->GetNext();
1209  continue;
1210  }
1211 
1212  EELEMENT e( element );
1213 
1214  // use "NULL-ness" as an indication of presence of the attribute:
1215  EATTR* nameAttr = nullptr;
1216  EATTR* valueAttr = nullptr;
1217 
1218  m_xpath->Value( e.name.c_str() );
1219 
1220  wxString pkg_key = makeKey( e.library, e.package );
1221 
1222  FOOTPRINT_MAP::const_iterator it = m_templates.find( pkg_key );
1223 
1224  if( it == m_templates.end() )
1225  {
1226  wxString emsg = wxString::Format( _( "No '%s' package in library '%s'." ),
1227  FROM_UTF8( e.package.c_str() ),
1228  FROM_UTF8( e.library.c_str() ) );
1229  THROW_IO_ERROR( emsg );
1230  }
1231 
1232  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( it->second->Duplicate() );
1233 
1234  m_board->Add( footprint, ADD_MODE::APPEND );
1235 
1236  // update the nets within the pads of the clone
1237  for( PAD* pad : footprint->Pads() )
1238  {
1239  wxString pn_key = makeKey( e.name, pad->GetNumber() );
1240 
1241  NET_MAP_CITER ni = m_pads_to_nets.find( pn_key );
1242  if( ni != m_pads_to_nets.end() )
1243  {
1244  const ENET* enet = &ni->second;
1245  pad->SetNetCode( enet->netcode );
1246  }
1247  }
1248 
1249  refanceNamePresetInPackageLayout = true;
1250  valueNamePresetInPackageLayout = true;
1251  footprint->SetPosition( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
1252 
1253  // Is >NAME field set in package layout ?
1254  if( footprint->GetReference().size() == 0 )
1255  {
1256  footprint->Reference().SetVisible( false ); // No so no show
1257  refanceNamePresetInPackageLayout = false;
1258  }
1259 
1260  // Is >VALUE field set in package layout
1261  if( footprint->GetValue().size() == 0 )
1262  {
1263  footprint->Value().SetVisible( false ); // No so no show
1264  valueNamePresetInPackageLayout = false;
1265  }
1266 
1267  wxString reference = e.name;
1268 
1269  // EAGLE allows references to be single digits. This breaks KiCad
1270  // netlisting, which requires parts to have non-digit + digit
1271  // annotation. If the reference begins with a number, we prepend
1272  // 'UNK' (unknown) for the symbol designator.
1273  if( reference.find_first_not_of( "0123456789" ) != 0 )
1274  reference.Prepend( "UNK" );
1275 
1276  // EAGLE allows designator to start with # but that is used in KiCad
1277  // for symbols which do not have a footprint
1278  if( reference.find_first_not_of( "#" ) != 0 )
1279  reference.Prepend( "UNK" );
1280 
1281  // reference must end with a number but EAGLE does not enforce this
1282  if( reference.find_last_not_of( "0123456789" ) == (reference.Length()-1) )
1283  reference.Append( "0" );
1284 
1285  footprint->SetReference( reference );
1286  footprint->SetValue( FROM_UTF8( e.value.c_str() ) );
1287 
1288  if( !e.smashed )
1289  {
1290  // Not smashed so show NAME & VALUE
1291  if( valueNamePresetInPackageLayout )
1292  footprint->Value().SetVisible( true ); // Only if place holder in package layout
1293 
1294  if( refanceNamePresetInPackageLayout )
1295  footprint->Reference().SetVisible( true ); // Only if place holder in package layout
1296  }
1297  else if( *e.smashed == true )
1298  {
1299  // Smashed so set default to no show for NAME and VALUE
1300  footprint->Value().SetVisible( false );
1301  footprint->Reference().SetVisible( false );
1302 
1303  // initialize these to default values in case the <attribute> elements are not present.
1304  m_xpath->push( "attribute", "name" );
1305 
1306  // VALUE and NAME can have something like our text "effects" overrides
1307  // in SWEET and new schematic. Eagle calls these XML elements "attribute".
1308  // There can be one for NAME and/or VALUE both. Features present in the
1309  // EATTR override the ones established in the package only if they are
1310  // present here (except for rot, which if not present means angle zero).
1311  // So the logic is a bit different than in packageText() and in plain text.
1312 
1313  // Get the first attribute and iterate
1314  wxXmlNode* attribute = element->GetChildren();
1315 
1316  while( attribute )
1317  {
1318  if( attribute->GetName() != wxT( "attribute" ) )
1319  {
1320  attribute = attribute->GetNext();
1321  continue;
1322  }
1323 
1324  EATTR a( attribute );
1325 
1326  if( a.name == wxT( "NAME" ) )
1327  {
1328  name = a;
1329  nameAttr = &name;
1330 
1331  // do we have a display attribute ?
1332  if( a.display )
1333  {
1334  // Yes!
1335  switch( *a.display )
1336  {
1337  case EATTR::VALUE :
1338  {
1339  nameAttr->name = reference;
1340 
1341  if( refanceNamePresetInPackageLayout )
1342  footprint->Reference().SetVisible( true );
1343 
1344  break;
1345  }
1346 
1347  case EATTR::NAME :
1348  if( refanceNamePresetInPackageLayout )
1349  {
1350  footprint->SetReference( "NAME" );
1351  footprint->Reference().SetVisible( true );
1352  }
1353 
1354  break;
1355 
1356  case EATTR::BOTH :
1357  if( refanceNamePresetInPackageLayout )
1358  footprint->Reference().SetVisible( true );
1359 
1360  nameAttr->name = nameAttr->name + wxT( " = " ) + e.name;
1361  footprint->SetReference( wxT( "NAME = " ) + e.name );
1362  break;
1363 
1364  case EATTR::Off :
1365  footprint->Reference().SetVisible( false );
1366  break;
1367 
1368  default:
1369  nameAttr->name = e.name;
1370 
1371  if( refanceNamePresetInPackageLayout )
1372  footprint->Reference().SetVisible( true );
1373  }
1374  }
1375  else
1376  {
1377  // No display, so default is visible, and show value of NAME
1378  footprint->Reference().SetVisible( true );
1379  }
1380  }
1381  else if( a.name == wxT( "VALUE" ) )
1382  {
1383  value = a;
1384  valueAttr = &value;
1385 
1386  if( a.display )
1387  {
1388  // Yes!
1389  switch( *a.display )
1390  {
1391  case EATTR::VALUE :
1392  valueAttr->value = opt_wxString( e.value );
1393  footprint->SetValue( e.value );
1394 
1395  if( valueNamePresetInPackageLayout )
1396  footprint->Value().SetVisible( true );
1397 
1398  break;
1399 
1400  case EATTR::NAME :
1401  if( valueNamePresetInPackageLayout )
1402  footprint->Value().SetVisible( true );
1403 
1404  footprint->SetValue( wxT( "VALUE" ) );
1405  break;
1406 
1407  case EATTR::BOTH :
1408  if( valueNamePresetInPackageLayout )
1409  footprint->Value().SetVisible( true );
1410 
1411  valueAttr->value = opt_wxString( wxT( "VALUE = " ) + e.value );
1412  footprint->SetValue( wxT( "VALUE = " ) + e.value );
1413  break;
1414 
1415  case EATTR::Off :
1416  footprint->Value().SetVisible( false );
1417  break;
1418 
1419  default:
1420  valueAttr->value = opt_wxString( e.value );
1421 
1422  if( valueNamePresetInPackageLayout )
1423  footprint->Value().SetVisible( true );
1424  }
1425  }
1426  else
1427  {
1428  // No display, so default is visible, and show value of NAME
1429  footprint->Value().SetVisible( true );
1430  }
1431 
1432  }
1433 
1434  attribute = attribute->GetNext();
1435  }
1436 
1437  m_xpath->pop(); // "attribute"
1438  }
1439 
1440  orientFootprintAndText( footprint, e, nameAttr, valueAttr );
1441 
1442  // Set the local coordinates for the footprint text items
1443  footprint->Reference().SetLocalCoord();
1444  footprint->Value().SetLocalCoord();
1445 
1446  // Get next element
1447  element = element->GetNext();
1448  }
1449 
1450  m_xpath->pop(); // "elements.element"
1451 }
1452 
1453 
1454 ZONE* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode )
1455 {
1456  EPOLYGON p( aPolyNode );
1457  PCB_LAYER_ID layer = kicad_layer( p.layer );
1458  ZONE* zone = nullptr;
1459  bool keepout = ( p.layer == EAGLE_LAYER::TRESTRICT
1461  || p.layer == EAGLE_LAYER::VRESTRICT );
1462 
1463  if( layer == UNDEFINED_LAYER )
1464  {
1465  wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
1466  "was not mapped" ),
1467  eagle_layer_name( p.layer ), p.layer ) );
1468  return nullptr;
1469  }
1470 
1471  if( !IsCopperLayer( layer ) && !keepout )
1472  return nullptr;
1473 
1474  // use a "netcode = 0" type ZONE:
1475  zone = new ZONE( m_board );
1476  m_board->Add( zone, ADD_MODE::APPEND );
1477 
1478  if( !keepout )
1479  zone->SetLayer( layer );
1480  else
1481  setKeepoutSettingsToZone( zone, p.layer );
1482 
1483  // Get the first vertex and iterate
1484  wxXmlNode* vertex = aPolyNode->GetChildren();
1485  std::vector<EVERTEX> vertices;
1486 
1487  // Create a circular vector of vertices
1488  // The "curve" parameter indicates a curve from the current
1489  // to the next vertex, so we keep the first at the end as well
1490  // to allow the curve to link back
1491  while( vertex )
1492  {
1493  if( vertex->GetName() == wxT( "vertex" ) )
1494  vertices.emplace_back( vertex );
1495 
1496  vertex = vertex->GetNext();
1497  }
1498 
1499  vertices.push_back( vertices[0] );
1500 
1501  SHAPE_POLY_SET polygon;
1502  polygon.NewOutline();
1503 
1504  for( size_t i = 0; i < vertices.size() - 1; i++ )
1505  {
1506  EVERTEX v1 = vertices[i];
1507 
1508  // Append the corner
1509  polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) );
1510 
1511  if( v1.curve )
1512  {
1513  EVERTEX v2 = vertices[i + 1];
1514  wxPoint center = ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
1515  wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ),
1516  *v1.curve );
1517  double angle = DEG2RAD( *v1.curve );
1518  double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
1519  double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
1520  + pow( center.y - kicad_y( v1.y ), 2 ) );
1521 
1522  int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
1523  double delta_angle = angle / segCount;
1524 
1525  for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle );
1526  a -= delta_angle )
1527  {
1528  polygon.Append( KiROUND( radius * cos( a ) ) + center.x,
1529  KiROUND( radius * sin( a ) ) + center.y );
1530  }
1531  }
1532  }
1533 
1534  // Eagle traces the zone such that half of the pen width is outside the polygon.
1535  // We trace the zone such that the copper is completely inside.
1536  if( p.width.ToPcbUnits() > 0 )
1537  {
1540  }
1541 
1542  zone->AddPolygon( polygon.COutline( 0 ) );
1543 
1544  // If the pour is a cutout it needs to be set to a keepout
1545  if( p.pour == EPOLYGON::CUTOUT )
1546  {
1547  zone->SetIsRuleArea( true );
1548  zone->SetDoNotAllowVias( false );
1549  zone->SetDoNotAllowTracks( false );
1550  zone->SetDoNotAllowPads( false );
1551  zone->SetDoNotAllowFootprints( false );
1552  zone->SetDoNotAllowCopperPour( true );
1554  }
1555  else if( p.pour == EPOLYGON::HATCH )
1556  {
1557  int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * IU_PER_MILS;
1558 
1560  zone->SetHatchThickness( p.width.ToPcbUnits() );
1561  zone->SetHatchGap( spacing - p.width.ToPcbUnits() );
1562  zone->SetHatchOrientation( 0 );
1563  }
1564 
1565  // We divide the thickness by half because we are tracing _inside_ the zone outline
1566  // This means the radius of curvature will be twice the size for an equivalent EAGLE zone
1568  p.width.ToPcbUnits() / 2 ) );
1569 
1570  if( p.isolate )
1571  zone->SetLocalClearance( p.isolate->ToPcbUnits() );
1572  else
1573  zone->SetLocalClearance( 1 ); // @todo: set minimum clearance value based on board settings
1574 
1575  // missing == yes per DTD.
1576  bool thermals = !p.thermals || *p.thermals;
1578 
1579  if( thermals )
1580  {
1581  // FIXME: eagle calculates dimensions for thermal spokes
1582  // based on what the zone is connecting to.
1583  // (i.e. width of spoke is half of the smaller side of an smd pad)
1584  // This is a basic workaround
1585  zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
1586  zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 );
1587  }
1588 
1589  int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
1590  zone->SetPriority( rank );
1591 
1592  return zone;
1593 }
1594 
1595 
1597  const EATTR* aNameAttr, const EATTR* aValueAttr )
1598 {
1599  if( e.rot )
1600  {
1601  if( e.rot->mirror )
1602  {
1603  double orientation = e.rot->degrees + 180.0;
1604  aFootprint->SetOrientation( orientation * 10 );
1605  aFootprint->Flip( aFootprint->GetPosition(), false );
1606  }
1607  else
1608  {
1609  aFootprint->SetOrientation( e.rot->degrees * 10 );
1610  }
1611  }
1612 
1613  orientFPText( aFootprint, e, &aFootprint->Reference(), aNameAttr );
1614  orientFPText( aFootprint, e, &aFootprint->Value(), aValueAttr );
1615 }
1616 
1617 
1618 void EAGLE_PLUGIN::orientFPText( FOOTPRINT* aFootprint, const EELEMENT& e, FP_TEXT* aFPText,
1619  const EATTR* aAttr )
1620 {
1621  // Smashed part ?
1622  if( aAttr )
1623  {
1624  // Yes
1625  const EATTR& a = *aAttr;
1626 
1627  if( a.value )
1628  {
1629  aFPText->SetText( FROM_UTF8( a.value->c_str() ) );
1630  }
1631 
1632  if( a.x && a.y ) // OPT
1633  {
1634  wxPoint pos( kicad_x( *a.x ), kicad_y( *a.y ) );
1635  aFPText->SetTextPos( pos );
1636  }
1637 
1638  // Even though size and ratio are both optional, I am not seeing
1639  // a case where ratio is present but size is not.
1640  double ratio = 8;
1641 
1642  if( a.ratio )
1643  ratio = *a.ratio;
1644 
1645  wxSize fontz = aFPText->GetTextSize();
1646  int textThickness = KiROUND( fontz.y * ratio / 100 );
1647 
1648  aFPText->SetTextThickness( textThickness );
1649  if( a.size )
1650  {
1651  fontz = kicad_fontz( *a.size, textThickness );
1652  aFPText->SetTextSize( fontz );
1653  }
1654 
1655 
1656  int align = ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1657 
1658  if( a.align )
1659  align = a.align;
1660 
1661  // The "rot" in a EATTR seems to be assumed to be zero if it is not
1662  // present, and this zero rotation becomes an override to the
1663  // package's text field. If they did not want zero, they specify
1664  // what they want explicitly.
1665  double degrees = a.rot ? a.rot->degrees : 0;
1666  double orient; // relative to parent
1667 
1668  int sign = 1;
1669  bool spin = false;
1670 
1671  if( a.rot )
1672  {
1673  spin = a.rot->spin;
1674  sign = a.rot->mirror ? -1 : 1;
1675  aFPText->SetMirrored( a.rot->mirror );
1676  }
1677 
1678  if( degrees == 90 || degrees == 0 || spin )
1679  {
1680  orient = degrees - aFootprint->GetOrientation() / 10;
1681  aFPText->SetTextAngle( sign * orient * 10 );
1682  }
1683  else if( degrees == 180 )
1684  {
1685  orient = 0 - aFootprint->GetOrientation() / 10;
1686  aFPText->SetTextAngle( sign * orient * 10 );
1687  align = -align;
1688  }
1689  else if( degrees == 270 )
1690  {
1691  orient = 90 - aFootprint->GetOrientation() / 10;
1692  align = -align;
1693  aFPText->SetTextAngle( sign * orient * 10 );
1694  }
1695  else
1696  {
1697  orient = 90 - degrees - aFootprint->GetOrientation() / 10;
1698  aFPText->SetTextAngle( sign * orient * 10 );
1699  }
1700 
1701  switch( align )
1702  {
1703  case ETEXT::TOP_RIGHT:
1706  break;
1707 
1708  case ETEXT::BOTTOM_LEFT:
1711  break;
1712 
1713  case ETEXT::TOP_LEFT:
1716  break;
1717 
1718  case ETEXT::BOTTOM_RIGHT:
1721  break;
1722 
1723  case ETEXT::TOP_CENTER:
1726  break;
1727 
1728  case ETEXT::BOTTOM_CENTER:
1731  break;
1732 
1733  case ETEXT::CENTER:
1736  break;
1737 
1738  case ETEXT::CENTER_LEFT:
1741  break;
1742 
1743  case ETEXT::CENTER_RIGHT:
1746  break;
1747 
1748  default:
1749  ;
1750  }
1751  }
1752  else
1753  {
1754  // Part is not smash so use Lib default for NAME/VALUE // the text is per the original
1755  // package, sans <attribute>.
1756  double degrees = ( aFPText->GetTextAngle() + aFootprint->GetOrientation() ) / 10;
1757 
1758  // @todo there are a few more cases than these to contend with:
1759  if( ( !aFPText->IsMirrored() && ( abs( degrees ) == 180 || abs( degrees ) == 270 ) )
1760  || ( aFPText->IsMirrored() && ( degrees == 360 ) ) )
1761  {
1762  // ETEXT::TOP_RIGHT:
1765  }
1766  }
1767 }
1768 
1769 
1770 FOOTPRINT* EAGLE_PLUGIN::makeFootprint( wxXmlNode* aPackage, const wxString& aPkgName )
1771 {
1772  std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>( m_board );
1773 
1774  LIB_ID fpID;
1775  fpID.Parse( aPkgName, true );
1776  m->SetFPID( fpID );
1777 
1778  // Get the first package item and iterate
1779  wxXmlNode* packageItem = aPackage->GetChildren();
1780 
1781  // layer 27 is default layer for tValues
1782  // set default layer for created footprint
1783  PCB_LAYER_ID layer = kicad_layer( 27 );
1784  m.get()->Value().SetLayer( layer );
1785 
1786  while( packageItem )
1787  {
1788  const wxString& itemName = packageItem->GetName();
1789 
1790  if( itemName == wxT( "description" ) )
1791  m->SetDescription( FROM_UTF8( packageItem->GetNodeContent().c_str() ) );
1792  else if( itemName == wxT( "wire" ) )
1793  packageWire( m.get(), packageItem );
1794  else if( itemName == wxT( "pad" ) )
1795  packagePad( m.get(), packageItem );
1796  else if( itemName == wxT( "text" ) )
1797  packageText( m.get(), packageItem );
1798  else if( itemName == wxT( "rectangle" ) )
1799  packageRectangle( m.get(), packageItem );
1800  else if( itemName == wxT( "polygon" ) )
1801  packagePolygon( m.get(), packageItem );
1802  else if( itemName == wxT( "circle" ) )
1803  packageCircle( m.get(), packageItem );
1804  else if( itemName == wxT( "hole" ) )
1805  packageHole( m.get(), packageItem, false );
1806  else if( itemName == wxT( "smd" ) )
1807  packageSMD( m.get(), packageItem );
1808 
1809  packageItem = packageItem->GetNext();
1810  }
1811 
1812  return m.release();
1813 }
1814 
1815 
1816 void EAGLE_PLUGIN::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1817 {
1818  EWIRE w( aTree );
1819  PCB_LAYER_ID layer = kicad_layer( w.layer );
1820  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
1821  wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
1822  int width = w.width.ToPcbUnits();
1823 
1824  if( layer == UNDEFINED_LAYER )
1825  {
1826  wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) "
1827  "was not mapped" ),
1828  eagle_layer_name( w.layer ), w.layer ) );
1829  return;
1830  }
1831 
1832  // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
1833  if( width <= 0 )
1834  {
1835  BOARD* board = aFootprint->GetBoard();
1836 
1837  if( board )
1838  {
1839  width = board->GetDesignSettings().GetLineThickness( layer );
1840  }
1841  else
1842  {
1843  // When loading footprint libraries, there is no board so use the default KiCad
1844  // line widths.
1845  switch( layer )
1846  {
1847  case Edge_Cuts: width = Millimeter2iu( DEFAULT_EDGE_WIDTH ); break;
1848 
1849  case F_SilkS:
1850  case B_SilkS: width = Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ); break;
1851 
1852  case F_CrtYd:
1853  case B_CrtYd: width = Millimeter2iu( DEFAULT_COURTYARD_WIDTH ); break;
1854 
1855  default: width = Millimeter2iu( DEFAULT_LINE_WIDTH ); break;
1856  }
1857  }
1858  }
1859 
1860  // FIXME: the cap attribute is ignored because KiCad can't create lines with flat ends.
1861  FP_SHAPE* dwg;
1862 
1863  if( !w.curve )
1864  {
1865  dwg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
1866 
1867  dwg->SetStart0( start );
1868  dwg->SetEnd0( end );
1869  }
1870  else
1871  {
1872  dwg = new FP_SHAPE( aFootprint, SHAPE_T::ARC );
1873  wxPoint center = ConvertArcCenter( start, end, *w.curve );
1874 
1875  dwg->SetCenter0( center );
1876  dwg->SetStart0( start );
1877  dwg->SetArcAngleAndEnd0( *w.curve * -10.0, true ); // KiCad rotates the other way
1878  }
1879 
1880  dwg->SetLayer( layer );
1881  dwg->SetWidth( width );
1882  dwg->SetDrawCoord();
1883 
1884  aFootprint->Add( dwg );
1885 }
1886 
1887 
1888 void EAGLE_PLUGIN::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
1889 {
1890  // this is thru hole technology here, no SMDs
1891  EPAD e( aTree );
1892  int shape = EPAD::UNDEF;
1893  int eagleDrillz = e.drill.ToPcbUnits();
1894 
1895  PAD* pad = new PAD( aFootprint );
1896  aFootprint->Add( pad );
1897  transferPad( e, pad );
1898 
1899  if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
1900  shape = m_rules->psFirst;
1901  else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
1902  shape = m_rules->psTop;
1903  else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
1904  shape = m_rules->psBottom;
1905 
1906  pad->SetDrillSize( wxSize( eagleDrillz, eagleDrillz ) );
1907  pad->SetLayerSet( LSET::AllCuMask() );
1908 
1909  if( eagleDrillz < m_min_hole )
1910  m_min_hole = eagleDrillz;
1911 
1912  // Solder mask
1913  if( !e.stop || *e.stop == true ) // enabled by default
1914  pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
1915 
1916  if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
1917  e.shape = shape;
1918 
1919  if( e.shape )
1920  {
1921  switch( *e.shape )
1922  {
1923  case EPAD::ROUND:
1924  pad->SetShape( PAD_SHAPE::CIRCLE );
1925  break;
1926 
1927  case EPAD::OCTAGON:
1928  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
1929  pad->SetChamferPositions( RECT_CHAMFER_ALL );
1930  pad->SetChamferRectRatio( 1 - M_SQRT1_2 ); // Regular polygon
1931  break;
1932 
1933  case EPAD::LONG:
1934  pad->SetShape( PAD_SHAPE::OVAL );
1935  break;
1936 
1937  case EPAD::SQUARE:
1938  pad->SetShape( PAD_SHAPE::RECT );
1939  break;
1940 
1941  case EPAD::OFFSET:
1942  pad->SetShape( PAD_SHAPE::OVAL );
1943  break;
1944  }
1945  }
1946  else
1947  {
1948  // if shape is not present, our default is circle and that matches their default "round"
1949  }
1950 
1951  if( e.diameter && e.diameter->value > 0 )
1952  {
1953  int diameter = e.diameter->ToPcbUnits();
1954  pad->SetSize( wxSize( diameter, diameter ) );
1955  }
1956  else
1957  {
1958  double drillz = pad->GetDrillSize().x;
1959  double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
1960  annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
1961  int diameter = KiROUND( drillz + 2 * annulus );
1962  pad->SetSize( wxSize( KiROUND( diameter ), KiROUND( diameter ) ) );
1963  }
1964 
1965  if( pad->GetShape() == PAD_SHAPE::OVAL )
1966  {
1967  // The Eagle "long" pad is wider than it is tall; m_elongation is percent elongation
1968  wxSize sz = pad->GetSize();
1969  sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
1970  pad->SetSize( sz );
1971 
1972  if( e.shape && *e.shape == EPAD::OFFSET )
1973  {
1974  int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
1975  pad->SetOffset( wxPoint( offset, 0 ) );
1976  }
1977  }
1978 
1979  if( e.rot )
1980  {
1981  pad->SetOrientation( e.rot->degrees * 10 );
1982  }
1983 }
1984 
1985 
1986 void EAGLE_PLUGIN::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1987 {
1988  ETEXT t( aTree );
1989  PCB_LAYER_ID layer = kicad_layer( t.layer );
1990 
1991  if( layer == UNDEFINED_LAYER )
1992  {
1993  wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) "
1994  "was not mapped" ),
1995  eagle_layer_name( t.layer ), t.layer ) );
1996  return;
1997  }
1998 
1999  FP_TEXT* txt;
2000 
2001  if( t.text.MakeUpper() == wxT( ">NAME" ) )
2002  txt = &aFootprint->Reference();
2003  else if( t.text.MakeUpper() == wxT( ">VALUE" ) )
2004  txt = &aFootprint->Value();
2005  else
2006  {
2007  // FIXME: graphical text items are rotated for some reason.
2008  txt = new FP_TEXT( aFootprint );
2009  aFootprint->Add( txt );
2010  }
2011 
2012  txt->SetText( FROM_UTF8( t.text.c_str() ) );
2013 
2014  wxPoint pos( kicad_x( t.x ), kicad_y( t.y ) );
2015 
2016  txt->SetTextPos( pos );
2017  txt->SetPos0( pos - aFootprint->GetPosition() );
2018 
2019  txt->SetLayer( layer );
2020 
2021  double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
2022  int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
2023 
2024  txt->SetTextThickness( textThickness );
2025  txt->SetTextSize( kicad_fontz( t.size, textThickness ) );
2026 
2027  int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
2028 
2029  // An eagle package is never rotated, the DTD does not allow it.
2030  // angle -= aFootprint->GetOrienation();
2031 
2032  if( t.rot )
2033  {
2034  int sign = t.rot->mirror ? -1 : 1;
2035  txt->SetMirrored( t.rot->mirror );
2036 
2037  double degrees = t.rot->degrees;
2038 
2039  if( degrees == 90 || t.rot->spin )
2040  {
2041  txt->SetTextAngle( sign * degrees * 10 );
2042  }
2043  else if( degrees == 180 )
2044  {
2045  align = ETEXT::TOP_RIGHT;
2046  }
2047  else if( degrees == 270 )
2048  {
2049  align = ETEXT::TOP_RIGHT;
2050  txt->SetTextAngle( sign * 90 * 10 );
2051  }
2052  }
2053 
2054  switch( align )
2055  {
2056  case ETEXT::CENTER:
2059  break;
2060 
2061  case ETEXT::CENTER_LEFT:
2064  break;
2065 
2066  case ETEXT::CENTER_RIGHT:
2069  break;
2070 
2071  case ETEXT::TOP_CENTER:
2074  break;
2075 
2076  case ETEXT::TOP_LEFT:
2079  break;
2080 
2081  case ETEXT::TOP_RIGHT:
2084  break;
2085 
2086  case ETEXT::BOTTOM_CENTER:
2089  break;
2090 
2091  case ETEXT::BOTTOM_LEFT:
2094  break;
2095 
2096  case ETEXT::BOTTOM_RIGHT:
2099  break;
2100  }
2101 }
2102 
2103 
2104 void EAGLE_PLUGIN::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2105 {
2106  ERECT r( aTree );
2107 
2108  if( r.layer == EAGLE_LAYER::TRESTRICT || r.layer == EAGLE_LAYER::BRESTRICT
2109  || r.layer == EAGLE_LAYER::VRESTRICT )
2110  {
2111  FP_ZONE* zone = new FP_ZONE( aFootprint );
2112  aFootprint->Add( zone, ADD_MODE::APPEND );
2113 
2114  setKeepoutSettingsToZone( zone, r.layer );
2115 
2116  const int outlineIdx = -1; // this is the id of the copper zone main outline
2117  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
2118  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
2119  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
2120  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
2121 
2122  if( r.rot )
2123  {
2124  wxPoint center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
2125  ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
2126  zone->Rotate( center, r.rot->degrees * 10 );
2127  }
2128 
2130  ZONE::GetDefaultHatchPitch(), true );
2131  }
2132  else
2133  {
2134  PCB_LAYER_ID layer = kicad_layer( r.layer );
2135 
2136  if( layer == UNDEFINED_LAYER )
2137  {
2138  wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) "
2139  "was not mapped" ),
2140  eagle_layer_name( r.layer ), r.layer ) );
2141  return;
2142  }
2143 
2144  FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2145 
2146  aFootprint->Add( dwg );
2147 
2148  dwg->SetLayer( layer );
2149  dwg->SetWidth( 0 );
2150  dwg->SetFilled( true );
2151 
2152  std::vector<wxPoint> pts;
2153 
2154  wxPoint start( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
2155  wxPoint end( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
2156 
2157  pts.push_back( start );
2158  pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
2159  pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
2160  pts.push_back( end );
2161 
2162  dwg->SetPolyPoints( pts );
2163 
2164  dwg->SetStart0( start );
2165  dwg->SetEnd0( end );
2166 
2167  if( r.rot )
2168  dwg->Rotate( dwg->GetCenter(), r.rot->degrees * 10 );
2169  }
2170 }
2171 
2172 
2173 void EAGLE_PLUGIN::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2174 {
2175  EPOLYGON p( aTree );
2176 
2177  std::vector<wxPoint> pts;
2178 
2179  // Get the first vertex and iterate
2180  wxXmlNode* vertex = aTree->GetChildren();
2181  std::vector<EVERTEX> vertices;
2182 
2183  // Create a circular vector of vertices
2184  // The "curve" parameter indicates a curve from the current
2185  // to the next vertex, so we keep the first at the end as well
2186  // to allow the curve to link back
2187  while( vertex )
2188  {
2189  if( vertex->GetName() == wxT( "vertex" ) )
2190  vertices.emplace_back( vertex );
2191 
2192  vertex = vertex->GetNext();
2193  }
2194 
2195  vertices.push_back( vertices[0] );
2196 
2197  for( size_t i = 0; i < vertices.size() - 1; i++ )
2198  {
2199  EVERTEX v1 = vertices[i];
2200 
2201  // Append the corner
2202  pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
2203 
2204  if( v1.curve )
2205  {
2206  EVERTEX v2 = vertices[i + 1];
2207  wxPoint center =
2208  ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
2209  wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
2210  double angle = DEG2RAD( *v1.curve );
2211  double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
2212  double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
2213  + pow( center.y - kicad_y( v1.y ), 2 ) );
2214 
2215  // Don't allow a zero-radius curve
2216  if( KiROUND( radius ) == 0 )
2217  radius = 1.0;
2218 
2219  int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
2220  double delta = angle / segCount;
2221 
2222  for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
2223  {
2224  pts.push_back( wxPoint( KiROUND( radius * cos( a ) ),
2225  KiROUND( radius * sin( a ) ) ) + center );
2226  }
2227  }
2228  }
2229 
2230  if( p.layer == EAGLE_LAYER::TRESTRICT
2232  || p.layer == EAGLE_LAYER::VRESTRICT )
2233  {
2234  FP_ZONE* zone = new FP_ZONE( aFootprint );
2235  aFootprint->Add( zone, ADD_MODE::APPEND );
2236 
2237  setKeepoutSettingsToZone( zone, p.layer );
2238 
2239  SHAPE_LINE_CHAIN outline( pts );
2240  outline.SetClosed( true );
2241  zone->Outline()->AddOutline( outline );
2242 
2244  ZONE::GetDefaultHatchPitch(), true );
2245  }
2246  else
2247  {
2248  PCB_LAYER_ID layer = kicad_layer( p.layer );
2249 
2250  if( layer == UNDEFINED_LAYER )
2251  {
2252  wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
2253  "was not mapped" ),
2254  eagle_layer_name( p.layer ), p.layer ) );
2255  return;
2256  }
2257 
2258  FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2259 
2260  aFootprint->Add( dwg );
2261 
2262  dwg->SetWidth( 0 ); // it's filled, no need for boundary width
2263  dwg->SetFilled( true );
2264  dwg->SetLayer( layer );
2265 
2266  dwg->SetPolyPoints( pts );
2267  dwg->SetStart0( *pts.begin() );
2268  dwg->SetEnd0( pts.back() );
2269  dwg->SetDrawCoord();
2270  dwg->GetPolyShape().Inflate( p.width.ToPcbUnits() / 2, 32,
2272  }
2273 }
2274 
2275 
2276 void EAGLE_PLUGIN::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2277 {
2278  ECIRCLE e( aTree );
2279 
2280  int width = e.width.ToPcbUnits();
2281  int radius = e.radius.ToPcbUnits();
2282 
2283  if( e.layer == EAGLE_LAYER::TRESTRICT
2285  || e.layer == EAGLE_LAYER::VRESTRICT )
2286  {
2287  FP_ZONE* zone = new FP_ZONE( aFootprint );
2288  aFootprint->Add( zone, ADD_MODE::APPEND );
2289 
2290  setKeepoutSettingsToZone( zone, e.layer );
2291 
2292  // approximate circle as polygon with a edge every 10 degree
2293  wxPoint center( kicad_x( e.x ), kicad_y( e.y ) );
2294  int outlineRadius = radius + ( width / 2 );
2295 
2296  for( int angle = 0; angle < 360; angle += 10 )
2297  {
2298  wxPoint rotatedPoint( outlineRadius, 0 );
2299  RotatePoint( &rotatedPoint, angle * 10. );
2300  zone->AppendCorner( center + rotatedPoint, -1 );
2301  }
2302 
2303  if( width > 0 )
2304  {
2305  zone->NewHole();
2306  int innerRadius = radius - ( width / 2 );
2307 
2308  for( int angle = 0; angle < 360; angle += 10 )
2309  {
2310  wxPoint rotatedPoint( innerRadius, 0 );
2311  RotatePoint( &rotatedPoint, angle * 10. );
2312  zone->AppendCorner( center + rotatedPoint, 0 );
2313  }
2314  }
2315 
2317  ZONE::GetDefaultHatchPitch(), true );
2318  }
2319  else
2320  {
2321  PCB_LAYER_ID layer = kicad_layer( e.layer );
2322 
2323  if( layer == UNDEFINED_LAYER )
2324  {
2325  wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) "
2326  "was not mapped" ),
2327  eagle_layer_name( e.layer ), e.layer ) );
2328  return;
2329  }
2330 
2331  FP_SHAPE* gr = new FP_SHAPE( aFootprint, SHAPE_T::CIRCLE );
2332 
2333  // width == 0 means filled circle
2334  if( width <= 0 )
2335  {
2336  width = radius;
2337  radius = radius / 2;
2338  gr->SetFilled( true );
2339  }
2340 
2341  aFootprint->Add( gr );
2342  gr->SetWidth( width );
2343 
2344  switch( (int) layer )
2345  {
2346  case UNDEFINED_LAYER:
2347  layer = Cmts_User;
2348  break;
2349  default:
2350  break;
2351  }
2352 
2353  gr->SetLayer( layer );
2354  gr->SetStart0( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
2355  gr->SetEnd0( wxPoint( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
2356  gr->SetDrawCoord();
2357  }
2358 }
2359 
2360 
2361 void EAGLE_PLUGIN::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
2362 {
2363  EHOLE e( aTree );
2364 
2365  if( e.drill.value == 0 )
2366  return;
2367 
2368  // we add a PAD_ATTRIB::NPTH pad to this footprint.
2369  PAD* pad = new PAD( aFootprint );
2370  aFootprint->Add( pad );
2371 
2372  pad->SetShape( PAD_SHAPE::CIRCLE );
2373  pad->SetAttribute( PAD_ATTRIB::NPTH );
2374 
2375  // Mechanical purpose only:
2376  // no offset, no net name, no pad name allowed
2377  // pad->SetOffset( wxPoint( 0, 0 ) );
2378  // pad->SetNumber( wxEmptyString );
2379 
2380  wxPoint padpos( kicad_x( e.x ), kicad_y( e.y ) );
2381 
2382  if( aCenter )
2383  {
2384  pad->SetPos0( wxPoint( 0, 0 ) );
2385  aFootprint->SetPosition( padpos );
2386  pad->SetPosition( padpos );
2387  }
2388  else
2389  {
2390  pad->SetPos0( padpos );
2391  pad->SetPosition( padpos + aFootprint->GetPosition() );
2392  }
2393 
2394  wxSize sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
2395 
2396  pad->SetDrillSize( sz );
2397  pad->SetSize( sz );
2398 
2399  pad->SetLayerSet( LSET::AllCuMask().set( B_Mask ).set( F_Mask ) );
2400 }
2401 
2402 
2403 void EAGLE_PLUGIN::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2404 {
2405  ESMD e( aTree );
2406  PCB_LAYER_ID layer = kicad_layer( e.layer );
2407 
2408  if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
2409  return;
2410 
2411  PAD* pad = new PAD( aFootprint );
2412  aFootprint->Add( pad );
2413  transferPad( e, pad );
2414 
2415  pad->SetShape( PAD_SHAPE::RECT );
2416  pad->SetAttribute( PAD_ATTRIB::SMD );
2417 
2418  wxSize padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
2419  pad->SetSize( padSize );
2420  pad->SetLayer( layer );
2421 
2422  const LSET front( 3, F_Cu, F_Paste, F_Mask );
2423  const LSET back( 3, B_Cu, B_Paste, B_Mask );
2424 
2425  if( layer == F_Cu )
2426  pad->SetLayerSet( front );
2427  else if( layer == B_Cu )
2428  pad->SetLayerSet( back );
2429 
2430  int minPadSize = std::min( padSize.x, padSize.y );
2431 
2432  // Rounded rectangle pads
2433  int roundRadius =
2434  eagleClamp( m_rules->srMinRoundness * 2, (int) ( minPadSize * m_rules->srRoundness ),
2435  m_rules->srMaxRoundness * 2 );
2436 
2437  if( e.roundness || roundRadius > 0 )
2438  {
2439  double roundRatio = (double) roundRadius / minPadSize / 2.0;
2440 
2441  // Eagle uses a different definition of roundness, hence division by 200
2442  if( e.roundness )
2443  roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
2444 
2445  pad->SetShape( PAD_SHAPE::ROUNDRECT );
2446  pad->SetRoundRectRadiusRatio( roundRatio );
2447  }
2448 
2449  if( e.rot )
2450  pad->SetOrientation( e.rot->degrees * 10 );
2451 
2452  pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
2453  (int) ( m_rules->mvCreamFrame * minPadSize ),
2454  m_rules->mlMaxCreamFrame ) );
2455 
2456  // Solder mask
2457  if( e.stop && *e.stop == false ) // enabled by default
2458  {
2459  if( layer == F_Cu )
2460  pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
2461  else if( layer == B_Cu )
2462  pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
2463  }
2464 
2465  // Solder paste (only for SMD pads)
2466  if( e.cream && *e.cream == false ) // enabled by default
2467  {
2468  if( layer == F_Cu )
2469  pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
2470  else if( layer == B_Cu )
2471  pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
2472  }
2473 }
2474 
2475 
2476 void EAGLE_PLUGIN::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
2477 {
2478  aPad->SetNumber( FROM_UTF8( aEaglePad.name.c_str() ) );
2479 
2480  // pad's "Position" is not relative to the footprint's,
2481  // whereas Pos0 is relative to the footprint's but is the unrotated coordinate.
2482  wxPoint padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
2483  aPad->SetPos0( padPos );
2484 
2485  // Solder mask
2486  const wxSize& padSize( aPad->GetSize() );
2487 
2490  (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
2491  m_rules->mlMaxStopFrame ) );
2492 
2493  // Solid connection to copper zones
2494  if( aEaglePad.thermals && !*aEaglePad.thermals )
2496 
2497  FOOTPRINT* footprint = aPad->GetParent();
2498  wxCHECK( footprint, /* void */ );
2499  RotatePoint( &padPos, footprint->GetOrientation() );
2500  aPad->SetPosition( padPos + footprint->GetPosition() );
2501 }
2502 
2503 
2505 {
2506  for( auto& t : m_templates )
2507  delete t.second;
2508 
2509  m_templates.clear();
2510 }
2511 
2512 
2513 void EAGLE_PLUGIN::loadClasses( wxXmlNode* aClasses )
2514 {
2516 
2517  m_xpath->push( "classes.class", "number" );
2518 
2519  std::vector<ECLASS> eClasses;
2520  wxXmlNode* classNode = aClasses->GetChildren();
2521 
2522  while( classNode )
2523  {
2524  checkpoint();
2525 
2526  ECLASS eClass( classNode );
2527  NETCLASSPTR netclass;
2528 
2529  if( eClass.name.CmpNoCase( wxT( "default" ) ) == 0 )
2530  {
2531  netclass = bds.GetNetClasses().GetDefault();
2532  }
2533  else
2534  {
2535  netclass.reset( new NETCLASS( eClass.name ) );
2536  m_board->GetDesignSettings().GetNetClasses().Add( netclass );
2537  }
2538 
2539  netclass->SetTrackWidth( INT_MAX );
2540  netclass->SetViaDiameter( INT_MAX );
2541  netclass->SetViaDrill( INT_MAX );
2542 
2543  eClasses.emplace_back( eClass );
2544  m_classMap[ eClass.number ] = netclass;
2545 
2546  // Get next class
2547  classNode = classNode->GetNext();
2548  }
2549 
2550  m_customRules = wxT( "(version 1)" );
2551 
2552  for( ECLASS& eClass : eClasses )
2553  {
2554  for( std::pair<const wxString&, ECOORD> entry : eClass.clearanceMap )
2555  {
2556  if( m_classMap[ entry.first ] != nullptr )
2557  {
2558  wxString rule;
2559  rule.Printf( wxT( "(rule \"class %s:%s\"\n"
2560  " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
2561  " (constraint clearance (min %smm)))\n" ),
2562  eClass.number,
2563  entry.first,
2564  eClass.name,
2565  m_classMap[ entry.first ]->GetName(),
2566  StringFromValue( EDA_UNITS::MILLIMETRES, entry.second.ToPcbUnits() ) );
2567 
2568  m_customRules += wxT( "\n" ) + rule;
2569  }
2570  }
2571  }
2572 
2573  m_xpath->pop(); // "classes.class"
2574 }
2575 
2576 
2577 void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
2578 {
2579  ZONES zones; // per net
2580  int netCode = 1;
2581 
2582  m_xpath->push( "signals.signal", "name" );
2583 
2584  // Get the first signal and iterate
2585  wxXmlNode* net = aSignals->GetChildren();
2586 
2587  while( net )
2588  {
2589  checkpoint();
2590 
2591  bool sawPad = false;
2592 
2593  zones.clear();
2594 
2595  const wxString& netName = escapeName( net->GetAttribute( "name" ) );
2596  NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, netName, netCode );
2597  NETCLASSPTR netclass;
2598 
2599  if( net->HasAttribute( "class" ) )
2600  {
2601  netclass = m_classMap[ net->GetAttribute( "class" ) ];
2602 
2603  netclass->Add( netName );
2604  netInfo->SetNetClass( netclass );
2605  }
2606 
2607  m_board->Add( netInfo );
2608 
2609  m_xpath->Value( netName.c_str() );
2610 
2611  // Get the first net item and iterate
2612  wxXmlNode* netItem = net->GetChildren();
2613 
2614  // (contactref | polygon | wire | via)*
2615  while( netItem )
2616  {
2617  const wxString& itemName = netItem->GetName();
2618 
2619  if( itemName == wxT( "wire" ) )
2620  {
2621  m_xpath->push( "wire" );
2622 
2623  EWIRE w( netItem );
2624  PCB_LAYER_ID layer = kicad_layer( w.layer );
2625 
2626  if( IsCopperLayer( layer ) )
2627  {
2628  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
2629  double angle = 0.0;
2630  double end_angle = 0.0;
2631  double radius = 0.0;
2632  double delta_angle = 0.0;
2633  wxPoint center;
2634 
2635  int width = w.width.ToPcbUnits();
2636 
2637  if( width < m_min_trace )
2638  m_min_trace = width;
2639 
2640  if( netclass && width < netclass->GetTrackWidth() )
2641  netclass->SetTrackWidth( width );
2642 
2643  if( w.curve )
2644  {
2645  center = ConvertArcCenter( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ),
2646  wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ),
2647  *w.curve );
2648 
2649  angle = DEG2RAD( *w.curve );
2650 
2651  end_angle = atan2( kicad_y( w.y2 ) - center.y,
2652  kicad_x( w.x2 ) - center.x );
2653 
2654  radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
2655  pow( center.y - kicad_y( w.y1 ), 2 ) );
2656 
2657  int segs = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
2658  *w.curve );
2659  delta_angle = angle / segs;
2660  }
2661 
2662  while( fabs( angle ) > fabs( delta_angle ) )
2663  {
2664  wxASSERT( radius > 0.0 );
2665  wxPoint end( KiROUND( radius * cos( end_angle + angle ) + center.x ),
2666  KiROUND( radius * sin( end_angle + angle ) + center.y ) );
2667 
2668  PCB_TRACK* t = new PCB_TRACK( m_board );
2669 
2670  t->SetPosition( start );
2671  t->SetEnd( end );
2672  t->SetWidth( width );
2673  t->SetLayer( layer );
2674  t->SetNetCode( netCode );
2675 
2676  m_board->Add( t );
2677 
2678  start = end;
2679  angle -= delta_angle;
2680  }
2681 
2682  PCB_TRACK* t = new PCB_TRACK( m_board );
2683 
2684  t->SetPosition( start );
2685  t->SetEnd( wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
2686  t->SetWidth( width );
2687  t->SetLayer( layer );
2688  t->SetNetCode( netCode );
2689 
2690  m_board->Add( t );
2691  }
2692  else
2693  {
2694  // put non copper wires where the sun don't shine.
2695  }
2696 
2697  m_xpath->pop();
2698  }
2699  else if( itemName == wxT( "via" ) )
2700  {
2701  m_xpath->push( "via" );
2702  EVIA v( netItem );
2703 
2704  if( v.layer_front_most > v.layer_back_most )
2705  std::swap( v.layer_front_most, v.layer_back_most );
2706 
2707  PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most );
2708  PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most );
2709 
2710  if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most )
2711  && layer_front_most != layer_back_most )
2712  {
2713  int kidiam;
2714  int drillz = v.drill.ToPcbUnits();
2715  PCB_VIA* via = new PCB_VIA( m_board );
2716  m_board->Add( via );
2717 
2718  if( v.diam )
2719  {
2720  kidiam = v.diam->ToPcbUnits();
2721  via->SetWidth( kidiam );
2722  }
2723  else
2724  {
2725  double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
2726  annulus = eagleClamp( m_rules->rlMinViaOuter, annulus,
2728  kidiam = KiROUND( drillz + 2 * annulus );
2729  via->SetWidth( kidiam );
2730  }
2731 
2732  via->SetDrill( drillz );
2733 
2734  // make sure the via diameter respects the restring rules
2735 
2736  if( !v.diam || via->GetWidth() <= via->GetDrill() )
2737  {
2738  double annulus =
2740  (double) ( via->GetWidth() / 2 - via->GetDrill() ),
2742  via->SetWidth( drillz + 2 * annulus );
2743  }
2744 
2745  if( kidiam < m_min_via )
2746  m_min_via = kidiam;
2747 
2748  if( netclass && kidiam < netclass->GetViaDiameter() )
2749  netclass->SetViaDiameter( kidiam );
2750 
2751  if( drillz < m_min_hole )
2752  m_min_hole = drillz;
2753 
2754  if( netclass && drillz < netclass->GetViaDrill() )
2755  netclass->SetViaDrill( drillz );
2756 
2757  if( ( kidiam - drillz ) / 2 < m_min_annulus )
2758  m_min_annulus = ( kidiam - drillz ) / 2;
2759 
2760  if( layer_front_most == F_Cu && layer_back_most == B_Cu )
2761  {
2762  via->SetViaType( VIATYPE::THROUGH );
2763  }
2767  else if( v.layer_back_most - v.layer_front_most == 1 )
2768  {
2769  via->SetViaType( VIATYPE::MICROVIA );
2770  }
2771  else
2772  {
2773  via->SetViaType( VIATYPE::BLIND_BURIED );
2774  }
2775 
2776  wxPoint pos( kicad_x( v.x ), kicad_y( v.y ) );
2777 
2778  via->SetLayerPair( layer_front_most, layer_back_most );
2779  via->SetPosition( pos );
2780  via->SetEnd( pos );
2781 
2782  via->SetNetCode( netCode );
2783  }
2784 
2785  m_xpath->pop();
2786  }
2787 
2788  else if( itemName == wxT( "contactref" ) )
2789  {
2790  m_xpath->push( "contactref" );
2791  // <contactref element="RN1" pad="7"/>
2792 
2793  const wxString& reference = netItem->GetAttribute( "element" );
2794  const wxString& pad = netItem->GetAttribute( "pad" );
2795  wxString key = makeKey( reference, pad ) ;
2796 
2797  m_pads_to_nets[ key ] = ENET( netCode, netName );
2798 
2799  m_xpath->pop();
2800 
2801  sawPad = true;
2802  }
2803 
2804  else if( itemName == wxT( "polygon" ) )
2805  {
2806  m_xpath->push( "polygon" );
2807  auto* zone = loadPolygon( netItem );
2808 
2809  if( zone )
2810  {
2811  zones.push_back( zone );
2812 
2813  if( !zone->GetIsRuleArea() )
2814  zone->SetNetCode( netCode );
2815  }
2816 
2817  m_xpath->pop(); // "polygon"
2818  }
2819 
2820  netItem = netItem->GetNext();
2821  }
2822 
2823  if( zones.size() && !sawPad )
2824  {
2825  // KiCad does not support an unconnected zone with its own non-zero netcode,
2826  // but only when assigned netcode = 0 w/o a name...
2827  for( ZONE* zone : zones )
2828  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
2829 
2830  // therefore omit this signal/net.
2831  }
2832  else
2833  {
2834  netCode++;
2835  }
2836 
2837  // Get next signal
2838  net = net->GetNext();
2839  }
2840 
2841  m_xpath->pop(); // "signals.signal"
2842 }
2843 
2844 
2845 std::map<wxString, PCB_LAYER_ID> EAGLE_PLUGIN::DefaultLayerMappingCallback(
2846  const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2847 {
2848  std::map<wxString, PCB_LAYER_ID> layer_map;
2849 
2850  for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
2851  {
2852  PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
2853  layer_map.emplace( layer.Name, layerId );
2854  }
2855 
2856  return layer_map;
2857 }
2858 
2859 
2861 {
2862  std::vector<INPUT_LAYER_DESC> inputDescs;
2863 
2864  for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
2865  {
2866  const ELAYER& eLayer = layerPair.second;
2867 
2868  INPUT_LAYER_DESC layerDesc;
2869  std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
2870  defaultKicadLayer( eLayer.number );
2871 
2872  if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
2873  continue; // Ignore unused copper layers
2874 
2875  layerDesc.Name = eLayer.name;
2876 
2877  inputDescs.push_back( layerDesc );
2878  }
2879 
2880  if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
2881  dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
2882 
2883  m_layer_map = m_layer_mapping_handler( inputDescs );
2884 
2885  if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
2886  dynamic_cast<wxWindow*>( m_progressReporter )->Show();
2887 }
2888 
2889 
2890 PCB_LAYER_ID EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) const
2891 {
2892  auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
2893  return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
2894 }
2895 
2896 
2897 std::tuple<PCB_LAYER_ID, LSET, bool> EAGLE_PLUGIN::defaultKicadLayer( int aEagleLayer ) const
2898 {
2899  // eagle copper layer:
2900  if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
2901  {
2902  LSET copperLayers;
2903 
2904  for( int copperLayer : m_cu_map )
2905  {
2906  if( copperLayer >= 0 )
2907  copperLayers[copperLayer] = true;
2908  }
2909 
2910  return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
2911  }
2912 
2913  int kiLayer = UNSELECTED_LAYER;
2914  bool required = false;
2915  LSET permittedLayers;
2916 
2917  permittedLayers.set();
2918 
2919  // translate non-copper eagle layer to pcbnew layer
2920  switch( aEagleLayer )
2921  {
2922  // Eagle says "Dimension" layer, but it's for board perimeter
2924  kiLayer = Edge_Cuts;
2925  required = true;
2926  permittedLayers = LSET( 1, Edge_Cuts );
2927  break;
2928 
2929  case EAGLE_LAYER::TPLACE:
2930  kiLayer = F_SilkS;
2931  break;
2932  case EAGLE_LAYER::BPLACE:
2933  kiLayer = B_SilkS;
2934  break;
2935  case EAGLE_LAYER::TNAMES:
2936  kiLayer = F_SilkS;
2937  break;
2938  case EAGLE_LAYER::BNAMES:
2939  kiLayer = B_SilkS;
2940  break;
2941  case EAGLE_LAYER::TVALUES:
2942  kiLayer = F_Fab;
2943  break;
2944  case EAGLE_LAYER::BVALUES:
2945  kiLayer = B_Fab;
2946  break;
2947  case EAGLE_LAYER::TSTOP:
2948  kiLayer = F_Mask;
2949  break;
2950  case EAGLE_LAYER::BSTOP:
2951  kiLayer = B_Mask;
2952  break;
2953  case EAGLE_LAYER::TCREAM:
2954  kiLayer = F_Paste;
2955  break;
2956  case EAGLE_LAYER::BCREAM:
2957  kiLayer = B_Paste;
2958  break;
2959  case EAGLE_LAYER::TFINISH:
2960  kiLayer = F_Mask;
2961  break;
2962  case EAGLE_LAYER::BFINISH:
2963  kiLayer = B_Mask;
2964  break;
2965  case EAGLE_LAYER::TGLUE:
2966  kiLayer = F_Adhes;
2967  break;
2968  case EAGLE_LAYER::BGLUE:
2969  kiLayer = B_Adhes;
2970  break;
2971  case EAGLE_LAYER::DOCUMENT:
2972  kiLayer = Cmts_User;
2973  break;
2975  kiLayer = Cmts_User;
2976  break;
2978  kiLayer = Cmts_User;
2979  break;
2980 
2981  // Packages show the future chip pins on SMD parts using layer 51.
2982  // This is an area slightly smaller than the PAD/SMD copper area.
2983  // Carry those visual aids into the FOOTPRINT on the fabrication layer,
2984  // not silkscreen. This is perhaps not perfect, but there is not a lot
2985  // of other suitable paired layers
2986  case EAGLE_LAYER::TDOCU:
2987  kiLayer = F_Fab;
2988  break;
2989  case EAGLE_LAYER::BDOCU:
2990  kiLayer = B_Fab;
2991  break;
2992 
2993  // these layers are defined as user layers. put them on ECO layers
2995  kiLayer = Eco1_User;
2996  break;
2998  kiLayer = Eco2_User;
2999  break;
3000 
3001  // these will also appear in the ratsnest, so there's no need for a warning
3002  case EAGLE_LAYER::UNROUTED:
3003  kiLayer = Dwgs_User;
3004  break;
3005 
3006  case EAGLE_LAYER::TKEEPOUT:
3007  kiLayer = F_CrtYd;
3008  break;
3009  case EAGLE_LAYER::BKEEPOUT:
3010  kiLayer = B_CrtYd;
3011  break;
3012 
3013  case EAGLE_LAYER::MILLING:
3014  case EAGLE_LAYER::TTEST:
3015  case EAGLE_LAYER::BTEST:
3016  case EAGLE_LAYER::HOLES:
3017  default:
3018  kiLayer = UNDEFINED_LAYER;
3019  break;
3020  }
3021 
3022  return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
3023 }
3024 
3025 
3026 const wxString& EAGLE_PLUGIN::eagle_layer_name( int aLayer ) const
3027 {
3028  static const wxString unknown( "unknown" );
3029  auto it = m_eagleLayers.find( aLayer );
3030  return it == m_eagleLayers.end() ? unknown : it->second.name;
3031 }
3032 
3033 
3034 int EAGLE_PLUGIN::eagle_layer_id( const wxString& aLayerName ) const
3035 {
3036  static const int unknown = -1;
3037  auto it = m_eagleLayersIds.find( aLayerName );
3038  return it == m_eagleLayersIds.end() ? unknown : it->second;
3039 }
3040 
3041 
3043 {
3044  if( m_props )
3045  {
3046  UTF8 page_width;
3047  UTF8 page_height;
3048 
3049  if( m_props->Value( "page_width", &page_width ) &&
3050  m_props->Value( "page_height", &page_height ) )
3051  {
3053 
3054  int w = atoi( page_width.c_str() );
3055  int h = atoi( page_height.c_str() );
3056 
3057  int desired_x = ( w - bbbox.GetWidth() ) / 2;
3058  int desired_y = ( h - bbbox.GetHeight() ) / 2;
3059 
3060  m_board->Move( wxPoint( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
3061  }
3062  }
3063 }
3064 
3065 
3066 wxDateTime EAGLE_PLUGIN::getModificationTime( const wxString& aPath )
3067 {
3068  // File hasn't been loaded yet.
3069  if( aPath.IsEmpty() )
3070  return wxDateTime::Now();
3071 
3072  wxFileName fn( aPath );
3073 
3074  if( fn.IsFileReadable() )
3075  return fn.GetModificationTime();
3076  else
3077  return wxDateTime( 0.0 );
3078 }
3079 
3080 
3081 void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath )
3082 {
3083  try
3084  {
3085  wxDateTime modtime = getModificationTime( aLibPath );
3086 
3087  // Fixes assertions in wxWidgets debug builds for the wxDateTime object. Refresh the
3088  // cache if either of the wxDateTime objects are invalid or the last file modification
3089  // time differs from the current file modification time.
3090  bool load = !m_mod_time.IsValid() || !modtime.IsValid() || m_mod_time != modtime;
3091 
3092  if( aLibPath != m_lib_path || load )
3093  {
3094  wxXmlNode* doc;
3095  LOCALE_IO toggle; // toggles on, then off, the C locale.
3096 
3097  deleteTemplates();
3098 
3099  // Set this before completion of loading, since we rely on it for
3100  // text of an exception. Delay setting m_mod_time until after successful load
3101  // however.
3102  m_lib_path = aLibPath;
3103 
3104  // 8 bit "filename" should be encoded according to disk filename encoding,
3105  // (maybe this is current locale, maybe not, its a filesystem issue),
3106  // and is not necessarily utf8.
3107  string filename = (const char*) aLibPath.char_str( wxConvFile );
3108 
3109  // Load the document
3110  wxFileName fn( filename );
3111  wxFFileInputStream stream( fn.GetFullPath() );
3112  wxXmlDocument xmlDocument;
3113 
3114  if( !stream.IsOk() || !xmlDocument.Load( stream ) )
3115  {
3116  THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ),
3117  fn.GetFullPath() ) );
3118  }
3119 
3120  doc = xmlDocument.GetRoot();
3121 
3122  wxXmlNode* drawing = MapChildren( doc )["drawing"];
3123  NODE_MAP drawingChildren = MapChildren( drawing );
3124 
3125  // clear the cu map and then rebuild it.
3126  clear_cu_map();
3127 
3128  m_xpath->push( "eagle.drawing.layers" );
3129  wxXmlNode* layers = drawingChildren["layers"];
3130  loadLayerDefs( layers );
3132  m_xpath->pop();
3133 
3134  m_xpath->push( "eagle.drawing.library" );
3135  wxXmlNode* library = drawingChildren["library"];
3136  loadLibrary( library, nullptr );
3137  m_xpath->pop();
3138 
3139  m_mod_time = modtime;
3140  }
3141  }
3142  catch(...){}
3143  // TODO: Handle exceptions
3144  // catch( file_parser_error fpe )
3145  // {
3146  // // for xml_parser_error, what() has the line number in it,
3147  // // but no byte offset. That should be an adequate error message.
3148  // THROW_IO_ERROR( fpe.what() );
3149  // }
3150  //
3151  // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
3152  // // so one catch should be OK for all errors.
3153  // catch( ptree_error pte )
3154  // {
3155  // string errmsg = pte.what();
3156  //
3157  // errmsg += " @\n";
3158  // errmsg += m_xpath->Contents();
3159  //
3160  // THROW_IO_ERROR( errmsg );
3161  // }
3162 }
3163 
3164 
3165 void EAGLE_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
3166  bool aBestEfforts, const PROPERTIES* aProperties )
3167 {
3168  wxString errorMsg;
3169 
3170  init( aProperties );
3171 
3172  try
3173  {
3174  cacheLib( aLibraryPath );
3175  }
3176  catch( const IO_ERROR& ioe )
3177  {
3178  errorMsg = ioe.What();
3179  }
3180 
3181  // Some of the files may have been parsed correctly so we want to add the valid files to
3182  // the library.
3183 
3184  for( FOOTPRINT_MAP::const_iterator it = m_templates.begin(); it != m_templates.end(); ++it )
3185  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
3186 
3187  if( !errorMsg.IsEmpty() && !aBestEfforts )
3188  THROW_IO_ERROR( errorMsg );
3189 }
3190 
3191 
3192 FOOTPRINT* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
3193  const wxString& aFootprintName, bool aKeepUUID,
3194  const PROPERTIES* aProperties )
3195 {
3196  init( aProperties );
3197  cacheLib( aLibraryPath );
3198  FOOTPRINT_MAP::const_iterator it = m_templates.find( aFootprintName );
3199 
3200  if( it == m_templates.end() )
3201  return nullptr;
3202 
3203  // Return a copy of the template
3204  FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate();
3205  copy->SetParent( nullptr );
3206  return copy;
3207 }
3208 
3209 
3210 void EAGLE_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const
3211 {
3212  PLUGIN::FootprintLibOptions( aListToAppendTo );
3213 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:209
void SetReference(const wxString &aReference)
Definition: footprint.h:475
std::vector< ZONE * > ZONES
Definition: eagle_plugin.h:44
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:759
#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:120
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:189
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:210
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:222
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
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:269
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:308
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:747
const wxString & GetValue() const
Definition: footprint.h:488
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.
void SetFilled(bool aFlag)
Definition: eda_shape.h:92
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:135
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:273
void FootprintLibOptions(PROPERTIES *aProperties) const override
Append supported PLUGIN options to aListToAppenTo along with internationalized descriptions.
virtual void SetPosition(const wxPoint &aPos) override
Definition: pcb_text.h:81
opt_ecoord diam
Definition: eagle_parser.h:554
int psTop
Shape of the top pads.
Definition: eagle_plugin.h:100
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:164
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:616
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aHatchStyle, int aHatchPitch, bool aRebuildHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:875
int GetX() const
Definition: eda_rect.h:107
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
ERULES * m_rules
Eagle design rules.
Definition: eagle_plugin.h:294
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:738
void SetArcAngleAndEnd0(double aAngle, bool aCheckNegativeAngle=false)
Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: fp_shape.cpp:194
void SetTextAngle(double aAngle) override
Definition: pcb_text.cpp:104
PCB_TEXT & Text()
A progress reporter interface for use in multi-threaded environments.
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:250
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:312
Keep track of what we are working on within a PTREE.
Definition: eagle_parser.h:110
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:116
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:267
void parse(wxXmlNode *aRules, std::function< void()> aCheckpoint)
percent over 100%.
int GetWidth() const
Definition: eda_rect.h:118
int mlMinStopFrame
solder mask, minimum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:95
opt_double ratio
Definition: eagle_parser.h:645
int number
Definition: eagle_parser.h:828
double GetOrientation() const
Definition: footprint.h:191
double GetTextAngle() const
Definition: eda_text.h:195
void SetCopperLayerCount(int aCount)
Definition: board.cpp:461
#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:107
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:41
std::map< wxString, int > m_eagleLayersIds
Eagle layer ids stored by layer name.
Definition: eagle_plugin.h:288
opt_bool smashed
Definition: eagle_parser.h:819
Definition: bitmap.cpp:64
void SetArcAngleAndEnd(double aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:547
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: eagle_plugin.h:310
wxString name
Definition: eagle_parser.h:695
NET_MAP::const_iterator NET_MAP_CITER
Definition: eagle_plugin.h:46
void init(const PROPERTIES *aProperties)
initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed.
double degrees
Definition: eagle_parser.h:474
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:258
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:147
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:410
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:317
double rvViaOuter
copper annulus is this percent of via hole
Definition: eagle_plugin.h:118
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:302
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
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:295
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:286
PADS & Pads()
Definition: footprint.h:169
This file contains miscellaneous commonly used macros and functions.
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:502
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:744
unsigned m_doneCount
Definition: eagle_plugin.h:311
FP_TEXT & Reference()
Definition: footprint.h:503
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:110
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:689
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:206
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:608
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
int GetTrackWidth() const
Definition: netclass.h:128
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:516
#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:599
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:307
ECOORD x
Definition: eagle_parser.h:801
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
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:287
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
opt_ecoord y
Definition: eagle_parser.h:599
opt_ecoord textsize
Definition: eagle_parser.h:628
Eagle thru hole pad.
Definition: eagle_parser.h:706
static int GetDefaultHatchPitch()
Definition: zone.cpp:1038
opt_ecoord spacing
Definition: eagle_parser.h:774
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:748
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)
void SetOrientation(double aNewAngle)
Definition: footprint.cpp:1681
const wxSize & GetTextSize() const
Definition: eda_text.h:259
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:343
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:223
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
void packagePad(FOOTPRINT *aFootprint, wxXmlNode *aTree)
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.h:79
void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1491
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:378
ECOORD dx
Definition: eagle_parser.h:730
int GetViaDrill() const
Definition: netclass.h:136
wxString m_lib_path
Definition: eagle_plugin.h:320
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:466
#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:746
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
void SetWidth(int aWidth)
Definition: eda_shape.h:97
NETCLASSES & GetNetClasses() const
a few functions useful in geometry calculations.
subset of eagle.drawing.board.designrules in the XML document
Definition: eagle_plugin.h:50
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:476
void SetFileName(const wxString &aFileName)
Definition: board.h:227
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:496
void SetCenter(const wxPoint &aCenter)
Definition: eda_shape.cpp:419
double mdWireWire
wire to wire spacing I presume.
Definition: eagle_plugin.h:121
wxString text
Definition: eagle_parser.h:639
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
Definition: fp_shape.cpp:81
int m_min_trace
smallest trace we find on Load(), in BIU.
Definition: eagle_plugin.h:315
E_SERIE r
Definition: eserie.cpp:41
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:298
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:104
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:119
Eagle net.
Definition: eagle_parser.h:453
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:227
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:808
int psElongationLong
Definition: eagle_plugin.h:86
int psFirst
Shape of the first pads.
Definition: eagle_plugin.h:102
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.
wxString m_customRules
Definition: eagle_plugin.h:292
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:1439
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:268
Use thermal relief for pads.
Parse an Eagle "attribute" XML element.
Definition: eagle_parser.h:594
bool Add(const NETCLASSPTR &aNetclass)
Add aNetclass and puts it into this NETCLASSES container.
Definition: netclass.cpp:90
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:283
Handle the data for a net.
Definition: netinfo.h:66
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 SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: eda_shape.cpp:1085
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:97
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:815
#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:216
wxString name
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:289
opt_ecoord diameter
Definition: eagle_parser.h:709
void packageCircle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
double DEG2RAD(double deg)
Definition: trigo.h:229
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
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:222
Structure holding common properties for through-hole and SMD pads.
Definition: eagle_parser.h:693
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:745
wxString Name
Imported layer name as displayed in original application.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
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:71
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:284
int GetCopperLayerCount() const
Definition: board.cpp:455
wxDateTime m_mod_time
Definition: eagle_plugin.h:321
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)
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:100
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 SetLocalCoord()
Definition: fp_text.cpp:209
int GetViaDiameter() const
Definition: netclass.h:132
double mvStopFrame
solderpaste mask, expressed as percentage of the smaller pad/via dimension
Definition: eagle_plugin.h:91
#define IU_PER_MILS
Definition: plotter.cpp:130
int GetY() const
Definition: eda_rect.h:108
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:318
NET_MAP m_pads_to_nets
net list
Definition: eagle_plugin.h:300
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:187
wxString number
opt_bool first
Definition: eagle_parser.h:721
Eagle wire.
Definition: eagle_parser.h:491
ECOORD x
Definition: eagle_parser.h:549
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:204
void SetThermalReliefGap(int aThermalReliefGap)
Definition: zone.h:183
void orientFPText(FOOTPRINT *aFootprint, const EELEMENT &e, FP_TEXT *aFPText, const EATTR *aAttr)
NETCLASSPTR GetDefault() const
Definition: netclass.h:253
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
Definition: zone_settings.h:46
A leader is a dimension-like object pointing to a specific point.
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:36
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:242
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:180
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:316
Eagle dimension element.
Definition: eagle_parser.h:620
double rvPadTop
top pad size as percent of drill size
Definition: eagle_plugin.h:112
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:798
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:1562
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
wxString library
Definition: eagle_parser.h:813
int psBottom
Shape of the bottom pads.
Definition: eagle_plugin.h:101
static constexpr int Millimeter2iu(double mm)
void SetCenter0(const wxPoint &aPt)
Definition: fp_shape.cpp:162
double rlMinPadTop
minimum copper annulus on through hole pads
Definition: eagle_plugin.h:115
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
double rlMinViaOuter
minimum copper annulus on via
Definition: eagle_plugin.h:119
unsigned m_totalCount
for progress reporting
Definition: eagle_plugin.h:313
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:372
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)
int mlMaxCreamFrame
solder paste mask, maximum size (Eagle mils, here nanometers)
Definition: eagle_plugin.h:98
A specialization of ZONE for use in footprints.
Definition: zone.h:947
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:110
opt_bool active
Definition: eagle_parser.h:833
std::map< wxString, NETCLASSPTR > m_classMap
Eagle class number to KiCad netclass.
Definition: eagle_plugin.h:291
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:188
int ToPcbUnits() const
Definition: eagle_parser.h:430
std::unordered_map< wxString, wxXmlNode * > NODE_MAP
Definition: eagle_parser.h:47
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:749
void loadClasses(wxXmlNode *aClasses)
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:96
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition: zone.h:194
double mvCreamFrame
Definition: eagle_plugin.h:94
opt_bool cream
Definition: eagle_parser.h:734
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).