From 2aa67ae1afe0b4d8ae447de1e1a2e921b5fd2502 Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Tue, 31 Oct 2023 02:20:33 +0300 Subject: [PATCH] EasyEDA Std: Support image transformations; SVG images in schematic. --- .../easyeda/sch_easyeda_parser.cpp | 191 +++++++++++++++++- 1 file changed, 184 insertions(+), 7 deletions(-) diff --git a/eeschema/sch_plugins/easyeda/sch_easyeda_parser.cpp b/eeschema/sch_plugins/easyeda/sch_easyeda_parser.cpp index faf2db5a6c..94a5c2451f 100644 --- a/eeschema/sch_plugins/easyeda/sch_easyeda_parser.cpp +++ b/eeschema/sch_plugins/easyeda/sch_easyeda_parser.cpp @@ -46,6 +46,7 @@ #include #include #include +#include SCH_EASYEDA_PARSER::SCH_EASYEDA_PARSER( SCHEMATIC* aSchematic, @@ -60,6 +61,77 @@ SCH_EASYEDA_PARSER::~SCH_EASYEDA_PARSER() } +static std::vector> RegexMatchAll( wxRegEx& aRegex, const wxString& aString ) +{ + std::vector> allMatches; + + size_t start = 0; + size_t len = 0; + size_t prevstart = 0; + + wxString str = aString; + + while( aRegex.Matches( str ) ) + { + std::vector matches; + aRegex.GetMatch( &start, &len ); + + for( size_t i = 0; i < aRegex.GetMatchCount(); i++ ) + matches.emplace_back( aRegex.GetMatch( str, i ) ); + + allMatches.emplace_back( matches ); + + prevstart = start + len; + str = str.Mid( prevstart ); + } + + return allMatches; +} + + +/** + * EasyEDA image transformations are in the form of: + * + * scale(1,-1) translate(-555,1025) rotate(270,425,-785) + * + * Order of operations is from end to start, similar to CSS. + */ +static std::vector>> +ParseImageTransform( const wxString& transformData ) +{ + wxRegEx transformRegex( "(rotate|translate|scale)\\(([\\w\\s,\\.\\-]*)\\)", wxRE_ICASE ); + std::vector> allMatches = RegexMatchAll( transformRegex, transformData ); + + std::vector>> transformCmds; + + for( int cmdId = allMatches.size() - 1; cmdId >= 0; cmdId-- ) + { + std::vector& groups = allMatches[cmdId]; + + if( groups.size() != 3 ) + continue; + + const wxString& cmdName = groups[1].Strip( wxString::both ).Lower(); + const wxString& cmdArgsStr = groups[2]; + + wxArrayString cmdParts = wxSplit( cmdArgsStr, ',', '\0' ); + std::vector cmdArgs; + + for( const wxString& cmdPart : cmdParts ) + { + double arg = 0; + wxASSERT( cmdPart.Strip( wxString::both ).ToCDouble( &arg ) ); + + cmdArgs.push_back( arg ); + } + + transformCmds.emplace_back( cmdName, cmdArgs ); + } + + return transformCmds; +} + + static LIB_ID EasyEdaToKiCadLibID( const wxString& aLibName, const wxString& aLibReference ) { wxString libReference = EscapeString( aLibReference, CTX_LIBID ); @@ -419,7 +491,7 @@ void SCH_EASYEDA_PARSER::ParseSymbolShapes( LIB_SYMBOL* aSymbol ParseLineChains( pointsData, schIUScale.MilsToIU( 10 ) ); for( SHAPE_LINE_CHAIN outline : lineChains ) - { + { std::unique_ptr shape = std::make_unique( aSymbol, SHAPE_T::POLY ); @@ -1397,8 +1469,86 @@ void SCH_EASYEDA_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRoot { VECTOR2D start( Convert( arr[1] ), Convert( arr[2] ) ); VECTOR2D size( Convert( arr[3] ), Convert( arr[4] ) ); - double angle = Convert( arr[5] ); + //double angle = Convert( arr[5] ); // CSS-like transformations are used instead. wxString imageUrl = arr[6]; + wxString transformData = arr[9]; + + VECTOR2D kstart = RelPos( start ); + VECTOR2D ksize = ScalePos( size ); + + std::vector>> transformCmds = + ParseImageTransform( transformData ); + + auto applyTransform = [&]( SCH_ITEM* aSchItem ) + { + for( const auto& cmd : transformCmds ) + { + if( cmd.first == wxS( "rotate" ) ) + { + if( cmd.second.size() != 3 ) + continue; + + double cmdAngle = 360 - cmd.second[0]; + VECTOR2D cmdAround( cmd.second[1], cmd.second[2] ); + + for( double i = cmdAngle; i > 0; i -= 90 ) + { + if( aSchItem->Type() == SCH_LINE_T ) + { + // Lines need special handling for some reason + aSchItem->SetFlags( STARTPOINT ); + aSchItem->Rotate( RelPos( cmdAround ) ); + aSchItem->ClearFlags( STARTPOINT ); + + aSchItem->SetFlags( ENDPOINT ); + aSchItem->Rotate( RelPos( cmdAround ) ); + aSchItem->ClearFlags( ENDPOINT ); + } + else + { + aSchItem->Rotate( RelPos( cmdAround ) ); + } + } + } + else if( cmd.first == wxS( "translate" ) ) + { + if( cmd.second.size() != 2 ) + continue; + + VECTOR2D cmdOffset( cmd.second[0], cmd.second[1] ); + aSchItem->Move( ScalePos( cmdOffset ) ); + } + else if( cmd.first == wxS( "scale" ) ) + { + if( cmd.second.size() != 2 ) + continue; + + double cmdScaleX = cmd.second[0]; + double cmdScaleY = cmd.second[1]; + + // Lines need special handling for some reason + if( aSchItem->Type() == SCH_LINE_T ) + aSchItem->SetFlags( STARTPOINT | ENDPOINT ); + + if( cmdScaleX < 0 && cmdScaleY > 0 ) + { + aSchItem->MirrorHorizontally( 0 ); + } + else if( cmdScaleX > 0 && cmdScaleY < 0 ) + { + aSchItem->MirrorVertically( 0 ); + } + else if( cmdScaleX < 0 && cmdScaleY < 0 ) + { + aSchItem->MirrorHorizontally( 0 ); + aSchItem->MirrorVertically( 0 ); + } + + if( aSchItem->Type() == SCH_LINE_T ) + aSchItem->ClearFlags( STARTPOINT | ENDPOINT ); + } + } + }; if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) ) { @@ -1414,7 +1564,37 @@ void SCH_EASYEDA_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRoot if( mimeType == wxS( "image/svg+xml" ) ) { - // TODO: Not supported yet + VECTOR2D offset = RelPos( start ); + + SVG_IMPORT_PLUGIN svgImportPlugin; + GRAPHICS_IMPORTER_SCH schImporter; + + svgImportPlugin.SetImporter( &schImporter ); + svgImportPlugin.LoadFromMemory( buf ); + + VECTOR2D imSize( svgImportPlugin.GetImageWidth(), + svgImportPlugin.GetImageHeight() ); + + VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x, + schIUScale.IUTomm( ScaleSize( size.y ) ) / imSize.y ); + + schImporter.SetScale( pixelScale ); + + VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ), + schIUScale.IUTomm( offset.y ) ); + + schImporter.SetImportOffsetMM( offsetMM ); + + svgImportPlugin.Import(); + + for( std::unique_ptr& item : schImporter.GetItems() ) + { + SCH_ITEM* schItem = static_cast( item.release() ); + + applyTransform( schItem ); + + createdItems.emplace_back( schItem ); + } } else { @@ -1425,16 +1605,13 @@ void SCH_EASYEDA_PARSER::ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRoot if( bitmap->ReadImageFile( buf ) ) { - VECTOR2D kstart = RelPos( start ); - VECTOR2D ksize = ScalePos( size ); VECTOR2D kcenter = kstart + ksize / 2; double scaleFactor = ScaleSize( size.x ) / bitmap->GetSize().x; bitmap->SetImageScale( scaleFactor ); bitmap->SetPosition( kcenter ); - for( double i = angle; i > 0; i -= 90 ) - bitmap->Rotate( kcenter ); + applyTransform( bitmap.get() ); createdItems.push_back( std::move( bitmap ) ); }