1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 
26 /*
27 
28 Pcbnew PLUGIN for Eagle 6.x XML *.brd and footprint format.
29 
30 XML parsing and converting:
31 Getting line numbers and byte offsets from the source XML file is not
32 possible using currently available XML libraries within KiCad project:
33 wxXmlDocument and boost::property_tree.
34 
35 property_tree will give line numbers but no byte offsets, and only during
36 document loading. This means that if we have a problem after the document is
37 successfully loaded, there is no way to correlate back to line number and byte
38 offset of the problem. So a different approach is taken, one which relies on the
39 XML elements themselves using an XPATH type of reporting mechanism. The path to
40 the problem is reported in the error messages. This means keeping track of that
41 path as we traverse the XML document for the sole purpose of accurate error
42 reporting.
43 
44 User can load the source XML file into firefox or other xml browser and follow
45 our error message.
46 
47 Load() TODO's
48 
49 *) verify zone fill clearances are correct
50 
51 */
52 
53 #include <cerrno>
54 
55 #include <wx/string.h>
56 #include <wx/xml/xml.h>
57 #include <wx/filename.h>
58 #include <wx/log.h>
59 #include <wx/wfstream.h>
60 
61 #include <convert_basic_shapes_to_polygon.h>
62 #include <core/arraydim.h>
63 #include <geometry/geometry_utils.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 
83 #include <plugins/eagle/eagle_plugin.h>
84 
85 using namespace std;
86 
87 
88 /// Parse an eagle distance which is either mm, or mils if there is "mil" suffix.
89 /// Return is in BIU.
parseEagle(const wxString & aDistance)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>
eagleClamp(T aMin,T aValue,T aMax)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 
111 /// Assemble a two part key as a simple concatenation of aFirst and aSecond parts,
112 /// using a separator.
makeKey(const wxString & aFirst,const wxString & aSecond)113 static wxString makeKey( const wxString& aFirst, const wxString& aSecond )
114 {
115     wxString key = aFirst + '\x02' +  aSecond;
116     return key;
117 }
118 
119 
120 /// interpret special characters in Eagle text and converts them to KiCAD notation
interpret_text(const wxString & aText)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 
setKeepoutSettingsToZone(ZONE * aZone,LAYER_NUM aLayer)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 
parse(wxXmlNode * aRules,std::function<void ()> aCheckpoint)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 
EAGLE_PLUGIN()274 EAGLE_PLUGIN::EAGLE_PLUGIN() :
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();
287     RegisterLayerMappingCallback( std::bind( &EAGLE_PLUGIN::DefaultLayerMappingCallback,
288                                              this, _1 ) );
289 }
290 
291 
~EAGLE_PLUGIN()292 EAGLE_PLUGIN::~EAGLE_PLUGIN()
293 {
294     deleteTemplates();
295     delete m_rules;
296     delete m_xpath;
297 }
298 
299 
PluginName() const300 const wxString EAGLE_PLUGIN::PluginName() const
301 {
302     return wxT( "Eagle" );
303 }
304 
305 
GetFileExtension() const306 const wxString EAGLE_PLUGIN::GetFileExtension() const
307 {
308     return wxT( "brd" );
309 }
310 
311 
checkpoint()312 void EAGLE_PLUGIN::checkpoint()
313 {
314     const unsigned PROGRESS_DELTA = 50;
315 
316     if( m_progressReporter )
317     {
318         if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
319         {
320             m_progressReporter->SetCurrentProgress( ( (double) m_doneCount )
321                                                     / std::max( 1U, m_totalCount ) );
322 
323             if( !m_progressReporter->KeepRefreshing() )
324                 THROW_IO_ERROR( ( "Open cancelled by user." ) );
325 
326             m_lastProgressCount = m_doneCount;
327         }
328     }
329 }
330 
331 
kicad_fontz(const ECOORD & d,int aTextThickness) const332 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 
Load(const wxString & aFileName,BOARD * aAppendToMe,const PROPERTIES * aProperties,PROJECT * aProject,PROGRESS_REPORTER * aProgressReporter)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 
365             if( !m_progressReporter->KeepRefreshing() )
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 
431         m_board->m_LegacyNetclassesLoaded = true;
432         m_board->m_LegacyDesignSettingsLoaded = true;
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 += "\n@ ";
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 
GetImportedCachedLibraryFootprints()471 std::vector<FOOTPRINT*> EAGLE_PLUGIN::GetImportedCachedLibraryFootprints()
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 
init(const PROPERTIES * aProperties)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 
clear_cu_map()501 void EAGLE_PLUGIN::clear_cu_map()
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 
loadAllSections(wxXmlNode * aDoc)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 );
575         mapEagleLayersToKicad();
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 
loadDesignRules(wxXmlNode * aDesignRules)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 
loadLayerDefs(wxXmlNode * aLayers)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 
loadPlain(wxXmlNode * aGraphics)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:
811                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
812                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
813                     break;
814 
815                 case ETEXT::CENTER_LEFT:
816                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
817                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
818                     break;
819 
820                 case ETEXT::CENTER_RIGHT:
821                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
822                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
823                     break;
824 
825                 case ETEXT::TOP_CENTER:
826                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
827                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
828                     break;
829 
830                 case ETEXT::TOP_LEFT:
831                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
832                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
833                     break;
834 
835                 case ETEXT::TOP_RIGHT:
836                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
837                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
838                     break;
839 
840                 case ETEXT::BOTTOM_CENTER:
841                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
842                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
843                     break;
844 
845                 case ETEXT::BOTTOM_LEFT:
846                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
847                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
848                     break;
849 
850                 case ETEXT::BOTTOM_RIGHT:
851                     pcbtxt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
852                     pcbtxt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
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 
868             if( c.layer == EAGLE_LAYER::TRESTRICT || c.layer == EAGLE_LAYER::BRESTRICT
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 
900                 zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
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 );
937                 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
938 
939                 ZONE_BORDER_DISPLAY_STYLE outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE;
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 
loadLibrary(wxXmlNode * aLib,const wxString * aLibName)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 
loadLibraries(wxXmlNode * aLibs)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 
loadElements(wxXmlNode * aElements)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 
loadPolygon(wxXmlNode * aPolyNode)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
1390                           || p.layer == EAGLE_LAYER::BRESTRICT
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     {
1468         polygon.Inflate( p.width.ToPcbUnits() / 2, 32, SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS );
1469         polygon.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
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 );
1483         zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
1484     }
1485     else if( p.pour == EPOLYGON::HATCH )
1486     {
1487         int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * IU_PER_MILS;
1488 
1489         zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
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
1497     zone->SetMinThickness( std::max<int>( ZONE_THICKNESS_MIN_VALUE_MIL * IU_PER_MILS,
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;
1507     zone->SetPadConnection( thermals ? ZONE_CONNECTION::THERMAL : ZONE_CONNECTION::FULL );
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 
orientFootprintAndText(FOOTPRINT * aFootprint,const EELEMENT & e,const EATTR * aNameAttr,const EATTR * aValueAttr)1526 void EAGLE_PLUGIN::orientFootprintAndText( FOOTPRINT* aFootprint, const EELEMENT& e,
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 
orientFPText(FOOTPRINT * aFootprint,const EELEMENT & e,FP_TEXT * aFPText,const EATTR * aAttr)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:
1634             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1635             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
1636             break;
1637 
1638         case ETEXT::BOTTOM_LEFT:
1639             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1640             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
1641             break;
1642 
1643         case ETEXT::TOP_LEFT:
1644             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1645             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
1646             break;
1647 
1648         case ETEXT::BOTTOM_RIGHT:
1649             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1650             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
1651             break;
1652 
1653         case ETEXT::TOP_CENTER:
1654             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
1655             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
1656             break;
1657 
1658         case ETEXT::BOTTOM_CENTER:
1659             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
1660             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
1661             break;
1662 
1663         case ETEXT::CENTER:
1664             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
1665             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1666             break;
1667 
1668         case ETEXT::CENTER_LEFT:
1669             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1670             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1671             break;
1672 
1673         case ETEXT::CENTER_RIGHT:
1674             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1675             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
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:
1693             aFPText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1694             aFPText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
1695         }
1696     }
1697 }
1698 
1699 
makeFootprint(wxXmlNode * aPackage,const wxString & aPkgName)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     while( packageItem )
1712     {
1713         const wxString& itemName = packageItem->GetName();
1714 
1715         if( itemName == "description" )
1716             m->SetDescription( FROM_UTF8( packageItem->GetNodeContent().c_str() ) );
1717         else if( itemName == "wire" )
1718             packageWire( m.get(), packageItem );
1719         else if( itemName == "pad" )
1720             packagePad( m.get(), packageItem );
1721         else if( itemName == "text" )
1722             packageText( m.get(), packageItem );
1723         else if( itemName == "rectangle" )
1724             packageRectangle( m.get(), packageItem );
1725         else if( itemName == "polygon" )
1726             packagePolygon( m.get(), packageItem );
1727         else if( itemName == "circle" )
1728             packageCircle( m.get(), packageItem );
1729         else if( itemName == "hole" )
1730             packageHole( m.get(), packageItem, false );
1731         else if( itemName == "smd" )
1732             packageSMD( m.get(), packageItem );
1733 
1734         packageItem = packageItem->GetNext();
1735     }
1736 
1737     return m.release();
1738 }
1739 
1740 
packageWire(FOOTPRINT * aFootprint,wxXmlNode * aTree) const1741 void EAGLE_PLUGIN::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1742 {
1743     EWIRE        w( aTree );
1744     PCB_LAYER_ID layer = kicad_layer( w.layer );
1745     wxPoint      start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
1746     wxPoint      end(   kicad_x( w.x2 ), kicad_y( w.y2 ) );
1747     int          width = w.width.ToPcbUnits();
1748 
1749     if( layer == UNDEFINED_LAYER )
1750     {
1751         wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) "
1752                                            "was not mapped" ),
1753                                         eagle_layer_name( w.layer ), w.layer ) );
1754         return;
1755     }
1756 
1757     // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
1758     if( width <= 0 )
1759     {
1760         BOARD* board = aFootprint->GetBoard();
1761 
1762         if( board )
1763         {
1764             width = board->GetDesignSettings().GetLineThickness( layer );
1765         }
1766         else
1767         {
1768             // When loading footprint libraries, there is no board so use the default KiCad
1769             // line widths.
1770             switch( layer )
1771             {
1772             case Edge_Cuts: width = Millimeter2iu( DEFAULT_EDGE_WIDTH );      break;
1773 
1774             case F_SilkS:
1775             case B_SilkS:   width = Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ); break;
1776 
1777             case F_CrtYd:
1778             case B_CrtYd:   width = Millimeter2iu( DEFAULT_COURTYARD_WIDTH ); break;
1779 
1780             default:        width = Millimeter2iu( DEFAULT_LINE_WIDTH );      break;
1781             }
1782         }
1783     }
1784 
1785     // FIXME: the cap attribute is ignored because KiCad can't create lines
1786     //        with flat ends.
1787     FP_SHAPE* dwg;
1788 
1789     if( !w.curve )
1790     {
1791         dwg = new FP_SHAPE( aFootprint, SHAPE_T::SEGMENT );
1792 
1793         dwg->SetStart0( start );
1794         dwg->SetEnd0( end );
1795     }
1796     else
1797     {
1798         dwg = new FP_SHAPE( aFootprint, SHAPE_T::ARC );
1799         wxPoint center = ConvertArcCenter( start, end, *w.curve );
1800 
1801         dwg->SetCenter0( center );
1802         dwg->SetStart0( start );
1803         dwg->SetArcAngleAndEnd0( *w.curve * -10.0, true ); // KiCad rotates the other way
1804     }
1805 
1806     dwg->SetLayer( layer );
1807     dwg->SetWidth( width );
1808     dwg->SetDrawCoord();
1809 
1810     aFootprint->Add( dwg );
1811 }
1812 
1813 
packagePad(FOOTPRINT * aFootprint,wxXmlNode * aTree)1814 void EAGLE_PLUGIN::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
1815 {
1816     // this is thru hole technology here, no SMDs
1817     EPAD e( aTree );
1818     int shape = EPAD::UNDEF;
1819     int eagleDrillz = e.drill.ToPcbUnits();
1820 
1821     PAD* pad = new PAD( aFootprint );
1822     aFootprint->Add( pad );
1823     transferPad( e, pad );
1824 
1825     if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
1826         shape = m_rules->psFirst;
1827     else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
1828         shape = m_rules->psTop;
1829     else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
1830         shape = m_rules->psBottom;
1831 
1832     pad->SetDrillSize( wxSize( eagleDrillz, eagleDrillz ) );
1833     pad->SetLayerSet( LSET::AllCuMask() );
1834 
1835     if( eagleDrillz < m_min_hole )
1836         m_min_hole = eagleDrillz;
1837 
1838     // Solder mask
1839     if( !e.stop || *e.stop == true )         // enabled by default
1840         pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
1841 
1842     if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
1843         e.shape = shape;
1844 
1845     if( e.shape )
1846     {
1847         switch( *e.shape )
1848         {
1849         case EPAD::ROUND:
1850             pad->SetShape( PAD_SHAPE::CIRCLE );
1851             break;
1852 
1853         case EPAD::OCTAGON:
1854             // no KiCad octagonal pad shape, use PAD_CIRCLE for now.
1855             // pad->SetShape( PAD_OCTAGON );
1856             wxASSERT( pad->GetShape() == PAD_SHAPE::CIRCLE );    // verify set in PAD constructor
1857             pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
1858             pad->SetChamferPositions( RECT_CHAMFER_ALL );
1859             pad->SetChamferRectRatio( 0.25 );
1860             break;
1861 
1862         case EPAD::LONG:
1863             pad->SetShape( PAD_SHAPE::OVAL );
1864             break;
1865 
1866         case EPAD::SQUARE:
1867             pad->SetShape( PAD_SHAPE::RECT );
1868             break;
1869 
1870         case EPAD::OFFSET:
1871             pad->SetShape( PAD_SHAPE::OVAL );
1872             break;
1873         }
1874     }
1875     else
1876     {
1877         // if shape is not present, our default is circle and that matches their default "round"
1878     }
1879 
1880     if( e.diameter && e.diameter->value > 0 )
1881     {
1882         int diameter = e.diameter->ToPcbUnits();
1883         pad->SetSize( wxSize( diameter, diameter ) );
1884     }
1885     else
1886     {
1887         double drillz  = pad->GetDrillSize().x;
1888         double annulus = drillz * m_rules->rvPadTop;   // copper annulus, eagle "restring"
1889         annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
1890         int diameter = KiROUND( drillz + 2 * annulus );
1891         pad->SetSize( wxSize( KiROUND( diameter ), KiROUND( diameter ) ) );
1892     }
1893 
1894     if( pad->GetShape() == PAD_SHAPE::OVAL )
1895     {
1896         // The Eagle "long" pad is wider than it is tall,
1897         // m_elongation is percent elongation
1898         wxSize sz = pad->GetSize();
1899         sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
1900         pad->SetSize( sz );
1901 
1902         if( e.shape && *e.shape == EPAD::OFFSET )
1903         {
1904             int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
1905             pad->SetOffset( wxPoint( offset, 0 ) );
1906         }
1907     }
1908 
1909     if( e.rot )
1910     {
1911         pad->SetOrientation( e.rot->degrees * 10 );
1912     }
1913 }
1914 
1915 
packageText(FOOTPRINT * aFootprint,wxXmlNode * aTree) const1916 void EAGLE_PLUGIN::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1917 {
1918     ETEXT        t( aTree );
1919     PCB_LAYER_ID layer = kicad_layer( t.layer );
1920 
1921     if( layer == UNDEFINED_LAYER )
1922     {
1923         wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) "
1924                                            "was not mapped" ),
1925                                         eagle_layer_name( t.layer ), t.layer ) );
1926         return;
1927     }
1928 
1929     FP_TEXT* txt;
1930 
1931     if( t.text == ">NAME" || t.text == ">name" )
1932         txt = &aFootprint->Reference();
1933     else if( t.text == ">VALUE" || t.text == ">value" )
1934         txt = &aFootprint->Value();
1935     else
1936     {
1937         // FIXME: graphical text items are rotated for some reason.
1938         txt = new FP_TEXT( aFootprint );
1939         aFootprint->Add( txt );
1940     }
1941 
1942     txt->SetText( FROM_UTF8( t.text.c_str() ) );
1943 
1944     wxPoint pos( kicad_x( t.x ), kicad_y( t.y ) );
1945 
1946     txt->SetTextPos( pos );
1947     txt->SetPos0( pos - aFootprint->GetPosition() );
1948 
1949     txt->SetLayer( layer );
1950 
1951     double ratio = t.ratio ? *t.ratio : 8;  // DTD says 8 is default
1952     int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
1953 
1954     txt->SetTextThickness( textThickness );
1955     txt->SetTextSize( kicad_fontz( t.size, textThickness ) );
1956 
1957     int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT;  // bottom-left is eagle default
1958 
1959     // An eagle package is never rotated, the DTD does not allow it.
1960     // angle -= aFootprint->GetOrienation();
1961 
1962     if( t.rot )
1963     {
1964         int sign = t.rot->mirror ? -1 : 1;
1965         txt->SetMirrored( t.rot->mirror );
1966 
1967         double degrees = t.rot->degrees;
1968 
1969         if( degrees == 90 || t.rot->spin )
1970         {
1971             txt->SetTextAngle( sign * degrees * 10 );
1972         }
1973         else if( degrees == 180 )
1974         {
1975             align = ETEXT::TOP_RIGHT;
1976         }
1977         else if( degrees == 270 )
1978         {
1979             align = ETEXT::TOP_RIGHT;
1980             txt->SetTextAngle( sign * 90 * 10 );
1981         }
1982     }
1983 
1984     switch( align )
1985     {
1986     case ETEXT::CENTER:
1987         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
1988         txt->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1989         break;
1990 
1991     case ETEXT::CENTER_LEFT:
1992         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1993         txt->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1994         break;
1995 
1996     case ETEXT::CENTER_RIGHT:
1997         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1998         txt->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1999         break;
2000 
2001     case ETEXT::TOP_CENTER:
2002         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2003         txt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2004         break;
2005 
2006     case ETEXT::TOP_LEFT:
2007         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2008         txt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2009         break;
2010 
2011     case ETEXT::TOP_RIGHT:
2012         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2013         txt->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2014         break;
2015 
2016     case ETEXT::BOTTOM_CENTER:
2017         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2018         txt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2019         break;
2020 
2021     case ETEXT::BOTTOM_LEFT:
2022         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2023         txt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2024         break;
2025 
2026     case ETEXT::BOTTOM_RIGHT:
2027         txt->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2028         txt->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2029         break;
2030     }
2031 }
2032 
2033 
packageRectangle(FOOTPRINT * aFootprint,wxXmlNode * aTree) const2034 void EAGLE_PLUGIN::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2035 {
2036     ERECT r( aTree );
2037 
2038     if( r.layer == EAGLE_LAYER::TRESTRICT || r.layer == EAGLE_LAYER::BRESTRICT
2039             || r.layer == EAGLE_LAYER::VRESTRICT )
2040     {
2041         FP_ZONE* zone = new FP_ZONE( aFootprint );
2042         aFootprint->Add( zone, ADD_MODE::APPEND );
2043 
2044         setKeepoutSettingsToZone( zone, r.layer );
2045 
2046         const int outlineIdx = -1; // this is the id of the copper zone main outline
2047         zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
2048         zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
2049         zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
2050         zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
2051 
2052         if( r.rot )
2053         {
2054             wxPoint center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
2055                     ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
2056             zone->Rotate( center, r.rot->degrees * 10 );
2057         }
2058 
2059         zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2060                                      ZONE::GetDefaultHatchPitch(), true );
2061     }
2062     else
2063     {
2064         PCB_LAYER_ID layer = kicad_layer( r.layer );
2065 
2066         if( layer == UNDEFINED_LAYER )
2067         {
2068             wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) "
2069                                                "was not mapped" ),
2070                                             eagle_layer_name( r.layer ), r.layer ) );
2071             return;
2072         }
2073 
2074         FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2075 
2076         aFootprint->Add( dwg );
2077 
2078         dwg->SetLayer( layer );
2079         dwg->SetWidth( 0 );
2080         dwg->SetFilled( true );
2081 
2082         std::vector<wxPoint> pts;
2083 
2084         wxPoint start( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
2085         wxPoint end( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
2086 
2087         pts.push_back( start );
2088         pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
2089         pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
2090         pts.push_back( end );
2091 
2092         dwg->SetPolyPoints( pts );
2093 
2094         dwg->SetStart0( start );
2095         dwg->SetEnd0( end );
2096 
2097         if( r.rot )
2098             dwg->Rotate( dwg->GetCenter(), r.rot->degrees * 10 );
2099     }
2100 }
2101 
2102 
packagePolygon(FOOTPRINT * aFootprint,wxXmlNode * aTree) const2103 void EAGLE_PLUGIN::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2104 {
2105     EPOLYGON      p( aTree );
2106 
2107     std::vector<wxPoint> pts;
2108 
2109     // Get the first vertex and iterate
2110     wxXmlNode* vertex = aTree->GetChildren();
2111     std::vector<EVERTEX> vertices;
2112 
2113     // Create a circular vector of vertices
2114     // The "curve" parameter indicates a curve from the current
2115     // to the next vertex, so we keep the first at the end as well
2116     // to allow the curve to link back
2117     while( vertex )
2118     {
2119         if( vertex->GetName() == "vertex" )
2120             vertices.emplace_back( vertex );
2121 
2122         vertex = vertex->GetNext();
2123     }
2124 
2125     vertices.push_back( vertices[0] );
2126 
2127     for( size_t i = 0; i < vertices.size() - 1; i++ )
2128     {
2129         EVERTEX v1 = vertices[i];
2130 
2131         // Append the corner
2132         pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
2133 
2134         if( v1.curve )
2135         {
2136             EVERTEX v2 = vertices[i + 1];
2137             wxPoint center =
2138                     ConvertArcCenter( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
2139                                       wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
2140             double angle = DEG2RAD( *v1.curve );
2141             double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
2142             double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
2143                                   + pow( center.y - kicad_y( v1.y ), 2 ) );
2144 
2145             // Don't allow a zero-radius curve
2146             if( KiROUND( radius ) == 0 )
2147                 radius = 1.0;
2148 
2149             int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
2150             double delta = angle / segCount;
2151 
2152             for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
2153             {
2154                 pts.push_back( wxPoint( KiROUND( radius * cos( a ) ),
2155                                         KiROUND( radius * sin( a ) ) ) + center );
2156             }
2157         }
2158     }
2159 
2160     if( p.layer == EAGLE_LAYER::TRESTRICT
2161      || p.layer == EAGLE_LAYER::BRESTRICT
2162      || p.layer == EAGLE_LAYER::VRESTRICT )
2163     {
2164         FP_ZONE* zone = new FP_ZONE( aFootprint );
2165         aFootprint->Add( zone, ADD_MODE::APPEND );
2166 
2167         setKeepoutSettingsToZone( zone, p.layer );
2168 
2169         SHAPE_LINE_CHAIN outline( pts );
2170         outline.SetClosed( true );
2171         zone->Outline()->AddOutline( outline );
2172 
2173         zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2174                                      ZONE::GetDefaultHatchPitch(), true );
2175     }
2176     else
2177     {
2178         PCB_LAYER_ID layer = kicad_layer( p.layer );
2179 
2180         if( layer == UNDEFINED_LAYER )
2181         {
2182             wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
2183                                                "was not mapped" ),
2184                                             eagle_layer_name( p.layer ), p.layer ) );
2185             return;
2186         }
2187 
2188         FP_SHAPE* dwg = new FP_SHAPE( aFootprint, SHAPE_T::POLY );
2189 
2190         aFootprint->Add( dwg );
2191 
2192         dwg->SetWidth( 0 ); // it's filled, no need for boundary width
2193         dwg->SetFilled( true );
2194         dwg->SetLayer( layer );
2195 
2196         dwg->SetPolyPoints( pts );
2197         dwg->SetStart0( *pts.begin() );
2198         dwg->SetEnd0( pts.back() );
2199         dwg->SetDrawCoord();
2200         dwg->GetPolyShape().Inflate( p.width.ToPcbUnits() / 2, 32,
2201                                      SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS );
2202     }
2203 }
2204 
2205 
packageCircle(FOOTPRINT * aFootprint,wxXmlNode * aTree) const2206 void EAGLE_PLUGIN::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2207 {
2208     ECIRCLE e( aTree );
2209 
2210     int width  = e.width.ToPcbUnits();
2211     int radius = e.radius.ToPcbUnits();
2212 
2213     if( e.layer == EAGLE_LAYER::TRESTRICT
2214      || e.layer == EAGLE_LAYER::BRESTRICT
2215      || e.layer == EAGLE_LAYER::VRESTRICT )
2216     {
2217         FP_ZONE* zone = new FP_ZONE( aFootprint );
2218         aFootprint->Add( zone, ADD_MODE::APPEND );
2219 
2220         setKeepoutSettingsToZone( zone, e.layer );
2221 
2222         // approximate circle as polygon with a edge every 10 degree
2223         wxPoint center( kicad_x( e.x ), kicad_y( e.y ) );
2224         int     outlineRadius = radius + ( width / 2 );
2225 
2226         for( int angle = 0; angle < 360; angle += 10 )
2227         {
2228             wxPoint rotatedPoint( outlineRadius, 0 );
2229             RotatePoint( &rotatedPoint, angle * 10. );
2230             zone->AppendCorner( center + rotatedPoint, -1 );
2231         }
2232 
2233         if( width > 0 )
2234         {
2235             zone->NewHole();
2236             int innerRadius = radius - ( width / 2 );
2237 
2238             for( int angle = 0; angle < 360; angle += 10 )
2239             {
2240                 wxPoint rotatedPoint( innerRadius, 0 );
2241                 RotatePoint( &rotatedPoint, angle * 10. );
2242                 zone->AppendCorner( center + rotatedPoint, 0 );
2243             }
2244         }
2245 
2246         zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
2247                                      ZONE::GetDefaultHatchPitch(), true );
2248     }
2249     else
2250     {
2251         PCB_LAYER_ID layer = kicad_layer( e.layer );
2252 
2253         if( layer == UNDEFINED_LAYER )
2254         {
2255             wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) "
2256                                                "was not mapped" ),
2257                                             eagle_layer_name( e.layer ), e.layer ) );
2258             return;
2259         }
2260 
2261         FP_SHAPE* gr = new FP_SHAPE( aFootprint, SHAPE_T::CIRCLE );
2262 
2263         // width == 0 means filled circle
2264         if( width <= 0 )
2265         {
2266             width  = radius;
2267             radius = radius / 2;
2268             gr->SetFilled( true );
2269         }
2270 
2271         aFootprint->Add( gr );
2272         gr->SetWidth( width );
2273 
2274         switch( (int) layer )
2275         {
2276         case UNDEFINED_LAYER:
2277             layer = Cmts_User;
2278             break;
2279         default:
2280             break;
2281         }
2282 
2283         gr->SetLayer( layer );
2284         gr->SetStart0( wxPoint( kicad_x( e.x ), kicad_y( e.y ) ) );
2285         gr->SetEnd0( wxPoint( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
2286         gr->SetDrawCoord();
2287     }
2288 }
2289 
2290 
packageHole(FOOTPRINT * aFootprint,wxXmlNode * aTree,bool aCenter) const2291 void EAGLE_PLUGIN::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
2292 {
2293     EHOLE   e( aTree );
2294 
2295     if( e.drill.value == 0 )
2296         return;
2297 
2298     // we add a PAD_ATTRIB::NPTH pad to this footprint.
2299     PAD* pad = new PAD( aFootprint );
2300     aFootprint->Add( pad );
2301 
2302     pad->SetShape( PAD_SHAPE::CIRCLE );
2303     pad->SetAttribute( PAD_ATTRIB::NPTH );
2304 
2305     // Mechanical purpose only:
2306     // no offset, no net name, no pad name allowed
2307     // pad->SetOffset( wxPoint( 0, 0 ) );
2308     // pad->SetNumber( wxEmptyString );
2309 
2310     wxPoint padpos( kicad_x( e.x ), kicad_y( e.y ) );
2311 
2312     if( aCenter )
2313     {
2314         pad->SetPos0( wxPoint( 0, 0 ) );
2315         aFootprint->SetPosition( padpos );
2316         pad->SetPosition( padpos );
2317     }
2318     else
2319     {
2320         pad->SetPos0( padpos );
2321         pad->SetPosition( padpos + aFootprint->GetPosition() );
2322     }
2323 
2324     wxSize  sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
2325 
2326     pad->SetDrillSize( sz );
2327     pad->SetSize( sz );
2328 
2329     pad->SetLayerSet( LSET::AllCuMask().set( B_Mask ).set( F_Mask ) );
2330 }
2331 
2332 
packageSMD(FOOTPRINT * aFootprint,wxXmlNode * aTree) const2333 void EAGLE_PLUGIN::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2334 {
2335     ESMD e( aTree );
2336     PCB_LAYER_ID layer = kicad_layer( e.layer );
2337 
2338     if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
2339         return;
2340 
2341     PAD* pad = new PAD( aFootprint );
2342     aFootprint->Add( pad );
2343     transferPad( e, pad );
2344 
2345     pad->SetShape( PAD_SHAPE::RECT );
2346     pad->SetAttribute( PAD_ATTRIB::SMD );
2347 
2348     wxSize padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
2349     pad->SetSize( padSize );
2350     pad->SetLayer( layer );
2351 
2352     const LSET front( 3, F_Cu, F_Paste, F_Mask );
2353     const LSET back(  3, B_Cu, B_Paste, B_Mask );
2354 
2355     if( layer == F_Cu )
2356         pad->SetLayerSet( front );
2357     else if( layer == B_Cu )
2358         pad->SetLayerSet( back );
2359 
2360     int minPadSize = std::min( padSize.x, padSize.y );
2361 
2362     // Rounded rectangle pads
2363     int roundRadius =
2364             eagleClamp( m_rules->srMinRoundness * 2, (int) ( minPadSize * m_rules->srRoundness ),
2365                         m_rules->srMaxRoundness * 2 );
2366 
2367     if( e.roundness || roundRadius > 0 )
2368     {
2369         double roundRatio = (double) roundRadius / minPadSize / 2.0;
2370 
2371         // Eagle uses a different definition of roundness, hence division by 200
2372         if( e.roundness )
2373             roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
2374 
2375         pad->SetShape( PAD_SHAPE::ROUNDRECT );
2376         pad->SetRoundRectRadiusRatio( roundRatio );
2377     }
2378 
2379     if( e.rot )
2380         pad->SetOrientation( e.rot->degrees * 10 );
2381 
2382     pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
2383                                                  (int) ( m_rules->mvCreamFrame * minPadSize ),
2384                                                  m_rules->mlMaxCreamFrame ) );
2385 
2386     // Solder mask
2387     if( e.stop && *e.stop == false )         // enabled by default
2388     {
2389         if( layer == F_Cu )
2390             pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
2391         else if( layer == B_Cu )
2392             pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
2393     }
2394 
2395     // Solder paste (only for SMD pads)
2396     if( e.cream && *e.cream == false )         // enabled by default
2397     {
2398         if( layer == F_Cu )
2399             pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
2400         else if( layer == B_Cu )
2401             pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
2402     }
2403 }
2404 
2405 
transferPad(const EPAD_COMMON & aEaglePad,PAD * aPad) const2406 void EAGLE_PLUGIN::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
2407 {
2408     aPad->SetNumber( FROM_UTF8( aEaglePad.name.c_str() ) );
2409 
2410     // pad's "Position" is not relative to the footprint's,
2411     // whereas Pos0 is relative to the footprint's but is the unrotated coordinate.
2412     wxPoint padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
2413     aPad->SetPos0( padPos );
2414 
2415     // Solder mask
2416     const wxSize& padSize( aPad->GetSize() );
2417 
2418     aPad->SetLocalSolderMaskMargin(
2419             eagleClamp( m_rules->mlMinStopFrame,
2420                         (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
2421                         m_rules->mlMaxStopFrame ) );
2422 
2423     // Solid connection to copper zones
2424     if( aEaglePad.thermals && !*aEaglePad.thermals )
2425         aPad->SetZoneConnection( ZONE_CONNECTION::FULL );
2426 
2427     FOOTPRINT* footprint = aPad->GetParent();
2428     wxCHECK( footprint, /* void */ );
2429     RotatePoint( &padPos, footprint->GetOrientation() );
2430     aPad->SetPosition( padPos + footprint->GetPosition() );
2431 }
2432 
2433 
deleteTemplates()2434 void EAGLE_PLUGIN::deleteTemplates()
2435 {
2436     for( auto& t : m_templates )
2437         delete t.second;
2438 
2439     m_templates.clear();
2440 }
2441 
2442 
loadClasses(wxXmlNode * aClasses)2443 void EAGLE_PLUGIN::loadClasses( wxXmlNode* aClasses )
2444 {
2445     BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2446 
2447     m_xpath->push( "classes.class", "number" );
2448 
2449     std::vector<ECLASS> eClasses;
2450     wxXmlNode*          classNode = aClasses->GetChildren();
2451 
2452     while( classNode )
2453     {
2454         checkpoint();
2455 
2456         ECLASS      eClass( classNode );
2457         NETCLASSPTR netclass;
2458 
2459         if( eClass.name.CmpNoCase( "default" ) == 0 )
2460         {
2461             netclass = bds.GetNetClasses().GetDefault();
2462         }
2463         else
2464         {
2465             netclass.reset( new NETCLASS( eClass.name ) );
2466             m_board->GetDesignSettings().GetNetClasses().Add( netclass );
2467         }
2468 
2469         netclass->SetTrackWidth( INT_MAX );
2470         netclass->SetViaDiameter( INT_MAX );
2471         netclass->SetViaDrill( INT_MAX );
2472 
2473         eClasses.emplace_back( eClass );
2474         m_classMap[ eClass.number ] = netclass;
2475 
2476         // Get next class
2477         classNode = classNode->GetNext();
2478     }
2479 
2480     m_customRules = "(version 1)";
2481 
2482     for( ECLASS& eClass : eClasses )
2483     {
2484         for( std::pair<const wxString&, ECOORD> entry : eClass.clearanceMap )
2485         {
2486             wxString rule;
2487             rule.Printf( "(rule \"class %s:%s\"\n"
2488                          "  (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
2489                          "  (constraint clearance (min %smm)))\n",
2490                          eClass.number,
2491                          entry.first,
2492                          eClass.name,
2493                          m_classMap[ entry.first ]->GetName(),
2494                          StringFromValue( EDA_UNITS::MILLIMETRES, entry.second.ToPcbUnits() ) );
2495 
2496             m_customRules += "\n" + rule;
2497         }
2498     }
2499 
2500     m_xpath->pop(); // "classes.class"
2501 }
2502 
2503 
loadSignals(wxXmlNode * aSignals)2504 void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
2505 {
2506     ZONES zones;      // per net
2507     int   netCode = 1;
2508 
2509     m_xpath->push( "signals.signal", "name" );
2510 
2511     // Get the first signal and iterate
2512     wxXmlNode* net = aSignals->GetChildren();
2513 
2514     while( net )
2515     {
2516         checkpoint();
2517 
2518         bool    sawPad = false;
2519 
2520         zones.clear();
2521 
2522         const wxString& netName = escapeName( net->GetAttribute( "name" ) );
2523         NETINFO_ITEM*   netInfo = new NETINFO_ITEM( m_board, netName, netCode );
2524         NETCLASSPTR     netclass;
2525 
2526         if( net->HasAttribute( "class" ) )
2527         {
2528             netclass = m_classMap[ net->GetAttribute( "class" ) ];
2529 
2530             netclass->Add( netName );
2531             netInfo->SetNetClass( netclass );
2532         }
2533 
2534         m_board->Add( netInfo );
2535 
2536         m_xpath->Value( netName.c_str() );
2537 
2538         // Get the first net item and iterate
2539         wxXmlNode* netItem = net->GetChildren();
2540 
2541         // (contactref | polygon | wire | via)*
2542         while( netItem )
2543         {
2544             const wxString& itemName = netItem->GetName();
2545 
2546             if( itemName == "wire" )
2547             {
2548                 m_xpath->push( "wire" );
2549 
2550                 EWIRE        w( netItem );
2551                 PCB_LAYER_ID layer = kicad_layer( w.layer );
2552 
2553                 if( IsCopperLayer( layer ) )
2554                 {
2555                     wxPoint start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
2556                     double angle = 0.0;
2557                     double end_angle = 0.0;
2558                     double radius = 0.0;
2559                     double delta_angle = 0.0;
2560                     wxPoint center;
2561 
2562                     int width = w.width.ToPcbUnits();
2563 
2564                     if( width < m_min_trace )
2565                         m_min_trace = width;
2566 
2567                     if( netclass && width < netclass->GetTrackWidth() )
2568                         netclass->SetTrackWidth( width );
2569 
2570                     if( w.curve )
2571                     {
2572                         center = ConvertArcCenter( wxPoint( kicad_x( w.x1 ), kicad_y( w.y1 ) ),
2573                                                    wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ),
2574                                                    *w.curve );
2575 
2576                         angle = DEG2RAD( *w.curve );
2577 
2578                         end_angle = atan2( kicad_y( w.y2 ) - center.y,
2579                                            kicad_x( w.x2 ) - center.x );
2580 
2581                         radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
2582                                        pow( center.y - kicad_y( w.y1 ), 2 ) );
2583 
2584                         int segs = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
2585                                                          *w.curve );
2586                         delta_angle = angle / segs;
2587                     }
2588 
2589                     while( fabs( angle ) > fabs( delta_angle ) )
2590                     {
2591                         wxASSERT( radius > 0.0 );
2592                         wxPoint end( KiROUND( radius * cos( end_angle + angle ) + center.x ),
2593                                      KiROUND( radius * sin( end_angle + angle ) + center.y ) );
2594 
2595                         PCB_TRACK*  t = new PCB_TRACK( m_board );
2596 
2597                         t->SetPosition( start );
2598                         t->SetEnd( end );
2599                         t->SetWidth( width );
2600                         t->SetLayer( layer );
2601                         t->SetNetCode( netCode );
2602 
2603                         m_board->Add( t );
2604 
2605                         start = end;
2606                         angle -= delta_angle;
2607                     }
2608 
2609                     PCB_TRACK*  t = new PCB_TRACK( m_board );
2610 
2611                     t->SetPosition( start );
2612                     t->SetEnd( wxPoint( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
2613                     t->SetWidth( width );
2614                     t->SetLayer( layer );
2615                     t->SetNetCode( netCode );
2616 
2617                     m_board->Add( t );
2618                 }
2619                 else
2620                 {
2621                     // put non copper wires where the sun don't shine.
2622                 }
2623 
2624                 m_xpath->pop();
2625             }
2626             else if( itemName == "via" )
2627             {
2628                 m_xpath->push( "via" );
2629                 EVIA    v( netItem );
2630 
2631                 if( v.layer_front_most > v.layer_back_most )
2632                     std::swap( v.layer_front_most, v.layer_back_most );
2633 
2634                 PCB_LAYER_ID  layer_front_most = kicad_layer( v.layer_front_most );
2635                 PCB_LAYER_ID  layer_back_most  = kicad_layer( v.layer_back_most );
2636 
2637                 if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most )
2638                         && layer_front_most != layer_back_most )
2639                 {
2640                     int      kidiam;
2641                     int      drillz = v.drill.ToPcbUnits();
2642                     PCB_VIA* via = new PCB_VIA( m_board );
2643                     m_board->Add( via );
2644 
2645                     if( v.diam )
2646                     {
2647                         kidiam = v.diam->ToPcbUnits();
2648                         via->SetWidth( kidiam );
2649                     }
2650                     else
2651                     {
2652                         double annulus = drillz * m_rules->rvViaOuter;  // eagle "restring"
2653                         annulus = eagleClamp( m_rules->rlMinViaOuter, annulus,
2654                                               m_rules->rlMaxViaOuter );
2655                         kidiam = KiROUND( drillz + 2 * annulus );
2656                         via->SetWidth( kidiam );
2657                     }
2658 
2659                     via->SetDrill( drillz );
2660 
2661                     // make sure the via diameter respects the restring rules
2662 
2663                     if( !v.diam || via->GetWidth() <= via->GetDrill() )
2664                     {
2665                         double annulus =
2666                                 eagleClamp( m_rules->rlMinViaOuter,
2667                                             (double) ( via->GetWidth() / 2 - via->GetDrill() ),
2668                                             m_rules->rlMaxViaOuter );
2669                         via->SetWidth( drillz + 2 * annulus );
2670                     }
2671 
2672                     if( kidiam < m_min_via )
2673                         m_min_via = kidiam;
2674 
2675                     if( netclass && kidiam < netclass->GetViaDiameter() )
2676                         netclass->SetViaDiameter( kidiam );
2677 
2678                     if( drillz < m_min_hole )
2679                         m_min_hole = drillz;
2680 
2681                     if( netclass && drillz < netclass->GetViaDrill() )
2682                         netclass->SetViaDrill( drillz );
2683 
2684                     if( ( kidiam - drillz ) / 2 < m_min_annulus )
2685                         m_min_annulus = ( kidiam - drillz ) / 2;
2686 
2687                     if( layer_front_most == F_Cu && layer_back_most == B_Cu )
2688                     {
2689                         via->SetViaType( VIATYPE::THROUGH );
2690                     }
2691                     /// This is, at best, a guess.  Eagle doesn't seem to differentiate
2692                     /// between blind/buried vias that only go one layer and micro vias
2693                     /// so the user will need to clean up a bit
2694                     else if( v.layer_back_most - v.layer_front_most == 1 )
2695                     {
2696                         via->SetViaType( VIATYPE::MICROVIA );
2697                     }
2698                     else
2699                     {
2700                         via->SetViaType( VIATYPE::BLIND_BURIED );
2701                     }
2702 
2703                     wxPoint pos( kicad_x( v.x ), kicad_y( v.y ) );
2704 
2705                     via->SetLayerPair( layer_front_most, layer_back_most );
2706                     via->SetPosition( pos  );
2707                     via->SetEnd( pos );
2708 
2709                     via->SetNetCode( netCode );
2710                 }
2711 
2712                 m_xpath->pop();
2713             }
2714 
2715             else if( itemName == "contactref" )
2716             {
2717                 m_xpath->push( "contactref" );
2718                 // <contactref element="RN1" pad="7"/>
2719 
2720                 const wxString& reference = netItem->GetAttribute( "element" );
2721                 const wxString& pad       = netItem->GetAttribute( "pad" );
2722                 wxString key = makeKey( reference, pad ) ;
2723 
2724                 m_pads_to_nets[ key ] = ENET( netCode, netName );
2725 
2726                 m_xpath->pop();
2727 
2728                 sawPad = true;
2729             }
2730 
2731             else if( itemName == "polygon" )
2732             {
2733                 m_xpath->push( "polygon" );
2734                 auto* zone = loadPolygon( netItem );
2735 
2736                 if( zone )
2737                 {
2738                     zones.push_back( zone );
2739 
2740                     if( !zone->GetIsRuleArea() )
2741                         zone->SetNetCode( netCode );
2742                 }
2743 
2744                 m_xpath->pop();     // "polygon"
2745             }
2746 
2747             netItem = netItem->GetNext();
2748         }
2749 
2750         if( zones.size() && !sawPad )
2751         {
2752             // KiCad does not support an unconnected zone with its own non-zero netcode,
2753             // but only when assigned netcode = 0 w/o a name...
2754             for( ZONE* zone : zones )
2755                 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
2756 
2757             // therefore omit this signal/net.
2758         }
2759         else
2760         {
2761             netCode++;
2762         }
2763 
2764         // Get next signal
2765         net = net->GetNext();
2766     }
2767 
2768     m_xpath->pop();     // "signals.signal"
2769 }
2770 
2771 
DefaultLayerMappingCallback(const std::vector<INPUT_LAYER_DESC> & aInputLayerDescriptionVector)2772 std::map<wxString, PCB_LAYER_ID> EAGLE_PLUGIN::DefaultLayerMappingCallback(
2773             const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2774 {
2775     std::map<wxString, PCB_LAYER_ID> layer_map;
2776 
2777     for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
2778     {
2779         PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
2780         layer_map.emplace( layer.Name, layerId );
2781     }
2782 
2783     return layer_map;
2784 }
2785 
2786 
mapEagleLayersToKicad()2787 void EAGLE_PLUGIN::mapEagleLayersToKicad()
2788 {
2789     std::vector<INPUT_LAYER_DESC> inputDescs;
2790 
2791     for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
2792     {
2793         const ELAYER& eLayer = layerPair.second;
2794 
2795         INPUT_LAYER_DESC layerDesc;
2796         std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
2797                 defaultKicadLayer( eLayer.number );
2798 
2799         if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
2800             continue; // Ignore unused copper layers
2801 
2802         layerDesc.Name = eLayer.name;
2803 
2804         inputDescs.push_back( layerDesc );
2805     }
2806 
2807     if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
2808         dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
2809 
2810     m_layer_map = m_layer_mapping_handler( inputDescs );
2811 
2812     if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
2813         dynamic_cast<wxWindow*>( m_progressReporter )->Show();
2814 }
2815 
2816 
kicad_layer(int aEagleLayer) const2817 PCB_LAYER_ID EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) const
2818 {
2819     auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
2820     return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
2821 }
2822 
2823 
defaultKicadLayer(int aEagleLayer) const2824 std::tuple<PCB_LAYER_ID, LSET, bool> EAGLE_PLUGIN::defaultKicadLayer( int aEagleLayer ) const
2825 {
2826     // eagle copper layer:
2827     if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
2828     {
2829         LSET copperLayers;
2830 
2831         for( int copperLayer : m_cu_map )
2832         {
2833             if( copperLayer >= 0 )
2834                 copperLayers[copperLayer] = true;
2835         }
2836 
2837         return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
2838     }
2839 
2840     int  kiLayer  = UNSELECTED_LAYER;
2841     bool required = false;
2842     LSET permittedLayers;
2843 
2844     permittedLayers.set();
2845 
2846     // translate non-copper eagle layer to pcbnew layer
2847     switch( aEagleLayer )
2848     {
2849     // Eagle says "Dimension" layer, but it's for board perimeter
2850     case EAGLE_LAYER::DIMENSION:
2851         kiLayer         = Edge_Cuts;
2852         required        = true;
2853         permittedLayers = LSET( 1, Edge_Cuts );
2854         break;
2855 
2856     case EAGLE_LAYER::TPLACE:
2857         kiLayer = F_SilkS;
2858         break;
2859     case EAGLE_LAYER::BPLACE:
2860         kiLayer = B_SilkS;
2861         break;
2862     case EAGLE_LAYER::TNAMES:
2863         kiLayer = F_SilkS;
2864         break;
2865     case EAGLE_LAYER::BNAMES:
2866         kiLayer = B_SilkS;
2867         break;
2868     case EAGLE_LAYER::TVALUES:
2869         kiLayer = F_Fab;
2870         break;
2871     case EAGLE_LAYER::BVALUES:
2872         kiLayer = B_Fab;
2873         break;
2874     case EAGLE_LAYER::TSTOP:
2875         kiLayer = F_Mask;
2876         break;
2877     case EAGLE_LAYER::BSTOP:
2878         kiLayer = B_Mask;
2879         break;
2880     case EAGLE_LAYER::TCREAM:
2881         kiLayer = F_Paste;
2882         break;
2883     case EAGLE_LAYER::BCREAM:
2884         kiLayer = B_Paste;
2885         break;
2886     case EAGLE_LAYER::TFINISH:
2887         kiLayer = F_Mask;
2888         break;
2889     case EAGLE_LAYER::BFINISH:
2890         kiLayer = B_Mask;
2891         break;
2892     case EAGLE_LAYER::TGLUE:
2893         kiLayer = F_Adhes;
2894         break;
2895     case EAGLE_LAYER::BGLUE:
2896         kiLayer = B_Adhes;
2897         break;
2898     case EAGLE_LAYER::DOCUMENT:
2899         kiLayer = Cmts_User;
2900         break;
2901     case EAGLE_LAYER::REFERENCELC:
2902         kiLayer = Cmts_User;
2903         break;
2904     case EAGLE_LAYER::REFERENCELS:
2905         kiLayer = Cmts_User;
2906         break;
2907 
2908     // Packages show the future chip pins on SMD parts using layer 51.
2909     // This is an area slightly smaller than the PAD/SMD copper area.
2910     // Carry those visual aids into the FOOTPRINT on the fabrication layer,
2911     // not silkscreen. This is perhaps not perfect, but there is not a lot
2912     // of other suitable paired layers
2913     case EAGLE_LAYER::TDOCU:
2914         kiLayer = F_Fab;
2915         break;
2916     case EAGLE_LAYER::BDOCU:
2917         kiLayer = B_Fab;
2918         break;
2919 
2920     // these layers are defined as user layers. put them on ECO layers
2921     case EAGLE_LAYER::USERLAYER1:
2922         kiLayer = Eco1_User;
2923         break;
2924     case EAGLE_LAYER::USERLAYER2:
2925         kiLayer = Eco2_User;
2926         break;
2927 
2928     // these will also appear in the ratsnest, so there's no need for a warning
2929     case EAGLE_LAYER::UNROUTED:
2930         kiLayer = Dwgs_User;
2931         break;
2932 
2933     case EAGLE_LAYER::TKEEPOUT:
2934         kiLayer = F_CrtYd;
2935         break;
2936     case EAGLE_LAYER::BKEEPOUT:
2937         kiLayer = B_CrtYd;
2938         break;
2939 
2940     case EAGLE_LAYER::MILLING:
2941     case EAGLE_LAYER::TTEST:
2942     case EAGLE_LAYER::BTEST:
2943     case EAGLE_LAYER::HOLES:
2944     default:
2945         kiLayer = UNSELECTED_LAYER;
2946         break;
2947     }
2948 
2949     return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
2950 }
2951 
2952 
eagle_layer_name(int aLayer) const2953 const wxString& EAGLE_PLUGIN::eagle_layer_name( int aLayer ) const
2954 {
2955     static const wxString unknown( "unknown" );
2956     auto it = m_eagleLayers.find( aLayer );
2957     return it == m_eagleLayers.end() ? unknown : it->second.name;
2958 }
2959 
2960 
eagle_layer_id(const wxString & aLayerName) const2961 int EAGLE_PLUGIN::eagle_layer_id( const wxString& aLayerName ) const
2962 {
2963     static const int unknown = -1;
2964     auto it = m_eagleLayersIds.find( aLayerName );
2965     return it == m_eagleLayersIds.end() ? unknown : it->second;
2966 }
2967 
2968 
centerBoard()2969 void EAGLE_PLUGIN::centerBoard()
2970 {
2971     if( m_props )
2972     {
2973         UTF8 page_width;
2974         UTF8 page_height;
2975 
2976         if( m_props->Value( "page_width",  &page_width ) &&
2977             m_props->Value( "page_height", &page_height ) )
2978         {
2979             EDA_RECT bbbox = m_board->GetBoardEdgesBoundingBox();
2980 
2981             int w = atoi( page_width.c_str() );
2982             int h = atoi( page_height.c_str() );
2983 
2984             int desired_x = ( w - bbbox.GetWidth() )  / 2;
2985             int desired_y = ( h - bbbox.GetHeight() ) / 2;
2986 
2987             m_board->Move( wxPoint( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
2988         }
2989     }
2990 }
2991 
2992 
getModificationTime(const wxString & aPath)2993 wxDateTime EAGLE_PLUGIN::getModificationTime( const wxString& aPath )
2994 {
2995     // File hasn't been loaded yet.
2996     if( aPath.IsEmpty() )
2997         return wxDateTime::Now();
2998 
2999     wxFileName  fn( aPath );
3000 
3001     if( fn.IsFileReadable() )
3002         return fn.GetModificationTime();
3003     else
3004         return wxDateTime( 0.0 );
3005 }
3006 
3007 
cacheLib(const wxString & aLibPath)3008 void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath )
3009 {
3010     try
3011     {
3012         wxDateTime  modtime = getModificationTime( aLibPath );
3013 
3014         // Fixes assertions in wxWidgets debug builds for the wxDateTime object.  Refresh the
3015         // cache if either of the wxDateTime objects are invalid or the last file modification
3016         // time differs from the current file modification time.
3017         bool load = !m_mod_time.IsValid() || !modtime.IsValid() || m_mod_time != modtime;
3018 
3019         if( aLibPath != m_lib_path || load )
3020         {
3021             wxXmlNode*  doc;
3022             LOCALE_IO   toggle;     // toggles on, then off, the C locale.
3023 
3024             deleteTemplates();
3025 
3026             // Set this before completion of loading, since we rely on it for
3027             // text of an exception.  Delay setting m_mod_time until after successful load
3028             // however.
3029             m_lib_path = aLibPath;
3030 
3031             // 8 bit "filename" should be encoded according to disk filename encoding,
3032             // (maybe this is current locale, maybe not, its a filesystem issue),
3033             // and is not necessarily utf8.
3034             string filename = (const char*) aLibPath.char_str( wxConvFile );
3035 
3036             // Load the document
3037             wxFileName fn( filename );
3038             wxFFileInputStream stream( fn.GetFullPath() );
3039             wxXmlDocument xmlDocument;
3040 
3041             if( !stream.IsOk() || !xmlDocument.Load( stream ) )
3042             {
3043                 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ),
3044                                                   fn.GetFullPath() ) );
3045             }
3046 
3047             doc = xmlDocument.GetRoot();
3048 
3049             wxXmlNode* drawing       = MapChildren( doc )["drawing"];
3050             NODE_MAP drawingChildren = MapChildren( drawing );
3051 
3052             // clear the cu map and then rebuild it.
3053             clear_cu_map();
3054 
3055             m_xpath->push( "eagle.drawing.layers" );
3056             wxXmlNode* layers  = drawingChildren["layers"];
3057             loadLayerDefs( layers );
3058             mapEagleLayersToKicad();
3059             m_xpath->pop();
3060 
3061             m_xpath->push( "eagle.drawing.library" );
3062             wxXmlNode* library = drawingChildren["library"];
3063             loadLibrary( library, nullptr );
3064             m_xpath->pop();
3065 
3066             m_mod_time = modtime;
3067         }
3068     }
3069     catch(...){}
3070     // TODO: Handle exceptions
3071     // catch( file_parser_error fpe )
3072     // {
3073     //     // for xml_parser_error, what() has the line number in it,
3074     //     // but no byte offset.  That should be an adequate error message.
3075     //     THROW_IO_ERROR( fpe.what() );
3076     // }
3077     //
3078     // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
3079     // // so one catch should be OK for all errors.
3080     // catch( ptree_error pte )
3081     // {
3082     //     string errmsg = pte.what();
3083     //
3084     //     errmsg += " @\n";
3085     //     errmsg += m_xpath->Contents();
3086     //
3087     //     THROW_IO_ERROR( errmsg );
3088     // }
3089 }
3090 
3091 
FootprintEnumerate(wxArrayString & aFootprintNames,const wxString & aLibraryPath,bool aBestEfforts,const PROPERTIES * aProperties)3092 void EAGLE_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
3093                                        bool aBestEfforts, const PROPERTIES* aProperties )
3094 {
3095     wxString errorMsg;
3096 
3097     init( aProperties );
3098 
3099     try
3100     {
3101         cacheLib( aLibraryPath );
3102     }
3103     catch( const IO_ERROR& ioe )
3104     {
3105         errorMsg = ioe.What();
3106     }
3107 
3108     // Some of the files may have been parsed correctly so we want to add the valid files to
3109     // the library.
3110 
3111     for( FOOTPRINT_MAP::const_iterator it = m_templates.begin(); it != m_templates.end(); ++it )
3112         aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
3113 
3114     if( !errorMsg.IsEmpty() && !aBestEfforts )
3115         THROW_IO_ERROR( errorMsg );
3116 }
3117 
3118 
FootprintLoad(const wxString & aLibraryPath,const wxString & aFootprintName,bool aKeepUUID,const PROPERTIES * aProperties)3119 FOOTPRINT* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
3120                                         const wxString& aFootprintName, bool aKeepUUID,
3121                                         const PROPERTIES* aProperties )
3122 {
3123     init( aProperties );
3124     cacheLib( aLibraryPath );
3125     FOOTPRINT_MAP::const_iterator it = m_templates.find( aFootprintName );
3126 
3127     if( it == m_templates.end() )
3128         return nullptr;
3129 
3130     // Return a copy of the template
3131     FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate();
3132     copy->SetParent( nullptr );
3133     return copy;
3134 }
3135 
3136 
FootprintLibOptions(PROPERTIES * aListToAppendTo) const3137 void EAGLE_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const
3138 {
3139     PLUGIN::FootprintLibOptions( aListToAppendTo );
3140 }
3141