Gerbview: Add ability to sort layers by file extension

This commit is contained in:
Mike Williams 2021-08-02 16:00:34 -04:00
parent d827f06a74
commit a9379ecf39
6 changed files with 277 additions and 7 deletions

View File

@ -200,6 +200,185 @@ const wxString GERBER_FILE_IMAGE_LIST::GetDisplayName( int aIdx, bool aNameOnly,
}
struct GERBER_ORDER
{
std::string m_FilenameMask;
GERBER_ORDER_ENUM m_Order;
};
// clang-format off
static struct GERBER_ORDER gerberFileExtensionOrder[] =
{
{ ".GM1", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".GM3", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".GBR", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".DIM", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".MIL", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".GML", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ "EDGE.CUTS", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".FAB", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
{ ".GKO", GERBER_ORDER_ENUM::GERBER_KEEP_OUT },
{ ".GM?", GERBER_ORDER_ENUM::GERBER_MECHANICAL },
{ ".GM??", GERBER_ORDER_ENUM::GERBER_MECHANICAL },
{ ".TXT", GERBER_ORDER_ENUM::GERBER_DRILL },
{ ".XLN", GERBER_ORDER_ENUM::GERBER_DRILL },
{ ".TAP", GERBER_ORDER_ENUM::GERBER_DRILL },
{ ".DRD", GERBER_ORDER_ENUM::GERBER_DRILL },
{ ".NC", GERBER_ORDER_ENUM::GERBER_DRILL },
{ ".XNC", GERBER_ORDER_ENUM::GERBER_DRILL },
{ ".GTP", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
{ ".CRC", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
{ ".TSP", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
{ "F.PASTE", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
{ ".SPT", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
{ "PT.PHO", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
{ ".GTO", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
{ ".PLC", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
{ ".TSK", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
{ "F.SILKS", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
{ ".SST", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
{ "ST.PHO", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
{ ".GTS", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
{ ".STC", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
{ ".TSM", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
{ "F.MASK", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
{ ".SMT", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
{ "MT.PHO", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
{ ".GTL", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ ".CMP", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ ".TOP", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ "F.CU", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ "L1.PHO", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ ".PHD", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ ".ART", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
{ ".GBL", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
{ ".SOL", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
{ ".BOT", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
{ "B.CU", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
{ ".BOT", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
{ ".GBS", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
{ ".STS", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
{ ".BSM", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
{ "B.MASK", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
{ ".SMB", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
{ "MB.PHO", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
{ ".GBO", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
{ ".PLS", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
{ ".BSK", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
{ "B.SILK", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
{ ".SSB", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
{ "SB.PHO", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
{ ".GBP", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
{ ".CRS", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
{ ".BSP", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
{ "B.PASTE", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
{ ".SMB", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
{ "MB.PHO", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
// EAGLE CAD file to explicitly ignore that can match some other
// layers otherwise
{ ".GPI", GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN },
// Inner copper layers need to come last so the wildcard
// number matching doesn't pick up other specific layer names.
{ ".GI?", GERBER_ORDER_ENUM::GERBER_INNER },
{ ".GI??", GERBER_ORDER_ENUM::GERBER_INNER },
{ ".G?", GERBER_ORDER_ENUM::GERBER_INNER },
{ ".G??", GERBER_ORDER_ENUM::GERBER_INNER },
{ ".G?L", GERBER_ORDER_ENUM::GERBER_INNER },
{ ".G??L", GERBER_ORDER_ENUM::GERBER_INNER },
};
// clang-format on
void GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( const wxString& filename,
enum GERBER_ORDER_ENUM& order,
wxString& matchedExtension )
{
order = GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN;
matchedExtension = "";
for( struct GERBER_ORDER o : gerberFileExtensionOrder )
{
wxString ext = filename.Right( o.m_FilenameMask.length() ).Upper();
if( ext.Matches( o.m_FilenameMask ) )
{
order = o.m_Order;
matchedExtension = ext;
return;
}
}
}
static bool sortFileExtension( const GERBER_FILE_IMAGE* const& ref,
const GERBER_FILE_IMAGE* const& test )
{
// Do not change order: no criteria to sort items
if( !ref && !test )
return false;
// Not used: ref ordered after
if( !ref || !ref->m_InUse )
return false;
// Not used: ref ordered before
if( !test || !test->m_InUse )
return true;
enum GERBER_ORDER_ENUM ref_layer;
enum GERBER_ORDER_ENUM test_layer;
wxString ref_extension;
wxString test_extension;
GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( ref->m_FileName, ref_layer,
ref_extension );
GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( test->m_FileName, test_layer,
test_extension );
// Inner layers have a numeric code that we can compare against
if( ref_layer == GERBER_ORDER_ENUM::GERBER_INNER
&& test_layer == GERBER_ORDER_ENUM::GERBER_INNER )
{
unsigned long ref_layer_number = 0;
unsigned long test_layer_number = 0;
// Strip extensions down to only the numbers in it. Later conversion to int will
// automatically skip the spaces
for( wxString::iterator it = ref_extension.begin(); it != ref_extension.end(); ++it )
{
if( !isdigit( *it ) )
*it = ' ';
}
for( wxString::iterator it = test_extension.begin(); it != test_extension.end(); ++it )
{
if( !isdigit( *it ) )
*it = ' ';
}
ref_extension.ToULong( &ref_layer_number );
test_extension.ToULong( &test_layer_number );
return ref_layer_number < test_layer_number;
}
return (int) ref_layer < (int) test_layer;
}
// Helper function, for std::sort.
// Sort loaded images by Z order priority, if they have the X2 FileFormat info
@ -231,9 +410,10 @@ static bool sortZorder( const GERBER_FILE_IMAGE* const& ref, const GERBER_FILE_I
}
std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SortImagesByZOrder()
std::unordered_map<int, int>
GERBER_FILE_IMAGE_LIST::SortImagesByFunction( LayerSortFunction sortFunction )
{
std::sort( m_GERBER_List.begin(), m_GERBER_List.end(), sortZorder );
std::sort( m_GERBER_List.begin(), m_GERBER_List.end(), sortFunction );
// The image order has changed.
// Graphic layer numbering must be updated to match the widgets layer order
@ -254,3 +434,15 @@ std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SortImagesByZOrder()
return tab_lyr;
}
std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SortImagesByFileExtension()
{
return SortImagesByFunction( sortFileExtension );
}
std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SortImagesByZOrder()
{
return SortImagesByFunction( sortZorder );
}

View File

@ -32,6 +32,26 @@
#include <gerber_draw_item.h>
#include <am_primitive.h>
enum class GERBER_ORDER_ENUM : int
{
GERBER_TOP_PASTE = 0,
GERBER_TOP_SILK_SCREEN,
GERBER_TOP_SOLDER_MASK,
GERBER_TOP_COPPER,
GERBER_INNER,
GERBER_BOTTOM_COPPER,
GERBER_BOTTOM_SOLDER_MASK,
GERBER_BOTTOM_SILK_SCREEN,
GERBER_BOTTOM_PASTE,
GERBER_KEEP_OUT,
GERBER_MECHANICAL,
GERBER_BOARD_OUTLINE,
GERBER_DRILL,
GERBER_LAYER_UNKNOWN,
};
/* gerber files have different parameters to define units and how items must be plotted.
* some are for the entire file, and other can change along a file.
* In Gerber world:
@ -53,6 +73,9 @@
* But a GERBER_FILE_IMAGE can use more than one GERBER_LAYER.
*/
typedef bool( LayerSortFunction )( const GERBER_FILE_IMAGE* const& ref,
const GERBER_FILE_IMAGE* const& test );
class GERBER_FILE_IMAGE;
/**
@ -66,6 +89,19 @@ public:
GERBER_FILE_IMAGE_LIST();
~GERBER_FILE_IMAGE_LIST();
/**
* @brief Utility function to guess which PCB layer of a gerber/drill file corresponds to based
* on its file extension.
*
* Supports many CAD program file extension patterns. Detects gerbers, drills, edge layers, etc.
*
* @param filename Filename to check against the extension matching table.
* @param order Returns the order that this layer should be placed in a set of layers.
* @param matchedExtension The actual extension pattern that this filename matched.
*/
static void GetGerberLayerFromFilename( const wxString& filename, enum GERBER_ORDER_ENUM& order,
wxString& matchedExtension );
static GERBER_FILE_IMAGE_LIST& GetImagesList();
GERBER_FILE_IMAGE* GetGbrImage( int aIdx );
@ -113,6 +149,20 @@ public:
*/
const wxString GetDisplayName( int aIdx, bool aNameOnly = false, bool aFullName = false );
/**
* Sort loaded images by file extension matching
*
* @return a mapping of old to new layer index
*/
std::unordered_map<int, int> SortImagesByFunction( LayerSortFunction sortFunction );
/**
* Sort loaded images by file extension matching
*
* @return a mapping of old to new layer index
*/
std::unordered_map<int, int> SortImagesByFileExtension();
/**
* Sort loaded images by Z order priority, if they have the X2 FileFormat info
* (SortImagesByZOrder updates the graphic layer of these items)

View File

@ -499,10 +499,20 @@ void GERBVIEW_FRAME::syncLayerBox( bool aRebuildLayerBox )
}
void GERBVIEW_FRAME::SortLayersByFileExtension()
{
RemapLayers( GetImagesList()->SortImagesByFileExtension() );
}
void GERBVIEW_FRAME::SortLayersByX2Attributes()
{
auto remapping = GetImagesList()->SortImagesByZOrder();
RemapLayers( GetImagesList()->SortImagesByZOrder() );
}
void GERBVIEW_FRAME::RemapLayers( std::unordered_map<int, int> remapping )
{
ReFillLayerWidget();
syncLayerBox( true );

View File

@ -349,8 +349,17 @@ public:
bool Clear_DrawLayers( bool query );
void Erase_Current_DrawLayer( bool query );
void SortLayersByFileExtension();
void SortLayersByX2Attributes();
/**
* Takes a layer remapping and reorders the layers.
*
* @param remapping A map of old layer number -> new layer number mapping.
* Generally sourced from the SortImagesBy* functions.
*/
void RemapLayers( std::unordered_map<int, int> remapping );
/**
* Update each layers' differential option. Needed when diff mode changes or the active layer
* changes (due to changing rendering order) which matters for diff mode but not otherwise.

View File

@ -134,7 +134,11 @@ void GERBER_LAYER_WIDGET::AddRightClickMenuItems( wxMenu* aMenu )
KiBitmap( BITMAPS::show_no_layers ) );
aMenu->AppendSeparator();
AddMenuItem( aMenu, ID_SORT_GBR_LAYERS, _( "Sort Layers if X2 Mode" ),
AddMenuItem( aMenu, ID_SORT_GBR_LAYERS_X2, _( "Sort Layers if X2 Mode" ),
KiBitmap( BITMAPS::reload ) );
AddMenuItem( aMenu, ID_SORT_GBR_LAYERS_FILE_EXT, _( "Sort Layers by File Extension" ),
KiBitmap( BITMAPS::reload ) );
}
@ -189,9 +193,13 @@ void GERBER_LAYER_WIDGET::onPopupSelection( wxCommandEvent& event )
m_frame->GetCanvas()->Refresh();
break;
case ID_SORT_GBR_LAYERS:
case ID_SORT_GBR_LAYERS_X2:
m_frame->SortLayersByX2Attributes();
break;
case ID_SORT_GBR_LAYERS_FILE_EXT:
m_frame->SortLayersByFileExtension();
break;
}
}

View File

@ -95,8 +95,9 @@ protected:
ID_SHOW_NO_LAYERS,
ID_SHOW_NO_LAYERS_BUT_ACTIVE,
ID_ALWAYS_SHOW_NO_LAYERS_BUT_ACTIVE,
ID_SORT_GBR_LAYERS,
ID_LAYER_MANAGER_END = ID_SORT_GBR_LAYERS,
ID_SORT_GBR_LAYERS_X2,
ID_SORT_GBR_LAYERS_FILE_EXT,
ID_LAYER_MANAGER_END = ID_SORT_GBR_LAYERS_FILE_EXT,
};
private: