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 vertices.push_back( vertices[0] );
1571
1572 SHAPE_POLY_SET polygon;
1573 polygon.NewOutline();
1574
1575 for( size_t i = 0; i < vertices.size() - 1; i++ )
1576 {
1577 EVERTEX v1 = vertices[i];
1578
1579 // Append the corner
1580 polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) );
1581
1582 if( v1.curve )
1583 {
1584 EVERTEX v2 = vertices[i + 1];
1586 VECTOR2I( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
1587 double angle = DEG2RAD( *v1.curve );
1588 double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
1589 double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 ) + pow( center.y - kicad_y( v1.y ), 2 ) );
1590
1591 int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
1592 EDA_ANGLE( *v1.curve, DEGREES_T ) );
1593 double delta_angle = angle / segCount;
1594
1595 for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle ); a -= delta_angle )
1596 {
1597 polygon.Append( KiROUND( radius * cos( a ) ) + center.x,
1598 KiROUND( radius * sin( a ) ) + center.y );
1599 }
1600 }
1601 }
1602
1603 // Eagle traces the zone such that half of the pen width is outside the polygon.
1604 // We trace the zone such that the copper is completely inside.
1605 if( p.width.ToPcbUnits() > 0 )
1607
1608 if( polygon.OutlineCount() != 1 )
1609 {
1610 wxLogMessage( wxString::Format( _( "Skipping a polygon on layer '%s' (%d): outline count is not 1" ),
1612 p.layer ) );
1613
1614 return nullptr;
1615 }
1616
1617 zone->AddPolygon( polygon.COutline( 0 ) );
1618
1619 // If the pour is a cutout it needs to be set to a keepout
1620 if( p.pour == EPOLYGON::ECUTOUT )
1621 {
1622 zone->SetIsRuleArea( true );
1623 zone->SetDoNotAllowVias( false );
1624 zone->SetDoNotAllowTracks( false );
1625 zone->SetDoNotAllowPads( false );
1626 zone->SetDoNotAllowFootprints( false );
1627 zone->SetDoNotAllowZoneFills( true );
1628 zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
1629 }
1630 else if( p.pour == EPOLYGON::EHATCH )
1631 {
1632 int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * pcbIUScale.IU_PER_MILS;
1633
1634 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
1635 zone->SetHatchThickness( p.width.ToPcbUnits() );
1636 zone->SetHatchGap( spacing - p.width.ToPcbUnits() );
1637 zone->SetHatchOrientation( ANGLE_0 );
1638 }
1639
1640 // We divide the thickness by half because we are tracing _inside_ the zone outline
1641 // This means the radius of curvature will be twice the size for an equivalent EAGLE zone
1642 zone->SetMinThickness( std::max<int>( ZONE_THICKNESS_MIN_VALUE_MM * pcbIUScale.IU_PER_MM,
1643 p.width.ToPcbUnits() / 2 ) );
1644
1645 if( p.isolate )
1646 zone->SetLocalClearance( p.isolate->ToPcbUnits() );
1647 else
1648 zone->SetLocalClearance( 1 ); // @todo: set minimum clearance value based on board settings
1649
1650 // missing == yes per DTD.
1651 bool thermals = !p.thermals || *p.thermals;
1652 zone->SetPadConnection( thermals ? ZONE_CONNECTION::THERMAL : ZONE_CONNECTION::FULL );
1653
1654 if( thermals )
1655 {
1656 // FIXME: eagle calculates dimensions for thermal spokes
1657 // based on what the zone is connecting to.
1658 // (i.e. width of spoke is half of the smaller side of an smd pad)
1659 // This is a basic workaround
1660 zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
1661 zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 );
1662 }
1663
1664 int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
1665 zone->SetAssignedPriority( rank );
1666
1667 ZONE* zonePtr = zone.release();
1668 m_board->Add( zonePtr, ADD_MODE::APPEND );
1669
1670 return zonePtr;
1671}
1672
1673
1675 const EATTR* aNameAttr, const EATTR* aValueAttr )
1676{
1677 if( e.rot )
1678 {
1679 if( e.rot->mirror )
1680 {
1681 aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees + 180.0, DEGREES_T ) );
1682 aFootprint->Flip( aFootprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1683 }
1684 else
1685 {
1686 aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
1687 }
1688 }
1689
1690 orientFPText( aFootprint, e, &aFootprint->Reference(), aNameAttr );
1691 orientFPText( aFootprint, e, &aFootprint->Value(), aValueAttr );
1692}
1693
1694
1695void PCB_IO_EAGLE::orientFPText( FOOTPRINT* aFootprint, const EELEMENT& e, PCB_TEXT* aFPText,
1696 const EATTR* aAttr )
1697{
1698 // Smashed part ?
1699 if( aAttr )
1700 {
1701 // Yes
1702 const EATTR& a = *aAttr;
1703
1704 if( a.value )
1705 aFPText->SetText( *a.value );
1706
1707 if( a.x && a.y ) // std::optional
1708 {
1709 VECTOR2I pos( kicad_x( *a.x ), kicad_y( *a.y ) );
1710 aFPText->SetTextPos( pos );
1711 }
1712
1713 // Even though size and ratio are both optional, I am not seeing
1714 // a case where ratio is present but size is not.
1715 double ratio = 8;
1716
1717 if( a.ratio )
1718 ratio = *a.ratio;
1719
1720 VECTOR2I fontz = aFPText->GetTextSize();
1721 int textThickness = KiROUND( fontz.y * ratio / 100.0 );
1722
1723 aFPText->SetTextThickness( textThickness );
1724
1725 if( a.size )
1726 {
1727 fontz = kicad_fontsize( *a.size, textThickness );
1728 aFPText->SetTextSize( fontz );
1729 }
1730
1731 int align = ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
1732
1733 if( a.align )
1734 align = *a.align;
1735
1736 // The "rot" in a EATTR seems to be assumed to be zero if it is not
1737 // present, and this zero rotation becomes an override to the
1738 // package's text field. If they did not want zero, they specify
1739 // what they want explicitly.
1740 double degrees = a.rot ? a.rot->degrees : 0.0;
1741 int sign = 1;
1742 bool spin = false;
1743
1744 if( a.rot )
1745 {
1746 spin = a.rot->spin;
1747 sign = a.rot->mirror ? -1 : 1;
1748 aFPText->SetMirrored( a.rot->mirror );
1749 }
1750
1751 if( degrees == 90 || degrees == 0 || spin )
1752 {
1753 degrees *= sign;
1754 }
1755 else if( degrees == 180 )
1756 {
1757 degrees = 0;
1758 align = -align;
1759 }
1760 else if( degrees == 270 )
1761 {
1762 align = -align;
1763 degrees = sign * 90;
1764 }
1765 else
1766 {
1767 degrees = 90 - (sign * degrees);
1768 }
1769
1770 aFPText->SetTextAngle( EDA_ANGLE( degrees, DEGREES_T ) );
1771
1772 switch( align )
1773 {
1774 case ETEXT::TOP_RIGHT:
1777 break;
1778
1779 case ETEXT::BOTTOM_LEFT:
1782 break;
1783
1784 case ETEXT::TOP_LEFT:
1787 break;
1788
1792 break;
1793
1794 case ETEXT::TOP_CENTER:
1797 break;
1798
1802 break;
1803
1804 case ETEXT::CENTER:
1807 break;
1808
1809 case ETEXT::CENTER_LEFT:
1812 break;
1813
1817 break;
1818
1819 default:
1820 ;
1821 }
1822
1823 // Refine justification and rotation for mirrored texts
1824 if( aFPText->IsMirrored() && degrees < -90 && degrees >= -270 )
1825 {
1826 aFPText->SetTextAngle( EDA_ANGLE( 180+degrees, DEGREES_T ) );
1827
1828 if( aFPText->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
1830 else if( aFPText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
1832
1833 if( aFPText->GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
1835 else if( aFPText->GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
1837 }
1838 }
1839 else
1840 {
1841 // Part is not smash so use Lib default for NAME/VALUE
1842 // the text is per the original package, sans <attribute>.
1843 double degrees = aFPText->GetTextAngle().AsDegrees()
1844 + aFootprint->GetOrientation().AsDegrees();
1845
1846 // bottom-left is eagle default
1849
1850 if( !aFPText->IsMirrored() && abs( degrees ) >= 180 )
1851 {
1854 }
1855 }
1856}
1857
1858
1859FOOTPRINT* PCB_IO_EAGLE::makeFootprint( wxXmlNode* aPackage, const wxString& aPkgName )
1860{
1861 std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>( m_board );
1862
1863 LIB_ID fpID;
1864 fpID.Parse( aPkgName, true );
1865 m->SetFPID( fpID );
1866
1867 // Get the first package item and iterate
1868 wxXmlNode* packageItem = aPackage->GetChildren();
1869
1870 // layer 27 is default layer for tValues
1871 // set default layer for created footprint
1872 PCB_LAYER_ID layer = kicad_layer( 27 );
1873 m.get()->Value().SetLayer( layer );
1874
1875 while( packageItem )
1876 {
1877 const wxString& itemName = packageItem->GetName();
1878
1879 if( itemName == wxT( "description" ) )
1880 {
1881 wxString descr = convertDescription( UnescapeHTML( packageItem->GetNodeContent() ) );
1882 m->SetLibDescription( descr );
1883 }
1884 else if( itemName == wxT( "wire" ) )
1885 packageWire( m.get(), packageItem );
1886 else if( itemName == wxT( "pad" ) )
1887 packagePad( m.get(), packageItem );
1888 else if( itemName == wxT( "text" ) )
1889 packageText( m.get(), packageItem );
1890 else if( itemName == wxT( "rectangle" ) )
1891 packageRectangle( m.get(), packageItem );
1892 else if( itemName == wxT( "polygon" ) )
1893 packagePolygon( m.get(), packageItem );
1894 else if( itemName == wxT( "circle" ) )
1895 packageCircle( m.get(), packageItem );
1896 else if( itemName == wxT( "hole" ) )
1897 packageHole( m.get(), packageItem, false );
1898 else if( itemName == wxT( "smd" ) )
1899 packageSMD( m.get(), packageItem );
1900
1901 packageItem = packageItem->GetNext();
1902 }
1903
1904 return m.release();
1905}
1906
1907
1908void PCB_IO_EAGLE::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
1909{
1910 EWIRE w( aTree );
1911 PCB_LAYER_ID layer = kicad_layer( w.layer );
1912 VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
1913 VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
1914 int width = w.width.ToPcbUnits();
1915
1916 if( layer == UNDEFINED_LAYER )
1917 {
1918 wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) was not mapped" ),
1920 w.layer ) );
1921 return;
1922 }
1923
1924 // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
1925 if( width <= 0 )
1926 {
1927 BOARD* board = aFootprint->GetBoard();
1928
1929 if( board )
1930 {
1931 width = board->GetDesignSettings().GetLineThickness( layer );
1932 }
1933 else
1934 {
1935 // When loading footprint libraries, there is no board so use the default KiCad
1936 // line widths.
1937 switch( layer )
1938 {
1939 case Edge_Cuts: width = pcbIUScale.mmToIU( DEFAULT_EDGE_WIDTH ); break;
1940
1941 case F_SilkS:
1942 case B_SilkS: width = pcbIUScale.mmToIU( DEFAULT_SILK_LINE_WIDTH ); break;
1943
1944 case F_CrtYd:
1945 case B_CrtYd: width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ); break;
1946
1947 default: width = pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ); break;
1948 }
1949 }
1950 }
1951
1952 // FIXME: the cap attribute is ignored because KiCad can't create lines with flat ends.
1953 PCB_SHAPE* dwg;
1954
1955 if( !w.curve )
1956 {
1957 dwg = new PCB_SHAPE( aFootprint, SHAPE_T::SEGMENT );
1958
1959 dwg->SetStart( start );
1960 dwg->SetEnd( end );
1961 }
1962 else
1963 {
1964 dwg = new PCB_SHAPE( aFootprint, SHAPE_T::ARC );
1965 VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
1966
1967 dwg->SetCenter( center );
1968 dwg->SetStart( start );
1969 dwg->SetArcAngleAndEnd( -EDA_ANGLE( *w.curve, DEGREES_T ), true ); // KiCad rotates the other way
1970 }
1971
1972 dwg->SetLayer( layer );
1973 dwg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
1974 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
1975 dwg->Move( aFootprint->GetPosition() );
1976
1977 aFootprint->Add( dwg );
1978}
1979
1980
1981void PCB_IO_EAGLE::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
1982{
1983 // this is thru hole technology here, no SMDs
1984 EPAD e( aTree );
1985 int shape = EPAD::UNDEF;
1986 int eagleDrillz = e.drill ? e.drill->ToPcbUnits() : 0;
1987
1988 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
1989 transferPad( e, pad.get() );
1990
1991 if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
1992 shape = m_rules->psFirst;
1993 else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
1994 shape = m_rules->psTop;
1995 else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
1996 shape = m_rules->psBottom;
1997
1998 pad->SetDrillSize( VECTOR2I( eagleDrillz, eagleDrillz ) );
1999 pad->SetLayerSet( LSET::AllCuMask() );
2000
2001 if( eagleDrillz < m_min_hole )
2002 m_min_hole = eagleDrillz;
2003
2004 // Solder mask
2005 if( !e.stop || *e.stop == true ) // enabled by default
2006 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
2007
2008 if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
2009 e.shape = shape;
2010
2011 if( e.shape )
2012 {
2013 switch( *e.shape )
2014 {
2015 case EPAD::ROUND:
2017 break;
2018
2019 case EPAD::OCTAGON:
2021 pad->SetChamferPositions( PADSTACK::ALL_LAYERS, RECT_CHAMFER_ALL );
2022 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, 1 - M_SQRT1_2 ); // Regular polygon
2023 break;
2024
2025 case EPAD::LONG:
2027 break;
2028
2029 case EPAD::SQUARE:
2031 break;
2032
2033 case EPAD::OFFSET:
2035 break;
2036 }
2037 }
2038 else
2039 {
2040 // if shape is not present, our default is circle and that matches their default "round"
2041 }
2042
2043 if( e.diameter && e.diameter->value > 0 )
2044 {
2045 int diameter = e.diameter->ToPcbUnits();
2046 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( diameter, diameter ) );
2047 }
2048 else
2049 {
2050 double drillz = pad->GetDrillSize().x;
2051 double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
2052 annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
2053 int diameter = KiROUND( drillz + 2 * annulus );
2054 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( diameter, diameter ) );
2055 }
2056
2057 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::OVAL )
2058 {
2059 // The Eagle "long" pad is wider than it is tall; m_elongation is percent elongation
2060 VECTOR2I sz = pad->GetSize( PADSTACK::ALL_LAYERS );
2061 sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
2062 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
2063
2064 if( e.shape && *e.shape == EPAD::OFFSET )
2065 {
2066 int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
2067 pad->SetOffset( PADSTACK::ALL_LAYERS, VECTOR2I( offset, 0 ) );
2068 }
2069 }
2070
2071 if( e.rot )
2072 pad->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
2073
2074 // Eagle spokes are always '+'
2075 pad->SetThermalSpokeAngle( ANGLE_0 );
2076
2077 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 && pad->HasHole() )
2078 {
2079 aFootprint->Add( pad.release() );
2080 }
2081 else
2082 {
2083 wxFileName fileName( m_lib_path );
2084
2085 if( m_board)
2086 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ), m_board->GetFileName() );
2087 else
2088 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ), fileName.GetFullName() );
2089 }
2090}
2091
2092
2093void PCB_IO_EAGLE::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2094{
2095 ETEXT t( aTree );
2096 PCB_LAYER_ID layer = kicad_layer( t.layer );
2097
2098 if( layer == UNDEFINED_LAYER )
2099 {
2100 wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) was not mapped" ),
2102 t.layer ) );
2103 return;
2104 }
2105
2106 PCB_TEXT* textItem;
2107
2108 if( t.text.Upper() == wxT( ">NAME" ) && aFootprint->GetReference().IsEmpty() )
2109 {
2110 textItem = &aFootprint->Reference();
2111
2112 textItem->SetText( wxT( "REF**" ) );
2113 }
2114 else if( t.text.Upper() == wxT( ">VALUE" ) && aFootprint->GetValue().IsEmpty() )
2115 {
2116 textItem = &aFootprint->Value();
2117
2118 textItem->SetText( aFootprint->GetFPID().GetLibItemName() );
2119 }
2120 else
2121 {
2122 textItem = new PCB_TEXT( aFootprint );
2123 aFootprint->Add( textItem );
2124
2125 textItem->SetText( interpretText( t.text ) );
2126 }
2127
2128 VECTOR2I pos( kicad_x( t.x ), kicad_y( t.y ) );
2129
2130 textItem->SetPosition( pos );
2131 textItem->SetLayer( layer );
2132
2133 double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
2134 int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100.0 );
2135
2136 textItem->SetTextThickness( textThickness );
2137 textItem->SetTextSize( kicad_fontsize( t.size, textThickness ) );
2138 textItem->SetKeepUpright( false );
2139
2140 int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
2141
2142 // An eagle package is never rotated, the DTD does not allow it.
2143 // angle -= aFootprint->GetOrienation();
2144
2145 if( t.rot )
2146 {
2147 int sign = t.rot->mirror ? -1 : 1;
2148 textItem->SetMirrored( t.rot->mirror );
2149
2150 double degrees = t.rot->degrees;
2151 textItem->SetTextAngle( EDA_ANGLE( sign * degrees, DEGREES_T ) );
2152 }
2153
2154 switch( align )
2155 {
2156 case ETEXT::CENTER:
2159 break;
2160
2161 case ETEXT::CENTER_LEFT:
2164 break;
2165
2169 break;
2170
2171 case ETEXT::TOP_CENTER:
2174 break;
2175
2176 case ETEXT::TOP_LEFT:
2179 break;
2180
2181 case ETEXT::TOP_RIGHT:
2184 break;
2185
2189 break;
2190
2191 case ETEXT::BOTTOM_LEFT:
2194 break;
2195
2199 break;
2200 }
2201}
2202
2203
2204void PCB_IO_EAGLE::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2205{
2206 ERECT r( aTree );
2207
2210 {
2211 ZONE* zone = new ZONE( aFootprint );
2212 aFootprint->Add( zone, ADD_MODE::APPEND );
2213
2215
2216 const int outlineIdx = -1; // this is the id of the copper zone main outline
2217 zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
2218 zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
2219 zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
2220 zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
2221
2222 if( r.rot )
2223 {
2224 VECTOR2I center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
2225 ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
2226 zone->Rotate( center, EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
2227 }
2228
2231 }
2232 else
2233 {
2234 PCB_LAYER_ID layer = kicad_layer( r.layer );
2235
2236 if( layer == UNDEFINED_LAYER )
2237 {
2238 wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) was not mapped" ),
2240 r.layer ) );
2241 return;
2242 }
2243
2244 PCB_SHAPE* dwg = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
2245
2246 aFootprint->Add( dwg );
2247
2248 dwg->SetLayer( layer );
2249 dwg->SetStroke( STROKE_PARAMS( 0 ) );
2250 dwg->SetFilled( true );
2251
2252 std::vector<VECTOR2I> pts;
2253
2254 VECTOR2I start( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
2255 VECTOR2I end( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
2256
2257 pts.push_back( start );
2258 pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
2259 pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
2260 pts.push_back( end );
2261
2262 dwg->SetPolyPoints( pts );
2263
2264 if( r.rot )
2265 dwg->Rotate( dwg->GetCenter(), EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
2266
2267 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
2268 dwg->Move( aFootprint->GetPosition() );
2269 }
2270}
2271
2272
2273void PCB_IO_EAGLE::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2274{
2275 EPOLYGON p( aTree );
2276
2277 std::vector<VECTOR2I> pts;
2278
2279 // Get the first vertex and iterate
2280 wxXmlNode* vertex = aTree->GetChildren();
2281 std::vector<EVERTEX> vertices;
2282
2283 // Create a circular vector of vertices
2284 // The "curve" parameter indicates a curve from the current
2285 // to the next vertex, so we keep the first at the end as well
2286 // to allow the curve to link back
2287 while( vertex )
2288 {
2289 if( vertex->GetName() == wxT( "vertex" ) )
2290 vertices.emplace_back( vertex );
2291
2292 vertex = vertex->GetNext();
2293 }
2294
2295 vertices.push_back( vertices[0] );
2296
2297 for( size_t i = 0; i < vertices.size() - 1; i++ )
2298 {
2299 EVERTEX v1 = vertices[i];
2300
2301 // Append the corner
2302 pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
2303
2304 if( v1.curve )
2305 {
2306 EVERTEX v2 = vertices[i + 1];
2308 VECTOR2I( kicad_x( v2.x ), kicad_y( v2.y ) ),
2309 *v1.curve );
2310 double angle = DEG2RAD( *v1.curve );
2311 double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
2312 double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 ) + pow( center.y - kicad_y( v1.y ), 2 ) );
2313
2314 // Don't allow a zero-radius curve
2315 if( KiROUND( radius ) == 0 )
2316 radius = 1.0;
2317
2318 int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
2319 EDA_ANGLE( *v1.curve, DEGREES_T ) );
2320 double delta = angle / segCount;
2321
2322 for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
2323 {
2324 pts.push_back( VECTOR2I( KiROUND( radius * cos( a ) ),
2325 KiROUND( radius * sin( a ) ) ) + center );
2326 }
2327 }
2328 }
2329
2330 PCB_LAYER_ID layer = kicad_layer( p.layer );
2331
2332 if( ( p.pour == EPOLYGON::ECUTOUT && layer != UNDEFINED_LAYER )
2336 {
2337 ZONE* zone = new ZONE( aFootprint );
2338 aFootprint->Add( zone, ADD_MODE::APPEND );
2339
2341
2342 SHAPE_LINE_CHAIN outline( pts );
2343 outline.SetClosed( true );
2344 zone->Outline()->AddOutline( outline );
2345
2348 }
2349 else
2350 {
2351 if( layer == UNDEFINED_LAYER )
2352 {
2353 wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) was not mapped" ),
2355 p.layer ) );
2356 return;
2357 }
2358
2359 PCB_SHAPE* dwg = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
2360
2361 aFootprint->Add( dwg );
2362
2363 dwg->SetStroke( STROKE_PARAMS( 0 ) );
2364 dwg->SetFilled( true );
2365 dwg->SetLayer( layer );
2366
2367 dwg->SetPolyPoints( pts );
2368 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
2369 dwg->Move( aFootprint->GetPosition() );
2371 ARC_HIGH_DEF );
2372 }
2373}
2374
2375
2376void PCB_IO_EAGLE::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2377{
2378 ECIRCLE e( aTree );
2379
2380 int width = e.width.ToPcbUnits();
2381 int radius = e.radius.ToPcbUnits();
2382
2386 {
2387 ZONE* zone = new ZONE( aFootprint );
2388 aFootprint->Add( zone, ADD_MODE::APPEND );
2389
2391
2392 // approximate circle as polygon
2393 VECTOR2I center( kicad_x( e.x ), kicad_y( e.y ) );
2394 int outlineRadius = radius + ( width / 2 );
2395 int segsInCircle = GetArcToSegmentCount( outlineRadius, ARC_HIGH_DEF, FULL_CIRCLE );
2396 EDA_ANGLE delta = ANGLE_360 / segsInCircle;
2397
2398 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
2399 {
2400 VECTOR2I rotatedPoint( outlineRadius, 0 );
2401 RotatePoint( rotatedPoint, angle );
2402 zone->AppendCorner( center + rotatedPoint, -1 );
2403 }
2404
2405 if( width > 0 )
2406 {
2407 zone->NewHole();
2408 int innerRadius = radius - ( width / 2 );
2409 segsInCircle = GetArcToSegmentCount( innerRadius, ARC_HIGH_DEF, FULL_CIRCLE );
2410 delta = ANGLE_360 / segsInCircle;
2411
2412 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
2413 {
2414 VECTOR2I rotatedPoint( innerRadius, 0 );
2415 RotatePoint( rotatedPoint, angle );
2416 zone->AppendCorner( center + rotatedPoint, 0 );
2417 }
2418 }
2419
2422 }
2423 else
2424 {
2425 PCB_LAYER_ID layer = kicad_layer( e.layer );
2426
2427 if( layer == UNDEFINED_LAYER )
2428 {
2429 wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) was not mapped" ),
2431 e.layer ) );
2432 return;
2433 }
2434
2435 PCB_SHAPE* gr = new PCB_SHAPE( aFootprint, SHAPE_T::CIRCLE );
2436
2437 // width == 0 means filled circle
2438 if( width <= 0 )
2439 {
2440 width = radius;
2441 radius = radius / 2;
2442 gr->SetFilled( true );
2443 }
2444
2445 aFootprint->Add( gr );
2446 gr->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
2447
2448 switch( (int) layer )
2449 {
2450 case UNDEFINED_LAYER:
2451 layer = Cmts_User;
2452 break;
2453 default:
2454 break;
2455 }
2456
2457 gr->SetLayer( layer );
2458 gr->SetStart( VECTOR2I( kicad_x( e.x ), kicad_y( e.y ) ) );
2459 gr->SetEnd( VECTOR2I( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
2460 gr->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
2461 gr->Move( aFootprint->GetPosition() );
2462 }
2463}
2464
2465
2466void PCB_IO_EAGLE::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
2467{
2468 EHOLE e( aTree );
2469
2470 if( e.drill.value == 0 )
2471 return;
2472
2473 // we add a PAD_ATTRIB::NPTH pad to this footprint.
2474 PAD* pad = new PAD( aFootprint );
2475 aFootprint->Add( pad );
2476
2478 pad->SetAttribute( PAD_ATTRIB::NPTH );
2479
2480 // Mechanical purpose only:
2481 // no offset, no net name, no pad name allowed
2482 // pad->SetOffset( VECTOR2I( 0, 0 ) );
2483 // pad->SetNumber( wxEmptyString );
2484
2485 VECTOR2I padpos( kicad_x( e.x ), kicad_y( e.y ) );
2486
2487 if( aCenter )
2488 {
2489 aFootprint->SetPosition( padpos );
2490 pad->SetPosition( padpos );
2491 }
2492 else
2493 {
2494 pad->SetPosition( padpos + aFootprint->GetPosition() );
2495 }
2496
2497 VECTOR2I sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
2498
2499 pad->SetDrillSize( sz );
2500 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
2501
2502 pad->SetLayerSet( LSET( LSET::AllCuMask() ).set( B_Mask ).set( F_Mask ) );
2503}
2504
2505
2506void PCB_IO_EAGLE::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
2507{
2508 ESMD e( aTree );
2509 PCB_LAYER_ID layer = kicad_layer( e.layer );
2510
2511 if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
2512 return;
2513
2514 PAD* pad = new PAD( aFootprint );
2515 aFootprint->Add( pad );
2516 transferPad( e, pad );
2517
2519 pad->SetAttribute( PAD_ATTRIB::SMD );
2520
2521 VECTOR2I padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
2522 pad->SetSize( PADSTACK::ALL_LAYERS, padSize );
2523 pad->SetLayer( layer );
2524
2525 const LSET front( { F_Cu, F_Paste, F_Mask } );
2526 const LSET back( { B_Cu, B_Paste, B_Mask } );
2527
2528 if( layer == F_Cu )
2529 pad->SetLayerSet( front );
2530 else if( layer == B_Cu )
2531 pad->SetLayerSet( back );
2532
2533 int minPadSize = std::min( padSize.x, padSize.y );
2534
2535 // Rounded rectangle pads
2536 int roundRadius = eagleClamp( m_rules->srMinRoundness * 2,
2537 (int) ( minPadSize * m_rules->srRoundness ),
2538 m_rules->srMaxRoundness * 2 );
2539
2540 if( e.roundness || roundRadius > 0 )
2541 {
2542 double roundRatio = (double) roundRadius / minPadSize / 2.0;
2543
2544 // Eagle uses a different definition of roundness, hence division by 200
2545 if( e.roundness )
2546 roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
2547
2549 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, roundRatio );
2550 }
2551
2552 if( e.rot )
2553 pad->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
2554
2555 // Eagle spokes are always '+'
2556 pad->SetThermalSpokeAngle( ANGLE_0 );
2557
2558 pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
2559 (int) ( m_rules->mvCreamFrame * minPadSize ),
2560 m_rules->mlMaxCreamFrame ) );
2561
2562 // Solder mask
2563 if( e.stop && *e.stop == false ) // enabled by default
2564 {
2565 if( layer == F_Cu )
2566 pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
2567 else if( layer == B_Cu )
2568 pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
2569 }
2570
2571 // Solder paste (only for SMD pads)
2572 if( e.cream && *e.cream == false ) // enabled by default
2573 {
2574 if( layer == F_Cu )
2575 pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
2576 else if( layer == B_Cu )
2577 pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
2578 }
2579}
2580
2581
2582void PCB_IO_EAGLE::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
2583{
2584 aPad->SetNumber( aEaglePad.name );
2585
2586 VECTOR2I padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
2587
2588 // Solder mask
2589 const VECTOR2I& padSize( aPad->GetSize( PADSTACK::ALL_LAYERS ) );
2590
2591 aPad->SetLocalSolderMaskMargin( eagleClamp( m_rules->mlMinStopFrame,
2592 (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
2593 m_rules->mlMaxStopFrame ) );
2594
2595 // Solid connection to copper zones
2596 if( aEaglePad.thermals && !*aEaglePad.thermals )
2598
2599 FOOTPRINT* footprint = aPad->GetParentFootprint();
2600 wxCHECK( footprint, /* void */ );
2601 RotatePoint( padPos, footprint->GetOrientation() );
2602 aPad->SetPosition( padPos + footprint->GetPosition() );
2603}
2604
2605
2607{
2608 for( const auto& [ name, footprint ] : m_templates )
2609 {
2610 footprint->SetParent( nullptr );
2611 delete footprint;
2612 }
2613
2614 m_templates.clear();
2615}
2616
2617
2618void PCB_IO_EAGLE::loadClasses( wxXmlNode* aClasses )
2619{
2620 // Eagle board DTD defines the "classes" element as 0 or 1.
2621 if( !aClasses )
2622 return;
2623
2624 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2625
2626 m_xpath->push( "classes.class", "number" );
2627
2628 std::vector<ECLASS> eClasses;
2629 wxXmlNode* classNode = aClasses->GetChildren();
2630
2631 while( classNode )
2632 {
2633 checkpoint();
2634
2635 ECLASS eClass( classNode );
2636 std::shared_ptr<NETCLASS> netclass;
2637
2638 if( eClass.name.CmpNoCase( wxT( "default" ) ) == 0 )
2639 {
2640 netclass = bds.m_NetSettings->GetDefaultNetclass();
2641 }
2642 else
2643 {
2644 netclass.reset( new NETCLASS( eClass.name ) );
2645 bds.m_NetSettings->SetNetclass( eClass.name, netclass );
2646 }
2647
2648 netclass->SetTrackWidth( INT_MAX );
2649 netclass->SetViaDiameter( INT_MAX );
2650 netclass->SetViaDrill( INT_MAX );
2651
2652 eClasses.emplace_back( eClass );
2653 m_classMap[ eClass.number ] = netclass;
2654
2655 // Set netclass clearance to the clearance-to-default-class value
2656 auto clearanceToDefaultIt = eClass.clearanceMap.find( wxT( "0" ) );
2657
2658 if( clearanceToDefaultIt != eClass.clearanceMap.end() )
2659 {
2660 netclass->SetClearance( clearanceToDefaultIt->second.ToPcbUnits() );
2661 }
2662
2663 // Get next class
2664 classNode = classNode->GetNext();
2665 }
2666
2667 m_customRules = wxT( "(version 1)" );
2668
2669 for( ECLASS& eClass : eClasses )
2670 {
2671 for( const auto& [className, pt] : eClass.clearanceMap )
2672 {
2673 // Skip clearances to default class (class "0") - these are handled via netclass clearances
2674 if( className == wxT( "0" ) )
2675 continue;
2676
2677 if( m_classMap[className] != nullptr )
2678 {
2679 wxString rule;
2680 rule.Printf( wxT( "(rule \"class %s:%s\"\n"
2681 " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
2682 " (constraint clearance (min %smm)))\n" ),
2683 eClass.number,
2684 className,
2685 eClass.name,
2686 m_classMap[className]->GetName(),
2688
2689 m_customRules += wxT( "\n" ) + rule;
2690 }
2691 }
2692 }
2693
2694 m_xpath->pop(); // "classes.class"
2695}
2696
2697
2698void PCB_IO_EAGLE::loadSignals( wxXmlNode* aSignals )
2699{
2700 // Eagle board DTD defines the "signals" element as 0 or 1.
2701 if( !aSignals )
2702 return;
2703
2704 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2705
2706 ZONES zones; // per net
2707 int netCode = 1;
2708
2709 m_xpath->push( "signals.signal", "name" );
2710
2711 // Get the first signal and iterate
2712 wxXmlNode* net = aSignals->GetChildren();
2713
2714 while( net )
2715 {
2716 checkpoint();
2717
2718 bool sawPad = false;
2719
2720 zones.clear();
2721
2722 const wxString& netName = escapeName( net->GetAttribute( "name" ) );
2723 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, netName, netCode );
2724 std::shared_ptr<NETCLASS> netclass;
2725
2726 if( net->HasAttribute( "class" ) )
2727 {
2728 auto netclassIt = m_classMap.find( net->GetAttribute( "class" ) );
2729
2730 if( netclassIt != m_classMap.end() )
2731 {
2732 bds.m_NetSettings->SetNetclassPatternAssignment( netName, netclassIt->second->GetName() );
2733 netInfo->SetNetClass( netclassIt->second );
2734 netclass = netclassIt->second;
2735 }
2736 }
2737
2738 m_board->Add( netInfo );
2739
2740 m_xpath->Value( netName.c_str() );
2741
2742 // Get the first net item and iterate
2743 wxXmlNode* netItem = net->GetChildren();
2744
2745 // (contactref | polygon | wire | via)*
2746 while( netItem )
2747 {
2748 const wxString& itemName = netItem->GetName();
2749
2750 if( itemName == wxT( "wire" ) )
2751 {
2752 m_xpath->push( "wire" );
2753
2754 EWIRE w( netItem );
2755 PCB_LAYER_ID layer = kicad_layer( w.layer );
2756
2757 if( IsCopperLayer( layer ) )
2758 {
2759 VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
2760 VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
2761
2762 int width = w.width.ToPcbUnits();
2763
2764 if( width < m_min_trace )
2765 m_min_trace = width;
2766
2767 if( netclass && width < netclass->GetTrackWidth() )
2768 netclass->SetTrackWidth( width );
2769
2770 if( w.curve )
2771 {
2772 VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
2773 double radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
2774 pow( center.y - kicad_y( w.y1 ), 2 ) );
2775 VECTOR2I mid = CalcArcMid( start, end, center, true );
2776 VECTOR2I otherMid = CalcArcMid( start, end, center, false );
2777
2778 double radiusA = ( mid - center ).EuclideanNorm();
2779 double radiusB = ( otherMid - center ).EuclideanNorm();
2780
2781 if( abs( radiusA - radius ) > abs( radiusB - radius ) )
2782 std::swap( mid, otherMid );
2783
2784 PCB_ARC* arc = new PCB_ARC( m_board );
2785
2786 arc->SetPosition( start );
2787 arc->SetMid( mid );
2788 arc->SetEnd( end );
2789 arc->SetWidth( width );
2790 arc->SetLayer( layer );
2791 arc->SetNetCode( netCode );
2792
2793 m_board->Add( arc );
2794 }
2795 else
2796 {
2797 PCB_TRACK* track = new PCB_TRACK( m_board );
2798
2799 track->SetPosition( start );
2800 track->SetEnd( VECTOR2I( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
2801 track->SetWidth( width );
2802 track->SetLayer( layer );
2803 track->SetNetCode( netCode );
2804
2805 m_board->Add( track );
2806 }
2807 }
2808 else
2809 {
2810 // put non copper wires where the sun don't shine.
2811 }
2812
2813 m_xpath->pop();
2814 }
2815 else if( itemName == wxT( "via" ) )
2816 {
2817 m_xpath->push( "via" );
2818 EVIA v( netItem );
2819
2821 std::swap( v.layer_front_most, v.layer_back_most );
2822
2823 PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most );
2824 PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most );
2825
2826 if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most )
2827 && layer_front_most != layer_back_most )
2828 {
2829 int kidiam;
2830 int drillz = v.drill.ToPcbUnits();
2831 PCB_VIA* via = new PCB_VIA( m_board );
2832 m_board->Add( via );
2833
2834 if( v.diam )
2835 {
2836 kidiam = v.diam->ToPcbUnits();
2837 via->SetWidth( PADSTACK::ALL_LAYERS, kidiam );
2838 }
2839 else
2840 {
2841 double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
2842 annulus = eagleClamp( m_rules->rlMinViaOuter, annulus, m_rules->rlMaxViaOuter );
2843 kidiam = KiROUND( drillz + 2 * annulus );
2844 via->SetWidth( PADSTACK::ALL_LAYERS, kidiam );
2845 }
2846
2847 via->SetDrill( drillz );
2848
2849 // make sure the via diameter respects the restring rules
2850
2851 int via_width = via->GetWidth( PADSTACK::ALL_LAYERS );
2852
2853 if( !v.diam || via_width <= via->GetDrill() )
2854 {
2855 double annular_width = ( via_width - via->GetDrill() ) / 2.0;
2856 double clamped_annular_width = eagleClamp( m_rules->rlMinViaOuter,
2857 annular_width,
2858 m_rules->rlMaxViaOuter );
2859 via->SetWidth( PADSTACK::ALL_LAYERS, drillz + 2 * clamped_annular_width );
2860 }
2861
2862 if( kidiam < m_min_via )
2863 m_min_via = kidiam;
2864
2865 if( netclass && kidiam < netclass->GetViaDiameter() )
2866 netclass->SetViaDiameter( kidiam );
2867
2868 if( drillz < m_min_hole )
2869 m_min_hole = drillz;
2870
2871 if( netclass && drillz < netclass->GetViaDrill() )
2872 netclass->SetViaDrill( drillz );
2873
2874 if( ( kidiam - drillz ) / 2 < m_min_annulus )
2875 m_min_annulus = ( kidiam - drillz ) / 2;
2876
2877 if( layer_front_most == F_Cu && layer_back_most == B_Cu )
2878 {
2879 via->SetViaType( VIATYPE::THROUGH );
2880 }
2881 else if( layer_front_most == F_Cu || layer_back_most == B_Cu )
2882 {
2883 via->SetViaType( VIATYPE::BLIND );
2884 }
2885 else
2886 {
2887 via->SetViaType( VIATYPE::BURIED );
2888 }
2889
2890 VECTOR2I pos( kicad_x( v.x ), kicad_y( v.y ) );
2891
2892 via->SetLayerPair( layer_front_most, layer_back_most );
2893 via->SetPosition( pos );
2894 via->SetEnd( pos );
2895
2896 via->SetNetCode( netCode );
2897 }
2898
2899 m_xpath->pop();
2900 }
2901
2902 else if( itemName == wxT( "contactref" ) )
2903 {
2904 m_xpath->push( "contactref" );
2905 // <contactref element="RN1" pad="7"/>
2906
2907 const wxString& reference = netItem->GetAttribute( "element" );
2908 const wxString& pad = netItem->GetAttribute( "pad" );
2909 wxString key = makeKey( reference, pad ) ;
2910
2911 m_pads_to_nets[ key ] = ENET( netCode, netName );
2912
2913 m_xpath->pop();
2914
2915 sawPad = true;
2916 }
2917
2918 else if( itemName == wxT( "polygon" ) )
2919 {
2920 m_xpath->push( "polygon" );
2921 auto* zone = loadPolygon( netItem );
2922
2923 if( zone )
2924 {
2925 zones.push_back( zone );
2926
2927 if( !zone->GetIsRuleArea() )
2928 zone->SetNetCode( netCode );
2929 }
2930
2931 m_xpath->pop(); // "polygon"
2932 }
2933
2934 netItem = netItem->GetNext();
2935 }
2936
2937 if( zones.size() && !sawPad )
2938 {
2939 // KiCad does not support an unconnected zone with its own non-zero netcode,
2940 // but only when assigned netcode = 0 w/o a name...
2941 for( ZONE* zone : zones )
2942 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
2943
2944 // therefore omit this signal/net.
2945 }
2946
2947 //Next signal needs a new netCode
2948 netCode++;
2949
2950 // Get next signal
2951 net = net->GetNext();
2952 }
2953
2954 m_xpath->pop(); // "signals.signal"
2955}
2956
2957
2958std::map<wxString, PCB_LAYER_ID> PCB_IO_EAGLE::DefaultLayerMappingCallback(
2959 const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
2960{
2961 std::map<wxString, PCB_LAYER_ID> layer_map;
2962
2963 for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
2964 {
2965 PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
2966 layer_map.emplace( layer.Name, layerId );
2967 }
2968
2969 return layer_map;
2970}
2971
2972
2973void PCB_IO_EAGLE::mapEagleLayersToKicad( bool aIsLibraryCache )
2974{
2975 std::vector<INPUT_LAYER_DESC> inputDescs;
2976
2977 for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
2978 {
2979 const ELAYER& eLayer = layerPair.second;
2980
2981 INPUT_LAYER_DESC layerDesc;
2982 std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
2983 defaultKicadLayer( eLayer.number, aIsLibraryCache );
2984
2985 if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
2986 continue; // Ignore unused copper layers
2987
2988 layerDesc.Name = eLayer.name;
2989
2990 inputDescs.push_back( layerDesc );
2991 }
2992
2993 if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
2994 dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
2995
2996 m_layer_map = m_layer_mapping_handler( inputDescs );
2997
2998 if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
2999 dynamic_cast<wxWindow*>( m_progressReporter )->Show();
3000}
3001
3002
3004{
3005 auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
3006 return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
3007}
3008
3009
3010std::tuple<PCB_LAYER_ID, LSET, bool> PCB_IO_EAGLE::defaultKicadLayer( int aEagleLayer,
3011 bool aIsLibraryCache ) const
3012{
3013 // eagle copper layer:
3014 if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
3015 {
3016 LSET copperLayers;
3017
3018 for( int copperLayer : m_cu_map )
3019 {
3020 if( copperLayer >= 0 )
3021 copperLayers[copperLayer] = true;
3022 }
3023
3024 return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
3025 }
3026
3027 int kiLayer = UNSELECTED_LAYER;
3028 bool required = false;
3029 LSET permittedLayers;
3030
3031 permittedLayers.set();
3032
3033 // translate non-copper eagle layer to pcbnew layer
3034 switch( aEagleLayer )
3035 {
3036 // Eagle says "Dimension" layer, but it's for board perimeter
3038 kiLayer = Edge_Cuts;
3039 required = true;
3040 permittedLayers = LSET( { Edge_Cuts } );
3041 break;
3042
3044 kiLayer = F_SilkS;
3045 break;
3047 kiLayer = B_SilkS;
3048 break;
3050 kiLayer = F_SilkS;
3051 break;
3053 kiLayer = B_SilkS;
3054 break;
3056 kiLayer = F_Fab;
3057 break;
3059 kiLayer = B_Fab;
3060 break;
3061 case EAGLE_LAYER::TSTOP:
3062 kiLayer = F_Mask;
3063 break;
3064 case EAGLE_LAYER::BSTOP:
3065 kiLayer = B_Mask;
3066 break;
3068 kiLayer = F_Paste;
3069 break;
3071 kiLayer = B_Paste;
3072 break;
3074 kiLayer = F_Mask;
3075 break;
3077 kiLayer = B_Mask;
3078 break;
3079 case EAGLE_LAYER::TGLUE:
3080 kiLayer = F_Adhes;
3081 break;
3082 case EAGLE_LAYER::BGLUE:
3083 kiLayer = B_Adhes;
3084 break;
3086 kiLayer = Cmts_User;
3087 break;
3089 kiLayer = Cmts_User;
3090 break;
3092 kiLayer = Cmts_User;
3093 break;
3094
3095 // Packages show the future chip pins on SMD parts using layer 51.
3096 // This is an area slightly smaller than the PAD/SMD copper area.
3097 // Carry those visual aids into the FOOTPRINT on the fabrication layer,
3098 // not silkscreen. This is perhaps not perfect, but there is not a lot
3099 // of other suitable paired layers
3100 case EAGLE_LAYER::TDOCU:
3101 kiLayer = F_Fab;
3102 break;
3103 case EAGLE_LAYER::BDOCU:
3104 kiLayer = B_Fab;
3105 break;
3106
3107 // these layers are defined as user layers. put them on ECO layers
3109 kiLayer = Eco1_User;
3110 break;
3112 kiLayer = Eco2_User;
3113 break;
3114
3115 // these will also appear in the ratsnest, so there's no need for a warning
3117 kiLayer = Dwgs_User;
3118 break;
3119
3121 kiLayer = F_CrtYd;
3122 break;
3124 kiLayer = B_CrtYd;
3125 break;
3126
3128 case EAGLE_LAYER::TTEST:
3129 case EAGLE_LAYER::BTEST:
3130 case EAGLE_LAYER::HOLES:
3131 default:
3132 if( aIsLibraryCache )
3133 kiLayer = UNDEFINED_LAYER;
3134 else
3135 kiLayer = UNSELECTED_LAYER;
3136
3137 break;
3138 }
3139
3140 return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
3141}
3142
3143
3144const wxString& PCB_IO_EAGLE::eagle_layer_name( int aLayer ) const
3145{
3146 static const wxString unknown( "unknown" );
3147 auto it = m_eagleLayers.find( aLayer );
3148 return it == m_eagleLayers.end() ? unknown : it->second.name;
3149}
3150
3151
3152int PCB_IO_EAGLE::eagle_layer_id( const wxString& aLayerName ) const
3153{
3154 static const int unknown = -1;
3155 auto it = m_eagleLayersIds.find( aLayerName );
3156 return it == m_eagleLayersIds.end() ? unknown : it->second;
3157}
3158
3159
3161{
3162 if( m_props )
3163 {
3164 UTF8 page_width;
3165 UTF8 page_height;
3166
3167 if( auto it = m_props->find( "page_width" ); it != m_props->end() )
3168 page_width = it->second;
3169
3170 if( auto it = m_props->find( "page_height" ); it != m_props->end() )
3171 page_height = it->second;
3172
3173 if( !page_width.empty() && !page_height.empty() )
3174 {
3175 BOX2I bbbox = m_board->GetBoardEdgesBoundingBox();
3176
3177 int w = atoi( page_width.c_str() );
3178 int h = atoi( page_height.c_str() );
3179
3180 int desired_x = ( w - bbbox.GetWidth() ) / 2;
3181 int desired_y = ( h - bbbox.GetHeight() ) / 2;
3182
3183 m_board->Move( VECTOR2I( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
3184 }
3185 }
3186}
3187
3188
3189long long PCB_IO_EAGLE::GetLibraryTimestamp( const wxString& aPath ) const
3190{
3191 // File hasn't been loaded yet.
3192 if( aPath.IsEmpty() )
3193 return wxDateTime::Now().GetValue().GetValue();
3194
3195 wxFileName fn( aPath );
3196
3197 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
3198 return fn.GetModificationTime().GetValue().GetValue();
3199 else
3200 return 0;
3201}
3202
3203
3204void PCB_IO_EAGLE::cacheLib( const wxString& aLibPath )
3205{
3206 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3207 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3208
3209 try
3210 {
3211 long long timestamp = GetLibraryTimestamp( aLibPath );
3212
3213 if( aLibPath != m_lib_path || m_timestamp != timestamp )
3214 {
3215 wxXmlNode* doc;
3216
3218
3219 // Set this before completion of loading, since we rely on it for
3220 // text of an exception. Delay setting m_mod_time until after successful load
3221 // however.
3222 m_lib_path = aLibPath;
3223
3224 // 8 bit "filename" should be encoded according to disk filename encoding,
3225 // (maybe this is current locale, maybe not, its a filesystem issue),
3226 // and is not necessarily utf8.
3227 string filename = (const char*) aLibPath.char_str( wxConvFile );
3228
3229 // Load the document
3230 wxFileName fn( filename );
3231 wxFFileInputStream stream( fn.GetFullPath() );
3232 wxXmlDocument xmlDocument;
3233
3234 if( !stream.IsOk() || !xmlDocument.Load( stream ) )
3235 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ), fn.GetFullPath() ) );
3236
3237 doc = xmlDocument.GetRoot();
3238
3239 wxXmlNode* drawing = MapChildren( doc )["drawing"];
3240 NODE_MAP drawingChildren = MapChildren( drawing );
3241
3242 // clear the cu map and then rebuild it.
3243 clear_cu_map();
3244
3245 m_xpath->push( "eagle.drawing.layers" );
3246 wxXmlNode* layers = drawingChildren["layers"];
3247 loadLayerDefs( layers );
3248 mapEagleLayersToKicad( true );
3249 m_xpath->pop();
3250
3251 m_xpath->push( "eagle.drawing.library" );
3252 wxXmlNode* library = drawingChildren["library"];
3253
3254 loadLibrary( library, nullptr );
3255 m_xpath->pop();
3256
3257 m_timestamp = timestamp;
3258 }
3259 }
3260 catch(...)
3261 {
3262 }
3263 // TODO: Handle exceptions
3264 // catch( file_parser_error fpe )
3265 // {
3266 // // for xml_parser_error, what() has the line number in it,
3267 // // but no byte offset. That should be an adequate error message.
3268 // THROW_IO_ERROR( fpe.what() );
3269 // }
3270 //
3271 // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
3272 // // so one catch should be OK for all errors.
3273 // catch( ptree_error pte )
3274 // {
3275 // string errmsg = pte.what();
3276 //
3277 // errmsg += " @\n";
3278 // errmsg += m_xpath->Contents();
3279 //
3280 // THROW_IO_ERROR( errmsg );
3281 // }
3282}
3283
3284
3285void PCB_IO_EAGLE::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
3286 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
3287{
3288 wxString errorMsg;
3289
3290 init( aProperties );
3291
3292 try
3293 {
3294 cacheLib( aLibraryPath );
3295 }
3296 catch( const IO_ERROR& ioe )
3297 {
3298 errorMsg = ioe.What();
3299 }
3300
3301 // Some of the files may have been parsed correctly so we want to add the valid files to
3302 // the library.
3303
3304 for( const auto& [ name, footprint ] : m_templates )
3305 aFootprintNames.Add( name );
3306
3307 if( !errorMsg.IsEmpty() && !aBestEfforts )
3308 THROW_IO_ERROR( errorMsg );
3309}
3310
3311
3312FOOTPRINT* PCB_IO_EAGLE::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
3313 bool aKeepUUID, const std::map<std::string, UTF8>* aProperties )
3314{
3315 init( aProperties );
3316 cacheLib( aLibraryPath );
3317 auto it = m_templates.find( aFootprintName );
3318
3319 if( it == m_templates.end() )
3320 return nullptr;
3321
3322 // Return a copy of the template
3323 FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate( IGNORE_PARENT_GROUP );
3324 copy->SetParent( nullptr );
3325 return copy;
3326}
3327
3328
3330{
3331 int minLayerCount = 2;
3332
3333 std::map<wxString, PCB_LAYER_ID>::const_iterator it;
3334
3335 for( it = m_layer_map.begin(); it != m_layer_map.end(); ++it )
3336 {
3337 PCB_LAYER_ID layerId = it->second;
3338
3339 if( !IsCopperLayer( layerId ) || layerId == F_Cu || layerId == B_Cu )
3340 continue;
3341
3342 int ordinal = CopperLayerToOrdinal( layerId );
3343
3344 if( ( ordinal + 2 ) > minLayerCount )
3345 minLayerCount = ordinal + 2;
3346 }
3347
3348 // Ensure the copper layers count is a multiple of 2
3349 // Pcbnew does not like boards with odd layers count
3350 // (these boards cannot exist. they actually have a even layers count)
3351 if( ( minLayerCount % 2 ) != 0 )
3352 minLayerCount++;
3353
3354 return minLayerCount;
3355}
const char * name
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition arraydim.h:31
constexpr int ARC_HIGH_DEF
Definition base_units.h:129
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
@ LT_SIGNAL
Definition board.h:187
#define DEFAULT_SILK_LINE_WIDTH
#define DEFAULT_EDGE_WIDTH
#define DEFAULT_LINE_WIDTH
#define DEFAULT_COURTYARD_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BASE_SET & set(size_t pos)
Definition base_set.h:116
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:285
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1082
constexpr coord_type GetY() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr coord_type GetX() const
Definition box2.h:207
constexpr size_type GetHeight() const
Definition box2.h:215
double AsDegrees() const
Definition eda_angle.h:116
void SetCenter(const VECTOR2I &aCenter)
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:136
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:178
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:168
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:590
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:407
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:431
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:298
bool IsMirrored() const
Definition eda_text.h:190
void SetKeepUpright(bool aKeepUpright)
Definition eda_text.cpp:439
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:284
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:313
VECTOR2I GetTextSize() const
Definition eda_text.h:261
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:423
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:352
EDA_ANGLE GetOrientation() const
Definition footprint.h:330
void SetOrientation(const EDA_ANGLE &aNewAngle)
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:787
std::deque< PAD * > & Pads()
Definition footprint.h:306
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:339
const LIB_ID & GetFPID() const
Definition footprint.h:351
void SetReference(const wxString &aReference)
Definition footprint.h:757
void SetValue(const wxString &aValue)
Definition footprint.h:778
PCB_FIELD & Reference()
Definition footprint.h:788
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:773
const wxString & GetReference() const
Definition footprint.h:751
VECTOR2I GetPosition() const override
Definition footprint.h:327
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:45
int GetViaDiameter() const
Definition netclass.h:133
int GetViaDrill() const
Definition netclass.h:141
int GetTrackWidth() const
Definition netclass.h:125
Handle the data for a net.
Definition netinfo.h:54
void SetNetClass(const std::shared_ptr< NETCLASS > &aNetClass)
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:247
void SetNetclassPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a netclass pattern assignment Calling this method will reset the effective netclass calculation ...
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
void SetNetclass(const wxString &netclassName, std::shared_ptr< NETCLASS > &netclass)
Sets the given netclass Calling user is responsible for resetting the effective netclass calculation ...
T & Get()
Return a reference to the value of the attribute assuming it is available.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:136
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition pad.h: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:349
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:346
void SetUnits(EDA_UNITS aUnits)
void SetLineThickness(int aWidth)
virtual void SetEnd(const VECTOR2I &aPoint)
void SetPrecision(DIM_PRECISION aPrecision)
virtual void SetStart(const VECTOR2I &aPoint)
void SetOverrideText(const wxString &aValue)
For better understanding of the points that make a dimension:
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
A leader is a dimension-like object pointing to a specific point.
A radial dimension indicates either the radius or diameter of an arc or circle.
std::vector< ELAYER > ELAYERS
void packageSMD(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
Handles common pad properties.
void loadPlain(wxXmlNode *aPlain)
int m_min_trace
smallest trace we find on Load(), in BIU.
std::map< wxString, PCB_LAYER_ID > m_layer_map
Map of Eagle layers to KiCad layers.
void orientFPText(FOOTPRINT *aFootprint, const EELEMENT &e, PCB_TEXT *aFPText, const EATTR *aAttr)
VECTOR2I kicad_fontsize(const ECOORD &d, int aTextThickness) const
create a font size (fontz) from an eagle font size scalar and KiCad font thickness
std::map< wxString, PCB_LAYER_ID > DefaultLayerMappingCallback(const std::vector< INPUT_LAYER_DESC > &aInputLayerDescriptionVector)
Return the automapped layers.
bool checkHeader(const wxString &aFileName) const
void loadElements(wxXmlNode *aElements)
FOOTPRINT * makeFootprint(wxXmlNode *aPackage, const wxString &aPkgName)
Create a FOOTPRINT from an Eagle package.
int m_min_hole
smallest diameter hole we find on Load(), in BIU.
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
XPATH * m_xpath
keeps track of what we are working on within XML document during a Load().
unsigned m_totalCount
for progress reporting
void cacheLib(const wxString &aLibraryPath)
This PLUGIN only caches one footprint library, this determines which one.
int m_hole_count
generates unique footprint names from eagle "hole"s.
std::map< wxString, FOOTPRINT * > m_templates
is part of a FOOTPRINT factory that operates using copy construction.
std::map< wxString, int > m_eagleLayersIds
Eagle layer ids stored by layer name.
void mapEagleLayersToKicad(bool aIsLibraryCache=false)
Generate mapping between Eagle and KiCad layers.
std::map< wxString, std::shared_ptr< NETCLASS > > m_classMap
std::tuple< PCB_LAYER_ID, LSET, bool > defaultKicadLayer(int aEagleLayer, bool aIsLibraryCache=false) const
Get the default KiCad layer corresponding to an Eagle layer of the board, a set of sensible layer map...
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
void loadLibraries(wxXmlNode *aLibs)
void init(const std::map< std::string, UTF8 > *aProperties)
initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed.
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
int eagle_layer_id(const wxString &aLayerName) const
Get Eagle layer number by its name.
void loadAllSections(wxXmlNode *aDocument)
bool CanReadFootprint(const wxString &aFileName) const override
Checks if this PCB_IO can read a footprint from specified file or directory.
void packageWire(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
const wxString & eagle_layer_name(int aLayer) const
Get Eagle layer name by its number.
ELAYERS::const_iterator EITER
long long m_timestamp
void packageRectangle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
int kicad_y(const ECOORD &y) const
Convert an Eagle distance to a KiCad distance.
void loadClasses(wxXmlNode *aClasses)
std::map< int, ELAYER > m_eagleLayers
Eagle layer data stored by layer number.
wxString m_customRules
NET_MAP m_pads_to_nets
net list
void packageText(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
int getMinimumCopperLayerCount() const
Determines the minimum copper layer stackup count that includes all mapped layers.
void packageHole(FOOTPRINT *aFootprint, wxXmlNode *aTree, bool aCenter) const
void packagePolygon(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void centerBoard()
move the BOARD into the center of the page
unsigned m_lastProgressCount
int m_cu_map[17]
map eagle to KiCad, cu layers only.
ZONE * loadPolygon(wxXmlNode *aPolyNode)
Load a copper or keepout polygon and adds it to the board.
int m_min_via
smallest via we find on Load(), in BIU.
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
void packageCircle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void loadLibrary(wxXmlNode *aLib, const wxString *aLibName)
Load the Eagle "library" XML element, which can occur either under a "libraries" element (if a *....
void packagePad(FOOTPRINT *aFootprint, wxXmlNode *aTree)
void loadSignals(wxXmlNode *aSignals)
ERULES * m_rules
Eagle design rules.
PCB_LAYER_ID kicad_layer(int aLayer) const
Convert an Eagle layer to a KiCad layer.
void orientFootprintAndText(FOOTPRINT *aFootprint, const EELEMENT &e, const EATTR *aNameAttr, const EATTR *aValueAttr)
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
int m_min_annulus
smallest via annulus we find on Load(), in BIU.
void loadLayerDefs(wxXmlNode *aLayers)
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
void setKeepoutSettingsToZone(ZONE *aZone, int aLayer) const
void transferPad(const EPAD_COMMON &aEaglePad, PAD *aPad) const
Deletes the footprint templates list.
int kicad_x(const ECOORD &x) const
void loadDesignRules(wxXmlNode *aDesignRules)
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
unsigned m_doneCount
wxString m_lib_path
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h: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:354
virtual void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:84
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:150
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_track.h:143
virtual void SetWidth(int aWidth)
Definition pcb_track.h:147
Container for project specific data.
Definition project.h:65
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:71
bool empty() const
Definition utf8.h:109
const char * c_str() const
Definition utf8.h:108
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:561
Keep track of what we are working on within a PTREE.
Handle a list of polygons defining a copper zone.
Definition zone.h:73
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:738
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition zone.cpp:1274
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:340
void NewHole()
Create a new hole on the zone; i.e., a new contour on the zone's outline.
Definition zone.h:578
void SetIsRuleArea(bool aEnable)
Definition zone.h:720
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:737
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition zone.cpp:1083
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:556
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:736
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:739
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:1183
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:735
static int GetDefaultHatchPitch()
Definition zone.cpp:1333
@ 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:56
@ SEGMENT
Definition eda_shape.h:45
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID BoardLayerFromLegacyId(int aLegacyId)
Retrieve a layer ID from an integer converted from a legacy (pre-V9) enum value.
Definition layer_id.cpp:218
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
size_t CopperLayerToOrdinal(PCB_LAYER_ID aLayer)
Converts KiCad copper layer enum to an ordinal between the front and back layers.
Definition layer_ids.h:913
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ Cmts_User
Definition layer_ids.h:108
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ UNSELECTED_LAYER
Definition layer_ids.h:62
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Eco2_User
Definition layer_ids.h:110
@ B_SilkS
Definition layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
KICOMMON_API wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Return the string from aValue according to aUnits (inch, mm ...) for display.
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ RECTANGLE
Definition padstack.h:54
static wxString makeKey(const wxString &aFirst, const wxString &aSecond)
Assemble a two part key as a simple concatenation of aFirst and aSecond parts, using a separator.
#define DIMENSION_PRECISION
static int parseEagle(const wxString &aDistance)
Parse an eagle distance which is either mm, or mils if there is "mil" suffix.
static T eagleClamp(T aMin, T aValue, T aMax)
std::vector< ZONE * > ZONES
NET_MAP::const_iterator NET_MAP_CITER
@ THROUGH
Definition pcb_track.h:68
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
Parse an Eagle "attribute" XML element.
opt_double ratio
opt_wxString value
opt_ecoord size
opt_ecoord y
wxString name
opt_erot rot
opt_int align
opt_int display
opt_ecoord x
Eagle circle.
ECOORD x
ECOORD radius
ECOORD y
ECOORD width
wxString number
std::map< wxString, ECOORD > clearanceMap
wxString name
@ EU_MM
millimeters
@ EU_MIL
mils/thous
long long int value
Value expressed in nanometers.
int ToPcbUnits() const
Eagle dimension element.
opt_wxString dimensionType
opt_ecoord textsize
Eagle element element.
opt_erot rot
wxString name
wxString library
wxString package
opt_bool smashed
wxString value
opt_eurn library_urn
Eagle hole element.
ECOORD y
ECOORD drill
ECOORD x
wxString name
opt_bool active
Eagle net.
int netcode
Structure holding common properties for through-hole and SMD pads.
opt_bool thermals
opt_bool stop
wxString name
Eagle thru hole pad.
opt_ecoord diameter
opt_bool first
opt_ecoord drill
opt_int shape
Eagle polygon, without vertices which are parsed as needed.
opt_bool orphans
opt_int rank
opt_bool thermals
opt_ecoord spacing
static const int max_priority
ECOORD width
opt_ecoord isolate
Eagle XML rectangle in binary.
ECOORD x2
ECOORD y1
opt_erot rot
int layer
ECOORD y2
ECOORD x1
double degrees
bool spin
bool mirror
subset of eagle.drawing.board.designrules in the XML document
int psBottom
Shape of the bottom pads.
int psElongationLong
double mvStopFrame
solderpaste mask, expressed as percentage of the smaller pad/via dimension
double srRoundness
corner rounding ratio for SMD pads (percentage)
double rlMinViaOuter
minimum copper annulus on via
int mlMaxCreamFrame
solder paste mask, maximum size (Eagle mils, here nanometers)
int mlMinCreamFrame
solder paste mask, minimum size (Eagle mils, here nanometers)
int psTop
Shape of the top pads.
double rlMaxViaOuter
maximum copper annulus on via
void parse(wxXmlNode *aRules, std::function< void()> aCheckpoint)
percent over 100%.
double mdWireWire
wire to wire spacing I presume.
double rvPadTop
top pad size as percent of drill size
int srMaxRoundness
double rvViaOuter
copper annulus is this percent of via hole
double rlMinPadTop
minimum copper annulus on through hole pads
double mvCreamFrame
int psElongationOffset
the offset of the hole within the "long" pad.
int mlMinStopFrame
solder mask, minimum size (Eagle mils, here nanometers)
int srMinRoundness
corner rounding radius, maximum size (Eagle mils, here nanometers)
int mlMaxStopFrame
solder mask, maximum size (Eagle mils, here nanometers)
double rlMaxPadTop
maximum copper annulus on through hole pads
int psFirst
Shape of the first pads.
Eagle SMD pad.
opt_int roundness
ECOORD dx
int layer
opt_bool cream
ECOORD dy
Eagle text element.
opt_double ratio
wxString text
@ BOTTOM_CENTER
@ BOTTOM_RIGHT
@ CENTER_RIGHT
@ CENTER_LEFT
@ BOTTOM_LEFT
ECOORD y
ECOORD size
opt_erot rot
opt_int align
ECOORD x
int layer
wxString assetId
The unique asset identifier for the asset type.
Eagle vertex.
Eagle via.
opt_ecoord diam
ECOORD drill
< inclusive
ECOORD y
int layer_front_most
int layer_back_most
< extent
ECOORD x
Eagle wire.
ECOORD width
int layer
ECOORD x2
ECOORD y2
ECOORD x1
ECOORD y1
opt_double curve
range is -359.9..359.9
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_LAYER_ID AutoMapLayer
Best guess as to what the equivalent KiCad layer might be.
bool Required
Should we require the layer to be assigned?
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
wxString Name
Imported layer name as displayed in original application.
Implement a simple wrapper around runtime_error to isolate the errors thrown by the Eagle XML parser.
VECTOR3I v1(5, 5, 5)
VECTOR2I center
int radius
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
VECTOR2I v2(1, 0)
int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
Definition trigo.cpp:209
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
double DEG2RAD(double deg)
Definition trigo.h:166
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:102
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ FULL
pads are covered by copper
Definition zones.h:51
#define ZONE_THICKNESS_MIN_VALUE_MM
Definition zones.h:35