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