From fc8bf6f0fe59359a8d47bb57142d7e09a76a6f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Mr=C3=A1zek?= Date: Fri, 9 Oct 2020 18:58:21 +0200 Subject: [PATCH] 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 --- pcbnew/dialogs/dialog_imported_layers.cpp | 71 ++++- pcbnew/dialogs/dialog_imported_layers.h | 32 +- pcbnew/files.cpp | 18 +- .../cadstar/cadstar_pcb_archive_loader.cpp | 2 +- .../cadstar/cadstar_pcb_archive_loader.h | 84 ++--- .../cadstar/cadstar_pcb_archive_plugin.cpp | 9 +- .../cadstar/cadstar_pcb_archive_plugin.h | 48 +-- .../common/plugin_common_layer_mapping.h | 80 +++++ pcbnew/plugins/eagle/eagle_plugin.cpp | 293 ++++++++++++++---- pcbnew/plugins/eagle/eagle_plugin.h | 29 +- 10 files changed, 475 insertions(+), 191 deletions(-) create mode 100644 pcbnew/plugins/common/plugin_common_layer_mapping.h diff --git a/pcbnew/dialogs/dialog_imported_layers.cpp b/pcbnew/dialogs/dialog_imported_layers.cpp index 67269fd87d..4a2d9e7f99 100644 --- a/pcbnew/dialogs/dialog_imported_layers.cpp +++ b/pcbnew/dialogs/dialog_imported_layers.cpp @@ -24,6 +24,30 @@ #include +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() { @@ -58,10 +82,14 @@ PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetSelectedLayerID() PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetAutoMatchLayerID( wxString aInputLayerName ) { + wxString pureInputLayerName = UnwrapRequired( aInputLayerName ); 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 PCB_LAYER_ID::UNDEFINED_LAYER; @@ -90,7 +118,8 @@ void DIALOG_IMPORTED_LAYERS::AddMappings() long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName ); 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 for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end(); @@ -121,12 +150,13 @@ void DIALOG_IMPORTED_LAYERS::RemoveMappings( int aStatus ) while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) ) != 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*/ ); - m_matched_layers_map.erase( selectedLayerName ); + m_matched_layers_map.erase( pureSelectedLayerName ); rowsToDelete.Add( itemIndex ); 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 ); 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 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 ) { 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; } - int maxTextWidth = 0; + int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x; for( const INPUT_LAYER_DESC& layer : m_input_layers ) maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x ); @@ -268,8 +299,21 @@ DIALOG_IMPORTED_LAYERS::DIALOG_IMPORTED_LAYERS( wxWindow* aParent, FinishDialogSettings(); } +std::vector DIALOG_IMPORTED_LAYERS::GetUnmappedRequiredLayers() const +{ + std::vector 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 DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent, const std::vector& aLayerDesc ) { DIALOG_IMPORTED_LAYERS dlg( aParent, aLayerDesc ); @@ -279,11 +323,12 @@ LAYER_MAP DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent, { 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' " - "to automatically match the remaining layers." ), - _( "Unmatched Layers" ), wxICON_ERROR | wxOK ); + wxMessageBox( _( "All required layers (marked with '*') must be matched. " + "Please click on 'Auto-Match Layers' to " + "automatically match the remaining layers" ), + _( "Unmatched Layers" ), wxICON_ERROR | wxOK ); } else { diff --git a/pcbnew/dialogs/dialog_imported_layers.h b/pcbnew/dialogs/dialog_imported_layers.h index 1f1861c3b5..3d83ecb123 100644 --- a/pcbnew/dialogs/dialog_imported_layers.h +++ b/pcbnew/dialogs/dialog_imported_layers.h @@ -22,7 +22,7 @@ #define DIALOG_IMPORTED_LAYERS_H #include -#include // INPUT_LAYER_DESC +#include class DIALOG_IMPORTED_LAYERS : public DIALOG_IMPORTED_LAYERS_BASE @@ -31,16 +31,22 @@ private: const int selected = wxLIST_STATE_SELECTED; const int allitems = wxLIST_STATE_DONTCARE; - std::vector m_input_layers; - std::vector m_unmatched_layer_names; - LAYER_MAP m_matched_layers_map; + std::vector m_input_layers; + std::vector m_unmatched_layer_names; + std::map m_matched_layers_map; //Helper functions PCB_LAYER_ID GetSelectedLayerID(); PCB_LAYER_ID GetAutoMatchLayerID( wxString aInputLayerName ); - void AddMappings(); - void RemoveMappings( int aStatus ); - void DeleteListItems( const wxArrayInt& aRowsToDelete, wxListCtrl* aListCtrl ); + + void AddMappings(); + void RemoveMappings( int aStatus ); + 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 void OnAutoMatchLayersClicked( wxCommandEvent& event ) override; @@ -55,13 +61,19 @@ public: DIALOG_IMPORTED_LAYERS( wxWindow* aParent, const std::vector& aLayerDesc ); /** - * @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 LAYER_MAP + * @brief Return a list of layers names that are required, but they are not mapped + */ + std::vector 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 aLayerDesc * @return Mapped layers */ - static LAYER_MAP GetMapModal( wxWindow* aParent, + static std::map GetMapModal( wxWindow* aParent, const std::vector& aLayerDesc ); }; diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index 1972eb99a4..703d4c6b52 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -621,18 +622,13 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) ); - - if( pluginType == IO_MGR::CADSTAR_PCB_ARCHIVE ) + LAYER_REMAPPABLE_PLUGIN* layerRemappable = + dynamic_cast< LAYER_REMAPPABLE_PLUGIN* >( (PLUGIN*) pi ); + if ( layerRemappable ) { - // TODO: Generalise this so that it is applicable to all non-kicad plugins - CADSTAR_PCB_ARCHIVE_PLUGIN* cadstarPlugin = nullptr; - - cadstarPlugin = dynamic_cast( (PLUGIN*) pi ); - - wxCHECK( cadstarPlugin, false ); - - cadstarPlugin->RegisterLayerMappingCallback( - std::bind( DIALOG_IMPORTED_LAYERS::GetMapModal, this, std::placeholders::_1 ) ); + using namespace std::placeholders; + layerRemappable->RegisterLayerMappingCallback( + std::bind( DIALOG_IMPORTED_LAYERS::GetMapModal, this, _1 ) ); } // This will rename the file if there is an autosave and the user want to recover diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp index 8558493156..bb3a43ddfd 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp @@ -537,7 +537,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::remapUnsureLayers() return; // Callback: - LAYER_MAP reMappedLayers = mLayerMappingHandler( inputLayers ); + std::map reMappedLayers = mLayerMappingHandler( inputLayers ); for( std::pair layerPair : reMappedLayers ) { diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h index 8b3de80a33..5f8769c74d 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h @@ -27,7 +27,7 @@ #define CADSTAR_PCB_ARCHIVE_LOADER_H_ #include -#include // LAYER_MAPPING_HANDLER definition +#include #include #include @@ -65,7 +65,7 @@ public: /** * @brief Loads a CADSTAR PCB Archive file into the KiCad BOARD object given - * @param aBoard + * @param aBoard */ void Load( ::BOARD* aBoard ); @@ -145,8 +145,8 @@ private: //Helper functions for drawing /loading objects onto screen: /** - * @brief - * @param aCadstarText + * @brief + * @param aCadstarText * @param aContainer to draw on (e.g. mBoard) * @param aCadstarGroupID to add the text to * @param aCadstarLayerOverride if not empty, overrides the LayerID in aCadstarText @@ -164,8 +164,8 @@ private: const bool& aMirrorInvert = false ); /** - * @brief - * @param aCadstarShape + * @brief + * @param aCadstarShape * @param aCadstarLayerID KiCad layer to draw on * @param aLineThickness Thickness of line to draw with * @param aShapeName for reporting warnings/errors to the user @@ -185,7 +185,7 @@ private: /** * @brief Uses PCB_SHAPE to draw the cutouts on mBoard object - * @param aVertices + * @param aVertices * @param aKiCadLayer KiCad layer to draw on * @param aLineThickness Thickness of line to draw with * @param aContainer to draw on (e.g. mBoard) @@ -205,7 +205,7 @@ private: /** * @brief Uses PCB_SHAPE to draw the vertices on mBoard object - * @param aCadstarVertices + * @param aCadstarVertices * @param aKiCadLayer KiCad layer to draw on * @param aLineThickness Thickness of line to draw with * @param aContainer to draw on (e.g. mBoard) @@ -234,7 +234,7 @@ private: * @param aScalingFactor scale draw segment by this amount * @param aTransformCentre around which all transforms are applied (KiCad coordinates) * @param aMirrorInvert if true, mirrors the drawsegment - * @return + * @return */ std::vector getDrawSegmentsFromVertices( const std::vector& aCadstarVertices, BOARD_ITEM_CONTAINER* aContainer = nullptr, @@ -253,7 +253,7 @@ private: * @param aScalingFactor scale draw segment by this amount * @param aTransformCentre around which all transforms are applied (KiCad coordinates) * @param aMirrorInvert if true, mirrors the drawsegment - * @return + * @return */ PCB_SHAPE* getDrawSegmentFromVertex( const POINT& aCadstarStartPoint, const VERTEX& aCadstarVertex, BOARD_ITEM_CONTAINER* aContainer = nullptr, @@ -262,8 +262,8 @@ private: const wxPoint& aTransformCentre = { 0, 0 }, const bool& aMirrorInvert = false ); /** - * @brief - * @param aCadstarShape + * @brief + * @param aCadstarShape * @param aLineThickness Thickness of line to draw with * @param aParentContainer Parent object (e.g. mBoard or a MODULE pointer) * @return Pointer to ZONE. Caller owns the object. @@ -281,7 +281,7 @@ private: * @param aScalingFactor scale draw segment by this amount * @param aTransformCentre around which all transforms are applied (KiCad coordinates) * @param aMirrorInvert if true, mirrors the shape - * @return + * @return */ SHAPE_POLY_SET getPolySetFromCadstarShape( const SHAPE& aCadstarShape, const int& aLineThickness = -1, BOARD_ITEM_CONTAINER* aContainer = nullptr, @@ -292,20 +292,20 @@ private: /** * @brief Returns a SHAPE_LINE_CHAIN object from a series of PCB_SHAPE objects * @param aDrawSegments - * @return + * @return */ SHAPE_LINE_CHAIN getLineChainFromDrawsegments( const std::vector aDrawSegments ); /** * @brief Returns a vector of pointers to TRACK/ARC objects. Caller owns the objects - * @param aDrawsegments + * @param aDrawsegments * @param aParentContainer sets this as the parent of each TRACK object and Add()s it to the parent * @param aNet sets all the tracks to this net, unless nullptr * @param aLayerOverride Sets all tracks to this layer, or, if it is UNDEFINED_LAYER, uses the layers * in the DrawSegments * @param aWidthOverride Sets all tracks to this width, or, if it is UNDEFINED_LAYER, uses the width * in the DrawSegments - * @return + * @return */ std::vector makeTracksFromDrawsegments( const std::vector aDrawsegments, BOARD_ITEM_CONTAINER* aParentContainer, NETINFO_ITEM* aNet = nullptr, @@ -313,10 +313,10 @@ private: /** * @brief Adds a CADSTAR Attribute to a KiCad module - * @param aCadstarAttrLoc + * @param aCadstarAttrLoc * @param aCadstarAttributeID - * @param aModule - * @param aAttributeValue + * @param aModule + * @param aAttributeValue */ void addAttribute( const ATTRIBUTE_LOCATION& aCadstarAttrLoc, const ATTRIBUTE_ID& aCadstarAttributeID, MODULE* aModule, @@ -346,15 +346,15 @@ private: /** * @brief Scales, offsets and inverts y axis to make the point usable directly in KiCad - * @param aCadstarPoint - * @return + * @param aCadstarPoint + * @return */ wxPoint getKiCadPoint( wxPoint aCadstarPoint ); /** - * @brief - * @param aCadstarLength - * @return + * @brief + * @param aCadstarLength + * @return */ int getKiCadLength( long long aCadstarLength ) { @@ -362,9 +362,9 @@ private: } /** - * @brief - * @param aCadstarAngle - * @return + * @brief + * @param aCadstarAngle + * @return */ double getAngleTenthDegree( const long long& aCadstarAngle ) { @@ -372,9 +372,9 @@ private: } /** - * @brief - * @param aCadstarAngle - * @return + * @brief + * @param aCadstarAngle + * @return */ double getAngleDegrees( const long long& aCadstarAngle ) { @@ -382,8 +382,8 @@ private: } /** - * @brief - * @param aPoint + * @brief + * @param aPoint * @return Angle in decidegrees of the polar representation of the point, scaled 0..360 */ double getPolarAngle( wxPoint aPoint ); @@ -391,35 +391,35 @@ private: /** * @brief Searches mNetMap and returns the NETINFO_ITEM pointer if exists. Otherwise * creates a new one and adds it to mBoard. - * @param aCadstarNetID - * @return + * @param aCadstarNetID + * @return */ NETINFO_ITEM* getKiCadNet( const NET_ID& aCadstarNetID ); /** - * @brief + * @brief * @param aLayerNum Physical / logical layer number (starts at 1) * @return PCB_LAYER_ID */ PCB_LAYER_ID getKiCadCopperLayerID( unsigned int aLayerNum ); /** - * @brief - * @param aCadstarLayerID + * @brief + * @param aCadstarLayerID * @return true if the layer corresponds to a KiCad LSET or false if the layer maps directly */ bool isLayerSet( const LAYER_ID& aCadstarLayerID ); /** - * @brief - * @param aCadstarLayerID + * @brief + * @param aCadstarLayerID * @return PCB_LAYER_ID */ PCB_LAYER_ID getKiCadLayer( const LAYER_ID& aCadstarLayerID ); /** - * @brief - * @param aCadstarLayerID + * @brief + * @param aCadstarLayerID * @return LSET */ LSET getKiCadLayerSet( const LAYER_ID& aCadstarLayerID ); @@ -437,7 +437,7 @@ private: * @brief Adds a new PCB_GROUP* to mGroupMap * @param aName Name to give the group. If name already exists, append "_1", "_2", etc. * to the end to ensure it is unique - * @return + * @return */ GROUP_ID createUniqueGroupID( const wxString& aName ); }; diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp index fcd6dce4c3..43b966b814 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp @@ -30,10 +30,10 @@ #include -LAYER_MAP CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback( +std::map CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback( const std::vector& aInputLayerDescriptionVector ) { - LAYER_MAP retval; + std::map retval; // Just return a the auto-mapped layers for( INPUT_LAYER_DESC layerDesc : aInputLayerDescriptionVector ) @@ -48,7 +48,7 @@ LAYER_MAP CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback( void CADSTAR_PCB_ARCHIVE_PLUGIN::RegisterLayerMappingCallback( 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 } @@ -57,8 +57,9 @@ CADSTAR_PCB_ARCHIVE_PLUGIN::CADSTAR_PCB_ARCHIVE_PLUGIN() { m_board = nullptr; m_props = nullptr; - m_layer_mapping_handler = CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback; m_show_layer_mapping_warnings = true; + LAYER_REMAPPABLE_PLUGIN::RegisterLayerMappingCallback( + CADSTAR_PCB_ARCHIVE_PLUGIN::DefaultLayerMappingCallback ); } diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.h b/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.h index 20a9ad11c8..91cf804730 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.h +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.h @@ -30,45 +30,10 @@ #include #include // PCB_LAYER_ID +#include -/** - * @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 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_MAPPING_HANDLER; - - -class CADSTAR_PCB_ARCHIVE_PLUGIN : public PLUGIN +class CADSTAR_PCB_ARCHIVE_PLUGIN : public PLUGIN, public LAYER_REMAPPABLE_PLUGIN { public: // ------------------------------------------------------- @@ -90,18 +55,18 @@ public: /** * @brief Default callback - just returns the automapped layers - * @param aInputLayerDescriptionVector + * @param aInputLayerDescriptionVector * @return Auto-mapped layers */ - static LAYER_MAP DefaultLayerMappingCallback( + static std::map DefaultLayerMappingCallback( const std::vector& aInputLayerDescriptionVector ); /** * @brief Register a different handler to be called when mapping of Cadstar to KiCad * 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(); @@ -109,7 +74,6 @@ public: private: const PROPERTIES* m_props; BOARD* m_board; - LAYER_MAPPING_HANDLER m_layer_mapping_handler; bool m_show_layer_mapping_warnings; }; diff --git a/pcbnew/plugins/common/plugin_common_layer_mapping.h b/pcbnew/plugins/common/plugin_common_layer_mapping.h new file mode 100644 index 0000000000..0de6fa70e3 --- /dev/null +++ b/pcbnew/plugins/common/plugin_common_layer_mapping.h @@ -0,0 +1,80 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 Jan Mrázek + * Copyright (C) 2020 Roberto Fernandez Bautista + * 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 . + */ + +#ifndef PLUGIN_COMMON_LAYER_MAPPING_H +#define PLUGIN_COMMON_LAYER_MAPPING_H + +#include + +#include +#include // 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( const std::vector& )>; + + +/** + * @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 \ No newline at end of file diff --git a/pcbnew/plugins/eagle/eagle_plugin.cpp b/pcbnew/plugins/eagle/eagle_plugin.cpp index d173142087..6607c8aa7b 100644 --- a/pcbnew/plugins/eagle/eagle_plugin.cpp +++ b/pcbnew/plugins/eagle/eagle_plugin.cpp @@ -274,8 +274,12 @@ EAGLE_PLUGIN::EAGLE_PLUGIN() : m_xpath( new XPATH() ), m_mod_time( wxDateTime::Now() ) { + using namespace std::placeholders; + init( NULL ); clear_cu_map(); + RegisterLayerMappingCallback( std::bind( + &EAGLE_PLUGIN::DefaultLayerMappingCallback, this, _1 ) ); } @@ -298,7 +302,6 @@ const wxString EAGLE_PLUGIN::GetFileExtension() const return wxT( "brd" ); } - wxSize inline EAGLE_PLUGIN::kicad_fontz( const ECOORD& d, int aTextThickness ) const { // 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"]; loadLayerDefs( layers ); + mapEagleLayersToKicad(); m_xpath->pop(); } @@ -501,11 +505,13 @@ void EAGLE_PLUGIN::loadLayerDefs( wxXmlNode* aLayers ) wxXmlNode* layerNode = aLayers->GetChildren(); m_eagleLayers.clear(); + m_eagleLayersIds.clear(); while( layerNode ) { ELAYER elayer( layerNode ); 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 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::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 ) 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 ) ); 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. if( width <= 0 ) { @@ -1747,8 +1771,14 @@ void EAGLE_PLUGIN::packageText( MODULE* aModule, wxXmlNode* aTree ) const ETEXT t( aTree ); PCB_LAYER_ID layer = kicad_layer( t.layer ); - if( layer == UNDEFINED_LAYER ) - layer = Cmts_User; + if( layer == UNDEFINED_LAYER ) { + 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; @@ -1878,7 +1908,16 @@ void EAGLE_PLUGIN::packageRectangle( MODULE* aModule, wxXmlNode* aTree ) const else { PCB_LAYER_ID layer = kicad_layer( r.layer ); - FP_SHAPE* dwg = new FP_SHAPE( aModule, S_POLYGON ); + 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 ); aModule->Add( dwg ); @@ -1983,7 +2022,16 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const else { PCB_LAYER_ID layer = kicad_layer( p.layer ); - FP_SHAPE* dwg = new FP_SHAPE( aModule, S_POLYGON ); + 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 ); aModule->Add( dwg ); @@ -2043,7 +2091,16 @@ void EAGLE_PLUGIN::packageCircle( MODULE* aModule, wxXmlNode* aTree ) const else { PCB_LAYER_ID layer = kicad_layer( e.layer ); - FP_SHAPE* gr = new FP_SHAPE( aModule, S_CIRCLE ); + 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 ); // with == 0 means filled circle if( width <= 0 ) @@ -2451,78 +2508,172 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals ) m_xpath->pop(); // "signals.signal" } +std::map EAGLE_PLUGIN::DefaultLayerMappingCallback( + const std::vector& aInputLayerDescriptionVector ) +{ + std::map 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 inputDescs; + + for ( const std::pair& 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 { - int kiLayer; + auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) ); + return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second; +} +std::tuple EAGLE_PLUGIN::defaultKicadLayer( int aEagleLayer ) const +{ // eagle copper layer: if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) ) { - kiLayer = m_cu_map[aEagleLayer]; - } - - else - { - // translate non-copper eagle layer to pcbnew layer - switch( aEagleLayer ) + LSET copperLayers; + for( int copperLayer : m_cu_map ) { - // Eagle says "Dimension" layer, but it's for board perimeter - case EAGLE_LAYER::DIMENSION: kiLayer = Edge_Cuts; break; - - case EAGLE_LAYER::TPLACE: kiLayer = F_SilkS; break; - case EAGLE_LAYER::BPLACE: kiLayer = B_SilkS; break; - case EAGLE_LAYER::TNAMES: kiLayer = F_SilkS; break; - case EAGLE_LAYER::BNAMES: kiLayer = B_SilkS; break; - case EAGLE_LAYER::TVALUES: kiLayer = F_Fab; break; - case EAGLE_LAYER::BVALUES: 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. - // This is an area slightly smaller than the PAD/SMD copper area. - // Carry those visual aids into the MODULE on the fabrication layer, - // not silkscreen. This is perhaps not perfect, but there is not a lot - // of other suitable paired layers - case EAGLE_LAYER::TDOCU: kiLayer = F_Fab; break; - case EAGLE_LAYER::BDOCU: kiLayer = B_Fab; break; - - // these layers are defined as user layers. put them on ECO layers - case EAGLE_LAYER::USERLAYER1: 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 - case EAGLE_LAYER::UNROUTED: kiLayer = Dwgs_User; break; - - case EAGLE_LAYER::TKEEPOUT: kiLayer = F_CrtYd; break; - case EAGLE_LAYER::BKEEPOUT: kiLayer = B_CrtYd; break; - - case EAGLE_LAYER::MILLING: - case EAGLE_LAYER::TTEST: - case EAGLE_LAYER::BTEST: - case EAGLE_LAYER::HOLES: - default: - // some layers do not map to KiCad - wxLogMessage( wxString::Format( _( "Unsupported Eagle layer '%s' (%d), " - "converted to Dwgs.User layer" ), - eagle_layer_name( aEagleLayer ), - aEagleLayer ) ); - - kiLayer = Dwgs_User; - break; + if( copperLayer >= 0 ) + copperLayers[copperLayer] = true; } + + return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true }; } - return PCB_LAYER_ID( kiLayer ); + int kiLayer = UNSELECTED_LAYER; + bool required = false; + LSET permittedLayers; + + permittedLayers.set(); + + // translate non-copper eagle layer to pcbnew layer + switch( aEagleLayer ) + { + // Eagle says "Dimension" layer, but it's for board perimeter + 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::BPLACE: + kiLayer = B_SilkS; + break; + case EAGLE_LAYER::TNAMES: + kiLayer = F_SilkS; + break; + case EAGLE_LAYER::BNAMES: + kiLayer = B_SilkS; + break; + case EAGLE_LAYER::TVALUES: + kiLayer = F_Fab; + break; + case EAGLE_LAYER::BVALUES: + 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. + // This is an area slightly smaller than the PAD/SMD copper area. + // Carry those visual aids into the MODULE on the fabrication layer, + // not silkscreen. This is perhaps not perfect, but there is not a lot + // of other suitable paired layers + case EAGLE_LAYER::TDOCU: + kiLayer = F_Fab; + break; + case EAGLE_LAYER::BDOCU: + kiLayer = B_Fab; + break; + + // these layers are defined as user layers. put them on ECO layers + case EAGLE_LAYER::USERLAYER1: + 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 + case EAGLE_LAYER::UNROUTED: + kiLayer = Dwgs_User; + break; + + case EAGLE_LAYER::TKEEPOUT: + kiLayer = F_CrtYd; + break; + case EAGLE_LAYER::BKEEPOUT: + kiLayer = B_CrtYd; + break; + + case EAGLE_LAYER::MILLING: + case EAGLE_LAYER::TTEST: + case EAGLE_LAYER::BTEST: + case EAGLE_LAYER::HOLES: + default: + kiLayer = UNSELECTED_LAYER; + break; + } + + 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() { if( m_props ) diff --git a/pcbnew/plugins/eagle/eagle_plugin.h b/pcbnew/plugins/eagle/eagle_plugin.h index ac9a9cf834..19b07f3add 100644 --- a/pcbnew/plugins/eagle/eagle_plugin.h +++ b/pcbnew/plugins/eagle/eagle_plugin.h @@ -28,8 +28,10 @@ #include #include #include +#include #include +#include #include class D_PAD; @@ -115,7 +117,7 @@ struct ERULES * works with Eagle 6.x XML board files and footprints to implement the * Pcbnew PLUGIN API, or a portion of it. */ -class EAGLE_PLUGIN : public PLUGIN +class EAGLE_PLUGIN : public PLUGIN, public LAYER_REMAPPABLE_PLUGIN { public: @@ -164,12 +166,27 @@ public: 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 DefaultLayerMappingCallback( + const std::vector& aInputLayerDescriptionVector ); + private: typedef std::vector ELAYERS; typedef ELAYERS::const_iterator EITER; int m_cu_map[17]; ///< map eagle to kicad, cu layers only. std::map m_eagleLayers; ///< Eagle layers data stored by the layer number + std::map m_eagleLayersIds; ///< Eagle layers id stored by the layer name + std::map m_layer_map; ///< Mapping of Eagle layers to KiCAD layers ERULES* m_rules; ///< Eagle design rules. 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 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. 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 defaultKicadLayer( int aEagleLayer ) const; + /// Get Eagle layer name by its number 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. void cacheLib( const wxString& aLibraryPath );