EasyEDA Std/Pro: import (some) component metadata for symbols.

Also fixes Reference numbering.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17806
This commit is contained in:
Alex Shvartzkop 2024-05-03 00:05:34 +03:00
parent ae610bbe85
commit 2a889f03fc
6 changed files with 254 additions and 122 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
* 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;
}

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
* 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 <bezier_curves.h>
#include <wx/base64.h>
#include <wx/mstream.h>
#include <core/map_helpers.h>
#include <gfx_import_utils.h>
#include <import_gfx/svg_import_plugin.h>
#include <import_gfx/graphics_importer_lib_symbol.h>
#include <import_gfx/graphics_importer_sch.h>
// clang-format off
static const std::vector<wxString> 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<SHAPE_LINE_CHAIN> 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<SCH_SHAPE> shape =
std::make_unique<SCH_SHAPE>( 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<SCH_SHAPE> shape =
std::make_unique<SCH_SHAPE>( 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<SCH_SHAPE>( 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<wxString> 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();

View File

@ -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 )

View File

@ -1,8 +1,8 @@
/*
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
* 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 <import_gfx/graphics_importer_sch.h>
// clang-format off
static const std::vector<wxString> 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<wxString, nlohmann:
}
EASYEDAPRO::SYM_INFO SCH_EASYEDAPRO_PARSER::ParseSymbol( const std::vector<nlohmann::json>& aLines )
EASYEDAPRO::SYM_INFO
SCH_EASYEDAPRO_PARSER::ParseSymbol( const std::vector<nlohmann::json>& aLines,
const std::map<wxString, wxString>& aDeviceAttributes )
{
EASYEDAPRO::SYM_INFO symInfo;
@ -674,11 +691,35 @@ EASYEDAPRO::SYM_INFO SCH_EASYEDAPRO_PARSER::ParseSymbol( const std::vector<nlohm
{
wxString symbolPrefix = designatorAttr->value;
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<wxString, wxString> 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<wxString>();
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 );
}
}

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
* 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<nlohmann::json>& aLines );
EASYEDAPRO::SYM_INFO ParseSymbol( const std::vector<nlohmann::json>& aLines,
const std::map<wxString, wxString>& aDeviceAttributes );
void ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
const nlohmann::json& aProject,

View File

@ -1,4 +1,4 @@
/*
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
@ -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<wxString, wxString> 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<wxString>();
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<wxString, wxString> 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<wxString>();
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" ) ) )