Save/load generative objects in PCB file.
This commit is contained in:
parent
7b645aa541
commit
6cfb05b4a3
|
@ -135,6 +135,7 @@ free
|
|||
full
|
||||
general
|
||||
generator
|
||||
generated
|
||||
grid_origin
|
||||
group
|
||||
gr_arc
|
||||
|
|
|
@ -40,10 +40,12 @@
|
|||
#include <pcb_shape.h>
|
||||
#include <pcb_bitmap.h>
|
||||
#include <pcb_group.h>
|
||||
#include <pcb_generator.h>
|
||||
#include <pcb_target.h>
|
||||
#include <pcb_track.h>
|
||||
#include <pcb_textbox.h>
|
||||
#include <pad.h>
|
||||
#include <generators_mgr.h>
|
||||
#include <zone.h>
|
||||
#include <footprint.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
@ -960,6 +962,10 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
|
|||
parseGROUP( m_board );
|
||||
break;
|
||||
|
||||
case T_generated:
|
||||
parseGENERATOR( m_board );
|
||||
break;
|
||||
|
||||
case T_via:
|
||||
item = parsePCB_VIA();
|
||||
m_board->Add( item, ADD_MODE::BULK_APPEND, true );
|
||||
|
@ -1109,29 +1115,58 @@ void PCB_PARSER::resolveGroups( BOARD_ITEM* aParent )
|
|||
//
|
||||
// First add all group objects so subsequent GetItem() calls for nested groups work.
|
||||
|
||||
std::vector<const GROUP_INFO*> groupTypeObjects;
|
||||
|
||||
for( const GROUP_INFO& groupInfo : m_groupInfos )
|
||||
groupTypeObjects.emplace_back( &groupInfo );
|
||||
|
||||
for( const GENERATOR_INFO& genInfo : m_generatorInfos )
|
||||
groupTypeObjects.emplace_back( &genInfo );
|
||||
|
||||
for( const GROUP_INFO* groupInfo : groupTypeObjects )
|
||||
{
|
||||
PCB_GROUP* group = new PCB_GROUP( groupInfo.parent );
|
||||
PCB_GROUP* group = nullptr;
|
||||
|
||||
group->SetName( groupInfo.name );
|
||||
const_cast<KIID&>( group->m_Uuid ) = groupInfo.uuid;
|
||||
if( const GENERATOR_INFO* genInfo = dynamic_cast<const GENERATOR_INFO*>( groupInfo ) )
|
||||
{
|
||||
GENERATORS_MGR& mgr = GENERATORS_MGR::Instance();
|
||||
|
||||
if( groupInfo.locked )
|
||||
PCB_GENERATOR* gen;
|
||||
group = gen = mgr.CreateFromType( genInfo->genType );
|
||||
|
||||
if( !gen )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
_( "Cannot create generated object of type '%s'" ), genInfo->genType ) );
|
||||
}
|
||||
|
||||
gen->SetLayer( genInfo->layer );
|
||||
gen->SetProperties( genInfo->properties );
|
||||
}
|
||||
else
|
||||
{
|
||||
group = new PCB_GROUP( groupInfo->parent );
|
||||
group->SetName( groupInfo->name );
|
||||
}
|
||||
|
||||
const_cast<KIID&>( group->m_Uuid ) = groupInfo->uuid;
|
||||
|
||||
if( groupInfo->locked )
|
||||
group->SetLocked( true );
|
||||
|
||||
if( groupInfo.parent->Type() == PCB_FOOTPRINT_T )
|
||||
static_cast<FOOTPRINT*>( groupInfo.parent )->Add( group, ADD_MODE::INSERT, true );
|
||||
if( groupInfo->parent->Type() == PCB_FOOTPRINT_T )
|
||||
static_cast<FOOTPRINT*>( groupInfo->parent )->Add( group, ADD_MODE::INSERT, true );
|
||||
else
|
||||
static_cast<BOARD*>( groupInfo.parent )->Add( group, ADD_MODE::INSERT, true );
|
||||
static_cast<BOARD*>( groupInfo->parent )->Add( group, ADD_MODE::INSERT, true );
|
||||
}
|
||||
|
||||
wxString error;
|
||||
|
||||
for( const GROUP_INFO& groupInfo : m_groupInfos )
|
||||
for( const GROUP_INFO* groupInfo : groupTypeObjects )
|
||||
{
|
||||
if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( getItem( groupInfo.uuid ) ) )
|
||||
if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( getItem( groupInfo->uuid ) ) )
|
||||
{
|
||||
for( const KIID& aUuid : groupInfo.memberUuids )
|
||||
for( const KIID& aUuid : groupInfo->memberUuids )
|
||||
{
|
||||
BOARD_ITEM* item;
|
||||
|
||||
|
@ -4790,6 +4825,21 @@ bool PCB_PARSER::parsePAD_option( PAD* aPad )
|
|||
}
|
||||
|
||||
|
||||
void PCB_PARSER::parseGROUP_members( GROUP_INFO& aGroupInfo )
|
||||
{
|
||||
T token;
|
||||
|
||||
while( ( token = NextTok() ) != T_RIGHT )
|
||||
{
|
||||
// This token is the Uuid of the item in the group.
|
||||
// Since groups are serialized at the end of the file/footprint, the Uuid should already
|
||||
// have been seen and exist in the board.
|
||||
KIID uuid( CurStr() );
|
||||
aGroupInfo.memberUuids.push_back( uuid );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_PARSER::parseGROUP( BOARD_ITEM* aParent )
|
||||
{
|
||||
wxCHECK_RET( CurTok() == T_group,
|
||||
|
@ -4827,19 +4877,150 @@ void PCB_PARSER::parseGROUP( BOARD_ITEM* aParent )
|
|||
if( token != T_members )
|
||||
Expecting( T_members );
|
||||
|
||||
while( ( token = NextTok() ) != T_RIGHT )
|
||||
{
|
||||
// This token is the Uuid of the item in the group.
|
||||
// Since groups are serialized at the end of the file/footprint, the Uuid should already
|
||||
// have been seen and exist in the board.
|
||||
KIID uuid( CurStr() );
|
||||
groupInfo.memberUuids.push_back( uuid );
|
||||
}
|
||||
parseGROUP_members( groupInfo );
|
||||
|
||||
NeedRIGHT();
|
||||
}
|
||||
|
||||
|
||||
void PCB_PARSER::parseGENERATOR( BOARD_ITEM* aParent )
|
||||
{
|
||||
wxCHECK_RET( CurTok() == T_generated, wxT( "Cannot parse " ) + GetTokenString( CurTok() )
|
||||
+ wxT( " as PCB_GENERATOR." ) );
|
||||
|
||||
T token;
|
||||
|
||||
m_generatorInfos.push_back( GENERATOR_INFO() );
|
||||
GENERATOR_INFO& genInfo = m_generatorInfos.back();
|
||||
|
||||
genInfo.layer = F_Cu;
|
||||
genInfo.parent = aParent;
|
||||
genInfo.properties = STRING_ANY_MAP( pcbIUScale.IU_PER_MM );
|
||||
|
||||
NeedLEFT();
|
||||
token = NextTok();
|
||||
|
||||
if( token != T_id )
|
||||
Expecting( T_id );
|
||||
|
||||
NextTok();
|
||||
genInfo.uuid = CurStrToKIID();
|
||||
NeedRIGHT();
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_type:
|
||||
NeedSYMBOL();
|
||||
genInfo.genType = FromUTF8();
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_name:
|
||||
NeedSYMBOL();
|
||||
genInfo.name = FromUTF8();
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_locked:
|
||||
token = NextTok();
|
||||
genInfo.locked = token == T_yes;
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_layer:
|
||||
genInfo.layer = parseBoardItemLayer();
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_members: parseGROUP_members( genInfo ); break;
|
||||
|
||||
default:
|
||||
{
|
||||
wxString pName = FromUTF8();
|
||||
T tok1 = NextTok();
|
||||
|
||||
switch( tok1 )
|
||||
{
|
||||
case T_yes:
|
||||
{
|
||||
genInfo.properties.emplace( pName, wxAny( true ) );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
case T_no:
|
||||
{
|
||||
genInfo.properties.emplace( pName, wxAny( false ) );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
case T_NUMBER:
|
||||
{
|
||||
double pValue = parseDouble();
|
||||
genInfo.properties.emplace( pName, wxAny( pValue ) );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
case T_STRING: // Quoted string
|
||||
{
|
||||
wxString pValue = FromUTF8();
|
||||
genInfo.properties.emplace( pName, pValue );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
case T_LEFT:
|
||||
{
|
||||
NeedSYMBOL();
|
||||
T tok2 = CurTok();
|
||||
|
||||
switch( tok2 )
|
||||
{
|
||||
case T_xy:
|
||||
{
|
||||
VECTOR2I pt;
|
||||
|
||||
pt.x = parseBoardUnits( "X coordinate" );
|
||||
pt.y = parseBoardUnits( "Y coordinate" );
|
||||
|
||||
genInfo.properties.emplace( pName, wxAny( pt ) );
|
||||
NeedRIGHT();
|
||||
NeedRIGHT();
|
||||
|
||||
break;
|
||||
}
|
||||
case T_pts:
|
||||
{
|
||||
SHAPE_LINE_CHAIN chain;
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
parseOutlinePoints( chain );
|
||||
|
||||
NeedRIGHT();
|
||||
|
||||
genInfo.properties.emplace( pName, wxAny( chain ) );
|
||||
break;
|
||||
}
|
||||
default: Expecting( "xy or pts" );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: Expecting( "a number, symbol, string or (" );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PCB_ARC* PCB_PARSER::parseARC()
|
||||
{
|
||||
wxCHECK_MSG( CurTok() == T_arc, nullptr,
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <pcb_lexer.h>
|
||||
#include <kiid.h>
|
||||
#include <math/box2.h>
|
||||
#include <string_any_map.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
@ -120,6 +121,29 @@ public:
|
|||
bool IsValidBoardHeader();
|
||||
|
||||
private:
|
||||
|
||||
// Group membership info refers to other Uuids in the file.
|
||||
// We don't want to rely on group declarations being last in the file, so
|
||||
// we store info about the group declarations here during parsing and then resolve
|
||||
// them into BOARD_ITEM* after we've parsed the rest of the file.
|
||||
struct GROUP_INFO
|
||||
{
|
||||
virtual ~GROUP_INFO() = default; // Make polymorphic
|
||||
|
||||
BOARD_ITEM* parent;
|
||||
wxString name;
|
||||
bool locked;
|
||||
KIID uuid;
|
||||
std::vector<KIID> memberUuids;
|
||||
};
|
||||
|
||||
struct GENERATOR_INFO : GROUP_INFO
|
||||
{
|
||||
PCB_LAYER_ID layer;
|
||||
wxString genType;
|
||||
STRING_ANY_MAP properties;
|
||||
};
|
||||
|
||||
///< Convert net code using the mapping table if available,
|
||||
///< otherwise returns unchanged net code if < 0 or if it's out of range
|
||||
inline int getNetCode( int aNetCode )
|
||||
|
@ -204,7 +228,9 @@ private:
|
|||
ZONE* parseZONE( BOARD_ITEM_CONTAINER* aParent );
|
||||
PCB_TARGET* parsePCB_TARGET();
|
||||
BOARD* parseBOARD();
|
||||
void parseGROUP_members( GROUP_INFO& aGroupInfo );
|
||||
void parseGROUP( BOARD_ITEM* aParent );
|
||||
void parseGENERATOR( BOARD_ITEM* aParent );
|
||||
|
||||
// Parse a board, but do not replace PARSE_ERROR with FUTURE_FORMAT_ERROR automatically.
|
||||
BOARD* parseBOARD_unchecked();
|
||||
|
@ -360,20 +386,8 @@ private:
|
|||
TIME_PT m_lastProgressTime; ///< for progress reporting
|
||||
unsigned m_lineCount; ///< for progress reporting
|
||||
|
||||
// Group membership info refers to other Uuids in the file.
|
||||
// We don't want to rely on group declarations being last in the file, so
|
||||
// we store info about the group declarations here during parsing and then resolve
|
||||
// them into BOARD_ITEM* after we've parsed the rest of the file.
|
||||
struct GROUP_INFO
|
||||
{
|
||||
BOARD_ITEM* parent;
|
||||
wxString name;
|
||||
bool locked;
|
||||
KIID uuid;
|
||||
std::vector<KIID> memberUuids;
|
||||
};
|
||||
|
||||
std::vector<GROUP_INFO> m_groupInfos;
|
||||
std::vector<GROUP_INFO> m_groupInfos;
|
||||
std::vector<GENERATOR_INFO> m_generatorInfos;
|
||||
|
||||
std::function<bool( wxString aTitle, int aIcon, wxString aMsg, wxString aAction )> m_queryUserCallback;
|
||||
};
|
||||
|
|
|
@ -31,10 +31,12 @@
|
|||
#include <kiface_base.h>
|
||||
#include <locale_io.h>
|
||||
#include <macros.h>
|
||||
#include <fmt/core.h>
|
||||
#include <callback_gal.h>
|
||||
#include <pad.h>
|
||||
#include <footprint.h>
|
||||
#include <pcb_group.h>
|
||||
#include <pcb_generator.h>
|
||||
#include <pcb_shape.h>
|
||||
#include <pcb_dimension.h>
|
||||
#include <pcb_bitmap.h>
|
||||
|
@ -283,7 +285,6 @@ bool PCB_PLUGIN::CanReadBoard( const wxString& aFileName ) const
|
|||
}
|
||||
catch( const IO_ERROR& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -405,6 +406,10 @@ void PCB_PLUGIN::Format( const BOARD_ITEM* aItem, int aNestLevel ) const
|
|||
format( static_cast<const PCB_GROUP*>( aItem ), aNestLevel );
|
||||
break;
|
||||
|
||||
case PCB_GENERATOR_T:
|
||||
format( static_cast<const PCB_GENERATOR*>( aItem ), aNestLevel );
|
||||
break;
|
||||
|
||||
case PCB_TRACE_T:
|
||||
case PCB_ARC_T:
|
||||
case PCB_VIA_T:
|
||||
|
@ -782,6 +787,8 @@ void PCB_PLUGIN::format( const BOARD* aBoard, int aNestLevel ) const
|
|||
aBoard->Zones().end() );
|
||||
std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
|
||||
aBoard->Groups().end() );
|
||||
std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_generators( aBoard->Generators().begin(),
|
||||
aBoard->Generators().end() );
|
||||
formatHeader( aBoard, aNestLevel );
|
||||
|
||||
// Save the footprints.
|
||||
|
@ -814,6 +821,10 @@ void PCB_PLUGIN::format( const BOARD* aBoard, int aNestLevel ) const
|
|||
// Save the groups
|
||||
for( BOARD_ITEM* group : sorted_groups )
|
||||
Format( group, aNestLevel );
|
||||
|
||||
// Save the generators
|
||||
for( BOARD_ITEM* gen : sorted_generators )
|
||||
Format( gen, aNestLevel );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1958,6 +1969,102 @@ void PCB_PLUGIN::format( const PCB_GROUP* aGroup, int aNestLevel ) const
|
|||
}
|
||||
|
||||
|
||||
void PCB_PLUGIN::format( const PCB_GENERATOR* aGenerator, int aNestLevel ) const
|
||||
{
|
||||
m_out->Print( aNestLevel, "(generated (id %s)\n", TO_UTF8( aGenerator->m_Uuid.AsString() ) );
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(type %s) (name %s)\n",
|
||||
TO_UTF8( aGenerator->GetGeneratorType() ),
|
||||
m_out->Quotew( aGenerator->GetName() ).c_str() );
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(layer %s)",
|
||||
m_out->Quotew( LSET::Name( aGenerator->GetLayer() ) ).c_str() );
|
||||
|
||||
if( aGenerator->IsLocked() )
|
||||
m_out->Print( 0, " (locked yes)" );
|
||||
|
||||
m_out->Print( 0, "\n" );
|
||||
|
||||
for( const auto& [key, value] : aGenerator->GetProperties() )
|
||||
{
|
||||
if( value.CheckType<double>() || value.CheckType<int>() || value.CheckType<long>()
|
||||
|| value.CheckType<long long>() )
|
||||
{
|
||||
double val;
|
||||
|
||||
if( !value.GetAs( &val ) )
|
||||
continue;
|
||||
|
||||
std::string buf = fmt::format( "{:.10g}", val );
|
||||
|
||||
// Don't quote numbers
|
||||
m_out->Print( aNestLevel + 1, "(%s %s)\n", key.c_str(), buf.c_str() );
|
||||
}
|
||||
else if( value.CheckType<bool>() )
|
||||
{
|
||||
bool val;
|
||||
value.GetAs( &val );
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(%s %s)\n", key.c_str(), val ? "yes" : "no" );
|
||||
}
|
||||
else if( value.CheckType<VECTOR2I>() )
|
||||
{
|
||||
VECTOR2I val;
|
||||
value.GetAs( &val );
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(%s (xy %s))\n", key.c_str(),
|
||||
formatInternalUnits( val ).c_str() );
|
||||
}
|
||||
else if( value.CheckType<SHAPE_LINE_CHAIN>() )
|
||||
{
|
||||
SHAPE_LINE_CHAIN val;
|
||||
value.GetAs( &val );
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(%s (pts\n", key.c_str() );
|
||||
|
||||
for( const VECTOR2I& pt : val.CPoints() )
|
||||
m_out->Print( aNestLevel + 2, "(xy %s)\n", formatInternalUnits( pt ).c_str() );
|
||||
|
||||
m_out->Print( aNestLevel + 1, "))\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString val;
|
||||
|
||||
if( value.CheckType<wxString>() )
|
||||
{
|
||||
value.GetAs( &val );
|
||||
}
|
||||
else if( value.CheckType<std::string>() )
|
||||
{
|
||||
std::string str;
|
||||
value.GetAs( &str );
|
||||
|
||||
val = wxString::FromUTF8( str );
|
||||
}
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(%s %s)\n", key.c_str(), m_out->Quotew( val ).c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
m_out->Print( aNestLevel + 1, "(members\n" );
|
||||
|
||||
wxArrayString memberIds;
|
||||
|
||||
for( BOARD_ITEM* member : aGenerator->GetItems() )
|
||||
memberIds.Add( member->m_Uuid.AsString() );
|
||||
|
||||
memberIds.Sort();
|
||||
|
||||
for( const wxString& memberId : memberIds )
|
||||
m_out->Print( aNestLevel + 2, "%s\n", TO_UTF8( memberId ) );
|
||||
|
||||
m_out->Print( aNestLevel + 1, ")\n" ); // Close `members` token.
|
||||
|
||||
m_out->Print( aNestLevel, ")\n" ); // Close `generator` token.
|
||||
}
|
||||
|
||||
|
||||
void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
|
||||
{
|
||||
if( aTrack->Type() == PCB_VIA_T )
|
||||
|
|
|
@ -45,6 +45,7 @@ class PCB_BITMAP;
|
|||
class PCB_TARGET;
|
||||
class PAD;
|
||||
class PCB_GROUP;
|
||||
class PCB_GENERATOR;
|
||||
class PCB_TRACK;
|
||||
class ZONE;
|
||||
class PCB_TEXT;
|
||||
|
@ -140,7 +141,8 @@ class PCB_PLUGIN; // forward decl
|
|||
//#define SEXPR_BOARD_FILE_VERSION 20230730 // Connectivity for graphic shapes
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20230825 // Textbox explicit border flag
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20230906 // Multiple image type support in files
|
||||
#define SEXPR_BOARD_FILE_VERSION 20230913 // Custom-shaped-pad spoke templates
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20230913 // Custom-shaped-pad spoke templates
|
||||
#define SEXPR_BOARD_FILE_VERSION 20231007 // Generative objects
|
||||
|
||||
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
|
||||
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting
|
||||
|
@ -420,6 +422,8 @@ private:
|
|||
void format( const PCB_TEXT* aText, int aNestLevel = 0 ) const;
|
||||
void format( const PCB_TEXTBOX* aTextBox, int aNestLevel = 0 ) const;
|
||||
|
||||
void format( const PCB_GENERATOR* aGenerator, int aNestLevel = 0 ) const;
|
||||
|
||||
void format( const PCB_TRACK* aTrack, int aNestLevel = 0 ) const;
|
||||
|
||||
void format( const ZONE* aZone, int aNestLevel = 0 ) const;
|
||||
|
|
Loading…
Reference in New Issue