KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_easyeda_parser.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) 2023 Alex Shvartzkop <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22
23#include <memory>
24
25#include <json_common.h>
26#include <core/map_helpers.h>
28#include <string_utils.h>
29
30#include <wx/log.h>
31
32#include <font/font.h>
33#include <footprint.h>
34#include <progress_reporter.h>
35#include <board.h>
37#include <bezier_curves.h>
38#include <pcb_group.h>
39#include <pcb_track.h>
40#include <pcb_shape.h>
41#include <pcb_text.h>
42#include <zone.h>
43#include <pad.h>
44#include <project.h>
45#include <fix_board_shape.h>
46
47
48static const wxString DIRECT_MODEL_UUID_KEY = wxS( "JLC_3DModel" );
49static const wxString MODEL_SIZE_KEY = wxS( "JLC_3D_Size" );
50
51static const int SHAPE_JOIN_DISTANCE = pcbIUScale.mmToIU( 1.5 );
52static const VECTOR2I HIDDEN_TEXT_SIZE( pcbIUScale.mmToIU( 0.5 ), pcbIUScale.mmToIU( 0.5 ) );
53
54
58
62
64{
65 int elayer = wxAtoi( aLayer );
66
67 switch( elayer )
68 {
69 case 1: return F_Cu;
70 case 2: return B_Cu;
71 case 3: return F_SilkS;
72 case 4: return B_SilkS;
73 case 5: return F_Paste;
74 case 6: return B_Paste;
75 case 7: return F_Mask;
76 case 8: return B_Mask;
77 /*case 9: return UNDEFINED_LAYER;*/ // Ratsnest
78 case 10: return Edge_Cuts;
79 case 11: return Eco1_User;
80 case 12: return Dwgs_User;
81 case 13: return F_Fab;
82 case 14: return B_Fab;
83 case 15: return Eco2_User;
84
85 case 19: return F_Fab; // 3D model
86
87 case 21: return In1_Cu;
88 case 22: return In2_Cu;
89 case 23: return In3_Cu;
90 case 24: return In4_Cu;
91 case 25: return In5_Cu;
92 case 26: return In6_Cu;
93 case 27: return In7_Cu;
94 case 28: return In8_Cu;
95 case 29: return In9_Cu;
96 case 30: return In10_Cu;
97 case 31: return In11_Cu;
98 case 32: return In12_Cu;
99 case 33: return In13_Cu;
100 case 34: return In14_Cu;
101 case 35: return In15_Cu;
102 case 36: return In16_Cu;
103 case 37: return In17_Cu;
104 case 38: return In18_Cu;
105 case 39: return In19_Cu;
106 case 40: return In20_Cu;
107 case 41: return In21_Cu;
108 case 42: return In22_Cu;
109 case 43: return In23_Cu;
110 case 44: return In24_Cu;
111 case 45: return In25_Cu;
112 case 46: return In26_Cu;
113 case 47: return In27_Cu;
114 case 48: return In28_Cu;
115 case 49: return In29_Cu;
116 case 50: return In30_Cu;
117
118 case 99: return User_3;
119 case 100: return User_4;
120 case 101: return User_5;
121
122 default: break;
123 }
124
125 return User_1;
126}
127
128
129static LIB_ID EasyEdaToKiCadLibID( const wxString& aLibName, const wxString& aLibReference )
130{
131 wxString libReference = EscapeString( aLibReference, CTX_LIBID );
132
133 wxString key = !aLibName.empty() ? ( aLibName + ':' + libReference ) : libReference;
134
135 LIB_ID libId;
136 libId.Parse( key, true );
137
138 return libId;
139}
140
141
143 BOARD_ITEM_CONTAINER* aContainer, BOARD* aParent, std::map<wxString, wxString> paramMap,
144 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap, wxArrayString aShapes )
145{
146 // TODO: make this path configurable?
147 const wxString easyedaModelDir = wxS( "EASYEDA_MODELS" );
148 wxString kicadModelPrefix = wxS( "${KIPRJMOD}/" ) + easyedaModelDir + wxS( "/" );
149
150 BOARD* board = aParent ? aParent : dynamic_cast<BOARD*>( aContainer );
151 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aContainer );
152
153 auto getOrAddNetItem = [&]( const wxString& aNetName ) -> NETINFO_ITEM*
154 {
155 if( !board )
156 return nullptr;
157
158 if( aNetName.empty() )
159 return nullptr;
160
161 if( NETINFO_ITEM* item = board->FindNet( aNetName ) )
162 {
163 return item;
164 }
165 else
166 {
167 item = new NETINFO_ITEM( board, aNetName, board->GetNetCount() + 1 );
168 board->Add( item, ADD_MODE::APPEND );
169 return item;
170 }
171 };
172
173 if( footprint )
174 {
175 // TODO: library name
176 LIB_ID fpID = EasyEdaToKiCadLibID( wxEmptyString, paramMap[wxS( "package" )] );
177 footprint->SetFPID( fpID );
178 }
179
180 for( wxString shape : aShapes )
181 {
182 wxArrayString arr = wxSplit( shape, '~', '\0' );
183
184 wxString elType = arr[0];
185 if( elType == wxS( "LIB" ) )
186 {
187 shape.Replace( wxS( "#@$" ), "\n" );
188 wxArrayString parts = wxSplit( shape, '\n', '\0' );
189
190 if( parts.size() < 1 )
191 continue;
192
193 wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
194
195 if( paramsRoot.size() < 4 )
196 continue;
197
198 VECTOR2D fpOrigin( Convert( paramsRoot[1] ), Convert( paramsRoot[2] ) );
199
200 wxString packageName =
201 wxString::Format( wxS( "Unknown_%s_%s" ), paramsRoot[1], paramsRoot[2] );
202
203 wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
204
205 EDA_ANGLE orientation;
206 if( !paramsRoot[4].IsEmpty() )
207 orientation = EDA_ANGLE( Convert( paramsRoot[4] ), DEGREES_T ); // Already applied
208
209 int layer = 1;
210
211 if( !paramsRoot[7].IsEmpty() )
212 layer = Convert( paramsRoot[7] );
213
214 std::map<wxString, wxString> innerParamMap;
215
216 for( int i = 1; i < (int) paramParts.size(); i += 2 )
217 {
218 wxString key = paramParts[i - 1];
219 wxString value = paramParts[i];
220
221 if( key == wxS( "package" ) )
222 packageName = value;
223
224 innerParamMap[key] = value;
225 }
226
227 parts.RemoveAt( 0 );
228
229 VECTOR2D pcbOrigin = m_relOrigin;
230 std::unique_ptr<FOOTPRINT> fp =
231 ParseFootprint( fpOrigin, orientation, layer, board, innerParamMap, aFootprintMap, parts );
232
233 if( !fp )
234 continue;
235
236 m_relOrigin = pcbOrigin;
237
238 fp->Move( RelPos( fpOrigin ) );
239
240 aContainer->Add( fp.release(), ADD_MODE::APPEND );
241 }
242 else if( elType == wxS( "TRACK" ) )
243 {
244 double width = ConvertSize( arr[1] );
245 PCB_LAYER_ID layer = LayerToKi( arr[2] );
246 wxString netname = arr[3];
247 wxArrayString data = wxSplit( arr[4], ' ', '\0' );
248
249 for( int i = 3; i < (int) data.size(); i += 2 )
250 {
251 VECTOR2D start, end;
252 start.x = RelPosX( data[i - 3] );
253 start.y = RelPosY( data[i - 2] );
254 end.x = RelPosX( data[i - 1] );
255 end.y = RelPosY( data[i] );
256
257 if( !footprint && IsCopperLayer( layer ) )
258 {
259 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aContainer );
260
261 track->SetLayer( layer );
262 track->SetWidth( width );
263 track->SetStart( start );
264 track->SetEnd( end );
265 track->SetNet( getOrAddNetItem( netname ) );
266
267 aContainer->Add( track.release(), ADD_MODE::APPEND );
268 }
269 else
270 {
271 std::unique_ptr<PCB_SHAPE> seg =
272 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::SEGMENT );
273
274 seg->SetLayer( layer );
275 seg->SetWidth( width );
276 seg->SetStart( start );
277 seg->SetEnd( end );
278
279 aContainer->Add( seg.release(), ADD_MODE::APPEND );
280 }
281 }
282 }
283 else if( elType == wxS( "CIRCLE" ) )
284 {
285 auto circleShape = std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::CIRCLE );
286 double width = ConvertSize( arr[4] );
287 circleShape->SetWidth( width );
288
289 PCB_LAYER_ID layer = LayerToKi( arr[5] );
290 circleShape->SetLayer( layer );
291
293 center.x = RelPosX( arr[1] );
294 center.y = RelPosY( arr[2] );
295
296 double radius = ConvertSize( arr[3] );
297
298 circleShape->SetCenter( center );
299 circleShape->SetEnd( center + VECTOR2I( radius, 0 ) );
300
301 if( IsCopperLayer( layer ) )
302 circleShape->SetNet( getOrAddNetItem( arr[8] ) );
303
304 aContainer->Add( circleShape.release(), ADD_MODE::APPEND );
305 }
306 else if( elType == wxS( "RECT" ) )
307 {
308 auto rectShape = std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::RECTANGLE );
309 double width = ConvertSize( arr[8] );
310 rectShape->SetWidth( width );
311
312 PCB_LAYER_ID layer = LayerToKi( arr[5] );
313 rectShape->SetLayer( layer );
314
315 bool filled = arr[9] != wxS( "none" );
316 rectShape->SetFilled( filled );
317
318 VECTOR2D rectStart;
319 rectStart.x = RelPosX( arr[1] );
320 rectStart.y = RelPosY( arr[2] );
321
322 VECTOR2D rectSize;
323 rectSize.x = ConvertSize( arr[3] );
324 rectSize.y = ConvertSize( arr[4] );
325
326 rectShape->SetStart( rectStart );
327 rectShape->SetEnd( rectStart + rectSize );
328
329 if( IsCopperLayer( layer ) )
330 rectShape->SetNet( getOrAddNetItem( arr[11] ) );
331
332 aContainer->Add( rectShape.release(), ADD_MODE::APPEND );
333 }
334 else if( elType == wxS( "ARC" ) )
335 {
336 std::unique_ptr<PCB_SHAPE> arcShape =
337 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::ARC );
338
339 double arcWidth = ConvertSize( arr[1] );
340 arcShape->SetWidth( arcWidth );
341
342 PCB_LAYER_ID layer = LayerToKi( arr[2] );
343 arcShape->SetLayer( layer );
344
345 if( IsCopperLayer( layer ) )
346 arcShape->SetNet( getOrAddNetItem( arr[3] ) );
347
348 VECTOR2D arcStart, arcEnd;
349 VECTOR2D rad( 10, 10 );
350 bool isFar = false;
351 bool cw = false;
352
353 int pos = 0;
354 wxString data = arr[4];
355 auto readNumber = [&]( wxString& aOut )
356 {
357 wxUniChar ch = data[pos];
358
359 while( ch == ' ' || ch == ',' )
360 ch = data[++pos];
361
362 while( isdigit( ch ) || ch == '.' || ch == '-' )
363 {
364 aOut += ch;
365 pos++;
366
367 if( pos == (int) data.size() )
368 break;
369
370 ch = data[pos];
371 }
372 };
373
374 do
375 {
376 wxUniChar sym = data[pos++];
377
378 if( sym == 'M' )
379 {
380 wxString xStr, yStr;
381 readNumber( xStr );
382 readNumber( yStr );
383
384 arcStart = VECTOR2D( Convert( xStr ), Convert( yStr ) );
385 }
386 else if( sym == 'A' )
387 {
388 wxString radX, radY, unknown, farFlag, cwFlag, endX, endY;
389 readNumber( radX );
390 readNumber( radY );
391 readNumber( unknown );
392 readNumber( farFlag );
393 readNumber( cwFlag );
394 readNumber( endX );
395 readNumber( endY );
396
397 isFar = farFlag == wxS( "1" );
398 cw = cwFlag == wxS( "1" );
399 rad = VECTOR2D( Convert( radX ), Convert( radY ) );
400 arcEnd = VECTOR2D( Convert( endX ), Convert( endY ) );
401 }
402 } while( pos < (int) data.size() );
403
404 VECTOR2D delta = arcEnd - arcStart;
405
406 double d = delta.EuclideanNorm();
407 double h = sqrt( std::max( 0.0, rad.x * rad.x - d * d / 4 ) );
408
409 //( !far && cw ) => h
410 //( far && cw ) => -h
411 //( !far && !cw ) => -h
412 //( far && !cw ) => h
413 VECTOR2D arcCenter =
414 arcStart + delta / 2 + delta.Perpendicular().Resize( ( isFar ^ cw ) ? h : -h );
415
416 if( !cw )
417 std::swap( arcStart, arcEnd );
418
419 arcShape->SetStart( RelPos( arcStart ) );
420 arcShape->SetEnd( RelPos( arcEnd ) );
421 arcShape->SetCenter( RelPos( arcCenter ) );
422
423 aContainer->Add( arcShape.release(), ADD_MODE::APPEND );
424 }
425 else if( elType == wxS( "DIMENSION" ) )
426 {
427 PCB_LAYER_ID layer = LayerToKi( arr[1] );
428 double lineWidth = ConvertSize( arr[7] );
429 wxString shapeData = arr[2].Trim();
430 //double textHeight = !arr[4].IsEmpty() ? ConvertSize( arr[4] ) : 0;
431
432 std::vector<SHAPE_LINE_CHAIN> lineChains =
434
435 std::unique_ptr<PCB_GROUP> group = std::make_unique<PCB_GROUP>( aContainer );
436 group->SetName( wxS( "Dimension" ) );
437
438 for( const SHAPE_LINE_CHAIN& chain : lineChains )
439 {
440 for( int segId = 0; segId < chain.SegmentCount(); segId++ )
441 {
442 SEG seg = chain.CSegment( segId );
443 auto dimSeg = std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::SEGMENT );
444
445 dimSeg->SetLayer( layer );
446 dimSeg->SetWidth( lineWidth );
447 dimSeg->SetStart( seg.A );
448 dimSeg->SetEnd( seg.B );
449
450 group->AddItem( dimSeg.get() );
451
452 aContainer->Add( dimSeg.release(), ADD_MODE::APPEND );
453 }
454 }
455
456 aContainer->Add( group.release(), ADD_MODE::APPEND );
457 }
458 else if( elType == wxS( "SOLIDREGION" ) )
459 {
460 wxString layer = arr[1];
461
462 SHAPE_POLY_SET polySet =
463 ParseLineChains( arr[3].Trim(), SHAPE_ARC::DefaultAccuracyForPCB(), true );
464
465 bool validPolySet = polySet.OutlineCount() > 0;
466
467 for( const SHAPE_POLY_SET::POLYGON& polygon : polySet.CPolygons() )
468 {
469 for( const SHAPE_LINE_CHAIN& contour : polygon )
470 validPolySet &= contour.PointCount() >= 3;
471 }
472
473 if( !validPolySet )
474 continue;
475
476 if( layer == wxS( "11" ) ) // Multi-layer (board cutout)
477 {
478 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
479 {
480 auto cutoutShape = std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::POLY );
481
482 cutoutShape->SetLayer( Edge_Cuts );
483 cutoutShape->SetFilled( false );
484 cutoutShape->SetWidth( pcbIUScale.mmToIU( 0.1 ) );
485 cutoutShape->SetPolyShape( poly );
486
487 aContainer->Add( cutoutShape.release(), ADD_MODE::APPEND );
488 }
489 }
490 else
491 {
492 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
493
494 PCB_LAYER_ID klayer = LayerToKi( layer );
495 zone->SetLayer( klayer );
496
497 if( IsCopperLayer( klayer ) )
498 zone->SetNet( getOrAddNetItem( arr[2] ) );
499
500 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
501 zone->Outline()->AddPolygon( poly );
502
503 if( zone->GetNumCorners() == 0 )
504 continue;
505
506 if( arr[4].Lower() == wxS( "cutout" ) )
507 {
508 zone->SetIsRuleArea( true );
509 zone->SetDoNotAllowZoneFills( true );
510 zone->SetDoNotAllowTracks( false );
511 zone->SetDoNotAllowVias( false );
512 zone->SetDoNotAllowPads( false );
513 zone->SetDoNotAllowFootprints( false );
514 }
515 else
516 { // solid
517 zone->SetFilledPolysList( klayer, polySet );
518 zone->SetPadConnection( ZONE_CONNECTION::FULL );
519 zone->SetIsFilled( true );
520 zone->SetNeedRefill( false );
521 }
522
523 zone->SetMinThickness( 0 );
524 zone->SetLocalClearance( 0 );
525 zone->SetAssignedPriority( 100 );
526
527 aContainer->Add( zone.release(), ADD_MODE::APPEND );
528 }
529 }
530 else if( elType == wxS( "COPPERAREA" ) )
531 {
532 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
533
534 PCB_LAYER_ID layer = LayerToKi( arr[2] );
535 zone->SetLayer( layer );
536
537 wxString netname = arr[3];
538
539 if( IsCopperLayer( layer ) )
540 zone->SetNet( getOrAddNetItem( netname ) );
541
542 zone->SetLocalClearance( ConvertSize( arr[5] ) );
543 zone->SetThermalReliefGap( zone->GetLocalClearance().value() );
544
545 wxString fillStyle = arr[5];
546 if( fillStyle == wxS( "none" ) )
547 {
548 // Do not fill?
549 }
550
551 SHAPE_POLY_SET polySet =
552 ParseLineChains( arr[4].Trim(), SHAPE_ARC::DefaultAccuracyForPCB(), true );
553
554 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
555 zone->Outline()->AddPolygon( poly );
556
557 wxString thermal = arr[8];
558 if( thermal == wxS( "direct" ) )
559 zone->SetPadConnection( ZONE_CONNECTION::FULL );
560
561 wxString keepIsland = arr[9];
562 if( keepIsland == wxS( "yes" ) )
563 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
564
565 wxString fillData = arr[10];
566 SHAPE_POLY_SET fillPolySet;
567 try
568 {
569 for( const nlohmann::json& polyData : nlohmann::json::parse( fillData ) )
570 {
571 for( const nlohmann::json& contourData : polyData )
572 {
573 SHAPE_POLY_SET contourPolySet = ParseLineChains(
574 contourData.get<wxString>(), SHAPE_ARC::DefaultAccuracyForPCB(), true );
575
576 SHAPE_POLY_SET currentOutline( contourPolySet.COutline( 0 ) );
577
578 for( int i = 0; i < contourPolySet.HoleCount( 0 ); i++ )
579 currentOutline.AddHole( contourPolySet.CHole( 0, i ) );
580
581 fillPolySet.Append( currentOutline );
582 }
583 }
584
585 fillPolySet.Fracture();
586
587 zone->SetFilledPolysList( layer, fillPolySet );
588 zone->SetIsFilled( true );
589 zone->SetNeedRefill( false );
590 }
591 catch( nlohmann::json::exception& )
592 {
593 }
594
595 int fillOrder = wxAtoi( arr[13] );
596 zone->SetAssignedPriority( 100 - fillOrder );
597
598 wxString improveFabrication = arr[17];
599 if( improveFabrication == wxS( "none" ) )
600 {
601 zone->SetMinThickness( 0 );
602 }
603 else
604 {
605 // arr[1] is "stroke Width" per docs
606 int minThickness = std::max( pcbIUScale.mmToIU( 0.03 ),
607 int( ConvertSize( arr[1] ) ) );
608 zone->SetMinThickness( minThickness );
609 }
610
611 if( arr.size() > 18 )
612 {
613 zone->SetThermalReliefSpokeWidth( std::max( int( ConvertSize( arr[18] ) ),
614 zone->GetMinThickness() ) );
615 }
616 else
617 {
618 wxFAIL_MSG( wxString::Format( "COPPERAREA unexpected size %d: %s ",
619 arr.size(),
620 shape ) );
621
622 zone->SetThermalReliefSpokeWidth( zone->GetMinThickness() );
623 }
624
625 aContainer->Add( zone.release(), ADD_MODE::APPEND );
626 }
627 else if( elType == wxS( "SVGNODE" ) )
628 {
629 nlohmann::json nodeData = nlohmann::json::parse( arr[1] );
630
631 int nodeType = nodeData.at( "nodeType" );
632 wxString layer = nodeData.at( "layerid" );
633 PCB_LAYER_ID klayer = LayerToKi( layer );
634
635 if( nodeType == 1 )
636 {
637 std::map<wxString, wxString> attributes = nodeData.at( "attrs" );
638
639 if( layer == wxS( "19" ) ) // 3DModel
640 {
641 if( !footprint )
642 continue;
643
644 auto ec_eType = get_opt( attributes, "c_etype" );
645 auto ec_rotation = get_opt( attributes, "c_rotation" );
646 auto ec_origin = get_opt( attributes, "c_origin" );
647 auto ec_width = get_opt( attributes, "c_width" );
648 auto ec_height = get_opt( attributes, "c_height" );
649 auto ez = get_opt( attributes, "z" );
650 auto etitle = get_opt( attributes, "title" );
651 auto euuid = get_opt( attributes, "uuid" );
652 auto etransform = get_opt( attributes, "transform" );
653
654 if( !ec_eType || *ec_eType != wxS( "outline3D" ) || !etitle )
655 continue;
656
657 wxString modelTitle = *etitle;
658 VECTOR3D kmodelOffset;
659 VECTOR3D kmodelRotation;
660
661 if( euuid )
662 {
663 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER,
665 field->SetLayer( Cmts_User );
666 field->SetVisible( false );
667 field->SetText( *euuid );
668 footprint->Add( field );
669 }
670
671 /*if( etransform )
672 {
673 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER,
674 "3D Transform" );
675 field->SetLayer( Cmts_User );
676 field->SetVisible( false );
677 field->SetText( *etransform );
678 footprint->Add( field );
679 }*/
680
681 if( ec_width && ec_height )
682 {
683 double fitXmm = pcbIUScale.IUTomm( ScaleSize( Convert( *ec_width ) ) );
684 double fitYmm = pcbIUScale.IUTomm( ScaleSize( Convert( *ec_height ) ) );
685
686 double rounding = 0.001;
687 fitXmm = KiROUND( fitXmm / rounding ) * rounding;
688 fitYmm = KiROUND( fitYmm / rounding ) * rounding;
689
690 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER,
692 field->SetLayer( Cmts_User );
693 field->SetVisible( false );
694 field->SetText( wxString::FromCDouble( fitXmm ) + wxS( " " )
695 + wxString::FromCDouble( fitYmm ) );
696 footprint->Add( field );
697 }
698
699 if( ec_origin )
700 {
701 wxArrayString orParts = wxSplit( *ec_origin, ',', '\0' );
702
703 if( orParts.size() == 2 )
704 {
705 VECTOR2D pos;
706 pos.x = Convert( orParts[0].Trim() );
707 pos.y = Convert( orParts[1].Trim() );
708
709 VECTOR2D rel = RelPos( pos );
710 kmodelOffset.x = -pcbIUScale.IUTomm( rel.x );
711 kmodelOffset.y = -pcbIUScale.IUTomm( rel.y );
712
713 RotatePoint( &kmodelOffset.x, &kmodelOffset.y,
714 -footprint->GetOrientation() );
715 }
716 }
717
718 if( ez )
719 {
720 kmodelOffset.z = pcbIUScale.IUTomm( ScaleSize( Convert( ez->Trim() ) ) );
721 }
722
723 if( ec_rotation )
724 {
725 wxArrayString rotParts = wxSplit( *ec_rotation, ',', '\0' );
726
727 if( rotParts.size() == 3 )
728 {
729 kmodelRotation.x = -Convert( rotParts[0].Trim() );
730 kmodelRotation.y = -Convert( rotParts[1].Trim() );
731 kmodelRotation.z = -Convert( rotParts[2].Trim() )
732 + footprint->GetOrientationDegrees();
733 }
734 }
735
736 if( footprint->GetLayer() == B_Cu )
737 {
738 kmodelRotation.z = 180 - kmodelRotation.z;
739 RotatePoint( &kmodelOffset.x, &kmodelOffset.y, ANGLE_180 );
740 }
741
743 model.m_Filename = kicadModelPrefix
745 + wxS( ".step" );
746 model.m_Offset = kmodelOffset;
747 model.m_Rotation = kmodelRotation;
748 footprint->Models().push_back( model );
749 }
750 else
751 {
752 if( auto dataStr = get_opt( attributes, "d" ) )
753 {
754 int maxError = SHAPE_ARC::DefaultAccuracyForPCB();
755
756 if( dataStr->size() >= 8000 )
757 maxError *= 10;
758
759 SHAPE_POLY_SET polySet = ParseLineChains( dataStr->Trim(), maxError, true );
760
761 polySet.RebuildHolesFromContours();
762
763 std::unique_ptr<PCB_GROUP> group;
764
765 if( polySet.OutlineCount() > 1 )
766 group = std::make_unique<PCB_GROUP>( aContainer );
767
768 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
769 {
770 auto svgShape = std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::POLY );
771
772 svgShape->SetFilled( true );
773 svgShape->SetPolyShape( poly );
774 svgShape->SetLayer( klayer );
775 svgShape->SetWidth( 0 );
776
777 if( group )
778 group->AddItem( svgShape.get() );
779
780 aContainer->Add( svgShape.release(), ADD_MODE::APPEND );
781 }
782
783 if( group )
784 aContainer->Add( group.release(), ADD_MODE::APPEND );
785 }
786 }
787 }
788 else
789 {
790 THROW_IO_ERRORF( _( "Unknown SVGNODE nodeType %d" ), nodeType );
791 }
792 }
793 else if( elType == wxS( "TEXT" ) )
794 {
795 PCB_TEXT* text;
796 wxString textType = arr[1];
797
798 if( footprint && textType == wxS( "P" ) )
799 {
800 text = footprint->GetField( FIELD_T::REFERENCE );
801 }
802 else if( footprint && textType == wxS( "N" ) )
803 {
804 text = footprint->GetField( FIELD_T::VALUE );
805 }
806 else if( arr[12] == wxS( "none" ) )
807 {
808 text = new PCB_FIELD( aContainer, FIELD_T::USER );
809 static_cast<PCB_FIELD*>( text )->SetVisible( false );
810 }
811 else
812 {
813 text = new PCB_TEXT( aContainer );
814 aContainer->Add( text, ADD_MODE::APPEND );
815 }
816
817 VECTOR2D start;
818 start.x = RelPosX( arr[2] );
819 start.y = RelPosY( arr[3] );
820 text->SetPosition( start );
821
822 double thickness = ConvertSize( arr[4] );
823 text->SetTextThickness( thickness );
824
825 double rot = Convert( arr[5] );
826 text->SetTextAngleDegrees( rot );
827
828 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
829 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
830
831 PCB_LAYER_ID layer = LayerToKi( arr[7] );
832 text->SetLayer( layer );
833
834 if( IsBackLayer( layer ) )
835 text->SetMirrored( true );
836
837 double height = ConvertSize( arr[9] ) * 0.8;
838 text->SetTextSize( VECTOR2I( height, height ) );
839
840 wxString textStr = arr[10];
841 textStr.Replace( wxS( "\\n" ), wxS( "\n" ) );
842 text->SetText( UnescapeHTML( textStr ) );
843
844 //arr[11] // Geometry data
845
846 wxString font = arr[14];
847 if( !font.IsEmpty() )
848 text->SetFont( KIFONT::FONT::GetFont( font ) );
849
850 TransformTextToBaseline( text, wxEmptyString );
851 }
852 else if( elType == wxS( "VIA" ) )
853 {
854 VECTOR2D center( RelPosX( arr[1] ), RelPosY( arr[2] ) );
855 int kdia = ConvertSize( arr[3] );
856 int kdrill = ConvertSize( arr[5] ) * 2;
857
858 if( footprint )
859 {
860 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint );
861
862 pad->SetPosition( center );
863 pad->SetLayerSet( PAD::PTHMask() );
864 pad->SetAttribute( PAD_ATTRIB::PTH );
865 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // EasyEDA doesn't have complex padstacks
867 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( kdia, kdia ) );
868 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
869 pad->SetDrillSize( VECTOR2I( kdrill, kdrill ) );
870
871 footprint->Add( pad.release(), ADD_MODE::APPEND );
872 }
873 else
874 {
875 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( aContainer );
876
877 via->SetPosition( center );
878 via->SetPadstackMode( PADSTACK::MODE::NORMAL ); // EasyEDA doesn't have complex padstacks
879 via->SetWidth( PADSTACK::ALL_LAYERS, kdia );
880 via->SetNet( getOrAddNetItem( arr[4] ) );
881 via->SetDrill( kdrill );
882
883 aContainer->Add( via.release(), ADD_MODE::APPEND );
884 }
885 }
886 else if( elType == wxS( "HOLE" ) )
887 {
888 FOOTPRINT* padContainer;
889
890 VECTOR2D center( RelPosX( arr[1] ), RelPosY( arr[2] ) );
891 int kdia = ConvertSize( arr[3] ) * 2;
892 wxString holeUuid = arr[4];
893
894 if( footprint )
895 {
896 padContainer = footprint;
897 }
898 else
899 {
900 std::unique_ptr<FOOTPRINT> newFootprint =
901 std::make_unique<FOOTPRINT>( dynamic_cast<BOARD*>( aContainer ) );
902
903 wxString name = wxS( "Hole_" ) + holeUuid;
904
905 newFootprint->SetFPID( LIB_ID( wxEmptyString, name ) );
906 newFootprint->SetPosition( center );
907 newFootprint->SetLocked( true );
908
909 newFootprint->Reference().SetText( name );
910 newFootprint->Reference().SetVisible( false );
911 newFootprint->Reference().SetTextSize( HIDDEN_TEXT_SIZE );
912 newFootprint->Value().SetText( name );
913 newFootprint->Value().SetVisible( false );
914 newFootprint->Value().SetTextSize( HIDDEN_TEXT_SIZE );
915
916 padContainer = newFootprint.get();
917 aContainer->Add( newFootprint.release(), ADD_MODE::APPEND );
918 }
919
920 std::unique_ptr<PAD> pad = std::make_unique<PAD>( padContainer );
921
922 pad->SetPosition( center );
923 pad->SetLayerSet( PAD::UnplatedHoleMask() );
924 pad->SetAttribute( PAD_ATTRIB::NPTH );
925 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // EasyEDA doesn't have complex padstacks
927 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( kdia, kdia ) );
928 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
929 pad->SetDrillSize( VECTOR2I( kdia, kdia ) );
930
931 padContainer->Add( pad.release(), ADD_MODE::APPEND );
932 }
933 else if( elType == wxS( "PAD" ) )
934 {
935 FOOTPRINT* padContainer;
936
937 VECTOR2D center( RelPosX( arr[2] ), RelPosY( arr[3] ) );
938 VECTOR2D size( ConvertSize( arr[4] ), ConvertSize( arr[5] ) );
939 wxString padUuid = arr[12];
940
941 if( footprint )
942 {
943 padContainer = footprint;
944 }
945 else
946 {
947 std::unique_ptr<FOOTPRINT> newFootprint =
948 std::make_unique<FOOTPRINT>( dynamic_cast<BOARD*>( aContainer ) );
949
950 wxString name = wxS( "Pad_" ) + padUuid;
951
952 newFootprint->SetFPID( LIB_ID( wxEmptyString, name ) );
953 newFootprint->SetPosition( center );
954 newFootprint->SetLocked( true );
955
956 newFootprint->Reference().SetText( name );
957 newFootprint->Reference().SetVisible( false );
958 newFootprint->Reference().SetTextSize( HIDDEN_TEXT_SIZE );
959 newFootprint->Value().SetText( name );
960 newFootprint->Value().SetVisible( false );
961 newFootprint->Value().SetTextSize( HIDDEN_TEXT_SIZE );
962
963 padContainer = newFootprint.get();
964 aContainer->Add( newFootprint.release(), ADD_MODE::APPEND );
965 }
966
967 std::unique_ptr<PAD> pad = std::make_unique<PAD>( padContainer );
968
969 pad->SetNet( getOrAddNetItem( arr[7] ) );
970 pad->SetNumber( arr[8] );
971 pad->SetPosition( center );
972 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // EasyEDA doesn't have complex padstacks
973 pad->SetSize( PADSTACK::ALL_LAYERS, size );
974 pad->SetOrientationDegrees( Convert( arr[11] ) );
975 pad->SetThermalSpokeAngle( ANGLE_0 );
976
977 wxString elayer = arr[6];
978 PCB_LAYER_ID klayer = LayerToKi( elayer );
979
980 bool plated = arr[15] == wxS( "Y" );
981
982 if( klayer == F_Cu )
983 {
984 pad->SetLayer( F_Cu );
985 pad->SetLayerSet( PAD::SMDMask() );
986 pad->SetAttribute( PAD_ATTRIB::SMD );
987 }
988 else if( klayer == B_Cu )
989 {
990 pad->SetLayer( B_Cu );
991 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
992 pad->SetAttribute( PAD_ATTRIB::SMD );
993 }
994 else if( elayer == wxS( "11" ) )
995 {
996 pad->SetLayerSet( plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
997 pad->SetAttribute( plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
998 }
999 else
1000 {
1001 pad->SetLayer( klayer );
1002 pad->SetLayerSet( LSET( { klayer } ) );
1003 pad->SetAttribute( PAD_ATTRIB::SMD );
1004 }
1005
1006 const wxString& padType = arr[1];
1007
1008 if( padType == wxS( "ELLIPSE" ) )
1009 {
1011 }
1012 else if( padType == wxS( "RECT" ) )
1013 {
1015 }
1016 else if( padType == wxS( "OVAL" ) )
1017 {
1018 if( pad->GetSizeX() == pad->GetSizeY() )
1020 else
1022 }
1023 else if( padType == wxS( "POLYGON" ) )
1024 {
1026 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
1027 pad->SetSize( PADSTACK::ALL_LAYERS, { 1, 1 } );
1028
1029 wxArrayString data = wxSplit( arr[10], ' ', '\0' );
1030
1032 for( int i = 1; i < (int) data.size(); i += 2 )
1033 {
1034 VECTOR2D pt;
1035 pt.x = RelPosX( data[i - 1] );
1036 pt.y = RelPosY( data[i] );
1037 chain.Append( pt );
1038 }
1039 chain.SetClosed( true );
1040
1041 chain.Move( -center );
1042 chain.Rotate( -pad->GetOrientation() );
1043 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, chain, 0, true );
1044 }
1045
1046 wxString holeDia = arr[9];
1047 if( !holeDia.IsEmpty() )
1048 {
1049 double holeD = ConvertSize( holeDia ) * 2;
1050 double holeL = 0;
1051
1052 wxString holeLength = arr[13];
1053 if( !holeLength.IsEmpty() )
1054 holeL = ConvertSize( holeLength );
1055
1056 if( holeL > 0 )
1057 {
1058 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
1059
1060 if( size.x < size.y )
1061 pad->SetDrillSize( VECTOR2I( holeD, holeL ) );
1062 else
1063 pad->SetDrillSize( VECTOR2I( holeL, holeD ) );
1064 }
1065 else
1066 {
1067 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
1068 pad->SetDrillSize( VECTOR2I( holeD, holeD ) );
1069 }
1070 }
1071
1072 wxString pasteExp = arr[17];
1073 if( !pasteExp.IsEmpty() )
1074 {
1075 double pasteExpansion = ConvertSize( pasteExp );
1076 pad->SetLocalSolderPasteMargin( pasteExpansion );
1077 }
1078
1079 wxString maskExp = arr[18];
1080 if( !maskExp.IsEmpty() )
1081 {
1082 double maskExpansion = ConvertSize( maskExp );
1083 pad->SetLocalSolderMaskMargin( maskExpansion );
1084 }
1085
1086 padContainer->Add( pad.release(), ADD_MODE::APPEND );
1087 }
1088 }
1089}
1090
1091
1092std::unique_ptr<FOOTPRINT>
1093PCB_IO_EASYEDA_PARSER::ParseFootprint( const VECTOR2D& aOrigin, const EDA_ANGLE& aOrientation, int aLayer,
1094 BOARD* aParent, std::map<wxString, wxString> aParams,
1095 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
1096 wxArrayString aShapes )
1097{
1098 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( aParent );
1099
1100 if( aLayer == 2 ) // Bottom layer
1101 {
1102 footprint->SetLayer( B_Cu );
1103 footprint->SetOrientation( aOrientation - ANGLE_180 );
1104 }
1105 else
1106 {
1107 footprint->SetLayer( F_Cu );
1108 footprint->SetOrientation( aOrientation );
1109 }
1110
1111 footprint->Value().SetText( aParams[wxS( "package" )] );
1112
1113 m_relOrigin = aOrigin;
1114
1115 ParseToBoardItemContainer( footprint.get(), aParent, aParams, aFootprintMap, aShapes );
1116
1117 // Heal board outlines
1118 std::vector<PCB_SHAPE*> shapes;
1119
1120 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1121 {
1122 if( !item->IsOnLayer( Edge_Cuts ) )
1123 continue;
1124
1125 if( item->Type() == PCB_SHAPE_T )
1126 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1127 }
1128
1130
1131 // EasyEDA footprints don't have courtyard, so build a box ourselves
1132 if( !footprint->IsOnLayer( F_CrtYd ) )
1133 {
1134 BOX2I bbox = footprint->GetLayerBoundingBox( { F_Cu, F_Fab, F_Paste, F_Mask, Edge_Cuts } );
1135 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Default courtyard clearance
1136
1137 std::unique_ptr<PCB_SHAPE> shape =
1138 std::make_unique<PCB_SHAPE>( footprint.get(), SHAPE_T::RECTANGLE );
1139
1140 shape->SetWidth( pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ) );
1141 shape->SetLayer( F_CrtYd );
1142 shape->SetStart( bbox.GetOrigin() );
1143 shape->SetEnd( bbox.GetEnd() );
1144
1145 footprint->Add( shape.release(), ADD_MODE::APPEND );
1146 }
1147
1148 bool hasFabRef = false;
1149
1150 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1151 {
1152 if( item->Type() == PCB_TEXT_T && item->IsOnLayer( F_Fab ) )
1153 {
1154 if( static_cast<PCB_TEXT*>( item )->GetText() == wxT( "${REFERENCE}" ) )
1155 {
1156 hasFabRef = true;
1157 break;
1158 }
1159 }
1160 }
1161
1162 if( !hasFabRef )
1163 {
1164 // Add reference text field on F_Fab
1165 int c_refTextSize = pcbIUScale.mmToIU( 0.5 ); // KLC min Fab text size
1166 int c_refTextThickness = pcbIUScale.mmToIU( 0.1 ); // Decent text thickness
1167 std::unique_ptr<PCB_TEXT> refText = std::make_unique<PCB_TEXT>( footprint.get() );
1168
1169 refText->SetLayer( F_Fab );
1170 refText->SetTextSize( VECTOR2I( c_refTextSize, c_refTextSize ) );
1171 refText->SetTextThickness( c_refTextThickness );
1172 refText->SetText( wxT( "${REFERENCE}" ) );
1173
1174 footprint->Add( refText.release(), ADD_MODE::APPEND );
1175 }
1176
1177 return footprint;
1178}
1179
1180
1182 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
1183 wxArrayString aShapes )
1184{
1185 m_relOrigin = aOrigin;
1186
1187 ParseToBoardItemContainer( aBoard, nullptr, {}, aFootprintMap, aShapes );
1188
1189 // Heal board outlines
1190 std::vector<PCB_SHAPE*> shapes;
1191
1192 for( BOARD_ITEM* item : aBoard->Drawings() )
1193 {
1194 if( !item->IsOnLayer( Edge_Cuts ) )
1195 continue;
1196
1197 if( item->Type() == PCB_SHAPE_T )
1198 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1199 }
1200
1202}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
#define DEFAULT_COURTYARD_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
Abstract interface for BOARD_ITEMs capable of storing other items inside.
virtual void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false)=0
Adds an item to the container.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:374
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:3001
unsigned GetNetCount() const
Definition board.h:1250
const DRAWINGS & Drawings() const
Definition board.h:466
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr const Vec & GetOrigin() const
Definition box2.h:207
double ConvertSize(const wxString &aValue)
static double Convert(const wxString &aValue)
double RelPosX(double aValue)
double RelPosY(double aValue)
VECTOR2< T > RelPos(const VECTOR2< T > &aVec)
void TransformTextToBaseline(EDA_TEXT *textItem, const wxString &baselineAlign)
std::vector< SHAPE_LINE_CHAIN > ParseLineChains(const wxString &aData, int aMaxError, bool aForceClosed)
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:475
EDA_ANGLE GetOrientation() const
Definition footprint.h:439
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
double GetOrientationDegrees() const
Definition footprint.h:469
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:450
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:425
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:50
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:616
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition pad.cpp:637
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:623
PCB_IO_EASYEDA_PARSER(PROGRESS_REPORTER *aProgressReporter)
void ParseToBoardItemContainer(BOARD_ITEM_CONTAINER *aContainer, BOARD *aParent, std::map< wxString, wxString > paramMap, std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, wxArrayString shapes)
std::unique_ptr< FOOTPRINT > ParseFootprint(const VECTOR2D &aOrigin, const EDA_ANGLE &aOrientation, int aLayer, BOARD *aParent, std::map< wxString, wxString > aParams, std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, wxArrayString aShapes)
double ScaleSize(double aValue) override
void ParseBoard(BOARD *aBoard, const VECTOR2D &aOrigin, std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, wxArrayString aShapes)
PCB_LAYER_ID LayerToKi(const wxString &aLayer)
A progress reporter interface for use in multi-threaded environments.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
static int DefaultAccuracyForPCB()
Definition shape_arc.h:283
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
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)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
void RebuildHolesFromContours()
Extract all contours from this polygon set, then recreate polygons with holes.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const std::vector< POLYGON > & CPolygons() const
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
@ DEGREES_T
Definition eda_angle.h:30
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
#define THROW_IO_ERRORF(msg,...)
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:829
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ In22_Cu
Definition layer_ids.h:83
@ In11_Cu
Definition layer_ids.h:72
@ In29_Cu
Definition layer_ids.h:90
@ In30_Cu
Definition layer_ids.h:91
@ F_CrtYd
Definition layer_ids.h:112
@ In17_Cu
Definition layer_ids.h:78
@ Edge_Cuts
Definition layer_ids.h:108
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ In9_Cu
Definition layer_ids.h:70
@ Cmts_User
Definition layer_ids.h:104
@ In19_Cu
Definition layer_ids.h:80
@ In7_Cu
Definition layer_ids.h:68
@ In28_Cu
Definition layer_ids.h:89
@ In26_Cu
Definition layer_ids.h:87
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ User_5
Definition layer_ids.h:124
@ Eco1_User
Definition layer_ids.h:105
@ F_Mask
Definition layer_ids.h:93
@ In21_Cu
Definition layer_ids.h:82
@ In23_Cu
Definition layer_ids.h:84
@ B_Paste
Definition layer_ids.h:101
@ In15_Cu
Definition layer_ids.h:76
@ In2_Cu
Definition layer_ids.h:63
@ F_Fab
Definition layer_ids.h:115
@ In10_Cu
Definition layer_ids.h:71
@ F_SilkS
Definition layer_ids.h:96
@ In4_Cu
Definition layer_ids.h:65
@ Eco2_User
Definition layer_ids.h:106
@ In16_Cu
Definition layer_ids.h:77
@ In24_Cu
Definition layer_ids.h:85
@ In1_Cu
Definition layer_ids.h:62
@ User_3
Definition layer_ids.h:122
@ User_1
Definition layer_ids.h:120
@ B_SilkS
Definition layer_ids.h:97
@ In13_Cu
Definition layer_ids.h:74
@ User_4
Definition layer_ids.h:123
@ In8_Cu
Definition layer_ids.h:69
@ In14_Cu
Definition layer_ids.h:75
@ In12_Cu
Definition layer_ids.h:73
@ In27_Cu
Definition layer_ids.h:88
@ In6_Cu
Definition layer_ids.h:67
@ In5_Cu
Definition layer_ids.h:66
@ In3_Cu
Definition layer_ids.h:64
@ In20_Cu
Definition layer_ids.h:81
@ F_Cu
Definition layer_ids.h:60
@ In18_Cu
Definition layer_ids.h:79
@ In25_Cu
Definition layer_ids.h:86
@ B_Fab
Definition layer_ids.h:114
std::optional< V > get_opt(const std::map< wxString, V > &aMap, const wxString &aKey)
Definition map_helpers.h:30
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ PTH
Plated through hole pad.
Definition padstack.h:97
@ RECTANGLE
Definition padstack.h:53
Class to handle a set of BOARD_ITEMs.
static const wxString MODEL_SIZE_KEY
static const wxString DIRECT_MODEL_UUID_KEY
static const int SHAPE_JOIN_DISTANCE
static const VECTOR2I HIDDEN_TEXT_SIZE(pcbIUScale.mmToIU(0.5), pcbIUScale.mmToIU(0.5))
static LIB_ID EasyEdaToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
static LIB_ID EasyEdaToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
@ CTX_FILENAME
@ CTX_LIBID
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
KIBIS_MODEL * model
bool cw
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_TOP
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
VECTOR3< double > VECTOR3D
Definition vector3.h:230
@ FULL
pads are covered by copper
Definition zones.h:47