ADDED: Importers for EasyEDA (JLCEDA) Standard / Professional.

This commit is contained in:
Alex Shvartzkop 2023-09-06 14:58:39 +03:00
parent 024622b0f6
commit 21ee65aa9c
48 changed files with 10019 additions and 3 deletions

View File

@ -266,6 +266,16 @@ set( PLUGINS_EAGLE_SRCS
plugins/eagle/eagle_parser.cpp plugins/eagle/eagle_parser.cpp
) )
set( PLUGINS_EASYEDA_SRCS
plugins/easyeda/easyeda_parser_base.cpp
plugins/easyeda/easyeda_parser_structs.cpp
)
set( PLUGINS_EASYEDAPRO_SRCS
plugins/easyedapro/easyedapro_parser.cpp
plugins/easyedapro/easyedapro_import_utils.cpp
)
set( FONT_SRCS set( FONT_SRCS
font/font.cpp font/font.cpp
font/glyph.cpp font/glyph.cpp
@ -295,6 +305,8 @@ set( COMMON_SRCS
${PLUGINS_ALTIUM_SRCS} ${PLUGINS_ALTIUM_SRCS}
${PLUGINS_CADSTAR_SRCS} ${PLUGINS_CADSTAR_SRCS}
${PLUGINS_EAGLE_SRCS} ${PLUGINS_EAGLE_SRCS}
${PLUGINS_EASYEDA_SRCS}
${PLUGINS_EASYEDAPRO_SRCS}
${FONT_SRCS} ${FONT_SRCS}
${COMMON_IMPORT_GFX_SRCS} ${COMMON_IMPORT_GFX_SRCS}
jobs/job_dispatcher.cpp jobs/job_dispatcher.cpp

View File

@ -0,0 +1,242 @@
/*
* 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.
*
* 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 "easyeda_parser_base.h"
#include <bezier_curves.h>
#include <ki_exception.h>
#include <wx/translation.h>
double EASYEDA_PARSER_BASE::Convert( const wxString& aValue )
{
double value = 0;
if( !aValue.ToCDouble( &value ) )
THROW_IO_ERROR( wxString::Format( _( "Failed to parse number from '%s'" ), aValue ) );
return value;
}
double EASYEDA_PARSER_BASE::RelPosX( double aValue )
{
double value = aValue - m_relOrigin.x;
return ScaleSize( value );
}
double EASYEDA_PARSER_BASE::RelPosY( double aValue )
{
double value = aValue - m_relOrigin.y;
return ScaleSize( value );
}
double EASYEDA_PARSER_BASE::RelPosX( const wxString& aValue )
{
return RelPosX( Convert( aValue ) );
}
double EASYEDA_PARSER_BASE::RelPosY( const wxString& aValue )
{
return RelPosY( Convert( aValue ) );
}
template <typename T>
VECTOR2<T> EASYEDA_PARSER_BASE::RelPos( const VECTOR2<T>& aVec )
{
return ScalePos( aVec - m_relOrigin );
}
SHAPE_POLY_SET EASYEDA_PARSER_BASE::ParsePolygons( const wxString& data, int aArcMinSegLen )
{
SHAPE_POLY_SET result;
VECTOR2D prevPt;
SHAPE_LINE_CHAIN chain;
size_t pos = 0;
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 == ' ' )
continue;
if( sym == 'M' )
{
wxString xStr, yStr;
readNumber( xStr );
readNumber( yStr );
if( chain.PointCount() > 2 )
{
chain.SetClosed( true );
result.Append( chain );
}
chain.Clear();
VECTOR2D pt( Convert( xStr ), Convert( yStr ) );
chain.Append( RelPos( pt ) );
prevPt = pt;
}
else if( sym == 'Z' )
{
if( chain.PointCount() > 2 )
{
chain.SetClosed( true );
result.Append( chain );
}
chain.Clear();
}
else if( sym == 'L' )
{
while( true )
{
if( pos >= data.size() )
break;
wxUniChar ch = data[pos];
while( ch == ' ' || ch == ',' )
{
if( ++pos >= data.size() )
break;
ch = data[pos];
}
if( !isdigit( ch ) )
break;
wxString xStr, yStr;
readNumber( xStr );
readNumber( yStr );
VECTOR2D pt( Convert( xStr ), Convert( yStr ) );
chain.Append( RelPos( pt ) );
prevPt = pt;
};
}
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 );
bool isFar = farFlag == wxS( "1" );
bool cw = cwFlag == wxS( "1" );
VECTOR2D rad( Convert( radX ), Convert( radY ) );
VECTOR2D end( Convert( endX ), Convert( endY ) );
VECTOR2D start = prevPt;
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 );
SHAPE_ARC arc;
arc.ConstructFromStartEndCenter( RelPos( start ), RelPos( end ), RelPos( arcCenter ),
!cw );
chain.Append( arc );
prevPt = end;
}
else if( sym == 'C' )
{
wxString p1_xStr, p1_yStr, p2_xStr, p2_yStr, p3_xStr, p3_yStr;
readNumber( p1_xStr );
readNumber( p1_yStr );
readNumber( p2_xStr );
readNumber( p2_yStr );
readNumber( p3_xStr );
readNumber( p3_yStr );
VECTOR2D pt1( Convert( p1_xStr ), Convert( p1_yStr ) );
VECTOR2D pt2( Convert( p2_xStr ), Convert( p2_yStr ) );
VECTOR2D pt3( Convert( p3_xStr ), Convert( p3_yStr ) );
std::vector<VECTOR2I> ctrlPoints = { RelPos( prevPt ), RelPos( pt1 ), RelPos( pt2 ),
RelPos( pt3 ) };
BEZIER_POLY converter( ctrlPoints );
std::vector<VECTOR2I> bezierPoints;
converter.GetPoly( bezierPoints, aArcMinSegLen, 16 );
chain.Append( bezierPoints );
prevPt = pt3;
}
} while( pos < data.size() );
if( chain.PointCount() > 2 )
{
chain.SetClosed( true );
result.Append( chain );
}
return result;
}

View File

@ -0,0 +1,65 @@
/*
* 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.
*
* 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
*/
#ifndef EASYEDA_PARSER_BASE_H_
#define EASYEDA_PARSER_BASE_H_
#include <geometry/shape_poly_set.h>
#include <math/vector2d.h>
#include <wx/string.h>
class EASYEDA_PARSER_BASE
{
public:
static double Convert( const wxString& aValue );
double ConvertSize( const wxString& aValue ) { return ScaleSize( Convert( aValue ) ); }
virtual double ScaleSize( double aValue ) = 0;
double ScaleSize( const wxString& aValue ) { return ScaleSize( Convert( aValue ) ); }
template <typename T>
VECTOR2<T> ScalePos( const VECTOR2<T>& aValue )
{
return VECTOR2<T>( ScaleSize( aValue.x ), ScaleSize( aValue.y ) );
}
double RelPosX( double aValue );
double RelPosY( double aValue );
double RelPosX( const wxString& aValue );
double RelPosY( const wxString& aValue );
template <typename T>
VECTOR2<T> RelPos( const VECTOR2<T>& aVec );
SHAPE_POLY_SET ParsePolygons( const wxString& aData, int aArcMinSegLen );
protected:
VECTOR2D m_relOrigin;
};
#endif // EASYEDA_PARSER_BASE_H_

View File

@ -0,0 +1,120 @@
/*
* 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.
*
* 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 "easyeda_parser_structs.h"
#include <nlohmann/json.hpp>
#include <core/json_serializers.h>
#define PARSE_VALUE( name ) \
if( j.find( #name ) != j.end() ) \
j.at( #name ).get_to( d.name )
#define PARSE_TO_DOUBLE( name, def ) \
if( j.find( #name ) == j.end() ) \
{ \
d.name = def; \
} \
else if( j.at( #name ).is_string() ) \
{ \
wxString str = j.at( #name ).get<wxString>(); \
\
double out = 0; \
str.ToCDouble( &out ); \
d.name = out; \
} \
else if( j.at( #name ).is_number() ) \
{ \
d.name = j.at( #name ).get<double>(); \
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::DOC_TYPE& d )
{
if( j.is_string() )
{
wxString str = j.get<wxString>();
int out = 0;
str.ToInt( &out );
d = static_cast<EASYEDA::DOC_TYPE>( out );
}
else if( j.is_number() )
{
d = static_cast<EASYEDA::DOC_TYPE>( j.get<int>() );
}
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::HEAD& d )
{
PARSE_VALUE( docType );
PARSE_VALUE( editorVersion );
PARSE_VALUE( title );
PARSE_VALUE( description );
if( j.find( "c_para" ) != j.end() && j.at( "c_para" ).is_object() )
d.c_para = j.at( "c_para" );
PARSE_TO_DOUBLE( x, 0 );
PARSE_TO_DOUBLE( y, 0 );
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::DOCUMENT& d )
{
PARSE_VALUE( docType );
PARSE_VALUE( head );
PARSE_VALUE( canvas );
PARSE_VALUE( shape );
PARSE_VALUE( dataStr );
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::DOCUMENT_PCB& d )
{
PARSE_VALUE( c_para );
d.layers = j.at( "layers" );
if( j.find( "DRCRULE" ) != j.end() && j.at( "DRCRULE" ).is_object() )
d.DRCRULE = j.at( "DRCRULE" );
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::DOCUMENT_SYM& d )
{
PARSE_VALUE( c_para );
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::DOCUMENT_SCHEMATICS& d )
{
PARSE_VALUE( schematics );
}
void EASYEDA::from_json( const nlohmann::json& j, EASYEDA::C_PARA& d )
{
PARSE_VALUE( package );
PARSE_VALUE( pre );
PARSE_VALUE( Contributor );
PARSE_VALUE( link );
PARSE_VALUE( Model_3D );
}

View File

@ -0,0 +1,136 @@
/*
* 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.
*
* 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
*/
#ifndef EASYEDA_PARSER_STRUCTS_H_
#define EASYEDA_PARSER_STRUCTS_H_
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <map>
#include <optional>
#include <wx/string.h>
#include <wx/arrstr.h>
#include <nlohmann/json.hpp>
namespace EASYEDA
{
enum class DOC_TYPE
{
UNKNOWN = 0,
SCHEMATIC_SHEET = 1,
SYMBOL = 2,
PCB = 3,
PCB_COMPONENT = 4,
SCHEMATIC_LIST = 5,
PCB_MODULE = 14,
};
struct HEAD
{
DOC_TYPE docType = DOC_TYPE::UNKNOWN;
wxString editorVersion;
wxString title;
wxString description;
double x = 0;
double y = 0;
std::optional<std::map<wxString, wxString>> c_para;
};
struct DOCUMENT
{
std::optional<DOC_TYPE> docType; // May be here or in head
HEAD head;
// BBox
// colors
wxString canvas;
wxArrayString shape;
std::optional<nlohmann::json> dataStr;
};
struct C_PARA
{
wxString package;
wxString pre;
wxString Contributor;
wxString link;
wxString Model_3D;
};
struct DOCUMENT_PCB
{
std::optional<std::map<wxString, wxString>> c_para;
std::vector<wxString> layers;
std::optional<wxString> uuid;
std::optional<std::map<wxString, nlohmann::json>> DRCRULE;
};
struct DOCUMENT_SYM
{
std::optional<std::map<wxString, wxString>> c_para;
};
struct DOCUMENT_SCHEMATICS
{
std::optional<std::vector<DOCUMENT>> schematics;
};
void from_json( const nlohmann::json& j, EASYEDA::DOC_TYPE& d );
void from_json( const nlohmann::json& j, EASYEDA::HEAD& d );
void from_json( const nlohmann::json& j, EASYEDA::DOCUMENT& d );
void from_json( const nlohmann::json& j, EASYEDA::C_PARA& d );
void from_json( const nlohmann::json& j, EASYEDA::DOCUMENT_PCB& d );
void from_json( const nlohmann::json& j, EASYEDA::DOCUMENT_SYM& d );
void from_json( const nlohmann::json& j, EASYEDA::DOCUMENT_SCHEMATICS& d );
enum class POWER_FLAG_STYLE
{
UNKNOWN = -1,
CIRCLE = 0,
ARROW = 1,
BAR = 2,
WAVE = 3,
POWER_GROUND = 4,
SIGNAL_GROUND = 5,
EARTH = 6,
GOST_ARROW = 7,
GOST_POWER_GROUND = 8,
GOST_EARTH = 9,
GOST_BAR = 10
};
} // namespace EASYEDA
#endif // EASYEDA_PARSER_STRUCTS_H_

View File

@ -0,0 +1,151 @@
/*
* 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.
*
* 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 "easyedapro_import_utils.h"
#include <string_utils.h>
#include <nlohmann/json.hpp>
#include <wx/log.h>
#include <wx/stream.h>
#include <wx/zipstrm.h>
#include <wx/wfstream.h>
#include <wx/mstream.h>
#include <wx/txtstrm.h>
wxString EASYEDAPRO::ShortenLibName( wxString aProjectName )
{
wxString shortenedName = aProjectName;
shortenedName.Replace( wxS( "ProProject_" ), wxS( "" ) );
shortenedName.Replace( wxS( "ProDocument_" ), wxS( "" ) );
shortenedName = shortenedName.substr( 0, 10 );
return LIB_ID::FixIllegalChars( shortenedName + wxS( "-easyedapro" ), true );
}
LIB_ID EASYEDAPRO::ToKiCadLibID( const wxString& aLibName, const wxString& aLibReference )
{
wxString libName = LIB_ID::FixIllegalChars( aLibName, true );
wxString libReference = EscapeString( aLibReference, CTX_LIBID );
wxString key = !aLibName.empty() ? ( aLibName + ':' + libReference ) : libReference;
LIB_ID libId;
libId.Parse( key, true );
return libId;
}
nlohmann::json EASYEDAPRO::ReadProjectFile( const wxString& aZipFileName )
{
std::shared_ptr<wxZipEntry> entry;
wxFFileInputStream in( aZipFileName );
wxZipInputStream zip( in );
while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
{
wxString name = entry->GetName();
if( name == wxS( "project.json" ) )
{
wxMemoryOutputStream memos;
memos << zip;
wxStreamBuffer* buf = memos.GetOutputStreamBuffer();
wxString str( (char*) buf->GetBufferStart(), buf->GetBufferSize() );
return nlohmann::json::parse( str );
}
}
THROW_IO_ERROR( wxString::Format( _( "'%s' does not appear to be a valid EasyEDA (JLCEDA) Pro "
"project file. Cannot find project.json." ),
aZipFileName ) );
}
void EASYEDAPRO::IterateZipFiles(
const wxString& aFileName,
std::function<bool( const wxString&, const wxString&, wxInputStream& )> aCallback )
{
std::shared_ptr<wxZipEntry> entry;
wxFFileInputStream in( aFileName );
wxZipInputStream zip( in );
if( !zip.IsOk() )
{
THROW_IO_ERROR( wxString::Format( _( "Cannot read ZIP archive '%s'" ), aFileName ) );
}
while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
{
wxString name = entry->GetName();
wxString baseName = name.AfterLast( '\\' ).AfterLast( '/' ).BeforeFirst( '.' );
try
{
if( aCallback( name, baseName, zip ) )
break;
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR(
wxString::Format( _( "JSON error reading '%s': %s" ), name, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error reading '%s': %s" ), name, e.what() ) );
}
}
}
std::vector<nlohmann::json> EASYEDAPRO::ParseJsonLines( wxInputStream& aInput,
const wxString& aSource )
{
wxTextInputStream txt( aInput, wxS( " " ), wxConvUTF8 );
int currentLine = 1;
std::vector<nlohmann::json> lines;
while( aInput.CanRead() )
{
try
{
nlohmann::json js = nlohmann::json::parse( txt.ReadLine() );
lines.emplace_back( js );
}
catch( nlohmann::json::exception& e )
{
wxLogWarning( wxString::Format( _( "Cannot parse JSON line %d in '%s': %s" ),
currentLine, aSource, e.what() ) );
}
currentLine++;
}
return lines;
}

View File

@ -0,0 +1,53 @@
/*
* 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.
*
* 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
*/
#ifndef EASYEDAPRO_IMPORT_UTILS_H_
#define EASYEDAPRO_IMPORT_UTILS_H_
#include <wx/string.h>
#include <lib_id.h>
#include <nlohmann/json_fwd.hpp>
#define EASY_IT_CONTINUE return false
#define EASY_IT_BREAK return true
namespace EASYEDAPRO
{
wxString ShortenLibName( wxString aProjectName );
LIB_ID ToKiCadLibID( const wxString& aLibName, const wxString& aLibReference );
nlohmann::json ReadProjectFile( const wxString& aZipFileName );
void IterateZipFiles(
const wxString& aFileName,
std::function<bool( const wxString&, const wxString&, wxInputStream& )> aCallback );
std::vector<nlohmann::json> ParseJsonLines( wxInputStream& aInput, const wxString& aSource );
} // namespace EASYEDAPRO
#endif // EASYEDAPRO_IMPORT_UTILS_H_

View File

@ -0,0 +1,274 @@
/*
* 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.
*
* 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 "easyedapro_parser.h"
#include <memory>
#include <nlohmann/json.hpp>
#include <core/json_serializers.h>
#include <string_utils.h>
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::SCH_ATTR& d )
{
d.id = j.at( 1 ).get<wxString>();
d.parentId = j.at( 2 ).get<wxString>();
d.key = j.at( 3 ).get<wxString>();
d.value = j.at( 4 ).get<wxString>();
if( j.at( 5 ).is_number() )
d.keyVisible = j.at( 5 ).get<int>();
else if( j.at( 5 ).is_boolean() )
d.keyVisible = j.at( 5 ).get<bool>();
if( j.at( 6 ).is_number() )
d.valVisible = j.at( 6 ).get<int>();
else if( j.at( 6 ).is_boolean() )
d.valVisible = j.at( 6 ).get<bool>();
if( j.at( 7 ).is_number() && j.at( 8 ).is_number() )
d.position = VECTOR2D( j.at( 7 ), j.at( 8 ) );
if( j.at( 9 ).is_number() )
d.rotation = j.at( 9 );
if( j.at( 10 ).is_string() )
d.fontStyle = j.at( 10 ).get<wxString>();
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PCB_ATTR& d )
{
d.id = j.at( 1 ).get<wxString>();
d.parentId = j.at( 3 ).get<wxString>();
d.layer = j.at( 4 );
if( j.at( 5 ).is_number() && j.at( 6 ).is_number() )
d.position = VECTOR2D( j.at( 5 ), j.at( 6 ) );
d.key = j.at( 7 ).get<wxString>();
d.value = j.at( 8 ).get<wxString>();
if( j.at( 9 ).is_number() )
d.keyVisible = j.at( 9 ).get<int>();
else if( j.at( 9 ).is_boolean() )
d.keyVisible = j.at( 9 ).get<bool>();
if( j.at( 10 ).is_number() )
d.valVisible = j.at( 10 ).get<int>();
else if( j.at( 10 ).is_boolean() )
d.valVisible = j.at( 10 ).get<bool>();
if( j.at( 11 ).is_string() )
d.fontName = j.at( 11 ).get<wxString>();
if( j.at( 12 ).is_number() )
d.height = j.at( 12 );
if( j.at( 13 ).is_number() )
d.strokeWidth = j.at( 13 );
if( j.at( 16 ).is_number() )
d.textOrigin = j.at( 16 );
if( j.at( 17 ).is_number() )
d.rotation = j.at( 17 );
if( j.at( 18 ).is_number() )
d.inverted = j.at( 18 );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::SCH_COMPONENT& d )
{
d.id = j.at( 1 ).get<wxString>();
d.name = j.at( 2 ).get<wxString>();
if( j.at( 3 ).is_number() && j.at( 4 ).is_number() )
d.position = VECTOR2D( j.at( 3 ), j.at( 4 ) );
if( j.at( 5 ).is_number() )
d.rotation = j.at( 5 );
if( j.at( 6 ).is_number() )
d.mirror = j.at( 6 ).get<int>();
if( j.at( 6 ).is_number() )
d.unk1 = j.at( 6 );
if( j.at( 7 ).is_object() )
d.customProps = j.at( 7 );
if( j.at( 8 ).is_number() )
d.unk2 = j.at( 8 );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::SCH_WIRE& d )
{
d.id = j.at( 1 ).get<wxString>();
d.geometry = j.at( 2 );
if( j.at( 3 ).is_string() )
d.lineStyle = j.at( 3 ).get<wxString>();
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::SYM_PIN& d )
{
d.id = j.at( 1 ).get<wxString>();
if( j.at( 4 ).is_number() && j.at( 5 ).is_number() )
d.position = VECTOR2D( j.at( 4 ), j.at( 5 ) );
if( j.at( 6 ).is_number() )
d.length = j.at( 6 );
if( j.at( 7 ).is_number() )
d.rotation = j.at( 7 );
if( j.at( 9 ).is_number() )
d.inverted = j.at( 9 ).get<int>() == 2;
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::SYM_HEAD& d )
{
if( !j.at( 1 ).is_object() )
return;
nlohmann::json config = j.at( 1 );
d.origin.x = config.value( "originX", 0 );
d.origin.y = config.value( "originY", 0 );
d.maxId = config.value( "maxId", 0 );
d.version = config.value( "version", "" );
d.symbolType = config.value( "symbolType", SYMBOL_TYPE::NORMAL );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_SHEET& d )
{
d.name = j.value( "name", "" );
d.uuid = j.value( "uuid", "" );
d.id = j.value( "id", 0 );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_SCHEMATIC& d )
{
d.name = j.value( "name", "" );
d.sheets = j.value( "sheets", std::vector<PRJ_SHEET>{} );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_BOARD& d )
{
d.schematic = j.value( "schematic", "" );
d.pcb = j.value( "pcb", "" );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_SYMBOL& d )
{
if( j.at( "source" ).is_string() )
d.source = j.at( "source" ).get<wxString>();
if( j.at( "desc" ).is_string() )
d.desc = j.at( "desc" ).get<wxString>();
if( j.at( "title" ).is_string() )
d.title = j.at( "title" ).get<wxString>();
if( j.at( "version" ).is_string() )
d.version = j.at( "version" ).get<wxString>();
if( j.at( "type" ).is_number() )
d.type = j.at( "type" );
if( j.at( "tags" ).is_object() )
d.tags = j.at( "tags" );
if( j.find( "custom_tags" ) != j.end() && j.at( "custom_tags" ).is_object() )
d.custom_tags = j.at( "custom_tags" );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_FOOTPRINT& d )
{
if( j.at( "source" ).is_string() )
d.source = j.at( "source" ).get<wxString>();
if( j.at( "desc" ).is_string() )
d.desc = j.at( "desc" ).get<wxString>();
if( j.at( "title" ).is_string() )
d.title = j.at( "title" ).get<wxString>();
if( j.at( "version" ).is_string() )
d.version = j.at( "version" ).get<wxString>();
if( j.at( "type" ).is_number() )
d.type = j.at( "type" );
if( j.at( "tags" ).is_object() )
d.tags = j.at( "tags" );
if( j.find( "custom_tags" ) != j.end() && j.at( "custom_tags" ).is_object() )
d.custom_tags = j.at( "custom_tags" );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_DEVICE& d )
{
if( j.at( "source" ).is_string() )
d.source = j.at( "source" ).get<wxString>();
if( j.at( "description" ).is_string() )
d.description = j.at( "description" ).get<wxString>();
if( j.at( "title" ).is_string() )
d.title = j.at( "title" ).get<wxString>();
if( j.at( "version" ).is_string() )
d.version = j.at( "version" ).get<wxString>();
if( j.at( "tags" ).is_object() )
d.tags = j.at( "tags" );
if( j.find( "custom_tags" ) != j.end() && j.at( "custom_tags" ).is_object() )
d.custom_tags = j.at( "custom_tags" );
if( j.at( "attributes" ).is_object() )
d.attributes = j.at( "attributes" );
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::BLOB& d )
{
d.objectId = j.at( 1 ).get<wxString>();
d.url = j.at( 3 ).get<wxString>();
}
void EASYEDAPRO::from_json( const nlohmann::json& j, EASYEDAPRO::POURED& d )
{
d.pouredId = j.at( 1 ).get<wxString>();
d.parentId = j.at( 2 ).get<wxString>();
d.unki = j.at( 3 ).get<int>();
d.isPoly = j.at( 4 ).get<bool>();
d.polyData = j.at( 5 );
}

View File

@ -0,0 +1,218 @@
/*
* 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.
*
* 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
*/
#ifndef EASYEDAPRO_PARSER_H_
#define EASYEDAPRO_PARSER_H_
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <map>
#include <optional>
#include <wx/string.h>
#include <nlohmann/json.hpp>
#include <math/vector2d.h>
namespace EASYEDAPRO
{
static const bool IMPORT_POURED = false;
enum class SYMBOL_TYPE
{
NORMAL = 2,
POWER_PORT = 18,
NETPORT = 19,
SHEET_SYMBOL = 20,
SHORT = 22,
};
enum class FOOTPRINT_TYPE
{
NORMAL = 4,
};
struct SCH_ATTR
{
wxString id;
wxString parentId;
wxString key;
wxString value;
bool keyVisible = false;
bool valVisible = false;
std::optional<VECTOR2D> position;
double rotation = 0;
wxString fontStyle;
};
struct PCB_ATTR
{
wxString id;
int layer = 0;
wxString parentId;
int textOrigin = 0;
VECTOR2D position;
wxString key;
wxString value;
bool keyVisible = false;
bool valVisible = false;
wxString fontName;
double height = 0;
double strokeWidth = 0;
double rotation = 0;
int inverted = 0;
};
struct SCH_COMPONENT
{
wxString id;
wxString name;
VECTOR2D position;
double rotation = 0;
bool mirror = false;
nlohmann::json customProps;
int unk1 = 0;
int unk2 = 0;
};
struct SCH_WIRE
{
wxString id;
std::vector<std::vector<double>> geometry;
wxString lineStyle;
int unk1 = 0;
};
struct SYM_PIN
{
wxString id;
VECTOR2D position;
double length = 0;
double rotation = 0;
bool inverted = false;
};
struct SYM_HEAD
{
VECTOR2D origin;
wxString version;
int maxId = 0;
SYMBOL_TYPE symbolType = SYMBOL_TYPE::NORMAL;
};
struct PRJ_SHEET
{
int id = 0;
wxString name;
wxString uuid;
};
struct PRJ_SCHEMATIC
{
wxString name;
std::vector<PRJ_SHEET> sheets;
};
struct PRJ_BOARD
{
wxString schematic;
wxString pcb;
};
struct PRJ_SYMBOL
{
wxString source;
wxString desc;
nlohmann::json tags;
nlohmann::json custom_tags;
wxString title;
wxString version;
SYMBOL_TYPE type = SYMBOL_TYPE::NORMAL;
};
struct PRJ_FOOTPRINT
{
wxString source;
wxString desc;
nlohmann::json tags;
nlohmann::json custom_tags;
wxString title;
wxString version;
FOOTPRINT_TYPE type = FOOTPRINT_TYPE::NORMAL;
};
struct PRJ_DEVICE
{
wxString source;
wxString description;
nlohmann::json tags;
nlohmann::json custom_tags;
wxString title;
wxString version;
std::map<wxString, wxString> attributes;
};
struct BLOB
{
wxString objectId;
wxString url;
};
struct POURED
{
wxString pouredId;
wxString parentId;
int unki = 0;
bool isPoly = false;
nlohmann::json polyData;
};
void from_json( const nlohmann::json& j, EASYEDAPRO::SCH_ATTR& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PCB_ATTR& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::SCH_COMPONENT& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::SCH_WIRE& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::SYM_PIN& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::SYM_HEAD& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_SHEET& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_SCHEMATIC& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_BOARD& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_SYMBOL& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_FOOTPRINT& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::PRJ_DEVICE& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::BLOB& d );
void from_json( const nlohmann::json& j, EASYEDAPRO::POURED& d );
} // namespace EASYEDAPRO
#endif // EASYEDAPRO_PARSER_H_

View File

@ -534,6 +534,20 @@ wxString EscapeHTML( const wxString& aString )
} }
wxString UnescapeHTML( const wxString& aString )
{
wxString converted = aString;
converted.Replace( wxS( "&quot;" ), wxS( "\"" ) );
converted.Replace( wxS( "&apos;" ), wxS( "'" ) );
converted.Replace( wxS( "&amp;" ), wxS( "&" ) );
converted.Replace( wxS( "&lt;" ), wxS( "<" ) );
converted.Replace( wxS( "&gt;" ), wxS( ">" ) );
return converted;
}
bool NoPrintableChars( const wxString& aString ) bool NoPrintableChars( const wxString& aString )
{ {
wxString tmp = aString; wxString tmp = aString;

View File

@ -291,6 +291,18 @@ wxString AllegroNetlistFileWildcard()
} }
wxString EasyEdaArchiveWildcard()
{
return _( "EasyEDA (JLCEDA) Std backup archive" ) + AddFileExtListToFilter( { "zip" } );
}
wxString EasyEdaProFileWildcard()
{
return _( "EasyEDA (JLCEDA) Pro files" ) + AddFileExtListToFilter( { "epro", "zip" } );
}
wxString PcbFileWildcard() wxString PcbFileWildcard()
{ {
return _( "KiCad printed circuit board files" ) return _( "KiCad printed circuit board files" )

View File

@ -60,6 +60,16 @@ set( EESCHEMA_SCH_PLUGINS_LTSPICE
sch_plugins/ltspice/ltspice_sch_plugin.cpp sch_plugins/ltspice/ltspice_sch_plugin.cpp
) )
set( EESCHEMA_SCH_PLUGINS_EASYEDA
sch_plugins/easyeda/sch_easyeda_parser.cpp
sch_plugins/easyeda/sch_easyeda_plugin.cpp
)
set( EESCHEMA_SCH_PLUGINS_EASYEDAPRO
sch_plugins/easyedapro/sch_easyedapro_parser.cpp
sch_plugins/easyedapro/sch_easyedapro_plugin.cpp
)
set( EESCHEMA_DLGS set( EESCHEMA_DLGS
dialogs/dialog_annotate.cpp dialogs/dialog_annotate.cpp
dialogs/dialog_annotate_base.cpp dialogs/dialog_annotate_base.cpp
@ -278,6 +288,8 @@ set( EESCHEMA_SRCS
${EESCHEMA_SCH_PLUGINS_ALTIUM} ${EESCHEMA_SCH_PLUGINS_ALTIUM}
${EESCHEMA_SCH_PLUGINS_CADSTAR} ${EESCHEMA_SCH_PLUGINS_CADSTAR}
${EESCHEMA_SCH_PLUGINS_LTSPICE} ${EESCHEMA_SCH_PLUGINS_LTSPICE}
${EESCHEMA_SCH_PLUGINS_EASYEDA}
${EESCHEMA_SCH_PLUGINS_EASYEDAPRO}
${EESCHEMA_SIM_SRCS} ${EESCHEMA_SIM_SRCS}
${EESCHEMA_WIDGETS} ${EESCHEMA_WIDGETS}
${EESCHEMA_IMPORT_GFX} ${EESCHEMA_IMPORT_GFX}
@ -301,6 +313,7 @@ set( EESCHEMA_SRCS
fields_grid_table.cpp fields_grid_table.cpp
files-io.cpp files-io.cpp
generate_alias_info.cpp generate_alias_info.cpp
gfx_import_utils.cpp
picksymbol.cpp picksymbol.cpp
lib_field.cpp lib_field.cpp
lib_item.cpp lib_item.cpp

View File

@ -1249,6 +1249,8 @@ void SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
case SCH_IO_MGR::SCH_CADSTAR_ARCHIVE: case SCH_IO_MGR::SCH_CADSTAR_ARCHIVE:
case SCH_IO_MGR::SCH_EAGLE: case SCH_IO_MGR::SCH_EAGLE:
case SCH_IO_MGR::SCH_LTSPICE: case SCH_IO_MGR::SCH_LTSPICE:
case SCH_IO_MGR::SCH_EASYEDA:
case SCH_IO_MGR::SCH_EASYEDAPRO:
{ {
// We insist on caller sending us an absolute path, if it does not, we say it's a bug. // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
wxCHECK_MSG( filename.IsAbsolute(), /*void*/, wxCHECK_MSG( filename.IsAbsolute(), /*void*/,

View File

@ -0,0 +1,155 @@
/*
* 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.
*
* 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 "gfx_import_utils.h"
#include <stdint.h>
#include <lib_symbol.h>
#include <lib_shape.h>
#include <import_gfx/graphics_importer_lib_symbol.h>
#include <import_gfx/svg_import_plugin.h>
std::unordered_map<uint32_t, SHAPE_POLY_SET> ConvertImageToPolygons( wxImage img,
VECTOR2D pixelScale )
{
// Quantize the image
for( int y = 0; y < img.GetHeight(); y++ )
{
for( int x = 0; x < img.GetWidth(); x++ )
{
int r = img.GetRed( x, y );
int g = img.GetGreen( x, y );
int b = img.GetBlue( x, y );
int a = img.GetAlpha( x, y );
int roundBits = 5; // 32
r = std::min( r >> roundBits << roundBits, 0xFF );
g = std::min( g >> roundBits << roundBits, 0xFF );
b = std::min( b >> roundBits << roundBits, 0xFF );
a = std::min( a >> roundBits << roundBits, 0xFF );
img.SetRGB( x, y, r, g, b );
img.SetAlpha( x, y, a );
}
}
std::unordered_map<uint32_t, SHAPE_POLY_SET> colorPolys;
// Create polygon sets
for( int y = 0; y < img.GetHeight(); y++ )
{
for( int x = 0; x < img.GetWidth(); x++ )
{
uint32_t r = img.GetRed( x, y );
uint32_t g = img.GetGreen( x, y );
uint32_t b = img.GetBlue( x, y );
uint32_t a = img.GetAlpha( x, y );
if( a > 0 )
{
uint32_t color = r | ( g << 8 ) | ( b << 16 ) | ( a << 24 );
SHAPE_POLY_SET& colorPoly = colorPolys[color];
SHAPE_LINE_CHAIN chain;
chain.Append( x * pixelScale.x, y * pixelScale.y, true );
chain.Append( ( x + 1 ) * pixelScale.x, y * pixelScale.y, true );
chain.Append( ( x + 1 ) * pixelScale.x, ( y + 1 ) * pixelScale.y, true );
chain.Append( x * pixelScale.x, ( y + 1 ) * pixelScale.y, true );
chain.SetClosed( true );
colorPoly.AddOutline( chain );
}
}
}
for( auto& [color, polySet] : colorPolys )
{
polySet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
for( int i = 0; i < polySet.OutlineCount(); i++ )
{
SHAPE_POLY_SET::POLYGON& poly = polySet.Polygon( i );
for( SHAPE_LINE_CHAIN& chain : poly )
chain.Simplify();
}
}
return colorPolys;
}
void ConvertImageToLibShapes( LIB_SYMBOL* aSymbol, int unit, wxImage img, VECTOR2D pixelScale,
VECTOR2D offset )
{
std::unordered_map<uint32_t, SHAPE_POLY_SET> colorPolys =
ConvertImageToPolygons( img, pixelScale );
for( auto& [color, polySet] : colorPolys )
{
polySet.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
{
std::unique_ptr<LIB_SHAPE> shape =
std::make_unique<LIB_SHAPE>( aSymbol, SHAPE_T::POLY );
shape->SetPolyShape( poly );
int r = color & 0xFF;
int g = ( color >> 8 ) & 0xFF;
int b = ( color >> 16 ) & 0xFF;
int a = ( color >> 24 ) & 0xFF;
shape->SetWidth( -1 );
shape->SetFillMode( FILL_T::FILLED_WITH_COLOR );
shape->SetFillColor( COLOR4D( r / 255.0, g / 255.0, b / 255.0, a / 255.0 ) );
shape->SetUnit( unit );
shape->Offset( offset );
aSymbol->AddDrawItem( shape.release() );
}
}
}
void ConvertSVGToLibShapes( LIB_SYMBOL* aSymbol, int unit, const wxMemoryBuffer& aImageData,
VECTOR2D pixelScale, VECTOR2D offset )
{
SVG_IMPORT_PLUGIN svgImportPlugin;
GRAPHICS_IMPORTER_LIB_SYMBOL libeditImporter( aSymbol, unit );
libeditImporter.SetScale( pixelScale );
libeditImporter.SetImportOffsetMM(
VECTOR2D( schIUScale.IUTomm( offset.x ), schIUScale.IUTomm( offset.y ) ) );
svgImportPlugin.SetImporter( &libeditImporter );
svgImportPlugin.LoadFromMemory( aImageData );
svgImportPlugin.Import();
}

View File

@ -0,0 +1,39 @@
/*
* 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.
*
* 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
*/
#ifndef GFX_IMPORT_UTILS_H_
#define GFX_IMPORT_UTILS_H_
#include <unordered_map>
#include <geometry/shape_poly_set.h>
#include <wx/image.h>
#include <lib_symbol.h>
std::unordered_map<uint32_t, SHAPE_POLY_SET> ConvertImageToPolygons( wxImage img,
VECTOR2D pixelScale );
void ConvertImageToLibShapes( LIB_SYMBOL* aSymbol, int unit, wxImage img, VECTOR2D pixelScale,
VECTOR2D offset );
#endif // GFX_IMPORT_UTILS_H_

View File

@ -30,6 +30,8 @@
#include <sch_plugins/altium/sch_altium_plugin.h> #include <sch_plugins/altium/sch_altium_plugin.h>
#include <sch_plugins/cadstar/cadstar_sch_archive_plugin.h> #include <sch_plugins/cadstar/cadstar_sch_archive_plugin.h>
#include <sch_plugins/easyeda/sch_easyeda_plugin.h>
#include <sch_plugins/easyedapro/sch_easyedapro_plugin.h>
#include <sch_plugins/database/sch_database_plugin.h> #include <sch_plugins/database/sch_database_plugin.h>
#include <sch_plugins/ltspice/ltspice_sch_plugin.h> #include <sch_plugins/ltspice/ltspice_sch_plugin.h>
#include <common.h> // for ExpandEnvVarSubstitutions #include <common.h> // for ExpandEnvVarSubstitutions
@ -67,6 +69,8 @@ SCH_PLUGIN* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType )
case SCH_CADSTAR_ARCHIVE: return new CADSTAR_SCH_ARCHIVE_PLUGIN(); case SCH_CADSTAR_ARCHIVE: return new CADSTAR_SCH_ARCHIVE_PLUGIN();
case SCH_DATABASE: return new SCH_DATABASE_PLUGIN(); case SCH_DATABASE: return new SCH_DATABASE_PLUGIN();
case SCH_EAGLE: return new SCH_EAGLE_PLUGIN(); case SCH_EAGLE: return new SCH_EAGLE_PLUGIN();
case SCH_EASYEDA: return new SCH_EASYEDA_PLUGIN();
case SCH_EASYEDAPRO: return new SCH_EASYEDAPRO_PLUGIN();
case SCH_LTSPICE: return new SCH_LTSPICE_PLUGIN(); case SCH_LTSPICE: return new SCH_LTSPICE_PLUGIN();
default: return nullptr; default: return nullptr;
} }
@ -97,6 +101,8 @@ const wxString SCH_IO_MGR::ShowType( SCH_FILE_T aType )
case SCH_CADSTAR_ARCHIVE: return wxString( wxT( "CADSTAR Schematic Archive" ) ); case SCH_CADSTAR_ARCHIVE: return wxString( wxT( "CADSTAR Schematic Archive" ) );
case SCH_DATABASE: return wxString( wxT( "Database" ) ); case SCH_DATABASE: return wxString( wxT( "Database" ) );
case SCH_EAGLE: return wxString( wxT( "EAGLE" ) ); case SCH_EAGLE: return wxString( wxT( "EAGLE" ) );
case SCH_EASYEDA: return wxString( wxT( "EasyEDA (JLCEDA) Std" ) );
case SCH_EASYEDAPRO: return wxString( wxT( "EasyEDA (JLCEDA) Pro" ) );
case SCH_LTSPICE: return wxString( wxT( "LTspice" ) ); case SCH_LTSPICE: return wxString( wxT( "LTspice" ) );
default: return wxString::Format( _( "Unknown SCH_FILE_T value: %d" ), default: return wxString::Format( _( "Unknown SCH_FILE_T value: %d" ),
aType ); aType );
@ -122,6 +128,10 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::EnumFromStr( const wxString& aType )
return SCH_DATABASE; return SCH_DATABASE;
else if( aType == wxT( "EAGLE" ) ) else if( aType == wxT( "EAGLE" ) )
return SCH_EAGLE; return SCH_EAGLE;
else if( aType == wxT( "EasyEDA (JLCEDA) Std" ) )
return SCH_EASYEDA;
else if( aType == wxT( "EasyEDA (JLCEDA) Pro" ) )
return SCH_EASYEDAPRO;
else if( aType == wxT( "LTspice" ) ) else if( aType == wxT( "LTspice" ) )
return SCH_LTSPICE; return SCH_LTSPICE;

View File

@ -64,6 +64,8 @@ public:
SCH_CADSTAR_ARCHIVE, ///< CADSTAR Schematic Archive SCH_CADSTAR_ARCHIVE, ///< CADSTAR Schematic Archive
SCH_DATABASE, ///< KiCad database library SCH_DATABASE, ///< KiCad database library
SCH_EAGLE, ///< Autodesk Eagle file format SCH_EAGLE, ///< Autodesk Eagle file format
SCH_EASYEDA, ///< EasyEDA Std schematic file
SCH_EASYEDAPRO, ///< EasyEDA Pro archive
SCH_LTSPICE, ///< LtSpice Schematic format SCH_LTSPICE, ///< LtSpice Schematic format
// Add your schematic type here. // Add your schematic type here.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
/*
* 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.
*
* 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
*/
#ifndef SCH_EASYEDA_PARSER_H_
#define SCH_EASYEDA_PARSER_H_
#include <plugins/easyeda/easyeda_parser_base.h>
#include <plugins/easyeda/easyeda_parser_structs.h>
#include <sch_io_mgr.h>
#include <pin_type.h>
#include <layer_ids.h>
#include <wx/filename.h>
#include <plotters/plotter.h>
class EDA_TEXT;
class LIB_SHAPE;
class LIB_PIN;
class SCH_LABEL_BASE;
class SCH_SYMBOL;
class SCH_TEXT;
class SCH_SHAPE;
class SCH_EASYEDA_PARSER: public EASYEDA_PARSER_BASE
{
public:
explicit SCH_EASYEDA_PARSER( SCHEMATIC* aSchematic, PROGRESS_REPORTER* aProgressReporter );
~SCH_EASYEDA_PARSER();
double ScaleSize( double aValue ) override
{
return KiROUND( schIUScale.MilsToIU( aValue * 10 ) );
}
template <typename T>
VECTOR2<T> RelPosSym( const VECTOR2<T>& aVec )
{
return VECTOR2<T>( RelPosX( aVec.x ), -RelPosY( aVec.y ) );
}
std::pair<LIB_SYMBOL*, bool> MakePowerSymbol( const wxString& aFlagTypename,
const wxString& aNetname );
void ParseSymbolShapes( LIB_SYMBOL* aContainer, std::map<wxString, wxString> paramMap,
wxArrayString aShapes );
LIB_SYMBOL* ParseSymbol( const VECTOR2D& aOrigin, std::map<wxString, wxString> aParams,
wxArrayString aShapes );
void ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const wxString& aFileName,
wxArrayString aShapes );
private:
SCHEMATIC* m_schematic;
};
#endif // SCH_EASYEDA_PARSER_H_

View File

@ -0,0 +1,583 @@
/*
* 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.
*
* 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 "sch_easyeda_plugin.h"
#include "sch_easyeda_parser.h"
#include <schematic.h>
#include <sch_sheet.h>
#include <sch_screen.h>
#include <kiplatform/environment.h>
#include <wx/log.h>
#include <wx/stdstream.h>
#include <wx/zipstrm.h>
#include <nlohmann/json.hpp>
#include <core/map_helpers.h>
#include <wx/wfstream.h>
const wxString SCH_EASYEDA_PLUGIN::GetName() const
{
return wxT( "EasyEDA (JLCEDA) Schematic Importer" );
}
static bool FindSchFileInStream( const wxString& aName, wxInputStream& aStream,
nlohmann::json& aOut, EASYEDA::DOCUMENT& aDoc,
EASYEDA::DOC_TYPE& aDocType )
{
if( aName.Lower().EndsWith( wxS( ".json" ) ) )
{
wxStdInputStream sin( aStream );
nlohmann::json js = nlohmann::json::parse( sin, nullptr, false );
if( js.is_discarded() )
return false;
EASYEDA::DOCUMENT doc = js.get<EASYEDA::DOCUMENT>();
EASYEDA::DOC_TYPE type;
if( doc.docType )
type = *doc.docType;
else
type = doc.head.docType;
if( type == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET || type == EASYEDA::DOC_TYPE::SCHEMATIC_LIST
|| type == EASYEDA::DOC_TYPE::SYMBOL )
{
aOut = js;
aDoc = doc;
aDocType = type;
return true;
}
}
else if( aName.Lower().EndsWith( wxS( ".zip" ) ) )
{
std::shared_ptr<wxZipEntry> entry;
wxZipInputStream zip( aStream );
if( !zip.IsOk() )
return false;
while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
{
wxString name = entry->GetName();
if( FindSchFileInStream( name, zip, aOut, aDoc, aDocType ) )
return true;
}
}
return false;
}
bool SCH_EASYEDA_PLUGIN::CanReadSchematicFile( const wxString& aFileName ) const
{
if( !SCH_PLUGIN::CanReadSchematicFile( aFileName ) )
return false;
try
{
wxFFileInputStream in( aFileName );
nlohmann::json js;
EASYEDA::DOCUMENT doc;
EASYEDA::DOC_TYPE docType;
return FindSchFileInStream( aFileName, in, js, doc, docType );
}
catch( nlohmann::json::exception& )
{
}
catch( std::exception& )
{
}
return false;
}
bool SCH_EASYEDA_PLUGIN::CanReadLibrary( const wxString& aFileName ) const
{
return CanReadSchematicFile( aFileName );
}
int SCH_EASYEDA_PLUGIN::GetModifyHash() const
{
return 0;
}
LIB_SYMBOL* loadSymbol( const wxString& aLibraryPath, nlohmann::json aFileData,
const wxString& aAliasName, const STRING_UTF8_MAP* aProperties )
{
SCH_EASYEDA_PARSER parser( nullptr, nullptr );
std::map<wxString, int> namesCounter;
try
{
wxFFileInputStream in( aLibraryPath );
nlohmann::json js;
EASYEDA::DOCUMENT topDoc;
EASYEDA::DOC_TYPE topDocType;
if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
aLibraryPath ) );
}
if( topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET
|| topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_LIST )
{
EASYEDA::DOCUMENT_SCHEMATICS schDoc = js.get<EASYEDA::DOCUMENT_SCHEMATICS>();
for( const EASYEDA::DOCUMENT& subDoc : *schDoc.schematics )
{
if( subDoc.docType )
{
if( *subDoc.docType != EASYEDA::DOC_TYPE::SCHEMATIC_SHEET )
continue;
}
else
{
if( subDoc.head.docType != EASYEDA::DOC_TYPE::SCHEMATIC_SHEET )
continue;
}
EASYEDA::DOCUMENT dataStrDoc = subDoc.dataStr->get<EASYEDA::DOCUMENT>();
for( wxString shap : dataStrDoc.shape )
{
if( !shap.Contains( wxS( "LIB" ) ) )
continue;
shap.Replace( wxS( "#@$" ), wxS( "\n" ) );
wxArrayString parts = wxSplit( shap, '\n', '\0' );
if( parts.size() < 1 )
continue;
wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
if( paramsRoot.size() < 1 )
continue;
wxString rootType = paramsRoot[0];
if( rootType == wxS( "LIB" ) )
{
if( paramsRoot.size() < 4 )
continue;
VECTOR2D origin( parser.Convert( paramsRoot[1] ),
parser.Convert( paramsRoot[2] ) );
wxString symbolName = wxString::Format( wxS( "Unknown_%s_%s" ),
paramsRoot[1], paramsRoot[2] );
wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
std::map<wxString, wxString> paramMap;
for( size_t i = 1; i < paramParts.size(); i += 2 )
{
wxString key = paramParts[i - 1];
wxString value = paramParts[i];
if( key == wxS( "spiceSymbolName" ) && !value.IsEmpty() )
symbolName = value;
paramMap[key] = value;
}
int& serial = namesCounter[symbolName];
if( serial > 0 )
symbolName << wxS( "_" ) << serial;
serial++;
paramMap[wxS( "spiceSymbolName" )] = symbolName;
if( symbolName == aAliasName )
{
parts.RemoveAt( 0 );
return parser.ParseSymbol( origin, paramMap, parts );
}
}
}
}
}
else if( topDocType == EASYEDA::DOC_TYPE::SYMBOL )
{
EASYEDA::DOCUMENT_SYM symDoc = js.get<EASYEDA::DOCUMENT_SYM>();
wxString symbolName = wxS( "Unknown" );
std::optional<std::map<wxString, wxString>> c_para;
if( symDoc.c_para )
c_para = symDoc.c_para;
else if( topDoc.head.c_para )
c_para = topDoc.head.c_para;
if( !c_para )
return nullptr;
symbolName = get_def( *c_para, wxS( "name" ), symbolName );
int& serial = namesCounter[symbolName];
if( serial > 0 )
symbolName << wxS( "_" ) << serial;
serial++;
if( symbolName != aAliasName )
return nullptr;
VECTOR2D origin( topDoc.head.x, topDoc.head.y );
return parser.ParseSymbol( origin, *c_para, topDoc.shape );
}
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
aAliasName, aLibraryPath, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
aAliasName, aLibraryPath, e.what() ) );
}
return nullptr;
}
void SCH_EASYEDA_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties )
{
std::map<wxString, int> namesCounter;
try
{
wxFFileInputStream in( aLibraryPath );
nlohmann::json js;
EASYEDA::DOCUMENT topDoc;
EASYEDA::DOC_TYPE topDocType;
if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
aLibraryPath ) );
}
if( topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET
|| topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_LIST )
{
EASYEDA::DOCUMENT_SCHEMATICS schDoc = js.get<EASYEDA::DOCUMENT_SCHEMATICS>();
for( const EASYEDA::DOCUMENT& subDoc : *schDoc.schematics )
{
if( subDoc.docType )
{
if( *subDoc.docType != EASYEDA::DOC_TYPE::SCHEMATIC_SHEET )
continue;
}
else
{
if( subDoc.head.docType != EASYEDA::DOC_TYPE::SCHEMATIC_SHEET )
continue;
}
EASYEDA::DOCUMENT dataStrDoc = subDoc.dataStr->get<EASYEDA::DOCUMENT>();
for( wxString shap : dataStrDoc.shape )
{
if( !shap.Contains( wxS( "LIB" ) ) )
continue;
shap.Replace( wxS( "#@$" ), wxS( "\n" ) );
wxArrayString parts = wxSplit( shap, '\n', '\0' );
if( parts.size() < 1 )
continue;
wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
if( paramsRoot.size() < 1 )
continue;
wxString rootType = paramsRoot[0];
if( rootType == wxS( "LIB" ) )
{
if( paramsRoot.size() < 4 )
continue;
wxString symbolName = wxString::Format( wxS( "Unknown_%s_%s" ),
paramsRoot[1], paramsRoot[2] );
wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
std::map<wxString, wxString> paramMap;
for( size_t i = 1; i < paramParts.size(); i += 2 )
{
wxString key = paramParts[i - 1];
wxString value = paramParts[i];
if( key == wxS( "spiceSymbolName" ) && !value.IsEmpty() )
symbolName = value;
paramMap[key] = value;
}
int& serial = namesCounter[symbolName];
if( serial > 0 )
symbolName << wxS( "_" ) << serial;
serial++;
aSymbolNameList.Add( symbolName );
}
}
}
}
else if( topDocType == EASYEDA::DOC_TYPE::SYMBOL )
{
EASYEDA::DOCUMENT_SYM symDoc = js.get<EASYEDA::DOCUMENT_SYM>();
wxString packageName = wxS( "Unknown" );
if( symDoc.c_para )
{
packageName = get_def( *symDoc.c_para, wxS( "name" ), packageName );
}
else if( topDoc.head.c_para )
{
packageName = get_def( *topDoc.head.c_para, wxS( "name" ), packageName );
}
aSymbolNameList.Add( packageName );
}
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
aLibraryPath, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
aLibraryPath, e.what() ) );
}
}
void SCH_EASYEDA_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties )
{
wxFFileInputStream in( aLibraryPath );
nlohmann::json js;
EASYEDA::DOCUMENT topDoc;
EASYEDA::DOC_TYPE topDocType;
if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
aLibraryPath ) );
}
try
{
wxArrayString symbolNameList;
EnumerateSymbolLib( symbolNameList, aLibraryPath, aProperties );
for( const wxString& symbolName : symbolNameList )
{
LIB_SYMBOL* sym = loadSymbol( aLibraryPath, js, symbolName, aProperties );
if( sym )
aSymbolList.push_back( sym );
}
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
aLibraryPath, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
aLibraryPath, e.what() ) );
}
}
LIB_SYMBOL* SCH_EASYEDA_PLUGIN::LoadSymbol( const wxString& aLibraryPath,
const wxString& aAliasName,
const STRING_UTF8_MAP* aProperties )
{
try
{
wxFFileInputStream in( aLibraryPath );
nlohmann::json js;
EASYEDA::DOCUMENT topDoc;
EASYEDA::DOC_TYPE topDocType;
if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
aLibraryPath ) );
}
return loadSymbol( aLibraryPath, js, aAliasName, aProperties );
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
aAliasName, aLibraryPath, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
aAliasName, aLibraryPath, e.what() ) );
}
return nullptr;
}
static void LoadSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const wxString& aFileName )
{
SCH_EASYEDA_PARSER parser( nullptr, nullptr );
try
{
wxFFileInputStream in( aFileName );
nlohmann::json js;
EASYEDA::DOCUMENT topDoc;
EASYEDA::DOC_TYPE topDocType;
if( !FindSchFileInStream( aFileName, in, js, topDoc, topDocType ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
aFileName ) );
}
if( topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET
|| topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_LIST )
{
EASYEDA::DOCUMENT_SCHEMATICS schDoc = js.get<EASYEDA::DOCUMENT_SCHEMATICS>();
bool first = true;
for( const EASYEDA::DOCUMENT& subDoc : *schDoc.schematics )
{
if( first )
first = false; // TODO
else
break;
if( subDoc.docType )
{
if( *subDoc.docType != EASYEDA::DOC_TYPE::SCHEMATIC_SHEET )
continue;
}
else
{
if( subDoc.head.docType != EASYEDA::DOC_TYPE::SCHEMATIC_SHEET )
continue;
}
EASYEDA::DOCUMENT dataStrDoc = subDoc.dataStr->get<EASYEDA::DOCUMENT>();
parser.ParseSchematic( aSchematic, aRootSheet, aFileName, dataStrDoc.shape );
}
}
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR(
wxString::Format( _( "Error loading schematic '%s': %s" ), aFileName, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR(
wxString::Format( _( "Error loading schematic '%s': %s" ), aFileName, e.what() ) );
}
}
SCH_SHEET* SCH_EASYEDA_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
SCH_SHEET* aAppendToMe,
const STRING_UTF8_MAP* aProperties )
{
wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
SCH_SHEET* rootSheet = nullptr;
if( aAppendToMe )
{
wxCHECK_MSG( aSchematic->IsValid(), nullptr,
wxS( "Can't append to a schematic with no root!" ) );
rootSheet = &aSchematic->Root();
}
else
{
rootSheet = new SCH_SHEET( aSchematic );
rootSheet->SetFileName( aFileName );
aSchematic->SetRoot( rootSheet );
}
if( !rootSheet->GetScreen() )
{
SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
screen->SetFileName( aFileName );
rootSheet->SetScreen( screen );
}
SYMBOL_LIB_TABLE* libTable = aSchematic->Prj().SchSymbolLibTable();
wxCHECK_MSG( libTable, nullptr, wxS( "Could not load symbol lib table." ) );
LoadSchematic( aSchematic, rootSheet, aFileName );
aSchematic->CurrentSheet().UpdateAllScreenReferences();
return rootSheet;
}

View File

@ -0,0 +1,90 @@
/*
* 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.
*
* 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
*/
#ifndef SCH_EASYEDA_PLUGIN_H_
#define SCH_EASYEDA_PLUGIN_H_
#include <sch_io_mgr.h>
#include <reporter.h>
class SCH_SHEET;
class SCH_SCREEN;
class SCH_EASYEDA_PLUGIN : public SCH_PLUGIN
{
public:
SCH_EASYEDA_PLUGIN()
{
m_reporter = &WXLOG_REPORTER::GetInstance();
m_progressReporter = nullptr;
}
~SCH_EASYEDA_PLUGIN() {}
const wxString GetName() const override;
void SetReporter( REPORTER* aReporter ) override { m_reporter = aReporter; }
void SetProgressReporter( PROGRESS_REPORTER* aReporter ) override
{
m_progressReporter = aReporter;
}
const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
{
return PLUGIN_FILE_DESC( _HKI( "EasyEDA (JLCEDA) Std files" ), { "json" } );
}
const PLUGIN_FILE_DESC GetLibraryFileDesc() const override { return GetSchematicFileDesc(); }
bool CanReadSchematicFile( const wxString& aFileName ) const override;
bool CanReadLibrary( const wxString& aFileName ) const override;
int GetModifyHash() const override;
SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
SCH_SHEET* aAppendToMe = nullptr,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
void EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
void EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList, const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
LIB_SYMBOL* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
bool IsSymbolLibWritable( const wxString& aLibraryPath ) override { return false; }
private:
REPORTER* m_reporter; // current reporter for warnings/errors
PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr
};
#endif // SCH_EASYEDA_PLUGIN_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
/*
* 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.
*
* 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
*/
#ifndef SCH_EASYEDAPRO_PARSER_H_
#define SCH_EASYEDAPRO_PARSER_H_
#include <plugins/easyedapro/easyedapro_parser.h>
#include <sch_io_mgr.h>
#include <pin_type.h>
#include <layer_ids.h>
#include <wx/filename.h>
#include <plotters/plotter.h>
class EDA_TEXT;
class LIB_SHAPE;
class LIB_FIELD;
class LIB_PIN;
class SCH_LABEL_BASE;
class SCH_SYMBOL;
class SCH_TEXT;
class SCH_SHAPE;
namespace EASYEDAPRO
{
struct PIN_INFO
{
EASYEDAPRO::SYM_PIN pin;
wxString number;
wxString name;
};
struct SYM_INFO
{
EASYEDAPRO::SYM_HEAD head;
std::vector<PIN_INFO> pins;
std::unique_ptr<LIB_SYMBOL> libSymbol;
std::optional<EASYEDAPRO::SCH_ATTR> symbolAttr;
std::map<wxString, int> partUnits;
};
} // namespace EASYEDAPRO
class SCH_EASYEDAPRO_PARSER
{
public:
explicit SCH_EASYEDAPRO_PARSER( SCHEMATIC* aSchematic, PROGRESS_REPORTER* aProgressReporter );
~SCH_EASYEDAPRO_PARSER();
/*void Parse( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping );*/
static double Convert( wxString aValue );
template <typename T>
static T ScaleSize( T aValue )
{
return KiROUND( schIUScale.MilsToIU( aValue * 10 ) );
}
template <typename T>
static VECTOR2<T> ScaleSize( VECTOR2<T> aValue )
{
return VECTOR2<T>( ScaleSize( aValue.x ), ScaleSize( aValue.y ) );
}
template <typename T>
static VECTOR2<T> ScalePos( VECTOR2<T> aValue )
{
return VECTOR2<T>( ScaleSize( aValue.x ), -ScaleSize( aValue.y ) );
}
template <typename T>
static VECTOR2<T> ScalePosSym( VECTOR2<T> aValue )
{
return VECTOR2<T>( ScaleSize( aValue.x ), ScaleSize( aValue.y ) );
}
double SizeToKi( wxString units );
EASYEDAPRO::SYM_INFO ParseSymbol( const std::vector<nlohmann::json>& aLines );
void ParseSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
const nlohmann::json& aProject,
std::map<wxString, EASYEDAPRO::SYM_INFO>& aSymbolMap,
const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
const std::vector<nlohmann::json>& aLines, const wxString& aLibName );
protected:
SCHEMATIC* m_schematic;
template <typename T>
void ApplyFontStyle( const std::map<wxString, nlohmann::json>& fontStyles, T& text,
const wxString& styleStr );
template <typename T>
void ApplyLineStyle( const std::map<wxString, nlohmann::json>& lineStyles, T& shape,
const wxString& styleStr );
template <typename T>
void ApplyAttrToField( const std::map<wxString, nlohmann::json>& fontStyles, T* text,
const EASYEDAPRO::SCH_ATTR& aAttr, bool aIsSym, bool aToSym,
SCH_SYMBOL* aParent = nullptr );
};
#endif // SCH_EASYEDAPRO_PARSER_H_

View File

@ -0,0 +1,566 @@
/*
* 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.
*
* 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 "sch_easyedapro_plugin.h"
#include "sch_easyedapro_parser.h"
#include <schematic.h>
#include <sch_sheet.h>
#include <sch_screen.h>
#include <kiplatform/environment.h>
#include <fstream>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/mstream.h>
#include <wx/zipstrm.h>
#include <wx/fs_zip.h>
#include <wx/log.h>
#include <nlohmann/json.hpp>
#include <string_utils.h>
#include <wildcards_and_files_ext.h>
#include <sch_plugins/kicad/sch_sexpr_plugin.h>
#include <plugins/easyedapro/easyedapro_import_utils.h>
#include <core/map_helpers.h>
struct SCH_EASYEDAPRO_PLUGIN::PRJ_DATA
{
std::map<wxString, EASYEDAPRO::SYM_INFO> m_Symbols;
std::map<wxString, EASYEDAPRO::BLOB> m_Blobs;
};
SCH_EASYEDAPRO_PLUGIN::SCH_EASYEDAPRO_PLUGIN()
{
m_reporter = &WXLOG_REPORTER::GetInstance();
m_progressReporter = nullptr;
}
SCH_EASYEDAPRO_PLUGIN::~SCH_EASYEDAPRO_PLUGIN()
{
if( m_projectData )
delete m_projectData;
}
const wxString SCH_EASYEDAPRO_PLUGIN::GetName() const
{
return wxT( "EasyEDA (JLCEDA) Schematic Importer" );
}
bool SCH_EASYEDAPRO_PLUGIN::CanReadSchematicFile( const wxString& aFileName ) const
{
if( aFileName.Lower().EndsWith( wxS( ".epro" ) ) )
{
return true;
}
else if( aFileName.Lower().EndsWith( wxS( ".zip" ) ) )
{
std::shared_ptr<wxZipEntry> entry;
wxFFileInputStream in( aFileName );
wxZipInputStream zip( in );
if( !zip.IsOk() )
return false;
while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
{
wxString name = entry->GetName();
if( name == wxS( "project.json" ) )
return true;
}
return false;
}
return false;
}
int SCH_EASYEDAPRO_PLUGIN::GetModifyHash() const
{
return 0;
}
static LIB_SYMBOL* loadSymbol( nlohmann::json project, const wxString& aLibraryPath,
const wxString& aAliasName, const STRING_UTF8_MAP* aProperties )
{
SCH_EASYEDAPRO_PARSER parser( nullptr, nullptr );
LIB_SYMBOL* symbol = nullptr;
wxFileName libFname( aLibraryPath );
wxString symLibName = EASYEDAPRO::ShortenLibName( libFname.GetName() );
/*if( libFname.GetExt() == wxS( "esym" ) )
{
wxFFileInputStream ffis( aLibraryPath );
wxTextInputStream txt( ffis, wxS( " " ), wxConvUTF8 );
bool loadThis = false;
std::vector<nlohmann::json> lines;
while( ffis.CanRead() )
{
nlohmann::json js = nlohmann::json::parse( txt.ReadLine() );
lines.emplace_back( js );
if( js.at( 0 ) == "ATTR" && js.at( 3 ) == "Symbol" )
{
if( js.at( 4 ).get<wxString>() == aAliasName )
{
loadThis = true;
}
}
}
if( loadThis )
{
EASYEDAPRO::SYM_INFO symInfo = parser.ParseSymbol( lines );
return symInfo.libSymbol.release();
}
}
else */
if( libFname.GetExt() == wxS( "epro" ) || libFname.GetExt() == wxS( "zip" ) )
{
std::map<wxString, EASYEDAPRO::PRJ_SYMBOL> prjSymbols = project.at( "symbols" );
std::map<wxString, EASYEDAPRO::PRJ_FOOTPRINT> prjFootprints = project.at( "footprints" );
std::map<wxString, EASYEDAPRO::PRJ_DEVICE> prjDevices = project.at( "devices" );
auto prjSymIt = std::find_if( prjSymbols.begin(), prjSymbols.end(),
[&]( const auto& pair )
{
return pair.second.title == aAliasName;
} );
if( prjSymIt == prjSymbols.end() )
return nullptr;
wxString prjSymUuid = prjSymIt->first;
wxString fpTitle;
for( auto& [key, device] : prjDevices )
{
auto val = get_opt( device.attributes, "Symbol" );
if( val && *val == prjSymUuid )
{
if( auto fpUuid = get_opt( device.attributes, "Footprint" ) )
{
if( auto prjFp = get_opt( prjFootprints, *fpUuid ) )
{
fpTitle = prjFp->title;
break;
}
}
}
}
auto cb = [&]( const wxString& name, const wxString& symUuid, wxInputStream& zip ) -> bool
{
if( !name.EndsWith( wxS( ".esym" ) ) )
EASY_IT_CONTINUE;
if( symUuid != prjSymUuid )
EASY_IT_CONTINUE;
wxTextInputStream txt( zip, wxS( " " ), wxConvUTF8 );
std::vector<nlohmann::json> lines;
while( zip.CanRead() )
{
nlohmann::json js = nlohmann::json::parse( txt.ReadLine() );
lines.emplace_back( js );
}
EASYEDAPRO::SYM_INFO symInfo = parser.ParseSymbol( lines );
if( !symInfo.libSymbol )
EASY_IT_CONTINUE;
LIB_ID libID = EASYEDAPRO::ToKiCadLibID( symLibName, aAliasName );
symInfo.libSymbol->SetLibId( libID );
symInfo.libSymbol->SetName( aAliasName );
symInfo.libSymbol->GetFootprintField().SetText( symLibName + wxS( "/" ) + fpTitle );
symbol = symInfo.libSymbol.release();
EASY_IT_BREAK;
};
EASYEDAPRO::IterateZipFiles( aLibraryPath, cb );
}
return symbol;
}
void SCH_EASYEDAPRO_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties )
{
wxFileName fname( aLibraryPath );
if( fname.GetExt() == wxS( "esym" ) )
{
wxFFileInputStream ffis( aLibraryPath );
wxTextInputStream txt( ffis, wxS( " " ), wxConvUTF8 );
while( ffis.CanRead() )
{
wxString line = txt.ReadLine();
if( !line.Contains( wxS( "ATTR" ) ) )
continue; // Don't bother parsing
nlohmann::json js = nlohmann::json::parse( line );
if( js.at( 0 ) == "ATTR" && js.at( 3 ) == "Symbol" )
{
aSymbolNameList.Add( js.at( 4 ).get<wxString>() );
}
}
}
else if( fname.GetExt() == wxS( "epro" ) || fname.GetExt() == wxS( "zip" ) )
{
nlohmann::json project = EASYEDAPRO::ReadProjectFile( aLibraryPath );
std::map<wxString, nlohmann::json> symbolMap = project.at( "symbols" );
for( auto& [key, value] : symbolMap )
aSymbolNameList.Add( value.at( "title" ) );
}
}
void SCH_EASYEDAPRO_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties )
{
wxFileName libFname( aLibraryPath );
wxArrayString symbolNameList;
nlohmann::json project;
EnumerateSymbolLib( symbolNameList, aLibraryPath, aProperties );
if( libFname.GetExt() == wxS( "epro" ) || libFname.GetExt() == wxS( "zip" ) )
project = EASYEDAPRO::ReadProjectFile( aLibraryPath );
for( const wxString& symbolName : symbolNameList )
{
LIB_SYMBOL* sym = loadSymbol( project, aLibraryPath, symbolName, aProperties );
if( sym )
aSymbolList.push_back( sym );
}
}
void SCH_EASYEDAPRO_PLUGIN::LoadAllDataFromProject( const wxString& aProjectPath )
{
if( m_projectData )
delete m_projectData;
m_projectData = new PRJ_DATA;
SCH_EASYEDAPRO_PARSER parser( nullptr, nullptr );
wxFileName fname( aProjectPath );
wxString symLibName = EASYEDAPRO::ShortenLibName( fname.GetName() );
if( fname.GetExt() != wxS( "epro" ) && fname.GetExt() != wxS( "zip" ) )
return;
nlohmann::json project = EASYEDAPRO::ReadProjectFile( aProjectPath );
std::map<wxString, EASYEDAPRO::PRJ_SYMBOL> prjSymbols = project.at( "symbols" );
std::map<wxString, EASYEDAPRO::PRJ_FOOTPRINT> prjFootprints = project.at( "footprints" );
std::map<wxString, EASYEDAPRO::PRJ_DEVICE> prjDevices = project.at( "devices" );
auto cb = [&]( const wxString& name, const wxString& baseName, wxInputStream& zip ) -> bool
{
if( !name.EndsWith( wxS( ".esym" ) ) && !name.EndsWith( wxS( ".eblob" ) ) )
EASY_IT_CONTINUE;
std::vector<nlohmann::json> lines = EASYEDAPRO::ParseJsonLines( zip, name );
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;
for( auto& [key, device] : prjDevices )
{
auto val = get_opt( device.attributes, "Symbol" );
if( val && *val == baseName )
{
if( auto fpUuid = get_opt( device.attributes, "Footprint" ) )
{
if( auto prjFp = get_opt( prjFootprints, *fpUuid ) )
{
fpTitle = prjFp->title;
break;
}
}
}
}
LIB_ID libID = EASYEDAPRO::ToKiCadLibID( symLibName, symData.title );
symInfo.libSymbol->SetLibId( libID );
symInfo.libSymbol->SetName( symData.title );
symInfo.libSymbol->GetFootprintField().SetText( symLibName + wxS( "/" ) + fpTitle );
m_projectData->m_Symbols.emplace( baseName, std::move( symInfo ) );
}
else if( name.EndsWith( wxS( ".eblob" ) ) )
{
for( const nlohmann::json& line : lines )
{
if( line.at( 0 ) == "BLOB" )
{
EASYEDAPRO::BLOB blob = line;
m_projectData->m_Blobs[blob.objectId] = blob;
}
}
}
EASY_IT_CONTINUE;
};
EASYEDAPRO::IterateZipFiles( aProjectPath, cb );
}
LIB_SYMBOL* SCH_EASYEDAPRO_PLUGIN::LoadSymbol( const wxString& aLibraryPath,
const wxString& aAliasName,
const STRING_UTF8_MAP* aProperties )
{
wxFileName libFname( aLibraryPath );
nlohmann::json project;
if( libFname.GetExt() == wxS( "epro" ) || libFname.GetExt() == wxS( "zip" ) )
project = EASYEDAPRO::ReadProjectFile( aLibraryPath );
return loadSymbol( project, aLibraryPath, aAliasName, aProperties );
}
SCH_SHEET* SCH_EASYEDAPRO_PLUGIN::LoadSchematicFile( const wxString& aFileName,
SCHEMATIC* aSchematic, SCH_SHEET* aAppendToMe,
const STRING_UTF8_MAP* aProperties )
{
wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
SCH_SHEET* rootSheet = nullptr;
if( aAppendToMe )
{
wxCHECK_MSG( aSchematic->IsValid(), nullptr,
wxS( "Can't append to a schematic with no root!" ) );
rootSheet = &aSchematic->Root();
}
else
{
rootSheet = new SCH_SHEET( aSchematic );
rootSheet->SetFileName( aFileName );
aSchematic->SetRoot( rootSheet );
}
if( !rootSheet->GetScreen() )
{
SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
screen->SetFileName( aFileName );
rootSheet->SetScreen( screen );
}
SYMBOL_LIB_TABLE* libTable = aSchematic->Prj().SchSymbolLibTable();
wxCHECK_MSG( libTable, nullptr, wxS( "Could not load symbol lib table." ) );
SCH_EASYEDAPRO_PARSER parser( nullptr, nullptr );
wxFileName fname( aFileName );
wxString libName = EASYEDAPRO::ShortenLibName( fname.GetName() );
wxFileName libFileName( fname.GetPath(), libName, KiCadSymbolLibFileExtension );
if( fname.GetExt() != wxS( "epro" ) && fname.GetExt() != wxS( "zip" ) )
return rootSheet;
nlohmann::json project = EASYEDAPRO::ReadProjectFile( aFileName );
std::map<wxString, EASYEDAPRO::PRJ_SCHEMATIC> prjSchematics = project.at( "schematics" );
std::map<wxString, EASYEDAPRO::PRJ_BOARD> prjBoards = project.at( "boards" );
std::map<wxString, wxString> prjPcbNames = project.at( "pcbs" );
wxString schematicToLoad;
if( prjBoards.size() > 0 )
{
EASYEDAPRO::PRJ_BOARD boardToLoad = prjBoards.begin()->second;
schematicToLoad = boardToLoad.schematic;
}
else if( prjSchematics.size() > 0 )
{
schematicToLoad = prjSchematics.begin()->first;
}
if( schematicToLoad.empty() )
return nullptr;
wxString rootBaseName = EscapeString( prjSchematics[schematicToLoad].name, CTX_FILENAME );
wxFileName rootFname( aFileName );
rootFname.SetFullName( rootBaseName + wxS( "." )
+ wxString::FromUTF8( KiCadSchematicFileExtension ) );
rootSheet->SetName( prjSchematics[schematicToLoad].name );
rootSheet->SetFileName( rootFname.GetFullPath() );
rootSheet->GetScreen()->SetFileName( rootFname.GetFullPath() );
const std::vector<EASYEDAPRO::PRJ_SHEET>& prjSchematicSheets =
prjSchematics[schematicToLoad].sheets;
LoadAllDataFromProject( aFileName );
if( !m_projectData )
return nullptr;
auto cbs = [&]( const wxString& name, const wxString& symUuid, wxInputStream& zip ) -> bool
{
if( !name.EndsWith( wxS( ".esch" ) ) )
EASY_IT_CONTINUE;
wxArrayString nameParts = wxSplit( name, '\\', '\0' );
if( nameParts.size() == 1 )
nameParts = wxSplit( name, '/', '\0' );
if( nameParts.size() < 3 )
EASY_IT_CONTINUE;
wxString schematicUuid = nameParts[1];
wxString sheetFileName = nameParts[2];
wxString sheetId = sheetFileName.BeforeLast( '.' );
int sheetId_i;
sheetId.ToInt( &sheetId_i );
if( schematicUuid != schematicToLoad )
EASY_IT_CONTINUE;
auto prjSheetIt = std::find_if( prjSchematicSheets.begin(), prjSchematicSheets.end(),
[&]( const EASYEDAPRO::PRJ_SHEET& s )
{
return s.id == sheetId_i;
} );
if( prjSheetIt == prjSchematicSheets.end() )
EASY_IT_CONTINUE;
wxString sheetBaseName =
sheetId + wxS( "_" ) + EscapeString( prjSheetIt->name, CTX_FILENAME );
wxFileName sheetFname( aFileName );
sheetFname.SetFullName( sheetBaseName + wxS( "." )
+ wxString::FromUTF8( KiCadSchematicFileExtension ) );
std::unique_ptr<SCH_SHEET> subSheet = std::make_unique<SCH_SHEET>( aSchematic );
subSheet->SetFileName( sheetFname.GetFullPath() );
subSheet->SetName( prjSheetIt->name );
SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
screen->SetFileName( sheetFname.GetFullPath() );
screen->SetPageNumber( sheetId );
subSheet->SetScreen( screen );
VECTOR2I pos;
pos.x = schIUScale.MilsToIU( 200 );
pos.y = schIUScale.MilsToIU( 200 )
+ ( subSheet->GetSize().y + schIUScale.MilsToIU( 200 ) ) * ( sheetId_i - 1 );
subSheet->SetPosition( pos );
std::vector<nlohmann::json> lines = EASYEDAPRO::ParseJsonLines( zip, name );
SCH_SHEET_PATH sheetPath;
sheetPath.push_back( rootSheet );
sheetPath.push_back( subSheet.get() );
sheetPath.SetPageNumber( sheetId );
aSchematic->SetCurrentSheet( sheetPath );
parser.ParseSchematic( aSchematic, subSheet.get(), project, m_projectData->m_Symbols,
m_projectData->m_Blobs, lines, libName );
rootSheet->GetScreen()->Append( subSheet.release() );
EASY_IT_CONTINUE;
};
EASYEDAPRO::IterateZipFiles( aFileName, cbs );
SCH_PLUGIN::SCH_PLUGIN_RELEASER sch_plugin;
sch_plugin.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
if( !libTable->HasLibrary( libName ) )
{
// Create a new empty symbol library.
sch_plugin->CreateSymbolLib( libFileName.GetFullPath() );
wxString libTableUri = wxS( "${KIPRJMOD}/" ) + libFileName.GetFullName();
// Add the new library to the project symbol library table.
libTable->InsertRow( new SYMBOL_LIB_TABLE_ROW( libName, libTableUri, wxS( "KiCad" ) ) );
// Save project symbol library table.
wxFileName fn( aSchematic->Prj().GetProjectPath(),
SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
// So output formatter goes out of scope and closes the file before reloading.
{
FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
libTable->Format( &formatter, 0 );
}
// Relaod the symbol library table.
aSchematic->Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL );
aSchematic->Prj().SchSymbolLibTable();
}
// set properties to prevent save file on every symbol save
STRING_UTF8_MAP properties;
properties.emplace( SCH_SEXPR_PLUGIN::PropBuffering, wxEmptyString );
for( auto& [symbolUuid, symInfo] : m_projectData->m_Symbols )
sch_plugin->SaveSymbol( libFileName.GetFullPath(), symInfo.libSymbol.release(),
&properties );
sch_plugin->SaveLibrary( libFileName.GetFullPath() );
aSchematic->CurrentSheet().UpdateAllScreenReferences();
aSchematic->FixupJunctions();
return rootSheet;
}

View File

@ -0,0 +1,88 @@
/*
* 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.
*
* 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
*/
#ifndef SCH_EASYEDAPRO_PLUGIN_H_
#define SCH_EASYEDAPRO_PLUGIN_H_
#include <sch_io_mgr.h>
#include <reporter.h>
class SCH_SHEET;
class SCH_SCREEN;
class SCH_EASYEDAPRO_PLUGIN : public SCH_PLUGIN
{
public:
SCH_EASYEDAPRO_PLUGIN();
~SCH_EASYEDAPRO_PLUGIN();
const wxString GetName() const override;
void SetReporter( REPORTER* aReporter ) override { m_reporter = aReporter; }
void SetProgressReporter( PROGRESS_REPORTER* aReporter ) override
{
m_progressReporter = aReporter;
}
const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
{
return PLUGIN_FILE_DESC( _HKI( "EasyEDA (JLCEDA) Pro files" ), { "epro", "zip" } );
}
const PLUGIN_FILE_DESC GetLibraryFileDesc() const override { return GetSchematicFileDesc(); }
bool CanReadSchematicFile( const wxString& aFileName ) const override;
int GetModifyHash() const override;
SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
SCH_SHEET* aAppendToMe = nullptr,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
void EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
void EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList, const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
void LoadAllDataFromProject( const wxString& aLibraryPath );
LIB_SYMBOL* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
bool IsSymbolLibWritable( const wxString& aLibraryPath ) override { return false; }
private:
struct PRJ_DATA; // Opaque data structure
PRJ_DATA* m_projectData = nullptr;
REPORTER* m_reporter; // current reporter for warnings/errors
PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr
};
#endif // SCH_EASYEDAPRO_PLUGIN_H_

View File

@ -123,6 +123,11 @@ std::string EscapedUTF8( const wxString& aString );
*/ */
wxString EscapeHTML( const wxString& aString ); wxString EscapeHTML( const wxString& aString );
/**
* Return a new wxString unescaped from HTML format.
*/
wxString UnescapeHTML( const wxString& aString );
/** /**
* Read one line line from \a aFile. * Read one line line from \a aFile.
* *

View File

@ -218,6 +218,8 @@ extern wxString CsvFileWildcard();
extern wxString PcbFileWildcard(); extern wxString PcbFileWildcard();
extern wxString CadstarArchiveFilesWildcard(); extern wxString CadstarArchiveFilesWildcard();
extern wxString EagleFilesWildcard(); extern wxString EagleFilesWildcard();
extern wxString EasyEdaArchiveWildcard();
extern wxString EasyEdaProFileWildcard();
extern wxString PdfFileWildcard(); extern wxString PdfFileWildcard();
extern wxString PSFileWildcard(); extern wxString PSFileWildcard();
extern wxString MacrosFileWildcard(); extern wxString MacrosFileWildcard();

View File

@ -135,3 +135,17 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
ImportNonKiCadProject( _( "Import Eagle Project Files" ), EagleFilesWildcard(), { "sch" }, ImportNonKiCadProject( _( "Import Eagle Project Files" ), EagleFilesWildcard(), { "sch" },
{ "brd" }, SCH_IO_MGR::SCH_EAGLE, IO_MGR::EAGLE ); { "brd" }, SCH_IO_MGR::SCH_EAGLE, IO_MGR::EAGLE );
} }
void KICAD_MANAGER_FRAME::OnImportEasyEdaFiles( wxCommandEvent& event )
{
ImportNonKiCadProject( _( "Import EasyEDA Std Backup" ), EasyEdaArchiveWildcard(), { "INPUT" },
{ "INPUT" }, SCH_IO_MGR::SCH_EASYEDA, IO_MGR::EASYEDA );
}
void KICAD_MANAGER_FRAME::OnImportEasyEdaProFiles( wxCommandEvent& event )
{
ImportNonKiCadProject( _( "Import EasyEDA Pro Project" ), EasyEdaProFileWildcard(), { "INPUT" },
{ "INPUT" }, SCH_IO_MGR::SCH_EASYEDAPRO, IO_MGR::EASYEDAPRO );
}

View File

@ -68,8 +68,10 @@ enum id_kicad_frm {
ID_SAVE_AND_ZIP_FILES, ID_SAVE_AND_ZIP_FILES,
ID_READ_ZIP_ARCHIVE, ID_READ_ZIP_ARCHIVE,
ID_INIT_WATCHED_PATHS, ID_INIT_WATCHED_PATHS,
ID_IMPORT_EAGLE_PROJECT,
ID_IMPORT_CADSTAR_ARCHIVE_PROJECT, ID_IMPORT_CADSTAR_ARCHIVE_PROJECT,
ID_IMPORT_EAGLE_PROJECT,
ID_IMPORT_EASYEDA_PROJECT,
ID_IMPORT_EASYEDAPRO_PROJECT,
// Please, verify: the number of items in this list should be // Please, verify: the number of items in this list should be
// less than ROOM_FOR_KICADMANAGER (see id.h) // less than ROOM_FOR_KICADMANAGER (see id.h)

View File

@ -97,6 +97,8 @@ BEGIN_EVENT_TABLE( KICAD_MANAGER_FRAME, EDA_BASE_FRAME )
EVT_MENU( ID_READ_ZIP_ARCHIVE, KICAD_MANAGER_FRAME::OnUnarchiveFiles ) EVT_MENU( ID_READ_ZIP_ARCHIVE, KICAD_MANAGER_FRAME::OnUnarchiveFiles )
EVT_MENU( ID_IMPORT_CADSTAR_ARCHIVE_PROJECT, KICAD_MANAGER_FRAME::OnImportCadstarArchiveFiles ) EVT_MENU( ID_IMPORT_CADSTAR_ARCHIVE_PROJECT, KICAD_MANAGER_FRAME::OnImportCadstarArchiveFiles )
EVT_MENU( ID_IMPORT_EAGLE_PROJECT, KICAD_MANAGER_FRAME::OnImportEagleFiles ) EVT_MENU( ID_IMPORT_EAGLE_PROJECT, KICAD_MANAGER_FRAME::OnImportEagleFiles )
EVT_MENU( ID_IMPORT_EASYEDA_PROJECT, KICAD_MANAGER_FRAME::OnImportEasyEdaFiles )
EVT_MENU( ID_IMPORT_EASYEDAPRO_PROJECT, KICAD_MANAGER_FRAME::OnImportEasyEdaProFiles )
// Range menu events // Range menu events
EVT_MENU_RANGE( ID_LANGUAGE_CHOICE, ID_LANGUAGE_CHOICE_END, EVT_MENU_RANGE( ID_LANGUAGE_CHOICE, ID_LANGUAGE_CHOICE_END,

View File

@ -97,6 +97,16 @@ public:
*/ */
void OnImportEagleFiles( wxCommandEvent& event ); void OnImportEagleFiles( wxCommandEvent& event );
/**
* Open dialog to import EasyEDA Std schematic and board files.
*/
void OnImportEasyEdaFiles( wxCommandEvent& event );
/**
* Open dialog to import EasyEDA Pro schematic and board files.
*/
void OnImportEasyEdaProFiles( wxCommandEvent& event );
/** /**
* Prints the current working directory name and the project name on the text panel. * Prints the current working directory name and the project name on the text panel.
*/ */

View File

@ -3,7 +3,7 @@
* *
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2009 Wayne Stambaugh <stambaughw@verizon.net> * Copyright (C) 2009 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019 CERN * Copyright (C) 2019 CERN
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -113,6 +113,15 @@ void KICAD_MANAGER_FRAME::doReCreateMenuBar()
ID_IMPORT_EAGLE_PROJECT, ID_IMPORT_EAGLE_PROJECT,
BITMAPS::import_project ); BITMAPS::import_project );
importMenu->Add( _( "EasyEDA (JLCEDA) Std Backup..." ),
_( "Import EasyEDA (JLCEDA) Standard schematic and board" ),
ID_IMPORT_EASYEDA_PROJECT,
BITMAPS::import_project );
importMenu->Add( _( "EasyEDA (JLCEDA) Pro Project..." ),
_( "Import EasyEDA (JLCEDA) Professional schematic and board" ),
ID_IMPORT_EASYEDAPRO_PROJECT, BITMAPS::import_project );
fileMenu->Add( importMenu ); fileMenu->Add( importMenu );
fileMenu->AppendSeparator(); fileMenu->AppendSeparator();

View File

@ -1002,6 +1002,12 @@ public:
void BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, void BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
POLYGON_MODE aFastMode ); POLYGON_MODE aFastMode );
/**
* Extract all contours from this polygon set, then recreate polygons with holes.
* Essentially XOR'ing, but faster. Self-intersecting polygons are not supported.
*/
void RebuildHolesFromContours();
/// define how inflate transform build inflated polygon /// define how inflate transform build inflated polygon
enum CORNER_STRATEGY enum CORNER_STRATEGY
{ {

View File

@ -624,6 +624,111 @@ void SHAPE_POLY_SET::ClearArcs()
} }
void SHAPE_POLY_SET::RebuildHolesFromContours()
{
std::vector<SHAPE_LINE_CHAIN> contours;
for( const POLYGON& poly : m_polys )
contours.insert( contours.end(), poly.begin(), poly.end() );
std::map<int, std::set<int>> parentToChildren;
std::map<int, std::set<int>> childToParents;
for( SHAPE_LINE_CHAIN& contour : contours )
contour.GenerateBBoxCache();
for( int i = 0; i < contours.size(); i++ )
{
const SHAPE_LINE_CHAIN& outline = contours[i];
for( int j = 0; j < contours.size(); j++ )
{
if( i == j )
continue;
const SHAPE_LINE_CHAIN& candidate = contours[j];
const VECTOR2I& pt0 = candidate.CPoint( 0 );
if( outline.PointInside( pt0, 0, true ) )
{
parentToChildren[i].emplace( j );
childToParents[j].emplace( i );
}
}
}
std::set<int> topLevelParents;
for( int i = 0; i < contours.size(); i++ )
{
if( childToParents[i].size() == 0 )
{
topLevelParents.emplace( i );
}
}
SHAPE_POLY_SET result;
std::function<void( int, int, std::vector<int> )> process;
process = [&]( int myId, int parentOutlineId, std::vector<int> path )
{
std::set<int> relParents = childToParents[myId];
for( int pathId : path )
{
int erased = relParents.erase( pathId );
wxASSERT( erased > 0 );
}
wxASSERT( relParents.size() == 0 );
int myOutline = -1;
bool isOutline = path.size() % 2 == 0;
if( isOutline )
{
int outlineId = result.AddOutline( contours[myId] );
myOutline = outlineId;
}
else
{
wxASSERT( parentOutlineId != -1 );
int holeId = result.AddHole( contours[myId], parentOutlineId );
}
auto it = parentToChildren.find( myId );
if( it != parentToChildren.end() )
{
std::vector<int> thisPath = path;
thisPath.emplace_back( myId );
std::set<int> thisPathSet;
thisPathSet.insert( thisPath.begin(), thisPath.end() );
for( int childId : it->second )
{
const std::set<int>& childPathSet = childToParents[childId];
if( thisPathSet != childPathSet )
continue; // Only interested in immediate children
process( childId, myOutline, thisPath );
}
}
};
for( int topParentId : topLevelParents )
{
std::vector<int> path;
process( topParentId, -1, path );
}
*this = result;
}
void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape, void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape,
POLYGON_MODE aFastMode ) POLYGON_MODE aFastMode )
{ {

View File

@ -573,9 +573,11 @@ endif()
add_subdirectory( plugins/pcad ) add_subdirectory( plugins/pcad )
add_subdirectory( plugins/altium ) add_subdirectory( plugins/altium )
add_subdirectory( plugins/cadstar ) add_subdirectory( plugins/cadstar )
add_subdirectory( plugins/easyeda )
add_subdirectory( plugins/easyedapro )
add_subdirectory( plugins/fabmaster ) add_subdirectory( plugins/fabmaster )
set( PCBNEW_IO_LIBRARIES pcad2kicadpcb altium2pcbnew cadstar2pcbnew fabmaster CACHE INTERNAL "") set( PCBNEW_IO_LIBRARIES pcad2kicadpcb altium2pcbnew cadstar2pcbnew easyeda easyedapro fabmaster CACHE INTERNAL "")
# a very small program launcher for pcbnew_kiface # a very small program launcher for pcbnew_kiface
add_executable( pcbnew WIN32 MACOSX_BUNDLE add_executable( pcbnew WIN32 MACOSX_BUNDLE

View File

@ -1176,6 +1176,8 @@ bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
{ {
case IO_MGR::CADSTAR_PCB_ARCHIVE: case IO_MGR::CADSTAR_PCB_ARCHIVE:
case IO_MGR::EAGLE: case IO_MGR::EAGLE:
case IO_MGR::EASYEDA:
case IO_MGR::EASYEDAPRO:
return OpenProjectFiles( std::vector<wxString>( 1, aFileName ), return OpenProjectFiles( std::vector<wxString>( 1, aFileName ),
KICTL_NONKICAD_ONLY | KICTL_IMPORT_LIB ); KICTL_NONKICAD_ONLY | KICTL_IMPORT_LIB );

View File

@ -41,6 +41,8 @@
#include <plugins/altium/solidworks_pcb_plugin.h> #include <plugins/altium/solidworks_pcb_plugin.h>
#include <plugins/cadstar/cadstar_pcb_archive_plugin.h> #include <plugins/cadstar/cadstar_pcb_archive_plugin.h>
#include <plugins/fabmaster/fabmaster_plugin.h> #include <plugins/fabmaster/fabmaster_plugin.h>
#include <plugins/easyeda/pcb_easyeda_plugin.h>
#include <plugins/easyedapro/pcb_easyedapro_plugin.h>
#define FMT_UNIMPLEMENTED _( "Plugin \"%s\" does not implement the \"%s\" function." ) #define FMT_UNIMPLEMENTED _( "Plugin \"%s\" does not implement the \"%s\" function." )
#define FMT_NOTFOUND _( "Plugin type \"%s\" is not found." ) #define FMT_NOTFOUND _( "Plugin type \"%s\" is not found." )
@ -231,6 +233,16 @@ static IO_MGR::REGISTER_PLUGIN registerEaglePlugin(
wxT( "Eagle" ), wxT( "Eagle" ),
[]() -> PLUGIN* { return new EAGLE_PLUGIN; } ); []() -> PLUGIN* { return new EAGLE_PLUGIN; } );
static IO_MGR::REGISTER_PLUGIN registerEasyEDAPlugin(
IO_MGR::EASYEDA,
wxT( "EasyEDA / JLCEDA Std" ),
[]() -> PLUGIN* { return new EASYEDA_PLUGIN; });
static IO_MGR::REGISTER_PLUGIN registerEasyEDAProPlugin(
IO_MGR::EASYEDAPRO,
wxT( "EasyEDA / JLCEDA Pro" ),
[]() -> PLUGIN* { return new EASYEDAPRO_PLUGIN; });
static IO_MGR::REGISTER_PLUGIN registerFabmasterPlugin( static IO_MGR::REGISTER_PLUGIN registerFabmasterPlugin(
IO_MGR::FABMASTER, IO_MGR::FABMASTER,
wxT( "Fabmaster" ), wxT( "Fabmaster" ),

View File

@ -59,6 +59,8 @@ public:
ALTIUM_DESIGNER, ALTIUM_DESIGNER,
CADSTAR_PCB_ARCHIVE, CADSTAR_PCB_ARCHIVE,
EAGLE, EAGLE,
EASYEDA,
EASYEDAPRO,
FABMASTER, FABMASTER,
GEDA_PCB, ///< Geda PCB file formats. GEDA_PCB, ///< Geda PCB file formats.
PCAD, PCAD,

View File

@ -0,0 +1,14 @@
# Sources for the pcbnew PLUGIN called EASYEDA_PLUGIN
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set( EASYEDA_SRCS
pcb_easyeda_plugin.cpp
pcb_easyeda_parser.cpp
)
add_library( easyeda STATIC ${EASYEDA_SRCS} )
target_link_libraries( easyeda pcbcommon )

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
/*
* 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.
*
* 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
*/
#ifndef PCB_EASYEDA_PARSER_H_
#define PCB_EASYEDA_PARSER_H_
#include <plugins/easyeda/easyeda_parser_base.h>
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <map>
#include <optional>
#include <wx/string.h>
#include <wx/arrstr.h>
#include <nlohmann/json.hpp>
#include <board_item_container.h>
class BOARD;
class PROGRESS_REPORTER;
class PCB_EASYEDA_PARSER : public EASYEDA_PARSER_BASE
{
public:
explicit PCB_EASYEDA_PARSER( PROGRESS_REPORTER* aProgressReporter );
~PCB_EASYEDA_PARSER();
PCB_LAYER_ID LayerToKi( const wxString& aLayer );
double ScaleSize( double aValue ) override
{ //
return KiROUND( ( aValue * 254000 ) / 100 ) * 100;
}
void ParseBoard( BOARD* aBoard, const VECTOR2D& aOrigin,
std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
wxArrayString aShapes );
FOOTPRINT* ParseFootprint( const VECTOR2D& aOrigin, const EDA_ANGLE& aOrientation, int aLayer,
BOARD* aParent, std::map<wxString, wxString> aParams,
std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
wxArrayString aShapes );
void ParseToBoardItemContainer( BOARD_ITEM_CONTAINER* aContainer, BOARD* aParent,
std::map<wxString, wxString> paramMap,
std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
wxArrayString shapes );
};
#endif // PCB_EASYEDA_PARSER_H_

View File

@ -0,0 +1,537 @@
/*
* 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.
*
* 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_easyeda_plugin.h"
#include "pcb_easyeda_parser.h"
#include <plugins/easyeda/easyeda_parser_structs.h>
#include <progress_reporter.h>
#include <common.h>
#include <macros.h>
#include <board.h>
#include <footprint.h>
#include <board_design_settings.h>
#include <wx/log.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <wx/stdstream.h>
#include <nlohmann/json.hpp>
#include <core/map_helpers.h>
EASYEDA_PLUGIN::EASYEDA_PLUGIN()
{
m_board = nullptr;
m_props = nullptr;
}
EASYEDA_PLUGIN::~EASYEDA_PLUGIN()
{
}
static bool FindBoardInStream( const wxString& aName, wxInputStream& aStream, nlohmann::json& aOut,
EASYEDA::DOCUMENT& aDoc )
{
if( aName.Lower().EndsWith( wxS( ".json" ) ) )
{
wxStdInputStream sin( aStream );
nlohmann::json js = nlohmann::json::parse( sin, nullptr, false );
if( js.is_discarded() )
return false;
EASYEDA::DOCUMENT doc = js.get<EASYEDA::DOCUMENT>();
if( doc.head.docType == EASYEDA::DOC_TYPE::PCB
|| doc.head.docType == EASYEDA::DOC_TYPE::PCB_MODULE
|| doc.head.docType == EASYEDA::DOC_TYPE::PCB_COMPONENT )
{
aOut = js;
aDoc = doc;
return true;
}
}
else if( aName.Lower().EndsWith( wxS( ".zip" ) ) )
{
std::shared_ptr<wxZipEntry> entry;
wxZipInputStream zip( aStream );
if( !zip.IsOk() )
return false;
while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
{
wxString name = entry->GetName();
if( FindBoardInStream( name, zip, aOut, aDoc ) )
return true;
}
}
return false;
}
bool EASYEDA_PLUGIN::CanReadBoard( const wxString& aFileName ) const
{
if( !PLUGIN::CanReadBoard( aFileName ) )
return false;
try
{
wxFFileInputStream in( aFileName );
nlohmann::json js;
EASYEDA::DOCUMENT doc;
return FindBoardInStream( aFileName, in, js, doc );
}
catch( nlohmann::json::exception& )
{
}
catch( std::exception& )
{
}
return false;
}
bool EASYEDA_PLUGIN::CanReadFootprint( const wxString& aFileName ) const
{
return CanReadBoard( aFileName );
}
bool EASYEDA_PLUGIN::CanReadFootprintLib( const wxString& aFileName ) const
{
return CanReadBoard( aFileName );
}
BOARD* EASYEDA_PLUGIN::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
const STRING_UTF8_MAP* aProperties, PROJECT* aProject,
PROGRESS_REPORTER* aProgressReporter )
{
m_loadedFootprints.clear();
m_props = aProperties;
m_board = aAppendToMe ? aAppendToMe : new BOARD();
// Give the filename to the board if it's new
if( !aAppendToMe )
m_board->SetFileName( aFileName );
if( aProgressReporter )
{
aProgressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
if( !aProgressReporter->KeepRefreshing() )
THROW_IO_ERROR( _( "Open cancelled by user." ) );
}
PCB_EASYEDA_PARSER parser( nullptr );
try
{
wxFFileInputStream in( aFileName );
nlohmann::json js;
EASYEDA::DOCUMENT doc;
if( !FindBoardInStream( aFileName, in, js, doc ) )
{
THROW_IO_ERROR(
wxString::Format( _( "Unable to find a valid board in '%s'" ), aFileName ) );
}
EASYEDA::DOCUMENT_PCB pcbDoc = js.get<EASYEDA::DOCUMENT_PCB>();
const int innerStart = 21;
const int innerEnd = 52;
int maxLayer = innerStart;
std::map<PCB_LAYER_ID, wxString> layerNames;
for( const wxString& layerLine : pcbDoc.layers )
{
wxArrayString parts = wxSplit( layerLine, '~', '\0' );
int layerId = wxAtoi( parts[0] );
wxString layerName = parts[1];
wxString layerColor = parts[2];
wxString visible = parts[3];
wxString active = parts[4];
bool enabled = parts[5] != wxS( "false" );
if( layerId >= innerStart && layerId <= innerEnd && enabled )
maxLayer = layerId + 1;
layerNames[parser.LayerToKi( parts[0] )] = layerName;
}
m_board->SetCopperLayerCount( 2 + maxLayer - innerStart );
for( auto& [klayer, name] : layerNames )
m_board->SetLayerName( klayer, name );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
std::shared_ptr<NETCLASS> defNetclass = bds.m_NetSettings->m_DefaultNetClass;
if( pcbDoc.DRCRULE )
{
std::map<wxString, nlohmann::json>& rules = *pcbDoc.DRCRULE;
if( auto defRules = get_opt( rules, "Default" ) )
{
wxString key;
key = wxS( "trackWidth" );
if( defRules->find( key ) != defRules->end() && defRules->at( key ).is_number() )
{
double val = parser.ScaleSize( defRules->at( key ) );
defNetclass->SetTrackWidth( val );
}
key = wxS( "clearance" );
if( defRules->find( key ) != defRules->end() && defRules->at( key ).is_number() )
{
double val = parser.ScaleSize( defRules->at( key ) );
defNetclass->SetClearance( val );
}
key = wxS( "viaHoleD" );
if( defRules->find( key ) != defRules->end() && defRules->at( key ).is_number() )
{
double val = parser.ScaleSize( defRules->at( key ) );
defNetclass->SetViaDrill( val );
}
key = wxS( "viaHoleDiameter" ); // Yes, this is via diameter, not drill diameter
if( defRules->find( key ) != defRules->end() && defRules->at( key ).is_number() )
{
double val = parser.ScaleSize( defRules->at( key ) );
defNetclass->SetViaDiameter( val );
}
}
}
VECTOR2D origin( doc.head.x, doc.head.y );
parser.ParseBoard( m_board, origin, m_loadedFootprints, doc.shape );
// Center the board
BOX2I outlineBbox = m_board->ComputeBoundingBox( true );
PAGE_INFO pageInfo = m_board->GetPageSettings();
VECTOR2D pageCenter( pcbIUScale.MilsToIU( pageInfo.GetWidthMils() / 2 ),
pcbIUScale.MilsToIU( pageInfo.GetHeightMils() / 2 ) );
VECTOR2D offset = pageCenter - outlineBbox.GetCenter();
int alignGrid = pcbIUScale.mmToIU( 10 );
offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
m_board->Move( offset );
bds.SetAuxOrigin( offset );
return m_board;
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR(
wxString::Format( _( "Error loading board '%s': %s" ), aFileName, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR(
wxString::Format( _( "Error loading board '%s': %s" ), aFileName, e.what() ) );
}
return m_board;
}
long long EASYEDA_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
{
return 0;
}
void EASYEDA_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
const wxString& aLibraryPath, bool aBestEfforts,
const STRING_UTF8_MAP* aProperties )
{
try
{
wxFFileInputStream in( aLibraryPath );
nlohmann::json js;
EASYEDA::DOCUMENT doc;
if( !FindBoardInStream( aLibraryPath, in, js, doc ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find valid footprints in '%s'" ),
aLibraryPath ) );
}
if( doc.head.docType == EASYEDA::DOC_TYPE::PCB
|| doc.head.docType == EASYEDA::DOC_TYPE::PCB_MODULE )
{
for( wxString shap : doc.shape )
{
shap.Replace( wxS( "#@$" ), "\n" );
wxArrayString parts = wxSplit( shap, '\n', '\0' );
if( parts.size() < 1 )
continue;
wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
if( paramsRoot.size() < 1 )
continue;
wxString rootType = paramsRoot[0];
if( rootType == wxS( "LIB" ) )
{
if( paramsRoot.size() < 4 )
continue;
wxString packageName = wxString::Format( wxS( "Unknown_%s_%s" ), paramsRoot[1],
paramsRoot[2] );
wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
std::map<wxString, wxString> 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;
}
aFootprintNames.Add( packageName );
}
}
}
else if( doc.head.docType == EASYEDA::DOC_TYPE::PCB_COMPONENT )
{
EASYEDA::DOCUMENT_PCB pcbDoc = js.get<EASYEDA::DOCUMENT_PCB>();
wxString packageName = wxString::Format( wxS( "Unknown_%s" ),
pcbDoc.uuid.value_or( wxS( "Unknown" ) ) );
if( pcbDoc.c_para )
{
packageName = get_def( *pcbDoc.c_para, wxS( "package" ), packageName );
}
aFootprintNames.Add( packageName );
}
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error enumerating footprints in library '%s': %s" ),
aLibraryPath, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error enumerating footprints in library '%s': %s" ),
aLibraryPath, e.what() ) );
}
}
FOOTPRINT* EASYEDA_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
const wxString& aFootprintName, bool aKeepUUID,
const STRING_UTF8_MAP* aProperties )
{
PCB_EASYEDA_PARSER parser( nullptr );
m_loadedFootprints.clear();
try
{
wxFFileInputStream in( aLibraryPath );
nlohmann::json js;
EASYEDA::DOCUMENT doc;
if( !FindBoardInStream( aLibraryPath, in, js, doc ) )
{
THROW_IO_ERROR( wxString::Format( _( "Unable to find valid footprints in '%s'" ),
aLibraryPath ) );
}
if( doc.head.docType == EASYEDA::DOC_TYPE::PCB
|| doc.head.docType == EASYEDA::DOC_TYPE::PCB_MODULE )
{
for( wxString shap : doc.shape )
{
if( !shap.Contains( wxS( "LIB" ) ) )
continue;
shap.Replace( wxS( "#@$" ), "\n" );
wxArrayString parts = wxSplit( shap, '\n', '\0' );
if( parts.size() < 1 )
continue;
wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
if( paramsRoot.size() < 1 )
continue;
wxString rootType = paramsRoot[0];
if( rootType == wxS( "LIB" ) )
{
if( paramsRoot.size() < 4 )
continue;
VECTOR2D origin( parser.Convert( paramsRoot[1] ),
parser.Convert( paramsRoot[2] ) );
wxString packageName = wxString::Format( wxS( "Unknown_%s_%s" ), paramsRoot[1],
paramsRoot[2] );
wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
std::map<wxString, wxString> 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;
}
EDA_ANGLE orientation;
if( !paramsRoot[4].IsEmpty() )
orientation = EDA_ANGLE( parser.Convert( paramsRoot[4] ), DEGREES_T );
int layer = 1;
if( !paramsRoot[7].IsEmpty() )
layer = parser.Convert( paramsRoot[7] );
if( packageName == aFootprintName )
{
parts.RemoveAt( 0 );
FOOTPRINT* footprint =
parser.ParseFootprint( origin, orientation, layer, nullptr,
paramMap, m_loadedFootprints, parts );
if( !footprint )
return nullptr;
footprint->Reference().SetPosition( VECTOR2I() );
footprint->Reference().SetTextAngle( ANGLE_0 );
footprint->Reference().SetVisible( true );
footprint->Value().SetPosition( VECTOR2I() );
footprint->Value().SetTextAngle( ANGLE_0 );
footprint->Value().SetVisible( true );
footprint->AutoPositionFields();
return footprint;
}
}
}
}
else if( doc.head.docType == EASYEDA::DOC_TYPE::PCB_COMPONENT )
{
EASYEDA::DOCUMENT_PCB pcbDoc = js.get<EASYEDA::DOCUMENT_PCB>();
wxString packageName = wxString::Format( wxS( "Unknown_%s" ),
pcbDoc.uuid.value_or( wxS( "Unknown" ) ) );
if( pcbDoc.c_para )
{
packageName = get_def( *pcbDoc.c_para, wxS( "package" ), packageName );
if( packageName != aFootprintName )
return nullptr;
VECTOR2D origin( doc.head.x, doc.head.y );
FOOTPRINT* footprint =
parser.ParseFootprint( origin, ANGLE_0, F_Cu, nullptr, *pcbDoc.c_para,
m_loadedFootprints, doc.shape );
if( !footprint )
return nullptr;
footprint->Reference().SetPosition( VECTOR2I() );
footprint->Reference().SetTextAngle( ANGLE_0 );
footprint->Reference().SetVisible( true );
footprint->Value().SetPosition( VECTOR2I() );
footprint->Value().SetTextAngle( ANGLE_0 );
footprint->Value().SetVisible( true );
footprint->AutoPositionFields();
return footprint;
}
}
}
catch( nlohmann::json::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error reading footprint '%s' from library '%s': %s" ),
aFootprintName, aLibraryPath, e.what() ) );
}
catch( std::exception& e )
{
THROW_IO_ERROR( wxString::Format( _( "Error reading footprint '%s' from library '%s': %s" ),
aFootprintName, aLibraryPath, e.what() ) );
}
return nullptr;
}
std::vector<FOOTPRINT*> EASYEDA_PLUGIN::GetImportedCachedLibraryFootprints()
{
std::vector<FOOTPRINT*> result;
for( auto& [fpUuid, footprint] : m_loadedFootprints )
{
result.push_back( static_cast<FOOTPRINT*>( footprint->Clone() ) );
}
return result;
}

View File

@ -0,0 +1,84 @@
/*
* 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.
*
* 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
*/
#ifndef PCB_EASYEDA_PLUGIN_H_
#define PCB_EASYEDA_PLUGIN_H_
#include <io_mgr.h>
#include <footprint.h>
class EASYEDA_PLUGIN : public PLUGIN
{
public:
const wxString PluginName() const override
{
return wxS( "EasyEDA (JLCEDA) Standard" );
}
PLUGIN_FILE_DESC GetBoardFileDesc() const override
{
return PLUGIN_FILE_DESC( _HKI( "EasyEDA (JLCEDA) Std files" ), { "json", "zip" } );
}
PLUGIN_FILE_DESC GetFootprintFileDesc() const override { return GetBoardFileDesc(); }
PLUGIN_FILE_DESC GetFootprintLibDesc() const override { return GetBoardFileDesc(); }
bool CanReadBoard( const wxString& aFileName ) const override;
bool CanReadFootprint( const wxString& aFileName ) const override;
bool CanReadFootprintLib( const wxString& aFileName ) const override;
BOARD* LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
const STRING_UTF8_MAP* aProperties = nullptr, PROJECT* aProject = nullptr,
PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override;
void FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
bool aBestEfforts,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
std::vector<FOOTPRINT*> GetImportedCachedLibraryFootprints();
FOOTPRINT* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
bool aKeepUUID = false,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
bool IsFootprintLibWritable( const wxString& aLibraryPath ) override { return false; }
EASYEDA_PLUGIN();
~EASYEDA_PLUGIN();
private:
const STRING_UTF8_MAP* m_props;
BOARD* m_board;
std::map<wxString, std::unique_ptr<FOOTPRINT>> m_loadedFootprints;
};
#endif // PCB_EASYEDA_PLUGIN_H_

View File

@ -0,0 +1,14 @@
# Sources for the pcbnew PLUGIN called EASYEDAPRO_PLUGIN
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set( EASYEDAPRO_SRCS
pcb_easyedapro_plugin.cpp
pcb_easyedapro_parser.cpp
)
add_library( easyedapro STATIC ${EASYEDAPRO_SRCS} )
target_link_libraries( easyedapro pcbcommon )

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
/*
* 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.
*
* 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
*/
#ifndef PCB_EASYEDAPRO_PARSER_H_
#define PCB_EASYEDAPRO_PARSER_H_
#include <plugins/easyedapro/easyedapro_parser.h>
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <map>
#include <optional>
#include <wx/string.h>
#include <wx/arrstr.h>
#include <nlohmann/json.hpp>
#include <board_item_container.h>
#include <pcb_shape.h>
class BOARD;
class PROGRESS_REPORTER;
class PCB_EASYEDAPRO_PARSER
{
public:
explicit PCB_EASYEDAPRO_PARSER( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter );
~PCB_EASYEDAPRO_PARSER();
PCB_LAYER_ID LayerToKi( int aLayer );
template <typename T>
static T ScaleSize( T aValue )
{
return KiROUND( aValue * 25400 / 500 ) * 500;
}
template <typename T>
static VECTOR2<T> ScaleSize( VECTOR2<T> aValue )
{
return VECTOR2<T>( ScaleSize( aValue.x ), ScaleSize( aValue.y ) );
}
template <typename T>
static VECTOR2<T> ScalePos( VECTOR2<T> aValue )
{
return VECTOR2<T>( ScaleSize( aValue.x ), -ScaleSize( aValue.y ) );
}
static double Convert( wxString aValue );
FOOTPRINT* ParseFootprint( const nlohmann::json& aProject, const wxString& aFpUuid,
const std::vector<nlohmann::json>& aLines );
void ParseBoard( BOARD* aBoard, const nlohmann::json& aProject,
std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
const std::multimap<wxString, EASYEDAPRO::POURED>& aPouredMap,
const std::vector<nlohmann::json>& aLines, const wxString& aFpLibName );
std::vector<std::unique_ptr<PCB_SHAPE>> ParsePoly( BOARD_ITEM_CONTAINER* aContainer,
nlohmann::json polyData, bool aClosed,
bool aInFill ) const;
SHAPE_LINE_CHAIN ParseContour( nlohmann::json polyData, bool aInFill,
double aArcAccuracy = SHAPE_ARC::DefaultAccuracyForPCB() ) const;
private:
BOARD* m_board;
VECTOR2D m_relOrigin;
std::unique_ptr<PAD> createPAD( FOOTPRINT* aFootprint, const nlohmann::json& line );
void fillFootprintModelInfo( FOOTPRINT* footprint, const wxString& modelUuid,
const wxString& modelTitle, const wxString& modelTransform ) const;
};
#endif // PCB_EASYEDAPRO_PARSER_H_

View File

@ -0,0 +1,409 @@
/*
* 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.
*
* 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_easyedapro_plugin.h"
#include "pcb_easyedapro_parser.h"
#include <plugins/easyedapro/easyedapro_import_utils.h>
#include <plugins/easyedapro/easyedapro_parser.h>
#include <board.h>
#include <footprint.h>
#include <progress_reporter.h>
#include <common.h>
#include <macros.h>
#include <fstream>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/mstream.h>
#include <wx/zipstrm.h>
#include <wx/log.h>
#include <nlohmann/json.hpp>
#include <core/json_serializers.h>
#include <core/map_helpers.h>
struct EASYEDAPRO_PLUGIN::PRJ_DATA
{
std::map<wxString, std::unique_ptr<FOOTPRINT>> m_Footprints;
std::map<wxString, EASYEDAPRO::BLOB> m_Blobs;
std::map<wxString, std::multimap<wxString, EASYEDAPRO::POURED>> m_Poured;
};
EASYEDAPRO_PLUGIN::EASYEDAPRO_PLUGIN()
{
m_board = nullptr;
m_props = nullptr;
}
EASYEDAPRO_PLUGIN::~EASYEDAPRO_PLUGIN()
{
if( m_projectData )
delete m_projectData;
}
bool EASYEDAPRO_PLUGIN::CanReadBoard( const wxString& aFileName ) const
{
if( aFileName.Lower().EndsWith( wxS( ".epro" ) ) )
{
return true;
}
else if( aFileName.Lower().EndsWith( wxS( ".zip" ) ) )
{
std::shared_ptr<wxZipEntry> entry;
wxFFileInputStream in( aFileName );
wxZipInputStream zip( in );
if( !zip.IsOk() )
return false;
while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
{
wxString name = entry->GetName();
if( name == wxS( "project.json" ) )
return true;
}
return false;
}
return false;
}
BOARD* EASYEDAPRO_PLUGIN::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
const STRING_UTF8_MAP* aProperties, PROJECT* aProject,
PROGRESS_REPORTER* aProgressReporter )
{
m_props = aProperties;
m_board = aAppendToMe ? aAppendToMe : new BOARD();
// Give the filename to the board if it's new
if( !aAppendToMe )
m_board->SetFileName( aFileName );
if( aProgressReporter )
{
aProgressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
if( !aProgressReporter->KeepRefreshing() )
THROW_IO_ERROR( _( "Open cancelled by user." ) );
}
PCB_EASYEDAPRO_PARSER parser( nullptr, nullptr );
wxFileName fname( aFileName );
if( fname.GetExt() == wxS( "epro" ) || fname.GetExt() == wxS( "zip" ) )
{
nlohmann::json project = EASYEDAPRO::ReadProjectFile( aFileName );
std::map<wxString, EASYEDAPRO::PRJ_SCHEMATIC> prjSchematics = project.at( "schematics" );
std::map<wxString, EASYEDAPRO::PRJ_BOARD> prjBoards = project.at( "boards" );
std::map<wxString, wxString> prjPcbNames = project.at( "pcbs" );
wxString pcbToLoad;
if( prjBoards.size() > 0 )
{
EASYEDAPRO::PRJ_BOARD boardToLoad = prjBoards.begin()->second;
pcbToLoad = boardToLoad.pcb;
}
else if( prjPcbNames.size() > 0 )
{
pcbToLoad = prjPcbNames.begin()->first;
}
if( prjPcbNames.empty() )
return nullptr;
LoadAllDataFromProject( aFileName, project );
if( !m_projectData )
return nullptr;
auto cb = [&]( const wxString& name, const wxString& pcbUuid, wxInputStream& zip ) -> bool
{
if( !name.EndsWith( wxS( ".epcb" ) ) )
EASY_IT_CONTINUE;
if( pcbUuid != pcbToLoad )
EASY_IT_CONTINUE;
std::vector<nlohmann::json> lines = EASYEDAPRO::ParseJsonLines( zip, name );
wxString boardKey = pcbUuid + wxS( "_0" );
wxScopedCharBuffer cb = boardKey.ToUTF8();
wxString boardPouredKey = wxBase64Encode( cb.data(), cb.length() );
const std::multimap<wxString, EASYEDAPRO::POURED>& boardPoured =
get_def( m_projectData->m_Poured, boardPouredKey );
parser.ParseBoard( m_board, project, m_projectData->m_Footprints,
m_projectData->m_Blobs, boardPoured, lines,
EASYEDAPRO::ShortenLibName( fname.GetName() ) );
EASY_IT_BREAK;
};
EASYEDAPRO::IterateZipFiles( aFileName, cb );
}
return m_board;
}
long long EASYEDAPRO_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
{
return 0;
}
void EASYEDAPRO_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
const wxString& aLibraryPath, bool aBestEfforts,
const STRING_UTF8_MAP* aProperties )
{
wxFileName fname( aLibraryPath );
if( fname.GetExt() == wxS( "efoo" ) )
{
wxFFileInputStream ffis( aLibraryPath );
wxTextInputStream txt( ffis, wxS( " " ), wxConvUTF8 );
while( ffis.CanRead() )
{
wxString line = txt.ReadLine();
if( !line.Contains( wxS( "ATTR" ) ) )
continue; // Don't bother parsing
nlohmann::json js = nlohmann::json::parse( line );
if( js.at( 0 ) == "ATTR" && js.at( 7 ) == "Footprint" )
{
aFootprintNames.Add( js.at( 8 ).get<wxString>() );
}
}
}
else if( fname.GetExt() == wxS( "epro" ) || fname.GetExt() == wxS( "zip" ) )
{
nlohmann::json project = EASYEDAPRO::ReadProjectFile( aLibraryPath );
std::map<wxString, nlohmann::json> footprintMap = project.at( "footprints" );
for( auto& [key, value] : footprintMap )
aFootprintNames.Add( value.at( "title" ) );
}
}
void EASYEDAPRO_PLUGIN::LoadAllDataFromProject( const wxString& aProjectPath,
const nlohmann::json& aProject )
{
if( m_projectData )
delete m_projectData;
m_projectData = new PRJ_DATA();
PCB_EASYEDAPRO_PARSER parser( nullptr, nullptr );
wxFileName fname( aProjectPath );
wxString fpLibName = EASYEDAPRO::ShortenLibName( fname.GetName() );
std::map<wxString, std::unique_ptr<FOOTPRINT>> result;
auto cb = [&]( const wxString& name, const wxString& baseName, wxInputStream& zip ) -> bool
{
if( !name.EndsWith( wxS( ".efoo" ) ) && !name.EndsWith( wxS( ".eblob" ) )
&& !name.EndsWith( wxS( ".ecop" ) ) )
{
EASY_IT_CONTINUE;
}
std::vector<nlohmann::json> lines = EASYEDAPRO::ParseJsonLines( zip, name );
if( name.EndsWith( wxS( ".efoo" ) ) )
{
nlohmann::json fpData = aProject.at( "footprints" ).at( baseName );
wxString fpTitle = fpData.at( "title" );
FOOTPRINT* footprint = parser.ParseFootprint( aProject, baseName, lines );
if( !footprint )
EASY_IT_CONTINUE;
LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( fpLibName, fpTitle );
footprint->SetFPID( fpID );
m_projectData->m_Footprints.emplace( baseName, footprint );
}
else if( name.EndsWith( wxS( ".eblob" ) ) )
{
for( const nlohmann::json& line : lines )
{
if( line.at( 0 ) == "BLOB" )
{
EASYEDAPRO::BLOB blob = line;
m_projectData->m_Blobs[blob.objectId] = blob;
}
}
}
else if( name.EndsWith( wxS( ".ecop" ) ) && EASYEDAPRO::IMPORT_POURED )
{
for( const nlohmann::json& line : lines )
{
if( line.at( 0 ) == "POURED" )
{
if( !line.at( 2 ).is_string() )
continue; // Unknown type of POURED
EASYEDAPRO::POURED poured = line;
m_projectData->m_Poured[baseName].emplace( poured.parentId, poured );
}
}
}
EASY_IT_CONTINUE;
};
EASYEDAPRO::IterateZipFiles( aProjectPath, cb );
}
FOOTPRINT* EASYEDAPRO_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
const wxString& aFootprintName, bool aKeepUUID,
const STRING_UTF8_MAP* aProperties )
{
PCB_EASYEDAPRO_PARSER parser( nullptr, nullptr );
FOOTPRINT* footprint = nullptr;
wxFileName libFname( aLibraryPath );
if( libFname.GetExt() == wxS( "efoo" ) )
{
wxFFileInputStream ffis( aLibraryPath );
wxTextInputStream txt( ffis, wxS( " " ), wxConvUTF8 );
std::vector<nlohmann::json> lines = EASYEDAPRO::ParseJsonLines( ffis, aLibraryPath );
for( const nlohmann::json& js : lines )
{
if( js.at( 0 ) == "ATTR" )
{
EASYEDAPRO::PCB_ATTR attr = js;
if( attr.key == wxS( "Footprint" ) && attr.value != aFootprintName )
return nullptr;
}
}
footprint = parser.ParseFootprint( nlohmann::json(), wxEmptyString, lines );
if( !footprint )
{
THROW_IO_ERROR( wxString::Format( _( "Cannot load footprint '%s' from '%s'" ),
aFootprintName, aLibraryPath ) );
}
LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( wxEmptyString, aFootprintName );
footprint->SetFPID( fpID );
footprint->Reference().SetVisible( true );
footprint->Value().SetText( aFootprintName );
footprint->Value().SetVisible( true );
footprint->AutoPositionFields();
}
else if( libFname.GetExt() == wxS( "epro" ) || libFname.GetExt() == wxS( "zip" ) )
{
nlohmann::json project = EASYEDAPRO::ReadProjectFile( aLibraryPath );
wxString fpUuid;
std::map<wxString, nlohmann::json> footprintMap = project.at( "footprints" );
for( auto& [uuid, data] : footprintMap )
{
wxString title = data.at( "title" );
if( title == aFootprintName )
{
fpUuid = uuid;
break;
}
}
if( !fpUuid )
{
THROW_IO_ERROR( wxString::Format( _( "Footprint '%s' not found in project '%s'" ),
aFootprintName, aLibraryPath ) );
}
auto cb = [&]( const wxString& name, const wxString& baseName, wxInputStream& zip ) -> bool
{
if( !name.EndsWith( wxS( ".efoo" ) ) )
EASY_IT_CONTINUE;
if( baseName != fpUuid )
EASY_IT_CONTINUE;
std::vector<nlohmann::json> lines = EASYEDAPRO::ParseJsonLines( zip, name );
footprint = parser.ParseFootprint( project, fpUuid, lines );
if( !footprint )
{
THROW_IO_ERROR( wxString::Format( _( "Cannot load footprint '%s' from '%s'" ),
aFootprintName, aLibraryPath ) );
}
LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( wxEmptyString, aFootprintName );
footprint->SetFPID( fpID );
footprint->Reference().SetVisible( true );
footprint->Value().SetText( aFootprintName );
footprint->Value().SetVisible( true );
footprint->AutoPositionFields();
EASY_IT_BREAK;
};
EASYEDAPRO::IterateZipFiles( aLibraryPath, cb );
}
return footprint;
}
std::vector<FOOTPRINT*> EASYEDAPRO_PLUGIN::GetImportedCachedLibraryFootprints()
{
std::vector<FOOTPRINT*> result;
if( !m_projectData )
return result;
for( auto& [fpUuid, footprint] : m_projectData->m_Footprints )
{
result.push_back( static_cast<FOOTPRINT*>( footprint->Clone() ) );
}
return result;
}

View File

@ -0,0 +1,89 @@
/*
* 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.
*
* 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
*/
#ifndef PCB_EASYEDAPRO_PLUGIN_H_
#define PCB_EASYEDAPRO_PLUGIN_H_
#include <io_mgr.h>
#include <nlohmann/json_fwd.hpp>
class EASYEDAPRO_PLUGIN : public PLUGIN
{
public:
const wxString PluginName() const override
{
return wxS( "EasyEDA (JLCEDA) Professional" );
}
PLUGIN_FILE_DESC GetBoardFileDesc() const override
{
return PLUGIN_FILE_DESC( _HKI( "EasyEDA (JLCEDA) Pro project" ), { "epro", "zip" } );
}
PLUGIN_FILE_DESC GetFootprintLibDesc() const override
{
return PLUGIN_FILE_DESC( _HKI( "EasyEDA (JLCEDA) Pro project" ), { "epro", "zip" } );
}
PLUGIN_FILE_DESC GetFootprintFileDesc() const override
{
return PLUGIN_FILE_DESC( _HKI( "EasyEDA (JLCEDA) Pro files" ), { "efoo", "epro", "zip" } );
}
bool CanReadBoard( const wxString& aFileName ) const override;
BOARD* LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
const STRING_UTF8_MAP* aProperties = nullptr, PROJECT* aProject = nullptr,
PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override;
void FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
bool aBestEfforts,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
std::vector<FOOTPRINT*> GetImportedCachedLibraryFootprints();
FOOTPRINT* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
bool aKeepUUID = false,
const STRING_UTF8_MAP* aProperties = nullptr ) override;
bool IsFootprintLibWritable( const wxString& aLibraryPath ) override { return false; }
EASYEDAPRO_PLUGIN();
~EASYEDAPRO_PLUGIN();
private:
struct PRJ_DATA; // Opaque data structure
PRJ_DATA* m_projectData = nullptr;
void LoadAllDataFromProject( const wxString& aLibraryPath, const nlohmann::json& aProject );
const STRING_UTF8_MAP* m_props;
BOARD* m_board;
};
#endif // PCB_EASYEDAPRO_PLUGIN_H_