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( " )]}'\"" );
156 
157  if( i + 1 != aText.size() && escapeChars.Find( aText[i + 1] ) == wxNOT_FOUND )
158  {
159  sectionOpen = true;
160  text.Append( '~' );
161  }
162  else
163  {
164  text.Append( aText[ i ] );
165  }
166  continue;
167  }
168 
169  if( aText[i] == ',' && sectionOpen )
170  {
171  text.Append( '~' );
172  sectionOpen = false;
173  }
174 
175  text.Append( aText[ i ] );
176  }
177 
178  return text;
179 }
180 
181 
182 static void setKeepoutSettingsToZone( ZONE* aZone, LAYER_NUM aLayer )
183 {
184  if( aLayer == EAGLE_LAYER::TRESTRICT || aLayer == EAGLE_LAYER::BRESTRICT )
185  {
186  aZone->SetIsRuleArea( true );
187  aZone->SetDoNotAllowVias( true );
188  aZone->SetDoNotAllowTracks( true );
189  aZone->SetDoNotAllowCopperPour( true );
190  aZone->SetDoNotAllowPads( true );
191  aZone->SetDoNotAllowFootprints( false );
192 
193  if( aLayer == EAGLE_LAYER::TRESTRICT ) // front layer keepout
194  aZone->SetLayer( F_Cu );
195  else // bottom layer keepout
196  aZone->SetLayer( B_Cu );
197  }
198  else if( aLayer == EAGLE_LAYER::VRESTRICT )
199  {
200  aZone->SetIsRuleArea( true );
201  aZone->SetDoNotAllowVias( true );
202  aZone->SetDoNotAllowTracks( false );
203  aZone->SetDoNotAllowCopperPour( false );
204  aZone->SetDoNotAllowPads( false );
205  aZone->SetDoNotAllowFootprints( false );
206 
207  aZone->SetLayerSet( LSET::AllCuMask() );
208  }
209 }
210 
211 
212 void ERULES::parse( wxXmlNode* aRules, std::function<void()> aCheckpoint )
213 {
214  wxXmlNode* child = aRules->GetChildren();
215 
216  while( child )
217  {
218  aCheckpoint();
219 
220  if( child->GetName() == "param" )
221  {
222  const wxString& name = child->GetAttribute( "name" );
223  const wxString& value = child->GetAttribute( "value" );
224 
225  if( name == "psElongationLong" )
226  psElongationLong = wxAtoi( value );
227  else if( name == "psElongationOffset" )
228  psElongationOffset = wxAtoi( value );
229  else if( name == "mvStopFrame" )
230  value.ToCDouble( &mvStopFrame );
231  else if( name == "mvCreamFrame" )
232  value.ToCDouble( &mvCreamFrame );
233  else if( name == "mlMinStopFrame" )
234  mlMinStopFrame = parseEagle( value );
235  else if( name == "mlMaxStopFrame" )
236  mlMaxStopFrame = parseEagle( value );
237  else if( name == "mlMinCreamFrame" )
238  mlMinCreamFrame = parseEagle( value );
239  else if( name == "mlMaxCreamFrame" )
240  mlMaxCreamFrame = parseEagle( value );
241  else if( name == "srRoundness" )
242  value.ToCDouble( &srRoundness );
243  else if( name == "srMinRoundness" )
244  srMinRoundness = parseEagle( value );
245  else if( name == "srMaxRoundness" )
246  srMaxRoundness = parseEagle( value );
247  else if( name == "psTop" )
248  psTop = wxAtoi( value );
249  else if( name == "psBottom" )
250  psBottom = wxAtoi( value );
251  else if( name == "psFirst" )
252  psFirst = wxAtoi( value );
253  else if( name == "rvPadTop" )
254  value.ToCDouble( &rvPadTop );
255  else if( name == "rlMinPadTop" )
256  rlMinPadTop = parseEagle( value );
257  else if( name == "rlMaxPadTop" )
258  rlMaxPadTop = parseEagle( value );
259  else if( name == "rvViaOuter" )
260  value.ToCDouble( &rvViaOuter );
261  else if( name == "rlMinViaOuter" )
262  rlMinViaOuter = parseEagle( value );
263  else if( name == "rlMaxViaOuter" )
264  rlMaxViaOuter = parseEagle( value );
265  else if( name == "mdWireWire" )
266  mdWireWire = parseEagle( value );
267  }
268 
269  child = child->GetNext();
270  }
271 }
272 
273 
275  m_rules( new ERULES() ),
276  m_xpath( new XPATH() ),
277  m_progressReporter( nullptr ),
278  m_doneCount( 0 ),
279  m_lastProgressCount( 0 ),
280  m_totalCount( 0 ),
281  m_mod_time( wxDateTime::Now() )
282 {
283  using namespace std::placeholders;
284 
285  init( nullptr );
286  clear_cu_map();
288  this, _1 ) );
289 }
290 
291 
293 {
294  deleteTemplates();
295  delete m_rules;
296  delete m_xpath;
297 }
298 
299 
300 const wxString EAGLE_PLUGIN::PluginName() const
301 {
302  return wxT( "Eagle" );
303 }
304 
305 
306 const wxString EAGLE_PLUGIN::GetFileExtension() const
307 {
308  return wxT( "brd" );
309 }
310 
311 
313 {
314  const unsigned PROGRESS_DELTA = 50;
315 
316  if( m_progressReporter )
317  {
318  if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
319  {
321  / std::max( 1U, m_totalCount ) );
322 
324  THROW_IO_ERROR( ( "Open cancelled by user." ) );
325 
327  }
328  }
329 }
330 
331 
332 wxSize inline EAGLE_PLUGIN::kicad_fontz( const ECOORD& d, int aTextThickness ) const
333 {
334  // Eagle includes stroke thickness in the text size, KiCAD does not
335  int kz = d.ToPcbUnits();
336  return wxSize( kz - aTextThickness, kz - aTextThickness );
337 }
338 
339 
340 BOARD* EAGLE_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe,
341  const PROPERTIES* aProperties, PROJECT* aProject,
342  PROGRESS_REPORTER* aProgressReporter )
343 {
344  LOCALE_IO toggle; // toggles on, then off, the C locale.
345  wxXmlNode* doc;
346 
347  init( aProperties );
348 
349  m_board = aAppendToMe ? aAppendToMe : new BOARD();
350  m_progressReporter = aProgressReporter;
351 
352  // Give the filename to the board if it's new
353  if( !aAppendToMe )
354  m_board->SetFileName( aFileName );
355 
356  // delete on exception, if I own m_board, according to aAppendToMe
357  unique_ptr<BOARD> deleter( aAppendToMe ? nullptr : m_board );
358 
359  try
360  {
361  if( m_progressReporter )
362  {
363  m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
364 
366  THROW_IO_ERROR( ( "Open cancelled by user." ) );
367  }
368 
369  wxFileName fn = aFileName;
370 
371  // Load the document
372  wxFFileInputStream stream( fn.GetFullPath() );
373  wxXmlDocument xmlDocument;
374 
375  if( !stream.IsOk() || !xmlDocument.Load( stream ) )
376  {
377  THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ),
378  fn.GetFullPath() ) );
379  }
380 
381  doc = xmlDocument.GetRoot();
382 
383  m_min_trace = INT_MAX;
384  m_min_hole = INT_MAX;
385  m_min_via = INT_MAX;
386  m_min_annulus = INT_MAX;
387 
388  loadAllSections( doc );
389 
390  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
391 
392  if( m_min_trace < designSettings.m_TrackMinWidth )
393  designSettings.m_TrackMinWidth = m_min_trace;
394 
395  if( m_min_via < designSettings.m_ViasMinSize )
396  designSettings.m_ViasMinSize = m_min_via;
397 
398  if( m_min_hole < designSettings.m_MinThroughDrill )
399  designSettings.m_MinThroughDrill = m_min_hole;
400 
401  if( m_min_annulus < designSettings.m_ViasMinAnnularWidth )
402  designSettings.m_ViasMinAnnularWidth = m_min_annulus;
403 
404  if( m_rules->mdWireWire )
405  designSettings.m_MinClearance = KiROUND( m_rules->mdWireWire );
406 
407  NETCLASS defaults( "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( "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 += "\[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 == "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 == "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 == "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 == "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 == "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( "@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 == "frame" )
974  {
975  // picture this
976  }
977  else if( grName == "polygon" )
978  {
979  m_xpath->push( "polygon" );
980  loadPolygon( gr );
981  m_xpath->pop(); // "polygon"
982  }
983  else if( grName == "dimension" )
984  {
985  EDIMENSION d( gr );
986  PCB_LAYER_ID layer = kicad_layer( d.layer );
987 
988  if( layer != UNDEFINED_LAYER )
989  {
990  const BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
991  PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board );
992  m_board->Add( dimension, ADD_MODE::APPEND );
993 
994  if( d.dimensionType )
995  {
996  // Eagle dimension graphic arms may have different lengths, but they look
997  // incorrect in KiCad (the graphic is tilted). Make them even length in
998  // such case.
999  if( *d.dimensionType == "horizontal" )
1000  {
1001  int newY = ( d.y1.ToPcbUnits() + d.y2.ToPcbUnits() ) / 2;
1002  d.y1 = ECOORD( newY, ECOORD::EAGLE_UNIT::EU_NM );
1003  d.y2 = ECOORD( newY, ECOORD::EAGLE_UNIT::EU_NM );
1004  }
1005  else if( *d.dimensionType == "vertical" )
1006  {
1007  int newX = ( d.x1.ToPcbUnits() + d.x2.ToPcbUnits() ) / 2;
1008  d.x1 = ECOORD( newX, ECOORD::EAGLE_UNIT::EU_NM );
1009  d.x2 = ECOORD( newX, ECOORD::EAGLE_UNIT::EU_NM );
1010  }
1011  }
1012 
1013  dimension->SetLayer( layer );
1014  dimension->SetPrecision( DIMENSION_PRECISION );
1015 
1016  // The origin and end are assumed to always be in this order from eagle
1017  dimension->SetStart( wxPoint( kicad_x( d.x1 ), kicad_y( d.y1 ) ) );
1018  dimension->SetEnd( wxPoint( kicad_x( d.x2 ), kicad_y( d.y2 ) ) );
1019  dimension->Text().SetTextSize( designSettings.GetTextSize( layer ) );
1020  dimension->Text().SetTextThickness( designSettings.GetTextThickness( layer ) );
1021  dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
1022  dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1023 
1024  // check which axis the dimension runs in
1025  // because the "height" of the dimension is perpendicular to that axis
1026  // Note the check is just if two axes are close enough to each other
1027  // Eagle appears to have some rounding errors
1028  if( abs( ( d.x1 - d.x2 ).ToPcbUnits() ) < 50000 ) // 50000 nm = 0.05 mm
1029  dimension->SetHeight( kicad_x( d.x3 - d.x1 ) );
1030  else
1031  dimension->SetHeight( kicad_y( d.y3 - d.y1 ) );
1032  }
1033  }
1034 
1035  // Get next graphic
1036  gr = gr->GetNext();
1037  }
1038 
1039  m_xpath->pop();
1040 }
1041 
1042 
1043 void EAGLE_PLUGIN::loadLibrary( wxXmlNode* aLib, const wxString* aLibName )
1044 {
1045  if( !aLib )
1046  return;
1047 
1048  // library will have <xmlattr> node, skip that and get the single packages node
1049  wxXmlNode* packages = MapChildren( aLib )["packages"];
1050 
1051  if( !packages )
1052  return;
1053 
1054  m_xpath->push( "packages" );
1055 
1056  // Create a FOOTPRINT for all the eagle packages, for use later via a copy constructor
1057  // to instantiate needed footprints in our BOARD. Save the FOOTPRINT templates in
1058  // a FOOTPRINT_MAP using a single lookup key consisting of libname+pkgname.
1059 
1060  // Get the first package and iterate
1061  wxXmlNode* package = packages->GetChildren();
1062 
1063  while( package )
1064  {
1065  checkpoint();
1066 
1067  m_xpath->push( "package", "name" );
1068 
1069  wxString pack_ref = package->GetAttribute( "name" );
1070  ReplaceIllegalFileNameChars( pack_ref, '_' );
1071 
1072  m_xpath->Value( pack_ref.ToUTF8() );
1073 
1074  wxString key = aLibName ? makeKey( *aLibName, pack_ref ) : pack_ref;
1075 
1076  FOOTPRINT* m = makeFootprint( package, pack_ref );
1077 
1078  // add the templating FOOTPRINT to the FOOTPRINT template factory "m_templates"
1079  std::pair<FOOTPRINT_MAP::iterator, bool> r = m_templates.insert( {key, m} );
1080 
1081  if( !r.second /* && !( m_props && m_props->Value( "ignore_duplicates" ) ) */ )
1082  {
1083  wxString lib = aLibName ? *aLibName : m_lib_path;
1084  const wxString& pkg = pack_ref;
1085 
1086  wxString emsg = wxString::Format( _( "<package> '%s' duplicated in <library> '%s'" ),
1087  pkg,
1088  lib );
1089  THROW_IO_ERROR( emsg );
1090  }
1091 
1092  m_xpath->pop();
1093 
1094  package = package->GetNext();
1095  }
1096 
1097  m_xpath->pop(); // "packages"
1098 }
1099 
1100 
1101 void EAGLE_PLUGIN::loadLibraries( wxXmlNode* aLibs )
1102 {
1103  if( !aLibs )
1104  return;
1105 
1106  m_xpath->push( "libraries.library", "name" );
1107 
1108  // Get the first library and iterate
1109  wxXmlNode* library = aLibs->GetChildren();
1110 
1111  while( library )
1112  {
1113  const wxString& lib_name = library->GetAttribute( "name" );
1114 
1115  m_xpath->Value( lib_name.c_str() );
1116  loadLibrary( library, &lib_name );
1117  library = library->GetNext();
1118  }
1119 
1120  m_xpath->pop();
1121 }
1122 
1123 
1124 void EAGLE_PLUGIN::loadElements( wxXmlNode* aElements )
1125 {
1126  if( !aElements )
1127  return;
1128 
1129  m_xpath->push( "elements.element", "name" );
1130 
1131  EATTR name;
1132  EATTR value;
1133  bool refanceNamePresetInPackageLayout;
1134  bool valueNamePresetInPackageLayout;
1135 
1136  // Get the first element and iterate
1137  wxXmlNode* element = aElements->GetChildren();
1138 
1139  while( element )
1140  {
1141  checkpoint();
1142 
1143  if( element->GetName() != "element" )
1144  {
1145  // Get next item
1146  element = element->GetNext();
1147  continue;
1148  }
1149 
1150  EELEMENT e( element );
1151 
1152  // use "NULL-ness" as an indication of presence of the attribute:
1153  EATTR* nameAttr = nullptr;
1154  EATTR* valueAttr = nullptr;
1155 
1156  m_xpath->Value( e.name.c_str() );
1157 
1158  wxString pkg_key = makeKey( e.library, e.package );
1159 
1160  FOOTPRINT_MAP::const_iterator it = m_templates.find( pkg_key );
1161 
1162  if( it == m_templates.end() )
1163  {
1164  wxString emsg = wxString::Format( _( "No '%s' package in library '%s'." ),
1165  FROM_UTF8( e.package.c_str() ),
1166  FROM_UTF8( e.library.c_str() ) );
1167  THROW_IO_ERROR( emsg );
1168  }
1169 
1170  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( it->second->Duplicate() );
1171 
1172  m_board->Add( footprint, ADD_MODE::APPEND );
1173 
1174  // update the nets within the pads of the clone
1175  for( PAD* pad : footprint->Pads() )
1176  {
1177  wxString pn_key = makeKey( e.name, pad->GetNumber() );
1178 
1179  NET_MAP_CITER ni = m_pads_to_nets.find( pn_key );
1180  if( ni != m_pads_to_nets.end() )
1181  {
1182  const ENET* enet = &ni->second;
1183  pad->SetNetCode( enet->netcode );
1184  }
1185  }
1186 
1187  refanceNamePresetInPackageLayout = true;
1188  valueNamePresetInPackageLayout = true;
1189  footprint->SetPosition( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
1190 
1191  // Is >NAME field set in package layout ?
1192  if( footprint->GetReference().size() == 0 )
1193  {
1194  footprint->Reference().SetVisible( false ); // No so no show
1195  refanceNamePresetInPackageLayout = false;
1196  }
1197 
1198  // Is >VALUE field set in package layout
1199  if( footprint->GetValue().size() == 0 )
1200  {
1201  footprint->Value().SetVisible( false ); // No so no show
1202  valueNamePresetInPackageLayout = false;
1203  }
1204 
1205  footprint->SetReference( FROM_UTF8( e.name.c_str() ) );
1206  footprint->SetValue( FROM_UTF8( e.value.c_str() ) );
1207 
1208  if( !e.smashed )
1209  {
1210  // Not smashed so show NAME & VALUE
1211  if( valueNamePresetInPackageLayout )
1212  footprint->Value().SetVisible( true ); // Only if place holder in package layout
1213 
1214  if( refanceNamePresetInPackageLayout )
1215  footprint->Reference().SetVisible( true ); // Only if place holder in package layout
1216  }
1217  else if( *e.smashed == true )
1218  {
1219  // Smashed so set default to no show for NAME and VALUE
1220  footprint->Value().SetVisible( false );
1221  footprint->Reference().SetVisible( false );
1222 
1223  // initialize these to default values in case the <attribute> elements are not present.
1224  m_xpath->push( "attribute", "name" );
1225 
1226  // VALUE and NAME can have something like our text "effects" overrides
1227  // in SWEET and new schematic. Eagle calls these XML elements "attribute".
1228  // There can be one for NAME and/or VALUE both. Features present in the
1229  // EATTR override the ones established in the package only if they are
1230  // present here (except for rot, which if not present means angle zero).
1231  // So the logic is a bit different than in packageText() and in plain text.
1232 
1233  // Get the first attribute and iterate
1234  wxXmlNode* attribute = element->GetChildren();
1235 
1236  while( attribute )
1237  {
1238  if( attribute->GetName() != "attribute" )
1239  {
1240  attribute = attribute->GetNext();
1241  continue;
1242  }
1243 
1244  EATTR a( attribute );
1245 
1246  if( a.name == "NAME" )
1247  {
1248  name = a;
1249  nameAttr = &name;
1250 
1251  // do we have a display attribute ?
1252  if( a.display )
1253  {
1254  // Yes!
1255  switch( *a.display )
1256  {
1257  case EATTR::VALUE :
1258  {
1259  wxString reference = e.name;
1260 
1261  // EAGLE allows references to be single digits. This breaks KiCad
1262  // netlisting, which requires parts to have non-digit + digit
1263  // annotation. If the reference begins with a number, we prepend
1264  // 'UNK' (unknown) for the symbol designator.
1265  if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
1266  reference.Prepend( "UNK" );
1267 
1268  nameAttr->name = reference;
1269  footprint->SetReference( reference );
1270 
1271  if( refanceNamePresetInPackageLayout )
1272  footprint->Reference().SetVisible( true );
1273 
1274  break;
1275  }
1276 
1277  case EATTR::NAME :
1278  if( refanceNamePresetInPackageLayout )
1279  {
1280  footprint->SetReference( "NAME" );
1281  footprint->Reference().SetVisible( true );
1282  }
1283 
1284  break;
1285 
1286  case EATTR::BOTH :
1287  if( refanceNamePresetInPackageLayout )
1288  footprint->Reference().SetVisible( true );
1289 
1290  nameAttr->name = nameAttr->name + " = " + e.name;
1291  footprint->SetReference( "NAME = " + e.name );
1292  break;
1293 
1294  case EATTR::Off :
1295  footprint->Reference().SetVisible( false );
1296  break;
1297 
1298  default:
1299  nameAttr->name = e.name;
1300 
1301  if( refanceNamePresetInPackageLayout )
1302  footprint->Reference().SetVisible( true );
1303  }
1304  }
1305  else
1306  {
1307  // No display, so default is visible, and show value of NAME
1308  footprint->Reference().SetVisible( true );
1309  }
1310  }
1311  else if( a.name == "VALUE" )
1312  {
1313  value = a;
1314  valueAttr = &value;
1315 
1316  if( a.display )
1317  {
1318  // Yes!
1319  switch( *a.display )
1320  {
1321  case EATTR::VALUE :
1322  valueAttr->value = opt_wxString( e.value );
1323  footprint->SetValue( e.value );
1324 
1325  if( valueNamePresetInPackageLayout )
1326  footprint->Value().SetVisible( true );
1327 
1328  break;
1329 
1330  case EATTR::NAME :
1331  if( valueNamePresetInPackageLayout )
1332  footprint->Value().SetVisible( true );
1333 
1334  footprint->SetValue( "VALUE" );
1335  break;
1336 
1337  case EATTR::BOTH :
1338  if( valueNamePresetInPackageLayout )
1339  footprint->Value().SetVisible( true );
1340 
1341  valueAttr->value = opt_wxString( "VALUE = " + e.value );
1342  footprint->SetValue( "VALUE = " + e.value );
1343  break;
1344 
1345  case EATTR::Off :
1346  footprint->Value().SetVisible( false );
1347  break;
1348 
1349  default:
1350  valueAttr->value = opt_wxString( e.value );
1351 
1352  if( valueNamePresetInPackageLayout )
1353  footprint->Value().SetVisible( true );
1354  }
1355  }
1356  else
1357  {
1358  // No display, so default is visible, and show value of NAME
1359  footprint->Value().SetVisible( true );
1360  }
1361 
1362  }
1363 
1364  attribute = attribute->GetNext();
1365  }
1366 
1367  m_xpath->pop(); // "attribute"
1368  }
1369 
1370  orientFootprintAndText( footprint, e, nameAttr, valueAttr );
1371 
1372  // Set the local coordinates for the footprint text items
1373  footprint->Reference().SetLocalCoord();
1374  footprint->Value().SetLocalCoord();
1375 
1376  // Get next element
1377  element = element->GetNext();
1378  }
1379 
1380  m_xpath->pop(); // "elements.element"
1381 }
1382 
1383 
1384 ZONE* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode )
1385 {
1386  EPOLYGON p( aPolyNode );
1387  PCB_LAYER_ID layer = kicad_layer( p.layer );
1388  ZONE* zone = nullptr;
1389  bool keepout = ( p.layer == EAGLE_LAYER::TRESTRICT
1391  || p.layer == EAGLE_LAYER::VRESTRICT );
1392 
1393  if( layer == UNDEFINED_LAYER )
1394  {
1395  wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
1396  "was not mapped" ),
1397  eagle_layer_name( p.layer ), p.layer ) );
1398  return nullptr;
1399  }
1400 
1401  if( !IsCopperLayer( layer ) && !keepout )
1402  return nullptr;
1403 
1404  // use a "netcode = 0" type ZONE:
1405  zone = new ZONE( m_board );
1406  m_board->Add( zone, ADD_MODE::APPEND );
1407 
1408  if( !keepout )
1409  zone->SetLayer( layer );
1410  else
1411  setKeepoutSettingsToZone( zone, p.layer );
1412 
1413  // Get the first vertex and iterate
1414  wxXmlNode* vertex = aPolyNode->GetChildren();
1415  std::vector<EVERTEX> vertices;
1416 
1417  // Create a circular vector of vertices
1418  // The "curve" parameter indicates a curve from the current
1419  // to the next vertex, so we keep the first at the end as well
1420  // to allow the curve to link back
1421  while( vertex )
1422  {
1423  if( vertex->GetName() == "vertex" )
1424  vertices.emplace_back( vertex );
1425 
1426  vertex = vertex->GetNext();
1427  }
1428 
1429  vertices.push_back( vertices[0] );
1430 
1431  SHAPE_POLY_SET polygon;
1432  polygon.NewOutline();
1433 
1434  for( size_t i = 0; i < vertices.size() - 1; i++ )
1435  {
1436  EVERTEX v1 = vertices[i];
1437 
1438  // Append the corner
1439  polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) );
1440 
1441  if( v1.curve )
1442  {
1443  EVERTEX v2 = vertices[i + 1];
1444  wxPoint center = ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
1445  wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ),
1446  *v1.curve );
1447  double angle = DEG2RAD( *v1.curve );
1448  double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
1449  double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
1450  + pow( center.y - kicad_y( v1.y ), 2 ) );
1451 
1452  int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
1453  double delta_angle = angle / segCount;
1454 
1455  for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle );
1456  a -= delta_angle )
1457  {
1458  polygon.Append( KiROUND( radius * cos( a ) ) + center.x,
1459  KiROUND( radius * sin( a ) ) + center.y );
1460  }
1461  }
1462  }
1463 
1464  // Eagle traces the zone such that half of the pen width is outside the polygon.
1465  // We trace the zone such that the copper is completely inside.
1466  if( p.width.ToPcbUnits() > 0 )
1467  {
1470  }
1471 
1472  zone->AddPolygon( polygon.COutline( 0 ) );
1473 
1474  // If the pour is a cutout it needs to be set to a keepout
1475  if( p.pour == EPOLYGON::CUTOUT )
1476  {
1477  zone->SetIsRuleArea( true );
1478  zone->SetDoNotAllowVias( false );
1479  zone->SetDoNotAllowTracks( false );
1480  zone->SetDoNotAllowPads( false );
1481  zone->SetDoNotAllowFootprints( false );
1482  zone->SetDoNotAllowCopperPour( true );
1484  }
1485  else if( p.pour == EPOLYGON::HATCH )
1486  {
1487  int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * IU_PER_MILS;
1488 
1490  zone->SetHatchThickness( p.width.ToPcbUnits() );
1491  zone->SetHatchGap( spacing - p.width.ToPcbUnits() );
1492  zone->SetHatchOrientation( 0 );
1493  }
1494 
1495  // We divide the thickness by half because we are tracing _inside_ the zone outline
1496  // This means the radius of curvature will be twice the size for an equivalent EAGLE zone
1498  p.width.ToPcbUnits() / 2 ) );
1499 
1500  if( p.isolate )
1501  zone->SetLocalClearance( p.isolate->ToPcbUnits() );
1502  else
1503  zone->SetLocalClearance( 1 ); // @todo: set minimum clearance value based on board settings
1504 
1505  // missing == yes per DTD.
1506  bool thermals = !p.thermals || *p.thermals;
1508 
1509  if( thermals )
1510  {
1511  // FIXME: eagle calculates dimensions for thermal spokes
1512  // based on what the zone is connecting to.
1513  // (i.e. width of spoke is half of the smaller side of an smd pad)
1514  // This is a basic workaround
1515  zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
1516  zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 );
1517  }
1518 
1519  int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
1520  zone->SetPriority( rank );
1521 
1522  return zone;
1523 }
1524 
1525 
1527  const EATTR* aNameAttr, const EATTR* aValueAttr )
1528 {
1529  if( e.rot )
1530  {
1531  if( e.rot->mirror )
1532  {
1533  double orientation = e.rot->degrees + 180.0;
1534  aFootprint->SetOrientation( orientation * 10 );
1535  aFootprint->Flip( aFootprint->GetPosition(), false );
1536  }
1537  else
1538  {
1539  aFootprint->SetOrientation( e.rot->degrees * 10 );
1540  }
1541  }
1542 
1543  orientFPText( aFootprint, e, &aFootprint->Reference(), aNameAttr );
1544  orientFPText( aFootprint, e, &aFootprint->Value(), aValueAttr );
1545 }
1546 
1547 
1548 void EAGLE_PLUGIN::orientFPText( FOOTPRINT* aFootprint, const EELEMENT& e, FP_TEXT* aFPText,
1549  const EATTR* aAttr )
1550 {
1551  // Smashed part ?
1552  if( aAttr )
1553  {
1554  // Yes
1555  const EATTR& a = *aAttr;
1556 
1557  if( a.value )
1558  {
1559  aFPText->SetText( FROM_UTF8( a.value->c_str() ) );
1560  }
1561 
1562  if( a.x && a.y ) // OPT
1563  {
1564  wxPoint pos( kicad_x( *a.x ), kicad_y( *a.y ) );
1565  aFPText->SetTextPos( pos );
1566  }
1567 
1568  // Even though size and ratio are both optional, I am not seeing
1569  // a case where ratio is present but size is not.
1570  double ratio = 8;
1571  wxSize fontz = aFPText->GetTextSize();
1572  int textThickness = KiROUND( fontz.y * ratio / 100 );
1573 
1574  aFPText->SetTextThickness( textThickness );
1575  if( a.size )
1576  {
1577  fontz = kicad_fontz( *a.size, textThickness );
1578  aFPText->SetTextSize( fontz );
1579 
1580  if( a.ratio )
1581  ratio = *a.ratio;
1582  }
1583 
1584 
1585 
1586  int align = ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1587 
1588  if( a.align )
1589  align = a.align;
1590 
1591  // The "rot" in a EATTR seems to be assumed to be zero if it is not
1592  // present, and this zero rotation becomes an override to the
1593  // package's text field. If they did not want zero, they specify
1594  // what they want explicitly.
1595  double degrees = a.rot ? a.rot->degrees : 0;
1596  double orient; // relative to parent
1597 
1598  int sign = 1;
1599  bool spin = false;
1600 
1601  if( a.rot )
1602  {
1603  spin = a.rot->spin;
1604  sign = a.rot->mirror ? -1 : 1;
1605  aFPText->SetMirrored( a.rot->mirror );
1606  }
1607 
1608  if( degrees == 90 || degrees == 0 || spin )
1609  {
1610  orient = degrees - aFootprint->GetOrientation() / 10;
1611  aFPText->SetTextAngle( sign * orient * 10 );
1612  }
1613  else if( degrees == 180 )
1614  {
1615  orient = 0 - aFootprint->GetOrientation() / 10;
1616  aFPText->SetTextAngle( sign * orient * 10 );
1617  align = -align;
1618  }
1619  else if( degrees == 270 )
1620  {
1621  orient = 90 - aFootprint->GetOrientation() / 10;
1622  align = -align;
1623  aFPText->SetTextAngle( sign * orient * 10 );
1624  }
1625  else
1626  {
1627  orient = 90 - degrees - aFootprint->GetOrientation() / 10;
1628  aFPText->SetTextAngle( sign * orient * 10 );
1629  }
1630 
1631  switch( align )
1632  {
1633  case ETEXT::TOP_RIGHT:
1636  break;
1637 
1638  case ETEXT::BOTTOM_LEFT:
1641  break;
1642 
1643  case ETEXT::TOP_LEFT:
1646  break;
1647 
1648  case ETEXT::BOTTOM_RIGHT:
1651  break;
1652 
1653  case ETEXT::TOP_CENTER:
1656  break;
1657 
1658  case ETEXT::BOTTOM_CENTER:
1661  break;
1662 
1663  case ETEXT::CENTER:
1666  break;
1667 
1668  case ETEXT::CENTER_LEFT:
1671  break;
1672 
1673  case ETEXT::CENTER_RIGHT:
1676  break;
1677 
1678  default:
1679  ;
1680  }
1681  }
1682  else
1683  {
1684  // Part is not smash so use Lib default for NAME/VALUE // the text is per the original
1685  // package, sans <attribute>.
1686  double degrees = ( aFPText->GetTextAngle() + aFootprint->GetOrientation() ) / 10;
1687 
1688  // @todo there are a few more cases than these to contend with:
1689  if( ( !aFPText->IsMirrored() && ( abs( degrees ) == 180 || abs( degrees ) == 270 ) )
1690  || ( aFPText->IsMirrored() && ( degrees == 360 ) ) )
1691  {
1692  // ETEXT::TOP_RIGHT:
1695  }
1696  }
1697 }
1698 
1699 
1700 FOOTPRINT* EAGLE_PLUGIN::makeFootprint( wxXmlNode* aPackage, const wxString& aPkgName )
1701 {
1702  std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>( m_board );
1703 
1704  LIB_ID fpID;
1705  fpID.Parse( aPkgName, true );
1706  m->SetFPID( fpID );
1707 
1708  // Get the first package item and iterate
1709  wxXmlNode* packageItem = aPackage->GetChildren();
1710 
1711  // layer 27 is default layer for tValues
1712  // set default layer for created footprint
1713  PCB_LAYER_ID layer = kicad_layer( 27 );
1714  m.get()->Value().SetLayer( layer );
1715 
1716  while( packageItem )
1717  {
1718  const wxString& itemName = packageItem->GetName();
1719 
1720  if( itemName == "description" )
1721  m->SetDescription( FROM_UTF8( packageItem->GetNodeContent().c_str() ) );
1722  else if( itemName == "wire" )
1723  packageWire( m.get(), packageItem );
1724  else if( itemName == "pad" )
1725  packagePad( m.get(), packageItem );
1726  else if( itemName == "text" )
1727  packageText( m.get(), packageItem );
1728  else if( itemName == "rectangle" )
1729  packageRectangle( m.get(), packageItem );
1730  else if( itemName == "polygon" )
1731  packagePolygon( m.get(), packageItem );
1732  else if( itemName == "circle" )
1733  packageCircle( m.get(), packageItem );
1734  else if( itemName == "hole" )
1735  packageHole( m.get(), packageItem, false );
1736  else if( itemName == "smd" )
1737  packageSMD( m.get(), packageItem );
1738 
1739  packageItem = packageItem->GetNext();
1740  }
1741 
1742  return m.release();
1743 }
1744 
1745 
1746 void EAGLE_PLUGIN::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1747 {
1748  EWIRE w( aTree );
1749  PCB_LAYER_ID layer = kicad_layer( w.layer );
1750  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
1751  wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
1752  int width = w.width.ToPcbUnits();
1753 
1754  if( layer == UNDEFINED_LAYER )
1755  {
1756  wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) "
1757  "was not mapped" ),
1758  eagle_layer_name( w.layer ), w.layer ) );
1759  return;
1760  }
1761 
1762  // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
1763  if( width <= 0 )
1764  {
1765  BOARD* board = aFootprint->GetBoard();
1766 
1767  if( board )
1768  {
1769  width = board->GetDesignSettings().GetLineThickness( layer );
1770  }
1771  else
1772  {
1773  // When loading footprint libraries, there is no board so use the default KiCad
1774  // line widths.
1775  switch( layer )
1776  {
1777  case Edge_Cuts: width = Millimeter2iu( DEFAULT_EDGE_WIDTH ); break;
1778 
1779  case F_SilkS:
1780  case B_SilkS: width = Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ); break;
1781 
1782  case F_CrtYd:
1783  case B_CrtYd: width = Millimeter2iu( DEFAULT_COURTYARD_WIDTH ); break;
1784 
1785  default: width = Millimeter2iu( DEFAULT_LINE_WIDTH ); break;
1786  }
1787  }
1788  }
1789 
1790  // FIXME: the cap attribute is ignored because KiCad can't create lines
1791  // with flat ends.
1792  FP_SHAPE* dwg;
1793 
1794  if( !w.curve )
1795  {
1796  dwg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
1797 
1798  dwg->SetStart0( start );
1799  dwg->SetEnd0( end );
1800  }
1801  else
1802  {
1803  dwg = new FP_SHAPE( aFootprint, SHAPE_T::ARC );
1804  wxPoint center = ConvertArcCenter( start, end, *w.curve );
1805 
1806  dwg->SetCenter0( center );
1807  dwg->SetStart0( start );
1808  dwg->SetArcAngleAndEnd0( *w.curve * -10.0, true ); // KiCad rotates the other way
1809  }
1810 
1811  dwg->SetLayer( layer );
1812  dwg->SetWidth( width );
1813  dwg->SetDrawCoord();
1814 
1815  aFootprint->Add( dwg );
1816 }
1817 
1818 
1819 void EAGLE_PLUGIN::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
1820 {
1821  // this is thru hole technology here, no SMDs
1822  EPAD e( aTree );
1823  int shape = EPAD::UNDEF;
1824  int eagleDrillz = e.drill.ToPcbUnits();
1825 
1826  PAD* pad = new PAD( aFootprint );
1827  aFootprint->Add( pad );
1828  transferPad( e, pad );
1829 
1830  if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
1831  shape = m_rules->psFirst;
1832  else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
1833  shape = m_rules->psTop;
1834  else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
1835  shape = m_rules->psBottom;
1836 
1837  pad->SetDrillSize( wxSize( eagleDrillz, eagleDrillz ) );
1838  pad->SetLayerSet( LSET::AllCuMask() );
1839 
1840  if( eagleDrillz < m_min_hole )
1841  m_min_hole = eagleDrillz;
1842 
1843  // Solder mask
1844  if( !e.stop || *e.stop == true ) // enabled by default
1845  pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
1846 
1847  if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
1848  e.shape = shape;
1849 
1850  if( e.shape )
1851  {
1852  switch( *e.shape )
1853  {
1854  case EPAD::ROUND:
1855  pad->SetShape( PAD_SHAPE::CIRCLE );
1856  break;
1857 
1858  case EPAD::OCTAGON:
1859  // no KiCad octagonal pad shape, use PAD_CIRCLE for now.
1860  // pad->SetShape( PAD_OCTAGON );
1861  wxASSERT( pad->GetShape() == PAD_SHAPE::CIRCLE ); // verify set in PAD constructor
1862  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
1863  pad->SetChamferPositions( RECT_CHAMFER_ALL );
1864  pad->SetChamferRectRatio( 0.25 );
1865  break;
1866 
1867  case EPAD::LONG:
1868  pad->SetShape( PAD_SHAPE::OVAL );
1869  break;
1870 
1871  case EPAD::SQUARE:
1872  pad->SetShape( PAD_SHAPE::RECT );
1873  break;
1874 
1875  case EPAD::OFFSET:
1876  pad->SetShape( PAD_SHAPE::OVAL );
1877  break;
1878  }
1879  }
1880  else
1881  {
1882  // if shape is not present, our default is circle and that matches their default "round"
1883  }
1884 
1885  if( e.diameter && e.diameter->value > 0 )
1886  {
1887  int diameter = e.diameter->ToPcbUnits();
1888  pad->SetSize( wxSize( diameter, diameter ) );
1889  }
1890  else
1891  {
1892  double drillz = pad->GetDrillSize().x;
1893  double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
1894  annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
1895  int diameter = KiROUND( drillz + 2 * annulus );
1896  pad->SetSize( wxSize( KiROUND( diameter ), KiROUND( diameter ) ) );
1897  }
1898 
1899  if( pad->GetShape() == PAD_SHAPE::OVAL )
1900  {
1901  // The Eagle "long" pad is wider than it is tall,
1902  // m_elongation is percent elongation
1903  wxSize sz = pad->GetSize();
1904  sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
1905  pad->SetSize( sz );
1906 
1907  if( e.shape && *e.shape == EPAD::OFFSET )
1908  {
1909  int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
1910  pad->SetOffset( wxPoint( offset, 0 ) );
1911  }
1912  }
1913 
1914  if( e.rot )
1915  {
1916  pad->SetOrientation( e.rot->degrees * 10 );
1917  }
1918 }
1919 
1920 
1921 void EAGLE_PLUGIN::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1922 {
1923  ETEXT t( aTree );
1924  PCB_LAYER_ID layer = kicad_layer( t.layer );
1925 
1926  if( layer == UNDEFINED_LAYER )
1927  {
1928  wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) "
1929  "was not mapped" ),
1930  eagle_layer_name( t.layer ), t.layer ) );
1931  return;
1932  }
1933 
1934  FP_TEXT* txt;
1935 
1936  if( t.text.MakeUpper() == ">NAME" )
1937  txt = &aFootprint->Reference();
1938  else if( t.text.MakeUpper() == ">VALUE" )
1939  txt = &aFootprint->Value();
1940  else
1941  {
1942  // FIXME: graphical text items are rotated for some reason.
1943  txt = new FP_TEXT( aFootprint );
1944  aFootprint->Add( txt );
1945  }
1946 
1947  txt->SetText( FROM_UTF8( t.text.c_str() ) );
1948 
1949  wxPoint pos( kicad_x( t.x ), kicad_y( t.y ) );
1950 
1951  txt->SetTextPos( pos );
1952  txt->SetPos0( pos - aFootprint->GetPosition() );
1953 
1954  txt->SetLayer( layer );
1955 
1956  double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
1957  int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
1958 
1959  txt->SetTextThickness( textThickness );
1960  txt->SetTextSize( kicad_fontz( t.size, textThickness ) );
1961 
1962  int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1963 
1964  // An eagle package is never rotated, the DTD does not allow it.
1965  // angle -= aFootprint->GetOrienation();
1966 
1967  if( t.rot )
1968  {
1969  int sign = t.rot->mirror ? -1 : 1;
1970  txt->SetMirrored( t.rot->mirror );
1971 
1972  double degrees = t.rot->degrees;
1973 
1974  if( degrees == 90 || t.rot->spin )
1975  {
1976  txt->SetTextAngle( sign * degrees * 10 );
1977  }
1978  else if( degrees == 180 )
1979  {
1980  align = ETEXT::TOP_RIGHT;
1981  }
1982  else if( degrees == 270 )
1983  {
1984  align = ETEXT::TOP_RIGHT;
1985  txt->SetTextAngle( sign * 90 * 10 );
1986  }
1987  }
1988 
1989  switch( align )
1990  {
1991  case ETEXT::CENTER:
1994  break;
1995 
1996  case ETEXT::CENTER_LEFT:
1999  break;
2000 
2001  case ETEXT::CENTER_RIGHT:
2004  break;
2005 
2006  case ETEXT::TOP_CENTER:
2009  break;
2010 
2011  case ETEXT::TOP_LEFT:
2014  break;
2015 
2016  case ETEXT::TOP_RIGHT:
2019  break;
2020 
2021  case ETEXT::BOTTOM_CENTER:
2024  break;
2025 
2026  case ETEXT::BOTTOM_LEFT:
2029  break;
2030 
2031  case ETEXT::BOTTOM_RIGHT:
2034  break;
2035  }
2036 }
2037 
2038 
2039 void EAGLE_PLUGIN::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2040 {
2041  ERECT r( aTree );
2042 
2043  if( r.layer == EAGLE_LAYER::TRESTRICT || r.layer == EAGLE_LAYER::BRESTRICT
2044  || r.layer == EAGLE_LAYER::VRESTRICT )
2045  {
2046  FP_ZONE* zone = new FP_ZONE( aFootprint );
2047  aFootprint->Add( zone, ADD_MODE::APPEND );
2048 
2049  setKeepoutSettingsToZone( zone, r.layer );
2050 
2051  const int outlineIdx = -1; // this is the id of the copper zone main outline
2052  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
2053  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
2054  zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
2055  zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
2056 
2057  if( r.rot )
2058  {
2059  wxPoint center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
2060  ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
2061  zone->Rotate( center, r.rot->degrees * 10 );
2062  }
2063 
2065  ZONE::GetDefaultHatchPitch(), true );
2066  }
2067  else
2068  {
2069  PCB_LAYER_ID layer = kicad_layer( r.layer );
2070 
2071  if( layer == UNDEFINED_LAYER )
2072  {
2073  wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) "
2074  "was not mapped" ),
2075  eagle_layer_name( r.layer ), r.layer ) );
2076  return;
2077  }
2078 
2079  FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2080 
2081  aFootprint->Add( dwg );
2082 
2083  dwg->SetLayer( layer );
2084  dwg->SetWidth( 0 );
2085  dwg->SetFilled( true );
2086 
2087  std::vector<wxPoint> pts;
2088 
2089  wxPoint start( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
2090  wxPoint end( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
2091 
2092  pts.push_back( start );
2093  pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
2094  pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
2095  pts.push_back( end );
2096 
2097  dwg->SetPolyPoints( pts );
2098 
2099  dwg->SetStart0( start );
2100  dwg->SetEnd0( end );
2101 
2102  if( r.rot )
2103  dwg->Rotate( dwg->GetCenter(), r.rot->degrees * 10 );
2104  }
2105 }
2106 
2107 
2108 void EAGLE_PLUGIN::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2109 {
2110  EPOLYGON p( aTree );
2111 
2112  std::vector<wxPoint> pts;
2113 
2114  // Get the first vertex and iterate
2115  wxXmlNode* vertex = aTree->GetChildren();
2116  std::vector<EVERTEX> vertices;
2117 
2118  // Create a circular vector of vertices
2119  // The "curve" parameter indicates a curve from the current
2120  // to the next vertex, so we keep the first at the end as well
2121  // to allow the curve to link back
2122  while( vertex )
2123  {
2124  if( vertex->GetName() == "vertex" )
2125  vertices.emplace_back( vertex );
2126 
2127  vertex = vertex->GetNext();
2128  }
2129 
2130  vertices.push_back( vertices[0] );
2131 
2132  for( size_t i = 0; i < vertices.size() - 1; i++ )
2133  {
2134  EVERTEX v1 = vertices[i];
2135 
2136  // Append the corner
2137  pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
2138 
2139  if( v1.curve )
2140  {
2141  EVERTEX v2 = vertices[i + 1];
2142  wxPoint center =
2143  ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
2144  wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
2145  double angle = DEG2RAD( *v1.curve );
2146  double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
2147  double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
2148  + pow( center.y - kicad_y( v1.y ), 2 ) );
2149 
2150  // Don't allow a zero-radius curve
2151  if( KiROUND( radius ) == 0 )
2152  radius = 1.0;
2153 
2154  int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
2155  double delta = angle / segCount;
2156 
2157  for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
2158  {
2159  pts.push_back( wxPoint( KiROUND( radius * cos( a ) ),
2160  KiROUND( radius * sin( a ) ) ) + center );
2161  }
2162  }
2163  }
2164 
2165  if( p.layer == EAGLE_LAYER::TRESTRICT
2167  || p.layer == EAGLE_LAYER::VRESTRICT )
2168  {
2169  FP_ZONE* zone = new FP_ZONE( aFootprint );
2170  aFootprint->Add( zone, ADD_MODE::APPEND );
2171 
2172  setKeepoutSettingsToZone( zone, p.layer );
2173 
2174  SHAPE_LINE_CHAIN outline( pts );
2175  outline.SetClosed( true );
2176  zone->Outline()->AddOutline( outline );
2177 
2179  ZONE::GetDefaultHatchPitch(), true );
2180  }
2181  else
2182  {
2183  PCB_LAYER_ID layer = kicad_layer( p.layer );
2184 
2185  if( layer == UNDEFINED_LAYER )
2186  {
2187  wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
2188  "was not mapped" ),
2189  eagle_layer_name( p.layer ), p.layer ) );
2190  return;
2191  }
2192 
2193  FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2194 
2195  aFootprint->Add( dwg );
2196 
2197  dwg->SetWidth( 0 ); // it's filled, no need for boundary width
2198  dwg->SetFilled( true );
2199  dwg->SetLayer( layer );
2200 
2201  dwg->SetPolyPoints( pts );
2202  dwg->SetStart0( *pts.begin() );
2203  dwg->SetEnd0( pts.back() );
2204  dwg->SetDrawCoord();
2205  dwg->GetPolyShape().Inflate( p.width.ToPcbUnits() / 2, 32,
2207  }
2208 }
2209 
2210 
2211 void EAGLE_PLUGIN::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2212 {
2213  ECIRCLE e( aTree );
2214 
2215  int width = e.width.ToPcbUnits();
2216  int radius = e.radius.ToPcbUnits();
2217 
2218  if( e.layer == EAGLE_LAYER::TRESTRICT
2220  || e.layer == EAGLE_LAYER::VRESTRICT )
2221  {
2222  FP_ZONE* zone = new FP_ZONE( aFootprint );
2223  aFootprint->Add( zone, ADD_MODE::APPEND );
2224 
2225  setKeepoutSettingsToZone( zone, e.layer );
2226 
2227  // approximate circle as polygon with a edge every 10 degree
2228  wxPoint center( kicad_x( e.x ), kicad_y( e.y ) );
2229  int outlineRadius = radius + ( width / 2 );
2230 
2231  for( int angle = 0; angle < 360; angle += 10 )
2232  {
2233  wxPoint rotatedPoint( outlineRadius, 0 );
2234  RotatePoint( &rotatedPoint, angle * 10. );
2235  zone->AppendCorner( center + rotatedPoint, -1 );
2236  }
2237 
2238  if( width > 0 )
2239  {
2240  zone->NewHole();
2241  int innerRadius = radius - ( width / 2 );
2242 
2243  for( int angle = 0; angle < 360; angle += 10 )
2244  {
2245  wxPoint rotatedPoint( innerRadius, 0 );
2246  RotatePoint( &rotatedPoint, angle * 10. );
2247  zone->AppendCorner( center + rotatedPoint, 0 );
2248  }
2249  }
2250 
2252  ZONE::GetDefaultHatchPitch(), true );
2253  }
2254  else
2255  {
2256  PCB_LAYER_ID layer = kicad_layer( e.layer );
2257 
2258  if( layer == UNDEFINED_LAYER )
2259  {
2260  wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) "
2261  "was not mapped" ),
2262  eagle_layer_name( e.layer ), e.layer ) );
2263  return;
2264  }
2265 
2266  FP_SHAPE* gr = new FP_SHAPE( aFootprint, SHAPE_T::CIRCLE );
2267 
2268  // width == 0 means filled circle
2269  if( width <= 0 )
2270  {
2271  width = radius;
2272  radius = radius / 2;
2273  gr->SetFilled( true );
2274  }
2275 
2276  aFootprint->Add( gr );
2277  gr->SetWidth( width );
2278 
2279  switch( (int) layer )
2280  {
2281  case UNDEFINED_LAYER:
2282  layer = Cmts_User;
2283  break;
2284  default:
2285  break;
2286  }
2287 
2288  gr->SetLayer( layer );
2289  gr->SetStart0( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
2290  gr->SetEnd0( wxPoint( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
2291  gr->SetDrawCoord();
2292  }
2293 }
2294 
2295 
2296 void EAGLE_PLUGIN::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
2297 {
2298  EHOLE e( aTree );
2299 
2300  if( e.drill.value == 0 )
2301  return;
2302 
2303  // we add a PAD_ATTRIB::NPTH pad to this footprint.
2304  PAD* pad = new PAD( aFootprint );
2305  aFootprint->Add( pad );
2306 
2307  pad->SetShape( PAD_SHAPE::CIRCLE );
2308  pad->SetAttribute( PAD_ATTRIB::NPTH );
2309 
2310  // Mechanical purpose only:
2311  // no offset, no net name, no pad name allowed
2312  // pad->SetOffset( wxPoint( 0, 0 ) );
2313  // pad->SetNumber( wxEmptyString );
2314 
2315  wxPoint padpos( kicad_x( e.x ), kicad_y( e.y ) );
2316 
2317  if( aCenter )
2318  {
2319  pad->SetPos0( wxPoint( 0, 0 ) );
2320  aFootprint->SetPosition( padpos );
2321  pad->SetPosition( padpos );
2322  }
2323  else
2324  {
2325  pad->SetPos0( padpos );
2326  pad->SetPosition( padpos + aFootprint->GetPosition() );
2327  }
2328 
2329  wxSize sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
2330 
2331  pad->SetDrillSize( sz );
2332  pad->SetSize( sz );
2333 
2334  pad->SetLayerSet( LSET::AllCuMask().set( B_Mask ).set( F_Mask ) );
2335 }
2336 
2337 
2338 void EAGLE_PLUGIN::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2339 {
2340  ESMD e( aTree );
2341  PCB_LAYER_ID layer = kicad_layer( e.layer );
2342 
2343  if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
2344  return;
2345 
2346  PAD* pad = new PAD( aFootprint );
2347  aFootprint->Add( pad );
2348  transferPad( e, pad );
2349 
2350  pad->SetShape( PAD_SHAPE::RECT );
2351  pad->SetAttribute( PAD_ATTRIB::SMD );
2352 
2353  wxSize padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
2354  pad->SetSize( padSize );
2355  pad->SetLayer( layer );
2356 
2357  const LSET front( 3, F_Cu, F_Paste, F_Mask );
2358  const LSET back( 3, B_Cu, B_Paste, B_Mask );
2359 
2360  if( layer == F_Cu )
2361  pad->SetLayerSet( front );
2362  else if( layer == B_Cu )
2363  pad->SetLayerSet( back );
2364 
2365  int minPadSize = std::min( padSize.x, padSize.y );
2366 
2367  // Rounded rectangle pads
2368  int roundRadius =
2369  eagleClamp( m_rules->srMinRoundness * 2, (int) ( minPadSize * m_rules->srRoundness ),
2370  m_rules->srMaxRoundness * 2 );
2371 
2372  if( e.roundness || roundRadius > 0 )
2373  {
2374  double roundRatio = (double) roundRadius / minPadSize / 2.0;
2375 
2376  // Eagle uses a different definition of roundness, hence division by 200
2377  if( e.roundness )
2378  roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
2379 
2380  pad->SetShape( PAD_SHAPE::ROUNDRECT );
2381  pad->SetRoundRectRadiusRatio( roundRatio );
2382  }
2383 
2384  if( e.rot )
2385  pad->SetOrientation( e.rot->degrees * 10 );
2386 
2387  pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
2388  (int) ( m_rules->mvCreamFrame * minPadSize ),
2389  m_rules->mlMaxCreamFrame ) );
2390 
2391  // Solder mask
2392  if( e.stop && *e.stop == false ) // enabled by default
2393  {
2394  if( layer == F_Cu )
2395  pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
2396  else if( layer == B_Cu )
2397  pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
2398  }
2399 
2400  // Solder paste (only for SMD pads)
2401  if( e.cream && *e.cream == false ) // enabled by default
2402  {
2403  if( layer == F_Cu )
2404  pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
2405  else if( layer == B_Cu )
2406  pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
2407  }
2408 }
2409 
2410 
2411 void EAGLE_PLUGIN::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
2412 {
2413  aPad->SetNumber( FROM_UTF8( aEaglePad.name.c_str() ) );
2414 
2415  // pad's "Position" is not relative to the footprint's,
2416  // whereas Pos0 is relative to the footprint's but is the unrotated coordinate.
2417  wxPoint padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
2418  aPad->SetPos0( padPos );
2419 
2420  // Solder mask
2421  const wxSize& padSize( aPad->GetSize() );
2422 
2425  (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
2426  m_rules->mlMaxStopFrame ) );
2427 
2428  // Solid connection to copper zones
2429  if( aEaglePad.thermals && !*aEaglePad.thermals )
2431 
2432  FOOTPRINT* footprint = aPad->GetParent();
2433  wxCHECK( footprint, /* void */ );
2434  RotatePoint( &padPos, footprint->GetOrientation() );
2435  aPad->SetPosition( padPos + footprint->GetPosition() );
2436 }
2437 
2438 
2440 {
2441  for( auto& t : m_templates )
2442  delete t.second;
2443 
2444  m_templates.clear();
2445 }
2446 
2447 
2448 void EAGLE_PLUGIN::loadClasses( wxXmlNode* aClasses )
2449 {
2451 
2452  m_xpath->push( "classes.class", "number" );
2453 
2454  std::vector<ECLASS> eClasses;
2455  wxXmlNode* classNode = aClasses->GetChildren();
2456 
2457  while( classNode )
2458  {
2459  checkpoint();
2460 
2461  ECLASS eClass( classNode );
2462  NETCLASSPTR netclass;
2463 
2464  if( eClass.name.CmpNoCase( "default" ) == 0 )
2465  {
2466  netclass = bds.GetNetClasses().GetDefault();
2467  }
2468  else
2469  {
2470  netclass.reset( new NETCLASS( eClass.name ) );
2471  m_board->GetDesignSettings().GetNetClasses().Add( netclass );
2472  }
2473 
2474  netclass->SetTrackWidth( INT_MAX );
2475  netclass->SetViaDiameter( INT_MAX );
2476  netclass->SetViaDrill( INT_MAX );
2477 
2478  eClasses.emplace_back( eClass );
2479  m_classMap[ eClass.number ] = netclass;
2480 
2481  // Get next class
2482  classNode = classNode->GetNext();
2483  }
2484 
2485  m_customRules = "(version 1)";
2486 
2487  for( ECLASS& eClass : eClasses )
2488  {
2489  for( std::pair<const wxString&, ECOORD> entry : eClass.clearanceMap )
2490  {
2491  if( m_classMap[ entry.first ] != nullptr )
2492  {
2493  wxString rule;
2494  rule.Printf( "(rule \"class %s:%s\"\n"
2495  " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
2496  " (constraint clearance (min %smm)))\n",
2497  eClass.number,
2498  entry.first,
2499  eClass.name,
2500  m_classMap[ entry.first ]->GetName(),
2501  StringFromValue( EDA_UNITS::MILLIMETRES, entry.second.ToPcbUnits() ) );
2502 
2503  m_customRules += "\n" + rule;
2504  }
2505  }
2506  }
2507 
2508  m_xpath->pop(); // "classes.class"
2509 }
2510 
2511 
2512 void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
2513 {
2514  ZONES zones; // per net
2515  int netCode = 1;
2516 
2517  m_xpath->push( "signals.signal", "name" );
2518 
2519  // Get the first signal and iterate
2520  wxXmlNode* net = aSignals->GetChildren();
2521 
2522  while( net )
2523  {
2524  checkpoint();
2525 
2526  bool sawPad = false;
2527 
2528  zones.clear();
2529 
2530  const wxString& netName = escapeName( net->GetAttribute( "name" ) );
2531  NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, netName, netCode );
2532  NETCLASSPTR netclass;
2533 
2534  if( net->HasAttribute( "class" ) )
2535  {
2536  netclass = m_classMap[ net->GetAttribute( "class" ) ];
2537 
2538  netclass->Add( netName );
2539  netInfo->SetNetClass( netclass );
2540  }
2541 
2542  m_board->Add( netInfo );
2543 
2544  m_xpath->Value( netName.c_str() );
2545 
2546  // Get the first net item and iterate
2547  wxXmlNode* netItem = net->GetChildren();
2548 
2549  // (contactref | polygon | wire | via)*
2550  while( netItem )
2551  {
2552  const wxString& itemName = netItem->GetName();
2553 
2554  if( itemName == "wire" )
2555  {
2556  m_xpath->push( "wire" );
2557 
2558  EWIRE w( netItem );
2559  PCB_LAYER_ID layer = kicad_layer( w.layer );
2560 
2561  if( IsCopperLayer( layer ) )
2562  {
2563  wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
2564  double angle = 0.0;
2565  double end_angle = 0.0;
2566  double radius = 0.0;
2567  double delta_angle = 0.0;
2568  wxPoint center;
2569 
2570  int width = w.width.ToPcbUnits();
2571 
2572  if( width < m_min_trace )
2573  m_min_trace = width;
2574 
2575  if( netclass && width < netclass->GetTrackWidth() )
2576  netclass->SetTrackWidth( width );
2577 
2578  if( w.curve )
2579  {
2580  center = ConvertArcCenter( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ),
2581  wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ),
2582  *w.curve );
2583 
2584  angle = DEG2RAD( *w.curve );
2585 
2586  end_angle = atan2( kicad_y( w.y2 ) - center.y,
2587  kicad_x( w.x2 ) - center.x );
2588 
2589  radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
2590  pow( center.y - kicad_y( w.y1 ), 2 ) );
2591 
2592  int segs = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
2593  *w.curve );
2594  delta_angle = angle / segs;
2595  }
2596 
2597  while( fabs( angle ) > fabs( delta_angle ) )
2598  {
2599  wxASSERT( radius > 0.0 );
2600  wxPoint end( KiROUND( radius * cos( end_angle + angle ) + center.x ),
2601  KiROUND( radius * sin( end_angle + angle ) + center.y ) );
2602 
2603  PCB_TRACK* t = new PCB_TRACK( m_board );
2604 
2605  t->SetPosition( start );
2606  t->SetEnd( end );
2607  t->SetWidth( width );
2608  t->SetLayer( layer );
2609  t->SetNetCode( netCode );
2610 
2611  m_board->Add( t );
2612 
2613  start = end;
2614  angle -= delta_angle;
2615  }
2616 
2617  PCB_TRACK* t = new PCB_TRACK( m_board );
2618 
2619  t->SetPosition( start );
2620  t->SetEnd( wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
2621  t->SetWidth( width );
2622  t->SetLayer( layer );
2623  t->SetNetCode( netCode );
2624 
2625  m_board->Add( t );
2626  }
2627  else
2628  {
2629  // put non copper wires where the sun don't shine.
2630  }
2631 
2632  m_xpath->pop();
2633  }
2634  else if( itemName == "via" )
2635  {
2636  m_xpath->push( "via" );
2637  EVIA v( netItem );
2638 
2639  if( v.layer_front_most > v.layer_back_most )
2640  std::swap( v.layer_front_most, v.layer_back_most );
2641 
2642  PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most );
2643  PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most );
2644 
2645  if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most )
2646  && layer_front_most != layer_back_most )
2647  {
2648  int kidiam;
2649  int drillz = v.drill.ToPcbUnits();
2650  PCB_VIA* via = new PCB_VIA( m_board );
2651  m_board->Add( via );
2652 
2653  if( v.diam )
2654  {
2655  kidiam = v.diam->ToPcbUnits();
2656  via->SetWidth( kidiam );
2657  }
2658  else
2659  {
2660  double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
2661  annulus = eagleClamp( m_rules->rlMinViaOuter, annulus,
2663  kidiam = KiROUND( drillz + 2 * annulus );
2664  via->SetWidth( kidiam );
2665  }
2666 
2667  via->SetDrill( drillz );
2668 
2669  // make sure the via diameter respects the restring rules
2670 
2671  if( !v.diam || via->GetWidth() <= via->GetDrill() )
2672  {
2673  double annulus =
2675  (double) ( via->GetWidth() / 2 - via->GetDrill() ),
2677  via->SetWidth( drillz + 2 * annulus );
2678  }
2679 
2680  if( kidiam < m_min_via )
2681  m_min_via = kidiam;
2682 
2683  if( netclass && kidiam < netclass->GetViaDiameter() )
2684  netclass->SetViaDiameter( kidiam );
2685 
2686  if( drillz < m_min_hole )
2687  m_min_hole = drillz;
2688 
2689  if( netclass && drillz < netclass->GetViaDrill() )
2690  netclass->SetViaDrill( drillz );
2691 
2692  if( ( kidiam - drillz ) / 2 < m_min_annulus )
2693  m_min_annulus = ( kidiam - drillz ) / 2;
2694 
2695  if( layer_front_most == F_Cu && layer_back_most == B_Cu )
2696  {
2697  via->SetViaType( VIATYPE::THROUGH );
2698  }
2702  else if( v.layer_back_most - v.layer_front_most == 1 )
2703  {
2704  via->SetViaType( VIATYPE::MICROVIA );
2705  }
2706  else
2707  {
2708  via->SetViaType( VIATYPE::BLIND_BURIED );
2709  }
2710 
2711  wxPoint pos( kicad_x( v.x ), kicad_y( v.y ) );
2712 
2713  via->SetLayerPair( layer_front_most, layer_back_most );
2714  via->SetPosition( pos );
2715  via->SetEnd( pos );
2716 
2717  via->SetNetCode( netCode );
2718  }
2719 
2720  m_xpath->pop();
2721  }
2722 
2723  else if( itemName == "contactref" )
2724  {
2725  m_xpath->push( "contactref" );
2726  // <contactref element="RN1" pad="7"/>
2727 
2728  const wxString& reference = netItem->GetAttribute( "element" );
2729  const wxString& pad = netItem->GetAttribute( "pad" );
2730  wxString key = makeKey( reference, pad ) ;
2731 
2732  m_pads_to_nets[ key ] = ENET( netCode, netName );
2733 
2734  m_xpath->pop();
2735 
2736  sawPad = true;
2737  }
2738 
2739  else if( itemName == "polygon" )
2740  {
2741  m_xpath->push( "polygon" );
2742  auto* zone = loadPolygon( netItem );
2743 
2744  if( zone )
2745  {
2746  zones.push_back( zone );
2747 
2748  if( !zone->GetIsRuleArea() )
2749  zone->SetNetCode( netCode );
2750  }
2751 
2752  m_xpath->pop(); // "polygon"
2753  }
2754 
2755  netItem = netItem->GetNext();
2756  }
2757 
2758  if( zones.size() && !sawPad )
2759  {
2760  // KiCad does not support an unconnected zone with its own non-zero netcode,
2761  // but only when assigned netcode = 0 w/o a name...
2762  for( ZONE* zone : zones )
2763  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
2764 
2765  // therefore omit this signal/net.
2766  }
2767  else
2768  {
2769  netCode++;
2770  }
2771 
2772  // Get next signal
2773  net = net->GetNext();
2774  }
2775 
2776  m_xpath->pop(); // "signals.signal"
2777 }
2778 
2779 
2780 std::map<wxString, PCB_LAYER_ID> EAGLE_PLUGIN::DefaultLayerMappingCallback(
2781  const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2782 {
2783  std::map<wxString, PCB_LAYER_ID> layer_map;
2784 
2785  for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
2786  {
2787  PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
2788  layer_map.emplace( layer.Name, layerId );
2789  }
2790 
2791  return layer_map;
2792 }
2793 
2794 
2796 {
2797  std::vector<INPUT_LAYER_DESC> inputDescs;
2798 
2799  for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
2800  {
2801  const ELAYER& eLayer = layerPair.second;
2802 
2803  INPUT_LAYER_DESC layerDesc;
2804  std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
2805  defaultKicadLayer( eLayer.number );
2806 
2807  if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
2808  continue; // Ignore unused copper layers
2809 
2810  layerDesc.Name = eLayer.name;
2811 
2812  inputDescs.push_back( layerDesc );
2813  }
2814 
2815  if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
2816  dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
2817 
2818  m_layer_map = m_layer_mapping_handler( inputDescs );
2819 
2820  if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
2821  dynamic_cast<wxWindow*>( m_progressReporter )->Show();
2822 }
2823 
2824 
2825 PCB_LAYER_ID EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) const
2826 {
2827  auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
2828  return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
2829 }
2830 
2831 
2832 std::tuple<PCB_LAYER_ID, LSET, bool> EAGLE_PLUGIN::defaultKicadLayer( int aEagleLayer ) const
2833 {
2834  // eagle copper layer:
2835  if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
2836  {
2837  LSET copperLayers;
2838 
2839  for( int copperLayer : m_cu_map )
2840  {
2841  if( copperLayer >= 0 )
2842  copperLayers[copperLayer] = true;
2843  }
2844 
2845  return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
2846  }
2847 
2848  int kiLayer = UNSELECTED_LAYER;
2849  bool required = false;
2850  LSET permittedLayers;
2851 
2852  permittedLayers.set();
2853 
2854  // translate non-copper eagle layer to pcbnew layer
2855  switch( aEagleLayer )
2856  {
2857  // Eagle says "Dimension" layer, but it's for board perimeter
2859  kiLayer = Edge_Cuts;
2860  required = true;
2861  permittedLayers = LSET( 1, Edge_Cuts );
2862  break;
2863 
2864  case EAGLE_LAYER::TPLACE:
2865  kiLayer = F_SilkS;
2866  break;
2867  case EAGLE_LAYER::BPLACE:
2868  kiLayer = B_SilkS;
2869  break;
2870  case EAGLE_LAYER::TNAMES:
2871  kiLayer = F_SilkS;
2872  break;
2873  case EAGLE_LAYER::BNAMES:
2874  kiLayer = B_SilkS;
2875  break;
2876  case EAGLE_LAYER::TVALUES:
2877  kiLayer = F_Fab;
2878  break;
2879  case EAGLE_LAYER::BVALUES:
2880  kiLayer = B_Fab;
2881  break;
2882  case EAGLE_LAYER::TSTOP:
2883  kiLayer = F_Mask;
2884  break;
2885  case EAGLE_LAYER::BSTOP:
2886  kiLayer = B_Mask;
2887  break;
2888  case EAGLE_LAYER::TCREAM:
2889  kiLayer = F_Paste;
2890  break;
2891  case EAGLE_LAYER::BCREAM:
2892  kiLayer = B_Paste;
2893  break;
2894  case EAGLE_LAYER::TFINISH:
2895  kiLayer = F_Mask;
2896  break;
2897  case EAGLE_LAYER::BFINISH:
2898  kiLayer = B_Mask;
2899  break;
2900  case EAGLE_LAYER::TGLUE:
2901  kiLayer = F_Adhes;
2902  break;
2903  case EAGLE_LAYER::BGLUE:
2904  kiLayer = B_Adhes;
2905  break;
2906  case EAGLE_LAYER::DOCUMENT:
2907  kiLayer = Cmts_User;
2908  break;
2910  kiLayer = Cmts_User;
2911  break;
2913  kiLayer = Cmts_User;
2914  break;
2915 
2916  // Packages show the future chip pins on SMD parts using layer 51.
2917  // This is an area slightly smaller than the PAD/SMD copper area.
2918  // Carry those visual aids into the FOOTPRINT on the fabrication layer,
2919  // not silkscreen. This is perhaps not perfect, but there is not a lot
2920  // of other suitable paired layers
2921  case EAGLE_LAYER::TDOCU:
2922  kiLayer = F_Fab;
2923  break;
2924  case EAGLE_LAYER::BDOCU:
2925  kiLayer = B_Fab;
2926  break;
2927 
2928  // these layers are defined as user layers. put them on ECO layers
2930  kiLayer = Eco1_User;
2931  break;
2933  kiLayer = Eco2_User;
2934  break;
2935 
2936  // these will also appear in the ratsnest, so there's no need for a warning
2937  case EAGLE_LAYER::UNROUTED:
2938  kiLayer = Dwgs_User;
2939  break;
2940 
2941  case EAGLE_LAYER::TKEEPOUT:
2942  kiLayer = F_CrtYd;
2943  break;
2944  case EAGLE_LAYER::BKEEPOUT:
2945  kiLayer = B_CrtYd;
2946  break;
2947 
2948  case EAGLE_LAYER::MILLING:
2949  case EAGLE_LAYER::TTEST:
2950  case EAGLE_LAYER::BTEST:
2951  case EAGLE_LAYER::HOLES:
2952  default:
2953  kiLayer = UNDEFINED_LAYER;
2954  break;
2955  }
2956 
2957  return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
2958 }
2959 
2960 
2961 const wxString& EAGLE_PLUGIN::eagle_layer_name( int aLayer ) const
2962 {
2963  static const wxString unknown( "unknown" );
2964  auto it = m_eagleLayers.find( aLayer );
2965  return it == m_eagleLayers.end() ? unknown : it->second.name;
2966 }
2967 
2968 
2969 int EAGLE_PLUGIN::eagle_layer_id( const wxString& aLayerName ) const
2970 {
2971  static const int unknown = -1;
2972  auto it = m_eagleLayersIds.find( aLayerName );
2973  return it == m_eagleLayersIds.end() ? unknown : it->second;
2974 }
2975 
2976 
2978 {
2979  if( m_props )
2980  {
2981  UTF8 page_width;
2982  UTF8 page_height;
2983 
2984  if( m_props->Value( "page_width", &page_width ) &&
2985  m_props->Value( "page_height", &page_height ) )
2986  {
2988 
2989  int w = atoi( page_width.c_str() );
2990  int h = atoi( page_height.c_str() );
2991 
2992  int desired_x = ( w - bbbox.GetWidth() ) / 2;
2993  int desired_y = ( h - bbbox.GetHeight() ) / 2;
2994 
2995  m_board->Move( wxPoint( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
2996  }
2997  }
2998 }
2999 
3000 
3001 wxDateTime EAGLE_PLUGIN::getModificationTime( const wxString& aPath )
3002 {
3003  // File hasn't been loaded yet.
3004  if( aPath.IsEmpty() )
3005  return wxDateTime::Now();
3006 
3007  wxFileName fn( aPath );
3008 
3009  if( fn.IsFileReadable() )
3010  return fn.GetModificationTime();
3011  else
3012  return wxDateTime( 0.0 );
3013 }
3014 
3015 
3016 void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath )
3017 {
3018  try
3019  {
3020  wxDateTime modtime = getModificationTime( aLibPath );
3021 
3022  // Fixes assertions in wxWidgets debug builds for the wxDateTime object. Refresh the
3023  // cache if either of the wxDateTime objects are invalid or the last file modification
3024  // time differs from the current file modification time.
3025  bool load = !m_mod_time.IsValid() || !modtime.IsValid() || m_mod_time != modtime;
3026 
3027  if( aLibPath != m_lib_path || load )
3028  {
3029  wxXmlNode* doc;
3030  LOCALE_IO toggle; // toggles on, then off, the C locale.
3031 
3032  deleteTemplates();
3033 
3034  // Set this before completion of loading, since we rely on it for
3035  // text of an exception. Delay setting m_mod_time until after successful load
3036  // however.
3037  m_lib_path = aLibPath;
3038 
3039  // 8 bit "filename" should be encoded according to disk filename encoding,
3040  // (maybe this is current locale, maybe not, its a filesystem issue),
3041  // and is not necessarily utf8.
3042  string filename = (const char*) aLibPath.char_str( wxConvFile );
3043 
3044  // Load the document
3045  wxFileName fn( filename );
3046  wxFFileInputStream stream( fn.GetFullPath() );
3047  wxXmlDocument xmlDocument;
3048 
3049  if( !stream.IsOk() || !xmlDocument.Load( stream ) )
3050  {
3051  THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ),
3052  fn.GetFullPath() ) );
3053  }
3054 
3055  doc = xmlDocument.GetRoot();
3056 
3057  wxXmlNode* drawing = MapChildren( doc )["drawing"];
3058  NODE_MAP drawingChildren = MapChildren( drawing );
3059 
3060  // clear the cu map and then rebuild it.
3061  clear_cu_map();
3062 
3063  m_xpath->push( "eagle.drawing.layers" );
3064  wxXmlNode* layers = drawingChildren["layers"];
3065  loadLayerDefs( layers );
3067  m_xpath->pop();
3068 
3069  m_xpath->push( "eagle.drawing.library" );
3070  wxXmlNode* library = drawingChildren["library"];
3071  loadLibrary( library, nullptr );
3072  m_xpath->pop();
3073 
3074  m_mod_time = modtime;
3075  }
3076  }
3077  catch(...){}
3078  // TODO: Handle exceptions
3079  // catch( file_parser_error fpe )
3080  // {
3081  // // for xml_parser_error, what() has the line number in it,
3082  // // but no byte offset. That should be an adequate error message.
3083  // THROW_IO_ERROR( fpe.what() );
3084  // }
3085  //
3086  // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
3087  // // so one catch should be OK for all errors.
3088  // catch( ptree_error pte )
3089  // {
3090  // string errmsg = pte.what();
3091  //
3092  // errmsg += " @\n";
3093  // errmsg += m_xpath->Contents();
3094  //
3095  // THROW_IO_ERROR( errmsg );
3096  // }
3097 }
3098 
3099 
3100 void EAGLE_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
3101  bool aBestEfforts, const PROPERTIES* aProperties )
3102 {
3103  wxString errorMsg;
3104 
3105  init( aProperties );
3106 
3107  try
3108  {
3109  cacheLib( aLibraryPath );
3110  }
3111  catch( const IO_ERROR& ioe )
3112  {
3113  errorMsg = ioe.What();
3114  }
3115 
3116  // Some of the files may have been parsed correctly so we want to add the valid files to
3117  // the library.
3118 
3119  for( FOOTPRINT_MAP::const_iterator it = m_templates.begin(); it != m_templates.end(); ++it )
3120  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
3121 
3122  if( !errorMsg.IsEmpty() && !aBestEfforts )
3123  THROW_IO_ERROR( errorMsg );
3124 }
3125 
3126 
3127 FOOTPRINT* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
3128  const wxString& aFootprintName, bool aKeepUUID,
3129  const PROPERTIES* aProperties )
3130 {
3131  init( aProperties );
3132  cacheLib( aLibraryPath );
3133  FOOTPRINT_MAP::const_iterator it = m_templates.find( aFootprintName );
3134 
3135  if( it == m_templates.end() )
3136  return nullptr;
3137 
3138  // Return a copy of the template
3139  FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate();
3140  copy->SetParent( nullptr );
3141  return copy;
3142 }
3143 
3144 
3145 void EAGLE_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const
3146 {
3147  PLUGIN::FootprintLibOptions( aListToAppendTo );
3148 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:209
void SetReference(const wxString &aReference)
Definition: footprint.h:473
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.
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:745
const wxString & GetValue() const
Definition: footprint.h:486
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:83
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:126
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.
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:163
void clear()
Definition: eagle_parser.h:120
std::map< wxString, PCB_LAYER_ID > DefaultLayerMappingCallback(const std::vector< INPUT_LAYER_DESC > &aInputLayerDescriptionVector)
Return the automapped layers.
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition: zone.h:614
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aHatchStyle, int aHatchPitch, bool aRebuildHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:910
int GetX() const
Definition: eda_rect.h:98
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
ERULES * m_rules
Eagle design rules.
Definition: eagle_plugin.h: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:188
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:109
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:525
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
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
ECOORD y
Definition: eagle_parser.h:817
A name/value tuple with unique names and optional values.
Definition: properties.h:33
Eagle hole element.
Definition: eagle_parser.h:799
Implement a simple wrapper around runtime_error to isolate the errors thrown by the Eagle XML parser.
Definition: eagle_parser.h:68
void SetPriority(unsigned aPriority)
Definition: zone.h:117
Eagle text element.
Definition: eagle_parser.h:637
const wxString & eagle_layer_name(int aLayer) const
Get Eagle layer name by its number.
FOOTPRINT_MAP m_templates
is part of a FOOTPRINT factory that operates using copy construction.
Definition: eagle_plugin.h: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:500
const char * c_str() const
Definition: utf8.h:102
void SetLineThickness(int aWidth)
For better understanding of the points that make a dimension:
OPTIONAL_XML_ATTRIBUTE< wxString > opt_wxString
Definition: eagle_parser.h:365
int layer_back_most
< extent
Definition: eagle_parser.h:552
void SetIsRuleArea(bool aEnable)
Definition: zone.h:742
unsigned m_doneCount
Definition: eagle_plugin.h:311
FP_TEXT & Reference()
Definition: footprint.h:501
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:101
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:721
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:505
#define DEFAULT_SILK_LINE_WIDTH
void NewHole()
Create a new hole on the zone; i.e., a new contour on the zone's outline.
Definition: zone.h:597
void cacheLib(const wxString &aLibraryPath)
This PLUGIN only caches one footprint library, this determines which one.
wxString escapeName(const wxString &aNetName)
pads are covered by copper
const PROPERTIES * m_props
passed via Save() or Load(), no ownership, may be NULL.
Definition: eagle_plugin.h: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
Eagle thru hole pad.
Definition: eagle_parser.h:706
static int GetDefaultHatchPitch()
Definition: zone.cpp:1102
opt_ecoord spacing
Definition: eagle_parser.h:774
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:746
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:1680
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:332
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:1490
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:464
#define _(s)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:744
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
void SetWidth(int aWidth)
Definition: eda_shape.h:88
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:475
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:494
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:110
Eagle net.
Definition: eagle_parser.h:453
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:207
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:797
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:1415
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:1063
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:850
#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:743
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:91
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:136
int GetY() const
Definition: eda_rect.h:99
ECOORD y
Definition: eagle_parser.h:550
void SetHatchOrientation(double aStep)
Definition: zone.h:260
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
int m_min_annulus
smallest via annulus we find on Load(), in BIU.
Definition: eagle_plugin.h: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
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:833
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:1561
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:142
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:945
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:747
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).