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