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