ADDED: Importers for EasyEDA (JLCEDA) Standard / Professional.
This commit is contained in:
parent
024622b0f6
commit
21ee65aa9c
|
@ -266,6 +266,16 @@ set( PLUGINS_EAGLE_SRCS
|
|||
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
|
||||
font/font.cpp
|
||||
font/glyph.cpp
|
||||
|
@ -295,6 +305,8 @@ set( COMMON_SRCS
|
|||
${PLUGINS_ALTIUM_SRCS}
|
||||
${PLUGINS_CADSTAR_SRCS}
|
||||
${PLUGINS_EAGLE_SRCS}
|
||||
${PLUGINS_EASYEDA_SRCS}
|
||||
${PLUGINS_EASYEDAPRO_SRCS}
|
||||
${FONT_SRCS}
|
||||
${COMMON_IMPORT_GFX_SRCS}
|
||||
jobs/job_dispatcher.cpp
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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 );
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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 );
|
||||
}
|
|
@ -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_
|
|
@ -534,6 +534,20 @@ wxString EscapeHTML( const wxString& aString )
|
|||
}
|
||||
|
||||
|
||||
wxString UnescapeHTML( const wxString& aString )
|
||||
{
|
||||
wxString converted = aString;
|
||||
|
||||
converted.Replace( wxS( """ ), wxS( "\"" ) );
|
||||
converted.Replace( wxS( "'" ), wxS( "'" ) );
|
||||
converted.Replace( wxS( "&" ), wxS( "&" ) );
|
||||
converted.Replace( wxS( "<" ), wxS( "<" ) );
|
||||
converted.Replace( wxS( ">" ), wxS( ">" ) );
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
|
||||
bool NoPrintableChars( const wxString& aString )
|
||||
{
|
||||
wxString tmp = aString;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
return _( "KiCad printed circuit board files" )
|
||||
|
|
|
@ -60,6 +60,16 @@ set( EESCHEMA_SCH_PLUGINS_LTSPICE
|
|||
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
|
||||
dialogs/dialog_annotate.cpp
|
||||
dialogs/dialog_annotate_base.cpp
|
||||
|
@ -278,6 +288,8 @@ set( EESCHEMA_SRCS
|
|||
${EESCHEMA_SCH_PLUGINS_ALTIUM}
|
||||
${EESCHEMA_SCH_PLUGINS_CADSTAR}
|
||||
${EESCHEMA_SCH_PLUGINS_LTSPICE}
|
||||
${EESCHEMA_SCH_PLUGINS_EASYEDA}
|
||||
${EESCHEMA_SCH_PLUGINS_EASYEDAPRO}
|
||||
${EESCHEMA_SIM_SRCS}
|
||||
${EESCHEMA_WIDGETS}
|
||||
${EESCHEMA_IMPORT_GFX}
|
||||
|
@ -301,6 +313,7 @@ set( EESCHEMA_SRCS
|
|||
fields_grid_table.cpp
|
||||
files-io.cpp
|
||||
generate_alias_info.cpp
|
||||
gfx_import_utils.cpp
|
||||
picksymbol.cpp
|
||||
lib_field.cpp
|
||||
lib_item.cpp
|
||||
|
|
|
@ -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_EAGLE:
|
||||
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.
|
||||
wxCHECK_MSG( filename.IsAbsolute(), /*void*/,
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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_
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <sch_plugins/altium/sch_altium_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/ltspice/ltspice_sch_plugin.h>
|
||||
#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_DATABASE: return new SCH_DATABASE_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();
|
||||
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_DATABASE: return wxString( wxT( "Database" ) );
|
||||
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" ) );
|
||||
default: return wxString::Format( _( "Unknown SCH_FILE_T value: %d" ),
|
||||
aType );
|
||||
|
@ -122,6 +128,10 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::EnumFromStr( const wxString& aType )
|
|||
return SCH_DATABASE;
|
||||
else if( aType == wxT( "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" ) )
|
||||
return SCH_LTSPICE;
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ public:
|
|||
SCH_CADSTAR_ARCHIVE, ///< CADSTAR Schematic Archive
|
||||
SCH_DATABASE, ///< KiCad database library
|
||||
SCH_EAGLE, ///< Autodesk Eagle file format
|
||||
SCH_EASYEDA, ///< EasyEDA Std schematic file
|
||||
SCH_EASYEDAPRO, ///< EasyEDA Pro archive
|
||||
SCH_LTSPICE, ///< LtSpice Schematic format
|
||||
|
||||
// Add your schematic type here.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -123,6 +123,11 @@ std::string EscapedUTF8( 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.
|
||||
*
|
||||
|
|
|
@ -218,6 +218,8 @@ extern wxString CsvFileWildcard();
|
|||
extern wxString PcbFileWildcard();
|
||||
extern wxString CadstarArchiveFilesWildcard();
|
||||
extern wxString EagleFilesWildcard();
|
||||
extern wxString EasyEdaArchiveWildcard();
|
||||
extern wxString EasyEdaProFileWildcard();
|
||||
extern wxString PdfFileWildcard();
|
||||
extern wxString PSFileWildcard();
|
||||
extern wxString MacrosFileWildcard();
|
||||
|
|
|
@ -135,3 +135,17 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
|||
ImportNonKiCadProject( _( "Import Eagle Project Files" ), EagleFilesWildcard(), { "sch" },
|
||||
{ "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 );
|
||||
}
|
|
@ -68,8 +68,10 @@ enum id_kicad_frm {
|
|||
ID_SAVE_AND_ZIP_FILES,
|
||||
ID_READ_ZIP_ARCHIVE,
|
||||
ID_INIT_WATCHED_PATHS,
|
||||
ID_IMPORT_EAGLE_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
|
||||
// less than ROOM_FOR_KICADMANAGER (see id.h)
|
||||
|
|
|
@ -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_IMPORT_CADSTAR_ARCHIVE_PROJECT, KICAD_MANAGER_FRAME::OnImportCadstarArchiveFiles )
|
||||
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
|
||||
EVT_MENU_RANGE( ID_LANGUAGE_CHOICE, ID_LANGUAGE_CHOICE_END,
|
||||
|
|
|
@ -97,6 +97,16 @@ public:
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* 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
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -113,6 +113,15 @@ void KICAD_MANAGER_FRAME::doReCreateMenuBar()
|
|||
ID_IMPORT_EAGLE_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->AppendSeparator();
|
||||
|
|
|
@ -1002,6 +1002,12 @@ public:
|
|||
void BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
|
||||
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
|
||||
enum CORNER_STRATEGY
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
POLYGON_MODE aFastMode )
|
||||
{
|
||||
|
|
|
@ -573,9 +573,11 @@ endif()
|
|||
add_subdirectory( plugins/pcad )
|
||||
add_subdirectory( plugins/altium )
|
||||
add_subdirectory( plugins/cadstar )
|
||||
add_subdirectory( plugins/easyeda )
|
||||
add_subdirectory( plugins/easyedapro )
|
||||
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
|
||||
add_executable( pcbnew WIN32 MACOSX_BUNDLE
|
||||
|
|
|
@ -1176,6 +1176,8 @@ bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
|
|||
{
|
||||
case IO_MGR::CADSTAR_PCB_ARCHIVE:
|
||||
case IO_MGR::EAGLE:
|
||||
case IO_MGR::EASYEDA:
|
||||
case IO_MGR::EASYEDAPRO:
|
||||
return OpenProjectFiles( std::vector<wxString>( 1, aFileName ),
|
||||
KICTL_NONKICAD_ONLY | KICTL_IMPORT_LIB );
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include <plugins/altium/solidworks_pcb_plugin.h>
|
||||
#include <plugins/cadstar/cadstar_pcb_archive_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_NOTFOUND _( "Plugin type \"%s\" is not found." )
|
||||
|
@ -231,6 +233,16 @@ static IO_MGR::REGISTER_PLUGIN registerEaglePlugin(
|
|||
wxT( "Eagle" ),
|
||||
[]() -> 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(
|
||||
IO_MGR::FABMASTER,
|
||||
wxT( "Fabmaster" ),
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
ALTIUM_DESIGNER,
|
||||
CADSTAR_PCB_ARCHIVE,
|
||||
EAGLE,
|
||||
EASYEDA,
|
||||
EASYEDAPRO,
|
||||
FABMASTER,
|
||||
GEDA_PCB, ///< Geda PCB file formats.
|
||||
PCAD,
|
||||
|
|
|
@ -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
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
Loading…
Reference in New Issue