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