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