144 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap, wxArrayString aShapes )
147 const wxString easyedaModelDir = wxS(
"EASYEDA_MODELS" );
148 wxString kicadModelPrefix = wxS(
"${KIPRJMOD}/" ) + easyedaModelDir + wxS(
"/" );
150 BOARD* board = aParent ? aParent :
dynamic_cast<BOARD*
>( aContainer );
153 auto getOrAddNetItem = [&](
const wxString& aNetName ) ->
NETINFO_ITEM*
158 if( aNetName.empty() )
180 for( wxString shape : aShapes )
182 wxArrayString arr = wxSplit( shape,
'~',
'\0' );
184 wxString elType = arr[0];
185 if( elType == wxS(
"LIB" ) )
187 shape.Replace( wxS(
"#@$" ),
"\n" );
188 wxArrayString parts = wxSplit( shape,
'\n',
'\0' );
190 if( parts.size() < 1 )
193 wxArrayString paramsRoot = wxSplit( parts[0],
'~',
'\0' );
195 if( paramsRoot.size() < 4 )
200 wxString packageName =
201 wxString::Format( wxS(
"Unknown_%s_%s" ), paramsRoot[1], paramsRoot[2] );
203 wxArrayString paramParts = wxSplit( paramsRoot[3],
'`',
'\0' );
206 if( !paramsRoot[4].IsEmpty() )
211 if( !paramsRoot[7].IsEmpty() )
212 layer =
Convert( paramsRoot[7] );
214 std::map<wxString, wxString> innerParamMap;
216 for(
int i = 1; i < (int) paramParts.size(); i += 2 )
218 wxString key = paramParts[i - 1];
219 wxString value = paramParts[i];
221 if( key == wxS(
"package" ) )
224 innerParamMap[key] = value;
231 aFootprintMap, parts );
242 else if( elType == wxS(
"TRACK" ) )
246 wxString netname = arr[3];
247 wxArrayString data = wxSplit( arr[4],
' ',
'\0' );
249 for(
int i = 3; i < (int) data.size(); i += 2 )
259 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aContainer );
261 track->SetLayer( layer );
262 track->SetWidth( width );
263 track->SetStart( start );
264 track->SetEnd(
end );
265 track->SetNet( getOrAddNetItem( netname ) );
271 std::unique_ptr<PCB_SHAPE> seg =
274 seg->SetLayer( layer );
275 seg->SetWidth( width );
276 seg->SetStart( start );
283 else if( elType == wxS(
"CIRCLE" ) )
285 auto circleShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::CIRCLE );
287 circleShape->SetWidth( width );
290 circleShape->SetLayer( layer );
298 circleShape->SetCenter(
center );
302 circleShape->SetNet( getOrAddNetItem( arr[8] ) );
306 else if( elType == wxS(
"RECT" ) )
310 rectShape->SetWidth( width );
313 rectShape->SetLayer( layer );
315 bool filled = arr[9] != wxS(
"none" );
316 rectShape->SetFilled( filled );
326 rectShape->SetStart( rectStart );
327 rectShape->SetEnd( rectStart + rectSize );
330 rectShape->SetNet( getOrAddNetItem( arr[11] ) );
334 else if( elType == wxS(
"ARC" ) )
336 std::unique_ptr<PCB_SHAPE> arcShape =
337 std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::ARC );
340 arcShape->SetWidth( arcWidth );
343 arcShape->SetLayer( layer );
346 arcShape->SetNet( getOrAddNetItem( arr[3] ) );
354 wxString data = arr[4];
355 auto readNumber = [&]( wxString& aOut )
357 wxUniChar ch = data[pos];
359 while( ch ==
' ' || ch ==
',' )
362 while( isdigit( ch ) || ch ==
'.' || ch ==
'-' )
367 if( pos == (
int) data.size() )
376 wxUniChar sym = data[pos++];
386 else if( sym ==
'A' )
388 wxString radX, radY,
unknown, farFlag, cwFlag, endX, endY;
392 readNumber( farFlag );
393 readNumber( cwFlag );
397 isFar = farFlag == wxS(
"1" );
398 cw = cwFlag == wxS(
"1" );
402 }
while( pos < (
int) data.size() );
406 double d =
delta.EuclideanNorm();
407 double h = sqrt( std::max( 0.0, rad.
x * rad.
x - d * d / 4 ) );
414 arcStart +
delta / 2 +
delta.Perpendicular().Resize( ( isFar ^
cw ) ? h : -h );
417 std::swap( arcStart, arcEnd );
419 arcShape->SetStart(
RelPos( arcStart ) );
420 arcShape->SetEnd(
RelPos( arcEnd ) );
421 arcShape->SetCenter(
RelPos( arcCenter ) );
425 else if( elType == wxS(
"DIMENSION" ) )
429 wxString shapeData = arr[2].Trim();
432 std::vector<SHAPE_LINE_CHAIN> lineChains =
435 std::unique_ptr<PCB_GROUP>
group = std::make_unique<PCB_GROUP>( aContainer );
436 group->SetName( wxS(
"Dimension" ) );
440 for(
int segId = 0; segId <
chain.SegmentCount(); segId++ )
445 dimSeg->SetLayer( layer );
446 dimSeg->SetWidth( lineWidth );
447 dimSeg->SetStart( seg.
A );
448 dimSeg->SetEnd( seg.
B );
450 group->AddItem( dimSeg.get() );
458 else if( elType == wxS(
"SOLIDREGION" ) )
460 wxString layer = arr[1];
465 if( layer == wxS(
"11" ) )
469 auto cutoutShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::POLY );
472 cutoutShape->SetFilled(
false );
473 cutoutShape->SetWidth(
pcbIUScale.mmToIU( 0.1 ) );
474 cutoutShape->SetPolyShape( poly );
481 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
484 zone->SetLayer( klayer );
487 zone->SetNet( getOrAddNetItem( arr[2] ) );
490 zone->Outline()->AddPolygon( poly );
492 if( arr[4].Lower() == wxS(
"cutout" ) )
494 zone->SetIsRuleArea(
true );
495 zone->SetDoNotAllowZoneFills(
true );
496 zone->SetDoNotAllowTracks(
false );
497 zone->SetDoNotAllowVias(
false );
498 zone->SetDoNotAllowPads(
false );
499 zone->SetDoNotAllowFootprints(
false );
503 zone->SetFilledPolysList( klayer, polySet );
505 zone->SetIsFilled(
true );
506 zone->SetNeedRefill(
false );
509 zone->SetMinThickness( 0 );
510 zone->SetLocalClearance( 0 );
511 zone->SetAssignedPriority( 100 );
516 else if( elType == wxS(
"COPPERAREA" ) )
518 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
521 zone->SetLayer( layer );
523 wxString netname = arr[3];
526 zone->SetNet( getOrAddNetItem( netname ) );
529 zone->SetThermalReliefGap( zone->GetLocalClearance().value() );
531 wxString fillStyle = arr[5];
532 if( fillStyle == wxS(
"none" ) )
541 zone->Outline()->AddPolygon( poly );
543 wxString thermal = arr[8];
544 if( thermal == wxS(
"direct" ) )
547 wxString keepIsland = arr[9];
548 if( keepIsland == wxS(
"yes" ) )
551 wxString fillData = arr[10];
555 for(
const nlohmann::json& polyData : nlohmann::json::parse( fillData ) )
557 for(
const nlohmann::json& contourData : polyData )
564 for(
int i = 1; i < contourPolySet.
OutlineCount(); i++ )
567 fillPolySet.
Append( currentOutline );
573 zone->SetFilledPolysList( layer, fillPolySet );
574 zone->SetIsFilled(
true );
575 zone->SetNeedRefill(
false );
577 catch( nlohmann::json::exception& )
581 int fillOrder = wxAtoi( arr[13] );
582 zone->SetAssignedPriority( 100 - fillOrder );
584 wxString improveFabrication = arr[17];
585 if( improveFabrication == wxS(
"none" ) )
587 zone->SetMinThickness( 0 );
592 int minThickness = std::max(
pcbIUScale.mmToIU( 0.03 ),
594 zone->SetMinThickness( minThickness );
597 if( arr.size() > 18 )
599 zone->SetThermalReliefSpokeWidth( std::max(
int(
ConvertSize( arr[18] ) ),
600 zone->GetMinThickness() ) );
604 wxFAIL_MSG( wxString::Format(
"COPPERAREA unexpected size %d: %s ",
608 zone->SetThermalReliefSpokeWidth( zone->GetMinThickness() );
613 else if( elType == wxS(
"SVGNODE" ) )
615 nlohmann::json nodeData = nlohmann::json::parse( arr[1] );
617 int nodeType = nodeData.at(
"nodeType" );
618 wxString layer = nodeData.at(
"layerid" );
623 std::map<wxString, wxString> attributes = nodeData.at(
"attrs" );
625 if( layer == wxS(
"19" ) )
630 auto ec_eType =
get_opt( attributes,
"c_etype" );
631 auto ec_rotation =
get_opt( attributes,
"c_rotation" );
632 auto ec_origin =
get_opt( attributes,
"c_origin" );
633 auto ec_width =
get_opt( attributes,
"c_width" );
634 auto ec_height =
get_opt( attributes,
"c_height" );
635 auto ez =
get_opt( attributes,
"z" );
636 auto etitle =
get_opt( attributes,
"title" );
637 auto euuid =
get_opt( attributes,
"uuid" );
638 auto etransform =
get_opt( attributes,
"transform" );
640 if( !ec_eType || *ec_eType != wxS(
"outline3D" ) || !etitle )
643 wxString modelTitle = *etitle;
654 footprint->
Add( field );
667 if( ec_width && ec_height )
672 double rounding = 0.001;
673 fitXmm =
KiROUND( fitXmm / rounding ) * rounding;
674 fitYmm =
KiROUND( fitYmm / rounding ) * rounding;
680 field->
SetText( wxString::FromCDouble( fitXmm ) + wxS(
" " )
681 + wxString::FromCDouble( fitYmm ) );
682 footprint->
Add( field );
687 wxArrayString orParts = wxSplit( *ec_origin,
',',
'\0' );
689 if( orParts.size() == 2 )
692 pos.
x =
Convert( orParts[0].Trim() );
693 pos.
y =
Convert( orParts[1].Trim() );
711 wxArrayString rotParts = wxSplit( *ec_rotation,
',',
'\0' );
713 if( rotParts.size() == 3 )
715 kmodelRotation.
x = -
Convert( rotParts[0].Trim() );
716 kmodelRotation.
y = -
Convert( rotParts[1].Trim() );
717 kmodelRotation.
z = -
Convert( rotParts[2].Trim() )
724 kmodelRotation.
z = 180 - kmodelRotation.
z;
729 model.m_Filename = kicadModelPrefix
732 model.m_Offset = kmodelOffset;
733 model.m_Rotation = kmodelRotation;
738 if(
auto dataStr =
get_opt( attributes,
"d" ) )
742 if( dataStr->size() >= 8000 )
749 std::unique_ptr<PCB_GROUP>
group;
752 group = std::make_unique<PCB_GROUP>( aContainer );
756 auto svgShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::POLY );
758 svgShape->SetFilled(
true );
759 svgShape->SetPolyShape( poly );
760 svgShape->SetLayer( klayer );
761 svgShape->SetWidth( 0 );
764 group->AddItem( svgShape.get() );
776 THROW_IO_ERROR( wxString::Format(
_(
"Unknown SVGNODE nodeType %d" ), nodeType ) );
779 else if( elType == wxS(
"TEXT" ) )
782 wxString textType = arr[1];
784 if( footprint && textType == wxS(
"P" ) )
788 else if( footprint && textType == wxS(
"N" ) )
792 else if( arr[12] == wxS(
"none" ) )
806 text->SetPosition( start );
809 text->SetTextThickness( thickness );
811 double rot =
Convert( arr[5] );
812 text->SetTextAngleDegrees( rot );
818 text->SetLayer( layer );
821 text->SetMirrored(
true );
826 wxString textStr = arr[10];
827 textStr.Replace( wxS(
"\\n" ), wxS(
"\n" ) );
832 wxString font = arr[14];
833 if( !font.IsEmpty() )
838 else if( elType == wxS(
"VIA" ) )
846 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( footprint );
860 std::unique_ptr<PCB_VIA>
via = std::make_unique<PCB_VIA>( aContainer );
865 via->SetNet( getOrAddNetItem( arr[4] ) );
866 via->SetDrill( kdrill );
871 else if( elType == wxS(
"HOLE" ) )
877 wxString holeUuid = arr[4];
881 padContainer = footprint;
885 std::unique_ptr<FOOTPRINT> newFootprint =
886 std::make_unique<FOOTPRINT>(
dynamic_cast<BOARD*
>( aContainer ) );
888 wxString
name = wxS(
"Hole_" ) + holeUuid;
890 newFootprint->SetFPID(
LIB_ID( wxEmptyString,
name ) );
891 newFootprint->SetPosition(
center );
892 newFootprint->SetLocked(
true );
894 newFootprint->Reference().SetText(
name );
895 newFootprint->Reference().SetVisible(
false );
897 newFootprint->Value().SetText(
name );
898 newFootprint->Value().SetVisible(
false );
901 padContainer = newFootprint.get();
905 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( padContainer );
917 else if( elType == wxS(
"PAD" ) )
923 wxString padUuid = arr[12];
927 padContainer = footprint;
931 std::unique_ptr<FOOTPRINT> newFootprint =
932 std::make_unique<FOOTPRINT>(
dynamic_cast<BOARD*
>( aContainer ) );
934 wxString
name = wxS(
"Pad_" ) + padUuid;
936 newFootprint->SetFPID(
LIB_ID( wxEmptyString,
name ) );
937 newFootprint->SetPosition(
center );
938 newFootprint->SetLocked(
true );
940 newFootprint->Reference().SetText(
name );
941 newFootprint->Reference().SetVisible(
false );
943 newFootprint->Value().SetText(
name );
944 newFootprint->Value().SetVisible(
false );
947 padContainer = newFootprint.get();
951 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( padContainer );
953 pad->SetNet( getOrAddNetItem( arr[7] ) );
954 pad->SetNumber( arr[8] );
957 pad->SetOrientationDegrees(
Convert( arr[11] ) );
960 wxString elayer = arr[6];
963 bool plated = arr[15] == wxS(
"Y" );
971 else if( klayer ==
B_Cu )
977 else if( elayer == wxS(
"11" ) )
984 pad->SetLayer( klayer );
985 pad->SetLayerSet(
LSET( { klayer } ) );
989 wxString padType = arr[1];
990 if( padType == wxS(
"ELLIPSE" ) )
994 else if( padType == wxS(
"RECT" ) )
998 else if( padType == wxS(
"OVAL" ) )
1000 if(
pad->GetSizeX() ==
pad->GetSizeY() )
1005 else if( padType == wxS(
"POLYGON" ) )
1011 wxArrayString data = wxSplit( arr[10],
' ',
'\0' );
1014 for(
int i = 1; i < (int) data.size(); i += 2 )
1021 chain.SetClosed(
true );
1024 chain.Rotate( -
pad->GetOrientation() );
1028 wxString holeDia = arr[9];
1029 if( !holeDia.IsEmpty() )
1034 wxString holeLength = arr[13];
1035 if( !holeLength.IsEmpty() )
1042 if( size.
x < size.
y )
1054 wxString pasteExp = arr[17];
1055 if( !pasteExp.IsEmpty() )
1058 pad->SetLocalSolderPasteMargin( pasteExpansion );
1061 wxString maskExp = arr[18];
1062 if( !maskExp.IsEmpty() )
1065 pad->SetLocalSolderMaskMargin( maskExpansion );