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