KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_eagle.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
5 * Copyright The 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
28Pcbnew PLUGIN for Eagle 6.x XML *.brd and footprint format.
29
30XML parsing and converting:
31Getting line numbers and byte offsets from the source XML file is not
32possible using currently available XML libraries within KiCad project:
33wxXmlDocument and boost::property_tree.
34
35property_tree will give line numbers but no byte offsets, and only during
36document loading. This means that if we have a problem after the document is
37successfully loaded, there is no way to correlate back to line number and byte
38offset of the problem. So a different approach is taken, one which relies on the
39XML elements themselves using an XPATH type of reporting mechanism. The path to
40the problem is reported in the error messages. This means keeping track of that
41path as we traverse the XML document for the sole purpose of accurate error
42reporting.
43
44User can load the source XML file into firefox or other xml browser and follow
45our error message.
46
47Load() 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#include <wx/txtstrm.h>
61#include <wx/window.h>
62
64#include <font/fontconfig.h>
66#include <string_utils.h>
67#include <trigo.h>
68#include <progress_reporter.h>
69#include <project.h>
70#include <board.h>
72#include <footprint.h>
73#include <pad.h>
74#include <pcb_track.h>
75#include <pcb_shape.h>
76#include <zone.h>
77#include <padstack.h>
78#include <pcb_text.h>
79#include <pcb_dimension.h>
80#include <reporter.h>
81
82#include <pcb_io/pcb_io.h>
84
85using namespace std;
86
87
90static int parseEagle( const wxString& aDistance )
91{
92 ECOORD::EAGLE_UNIT unit = ( aDistance.npos != aDistance.find( "mil" ) )
95
96 ECOORD coord( aDistance, unit );
97
98 return coord.ToPcbUnits();
99}
100
101
102// In Eagle one can specify DRC rules where min value > max value,
103// in such case the max value has the priority
104template<typename T>
105static T eagleClamp( T aMin, T aValue, T aMax )
106{
107 T ret = std::max( aMin, aValue );
108 return std::min( aMax, ret );
109}
110
111
114static wxString makeKey( const wxString& aFirst, const wxString& aSecond )
115{
116 wxString key = aFirst + '\x02' + aSecond;
117 return key;
118}
119
120
121void PCB_IO_EAGLE::setKeepoutSettingsToZone( ZONE* aZone, int aLayer ) const
122{
123 if( aLayer == EAGLE_LAYER::TRESTRICT || aLayer == EAGLE_LAYER::BRESTRICT )
124 {
125 aZone->SetIsRuleArea( true );
126 aZone->SetDoNotAllowVias( true );
127 aZone->SetDoNotAllowTracks( true );
128 aZone->SetDoNotAllowZoneFills( true );
129 aZone->SetDoNotAllowPads( true );
130 aZone->SetDoNotAllowFootprints( false );
131
132 if( aLayer == EAGLE_LAYER::TRESTRICT ) // front layer keepout
133 aZone->SetLayer( F_Cu );
134 else // bottom layer keepout
135 aZone->SetLayer( B_Cu );
136 }
137 else if( aLayer == EAGLE_LAYER::VRESTRICT )
138 {
139 aZone->SetIsRuleArea( true );
140 aZone->SetDoNotAllowVias( true );
141 aZone->SetDoNotAllowTracks( false );
142 aZone->SetDoNotAllowZoneFills( false );
143 aZone->SetDoNotAllowPads( false );
144 aZone->SetDoNotAllowFootprints( false );
145
146 aZone->SetLayerSet( LSET::AllCuMask() );
147 }
148 else // copper pour cutout
149 {
150 aZone->SetIsRuleArea( true );
151 aZone->SetDoNotAllowVias( false );
152 aZone->SetDoNotAllowTracks( false );
153 aZone->SetDoNotAllowZoneFills( true );
154 aZone->SetDoNotAllowPads( false );
155 aZone->SetDoNotAllowFootprints( false );
156
157 aZone->SetLayerSet( { kicad_layer( aLayer ) } );
158 }
159}
160
161
162void ERULES::parse( wxXmlNode* aRules, std::function<void()> aCheckpoint )
163{
164 wxXmlNode* child = aRules->GetChildren();
165
166 while( child )
167 {
168 aCheckpoint();
169
170 if( child->GetName() == wxT( "param" ) )
171 {
172 const wxString& name = child->GetAttribute( wxT( "name" ) );
173 const wxString& value = child->GetAttribute( wxT( "value" ) );
174
175 if( name == wxT( "psElongationLong" ) )
176 psElongationLong = wxAtoi( value );
177 else if( name == wxT( "psElongationOffset" ) )
178 psElongationOffset = wxAtoi( value );
179 else if( name == wxT( "mvStopFrame" ) )
180 value.ToCDouble( &mvStopFrame );
181 else if( name == wxT( "mvCreamFrame" ) )
182 value.ToCDouble( &mvCreamFrame );
183 else if( name == wxT( "mlMinStopFrame" ) )
184 mlMinStopFrame = parseEagle( value );
185 else if( name == wxT( "mlMaxStopFrame" ) )
186 mlMaxStopFrame = parseEagle( value );
187 else if( name == wxT( "mlMinCreamFrame" ) )
188 mlMinCreamFrame = parseEagle( value );
189 else if( name == wxT( "mlMaxCreamFrame" ) )
190 mlMaxCreamFrame = parseEagle( value );
191 else if( name == wxT( "srRoundness" ) )
192 value.ToCDouble( &srRoundness );
193 else if( name == wxT( "srMinRoundness" ) )
194 srMinRoundness = parseEagle( value );
195 else if( name == wxT( "srMaxRoundness" ) )
196 srMaxRoundness = parseEagle( value );
197 else if( name == wxT( "psTop" ) )
198 psTop = wxAtoi( value );
199 else if( name == wxT( "psBottom" ) )
200 psBottom = wxAtoi( value );
201 else if( name == wxT( "psFirst" ) )
202 psFirst = wxAtoi( value );
203 else if( name == wxT( "rvPadTop" ) )
204 value.ToCDouble( &rvPadTop );
205 else if( name == wxT( "rlMinPadTop" ) )
206 rlMinPadTop = parseEagle( value );
207 else if( name == wxT( "rlMaxPadTop" ) )
208 rlMaxPadTop = parseEagle( value );
209 else if( name == wxT( "rvViaOuter" ) )
210 value.ToCDouble( &rvViaOuter );
211 else if( name == wxT( "rlMinViaOuter" ) )
212 rlMinViaOuter = parseEagle( value );
213 else if( name == wxT( "rlMaxViaOuter" ) )
214 rlMaxViaOuter = parseEagle( value );
215 else if( name == wxT( "mdWireWire" ) )
216 mdWireWire = parseEagle( value );
217 }
218
219 child = child->GetNext();
220 }
221}
222
223
225 PCB_IO( wxS( "Eagle" ) ),
226 m_rules( new ERULES() ),
227 m_xpath( new XPATH() ),
228 m_progressReporter( nullptr ),
229 m_doneCount( 0 ),
231 m_totalCount( 0 ),
232 m_timestamp( wxDateTime::Now().GetValue().GetValue() )
233{
234 using namespace std::placeholders;
235
236 init( nullptr );
237 clear_cu_map();
239}
240
241
243{
245 delete m_rules;
246 delete m_xpath;
247}
248
249
250bool PCB_IO_EAGLE::CanReadBoard( const wxString& aFileName ) const
251{
252 if( !PCB_IO::CanReadBoard( aFileName ) )
253 return false;
254
255 return checkHeader( aFileName );
256}
257
258
259bool PCB_IO_EAGLE::CanReadLibrary( const wxString& aFileName ) const
260{
261 if( !PCB_IO::CanReadLibrary( aFileName ) )
262 return false;
263
264 return checkHeader( aFileName );
265}
266
267
268bool PCB_IO_EAGLE::CanReadFootprint( const wxString& aFileName ) const
269{
270 return CanReadLibrary( aFileName );
271}
272
273
274bool PCB_IO_EAGLE::checkHeader(const wxString& aFileName) const
275{
276 wxFileInputStream input( aFileName );
277
278 if( !input.IsOk() )
279 return false;
280
281 wxTextInputStream text( input );
282
283 for( int i = 0; i < 8; i++ )
284 {
285 if( input.Eof() )
286 return false;
287
288 if( text.ReadLine().Contains( wxS( "<eagle" ) ) )
289 return true;
290 }
291
292 return false;
293}
294
295
297{
298 const unsigned PROGRESS_DELTA = 50;
299
301 {
302 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
303 {
304 m_progressReporter->SetCurrentProgress( ( (double) m_doneCount ) / std::max( 1U, m_totalCount ) );
305
306 if( !m_progressReporter->KeepRefreshing() )
307 THROW_IO_ERROR( _( "File import canceled by user." ) );
308
310 }
311 }
312}
313
314
315VECTOR2I inline PCB_IO_EAGLE::kicad_fontsize( const ECOORD& d, int aTextThickness ) const
316{
317 // Eagle includes stroke thickness in the text size, KiCAD does not
318 int kz = d.ToPcbUnits();
319 return VECTOR2I( kz - aTextThickness, kz - aTextThickness );
320}
321
322
323BOARD* PCB_IO_EAGLE::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
324 const std::map<std::string, UTF8>* aProperties, PROJECT* aProject )
325{
326 wxXmlNode* doc;
327
328 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
330
331 init( aProperties );
332
333 m_board = aAppendToMe ? aAppendToMe : new BOARD();
334
335 // Give the filename to the board if it's new
336 if( !aAppendToMe )
337 m_board->SetFileName( aFileName );
338
339 // delete on exception, if I own m_board, according to aAppendToMe
340 unique_ptr<BOARD> deleter( aAppendToMe ? nullptr : m_board );
341
342 try
343 {
345 {
346 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
347
348 if( !m_progressReporter->KeepRefreshing() )
349 THROW_IO_ERROR( _( "File import canceled by user." ) );
350 }
351
352 wxFileName fn = aFileName;
353
354 // Load the document
355 wxFFileInputStream stream( fn.GetFullPath() );
356 wxXmlDocument xmlDocument;
357
358 if( !stream.IsOk() || !xmlDocument.Load( stream ) )
359 {
360 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ),
361 fn.GetFullPath() ) );
362 }
363
364 doc = xmlDocument.GetRoot();
365
366 m_min_trace = INT_MAX;
367 m_min_hole = INT_MAX;
368 m_min_via = INT_MAX;
369 m_min_annulus = INT_MAX;
370
371 loadAllSections( doc );
372
373 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
374
375 if( m_min_trace < bds.m_TrackMinWidth )
377
378 if( m_min_via < bds.m_ViasMinSize )
380
381 if( m_min_hole < bds.m_MinThroughDrill )
383
386
387 if( m_rules->mdWireWire )
388 bds.m_MinClearance = KiROUND( m_rules->mdWireWire );
389
390 NETCLASS defaults( wxT( "dummy" ) );
391
392 auto finishNetclass =
393 [&]( const std::shared_ptr<NETCLASS>& netclass )
394 {
395 // If Eagle has a clearance matrix then we'll build custom rules from that.
396 // For classes with a clearance-to-default, use that; otherwise use board minimum.
397 if( !netclass->HasClearance() )
398 {
399 netclass->SetClearance( KiROUND( bds.m_MinClearance ) );
400 }
401
402 if( netclass->GetTrackWidth() == INT_MAX )
403 netclass->SetTrackWidth( defaults.GetTrackWidth() );
404
405 if( netclass->GetViaDiameter() == INT_MAX )
406 netclass->SetViaDiameter( defaults.GetViaDiameter() );
407
408 if( netclass->GetViaDrill() == INT_MAX )
409 netclass->SetViaDrill( defaults.GetViaDrill() );
410 };
411
412 std::shared_ptr<NET_SETTINGS>& netSettings = bds.m_NetSettings;
413
414 finishNetclass( netSettings->GetDefaultNetclass() );
415
416 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
417 finishNetclass( netclass );
418
419 m_board->m_LegacyNetclassesLoaded = true;
420 m_board->m_LegacyDesignSettingsLoaded = true;
421
422 fn.SetExt( wxT( "kicad_dru" ) );
423 wxFile rulesFile( fn.GetFullPath(), wxFile::write );
424 rulesFile.Write( m_customRules );
425
426 // should be empty, else missing m_xpath->pop()
427 wxASSERT( m_xpath->Contents().size() == 0 );
428 }
429 catch( const XML_PARSER_ERROR &exc )
430 {
431 wxString errmsg = exc.what();
432
433 errmsg += wxT( "\n@ " );
434 errmsg += m_xpath->Contents();
435
436 THROW_IO_ERROR( errmsg );
437 }
438
439 // IO_ERROR exceptions are left uncaught, they pass upwards from here.
440
441 m_board->SetCopperLayerCount( getMinimumCopperLayerCount() );
442
443 LSET enabledLayers = m_board->GetDesignSettings().GetEnabledLayers();
444
445 for( const auto& [eagleLayerName, layer] : m_layer_map )
446 enabledLayers.set( layer );
447
448 m_board->GetDesignSettings().SetEnabledLayers( enabledLayers );
449
450 centerBoard();
451
452 deleter.release();
453 return m_board;
454}
455
456
458{
459 std::vector<FOOTPRINT*> retval;
460
461 for( const auto& [ name, footprint ] : m_templates )
462 retval.push_back( static_cast<FOOTPRINT*>( footprint->Clone() ) );
463
464 return retval;
465}
466
467
468void PCB_IO_EAGLE::init( const std::map<std::string, UTF8>* aProperties )
469{
470 m_hole_count = 0;
471 m_min_trace = 0;
472 m_min_hole = 0;
473 m_min_via = 0;
474 m_min_annulus = 0;
475 m_xpath->clear();
476 m_pads_to_nets.clear();
477
478 m_board = nullptr;
479 m_props = aProperties;
480
481
482 delete m_rules;
483 m_rules = new ERULES();
484}
485
486
488{
489 // All cu layers are invalid until we see them in the <layers> section while
490 // loading either a board or library. See loadLayerDefs().
491 for( unsigned i = 0; i < arrayDim(m_cu_map); ++i )
492 m_cu_map[i] = -1;
493}
494
495
496void PCB_IO_EAGLE::loadAllSections( wxXmlNode* aDoc )
497{
498 wxXmlNode* drawing = MapChildren( aDoc )["drawing"];
499 NODE_MAP drawingChildren = MapChildren( drawing );
500
501 wxXmlNode* board = drawingChildren["board"];
502 NODE_MAP boardChildren = MapChildren( board );
503
504 auto count_children =
505 [this]( wxXmlNode* aNode )
506 {
507 if( aNode )
508 {
509 wxXmlNode* child = aNode->GetChildren();
510
511 while( child )
512 {
513 m_totalCount++;
514 child = child->GetNext();
515 }
516 }
517 };
518
519 wxXmlNode* designrules = boardChildren["designrules"];
520 wxXmlNode* layers = drawingChildren["layers"];
521 wxXmlNode* plain = boardChildren["plain"];
522 wxXmlNode* classes = boardChildren["classes"];
523 wxXmlNode* signals = boardChildren["signals"];
524 wxXmlNode* libs = boardChildren["libraries"];
525 wxXmlNode* elems = boardChildren["elements"];
526
528 {
529 m_totalCount = 0;
530 m_doneCount = 0;
531
532 count_children( designrules );
533 count_children( layers );
534 count_children( plain );
535 count_children( signals );
536 count_children( elems );
537
538 while( libs )
539 {
540 count_children( MapChildren( libs )["packages"] );
541 libs = libs->GetNext();
542 }
543
544 // Rewind
545 libs = boardChildren["libraries"];
546 }
547
548 m_xpath->push( "eagle.drawing" );
549
550 {
551 m_xpath->push( "board" );
552
553 loadDesignRules( designrules );
554
555 m_xpath->pop();
556 }
557
558 {
559 m_xpath->push( "layers" );
560
561 loadLayerDefs( layers );
563
564 m_xpath->pop();
565 }
566
567 {
568 m_xpath->push( "board" );
569
570 loadPlain( plain );
571 loadClasses( classes );
572 loadSignals( signals );
573 loadLibraries( libs );
574 loadElements( elems );
575
576 m_xpath->pop();
577 }
578
579 m_xpath->pop(); // "eagle.drawing"
580}
581
582
583void PCB_IO_EAGLE::loadDesignRules( wxXmlNode* aDesignRules )
584{
585 if( aDesignRules )
586 {
587 m_xpath->push( "designrules" );
588 m_rules->parse( aDesignRules,
589 [this]()
590 {
591 checkpoint();
592 } );
593 m_xpath->pop(); // "designrules"
594 }
595}
596
597
598void PCB_IO_EAGLE::loadLayerDefs( wxXmlNode* aLayers )
599{
600 if( !aLayers )
601 return;
602
603 ELAYERS cu; // copper layers
604
605 // Get the first layer and iterate
606 wxXmlNode* layerNode = aLayers->GetChildren();
607
608 m_eagleLayers.clear();
609 m_eagleLayersIds.clear();
610
611 while( layerNode )
612 {
613 ELAYER elayer( layerNode );
614 m_eagleLayers.insert( std::make_pair( elayer.number, elayer ) );
615 m_eagleLayersIds.insert( std::make_pair( elayer.name, elayer.number ) );
616
617 // find the subset of layers that are copper and active
618 if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) )
619 cu.push_back( elayer );
620
621 layerNode = layerNode->GetNext();
622 }
623
624 // establish cu layer map:
625 int ki_layer_count = 0;
626
627 for( EITER it = cu.begin(); it != cu.end(); ++it, ++ki_layer_count )
628 {
629 if( ki_layer_count == 0 )
630 {
631 m_cu_map[it->number] = F_Cu;
632 }
633 else if( ki_layer_count == int( cu.size()-1 ) )
634 {
635 m_cu_map[it->number] = B_Cu;
636 }
637 else
638 {
639 // some eagle boards do not have contiguous layer number sequences.
640 m_cu_map[it->number] = BoardLayerFromLegacyId( ki_layer_count );
641 }
642 }
643
644 // Set the layer names and cu count if we're loading a board.
645 if( m_board )
646 {
647 m_board->SetCopperLayerCount( cu.size() );
648
649 for( EITER it = cu.begin(); it != cu.end(); ++it )
650 {
651 PCB_LAYER_ID layer = kicad_layer( it->number );
652
653 // these function provide their own protection against non enabled layers:
654 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT ) // layer should be valid
655 {
656 m_board->SetLayerName( layer, it->name );
657 m_board->SetLayerType( layer, LT_SIGNAL );
658 }
659
660 // could map the colors here
661 }
662 }
663}
664
665
666#define DIMENSION_PRECISION DIM_PRECISION::X_XX // 0.01 mm
667
668
669void PCB_IO_EAGLE::loadPlain( wxXmlNode* aGraphics )
670{
671 if( !aGraphics )
672 return;
673
674 m_xpath->push( "plain" );
675
676 // Get the first graphic and iterate
677 wxXmlNode* gr = aGraphics->GetChildren();
678
679 // (polygon | wire | text | circle | rectangle | frame | hole)*
680 while( gr )
681 {
682 checkpoint();
683
684 wxString grName = gr->GetName();
685
686 if( grName == wxT( "wire" ) )
687 {
688 m_xpath->push( "wire" );
689
690 EWIRE w( gr );
691 PCB_LAYER_ID layer = kicad_layer( w.layer );
692
693 VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
694 VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
695
696 if( layer != UNDEFINED_LAYER )
697 {
698 PCB_SHAPE* shape = new PCB_SHAPE( m_board );
699 int width = w.width.ToPcbUnits();
700
701 // KiCad cannot handle zero or negative line widths
702 if( width <= 0 )
703 width = m_board->GetDesignSettings().GetLineThickness( layer );
704
705 m_board->Add( shape, ADD_MODE::APPEND );
706
707 if( !w.curve )
708 {
709 shape->SetShape( SHAPE_T::SEGMENT );
710 shape->SetStart( start );
711 shape->SetEnd( end );
712 }
713 else
714 {
715 VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
716
717 shape->SetShape( SHAPE_T::ARC );
718 shape->SetCenter( center );
719 shape->SetStart( start );
720 shape->SetArcAngleAndEnd( -EDA_ANGLE( *w.curve, DEGREES_T ), true ); // KiCad rotates the other way
721 }
722
723 shape->SetLayer( layer );
724 shape->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
725 }
726
727 m_xpath->pop();
728 }
729 else if( grName == wxT( "text" ) )
730 {
731 m_xpath->push( "text" );
732
733 ETEXT t( gr );
734 PCB_LAYER_ID layer = kicad_layer( t.layer );
735
736 if( layer != UNDEFINED_LAYER )
737 {
738 PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
739 m_board->Add( pcbtxt, ADD_MODE::APPEND );
740
741 pcbtxt->SetLayer( layer );
742 wxString kicadText = interpretText( t.text );
743 pcbtxt->SetText( kicadText );
744
745 double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
746 int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100.0 );
747 pcbtxt->SetTextThickness( textThickness );
748 pcbtxt->SetTextSize( kicad_fontsize( t.size, textThickness ) );
749 pcbtxt->SetKeepUpright( false );
750
751 // Eagle's anchor is independent of text justification; KiCad's is not.
752 VECTOR2I eagleAnchor( kicad_x( t.x ), kicad_y( t.y ) );
753 int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT;
754 BOX2I textbox = pcbtxt->GetBoundingBox();
755 VECTOR2I offset( 0, 0 );
756 double degrees = 0;
757 int signX = 0;
758 int signY = 0;
759
760 if( t.rot )
761 {
762 degrees = t.rot->degrees;
763
764 if( t.rot->mirror && !t.rot->spin )
765 degrees *= -1;
766
767 if( t.rot->mirror )
768 pcbtxt->SetMirrored( t.rot->mirror );
769
770 if( degrees > 90 && degrees <= 270 )
771 {
772 if( degrees == 270 && t.rot->mirror )
773 degrees = 270; // an odd special-case
774 else
775 degrees -= 180;
776
777 signX = t.rot->mirror ? 1 : -1;
778 signY = 1;
779 }
780
781 pcbtxt->SetTextAngle( EDA_ANGLE( degrees, DEGREES_T ) );
782 }
783
784 switch( align )
785 {
789 offset.y = signY * (int) textbox.GetHeight();
790 break;
791
793 case ETEXT::TOP_LEFT:
794 case ETEXT::TOP_RIGHT:
795 offset.y = -signY * (int) textbox.GetHeight();
796 break;
797
798 default:
799 break;
800 }
801
802 switch( align )
803 {
804 case ETEXT::TOP_LEFT:
807 offset.x = signX * (int) textbox.GetWidth();
808 break;
809
810 case ETEXT::TOP_RIGHT:
813 offset.x = -signX * (int) textbox.GetWidth();
814 break;
815
816 default:
817 break;
818 }
819
820 RotatePoint( offset, EDA_ANGLE( degrees, DEGREES_T ) );
821 pcbtxt->SetTextPos( eagleAnchor + offset );
822
823 switch( align )
824 {
825 case ETEXT::CENTER:
828 break;
829
833 break;
834
838 break;
839
843 break;
844
845 case ETEXT::TOP_LEFT:
848 break;
849
850 case ETEXT::TOP_RIGHT:
853 break;
854
858 break;
859
863 break;
864
868 break;
869 }
870
871 // Refine justification and rotation for mirrored texts
872 if( pcbtxt->IsMirrored() && degrees < -90 && degrees >= -270 )
873 {
874 pcbtxt->SetTextAngle( EDA_ANGLE( 180+degrees, DEGREES_T ) );
875
876 if( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
878 else if( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
880
881 if( pcbtxt->GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
883 else if( pcbtxt->GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
885 }
886 }
887
888 m_xpath->pop();
889 }
890 else if( grName == wxT( "circle" ) )
891 {
892 m_xpath->push( "circle" );
893
894 ECIRCLE c( gr );
895
896 int width = c.width.ToPcbUnits();
897 int radius = c.radius.ToPcbUnits();
898
901 {
902 ZONE* zone = new ZONE( m_board );
903 m_board->Add( zone, ADD_MODE::APPEND );
904
906
907 // approximate circle as polygon
908 VECTOR2I center( kicad_x( c.x ), kicad_y( c.y ) );
909 int outlineRadius = radius + ( width / 2 );
910 int segsInCircle = GetArcToSegmentCount( outlineRadius, ARC_HIGH_DEF, FULL_CIRCLE );
911 EDA_ANGLE delta = ANGLE_360 / segsInCircle;
912
913 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
914 {
915 VECTOR2I rotatedPoint( outlineRadius, 0 );
916 RotatePoint( rotatedPoint, angle );
917 zone->AppendCorner( center + rotatedPoint, -1 );
918 }
919
920 if( width > 0 )
921 {
922 zone->NewHole();
923 int innerRadius = radius - ( width / 2 );
924 segsInCircle = GetArcToSegmentCount( innerRadius, ARC_HIGH_DEF, FULL_CIRCLE );
925 delta = ANGLE_360 / segsInCircle;
926
927 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
928 {
929 VECTOR2I rotatedPoint( innerRadius, 0 );
930 RotatePoint( rotatedPoint, angle );
931 zone->AppendCorner( center + rotatedPoint, 0 );
932 }
933 }
934
937 }
938 else
939 {
940 PCB_LAYER_ID layer = kicad_layer( c.layer );
941
942 if( layer != UNDEFINED_LAYER ) // unsupported layer
943 {
945 m_board->Add( shape, ADD_MODE::APPEND );
946 shape->SetFilled( false );
947 shape->SetLayer( layer );
948 shape->SetStart( VECTOR2I( kicad_x( c.x ), kicad_y( c.y ) ) );
949 shape->SetEnd( VECTOR2I( kicad_x( c.x ) + radius, kicad_y( c.y ) ) );
950 shape->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
951 }
952 }
953
954 m_xpath->pop();
955 }
956 else if( grName == wxT( "rectangle" ) )
957 {
958 // This seems to be a simplified rectangular [copper] zone, cannot find any
959 // net related info on it from the DTD.
960 m_xpath->push( "rectangle" );
961
962 ERECT r( gr );
963 PCB_LAYER_ID layer = kicad_layer( r.layer );
964
965 if( layer != UNDEFINED_LAYER )
966 {
967 ZONE* zone = new ZONE( m_board );
968
969 m_board->Add( zone, ADD_MODE::APPEND );
970
971 zone->SetLayer( layer );
973
975
976 const int outlineIdx = -1; // this is the id of the copper zone main outline
977 zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
978 zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
979 zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
980 zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
981
982 if( r.rot )
983 {
984 VECTOR2I center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
985 ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
986 zone->Rotate( center, EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
987 }
988
989 // this is not my fault:
990 zone->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
991 }
992
993 m_xpath->pop();
994 }
995 else if( grName == wxT( "hole" ) )
996 {
997 m_xpath->push( "hole" );
998
999 // Fabricate a FOOTPRINT with a single PAD_ATTRIB::NPTH pad.
1000 // Use m_hole_count to gen up a unique reference designator.
1001
1002 FOOTPRINT* footprint = new FOOTPRINT( m_board );
1003 m_board->Add( footprint, ADD_MODE::APPEND );
1004 int hole_count = m_hole_count++;
1005 footprint->SetReference( wxString::Format( wxT( "UNK_HOLE_%d" ), hole_count ) );
1006 footprint->Reference().SetVisible( false );
1007 // Mandatory: gives a dummy but valid LIB_ID
1008 LIB_ID fpid( wxEmptyString, wxString::Format( wxT( "dummyfp%d" ), hole_count ) );
1009 footprint->SetFPID( fpid );
1010
1011 packageHole( footprint, gr, true );
1012
1013 m_xpath->pop();
1014 }
1015 else if( grName == wxT( "frame" ) )
1016 {
1017 // picture this
1018 }
1019 else if( grName == wxT( "polygon" ) )
1020 {
1021 m_xpath->push( "polygon" );
1022 loadPolygon( gr );
1023 m_xpath->pop(); // "polygon"
1024 }
1025 else if( grName == wxT( "dimension" ) )
1026 {
1027 const BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
1028
1029 EDIMENSION d( gr );
1030 PCB_LAYER_ID layer = kicad_layer( d.layer );
1031 VECTOR2I pt1( kicad_x( d.x1 ), kicad_y( d.y1 ) );
1032 VECTOR2I pt2( kicad_x( d.x2 ), kicad_y( d.y2 ) );
1033 VECTOR2I pt3( kicad_x( d.x3 ), kicad_y( d.y3 ) );
1034 VECTOR2I textSize = designSettings.GetTextSize( layer );
1035 int textThickness = designSettings.GetLineThickness( layer );
1036
1037 if( d.textsize )
1038 {
1039 double ratio = 8; // DTD says 8 is default
1040 textThickness = KiROUND( d.textsize->ToPcbUnits() * ratio / 100.0 );
1041 textSize = kicad_fontsize( *d.textsize, textThickness );
1042 }
1043
1044 if( layer != UNDEFINED_LAYER )
1045 {
1046 if( d.dimensionType == wxT( "angle" ) )
1047 {
1048 // Kicad doesn't (at present) support angle dimensions
1049 }
1050 else if( d.dimensionType == wxT( "radius" ) )
1051 {
1052 PCB_DIM_RADIAL* dimension = new PCB_DIM_RADIAL( m_board );
1053 m_board->Add( dimension, ADD_MODE::APPEND );
1054
1055 dimension->SetLayer( layer );
1056 dimension->SetPrecision( DIMENSION_PRECISION );
1057
1058 dimension->SetStart( pt1 );
1059 dimension->SetEnd( pt2 );
1060 dimension->SetTextPos( pt3 );
1061 dimension->SetTextSize( textSize );
1062 dimension->SetTextThickness( textThickness );
1063 dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
1064 dimension->SetUnits( EDA_UNITS::MM );
1065 }
1066 else if( d.dimensionType == wxT( "leader" ) )
1067 {
1068 PCB_DIM_LEADER* leader = new PCB_DIM_LEADER( m_board );
1069 m_board->Add( leader, ADD_MODE::APPEND );
1070
1071 leader->SetLayer( layer );
1073
1074 leader->SetStart( pt1 );
1075 leader->SetEnd( pt2 );
1076 leader->SetTextPos( pt3 );
1077 leader->SetTextSize( textSize );
1078 leader->SetTextThickness( textThickness );
1079 leader->SetOverrideText( wxEmptyString );
1080 leader->SetLineThickness( designSettings.GetLineThickness( layer ) );
1081 }
1082 else // horizontal, vertical, <default>, diameter
1083 {
1085 m_board->Add( dimension, ADD_MODE::APPEND );
1086
1087 if( d.dimensionType )
1088 {
1089 // Eagle dimension graphic arms may have different lengths, but they look
1090 // incorrect in KiCad (the graphic is tilted). Make them even length in
1091 // such case.
1092 if( *d.dimensionType == wxT( "horizontal" ) )
1093 {
1094 int newY = ( pt1.y + pt2.y ) / 2;
1095 pt1.y = newY;
1096 pt2.y = newY;
1097 }
1098 else if( *d.dimensionType == wxT( "vertical" ) )
1099 {
1100 int newX = ( pt1.x + pt2.x ) / 2;
1101 pt1.x = newX;
1102 pt2.x = newX;
1103 }
1104 }
1105
1106 dimension->SetLayer( layer );
1107 dimension->SetPrecision( DIMENSION_PRECISION );
1108
1109 // The origin and end are assumed to always be in this order from eagle
1110 dimension->SetStart( pt1 );
1111 dimension->SetEnd( pt2 );
1112 dimension->SetTextSize( textSize );
1113 dimension->SetTextThickness( textThickness );
1114 dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
1115 dimension->SetUnits( EDA_UNITS::MM );
1116
1117 // check which axis the dimension runs in
1118 // because the "height" of the dimension is perpendicular to that axis
1119 // Note the check is just if two axes are close enough to each other
1120 // Eagle appears to have some rounding errors
1121 if( abs( pt1.x - pt2.x ) < 50000 ) // 50000 nm = 0.05 mm
1122 {
1123 int offset = pt3.x - pt1.x;
1124
1125 if( pt1.y > pt2.y )
1126 dimension->SetHeight( offset );
1127 else
1128 dimension->SetHeight( -offset );
1129 }
1130 else if( abs( pt1.y - pt2.y ) < 50000 )
1131 {
1132 int offset = pt3.y - pt1.y;
1133
1134 if( pt1.x > pt2.x )
1135 dimension->SetHeight( -offset );
1136 else
1137 dimension->SetHeight( offset );
1138 }
1139 else
1140 {
1141 int offset = KiROUND( pt3.Distance( pt1 ) );
1142
1143 if( pt1.y > pt2.y )
1144 dimension->SetHeight( offset );
1145 else
1146 dimension->SetHeight( -offset );
1147 }
1148 }
1149 }
1150 }
1151
1152 // Get next graphic
1153 gr = gr->GetNext();
1154 }
1155
1156 m_xpath->pop();
1157}
1158
1159
1160void PCB_IO_EAGLE::loadLibrary( wxXmlNode* aLib, const wxString* aLibName )
1161{
1162 if( !aLib )
1163 return;
1164
1165 wxString urn = aLib->GetAttribute( "urn" );
1166
1167 wxString urnOrdinal;
1168
1169 if( !urn.IsEmpty() )
1170 urnOrdinal = urn.AfterLast( ':' );
1171
1172 // library will have <xmlattr> node, skip that and get the single packages node
1173 wxXmlNode* packages = MapChildren( aLib )["packages"];
1174
1175 if( !packages )
1176 return;
1177
1178 m_xpath->push( "packages" );
1179
1180 // Create a FOOTPRINT for all the eagle packages, for use later via a copy constructor
1181 // to instantiate needed footprints in our BOARD. Save the FOOTPRINT templates in
1182 // a FOOTPRINT_MAP using a single lookup key consisting of libname+pkgname.
1183
1184 // Get the first package and iterate
1185 wxXmlNode* package = packages->GetChildren();
1186
1187 while( package )
1188 {
1189 checkpoint();
1190
1191 m_xpath->push( "package", "name" );
1192
1193 wxString pack_ref = package->GetAttribute( "name" );
1194
1195 if( !urnOrdinal.IsEmpty() )
1196 pack_ref += wxS( "_" ) + urnOrdinal;
1197
1198 ReplaceIllegalFileNameChars( pack_ref, '_' );
1199
1200 m_xpath->Value( pack_ref.ToUTF8() );
1201
1202 wxString key = aLibName ? makeKey( *aLibName, pack_ref ) : pack_ref;
1203
1204 FOOTPRINT* footprint = makeFootprint( package, pack_ref );
1205
1206 // add the templating FOOTPRINT to the FOOTPRINT template factory "m_templates"
1207 auto r = m_templates.insert( { key, footprint } );
1208
1209 if( !r.second /* && !( m_props && m_props->Value( "ignore_duplicates" ) ) */ )
1210 {
1211 wxString lib = aLibName ? *aLibName : m_lib_path;
1212 const wxString& pkg = pack_ref;
1213
1214 wxString emsg = wxString::Format( _( "<package> '%s' duplicated in <library> '%s'" ),
1215 pkg,
1216 lib );
1217 THROW_IO_ERROR( emsg );
1218 }
1219
1220 m_xpath->pop();
1221
1222 package = package->GetNext();
1223 }
1224
1225 m_xpath->pop(); // "packages"
1226}
1227
1228
1229void PCB_IO_EAGLE::loadLibraries( wxXmlNode* aLibs )
1230{
1231 if( !aLibs )
1232 return;
1233
1234 m_xpath->push( "libraries.library", "name" );
1235
1236 // Get the first library and iterate
1237 wxXmlNode* library = aLibs->GetChildren();
1238
1239 while( library )
1240 {
1241 const wxString& lib_name = library->GetAttribute( "name" );
1242
1243 m_xpath->Value( lib_name.c_str() );
1244 loadLibrary( library, &lib_name );
1245 library = library->GetNext();
1246 }
1247
1248 m_xpath->pop();
1249}
1250
1251
1252void PCB_IO_EAGLE::loadElements( wxXmlNode* aElements )
1253{
1254 if( !aElements )
1255 return;
1256
1257 m_xpath->push( "elements.element", "name" );
1258
1259 EATTR name;
1260 EATTR value;
1261 bool refanceNamePresetInPackageLayout;
1262 bool valueNamePresetInPackageLayout;
1263
1264 // Get the first element and iterate
1265 wxXmlNode* element = aElements->GetChildren();
1266
1267 while( element )
1268 {
1269 checkpoint();
1270
1271 if( element->GetName() != wxT( "element" ) )
1272 {
1273 // Get next item
1274 element = element->GetNext();
1275 continue;
1276 }
1277
1278 EELEMENT e( element );
1279
1280 // use "NULL-ness" as an indication of presence of the attribute:
1281 EATTR* nameAttr = nullptr;
1282 EATTR* valueAttr = nullptr;
1283
1284 m_xpath->Value( e.name.c_str() );
1285
1286 wxString packageName = e.package;
1287
1288 if( e.library_urn )
1289 packageName = e.package + wxS( "_" ) + e.library_urn->assetId;
1290
1291 wxString pkg_key = makeKey( e.library, packageName );
1292 auto it = m_templates.find( pkg_key );
1293
1294 if( it == m_templates.end() )
1295 {
1296 wxString emsg = wxString::Format( _( "No '%s' package in library '%s'." ),
1297 packageName, e.library );
1298 THROW_IO_ERROR( emsg );
1299 }
1300
1301 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( it->second->Duplicate( IGNORE_PARENT_GROUP ) );
1302
1303 m_board->Add( footprint, ADD_MODE::APPEND );
1304
1305 // update the nets within the pads of the clone
1306 for( PAD* pad : footprint->Pads() )
1307 {
1308 wxString pn_key = makeKey( e.name, pad->GetNumber() );
1309
1310 NET_MAP_CITER ni = m_pads_to_nets.find( pn_key );
1311 if( ni != m_pads_to_nets.end() )
1312 {
1313 const ENET* enet = &ni->second;
1314 pad->SetNetCode( enet->netcode );
1315 }
1316 }
1317
1318 refanceNamePresetInPackageLayout = true;
1319 valueNamePresetInPackageLayout = true;
1320 footprint->SetPosition( VECTOR2I( kicad_x( e.x ), kicad_y( e.y ) ) );
1321
1322 // Is >NAME field set in package layout ?
1323 if( footprint->GetReference().size() == 0 )
1324 {
1325 footprint->Reference().SetVisible( false ); // No so no show
1326 refanceNamePresetInPackageLayout = false;
1327 }
1328
1329 // Is >VALUE field set in package layout
1330 if( footprint->GetValue().size() == 0 )
1331 {
1332 footprint->Value().SetVisible( false ); // No so no show
1333 valueNamePresetInPackageLayout = false;
1334 }
1335
1336 wxString reference = e.name;
1337
1338 // EAGLE allows references to be single digits. This breaks KiCad
1339 // netlisting, which requires parts to have non-digit + digit
1340 // annotation. If the reference begins with a number, we prepend
1341 // 'UNK' (unknown) for the symbol designator.
1342 if( reference.find_first_not_of( "0123456789" ) != 0 )
1343 reference.Prepend( "UNK" );
1344
1345 // EAGLE allows designator to start with # but that is used in KiCad
1346 // for symbols which do not have a footprint
1347 if( reference.find_first_not_of( "#" ) != 0 )
1348 reference.Prepend( "UNK" );
1349
1350 // reference must end with a number but EAGLE does not enforce this
1351 if( reference.find_last_not_of( "0123456789" ) == (reference.Length()-1) )
1352 reference.Append( "0" );
1353
1354 footprint->SetReference( reference );
1355 footprint->SetValue( e.value );
1356
1357 if( !e.smashed )
1358 {
1359 // Not smashed so show NAME & VALUE
1360 if( valueNamePresetInPackageLayout )
1361 footprint->Value().SetVisible( true ); // Only if place holder in package layout
1362
1363 if( refanceNamePresetInPackageLayout )
1364 footprint->Reference().SetVisible( true ); // Only if place holder in package layout
1365 }
1366 else if( *e.smashed == true )
1367 {
1368 // Smashed so set default to no show for NAME and VALUE
1369 footprint->Value().SetVisible( false );
1370 footprint->Reference().SetVisible( false );
1371
1372 // initialize these to default values in case the <attribute> elements are not present.
1373 m_xpath->push( "attribute", "name" );
1374
1375 // VALUE and NAME can have something like our text "effects" overrides
1376 // in SWEET and new schematic. Eagle calls these XML elements "attribute".
1377 // There can be one for NAME and/or VALUE both. Features present in the
1378 // EATTR override the ones established in the package only if they are
1379 // present here (except for rot, which if not present means angle zero).
1380 // So the logic is a bit different than in packageText() and in plain text.
1381
1382 // Get the first attribute and iterate
1383 wxXmlNode* attribute = element->GetChildren();
1384
1385 while( attribute )
1386 {
1387 if( attribute->GetName() != wxT( "attribute" ) )
1388 {
1389 attribute = attribute->GetNext();
1390 continue;
1391 }
1392
1393 EATTR a( attribute );
1394
1395 if( a.name == wxT( "NAME" ) )
1396 {
1397 name = a;
1398 nameAttr = &name;
1399
1400 // do we have a display attribute ?
1401 if( a.display )
1402 {
1403 // Yes!
1404 switch( *a.display )
1405 {
1406 case EATTR::VALUE :
1407 {
1408 nameAttr->name = reference;
1409
1410 if( refanceNamePresetInPackageLayout )
1411 footprint->Reference().SetVisible( true );
1412
1413 break;
1414 }
1415
1416 case EATTR::NAME :
1417 if( refanceNamePresetInPackageLayout )
1418 {
1419 footprint->SetReference( "NAME" );
1420 footprint->Reference().SetVisible( true );
1421 }
1422
1423 break;
1424
1425 case EATTR::BOTH :
1426 if( refanceNamePresetInPackageLayout )
1427 footprint->Reference().SetVisible( true );
1428
1429 nameAttr->name = nameAttr->name + wxT( " = " ) + e.name;
1430 footprint->SetReference( wxT( "NAME = " ) + e.name );
1431 break;
1432
1433 case EATTR::Off :
1434 footprint->Reference().SetVisible( false );
1435 break;
1436
1437 default:
1438 nameAttr->name = e.name;
1439
1440 if( refanceNamePresetInPackageLayout )
1441 footprint->Reference().SetVisible( true );
1442 }
1443 }
1444 else
1445 {
1446 // No display, so default is visible, and show value of NAME
1447 footprint->Reference().SetVisible( true );
1448 }
1449 }
1450 else if( a.name == wxT( "VALUE" ) )
1451 {
1452 value = a;
1453 valueAttr = &value;
1454
1455 if( a.display )
1456 {
1457 // Yes!
1458 switch( *a.display )
1459 {
1460 case EATTR::VALUE :
1461 valueAttr->value = opt_wxString( e.value );
1462 footprint->SetValue( e.value );
1463
1464 if( valueNamePresetInPackageLayout )
1465 footprint->Value().SetVisible( true );
1466
1467 break;
1468
1469 case EATTR::NAME :
1470 if( valueNamePresetInPackageLayout )
1471 footprint->Value().SetVisible( true );
1472
1473 footprint->SetValue( wxT( "VALUE" ) );
1474 break;
1475
1476 case EATTR::BOTH :
1477 if( valueNamePresetInPackageLayout )
1478 footprint->Value().SetVisible( true );
1479
1480 valueAttr->value = opt_wxString( wxT( "VALUE = " ) + e.value );
1481 footprint->SetValue( wxT( "VALUE = " ) + e.value );
1482 break;
1483
1484 case EATTR::Off :
1485 footprint->Value().SetVisible( false );
1486 break;
1487
1488 default:
1489 valueAttr->value = opt_wxString( e.value );
1490
1491 if( valueNamePresetInPackageLayout )
1492 footprint->Value().SetVisible( true );
1493 }
1494 }
1495 else
1496 {
1497 // No display, so default is visible, and show value of NAME
1498 footprint->Value().SetVisible( true );
1499 }
1500
1501 }
1502
1503 attribute = attribute->GetNext();
1504 }
1505
1506 m_xpath->pop(); // "attribute"
1507 }
1508
1509 orientFootprintAndText( footprint, e, nameAttr, valueAttr );
1510
1511 // Get next element
1512 element = element->GetNext();
1513 }
1514
1515 m_xpath->pop(); // "elements.element"
1516}
1517
1518
1519ZONE* PCB_IO_EAGLE::loadPolygon( wxXmlNode* aPolyNode )
1520{
1521 EPOLYGON p( aPolyNode );
1522 PCB_LAYER_ID layer = kicad_layer( p.layer );
1523 bool keepout = ( p.layer == EAGLE_LAYER::TRESTRICT
1525 || p.layer == EAGLE_LAYER::VRESTRICT );
1526
1527 if( layer == UNDEFINED_LAYER )
1528 {
1529 wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) was not mapped" ),
1531 p.layer ) );
1532 return nullptr;
1533 }
1534
1535 // use a "netcode = 0" type ZONE:
1536 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
1537
1538 if( !keepout )
1539 zone->SetLayer( layer );
1540 else
1541 setKeepoutSettingsToZone( zone.get(), p.layer );
1542
1543 // Get the first vertex and iterate
1544 wxXmlNode* vertex = aPolyNode->GetChildren();
1545 std::vector<EVERTEX> vertices;
1546
1547 // Create a circular vector of vertices
1548 // The "curve" parameter indicates a curve from the current
1549 // to the next vertex, so we keep the first at the end as well
1550 // to allow the curve to link back
1551 while( vertex )
1552 {
1553 if( vertex->GetName() == wxT( "vertex" ) )
1554 vertices.emplace_back( vertex );
1555
1556 vertex = vertex->GetNext();
1557 }
1558
1559 // According to Eagle's doc, by default, the orphans (islands in KiCad parlance)
1560 // are always removed
1561 if( !p.orphans || !p.orphans.Get() )
1562 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
1563 else
1564 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
1565
1566 vertices.push_back( vertices[0] );
1567
1568 SHAPE_POLY_SET polygon;
1569 polygon.NewOutline();
1570
1571 for( size_t i = 0; i < vertices.size() - 1; i++ )
1572 {
1573 EVERTEX v1 = vertices[i];
1574
1575 // Append the corner
1576 polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) );
1577
1578 if( v1.curve )
1579 {
1580 EVERTEX v2 = vertices[i + 1];
1582 VECTOR2I( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
1583 double angle = DEG2RAD( *v1.curve );
1584 double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
1585 double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 ) + pow( center.y - kicad_y( v1.y ), 2 ) );
1586
1587 int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
1588 EDA_ANGLE( *v1.curve, DEGREES_T ) );
1589 double delta_angle = angle / segCount;
1590
1591 for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle ); a -= delta_angle )
1592 {
1593 polygon.Append( KiROUND( radius * cos( a ) ) + center.x,
1594 KiROUND( radius * sin( a ) ) + center.y );
1595 }
1596 }
1597 }
1598
1599 // Eagle traces the zone such that half of the pen width is outside the polygon.
1600 // We trace the zone such that the copper is completely inside.
1601 if( p.width.ToPcbUnits() > 0 )
1603
1604 if( polygon.OutlineCount() != 1 )
1605 {
1606 wxLogMessage( wxString::Format( _( "Skipping a polygon on layer '%s' (%d): outline count is not 1" ),
1608 p.layer ) );
1609
1610 return nullptr;
1611 }
1612
1613 zone->AddPolygon( polygon.COutline( 0 ) );
1614
1615 // If the pour is a cutout it needs to be set to a keepout
1616 if( p.pour == EPOLYGON::ECUTOUT )
1617 {
1618 zone->SetIsRuleArea( true );
1619 zone->SetDoNotAllowVias( false );
1620 zone->SetDoNotAllowTracks( false );
1621 zone->SetDoNotAllowPads( false );
1622 zone->SetDoNotAllowFootprints( false );
1623 zone->SetDoNotAllowZoneFills( true );
1624 zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
1625 }
1626 else if( p.pour == EPOLYGON::EHATCH )
1627 {
1628 int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * pcbIUScale.IU_PER_MILS;
1629
1630 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
1631 zone->SetHatchThickness( p.width.ToPcbUnits() );
1632 zone->SetHatchGap( spacing - p.width.ToPcbUnits() );
1633 zone->SetHatchOrientation( ANGLE_0 );
1634 }
1635
1636 // We divide the thickness by half because we are tracing _inside_ the zone outline
1637 // This means the radius of curvature will be twice the size for an equivalent EAGLE zone
1638 zone->SetMinThickness( std::max<int>( ZONE_THICKNESS_MIN_VALUE_MM * pcbIUScale.IU_PER_MM,
1639 p.width.ToPcbUnits() / 2 ) );
1640
1641 if( p.isolate )
1642 zone->SetLocalClearance( p.isolate->ToPcbUnits() );
1643 else
1644 zone->SetLocalClearance( 1 ); // @todo: set minimum clearance value based on board settings
1645
1646 // missing == yes per DTD.
1647 bool thermals = !p.thermals || *p.thermals;
1648 zone->SetPadConnection( thermals ? ZONE_CONNECTION::THERMAL : ZONE_CONNECTION::FULL );
1649
1650 if( thermals )
1651 {
1652 // FIXME: eagle calculates dimensions for thermal spokes
1653 // based on what the zone is connecting to.
1654 // (i.e. width of spoke is half of the smaller side of an smd pad)
1655 // This is a basic workaround
1656 zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
1657 zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 );
1658 }
1659
1660 int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
1661 zone->SetAssignedPriority( rank );
1662
1663 ZONE* zonePtr = zone.release();
1664 m_board->Add( zonePtr, ADD_MODE::APPEND );
1665
1666 return zonePtr;
1667}
1668
1669
1671 const EATTR* aNameAttr, const EATTR* aValueAttr )
1672{
1673 if( e.rot )
1674 {
1675 if( e.rot->mirror )
1676 {
1677 aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees + 180.0, DEGREES_T ) );
1678 aFootprint->Flip( aFootprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1679 }
1680 else
1681 {
1682 aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
1683 }
1684 }
1685
1686 orientFPText( aFootprint, e, &aFootprint->Reference(), aNameAttr );
1687 orientFPText( aFootprint, e, &aFootprint->Value(), aValueAttr );
1688}
1689
1690
1691void PCB_IO_EAGLE::orientFPText( FOOTPRINT* aFootprint, const EELEMENT& e, PCB_TEXT* aFPText,
1692 const EATTR* aAttr )
1693{
1694 // Smashed part ?
1695 if( aAttr )
1696 {
1697 // Yes
1698 const EATTR& a = *aAttr;
1699
1700 if( a.value )
1701 aFPText->SetText( *a.value );
1702
1703 if( a.x && a.y ) // std::optional
1704 {
1705 VECTOR2I pos( kicad_x( *a.x ), kicad_y( *a.y ) );
1706 aFPText->SetTextPos( pos );
1707 }
1708
1709 // Even though size and ratio are both optional, I am not seeing
1710 // a case where ratio is present but size is not.
1711 double ratio = 8;
1712
1713 if( a.ratio )
1714 ratio = *a.ratio;
1715
1716 VECTOR2I fontz = aFPText->GetTextSize();
1717 int textThickness = KiROUND( fontz.y * ratio / 100.0 );
1718
1719 aFPText->SetTextThickness( textThickness );
1720
1721 if( a.size )
1722 {
1723 fontz = kicad_fontsize( *a.size, textThickness );
1724 aFPText->SetTextSize( fontz );
1725 }
1726
1727 int align = ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1728
1729 if( a.align )
1730 align = *a.align;
1731
1732 // The "rot" in a EATTR seems to be assumed to be zero if it is not
1733 // present, and this zero rotation becomes an override to the
1734 // package's text field. If they did not want zero, they specify
1735 // what they want explicitly.
1736 double degrees = a.rot ? a.rot->degrees : 0.0;
1737 int sign = 1;
1738 bool spin = false;
1739
1740 if( a.rot )
1741 {
1742 spin = a.rot->spin;
1743 sign = a.rot->mirror ? -1 : 1;
1744 aFPText->SetMirrored( a.rot->mirror );
1745 }
1746
1747 if( degrees == 90 || degrees == 0 || spin )
1748 {
1749 degrees *= sign;
1750 }
1751 else if( degrees == 180 )
1752 {
1753 degrees = 0;
1754 align = -align;
1755 }
1756 else if( degrees == 270 )
1757 {
1758 align = -align;
1759 degrees = sign * 90;
1760 }
1761 else
1762 {
1763 degrees = 90 - (sign * degrees);
1764 }
1765
1766 aFPText->SetTextAngle( EDA_ANGLE( degrees, DEGREES_T ) );
1767
1768 switch( align )
1769 {
1770 case ETEXT::TOP_RIGHT:
1773 break;
1774
1775 case ETEXT::BOTTOM_LEFT:
1778 break;
1779
1780 case ETEXT::TOP_LEFT:
1783 break;
1784
1788 break;
1789
1790 case ETEXT::TOP_CENTER:
1793 break;
1794
1798 break;
1799
1800 case ETEXT::CENTER:
1803 break;
1804
1805 case ETEXT::CENTER_LEFT:
1808 break;
1809
1813 break;
1814
1815 default:
1816 ;
1817 }
1818
1819 // Refine justification and rotation for mirrored texts
1820 if( aFPText->IsMirrored() && degrees < -90 && degrees >= -270 )
1821 {
1822 aFPText->SetTextAngle( EDA_ANGLE( 180+degrees, DEGREES_T ) );
1823
1824 if( aFPText->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
1826 else if( aFPText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
1828
1829 if( aFPText->GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
1831 else if( aFPText->GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
1833 }
1834 }
1835 else
1836 {
1837 // Part is not smash so use Lib default for NAME/VALUE
1838 // the text is per the original package, sans <attribute>.
1839 double degrees = aFPText->GetTextAngle().AsDegrees()
1840 + aFootprint->GetOrientation().AsDegrees();
1841
1842 // bottom-left is eagle default
1845
1846 if( !aFPText->IsMirrored() && abs( degrees ) <= -180 )
1847 {
1850 }
1851 }
1852}
1853
1854
1855FOOTPRINT* PCB_IO_EAGLE::makeFootprint( wxXmlNode* aPackage, const wxString& aPkgName )
1856{
1857 std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>( m_board );
1858
1859 LIB_ID fpID;
1860 fpID.Parse( aPkgName, true );
1861 m->SetFPID( fpID );
1862
1863 // Get the first package item and iterate
1864 wxXmlNode* packageItem = aPackage->GetChildren();
1865
1866 // layer 27 is default layer for tValues
1867 // set default layer for created footprint
1868 PCB_LAYER_ID layer = kicad_layer( 27 );
1869 m.get()->Value().SetLayer( layer );
1870
1871 while( packageItem )
1872 {
1873 const wxString& itemName = packageItem->GetName();
1874
1875 if( itemName == wxT( "description" ) )
1876 {
1877 wxString descr = convertDescription( UnescapeHTML( packageItem->GetNodeContent() ) );
1878 m->SetLibDescription( descr );
1879 }
1880 else if( itemName == wxT( "wire" ) )
1881 packageWire( m.get(), packageItem );
1882 else if( itemName == wxT( "pad" ) )
1883 packagePad( m.get(), packageItem );
1884 else if( itemName == wxT( "text" ) )
1885 packageText( m.get(), packageItem );
1886 else if( itemName == wxT( "rectangle" ) )
1887 packageRectangle( m.get(), packageItem );
1888 else if( itemName == wxT( "polygon" ) )
1889 packagePolygon( m.get(), packageItem );
1890 else if( itemName == wxT( "circle" ) )
1891 packageCircle( m.get(), packageItem );
1892 else if( itemName == wxT( "hole" ) )
1893 packageHole( m.get(), packageItem, false );
1894 else if( itemName == wxT( "smd" ) )
1895 packageSMD( m.get(), packageItem );
1896
1897 packageItem = packageItem->GetNext();
1898 }
1899
1900 return m.release();
1901}
1902
1903
1904void PCB_IO_EAGLE::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1905{
1906 EWIRE w( aTree );
1907 PCB_LAYER_ID layer = kicad_layer( w.layer );
1908 VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
1909 VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
1910 int width = w.width.ToPcbUnits();
1911
1912 if( layer == UNDEFINED_LAYER )
1913 {
1914 wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) was not mapped" ),
1916 w.layer ) );
1917 return;
1918 }
1919
1920 // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
1921 if( width <= 0 )
1922 {
1923 BOARD* board = aFootprint->GetBoard();
1924
1925 if( board )
1926 {
1927 width = board->GetDesignSettings().GetLineThickness( layer );
1928 }
1929 else
1930 {
1931 // When loading footprint libraries, there is no board so use the default KiCad
1932 // line widths.
1933 switch( layer )
1934 {
1935 case Edge_Cuts: width = pcbIUScale.mmToIU( DEFAULT_EDGE_WIDTH ); break;
1936
1937 case F_SilkS:
1938 case B_SilkS: width = pcbIUScale.mmToIU( DEFAULT_SILK_LINE_WIDTH ); break;
1939
1940 case F_CrtYd:
1941 case B_CrtYd: width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ); break;
1942
1943 default: width = pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ); break;
1944 }
1945 }
1946 }
1947
1948 // FIXME: the cap attribute is ignored because KiCad can't create lines with flat ends.
1949 PCB_SHAPE* dwg;
1950
1951 if( !w.curve )
1952 {
1953 dwg = new PCB_SHAPE( aFootprint, SHAPE_T::SEGMENT );
1954
1955 dwg->SetStart( start );
1956 dwg->SetEnd( end );
1957 }
1958 else
1959 {
1960 dwg = new PCB_SHAPE( aFootprint, SHAPE_T::ARC );
1961 VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
1962
1963 dwg->SetCenter( center );
1964 dwg->SetStart( start );
1965 dwg->SetArcAngleAndEnd( -EDA_ANGLE( *w.curve, DEGREES_T ), true ); // KiCad rotates the other way
1966 }
1967
1968 dwg->SetLayer( layer );
1969 dwg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
1970 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
1971 dwg->Move( aFootprint->GetPosition() );
1972
1973 aFootprint->Add( dwg );
1974}
1975
1976
1977void PCB_IO_EAGLE::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
1978{
1979 // this is thru hole technology here, no SMDs
1980 EPAD e( aTree );
1981 int shape = EPAD::UNDEF;
1982 int eagleDrillz = e.drill ? e.drill->ToPcbUnits() : 0;
1983
1984 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
1985 transferPad( e, pad.get() );
1986
1987 if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
1988 shape = m_rules->psFirst;
1989 else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
1990 shape = m_rules->psTop;
1991 else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
1992 shape = m_rules->psBottom;
1993
1994 pad->SetDrillSize( VECTOR2I( eagleDrillz, eagleDrillz ) );
1995 pad->SetLayerSet( LSET::AllCuMask() );
1996
1997 if( eagleDrillz < m_min_hole )
1998 m_min_hole = eagleDrillz;
1999
2000 // Solder mask
2001 if( !e.stop || *e.stop == true ) // enabled by default
2002 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
2003
2004 if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
2005 e.shape = shape;
2006
2007 if( e.shape )
2008 {
2009 switch( *e.shape )
2010 {
2011 case EPAD::ROUND:
2013 break;
2014
2015 case EPAD::OCTAGON:
2017 pad->SetChamferPositions( PADSTACK::ALL_LAYERS, RECT_CHAMFER_ALL );
2018 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, 1 - M_SQRT1_2 ); // Regular polygon
2019 break;
2020
2021 case EPAD::LONG:
2023 break;
2024
2025 case EPAD::SQUARE:
2027 break;
2028
2029 case EPAD::OFFSET:
2031 break;
2032 }
2033 }
2034 else
2035 {
2036 // if shape is not present, our default is circle and that matches their default "round"
2037 }
2038
2039 if( e.diameter && e.diameter->value > 0 )
2040 {
2041 int diameter = e.diameter->ToPcbUnits();
2042 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( diameter, diameter ) );
2043 }
2044 else
2045 {
2046 double drillz = pad->GetDrillSize().x;
2047 double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
2048 annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
2049 int diameter = KiROUND( drillz + 2 * annulus );
2050 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( diameter, diameter ) );
2051 }
2052
2053 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::OVAL )
2054 {
2055 // The Eagle "long" pad is wider than it is tall; m_elongation is percent elongation
2056 VECTOR2I sz = pad->GetSize( PADSTACK::ALL_LAYERS );
2057 sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
2058 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
2059
2060 if( e.shape && *e.shape == EPAD::OFFSET )
2061 {
2062 int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
2063 pad->SetOffset( PADSTACK::ALL_LAYERS, VECTOR2I( offset, 0 ) );
2064 }
2065 }
2066
2067 if( e.rot )
2068 pad->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
2069
2070 // Eagle spokes are always '+'
2071 pad->SetThermalSpokeAngle( ANGLE_0 );
2072
2073 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 && pad->HasHole() )
2074 {
2075 aFootprint->Add( pad.release() );
2076 }
2077 else
2078 {
2079 wxFileName fileName( m_lib_path );
2080
2081 if( m_board)
2082 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ), m_board->GetFileName() );
2083 else
2084 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ), fileName.GetFullName() );
2085 }
2086}
2087
2088
2089void PCB_IO_EAGLE::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2090{
2091 ETEXT t( aTree );
2092 PCB_LAYER_ID layer = kicad_layer( t.layer );
2093
2094 if( layer == UNDEFINED_LAYER )
2095 {
2096 wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) was not mapped" ),
2098 t.layer ) );
2099 return;
2100 }
2101
2102 PCB_TEXT* textItem;
2103
2104 if( t.text.Upper() == wxT( ">NAME" ) && aFootprint->GetReference().IsEmpty() )
2105 {
2106 textItem = &aFootprint->Reference();
2107
2108 textItem->SetText( wxT( "REF**" ) );
2109 }
2110 else if( t.text.Upper() == wxT( ">VALUE" ) && aFootprint->GetValue().IsEmpty() )
2111 {
2112 textItem = &aFootprint->Value();
2113
2114 textItem->SetText( aFootprint->GetFPID().GetLibItemName() );
2115 }
2116 else
2117 {
2118 textItem = new PCB_TEXT( aFootprint );
2119 aFootprint->Add( textItem );
2120
2121 textItem->SetText( interpretText( t.text ) );
2122 }
2123
2124 VECTOR2I pos( kicad_x( t.x ), kicad_y( t.y ) );
2125
2126 textItem->SetPosition( pos );
2127 textItem->SetLayer( layer );
2128
2129 double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
2130 int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100.0 );
2131
2132 textItem->SetTextThickness( textThickness );
2133 textItem->SetTextSize( kicad_fontsize( t.size, textThickness ) );
2134 textItem->SetKeepUpright( false );
2135
2136 int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
2137
2138 // An eagle package is never rotated, the DTD does not allow it.
2139 // angle -= aFootprint->GetOrienation();
2140
2141 if( t.rot )
2142 {
2143 int sign = t.rot->mirror ? -1 : 1;
2144 textItem->SetMirrored( t.rot->mirror );
2145
2146 double degrees = t.rot->degrees;
2147 textItem->SetTextAngle( EDA_ANGLE( sign * degrees, DEGREES_T ) );
2148 }
2149
2150 switch( align )
2151 {
2152 case ETEXT::CENTER:
2155 break;
2156
2157 case ETEXT::CENTER_LEFT:
2160 break;
2161
2165 break;
2166
2167 case ETEXT::TOP_CENTER:
2170 break;
2171
2172 case ETEXT::TOP_LEFT:
2175 break;
2176
2177 case ETEXT::TOP_RIGHT:
2180 break;
2181
2185 break;
2186
2187 case ETEXT::BOTTOM_LEFT:
2190 break;
2191
2195 break;
2196 }
2197}
2198
2199
2200void PCB_IO_EAGLE::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2201{
2202 ERECT r( aTree );
2203
2206 {
2207 ZONE* zone = new ZONE( aFootprint );
2208 aFootprint->Add( zone, ADD_MODE::APPEND );
2209
2211
2212 const int outlineIdx = -1; // this is the id of the copper zone main outline
2213 zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
2214 zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
2215 zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
2216 zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
2217
2218 if( r.rot )
2219 {
2220 VECTOR2I center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
2221 ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
2222 zone->Rotate( center, EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
2223 }
2224
2227 }
2228 else
2229 {
2230 PCB_LAYER_ID layer = kicad_layer( r.layer );
2231
2232 if( layer == UNDEFINED_LAYER )
2233 {
2234 wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) was not mapped" ),
2236 r.layer ) );
2237 return;
2238 }
2239
2240 PCB_SHAPE* dwg = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
2241
2242 aFootprint->Add( dwg );
2243
2244 dwg->SetLayer( layer );
2245 dwg->SetStroke( STROKE_PARAMS( 0 ) );
2246 dwg->SetFilled( true );
2247
2248 std::vector<VECTOR2I> pts;
2249
2250 VECTOR2I start( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
2251 VECTOR2I end( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
2252
2253 pts.push_back( start );
2254 pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
2255 pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
2256 pts.push_back( end );
2257
2258 dwg->SetPolyPoints( pts );
2259
2260 if( r.rot )
2261 dwg->Rotate( dwg->GetCenter(), EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
2262
2263 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
2264 dwg->Move( aFootprint->GetPosition() );
2265 }
2266}
2267
2268
2269void PCB_IO_EAGLE::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2270{
2271 EPOLYGON p( aTree );
2272
2273 std::vector<VECTOR2I> pts;
2274
2275 // Get the first vertex and iterate
2276 wxXmlNode* vertex = aTree->GetChildren();
2277 std::vector<EVERTEX> vertices;
2278
2279 // Create a circular vector of vertices
2280 // The "curve" parameter indicates a curve from the current
2281 // to the next vertex, so we keep the first at the end as well
2282 // to allow the curve to link back
2283 while( vertex )
2284 {
2285 if( vertex->GetName() == wxT( "vertex" ) )
2286 vertices.emplace_back( vertex );
2287
2288 vertex = vertex->GetNext();
2289 }
2290
2291 vertices.push_back( vertices[0] );
2292
2293 for( size_t i = 0; i < vertices.size() - 1; i++ )
2294 {
2295 EVERTEX v1 = vertices[i];
2296
2297 // Append the corner
2298 pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
2299
2300 if( v1.curve )
2301 {
2302 EVERTEX v2 = vertices[i + 1];
2304 VECTOR2I( kicad_x( v2.x ), kicad_y( v2.y ) ),
2305 *v1.curve );
2306 double angle = DEG2RAD( *v1.curve );
2307 double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
2308 double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 ) + pow( center.y - kicad_y( v1.y ), 2 ) );
2309
2310 // Don't allow a zero-radius curve
2311 if( KiROUND( radius ) == 0 )
2312 radius = 1.0;
2313
2314 int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
2315 EDA_ANGLE( *v1.curve, DEGREES_T ) );
2316 double delta = angle / segCount;
2317
2318 for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
2319 {
2320 pts.push_back( VECTOR2I( KiROUND( radius * cos( a ) ),
2321 KiROUND( radius * sin( a ) ) ) + center );
2322 }
2323 }
2324 }
2325
2326 PCB_LAYER_ID layer = kicad_layer( p.layer );
2327
2328 if( ( p.pour == EPOLYGON::ECUTOUT && layer != UNDEFINED_LAYER )
2332 {
2333 ZONE* zone = new ZONE( aFootprint );
2334 aFootprint->Add( zone, ADD_MODE::APPEND );
2335
2337
2338 SHAPE_LINE_CHAIN outline( pts );
2339 outline.SetClosed( true );
2340 zone->Outline()->AddOutline( outline );
2341
2344 }
2345 else
2346 {
2347 if( layer == UNDEFINED_LAYER )
2348 {
2349 wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) was not mapped" ),
2351 p.layer ) );
2352 return;
2353 }
2354
2355 PCB_SHAPE* dwg = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
2356
2357 aFootprint->Add( dwg );
2358
2359 dwg->SetStroke( STROKE_PARAMS( 0 ) );
2360 dwg->SetFilled( true );
2361 dwg->SetLayer( layer );
2362
2363 dwg->SetPolyPoints( pts );
2364 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
2365 dwg->Move( aFootprint->GetPosition() );
2367 ARC_HIGH_DEF );
2368 }
2369}
2370
2371
2372void PCB_IO_EAGLE::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2373{
2374 ECIRCLE e( aTree );
2375
2376 int width = e.width.ToPcbUnits();
2377 int radius = e.radius.ToPcbUnits();
2378
2382 {
2383 ZONE* zone = new ZONE( aFootprint );
2384 aFootprint->Add( zone, ADD_MODE::APPEND );
2385
2387
2388 // approximate circle as polygon
2389 VECTOR2I center( kicad_x( e.x ), kicad_y( e.y ) );
2390 int outlineRadius = radius + ( width / 2 );
2391 int segsInCircle = GetArcToSegmentCount( outlineRadius, ARC_HIGH_DEF, FULL_CIRCLE );
2392 EDA_ANGLE delta = ANGLE_360 / segsInCircle;
2393
2394 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
2395 {
2396 VECTOR2I rotatedPoint( outlineRadius, 0 );
2397 RotatePoint( rotatedPoint, angle );
2398 zone->AppendCorner( center + rotatedPoint, -1 );
2399 }
2400
2401 if( width > 0 )
2402 {
2403 zone->NewHole();
2404 int innerRadius = radius - ( width / 2 );
2405 segsInCircle = GetArcToSegmentCount( innerRadius, ARC_HIGH_DEF, FULL_CIRCLE );
2406 delta = ANGLE_360 / segsInCircle;
2407
2408 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
2409 {
2410 VECTOR2I rotatedPoint( innerRadius, 0 );
2411 RotatePoint( rotatedPoint, angle );
2412 zone->AppendCorner( center + rotatedPoint, 0 );
2413 }
2414 }
2415
2418 }
2419 else
2420 {
2421 PCB_LAYER_ID layer = kicad_layer( e.layer );
2422
2423 if( layer == UNDEFINED_LAYER )
2424 {
2425 wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) was not mapped" ),
2427 e.layer ) );
2428 return;
2429 }
2430
2431 PCB_SHAPE* gr = new PCB_SHAPE( aFootprint, SHAPE_T::CIRCLE );
2432
2433 // width == 0 means filled circle
2434 if( width <= 0 )
2435 {
2436 width = radius;
2437 radius = radius / 2;
2438 gr->SetFilled( true );
2439 }
2440
2441 aFootprint->Add( gr );
2442 gr->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2443
2444 switch( (int) layer )
2445 {
2446 case UNDEFINED_LAYER:
2447 layer = Cmts_User;
2448 break;
2449 default:
2450 break;
2451 }
2452
2453 gr->SetLayer( layer );
2454 gr->SetStart( VECTOR2I( kicad_x( e.x ), kicad_y( e.y ) ) );
2455 gr->SetEnd( VECTOR2I( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
2456 gr->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
2457 gr->Move( aFootprint->GetPosition() );
2458 }
2459}
2460
2461
2462void PCB_IO_EAGLE::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
2463{
2464 EHOLE e( aTree );
2465
2466 if( e.drill.value == 0 )
2467 return;
2468
2469 // we add a PAD_ATTRIB::NPTH pad to this footprint.
2470 PAD* pad = new PAD( aFootprint );
2471 aFootprint->Add( pad );
2472
2474 pad->SetAttribute( PAD_ATTRIB::NPTH );
2475
2476 // Mechanical purpose only:
2477 // no offset, no net name, no pad name allowed
2478 // pad->SetOffset( VECTOR2I( 0, 0 ) );
2479 // pad->SetNumber( wxEmptyString );
2480
2481 VECTOR2I padpos( kicad_x( e.x ), kicad_y( e.y ) );
2482
2483 if( aCenter )
2484 {
2485 aFootprint->SetPosition( padpos );
2486 pad->SetPosition( padpos );
2487 }
2488 else
2489 {
2490 pad->SetPosition( padpos + aFootprint->GetPosition() );
2491 }
2492
2493 VECTOR2I sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
2494
2495 pad->SetDrillSize( sz );
2496 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
2497
2498 pad->SetLayerSet( LSET( LSET::AllCuMask() ).set( B_Mask ).set( F_Mask ) );
2499}
2500
2501
2502void PCB_IO_EAGLE::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2503{
2504 ESMD e( aTree );
2505 PCB_LAYER_ID layer = kicad_layer( e.layer );
2506
2507 if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
2508 return;
2509
2510 PAD* pad = new PAD( aFootprint );
2511 aFootprint->Add( pad );
2512 transferPad( e, pad );
2513
2515 pad->SetAttribute( PAD_ATTRIB::SMD );
2516
2517 VECTOR2I padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
2518 pad->SetSize( PADSTACK::ALL_LAYERS, padSize );
2519 pad->SetLayer( layer );
2520
2521 const LSET front( { F_Cu, F_Paste, F_Mask } );
2522 const LSET back( { B_Cu, B_Paste, B_Mask } );
2523
2524 if( layer == F_Cu )
2525 pad->SetLayerSet( front );
2526 else if( layer == B_Cu )
2527 pad->SetLayerSet( back );
2528
2529 int minPadSize = std::min( padSize.x, padSize.y );
2530
2531 // Rounded rectangle pads
2532 int roundRadius = eagleClamp( m_rules->srMinRoundness * 2,
2533 (int) ( minPadSize * m_rules->srRoundness ),
2534 m_rules->srMaxRoundness * 2 );
2535
2536 if( e.roundness || roundRadius > 0 )
2537 {
2538 double roundRatio = (double) roundRadius / minPadSize / 2.0;
2539
2540 // Eagle uses a different definition of roundness, hence division by 200
2541 if( e.roundness )
2542 roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
2543
2545 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, roundRatio );
2546 }
2547
2548 if( e.rot )
2549 pad->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
2550
2551 // Eagle spokes are always '+'
2552 pad->SetThermalSpokeAngle( ANGLE_0 );
2553
2554 pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
2555 (int) ( m_rules->mvCreamFrame * minPadSize ),
2556 m_rules->mlMaxCreamFrame ) );
2557
2558 // Solder mask
2559 if( e.stop && *e.stop == false ) // enabled by default
2560 {
2561 if( layer == F_Cu )
2562 pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
2563 else if( layer == B_Cu )
2564 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
2565 }
2566
2567 // Solder paste (only for SMD pads)
2568 if( e.cream && *e.cream == false ) // enabled by default
2569 {
2570 if( layer == F_Cu )
2571 pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
2572 else if( layer == B_Cu )
2573 pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
2574 }
2575}
2576
2577
2578void PCB_IO_EAGLE::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
2579{
2580 aPad->SetNumber( aEaglePad.name );
2581
2582 VECTOR2I padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
2583
2584 // Solder mask
2585 const VECTOR2I& padSize( aPad->GetSize( PADSTACK::ALL_LAYERS ) );
2586
2587 aPad->SetLocalSolderMaskMargin( eagleClamp( m_rules->mlMinStopFrame,
2588 (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
2589 m_rules->mlMaxStopFrame ) );
2590
2591 // Solid connection to copper zones
2592 if( aEaglePad.thermals && !*aEaglePad.thermals )
2594
2595 FOOTPRINT* footprint = aPad->GetParentFootprint();
2596 wxCHECK( footprint, /* void */ );
2597 RotatePoint( padPos, footprint->GetOrientation() );
2598 aPad->SetPosition( padPos + footprint->GetPosition() );
2599}
2600
2601
2603{
2604 for( const auto& [ name, footprint ] : m_templates )
2605 {
2606 footprint->SetParent( nullptr );
2607 delete footprint;
2608 }
2609
2610 m_templates.clear();
2611}
2612
2613
2614void PCB_IO_EAGLE::loadClasses( wxXmlNode* aClasses )
2615{
2616 // Eagle board DTD defines the "classes" element as 0 or 1.
2617 if( !aClasses )
2618 return;
2619
2620 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2621
2622 m_xpath->push( "classes.class", "number" );
2623
2624 std::vector<ECLASS> eClasses;
2625 wxXmlNode* classNode = aClasses->GetChildren();
2626
2627 while( classNode )
2628 {
2629 checkpoint();
2630
2631 ECLASS eClass( classNode );
2632 std::shared_ptr<NETCLASS> netclass;
2633
2634 if( eClass.name.CmpNoCase( wxT( "default" ) ) == 0 )
2635 {
2636 netclass = bds.m_NetSettings->GetDefaultNetclass();
2637 }
2638 else
2639 {
2640 netclass.reset( new NETCLASS( eClass.name ) );
2641 bds.m_NetSettings->SetNetclass( eClass.name, netclass );
2642 }
2643
2644 netclass->SetTrackWidth( INT_MAX );
2645 netclass->SetViaDiameter( INT_MAX );
2646 netclass->SetViaDrill( INT_MAX );
2647
2648 eClasses.emplace_back( eClass );
2649 m_classMap[ eClass.number ] = netclass;
2650
2651 // Set netclass clearance to the clearance-to-default-class value
2652 auto clearanceToDefaultIt = eClass.clearanceMap.find( wxT( "0" ) );
2653
2654 if( clearanceToDefaultIt != eClass.clearanceMap.end() )
2655 {
2656 netclass->SetClearance( clearanceToDefaultIt->second.ToPcbUnits() );
2657 }
2658
2659 // Get next class
2660 classNode = classNode->GetNext();
2661 }
2662
2663 m_customRules = wxT( "(version 1)" );
2664
2665 for( ECLASS& eClass : eClasses )
2666 {
2667 for( const auto& [className, pt] : eClass.clearanceMap )
2668 {
2669 // Skip clearances to default class (class "0") - these are handled via netclass clearances
2670 if( className == wxT( "0" ) )
2671 continue;
2672
2673 if( m_classMap[className] != nullptr )
2674 {
2675 wxString rule;
2676 rule.Printf( wxT( "(rule \"class %s:%s\"\n"
2677 " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
2678 " (constraint clearance (min %smm)))\n" ),
2679 eClass.number,
2680 className,
2681 eClass.name,
2682 m_classMap[className]->GetName(),
2684
2685 m_customRules += wxT( "\n" ) + rule;
2686 }
2687 }
2688 }
2689
2690 m_xpath->pop(); // "classes.class"
2691}
2692
2693
2694void PCB_IO_EAGLE::loadSignals( wxXmlNode* aSignals )
2695{
2696 // Eagle board DTD defines the "signals" element as 0 or 1.
2697 if( !aSignals )
2698 return;
2699
2700 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2701
2702 ZONES zones; // per net
2703 int netCode = 1;
2704
2705 m_xpath->push( "signals.signal", "name" );
2706
2707 // Get the first signal and iterate
2708 wxXmlNode* net = aSignals->GetChildren();
2709
2710 while( net )
2711 {
2712 checkpoint();
2713
2714 bool sawPad = false;
2715
2716 zones.clear();
2717
2718 const wxString& netName = escapeName( net->GetAttribute( "name" ) );
2719 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, netName, netCode );
2720 std::shared_ptr<NETCLASS> netclass;
2721
2722 if( net->HasAttribute( "class" ) )
2723 {
2724 auto netclassIt = m_classMap.find( net->GetAttribute( "class" ) );
2725
2726 if( netclassIt != m_classMap.end() )
2727 {
2728 bds.m_NetSettings->SetNetclassPatternAssignment( netName, netclassIt->second->GetName() );
2729 netInfo->SetNetClass( netclassIt->second );
2730 netclass = netclassIt->second;
2731 }
2732 }
2733
2734 m_board->Add( netInfo );
2735
2736 m_xpath->Value( netName.c_str() );
2737
2738 // Get the first net item and iterate
2739 wxXmlNode* netItem = net->GetChildren();
2740
2741 // (contactref | polygon | wire | via)*
2742 while( netItem )
2743 {
2744 const wxString& itemName = netItem->GetName();
2745
2746 if( itemName == wxT( "wire" ) )
2747 {
2748 m_xpath->push( "wire" );
2749
2750 EWIRE w( netItem );
2751 PCB_LAYER_ID layer = kicad_layer( w.layer );
2752
2753 if( IsCopperLayer( layer ) )
2754 {
2755 VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
2756 VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
2757
2758 int width = w.width.ToPcbUnits();
2759
2760 if( width < m_min_trace )
2761 m_min_trace = width;
2762
2763 if( netclass && width < netclass->GetTrackWidth() )
2764 netclass->SetTrackWidth( width );
2765
2766 if( w.curve )
2767 {
2768 VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
2769 double radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
2770 pow( center.y - kicad_y( w.y1 ), 2 ) );
2771 VECTOR2I mid = CalcArcMid( start, end, center, true );
2772 VECTOR2I otherMid = CalcArcMid( start, end, center, false );
2773
2774 double radiusA = ( mid - center ).EuclideanNorm();
2775 double radiusB = ( otherMid - center ).EuclideanNorm();
2776
2777 if( abs( radiusA - radius ) > abs( radiusB - radius ) )
2778 std::swap( mid, otherMid );
2779
2780 PCB_ARC* arc = new PCB_ARC( m_board );
2781
2782 arc->SetPosition( start );
2783 arc->SetMid( mid );
2784 arc->SetEnd( end );
2785 arc->SetWidth( width );
2786 arc->SetLayer( layer );
2787 arc->SetNetCode( netCode );
2788
2789 m_board->Add( arc );
2790 }
2791 else
2792 {
2793 PCB_TRACK* track = new PCB_TRACK( m_board );
2794
2795 track->SetPosition( start );
2796 track->SetEnd( VECTOR2I( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
2797 track->SetWidth( width );
2798 track->SetLayer( layer );
2799 track->SetNetCode( netCode );
2800
2801 m_board->Add( track );
2802 }
2803 }
2804 else
2805 {
2806 // put non copper wires where the sun don't shine.
2807 }
2808
2809 m_xpath->pop();
2810 }
2811 else if( itemName == wxT( "via" ) )
2812 {
2813 m_xpath->push( "via" );
2814 EVIA v( netItem );
2815
2817 std::swap( v.layer_front_most, v.layer_back_most );
2818
2819 PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most );
2820 PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most );
2821
2822 if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most )
2823 && layer_front_most != layer_back_most )
2824 {
2825 int kidiam;
2826 int drillz = v.drill.ToPcbUnits();
2827 PCB_VIA* via = new PCB_VIA( m_board );
2828 m_board->Add( via );
2829
2830 if( v.diam )
2831 {
2832 kidiam = v.diam->ToPcbUnits();
2833 via->SetWidth( PADSTACK::ALL_LAYERS, kidiam );
2834 }
2835 else
2836 {
2837 double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
2838 annulus = eagleClamp( m_rules->rlMinViaOuter, annulus, m_rules->rlMaxViaOuter );
2839 kidiam = KiROUND( drillz + 2 * annulus );
2840 via->SetWidth( PADSTACK::ALL_LAYERS, kidiam );
2841 }
2842
2843 via->SetDrill( drillz );
2844
2845 // make sure the via diameter respects the restring rules
2846
2847 int via_width = via->GetWidth( PADSTACK::ALL_LAYERS );
2848
2849 if( !v.diam || via_width <= via->GetDrill() )
2850 {
2851 double annular_width = ( via_width - via->GetDrill() ) / 2.0;
2852 double clamped_annular_width = eagleClamp( m_rules->rlMinViaOuter,
2853 annular_width,
2854 m_rules->rlMaxViaOuter );
2855 via->SetWidth( PADSTACK::ALL_LAYERS, drillz + 2 * clamped_annular_width );
2856 }
2857
2858 if( kidiam < m_min_via )
2859 m_min_via = kidiam;
2860
2861 if( netclass && kidiam < netclass->GetViaDiameter() )
2862 netclass->SetViaDiameter( kidiam );
2863
2864 if( drillz < m_min_hole )
2865 m_min_hole = drillz;
2866
2867 if( netclass && drillz < netclass->GetViaDrill() )
2868 netclass->SetViaDrill( drillz );
2869
2870 if( ( kidiam - drillz ) / 2 < m_min_annulus )
2871 m_min_annulus = ( kidiam - drillz ) / 2;
2872
2873 if( layer_front_most == F_Cu && layer_back_most == B_Cu )
2874 {
2875 via->SetViaType( VIATYPE::THROUGH );
2876 }
2877 else if( layer_front_most == F_Cu || layer_back_most == B_Cu )
2878 {
2879 via->SetViaType( VIATYPE::BLIND );
2880 }
2881 else
2882 {
2883 via->SetViaType( VIATYPE::BURIED );
2884 }
2885
2886 VECTOR2I pos( kicad_x( v.x ), kicad_y( v.y ) );
2887
2888 via->SetLayerPair( layer_front_most, layer_back_most );
2889 via->SetPosition( pos );
2890 via->SetEnd( pos );
2891
2892 via->SetNetCode( netCode );
2893 }
2894
2895 m_xpath->pop();
2896 }
2897
2898 else if( itemName == wxT( "contactref" ) )
2899 {
2900 m_xpath->push( "contactref" );
2901 // <contactref element="RN1" pad="7"/>
2902
2903 const wxString& reference = netItem->GetAttribute( "element" );
2904 const wxString& pad = netItem->GetAttribute( "pad" );
2905 wxString key = makeKey( reference, pad ) ;
2906
2907 m_pads_to_nets[ key ] = ENET( netCode, netName );
2908
2909 m_xpath->pop();
2910
2911 sawPad = true;
2912 }
2913
2914 else if( itemName == wxT( "polygon" ) )
2915 {
2916 m_xpath->push( "polygon" );
2917 auto* zone = loadPolygon( netItem );
2918
2919 if( zone )
2920 {
2921 zones.push_back( zone );
2922
2923 if( !zone->GetIsRuleArea() )
2924 zone->SetNetCode( netCode );
2925 }
2926
2927 m_xpath->pop(); // "polygon"
2928 }
2929
2930 netItem = netItem->GetNext();
2931 }
2932
2933 if( zones.size() && !sawPad )
2934 {
2935 // KiCad does not support an unconnected zone with its own non-zero netcode,
2936 // but only when assigned netcode = 0 w/o a name...
2937 for( ZONE* zone : zones )
2938 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
2939
2940 // therefore omit this signal/net.
2941 }
2942
2943 //Next signal needs a new netCode
2944 netCode++;
2945
2946 // Get next signal
2947 net = net->GetNext();
2948 }
2949
2950 m_xpath->pop(); // "signals.signal"
2951}
2952
2953
2954std::map<wxString, PCB_LAYER_ID> PCB_IO_EAGLE::DefaultLayerMappingCallback(
2955 const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2956{
2957 std::map<wxString, PCB_LAYER_ID> layer_map;
2958
2959 for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
2960 {
2961 PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
2962 layer_map.emplace( layer.Name, layerId );
2963 }
2964
2965 return layer_map;
2966}
2967
2968
2969void PCB_IO_EAGLE::mapEagleLayersToKicad( bool aIsLibraryCache )
2970{
2971 std::vector<INPUT_LAYER_DESC> inputDescs;
2972
2973 for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
2974 {
2975 const ELAYER& eLayer = layerPair.second;
2976
2977 INPUT_LAYER_DESC layerDesc;
2978 std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
2979 defaultKicadLayer( eLayer.number, aIsLibraryCache );
2980
2981 if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
2982 continue; // Ignore unused copper layers
2983
2984 layerDesc.Name = eLayer.name;
2985
2986 inputDescs.push_back( layerDesc );
2987 }
2988
2989 if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
2990 dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
2991
2992 m_layer_map = m_layer_mapping_handler( inputDescs );
2993
2994 if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
2995 dynamic_cast<wxWindow*>( m_progressReporter )->Show();
2996}
2997
2998
3000{
3001 auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
3002 return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
3003}
3004
3005
3006std::tuple<PCB_LAYER_ID, LSET, bool> PCB_IO_EAGLE::defaultKicadLayer( int aEagleLayer,
3007 bool aIsLibraryCache ) const
3008{
3009 // eagle copper layer:
3010 if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
3011 {
3012 LSET copperLayers;
3013
3014 for( int copperLayer : m_cu_map )
3015 {
3016 if( copperLayer >= 0 )
3017 copperLayers[copperLayer] = true;
3018 }
3019
3020 return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
3021 }
3022
3023 int kiLayer = UNSELECTED_LAYER;
3024 bool required = false;
3025 LSET permittedLayers;
3026
3027 permittedLayers.set();
3028
3029 // translate non-copper eagle layer to pcbnew layer
3030 switch( aEagleLayer )
3031 {
3032 // Eagle says "Dimension" layer, but it's for board perimeter
3034 kiLayer = Edge_Cuts;
3035 required = true;
3036 permittedLayers = LSET( { Edge_Cuts } );
3037 break;
3038
3040 kiLayer = F_SilkS;
3041 break;
3043 kiLayer = B_SilkS;
3044 break;
3046 kiLayer = F_SilkS;
3047 break;
3049 kiLayer = B_SilkS;
3050 break;
3052 kiLayer = F_Fab;
3053 break;
3055 kiLayer = B_Fab;
3056 break;
3057 case EAGLE_LAYER::TSTOP:
3058 kiLayer = F_Mask;
3059 break;
3060 case EAGLE_LAYER::BSTOP:
3061 kiLayer = B_Mask;
3062 break;
3064 kiLayer = F_Paste;
3065 break;
3067 kiLayer = B_Paste;
3068 break;
3070 kiLayer = F_Mask;
3071 break;
3073 kiLayer = B_Mask;
3074 break;
3075 case EAGLE_LAYER::TGLUE:
3076 kiLayer = F_Adhes;
3077 break;
3078 case EAGLE_LAYER::BGLUE:
3079 kiLayer = B_Adhes;
3080 break;
3082 kiLayer = Cmts_User;
3083 break;
3085 kiLayer = Cmts_User;
3086 break;
3088 kiLayer = Cmts_User;
3089 break;
3090
3091 // Packages show the future chip pins on SMD parts using layer 51.
3092 // This is an area slightly smaller than the PAD/SMD copper area.
3093 // Carry those visual aids into the FOOTPRINT on the fabrication layer,
3094 // not silkscreen. This is perhaps not perfect, but there is not a lot
3095 // of other suitable paired layers
3096 case EAGLE_LAYER::TDOCU:
3097 kiLayer = F_Fab;
3098 break;
3099 case EAGLE_LAYER::BDOCU:
3100 kiLayer = B_Fab;
3101 break;
3102
3103 // these layers are defined as user layers. put them on ECO layers
3105 kiLayer = Eco1_User;
3106 break;
3108 kiLayer = Eco2_User;
3109 break;
3110
3111 // these will also appear in the ratsnest, so there's no need for a warning
3113 kiLayer = Dwgs_User;
3114 break;
3115
3117 kiLayer = F_CrtYd;
3118 break;
3120 kiLayer = B_CrtYd;
3121 break;
3122
3124 case EAGLE_LAYER::TTEST:
3125 case EAGLE_LAYER::BTEST:
3126 case EAGLE_LAYER::HOLES:
3127 default:
3128 if( aIsLibraryCache )
3129 kiLayer = UNDEFINED_LAYER;
3130 else
3131 kiLayer = UNSELECTED_LAYER;
3132
3133 break;
3134 }
3135
3136 return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
3137}
3138
3139
3140const wxString& PCB_IO_EAGLE::eagle_layer_name( int aLayer ) const
3141{
3142 static const wxString unknown( "unknown" );
3143 auto it = m_eagleLayers.find( aLayer );
3144 return it == m_eagleLayers.end() ? unknown : it->second.name;
3145}
3146
3147
3148int PCB_IO_EAGLE::eagle_layer_id( const wxString& aLayerName ) const
3149{
3150 static const int unknown = -1;
3151 auto it = m_eagleLayersIds.find( aLayerName );
3152 return it == m_eagleLayersIds.end() ? unknown : it->second;
3153}
3154
3155
3157{
3158 if( m_props )
3159 {
3160 UTF8 page_width;
3161 UTF8 page_height;
3162
3163 if( auto it = m_props->find( "page_width" ); it != m_props->end() )
3164 page_width = it->second;
3165
3166 if( auto it = m_props->find( "page_height" ); it != m_props->end() )
3167 page_height = it->second;
3168
3169 if( !page_width.empty() && !page_height.empty() )
3170 {
3171 BOX2I bbbox = m_board->GetBoardEdgesBoundingBox();
3172
3173 int w = atoi( page_width.c_str() );
3174 int h = atoi( page_height.c_str() );
3175
3176 int desired_x = ( w - bbbox.GetWidth() ) / 2;
3177 int desired_y = ( h - bbbox.GetHeight() ) / 2;
3178
3179 m_board->Move( VECTOR2I( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
3180 }
3181 }
3182}
3183
3184
3185long long PCB_IO_EAGLE::GetLibraryTimestamp( const wxString& aPath ) const
3186{
3187 // File hasn't been loaded yet.
3188 if( aPath.IsEmpty() )
3189 return wxDateTime::Now().GetValue().GetValue();
3190
3191 wxFileName fn( aPath );
3192
3193 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
3194 return fn.GetModificationTime().GetValue().GetValue();
3195 else
3196 return 0;
3197}
3198
3199
3200void PCB_IO_EAGLE::cacheLib( const wxString& aLibPath )
3201{
3202 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3203 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3204
3205 try
3206 {
3207 long long timestamp = GetLibraryTimestamp( aLibPath );
3208
3209 if( aLibPath != m_lib_path || m_timestamp != timestamp )
3210 {
3211 wxXmlNode* doc;
3212
3214
3215 // Set this before completion of loading, since we rely on it for
3216 // text of an exception. Delay setting m_mod_time until after successful load
3217 // however.
3218 m_lib_path = aLibPath;
3219
3220 // 8 bit "filename" should be encoded according to disk filename encoding,
3221 // (maybe this is current locale, maybe not, its a filesystem issue),
3222 // and is not necessarily utf8.
3223 string filename = (const char*) aLibPath.char_str( wxConvFile );
3224
3225 // Load the document
3226 wxFileName fn( filename );
3227 wxFFileInputStream stream( fn.GetFullPath() );
3228 wxXmlDocument xmlDocument;
3229
3230 if( !stream.IsOk() || !xmlDocument.Load( stream ) )
3231 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ), fn.GetFullPath() ) );
3232
3233 doc = xmlDocument.GetRoot();
3234
3235 wxXmlNode* drawing = MapChildren( doc )["drawing"];
3236 NODE_MAP drawingChildren = MapChildren( drawing );
3237
3238 // clear the cu map and then rebuild it.
3239 clear_cu_map();
3240
3241 m_xpath->push( "eagle.drawing.layers" );
3242 wxXmlNode* layers = drawingChildren["layers"];
3243 loadLayerDefs( layers );
3244 mapEagleLayersToKicad( true );
3245 m_xpath->pop();
3246
3247 m_xpath->push( "eagle.drawing.library" );
3248 wxXmlNode* library = drawingChildren["library"];
3249
3250 loadLibrary( library, nullptr );
3251 m_xpath->pop();
3252
3253 m_timestamp = timestamp;
3254 }
3255 }
3256 catch(...)
3257 {
3258 }
3259 // TODO: Handle exceptions
3260 // catch( file_parser_error fpe )
3261 // {
3262 // // for xml_parser_error, what() has the line number in it,
3263 // // but no byte offset. That should be an adequate error message.
3264 // THROW_IO_ERROR( fpe.what() );
3265 // }
3266 //
3267 // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
3268 // // so one catch should be OK for all errors.
3269 // catch( ptree_error pte )
3270 // {
3271 // string errmsg = pte.what();
3272 //
3273 // errmsg += " @\n";
3274 // errmsg += m_xpath->Contents();
3275 //
3276 // THROW_IO_ERROR( errmsg );
3277 // }
3278}
3279
3280
3281void PCB_IO_EAGLE::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
3282 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
3283{
3284 wxString errorMsg;
3285
3286 init( aProperties );
3287
3288 try
3289 {
3290 cacheLib( aLibraryPath );
3291 }
3292 catch( const IO_ERROR& ioe )
3293 {
3294 errorMsg = ioe.What();
3295 }
3296
3297 // Some of the files may have been parsed correctly so we want to add the valid files to
3298 // the library.
3299
3300 for( const auto& [ name, footprint ] : m_templates )
3301 aFootprintNames.Add( name );
3302
3303 if( !errorMsg.IsEmpty() && !aBestEfforts )
3304 THROW_IO_ERROR( errorMsg );
3305}
3306
3307
3308FOOTPRINT* PCB_IO_EAGLE::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
3309 bool aKeepUUID, const std::map<std::string, UTF8>* aProperties )
3310{
3311 init( aProperties );
3312 cacheLib( aLibraryPath );
3313 auto it = m_templates.find( aFootprintName );
3314
3315 if( it == m_templates.end() )
3316 return nullptr;
3317
3318 // Return a copy of the template
3319 FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate( IGNORE_PARENT_GROUP );
3320 copy->SetParent( nullptr );
3321 return copy;
3322}
3323
3324
3326{
3327 int minLayerCount = 2;
3328
3329 std::map<wxString, PCB_LAYER_ID>::const_iterator it;
3330
3331 for( it = m_layer_map.begin(); it != m_layer_map.end(); ++it )
3332 {
3333 PCB_LAYER_ID layerId = it->second;
3334
3335 if( !IsCopperLayer( layerId ) || layerId == F_Cu || layerId == B_Cu )
3336 continue;
3337
3338 int ordinal = CopperLayerToOrdinal( layerId );
3339
3340 if( ( ordinal + 2 ) > minLayerCount )
3341 minLayerCount = ordinal + 2;
3342 }
3343
3344 // Ensure the copper layers count is a multiple of 2
3345 // Pcbnew does not like boards with odd layers count
3346 // (these boards cannot exist. they actually have a even layers count)
3347 if( ( minLayerCount % 2 ) != 0 )
3348 minLayerCount++;
3349
3350 return minLayerCount;
3351}
const char * name
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition arraydim.h:31
constexpr int ARC_HIGH_DEF
Definition base_units.h:129
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
@ LT_SIGNAL
Definition board.h:187
#define DEFAULT_SILK_LINE_WIDTH
#define DEFAULT_EDGE_WIDTH
#define DEFAULT_LINE_WIDTH
#define DEFAULT_COURTYARD_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BASE_SET & set(size_t pos)
Definition base_set.h:116
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:284
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1082
constexpr coord_type GetY() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr coord_type GetX() const
Definition box2.h:207
constexpr size_type GetHeight() const
Definition box2.h:215
double AsDegrees() const
Definition eda_angle.h:116
void SetCenter(const VECTOR2I &aCenter)
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:136
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:178
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:168
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:541
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:585
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:402
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:426
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:395
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:293
bool IsMirrored() const
Definition eda_text.h:190
void SetKeepUpright(bool aKeepUpright)
Definition eda_text.cpp:434
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:279
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:308
VECTOR2I GetTextSize() const
Definition eda_text.h:261
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:418
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:322
void SetPosition(const VECTOR2I &aPos) override
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:350
EDA_ANGLE GetOrientation() const
Definition footprint.h:328
void SetOrientation(const EDA_ANGLE &aNewAngle)
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:777
std::deque< PAD * > & Pads()
Definition footprint.h:304
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:337
const LIB_ID & GetFPID() const
Definition footprint.h:349
void SetReference(const wxString &aReference)
Definition footprint.h:747
void SetValue(const wxString &aValue)
Definition footprint.h:768
PCB_FIELD & Reference()
Definition footprint.h:778
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
const wxString & GetValue() const
Definition footprint.h:763
const wxString & GetReference() const
Definition footprint.h:741
VECTOR2I GetPosition() const override
Definition footprint.h:325
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition io_base.cpp:71
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
virtual void RegisterCallback(LAYER_MAPPING_HANDLER aLayerMappingHandler)
Register a different handler to be called when mapping of input layers to KiCad layers occurs.
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:52
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:218
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:582
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:45
int GetViaDiameter() const
Definition netclass.h:133
int GetViaDrill() const
Definition netclass.h:141
int GetTrackWidth() const
Definition netclass.h:125
Handle the data for a net.
Definition netinfo.h:54
void SetNetClass(const std::shared_ptr< NETCLASS > &aNetClass)
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:247
void SetNetclassPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a netclass pattern assignment Calling this method will reset the effective netclass calculation ...
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
void SetNetclass(const wxString &netclassName, std::shared_ptr< NETCLASS > &netclass)
Sets the given netclass Calling user is responsible for resetting the effective netclass calculation ...
T & Get()
Return a reference to the value of the attribute assuming it is available.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:136
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition pad.h:585
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition pad.h:608
void SetPosition(const VECTOR2I &aPos) override
Definition pad.h:203
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_track.h:349
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:346
void SetUnits(EDA_UNITS aUnits)
void SetLineThickness(int aWidth)
virtual void SetEnd(const VECTOR2I &aPoint)
void SetPrecision(DIM_PRECISION aPrecision)
virtual void SetStart(const VECTOR2I &aPoint)
void SetOverrideText(const wxString &aValue)
For better understanding of the points that make a dimension:
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
A leader is a dimension-like object pointing to a specific point.
A radial dimension indicates either the radius or diameter of an arc or circle.
std::vector< ELAYER > ELAYERS
void packageSMD(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
Handles common pad properties.
void loadPlain(wxXmlNode *aPlain)
int m_min_trace
smallest trace we find on Load(), in BIU.
std::map< wxString, PCB_LAYER_ID > m_layer_map
Map of Eagle layers to KiCad layers.
void orientFPText(FOOTPRINT *aFootprint, const EELEMENT &e, PCB_TEXT *aFPText, const EATTR *aAttr)
VECTOR2I kicad_fontsize(const ECOORD &d, int aTextThickness) const
create a font size (fontz) from an eagle font size scalar and KiCad font thickness
std::map< wxString, PCB_LAYER_ID > DefaultLayerMappingCallback(const std::vector< INPUT_LAYER_DESC > &aInputLayerDescriptionVector)
Return the automapped layers.
bool checkHeader(const wxString &aFileName) const
void loadElements(wxXmlNode *aElements)
FOOTPRINT * makeFootprint(wxXmlNode *aPackage, const wxString &aPkgName)
Create a FOOTPRINT from an Eagle package.
int m_min_hole
smallest diameter hole we find on Load(), in BIU.
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
XPATH * m_xpath
keeps track of what we are working on within XML document during a Load().
unsigned m_totalCount
for progress reporting
void cacheLib(const wxString &aLibraryPath)
This PLUGIN only caches one footprint library, this determines which one.
int m_hole_count
generates unique footprint names from eagle "hole"s.
std::map< wxString, FOOTPRINT * > m_templates
is part of a FOOTPRINT factory that operates using copy construction.
std::map< wxString, int > m_eagleLayersIds
Eagle layer ids stored by layer name.
void mapEagleLayersToKicad(bool aIsLibraryCache=false)
Generate mapping between Eagle and KiCad layers.
std::map< wxString, std::shared_ptr< NETCLASS > > m_classMap
std::tuple< PCB_LAYER_ID, LSET, bool > defaultKicadLayer(int aEagleLayer, bool aIsLibraryCache=false) const
Get the default KiCad layer corresponding to an Eagle layer of the board, a set of sensible layer map...
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
void loadLibraries(wxXmlNode *aLibs)
void init(const std::map< std::string, UTF8 > *aProperties)
initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed.
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
int eagle_layer_id(const wxString &aLayerName) const
Get Eagle layer number by its name.
void loadAllSections(wxXmlNode *aDocument)
bool CanReadFootprint(const wxString &aFileName) const override
Checks if this PCB_IO can read a footprint from specified file or directory.
void packageWire(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
const wxString & eagle_layer_name(int aLayer) const
Get Eagle layer name by its number.
ELAYERS::const_iterator EITER
long long m_timestamp
void packageRectangle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
int kicad_y(const ECOORD &y) const
Convert an Eagle distance to a KiCad distance.
void loadClasses(wxXmlNode *aClasses)
std::map< int, ELAYER > m_eagleLayers
Eagle layer data stored by layer number.
wxString m_customRules
NET_MAP m_pads_to_nets
net list
void packageText(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
int getMinimumCopperLayerCount() const
Determines the minimum copper layer stackup count that includes all mapped layers.
void packageHole(FOOTPRINT *aFootprint, wxXmlNode *aTree, bool aCenter) const
void packagePolygon(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void centerBoard()
move the BOARD into the center of the page
unsigned m_lastProgressCount
int m_cu_map[17]
map eagle to KiCad, cu layers only.
ZONE * loadPolygon(wxXmlNode *aPolyNode)
Load a copper or keepout polygon and adds it to the board.
int m_min_via
smallest via we find on Load(), in BIU.
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
void packageCircle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void loadLibrary(wxXmlNode *aLib, const wxString *aLibName)
Load the Eagle "library" XML element, which can occur either under a "libraries" element (if a *....
void packagePad(FOOTPRINT *aFootprint, wxXmlNode *aTree)
void loadSignals(wxXmlNode *aSignals)
ERULES * m_rules
Eagle design rules.
PCB_LAYER_ID kicad_layer(int aLayer) const
Convert an Eagle layer to a KiCad layer.
void orientFootprintAndText(FOOTPRINT *aFootprint, const EELEMENT &e, const EATTR *aNameAttr, const EATTR *aValueAttr)
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
int m_min_annulus
smallest via annulus we find on Load(), in BIU.
void loadLayerDefs(wxXmlNode *aLayers)
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
void setKeepoutSettingsToZone(ZONE *aZone, int aLayer) const
void transferPad(const EPAD_COMMON &aEaglePad, PAD *aPad) const
Deletes the footprint templates list.
int kicad_x(const ECOORD &x) const
void loadDesignRules(wxXmlNode *aDesignRules)
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
unsigned m_doneCount
wxString m_lib_path
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h:326
virtual bool CanReadBoard(const wxString &aFileName) const
Checks if this PCB_IO can read the specified board file.
Definition pcb_io.cpp:42
PCB_IO(const wxString &aName)
Definition pcb_io.h:319
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:329
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:81
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:92
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition pcb_text.cpp:351
virtual void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:84
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:150
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_track.h:143
virtual void SetWidth(int aWidth)
Definition pcb_track.h:147
Container for project specific data.
Definition project.h:65
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:71
bool empty() const
Definition utf8.h:109
const char * c_str() const
Definition utf8.h:108
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:561
Keep track of what we are working on within a PTREE.
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:720
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition zone.cpp:1226
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:517
SHAPE_POLY_SET * Outline()
Definition zone.h:331
void NewHole()
Create a new hole on the zone; i.e., a new contour on the zone's outline.
Definition zone.h:569
void SetIsRuleArea(bool aEnable)
Definition zone.h:702
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:719
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition zone.cpp:1035
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:523
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:718
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:721
bool AppendCorner(VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition zone.cpp:1135
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:717
static int GetDefaultHatchPitch()
Definition zone.cpp:1285
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
NODE_MAP MapChildren(wxXmlNode *aCurrentNode)
Provide an easy access to the children of an XML node via their names.
wxString escapeName(const wxString &aNetName)
Translates Eagle special characters to their counterparts in KiCad.
wxString interpretText(const wxString &aText)
Interprets special characters in Eagle text and converts them to KiCAD notation.
VECTOR2I ConvertArcCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, double aAngle)
Convert an Eagle curve end to a KiCad center for S_ARC.
wxString convertDescription(wxString aDescr)
Converts Eagle's HTML description into KiCad description format.
OPTIONAL_XML_ATTRIBUTE< wxString > opt_wxString
std::unordered_map< wxString, wxXmlNode * > NODE_MAP
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE FULL_CIRCLE
Definition eda_angle.h:409
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
@ SEGMENT
Definition eda_shape.h:45
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID BoardLayerFromLegacyId(int aLegacyId)
Retrieve a layer ID from an integer converted from a legacy (pre-V9) enum value.
Definition layer_id.cpp:218
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
size_t CopperLayerToOrdinal(PCB_LAYER_ID aLayer)
Converts KiCad copper layer enum to an ordinal between the front and back layers.
Definition layer_ids.h:913
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ Cmts_User
Definition layer_ids.h:108
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ UNSELECTED_LAYER
Definition layer_ids.h:62
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Eco2_User
Definition layer_ids.h:110
@ B_SilkS
Definition layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
KICOMMON_API wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Return the string from aValue according to aUnits (inch, mm ...) for display.
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ RECTANGLE
Definition padstack.h:54
static wxString makeKey(const wxString &aFirst, const wxString &aSecond)
Assemble a two part key as a simple concatenation of aFirst and aSecond parts, using a separator.
#define DIMENSION_PRECISION
static int parseEagle(const wxString &aDistance)
Parse an eagle distance which is either mm, or mils if there is "mil" suffix.
static T eagleClamp(T aMin, T aValue, T aMax)
std::vector< ZONE * > ZONES
NET_MAP::const_iterator NET_MAP_CITER
@ THROUGH
Definition pcb_track.h:68
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
Parse an Eagle "attribute" XML element.
opt_double ratio
opt_wxString value
opt_ecoord size
opt_ecoord y
wxString name
opt_erot rot
opt_int align
opt_int display
opt_ecoord x
Eagle circle.
ECOORD x
ECOORD radius
ECOORD y
ECOORD width
wxString number
std::map< wxString, ECOORD > clearanceMap
wxString name
@ EU_MM
millimeters
@ EU_MIL
mils/thous
long long int value
Value expressed in nanometers.
int ToPcbUnits() const
Eagle dimension element.
opt_wxString dimensionType
opt_ecoord textsize
Eagle element element.
opt_erot rot
wxString name
wxString library
wxString package
opt_bool smashed
wxString value
opt_eurn library_urn
Eagle hole element.
ECOORD y
ECOORD drill
ECOORD x
wxString name
opt_bool active
Eagle net.
int netcode
Structure holding common properties for through-hole and SMD pads.
opt_bool thermals
opt_bool stop
wxString name
Eagle thru hole pad.
opt_ecoord diameter
opt_bool first
opt_ecoord drill
opt_int shape
Eagle polygon, without vertices which are parsed as needed.
opt_bool orphans
opt_int rank
opt_bool thermals
opt_ecoord spacing
static const int max_priority
ECOORD width
opt_ecoord isolate
Eagle XML rectangle in binary.
ECOORD x2
ECOORD y1
opt_erot rot
int layer
ECOORD y2
ECOORD x1
double degrees
bool spin
bool mirror
subset of eagle.drawing.board.designrules in the XML document
int psBottom
Shape of the bottom pads.
int psElongationLong
double mvStopFrame
solderpaste mask, expressed as percentage of the smaller pad/via dimension
double srRoundness
corner rounding ratio for SMD pads (percentage)
double rlMinViaOuter
minimum copper annulus on via
int mlMaxCreamFrame
solder paste mask, maximum size (Eagle mils, here nanometers)
int mlMinCreamFrame
solder paste mask, minimum size (Eagle mils, here nanometers)
int psTop
Shape of the top pads.
double rlMaxViaOuter
maximum copper annulus on via
void parse(wxXmlNode *aRules, std::function< void()> aCheckpoint)
percent over 100%.
double mdWireWire
wire to wire spacing I presume.
double rvPadTop
top pad size as percent of drill size
int srMaxRoundness
double rvViaOuter
copper annulus is this percent of via hole
double rlMinPadTop
minimum copper annulus on through hole pads
double mvCreamFrame
int psElongationOffset
the offset of the hole within the "long" pad.
int mlMinStopFrame
solder mask, minimum size (Eagle mils, here nanometers)
int srMinRoundness
corner rounding radius, maximum size (Eagle mils, here nanometers)
int mlMaxStopFrame
solder mask, maximum size (Eagle mils, here nanometers)
double rlMaxPadTop
maximum copper annulus on through hole pads
int psFirst
Shape of the first pads.
Eagle SMD pad.
opt_int roundness
ECOORD dx
int layer
opt_bool cream
ECOORD dy
Eagle text element.
opt_double ratio
wxString text
@ BOTTOM_CENTER
@ BOTTOM_RIGHT
@ CENTER_RIGHT
@ CENTER_LEFT
@ BOTTOM_LEFT
ECOORD y
ECOORD size
opt_erot rot
opt_int align
ECOORD x
int layer
wxString assetId
The unique asset identifier for the asset type.
Eagle vertex.
Eagle via.
opt_ecoord diam
ECOORD drill
< inclusive
ECOORD y
int layer_front_most
int layer_back_most
< extent
ECOORD x
Eagle wire.
ECOORD width
int layer
ECOORD x2
ECOORD y2
ECOORD x1
ECOORD y1
opt_double curve
range is -359.9..359.9
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_LAYER_ID AutoMapLayer
Best guess as to what the equivalent KiCad layer might be.
bool Required
Should we require the layer to be assigned?
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
wxString Name
Imported layer name as displayed in original application.
Implement a simple wrapper around runtime_error to isolate the errors thrown by the Eagle XML parser.
VECTOR3I v1(5, 5, 5)
VECTOR2I center
int radius
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
VECTOR2I v2(1, 0)
int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
Definition trigo.cpp:209
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
double DEG2RAD(double deg)
Definition trigo.h:166
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:102
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ FULL
pads are covered by copper
Definition zones.h:51
#define ZONE_THICKNESS_MIN_VALUE_MM
Definition zones.h:35