CLI: Allow controlling layer order for multilayer plots

This commit is contained in:
Jon Evans 2023-09-28 10:59:11 -04:00
parent dc47d251f5
commit 0b136ae0df
14 changed files with 63 additions and 45 deletions

View File

@ -47,7 +47,7 @@ public:
bool m_plotBorderTitleBlocks; bool m_plotBorderTitleBlocks;
DXF_UNITS m_dxfUnits; DXF_UNITS m_dxfUnits;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
}; };
#endif #endif

View File

@ -49,7 +49,7 @@ public:
int m_precision; int m_precision;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
}; };
#endif #endif

View File

@ -44,7 +44,7 @@ public:
bool m_plotRefDes; bool m_plotRefDes;
bool m_plotBorderTitleBlocks; bool m_plotBorderTitleBlocks;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
// How holes in pads/vias are plotted: // How holes in pads/vias are plotted:
// 0 = no hole, 1 = small shape, 2 = actual shape // 0 = no hole, 1 = small shape, 2 = actual shape

View File

@ -43,7 +43,7 @@ public:
bool m_plotDrawingSheet; bool m_plotDrawingSheet;
int m_pageSizeMode; int m_pageSizeMode;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
// How holes in pads/vias are plotted: // How holes in pads/vias are plotted:
// 0 = no hole, 1 = small shape, 2 = actual shape // 0 = no hole, 1 = small shape, 2 = actual shape

View File

@ -39,7 +39,7 @@ public:
wxString m_colorTheme; wxString m_colorTheme;
bool m_blackAndWhite; bool m_blackAndWhite;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
}; };
#endif #endif

View File

@ -74,6 +74,13 @@ LSET::LSET( unsigned aIdCount, int aFirst, ... ) :
} }
LSET::LSET( const LSEQ& aSeq )
{
for( PCB_LAYER_ID layer : aSeq )
set( layer );
}
/** /**
* NOTE: These names must not be translated or changed. They are used as tokens in the board * NOTE: These names must not be translated or changed. They are used as tokens in the board
* file format because the ordinal value of the PCB_LAYER_ID enum was not stable over time. * file format because the ordinal value of the PCB_LAYER_ID enum was not stable over time.
@ -475,23 +482,16 @@ LSEQ LSET::Seq() const
LSEQ LSET::SeqStackupBottom2Top() const LSEQ LSET::SeqStackupBottom2Top() const
{ {
// bottom-to-top stack-up layers // bottom-to-top stack-up layers
// Note that the bottom technical layers are flipped so that when plotting a bottom-side view,
// they appear in the correct sequence.
static const PCB_LAYER_ID sequence[] = { static const PCB_LAYER_ID sequence[] = {
User_9,
User_8,
User_7,
User_6,
User_5,
User_4,
User_3,
User_2,
User_1,
B_Fab,
B_CrtYd,
B_Adhes,
B_SilkS,
B_Paste,
B_Mask,
B_Cu, B_Cu,
B_Mask,
B_Paste,
B_SilkS,
B_Adhes,
B_CrtYd,
B_Fab,
In30_Cu, In30_Cu,
In29_Cu, In29_Cu,
In28_Cu, In28_Cu,
@ -533,6 +533,15 @@ LSEQ LSET::SeqStackupBottom2Top() const
Cmts_User, Cmts_User,
Eco1_User, Eco1_User,
Eco2_User, Eco2_User,
User_1,
User_2,
User_3,
User_4,
User_5,
User_6,
User_7,
User_8,
User_9,
Margin, Margin,
Edge_Cuts, Edge_Cuts,
}; };

View File

@ -524,6 +524,10 @@ public:
BASE_SEQ( aStart, aEnd ), m_index( 0 ) BASE_SEQ( aStart, aEnd ), m_index( 0 )
{} {}
LSEQ( std::initializer_list<PCB_LAYER_ID> aLayers ) :
BASE_SEQ( aLayers )
{}
void Rewind() { m_index = 0; } void Rewind() { m_index = 0; }
void operator ++ () { ++m_index; } // returns nothing, used in simple statements only. void operator ++ () { ++m_index; } // returns nothing, used in simple statements only.
@ -613,6 +617,8 @@ public:
*/ */
LSET( unsigned aIdCount, int aFirst, ... ); // args chosen to prevent LSET( int ) from compiling LSET( unsigned aIdCount, int aFirst, ... ); // args chosen to prevent LSET( int ) from compiling
LSET( const LSEQ& aSeq );
/** /**
* See if the layer set contains a PCB layer. * See if the layer set contains a PCB layer.
* *

View File

@ -77,10 +77,10 @@ int CLI::FP_EXPORT_SVG_COMMAND::doPerform( KIWAY& aKiway )
svgJob->m_colorTheme = From_UTF8( m_argParser.get<std::string>( ARG_THEME ).c_str() ); svgJob->m_colorTheme = From_UTF8( m_argParser.get<std::string>( ARG_THEME ).c_str() );
if( m_selectedLayers.count() > 0 ) if( !m_selectedLayers.empty() )
svgJob->m_printMaskLayer = m_selectedLayers; svgJob->m_printMaskLayer = m_selectedLayers;
else else
svgJob->m_printMaskLayer = LSET::AllLayersMask(); svgJob->m_printMaskLayer = LSET::AllLayersMask().SeqStackupBottom2Top();
int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, svgJob.get() ); int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, svgJob.get() );

View File

@ -72,13 +72,12 @@ CLI::PCB_EXPORT_BASE_COMMAND::PCB_EXPORT_BASE_COMMAND( const std::string& aName,
} }
LSET CLI::PCB_EXPORT_BASE_COMMAND::convertLayerStringList( wxString& aLayerString, bool& aLayerArgSet ) const LSEQ CLI::PCB_EXPORT_BASE_COMMAND::convertLayerStringList( wxString& aLayerString, bool& aLayerArgSet ) const
{ {
LSET layerMask; LSEQ layerMask;
if( !aLayerString.IsEmpty() ) if( !aLayerString.IsEmpty() )
{ {
layerMask.reset();
wxStringTokenizer layerTokens( aLayerString, "," ); wxStringTokenizer layerTokens( aLayerString, "," );
while( layerTokens.HasMoreTokens() ) while( layerTokens.HasMoreTokens() )
@ -88,13 +87,17 @@ LSET CLI::PCB_EXPORT_BASE_COMMAND::convertLayerStringList( wxString& aLayerStrin
// Search for a layer name in canonical layer name used in .kicad_pcb files: // Search for a layer name in canonical layer name used in .kicad_pcb files:
if( m_layerMasks.count( token ) ) if( m_layerMasks.count( token ) )
{ {
layerMask |= m_layerMasks.at(token); for( PCB_LAYER_ID layer : m_layerMasks.at( token ).Seq() )
layerMask.push_back( layer );
aLayerArgSet = true; aLayerArgSet = true;
} }
// Search for a layer name in canonical layer name used in GUI (not translated): // Search for a layer name in canonical layer name used in GUI (not translated):
else if( m_layerGuiMasks.count( token ) ) else if( m_layerGuiMasks.count( token ) )
{ {
layerMask |= m_layerGuiMasks.at(token); for( PCB_LAYER_ID layer : m_layerGuiMasks.at( token ).Seq() )
layerMask.push_back( layer );
aLayerArgSet = true; aLayerArgSet = true;
} }
else else
@ -128,8 +131,9 @@ int CLI::PCB_EXPORT_BASE_COMMAND::doPerform( KIWAY& aKiway )
{ {
wxString layers = From_UTF8( m_argParser.get<std::string>( ARG_LAYERS ).c_str() ); wxString layers = From_UTF8( m_argParser.get<std::string>( ARG_LAYERS ).c_str() );
LSET layerMask = convertLayerStringList( layers, m_selectedLayersSet ); LSEQ layerMask = convertLayerStringList( layers, m_selectedLayersSet );
if( m_requireLayers && layerMask.Seq().size() < 1 )
if( m_requireLayers && layerMask.size() < 1 )
{ {
wxFprintf( stderr, _( "At least one layer must be specified\n" ) ); wxFprintf( stderr, _( "At least one layer must be specified\n" ) );
return EXIT_CODES::ERR_ARGS; return EXIT_CODES::ERR_ARGS;

View File

@ -46,7 +46,7 @@ struct PCB_EXPORT_BASE_COMMAND : public COMMAND
protected: protected:
int doPerform( KIWAY& aKiway ) override; int doPerform( KIWAY& aKiway ) override;
LSET convertLayerStringList( wxString& aLayerString, bool& aLayerArgSet ) const; LSEQ convertLayerStringList( wxString& aLayerString, bool& aLayerArgSet ) const;
void addLayerArg( bool aRequire ); void addLayerArg( bool aRequire );
// The list of canonical layer names used in .kicad_pcb files: // The list of canonical layer names used in .kicad_pcb files:
@ -55,7 +55,7 @@ protected:
// The list of canonical layer names used in GUI (not translated): // The list of canonical layer names used in GUI (not translated):
std::map<std::string, LSET> m_layerGuiMasks; std::map<std::string, LSET> m_layerGuiMasks;
LSET m_selectedLayers; LSEQ m_selectedLayers;
bool m_selectedLayersSet; bool m_selectedLayersSet;
bool m_hasLayerArg; bool m_hasLayerArg;

View File

@ -50,7 +50,7 @@ public:
private: private:
BOARD* m_board; BOARD* m_board;
PCB_EDIT_FRAME* m_parent; PCB_EDIT_FRAME* m_parent;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
// the list of existing board layers in wxCheckListBox, with the // the list of existing board layers in wxCheckListBox, with the
// board layers id: // board layers id:
std::pair<wxCheckListBox*, int> m_boxSelectLayer[PCB_LAYER_ID_COUNT]; std::pair<wxCheckListBox*, int> m_boxSelectLayer[PCB_LAYER_ID_COUNT];
@ -326,10 +326,10 @@ void DIALOG_EXPORT_SVG::ExportSVGFile( bool aOnlyOneFile )
BuildPlotFileName( &fn, outputDir.GetPath(), suffix, SVGFileExtension ); BuildPlotFileName( &fn, outputDir.GetPath(), suffix, SVGFileExtension );
wxString svgPath = fn.GetFullPath(); wxString svgPath = fn.GetFullPath();
m_printMaskLayer = aOnlyOneFile ? all_selected : LSET( layer ); m_printMaskLayer = aOnlyOneFile ? all_selected.SeqStackupBottom2Top() : LSEQ( { layer } );
if( m_checkboxEdgesOnAllPages->GetValue() ) if( m_checkboxEdgesOnAllPages->GetValue() )
m_printMaskLayer.set( Edge_Cuts ); m_printMaskLayer.push_back( Edge_Cuts );
svgPlotOptions.m_outputFile = svgPath; svgPlotOptions.m_outputFile = svgPath;
svgPlotOptions.m_printMaskLayer = m_printMaskLayer; svgPlotOptions.m_printMaskLayer = m_printMaskLayer;

View File

@ -35,7 +35,7 @@ bool EXPORT_SVG::Plot( BOARD* aBoard, const PCB_PLOT_SVG_OPTIONS& aSvgPlotOption
plot_opts.SetPlotFrameRef( aSvgPlotOptions.m_plotFrame ); plot_opts.SetPlotFrameRef( aSvgPlotOptions.m_plotFrame );
// Adding drill marks, for copper layers // Adding drill marks, for copper layers
if( ( aSvgPlotOptions.m_printMaskLayer & LSET::AllCuMask() ).any() ) if( ( LSET( aSvgPlotOptions.m_printMaskLayer ) & LSET::AllCuMask() ).any() )
{ {
switch( aSvgPlotOptions.m_drillShapeOption ) switch( aSvgPlotOptions.m_drillShapeOption )
{ {
@ -97,7 +97,7 @@ bool EXPORT_SVG::Plot( BOARD* aBoard, const PCB_PLOT_SVG_OPTIONS& aSvgPlotOption
if( plotter ) if( plotter )
{ {
plotter->SetColorMode( !aSvgPlotOptions.m_blackAndWhite ); plotter->SetColorMode( !aSvgPlotOptions.m_blackAndWhite );
PlotBoardLayers( aBoard, plotter, aSvgPlotOptions.m_printMaskLayer.SeqStackupBottom2Top(), PlotBoardLayers( aBoard, plotter, aSvgPlotOptions.m_printMaskLayer,
plot_opts ); plot_opts );
plotter->EndPlot(); plotter->EndPlot();
} }

View File

@ -33,7 +33,7 @@ struct PCB_PLOT_SVG_OPTIONS
int m_pageSizeMode; int m_pageSizeMode;
LSET m_printMaskLayer; LSEQ m_printMaskLayer;
// How holes in pads/vias are plotted: // How holes in pads/vias are plotted:
// 0 = no hole, 1 = small shape, 2 = actual shape // 0 = no hole, 1 = small shape, 2 = actual shape

View File

@ -263,7 +263,7 @@ int PCBNEW_JOBS_HANDLER::JobExportDxf( JOB* aJob )
if( plotter ) if( plotter )
{ {
PlotBoardLayers( brd, plotter, aDxfJob->m_printMaskLayer.SeqStackupBottom2Top(), plotOpts ); PlotBoardLayers( brd, plotter, aDxfJob->m_printMaskLayer, plotOpts );
plotter->EndPlot(); plotter->EndPlot();
} }
@ -332,7 +332,7 @@ int PCBNEW_JOBS_HANDLER::JobExportPdf( JOB* aJob )
if( plotter ) if( plotter )
{ {
PlotBoardLayers( brd, plotter, aPdfJob->m_printMaskLayer.SeqStackupBottom2Top(), plotOpts ); PlotBoardLayers( brd, plotter, aPdfJob->m_printMaskLayer, plotOpts );
PlotInteractiveLayer( brd, plotter, plotOpts ); PlotInteractiveLayer( brd, plotter, plotOpts );
plotter->EndPlot(); plotter->EndPlot();
} }
@ -365,20 +365,20 @@ int PCBNEW_JOBS_HANDLER::JobExportGerbers( JOB* aJob )
if( aGerberJob->m_useBoardPlotParams ) if( aGerberJob->m_useBoardPlotParams )
{ {
aGerberJob->m_printMaskLayer = boardPlotOptions.GetLayerSelection(); aGerberJob->m_printMaskLayer = boardPlotOptions.GetLayerSelection().SeqStackupBottom2Top();
aGerberJob->m_layersIncludeOnAll = boardPlotOptions.GetPlotOnAllLayersSelection(); aGerberJob->m_layersIncludeOnAll = boardPlotOptions.GetPlotOnAllLayersSelection();
} }
else else
{ {
// default to the board enabled layers // default to the board enabled layers
if( aGerberJob->m_printMaskLayer == 0 ) if( aGerberJob->m_printMaskLayer == 0 )
aGerberJob->m_printMaskLayer = brd->GetEnabledLayers(); aGerberJob->m_printMaskLayer = brd->GetEnabledLayers().SeqStackupBottom2Top();
if( aGerberJob->m_layersIncludeOnAllSet ) if( aGerberJob->m_layersIncludeOnAllSet )
aGerberJob->m_layersIncludeOnAll = plotOnAllLayersSelection; aGerberJob->m_layersIncludeOnAll = plotOnAllLayersSelection;
} }
for( LSEQ seq = aGerberJob->m_printMaskLayer.UIOrder(); seq; ++seq ) for( LSEQ seq = LSET( aGerberJob->m_printMaskLayer ).UIOrder(); seq; ++seq )
{ {
LSEQ plotSequence; LSEQ plotSequence;
@ -499,13 +499,12 @@ int PCBNEW_JOBS_HANDLER::JobExportGerber( JOB* aJob )
// We are feeding it one layer at the start here to silence a logic check // We are feeding it one layer at the start here to silence a logic check
GERBER_PLOTTER* plotter = (GERBER_PLOTTER*) StartPlotBoard( GERBER_PLOTTER* plotter = (GERBER_PLOTTER*) StartPlotBoard(
brd, &plotOpts, aGerberJob->m_printMaskLayer.Seq().front(), aGerberJob->m_outputFile, brd, &plotOpts, aGerberJob->m_printMaskLayer.front(), aGerberJob->m_outputFile,
wxEmptyString, wxEmptyString ); wxEmptyString, wxEmptyString );
if( plotter ) if( plotter )
{ {
PlotBoardLayers( brd, plotter, aGerberJob->m_printMaskLayer.SeqStackupBottom2Top(), PlotBoardLayers( brd, plotter, aGerberJob->m_printMaskLayer, plotOpts );
plotOpts );
plotter->EndPlot(); plotter->EndPlot();
} }
else else