Allow a user to remap layers in Eagle import

ADDED Allow for user mapping of layers in Eagle import
ADDED Support required and optional layers in the layer mapping dialog
ADDED Add base class for plugins supporting remappable layers
This commit is contained in:
Jan Mrázek 2020-10-09 18:58:21 +02:00
parent f7333ad64a
commit fc8bf6f0fe
No known key found for this signature in database
GPG Key ID: A3E71126F180EB6F
10 changed files with 475 additions and 191 deletions

View File

@ -24,6 +24,30 @@
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
wxString DIALOG_IMPORTED_LAYERS::WrapRequired( const wxString& aLayerName )
{
return aLayerName + " *";
}
wxString DIALOG_IMPORTED_LAYERS::UnwrapRequired( const wxString& aLayerName )
{
if( !aLayerName.EndsWith( " *" ) )
return aLayerName;
return aLayerName.Left( aLayerName.Length() - 2 );
}
const INPUT_LAYER_DESC* DIALOG_IMPORTED_LAYERS::GetLayerDescription(
const wxString& aLayerName ) const
{
wxString layerName = UnwrapRequired( aLayerName );
for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
{
if( layerDescription.Name == layerName )
return &layerDescription;
}
return nullptr;
}
PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetSelectedLayerID() PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetSelectedLayerID()
{ {
@ -58,11 +82,15 @@ PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetSelectedLayerID()
PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetAutoMatchLayerID( wxString aInputLayerName ) PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetAutoMatchLayerID( wxString aInputLayerName )
{ {
wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
for( INPUT_LAYER_DESC inputLayerDesc : m_input_layers ) for( INPUT_LAYER_DESC inputLayerDesc : m_input_layers )
{ {
if( inputLayerDesc.Name == aInputLayerName ) if( inputLayerDesc.Name == pureInputLayerName
&& inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
{
return inputLayerDesc.AutoMapLayer; return inputLayerDesc.AutoMapLayer;
} }
}
return PCB_LAYER_ID::UNDEFINED_LAYER; return PCB_LAYER_ID::UNDEFINED_LAYER;
} }
@ -90,7 +118,8 @@ void DIALOG_IMPORTED_LAYERS::AddMappings()
long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName ); long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
m_matched_layers_list->SetItem( newItemIndex, 1, kiName ); m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
m_matched_layers_map.insert( { selectedLayerName, selectedKiCadLayerID } ); m_matched_layers_map.insert(
{ UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
// remove selected layer from vector and also GUI list // remove selected layer from vector and also GUI list
for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
@ -122,11 +151,12 @@ void DIALOG_IMPORTED_LAYERS::RemoveMappings( int aStatus )
!= wxNOT_FOUND ) != wxNOT_FOUND )
{ {
wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 ); wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
wxCHECK( m_matched_layers_map.find( selectedLayerName ) != m_matched_layers_map.end(), wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
/*void*/ ); /*void*/ );
m_matched_layers_map.erase( selectedLayerName ); m_matched_layers_map.erase( pureSelectedLayerName );
rowsToDelete.Add( itemIndex ); rowsToDelete.Add( itemIndex );
m_unmatched_layers_list->InsertItem( 0, selectedLayerName ); m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
@ -169,7 +199,7 @@ void DIALOG_IMPORTED_LAYERS::OnAutoMatchLayersClicked( wxCommandEvent& event )
long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName ); long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
m_matched_layers_list->SetItem( newItemIndex, 1, kiName ); m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
m_matched_layers_map.insert( { layerName, autoMatchLayer } ); m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
// remove selected layer from vector and also GUI list // remove selected layer from vector and also GUI list
for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
@ -199,11 +229,12 @@ DIALOG_IMPORTED_LAYERS::DIALOG_IMPORTED_LAYERS( wxWindow* aParent,
for( INPUT_LAYER_DESC inLayer : aLayerDesc ) for( INPUT_LAYER_DESC inLayer : aLayerDesc )
{ {
m_input_layers.push_back( inLayer ); m_input_layers.push_back( inLayer );
m_unmatched_layer_names.push_back( inLayer.Name ); wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
m_unmatched_layer_names.push_back( layerName );
kiCadLayers |= inLayer.PermittedLayers; kiCadLayers |= inLayer.PermittedLayers;
} }
int maxTextWidth = 0; int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
for( const INPUT_LAYER_DESC& layer : m_input_layers ) for( const INPUT_LAYER_DESC& layer : m_input_layers )
maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x ); maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
@ -268,8 +299,21 @@ DIALOG_IMPORTED_LAYERS::DIALOG_IMPORTED_LAYERS( wxWindow* aParent,
FinishDialogSettings(); FinishDialogSettings();
} }
std::vector<wxString> DIALOG_IMPORTED_LAYERS::GetUnmappedRequiredLayers() const
{
std::vector<wxString> unmappedLayers;
for( const wxString& layerName : m_unmatched_layer_names )
{
const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
wxASSERT_MSG( layerDesc != nullptr, "Expected to find layer decription" );
if( layerDesc->Required )
unmappedLayers.push_back( layerDesc->Name );
}
return unmappedLayers;
}
LAYER_MAP DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent,
std::map<wxString, PCB_LAYER_ID> DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent,
const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
{ {
DIALOG_IMPORTED_LAYERS dlg( aParent, aLayerDesc ); DIALOG_IMPORTED_LAYERS dlg( aParent, aLayerDesc );
@ -279,10 +323,11 @@ LAYER_MAP DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent,
{ {
dlg.ShowModal(); dlg.ShowModal();
if( dlg.m_unmatched_layer_names.size() > 0 ) if( dlg.GetUnmappedRequiredLayers().size() > 0 )
{ {
wxMessageBox( _( "All layers must be matched. Please click on 'Auto-Matched Layers' " wxMessageBox( _( "All required layers (marked with '*') must be matched. "
"to automatically match the remaining layers." ), "Please click on 'Auto-Match Layers' to "
"automatically match the remaining layers" ),
_( "Unmatched Layers" ), wxICON_ERROR | wxOK ); _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
} }
else else

View File

@ -22,7 +22,7 @@
#define DIALOG_IMPORTED_LAYERS_H #define DIALOG_IMPORTED_LAYERS_H
#include <dialog_imported_layers_base.h> #include <dialog_imported_layers_base.h>
#include <plugins/cadstar/cadstar_pcb_archive_plugin.h> // INPUT_LAYER_DESC #include <plugins/common/plugin_common_layer_mapping.h>
class DIALOG_IMPORTED_LAYERS : public DIALOG_IMPORTED_LAYERS_BASE class DIALOG_IMPORTED_LAYERS : public DIALOG_IMPORTED_LAYERS_BASE
@ -33,15 +33,21 @@ private:
std::vector<INPUT_LAYER_DESC> m_input_layers; std::vector<INPUT_LAYER_DESC> m_input_layers;
std::vector<wxString> m_unmatched_layer_names; std::vector<wxString> m_unmatched_layer_names;
LAYER_MAP m_matched_layers_map; std::map<wxString, PCB_LAYER_ID> m_matched_layers_map;
//Helper functions //Helper functions
PCB_LAYER_ID GetSelectedLayerID(); PCB_LAYER_ID GetSelectedLayerID();
PCB_LAYER_ID GetAutoMatchLayerID( wxString aInputLayerName ); PCB_LAYER_ID GetAutoMatchLayerID( wxString aInputLayerName );
void AddMappings(); void AddMappings();
void RemoveMappings( int aStatus ); void RemoveMappings( int aStatus );
void DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl ); void DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl );
const INPUT_LAYER_DESC* GetLayerDescription( const wxString& aLayerName ) const;
static wxString WrapRequired( const wxString& aLayerName );
static wxString UnwrapRequired( const wxString& aLayerName );
//Event Handlers //Event Handlers
void OnAutoMatchLayersClicked( wxCommandEvent& event ) override; void OnAutoMatchLayersClicked( wxCommandEvent& event ) override;
@ -55,13 +61,19 @@ public:
DIALOG_IMPORTED_LAYERS( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc ); DIALOG_IMPORTED_LAYERS( wxWindow* aParent, const std::vector<INPUT_LAYER_DESC>& aLayerDesc );
/** /**
* @brief Creates and shows a dialog (modal) and returns the data from it after completion. * @brief Return a list of layers names that are required, but they are not mapped
* If the dialog is closed or cancel is pressed, returns an empty LAYER_MAP */
std::vector<wxString> GetUnmappedRequiredLayers() const;
/**
* @brief Creates and shows a dialog (modal) and returns the data from it
* after completion. If the dialog is closed or cancel is pressed, returns
* an empty map
* @param aParent Parent window for the invoked dialog. * @param aParent Parent window for the invoked dialog.
* @param aLayerDesc * @param aLayerDesc
* @return Mapped layers * @return Mapped layers
*/ */
static LAYER_MAP GetMapModal( wxWindow* aParent, static std::map<wxString, PCB_LAYER_ID> GetMapModal( wxWindow* aParent,
const std::vector<INPUT_LAYER_DESC>& aLayerDesc ); const std::vector<INPUT_LAYER_DESC>& aLayerDesc );
}; };

View File

@ -50,6 +50,7 @@
#include <project/project_file.h> #include <project/project_file.h>
#include <project/project_local_settings.h> #include <project/project_local_settings.h>
#include <plugins/cadstar/cadstar_pcb_archive_plugin.h> #include <plugins/cadstar/cadstar_pcb_archive_plugin.h>
#include <plugins/eagle/eagle_plugin.h>
#include <dialogs/dialog_imported_layers.h> #include <dialogs/dialog_imported_layers.h>
@ -621,18 +622,13 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) ); PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );
LAYER_REMAPPABLE_PLUGIN* layerRemappable =
if( pluginType == IO_MGR::CADSTAR_PCB_ARCHIVE ) dynamic_cast< LAYER_REMAPPABLE_PLUGIN* >( (PLUGIN*) pi );
if ( layerRemappable )
{ {
// TODO: Generalise this so that it is applicable to all non-kicad plugins using namespace std::placeholders;
CADSTAR_PCB_ARCHIVE_PLUGIN* cadstarPlugin = nullptr; layerRemappable->RegisterLayerMappingCallback(
std::bind( DIALOG_IMPORTED_LAYERS::GetMapModal, this, _1 ) );
cadstarPlugin = dynamic_cast<CADSTAR_PCB_ARCHIVE_PLUGIN*>( (PLUGIN*) pi );
wxCHECK( cadstarPlugin, false );
cadstarPlugin->RegisterLayerMappingCallback(
std::bind( DIALOG_IMPORTED_LAYERS::GetMapModal, this, std::placeholders::_1 ) );
} }
// This will rename the file if there is an autosave and the user want to recover // This will rename the file if there is an autosave and the user want to recover

View File

@ -537,7 +537,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::remapUnsureLayers()
return; return;
// Callback: // Callback:
LAYER_MAP reMappedLayers = mLayerMappingHandler( inputLayers ); std::map<wxString, PCB_LAYER_ID> reMappedLayers = mLayerMappingHandler( inputLayers );
for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers ) for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
{ {

View File

@ -27,7 +27,7 @@
#define CADSTAR_PCB_ARCHIVE_LOADER_H_ #define CADSTAR_PCB_ARCHIVE_LOADER_H_
#include <cadstar_pcb_archive_parser.h> #include <cadstar_pcb_archive_parser.h>
#include <cadstar_pcb_archive_plugin.h> // LAYER_MAPPING_HANDLER definition #include <cadstar_pcb_archive_plugin.h>
#include <class_board.h> #include <class_board.h>
#include <set> #include <set>

View File

@ -30,10 +30,10 @@
#include <properties.h> #include <properties.h>
LAYER_MAP CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback( std::map<wxString, PCB_LAYER_ID> CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback(
const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector ) const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
{ {
LAYER_MAP retval; std::map<wxString, PCB_LAYER_ID> retval;
// Just return a the auto-mapped layers // Just return a the auto-mapped layers
for( INPUT_LAYER_DESC layerDesc : aInputLayerDescriptionVector ) for( INPUT_LAYER_DESC layerDesc : aInputLayerDescriptionVector )
@ -48,7 +48,7 @@ LAYER_MAP CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback(
void CADSTAR_PCB_ARCHIVE_PLUGIN::RegisterLayerMappingCallback( void CADSTAR_PCB_ARCHIVE_PLUGIN::RegisterLayerMappingCallback(
LAYER_MAPPING_HANDLER aLayerMappingHandler ) LAYER_MAPPING_HANDLER aLayerMappingHandler )
{ {
m_layer_mapping_handler = aLayerMappingHandler; LAYER_REMAPPABLE_PLUGIN::RegisterLayerMappingCallback( aLayerMappingHandler );
m_show_layer_mapping_warnings = false; // only show warnings with default callback m_show_layer_mapping_warnings = false; // only show warnings with default callback
} }
@ -57,8 +57,9 @@ CADSTAR_PCB_ARCHIVE_PLUGIN::CADSTAR_PCB_ARCHIVE_PLUGIN()
{ {
m_board = nullptr; m_board = nullptr;
m_props = nullptr; m_props = nullptr;
m_layer_mapping_handler = CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback;
m_show_layer_mapping_warnings = true; m_show_layer_mapping_warnings = true;
LAYER_REMAPPABLE_PLUGIN::RegisterLayerMappingCallback(
CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback );
} }

View File

@ -30,45 +30,10 @@
#include <io_mgr.h> #include <io_mgr.h>
#include <layers_id_colors_and_visibility.h> // PCB_LAYER_ID #include <layers_id_colors_and_visibility.h> // PCB_LAYER_ID
#include <plugins/common/plugin_common_layer_mapping.h>
/** class CADSTAR_PCB_ARCHIVE_PLUGIN : public PLUGIN, public LAYER_REMAPPABLE_PLUGIN
* @brief Describes an imported layer and how it could be mapped to KiCad Layers
*/
struct INPUT_LAYER_DESC
{
wxString Name; ///< Imported layer name as displayed in original application.
LSET PermittedLayers; ///< KiCad layers that the imported layer can be mapped onto.
PCB_LAYER_ID AutoMapLayer; ///< Best guess as to what the equivalent KiCad layer might be.
INPUT_LAYER_DESC()
{
Name = wxEmptyString;
PermittedLayers = LSET();
AutoMapLayer = PCB_LAYER_ID::UNDEFINED_LAYER;
}
};
/**
* A CADSTAR layer name.
*/
typedef wxString INPUT_LAYER_NAME;
/**
* @brief Map of CADSTAR (INPUT_LAYER_NAME) to KiCad Layers.
* If the mapped KiCad layer is UNDEFINED_LAYER, then the CADSTAR layer will not
* be imported
*/
typedef std::map<INPUT_LAYER_NAME, PCB_LAYER_ID> LAYER_MAP;
/**
* @brief Pointer to a function that takes a map of Cadstar and KiCad layers
* and returns a re-mapped version. If the re-mapped layer
*/
typedef std::function<LAYER_MAP( const std::vector<INPUT_LAYER_DESC>& )> LAYER_MAPPING_HANDLER;
class CADSTAR_PCB_ARCHIVE_PLUGIN : public PLUGIN
{ {
public: public:
// -----<PUBLIC PLUGIN API>-------------------------------------------------- // -----<PUBLIC PLUGIN API>--------------------------------------------------
@ -93,7 +58,7 @@ public:
* @param aInputLayerDescriptionVector * @param aInputLayerDescriptionVector
* @return Auto-mapped layers * @return Auto-mapped layers
*/ */
static LAYER_MAP DefaultLayerMappingCallback( static std::map<wxString, PCB_LAYER_ID> DefaultLayerMappingCallback(
const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector ); const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector );
/** /**
@ -101,7 +66,7 @@ public:
* layers occurs * layers occurs
* @param aLayerMappingHandler * @param aLayerMappingHandler
*/ */
void RegisterLayerMappingCallback( LAYER_MAPPING_HANDLER aLayerMappingHandler ); void RegisterLayerMappingCallback( LAYER_MAPPING_HANDLER aLayerMappingHandler ) override;
CADSTAR_PCB_ARCHIVE_PLUGIN(); CADSTAR_PCB_ARCHIVE_PLUGIN();
~CADSTAR_PCB_ARCHIVE_PLUGIN(); ~CADSTAR_PCB_ARCHIVE_PLUGIN();
@ -109,7 +74,6 @@ public:
private: private:
const PROPERTIES* m_props; const PROPERTIES* m_props;
BOARD* m_board; BOARD* m_board;
LAYER_MAPPING_HANDLER m_layer_mapping_handler;
bool m_show_layer_mapping_warnings; bool m_show_layer_mapping_warnings;
}; };

View File

@ -0,0 +1,80 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Jan Mrázek <email@honzamrazek.cz>
* Copyright (C) 2020 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
* Copyright (C) 2020 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef PLUGIN_COMMON_LAYER_MAPPING_H
#define PLUGIN_COMMON_LAYER_MAPPING_H
#include <functional>
#include <io_mgr.h>
#include <layers_id_colors_and_visibility.h> // PCB_LAYER_ID
/**
* @brief Describes an imported layer and how it could be mapped to KiCad Layers
*/
struct INPUT_LAYER_DESC
{
wxString Name; ///< Imported layer name as displayed in original application.
LSET PermittedLayers; ///< KiCad layers that the imported layer can be mapped onto.
PCB_LAYER_ID AutoMapLayer; ///< Best guess as to what the equivalent KiCad layer might be.
bool Required; ///< Should we require the layer to be assigned?
INPUT_LAYER_DESC()
: Name( wxEmptyString ),
PermittedLayers(),
AutoMapLayer( PCB_LAYER_ID::UNDEFINED_LAYER ),
Required( true )
{
}
};
/**
* @brief Pointer to a function that takes a map of source and KiCad layers
* and returns a re-mapped version. If the re-mapped layer is UNDEFINED_LAYER,
* then the source layer will not be imported
*/
using LAYER_MAPPING_HANDLER = std::function<std::map<wxString, PCB_LAYER_ID>( const std::vector<INPUT_LAYER_DESC>& )>;
/**
* @brief Plugin class for import plugins that support remappable layers
*/
class LAYER_REMAPPABLE_PLUGIN
{
public:
/**
* @brief Register a different handler to be called when mapping of input
* layers to KiCad layers occurs
*
* The function is marked as virtual, so the plugins can implement extra
* logic (e.g., enable warnings or checks)
*
* @param aLayerMappingHandler
*/
virtual void RegisterLayerMappingCallback( LAYER_MAPPING_HANDLER aLayerMappingHandler )
{
m_layer_mapping_handler = aLayerMappingHandler;
}
protected:
LAYER_MAPPING_HANDLER m_layer_mapping_handler; ///< Callback to get layer mapping
};
#endif // PLUGIN_COMMON_LAYER_MAPPING_H

View File

@ -274,8 +274,12 @@ EAGLE_PLUGIN::EAGLE_PLUGIN() :
m_xpath( new XPATH() ), m_xpath( new XPATH() ),
m_mod_time( wxDateTime::Now() ) m_mod_time( wxDateTime::Now() )
{ {
using namespace std::placeholders;
init( NULL ); init( NULL );
clear_cu_map(); clear_cu_map();
RegisterLayerMappingCallback( std::bind(
&EAGLE_PLUGIN::DefaultLayerMappingCallback, this, _1 ) );
} }
@ -298,7 +302,6 @@ const wxString EAGLE_PLUGIN::GetFileExtension() const
return wxT( "brd" ); return wxT( "brd" );
} }
wxSize inline EAGLE_PLUGIN::kicad_fontz( const ECOORD& d, int aTextThickness ) const wxSize inline EAGLE_PLUGIN::kicad_fontz( const ECOORD& d, int aTextThickness ) const
{ {
// Eagle includes stroke thickness in the text size, KiCAD does not // Eagle includes stroke thickness in the text size, KiCAD does not
@ -453,6 +456,7 @@ void EAGLE_PLUGIN::loadAllSections( wxXmlNode* aDoc )
wxXmlNode* layers = drawingChildren["layers"]; wxXmlNode* layers = drawingChildren["layers"];
loadLayerDefs( layers ); loadLayerDefs( layers );
mapEagleLayersToKicad();
m_xpath->pop(); m_xpath->pop();
} }
@ -501,11 +505,13 @@ void EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers )
wxXmlNode* layerNode = aLayers->GetChildren(); wxXmlNode* layerNode = aLayers->GetChildren();
m_eagleLayers.clear(); m_eagleLayers.clear();
m_eagleLayersIds.clear();
while( layerNode ) while( layerNode )
{ {
ELAYER elayer( layerNode ); ELAYER elayer( layerNode );
m_eagleLayers.insert( std::make_pair( elayer.number, elayer ) ); m_eagleLayers.insert( std::make_pair( elayer.number, elayer ) );
m_eagleLayersIds.insert( std::make_pair( elayer.name, elayer.number ) );
// find the subset of layers that are copper and active // find the subset of layers that are copper and active
if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) ) if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) )
@ -1245,6 +1251,15 @@ ZONE* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode )
|| p.layer == EAGLE_LAYER::BRESTRICT || p.layer == EAGLE_LAYER::BRESTRICT
|| p.layer == EAGLE_LAYER::VRESTRICT ); || p.layer == EAGLE_LAYER::VRESTRICT );
if( layer == UNDEFINED_LAYER ) {
wxLogMessage( wxString::Format(
_( "Ignoring a polygon since Eagle layer '%s' (%d) "
"was not mapped" ),
eagle_layer_name( p.layer ),
p.layer ) );
return nullptr;
}
if( !IsCopperLayer( layer ) && !keepout ) if( !IsCopperLayer( layer ) && !keepout )
return nullptr; return nullptr;
@ -1583,6 +1598,15 @@ void EAGLE_PLUGIN::packageWire( MODULE* aModule, wxXmlNode* aTree ) const
wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) ); wxPoint end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
int width = w.width.ToPcbUnits(); int width = w.width.ToPcbUnits();
if( layer == UNDEFINED_LAYER ) {
wxLogMessage( wxString::Format(
_( "Ignoring a wire since Eagle layer '%s' (%d) "
"was not mapped" ),
eagle_layer_name( w.layer ),
w.layer ) );
return;
}
// KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle. // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
if( width <= 0 ) if( width <= 0 )
{ {
@ -1747,8 +1771,14 @@ void EAGLE_PLUGIN::packageText( MODULE* aModule, wxXmlNode* aTree ) const
ETEXT t( aTree ); ETEXT t( aTree );
PCB_LAYER_ID layer = kicad_layer( t.layer ); PCB_LAYER_ID layer = kicad_layer( t.layer );
if( layer == UNDEFINED_LAYER ) if( layer == UNDEFINED_LAYER ) {
layer = Cmts_User; wxLogMessage( wxString::Format(
_( "Ignoring a text since Eagle layer '%s' (%d) "
"was not mapped" ),
eagle_layer_name( t.layer ),
t.layer ) );
return;
}
FP_TEXT* txt; FP_TEXT* txt;
@ -1878,6 +1908,15 @@ void EAGLE_PLUGIN::packageRectangle( MODULE* aModule, wxXmlNode* aTree ) const
else else
{ {
PCB_LAYER_ID layer = kicad_layer( r.layer ); PCB_LAYER_ID layer = kicad_layer( r.layer );
if( layer == UNDEFINED_LAYER ) {
wxLogMessage( wxString::Format(
_( "Ignoring a rectange since Eagle layer '%s' (%d) "
"was not mapped" ),
eagle_layer_name( r.layer ),
r.layer ) );
return;
}
FP_SHAPE* dwg = new FP_SHAPE( aModule, S_POLYGON ); FP_SHAPE* dwg = new FP_SHAPE( aModule, S_POLYGON );
aModule->Add( dwg ); aModule->Add( dwg );
@ -1983,6 +2022,15 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const
else else
{ {
PCB_LAYER_ID layer = kicad_layer( p.layer ); PCB_LAYER_ID layer = kicad_layer( p.layer );
if( layer == UNDEFINED_LAYER ) {
wxLogMessage( wxString::Format(
_( "Ignoring a polygon since Eagle layer '%s' (%d) "
"was not mapped" ),
eagle_layer_name( p.layer ),
p.layer ) );
return;
}
FP_SHAPE* dwg = new FP_SHAPE( aModule, S_POLYGON ); FP_SHAPE* dwg = new FP_SHAPE( aModule, S_POLYGON );
aModule->Add( dwg ); aModule->Add( dwg );
@ -2043,6 +2091,15 @@ void EAGLE_PLUGIN::packageCircle( MODULE* aModule, wxXmlNode* aTree ) const
else else
{ {
PCB_LAYER_ID layer = kicad_layer( e.layer ); PCB_LAYER_ID layer = kicad_layer( e.layer );
if( layer == UNDEFINED_LAYER ) {
wxLogMessage( wxString::Format(
_( "Ignoring a cricle since Eagle layer '%s' (%d) "
"was not mapped" ),
eagle_layer_name( e.layer ),
e.layer ) );
return;
}
FP_SHAPE* gr = new FP_SHAPE( aModule, S_CIRCLE ); FP_SHAPE* gr = new FP_SHAPE( aModule, S_CIRCLE );
// with == 0 means filled circle // with == 0 means filled circle
@ -2451,78 +2508,172 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
m_xpath->pop(); // "signals.signal" m_xpath->pop(); // "signals.signal"
} }
std::map<wxString, PCB_LAYER_ID> EAGLE_PLUGIN::DefaultLayerMappingCallback(
const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
{
std::map<wxString, PCB_LAYER_ID> layer_map;
for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
{
PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
layer_map.emplace( layer.Name, layerId );
}
return layer_map;
}
void EAGLE_PLUGIN::mapEagleLayersToKicad()
{
std::vector<INPUT_LAYER_DESC> inputDescs;
for ( const std::pair<int, ELAYER>& layerPair : m_eagleLayers )
{
const ELAYER& eLayer = layerPair.second;
INPUT_LAYER_DESC layerDesc;
std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
defaultKicadLayer( eLayer.number );
if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
continue; // Ignore unused copper layers
layerDesc.Name = eLayer.name;
inputDescs.push_back( layerDesc );
}
m_layer_map = m_layer_mapping_handler( inputDescs );
}
PCB_LAYER_ID EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) const PCB_LAYER_ID EAGLE_PLUGIN::kicad_layer( int aEagleLayer ) const
{ {
int kiLayer; auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
}
std::tuple<PCB_LAYER_ID, LSET, bool> EAGLE_PLUGIN::defaultKicadLayer( int aEagleLayer ) const
{
// eagle copper layer: // eagle copper layer:
if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) ) if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
{ {
kiLayer = m_cu_map[aEagleLayer]; LSET copperLayers;
for( int copperLayer : m_cu_map )
{
if( copperLayer >= 0 )
copperLayers[copperLayer] = true;
} }
else return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
{ }
int kiLayer = UNSELECTED_LAYER;
bool required = false;
LSET permittedLayers;
permittedLayers.set();
// translate non-copper eagle layer to pcbnew layer // translate non-copper eagle layer to pcbnew layer
switch( aEagleLayer ) switch( aEagleLayer )
{ {
// Eagle says "Dimension" layer, but it's for board perimeter // Eagle says "Dimension" layer, but it's for board perimeter
case EAGLE_LAYER::DIMENSION: kiLayer = Edge_Cuts; break; case EAGLE_LAYER::DIMENSION:
kiLayer = Edge_Cuts;
required = true;
permittedLayers = LSET( 1, Edge_Cuts );
break;
case EAGLE_LAYER::TPLACE: kiLayer = F_SilkS; break; case EAGLE_LAYER::TPLACE:
case EAGLE_LAYER::BPLACE: kiLayer = B_SilkS; break; kiLayer = F_SilkS;
case EAGLE_LAYER::TNAMES: kiLayer = F_SilkS; break; break;
case EAGLE_LAYER::BNAMES: kiLayer = B_SilkS; break; case EAGLE_LAYER::BPLACE:
case EAGLE_LAYER::TVALUES: kiLayer = F_Fab; break; kiLayer = B_SilkS;
case EAGLE_LAYER::BVALUES: kiLayer = B_Fab; break; break;
case EAGLE_LAYER::TSTOP: kiLayer = F_Mask; break; case EAGLE_LAYER::TNAMES:
case EAGLE_LAYER::BSTOP: kiLayer = B_Mask; break; kiLayer = F_SilkS;
case EAGLE_LAYER::TCREAM: kiLayer = F_Paste; break; break;
case EAGLE_LAYER::BCREAM: kiLayer = B_Paste; break; case EAGLE_LAYER::BNAMES:
case EAGLE_LAYER::TFINISH: kiLayer = F_Mask; break; kiLayer = B_SilkS;
case EAGLE_LAYER::BFINISH: kiLayer = B_Mask; break; break;
case EAGLE_LAYER::TGLUE: kiLayer = F_Adhes; break; case EAGLE_LAYER::TVALUES:
case EAGLE_LAYER::BGLUE: kiLayer = B_Adhes; break; kiLayer = F_Fab;
case EAGLE_LAYER::DOCUMENT: kiLayer = Cmts_User; break; break;
case EAGLE_LAYER::REFERENCELC: kiLayer = Cmts_User; break; case EAGLE_LAYER::BVALUES:
case EAGLE_LAYER::REFERENCELS: kiLayer = Cmts_User; break; kiLayer = B_Fab;
break;
case EAGLE_LAYER::TSTOP:
kiLayer = F_Mask;
break;
case EAGLE_LAYER::BSTOP:
kiLayer = B_Mask;
break;
case EAGLE_LAYER::TCREAM:
kiLayer = F_Paste;
break;
case EAGLE_LAYER::BCREAM:
kiLayer = B_Paste;
break;
case EAGLE_LAYER::TFINISH:
kiLayer = F_Mask;
break;
case EAGLE_LAYER::BFINISH:
kiLayer = B_Mask;
break;
case EAGLE_LAYER::TGLUE:
kiLayer = F_Adhes;
break;
case EAGLE_LAYER::BGLUE:
kiLayer = B_Adhes;
break;
case EAGLE_LAYER::DOCUMENT:
kiLayer = Cmts_User;
break;
case EAGLE_LAYER::REFERENCELC:
kiLayer = Cmts_User;
break;
case EAGLE_LAYER::REFERENCELS:
kiLayer = Cmts_User;
break;
// Packages show the future chip pins on SMD parts using layer 51. // Packages show the future chip pins on SMD parts using layer 51.
// This is an area slightly smaller than the PAD/SMD copper area. // This is an area slightly smaller than the PAD/SMD copper area.
// Carry those visual aids into the MODULE on the fabrication layer, // Carry those visual aids into the MODULE on the fabrication layer,
// not silkscreen. This is perhaps not perfect, but there is not a lot // not silkscreen. This is perhaps not perfect, but there is not a lot
// of other suitable paired layers // of other suitable paired layers
case EAGLE_LAYER::TDOCU: kiLayer = F_Fab; break; case EAGLE_LAYER::TDOCU:
case EAGLE_LAYER::BDOCU: kiLayer = B_Fab; break; kiLayer = F_Fab;
break;
case EAGLE_LAYER::BDOCU:
kiLayer = B_Fab;
break;
// these layers are defined as user layers. put them on ECO layers // these layers are defined as user layers. put them on ECO layers
case EAGLE_LAYER::USERLAYER1: kiLayer = Eco1_User; break; case EAGLE_LAYER::USERLAYER1:
case EAGLE_LAYER::USERLAYER2: kiLayer = Eco2_User; break; kiLayer = Eco1_User;
break;
case EAGLE_LAYER::USERLAYER2:
kiLayer = Eco2_User;
break;
// these will also appear in the ratsnest, so there's no need for a warning // these will also appear in the ratsnest, so there's no need for a warning
case EAGLE_LAYER::UNROUTED: kiLayer = Dwgs_User; break; case EAGLE_LAYER::UNROUTED:
kiLayer = Dwgs_User;
break;
case EAGLE_LAYER::TKEEPOUT: kiLayer = F_CrtYd; break; case EAGLE_LAYER::TKEEPOUT:
case EAGLE_LAYER::BKEEPOUT: kiLayer = B_CrtYd; break; kiLayer = F_CrtYd;
break;
case EAGLE_LAYER::BKEEPOUT:
kiLayer = B_CrtYd;
break;
case EAGLE_LAYER::MILLING: case EAGLE_LAYER::MILLING:
case EAGLE_LAYER::TTEST: case EAGLE_LAYER::TTEST:
case EAGLE_LAYER::BTEST: case EAGLE_LAYER::BTEST:
case EAGLE_LAYER::HOLES: case EAGLE_LAYER::HOLES:
default: default:
// some layers do not map to KiCad kiLayer = UNSELECTED_LAYER;
wxLogMessage( wxString::Format( _( "Unsupported Eagle layer '%s' (%d), "
"converted to Dwgs.User layer" ),
eagle_layer_name( aEagleLayer ),
aEagleLayer ) );
kiLayer = Dwgs_User;
break; break;
} }
}
return PCB_LAYER_ID( kiLayer ); return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
} }
@ -2534,6 +2685,14 @@ const wxString& EAGLE_PLUGIN::eagle_layer_name( int aLayer ) const
} }
int EAGLE_PLUGIN::eagle_layer_id( const wxString& aLayerName ) const
{
static const int unknown = -1;
auto it = m_eagleLayersIds.find( aLayerName );
return it == m_eagleLayersIds.end() ? unknown : it->second;
}
void EAGLE_PLUGIN::centerBoard() void EAGLE_PLUGIN::centerBoard()
{ {
if( m_props ) if( m_props )

View File

@ -28,8 +28,10 @@
#include <io_mgr.h> #include <io_mgr.h>
#include <layers_id_colors_and_visibility.h> #include <layers_id_colors_and_visibility.h>
#include <plugins/eagle/eagle_parser.h> #include <plugins/eagle/eagle_parser.h>
#include <plugins/common/plugin_common_layer_mapping.h>
#include <map> #include <map>
#include <tuple>
#include <wx/xml/xml.h> #include <wx/xml/xml.h>
class D_PAD; class D_PAD;
@ -115,7 +117,7 @@ struct ERULES
* works with Eagle 6.x XML board files and footprints to implement the * works with Eagle 6.x XML board files and footprints to implement the
* Pcbnew PLUGIN API, or a portion of it. * Pcbnew PLUGIN API, or a portion of it.
*/ */
class EAGLE_PLUGIN : public PLUGIN class EAGLE_PLUGIN : public PLUGIN, public LAYER_REMAPPABLE_PLUGIN
{ {
public: public:
@ -164,12 +166,27 @@ public:
EAGLE_PLUGIN(); EAGLE_PLUGIN();
~EAGLE_PLUGIN(); ~EAGLE_PLUGIN();
/**
* @brief Default callback - just returns the automapped layers
*
* The callback needs to have the context of the current board so it can
* correctly determine copper layer mapping. Thus, it is not static and is
* expected to be bind to an instance of EAGLE_PLUGIN.
*
* @param aInputLayerDescriptionVector
* @return Auto-mapped layers
*/
std::map<wxString, PCB_LAYER_ID> DefaultLayerMappingCallback(
const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector );
private: private:
typedef std::vector<ELAYER> ELAYERS; typedef std::vector<ELAYER> ELAYERS;
typedef ELAYERS::const_iterator EITER; typedef ELAYERS::const_iterator EITER;
int m_cu_map[17]; ///< map eagle to kicad, cu layers only. int m_cu_map[17]; ///< map eagle to kicad, cu layers only.
std::map<int, ELAYER> m_eagleLayers; ///< Eagle layers data stored by the layer number std::map<int, ELAYER> m_eagleLayers; ///< Eagle layers data stored by the layer number
std::map<wxString, int> m_eagleLayersIds; ///< Eagle layers id stored by the layer name
std::map<wxString, PCB_LAYER_ID> m_layer_map; ///< Mapping of Eagle layers to KiCAD layers
ERULES* m_rules; ///< Eagle design rules. ERULES* m_rules; ///< Eagle design rules.
XPATH* m_xpath; ///< keeps track of what we are working on within XPATH* m_xpath; ///< keeps track of what we are working on within
@ -207,12 +224,22 @@ private:
/// create a font size (fontz) from an eagle font size scalar and KiCAD font thickness /// create a font size (fontz) from an eagle font size scalar and KiCAD font thickness
wxSize kicad_fontz( const ECOORD& d, int aTextThickness ) const; wxSize kicad_fontz( const ECOORD& d, int aTextThickness ) const;
/// Generate mapping between Eagle na KiCAD layers
void mapEagleLayersToKicad();
/// Convert an Eagle layer to a KiCad layer. /// Convert an Eagle layer to a KiCad layer.
PCB_LAYER_ID kicad_layer( int aLayer ) const; PCB_LAYER_ID kicad_layer( int aLayer ) const;
/// Get default KiCAD layer corresponding to an Eagle layer of the board,
/// a set of sensible layer mapping options and required flag
std::tuple<PCB_LAYER_ID, LSET, bool> defaultKicadLayer( int aEagleLayer ) const;
/// Get Eagle layer name by its number /// Get Eagle layer name by its number
const wxString& eagle_layer_name( int aLayer ) const; const wxString& eagle_layer_name( int aLayer ) const;
/// Get Eagle leayer number by its name
int eagle_layer_id( const wxString& aLayerName ) const;
/// This PLUGIN only caches one footprint library, this determines which one. /// This PLUGIN only caches one footprint library, this determines which one.
void cacheLib( const wxString& aLibraryPath ); void cacheLib( const wxString& aLibraryPath );