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;
230 std::unique_ptr<FOOTPRINT> fp =
231 ParseFootprint( fpOrigin, orientation, layer, board, innerParamMap, aFootprintMap, parts );
238 fp->Move(
RelPos( fpOrigin ) );
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];
470 validPolySet &= contour.PointCount() >= 3;
476 if( layer == wxS(
"11" ) )
480 auto cutoutShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::POLY );
483 cutoutShape->SetFilled(
false );
484 cutoutShape->SetWidth(
pcbIUScale.mmToIU( 0.1 ) );
485 cutoutShape->SetPolyShape( poly );
492 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
495 zone->SetLayer( klayer );
498 zone->SetNet( getOrAddNetItem( arr[2] ) );
501 zone->Outline()->AddPolygon( poly );
503 if( zone->GetNumCorners() == 0 )
506 if( arr[4].Lower() == wxS(
"cutout" ) )
508 zone->SetIsRuleArea(
true );
509 zone->SetDoNotAllowZoneFills(
true );
510 zone->SetDoNotAllowTracks(
false );
511 zone->SetDoNotAllowVias(
false );
512 zone->SetDoNotAllowPads(
false );
513 zone->SetDoNotAllowFootprints(
false );
517 zone->SetFilledPolysList( klayer, polySet );
519 zone->SetIsFilled(
true );
520 zone->SetNeedRefill(
false );
523 zone->SetMinThickness( 0 );
524 zone->SetLocalClearance( 0 );
525 zone->SetAssignedPriority( 100 );
530 else if( elType == wxS(
"COPPERAREA" ) )
532 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
535 zone->SetLayer( layer );
537 wxString netname = arr[3];
540 zone->SetNet( getOrAddNetItem( netname ) );
543 zone->SetThermalReliefGap( zone->GetLocalClearance().value() );
545 wxString fillStyle = arr[5];
546 if( fillStyle == wxS(
"none" ) )
555 zone->Outline()->AddPolygon( poly );
557 wxString thermal = arr[8];
558 if( thermal == wxS(
"direct" ) )
561 wxString keepIsland = arr[9];
562 if( keepIsland == wxS(
"yes" ) )
565 wxString fillData = arr[10];
569 for(
const nlohmann::json& polyData : nlohmann::json::parse( fillData ) )
571 for(
const nlohmann::json& contourData : polyData )
578 for(
int i = 0; i < contourPolySet.
HoleCount( 0 ); i++ )
581 fillPolySet.
Append( currentOutline );
587 zone->SetFilledPolysList( layer, fillPolySet );
588 zone->SetIsFilled(
true );
589 zone->SetNeedRefill(
false );
591 catch( nlohmann::json::exception& )
595 int fillOrder = wxAtoi( arr[13] );
596 zone->SetAssignedPriority( 100 - fillOrder );
598 wxString improveFabrication = arr[17];
599 if( improveFabrication == wxS(
"none" ) )
601 zone->SetMinThickness( 0 );
606 int minThickness = std::max(
pcbIUScale.mmToIU( 0.03 ),
608 zone->SetMinThickness( minThickness );
611 if( arr.size() > 18 )
613 zone->SetThermalReliefSpokeWidth( std::max(
int(
ConvertSize( arr[18] ) ),
614 zone->GetMinThickness() ) );
618 wxFAIL_MSG( wxString::Format(
"COPPERAREA unexpected size %d: %s ",
622 zone->SetThermalReliefSpokeWidth( zone->GetMinThickness() );
627 else if( elType == wxS(
"SVGNODE" ) )
629 nlohmann::json nodeData = nlohmann::json::parse( arr[1] );
631 int nodeType = nodeData.at(
"nodeType" );
632 wxString layer = nodeData.at(
"layerid" );
637 std::map<wxString, wxString> attributes = nodeData.at(
"attrs" );
639 if( layer == wxS(
"19" ) )
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" );
654 if( !ec_eType || *ec_eType != wxS(
"outline3D" ) || !etitle )
657 wxString modelTitle = *etitle;
668 footprint->
Add( field );
681 if( ec_width && ec_height )
686 double rounding = 0.001;
687 fitXmm =
KiROUND( fitXmm / rounding ) * rounding;
688 fitYmm =
KiROUND( fitYmm / rounding ) * rounding;
694 field->
SetText( wxString::FromCDouble( fitXmm ) + wxS(
" " )
695 + wxString::FromCDouble( fitYmm ) );
696 footprint->
Add( field );
701 wxArrayString orParts = wxSplit( *ec_origin,
',',
'\0' );
703 if( orParts.size() == 2 )
706 pos.
x =
Convert( orParts[0].Trim() );
707 pos.
y =
Convert( orParts[1].Trim() );
725 wxArrayString rotParts = wxSplit( *ec_rotation,
',',
'\0' );
727 if( rotParts.size() == 3 )
729 kmodelRotation.
x = -
Convert( rotParts[0].Trim() );
730 kmodelRotation.
y = -
Convert( rotParts[1].Trim() );
731 kmodelRotation.
z = -
Convert( rotParts[2].Trim() )
738 kmodelRotation.
z = 180 - kmodelRotation.
z;
743 model.m_Filename = kicadModelPrefix
746 model.m_Offset = kmodelOffset;
747 model.m_Rotation = kmodelRotation;
752 if(
auto dataStr =
get_opt( attributes,
"d" ) )
756 if( dataStr->size() >= 8000 )
763 std::unique_ptr<PCB_GROUP>
group;
766 group = std::make_unique<PCB_GROUP>( aContainer );
770 auto svgShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::POLY );
772 svgShape->SetFilled(
true );
773 svgShape->SetPolyShape( poly );
774 svgShape->SetLayer( klayer );
775 svgShape->SetWidth( 0 );
778 group->AddItem( svgShape.get() );
793 else if( elType == wxS(
"TEXT" ) )
796 wxString textType = arr[1];
798 if( footprint && textType == wxS(
"P" ) )
802 else if( footprint && textType == wxS(
"N" ) )
806 else if( arr[12] == wxS(
"none" ) )
820 text->SetPosition( start );
823 text->SetTextThickness( thickness );
825 double rot =
Convert( arr[5] );
826 text->SetTextAngleDegrees( rot );
832 text->SetLayer( layer );
835 text->SetMirrored(
true );
840 wxString textStr = arr[10];
841 textStr.Replace( wxS(
"\\n" ), wxS(
"\n" ) );
846 wxString font = arr[14];
847 if( !font.IsEmpty() )
852 else if( elType == wxS(
"VIA" ) )
860 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( footprint );
875 std::unique_ptr<PCB_VIA>
via = std::make_unique<PCB_VIA>( aContainer );
880 via->SetNet( getOrAddNetItem( arr[4] ) );
881 via->SetDrill( kdrill );
886 else if( elType == wxS(
"HOLE" ) )
892 wxString holeUuid = arr[4];
896 padContainer = footprint;
900 std::unique_ptr<FOOTPRINT> newFootprint =
901 std::make_unique<FOOTPRINT>(
dynamic_cast<BOARD*
>( aContainer ) );
903 wxString
name = wxS(
"Hole_" ) + holeUuid;
905 newFootprint->SetFPID(
LIB_ID( wxEmptyString,
name ) );
906 newFootprint->SetPosition(
center );
907 newFootprint->SetLocked(
true );
909 newFootprint->Reference().SetText(
name );
910 newFootprint->Reference().SetVisible(
false );
912 newFootprint->Value().SetText(
name );
913 newFootprint->Value().SetVisible(
false );
916 padContainer = newFootprint.get();
920 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( padContainer );
933 else if( elType == wxS(
"PAD" ) )
939 wxString padUuid = arr[12];
943 padContainer = footprint;
947 std::unique_ptr<FOOTPRINT> newFootprint =
948 std::make_unique<FOOTPRINT>(
dynamic_cast<BOARD*
>( aContainer ) );
950 wxString
name = wxS(
"Pad_" ) + padUuid;
952 newFootprint->SetFPID(
LIB_ID( wxEmptyString,
name ) );
953 newFootprint->SetPosition(
center );
954 newFootprint->SetLocked(
true );
956 newFootprint->Reference().SetText(
name );
957 newFootprint->Reference().SetVisible(
false );
959 newFootprint->Value().SetText(
name );
960 newFootprint->Value().SetVisible(
false );
963 padContainer = newFootprint.get();
967 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( padContainer );
969 pad->SetNet( getOrAddNetItem( arr[7] ) );
970 pad->SetNumber( arr[8] );
974 pad->SetOrientationDegrees(
Convert( arr[11] ) );
977 wxString elayer = arr[6];
980 bool plated = arr[15] == wxS(
"Y" );
988 else if( klayer ==
B_Cu )
994 else if( elayer == wxS(
"11" ) )
1001 pad->SetLayer( klayer );
1002 pad->SetLayerSet(
LSET( { klayer } ) );
1006 const wxString& padType = arr[1];
1008 if( padType == wxS(
"ELLIPSE" ) )
1012 else if( padType == wxS(
"RECT" ) )
1016 else if( padType == wxS(
"OVAL" ) )
1018 if(
pad->GetSizeX() ==
pad->GetSizeY() )
1023 else if( padType == wxS(
"POLYGON" ) )
1029 wxArrayString data = wxSplit( arr[10],
' ',
'\0' );
1032 for(
int i = 1; i < (int) data.size(); i += 2 )
1039 chain.SetClosed(
true );
1042 chain.Rotate( -
pad->GetOrientation() );
1046 wxString holeDia = arr[9];
1047 if( !holeDia.IsEmpty() )
1052 wxString holeLength = arr[13];
1053 if( !holeLength.IsEmpty() )
1060 if( size.
x < size.
y )
1072 wxString pasteExp = arr[17];
1073 if( !pasteExp.IsEmpty() )
1076 pad->SetLocalSolderPasteMargin( pasteExpansion );
1079 wxString maskExp = arr[18];
1080 if( !maskExp.IsEmpty() )
1083 pad->SetLocalSolderMaskMargin( maskExpansion );