145 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap, wxArrayString aShapes )
148 const wxString easyedaModelDir = wxS(
"EASYEDA_MODELS" );
149 wxString kicadModelPrefix = wxS(
"${KIPRJMOD}/" ) + easyedaModelDir + wxS(
"/" );
151 BOARD* board = aParent ? aParent :
dynamic_cast<BOARD*
>( aContainer );
154 auto getOrAddNetItem =
160 if( aNetName.empty() )
182 for( wxString shape : aShapes )
184 wxArrayString arr = wxSplit( shape,
'~',
'\0' );
186 wxString elType = arr[0];
187 if( elType == wxS(
"LIB" ) )
189 shape.Replace( wxS(
"#@$" ),
"\n" );
190 wxArrayString parts = wxSplit( shape,
'\n',
'\0' );
192 if( parts.size() < 1 )
195 wxArrayString paramsRoot = wxSplit( parts[0],
'~',
'\0' );
197 if( paramsRoot.size() < 4 )
202 wxString packageName =
203 wxString::Format( wxS(
"Unknown_%s_%s" ), paramsRoot[1], paramsRoot[2] );
205 wxArrayString paramParts = wxSplit( paramsRoot[3],
'`',
'\0' );
208 if( !paramsRoot[4].IsEmpty() )
213 if( !paramsRoot[7].IsEmpty() )
214 layer =
Convert( paramsRoot[7] );
216 std::map<wxString, wxString> innerParamMap;
218 for(
int i = 1; i < (int) paramParts.size(); i += 2 )
220 wxString key = paramParts[i - 1];
221 wxString value = paramParts[i];
223 if( key == wxS(
"package" ) )
226 innerParamMap[key] = value;
232 std::unique_ptr<FOOTPRINT> fp =
233 ParseFootprint( fpOrigin, orientation, layer, board, innerParamMap, aFootprintMap, parts );
240 fp->Move(
RelPos( fpOrigin ) );
244 else if( elType == wxS(
"TRACK" ) )
248 wxString netname = arr[3];
249 wxArrayString data = wxSplit( arr[4],
' ',
'\0' );
251 for(
int i = 3; i < (int) data.size(); i += 2 )
261 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aContainer );
263 track->SetLayer( layer );
264 track->SetWidth( width );
265 track->SetStart( start );
266 track->SetEnd(
end );
267 track->SetNet( getOrAddNetItem( netname ) );
273 std::unique_ptr<PCB_SHAPE> seg =
276 seg->SetLayer( layer );
277 seg->SetWidth( width );
278 seg->SetStart( start );
285 else if( elType == wxS(
"CIRCLE" ) )
287 auto circleShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::CIRCLE );
289 circleShape->SetWidth( width );
292 circleShape->SetLayer( layer );
300 circleShape->SetCenter(
center );
304 circleShape->SetNet( getOrAddNetItem( arr[8] ) );
308 else if( elType == wxS(
"RECT" ) )
312 rectShape->SetWidth( width );
315 rectShape->SetLayer( layer );
317 bool filled = arr[9] != wxS(
"none" );
318 rectShape->SetFilled( filled );
328 rectShape->SetStart( rectStart );
329 rectShape->SetEnd( rectStart + rectSize );
332 rectShape->SetNet( getOrAddNetItem( arr[11] ) );
336 else if( elType == wxS(
"ARC" ) )
338 std::unique_ptr<PCB_SHAPE> arcShape =
339 std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::ARC );
342 arcShape->SetWidth( arcWidth );
345 arcShape->SetLayer( layer );
348 arcShape->SetNet( getOrAddNetItem( arr[3] ) );
356 wxString data = arr[4];
357 auto readNumber = [&]( wxString& aOut )
359 wxUniChar ch = data[pos];
361 while( ch ==
' ' || ch ==
',' )
364 while( isdigit( ch ) || ch ==
'.' || ch ==
'-' )
369 if( pos == (
int) data.size() )
378 wxUniChar sym = data[pos++];
388 else if( sym ==
'A' )
390 wxString radX, radY,
unknown, farFlag, cwFlag, endX, endY;
394 readNumber( farFlag );
395 readNumber( cwFlag );
399 isFar = farFlag == wxS(
"1" );
400 cw = cwFlag == wxS(
"1" );
404 }
while( pos < (
int) data.size() );
408 double d =
delta.EuclideanNorm();
409 double h = sqrt( std::max( 0.0, rad.
x * rad.
x - d * d / 4 ) );
416 arcStart +
delta / 2 +
delta.Perpendicular().Resize( ( isFar ^
cw ) ? h : -h );
419 std::swap( arcStart, arcEnd );
421 arcShape->SetStart(
RelPos( arcStart ) );
422 arcShape->SetEnd(
RelPos( arcEnd ) );
423 arcShape->SetCenter(
RelPos( arcCenter ) );
427 else if( elType == wxS(
"DIMENSION" ) )
431 wxString shapeData = arr[2].Trim();
434 std::vector<SHAPE_LINE_CHAIN> lineChains =
437 std::unique_ptr<PCB_GROUP>
group = std::make_unique<PCB_GROUP>( aContainer );
438 group->SetName( wxS(
"Dimension" ) );
442 for(
int segId = 0; segId <
chain.SegmentCount(); segId++ )
447 dimSeg->SetLayer( layer );
448 dimSeg->SetWidth( lineWidth );
449 dimSeg->SetStart( seg.
A );
450 dimSeg->SetEnd( seg.
B );
452 group->AddItem( dimSeg.get() );
460 else if( elType == wxS(
"SOLIDREGION" ) )
462 wxString layer = arr[1];
472 validPolySet &= contour.PointCount() >= 3;
478 if( layer == wxS(
"11" ) )
482 auto cutoutShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::POLY );
485 cutoutShape->SetFilled(
false );
486 cutoutShape->SetWidth(
pcbIUScale.mmToIU( 0.1 ) );
487 cutoutShape->SetPolyShape( poly );
494 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
497 zone->SetLayer( klayer );
500 zone->SetNet( getOrAddNetItem( arr[2] ) );
503 zone->Outline()->AddPolygon( poly );
505 if( zone->GetNumCorners() == 0 )
508 if( arr[4].Lower() == wxS(
"cutout" ) )
510 zone->SetIsRuleArea(
true );
511 zone->SetDoNotAllowZoneFills(
true );
512 zone->SetDoNotAllowTracks(
false );
513 zone->SetDoNotAllowVias(
false );
514 zone->SetDoNotAllowPads(
false );
515 zone->SetDoNotAllowFootprints(
false );
519 zone->SetFilledPolysList( klayer, polySet );
521 zone->SetIsFilled(
true );
522 zone->SetNeedRefill(
false );
525 zone->SetMinThickness( 0 );
526 zone->SetLocalClearance( 0 );
527 zone->SetAssignedPriority( 100 );
532 else if( elType == wxS(
"COPPERAREA" ) )
534 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aContainer );
537 zone->SetLayer( layer );
539 wxString netname = arr[3];
542 zone->SetNet( getOrAddNetItem( netname ) );
545 zone->SetThermalReliefGap( zone->GetLocalClearance().value() );
547 wxString fillStyle = arr[5];
548 if( fillStyle == wxS(
"none" ) )
557 zone->Outline()->AddPolygon( poly );
559 wxString thermal = arr[8];
560 if( thermal == wxS(
"direct" ) )
563 wxString keepIsland = arr[9];
564 if( keepIsland == wxS(
"yes" ) )
567 wxString fillData = arr[10];
571 for(
const nlohmann::json& polyData : nlohmann::json::parse( fillData ) )
573 for(
const nlohmann::json& contourData : polyData )
580 for(
int i = 0; i < contourPolySet.
HoleCount( 0 ); i++ )
583 fillPolySet.
Append( currentOutline );
589 zone->SetFilledPolysList( layer, fillPolySet );
590 zone->SetIsFilled(
true );
591 zone->SetNeedRefill(
false );
593 catch( nlohmann::json::exception& )
597 int fillOrder = wxAtoi( arr[13] );
598 zone->SetAssignedPriority( 100 - fillOrder );
600 wxString improveFabrication = arr[17];
601 if( improveFabrication == wxS(
"none" ) )
603 zone->SetMinThickness( 0 );
608 int minThickness = std::max(
pcbIUScale.mmToIU( 0.03 ),
610 zone->SetMinThickness( minThickness );
613 if( arr.size() > 18 )
615 zone->SetThermalReliefSpokeWidth( std::max(
int(
ConvertSize( arr[18] ) ),
616 zone->GetMinThickness() ) );
620 wxFAIL_MSG( wxString::Format(
"COPPERAREA unexpected size %d: %s ",
624 zone->SetThermalReliefSpokeWidth( zone->GetMinThickness() );
629 else if( elType == wxS(
"SVGNODE" ) )
631 nlohmann::json nodeData = nlohmann::json::parse( arr[1] );
633 int nodeType = nodeData.at(
"nodeType" );
634 wxString layer = nodeData.at(
"layerid" );
639 std::map<wxString, wxString> attributes = nodeData.at(
"attrs" );
641 if( layer == wxS(
"19" ) )
646 auto ec_eType =
get_opt( attributes,
"c_etype" );
647 auto ec_rotation =
get_opt( attributes,
"c_rotation" );
648 auto ec_origin =
get_opt( attributes,
"c_origin" );
649 auto ec_width =
get_opt( attributes,
"c_width" );
650 auto ec_height =
get_opt( attributes,
"c_height" );
651 auto ez =
get_opt( attributes,
"z" );
652 auto etitle =
get_opt( attributes,
"title" );
653 auto euuid =
get_opt( attributes,
"uuid" );
654 auto etransform =
get_opt( attributes,
"transform" );
656 if( !ec_eType || *ec_eType != wxS(
"outline3D" ) || !etitle )
659 wxString modelTitle = *etitle;
670 footprint->
Add( field );
683 if( ec_width && ec_height )
688 double rounding = 0.001;
689 fitXmm =
KiROUND( fitXmm / rounding ) * rounding;
690 fitYmm =
KiROUND( fitYmm / rounding ) * rounding;
696 field->
SetText( wxString::FromCDouble( fitXmm ) + wxS(
" " )
697 + wxString::FromCDouble( fitYmm ) );
698 footprint->
Add( field );
703 wxArrayString orParts = wxSplit( *ec_origin,
',',
'\0' );
705 if( orParts.size() == 2 )
708 pos.
x =
Convert( orParts[0].Trim() );
709 pos.
y =
Convert( orParts[1].Trim() );
727 wxArrayString rotParts = wxSplit( *ec_rotation,
',',
'\0' );
729 if( rotParts.size() == 3 )
731 kmodelRotation.
x = -
Convert( rotParts[0].Trim() );
732 kmodelRotation.
y = -
Convert( rotParts[1].Trim() );
733 kmodelRotation.
z = -
Convert( rotParts[2].Trim() )
740 kmodelRotation.
z = 180 - kmodelRotation.
z;
745 model.m_Filename = kicadModelPrefix
748 model.m_Offset = kmodelOffset;
749 model.m_Rotation = kmodelRotation;
754 if(
auto dataStr =
get_opt( attributes,
"d" ) )
758 if( dataStr->size() >= 8000 )
765 std::unique_ptr<PCB_GROUP>
group;
768 group = std::make_unique<PCB_GROUP>( aContainer );
772 auto svgShape = std::make_unique<PCB_SHAPE>( aContainer,
SHAPE_T::POLY );
774 svgShape->SetFilled(
true );
775 svgShape->SetPolyShape( poly );
776 svgShape->SetLayer( klayer );
777 svgShape->SetWidth( 0 );
780 group->AddItem( svgShape.get() );
795 else if( elType == wxS(
"TEXT" ) )
798 wxString textType = arr[1];
800 if( footprint && textType == wxS(
"P" ) )
804 else if( footprint && textType == wxS(
"N" ) )
808 else if( arr[12] == wxS(
"none" ) )
822 text->SetPosition( start );
825 text->SetTextThickness( thickness );
827 double rot =
Convert( arr[5] );
828 text->SetTextAngleDegrees( rot );
834 text->SetLayer( layer );
837 text->SetMirrored(
true );
842 wxString textStr = arr[10];
843 textStr.Replace( wxS(
"\\n" ), wxS(
"\n" ) );
848 wxString font = arr[14];
849 if( !font.IsEmpty() )
854 else if( elType == wxS(
"VIA" ) )
862 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( footprint );
877 std::unique_ptr<PCB_VIA>
via = std::make_unique<PCB_VIA>( aContainer );
882 via->SetNet( getOrAddNetItem( arr[4] ) );
883 via->SetDrill( kdrill );
888 else if( elType == wxS(
"HOLE" ) )
894 wxString holeUuid = arr[4];
898 padContainer = footprint;
902 std::unique_ptr<FOOTPRINT> newFootprint =
903 std::make_unique<FOOTPRINT>(
dynamic_cast<BOARD*
>( aContainer ) );
905 wxString
name = wxS(
"Hole_" ) + holeUuid;
907 newFootprint->SetFPID(
LIB_ID( wxEmptyString,
name ) );
908 newFootprint->SetPosition(
center );
909 newFootprint->SetLocked(
true );
911 newFootprint->Reference().SetText(
name );
912 newFootprint->Reference().SetVisible(
false );
914 newFootprint->Value().SetText(
name );
915 newFootprint->Value().SetVisible(
false );
918 padContainer = newFootprint.get();
922 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( padContainer );
935 else if( elType == wxS(
"PAD" ) )
941 wxString padUuid = arr[12];
945 padContainer = footprint;
949 std::unique_ptr<FOOTPRINT> newFootprint =
950 std::make_unique<FOOTPRINT>(
dynamic_cast<BOARD*
>( aContainer ) );
952 wxString
name = wxS(
"Pad_" ) + padUuid;
954 newFootprint->SetFPID(
LIB_ID( wxEmptyString,
name ) );
955 newFootprint->SetPosition(
center );
956 newFootprint->SetLocked(
true );
958 newFootprint->Reference().SetText(
name );
959 newFootprint->Reference().SetVisible(
false );
961 newFootprint->Value().SetText(
name );
962 newFootprint->Value().SetVisible(
false );
965 padContainer = newFootprint.get();
969 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( padContainer );
971 pad->SetNet( getOrAddNetItem( arr[7] ) );
972 pad->SetNumber( arr[8] );
976 pad->SetOrientationDegrees(
Convert( arr[11] ) );
979 wxString elayer = arr[6];
982 bool plated = arr[15] == wxS(
"Y" );
990 else if( klayer ==
B_Cu )
996 else if( elayer == wxS(
"11" ) )
1003 pad->SetLayer( klayer );
1004 pad->SetLayerSet(
LSET( { klayer } ) );
1008 const wxString& padType = arr[1];
1010 if( padType == wxS(
"ELLIPSE" ) )
1014 else if( padType == wxS(
"RECT" ) )
1018 else if( padType == wxS(
"OVAL" ) )
1025 else if( padType == wxS(
"POLYGON" ) )
1031 wxArrayString data = wxSplit( arr[10],
' ',
'\0' );
1034 for(
int i = 1; i < (int) data.size(); i += 2 )
1041 chain.SetClosed(
true );
1044 chain.Rotate( -
pad->GetOrientation() );
1048 wxString holeDia = arr[9];
1050 if( !holeDia.IsEmpty() )
1055 wxString holeLength = arr[13];
1056 if( !holeLength.IsEmpty() )
1063 if( size.
x < size.
y )
1075 wxString pasteExp = arr[17];
1077 if( !pasteExp.IsEmpty() )
1080 pad->SetLocalSolderPasteMargin( pasteExpansion );
1083 wxString maskExp = arr[18];
1085 if( !maskExp.IsEmpty() )
1088 pad->SetLocalSolderMaskMargin( maskExpansion );
1091 bool badPad =
false;
1093 pad->Padstack().ForEachUniqueLayer(
1098 if( padSize.
x <= 0 || padSize.
y <= 0 )