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