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