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