Pcbnew: Add creation of Drill Files in Gerber X2 format.

Gerbview: add support of drill files in Gerber format.
Fix also an issue in drill dialog: Axis choice is now common to plot a drill files.
This commit is contained in:
jean-pierre charras 2017-04-28 12:53:30 +02:00
parent a92ea8c75b
commit afe92c7bfc
21 changed files with 1611 additions and 631 deletions

View File

@ -99,9 +99,23 @@ std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribu
attribute_string = "%TA.AperFunction,WasherPad*%\n";
break;
case GBR_APERTURE_ATTRIB_HEATSINKPAD: // print info associated to a flashed heat sink pad (typically for SMDs)
case GBR_APERTURE_ATTRIB_HEATSINKPAD: // print info associated to a flashed heat sink pad
// (typically for SMDs)
attribute_string = "%TA.AperFunction,HeatsinkPad*%\n";
break;
case GBR_APERTURE_ATTRIB_VIADRILL: // print info associated to a via hole in drill files
attribute_string = "%TA.AperFunction,ViaDrill*%\n";
break;
case GBR_APERTURE_ATTRIB_COMPONENTDRILL: // print info associated to a component
// round hole in drill files
attribute_string = "%TA.AperFunction,ComponentDrill*%\n";
break;
case GBR_APERTURE_ATTRIB_SLOTDRILL: // print info associated to a oblong hole in drill files
attribute_string = "%TA.AperFunction,Slot*%\n";
break;
}
return attribute_string;

View File

@ -174,8 +174,8 @@ X2_ATTRIBUTE_FILEFUNCTION::X2_ATTRIBUTE_FILEFUNCTION( X2_ATTRIBUTE& aAttributeBa
m_Prms = aAttributeBase.GetPrms();
m_z_order = 0;
//ensure at least 5 parameters
while( GetPrmCount() < 5 )
// ensure at least 7 parameters exist.
while( GetPrmCount() < 7 )
m_Prms.Add( wxEmptyString );
set_Z_Order();
@ -183,7 +183,7 @@ X2_ATTRIBUTE_FILEFUNCTION::X2_ATTRIBUTE_FILEFUNCTION( X2_ATTRIBUTE& aAttributeBa
const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetFileType()
{
// the type of layer (Copper , Soldermask ... )
// the type of layer (Copper, Soldermask ... )
return m_Prms.Item( 1 );
}
@ -193,6 +193,14 @@ const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetBrdLayerId()
return m_Prms.Item( 2 );
}
const wxString X2_ATTRIBUTE_FILEFUNCTION::GetDrillLayerPair()
{
// the layer pair identifiers, for drill files, i.e.
// with m_Prms.Item( 1 ) = "Plated" or "NonPlated"
wxString lpair = m_Prms.Item( 2 ) + ',' + m_Prms.Item( 3 );
return lpair;
}
const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetBrdLayerSide()
{
if( IsCopper() )
@ -203,6 +211,7 @@ const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetBrdLayerSide()
return m_Prms.Item( 2 );
}
const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetLabel()
{
if( IsCopper() )
@ -212,12 +221,35 @@ const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetLabel()
}
const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetLPType()
{
// Only for drill files: the Layer Pair type (PTH, NPTH, Blind or Buried)
return m_Prms.Item( 4 );
}
const wxString& X2_ATTRIBUTE_FILEFUNCTION::GetRouteType()
{
// Only for drill files: the drill/routing type(Drill, Route, Mixed)
return m_Prms.Item( 5 );
}
bool X2_ATTRIBUTE_FILEFUNCTION::IsCopper()
{
// the filefunction label, if any
return GetFileType().IsSameAs( wxT( "Copper" ), false );
}
bool X2_ATTRIBUTE_FILEFUNCTION::IsDrillFile()
{
// the filefunction label, if any
return GetFileType().IsSameAs( wxT( "Plated" ), false )
|| GetFileType().IsSameAs( wxT( "NonPlated" ), false );
}
// Initialize the z order priority of the current file, from its attributes
// this priority is the order of layers from top to bottom to draw/display gerber images
// Stack up is( from external copper layer to external)

View File

@ -156,9 +156,33 @@ public:
bool IsCopper(); ///< return true if the filefunction type is "Copper"
/**
* @return true if the filefunction type is "Plated" or "NotPlated"
* therefore a drill file
*/
bool IsDrillFile();
const wxString& GetFileType(); ///< the type of layer (Copper, Soldermask ... )
const wxString& GetBrdLayerId(); ///< the brd layer identifier: Ln, only for Copper type
///< or Top, Bot for other types
/**
* @return the brd layer pair identifier: n,m for drill files
* (files with m_Prms.Item( 1 ) = "Plated" or "NotPlated")
*/
const wxString GetDrillLayerPair();
/**
* @return the Layer Pair type for drill files
* (PTH, NPTH, Blind or Buried)
*/
const wxString& GetLPType();
/**
* @return the drill/routing type for drill files
* (Drill, Route, Mixed)
*/
const wxString& GetRouteType();
const wxString& GetBrdLayerSide(); ///< the brd layer Pos: Top, Bot, Inr
///< same as GetBrdLayerId() for non copper type
const wxString& GetLabel(); ///< the filefunction label, if any

View File

@ -157,6 +157,15 @@ const wxString GERBER_FILE_IMAGE_LIST::GetDisplayName( int aIdx, bool aNameOnly
GetChars( gerber->m_FileFunction->GetBrdLayerId() ),
GetChars( gerber->m_FileFunction->GetBrdLayerSide() ) );
}
if( gerber->m_FileFunction->IsDrillFile() )
{
name.Printf( "%s (%s,%s,%s,%s)",
filename.GetData(),
GetChars( gerber->m_FileFunction->GetFileType() ),
GetChars( gerber->m_FileFunction->GetDrillLayerPair() ),
GetChars( gerber->m_FileFunction->GetLPType() ),
GetChars( gerber->m_FileFunction->GetRouteType() ) );
}
else
{
name.Printf( "%s (%s, %s)",

View File

@ -60,6 +60,9 @@ public:
GBR_APERTURE_ATTRIB_CONNECTORPAD, ///< aperture used for edge connecto pad (outer layers)
GBR_APERTURE_ATTRIB_WASHERPAD, ///< aperture used for mechanical pads (NPTH)
GBR_APERTURE_ATTRIB_HEATSINKPAD, ///< aperture used for heat sink pad (typically for SMDs)
GBR_APERTURE_ATTRIB_VIADRILL, ///< aperture used for via holes in drill files
GBR_APERTURE_ATTRIB_COMPONENTDRILL, ///< aperture used for pad holes in drill files
GBR_APERTURE_ATTRIB_SLOTDRILL, ///< aperture used for oblong holes in drill files
GBR_APERTURE_ATTRIB_END ///< sentinel: max value
};

View File

@ -178,6 +178,8 @@ set( PCBNEW_EXPORTERS
exporters/gen_drill_report_files.cpp
exporters/gen_modules_placefile.cpp
exporters/gendrill_Excellon_writer.cpp
exporters/gendrill_file_writer_base.cpp
exporters/gendrill_gerber_writer.cpp
)
set( PCBNEW_AUTOROUTER_SRCS

View File

@ -5,8 +5,8 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2012 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2016 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 1992-2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
@ -32,7 +32,8 @@
#include <wxPcbStruct.h>
#include <pcbplot.h>
#include <gendrill_Excellon_writer.h>
#include <macros.h>
#include <gendrill_gerber_writer.h>
//#include <macros.h>
#include <class_board.h>
#include <class_track.h>
@ -54,6 +55,7 @@
#define UnitDrillInchKey wxT( "DrillUnit" )
#define DrillOriginIsAuxAxisKey wxT( "DrillAuxAxis" )
#define DrillMapFileTypeKey wxT( "DrillMapFileType" )
#define DrillFileFormatKey wxT( "DrillFileType" )
// list of allowed precision for EXCELLON files, for integer format:
// Due to difference between inches and mm,
@ -87,13 +89,13 @@ DIALOG_GENDRILL::DIALOG_GENDRILL( PCB_EDIT_FRAME* parent ) :
// Static members of DIALOG_GENDRILL
int DIALOG_GENDRILL::m_UnitDrillIsInch = true;
int DIALOG_GENDRILL::m_ZerosFormat = EXCELLON_WRITER::DECIMAL_FORMAT;
bool DIALOG_GENDRILL::m_MinimalHeader = false;
bool DIALOG_GENDRILL::m_Mirror = false;
bool DIALOG_GENDRILL::m_Merge_PTH_NPTH = false;
bool DIALOG_GENDRILL::m_DrillOriginIsAuxAxis = false;
int DIALOG_GENDRILL::m_mapFileType = 1;
int DIALOG_GENDRILL::m_UnitDrillIsInch = true; // Only for Excellon format
int DIALOG_GENDRILL::m_ZerosFormat = EXCELLON_WRITER::DECIMAL_FORMAT;
bool DIALOG_GENDRILL::m_MinimalHeader = false; // Only for Excellon format
bool DIALOG_GENDRILL::m_Mirror = false; // Only for Excellon format
bool DIALOG_GENDRILL::m_Merge_PTH_NPTH = false; // Only for Excellon format
int DIALOG_GENDRILL::m_mapFileType = 1;
int DIALOG_GENDRILL::m_drillFileType = 0;
DIALOG_GENDRILL::~DIALOG_GENDRILL()
@ -109,8 +111,9 @@ void DIALOG_GENDRILL::initDialog()
m_config->Read( MergePTHNPTHKey, &m_Merge_PTH_NPTH );
m_config->Read( MinimalHeaderKey, &m_MinimalHeader );
m_config->Read( UnitDrillInchKey, &m_UnitDrillIsInch );
m_config->Read( DrillOriginIsAuxAxisKey, &m_DrillOriginIsAuxAxis );
m_drillOriginIsAuxAxis = m_plotOpts.GetUseAuxOrigin();
m_config->Read( DrillMapFileTypeKey, &m_mapFileType );
m_config->Read( DrillFileFormatKey, &m_drillFileType );
InitDisplayParams();
}
@ -120,12 +123,13 @@ void DIALOG_GENDRILL::InitDisplayParams()
{
wxString msg;
m_rbFileFormat->SetSelection( m_drillFileType );
m_Choice_Unit->SetSelection( m_UnitDrillIsInch ? 1 : 0 );
m_Choice_Zeros_Format->SetSelection( m_ZerosFormat );
UpdatePrecisionOptions();
m_Check_Minimal->SetValue( m_MinimalHeader );
if( m_DrillOriginIsAuxAxis )
if( m_drillOriginIsAuxAxis )
m_Choice_Drill_Offset->SetSelection( 1 );
m_Check_Mirror->SetValue( m_Mirror );
@ -221,6 +225,30 @@ void DIALOG_GENDRILL::InitDisplayParams()
// Output directory
m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() );
wxCommandEvent dummy;
onFileFormatSelection( dummy );
}
void DIALOG_GENDRILL::onFileFormatSelection( wxCommandEvent& event )
{
m_drillFileType = m_rbFileFormat->GetSelection();
bool enbl_Excellon = m_drillFileType == 0;
m_Choice_Unit->Enable( enbl_Excellon );
m_Choice_Zeros_Format->Enable( enbl_Excellon );
m_Check_Mirror->Enable( enbl_Excellon );
m_Check_Minimal->Enable( enbl_Excellon );
m_Check_Merge_PTH_NPTH->Enable( enbl_Excellon );
if( enbl_Excellon )
UpdatePrecisionOptions();
else
{
m_staticTextPrecision->Enable( true );
m_staticTextPrecision->SetLabel( m_plotOpts.GetGerberPrecision() == 6 ? "4.6" : "4.5" );
}
}
@ -233,8 +261,8 @@ void DIALOG_GENDRILL::UpdateConfig()
m_config->Write( MergePTHNPTHKey, m_Merge_PTH_NPTH );
m_config->Write( MinimalHeaderKey, m_MinimalHeader );
m_config->Write( UnitDrillInchKey, m_UnitDrillIsInch );
m_config->Write( DrillOriginIsAuxAxisKey, m_DrillOriginIsAuxAxis );
m_config->Write( DrillMapFileTypeKey, m_mapFileType );
m_config->Write( DrillFileFormatKey, m_drillFileType );
}
@ -328,6 +356,8 @@ void DIALOG_GENDRILL::SetParams()
dirStr = m_outputDirectoryName->GetValue();
dirStr.Replace( wxT( "\\" ), wxT( "/" ) );
m_plotOpts.SetOutputDirectory( dirStr );
m_drillOriginIsAuxAxis = m_Choice_Drill_Offset->GetSelection();
m_plotOpts.SetUseAuxOrigin( m_drillOriginIsAuxAxis );
m_mapFileType = m_Choice_Drill_Map->GetSelection();
@ -336,7 +366,6 @@ void DIALOG_GENDRILL::SetParams()
m_Mirror = m_Check_Mirror->IsChecked();
m_Merge_PTH_NPTH = m_Check_Merge_PTH_NPTH->IsChecked();
m_ZerosFormat = m_Choice_Zeros_Format->GetSelection();
m_DrillOriginIsAuxAxis = m_Choice_Drill_Offset->GetSelection();
if( m_Choice_Drill_Offset->GetSelection() == 0 )
m_FileDrillOffset = wxPoint( 0, 0 );
@ -371,13 +400,28 @@ void DIALOG_GENDRILL::GenDrillAndMapFiles( bool aGenDrill, bool aGenMap )
if( choice >= DIM( filefmt ) )
choice = 1;
EXCELLON_WRITER excellonWriter( m_parent->GetBoard() );
excellonWriter.SetFormat( !m_UnitDrillIsInch, (EXCELLON_WRITER::ZEROS_FMT) m_ZerosFormat,
m_Precision.m_lhs, m_Precision.m_rhs );
excellonWriter.SetOptions( m_Mirror, m_MinimalHeader, m_FileDrillOffset, m_Merge_PTH_NPTH );
excellonWriter.SetMapFileFormat( filefmt[choice] );
if( m_drillFileType == 0 )
{
EXCELLON_WRITER excellonWriter( m_parent->GetBoard() );
excellonWriter.SetFormat( !m_UnitDrillIsInch, (EXCELLON_WRITER::ZEROS_FMT) m_ZerosFormat,
m_Precision.m_lhs, m_Precision.m_rhs );
excellonWriter.SetOptions( m_Mirror, m_MinimalHeader, m_FileDrillOffset, m_Merge_PTH_NPTH );
excellonWriter.SetMapFileFormat( filefmt[choice] );
excellonWriter.CreateDrillandMapFilesSet( defaultPath, aGenDrill, aGenMap, &reporter );
excellonWriter.CreateDrillandMapFilesSet( defaultPath, aGenDrill, aGenMap, &reporter );
}
else
{
GERBER_WRITER gerberWriter( m_parent->GetBoard() );
// Set gerber precision: only 5 or 6 digits for mantissa are allowed
// (SetFormat() accept 5 or 6, and any other value set the precision to 5)
// the integer part precision is always 4, and units always mm
gerberWriter.SetFormat( m_plotOpts.GetGerberPrecision() );
gerberWriter.SetOptions( m_FileDrillOffset );
gerberWriter.SetMapFileFormat( filefmt[choice] );
gerberWriter.CreateDrillandMapFilesSet( defaultPath, aGenDrill, aGenMap, &reporter );
}
}
@ -401,12 +445,21 @@ void DIALOG_GENDRILL::OnGenReportFile( wxCommandEvent& event )
if( dlg.ShowModal() == wxID_CANCEL )
return;
EXCELLON_WRITER excellonWriter( m_parent->GetBoard() );
excellonWriter.SetFormat( !m_UnitDrillIsInch, (EXCELLON_WRITER::ZEROS_FMT) m_ZerosFormat,
m_Precision.m_lhs, m_Precision.m_rhs );
excellonWriter.SetOptions( m_Mirror, m_MinimalHeader, m_FileDrillOffset, m_Merge_PTH_NPTH );
bool success;
bool success = excellonWriter.GenDrillReportFile( dlg.GetPath() );
// Info is slightly different between Excellon and Gerber
// (file ext, Merge PTH/NPTH option)
if( m_drillFileType == 0 )
{
EXCELLON_WRITER excellonWriter( m_parent->GetBoard() );
excellonWriter.SetMergeOption( m_Merge_PTH_NPTH );
success = excellonWriter.GenDrillReportFile( dlg.GetPath() );
}
else
{
GERBER_WRITER gerberWriter( m_parent->GetBoard() );
success = gerberWriter.GenDrillReportFile( dlg.GetPath() );
}
wxString msg;

View File

@ -42,8 +42,6 @@ public:
static bool m_MinimalHeader;
static bool m_Mirror;
static bool m_Merge_PTH_NPTH;
static bool m_DrillOriginIsAuxAxis; /* Axis selection (main / auxiliary)
* for drill origin coordinates */
DRILL_PRECISION m_Precision; // Selected precision for drill files
wxPoint m_FileDrillOffset; // Drill offset: 0,0 for absolute coordinates,
// or origin of the auxiliary axis
@ -51,10 +49,11 @@ public:
private:
PCB_EDIT_FRAME* m_parent;
wxConfigBase* m_config;
wxConfigBase* m_config;
BOARD* m_board;
PCB_PLOT_PARAMS m_plotOpts;
bool m_drillOriginIsAuxAxis; // Axis selection (main / auxiliary)
// for drill origin coordinates
int m_platedPadsHoleCount;
int m_notplatedPadsHoleCount;
int m_throughViasCount;
@ -62,6 +61,7 @@ private:
int m_blindOrBuriedViasCount;
static int m_mapFileType; // HPGL, PS ...
static int m_drillFileType; // Excellon, Gerber
void initDialog();
@ -72,6 +72,7 @@ private:
void OnSelZerosFmtSelected( wxCommandEvent& event ) override;
void OnGenDrillFile( wxCommandEvent& event ) override;
void OnGenMapFile( wxCommandEvent& event ) override;
void onFileFormatSelection( wxCommandEvent& event ) override;
/*
* Create a plain text report file giving a list of drill values and drill count

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 17 2015)
// C++ code generated with wxFormBuilder (version Feb 19 2017)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@ -23,7 +23,6 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
bdirnameSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Output directory:") ), wxHORIZONTAL );
m_outputDirectoryName = new wxTextCtrl( bdirnameSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_outputDirectoryName->SetMaxLength( 0 );
bdirnameSizer->Add( m_outputDirectoryName, 1, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_buttonBrowse = new wxButton( bdirnameSizer->GetStaticBox(), wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 );
@ -41,6 +40,12 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
wxBoxSizer* m_LeftBoxSizer;
m_LeftBoxSizer = new wxBoxSizer( wxVERTICAL );
wxString m_rbFileFormatChoices[] = { _("Excellon"), _("Gerber X2 (experimental)") };
int m_rbFileFormatNChoices = sizeof( m_rbFileFormatChoices ) / sizeof( wxString );
m_rbFileFormat = new wxRadioBox( this, wxID_ANY, _("File Format:"), wxDefaultPosition, wxDefaultSize, m_rbFileFormatNChoices, m_rbFileFormatChoices, 1, wxRA_SPECIFY_COLS );
m_rbFileFormat->SetSelection( 0 );
m_LeftBoxSizer->Add( m_rbFileFormat, 0, wxALL|wxEXPAND, 5 );
wxString m_Choice_UnitChoices[] = { _("Millimeters"), _("Inches") };
int m_Choice_UnitNChoices = sizeof( m_Choice_UnitChoices ) / sizeof( wxString );
m_Choice_Unit = new wxRadioBox( this, wxID_ANY, _("Drill Units:"), wxDefaultPosition, wxDefaultSize, m_Choice_UnitNChoices, m_Choice_UnitChoices, 1, wxRA_SPECIFY_COLS );
@ -55,15 +60,22 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
m_LeftBoxSizer->Add( m_Choice_Zeros_Format, 0, wxALL|wxEXPAND, 5 );
wxStaticBoxSizer* sbSizerPrecision;
sbSizerPrecision = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Precision") ), wxVERTICAL );
wxFlexGridSizer* fgSizer1;
fgSizer1 = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizer1->AddGrowableCol( 1 );
fgSizer1->SetFlexibleDirection( wxBOTH );
fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticTextPrecision = new wxStaticText( sbSizerPrecision->GetStaticBox(), wxID_ANY, _("Precision"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextTitle = new wxStaticText( this, wxID_ANY, _("Precision"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextTitle->Wrap( -1 );
fgSizer1->Add( m_staticTextTitle, 0, wxALL, 5 );
m_staticTextPrecision = new wxStaticText( this, wxID_ANY, _("Precision"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextPrecision->Wrap( -1 );
sbSizerPrecision->Add( m_staticTextPrecision, 0, wxALL, 5 );
fgSizer1->Add( m_staticTextPrecision, 0, wxALL, 5 );
m_LeftBoxSizer->Add( sbSizerPrecision, 0, wxEXPAND, 5 );
m_LeftBoxSizer->Add( fgSizer1, 0, wxEXPAND, 5 );
bmiddlerSizer->Add( m_LeftBoxSizer, 0, wxEXPAND, 5 );
@ -79,26 +91,26 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
bMiddleBoxSizer->Add( m_Choice_Drill_Map, 0, wxALL|wxEXPAND, 5 );
wxStaticBoxSizer* sbOptSizer;
sbOptSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Drill File Options:") ), wxVERTICAL );
wxStaticBoxSizer* sbExcellonOptSizer;
sbExcellonOptSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Excellon Drill File Options:") ), wxVERTICAL );
m_Check_Mirror = new wxCheckBox( sbOptSizer->GetStaticBox(), wxID_ANY, _("Mirror y axis"), wxDefaultPosition, wxDefaultSize, 0 );
m_Check_Mirror = new wxCheckBox( sbExcellonOptSizer->GetStaticBox(), wxID_ANY, _("Mirror y axis"), wxDefaultPosition, wxDefaultSize, 0 );
m_Check_Mirror->SetToolTip( _("Not recommended.\nUsed mostly by users who make the boards themselves.") );
sbOptSizer->Add( m_Check_Mirror, 0, wxRIGHT|wxLEFT, 5 );
sbExcellonOptSizer->Add( m_Check_Mirror, 0, wxRIGHT|wxLEFT, 5 );
m_Check_Minimal = new wxCheckBox( sbOptSizer->GetStaticBox(), wxID_ANY, _("Minimal header"), wxDefaultPosition, wxDefaultSize, 0 );
m_Check_Minimal->SetToolTip( _("Not recommended.\nOnly use for board houses which do not accept fully featured headers.") );
m_Check_Minimal = new wxCheckBox( sbExcellonOptSizer->GetStaticBox(), wxID_ANY, _("Minimal header"), wxDefaultPosition, wxDefaultSize, 0 );
m_Check_Minimal->SetToolTip( _("Not recommended.\nOnly use it for board houses which do not accept fully featured headers.") );
sbOptSizer->Add( m_Check_Minimal, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
sbExcellonOptSizer->Add( m_Check_Minimal, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
m_Check_Merge_PTH_NPTH = new wxCheckBox( sbOptSizer->GetStaticBox(), wxID_ANY, _("Merge PTH and NPTH holes into one file"), wxDefaultPosition, wxDefaultSize, 0 );
m_Check_Merge_PTH_NPTH->SetToolTip( _("Not recommended.\nOnly use for board houses which ask for merged PTH and NPTH into a single file.") );
m_Check_Merge_PTH_NPTH = new wxCheckBox( sbExcellonOptSizer->GetStaticBox(), wxID_ANY, _("Merge PTH and NPTH holes into one file"), wxDefaultPosition, wxDefaultSize, 0 );
m_Check_Merge_PTH_NPTH->SetToolTip( _("Not recommended.\nOnly use for board houses which ask for merged PTH and NTPH into a single file.") );
sbOptSizer->Add( m_Check_Merge_PTH_NPTH, 0, wxALL, 5 );
sbExcellonOptSizer->Add( m_Check_Merge_PTH_NPTH, 0, wxALL, 5 );
bMiddleBoxSizer->Add( sbOptSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
bMiddleBoxSizer->Add( sbExcellonOptSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
wxString m_Choice_Drill_OffsetChoices[] = { _("Absolute"), _("Auxiliary axis") };
int m_Choice_Drill_OffsetNChoices = sizeof( m_Choice_Drill_OffsetChoices ) / sizeof( wxString );
@ -211,6 +223,7 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
// Connect Events
m_buttonBrowse->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnOutputDirectoryBrowseClicked ), NULL, this );
m_rbFileFormat->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::onFileFormatSelection ), NULL, this );
m_Choice_Unit->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnSelDrillUnitsSelected ), NULL, this );
m_Choice_Zeros_Format->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnSelZerosFmtSelected ), NULL, this );
m_buttonDrill->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnGenDrillFile ), NULL, this );
@ -223,6 +236,7 @@ DIALOG_GENDRILL_BASE::~DIALOG_GENDRILL_BASE()
{
// Disconnect Events
m_buttonBrowse->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnOutputDirectoryBrowseClicked ), NULL, this );
m_rbFileFormat->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::onFileFormatSelection ), NULL, this );
m_Choice_Unit->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnSelDrillUnitsSelected ), NULL, this );
m_Choice_Zeros_Format->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnSelZerosFmtSelected ), NULL, this );
m_buttonDrill->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GENDRILL_BASE::OnGenDrillFile ), NULL, this );

View File

@ -44,7 +44,7 @@
<property name="minimum_size"></property>
<property name="name">DIALOG_GENDRILL_BASE</property>
<property name="pos"></property>
<property name="size">506,518</property>
<property name="size">661,518</property>
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
<property name="title">Drill Files Generation</property>
@ -112,6 +112,7 @@
<property name="minimum_size"></property>
<property name="name">bdirnameSizer</property>
<property name="orient">wxHORIZONTAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -315,6 +316,96 @@
<property name="name">m_LeftBoxSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxRadioBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;Excellon&quot; &quot;Gerber X2 (experimental)&quot;</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">File Format:</property>
<property name="majorDimension">1</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_rbFileFormat</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="selection">0</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxRA_SPECIFY_COLS</property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRadioBox">onFileFormatSelection</event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
@ -499,14 +590,101 @@
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Precision</property>
<object class="wxFlexGridSizer" expanded="1">
<property name="cols">2</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">1</property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">sbSizerPrecision</property>
<property name="orient">wxVERTICAL</property>
<property name="name">fgSizer1</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<event name="OnUpdateUI"></event>
<property name="rows">0</property>
<property name="vgap">0</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Precision</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticTextTitle</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
@ -699,10 +877,11 @@
<property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Drill File Options:</property>
<property name="label">Excellon Drill File Options:</property>
<property name="minimum_size"></property>
<property name="name">sbOptSizer</property>
<property name="name">sbExcellonOptSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -1082,6 +1261,7 @@
<property name="minimum_size"></property>
<property name="name">sbSizerInfo</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -1094,6 +1274,7 @@
<property name="minimum_size"></property>
<property name="name">m_DefaultViasDrillSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">protected</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -1191,6 +1372,7 @@
<property name="minimum_size"></property>
<property name="name">m_MicroViasDrillSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">protected</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -1288,6 +1470,7 @@
<property name="minimum_size"></property>
<property name="name">sbSizerHoles</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -2096,6 +2279,7 @@
<property name="minimum_size"></property>
<property name="name">bmsgSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<event name="OnUpdateUI"></event>
<object class="sizeritem" expanded="1">
@ -2133,7 +2317,7 @@
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="maxlength">0</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">-1,90</property>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 17 2015)
// C++ code generated with wxFormBuilder (version Feb 19 2017)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@ -42,8 +42,10 @@ class DIALOG_GENDRILL_BASE : public DIALOG_SHIM
protected:
wxTextCtrl* m_outputDirectoryName;
wxButton* m_buttonBrowse;
wxRadioBox* m_rbFileFormat;
wxRadioBox* m_Choice_Unit;
wxRadioBox* m_Choice_Zeros_Format;
wxStaticText* m_staticTextTitle;
wxStaticText* m_staticTextPrecision;
wxRadioBox* m_Choice_Drill_Map;
wxCheckBox* m_Check_Mirror;
@ -67,6 +69,7 @@ class DIALOG_GENDRILL_BASE : public DIALOG_SHIM
// Virtual event handlers, overide them in your derived class
virtual void OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void onFileFormatSelection( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSelDrillUnitsSelected( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSelZerosFmtSelected( wxCommandEvent& event ) { event.Skip(); }
virtual void OnGenDrillFile( wxCommandEvent& event ) { event.Skip(); }
@ -77,7 +80,7 @@ class DIALOG_GENDRILL_BASE : public DIALOG_SHIM
public:
DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Drill Files Generation"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 506,473 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Drill Files Generation"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 661,518 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_GENDRILL_BASE();
};

View File

@ -5,7 +5,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2017 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
@ -40,15 +40,12 @@
#include <dialog_plot.h>
#include <wx_html_report_panel.h>
// Uncomment this line to allow experimetal net attributes in Gerber files:
#define KICAD_USE_GBR_NETATTRIBUTES
DIALOG_PLOT::DIALOG_PLOT( PCB_EDIT_FRAME* aParent ) :
DIALOG_PLOT_BASE( aParent ), m_parent( aParent ),
m_board( aParent->GetBoard() ),
m_plotOpts( aParent->GetPlotSettings() )
m_board( aParent->GetBoard() )
{
m_config = Kiface().KifaceSettings();
m_plotOpts = aParent->GetPlotSettings();
Init_Dialog();
GetSizer()->Fit( this );
@ -164,15 +161,11 @@ void DIALOG_PLOT::Init_Dialog()
m_useGerberX2Attributes->SetValue( m_plotOpts.GetUseGerberAttributes() );
// Option for including Gerber netlist info (from Gerber X2 format) in the output
#ifdef KICAD_USE_GBR_NETATTRIBUTES
m_useGerberNetAttributes->SetValue( m_plotOpts.GetIncludeGerberNetlistInfo() );
// Grey out if m_useGerberX2Attributes is not checked
m_useGerberNetAttributes->Enable( m_useGerberX2Attributes->GetValue() );
#else
m_plotOpts.SetIncludeGerberNetlistInfo( false );
m_useGerberNetAttributes->SetValue( false );
#endif
// Gerber precision for coordinates
m_rbGerberFormat->SetSelection( m_plotOpts.GetGerberPrecision() == 5 ? 0 : 1 );
@ -244,7 +237,7 @@ void DIALOG_PLOT::OnRightClick( wxMouseEvent& event )
#include <layers_id_colors_and_visibility.h>
void DIALOG_PLOT::OnPopUpLayers( wxCommandEvent& event )
{
unsigned int i;
unsigned int i;
switch( event.GetId() )
{
@ -294,7 +287,14 @@ void DIALOG_PLOT::OnPopUpLayers( wxCommandEvent& event )
void DIALOG_PLOT::CreateDrillFile( wxCommandEvent& event )
{
// Be sure drill file use the same settings (axis option, plot directory)
// than plot files:
applyPlotSettings();
m_parent->InstallDrillFrame( event );
// a few plot settings can be modified: take them in account
m_plotOpts = m_parent->GetPlotSettings();
Init_Dialog();
}
@ -443,9 +443,6 @@ void DIALOG_PLOT::SetPlotFormat( wxCommandEvent& event )
m_PlotOptionsSizer->Show( m_GerberOptionsSizer );
m_PlotOptionsSizer->Hide( m_HPGLOptionsSizer );
m_PlotOptionsSizer->Hide( m_PSOptionsSizer );
#ifndef KICAD_USE_GBR_NETATTRIBUTES
m_useGerberNetAttributes->Show( false );
#endif
break;
case PLOT_FORMAT_HPGL:

View File

@ -6,8 +6,8 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2015 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2015 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 1992-2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
@ -40,7 +40,7 @@
#include <pcbnew.h>
#include <pcbplot.h>
#include <gendrill_Excellon_writer.h>
#include <gendrill_file_writer_base.h>
/* Conversion utilities - these will be used often in there... */
inline double diameter_in_inches( double ius )
@ -55,9 +55,13 @@ inline double diameter_in_mm( double ius )
}
bool EXCELLON_WRITER::GenDrillMapFile( const wxString& aFullFileName,
PlotFormat aFormat )
bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName,
PlotFormat aFormat )
{
// Remark:
// Hole list must be created before calling this function, by buildHolesList(),
// for the right holes set (PTH, NPTH, buried/blind vias ...)
double scale = 1.0;
wxPoint offset;
PLOTTER* plotter = NULL;
@ -285,7 +289,7 @@ bool EXCELLON_WRITER::GenDrillMapFile( const wxString& aFullFileName,
}
bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName )
{
FILE_OUTPUTFORMATTER out( aFullFileName );
@ -309,6 +313,7 @@ bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
LSET cu = m_pcb->GetEnabledLayers() & LSET::AllCuMask();
int conventional_layer_num = 1;
for( LSEQ seq = cu.Seq(); seq; ++seq, ++conventional_layer_num )
{
out.Print( 0, " L%-2d: %-25s %s\n",
@ -326,10 +331,10 @@ bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
* 3 - Non Plated through holes
*/
bool buildNPTHlist = false;
bool buildNPTHlist = false; // First pass: build PTH list only
// in this loop are plated only:
for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
{
DRILL_LAYER_PAIR pair = hole_sets[pair_ndx];
@ -338,7 +343,7 @@ bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
if( pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
out.Print( 0, "Drill file '%s' contains\n",
TO_UTF8( drillFileName( pair, false, m_merge_PTH_NPTH ) ) );
TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) );
out.Print( 0, " plated through holes:\n" );
out.Print( 0, separator );
@ -348,7 +353,7 @@ bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
else // blind/buried
{
out.Print( 0, "Drill file '%s' contains\n",
TO_UTF8( drillFileName( pair, false, m_merge_PTH_NPTH ) ) );
TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) );
out.Print( 0, " holes connecting layer pair: '%s and %s' (%s vias):\n",
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.first ) ) ),
@ -376,7 +381,7 @@ bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
out.Print( 0, "Not plated through holes are merged with plated holes\n" );
else
out.Print( 0, "Drill file '%s' contains\n",
TO_UTF8( drillFileName( DRILL_LAYER_PAIR( F_Cu, B_Cu ),
TO_UTF8( getDrillFileName( DRILL_LAYER_PAIR( F_Cu, B_Cu ),
true, m_merge_PTH_NPTH ) ) );
out.Print( 0, " unplated through holes:\n" );
@ -388,7 +393,7 @@ bool EXCELLON_WRITER::GenDrillReportFile( const wxString& aFullFileName )
}
bool EXCELLON_WRITER::plotDrillMarks( PLOTTER* aPlotter )
bool GENDRILL_WRITER_BASE::plotDrillMarks( PLOTTER* aPlotter )
{
// Plot the drill map:
wxPoint pos;
@ -412,7 +417,7 @@ bool EXCELLON_WRITER::plotDrillMarks( PLOTTER* aPlotter )
}
unsigned EXCELLON_WRITER::printToolSummary( OUTPUTFORMATTER& out, bool aSummaryNPTH ) const
unsigned GENDRILL_WRITER_BASE::printToolSummary( OUTPUTFORMATTER& out, bool aSummaryNPTH ) const
{
unsigned totalHoleCount = 0;

View File

@ -1,8 +1,3 @@
/**
* @file gendrill_Excellon_writer.cpp
* @brief Functions to create EXCELLON drill files and report files.
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
@ -28,6 +23,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file gendrill_Excellon_writer.cpp
* @brief Functions to create EXCELLON drill files and report files.
*/
/**
* @see for EXCELLON format, see:
* http://www.excellon.com/manuals/program.htm
@ -36,26 +37,17 @@
#include <fctsys.h>
#include <vector>
#include <plot_common.h>
#include <trigo.h>
#include <macros.h>
#include <kicad_string.h>
#include <wxPcbStruct.h>
#include <pgm_base.h>
#include <build_version.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <pcbplot.h>
#include <pcbnew.h>
#include <gendrill_Excellon_writer.h>
#include <wildcards_and_files_ext.h>
#include <reporter.h>
#include <collectors.h>
// Comment/uncomment this to write or not a comment
// in drill file when PTH and NPTH are merged to flag
@ -64,18 +56,15 @@
EXCELLON_WRITER::EXCELLON_WRITER( BOARD* aPcb )
: GENDRILL_WRITER_BASE( aPcb )
{
m_file = NULL;
m_pcb = aPcb;
m_zeroFormat = DECIMAL_FORMAT;
m_conversionUnits = 0.0001;
m_unitsDecimal = true;
m_mirror = false;
m_merge_PTH_NPTH = false;
m_minimalHeader = false;
m_ShortHeader = false;
m_mapFileFmt = PLOT_FORMAT_PDF;
m_pageInfo = NULL;
m_drillFileExtension = DrillFileExtension;
}
@ -105,7 +94,7 @@ void EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
// to be sure the NPTH file is up to date in separate files mode.
if( getHolesCount() > 0 || doing_npth )
{
fn = drillFileName( pair, doing_npth, m_merge_PTH_NPTH );
fn = getDrillFileName( pair, doing_npth, m_merge_PTH_NPTH );
fn.SetPath( aPlotDirectory );
if( aGenDrill )
@ -134,36 +123,11 @@ void EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
createDrillFile( file );
}
if( aGenMap )
{
fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
bool success = GenDrillMapFile( fullfilename, m_mapFileFmt );
if( ! success )
{
if( aReporter )
{
msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullfilename ) );
aReporter->Report( msg );
}
return;
}
else
{
if( aReporter )
{
msg.Printf( _( "Create file %s\n" ), GetChars( fullfilename ) );
aReporter->Report( msg );
}
}
}
}
}
if( aGenMap )
CreateMapFilesSet( aPlotDirectory, aReporter );
}
@ -532,240 +496,3 @@ void EXCELLON_WRITER::writeEXCELLONEndOfFile()
fputs( "T0\nM30\n", m_file );
fclose( m_file );
}
/* Helper function for sorting hole list.
* Compare function used for sorting holes type type (plated then not plated)
* then by increasing diameter value and X value
*/
static bool CmpHoleSettings( const HOLE_INFO& a, const HOLE_INFO& b )
{
if( a.m_Hole_NotPlated != b.m_Hole_NotPlated )
return b.m_Hole_NotPlated;
if( a.m_Hole_Diameter != b.m_Hole_Diameter )
return a.m_Hole_Diameter < b.m_Hole_Diameter;
if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
return a.m_Hole_Pos.x < b.m_Hole_Pos.x;
return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
}
void EXCELLON_WRITER::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
bool aGenerateNPTH_list )
{
HOLE_INFO new_hole;
m_holeListBuffer.clear();
m_toolListBuffer.clear();
wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller
// build hole list for vias
if( ! aGenerateNPTH_list ) // vias are always plated !
{
for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) )
{
int hole_sz = via->GetDrillValue();
if( hole_sz == 0 ) // Should not occur.
continue;
new_hole.m_Tool_Reference = -1; // Flag value for Not initialized
new_hole.m_Hole_Orient = 0;
new_hole.m_Hole_Diameter = hole_sz;
new_hole.m_Hole_NotPlated = false;
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
new_hole.m_Hole_Shape = 0; // hole shape: round
new_hole.m_Hole_Pos = via->GetStart();
via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );
// LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
// Remember: top layer = 0 and bottom layer = 31 for through hole vias
// Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
if( new_hole.m_Hole_Top_Layer != aLayerPair.first ||
new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
continue;
m_holeListBuffer.push_back( new_hole );
}
}
if( aLayerPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
// add holes for thru hole pads
for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() )
{
for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
{
if( !m_merge_PTH_NPTH )
{
if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
continue;
if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
continue;
}
if( pad->GetDrillSize().x == 0 )
continue;
new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
new_hole.m_Hole_Orient = pad->GetOrientation();
new_hole.m_Hole_Shape = 0; // hole shape: round
new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
new_hole.m_Hole_Shape = 1; // oval flag set
new_hole.m_Hole_Size = pad->GetDrillSize();
new_hole.m_Hole_Pos = pad->GetPosition(); // hole position
new_hole.m_Hole_Bottom_Layer = B_Cu;
new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes
m_holeListBuffer.push_back( new_hole );
}
}
}
// Sort holes per increasing diameter value
sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSettings );
// build the tool list
int last_hole = -1; // Set to not initialized (this is a value not used
// for m_holeListBuffer[ii].m_Hole_Diameter)
bool last_notplated_opt = false;
DRILL_TOOL new_tool( 0, false );
unsigned jj;
for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
{
if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole ||
m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt )
{
new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
m_toolListBuffer.push_back( new_tool );
last_hole = new_tool.m_Diameter;
last_notplated_opt = new_tool.m_Hole_NotPlated;
}
jj = m_toolListBuffer.size();
if( jj == 0 )
continue; // Should not occurs
m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
m_toolListBuffer.back().m_TotalCount++;
if( m_holeListBuffer[ii].m_Hole_Shape )
m_toolListBuffer.back().m_OvalCount++;
}
}
std::vector<DRILL_LAYER_PAIR> EXCELLON_WRITER::getUniqueLayerPairs() const
{
wxASSERT( m_pcb );
static const KICAD_T interesting_stuff_to_collect[] = {
PCB_VIA_T,
EOT
};
PCB_TYPE_COLLECTOR vias;
vias.Collect( m_pcb, interesting_stuff_to_collect );
std::set< DRILL_LAYER_PAIR > unique;
DRILL_LAYER_PAIR layer_pair;
for( int i = 0; i < vias.GetCount(); ++i )
{
VIA* v = (VIA*) vias[i];
v->LayerPair( &layer_pair.first, &layer_pair.second );
// only make note of blind buried.
// thru hole is placed unconditionally as first in fetched list.
if( layer_pair != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
unique.insert( layer_pair );
}
}
std::vector<DRILL_LAYER_PAIR> ret;
ret.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) ); // always first in returned list
for( std::set< DRILL_LAYER_PAIR >::const_iterator it = unique.begin(); it != unique.end(); ++it )
ret.push_back( *it );
return ret;
}
const std::string EXCELLON_WRITER::layerName( PCB_LAYER_ID aLayer ) const
{
// Generic names here.
switch( aLayer )
{
case F_Cu:
return "front";
case B_Cu:
return "back";
default:
return StrPrintf( "inner%d", aLayer );
}
}
const std::string EXCELLON_WRITER::layerPairName( DRILL_LAYER_PAIR aPair ) const
{
std::string ret = layerName( aPair.first );
ret += '-';
ret += layerName( aPair.second );
return ret;
}
const wxString EXCELLON_WRITER::drillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
bool aMerge_PTH_NPTH ) const
{
wxASSERT( m_pcb );
wxString extend;
if( aNPTH )
extend = "-NPTH";
else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
if( !aMerge_PTH_NPTH )
extend = "-PTH";
// if merged, extend with nothing
}
else
{
extend += '-';
extend += layerPairName( aPair );
}
wxFileName fn = m_pcb->GetFileName();
fn.SetName( fn.GetName() + extend );
fn.SetExt( DrillFileExtension );
wxString ret = fn.GetFullName(); // show me in debugger
return ret;
}

View File

@ -1,8 +1,3 @@
/**
* @file gendrill_Excellon_writer.h
* @brief Classes used in drill files, map files and report files generation.
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
@ -27,135 +22,31 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file gendrill_Excellon_writer.h
* @brief Classes used in drill files, map files and report files generation.
*/
#ifndef _GENDRILL_EXCELLON_WRITER_
#define _GENDRILL_EXCELLON_WRITER_
#include <vector>
#include <gendrill_file_writer_base.h>
class BOARD;
class PLOTTER;
// the DRILL_TOOL class handles tools used in the excellon drill file:
class DRILL_TOOL
{
public:
int m_Diameter; // the diameter of the used tool (for oblong, the smaller size)
int m_TotalCount; // how many times it is used (round and oblong)
int m_OvalCount; // oblong count
bool m_Hole_NotPlated; // Is the hole plated or not plated
public:
DRILL_TOOL( int aDiameter, bool a_NotPlated )
{
m_TotalCount = 0;
m_OvalCount = 0;
m_Diameter = aDiameter;
m_Hole_NotPlated = a_NotPlated;
}
};
/* the HOLE_INFO class handle hole which must be drilled (diameter, position and layers)
* For buried or micro vias, the hole is not on all layers.
* So we must generate a drill file for each layer pair (adjacent layers)
* Not plated holes are always through holes, and must be output on a specific drill file
* because they are drilled after the Pcb process is finished.
*/
class HOLE_INFO
{
public:
int m_Hole_Diameter; // hole value, and for oblong: min(hole size x, hole size y)
int m_Tool_Reference; // Tool reference for this hole = 1 ... n (values <=0 must not be used)
wxSize m_Hole_Size; // hole size for oblong holes
double m_Hole_Orient; // Hole rotation (= pad rotation) for oblong holes
int m_Hole_Shape; // hole shape: round (0) or oval (1)
wxPoint m_Hole_Pos; // hole position
PCB_LAYER_ID m_Hole_Bottom_Layer; // hole ending layer (usually back layer)
PCB_LAYER_ID m_Hole_Top_Layer; // hole starting layer (usually front layer):
// m_Hole_Top_Layer < m_Hole_Bottom_Layer
bool m_Hole_NotPlated; // hole not plated. Must be in a specific drill file or section
public:
HOLE_INFO()
{
m_Hole_NotPlated = false;
m_Hole_Diameter = 0;
m_Tool_Reference = 0;
m_Hole_Orient = 0.0;
m_Hole_Shape = 0;
m_Hole_Bottom_Layer = B_Cu;
m_Hole_Top_Layer = F_Cu;
}
};
/* the DRILL_PRECISION helper class to handle drill precision format in excellon files
*/
class DRILL_PRECISION
{
public:
int m_lhs; // Left digit number (integer value of coordinates)
int m_rhs; // Right digit number (decimal value of coordinates)
public:
DRILL_PRECISION( int l = 2, int r = 4 )
{
m_lhs = l; m_rhs = r;
}
wxString GetPrecisionString()
{
wxString text;
text << m_lhs << wxT( ":" ) << m_rhs;
return text;
}
};
typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> DRILL_LAYER_PAIR;
class OUTPUTFORMATTER;
/**
* EXCELLON_WRITER is a class mainly used to create Excellon drill files
* However, this class is also used to create drill maps and drill report
*/
class EXCELLON_WRITER
class EXCELLON_WRITER: public GENDRILL_WRITER_BASE
{
public:
enum ZEROS_FMT { // Zero format in coordinates
DECIMAL_FORMAT, // Floating point coordinates
SUPPRESS_LEADING, // Suppress leading zeros
SUPPRESS_TRAILING, // Suppress trainling zeros
KEEP_ZEROS // keep zeros
};
wxPoint m_Offset; // offset coordinates
bool m_ShortHeader; // true to generate the smallest header (strip comments)
private:
FILE* m_file; // The output file
BOARD* m_pcb;
bool m_minimalHeader; // True to use minimal header
// in excellon file (strip comments)
bool m_unitsDecimal; // true = decimal, false = inches
ZEROS_FMT m_zeroFormat; // the zero format option for output file
DRILL_PRECISION m_precision; // The current coordinate precision (not used in decimal format)
double m_conversionUnits; // scaling factor to convert the board unites to Excellon units
// (i.e inches or mm)
bool m_mirror;
wxPoint m_offset; // Drill offset coordinates
bool m_merge_PTH_NPTH; // True to generate only one drill file
std::vector<HOLE_INFO> m_holeListBuffer; // Buffer containing holes
std::vector<DRILL_TOOL> m_toolListBuffer; // Buffer containing tools
PlotFormat m_mapFileFmt; // the format of the map drill file,
// if this map is needed
const PAGE_INFO* m_pageInfo; // the page info used to plot drill maps
// If NULL, use a A4 page format
public:
EXCELLON_WRITER( BOARD* aPcb );
@ -183,22 +74,6 @@ public:
void SetFormat( bool aMetric, ZEROS_FMT aZerosFmt = DECIMAL_FORMAT,
int aLeftDigits = 0, int aRightDigits = 0 );
/**
* Sets the page info used to plot drill maps
* If NULL, a A4 page format will be used
* @param aPageInfo = a reference to the page info, usually used to plot/display the board
*/
void SetPageInfo( const PAGE_INFO* aPageInfo ) { m_pageInfo = aPageInfo; }
/**
* Function SetMapFileFormat
* Initialize the format for the drill map file
* @param SetMapFileFormat = a PlotFormat value (one of
* PLOT_FORMAT_HPGL, PLOT_FORMAT_POST, PLOT_FORMAT_GERBER,
* PLOT_FORMAT_DXF, PLOT_FORMAT_SVG, PLOT_FORMAT_PDF
* the most useful are PLOT_FORMAT_PDF and PLOT_FORMAT_POST
*/
void SetMapFileFormat( PlotFormat aMapFmt ) { m_mapFileFmt = aMapFmt; }
/**
@ -229,70 +104,6 @@ public:
bool aGenDrill, bool aGenMap,
REPORTER * aReporter = NULL );
/**
* Function GenDrillReportFile
* Create a plain text report file giving a list of drill values and drill count
* for through holes, oblong holes, and for buried vias,
* drill values and drill count per layer pair
* there is only one report for all drill files even when buried or blinds vias exist
*
* Here is a sample created by this function:
* Drill report for F:/tmp/interf_u/interf_u.brd
* Created on 04/10/2012 20:48:38
* Selected Drill Unit: Imperial (inches)
*
* Drill report for plated through holes :
* T1 0,025" 0,64mm (88 holes)
* T2 0,031" 0,79mm (120 holes)
* T3 0,032" 0,81mm (151 holes) (with 1 slot)
* T4 0,040" 1,02mm (43 holes)
* T5 0,079" 2,00mm (1 hole) (with 1 slot)
* T6 0,120" 3,05mm (1 hole) (with 1 slot)
*
* Total plated holes count 404
*
*
* Drill report for buried and blind vias :
*
* Drill report for holes from layer Soudure to layer Interne1 :
*
* Total plated holes count 0
*
*
* Drill report for holes from layer Interne1 to layer Interne2 :
* T1 0,025" 0,64mm (3 holes)
*
* Total plated holes count 3
*
*
* Drill report for holes from layer Interne2 to layer Composant :
* T1 0,025" 0,64mm (1 hole)
*
* Total plated holes count 1
*
*
* Drill report for unplated through holes :
* T1 0,120" 3,05mm (1 hole) (with 1 slot)
*
* Total unplated holes count 1
*
* @param aFullFileName : the name of the file to create
* m_unitsDecimal = false to use inches, true to use mm in report file
*
* @return success if the file is created
*/
bool GenDrillReportFile( const wxString& aFullFileName );
/**
* Function GenDrillMapFile
* Plot a map of drill marks for holes.
* the paper sheet to use to plot the map is set in m_pageInfo
* ( calls SetPageInfo() to set it )
* if NULL, A4 format will be used
* @param aFullFileName : the full filename of the map file to create,
* @param aFormat : one of the supported plot formats (see enum PlotFormat )
*/
bool GenDrillMapFile( const wxString& aFullFileName, PlotFormat aFormat );
private:
/**
@ -303,23 +114,6 @@ private:
*/
int createDrillFile( FILE * aFile );
/**
* Function BuildHolesList
* Create the list of holes and tools for a given board
* The list is sorted by increasing drill size.
* Only holes included within aLayerPair are listed.
* If aLayerPair identifies with [F_Cu, B_Cu], then
* pad holes are always included also.
*
* @param aLayerPair is an inclusive range of layers.
* @param aGenerateNPTH_list :
* true to create NPTH only list (with no plated holes)
* false to created plated holes list (with no NPTH )
*/
void buildHolesList( DRILL_LAYER_PAIR aLayerPair,
bool aGenerateNPTH_list );
int getHolesCount() const { return m_holeListBuffer.size(); }
/* Print the DRILL file header. The full header is:
* M48
@ -337,43 +131,6 @@ private:
* According to the selected format
*/
void writeCoordinates( char* aLine, double aCoordX, double aCoordY );
/** Helper function.
* Writes the drill marks in HPGL, POSTSCRIPT or other supported formats
* Each hole size has a symbol (circle, cross X, cross + ...) up to
* PLOTTER::MARKER_COUNT different values.
* If more than PLOTTER::MARKER_COUNT different values,
* these other values share the same mark shape
* @param aPlotter = a PLOTTER instance (HPGL, POSTSCRIPT ... plotter).
*/
bool plotDrillMarks( PLOTTER* aPlotter );
/// Get unique layer pairs by examining the micro and blind_buried vias.
std::vector<DRILL_LAYER_PAIR> getUniqueLayerPairs() const;
/**
* Function printToolSummary
* prints m_toolListBuffer[] tools to aOut and returns total hole count.
* @param aOut = the current OUTPUTFORMATTER to print summary
* @param aSummaryNPTH = true to print summary for NPTH, false for PTH
*/
unsigned printToolSummary( OUTPUTFORMATTER& aOut, bool aSummaryNPTH ) const;
const std::string layerPairName( DRILL_LAYER_PAIR aPair ) const;
const std::string layerName( PCB_LAYER_ID aLayer ) const;
/**
* @return a filename which identify the drill file function.
* it is the board name with the layer pair names added, and for separate
* (PTH and NPTH) files, "-NPH" or "-NPTH" added
* @param aPair = the layer pair
* @param aNPTH = true to generate the filename of NPTH holes
* @param aMerge_PTH_NPTH = true to generate the filename of a file which containd both
* NPH and NPTH holes
*/
const wxString drillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
bool aMerge_PTH_NPTH ) const;
};
#endif // #ifndef _GENDRILL_EXCELLON_WRITER_

View File

@ -0,0 +1,340 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
*/
#include <fctsys.h>
#include <class_board.h>
#include <class_module.h>
#include <collectors.h>
#include <reporter.h>
#include <gendrill_file_writer_base.h>
/* Helper function for sorting hole list.
* Compare function used for sorting holes type type (plated then not plated)
* then by increasing diameter value and X value
*/
static bool CmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b )
{
if( a.m_Hole_NotPlated != b.m_Hole_NotPlated )
return b.m_Hole_NotPlated;
if( a.m_Hole_Diameter != b.m_Hole_Diameter )
return a.m_Hole_Diameter < b.m_Hole_Diameter;
// group by components when possible
const D_PAD* pada = dyn_cast<const D_PAD*>( a.m_ItemParent );
const D_PAD* padb = dyn_cast<const D_PAD*>( b.m_ItemParent );
if( pada && padb )
{
if( pada->GetParent()->GetReference().Cmp( padb->GetParent()->GetReference() ) != 0 )
return pada->GetParent()->GetReference().Cmp( padb->GetParent()->GetReference() ) < 0;
}
else if( pada || padb )
return true;
// At this point, holes are via holes: sort by position
if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
return a.m_Hole_Pos.x < b.m_Hole_Pos.x;
return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
}
void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
bool aGenerateNPTH_list )
{
HOLE_INFO new_hole;
m_holeListBuffer.clear();
m_toolListBuffer.clear();
wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller
// build hole list for vias
if( ! aGenerateNPTH_list ) // vias are always plated !
{
for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) )
{
int hole_sz = via->GetDrillValue();
if( hole_sz == 0 ) // Should not occur.
continue;
new_hole.m_ItemParent = via;
new_hole.m_Tool_Reference = -1; // Flag value for Not initialized
new_hole.m_Hole_Orient = 0;
new_hole.m_Hole_Diameter = hole_sz;
new_hole.m_Hole_NotPlated = false;
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
new_hole.m_Hole_Shape = 0; // hole shape: round
new_hole.m_Hole_Pos = via->GetStart();
via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );
// LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
// Remember: top layer = 0 and bottom layer = 31 for through hole vias
// Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
if( new_hole.m_Hole_Top_Layer != aLayerPair.first ||
new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
continue;
m_holeListBuffer.push_back( new_hole );
}
}
if( aLayerPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
// add holes for thru hole pads
for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() )
{
for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
{
if( !m_merge_PTH_NPTH )
{
if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
continue;
if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
continue;
}
if( pad->GetDrillSize().x == 0 )
continue;
new_hole.m_ItemParent = pad;
new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
new_hole.m_Hole_Orient = pad->GetOrientation();
new_hole.m_Hole_Shape = 0; // hole shape: round
new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
new_hole.m_Hole_Shape = 1; // oval flag set
new_hole.m_Hole_Size = pad->GetDrillSize();
new_hole.m_Hole_Pos = pad->GetPosition(); // hole position
new_hole.m_Hole_Bottom_Layer = B_Cu;
new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes
m_holeListBuffer.push_back( new_hole );
}
}
}
// Sort holes per increasing diameter value
sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSorting );
// build the tool list
int last_hole = -1; // Set to not initialized (this is a value not used
// for m_holeListBuffer[ii].m_Hole_Diameter)
bool last_notplated_opt = false;
DRILL_TOOL new_tool( 0, false );
unsigned jj;
for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
{
if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole ||
m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt )
{
new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
m_toolListBuffer.push_back( new_tool );
last_hole = new_tool.m_Diameter;
last_notplated_opt = new_tool.m_Hole_NotPlated;
}
jj = m_toolListBuffer.size();
if( jj == 0 )
continue; // Should not occurs
m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
m_toolListBuffer.back().m_TotalCount++;
if( m_holeListBuffer[ii].m_Hole_Shape )
m_toolListBuffer.back().m_OvalCount++;
}
}
std::vector<DRILL_LAYER_PAIR> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
{
wxASSERT( m_pcb );
static const KICAD_T interesting_stuff_to_collect[] = {
PCB_VIA_T,
EOT
};
PCB_TYPE_COLLECTOR vias;
vias.Collect( m_pcb, interesting_stuff_to_collect );
std::set< DRILL_LAYER_PAIR > unique;
DRILL_LAYER_PAIR layer_pair;
for( int i = 0; i < vias.GetCount(); ++i )
{
VIA* v = (VIA*) vias[i];
v->LayerPair( &layer_pair.first, &layer_pair.second );
// only make note of blind buried.
// thru hole is placed unconditionally as first in fetched list.
if( layer_pair != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
unique.insert( layer_pair );
}
}
std::vector<DRILL_LAYER_PAIR> ret;
ret.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) ); // always first in returned list
for( std::set< DRILL_LAYER_PAIR >::const_iterator it = unique.begin(); it != unique.end(); ++it )
ret.push_back( *it );
return ret;
}
const std::string GENDRILL_WRITER_BASE::layerName( PCB_LAYER_ID aLayer ) const
{
// Generic names here.
switch( aLayer )
{
case F_Cu:
return "front";
case B_Cu:
return "back";
default:
return StrPrintf( "in%d", aLayer );
}
}
const std::string GENDRILL_WRITER_BASE::layerPairName( DRILL_LAYER_PAIR aPair ) const
{
std::string ret = layerName( aPair.first );
ret += '-';
ret += layerName( aPair.second );
return ret;
}
const wxString GENDRILL_WRITER_BASE::getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
bool aMerge_PTH_NPTH ) const
{
wxASSERT( m_pcb );
wxString extend;
if( aNPTH )
extend = "-NPTH";
else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
{
if( !aMerge_PTH_NPTH )
extend = "-PTH";
// if merged, extend with nothing
}
else
{
extend += '-';
extend += layerPairName( aPair );
}
wxFileName fn = m_pcb->GetFileName();
fn.SetName( fn.GetName() + extend );
fn.SetExt( m_drillFileExtension );
wxString ret = fn.GetFullName();
return ret;
}
void GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory,
REPORTER * aReporter )
{
wxFileName fn;
wxString msg;
std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
// append a pair representing the NPTH set of holes, for separate drill files.
if( !m_merge_PTH_NPTH )
hole_sets.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) );
for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
it != hole_sets.end(); ++it )
{
DRILL_LAYER_PAIR pair = *it;
// For separate drill files, the last layer pair is the NPTH drill file.
bool doing_npth = m_merge_PTH_NPTH ? false : ( it == hole_sets.end() - 1 );
buildHolesList( pair, doing_npth );
// The file is created if it has holes, or if it is the non plated drill file
// to be sure the NPTH file is up to date in separate files mode.
if( getHolesCount() > 0 || doing_npth )
{
fn = getDrillFileName( pair, doing_npth, m_merge_PTH_NPTH );
fn.SetPath( aPlotDirectory );
fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
bool success = genDrillMapFile( fullfilename, m_mapFileFmt );
if( ! success )
{
if( aReporter )
{
msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullfilename ) );
aReporter->Report( msg );
}
return;
}
else
{
if( aReporter )
{
msg.Printf( _( "Create file %s\n" ), GetChars( fullfilename ) );
aReporter->Report( msg );
}
}
}
}
}

View File

@ -0,0 +1,346 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
*/
/**
* @file gendrill_file_writer_base.h
* @brief helper classes to handle hole info for drill files generators.
*/
#ifndef GENDRILL_FILE_WRITER_BASE_H
#define GENDRILL_FILE_WRITER_BASE_H
#include <vector>
class BOARD_ITEM;
// the DRILL_TOOL class handles tools used in the excellon drill file:
class DRILL_TOOL
{
public:
int m_Diameter; // the diameter of the used tool (for oblong, the smaller size)
int m_TotalCount; // how many times it is used (round and oblong)
int m_OvalCount; // oblong count
bool m_Hole_NotPlated; // Is the hole plated or not plated
public:
DRILL_TOOL( int aDiameter, bool a_NotPlated )
{
m_TotalCount = 0;
m_OvalCount = 0;
m_Diameter = aDiameter;
m_Hole_NotPlated = a_NotPlated;
}
};
/* the HOLE_INFO class handle hole which must be drilled (diameter, position and layers)
* For buried or micro vias, the hole is not on all layers.
* So we must generate a drill file for each layer pair (adjacent layers)
* Not plated holes are always through holes, and must be output on a specific drill file
* because they are drilled after the Pcb process is finished.
*/
class HOLE_INFO
{
public:
BOARD_ITEM* m_ItemParent; // The pad or via parent of this hole
int m_Hole_Diameter; // hole value, and for oblong: min(hole size x, hole size y)
int m_Tool_Reference; // Tool reference for this hole = 1 ... n (values <=0 must not be used)
wxSize m_Hole_Size; // hole size for oblong holes
double m_Hole_Orient; // Hole rotation (= pad rotation) for oblong holes
int m_Hole_Shape; // hole shape: round (0) or oval (1)
wxPoint m_Hole_Pos; // hole position
PCB_LAYER_ID m_Hole_Bottom_Layer; // hole ending layer (usually back layer)
PCB_LAYER_ID m_Hole_Top_Layer; // hole starting layer (usually front layer):
// m_Hole_Top_Layer < m_Hole_Bottom_Layer
bool m_Hole_NotPlated; // hole not plated. Must be in a specific drill file or section
public:
HOLE_INFO()
{
m_ItemParent = nullptr;
m_Hole_NotPlated = false;
m_Hole_Diameter = 0;
m_Tool_Reference = 0;
m_Hole_Orient = 0.0;
m_Hole_Shape = 0;
m_Hole_Bottom_Layer = B_Cu;
m_Hole_Top_Layer = F_Cu;
}
};
/* the DRILL_PRECISION helper class to handle drill precision format in excellon files
*/
class DRILL_PRECISION
{
public:
int m_lhs; // Left digit number (integer value of coordinates)
int m_rhs; // Right digit number (decimal value of coordinates)
public:
DRILL_PRECISION( int l = 2, int r = 4 )
{
m_lhs = l; m_rhs = r;
}
wxString GetPrecisionString()
{
wxString text;
text << m_lhs << wxT( ":" ) << m_rhs;
return text;
}
};
typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> DRILL_LAYER_PAIR;
/**
* GENDRILL_WRITER_BASE is a class to create drill maps and drill report,
* and a helper class to created drill files.
* drill files are created by specialized derived classes, depenfing on the
* file format.
*/
class GENDRILL_WRITER_BASE
{
public:
enum ZEROS_FMT { // Zero format in coordinates
DECIMAL_FORMAT, // Floating point coordinates
SUPPRESS_LEADING, // Suppress leading zeros
SUPPRESS_TRAILING, // Suppress trainling zeros
KEEP_ZEROS // keep zeros
};
protected:
BOARD* m_pcb;
wxString m_drillFileExtension; // .drl or .gbr, depending on format
bool m_unitsDecimal; // true = decimal, false = inches
ZEROS_FMT m_zeroFormat; // the zero format option for output file
DRILL_PRECISION m_precision; // The current coordinate precision (not used in decimal format)
double m_conversionUnits; // scaling factor to convert the board unites to
// Excellon/Gerber units (i.e inches or mm)
wxPoint m_offset; // Drill offset coordinates
bool m_merge_PTH_NPTH; // True to generate only one drill file
std::vector<HOLE_INFO> m_holeListBuffer; // Buffer containing holes
std::vector<DRILL_TOOL> m_toolListBuffer; // Buffer containing tools
PlotFormat m_mapFileFmt; // the format of the map drill file,
// if this map is needed
const PAGE_INFO* m_pageInfo; // the page info used to plot drill maps
// If NULL, use a A4 page format
// This Ctor is protected.
// Use derived classes to build a fully initialized GENDRILL_WRITER_BASE class.
GENDRILL_WRITER_BASE( BOARD* aPcb )
{
m_pcb = aPcb;
m_conversionUnits = 1.0;
m_unitsDecimal = true;
m_mapFileFmt = PLOT_FORMAT_PDF;
m_pageInfo = NULL;
m_merge_PTH_NPTH = false;
}
public:
~GENDRILL_WRITER_BASE()
{
}
/**
* set the option to make separate drill files for PTH and NPTH
* @param aMerge = true to make only one file containing PTH and NPTH
* = false to create 2 separate files
*/
void SetMergeOption( bool aMerge ) { m_merge_PTH_NPTH = aMerge; }
/**
* Return the plot offset (usually the position
* of the auxiliary axis
*/
const wxPoint GetOffset() { return m_offset; }
/**
* Sets the page info used to plot drill maps
* If NULL, a A4 page format will be used
* @param aPageInfo = a reference to the page info, usually used to plot/display the board
*/
void SetPageInfo( const PAGE_INFO* aPageInfo ) { m_pageInfo = aPageInfo; }
/**
* Function SetMapFileFormat
* Initialize the format for the drill map file
* @param SetMapFileFormat = a PlotFormat value (one of
* PLOT_FORMAT_HPGL, PLOT_FORMAT_POST, PLOT_FORMAT_GERBER,
* PLOT_FORMAT_DXF, PLOT_FORMAT_SVG, PLOT_FORMAT_PDF
* the most useful are PLOT_FORMAT_PDF and PLOT_FORMAT_POST
*/
void SetMapFileFormat( PlotFormat aMapFmt ) { m_mapFileFmt = aMapFmt; }
/**
* Function CreateMapFilesSet
* Creates the full set of map files for the board, in PS, PDF ... format
* (use SetMapFileFormat() to select the format)
* filenames are computed from the board name, and layers id
* @param aPlotDirectory = the output folder
* @param aReporter = a REPORTER to return activity or any message (can be NULL)
*/
void CreateMapFilesSet( const wxString& aPlotDirectory,
REPORTER* aReporter = NULL );
/**
* Function GenDrillReportFile
* Create a plain text report file giving a list of drill values and drill count
* for through holes, oblong holes, and for buried vias,
* drill values and drill count per layer pair
* there is only one report for all drill files even when buried or blinds vias exist
*
* Here is a sample created by this function:
* Drill report for F:/tmp/interf_u/interf_u.brd
* Created on 04/10/2012 20:48:38
* Selected Drill Unit: Imperial (inches)
*
* Drill report for plated through holes :
* T1 0,025" 0,64mm (88 holes)
* T2 0,031" 0,79mm (120 holes)
* T3 0,032" 0,81mm (151 holes) (with 1 slot)
* T4 0,040" 1,02mm (43 holes)
* T5 0,079" 2,00mm (1 hole) (with 1 slot)
* T6 0,120" 3,05mm (1 hole) (with 1 slot)
*
* Total plated holes count 404
*
*
* Drill report for buried and blind vias :
*
* Drill report for holes from layer Soudure to layer Interne1 :
*
* Total plated holes count 0
*
*
* Drill report for holes from layer Interne1 to layer Interne2 :
* T1 0,025" 0,64mm (3 holes)
*
* Total plated holes count 3
*
*
* Drill report for holes from layer Interne2 to layer Composant :
* T1 0,025" 0,64mm (1 hole)
*
* Total plated holes count 1
*
*
* Drill report for unplated through holes :
* T1 0,120" 3,05mm (1 hole) (with 1 slot)
*
* Total unplated holes count 1
*
* @param aFullFileName : the name of the file to create
*
* @return true if the file is created
*/
bool GenDrillReportFile( const wxString& aFullFileName );
protected:
/**
* Function GenDrillMapFile
* Plot a map of drill marks for holes.
* Hole list must be created before calling this function, by buildHolesList()
* for the right holes set (PTH, NPTH, buried/blind vias ...)
* the paper sheet to use to plot the map is set in m_pageInfo
* ( calls SetPageInfo() to set it )
* if NULL, A4 format will be used
* @param aFullFileName : the full filename of the map file to create,
* @param aFormat : one of the supported plot formats (see enum PlotFormat )
*/
bool genDrillMapFile( const wxString& aFullFileName, PlotFormat aFormat );
/**
* Function BuildHolesList
* Create the list of holes and tools for a given board
* The list is sorted by increasing drill size.
* Only holes included within aLayerPair are listed.
* If aLayerPair identifies with [F_Cu, B_Cu], then
* pad holes are always included also.
*
* @param aLayerPair is an inclusive range of layers.
* @param aGenerateNPTH_list :
* true to create NPTH only list (with no plated holes)
* false to created plated holes list (with no NPTH )
*/
void buildHolesList( DRILL_LAYER_PAIR aLayerPair,
bool aGenerateNPTH_list );
int getHolesCount() const { return m_holeListBuffer.size(); }
/** Helper function.
* Writes the drill marks in HPGL, POSTSCRIPT or other supported formats
* Each hole size has a symbol (circle, cross X, cross + ...) up to
* PLOTTER::MARKER_COUNT different values.
* If more than PLOTTER::MARKER_COUNT different values,
* these other values share the same mark shape
* @param aPlotter = a PLOTTER instance (HPGL, POSTSCRIPT ... plotter).
*/
bool plotDrillMarks( PLOTTER* aPlotter );
/// Get unique layer pairs by examining the micro and blind_buried vias.
std::vector<DRILL_LAYER_PAIR> getUniqueLayerPairs() const;
/**
* Function printToolSummary
* prints m_toolListBuffer[] tools to aOut and returns total hole count.
* @param aOut = the current OUTPUTFORMATTER to print summary
* @param aSummaryNPTH = true to print summary for NPTH, false for PTH
*/
unsigned printToolSummary( OUTPUTFORMATTER& aOut, bool aSummaryNPTH ) const;
/**
* minor helper function.
* @return a string from aPair to identify the layer layer pair.
* string is "<layer1Name>"-"<layer2Name>"
* used to generate a filename for drill files and drill maps
*/
const std::string layerPairName( DRILL_LAYER_PAIR aPair ) const;
/**
* minor helper function.
* @return a string from aLayer to identify the layer.
* string are "front" "back" or "in<aLayer>"
*/
const std::string layerName( PCB_LAYER_ID aLayer ) const;
/**
* @return a filename which identify the drill file function.
* it is the board name with the layer pair names added, and for separate
* (PTH and NPTH) files, "-NPH" or "-NPTH" added
* @param aPair = the layer pair
* @param aNPTH = true to generate the filename of NPTH holes
* @param aMerge_PTH_NPTH = true to generate the filename of a file which containd both
* NPH and NPTH holes
*/
virtual const wxString getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
bool aMerge_PTH_NPTH ) const;
};
#endif // #define GENDRILL_FILE_WRITER_BASE_H

View File

@ -0,0 +1,328 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
*/
/**
* @file gendrill_gerber_writer.cpp
* @brief Functions to create drill files in gerber X2 format.
*/
#include <fctsys.h>
#include <vector>
#include <plot_common.h>
#include <kicad_string.h>
#include <wxPcbStruct.h>
#include <pgm_base.h>
#include <build_version.h>
#include <class_board.h>
#include <pcbplot.h>
#include <pcbnew.h>
#include <gendrill_gerber_writer.h>
#include <wildcards_and_files_ext.h>
#include <reporter.h>
#include <plot_auxiliary_data.h>
#include <class_module.h>
GERBER_WRITER::GERBER_WRITER( BOARD* aPcb )
: GENDRILL_WRITER_BASE( aPcb )
{
m_zeroFormat = SUPPRESS_LEADING;
m_conversionUnits = 1.0;
m_unitsDecimal = true;
m_drillFileExtension = "gbr";
m_merge_PTH_NPTH = false;
}
void GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
bool aGenDrill, bool aGenMap,
REPORTER * aReporter )
{
// Note: In Gerber drill files, NPTH and PTH are always separate files
m_merge_PTH_NPTH = false;
wxFileName fn;
wxString msg;
std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
// append a pair representing the NPTH set of holes, for separate drill files.
// (Gerber drill files are separate files for PTH and NPTH)
hole_sets.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) );
for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
it != hole_sets.end(); ++it )
{
DRILL_LAYER_PAIR pair = *it;
// For separate drill files, the last layer pair is the NPTH drill file.
bool doing_npth = ( it == hole_sets.end() - 1 );
buildHolesList( pair, doing_npth );
// The file is created if it has holes, or if it is the non plated drill file
// to be sure the NPTH file is up to date in separate files mode.
if( getHolesCount() > 0 || doing_npth )
{
fn = getDrillFileName( pair, doing_npth, false );
fn.SetPath( aPlotDirectory );
if( aGenDrill )
{
wxString fullFilename = fn.GetFullPath();
int result = createDrillFile( fullFilename, doing_npth, pair.first, pair.second );
if( result < 0 )
{
if( aReporter )
{
msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullFilename ) );
aReporter->Report( msg );
}
break;
}
else
{
if( aReporter )
{
msg.Printf( _( "Create file %s\n" ), GetChars( fullFilename ) );
aReporter->Report( msg );
}
}
}
}
}
if( aGenMap )
CreateMapFilesSet( aPlotDirectory, aReporter );
}
// A helper class to transform an oblong hole to a segment
static void convertOblong2Segment( wxSize aSize, double aOrient, wxPoint& aStart, wxPoint& aEnd );
int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
int aLayer1, int aLayer2 )
{
int holes_count;
LOCALE_IO dummy; // Use the standard notation for double numbers
GERBER_PLOTTER plotter;
// Gerber drill file imply X2 format:
plotter.UseX2Attributes( true );
plotter.UseX2NetAttributes( true );
// Add the standard X2 header, without FileFunction
AddGerberX2Header( &plotter, m_pcb );
plotter.SetViewport( m_offset, IU_PER_MILS/10, /* scale */ 1.0, /* mirror */false );
// has meaning only for gerber plotter. Must be called only after SetViewport
plotter.SetGerberCoordinatesFormat( 6 );
plotter.SetCreator( wxT( "PCBNEW" ) );
// Add the standard X2 FileFunction for drill files
// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Route][Mixed]*%
wxString text( "%TF.FileFunction," );
if( aIsNpth )
text << "NonPlated,";
else
text << "Plated,";
// In Gerber files, layers num are 1 to copper layer count instead of F_Cu to B_Cu
// (0 to copper layer count-1)
// Note also for a n copper layers board, gerber layers num are 1 ... n
aLayer1 += 1;
if( aLayer2 == B_Cu )
aLayer2 = m_pcb->GetCopperLayerCount();
else
aLayer2 += 1;
text << aLayer1 << ",";
text << aLayer2 << ",";
// Now add PTH or NPTH or Blind or Buried attribute
int toplayer = 1;
int bottomlayer = m_pcb->GetCopperLayerCount();
if( aIsNpth )
text << "NPTH";
else if( aLayer1 == toplayer && aLayer2 == bottomlayer )
text << "PTH";
else if( aLayer1 == toplayer || aLayer2 == bottomlayer )
text << "Blind";
else
text << "Buried";
// Now add Drill or Route or Mixed:
// file containing only round holes have Drill attribute
// file containing only oblong holes have Routed attribute
// file containing both holes have Mixed attribute
bool hasOblong = false;
bool hasDrill = false;
for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
{
HOLE_INFO& hole_descr = m_holeListBuffer[ii];
if( hole_descr.m_Hole_Shape ) // m_Hole_Shape not 0 is an oblong hole)
hasOblong = true;
else
hasDrill = true;
}
if( hasOblong && hasDrill )
text << ",Mixed";
else if( hasDrill )
text << ",Drill";
else if( hasOblong )
text << ",Route";
// else: empty file.
// End of attribute:
text << "*%";
plotter.AddLineToHeader( text );
if( !plotter.OpenFile( aFullFilename ) )
return -1;
plotter.StartPlot();
holes_count = 0;
wxPoint hole_pos;
for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
{
HOLE_INFO& hole_descr = m_holeListBuffer[ii];
hole_pos = hole_descr.m_Hole_Pos;
// Manage the aperture attributes: in drill files 3 attributes can be used:
// "ViaDrill", only for vias, not pads
// "ComponentDrill", only for Through Holes pads
// "Slot" for oblong holes;
GBR_METADATA gbr_metadata;
if( dyn_cast<const VIA*>(hole_descr.m_ItemParent ) )
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_VIADRILL );
else if( dyn_cast<const D_PAD*>( hole_descr.m_ItemParent ) )
{
if( hole_descr.m_Hole_Shape )
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_SLOTDRILL );
else
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_COMPONENTDRILL );
// Add object attribute: component reference to pads (mainly usefull for users)
const D_PAD* pad = dyn_cast<const D_PAD*>( hole_descr.m_ItemParent );
gbr_metadata.SetCmpReference( pad->GetParent()->GetReference() );
gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
}
if( hole_descr.m_Hole_Shape )
{
#if 0 // set to 1 to use flashed oblong holes.
// Currently not possible for hole orient != 0 or 90 deg
// Use flashed oblong hole
plotter.FlashPadOval( hole_pos, hole_descr.m_Hole_Size,
hole_descr.m_Hole_Orient, FILLED, &gbr_metadata );
#else
// Use routing for oblong hole (Slots)
wxPoint start, end;
convertOblong2Segment( hole_descr.m_Hole_Size,
hole_descr.m_Hole_Orient, start, end );
int width = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
plotter.ThickSegment( start+hole_pos, end+hole_pos,
width, FILLED, &gbr_metadata );
#endif
}
else
{
int diam = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
plotter.FlashPadCircle( hole_pos, diam, FILLED, &gbr_metadata );
}
holes_count++;
}
plotter.EndPlot();
return holes_count;
}
void convertOblong2Segment( wxSize aSize, double aOrient, wxPoint& aStart, wxPoint& aEnd )
{
wxSize size( aSize );
double orient = aOrient;
/* The pad will be drawn as an oblong shape with size.y > size.x
* (Oval vertical orientation 0)
*/
if( size.x > size.y )
{
std::swap( size.x, size.y );
orient = AddAngles( orient, 900 );
}
int deltaxy = size.y - size.x; // distance between centers of the oval
int cx = 0;
int cy = deltaxy / 2;
RotatePoint( &cx, &cy, orient );
aStart = wxPoint( cx, cy );
cx = 0; cy = -deltaxy / 2;
RotatePoint( &cx, &cy, orient );
aEnd = wxPoint( cx, cy );
}
void GERBER_WRITER::SetFormat( int aRightDigits )
{
/* Set conversion scale depending on drill file units */
m_conversionUnits = 1.0 / IU_PER_MM; // Gerber units = mm
// Set precison (unit is mm).
m_precision.m_lhs = 4;
m_precision.m_rhs = aRightDigits == 6 ? 6 : 5;
}
const wxString GERBER_WRITER::getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
bool aMerge_PTH_NPTH ) const
{
// Gerber files extension is always .gbr.
// Therefore, to mark drill files, add "-drl" to the filename.
wxFileName fname( GENDRILL_WRITER_BASE::getDrillFileName( aPair, aNPTH, aMerge_PTH_NPTH ) );
fname.SetName( fname.GetName() + "-drl" );
return fname.GetFullPath();
}

View File

@ -0,0 +1,107 @@
/**
* @file gendrill_gerber_writer.h
* @brief Classes used in drill files, map files and report files generation.
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-2017 KiCad Developers, see change_log.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
*/
#ifndef _GENDRILL_GERBER_WRITER_
#define _GENDRILL_GERBER_WRITER_
#include <gendrill_file_writer_base.h>
class BOARD;
/**
* GERBER_WRITER is a class mainly used to create Gerber drill files
*/
class GERBER_WRITER: public GENDRILL_WRITER_BASE
{
public:
GERBER_WRITER( BOARD* aPcb );
~GERBER_WRITER()
{
}
/**
* Function SetFormat
* Initialize internal parameters to match the given format
* @param aRightDigits = number of digits for mantissa part of coordinates (5 or 6)
*/
void SetFormat( int aRightDigits = 6 );
/**
* Function SetOptions
* Initialize internal parameters to match drill options
* note: PTH and NPTH are always separate files in Gerber format
* @param aOffset = drill coordinates offset
*/
void SetOptions( wxPoint aOffset )
{
m_offset = aOffset;
m_merge_PTH_NPTH = false;
}
/**
* Function CreateDrillandMapFilesSet
* Creates the full set of Excellon drill file for the board
* filenames are computed from the board name, and layers id
* @param aPlotDirectory = the output folder
* @param aGenDrill = true to generate the EXCELLON drill file
* @param aGenMap = true to generate a drill map file
* @param aReporter = a REPORTER to return activity or any message (can be NULL)
*/
void CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
bool aGenDrill, bool aGenMap,
REPORTER * aReporter = NULL );
private:
/**
* Function createDrillFile
* Creates an Excellon drill file
* @param aFullFilename = the full filename
* @param aIsNpth = true for a NPTH file, false for a PTH file
* @param aLayer1 = the first board layer
* @param aLayer2 = the last board layer
* for blind buried vias, they are not always top and bottom layers
* @return hole count, or -1 if the file cannot be created
*/
int createDrillFile( wxString& aFullFilename, bool aIsNpth, int aLayer1, int aLayer2 );
/**
* @return a filename which identify the drill file function.
* it is the board name with the layer pair names added, and for separate
* (PTH and NPTH) files, "-NPH" or "-NPTH" added
* @param aPair = the layer pair
* @param aNPTH = true to generate the filename of NPTH holes
* @param aMerge_PTH_NPTH = true to generate the filename of a file which containd both
* NPH and NPTH holes
*/
virtual const wxString getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
bool aMerge_PTH_NPTH ) const override;
};
#endif // #ifndef _GENDRILL_GERBER_WRITER_

View File

@ -269,8 +269,9 @@ static wxString& makeStringCompatX1( wxString& aText, bool aUseX1CompatibilityMo
return aText;
}
void AddGerberX2Attribute( PLOTTER * aPlotter,
const BOARD *aBoard, LAYER_NUM aLayer, bool aUseX1CompatibilityMode )
void AddGerberX2Header( PLOTTER * aPlotter,
const BOARD *aBoard, bool aUseX1CompatibilityMode )
{
wxString text;
@ -339,6 +340,15 @@ void AddGerberX2Attribute( PLOTTER * aPlotter,
text.Printf( wxT( "%%TF.ProjectId,%s,%s,%s*%%" ), msg.ToAscii(), GetChars( guid ), rev.ToAscii() );
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
}
void AddGerberX2Attribute( PLOTTER * aPlotter,
const BOARD *aBoard, LAYER_NUM aLayer, bool aUseX1CompatibilityMode )
{
AddGerberX2Header( aPlotter, aBoard, aUseX1CompatibilityMode );
wxString text;
// Add the TF.FileFunction
text = GetGerberFileFunctionAttribute( aBoard, aLayer );

View File

@ -268,11 +268,35 @@ const wxString GetGerberProtelExtension( LAYER_NUM aLayer );
*/
const wxString GetGerberFileFunctionAttribute( const BOARD *aBoard, LAYER_NUM aLayer );
/**
* Function AddGerberX2Header
* Calculates some X2 attributes, as defined in the
* Gerber file format specification J4 (chapter 5) and add them
* the to the gerber file header:
* TF.GenerationSoftware
* TF.CreationDate
* TF.ProjectId
* file format attribute is not added
* @param aPlotter, the current plotter.
* @param aBoard = the board, needed to extract some info
* @param aUseX1CompatibilityMode = false to generate X2 attributes, true to
* use X1 compatibility (X2 attributes added as structured comments,
* starting by "G04 #@! " followed by the X2 attribute
*/
void AddGerberX2Header( PLOTTER * aPlotter,
const BOARD *aBoard, bool aUseX1CompatibilityMode = false );
/**
* Function AddGerberX2Attribute
* Calculates some X2 attributes, as defined in the
* Gerber file format specification J4 (chapter 5) and add them
* the to the gerber file header
* TF.GenerationSoftware
* TF.CreationDate
* TF.ProjectId
* TF.FileFunction
* TF.FilePolarity
*
* @param aPlotter, the current plotter.
* @param aBoard = the board, needed to extract some info
* @param aLayer = the layer number to create the attribute for
@ -280,7 +304,7 @@ const wxString GetGerberFileFunctionAttribute( const BOARD *aBoard, LAYER_NUM aL
* use X1 compatibility (X2 attributes added as structured comments,
* starting by "G04 #@! " followed by the X2 attribute
*/
extern void AddGerberX2Attribute( PLOTTER * aPlotter, const BOARD *aBoard,
LAYER_NUM aLayer, bool aUseX1CompatibilityMode );
void AddGerberX2Attribute( PLOTTER * aPlotter, const BOARD *aBoard,
LAYER_NUM aLayer, bool aUseX1CompatibilityMode );
#endif // PCBPLOT_H_