Save/load generative objects in PCB file.

This commit is contained in:
Alex Shvartzkop 2023-10-07 04:55:45 +03:00 committed by dsa-t
parent 7b645aa541
commit 6cfb05b4a3
5 changed files with 341 additions and 34 deletions

View File

@ -135,6 +135,7 @@ free
full
general
generator
generated
grid_origin
group
gr_arc

View File

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

View File

@ -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;
};

View File

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

View File

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