/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 Alex Shvartzkop * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "pcb_io_easyeda_parser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const wxString DIRECT_MODEL_UUID_KEY = wxS( "JLC_3DModel" ); static const wxString MODEL_SIZE_KEY = wxS( "JLC_3D_Size" ); static const int SHAPE_JOIN_DISTANCE = pcbIUScale.mmToIU( 1.5 ); static const VECTOR2I HIDDEN_TEXT_SIZE( pcbIUScale.mmToIU( 0.5 ), pcbIUScale.mmToIU( 0.5 ) ); PCB_IO_EASYEDA_PARSER::PCB_IO_EASYEDA_PARSER( PROGRESS_REPORTER* aProgressReporter ) { } PCB_IO_EASYEDA_PARSER::~PCB_IO_EASYEDA_PARSER() { } PCB_LAYER_ID PCB_IO_EASYEDA_PARSER::LayerToKi( const wxString& aLayer ) { int elayer = wxAtoi( aLayer ); switch( elayer ) { case 1: return F_Cu; case 2: return B_Cu; case 3: return F_SilkS; case 4: return B_SilkS; case 5: return F_Paste; case 6: return B_Paste; case 7: return F_Mask; case 8: return B_Mask; /*case 9: return UNDEFINED_LAYER;*/ // Ratsnest case 10: return Edge_Cuts; case 11: return Eco1_User; case 12: return Dwgs_User; case 13: return F_Fab; case 14: return B_Fab; case 15: return Eco2_User; case 19: return User_2; // 3D model case 21: return In1_Cu; case 22: return In2_Cu; case 23: return In3_Cu; case 24: return In4_Cu; case 25: return In5_Cu; case 26: return In6_Cu; case 27: return In7_Cu; case 28: return In8_Cu; case 29: return In9_Cu; case 30: return In10_Cu; case 31: return In11_Cu; case 32: return In12_Cu; case 33: return In13_Cu; case 34: return In14_Cu; case 35: return In15_Cu; case 36: return In16_Cu; case 37: return In17_Cu; case 38: return In18_Cu; case 39: return In19_Cu; case 40: return In20_Cu; case 41: return In21_Cu; case 42: return In22_Cu; case 43: return In23_Cu; case 44: return In24_Cu; case 45: return In25_Cu; case 46: return In26_Cu; case 47: return In27_Cu; case 48: return In28_Cu; case 49: return In29_Cu; case 50: return In30_Cu; case 99: return User_3; case 100: return User_4; case 101: return User_5; default: break; } return User_1; } static LIB_ID EasyEdaToKiCadLibID( const wxString& aLibName, const wxString& aLibReference ) { wxString libReference = EscapeString( aLibReference, CTX_LIBID ); wxString key = !aLibName.empty() ? ( aLibName + ':' + libReference ) : libReference; LIB_ID libId; libId.Parse( key, true ); return libId; } void PCB_IO_EASYEDA_PARSER::ParseToBoardItemContainer( BOARD_ITEM_CONTAINER* aContainer, BOARD* aParent, std::map paramMap, std::map>& aFootprintMap, wxArrayString aShapes ) { // TODO: make this path configurable? const wxString easyedaModelDir = wxS( "EASYEDA_MODELS" ); wxString kicadModelPrefix = wxS( "${KIPRJMOD}/" ) + easyedaModelDir + wxS( "/" ); BOARD* board = aParent ? aParent : dynamic_cast( aContainer ); FOOTPRINT* footprint = dynamic_cast( aContainer ); auto getOrAddNetItem = [&]( const wxString& aNetName ) -> NETINFO_ITEM* { if( !board ) return nullptr; if( aNetName.empty() ) return nullptr; if( NETINFO_ITEM* item = board->FindNet( aNetName ) ) { return item; } else { item = new NETINFO_ITEM( board, aNetName, board->GetNetCount() + 1 ); board->Add( item, ADD_MODE::APPEND ); return item; } }; if( footprint ) { // TODO: library name LIB_ID fpID = EasyEdaToKiCadLibID( wxEmptyString, paramMap[wxS( "package" )] ); footprint->SetFPID( fpID ); } for( wxString shape : aShapes ) { wxArrayString arr = wxSplit( shape, '~', '\0' ); wxString elType = arr[0]; if( elType == wxS( "LIB" ) ) { shape.Replace( wxS( "#@$" ), "\n" ); wxArrayString parts = wxSplit( shape, '\n', '\0' ); if( parts.size() < 1 ) continue; wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' ); if( paramsRoot.size() < 4 ) continue; VECTOR2D fpOrigin( Convert( paramsRoot[1] ), Convert( paramsRoot[2] ) ); wxString packageName = wxString::Format( wxS( "Unknown_%s_%s" ), paramsRoot[1], paramsRoot[2] ); wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' ); EDA_ANGLE orientation; if( !paramsRoot[4].IsEmpty() ) orientation = EDA_ANGLE( Convert( paramsRoot[4] ), DEGREES_T ); // Already applied int layer = 1; if( !paramsRoot[7].IsEmpty() ) layer = Convert( paramsRoot[7] ); std::map paramMap; for( int i = 1; i < paramParts.size(); i += 2 ) { wxString key = paramParts[i - 1]; wxString value = paramParts[i]; if( key == wxS( "package" ) ) packageName = value; paramMap[key] = value; } parts.RemoveAt( 0 ); VECTOR2D pcbOrigin = m_relOrigin; FOOTPRINT* fp = ParseFootprint( fpOrigin, orientation, layer, board, paramMap, aFootprintMap, parts ); if( !fp ) continue; m_relOrigin = pcbOrigin; fp->Move( RelPos( fpOrigin ) ); aContainer->Add( fp, ADD_MODE::APPEND ); } else if( elType == wxS( "TRACK" ) ) { double width = ConvertSize( arr[1] ); PCB_LAYER_ID layer = LayerToKi( arr[2] ); wxString netname = arr[3]; wxArrayString data = wxSplit( arr[4], ' ', '\0' ); for( int i = 3; i < data.size(); i += 2 ) { VECTOR2D start, end; start.x = RelPosX( data[i - 3] ); start.y = RelPosY( data[i - 2] ); end.x = RelPosX( data[i - 1] ); end.y = RelPosY( data[i] ); if( !footprint && IsCopperLayer( layer ) ) { std::unique_ptr track = std::make_unique( aContainer ); track->SetLayer( layer ); track->SetWidth( width ); track->SetStart( start ); track->SetEnd( end ); track->SetNet( getOrAddNetItem( netname ) ); aContainer->Add( track.release(), ADD_MODE::APPEND ); } else { std::unique_ptr seg = std::make_unique( aContainer, SHAPE_T::SEGMENT ); seg->SetLayer( layer ); seg->SetWidth( width ); seg->SetStart( start ); seg->SetEnd( end ); aContainer->Add( seg.release(), ADD_MODE::APPEND ); } } } else if( elType == wxS( "CIRCLE" ) ) { std::unique_ptr shape = std::make_unique( aContainer, SHAPE_T::CIRCLE ); double width = ConvertSize( arr[4] ); shape->SetWidth( width ); PCB_LAYER_ID layer = LayerToKi( arr[5] ); shape->SetLayer( layer ); VECTOR2D center; center.x = RelPosX( arr[1] ); center.y = RelPosY( arr[2] ); double radius = ConvertSize( arr[3] ); shape->SetCenter( center ); shape->SetEnd( center + VECTOR2I( radius, 0 ) ); if( IsCopperLayer( layer ) ) shape->SetNet( getOrAddNetItem( arr[8] ) ); aContainer->Add( shape.release(), ADD_MODE::APPEND ); } else if( elType == wxS( "RECT" ) ) { std::unique_ptr shape = std::make_unique( aContainer, SHAPE_T::RECTANGLE ); double width = ConvertSize( arr[8] ); shape->SetWidth( width ); PCB_LAYER_ID layer = LayerToKi( arr[5] ); shape->SetLayer( layer ); bool filled = arr[9] != wxS( "none" ); shape->SetFilled( filled ); VECTOR2D start; start.x = RelPosX( arr[1] ); start.y = RelPosY( arr[2] ); VECTOR2D size; size.x = ConvertSize( arr[3] ); size.y = ConvertSize( arr[4] ); shape->SetStart( start ); shape->SetEnd( start + size ); if( IsCopperLayer( layer ) ) shape->SetNet( getOrAddNetItem( arr[11] ) ); aContainer->Add( shape.release(), ADD_MODE::APPEND ); } else if( elType == wxS( "ARC" ) ) { std::unique_ptr shape = std::make_unique( aContainer, SHAPE_T::ARC ); double width = ConvertSize( arr[1] ); shape->SetWidth( width ); PCB_LAYER_ID layer = LayerToKi( arr[2] ); shape->SetLayer( layer ); if( IsCopperLayer( layer ) ) shape->SetNet( getOrAddNetItem( arr[3] ) ); VECTOR2D start, end; VECTOR2D rad( 10, 10 ); bool isFar = false; bool cw = false; int pos = 0; wxString data = arr[4]; auto readNumber = [&]( wxString& aOut ) { wxUniChar ch = data[pos]; while( ch == ' ' || ch == ',' ) ch = data[++pos]; while( isdigit( ch ) || ch == '.' || ch == '-' ) { aOut += ch; pos++; if( pos == data.size() ) break; ch = data[pos]; } }; do { wxUniChar sym = data[pos++]; if( sym == 'M' ) { wxString xStr, yStr; readNumber( xStr ); readNumber( yStr ); start = VECTOR2D( Convert( xStr ), Convert( yStr ) ); } else if( sym == 'A' ) { wxString radX, radY, unknown, farFlag, cwFlag, endX, endY; readNumber( radX ); readNumber( radY ); readNumber( unknown ); readNumber( farFlag ); readNumber( cwFlag ); readNumber( endX ); readNumber( endY ); isFar = farFlag == wxS( "1" ); cw = cwFlag == wxS( "1" ); rad = VECTOR2D( Convert( radX ), Convert( radY ) ); end = VECTOR2D( Convert( endX ), Convert( endY ) ); } } while( pos < data.size() ); VECTOR2D delta = end - start; double d = delta.EuclideanNorm(); double h = sqrt( std::max( 0.0, rad.x * rad.x - d * d / 4 ) ); //( !far && cw ) => h //( far && cw ) => -h //( !far && !cw ) => -h //( far && !cw ) => h VECTOR2D arcCenter = start + delta / 2 + delta.Perpendicular().Resize( ( isFar ^ cw ) ? h : -h ); if( !cw ) std::swap( start, end ); shape->SetStart( RelPos( start ) ); shape->SetEnd( RelPos( end ) ); shape->SetCenter( RelPos( arcCenter ) ); aContainer->Add( shape.release(), ADD_MODE::APPEND ); } else if( elType == wxS( "DIMENSION" ) ) { PCB_LAYER_ID layer = LayerToKi( arr[1] ); double lineWidth = ConvertSize( arr[7] ); wxString shapeData = arr[2].Trim(); //double textHeight = !arr[4].IsEmpty() ? ConvertSize( arr[4] ) : 0; std::vector lineChains = ParseLineChains( shapeData, pcbIUScale.mmToIU( 0.01 ), false ); std::unique_ptr group = std::make_unique( aContainer ); group->SetName( wxS( "Dimension" ) ); for( const SHAPE_LINE_CHAIN& chain : lineChains ) { for( int segId = 0; segId < chain.SegmentCount(); segId++ ) { SEG seg = chain.CSegment( segId ); std::unique_ptr shape = std::make_unique( aContainer, SHAPE_T::SEGMENT ); shape->SetLayer( layer ); shape->SetWidth( lineWidth ); shape->SetStart( seg.A ); shape->SetEnd( seg.B ); group->AddItem( shape.get() ); aContainer->Add( shape.release(), ADD_MODE::APPEND ); } } aContainer->Add( group.release(), ADD_MODE::APPEND ); } else if( elType == wxS( "SOLIDREGION" ) ) { wxString layer = arr[1]; SHAPE_POLY_SET polySet = ParseLineChains( arr[3].Trim(), pcbIUScale.mmToIU( 0.01 ), true ); if( layer == wxS( "11" ) ) // Multi-layer (board cutout) { for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() ) { std::unique_ptr shape = std::make_unique( aContainer, SHAPE_T::POLY ); shape->SetLayer( Edge_Cuts ); shape->SetFilled( false ); shape->SetWidth( pcbIUScale.mmToIU( 0.1 ) ); shape->SetPolyShape( poly ); aContainer->Add( shape.release(), ADD_MODE::APPEND ); } } else { std::unique_ptr zone = std::make_unique( aContainer ); PCB_LAYER_ID klayer = LayerToKi( layer ); zone->SetLayer( klayer ); if( IsCopperLayer( klayer ) ) zone->SetNet( getOrAddNetItem( arr[2] ) ); for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() ) zone->Outline()->AddPolygon( poly ); if( arr[4].Lower() == wxS( "cutout" ) ) { zone->SetIsRuleArea( true ); zone->SetDoNotAllowCopperPour( true ); zone->SetDoNotAllowTracks( false ); zone->SetDoNotAllowVias( false ); zone->SetDoNotAllowPads( false ); zone->SetDoNotAllowFootprints( false ); } else { // solid zone->SetFilledPolysList( klayer, polySet ); zone->SetPadConnection( ZONE_CONNECTION::FULL ); zone->SetIsFilled( true ); zone->SetNeedRefill( false ); } zone->SetMinThickness( 0 ); zone->SetLocalClearance( 0 ); zone->SetAssignedPriority( 100 ); aContainer->Add( zone.release(), ADD_MODE::APPEND ); } } else if( elType == wxS( "COPPERAREA" ) ) { std::unique_ptr zone = std::make_unique( aContainer ); PCB_LAYER_ID layer = LayerToKi( arr[2] ); zone->SetLayer( layer ); wxString netname = arr[3]; if( IsCopperLayer( layer ) ) zone->SetNet( getOrAddNetItem( netname ) ); zone->SetLocalClearance( ConvertSize( arr[5] ) ); zone->SetThermalReliefGap( zone->GetLocalClearance() ); wxString fillStyle = arr[5]; if( fillStyle == wxS( "none" ) ) { // Do not fill? } SHAPE_POLY_SET polySet = ParseLineChains( arr[4].Trim(), pcbIUScale.mmToIU( 0.01 ), true ); for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() ) zone->Outline()->AddPolygon( poly ); wxString thermal = arr[8]; if( thermal == wxS( "direct" ) ) zone->SetPadConnection( ZONE_CONNECTION::FULL ); wxString keepIsland = arr[9]; if( keepIsland == wxS( "yes" ) ) zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER ); wxString fillData = arr[10]; SHAPE_POLY_SET fillPolySet; try { for( const nlohmann::json& polyData : nlohmann::json::parse( fillData ) ) { for( const nlohmann::json& contourData : polyData ) { SHAPE_POLY_SET contourPolySet = ParseLineChains( contourData.get(), pcbIUScale.mmToIU( 0.01 ), true ); SHAPE_POLY_SET currentOutline( contourPolySet.COutline( 0 ) ); for( int i = 1; i < contourPolySet.OutlineCount(); i++ ) currentOutline.AddHole( contourPolySet.COutline( i ) ); fillPolySet.Append( currentOutline ); } } fillPolySet.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); zone->SetFilledPolysList( layer, fillPolySet ); zone->SetIsFilled( true ); zone->SetNeedRefill( false ); } catch( nlohmann::json::exception& e ) { } int fillOrder = wxAtoi( arr[13] ); zone->SetAssignedPriority( 100 - fillOrder ); wxString improveFabrication = arr[17]; if( improveFabrication == wxS( "none" ) ) { zone->SetMinThickness( 0 ); } else { // arr[1] is "stroke Width" per docs int minThickness = std::max( pcbIUScale.mmToIU( 0.03 ), int( ConvertSize( arr[1] ) ) ); zone->SetMinThickness( minThickness ); } if( arr.size() > 18 ) { zone->SetThermalReliefSpokeWidth( std::max( int( ConvertSize( arr[18] ) ), zone->GetMinThickness() ) ); } else { wxFAIL_MSG( wxString::Format( "COPPERAREA unexpected size %d: %s ", arr.size(), shape ) ); zone->SetThermalReliefSpokeWidth( zone->GetMinThickness() ); } aContainer->Add( zone.release(), ADD_MODE::APPEND ); } else if( elType == wxS( "SVGNODE" ) ) { nlohmann::json nodeData = nlohmann::json::parse( arr[1] ); int nodeType = nodeData.at( "nodeType" ); wxString layer = nodeData.at( "layerid" ); PCB_LAYER_ID klayer = LayerToKi( layer ); if( nodeType == 1 ) { std::map attributes = nodeData.at( "attrs" ); if( layer == wxS( "19" ) ) // 3DModel { if( !footprint ) continue; auto ec_eType = get_opt( attributes, "c_etype" ); auto ec_rotation = get_opt( attributes, "c_rotation" ); auto ec_origin = get_opt( attributes, "c_origin" ); auto ec_width = get_opt( attributes, "c_width" ); auto ec_height = get_opt( attributes, "c_height" ); auto ez = get_opt( attributes, "z" ); auto etitle = get_opt( attributes, "title" ); auto euuid = get_opt( attributes, "uuid" ); auto etransform = get_opt( attributes, "transform" ); if( !ec_eType || *ec_eType != wxS( "outline3D" ) || !etitle ) continue; wxString modelTitle = *etitle; VECTOR3D kmodelOffset; VECTOR3D kmodelRotation; if( euuid ) { PCB_FIELD field( footprint, footprint->GetFieldCount(), DIRECT_MODEL_UUID_KEY ); field.SetLayer( Cmts_User ); field.SetVisible( false ); field.SetText( *euuid ); footprint->AddField( field ); } /*if( etransform ) { PCB_FIELD field( footprint, footprint->GetFieldCount(), "3D Transform" ); field.SetLayer( Cmts_User ); field.SetVisible( false ); field.SetText( *etransform ); footprint->AddField( field ); }*/ if( ec_width && ec_height ) { double fitXmm = pcbIUScale.IUTomm( ScaleSize( Convert( *ec_width ) ) ); double fitYmm = pcbIUScale.IUTomm( ScaleSize( Convert( *ec_height ) ) ); double rounding = 0.001; fitXmm = KiROUND( fitXmm / rounding ) * rounding; fitYmm = KiROUND( fitYmm / rounding ) * rounding; PCB_FIELD field( footprint, footprint->GetFieldCount(), MODEL_SIZE_KEY ); field.SetLayer( Cmts_User ); field.SetVisible( false ); field.SetText( wxString::FromCDouble( fitXmm ) + wxS( " " ) + wxString::FromCDouble( fitYmm ) ); footprint->AddField( field ); } if( ec_origin ) { wxArrayString orParts = wxSplit( *ec_origin, ',', '\0' ); if( orParts.size() == 2 ) { VECTOR2D pos; pos.x = Convert( orParts[0].Trim() ); pos.y = Convert( orParts[1].Trim() ); VECTOR2D rel = RelPos( pos ); kmodelOffset.x = -pcbIUScale.IUTomm( rel.x ); kmodelOffset.y = -pcbIUScale.IUTomm( rel.y ); RotatePoint( &kmodelOffset.x, &kmodelOffset.y, -footprint->GetOrientation() ); } } if( ez ) { kmodelOffset.z = pcbIUScale.IUTomm( ScaleSize( Convert( ez->Trim() ) ) ); } if( ec_rotation ) { wxArrayString rotParts = wxSplit( *ec_rotation, ',', '\0' ); if( rotParts.size() == 3 ) { kmodelRotation.x = -Convert( rotParts[0].Trim() ); kmodelRotation.y = -Convert( rotParts[1].Trim() ); kmodelRotation.z = -Convert( rotParts[2].Trim() ) + footprint->GetOrientationDegrees(); } } if( footprint->GetLayer() == B_Cu ) { kmodelRotation.z = 180 - kmodelRotation.z; RotatePoint( &kmodelOffset.x, &kmodelOffset.y, ANGLE_180 ); } FP_3DMODEL model; model.m_Filename = kicadModelPrefix + EscapeString( modelTitle, ESCAPE_CONTEXT::CTX_FILENAME ) + wxS( ".step" ); model.m_Offset = kmodelOffset; model.m_Rotation = kmodelRotation; footprint->Models().push_back( model ); } else { if( auto dataStr = get_opt( attributes, "d" ) ) { int minSegLen = dataStr->size() < 8000 ? pcbIUScale.mmToIU( 0.005 ) : pcbIUScale.mmToIU( 0.05 ); SHAPE_POLY_SET polySet = ParseLineChains( dataStr->Trim(), minSegLen, true ); polySet.RebuildHolesFromContours(); std::unique_ptr group; if( polySet.OutlineCount() > 1 ) group = std::make_unique( aContainer ); for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() ) { std::unique_ptr shape = std::make_unique( aContainer, SHAPE_T::POLY ); shape->SetFilled( true ); shape->SetPolyShape( poly ); shape->SetLayer( klayer ); shape->SetWidth( 0 ); if( group ) group->AddItem( shape.get() ); aContainer->Add( shape.release(), ADD_MODE::APPEND ); } if( group ) aContainer->Add( group.release(), ADD_MODE::APPEND ); } } } else { THROW_IO_ERROR( wxString::Format( _( "Unknown SVGNODE nodeType %d" ), nodeType ) ); } } else if( elType == wxS( "TEXT" ) ) { PCB_TEXT* text; wxString textType = arr[1]; bool add = false; if( textType == wxS( "P" ) ) { text = footprint->GetField( REFERENCE_FIELD ); } else if( textType == wxS( "N" ) ) { text = footprint->GetField( VALUE_FIELD ); } else { text = new PCB_TEXT( aContainer ); add = true; } VECTOR2D start; start.x = RelPosX( arr[2] ); start.y = RelPosY( arr[3] ); text->SetPosition( start ); double thickness = ConvertSize( arr[4] ); text->SetTextThickness( thickness ); double rot = Convert( arr[5] ); text->SetTextAngleDegrees( rot ); text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); PCB_LAYER_ID layer = LayerToKi( arr[7] ); text->SetLayer( layer ); if( IsBackLayer( layer ) ) text->SetMirrored( true ); double height = ConvertSize( arr[9] ) * 0.8; text->SetTextSize( VECTOR2I( height, height ) ); text->SetText( UnescapeHTML( arr[10] ) ); //arr[11] // Geometry data text->SetVisible( arr[12] != wxS( "none" ) ); wxString font = arr[14]; if( !font.IsEmpty() ) text->SetFont( KIFONT::FONT::GetFont( font ) ); if( add ) aContainer->Add( text, ADD_MODE::APPEND ); } else if( elType == wxS( "VIA" ) ) { VECTOR2D center( RelPosX( arr[1] ), RelPosY( arr[2] ) ); int kdia = ConvertSize( arr[3] ); int kdrill = ConvertSize( arr[5] ) * 2; if( footprint ) { std::unique_ptr pad = std::make_unique( footprint ); pad->SetPosition( center ); pad->SetLayerSet( PAD::PTHMask() ); pad->SetAttribute( PAD_ATTRIB::PTH ); pad->SetShape( PAD_SHAPE::CIRCLE ); pad->SetSize( VECTOR2I( kdia, kdia ) ); pad->SetDrillShape( PAD_DRILL_SHAPE_CIRCLE ); pad->SetDrillSize( VECTOR2I( kdrill, kdrill ) ); footprint->Add( pad.release(), ADD_MODE::APPEND ); } else { std::unique_ptr via = std::make_unique( aContainer ); via->SetPosition( center ); via->SetWidth( kdia ); via->SetNet( getOrAddNetItem( arr[4] ) ); via->SetDrill( kdrill ); aContainer->Add( via.release(), ADD_MODE::APPEND ); } } else if( elType == wxS( "HOLE" ) ) { FOOTPRINT* padContainer; VECTOR2D center( RelPosX( arr[1] ), RelPosY( arr[2] ) ); int kdia = ConvertSize( arr[3] ) * 2; wxString holeUuid = arr[4]; if( footprint ) { padContainer = footprint; } else { std::unique_ptr newFootprint = std::make_unique( dynamic_cast( aContainer ) ); wxString name = wxS( "Hole_" ) + holeUuid; newFootprint->SetFPID( LIB_ID( wxEmptyString, name ) ); newFootprint->SetPosition( center ); newFootprint->SetLocked( true ); newFootprint->Reference().SetText( name ); newFootprint->Reference().SetVisible( false ); newFootprint->Reference().SetTextSize( HIDDEN_TEXT_SIZE ); newFootprint->Value().SetText( name ); newFootprint->Value().SetVisible( false ); newFootprint->Value().SetTextSize( HIDDEN_TEXT_SIZE ); padContainer = newFootprint.get(); aContainer->Add( newFootprint.release(), ADD_MODE::APPEND ); } std::unique_ptr pad = std::make_unique( padContainer ); pad->SetPosition( center ); pad->SetLayerSet( PAD::UnplatedHoleMask() ); pad->SetAttribute( PAD_ATTRIB::NPTH ); pad->SetShape( PAD_SHAPE::CIRCLE ); pad->SetSize( VECTOR2I( kdia, kdia ) ); pad->SetDrillShape( PAD_DRILL_SHAPE_CIRCLE ); pad->SetDrillSize( VECTOR2I( kdia, kdia ) ); padContainer->Add( pad.release(), ADD_MODE::APPEND ); } else if( elType == wxS( "PAD" ) ) { FOOTPRINT* padContainer; VECTOR2D center( RelPosX( arr[2] ), RelPosY( arr[3] ) ); VECTOR2D size( ConvertSize( arr[4] ), ConvertSize( arr[5] ) ); wxString padUuid = arr[12]; if( footprint ) { padContainer = footprint; } else { std::unique_ptr newFootprint = std::make_unique( dynamic_cast( aContainer ) ); wxString name = wxS( "Pad_" ) + padUuid; newFootprint->SetFPID( LIB_ID( wxEmptyString, name ) ); newFootprint->SetPosition( center ); newFootprint->SetLocked( true ); newFootprint->Reference().SetText( name ); newFootprint->Reference().SetVisible( false ); newFootprint->Reference().SetTextSize( HIDDEN_TEXT_SIZE ); newFootprint->Value().SetText( name ); newFootprint->Value().SetVisible( false ); newFootprint->Value().SetTextSize( HIDDEN_TEXT_SIZE ); padContainer = newFootprint.get(); aContainer->Add( newFootprint.release(), ADD_MODE::APPEND ); } std::unique_ptr pad = std::make_unique( padContainer ); pad->SetNet( getOrAddNetItem( arr[7] ) ); pad->SetNumber( arr[8] ); pad->SetPosition( center ); pad->SetSize( size ); pad->SetOrientationDegrees( Convert( arr[11] ) ); pad->SetThermalSpokeAngle( ANGLE_0 ); wxString elayer = arr[6]; PCB_LAYER_ID klayer = LayerToKi( elayer ); bool plated = arr[15] == wxS( "Y" ); if( klayer == F_Cu ) { pad->SetLayer( F_Cu ); pad->SetLayerSet( PAD::SMDMask() ); pad->SetAttribute( PAD_ATTRIB::SMD ); } else if( klayer == B_Cu ) { pad->SetLayer( B_Cu ); pad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) ); pad->SetAttribute( PAD_ATTRIB::SMD ); } else if( elayer == wxS( "11" ) ) { pad->SetLayerSet( plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() ); pad->SetAttribute( plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH ); } else { pad->SetLayer( klayer ); pad->SetLayerSet( LSET( 1, klayer ) ); pad->SetAttribute( PAD_ATTRIB::SMD ); } wxString padType = arr[1]; if( padType == wxS( "ELLIPSE" ) ) { pad->SetShape( PAD_SHAPE::OVAL ); } else if( padType == wxS( "RECT" ) ) { pad->SetShape( PAD_SHAPE::RECTANGLE ); } else if( padType == wxS( "OVAL" ) ) { if( pad->GetSizeX() == pad->GetSizeY() ) pad->SetShape( PAD_SHAPE::CIRCLE ); else pad->SetShape( PAD_SHAPE::OVAL ); } else if( padType == wxS( "POLYGON" ) ) { pad->SetShape( PAD_SHAPE::CUSTOM ); pad->SetAnchorPadShape( PAD_SHAPE::CIRCLE ); pad->SetSize( { 1, 1 } ); wxArrayString data = wxSplit( arr[10], ' ', '\0' ); SHAPE_LINE_CHAIN chain; for( int i = 1; i < data.size(); i += 2 ) { VECTOR2D pt; pt.x = RelPosX( data[i - 1] ); pt.y = RelPosY( data[i] ); chain.Append( pt ); } chain.SetClosed( true ); chain.Move( -center ); chain.Rotate( -pad->GetOrientation() ); pad->AddPrimitivePoly( chain, 0, true ); } wxString holeDia = arr[9]; if( !holeDia.IsEmpty() ) { double holeD = ConvertSize( holeDia ) * 2; double holeL = 0; wxString holeLength = arr[13]; if( !holeLength.IsEmpty() ) holeL = ConvertSize( holeLength ); if( holeL > 0 ) { pad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG ); if( size.x < size.y ) pad->SetDrillSize( VECTOR2I( holeD, holeL ) ); else pad->SetDrillSize( VECTOR2I( holeL, holeD ) ); } else { pad->SetDrillShape( PAD_DRILL_SHAPE_CIRCLE ); pad->SetDrillSize( VECTOR2I( holeD, holeD ) ); } } wxString pasteExp = arr[17]; if( !pasteExp.IsEmpty() ) { double pasteExpansion = ConvertSize( pasteExp ); pad->SetLocalSolderPasteMargin( pasteExpansion ); } wxString maskExp = arr[18]; if( !maskExp.IsEmpty() ) { double maskExpansion = ConvertSize( maskExp ); pad->SetLocalSolderMaskMargin( maskExpansion ); } padContainer->Add( pad.release(), ADD_MODE::APPEND ); } } } FOOTPRINT* PCB_IO_EASYEDA_PARSER::ParseFootprint( const VECTOR2D& aOrigin, const EDA_ANGLE& aOrientation, int aLayer, BOARD* aParent, std::map aParams, std::map>& aFootprintMap, wxArrayString aShapes ) { std::unique_ptr footprint = std::make_unique( aParent ); if( aLayer == 2 ) // Bottom layer { footprint->SetLayer( B_Cu ); footprint->SetOrientation( aOrientation - ANGLE_180 ); } else { footprint->SetLayer( F_Cu ); footprint->SetOrientation( aOrientation ); } footprint->Value().SetText( aParams[wxS( "package" )] ); m_relOrigin = aOrigin; ParseToBoardItemContainer( footprint.get(), aParent, aParams, aFootprintMap, aShapes ); // Heal board outlines std::vector shapes; std::vector> newShapes; for( BOARD_ITEM* item : footprint->GraphicalItems() ) { if( !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() == PCB_SHAPE_T ) shapes.push_back( static_cast( item ) ); } ConnectBoardShapes( shapes, newShapes, SHAPE_JOIN_DISTANCE ); for( std::unique_ptr& ptr : newShapes ) footprint->Add( ptr.release(), ADD_MODE::APPEND ); return footprint.release(); } void PCB_IO_EASYEDA_PARSER::ParseBoard( BOARD* aBoard, const VECTOR2D& aOrigin, std::map>& aFootprintMap, wxArrayString aShapes ) { m_relOrigin = aOrigin; ParseToBoardItemContainer( aBoard, nullptr, {}, aFootprintMap, aShapes ); // Heal board outlines std::vector shapes; std::vector> newShapes; for( BOARD_ITEM* item : aBoard->Drawings() ) { if( !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() == PCB_SHAPE_T ) shapes.push_back( static_cast( item ) ); } ConnectBoardShapes( shapes, newShapes, SHAPE_JOIN_DISTANCE ); for( std::unique_ptr& ptr : newShapes ) aBoard->Add( ptr.release(), ADD_MODE::APPEND ); }