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