From afe92c7bfc9d02d551b2cfa675e73d18b956f010 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Fri, 28 Apr 2017 12:53:30 +0200 Subject: [PATCH] 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. --- common/gbr_metadata.cpp | 16 +- gerbview/class_X2_gerber_attributes.cpp | 38 +- gerbview/class_X2_gerber_attributes.h | 24 ++ gerbview/class_gerber_file_image_list.cpp | 9 + include/plot_auxiliary_data.h | 3 + pcbnew/CMakeLists.txt | 2 + pcbnew/dialogs/dialog_gendrill.cpp | 103 ++++-- pcbnew/dialogs/dialog_gendrill.h | 9 +- pcbnew/dialogs/dialog_gendrill_base.cpp | 50 ++- pcbnew/dialogs/dialog_gendrill_base.fbp | 204 ++++++++++- pcbnew/dialogs/dialog_gendrill_base.h | 7 +- pcbnew/dialogs/dialog_plot.cpp | 27 +- pcbnew/exporters/gen_drill_report_files.cpp | 31 +- pcbnew/exporters/gendrill_Excellon_writer.cpp | 297 +-------------- pcbnew/exporters/gendrill_Excellon_writer.h | 259 +------------ .../exporters/gendrill_file_writer_base.cpp | 340 +++++++++++++++++ pcbnew/exporters/gendrill_file_writer_base.h | 346 ++++++++++++++++++ pcbnew/exporters/gendrill_gerber_writer.cpp | 328 +++++++++++++++++ pcbnew/exporters/gendrill_gerber_writer.h | 107 ++++++ pcbnew/pcbplot.cpp | 14 +- pcbnew/pcbplot.h | 28 +- 21 files changed, 1611 insertions(+), 631 deletions(-) create mode 100644 pcbnew/exporters/gendrill_file_writer_base.cpp create mode 100644 pcbnew/exporters/gendrill_file_writer_base.h create mode 100644 pcbnew/exporters/gendrill_gerber_writer.cpp create mode 100644 pcbnew/exporters/gendrill_gerber_writer.h diff --git a/common/gbr_metadata.cpp b/common/gbr_metadata.cpp index 65609f3601..0511fbb0e2 100644 --- a/common/gbr_metadata.cpp +++ b/common/gbr_metadata.cpp @@ -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; diff --git a/gerbview/class_X2_gerber_attributes.cpp b/gerbview/class_X2_gerber_attributes.cpp index 8184c91565..fef61ede45 100644 --- a/gerbview/class_X2_gerber_attributes.cpp +++ b/gerbview/class_X2_gerber_attributes.cpp @@ -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) diff --git a/gerbview/class_X2_gerber_attributes.h b/gerbview/class_X2_gerber_attributes.h index e168a6e668..25b2a8e8bd 100644 --- a/gerbview/class_X2_gerber_attributes.h +++ b/gerbview/class_X2_gerber_attributes.h @@ -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 diff --git a/gerbview/class_gerber_file_image_list.cpp b/gerbview/class_gerber_file_image_list.cpp index 4b09436bb6..8c89ac0647 100644 --- a/gerbview/class_gerber_file_image_list.cpp +++ b/gerbview/class_gerber_file_image_list.cpp @@ -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)", diff --git a/include/plot_auxiliary_data.h b/include/plot_auxiliary_data.h index acda36d5b1..a58e1d6e27 100644 --- a/include/plot_auxiliary_data.h +++ b/include/plot_auxiliary_data.h @@ -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 }; diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index d81418deb6..ab32253a55 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -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 diff --git a/pcbnew/dialogs/dialog_gendrill.cpp b/pcbnew/dialogs/dialog_gendrill.cpp index eb34fb404b..4c58679cee 100644 --- a/pcbnew/dialogs/dialog_gendrill.cpp +++ b/pcbnew/dialogs/dialog_gendrill.cpp @@ -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 - * Copyright (C) 1992-2016 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-2017 Jean_Pierre Charras + * 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 #include #include -#include +#include +//#include #include #include @@ -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; diff --git a/pcbnew/dialogs/dialog_gendrill.h b/pcbnew/dialogs/dialog_gendrill.h index 1e9471b21a..26eb65edcb 100644 --- a/pcbnew/dialogs/dialog_gendrill.h +++ b/pcbnew/dialogs/dialog_gendrill.h @@ -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 diff --git a/pcbnew/dialogs/dialog_gendrill_base.cpp b/pcbnew/dialogs/dialog_gendrill_base.cpp index a8d67b0d65..6c9074e16d 100644 --- a/pcbnew/dialogs/dialog_gendrill_base.cpp +++ b/pcbnew/dialogs/dialog_gendrill_base.cpp @@ -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 ); diff --git a/pcbnew/dialogs/dialog_gendrill_base.fbp b/pcbnew/dialogs/dialog_gendrill_base.fbp index 47ae370f87..8f098df6c1 100644 --- a/pcbnew/dialogs/dialog_gendrill_base.fbp +++ b/pcbnew/dialogs/dialog_gendrill_base.fbp @@ -44,7 +44,7 @@ DIALOG_GENDRILL_BASE - 506,518 + 661,518 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER DIALOG_SHIM; dialog_shim.h Drill Files Generation @@ -112,6 +112,7 @@ bdirnameSizer wxHORIZONTAL + 1 none @@ -315,6 +316,96 @@ m_LeftBoxSizer wxVERTICAL none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "Excellon" "Gerber X2 (experimental)" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + File Format: + 1 + + 0 + + + 0 + + 1 + m_rbFileFormat + 1 + + + protected + 1 + + Resizable + 0 + 1 + + wxRA_SPECIFY_COLS + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + onFileFormatSelection + + + + + + + + 5 wxALL|wxEXPAND @@ -499,14 +590,101 @@ 5 wxEXPAND 0 - - wxID_ANY - Precision + + 2 + wxBOTH + 1 + + 0 - sbSizerPrecision - wxVERTICAL + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED none - + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Precision + + 0 + + + 0 + + 1 + m_staticTextTitle + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + 5 wxALL @@ -699,10 +877,11 @@ 0 wxID_ANY - Drill File Options: + Excellon Drill File Options: - sbOptSizer + sbExcellonOptSizer wxVERTICAL + 1 none @@ -1082,6 +1261,7 @@ sbSizerInfo wxVERTICAL + 1 none @@ -1094,6 +1274,7 @@ m_DefaultViasDrillSizer wxVERTICAL + 1 protected @@ -1191,6 +1372,7 @@ m_MicroViasDrillSizer wxVERTICAL + 1 protected @@ -1288,6 +1470,7 @@ sbSizerHoles wxVERTICAL + 1 none @@ -2096,6 +2279,7 @@ bmsgSizer wxVERTICAL + 1 none @@ -2133,7 +2317,7 @@ 0 - + 0 0 -1,90 diff --git a/pcbnew/dialogs/dialog_gendrill_base.h b/pcbnew/dialogs/dialog_gendrill_base.h index d0b57069fb..cdc073d424 100644 --- a/pcbnew/dialogs/dialog_gendrill_base.h +++ b/pcbnew/dialogs/dialog_gendrill_base.h @@ -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(); }; diff --git a/pcbnew/dialogs/dialog_plot.cpp b/pcbnew/dialogs/dialog_plot.cpp index bf336c6577..5d01bdf0d1 100644 --- a/pcbnew/dialogs/dialog_plot.cpp +++ b/pcbnew/dialogs/dialog_plot.cpp @@ -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 #include -// 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 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: diff --git a/pcbnew/exporters/gen_drill_report_files.cpp b/pcbnew/exporters/gen_drill_report_files.cpp index 06b69c854e..76bcd96e61 100644 --- a/pcbnew/exporters/gen_drill_report_files.cpp +++ b/pcbnew/exporters/gen_drill_report_files.cpp @@ -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 - * Copyright (C) 1992-2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-2017 Jean_Pierre Charras + * 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 #include -#include +#include /* 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; diff --git a/pcbnew/exporters/gendrill_Excellon_writer.cpp b/pcbnew/exporters/gendrill_Excellon_writer.cpp index 1d52dcd99d..6d4d399c38 100644 --- a/pcbnew/exporters/gendrill_Excellon_writer.cpp +++ b/pcbnew/exporters/gendrill_Excellon_writer.cpp @@ -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 -#include - #include -#include -#include #include #include #include #include -#include -#include -#include - #include #include #include #include #include -#include // 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 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 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; -} diff --git a/pcbnew/exporters/gendrill_Excellon_writer.h b/pcbnew/exporters/gendrill_Excellon_writer.h index 79e0e6cec8..6267571fa4 100644 --- a/pcbnew/exporters/gendrill_Excellon_writer.h +++ b/pcbnew/exporters/gendrill_Excellon_writer.h @@ -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 - +#include 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 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 m_holeListBuffer; // Buffer containing holes - std::vector 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 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_ diff --git a/pcbnew/exporters/gendrill_file_writer_base.cpp b/pcbnew/exporters/gendrill_file_writer_base.cpp new file mode 100644 index 0000000000..b58ad93b7f --- /dev/null +++ b/pcbnew/exporters/gendrill_file_writer_base.cpp @@ -0,0 +1,340 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Jean_Pierre Charras + * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck + * 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 + +#include +#include +#include +#include + +#include + + +/* 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( a.m_ItemParent ); + const D_PAD* padb = dyn_cast( 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 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 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 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::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 ); + } + } + } + } +} diff --git a/pcbnew/exporters/gendrill_file_writer_base.h b/pcbnew/exporters/gendrill_file_writer_base.h new file mode 100644 index 0000000000..55b1507bd7 --- /dev/null +++ b/pcbnew/exporters/gendrill_file_writer_base.h @@ -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 + * 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 + +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 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 m_holeListBuffer; // Buffer containing holes + std::vector 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 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 ""-"" + * 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" + */ + 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 + diff --git a/pcbnew/exporters/gendrill_gerber_writer.cpp b/pcbnew/exporters/gendrill_gerber_writer.cpp new file mode 100644 index 0000000000..3725827844 --- /dev/null +++ b/pcbnew/exporters/gendrill_gerber_writer.cpp @@ -0,0 +1,328 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Jean_Pierre Charras + * 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 + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +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 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::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(hole_descr.m_ItemParent ) ) + gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_VIADRILL ); + else if( dyn_cast( 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( 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(); +} diff --git a/pcbnew/exporters/gendrill_gerber_writer.h b/pcbnew/exporters/gendrill_gerber_writer.h new file mode 100644 index 0000000000..2f28b3d9aa --- /dev/null +++ b/pcbnew/exporters/gendrill_gerber_writer.h @@ -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 + * 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 + +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_ diff --git a/pcbnew/pcbplot.cpp b/pcbnew/pcbplot.cpp index 60bf961698..d461db4212 100644 --- a/pcbnew/pcbplot.cpp +++ b/pcbnew/pcbplot.cpp @@ -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 ); diff --git a/pcbnew/pcbplot.h b/pcbnew/pcbplot.h index e923d408da..64413c7ee0 100644 --- a/pcbnew/pcbplot.h +++ b/pcbnew/pcbplot.h @@ -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_