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