diff --git a/common/io/easyeda/easyeda_parser_base.cpp b/common/io/easyeda/easyeda_parser_base.cpp index 39c1df6153..7e800d1bc1 100644 --- a/common/io/easyeda/easyeda_parser_base.cpp +++ b/common/io/easyeda/easyeda_parser_base.cpp @@ -2,7 +2,7 @@ * 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. + * Copyright (C) 2023-2024 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 @@ -243,7 +243,7 @@ EASYEDA_PARSER_BASE::ParseLineChains( const wxString& data, int aArcMinSegLen, b arc.ConstructFromStartEndCenter( RelPos( start ), RelPos( end ), RelPos( arcCenter ), !cw ); - chain.Append( arc ); + chain.Append( arc, aArcMinSegLen ); prevPt = end; } diff --git a/eeschema/sch_io/easyeda/sch_easyeda_parser.cpp b/eeschema/sch_io/easyeda/sch_easyeda_parser.cpp index 0af358caa7..1793bfb6b9 100644 --- a/eeschema/sch_io/easyeda/sch_easyeda_parser.cpp +++ b/eeschema/sch_io/easyeda/sch_easyeda_parser.cpp @@ -2,7 +2,7 @@ * 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. + * Copyright (C) 2023-2024 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 @@ -38,12 +38,27 @@ #include #include #include +#include #include #include #include #include +// clang-format off +static const std::vector c_attributesWhitelist = { "Datasheet", + "Manufacturer Part", + "Manufacturer", + "BOM_Manufacturer Part", + "BOM_Manufacturer", + "Supplier Part", + "Supplier", + "BOM_Supplier Part", + "BOM_Supplier", + "LCSC Part Name" }; +// clang-format on + + SCH_EASYEDA_PARSER::SCH_EASYEDA_PARSER( SCHEMATIC* aSchematic, PROGRESS_REPORTER* aProgressReporter ) { @@ -555,97 +570,61 @@ void SCH_EASYEDA_PARSER::ParseSymbolShapes( LIB_SYMBOL* aSymbol wxString fillColor = arr[6].Lower(); //bool locked = arr[8] != wxS( "0" ); - VECTOR2D start, end; - VECTOR2D rad( 10, 10 ); - bool isFar = false; - bool cw = false; + std::vector chains = + ParseLineChains( data, schIUScale.MilsToIU( 10 ), false ); - size_t pos = 0; - auto readNumber = [&]( wxString& aOut ) + auto transform = []( VECTOR2I aVec ) { - 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]; - } + return VECTOR2I( aVec.x, -aVec.y ); }; - do + for( const SHAPE_LINE_CHAIN& chain : chains ) { - wxUniChar sym = data[pos++]; - - if( sym == 'M' ) + for( int i = 0; i <= chain.PointCount() && i != -1; i = chain.NextShape( i ) ) { - wxString xStr, yStr; - readNumber( xStr ); - readNumber( yStr ); + if( chain.IsArcStart( i ) ) + { + SHAPE_ARC arc = chain.Arc( chain.ArcIndex( i ) ); - start = VECTOR2D( Convert( xStr ), Convert( yStr ) ); + std::unique_ptr shape = + std::make_unique( SHAPE_T::ARC, LAYER_DEVICE ); + + shape->SetArcGeometry( transform( arc.GetP0() ), + transform( arc.GetArcMid() ), + transform( arc.GetP1() ) ); + + shape->SetUnit( 0 ); + shape->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) ); + + if( fillColor != wxS( "none" ) ) + { + shape->SetFilled( true ); + + if( fillColor == strokeColor ) + shape->SetFillMode( FILL_T::FILLED_SHAPE ); + else + shape->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR ); + } + + aSymbol->AddDrawItem( shape.release() ); + } + else + { + SEG seg = chain.CSegment( i ); + + std::unique_ptr shape = + std::make_unique( SHAPE_T::POLY, LAYER_DEVICE ); + + shape->AddPoint( transform( seg.A ) ); + shape->AddPoint( transform( seg.B ) ); + + shape->SetUnit( 0 ); + shape->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) ); + + aSymbol->AddDrawItem( shape.release() ); + } } - 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 avgRad = ( rad.x + rad.y ) / 2; - double d = delta.EuclideanNorm(); - double h = sqrt( std::max( 0.0, avgRad * avgRad - 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 ); - - auto shape = std::make_unique( SHAPE_T::ARC, LAYER_DEVICE ); - - shape->SetStart( RelPosSym( start ) ); - shape->SetEnd( RelPosSym( end ) ); - shape->SetCenter( RelPosSym( arcCenter ) ); - - shape->SetUnit( 0 ); - shape->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) ); - - if( fillColor != wxS( "none" ) ) - { - shape->SetFilled( true ); - - if( fillColor == strokeColor ) - shape->SetFillMode( FILL_T::FILLED_SHAPE ); - else - shape->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR ); } - - aSymbol->AddDrawItem( shape.release() ); } else if( elType == wxS( "R" ) ) { @@ -953,22 +932,20 @@ LIB_SYMBOL* SCH_EASYEDA_PARSER::ParseSymbol( const VECTOR2D& aOrigi m_relOrigin = aOrigin; - wxString symbolName = wxS( "Unknown" ); + std::optional valOpt; + wxString symbolName = wxS( "Unknown" ); - if( aParams.find( wxS( "name" ) ) != aParams.end() ) - symbolName = aParams.at( wxS( "name" ) ); - else if( aParams.find( wxS( "spiceSymbolName" ) ) != aParams.end() ) - symbolName = aParams.at( wxS( "spiceSymbolName" ) ); + if( valOpt = get_opt( aParams, wxS( "name" ) ) ) + symbolName = *valOpt; + else if( valOpt = get_opt( aParams, wxS( "spiceSymbolName" ) ) ) + symbolName = *valOpt; wxString symbolPrefix; - if( aParams.find( wxS( "pre" ) ) != aParams.end() ) - symbolPrefix = aParams.at( wxS( "pre" ) ); - else if( aParams.find( wxS( "spicePre" ) ) != aParams.end() ) - symbolPrefix = aParams.at( wxS( "spicePre" ) ); - - if( !symbolPrefix.EndsWith( wxS( "?" ) ) ) - symbolPrefix += wxS( "?" ); + if( valOpt = get_opt( aParams, wxS( "pre" ) ) ) + symbolPrefix = *valOpt; + else if( valOpt = get_opt( aParams, wxS( "spicePre" ) ) ) + symbolPrefix = *valOpt; LIB_ID libId = EasyEdaToKiCadLibID( wxEmptyString, symbolName ); @@ -978,6 +955,31 @@ LIB_SYMBOL* SCH_EASYEDA_PARSER::ParseSymbol( const VECTOR2D& aOrigi ksymbol->GetReferenceField().SetText( symbolPrefix ); ksymbol->GetValueField().SetText( symbolName ); + for( wxString attrName : c_attributesWhitelist ) + { + wxString srcName = attrName; + + if( srcName == wxS( "Datasheet" ) ) + srcName = wxS( "link" ); + + if( valOpt = get_opt( aParams, srcName ) ) + { + if( valOpt->empty() ) + continue; + + SCH_FIELD* fd = ksymbol->FindField( attrName, true ); + + if( !fd ) + { + fd = new SCH_FIELD( ksymbol.get(), ksymbol->GetNextAvailableFieldId(), attrName ); + ksymbol->AddField( fd ); + } + + fd->SetText( *valOpt ); + fd->SetVisible( false ); + } + } + ParseSymbolShapes( ksymbol.get(), aParams, aShapes ); return ksymbol.release(); diff --git a/eeschema/sch_io/easyeda/sch_io_easyeda.cpp b/eeschema/sch_io/easyeda/sch_io_easyeda.cpp index ba772316f0..9fd3931c34 100644 --- a/eeschema/sch_io/easyeda/sch_io_easyeda.cpp +++ b/eeschema/sch_io/easyeda/sch_io_easyeda.cpp @@ -225,7 +225,19 @@ LIB_SYMBOL* loadSymbol( const wxString& aLibraryPath, nlohmann::json aFileData, { parts.RemoveAt( 0 ); - return parser.ParseSymbol( origin, paramMap, parts ); + LIB_SYMBOL* ksymbol = parser.ParseSymbol( origin, paramMap, parts ); + + // Clear reference numbers + SCH_FIELD& refField = ksymbol->GetReferenceField(); + wxString origRef = refField.GetText(); + wxString reference; + + for( size_t i = 0; i < origRef.size() && !wxIsdigit( origRef[i] ); i++ ) + reference << origRef[i]; + + refField.SetText( reference ); + + return ksymbol; } } } @@ -261,7 +273,19 @@ LIB_SYMBOL* loadSymbol( const wxString& aLibraryPath, nlohmann::json aFileData, VECTOR2D origin( topDoc.head.x, topDoc.head.y ); - return parser.ParseSymbol( origin, *c_para, topDoc.shape ); + LIB_SYMBOL* ksymbol = parser.ParseSymbol( origin, *c_para, topDoc.shape ); + + // Clear reference numbers + SCH_FIELD& refField = ksymbol->GetReferenceField(); + wxString origRef = refField.GetText(); + wxString reference; + + for( size_t i = 0; i < origRef.size() && !wxIsdigit( origRef[i] ); i++ ) + reference << origRef[i]; + + refField.SetText( reference ); + + return ksymbol; } } catch( nlohmann::json::exception& e ) diff --git a/eeschema/sch_io/easyedapro/sch_easyedapro_parser.cpp b/eeschema/sch_io/easyedapro/sch_easyedapro_parser.cpp index f86f2edbfb..3059c18ab1 100644 --- a/eeschema/sch_io/easyedapro/sch_easyedapro_parser.cpp +++ b/eeschema/sch_io/easyedapro/sch_easyedapro_parser.cpp @@ -1,8 +1,8 @@ -/* +/* * 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. + * Copyright (C) 2023-2024 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 @@ -48,6 +48,21 @@ #include +// clang-format off +static const std::vector c_attributesWhitelist = { "Value", + "Datasheet", + "Manufacturer Part", + "Manufacturer", + "BOM_Manufacturer Part", + "BOM_Manufacturer", + "Supplier Part", + "Supplier", + "BOM_Supplier Part", + "BOM_Supplier", + "LCSC Part Name" }; +// clang-format on + + SCH_EASYEDAPRO_PARSER::SCH_EASYEDAPRO_PARSER( SCHEMATIC* aSchematic, PROGRESS_REPORTER* aProgressReporter ) { @@ -379,7 +394,9 @@ void SCH_EASYEDAPRO_PARSER::ApplyAttrToField( const std::map& aLines ) +EASYEDAPRO::SYM_INFO +SCH_EASYEDAPRO_PARSER::ParseSymbol( const std::vector& aLines, + const std::map& aDeviceAttributes ) { EASYEDAPRO::SYM_INFO symInfo; @@ -674,11 +691,35 @@ EASYEDAPRO::SYM_INFO SCH_EASYEDAPRO_PARSER::ParseSymbol( const std::vectorvalue; - if( !symbolPrefix.EndsWith( wxS( "?" ) ) ) - symbolPrefix += wxS( "?" ); + if( symbolPrefix.EndsWith( wxS( "?" ) ) ) + symbolPrefix.RemoveLast(); ksymbol->GetReferenceField().SetText( symbolPrefix ); } + + for( const wxString& attrName : c_attributesWhitelist ) + { + if( auto valOpt = get_opt( aDeviceAttributes, attrName ) ) + { + if( valOpt->empty() ) + continue; + + SCH_FIELD* fd = ksymbol->FindField( attrName, true ); + + if( !fd ) + { + fd = new SCH_FIELD( ksymbol, ksymbol->GetNextAvailableFieldId(), attrName ); + ksymbol->AddField( fd ); + } + + wxString value = *valOpt; + + value.Replace( wxS( "\u2103" ), wxS( "\u00B0C" ), true ); // ℃ -> °C + + fd->SetText( value ); + fd->SetVisible( false ); + } + } } for( auto& [unitId, parentedLines] : unitParentedLines ) @@ -1095,7 +1136,7 @@ void SCH_EASYEDAPRO_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aR if( !deviceAttr ) continue; - nlohmann::json compAttrs = + std::map compAttrs = aProject.at( "devices" ).at( deviceAttr->value ).at( "attributes" ); wxString symbolId; @@ -1103,7 +1144,7 @@ void SCH_EASYEDAPRO_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aR if( symbolAttr && !symbolAttr->value.IsEmpty() ) symbolId = symbolAttr->value; else - symbolId = compAttrs.at( "Symbol" ).get(); + symbolId = compAttrs.at( "Symbol" ); auto it = aSymbolMap.find( symbolId ); if( it == aSymbolMap.end() ) @@ -1205,6 +1246,30 @@ void SCH_EASYEDAPRO_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aR } else { + for( const wxString& attrKey : c_attributesWhitelist ) + { + if( auto valOpt = get_opt( compAttrs, attrKey ) ) + { + if( valOpt->empty() ) + continue; + + SCH_FIELD* text = schSym->FindField( attrKey, true ); + + if( !text ) + { + text = schSym->AddField( + SCH_FIELD( schSym.get(), schSym->GetFieldCount(), attrKey ) ); + } + + wxString value = *valOpt; + + value.Replace( wxS( "\u2103" ), wxS( "\u00B0C" ), true ); // ℃ -> °C + + text->SetText( value ); + text->SetVisible( false ); + } + } + auto nameAttr = get_opt( attributes, "Name" ); auto valueAttr = get_opt( attributes, "Value" ); @@ -1253,15 +1318,18 @@ void SCH_EASYEDAPRO_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aR if( attr.value.IsEmpty() ) continue; - SCH_FIELD* text = - new SCH_FIELD( schSym.get(), schSym->GetFieldCount(), attrKey ); + SCH_FIELD* text = schSym->FindField( attrKey, true ); + + if( !text ) + { + text = schSym->AddField( + SCH_FIELD( schSym.get(), schSym->GetFieldCount(), attrKey ) ); + } text->SetPosition( schSym->GetPosition() ); ApplyAttrToField( fontStyles, text, attr, false, true, compAttrs, schSym.get() ); - - schSym->AddField( *text ); } } diff --git a/eeschema/sch_io/easyedapro/sch_easyedapro_parser.h b/eeschema/sch_io/easyedapro/sch_easyedapro_parser.h index 10533f2331..8217225889 100644 --- a/eeschema/sch_io/easyedapro/sch_easyedapro_parser.h +++ b/eeschema/sch_io/easyedapro/sch_easyedapro_parser.h @@ -2,7 +2,7 @@ * 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. + * Copyright (C) 2023-2024 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 @@ -94,7 +94,8 @@ public: double SizeToKi( wxString units ); - EASYEDAPRO::SYM_INFO ParseSymbol( const std::vector& aLines ); + EASYEDAPRO::SYM_INFO ParseSymbol( const std::vector& aLines, + const std::map& aDeviceAttributes ); void ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const nlohmann::json& aProject, diff --git a/eeschema/sch_io/easyedapro/sch_io_easyedapro.cpp b/eeschema/sch_io/easyedapro/sch_io_easyedapro.cpp index baa4489352..3f0e446c09 100644 --- a/eeschema/sch_io/easyedapro/sch_io_easyedapro.cpp +++ b/eeschema/sch_io/easyedapro/sch_io_easyedapro.cpp @@ -1,4 +1,4 @@ -/* +/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 Alex Shvartzkop @@ -156,7 +156,11 @@ static LIB_SYMBOL* loadSymbol( nlohmann::json project, const wxString& aLibraryP return nullptr; wxString prjSymUuid = prjSymIt->first; - wxString fpTitle; + + wxString description; + wxString customTags; + std::map deviceAttributes; + wxString fpTitle; for( auto& [key, device] : prjDevices ) { @@ -164,6 +168,12 @@ static LIB_SYMBOL* loadSymbol( nlohmann::json project, const wxString& aLibraryP if( val && *val == prjSymUuid ) { + description = device.description; + deviceAttributes = device.attributes; + + if( device.custom_tags.is_string() ) + customTags = device.custom_tags.get(); + if( auto fpUuid = get_opt( device.attributes, "Footprint" ) ) { if( auto prjFp = get_opt( prjFootprints, *fpUuid ) ) @@ -192,7 +202,7 @@ static LIB_SYMBOL* loadSymbol( nlohmann::json project, const wxString& aLibraryP lines.emplace_back( js ); } - EASYEDAPRO::SYM_INFO symInfo = parser.ParseSymbol( lines ); + EASYEDAPRO::SYM_INFO symInfo = parser.ParseSymbol( lines, deviceAttributes ); if( !symInfo.libSymbol ) EASY_IT_CONTINUE; @@ -202,6 +212,15 @@ static LIB_SYMBOL* loadSymbol( nlohmann::json project, const wxString& aLibraryP symInfo.libSymbol->SetName( aAliasName ); symInfo.libSymbol->GetFootprintField().SetText( symLibName + wxS( ":" ) + fpTitle ); + wxString keywords = customTags; + keywords.Replace( wxS( ":" ), wxS( " " ), true ); + + symInfo.libSymbol->SetKeyWords( keywords ); + + description.Replace( wxS( "\u2103" ), wxS( "\u00B0C" ), true ); // ℃ -> °C + + symInfo.libSymbol->SetDescription( description ); + symbol = symInfo.libSymbol.release(); EASY_IT_BREAK; @@ -314,13 +333,10 @@ void SCH_IO_EASYEDAPRO::LoadAllDataFromProject( const wxString& aProjectPath ) if( name.EndsWith( wxS( ".esym" ) ) ) { - EASYEDAPRO::PRJ_SYMBOL symData = prjSymbols.at( baseName ); - EASYEDAPRO::SYM_INFO symInfo = parser.ParseSymbol( lines ); - - if( !symInfo.libSymbol ) - EASY_IT_CONTINUE; - - wxString fpTitle; + wxString description; + wxString customTags; + std::map deviceAttributes; + wxString fpTitle; for( auto& [key, device] : prjDevices ) { @@ -328,6 +344,12 @@ void SCH_IO_EASYEDAPRO::LoadAllDataFromProject( const wxString& aProjectPath ) if( val && *val == baseName ) { + description = device.description; + deviceAttributes = device.attributes; + + if( device.custom_tags.is_string() ) + customTags = device.custom_tags.get(); + if( auto fpUuid = get_opt( device.attributes, "Footprint" ) ) { if( auto prjFp = get_opt( prjFootprints, *fpUuid ) ) @@ -339,11 +361,26 @@ void SCH_IO_EASYEDAPRO::LoadAllDataFromProject( const wxString& aProjectPath ) } } + EASYEDAPRO::PRJ_SYMBOL symData = prjSymbols.at( baseName ); + EASYEDAPRO::SYM_INFO symInfo = parser.ParseSymbol( lines, deviceAttributes ); + + if( !symInfo.libSymbol ) + EASY_IT_CONTINUE; + LIB_ID libID = EASYEDAPRO::ToKiCadLibID( symLibName, symData.title ); symInfo.libSymbol->SetLibId( libID ); symInfo.libSymbol->SetName( symData.title ); symInfo.libSymbol->GetFootprintField().SetText( symLibName + wxS( ":" ) + fpTitle ); + wxString keywords = customTags; + keywords.Replace( wxS( ":" ), wxS( " " ), true ); + + symInfo.libSymbol->SetKeyWords( keywords ); + + description.Replace( wxS( "\u2103" ), wxS( "\u00B0C" ), true ); // ℃ -> °C + + symInfo.libSymbol->SetDescription( description ); + m_projectData->m_Symbols.emplace( baseName, std::move( symInfo ) ); } else if( name.EndsWith( wxS( ".eblob" ) ) )