From 0b136ae0df696908b393b00e8c392cd20e1ff7a7 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Thu, 28 Sep 2023 10:59:11 -0400 Subject: [PATCH] CLI: Allow controlling layer order for multilayer plots --- common/jobs/job_export_pcb_dxf.h | 2 +- common/jobs/job_export_pcb_gerber.h | 2 +- common/jobs/job_export_pcb_pdf.h | 2 +- common/jobs/job_export_pcb_svg.h | 2 +- common/jobs/job_fp_export_svg.h | 2 +- common/lset.cpp | 39 ++++++++++++++++----------- include/layer_ids.h | 6 +++++ kicad/cli/command_fp_export_svg.cpp | 4 +-- kicad/cli/command_pcb_export_base.cpp | 18 ++++++++----- kicad/cli/command_pcb_export_base.h | 4 +-- pcbnew/dialogs/dialog_export_svg.cpp | 6 ++--- pcbnew/exporters/export_svg.cpp | 4 +-- pcbnew/exporters/export_svg.h | 2 +- pcbnew/pcbnew_jobs_handler.cpp | 15 +++++------ 14 files changed, 63 insertions(+), 45 deletions(-) diff --git a/common/jobs/job_export_pcb_dxf.h b/common/jobs/job_export_pcb_dxf.h index cc903e17d7..13a95cd543 100644 --- a/common/jobs/job_export_pcb_dxf.h +++ b/common/jobs/job_export_pcb_dxf.h @@ -47,7 +47,7 @@ public: bool m_plotBorderTitleBlocks; DXF_UNITS m_dxfUnits; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; }; #endif diff --git a/common/jobs/job_export_pcb_gerber.h b/common/jobs/job_export_pcb_gerber.h index 9ddf3b6155..8fbb777294 100644 --- a/common/jobs/job_export_pcb_gerber.h +++ b/common/jobs/job_export_pcb_gerber.h @@ -49,7 +49,7 @@ public: int m_precision; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; }; #endif diff --git a/common/jobs/job_export_pcb_pdf.h b/common/jobs/job_export_pcb_pdf.h index e9c51e39be..339d9d8ae2 100644 --- a/common/jobs/job_export_pcb_pdf.h +++ b/common/jobs/job_export_pcb_pdf.h @@ -44,7 +44,7 @@ public: bool m_plotRefDes; bool m_plotBorderTitleBlocks; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; // How holes in pads/vias are plotted: // 0 = no hole, 1 = small shape, 2 = actual shape diff --git a/common/jobs/job_export_pcb_svg.h b/common/jobs/job_export_pcb_svg.h index 4ac99b5352..bc11f821fc 100644 --- a/common/jobs/job_export_pcb_svg.h +++ b/common/jobs/job_export_pcb_svg.h @@ -43,7 +43,7 @@ public: bool m_plotDrawingSheet; int m_pageSizeMode; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; // How holes in pads/vias are plotted: // 0 = no hole, 1 = small shape, 2 = actual shape diff --git a/common/jobs/job_fp_export_svg.h b/common/jobs/job_fp_export_svg.h index 90a36d06e5..7a58cbd5da 100644 --- a/common/jobs/job_fp_export_svg.h +++ b/common/jobs/job_fp_export_svg.h @@ -39,7 +39,7 @@ public: wxString m_colorTheme; bool m_blackAndWhite; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; }; #endif \ No newline at end of file diff --git a/common/lset.cpp b/common/lset.cpp index ec86155d94..3bddba0966 100644 --- a/common/lset.cpp +++ b/common/lset.cpp @@ -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 * 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 { // 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[] = { - 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_Mask, + B_Paste, + B_SilkS, + B_Adhes, + B_CrtYd, + B_Fab, In30_Cu, In29_Cu, In28_Cu, @@ -533,6 +533,15 @@ LSEQ LSET::SeqStackupBottom2Top() const Cmts_User, Eco1_User, Eco2_User, + User_1, + User_2, + User_3, + User_4, + User_5, + User_6, + User_7, + User_8, + User_9, Margin, Edge_Cuts, }; diff --git a/include/layer_ids.h b/include/layer_ids.h index c3921cf0ea..99af33a04f 100644 --- a/include/layer_ids.h +++ b/include/layer_ids.h @@ -524,6 +524,10 @@ public: BASE_SEQ( aStart, aEnd ), m_index( 0 ) {} + LSEQ( std::initializer_list aLayers ) : + BASE_SEQ( aLayers ) + {} + void Rewind() { m_index = 0; } 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( const LSEQ& aSeq ); + /** * See if the layer set contains a PCB layer. * diff --git a/kicad/cli/command_fp_export_svg.cpp b/kicad/cli/command_fp_export_svg.cpp index 975cff74ab..8b8fe2aeac 100644 --- a/kicad/cli/command_fp_export_svg.cpp +++ b/kicad/cli/command_fp_export_svg.cpp @@ -77,10 +77,10 @@ int CLI::FP_EXPORT_SVG_COMMAND::doPerform( KIWAY& aKiway ) svgJob->m_colorTheme = From_UTF8( m_argParser.get( ARG_THEME ).c_str() ); - if( m_selectedLayers.count() > 0 ) + if( !m_selectedLayers.empty() ) svgJob->m_printMaskLayer = m_selectedLayers; else - svgJob->m_printMaskLayer = LSET::AllLayersMask(); + svgJob->m_printMaskLayer = LSET::AllLayersMask().SeqStackupBottom2Top(); int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, svgJob.get() ); diff --git a/kicad/cli/command_pcb_export_base.cpp b/kicad/cli/command_pcb_export_base.cpp index 788753c0bd..640b60675a 100644 --- a/kicad/cli/command_pcb_export_base.cpp +++ b/kicad/cli/command_pcb_export_base.cpp @@ -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() ) { - layerMask.reset(); wxStringTokenizer layerTokens( aLayerString, "," ); 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: 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; } // Search for a layer name in canonical layer name used in GUI (not translated): 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; } else @@ -128,8 +131,9 @@ int CLI::PCB_EXPORT_BASE_COMMAND::doPerform( KIWAY& aKiway ) { wxString layers = From_UTF8( m_argParser.get( ARG_LAYERS ).c_str() ); - LSET layerMask = convertLayerStringList( layers, m_selectedLayersSet ); - if( m_requireLayers && layerMask.Seq().size() < 1 ) + LSEQ layerMask = convertLayerStringList( layers, m_selectedLayersSet ); + + if( m_requireLayers && layerMask.size() < 1 ) { wxFprintf( stderr, _( "At least one layer must be specified\n" ) ); return EXIT_CODES::ERR_ARGS; diff --git a/kicad/cli/command_pcb_export_base.h b/kicad/cli/command_pcb_export_base.h index 6f71dd11f9..6232ff13f2 100644 --- a/kicad/cli/command_pcb_export_base.h +++ b/kicad/cli/command_pcb_export_base.h @@ -46,7 +46,7 @@ struct PCB_EXPORT_BASE_COMMAND : public COMMAND protected: int doPerform( KIWAY& aKiway ) override; - LSET convertLayerStringList( wxString& aLayerString, bool& aLayerArgSet ) const; + LSEQ convertLayerStringList( wxString& aLayerString, bool& aLayerArgSet ) const; void addLayerArg( bool aRequire ); // 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): std::map m_layerGuiMasks; - LSET m_selectedLayers; + LSEQ m_selectedLayers; bool m_selectedLayersSet; bool m_hasLayerArg; diff --git a/pcbnew/dialogs/dialog_export_svg.cpp b/pcbnew/dialogs/dialog_export_svg.cpp index c9e28c0317..60355659b8 100644 --- a/pcbnew/dialogs/dialog_export_svg.cpp +++ b/pcbnew/dialogs/dialog_export_svg.cpp @@ -50,7 +50,7 @@ public: private: BOARD* m_board; PCB_EDIT_FRAME* m_parent; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; // the list of existing board layers in wxCheckListBox, with the // board layers id: std::pair m_boxSelectLayer[PCB_LAYER_ID_COUNT]; @@ -326,10 +326,10 @@ void DIALOG_EXPORT_SVG::ExportSVGFile( bool aOnlyOneFile ) BuildPlotFileName( &fn, outputDir.GetPath(), suffix, SVGFileExtension ); wxString svgPath = fn.GetFullPath(); - m_printMaskLayer = aOnlyOneFile ? all_selected : LSET( layer ); + m_printMaskLayer = aOnlyOneFile ? all_selected.SeqStackupBottom2Top() : LSEQ( { layer } ); if( m_checkboxEdgesOnAllPages->GetValue() ) - m_printMaskLayer.set( Edge_Cuts ); + m_printMaskLayer.push_back( Edge_Cuts ); svgPlotOptions.m_outputFile = svgPath; svgPlotOptions.m_printMaskLayer = m_printMaskLayer; diff --git a/pcbnew/exporters/export_svg.cpp b/pcbnew/exporters/export_svg.cpp index 5cd77f467e..b17817d6d9 100644 --- a/pcbnew/exporters/export_svg.cpp +++ b/pcbnew/exporters/export_svg.cpp @@ -35,7 +35,7 @@ bool EXPORT_SVG::Plot( BOARD* aBoard, const PCB_PLOT_SVG_OPTIONS& aSvgPlotOption plot_opts.SetPlotFrameRef( aSvgPlotOptions.m_plotFrame ); // 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 ) { @@ -97,7 +97,7 @@ bool EXPORT_SVG::Plot( BOARD* aBoard, const PCB_PLOT_SVG_OPTIONS& aSvgPlotOption if( plotter ) { plotter->SetColorMode( !aSvgPlotOptions.m_blackAndWhite ); - PlotBoardLayers( aBoard, plotter, aSvgPlotOptions.m_printMaskLayer.SeqStackupBottom2Top(), + PlotBoardLayers( aBoard, plotter, aSvgPlotOptions.m_printMaskLayer, plot_opts ); plotter->EndPlot(); } diff --git a/pcbnew/exporters/export_svg.h b/pcbnew/exporters/export_svg.h index e80b5aa9a7..71ab30bc79 100644 --- a/pcbnew/exporters/export_svg.h +++ b/pcbnew/exporters/export_svg.h @@ -33,7 +33,7 @@ struct PCB_PLOT_SVG_OPTIONS int m_pageSizeMode; - LSET m_printMaskLayer; + LSEQ m_printMaskLayer; // How holes in pads/vias are plotted: // 0 = no hole, 1 = small shape, 2 = actual shape diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 84243cc460..32a33fa9ca 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -263,7 +263,7 @@ int PCBNEW_JOBS_HANDLER::JobExportDxf( JOB* aJob ) if( plotter ) { - PlotBoardLayers( brd, plotter, aDxfJob->m_printMaskLayer.SeqStackupBottom2Top(), plotOpts ); + PlotBoardLayers( brd, plotter, aDxfJob->m_printMaskLayer, plotOpts ); plotter->EndPlot(); } @@ -332,7 +332,7 @@ int PCBNEW_JOBS_HANDLER::JobExportPdf( JOB* aJob ) if( plotter ) { - PlotBoardLayers( brd, plotter, aPdfJob->m_printMaskLayer.SeqStackupBottom2Top(), plotOpts ); + PlotBoardLayers( brd, plotter, aPdfJob->m_printMaskLayer, plotOpts ); PlotInteractiveLayer( brd, plotter, plotOpts ); plotter->EndPlot(); } @@ -365,20 +365,20 @@ int PCBNEW_JOBS_HANDLER::JobExportGerbers( JOB* aJob ) if( aGerberJob->m_useBoardPlotParams ) { - aGerberJob->m_printMaskLayer = boardPlotOptions.GetLayerSelection(); + aGerberJob->m_printMaskLayer = boardPlotOptions.GetLayerSelection().SeqStackupBottom2Top(); aGerberJob->m_layersIncludeOnAll = boardPlotOptions.GetPlotOnAllLayersSelection(); } else { // default to the board enabled layers if( aGerberJob->m_printMaskLayer == 0 ) - aGerberJob->m_printMaskLayer = brd->GetEnabledLayers(); + aGerberJob->m_printMaskLayer = brd->GetEnabledLayers().SeqStackupBottom2Top(); if( aGerberJob->m_layersIncludeOnAllSet ) 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; @@ -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 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 ); if( plotter ) { - PlotBoardLayers( brd, plotter, aGerberJob->m_printMaskLayer.SeqStackupBottom2Top(), - plotOpts ); + PlotBoardLayers( brd, plotter, aGerberJob->m_printMaskLayer, plotOpts ); plotter->EndPlot(); } else