2012-06-09 09:38:58 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
|
|
|
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
|
|
* Copyright (C) 1992-2012 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 2
|
|
|
|
* 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, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
/**
|
|
|
|
* @file pcbnew/pcbplot.cpp
|
|
|
|
*/
|
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <fctsys.h>
|
|
|
|
#include <plot_common.h>
|
|
|
|
#include <confirm.h>
|
|
|
|
#include <wxPcbStruct.h>
|
|
|
|
#include <pcbplot.h>
|
|
|
|
#include <pcbstruct.h>
|
2012-04-13 18:51:24 +00:00
|
|
|
#include <base_units.h>
|
2013-05-10 19:22:29 +00:00
|
|
|
#include <reporter.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <class_board.h>
|
|
|
|
#include <pcbnew.h>
|
2012-08-29 16:59:50 +00:00
|
|
|
#include <plotcontroller.h>
|
2012-09-20 18:58:41 +00:00
|
|
|
#include <pcb_plot_params.h>
|
2012-08-29 16:59:50 +00:00
|
|
|
#include <wx/ffile.h>
|
2012-09-15 12:13:03 +00:00
|
|
|
#include <dialog_plot.h>
|
2013-05-02 18:06:58 +00:00
|
|
|
#include <macros.h>
|
2007-05-06 16:03:28 +00:00
|
|
|
|
2011-02-03 22:26:30 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
const wxString GetGerberExtension( LAYER_NUM aLayer )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
if( IsCopperLayer( aLayer ) )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
if( aLayer == F_Cu )
|
|
|
|
return wxT( "gtl" );
|
|
|
|
else if( aLayer == B_Cu )
|
|
|
|
return wxT( "gbl" );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return wxT( "gbr" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch( aLayer )
|
|
|
|
{
|
|
|
|
case B_Adhes: return wxT( "gba" );
|
|
|
|
case F_Adhes: return wxT( "gta" );
|
|
|
|
|
|
|
|
case B_Paste: return wxT( "gbp" );
|
|
|
|
case F_Paste: return wxT( "gtp" );
|
|
|
|
|
|
|
|
case B_SilkS: return wxT( "gbo" );
|
|
|
|
case F_SilkS: return wxT( "gto" );
|
|
|
|
|
|
|
|
case B_Mask: return wxT( "gbs" );
|
|
|
|
case F_Mask: return wxT( "gts" );
|
|
|
|
|
|
|
|
case Dwgs_User:
|
|
|
|
case Cmts_User:
|
|
|
|
case Eco1_User:
|
|
|
|
case Eco2_User:
|
|
|
|
case Edge_Cuts:
|
|
|
|
default: return wxT( "gbr" );
|
|
|
|
}
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
2013-05-10 19:22:29 +00:00
|
|
|
}
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2014-06-19 06:26:53 +00:00
|
|
|
wxString GetGerberFileFunction( const BOARD *aBoard, LAYER_NUM aLayer )
|
|
|
|
{
|
|
|
|
wxString attrib = wxEmptyString;
|
|
|
|
|
|
|
|
switch( aLayer )
|
|
|
|
{
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_Adhes:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Glue,Top" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Adhes:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Glue,Bot" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_SilkS:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Legend,Top" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_SilkS:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Legend,Bot" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_Mask:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Soldermask,Top" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Mask:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Soldermask,Bot" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_Paste:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Paste,Top" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Paste:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Paste,Bot" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Edge_Cuts:
|
2014-07-04 14:22:38 +00:00
|
|
|
// Board outline.
|
|
|
|
// Can be "Profile,NP" (Not Plated: usual) or "Profile,P"
|
|
|
|
// This last is the exception (Plated)
|
|
|
|
attrib = wxString( wxT( "Profile,NP" ) );
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Dwgs_User:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Drawing" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Cmts_User:
|
2014-06-19 06:26:53 +00:00
|
|
|
attrib = wxString( wxT( "Other,Comment" ) );
|
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Eco1_User:
|
2014-07-01 19:20:38 +00:00
|
|
|
attrib = wxString( wxT( "Other,ECO1" ) );
|
2014-06-30 06:44:46 +00:00
|
|
|
break;
|
|
|
|
|
2014-07-01 19:20:38 +00:00
|
|
|
case Eco2_User:
|
|
|
|
attrib = wxString( wxT( "Other,ECO2" ) );
|
2014-06-30 06:44:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case B_Cu:
|
|
|
|
attrib = wxString::Format( wxT( "Copper,L%d" ), aBoard->GetCopperLayerCount() );
|
|
|
|
break;
|
|
|
|
|
2014-07-01 19:20:38 +00:00
|
|
|
case F_Cu:
|
2014-06-30 06:44:46 +00:00
|
|
|
default:
|
|
|
|
if( IsCopperLayer( aLayer ) )
|
|
|
|
{
|
2014-07-01 19:20:38 +00:00
|
|
|
attrib = wxString::Format( wxT( "Copper,L%d" ), aLayer+1 );
|
2014-06-30 06:44:46 +00:00
|
|
|
}
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the signal type of the layer, if relevant
|
2014-06-30 06:44:46 +00:00
|
|
|
if( IsCopperLayer( aLayer ) )
|
|
|
|
{
|
|
|
|
LAYER_T type = aBoard->GetLayerType( ToLAYER_ID( aLayer ) );
|
|
|
|
|
2014-06-19 06:26:53 +00:00
|
|
|
switch( type )
|
|
|
|
{
|
|
|
|
case LT_SIGNAL:
|
|
|
|
attrib += wxString( wxT( ",Signal" ) );
|
|
|
|
break;
|
|
|
|
case LT_POWER:
|
|
|
|
attrib += wxString( wxT( ",Plane" ) );
|
|
|
|
break;
|
|
|
|
case LT_MIXED:
|
|
|
|
attrib += wxString( wxT( ",Mixed" ) );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
; // do nothing (but avoid a warning for unhandled LAYER_T values from GCC)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return attrib;
|
|
|
|
}
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
void BuildPlotFileName( wxFileName* aFilename,
|
|
|
|
const wxString& aOutputDir,
|
|
|
|
const wxString& aSuffix,
|
|
|
|
const wxString& aExtension )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
|
|
|
aFilename->SetPath( aOutputDir );
|
|
|
|
|
|
|
|
// Set the file extension
|
|
|
|
aFilename->SetExt( aExtension );
|
|
|
|
|
|
|
|
/* remove leading and trailing spaces if any from the suffix, if
|
2012-11-15 10:47:23 +00:00
|
|
|
something survives add it to the name;
|
|
|
|
also the suffix can contain some not allowed chars in filename (/ \ .),
|
|
|
|
so change them to underscore
|
|
|
|
*/
|
|
|
|
wxString suffix = aSuffix;
|
|
|
|
suffix.Trim( true );
|
|
|
|
suffix.Trim( false );
|
|
|
|
|
|
|
|
suffix.Replace( wxT("."), wxT("_") );
|
|
|
|
suffix.Replace( wxT("/"), wxT("_") );
|
|
|
|
suffix.Replace( wxT("\\"), wxT("_") );
|
|
|
|
|
|
|
|
if( !suffix.IsEmpty() )
|
|
|
|
aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
|
|
|
|
}
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
|
|
|
bool EnsureOutputDirectory( wxFileName* aOutputDir,
|
2012-11-16 14:13:31 +00:00
|
|
|
const wxString& aBoardFilename,
|
2013-05-10 19:22:29 +00:00
|
|
|
REPORTER* aReporter )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2013-05-10 19:22:29 +00:00
|
|
|
wxString msg;
|
2012-08-29 16:59:50 +00:00
|
|
|
wxString boardFilePath = wxFileName( aBoardFilename ).GetPath();
|
|
|
|
|
|
|
|
if( !aOutputDir->MakeAbsolute( boardFilePath ) )
|
|
|
|
{
|
2013-05-10 19:22:29 +00:00
|
|
|
if( aReporter )
|
|
|
|
{
|
2014-01-19 13:12:57 +00:00
|
|
|
msg.Printf( _( "*** Error: cannot make path '%s' absolute with respect to '%s'! ***" ),
|
2013-05-10 19:22:29 +00:00
|
|
|
GetChars( aOutputDir->GetPath() ),
|
|
|
|
GetChars( boardFilePath ) );
|
|
|
|
aReporter->Report( msg );
|
|
|
|
}
|
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString outputPath( aOutputDir->GetPath() );
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
if( !wxFileName::DirExists( outputPath ) )
|
|
|
|
{
|
|
|
|
if( wxMkdir( outputPath ) )
|
|
|
|
{
|
2013-05-10 19:22:29 +00:00
|
|
|
if( aReporter )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2014-01-19 13:12:57 +00:00
|
|
|
msg.Printf( _( "Output directory '%s' created.\n" ), GetChars( outputPath ) );
|
2013-05-10 19:22:29 +00:00
|
|
|
aReporter->Report( msg );
|
2013-04-09 17:49:01 +00:00
|
|
|
return true;
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-10 19:22:29 +00:00
|
|
|
if( aReporter )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2014-01-19 13:12:57 +00:00
|
|
|
msg.Printf( _( "*** Error: cannot create output directory '%s'! ***\n" ),
|
2013-05-10 19:22:29 +00:00
|
|
|
GetChars( outputPath ) );
|
|
|
|
aReporter->Report( msg );
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
2011-09-29 16:49:40 +00:00
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
return false;
|
2008-03-04 04:22:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
return true;
|
2007-05-06 16:03:28 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
|
|
|
|
PLOT_CONTROLLER::PLOT_CONTROLLER( BOARD *aBoard )
|
|
|
|
: m_plotter( NULL ), m_board( aBoard )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
PLOT_CONTROLLER::~PLOT_CONTROLLER()
|
|
|
|
{
|
|
|
|
ClosePlot();
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
/* IMPORTANT THING TO KNOW: the locale during plots *MUST* be kept as
|
|
|
|
* C/POSIX using a LOCALE_IO object on the stack. This even when
|
|
|
|
* opening/closing the plotfile, since some drivers do I/O even then */
|
|
|
|
|
|
|
|
void PLOT_CONTROLLER::ClosePlot()
|
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
if( m_plotter )
|
|
|
|
{
|
|
|
|
m_plotter->EndPlot();
|
|
|
|
delete m_plotter;
|
|
|
|
m_plotter = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
|
|
|
bool PLOT_CONTROLLER::OpenPlotfile( const wxString &aSuffix,
|
|
|
|
PlotFormat aFormat,
|
2012-08-29 16:59:50 +00:00
|
|
|
const wxString &aSheetDesc )
|
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
/* Save the current format: sadly some plot routines depends on this
|
|
|
|
but the main reason is that the StartPlot method uses it to
|
|
|
|
dispatch the plotter creation */
|
|
|
|
m_plotOpts.SetFormat( aFormat );
|
|
|
|
|
|
|
|
// Ensure that the previous plot is closed
|
|
|
|
ClosePlot();
|
|
|
|
|
|
|
|
// Now compute the full filename for the output and start the plot
|
|
|
|
// (after ensuring the output directory is OK)
|
|
|
|
wxString outputDirName = m_plotOpts.GetOutputDirectory() ;
|
|
|
|
wxFileName outputDir = wxFileName::DirName( outputDirName );
|
|
|
|
wxString boardFilename = m_board->GetFileName();
|
2013-05-10 19:22:29 +00:00
|
|
|
|
|
|
|
if( EnsureOutputDirectory( &outputDir, boardFilename ) )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
|
|
|
wxFileName fn( boardFilename );
|
2013-05-10 19:22:29 +00:00
|
|
|
BuildPlotFileName( &fn, outputDirName, aSuffix, GetDefaultPlotExtension( aFormat ) );
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2014-06-19 06:26:53 +00:00
|
|
|
m_plotter = StartPlotBoard( m_board, &m_plotOpts, UNDEFINED_LAYER, fn.GetFullPath(), aSheetDesc );
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
return( m_plotter != NULL );
|
2013-05-10 19:22:29 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
bool PLOT_CONTROLLER::PlotLayer( LAYER_NUM aLayer )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
// No plot open, nothing to do...
|
|
|
|
if( !m_plotter )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Fully delegated to the parent
|
2012-09-24 06:39:59 +00:00
|
|
|
PlotOneBoardLayer( m_board, m_plotter, aLayer, m_plotOpts );
|
2012-08-29 16:59:50 +00:00
|
|
|
|
|
|
|
return true;
|
2013-05-10 19:22:29 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2013-04-18 21:21:26 +00:00
|
|
|
void PLOT_CONTROLLER::SetColorMode( bool aColorMode )
|
|
|
|
{
|
|
|
|
if( !m_plotter )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_plotter->SetColorMode( aColorMode );
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2013-04-18 21:21:26 +00:00
|
|
|
bool PLOT_CONTROLLER::GetColorMode()
|
|
|
|
{
|
|
|
|
if( !m_plotter )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return m_plotter->GetColorMode();
|
|
|
|
}
|